From 985d5a19b61d54f694ae07466e82649fba3b88d8 Mon Sep 17 00:00:00 2001 From: Juan Soto Date: Tue, 9 Feb 2016 11:24:47 -0500 Subject: [PATCH 01/16] Move `files` argument to `api.run()` --- api.js | 38 +++++----- cli.js | 6 +- test/api.js | 198 ++++++++++++++++++++-------------------------------- 3 files changed, 97 insertions(+), 145 deletions(-) diff --git a/api.js b/api.js index 31ce3f569..ec478a916 100644 --- a/api.js +++ b/api.js @@ -18,7 +18,7 @@ var fork = require('./lib/fork'); var formatter = require('./lib/enhance-assert').formatter(); var CachingPrecompiler = require('./lib/caching-precompiler'); -function Api(files, options) { +function Api(options) { if (!(this instanceof Api)) { throw new TypeError('Class constructor Api cannot be invoked without \'new\''); } @@ -28,22 +28,6 @@ function Api(files, options) { this.options = options || {}; this.options.require = (this.options.require || []).map(resolveCwd); - if (!files || files.length === 0) { - this.files = [ - 'test.js', - 'test-*.js', - 'test' - ]; - } else { - this.files = files; - } - - this.excludePatterns = [ - '!**/node_modules/**', - '!**/fixtures/**', - '!**/helpers/**' - ]; - Object.keys(Api.prototype).forEach(function (key) { this[key] = this[key].bind(this); }, this); @@ -165,9 +149,25 @@ Api.prototype._prefixTitle = function (file) { Api.prototype.run = function (files) { var self = this; + if (!files || files.length === 0) { + this.files = [ + 'test.js', + 'test-*.js', + 'test' + ]; + } else { + this.files = files; + } + + this.excludePatterns = [ + '!**/node_modules/**', + '!**/fixtures/**', + '!**/helpers/**' + ]; + this._reset(); - this.explicitTitles = Boolean(files); - return handlePaths(files || this.files, this.excludePatterns) + this.explicitTitles = Boolean(this.files); + return handlePaths(this.files, this.excludePatterns) .map(function (file) { return path.resolve(file); }) diff --git a/cli.js b/cli.js index 449fea459..fd41e59da 100755 --- a/cli.js +++ b/cli.js @@ -93,7 +93,7 @@ if (cli.flags.init) { return; } -var api = new Api(cli.input.length ? cli.input : arrify(conf.files), { +var api = new Api({ failFast: cli.flags.failFast, serial: cli.flags.serial, require: arrify(cli.flags.require), @@ -119,6 +119,8 @@ api.on('error', logger.unhandledError); api.on('stdout', logger.stdout); api.on('stderr', logger.stderr); +var files = cli.input.length ? cli.input : arrify(conf.files); + if (cli.flags.watch) { try { watcher.start(logger, api, arrify(cli.flags.source), process.stdin); @@ -133,7 +135,7 @@ if (cli.flags.watch) { } } } else { - api.run() + api.run(files) .then(function () { logger.finish(); logger.exit(api.failCount > 0 || api.rejectionCount > 0 || api.exceptionCount > 0 ? 1 : 0); diff --git a/test/api.js b/test/api.js index faa32e07e..7d24a593f 100644 --- a/test/api.js +++ b/test/api.js @@ -9,9 +9,9 @@ var Api = require('../api'); test('ES2015 support', function (t) { t.plan(1); - var api = new Api([path.join(__dirname, 'fixture/es2015.js')]); + var api = new Api(); - api.run() + api.run([path.join(__dirname, 'fixture/es2015.js')]) .then(function () { t.is(api.passCount, 1); }); @@ -20,9 +20,9 @@ test('ES2015 support', function (t) { test('generators support', function (t) { t.plan(1); - var api = new Api([path.join(__dirname, 'fixture/generators.js')]); + var api = new Api(); - api.run() + api.run([path.join(__dirname, 'fixture/generators.js')]) .then(function () { t.is(api.passCount, 1); }); @@ -31,9 +31,9 @@ test('generators support', function (t) { test('async/await support', function (t) { t.plan(1); - var api = new Api([path.join(__dirname, 'fixture/async-await.js')]); + var api = new Api(); - api.run() + api.run([path.join(__dirname, 'fixture/async-await.js')]) .then(function () { t.is(api.passCount, 2); }); @@ -58,40 +58,9 @@ test('test title prefixes — multiple files', function (t) { ]; var index; - var api = new Api(files); - - api.run() - .then(function () { - // if all lines were removed from expected output - // actual output matches expected output - t.is(expected.length, 0); - }); - - api.on('test', function (a) { - index = expected.indexOf(a.title); - - t.true(index >= 0); - - // remove line from expected output - expected.splice(index, 1); - }); -}); - -test('test title prefixes — single file', function (t) { - t.plan(2); - - var separator = ' ' + figures.pointerSmall + ' '; - var files = [ - path.join(__dirname, 'fixture/generators.js') - ]; - var expected = [ - ['generator function'].join(separator) - ]; - var index; - - var api = new Api(files); + var api = new Api(); - api.run() + api.run(files) .then(function () { // if all lines were removed from expected output // actual output matches expected output @@ -147,9 +116,9 @@ test('display filename prefixes for failed test stack traces', function (t) { path.join(__dirname, 'fixture/one-pass-one-fail.js') ]; - var api = new Api(files); + var api = new Api(); - api.run() + api.run(files) .then(function () { t.is(api.passCount, 2); t.is(api.failCount, 1); @@ -167,9 +136,9 @@ test('display filename prefixes for failed test stack traces in subdirs', functi path.join(__dirname, 'fixture/subdir/failing-subdir.js') ]; - var api = new Api(files); + var api = new Api(); - api.run() + api.run(files) .then(function () { t.is(api.passCount, 1); t.is(api.failCount, 1); @@ -180,7 +149,7 @@ test('display filename prefixes for failed test stack traces in subdirs', functi test('fail-fast mode', function (t) { t.plan(5); - var api = new Api([path.join(__dirname, 'fixture/fail-fast.js')], { + var api = new Api({ failFast: true }); @@ -193,15 +162,15 @@ test('fail-fast mode', function (t) { }); }); - api.run() + api.run([path.join(__dirname, 'fixture/fail-fast.js')]) .then(function () { t.ok(api.options.failFast); t.same(tests, [{ ok: true, - title: 'first pass' + title: 'fail-fast › first pass' }, { ok: false, - title: 'second fail' + title: 'fail-fast › second fail' }]); t.is(api.passCount, 1); t.is(api.failCount, 1); @@ -212,11 +181,11 @@ test('fail-fast mode', function (t) { test('serial execution mode', function (t) { t.plan(3); - var api = new Api([path.join(__dirname, 'fixture/serial.js')], { + var api = new Api({ serial: true }); - api.run() + api.run([path.join(__dirname, 'fixture/serial.js')]) .then(function () { t.ok(api.options.serial); t.is(api.passCount, 3); @@ -227,9 +196,9 @@ test('serial execution mode', function (t) { test('circular references on assertions do not break process.send', function (t) { t.plan(2); - var api = new Api([path.join(__dirname, 'fixture/circular-reference-on-assertion.js')]); + var api = new Api(); - api.run() + api.run([path.join(__dirname, 'fixture/circular-reference-on-assertion.js')]) .then(function () { t.is(api.failCount, 1); t.match(api.errors[0].error.message, /'c'.*?'d'/); @@ -239,9 +208,9 @@ test('circular references on assertions do not break process.send', function (t) test('change process.cwd() to a test\'s directory', function (t) { t.plan(1); - var api = new Api([path.join(__dirname, 'fixture/process-cwd.js')]); + var api = new Api(); - api.run() + api.run([path.join(__dirname, 'fixture/process-cwd.js')]) .then(function () { t.is(api.passCount, 1); }); @@ -250,14 +219,14 @@ test('change process.cwd() to a test\'s directory', function (t) { test('unhandled promises will throw an error', function (t) { t.plan(3); - var api = new Api([path.join(__dirname, 'fixture/loud-rejection.js')]); + var api = new Api(); api.on('error', function (data) { t.is(data.name, 'Error'); t.match(data.message, /You can\'t handle this!/); }); - api.run() + api.run([path.join(__dirname, 'fixture/loud-rejection.js')]) .then(function () { t.is(api.passCount, 1); }); @@ -266,14 +235,14 @@ test('unhandled promises will throw an error', function (t) { test('uncaught exception will throw an error', function (t) { t.plan(3); - var api = new Api([path.join(__dirname, 'fixture/uncaught-exception.js')]); + var api = new Api(); api.on('error', function (data) { t.is(data.name, 'Error'); t.match(data.message, /Can\'t catch me!/); }); - api.run() + api.run([path.join(__dirname, 'fixture/uncaught-exception.js')]) .then(function () { t.is(api.passCount, 1); }); @@ -282,7 +251,9 @@ test('uncaught exception will throw an error', function (t) { test('stack traces for exceptions are corrected using a source map file', function (t) { t.plan(4); - var api = new Api([path.join(__dirname, 'fixture/source-map-file.js')], {cacheEnabled: true}); + var api = new Api({ + cacheEnabled: true + }); api.on('error', function (data) { t.match(data.message, /Thrown by source-map-fixtures/); @@ -290,7 +261,7 @@ test('stack traces for exceptions are corrected using a source map file', functi t.match(data.stack, /^.*?Immediate\b.*source-map-file.js:11.*$/m); }); - api.run() + api.run([path.join(__dirname, 'fixture/source-map-file.js')]) .then(function () { t.is(api.passCount, 1); }); @@ -299,7 +270,9 @@ test('stack traces for exceptions are corrected using a source map file', functi test('stack traces for exceptions are corrected using a source map file (cache off)', function (t) { t.plan(4); - var api = new Api([path.join(__dirname, 'fixture/source-map-file.js')], {cacheEnabled: false}); + var api = new Api({ + cacheEnabled: false + }); api.on('error', function (data) { t.match(data.message, /Thrown by source-map-fixtures/); @@ -307,7 +280,7 @@ test('stack traces for exceptions are corrected using a source map file (cache o t.match(data.stack, /^.*?Immediate\b.*source-map-file.js:11.*$/m); }); - api.run() + api.run([path.join(__dirname, 'fixture/source-map-file.js')]) .then(function () { t.is(api.passCount, 1); }); @@ -316,7 +289,9 @@ test('stack traces for exceptions are corrected using a source map file (cache o test('stack traces for exceptions are corrected using a source map, taking an initial source map for the test file into account (cache on)', function (t) { t.plan(4); - var api = new Api([path.join(__dirname, 'fixture/source-map-initial.js')], {cacheEnabled: true}); + var api = new Api({ + cacheEnabled: true + }); api.on('error', function (data) { t.match(data.message, /Thrown by source-map-fixtures/); @@ -324,7 +299,7 @@ test('stack traces for exceptions are corrected using a source map, taking an in t.match(data.stack, /^.*?Immediate\b.*source-map-initial-input.js:7.*$/m); }); - api.run() + api.run([path.join(__dirname, 'fixture/source-map-initial.js')]) .then(function () { t.is(api.passCount, 1); }); @@ -333,7 +308,9 @@ test('stack traces for exceptions are corrected using a source map, taking an in test('stack traces for exceptions are corrected using a source map, taking an initial source map for the test file into account (cache off)', function (t) { t.plan(4); - var api = new Api([path.join(__dirname, 'fixture/source-map-initial.js')], {cacheEnabled: false}); + var api = new Api({ + cacheEnabled: false + }); api.on('error', function (data) { t.match(data.message, /Thrown by source-map-fixtures/); @@ -341,7 +318,7 @@ test('stack traces for exceptions are corrected using a source map, taking an in t.match(data.stack, /^.*?Immediate\b.*source-map-initial-input.js:7.*$/m); }); - api.run() + api.run([path.join(__dirname, 'fixture/source-map-initial.js')]) .then(function () { t.is(api.passCount, 1); }); @@ -350,9 +327,9 @@ test('stack traces for exceptions are corrected using a source map, taking an in test('absolute paths', function (t) { t.plan(1); - var api = new Api([path.resolve('test/fixture/es2015.js')]); + var api = new Api(); - api.run() + api.run([path.resolve('test/fixture/es2015.js')]) .then(function () { t.is(api.passCount, 1); }); @@ -361,9 +338,9 @@ test('absolute paths', function (t) { test('search directories recursively for files', function (t) { t.plan(2); - var api = new Api([path.join(__dirname, 'fixture/subdir')]); + var api = new Api(); - api.run() + api.run([path.join(__dirname, 'fixture/subdir')]) .then(function () { t.is(api.passCount, 2); t.is(api.failCount, 1); @@ -373,9 +350,9 @@ test('search directories recursively for files', function (t) { test('titles of both passing and failing tests and AssertionErrors are returned', function (t) { t.plan(3); - var api = new Api([path.join(__dirname, 'fixture/one-pass-one-fail.js')]); + var api = new Api(); - api.run() + api.run([path.join(__dirname, 'fixture/one-pass-one-fail.js')]) .then(function () { t.match(api.errors[0].title, /this is a failing test/); t.match(api.tests[0].title, /this is a passing test/); @@ -386,69 +363,69 @@ test('titles of both passing and failing tests and AssertionErrors are returned' test('empty test files cause an AvaError to be emitted', function (t) { t.plan(2); - var api = new Api([path.join(__dirname, 'fixture/empty.js')]); + var api = new Api(); api.on('error', function (err) { t.is(err.name, 'AvaError'); t.match(err.message, /No tests found.*?import "ava"/); }); - return api.run(); + return api.run([path.join(__dirname, 'fixture/empty.js')]); }); test('test file with no tests causes an AvaError to be emitted', function (t) { t.plan(2); - var api = new Api([path.join(__dirname, 'fixture/no-tests.js')]); + var api = new Api(); api.on('error', function (err) { t.is(err.name, 'AvaError'); t.match(err.message, /No tests/); }); - return api.run(); + return api.run([path.join(__dirname, 'fixture/no-tests.js')]); }); test('test file that immediately exits with 0 exit code ', function (t) { t.plan(2); - var api = new Api([path.join(__dirname, 'fixture/immediate-0-exit.js')]); + var api = new Api(); api.on('error', function (err) { t.is(err.name, 'AvaError'); t.match(err.message, /Test results were not received from/); }); - return api.run(); + return api.run([path.join(__dirname, 'fixture/immediate-0-exit.js')]); }); test('testing nonexistent files causes an AvaError to be emitted', function (t) { t.plan(2); - var api = new Api([path.join(__dirname, 'fixture/broken.js')]); + var api = new Api(); api.on('error', function (err) { t.is(err.name, 'AvaError'); t.match(err.message, /Couldn't find any files to test/); }); - return api.run(); + return api.run([path.join(__dirname, 'fixture/broken.js')]); }); test('test file in node_modules is ignored', function (t) { t.plan(2); - var api = new Api([path.join(__dirname, 'fixture/ignored-dirs/node_modules/test.js')]); + var api = new Api(); api.on('error', function (err) { t.is(err.name, 'AvaError'); t.match(err.message, /Couldn't find any files to test/); }); - return api.run(); + return api.run([path.join(__dirname, 'fixture/ignored-dirs/node_modules/test.js')]); }); -test('test file in node_modules is ignored (explicit)', function (t) { +test('test file in node_modules is ignored', function (t) { t.plan(2); var api = new Api(); @@ -464,19 +441,6 @@ test('test file in node_modules is ignored (explicit)', function (t) { test('test file in fixtures is ignored', function (t) { t.plan(2); - var api = new Api([path.join(__dirname, 'fixture/ignored-dirs/fixtures/test.js')]); - - api.on('error', function (err) { - t.is(err.name, 'AvaError'); - t.match(err.message, /Couldn't find any files to test/); - }); - - return api.run(); -}); - -test('test file in fixtures is ignored (explicit)', function (t) { - t.plan(2); - var api = new Api(); api.on('error', function (err) { @@ -490,19 +454,6 @@ test('test file in fixtures is ignored (explicit)', function (t) { test('test file in helpers is ignored', function (t) { t.plan(2); - var api = new Api([path.join(__dirname, 'fixture/ignored-dirs/helpers/test.js')]); - - api.on('error', function (err) { - t.is(err.name, 'AvaError'); - t.match(err.message, /Couldn't find any files to test/); - }); - - return api.run(); -}); - -test('test file in helpers is ignored (explicit)', function (t) { - t.plan(2); - var api = new Api(); api.on('error', function (err) { @@ -518,12 +469,11 @@ test('Node.js-style --require CLI argument', function (t) { var requirePath = './' + path.relative('.', path.join(__dirname, 'fixture/install-global.js')).replace(/\\/g, '/'); - var api = new Api( - [path.join(__dirname, 'fixture/validate-installed-global.js')], - {require: [requirePath]} - ); + var api = new Api({ + require: [requirePath] + }); - api.run() + api.run([path.join(__dirname, 'fixture/validate-installed-global.js')]) .then(function () { t.is(api.passCount, 1); }); @@ -532,9 +482,9 @@ test('Node.js-style --require CLI argument', function (t) { test('power-assert support', function (t) { t.plan(3); - var api = new Api([path.join(__dirname, 'fixture/power-assert.js')]); + var api = new Api(); - api.run() + api.run([path.join(__dirname, 'fixture/power-assert.js')]) .then(function () { t.ok(api.errors[0].error.powerAssertContext); @@ -553,9 +503,9 @@ test('power-assert support', function (t) { test('caching is enabled by default', function (t) { t.plan(3); rimraf.sync(path.join(__dirname, 'fixture/caching/node_modules')); - var api = new Api([path.join(__dirname, 'fixture/caching/test.js')]); + var api = new Api(); - api.run() + api.run([path.join(__dirname, 'fixture/caching/test.js')]) .then(function () { var files = fs.readdirSync(path.join(__dirname, 'fixture/caching/node_modules/.cache/ava')); t.is(files.length, 2); @@ -576,9 +526,9 @@ test('caching is enabled by default', function (t) { test('caching can be disabled', function (t) { t.plan(1); rimraf.sync(path.join(__dirname, 'fixture/caching/node_modules')); - var api = new Api([path.join(__dirname, 'fixture/caching/test.js')], {cacheEnabled: false}); + var api = new Api({cacheEnabled: false}); - api.run() + api.run([path.join(__dirname, 'fixture/caching/test.js')]) .then(function () { t.false(fs.existsSync(path.join(__dirname, 'fixture/caching/node_modules/.cache/ava'))); t.end(); @@ -588,9 +538,9 @@ test('caching can be disabled', function (t) { test('test file with only skipped tests does not create a failure', function (t) { t.plan(2); - var api = new Api([path.join(__dirname, 'fixture/skip-only.js')]); + var api = new Api(); - api.run() + api.run([path.join(__dirname, 'fixture/skip-only.js')]) .then(function () { t.is(api.tests.length, 1); t.true(api.tests[0].skip); @@ -600,11 +550,11 @@ test('test file with only skipped tests does not create a failure', function (t) test('resets state before running', function (t) { t.plan(2); - var api = new Api([path.resolve('test/fixture/es2015.js')]); + var api = new Api(); - api.run().then(function () { + api.run([path.resolve('test/fixture/es2015.js')]).then(function () { t.is(api.passCount, 1); - return api.run(); + return api.run([path.resolve('test/fixture/es2015.js')]); }).then(function () { t.is(api.passCount, 1); }); From c47e2e448c9ea5a1281b5f11f97cc569478b2208 Mon Sep 17 00:00:00 2001 From: Juan Soto Date: Wed, 10 Feb 2016 11:49:00 -0500 Subject: [PATCH 02/16] Pass files and exclude patterns directly to `watcher.start` --- cli.js | 16 +++++++++++++++- lib/watcher.js | 6 +++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/cli.js b/cli.js index fd41e59da..3eb767f87 100755 --- a/cli.js +++ b/cli.js @@ -121,9 +121,23 @@ api.on('stderr', logger.stderr); var files = cli.input.length ? cli.input : arrify(conf.files); +if (files.length === 0) { + files = [ + 'test.js', + 'test-*.js', + 'test' + ]; +} + +var excludePatterns = [ + '!**/node_modules/**', + '!**/fixtures/**', + '!**/helpers/**' + ]; + if (cli.flags.watch) { try { - watcher.start(logger, api, arrify(cli.flags.source), process.stdin); + watcher.start(logger, api, files, excludePatterns, arrify(cli.flags.source), process.stdin); } catch (err) { if (err.name === 'AvaError') { // An AvaError may be thrown if chokidar is not installed. Log it nicely. diff --git a/lib/watcher.js b/lib/watcher.js index 2805d730b..4aac116fc 100644 --- a/lib/watcher.js +++ b/lib/watcher.js @@ -47,10 +47,10 @@ function getChokidarPatterns(sources, initialFiles) { return {paths: paths, ignored: ignored}; } -exports.start = function (logger, api, sources, stdin) { - var isTest = makeTestMatcher(api.files, api.excludePatterns); +exports.start = function (logger, api, files, excludePatterns, sources, stdin) { + var isTest = makeTestMatcher(files, excludePatterns); + var patterns = getChokidarPatterns(sources, files); - var patterns = getChokidarPatterns(sources, api.files); var watcher = requireChokidar().watch(patterns.paths, { ignored: patterns.ignored, ignoreInitial: true From 86581d482867d654f3ceccbb480186a9181ef08e Mon Sep 17 00:00:00 2001 From: Juan Soto Date: Wed, 10 Feb 2016 11:49:56 -0500 Subject: [PATCH 03/16] Update watcher test --- test/watcher.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/watcher.js b/test/watcher.js index 29d5880e6..eeca86b7b 100644 --- a/test/watcher.js +++ b/test/watcher.js @@ -20,7 +20,7 @@ test('chokidar is not installed', function (t) { }); try { - subject.start({}, {files: [], excludePatterns: []}, []); + subject.start({}, {}, [], [], []); } catch (err) { t.is(err.name, 'AvaError'); t.is(err.message, 'The optional dependency chokidar failed to install and is required for --watch. Chokidar is likely not supported on your platform.'); @@ -74,12 +74,12 @@ test('chokidar is installed', function (_t) { api.run.reset(); api.run.returns(new Promise(function () {})); - api.files = [ + files = [ 'test.js', 'test-*.js', 'test' ]; - api.excludePatterns = [ + excludePatterns = [ '!**/node_modules/**', '!**/fixtures/**', '!**/helpers/**' @@ -92,7 +92,7 @@ test('chokidar is installed', function (_t) { }); var start = function (sources) { - subject.start(logger, api, sources || [], stdin); + subject.start(logger, api, files, excludePatterns, sources || [], stdin); }; var add = function (path) { @@ -134,7 +134,7 @@ test('chokidar is installed', function (_t) { t.ok(chokidar.watch.calledOnce); t.same(chokidar.watch.firstCall.args, [ - ['package.json', '**/*.js'].concat(api.files), + ['package.json', '**/*.js'].concat(files), { ignored: defaultIgnore, ignoreInitial: true @@ -148,7 +148,7 @@ test('chokidar is installed', function (_t) { t.ok(chokidar.watch.calledOnce); t.same(chokidar.watch.firstCall.args, [ - ['foo.js', 'baz.js'].concat(api.files), + ['foo.js', 'baz.js'].concat(files), { ignored: ['bar.js', 'qux.js'], ignoreInitial: true From 57c3de24202fe8d71d40d3614e82aa03c7b12d49 Mon Sep 17 00:00:00 2001 From: Juan Soto Date: Thu, 11 Feb 2016 15:10:25 -0500 Subject: [PATCH 04/16] Move `excludePatterns` to API constructor --- api.js | 6 ++++++ cli.js | 8 +------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/api.js b/api.js index ec478a916..b74cf72c4 100644 --- a/api.js +++ b/api.js @@ -33,6 +33,12 @@ function Api(options) { }, this); this._reset(); + + this.excludePatterns = [ + '!**/node_modules/**', + '!**/fixtures/**', + '!**/helpers/**' + ]; } util.inherits(Api, EventEmitter); diff --git a/cli.js b/cli.js index 3eb767f87..4c6251bd7 100755 --- a/cli.js +++ b/cli.js @@ -129,15 +129,9 @@ if (files.length === 0) { ]; } -var excludePatterns = [ - '!**/node_modules/**', - '!**/fixtures/**', - '!**/helpers/**' - ]; - if (cli.flags.watch) { try { - watcher.start(logger, api, files, excludePatterns, arrify(cli.flags.source), process.stdin); + watcher.start(logger, api, files, arrify(cli.flags.source), process.stdin); } catch (err) { if (err.name === 'AvaError') { // An AvaError may be thrown if chokidar is not installed. Log it nicely. From f17d697f5ded1538cf8cb4d0de3daf2ad099ce2b Mon Sep 17 00:00:00 2001 From: Juan Soto Date: Thu, 11 Feb 2016 15:11:02 -0500 Subject: [PATCH 05/16] Update `watcher.start` to not take `excludePatterns` --- lib/watcher.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/watcher.js b/lib/watcher.js index 4aac116fc..c3419c146 100644 --- a/lib/watcher.js +++ b/lib/watcher.js @@ -47,8 +47,8 @@ function getChokidarPatterns(sources, initialFiles) { return {paths: paths, ignored: ignored}; } -exports.start = function (logger, api, files, excludePatterns, sources, stdin) { - var isTest = makeTestMatcher(files, excludePatterns); +exports.start = function (logger, api, files, sources, stdin) { + var isTest = makeTestMatcher(files, api.excludePatterns); var patterns = getChokidarPatterns(sources, files); var watcher = requireChokidar().watch(patterns.paths, { From ab0d2aef1b097463747f5d6e27c45ebc21c1b9b6 Mon Sep 17 00:00:00 2001 From: Juan Soto Date: Thu, 11 Feb 2016 15:11:35 -0500 Subject: [PATCH 06/16] Fix watcher tests --- test/watcher.js | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/test/watcher.js b/test/watcher.js index eeca86b7b..89f513880 100644 --- a/test/watcher.js +++ b/test/watcher.js @@ -20,7 +20,7 @@ test('chokidar is not installed', function (t) { }); try { - subject.start({}, {}, [], [], []); + subject.start({}, {}, [], []); } catch (err) { t.is(err.name, 'AvaError'); t.is(err.message, 'The optional dependency chokidar failed to install and is required for --watch. Chokidar is likely not supported on your platform.'); @@ -57,6 +57,7 @@ test('chokidar is installed', function (_t) { var clock; var emitter; var stdin; + var files; _t.beforeEach(function (done) { if (clock) { clock.uninstall(); @@ -79,11 +80,6 @@ test('chokidar is installed', function (_t) { 'test-*.js', 'test' ]; - excludePatterns = [ - '!**/node_modules/**', - '!**/fixtures/**', - '!**/helpers/**' - ]; stdin = new PassThrough(); stdin.pause(); @@ -91,8 +87,14 @@ test('chokidar is installed', function (_t) { done(); }); + api.excludePatterns = [ + '!**/node_modules/**', + '!**/fixtures/**', + '!**/helpers/**' + ]; + var start = function (sources) { - subject.start(logger, api, files, excludePatterns, sources || [], stdin); + subject.start(logger, api, files, sources || [], stdin); }; var add = function (path) { @@ -162,7 +164,7 @@ test('chokidar is installed', function (_t) { t.ok(chokidar.watch.calledOnce); t.same(chokidar.watch.firstCall.args, [ - ['foo.js', 'baz.js'].concat(api.files), + ['foo.js', 'baz.js'].concat(files), { ignored: defaultIgnore, ignoreInitial: true @@ -207,8 +209,8 @@ test('chokidar is installed', function (_t) { [ {label: 'is added', fire: add}, - {label: 'changes', fire: change}, - {label: 'is removed', fire: unlink} + {label: 'changes', fire: change, event: 'change'}, + {label: 'is removed', fire: unlink, event: 'unlink'} ].forEach(function (variant) { test('reruns initial tests when a source file ' + variant.label, function (t) { t.plan(6); @@ -397,7 +399,7 @@ test('chokidar is installed', function (_t) { test('determines whether changed files are tests based on the initial files patterns', function (t) { t.plan(2); - api.files = ['foo-{bar,baz}.js']; + files = ['foo-{bar,baz}.js']; api.run.returns(Promise.resolve()); start(); @@ -412,7 +414,7 @@ test('chokidar is installed', function (_t) { test('initial exclude patterns override whether something is a test file', function (t) { t.plan(2); - api.files = ['foo-{bar,baz}.js']; + files = ['foo-{bar,baz}.js']; api.excludePatterns = ['!*bar*']; api.run.returns(Promise.resolve()); start(); @@ -430,7 +432,7 @@ test('chokidar is installed', function (_t) { test('test files must end in .js', function (t) { t.plan(2); - api.files = ['foo.bar']; + files = ['foo.bar']; api.run.returns(Promise.resolve()); start(); @@ -460,7 +462,7 @@ test('chokidar is installed', function (_t) { test('files patterns may match directories', function (t) { t.plan(2); - api.files = ['dir', 'dir2/*/dir3']; + files = ['dir', 'dir2/*/dir3']; api.run.returns(Promise.resolve()); start(); @@ -475,7 +477,7 @@ test('chokidar is installed', function (_t) { test('exclude patterns override directory matches', function (t) { t.plan(2); - api.files = ['dir']; + files = ['dir']; api.excludePatterns = ['!**/exclude/**']; api.run.returns(Promise.resolve()); start(); From f48340820e6e9f2ce95c724e2e9d39613d4d60a6 Mon Sep 17 00:00:00 2001 From: Juan Soto Date: Tue, 16 Feb 2016 15:44:04 -0500 Subject: [PATCH 07/16] Make `explicitTitles` an API option --- api.js | 6 +++--- cli.js | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/api.js b/api.js index b74cf72c4..b4abd0c20 100644 --- a/api.js +++ b/api.js @@ -56,7 +56,7 @@ Api.prototype._reset = function () { this.stats = []; this.tests = []; this.base = ''; - this.explicitTitles = false; + this.options.explicitTitles = false; }; Api.prototype._runFile = function (file) { @@ -130,7 +130,7 @@ Api.prototype._handleTest = function (test) { }; Api.prototype._prefixTitle = function (file) { - if (this.fileCount === 1 && !this.explicitTitles) { + if (this.fileCount === 1 && !this.options.explicitTitles) { return ''; } @@ -172,7 +172,7 @@ Api.prototype.run = function (files) { ]; this._reset(); - this.explicitTitles = Boolean(this.files); + this.options.explicitTitles = Boolean(files); return handlePaths(this.files, this.excludePatterns) .map(function (file) { return path.resolve(file); diff --git a/cli.js b/cli.js index 4c6251bd7..5939444ee 100755 --- a/cli.js +++ b/cli.js @@ -97,7 +97,8 @@ var api = new Api({ failFast: cli.flags.failFast, serial: cli.flags.serial, require: arrify(cli.flags.require), - cacheEnabled: cli.flags.cache !== false + cacheEnabled: cli.flags.cache !== false, + explicitTitles: cli.flags.watch }); var logger = new Logger(); From 1d6d84442b99407ae0c329a48c75125ad6a1e4f9 Mon Sep 17 00:00:00 2001 From: Juan Soto Date: Wed, 17 Feb 2016 13:42:50 -0500 Subject: [PATCH 08/16] Clean up API and update tests --- api.js | 32 +++++++------------------------ test/api.js | 55 ++++++++++++++++++++++++++++------------------------- 2 files changed, 36 insertions(+), 51 deletions(-) diff --git a/api.js b/api.js index b4abd0c20..4036755f2 100644 --- a/api.js +++ b/api.js @@ -28,17 +28,17 @@ function Api(options) { this.options = options || {}; this.options.require = (this.options.require || []).map(resolveCwd); - Object.keys(Api.prototype).forEach(function (key) { - this[key] = this[key].bind(this); - }, this); - - this._reset(); - this.excludePatterns = [ '!**/node_modules/**', '!**/fixtures/**', '!**/helpers/**' ]; + + Object.keys(Api.prototype).forEach(function (key) { + this[key] = this[key].bind(this); + }, this); + + this._reset(); } util.inherits(Api, EventEmitter); @@ -56,7 +56,6 @@ Api.prototype._reset = function () { this.stats = []; this.tests = []; this.base = ''; - this.options.explicitTitles = false; }; Api.prototype._runFile = function (file) { @@ -155,25 +154,8 @@ Api.prototype._prefixTitle = function (file) { Api.prototype.run = function (files) { var self = this; - if (!files || files.length === 0) { - this.files = [ - 'test.js', - 'test-*.js', - 'test' - ]; - } else { - this.files = files; - } - - this.excludePatterns = [ - '!**/node_modules/**', - '!**/fixtures/**', - '!**/helpers/**' - ]; - this._reset(); - this.options.explicitTitles = Boolean(files); - return handlePaths(this.files, this.excludePatterns) + return handlePaths(files, this.excludePatterns) .map(function (file) { return path.resolve(file); }) diff --git a/test/api.js b/test/api.js index 7d24a593f..b8d3e3756 100644 --- a/test/api.js +++ b/test/api.js @@ -6,38 +6,38 @@ var fs = require('fs'); var test = require('tap').test; var Api = require('../api'); -test('ES2015 support', function (t) { - t.plan(1); +// test('ES2015 support', function (t) { +// t.plan(1); - var api = new Api(); +// var api = new Api(); - api.run([path.join(__dirname, 'fixture/es2015.js')]) - .then(function () { - t.is(api.passCount, 1); - }); -}); +// api.run([path.join(__dirname, 'fixture/es2015.js')]) +// .then(function () { +// t.is(api.passCount, 1); +// }); +// }); -test('generators support', function (t) { - t.plan(1); +// test('generators support', function (t) { +// t.plan(1); - var api = new Api(); +// var api = new Api(); - api.run([path.join(__dirname, 'fixture/generators.js')]) - .then(function () { - t.is(api.passCount, 1); - }); -}); +// api.run([path.join(__dirname, 'fixture/generators.js')]) +// .then(function () { +// t.is(api.passCount, 1); +// }); +// }); -test('async/await support', function (t) { - t.plan(1); +// test('async/await support', function (t) { +// t.plan(1); - var api = new Api(); +// var api = new Api(); - api.run([path.join(__dirname, 'fixture/async-await.js')]) - .then(function () { - t.is(api.passCount, 2); - }); -}); +// api.run([path.join(__dirname, 'fixture/async-await.js')]) +// .then(function () { +// t.is(api.passCount, 2); +// }); +// }); test('test title prefixes — multiple files', function (t) { t.plan(6); @@ -89,7 +89,9 @@ test('test title prefixes — single file (explicit)', function (t) { ]; var index; - var api = new Api(); + var api = new Api({ + explicitTitles: true + }); api.run(files) .then(function () { @@ -150,7 +152,8 @@ test('fail-fast mode', function (t) { t.plan(5); var api = new Api({ - failFast: true + failFast: true, + explicitTitles: true }); var tests = []; From 90075f280860317193e093d765712fe4551fa9f9 Mon Sep 17 00:00:00 2001 From: Juan Soto Date: Thu, 18 Feb 2016 10:58:57 -0500 Subject: [PATCH 09/16] Uncomment tests --- test/api.js | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/test/api.js b/test/api.js index b8d3e3756..c9fce5396 100644 --- a/test/api.js +++ b/test/api.js @@ -6,38 +6,38 @@ var fs = require('fs'); var test = require('tap').test; var Api = require('../api'); -// test('ES2015 support', function (t) { -// t.plan(1); +test('ES2015 support', function (t) { + t.plan(1); -// var api = new Api(); + var api = new Api(); -// api.run([path.join(__dirname, 'fixture/es2015.js')]) -// .then(function () { -// t.is(api.passCount, 1); -// }); -// }); + api.run([path.join(__dirname, 'fixture/es2015.js')]) + .then(function () { + t.is(api.passCount, 1); + }); +}); -// test('generators support', function (t) { -// t.plan(1); +test('generators support', function (t) { + t.plan(1); -// var api = new Api(); + var api = new Api(); -// api.run([path.join(__dirname, 'fixture/generators.js')]) -// .then(function () { -// t.is(api.passCount, 1); -// }); -// }); + api.run([path.join(__dirname, 'fixture/generators.js')]) + .then(function () { + t.is(api.passCount, 1); + }); +}); -// test('async/await support', function (t) { -// t.plan(1); +test('async/await support', function (t) { + t.plan(1); -// var api = new Api(); + var api = new Api(); -// api.run([path.join(__dirname, 'fixture/async-await.js')]) -// .then(function () { -// t.is(api.passCount, 2); -// }); -// }); + api.run([path.join(__dirname, 'fixture/async-await.js')]) + .then(function () { + t.is(api.passCount, 2); + }); +}); test('test title prefixes — multiple files', function (t) { t.plan(6); From 63f11be70195662a03e74c69fa6107d605858e8a Mon Sep 17 00:00:00 2001 From: Juan Soto Date: Thu, 18 Feb 2016 10:59:37 -0500 Subject: [PATCH 10/16] Remove `event` field in watcher tests --- test/watcher.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/watcher.js b/test/watcher.js index 89f513880..69ed74c35 100644 --- a/test/watcher.js +++ b/test/watcher.js @@ -209,8 +209,8 @@ test('chokidar is installed', function (_t) { [ {label: 'is added', fire: add}, - {label: 'changes', fire: change, event: 'change'}, - {label: 'is removed', fire: unlink, event: 'unlink'} + {label: 'changes', fire: change}, + {label: 'is removed', fire: unlink} ].forEach(function (variant) { test('reruns initial tests when a source file ' + variant.label, function (t) { t.plan(6); From 7a476b5aa5472ac6d869e029c7f87945072a1e27 Mon Sep 17 00:00:00 2001 From: Juan Soto Date: Thu, 18 Feb 2016 11:05:39 -0500 Subject: [PATCH 11/16] Restore deleted API test --- test/api.js | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/test/api.js b/test/api.js index c9fce5396..86b5d271f 100644 --- a/test/api.js +++ b/test/api.js @@ -77,6 +77,37 @@ test('test title prefixes — multiple files', function (t) { }); }); +test('test title prefixes — single file', function (t) { + t.plan(2); + + var separator = ' ' + figures.pointerSmall + ' '; + var files = [ + path.join(__dirname, 'fixture/generators.js') + ]; + var expected = [ + ['generator function'].join(separator) + ]; + var index; + + var api = new Api(); + + api.run(files) + .then(function () { + // if all lines were removed from expected output + // actual output matches expected output + t.is(expected.length, 0); + }); + + api.on('test', function (a) { + index = expected.indexOf(a.title); + + t.true(index >= 0); + + // remove line from expected output + expected.splice(index, 1); + }); +}); + test('test title prefixes — single file (explicit)', function (t) { t.plan(2); From 4ffbc42465b47190d08e0548dbfeddabd13d1135 Mon Sep 17 00:00:00 2001 From: Juan Soto Date: Thu, 18 Feb 2016 11:10:30 -0500 Subject: [PATCH 12/16] Add `excludePatterns` to dummy API in watcher test --- test/watcher.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/test/watcher.js b/test/watcher.js index 69ed74c35..de83ce2c0 100644 --- a/test/watcher.js +++ b/test/watcher.js @@ -20,7 +20,7 @@ test('chokidar is not installed', function (t) { }); try { - subject.start({}, {}, [], []); + subject.start({}, {excludePatterns: []}, [], []); } catch (err) { t.is(err.name, 'AvaError'); t.is(err.message, 'The optional dependency chokidar failed to install and is required for --watch. Chokidar is likely not supported on your platform.'); @@ -40,7 +40,12 @@ test('chokidar is installed', function (_t) { }; var api = { - run: sinon.stub() + run: sinon.stub(), + excludePatterns: [ + '!**/node_modules/**', + '!**/fixtures/**', + '!**/helpers/**' + ] }; var subject = proxyquire.noCallThru().load('../lib/watcher', { From 72651afe5b45506f5a033bc6ee1b3f245df928bd Mon Sep 17 00:00:00 2001 From: Juan Soto Date: Thu, 18 Feb 2016 12:05:41 -0500 Subject: [PATCH 13/16] Remove prefixes from fail-fast test --- test/api.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/api.js b/test/api.js index 86b5d271f..c1f105031 100644 --- a/test/api.js +++ b/test/api.js @@ -183,8 +183,7 @@ test('fail-fast mode', function (t) { t.plan(5); var api = new Api({ - failFast: true, - explicitTitles: true + failFast: true }); var tests = []; @@ -201,10 +200,10 @@ test('fail-fast mode', function (t) { t.ok(api.options.failFast); t.same(tests, [{ ok: true, - title: 'fail-fast › first pass' + title: 'first pass' }, { ok: false, - title: 'fail-fast › second fail' + title: 'second fail' }]); t.is(api.passCount, 1); t.is(api.failCount, 1); From 10ed6a8bcfd43ed812af609777ad392ed3ff13b8 Mon Sep 17 00:00:00 2001 From: Juan Soto Date: Thu, 18 Feb 2016 12:05:41 -0500 Subject: [PATCH 14/16] Cleanup watcher tests --- test/watcher.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test/watcher.js b/test/watcher.js index de83ce2c0..250acc32b 100644 --- a/test/watcher.js +++ b/test/watcher.js @@ -92,12 +92,6 @@ test('chokidar is installed', function (_t) { done(); }); - api.excludePatterns = [ - '!**/node_modules/**', - '!**/fixtures/**', - '!**/helpers/**' - ]; - var start = function (sources) { subject.start(logger, api, files, sources || [], stdin); }; From 9b2d29c00ff5d96e32312ac92311c73d93b268b8 Mon Sep 17 00:00:00 2001 From: Juan Soto Date: Wed, 24 Feb 2016 10:24:29 -0500 Subject: [PATCH 15/16] Update watcher and tests --- lib/watcher.js | 10 +++++----- test/watcher.js | 14 +++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/watcher.js b/lib/watcher.js index c3419c146..064648497 100644 --- a/lib/watcher.js +++ b/lib/watcher.js @@ -56,7 +56,7 @@ exports.start = function (logger, api, files, sources, stdin) { ignoreInitial: true }); - var busy = api.run().then(function () { + var busy = api.run(files).then(function () { logger.finish(); }).catch(rethrowAsync); @@ -90,7 +90,7 @@ exports.start = function (logger, api, files, sources, stdin) { debounceAgain = false; debounce(); } else { - busy = runAfterChanges(logger, api, isTest, dirtyStates); + busy = runAfterChanges(logger, api, files, isTest, dirtyStates); dirtyStates = {}; debouncing = null; debounceAgain = false; @@ -122,7 +122,7 @@ exports.start = function (logger, api, files, sources, stdin) { // Cancel the debouncer again, it might have restarted while waiting for // the busy promise to fulfil. cancelDebounce(); - busy = runAfterChanges(logger, api, isTest, {}); + busy = runAfterChanges(logger, api, files, isTest, {}); }); }); }; @@ -172,7 +172,7 @@ function makeTestMatcher(files, excludePatterns) { }; } -function runAfterChanges(logger, api, isTest, dirtyStates) { +function runAfterChanges(logger, api, files, isTest, dirtyStates) { var dirtyPaths = Object.keys(dirtyStates); var dirtyTests = dirtyPaths.filter(isTest); var addedOrChangedTests = dirtyTests.filter(function (path) { @@ -195,7 +195,7 @@ function runAfterChanges(logger, api, isTest, dirtyStates) { if (dirtyPaths.length > 0 && dirtyTests.length === dirtyPaths.length) { resolve(api.run(addedOrChangedTests)); } else { - resolve(api.run()); + resolve(api.run(files)); } }).then(function () { logger.finish(); diff --git a/test/watcher.js b/test/watcher.js index 250acc32b..a14c9616f 100644 --- a/test/watcher.js +++ b/test/watcher.js @@ -181,7 +181,7 @@ test('chokidar is installed', function (_t) { start(); t.ok(api.run.calledOnce); - t.same(api.run.firstCall.args, []); + t.same(api.run.firstCall.args, [files]); // finish is only called after the run promise fulfils. t.ok(logger.finish.notCalled); @@ -230,7 +230,7 @@ test('chokidar is installed', function (_t) { // reset is called before the second run. t.ok(logger.reset.calledBefore(api.run.secondCall)); // no explicit files are provided. - t.same(api.run.secondCall.args, []); + t.same(api.run.secondCall.args, [files]); // finish is only called after the run promise fulfils. t.ok(logger.finish.calledOnce); @@ -379,7 +379,7 @@ test('chokidar is installed', function (_t) { return debounce(2).then(function () { t.ok(api.run.calledTwice); // no explicit files are provided. - t.same(api.run.secondCall.args, []); + t.same(api.run.secondCall.args, [files]); }); }); @@ -424,7 +424,7 @@ test('chokidar is installed', function (_t) { t.ok(api.run.calledTwice); // foo-bar.js is excluded from being a test file, thus the initial tests // are run. - t.same(api.run.secondCall.args, []); + t.same(api.run.secondCall.args, [files]); }); }); @@ -439,7 +439,7 @@ test('chokidar is installed', function (_t) { return debounce(2).then(function () { t.ok(api.run.calledTwice); // foo.bar cannot be a test file, thus the initial tests are run. - t.same(api.run.secondCall.args, []); + t.same(api.run.secondCall.args, [files]); }); }); @@ -454,7 +454,7 @@ test('chokidar is installed', function (_t) { return debounce(2).then(function () { t.ok(api.run.calledTwice); // _foo.bar cannot be a test file, thus the initial tests are run. - t.same(api.run.secondCall.args, []); + t.same(api.run.secondCall.args, [files]); }); }); @@ -486,7 +486,7 @@ test('chokidar is installed', function (_t) { t.ok(api.run.calledTwice); // dir/exclude/foo.js is excluded from being a test file, thus the initial // tests are run. - t.same(api.run.secondCall.args, []); + t.same(api.run.secondCall.args, [files]); }); }); From 463fa858a783a99980089a202e098cca6d58bc3c Mon Sep 17 00:00:00 2001 From: Juan Soto Date: Wed, 24 Feb 2016 10:38:31 -0500 Subject: [PATCH 16/16] Update api tests --- test/api.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/api.js b/test/api.js index a77a3ea23..453f14105 100644 --- a/test/api.js +++ b/test/api.js @@ -292,8 +292,9 @@ test('uncaught exception will throw an error', function (t) { test('errors can occur without messages', function (t) { t.plan(2); - var api = new Api([path.join(__dirname, 'fixture/error-without-message.js')]); - api.run() + var api = new Api(); + + api.run([path.join(__dirname, 'fixture/error-without-message.js')]) .then(function () { t.is(api.failCount, 1); t.is(api.errors.length, 1);