├── .gitignore ├── .jshintrc ├── .npmignore ├── .travis.yml ├── README.md ├── appveyor.yml ├── extend.js ├── gulpfile.js ├── index.js ├── package.json └── test ├── fixture-fail.html ├── fixture-pass.html └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "bitwise": true, 3 | "camelcase": true, 4 | "curly": true, 5 | "eqeqeq": true, 6 | "esnext": true, 7 | "freeze": true, 8 | "immed": true, 9 | "indent": 2, 10 | "latedef": "nofunc", 11 | "laxcomma": true, 12 | "newcap": true, 13 | "noarg": true, 14 | "noempty": true, 15 | "nonew": true, 16 | "quotmark": "single", 17 | "regexp": true, 18 | "smarttabs": true, 19 | "strict": true, 20 | "trailing": true, 21 | "undef": true, 22 | "unused": true, 23 | "white": true, 24 | "browser": true, 25 | "node": true, 26 | "globals": { 27 | "describe": false, 28 | "it": false, 29 | "before": false, 30 | "beforeEach": false, 31 | "after": false, 32 | "afterEach": false, 33 | "define": false 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .gitignore 2 | .jshintrc 3 | .npmignore 4 | .travis.yml 5 | gulpfile.js 6 | Makefile 7 | test/ 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "4.2" 4 | - "5.5" 5 | sudo: false 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [gulp](https://github.com/wearefractal/gulp)-mocha-phantomjs [![Build Status](https://travis-ci.org/mrhooray/gulp-mocha-phantomjs.svg?branch=master)](https://travis-ci.org/mrhooray/gulp-mocha-phantomjs) [![Build status](https://ci.appveyor.com/api/projects/status/4ngkp3ijx27alr5u?svg=true)](https://ci.appveyor.com/project/mrhooray/gulp-mocha-phantomjs) 2 | > run client-side [Mocha](https://github.com/visionmedia/mocha) tests with [PhantomJS](https://github.com/ariya/phantomjs) 3 | 4 | > a simple wrapper for [mocha-phantomjs-core](https://github.com/nathanboktae/mocha-phantomjs-core) library 5 | 6 | ## Warning 7 | This project is no longer maintained. 8 | 9 | ## Installation 10 | ### node 11 | ```shell 12 | $ npm install gulp-mocha-phantomjs --save-dev 13 | ``` 14 | 15 | ## Usage 16 | ```html 17 | 18 | 19 | 20 | Mocha 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 36 | 39 | 40 | 41 | ``` 42 | 43 | ```javascript 44 | var gulp = require('gulp'); 45 | var mochaPhantomJS = require('gulp-mocha-phantomjs'); 46 | 47 | gulp.task('test', function () { 48 | return gulp 49 | .src('test/runner.html') 50 | .pipe(mochaPhantomJS()); 51 | }); 52 | ``` 53 | 54 | Reporter can be chosen via `reporter` option: 55 | 56 | ```javascript 57 | gulp.task('test', function () { 58 | return gulp 59 | .src('test/runner.html') 60 | .pipe(mochaPhantomJS({reporter: 'spec'})); 61 | }); 62 | ``` 63 | 64 | Output of mocha tests can be piped into a file via `dump` option: 65 | 66 | ```javascript 67 | gulp.task('test', function () { 68 | return gulp 69 | .src('test/runner.html') 70 | .pipe(mochaPhantomJS({reporter: 'spec', dump:'test.log'})); 71 | }); 72 | ``` 73 | 74 | Test against remote by url: 75 | 76 | ```javascript 77 | gulp.task('test', function () { 78 | var stream = mochaPhantomJS(); 79 | stream.write({path: 'http://localhost:8000/index.html'}); 80 | stream.end(); 81 | return stream; 82 | }); 83 | ``` 84 | 85 | Suppress PhantomJS’s console output: 86 | 87 | ```javascript 88 | gulp.task('test', function() { 89 | return gulp 90 | .src('test/runner.html') 91 | .pipe(mochaPhantomJS({ 92 | suppressStdout: true, 93 | suppressStderr: true 94 | })); 95 | }); 96 | ``` 97 | 98 | Pass options to mocha and/or PhantomJS: 99 | 100 | ```javascript 101 | gulp.task('test', function () { 102 | return gulp 103 | .src('test/runner.html') 104 | .pipe(mochaPhantomJS({ 105 | reporter: 'tap', 106 | mocha: { 107 | grep: 'pattern' 108 | }, 109 | phantomjs: { 110 | viewportSize: { 111 | width: 1024, 112 | height: 768 113 | }, 114 | useColors:true 115 | } 116 | })); 117 | }); 118 | ``` 119 | 120 | ## License 121 | MIT 122 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | matrix: 3 | - nodejs_version: "4.2" 4 | - nodejs_version: "5.5" 5 | 6 | install: 7 | - ps: Install-Product node $env:nodejs_version 8 | - npm install 9 | 10 | test_script: 11 | - node --version 12 | - npm --version 13 | - npm test 14 | 15 | build: off 16 | -------------------------------------------------------------------------------- /extend.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function extend() { 4 | var target = {}; 5 | 6 | for (var i = 0; i < arguments.length; i++) { 7 | var source = arguments[i]; 8 | 9 | for (var key in source) { 10 | if (source.hasOwnProperty(key)) { 11 | target[key] = source[key]; 12 | } 13 | } 14 | } 15 | 16 | return target; 17 | } 18 | 19 | module.exports = extend; 20 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'); 4 | var jshint = require('gulp-jshint'); 5 | var mocha = require('gulp-mocha'); 6 | var mochaPhantomJS = require('./'); 7 | 8 | var paths = { 9 | scripts: ['**/*.js', '!node_modules/**'], 10 | tests: 'test/**.*js' 11 | }; 12 | 13 | gulp.task('jshint', function () { 14 | return gulp 15 | .src(paths.scripts) 16 | .pipe(jshint()) 17 | .pipe(jshint.reporter('default')) 18 | .pipe(jshint.reporter('fail')); 19 | }); 20 | 21 | gulp.task('test', ['jshint'], function () { 22 | return gulp 23 | .src(paths.tests) 24 | .pipe(mocha({reporter: 'spec'})); 25 | }); 26 | 27 | gulp.task('run:pass', function () { 28 | return gulp 29 | .src('test/fixture-pass.html') 30 | .pipe(mochaPhantomJS({reporter: 'spec'})); 31 | }); 32 | 33 | gulp.task('run:dump', function () { 34 | return gulp 35 | .src('test/fixture-pass.html') 36 | .pipe(mochaPhantomJS({reporter: 'xunit', dump:'dump.xml'})); 37 | }); 38 | 39 | gulp.task('run:http', function () { 40 | var stream = mochaPhantomJS(); 41 | stream.write({path: 'http://localhost:8000/test/fixture-pass.html'}); 42 | stream.end(); 43 | return stream; 44 | }); 45 | 46 | gulp.task('default', ['test']); 47 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var path = require('path'); 5 | var url = require('url'); 6 | var spawn = require('child_process').spawn; 7 | var through = require('through2'); 8 | var gutil = require('gulp-util'); 9 | 10 | var pluginName = require('./package.json').name; 11 | var extend = require('./extend'); 12 | 13 | function mochaPhantomJS(options) { 14 | options = options || {}; 15 | 16 | var scriptPath = lookup('mocha-phantomjs-core/mocha-phantomjs-core.js'); 17 | 18 | if (!scriptPath) { 19 | throw new gutil.PluginError(pluginName, 'mocha-phantomjs-core.js not found'); 20 | } 21 | 22 | return through.obj(function (file, enc, cb) { 23 | var args = [ 24 | scriptPath, 25 | toURL(file.path, options.mocha), 26 | options.reporter || 'spec', 27 | JSON.stringify(options.phantomjs || {}) 28 | ]; 29 | 30 | spawnPhantomJS(args, options, this, function (err) { 31 | if (err) { 32 | return cb(err); 33 | } 34 | 35 | this.push(file); 36 | 37 | cb(); 38 | }.bind(this)); 39 | }); 40 | } 41 | 42 | function toURL(path, query) { 43 | var parsed = url.parse(path, true); 44 | 45 | parsed.query = extend(parsed.query, query); 46 | parsed.search = null; 47 | 48 | if (['http:', 'https:', 'file:'].indexOf(parsed.protocol) > -1) { 49 | return url.format(parsed); 50 | } else { 51 | return fileURL(url.format(parsed)); 52 | } 53 | } 54 | 55 | function fileURL(str) { 56 | var pathName = path.resolve(str).replace(/\\/g, '/'); 57 | 58 | // for windows 59 | if (pathName[0] !== '/') { 60 | pathName = '/' + pathName; 61 | } 62 | 63 | return encodeURI('file://' + pathName); 64 | } 65 | 66 | function spawnPhantomJS(args, options, stream, cb) { 67 | // in case npm is started with --no-bin-links 68 | var phantomjsPath = lookup('.bin/phantomjs', true) || lookup('phantomjs-prebuilt/bin/phantomjs', true); 69 | 70 | if (!phantomjsPath) { 71 | return cb(new gutil.PluginError(pluginName, 'PhantomJS not found')); 72 | } 73 | 74 | var phantomjs = spawn(phantomjsPath, args); 75 | 76 | if (options.dump) { 77 | phantomjs.stdout.pipe(fs.createWriteStream(options.dump, {flags: 'a'})); 78 | } 79 | 80 | if (!options.suppressStdout) { 81 | phantomjs.stdout.pipe(process.stdout); 82 | } 83 | 84 | if (!options.suppressStderr) { 85 | phantomjs.stderr.pipe(process.stderr); 86 | } 87 | 88 | phantomjs.stdout.on('data', stream.emit.bind(stream, 'phantomjsStdoutData')); 89 | phantomjs.stdout.on('end', stream.emit.bind(stream, 'phantomjsStdoutEnd')); 90 | 91 | phantomjs.stderr.on('data', stream.emit.bind(stream, 'phantomjsStderrData')); 92 | phantomjs.stderr.on('end', stream.emit.bind(stream, 'phantomjsStderrEnd')); 93 | 94 | phantomjs.on('error', stream.emit.bind(stream, 'phantomjsError')); 95 | phantomjs.on('exit', stream.emit.bind(stream, 'phantomjsExit')); 96 | 97 | phantomjs.on('error', function (err) { 98 | cb(new gutil.PluginError(pluginName, err.message)); 99 | }); 100 | 101 | phantomjs.on('exit', function (code) { 102 | if (code === 0 || options.silent) { 103 | cb(); 104 | } else { 105 | cb(new gutil.PluginError(pluginName, 'test failed. phantomjs exit code: ' + code)); 106 | } 107 | }); 108 | } 109 | 110 | function lookup(path, isExecutable) { 111 | for (var i = 0 ; i < module.paths.length; i++) { 112 | var absPath = require('path').join(module.paths[i], path); 113 | if (isExecutable && process.platform === 'win32') { 114 | absPath += '.cmd'; 115 | } 116 | if (fs.existsSync(absPath)) { 117 | return absPath; 118 | } 119 | } 120 | } 121 | 122 | module.exports = mochaPhantomJS; 123 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gulp-mocha-phantomjs", 3 | "version": "0.12.2", 4 | "description": "run client-side Mocha tests with PhantomJS", 5 | "keywords": [ 6 | "gulpplugin", 7 | "mocha", 8 | "phantomjs", 9 | "test", 10 | "testing", 11 | "framework", 12 | "runner", 13 | "unit", 14 | "spec", 15 | "tdd", 16 | "bdd" 17 | ], 18 | "author": "Rui Hu ", 19 | "main": "index.js", 20 | "scripts": { 21 | "test": "gulp test" 22 | }, 23 | "dependencies": { 24 | "gulp-util": "^3.0.7", 25 | "mocha-phantomjs-core": "^2.0.0", 26 | "phantomjs-prebuilt": "^2.1.4", 27 | "through2": "^2.0.1" 28 | }, 29 | "devDependencies": { 30 | "gulp": "^3.9.1", 31 | "gulp-jshint": "^2.0.0", 32 | "gulp-mocha": "^2.2.0", 33 | "jshint": "^2.9.1", 34 | "mocha": "^2.4.5", 35 | "should": "^8.2.1" 36 | }, 37 | "homepage": "https://github.com/mrhooray/gulp-mocha-phantomjs", 38 | "repository": { 39 | "type": "git", 40 | "url": "git://github.com/mrhooray/gulp-mocha-phantomjs.git" 41 | }, 42 | "bugs": { 43 | "url": "https://github.com/mrhooray/gulp-mocha-phantomjs/issues" 44 | }, 45 | "license": "MIT" 46 | } 47 | -------------------------------------------------------------------------------- /test/fixture-fail.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Mocha 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 20 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /test/fixture-pass.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Mocha 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 38 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var path = require('path'); 5 | var gutil = require('gulp-util'); 6 | var mochaPhantomJS = require('../index'); 7 | var out = process.stdout.write.bind(process.stdout); 8 | 9 | describe('gulp-mocha-phantomjs', function () { 10 | this.timeout(0); 11 | 12 | it('should pass when test passed', function (cb) { 13 | var file = new gutil.File({path: path.join(__dirname, 'fixture-pass.html')}); 14 | var stream = mochaPhantomJS(); 15 | var passed = false; 16 | 17 | stream.on('error', function () { 18 | assert.fail(undefined, undefined, 'should not emit error'); 19 | }); 20 | 21 | stream.on('finish', function () { 22 | assert.equal(passed, true); 23 | process.stdout.write = out; 24 | cb(); 25 | }); 26 | 27 | process.stdout.write = function (str) { 28 | if (/3 passing/.test(str)) { 29 | passed = true; 30 | } 31 | }; 32 | 33 | stream.write(file); 34 | stream.end(); 35 | }); 36 | 37 | it('should fail build when test failed', function (cb) { 38 | var file = new gutil.File({path: path.join(__dirname, 'fixture-fail.html')}); 39 | var stream = mochaPhantomJS(); 40 | 41 | stream.on('error', function (err) { 42 | assert.equal(err.plugin, require('../package.json').name); 43 | process.stdout.write = out; 44 | cb(); 45 | }); 46 | 47 | process.stdout.write = function () {}; 48 | 49 | stream.write(file); 50 | stream.end(); 51 | }); 52 | 53 | it('should fail silently in silent mode', function (cb) { 54 | var file = new gutil.File({path: path.join(__dirname, 'fixture-fail.html')}); 55 | var stream = mochaPhantomJS({silent: true}); 56 | 57 | stream.on('error', function () { 58 | assert.fail(undefined, undefined, 'should not emit error'); 59 | }); 60 | 61 | stream.on('finish', function () { 62 | process.stdout.write = out; 63 | cb(); 64 | }); 65 | 66 | process.stdout.write = function () {}; 67 | 68 | stream.write(file); 69 | stream.end(); 70 | }); 71 | 72 | it('should use the tap reporter when chosen', function (cb) { 73 | var file = new gutil.File({path: path.join(__dirname, 'fixture-pass.html')}); 74 | var stream = mochaPhantomJS({reporter: 'tap'}); 75 | var passed = false; 76 | 77 | stream.on('error', function () { 78 | assert.fail(undefined, undefined, 'should not emit error'); 79 | }); 80 | 81 | stream.on('finish', function () { 82 | assert.equal(passed, true); 83 | process.stdout.write = out; 84 | cb(); 85 | }); 86 | 87 | process.stdout.write = function (str) { 88 | if (/# pass 3/.test(str)) { 89 | passed = true; 90 | } 91 | }; 92 | 93 | stream.write(file); 94 | stream.end(); 95 | }); 96 | 97 | it('should pass through mocha options', function (cb) { 98 | var file = new gutil.File({path: path.join(__dirname, 'fixture-pass.html')}); 99 | var stream = mochaPhantomJS({mocha: {grep: 'viewport'}}); 100 | var passed = false; 101 | 102 | stream.on('error', function () { 103 | assert.fail(undefined, undefined, 'should not emit error'); 104 | }); 105 | 106 | stream.on('finish', function () { 107 | assert.equal(passed, true); 108 | process.stdout.write = out; 109 | cb(); 110 | }); 111 | 112 | process.stdout.write = function (str) { 113 | if (/1 passing/.test(str)) { 114 | passed = true; 115 | } 116 | if (/should be false/.test(str) || /should be true/.test(str)) { 117 | assert.fail(); 118 | } 119 | }; 120 | 121 | stream.write(file); 122 | stream.end(); 123 | }); 124 | 125 | it('should pass through phantomjs options', function (cb) { 126 | var file = new gutil.File({path: path.join(__dirname, 'fixture-pass.html')}); 127 | var stream = mochaPhantomJS({ 128 | phantomjs: { 129 | viewportSize: { 130 | width: 1, 131 | height: 1 132 | } 133 | } 134 | }); 135 | var passed = false; 136 | 137 | stream.on('error', function () { 138 | assert.fail(undefined, undefined, 'should not emit error'); 139 | }); 140 | 141 | stream.on('finish', function () { 142 | assert.equal(passed, true); 143 | process.stdout.write = out; 144 | cb(); 145 | }); 146 | 147 | process.stdout.write = function (str) { 148 | if (/3 passing/.test(str)) { 149 | passed = true; 150 | } 151 | }; 152 | 153 | stream.write(file); 154 | stream.end(); 155 | }); 156 | 157 | it('should handle uri with querystring properly', function (cb) { 158 | // mocha options with higher precedence 159 | var file = new gutil.File({path: path.join(__dirname, 'fixture-pass.html?grep=should')}); 160 | var stream = mochaPhantomJS({mocha: {grep: 'viewport'}}); 161 | var passed = false; 162 | 163 | stream.on('error', function () { 164 | assert.fail(undefined, undefined, 'should not emit error'); 165 | }); 166 | 167 | stream.on('finish', function () { 168 | assert.equal(passed, true); 169 | process.stdout.write = out; 170 | cb(); 171 | }); 172 | 173 | process.stdout.write = function (str) { 174 | if (/1 passing/.test(str)) { 175 | passed = true; 176 | } 177 | if (/should be false/.test(str) || /should be true/.test(str)) { 178 | assert.fail(); 179 | } 180 | }; 181 | 182 | stream.write(file); 183 | stream.end(); 184 | }); 185 | }); 186 | --------------------------------------------------------------------------------