Skip to content

Commit

Permalink
Native types exceptions can crash Mocha (#3471)
Browse files Browse the repository at this point in the history
- Any non-extensible type thrown in an async callback raises an exception in
 Mocha runner, stopping tests.
  • Loading branch information
fargies authored and outsideris committed Oct 21, 2018
1 parent ceff283 commit 2344119
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 8 deletions.
40 changes: 32 additions & 8 deletions lib/runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -231,14 +231,8 @@ Runner.prototype.fail = function(test, err) {
++this.failures;
test.state = 'failed';

if (!(err instanceof Error || (err && typeof err.message === 'string'))) {
err = new Error(
'the ' +
type(err) +
' ' +
stringify(err) +
' was thrown, throw an Error :)'
);
if (!isError(err)) {
err = thrown2Error(err);
}

try {
Expand Down Expand Up @@ -731,6 +725,10 @@ Runner.prototype.uncaught = function(err) {
debug('uncaught undefined exception');
err = undefinedError();
}

if (!isError(err)) {
err = thrown2Error(err);
}
err.uncaught = true;

var runnable = this.currentRunnable;
Expand Down Expand Up @@ -993,6 +991,32 @@ function filterLeaks(ok, globals) {
});
}

/**
* Check if argument is an instance of Error object or a duck-typed equivalent.
*
* @private
* @param {Object} err - object to check
* @param {string} err.message - error message
* @returns {boolean}
*/
function isError(err) {
return err instanceof Error || (err && typeof err.message === 'string');
}

/**
*
* Converts thrown non-extensible type into proper Error.
*
* @private
* @param {*} thrown - Non-extensible type thrown by code
* @return {Error}
*/
function thrown2Error(err) {
return new Error(
'the ' + type(err) + ' ' + stringify(err) + ' was thrown, throw an Error :)'
);
}

/**
* Array of globals dependent on the environment.
*
Expand Down
46 changes: 46 additions & 0 deletions test/unit/throw.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,52 @@ describe('a test that throws', function() {
});
});

describe('non-extensible', function() {
it('should not pass if throwing sync and test is sync', function(done) {
var test = new Test('im sync and throw string sync', function() {
throw 'non-extensible';
});
suite.addTest(test);
runner = new Runner(suite);
runner.on('end', function() {
expect(runner.failures, 'to be', 1);
expect(test.state, 'to be', 'failed');
done();
});
runner.run();
});

it('should not pass if throwing sync and test is async', function(done) {
var test = new Test('im async and throw string sync', function(done2) {
throw 'non-extensible';
});
suite.addTest(test);
runner = new Runner(suite);
runner.on('end', function() {
expect(runner.failures, 'to be', 1);
expect(test.state, 'to be', 'failed');
done();
});
runner.run();
});

it('should not pass if throwing async and test is async', function(done) {
var test = new Test('im async and throw string async', function(done2) {
process.nextTick(function() {
throw 'non-extensible';
});
});
suite.addTest(test);
runner = new Runner(suite);
runner.on('end', function() {
expect(runner.failures, 'to be', 1);
expect(test.state, 'to be', 'failed');
done();
});
runner.run();
});
});

describe('undefined', function() {
it('should not pass if throwing sync and test is sync', function(done) {
var test = new Test('im sync and throw undefined sync', function() {
Expand Down

0 comments on commit 2344119

Please sign in to comment.