Skip to content

Commit

Permalink
feat(runner): fix '.only()' exclusive feature, mochajs#1481
Browse files Browse the repository at this point in the history
This PR fix mochajs#1481, and also extends the .only() behaviour.
(i.e: it's not use grep anymore, support suite, test-case or both, add
the ability to run multiple .only)
  • Loading branch information
a8m committed Mar 9, 2016
1 parent 0b9876b commit 55ea1bb
Show file tree
Hide file tree
Showing 17 changed files with 527 additions and 43 deletions.
18 changes: 17 additions & 1 deletion Makefile
Expand Up @@ -35,7 +35,7 @@ lint:

test: lint test-unit

test-all: lint test-bdd test-tdd test-qunit test-exports test-unit test-integration test-jsapi test-compilers test-glob test-requires test-reporters test-only
test-all: lint test-bdd test-tdd test-qunit test-exports test-unit test-integration test-jsapi test-compilers test-glob test-requires test-reporters test-only test-global-only

test-jsapi:
@node test/jsapi
Expand Down Expand Up @@ -117,6 +117,22 @@ test-only:
--ui qunit \
test/acceptance/misc/only/qunit

test-global-only:
@./bin/mocha \
--reporter $(REPORTER) \
--ui tdd \
test/acceptance/misc/only/global/tdd

@./bin/mocha \
--reporter $(REPORTER) \
--ui bdd \
test/acceptance/misc/only/global/bdd

@./bin/mocha \
--reporter $(REPORTER) \
--ui qunit \
test/acceptance/misc/only/global/qunit

test-mocha:
@./bin/mocha \
--reporter $(REPORTER) \
Expand Down
12 changes: 3 additions & 9 deletions lib/interfaces/bdd.js
Expand Up @@ -4,7 +4,6 @@

var Suite = require('../suite');
var Test = require('../test');
var escapeRe = require('escape-string-regexp');

/**
* BDD-style interface:
Expand All @@ -27,7 +26,7 @@ module.exports = function(suite) {
var suites = [suite];

suite.on('pre-require', function(context, file, mocha) {
var common = require('./common')(suites, context);
var common = require('./common')(suites, context, mocha);

context.before = common.before;
context.after = common.after;
Expand Down Expand Up @@ -66,9 +65,7 @@ module.exports = function(suite) {
*/

context.describe.only = function(title, fn) {
var suite = context.describe(title, fn);
mocha.grep(suite.fullTitle());
return suite;
return common.suite.only(mocha, context.describe(title, fn));
};

/**
Expand All @@ -93,10 +90,7 @@ module.exports = function(suite) {
*/

context.it.only = function(title, fn) {
var test = context.it(title, fn);
var reString = '^' + escapeRe(test.fullTitle()) + '$';
mocha.grep(new RegExp(reString));
return test;
return common.test.only(mocha, context.it(title, fn));
};

/**
Expand Down
31 changes: 31 additions & 0 deletions lib/interfaces/common.js
Expand Up @@ -62,7 +62,38 @@ module.exports = function(suites, context) {
suites[0].afterEach(name, fn);
},

suite: {
/**
* Exclusive suite.
*
* @param {Object} mocha
* @param {Function} suite
*/

only: function(mocha, suite) {
suite.isOnly = true;
mocha.options.hasOnly = true;
return suite;
}
},

