Skip to content

Commit

Permalink
Add fail fast support
Browse files Browse the repository at this point in the history
A new config option `bail` is available. Setting this to a truthy value
will cause all subsequent tests (in all suites) after a failing test to
be skipped.

Resolves #413
  • Loading branch information
jason0x43 committed Mar 23, 2016
1 parent 86193c2 commit a6a732e
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 4 deletions.
38 changes: 35 additions & 3 deletions lib/Suite.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ define([
this.reporterManager && this.reporterManager.emit('newSuite', this);
}

// BAIL_REASON needs to be a string so that Intern can tell when a remote has bailed during unit tests so that it
// can skip functional tests.
var BAIL_REASON = 'bailed';

Suite.prototype = {
constructor: Suite,
name: null,
Expand All @@ -22,6 +26,7 @@ define([
teardown: null,
error: null,
timeElapsed: null,
_bail: null,
_grep: null,
_remote: null,
_environmentType: null,
Expand All @@ -33,6 +38,17 @@ define([
*/
publishAfterSetup: false,

/**
* A flag used to indicate whether a test run shoudl stop after a failed test.
*/
get bail() {
return this._bail || (this.parent && this.parent.bail);
},

set bail(value) {
this._bail = value;
},

/**
* A regular expression used to filter, by test ID, which tests are run.
*/
Expand Down Expand Up @@ -291,8 +307,8 @@ define([
throw reason;
});

function next() {
var test = tests[i++];
function next(test) {
test = tests[i++];

if (!test) {
firstError ? reject(firstError) : resolve();
Expand Down Expand Up @@ -350,7 +366,23 @@ define([
});
}

current.then(next);
current.then(function () {
function skipRestOfSuite() {
self.skipped = self.skipped != null ? self.skipped : BAIL_REASON;
}

// If the test was a suite and the suite was skipped due to bailing, skip the rest of this
// suite
if (test.tests && test.skipped === BAIL_REASON) {
skipRestOfSuite();
}
// If the test errored and bail mode is enabled, skip the rest of this suite
else if (test.error && self.bail) {
skipRestOfSuite();
}

next();
});
}

next();
Expand Down
3 changes: 2 additions & 1 deletion lib/executors/Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ define([
grep: config.grep,
sessionId: config.sessionId,
timeout: config.defaultTimeout,
reporterManager: this.reporterManager
reporterManager: this.reporterManager,
bail: config.bail
});

this.suites = [ suite ];
Expand Down
1 change: 1 addition & 0 deletions lib/executors/Runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ define([
reporterManager: reporterManager,
publishAfterSetup: true,
grep: config.grep,
bail: config.bail,
timeout: config.defaultTimeout,
setup: function () {
return util.retry(function () {
Expand Down
59 changes: 59 additions & 0 deletions tests/unit/lib/Suite.js
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,65 @@ define([
}));
},

'Suite#run bail': function () {
var dfd = this.async(5000);
var suite = createSuite({
bail: true
});
var testsRun = [];
var fooTest = new Test({
name: 'foo',
parent: suite,
test: function () {
testsRun.push(this);
}
});
var barSuite = createSuite({
name: 'bar',
parent: suite,
tests: [
new Test({
name: 'foo',
test: function () {
testsRun.push(this);
// Fail this test; everything after this should not run
throw new Error('fail');
}
}),
new Test({
name: 'baz',
test: function () {
testsRun.push(this);
}
})
]
});
var foodTest = new Test({
name: 'food',
parent: suite,
test: function () {
testsRun.push(this);
}
});

var teardownRan = false;
barSuite.teardown = function () {
teardownRan = true;
};

suite.tests.push(fooTest);
suite.tests.push(barSuite);
suite.tests.push(foodTest);

suite.run().then(dfd.callback(function () {
assert.deepEqual(testsRun, [ fooTest, barSuite.tests[0] ],
'Only tests before failing test should have run');
assert.isTrue(teardownRan, 'teardown should have run for bailing suite');
}, function () {
dfd.reject(new assert.AssertionError('Suite should not fail'));
}));
},

'Suite#run skip': function () {
var dfd = this.async(5000);
var suite = createSuite();
Expand Down

0 comments on commit a6a732e

Please sign in to comment.