Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add optional timeouts on before(Each), after(Each) #215

Merged
merged 1 commit into from Sep 24, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Expand Up @@ -27,6 +27,7 @@ global manipulation. Our goal with **lab** is to keep the execution engine as si
- `-I`, `--ignore` - ignore a list of globals for the leak detection (comma separated)
- `-l`, `--leaks` - disables global variable leak detection.
- `-m`, `--timeout` - individual tests timeout in milliseconds (zero disables timeout). Defaults to 2 seconds.
- `-M`, `--context-timeout` - default timeouts for before, after, beforeEach and afterEach in milliseconds. Disabled by default.
- `-o`, `--output` - file to write the report to, otherwise sent to stdout.
- `-p`, `--parallel` - sets parallel execution as default test option. Defaults to serial execution.
- `-r`, `--reporter` - the reporter used to generate the test results. Defaults to `console`. Options are:
Expand Down Expand Up @@ -119,9 +120,18 @@ Both `test()` and `experiment()` accept an optional `options` argument which mus
- `skip` - skip execution. Cannot be overridden in children once parent is set to skip.
- `only` - marks all other tests or experiments with `skip`. This doesn't mark all other experiments and tests in a suite of scripts as skipped, instead it works within a single test script.

`before()`, `after()`, `beforeEach()`, `afterEach()` accept an optional `options` argument which must be an object with the following optional keys:
- `timeout` - set a specific timeout in milliseconds. Disabled by default or the value of `-M`.