test: {

/**
* Exclusive test-case.
*
* @param {Object} mocha
* @param {Function} test
* @returns {*}
*/
only: function(mocha, test) {
var suite = test.parent;
suite.isOnly = true;
suite.onlyTests = (suite.onlyTests || []).concat(test);
mocha.options.hasOnly = true;
return test;
},

/**
* Pending test case.
*
Expand Down
8 changes: 2 additions & 6 deletions lib/interfaces/qunit.js
Expand Up @@ -4,7 +4,6 @@

var Suite = require('../suite');
var Test = require('../test');
var escapeRe = require('escape-string-regexp');

/**
* QUnit-style interface:
Expand Down Expand Up @@ -61,8 +60,7 @@ module.exports = function(suite) {
*/

context.suite.only = function(title, fn) {
var suite = context.suite(title, fn);
mocha.grep(suite.fullTitle());
return common.suite.only(mocha, context.suite(title, fn));
};

/**
Expand All @@ -83,9 +81,7 @@ module.exports = function(suite) {
*/

context.test.only = function(title, fn) {
var test = context.test(title, fn);
var reString = '^' + escapeRe(test.fullTitle()) + '$';
mocha.grep(new RegExp(reString));
return common.test.only(mocha, context.test(title, fn));
};

context.test.skip = common.test.skip;
Expand Down
8 changes: 2 additions & 6 deletions lib/interfaces/tdd.js
Expand Up @@ -4,7 +4,6 @@

var Suite = require('../suite');
var Test = require('../test');
var escapeRe = require('escape-string-regexp');

/**
* TDD-style interface:
Expand Down Expand Up @@ -71,8 +70,7 @@ module.exports = function(suite) {
* Exclusive test-case.
*/
context.suite.only = function(title, fn) {
var suite = context.suite(title, fn);
mocha.grep(suite.fullTitle());
return common.suite.only(mocha, context.suite(title, fn));
};

/**
Expand All @@ -95,9 +93,7 @@ module.exports = function(suite) {
*/

context.test.only = function(title, fn) {
var test = context.test(title, fn);
var reString = '^' + escapeRe(test.fullTitle()) + '$';
mocha.grep(new RegExp(reString));
return common.test.only(mocha, context.test(title, fn));
};

context.test.skip = common.test.skip;
Expand Down
1 change: 1 addition & 0 deletions lib/mocha.js
Expand Up @@ -433,6 +433,7 @@ Mocha.prototype.run = function(fn) {
var reporter = new this._reporter(runner, options);
runner.ignoreLeaks = options.ignoreLeaks !== false;
runner.fullStackTrace = options.fullStackTrace;
runner.hasOnly = options.hasOnly;
runner.asyncOnly = options.asyncOnly;
if (options.grep) {
runner.grep(options.grep, options.invert);
Expand Down
25 changes: 25 additions & 0 deletions lib/runner.js
Expand Up @@ -635,6 +635,11 @@ Runner.prototype.run = function(fn) {
var self = this;
var rootSuite = this.suite;

// If there is an `only` filter
if (this.hasOnly) {
filterOnly(rootSuite);
}

fn = fn || function() {};

function uncaught(err) {
Expand Down Expand Up @@ -686,6 +691,26 @@ Runner.prototype.abort = function() {
return this;
};

/**
* Filter suites based on `isOnly` logic.
*
* @param {Array} suite
* @returns {Boolean}
* @api private
*/
function filterOnly(suite) {
// If it has `only` tests, run only those
if (suite.onlyTests) {
suite.tests = suite.onlyTests;
}
// Filter the nested suites
suite.suites = filter(suite.suites, filterOnly);
// Don't run tests from suites that are not marked as `only`
suite.tests = suite.isOnly ? suite.tests : [];
// Keep the suite only if there is something to run
return suite.suites.length || suite.tests.length;
}

/**
* Filter leaks with the given globals flagged as `ok`.
*
Expand Down
125 changes: 118 additions & 7 deletions test/acceptance/misc/only/bdd.js
@@ -1,14 +1,125 @@
describe('should only run .only test in this bdd suite', function() {
it('should not run this test', function() {
var zero = 0;
zero.should.equal(1, 'this test should have been skipped');
(0).should.equal(1, 'this test should have been skipped');
});
it.only('should run this test', function() {
var zero = 0;
zero.should.equal(0, 'this .only test should run');
it.only('should run this test', function() {
(0).should.equal(0, 'this .only test should run');
});
it('should run this test, not (includes the title of the .only test)', function() {
var zero = 0;
zero.should.equal(1, 'this test should have been skipped');
(0).should.equal(1, 'this test should have been skipped');
});
});

describe('should not run this suite', function() {
it('should not run this test', function() {
(true).should.equal(false);
});

it('should not run this test', function() {
(true).should.equal(false);
});

it('should not run this test', function() {
(true).should.equal(false);
});
});

describe.only('should run all tests in this bdd suite', function() {
it('should run this test #1', function() {
(true).should.equal(true);
});

it('should run this test #2', function() {
(1).should.equal(1);
});

it('should run this test #3', function() {
('foo').should.equal('foo');
});
});

describe('should run only suites that marked as `only`', function() {
describe.only('should run all this tdd suite', function() {
it('should run this test #1', function() {
(true).should.equal(true);
});

it('should run this test #2', function() {
(true).should.equal(true);
});
});

describe('should not run this suite', function() {
it('should run this test', function() {
(true).should.equal(false);
});
});
});

// Nested situation
describe('should not run parent tests', function() {
it('should not run this test', function() {
(true).should.equal(false);
});
describe('and not the child tests too', function() {
it('should not run this test', function() {
(true).should.equal(false);
});
describe.only('but run all the tests in this suite', function() {
it('should run this test #1', function() {
(true).should.equal(true);
});
it('should run this test #2', function() {
(true).should.equal(true);
});
});
});
});

// mark test as `only` override the suite behavior
describe.only('should run only tests that marked as `only`', function() {
it('should not run this test #1', function() {
(false).should.equal(true);
});

it.only('should run this test #2', function() {
(true).should.equal(true);
});

it('should not run this test #3', function() {
(false).should.equal(true);
});

it.only('should run this test #4', function() {
(true).should.equal(true);
});
});

describe.only('Should run only test cases that mark as only', function() {
it.only('should runt his test', function() {
(true).should.equal(true);
});

it('should not run this test', function() {
(false).should.equal(true);
});

describe('should not run this suite', function() {
it('should not run this test', function() {
(false).should.equal(true);
});
});
});

// Root Suite
it.only('#Root-Suite, should run this test-case #1', function() {
(true).should.equal(true);
});

it.only('#Root-Suite, should run this test-case #2', function() {
(true).should.equal(true);
});

it('#Root-Suite, should not run this test', function() {
(false).should.equal(true);
});
12 changes: 12 additions & 0 deletions test/acceptance/misc/only/global/bdd.js
@@ -0,0 +1,12 @@
// Root-only test cases
it.only('#Root-Suite, should run this bdd test-case #1', function() {
(true).should.equal(true);
});

it('#Root-Suite, should not run this bdd test-case #2', function() {
(false).should.equal(true);
});

it('#Root-Suite, should not run this bdd test-case #3', function() {
(false).should.equal(true);
});
12 changes: 12 additions & 0 deletions test/acceptance/misc/only/global/qunit.js
@@ -0,0 +1,12 @@
// Root-only test cases
test.only('#Root-Suite, should run this qunit test-case #1', function() {
(true).should.equal(true);
});

test('#Root-Suite, should not run this qunit test-case #2', function() {
(false).should.equal(true);
});

test('#Root-Suite, should not run this qunit test-case #3', function() {
(false).should.equal(true);
});
12 changes: 12 additions & 0 deletions test/acceptance/misc/only/global/tdd.js
@@ -0,0 +1,12 @@
// Root-only test cases
test.only('#Root-Suite, should run this tdd test-case #1', function() {
(true).should.equal(true);
});

test('#Root-Suite, should not run this tdd test-case #2', function() {
(false).should.equal(true);
});

test('#Root-Suite, should not run this tdd test-case #3', function() {
(false).should.equal(true);
});

0 comments on commit 55ea1bb

Please sign in to comment.