From 6b1617a9ed84244235e5034ffc0d3e4d2cb4c554 Mon Sep 17 00:00:00 2001 From: Andrew McConnell Date: Mon, 15 Feb 2016 15:30:32 -0500 Subject: [PATCH] Support ".only()" across all test files in a suite. --- api.js | 15 ++++++++- index.js | 8 +++-- lib/fork.js | 6 ++-- lib/runner.js | 6 +++- test/api.js | 20 ++++++++++++ test/fixture/exclusive-nonexclusive.js | 9 ++++++ test/fixture/exclusive.js | 5 +++ test/fork.js | 12 ++++---- test/hooks.js | 26 ++++++++-------- test/runner.js | 42 +++++++++++++++++++------- 10 files changed, 111 insertions(+), 38 deletions(-) create mode 100644 test/fixture/exclusive-nonexclusive.js create mode 100644 test/fixture/exclusive.js diff --git a/api.js b/api.js index f31d2d0c6..d8d2e31c4 100644 --- a/api.js +++ b/api.js @@ -53,6 +53,7 @@ Api.prototype._reset = function () { this.failCount = 0; this.fileCount = 0; this.testCount = 0; + this.hasExclusive = false; this.errors = []; this.stats = []; this.tests = []; @@ -103,6 +104,15 @@ Api.prototype._handleTeardown = function (data) { }; Api.prototype._handleStats = function (stats) { + if (this.hasExclusive && !stats.hasExclusive) { + return; + } + + if (!this.hasExclusive && stats.hasExclusive) { + this.hasExclusive = true; + this.testCount = 0; + } + this.testCount += stats.testCount; }; @@ -198,9 +208,12 @@ Api.prototype.run = function (files) { self.emit('ready'); var method = self.options.serial ? 'mapSeries' : 'map'; + var options = { + runOnlyExclusive: self.hasExclusive + }; resolve(Promise[method](files, function (file, index) { - return tests[index].run().catch(function (err) { + return tests[index].run(options).catch(function (err) { // The test failed catastrophically. Flag it up as an // exception, then return an empty result. Other tests may // continue to run. diff --git a/index.js b/index.js index aebc88be3..0c0ca56cd 100644 --- a/index.js +++ b/index.js @@ -70,6 +70,7 @@ function exit() { } globals.setImmediate(function () { + var hasExclusive = runner.tests.hasExclusive; var numberOfTests = runner.tests.tests.concurrent.length + runner.tests.tests.serial.length; if (numberOfTests === 0) { @@ -78,13 +79,14 @@ globals.setImmediate(function () { } send('stats', { - testCount: numberOfTests + testCount: numberOfTests, + hasExclusive: hasExclusive }); runner.on('test', test); - process.on('ava-run', function () { - runner.run().then(exit); + process.on('ava-run', function (options) { + runner.run(options).then(exit); }); }); diff --git a/lib/fork.js b/lib/fork.js index 047f356db..d863a2a00 100644 --- a/lib/fork.js +++ b/lib/fork.js @@ -134,14 +134,14 @@ module.exports = function (file, opts) { isReady = true; }); - promise.run = function () { + promise.run = function (options) { if (isReady) { - send(ps, 'run'); + send(ps, 'run', options); return promise; } ps.on('stats', function () { - send(ps, 'run'); + send(ps, 'run', options); }); return promise; diff --git a/lib/runner.js b/lib/runner.js index bec3dd992..68a8d8e13 100644 --- a/lib/runner.js +++ b/lib/runner.js @@ -88,7 +88,7 @@ Runner.prototype._addTestResult = function (result) { this.emit('test', props); }; -Runner.prototype.run = function () { +Runner.prototype.run = function (options) { var stats = this.stats = { failCount: 0, passCount: 0, @@ -96,6 +96,10 @@ Runner.prototype.run = function () { testCount: 0 }; + if (options.runOnlyExclusive && !this.tests.hasExclusive) { + return Promise.resolve(); + } + this.tests.on('test', this._addTestResult); return Promise.resolve(this.tests.build(this._bail).run()).then(function () { diff --git a/test/api.js b/test/api.js index 1fe220431..4acb28024 100644 --- a/test/api.js +++ b/test/api.js @@ -600,6 +600,26 @@ test('test file with only skipped tests does not create a failure', function (t) }); }); +test('test file with exclusive tests causes non-exclusive tests in other files to be ignored', function (t) { + t.plan(4); + + var files = [ + path.join(__dirname, 'fixture/exclusive.js'), + path.join(__dirname, 'fixture/exclusive-nonexclusive.js'), + path.join(__dirname, 'fixture/one-pass-one-fail.js') + ]; + + var api = new Api(); + + api.run(files) + .then(function () { + t.ok(api.hasExclusive); + t.is(api.testCount, 2); + t.is(api.passCount, 2); + t.is(api.failCount, 0); + }); +}); + test('resets state before running', function (t) { t.plan(2); diff --git a/test/fixture/exclusive-nonexclusive.js b/test/fixture/exclusive-nonexclusive.js new file mode 100644 index 000000000..e0d1fbbf9 --- /dev/null +++ b/test/fixture/exclusive-nonexclusive.js @@ -0,0 +1,9 @@ +import test from '../../'; + +test.only(t => { + t.pass(); +}); + +test(t => { + t.fail(); +}); diff --git a/test/fixture/exclusive.js b/test/fixture/exclusive.js new file mode 100644 index 000000000..3060d8b66 --- /dev/null +++ b/test/fixture/exclusive.js @@ -0,0 +1,5 @@ +import test from '../../'; + +test.only(t => { + t.pass(); +}); diff --git a/test/fork.js b/test/fork.js index 2c3f4b2ad..b2406db24 100644 --- a/test/fork.js +++ b/test/fork.js @@ -21,7 +21,7 @@ test('emits test event', function (t) { t.plan(1); fork(fixture('generators.js')) - .run() + .run({}) .on('test', function (tt) { t.is(tt.title, 'generator function'); t.end(); @@ -34,7 +34,7 @@ test('resolves promise with tests info', function (t) { var file = fixture('generators.js'); fork(file) - .run() + .run({}) .then(function (info) { t.is(info.stats.passCount, 1); t.is(info.tests.length, 1); @@ -50,7 +50,7 @@ test('exit after tests are finished', function (t) { var cleanupCompleted = false; fork(fixture('long-running.js')) - .run() + .run({}) .on('exit', function () { t.true(Date.now() - start < 10000, 'test waited for a pending setTimeout'); t.true(cleanupCompleted, 'cleanup did not complete'); @@ -62,7 +62,7 @@ test('exit after tests are finished', function (t) { test('fake timers do not break duration', function (t) { fork(fixture('fake-timers.js')) - .run() + .run({}) .then(function (info) { var duration = info.tests[0].duration; t.true(duration < 1000, duration + ' < 1000'); @@ -75,7 +75,7 @@ test('fake timers do not break duration', function (t) { /* test('destructuring of `t` is allowed', function (t) { fork(fixture('destructuring-public-api.js')) - .run() + .run({}) .then(function (info) { t.is(info.stats.failCount, 0); t.is(info.stats.passCount, 3); @@ -86,7 +86,7 @@ test('destructuring of `t` is allowed', function (t) { test('babelrc is ignored', function (t) { fork(fixture('babelrc/test.js')) - .run() + .run({}) .then(function (info) { t.is(info.stats.passCount, 1); t.end(); diff --git a/test/hooks.js b/test/hooks.js index d3e740e3e..8a49780e6 100644 --- a/test/hooks.js +++ b/test/hooks.js @@ -28,7 +28,7 @@ test('before', function (t) { arr.push('b'); }); - runner.run().then(function () { + runner.run({}).then(function () { t.same(arr, ['a', 'b']); }); }); @@ -47,7 +47,7 @@ test('after', function (t) { arr.push('a'); }); - runner.run().then(function () { + runner.run({}).then(function () { t.is(runner.stats.passCount, 1); t.is(runner.stats.failCount, 0); t.same(arr, ['a', 'b']); @@ -74,7 +74,7 @@ test('stop if before hooks failed', function (t) { a.end(); }); - runner.run().then(function () { + runner.run({}).then(function () { t.same(arr, ['a']); t.end(); }); @@ -104,7 +104,7 @@ test('before each with concurrent tests', function (t) { arr[1].push('d'); }); - runner.run().then(function () { + runner.run({}).then(function () { t.same(arr, [['a', 'b', 'c'], ['a', 'b', 'd']]); t.end(); }); @@ -132,7 +132,7 @@ test('before each with serial tests', function (t) { arr.push('d'); }); - runner.run().then(function () { + runner.run({}).then(function () { t.same(arr, ['a', 'b', 'c', 'a', 'b', 'd']); t.end(); }); @@ -154,7 +154,7 @@ test('fail if beforeEach hook fails', function (t) { a.pass(); }); - runner.run().then(function () { + runner.run({}).then(function () { t.is(runner.stats.failCount, 1); t.same(arr, ['a']); t.end(); @@ -185,7 +185,7 @@ test('after each with concurrent tests', function (t) { arr[1].push('d'); }); - runner.run().then(function () { + runner.run({}).then(function () { t.same(arr, [['c', 'a', 'b'], ['d', 'a', 'b']]); t.end(); }); @@ -213,7 +213,7 @@ test('after each with serial tests', function (t) { arr.push('d'); }); - runner.run().then(function () { + runner.run({}).then(function () { t.same(arr, ['c', 'a', 'b', 'd', 'a', 'b']); t.end(); }); @@ -245,7 +245,7 @@ test('ensure hooks run only around tests', function (t) { arr.push('test'); }); - runner.run().then(function () { + runner.run({}).then(function () { t.same(arr, ['before', 'beforeEach', 'test', 'afterEach', 'after']); t.end(); }); @@ -278,7 +278,7 @@ test('shared context', function (t) { a.same(a.context.arr, ['a', 'b', 'c']); }); - runner.run().then(function () { + runner.run({}).then(function () { t.is(runner.stats.failCount, 0); t.end(); }); @@ -297,7 +297,7 @@ test('shared context of any type', function (t) { a.is(a.context, 'foo'); }); - runner.run().then(function () { + runner.run({}).then(function () { t.is(runner.stats.failCount, 0); t.end(); }); @@ -307,7 +307,7 @@ test('don\'t display hook title if it did not fail', function (t) { t.plan(2); fork(path.join(__dirname, 'fixture', 'hooks-passing.js')) - .run() + .run({}) .on('test', function (test) { t.same(test.error, null); t.is(test.title, 'pass'); @@ -321,7 +321,7 @@ test('display hook title if it failed', function (t) { t.plan(2); fork(path.join(__dirname, 'fixture', 'hooks-failing.js')) - .run() + .run({}) .on('test', function (test) { t.is(test.error.name, 'AssertionError'); t.is(test.title, 'fail for pass'); diff --git a/test/runner.js b/test/runner.js index a95c30d7e..1427ca145 100644 --- a/test/runner.js +++ b/test/runner.js @@ -26,7 +26,7 @@ test('runner emits a "test" event', function (t) { t.end(); }); - runner.run(); + runner.run({}); }); test('run serial tests before concurrent ones', function (t) { @@ -48,7 +48,7 @@ test('run serial tests before concurrent ones', function (t) { a.end(); }); - runner.run().then(function () { + runner.run({}).then(function () { t.same(arr, ['a', 'b', 'c']); t.end(); }); @@ -82,7 +82,7 @@ test('anything can be skipped', function (t) { runner.serial(pusher('serial')); runner.serial.skip(pusher('serial.skip')); - runner.run().then(function () { + runner.run({}).then(function () { // Note that afterEach and beforeEach run twice because there are two actual tests - "serial" and "concurrent" t.same(arr, [ 'before', @@ -122,7 +122,7 @@ test('include skipped tests in results', function (t) { titles.push(test.title); }); - runner.run().then(function () { + runner.run({}).then(function () { t.same(titles, [ 'before', 'before.skip', @@ -173,7 +173,7 @@ test('test types and titles', function (t) { t.is(props.type, test.type); }); - runner.run().then(t.end); + runner.run({}).then(t.end); }); test('skip test', function (t) { @@ -190,7 +190,7 @@ test('skip test', function (t) { arr.push('b'); }); - runner.run().then(function () { + runner.run({}).then(function () { t.is(runner.stats.testCount, 2); t.is(runner.stats.passCount, 1); t.same(arr, ['a']); @@ -212,7 +212,7 @@ test('only test', function (t) { arr.push('b'); }); - runner.run().then(function () { + runner.run({}).then(function () { t.is(runner.stats.testCount, 1); t.is(runner.stats.passCount, 1); t.same(arr, ['b']); @@ -220,6 +220,26 @@ test('only test', function (t) { }); }); +test('runOnlyExclusive option test', function (t) { + t.plan(4); + + var runner = new Runner(); + var options = {runOnlyExclusive: true}; + var arr = []; + + runner.test(function () { + arr.push('a'); + }); + + runner.run(options).then(function () { + t.is(runner.stats.failCount, 0); + t.is(runner.stats.passCount, 0); + t.is(runner.stats.skipCount, 0); + t.is(runner.stats.testCount, 0); + t.end(); + }); +}); + test('options.serial forces all tests to be serial', function (t) { t.plan(1); @@ -245,7 +265,7 @@ test('options.serial forces all tests to be serial', function (t) { t.end(); }); - runner.run(); + runner.run({}); }); test('options.bail will bail out', function (t) { @@ -262,7 +282,7 @@ test('options.bail will bail out', function (t) { t.fail(); }); - runner.run().then(function () { + runner.run({}).then(function () { t.end(); }); }); @@ -288,7 +308,7 @@ test('options.bail will bail out (async)', function (t) { }, 300); }); - runner.run().then(function () { + runner.run({}).then(function () { t.same(tests, [1]); // With concurrent tests there is no stopping the second `setTimeout` callback from happening. // See the `bail + serial` test below for comparison @@ -320,7 +340,7 @@ test('options.bail + serial - tests will never happen (async)', function (t) { }, 300); }); - runner.run().then(function () { + runner.run({}).then(function () { t.same(tests, [1]); setTimeout(function () { t.same(tests, [1]);