diff --git a/.travis.yml b/.travis.yml index 2c6e9b0..96ce5e6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ sudo: false language: node_js node_js: - - '6' - '4' - - '0.12' - - '0.10' + - '6' + - '7' diff --git a/index.js b/index.js index c50c607..621fc93 100644 --- a/index.js +++ b/index.js @@ -1,72 +1,55 @@ 'use strict'; -var domain = require('domain'); // eslint-disable-line no-restricted-modules -var gutil = require('gulp-util'); -var through = require('through'); -var Mocha = require('mocha'); -var plur = require('plur'); -var reqCwd = require('req-cwd'); -module.exports = function (opts) { - opts = opts || {}; - - var mocha = new Mocha(opts); - var cache = {}; - - for (var key in require.cache) { // eslint-disable-line guard-for-in - cache[key] = true; - } - - function clearCache() { - for (var key in require.cache) { - if (!cache[key] && !/\.node$/.test(key)) { - delete require.cache[key]; - } - } - } - - if (Array.isArray(opts.require) && opts.require.length) { - opts.require.forEach(function (x) { - reqCwd(x); - }); - } - - return through(function (file) { - mocha.addFile(file.path); - this.queue(file); - }, function () { - var self = this; - var d = domain.create(); - var runner; - - function handleException(err) { - if (runner) { - runner.uncaught(err); - } else { - clearCache(); - self.emit('error', new gutil.PluginError('gulp-mocha', err, { - stack: err.stack, - showStack: true - })); - } - } - - d.on('error', handleException); - d.run(function () { - try { - runner = mocha.run(function (errCount) { - clearCache(); - - if (errCount > 0) { - self.emit('error', new gutil.PluginError('gulp-mocha', errCount + ' ' + plur('test', errCount) + ' failed.', { - showStack: false - })); - } - - self.emit('end'); - }); - } catch (err) { - handleException(err); - } - }); - }); +const dargs = require('dargs'); +const execa = require('execa'); +const gutil = require('gulp-util'); +const through = require('through2'); + +module.exports = options => { + const defaults = {colors: true, suppress: false}; + + options = Object.assign(defaults, options); + + if (Object.prototype.toString.call(options.globals) === '[object Array]') { + // typically wouldn't modify passed options, but mocha requires a comma- + // separated list of names, http://mochajs.org/#globals-names, whereas dargs + // will treat arrays differently. + options.globals = options.globals.join(','); + } + + // exposing args for testing + const args = dargs(options, {excludes: ['suppress'], ignoreFalse: true}); + const files = []; + + function aggregate(file, encoding, done) { + if (file.isNull()) { + return done(null, file); + } + + if (file.isStream()) { + return done(new gutil.PluginError('gulp-mocha', 'Streaming not supported')); + } + + files.push(file.path); + + return done(); + } + + function flush(done) { + execa('mocha', files.concat(args)) + .then(result => { + if (!options.suppress) { + process.stdout.write(result.stdout); + } + + this.emit('result', result); + done(); + }) + .catch(err => { + this.emit('error', new gutil.PluginError('gulp-mocha', err)); + done(); + }); + } + + return through.obj(aggregate, flush); }; diff --git a/package.json b/package.json index cf6ed0b..74886d7 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "url": "sindresorhus.com" }, "engines": { - "node": ">=0.10.0" + "node": ">=4" }, "scripts": { "test": "xo && mocha" @@ -33,12 +33,11 @@ "tap" ], "dependencies": { + "dargs": "^5.1.0", + "execa": "^0.6.0", "gulp-util": "^3.0.0", "mocha": "^3.0.0", - "plur": "^2.1.0", - "req-cwd": "^1.0.1", - "temp": "^0.8.3", - "through": "^2.3.4" + "through2": "^2.0.3" }, "devDependencies": { "xo": "*" @@ -47,6 +46,7 @@ "envs": [ "node", "mocha" - ] + ], + "space": true } } diff --git a/readme.md b/readme.md index 1cd601c..1fd231f 100644 --- a/readme.md +++ b/readme.md @@ -26,7 +26,7 @@ $ npm install --save-dev gulp-mocha const gulp = require('gulp'); const mocha = require('gulp-mocha'); -gulp.task('default', () => +gulp.task('default', () => gulp.src('test.js', {read: false}) // gulp-mocha needs filepaths so you can't have any plugins before it .pipe(mocha({reporter: 'nyan'})) @@ -42,6 +42,10 @@ gulp.task('default', () => #### options +gulp-mocha will pass any options defined directly to the `mocha` binary. That +means you have every [command line option](http://mochajs.org/#usage) available +by default. Listed below are some of the more commonly used options: + ##### ui Type: `string`
@@ -80,12 +84,12 @@ Default: `false` Bail on the first test failure. -##### ignoreLeaks +##### checkLeaks Type: `boolean`
Default: `false` -Ignore global leaks. +Check for global variable leaks. ##### grep @@ -107,7 +111,7 @@ Require custom modules before tests are run. If your test suite is not exiting it might be because you still have a lingering callback, most often caused by an open database connection. You should close this connection or do the following: ```js -gulp.task('default', () => +gulp.task('default', () => gulp.src('test.js') .pipe(mocha()) .once('error', () => { diff --git a/test/fixtures/fixture-async.js b/test/fixtures/fixture-async.js index 332da87..49eb53c 100644 --- a/test/fixtures/fixture-async.js +++ b/test/fixtures/fixture-async.js @@ -3,7 +3,7 @@ var assert = require('assert'); it('should fail after timeout', function (done) { - setTimeout(function () { - assert(false); - }, 10); + setTimeout(function () { + assert(false); + }, 10); }); diff --git a/test/fixtures/fixture-fail.js b/test/fixtures/fixture-fail.js index ae07f1d..1a19cfe 100644 --- a/test/fixtures/fixture-fail.js +++ b/test/fixtures/fixture-fail.js @@ -2,5 +2,5 @@ var assert = require('assert'); it('should fail', function () { - assert(false); + assert(false); }); diff --git a/test/fixtures/fixture-pass.js b/test/fixtures/fixture-pass.js index 938a935..2f5472e 100644 --- a/test/fixtures/fixture-pass.js +++ b/test/fixtures/fixture-pass.js @@ -2,5 +2,5 @@ var assert = require('assert'); it('should pass', function () { - assert(true); + assert(true); }); diff --git a/test/fixtures/fixture-throws-uncaught.js b/test/fixtures/fixture-throws-uncaught.js index 45b9b60..2231295 100644 --- a/test/fixtures/fixture-throws-uncaught.js +++ b/test/fixtures/fixture-throws-uncaught.js @@ -2,7 +2,7 @@ var assert = require('assert'); it('throws after timeout', function (done) { - setTimeout(function () { - throw new Error('Exception in delayed function'); - }, 10); + setTimeout(function () { + throw new Error('Exception in delayed function'); + }, 10); }); diff --git a/test/fixtures/fixture-throws.js b/test/fixtures/fixture-throws.js index e87d2ab..ae70231 100644 --- a/test/fixtures/fixture-throws.js +++ b/test/fixtures/fixture-throws.js @@ -2,5 +2,5 @@ var assert = require('assert'); it('contains syntax errors', function () { - assert false; + assert false; }); diff --git a/test/require-test.js b/test/require-test.js deleted file mode 100644 index 77b5f52..0000000 --- a/test/require-test.js +++ /dev/null @@ -1,49 +0,0 @@ -'use strict'; -var path = require('path'); -var fs = require('fs'); -var assert = require('assert'); -var temp = require('temp'); -var mocha = require('../'); - -var tempFile; -var tempFileBaseName; -var filePrefix = './'; - -function removeFile(file) { - try { - fs.unlinkSync(file); - } catch (err) {} -} - -beforeEach(function () { - tempFile = temp.path({ - dir: process.cwd(), - suffix: '.js' - }); - - tempFileBaseName = path.basename(tempFile); - - fs.writeFileSync(tempFile, ''); -}); - -afterEach(function () { - removeFile(tempFile); -}); - -it('should fail when trying to require a file that doesn\'t exist', function () { - removeFile(tempFile); - - assert.throws(function () { - mocha({require: [filePrefix + tempFileBaseName]}); - }); -}); - -it('should be able to import js-files in cwd', function () { - mocha({require: [filePrefix + tempFileBaseName]}); -}); - -it('should fail when not having the ./ file prefix', function () { - assert.throws(function () { - mocha({require: [tempFileBaseName]}); - }); -}); diff --git a/test/test.js b/test/test.js index 8b24687..98c50f0 100644 --- a/test/test.js +++ b/test/test.js @@ -1,140 +1,65 @@ 'use strict'; -var assert = require('assert'); -var gutil = require('gulp-util'); -var mocha = require('../'); -var out = process.stdout.write.bind(process.stdout); -var err = process.stderr.write.bind(process.stderr); - -afterEach(function () { - process.stdout.write = out; - process.stderr.write = err; -}); - -it('should run unit test and pass', function (cb) { - var stream = mocha(); - - process.stdout.write = function (str) { - if (/1 passing/.test(str)) { - assert(true); - cb(); - } - }; - - stream.write(new gutil.File({path: './test/fixtures/fixture-pass.js'})); - stream.end(); -}); - -it('should run unit test and fail', function (cb) { - var stream = mocha(); - - process.stdout.write = function (str) { - if (/1 failing/.test(str)) { - assert(true); - cb(); - } - }; - - stream.once('error', function () {}); - stream.write(new gutil.File({path: './test/fixtures/fixture-fail.js'})); - stream.end(); -}); - -it('should call the callback right after end', function (cb) { - var stream = mocha(); - - stream.once('end', function () { - assert(true); - cb(); - }); - - stream.end(); -}); - -it('should clear cache after successful run', function (done) { - var stream = mocha(); - - stream.once('end', function () { - for (var key in require.cache) { - if (/fixture-pass/.test(key.toString())) { - done(new Error('require cache still contained: ' + key)); - return; - } - } - - done(); - }); - - stream.write(new gutil.File({path: './test/fixtures/fixture-pass.js'})); - stream.end(); -}); - -it('should clear cache after failing run', function (done) { - var stream = mocha(); - - stream.once('error', function () { - for (var key in require.cache) { - if (/fixture-fail/.test(key.toString())) { - done(new Error('require cache still contained: ' + key)); - return; - } - } - - done(); - }); - - stream.write(new gutil.File({path: './test/fixtures/fixture-fail.js'})); - stream.end(); -}); - -it('should clear cache after mocha threw', function (done) { - var stream = mocha(); - - stream.once('error', function () { - for (var key in require.cache) { - if (/fixture-pass/.test(key.toString()) || /fixture-throws/.test(key.toString())) { - done(new Error('require cache still contained: ' + key)); - return; - } - } - - done(); - }); - stream.write(new gutil.File({path: './test/fixtures/fixture-pass.js'})); - stream.write(new gutil.File({path: './test/fixtures/fixture-throws.js'})); - stream.end(); -}); - -it('should clear cache after mocha threw uncaught exception', function (done) { - var stream = mocha(); - - stream.once('error', function () { - for (var key in require.cache) { - if (/fixture-pass/.test(key.toString()) || /fixture-throws/.test(key.toString())) { - done(new Error('require cache still contained: ' + key)); - return; - } - } - - done(); - }); - stream.write(new gutil.File({path: './test/fixtures/fixture-pass.js'})); - stream.write(new gutil.File({path: './test/fixtures/fixture-throws-uncaught.js'})); - stream.end(); -}); - -it('should pass async AssertionError to mocha', function (done) { - var stream = mocha(); - - process.stdout.write = function (str) { - if (/throws after timeout/.test(str)) { - done(new Error('mocha timeout not expected')); - } else if (/Uncaught AssertionError: false == true/.test(str)) { - done(); - } - }; - - stream.once('error', function () {}); - stream.write(new gutil.File({path: './test/fixtures/fixture-async.js'})); - stream.end(); +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); +const gutil = require('gulp-util'); +const mocha = require('../'); + +function fixture(name) { + let fileName = path.join(__dirname, 'fixtures', name); + + return new gutil.File({ + path: fileName, + contents: fs.existsSync(fileName) ? fs.readFileSync(fileName) : null + }); +} + +describe('mocha()', () => { + it('should run unit test and pass', done => { + let stream = mocha({suppress: true}); + + stream.once('result', result => { + assert(/1 passing/.test(result.stdout)); + done(); + }); + stream.write(fixture('fixture-pass.js')); + stream.end(); + }); + + it('should run unit test and fail', done => { + let stream = mocha({suppress: true}); + + stream.once('error', function (err) { + assert(/1 failing/.test(err.stdout)); + done(); + }); + stream.write(fixture('fixture-fail.js')); + stream.end(); + }); + + it('should pass async AssertionError to mocha', function (done) { + let stream = mocha({suppress: true}); + + stream.once('error', function (err) { + let throws = /throws after timeout/.test(err.stdout); + let uncaught = /Uncaught AssertionError: false == true/.test(err.stdout); + + assert(throws || uncaught); + done(); + }); + stream.write(fixture('fixture-async.js')); + stream.end(); + }); + + it('should not suppress output', done => { + let stream = mocha(); + + stream.once('result', result => { + assert(/should pass/.test(result.stdout)); + done(); + }); + stream.write(fixture('fixture-pass.js')); + stream.end(); + }); });