Skip to content
This repository was archived by the owner on Dec 4, 2023. It is now read-only.

Commit 9bce05b

Browse files
fix nested describe.only suites; closes mochajs#2406
1 parent 4ad372b commit 9bce05b

File tree

6 files changed

+86
-23
lines changed

6 files changed

+86
-23
lines changed

lib/interfaces/common.js

+3-14
Original file line numberDiff line numberDiff line change
@@ -106,15 +106,9 @@ module.exports = function(suites, context, mocha) {
106106
suite.pending = Boolean(opts.pending);
107107
suite.file = opts.file;
108108
suites.unshift(suite);
109-
// I should be pilloried for the following.
110109
if (opts.isOnly) {
111-
if (suite.parent && suite.parent.onlyTests) {
112-
suite.onlyTests = suite.parent.onlyTests === suite.parent.tests ? suite.tests : [];
113-
} else {
114-
suite.onlyTests = suite.tests;
115-
}
116-
} else {
117-
suite.onlyTests = suite.parent && suite.parent.onlyTests === suite.parent.tests ? suite.tests : [];
110+
suite.parent._onlySuites = suite.parent._onlySuites.concat(suite);
111+
mocha.options.hasOnly = true;
118112
}
119113
if (typeof opts.fn === 'function') {
120114
opts.fn.call(suite);
@@ -135,12 +129,7 @@ module.exports = function(suites, context, mocha) {
135129
* @returns {*}
136130
*/
137131
only: function(mocha, test) {
138-
var suite = test.parent;
139-
if (suite.onlyTests === suite.tests) {
140-
suite.onlyTests = [test];
141-
} else {
142-
suite.onlyTests = (suite.onlyTests || []).concat(test);
143-
}
132+
test.parent._onlyTests = test.parent._onlyTests.concat(test);
144133
mocha.options.hasOnly = true;
145134
return test;
146135
},

lib/runner.js

+32-5
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ var debug = require('debug')('mocha:runner');
1010
var Runnable = require('./runnable');
1111
var filter = utils.filter;
1212
var indexOf = utils.indexOf;
13+
var some = utils.some;
1314
var keys = utils.keys;
1415
var stackFilter = utils.stackTraceFilter();
1516
var stringify = utils.stringify;
@@ -846,12 +847,38 @@ Runner.prototype.abort = function() {
846847
* @api private
847848
*/
848849
function filterOnly(suite) {
849-
// If it has `only` tests, run only those
850-
suite.tests = suite.onlyTests ? suite.onlyTests : [];
851-
// Filter the nested suites
852-
suite.suites = filter(suite.suites, filterOnly);
850+
if (suite._onlyTests.length) {
851+
// If the suite contains `only` tests, run those and ignore any nested suites.
852+
suite.tests = suite._onlyTests;
853+
suite.suites = [];
854+
} else {
855+
// Otherwise, do not run any of the tests in this suite.
856+
suite.tests = [];
857+
suite._onlySuites.forEach(function(onlySuite) {
858+
// If there are other `only` tests/suites nested in the current `only` suite, then filter the current suite.
859+
// Otherwise, all of the tests on this `only` suite should be run, so don't filter it.
860+
if (hasOnly(onlySuite)) {
861+
filterOnly(suite);
862+
}
863+
});
864+
// Run the `only` suites, as well as any other suites that have `only` tests/suites as descendants.
865+
suite.suites = filter(suite.suites, function(childSuite) {
866+
return indexOf(suite._onlySuites, childSuite) !== -1 || filterOnly(childSuite);
867+
});
868+
}
853869
// Keep the suite only if there is something to run
854-
return suite.suites.length || suite.tests.length;
870+
return suite.tests.length || suite.suites.length;
871+
}
872+
873+
/**
874+
* Determines whether a suite has an `only` test or suite as a descendant.
875+
*
876+
* @param {Array} suite
877+
* @returns {Boolean}
878+
* @api private
879+
*/
880+
function hasOnly(suite) {
881+
return suite._onlyTests.length || suite._onlySuites.length || some(suite.suites, hasOnly);
855882
}
856883

857884
/**

lib/suite.js

+2
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ function Suite(title, parentContext) {
6161
this._slow = 75;
6262
this._bail = false;
6363
this._retries = -1;
64+
this._onlyTests = [];
65+
this._onlySuites = [];
6466
this.delayed = false;
6567
}
6668

lib/utils.js

+17
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,23 @@ exports.filter = function(arr, fn) {
138138
return ret;
139139
};
140140

141+
/**
142+
* Array#some (<=IE8)
143+
*
144+
* @api private
145+
* @param {Array} arr
146+
* @param {Function} fn
147+
* @return {Array}
148+
*/
149+
exports.some = function(arr, fn) {
150+
for (var i = 0, l = arr.length; i < l; i++) {
151+
if (fn(arr[i])) {
152+
return true;
153+
}
154+
}
155+
return false;
156+
};
157+
141158
/**
142159
* Object.keys (<=IE8)
143160
*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
describe('outer describe', function() {
2+
it('should not run this test', function() {});
3+
describe('this suite should not run', function() {
4+
it('should not run this test', function() {});
5+
});
6+
describe.only('this .only suite should run', function() {
7+
describe('this suite should run', function() {
8+
it('should run this test in a nested suite', function() {});
9+
});
10+
it('should run this test', function() {});
11+
});
12+
describe('this suite should not run', function() {
13+
it('should not run this test', function() {});
14+
});
15+
});

test/integration/regression.js

+17-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
var assert = require('assert');
2-
var fs = require('fs');
3-
var path = require('path');
4-
var run = require('./helpers').runMocha;
1+
var assert = require('assert');
2+
var fs = require('fs');
3+
var path = require('path');
4+
var run = require('./helpers').runMocha;
5+
var runJSON = require('./helpers').runMochaJSON;
56

67
describe('regressions', function() {
78
it('issue-1327: should run all 3 specs exactly once', function(done) {
@@ -60,4 +61,16 @@ describe('regressions', function() {
6061
afterWasRun.should.be.ok();
6162
});
6263
});
64+
65+
it('issue-2406: should run nested describe.only suites', function(done) {
66+
this.timeout(2000);
67+
runJSON('regression/issue-2406.js', [], function(err, res) {
68+
assert(!err);
69+
assert.equal(res.stats.pending, 0);
70+
assert.equal(res.stats.passes, 2);
71+
assert.equal(res.stats.failures, 0);
72+
assert.equal(res.code, 0);
73+
done();
74+
});
75+
});
6376
});

0 commit comments

Comments
 (0)