```javascript
lab.experiment('math', { timeout: 1000 }, function () {

lab.before({ timeout: 500 }, function (done) {

doSomething();
done();
});

lab.test('returns true when 1 + 1 equals 2', { parallel: true }, function (done) {

Lab.expect(1+1).to.equal(2);
Expand Down
7 changes: 6 additions & 1 deletion lib/cli.js
Expand Up @@ -117,6 +117,11 @@ internals.options = function () {
default: null,
description: 'enable color output (defaults to terminal capabilities)'
},
'context-timeout': {
alias: 'M',
type: 'number',
description: 'timeout for before, after, beforeEach, afterEach in milliseconds'
},
dry: {
alias: 'd',
type: 'boolean',
Expand Down Expand Up @@ -214,7 +219,7 @@ internals.options = function () {
paths: argv._ ? [].concat(argv._) : ['test']
};

var keys = ['coverage', 'colors', 'dry', 'environment', 'flat', 'grep', 'globals', 'timeout', 'parallel', 'reporter', 'threshold'];
var keys = ['coverage', 'colors', 'dry', 'environment', 'flat', 'grep', 'globals', 'timeout', 'parallel', 'reporter', 'threshold', 'context-timeout'];
for (var i = 0, il = keys.length; i < il; ++i) {
if (argv.hasOwnProperty(keys[i]) && argv[keys[i]] !== undefined) {
options[keys[i]] = argv[keys[i]];
Expand Down
32 changes: 24 additions & 8 deletions lib/index.js
Expand Up @@ -143,47 +143,63 @@ internals.experiment = function (title /*, options, fn */) {
};


internals.before = function (fn) {
internals.before = function (/* options, */ fn) {

var options = arguments.length === 2 ? arguments[0] : {};
fn = arguments.length === 2 ? arguments[1] : fn;

var before = {
title: 'Before ' + this._titles.join(' '),
fn: fn
fn: fn,
options: options
};

this._current.befores = this._current.befores || [];
this._current.befores.push(before);
};


internals.after = function (fn) {
internals.after = function (/* options, */ fn) {

var options = arguments.length === 2 ? arguments[0] : {};
fn = arguments.length === 2 ? arguments[1] : fn;

var after = {
title: 'After ' + this._titles.join(' '),
fn: fn
fn: fn,
options: options
};

this._current.afters = this._current.afters || [];
this._current.afters.push(after);
};


internals.beforeEach = function (fn) {
internals.beforeEach = function (/* options, */ fn) {

var options = arguments.length === 2 ? arguments[0] : {};
fn = arguments.length === 2 ? arguments[1] : fn;

var beforeEach = {
title: 'Before each ' + this._titles.join(' '),
fn: fn
fn: fn,
options: options
};

this._current.beforeEaches = this._current.beforeEaches || [];
this._current.beforeEaches.push(beforeEach);
};


internals.afterEach = function (fn) {
internals.afterEach = function (/* options, */ fn) {

var options = arguments.length === 2 ? arguments[0] : {};
fn = arguments.length === 2 ? arguments[1] : fn;

var afterEach = {
title: 'After each ' + this._titles.join(' '),
fn: fn
fn: fn,
options: options
};

this._current.afterEaches = this._current.afterEaches || [];
Expand Down
32 changes: 24 additions & 8 deletions lib/runner.js
Expand Up @@ -129,7 +129,7 @@ internals.executeExperiments = function (experiments, state, skip, callback) {
return next();
}

internals.executeDeps(experiment.befores, function (err) {
internals.executeDeps(experiment.befores, state, function (err) {

if (err) {
internals.fail([experiment], state, skip, '\'before\' action failed');
Expand Down Expand Up @@ -158,10 +158,10 @@ internals.executeExperiments = function (experiments, state, skip, callback) {
return next();
}

internals.executeDeps(experiment.afters, next);
internals.executeDeps(experiment.afters, state, next);
}
];

Items.serial(steps, function (item, next) {

item(next);
Expand All @@ -182,15 +182,31 @@ internals.executeExperiments = function (experiments, state, skip, callback) {
};


internals.executeDeps = function (deps, callback) {
internals.executeDeps = function (deps, state, callback) {

if (!deps) {
return callback();
}

Items.serial(deps, function (dep, next) {

dep.fn.call(null, next);
var finish = function (err) {

clearTimeout(timeoutId);
next(err);
};

var timeout = dep.options['timeout'] || state.options['context-timeout'];
if (timeout) {
var timeoutId = setTimeout(function () {

var error = new Error('Timed out (' + timeout + 'ms) - ' + dep.title);
error.timeout = true;
finish(error);
});
}

dep.fn.call(null, finish);
}, callback);
};

Expand Down Expand Up @@ -238,7 +254,7 @@ internals.executeTests = function (experiment, state, skip, callback) {

// Before each

internals.executeDeps(befores, function (err) {
internals.executeDeps(befores, state, function (err) {

if (err) {
internals.failTest(test, state, skip, '\'before each\' action failed');
Expand Down Expand Up @@ -282,10 +298,10 @@ internals.executeTests = function (experiment, state, skip, callback) {

// After each

return internals.executeDeps(afters, next);
return internals.executeDeps(afters, state, next);
}
];

Items.serial(steps, function (item, next) {

item(next);
Expand Down
83 changes: 73 additions & 10 deletions test/reporters.js
Expand Up @@ -263,21 +263,84 @@ describe('Reporter', function () {
});
});

it('generates a report with timeout', function (done) {
describe('timeouts', function () {

var script = Lab.script();
script.experiment('test', function () {
it('generates a report with timeout', function (done) {

script.test('works', function (finished) { });
var script = Lab.script();
script.experiment('test', function () {

script.test('works', function (finished) { });
});

Lab.report(script, { reporter: 'console', colors: false, timeout: 1 }, function (err, code, output) {

expect(err).to.not.exist;
expect(code).to.equal(1);
var result = output.replace(/\/[\/\w]+\.js\:\d+\:\d+/g, '<trace>');
expect(result).to.match(/^\n \n x\n\nFailed tests:\n\n 1\) test works:\n\n Error: Timed out \(\d+ms\)\n\n\n\n1 of 1 tests failed\nTest duration: \d+ ms\nNo global variable leaks detected\n\n$/);
done();
});
});

Lab.report(script, { reporter: 'console', colors: false, timeout: 1 }, function (err, code, output) {
var tests = [
{
type: 'before',
expect: /^\n \n x\n\nTest script errors:\n\nTimed out \(1ms\) - Before test\n at null\.<anonymous> \(<trace>\)\n at Timer\.listOnTimeout \[as ontimeout\] \(<trace>\)\n\nThere were 1 test script error\(s\)\.\n\nFailed tests:\n\n 1\) test works:\n\n \n\n \n\n\n1 of 1 tests failed\nTest duration: \d+ ms\nNo global variable leaks detected\n\n$/
},
{
type: 'after',
expect: /^\n \n \.\n\nTest script errors:\n\nTimed out \(1ms\) - After test\n at null\.<anonymous> \(<trace>\)\n at Timer\.listOnTimeout \[as ontimeout\] \(<trace>\)\n\nThere were 1 test script error\(s\)\.\n\n1 tests complete\nTest duration: \d+ ms\nNo global variable leaks detected\n\n$/
},
{
type: 'beforeEach',
expect: /^\n \n x\n\nTest script errors:\n\nTimed out \(1ms\) - Before each test\n at null\.<anonymous> \(<trace>\)\n at Timer\.listOnTimeout \[as ontimeout\] \(<trace>\)\n\nThere were 1 test script error\(s\)\.\n\nFailed tests:\n\n 1\) test works:\n\n \n\n \n\n\n1 of 1 tests failed\nTest duration: \d+ ms\nNo global variable leaks detected\n\n$/
},
{
type: 'afterEach',
expect: /^\n \n \.\n\nTest script errors:\n\nTimed out \(1ms\) - After each test\n at null\.<anonymous> \(<trace>\)\n at Timer\.listOnTimeout \[as ontimeout\] \(<trace>\)\n\nThere were 1 test script error\(s\)\.\n\n1 tests complete\nTest duration: \d+ ms\nNo global variable leaks detected\n\n$/
}
];

expect(err).to.not.exist;
expect(code).to.equal(1);
var result = output.replace(/\/[\/\w]+\.js\:\d+\:\d+/g, '<trace>');
expect(result).to.match(/^\n \n x\n\nFailed tests:\n\n 1\) test works:\n\n Error: Timed out \(\d+ms\)\n\n\n\n1 of 1 tests failed\nTest duration: \d+ ms\nNo global variable leaks detected\n\n$/);
done();
tests.forEach(function (test) {

it('generates a report with timeout on ' + test.type, function (done) {

var script = Lab.script();
script.experiment('test', function () {

script[test.type](function (finished) { });
script.test('works', function (finished) { finished() });
});

Lab.report(script, { reporter: 'console', colors: false, 'context-timeout': 1 }, function (err, code, output) {

expect(err).to.not.exist;
expect(code).to.equal(1);
var result = output.replace(/\/?[\/\w]+\.js\:\d+\:\d+/g, '<trace>');
expect(result).to.match(test.expect);
done();
});
});

it('generates a report with inline timeout on ' + test.type, function (done) {

var script = Lab.script();
script.experiment('test', function () {

script[test.type]({ timeout: 1 }, function (finished) { });
script.test('works', function (finished) { finished() });
});

Lab.report(script, { reporter: 'console', colors: false }, function (err, code, output) {

expect(err).to.not.exist;
expect(code).to.equal(1);
var result = output.replace(/\/?[\/\w]+\.js\:\d+\:\d+/g, '<trace>');
expect(result).to.match(test.expect);
done();
});
});
});
});

Expand Down