Skip to content

Commit

Permalink
added allowUncaught option (mochajs#553)
Browse files Browse the repository at this point in the history
allows unhandled exceptions to propagate in the browser
added tests for allowUncaught option
global error handler prints to dom with allowUncaught
  • Loading branch information
amsul authored and boneskull committed Aug 31, 2015
1 parent 242ee53 commit 92dfdd4
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 33 deletions.
43 changes: 35 additions & 8 deletions lib/mocha.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ function image(name) {
* - `fullTrace` display the full stack-trace on failing
* - `grep` string or regexp to filter tests with
*
* @api public
* @param {Object} options
* @api public
*/
function Mocha(options) {
options = options || {};
Expand Down Expand Up @@ -141,6 +141,8 @@ Mocha.prototype.addFile = function(file) {
/**
* Set reporter to `reporter`, defaults to "spec".
*
* @param {String|Function} reporter name or constructor
* @param {Object} reporterOptions optional options
* @api public
* @param {string|Function} reporter name or constructor
* @param {Object} reporterOptions optional options
Expand Down Expand Up @@ -243,6 +245,8 @@ Mocha.prototype._growl = function(runner, reporter) {
/**
* Add regexp to grep, if `re` is a string it is escaped.
*
* @param {RegExp|String} re
* @return {Mocha}
* @api public
* @param {RegExp|string} re
* @return {Mocha}
Expand All @@ -255,8 +259,8 @@ Mocha.prototype.grep = function(re) {
/**
* Invert `.grep()` matches.
*
* @api public
* @return {Mocha}
* @api public
*/
Mocha.prototype.invert = function() {
this.options.invert = true;
Expand All @@ -266,6 +270,8 @@ Mocha.prototype.invert = function() {
/**
* Ignore global leaks.
*
* @param {Boolean} ignore
* @return {Mocha}
* @api public
* @param {boolean} ignore
* @return {Mocha}
Expand All @@ -278,8 +284,8 @@ Mocha.prototype.ignoreLeaks = function(ignore) {
/**
* Enable global leak checking.
*
* @api public
* @return {Mocha}
* @api public
*/
Mocha.prototype.checkLeaks = function() {
this.options.ignoreLeaks = false;
Expand All @@ -289,8 +295,8 @@ Mocha.prototype.checkLeaks = function() {
/**
* Display long stack-trace on failing
*
* @api public
* @return {Mocha}
* @api public
*/
Mocha.prototype.fullTrace = function() {
this.options.fullStackTrace = true;
Expand All @@ -300,8 +306,8 @@ Mocha.prototype.fullTrace = function() {
/**
* Enable growl support.
*
* @api public
* @return {Mocha}
* @api public
*/
Mocha.prototype.growl = function() {
this.options.growl = true;
Expand All @@ -311,6 +317,8 @@ Mocha.prototype.growl = function() {
/**
* Ignore `globals` array or string.
*
* @param {Array|String} globals
* @return {Mocha}
* @api public
* @param {Array|string} globals
* @return {Mocha}
Expand All @@ -323,6 +331,8 @@ Mocha.prototype.globals = function(globals) {
/**
* Emit color output.
*
* @param {Boolean} colors
* @return {Mocha}
* @api public
* @param {boolean} colors
* @return {Mocha}
Expand All @@ -337,6 +347,8 @@ Mocha.prototype.useColors = function(colors) {
/**
* Use inline diffs rather than +/-.
*
* @param {Boolean} inlineDiffs
* @return {Mocha}
* @api public
* @param {boolean} inlineDiffs
* @return {Mocha}
Expand All @@ -349,6 +361,8 @@ Mocha.prototype.useInlineDiffs = function(inlineDiffs) {
/**
* Set the timeout in milliseconds.
*
* @param {Number} timeout
* @return {Mocha}
* @api public
* @param {number} timeout
* @return {Mocha}
Expand All @@ -361,6 +375,8 @@ Mocha.prototype.timeout = function(timeout) {
/**
* Set slowness threshold in milliseconds.
*
* @param {Number} slow
* @return {Mocha}
* @api public
* @param {number} slow
* @return {Mocha}
Expand All @@ -373,6 +389,8 @@ Mocha.prototype.slow = function(slow) {
/**
* Enable timeouts.
*
* @param {Boolean} enabled
* @return {Mocha}
* @api public
* @param {boolean} enabled
* @return {Mocha}
Expand All @@ -385,8 +403,8 @@ Mocha.prototype.enableTimeouts = function(enabled) {
/**
* Makes all tests async (accepting a callback)
*
* @api public
* @return {Mocha}
* @api public
*/
Mocha.prototype.asyncOnly = function() {
this.options.asyncOnly = true;
Expand All @@ -397,17 +415,25 @@ Mocha.prototype.asyncOnly = function() {
* Disable syntax highlighting (in browser).
*
* @api public
* @returns {Mocha}
*/
Mocha.prototype.noHighlighting = function() {
this.options.noHighlighting = true;
return this;
};

/**
* Delay root suite execution.
* Enable uncaught errors to propagate (in browser).
*
* @return {Mocha}
* @api public
*/
Mocha.prototype.allowUncaught = function() {
this.options.allowUncaught = true;
return this;
};

/**
* Delay root suite execution.
* @returns {Mocha}
*/
Mocha.prototype.delay = function delay() {
Expand All @@ -434,6 +460,7 @@ Mocha.prototype.run = function(fn) {
runner.ignoreLeaks = options.ignoreLeaks !== false;
runner.fullStackTrace = options.fullStackTrace;
runner.asyncOnly = options.asyncOnly;
runner.allowUncaught = options.allowUncaught;
if (options.grep) {
runner.grep(options.grep, options.invert);
}
Expand Down
42 changes: 29 additions & 13 deletions lib/runnable.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ module.exports = Runnable;
/**
* Initialize a new `Runnable` with the given `title` and callback `fn`.
*
* @param {String} title
* @param {Function} fn
* @api private
* @param {string} title
* @param {Function} fn
Expand Down Expand Up @@ -205,8 +207,8 @@ Runnable.prototype.globals = function(globals) {
/**
* Run the test and invoke `fn(err)`.
*
* @api private
* @param {Function} fn
* @api private
*/
Runnable.prototype.run = function(fn) {
var self = this;
Expand Down Expand Up @@ -255,25 +257,23 @@ Runnable.prototype.run = function(fn) {
if (this.async) {
this.resetTimeout();

if (this.allowUncaught) {
return callFnAsync(this.fn);
}
try {
this.fn.call(ctx, function(err) {
if (err instanceof Error || toString.call(err) === '[object Error]') {
return done(err);
}
if (err != null) {
if (Object.prototype.toString.call(err) === '[object Object]') {
return done(new Error('done() invoked with non-Error: ' + JSON.stringify(err)));
}
return done(new Error('done() invoked with non-Error: ' + err));
}
done();
});
callFnAsync(this.fn);
} catch (err) {
done(utils.getError(err));
}
return;
}

if (this.allowUncaught) {
callFn(this.fn);
done();
return;
}

// sync or promise-returning
try {
if (this.pending) {
Expand Down Expand Up @@ -304,4 +304,20 @@ Runnable.prototype.run = function(fn) {
done();
}
}

function callFnAsync(fn) {
fn.call(ctx, function(err) {
if (err instanceof Error || toString.call(err) === '[object Error]') {
return done(err);
}
if (err) {
if (Object.prototype.toString.call(err) === '[object Object]') {
return done(new Error('done() invoked with non-Error: '
+ JSON.stringify(err)));
}
return done(new Error('done() invoked with non-Error: ' + err));
}
done();
});
}
};
36 changes: 25 additions & 11 deletions lib/runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ function Runner(suite, delay) {
/**
* Wrapper for setImmediate, process.nextTick, or browser polyfill.
*
* @api private
* @param {Function} fn
* @api private
*/
Runner.immediately = global.setImmediate || process.nextTick;

Expand All @@ -96,6 +96,9 @@ Runner.prototype = create(EventEmitter.prototype, {
* Run tests with full titles matching `re`. Updates runner.total
* with number of tests matched.
*
* @param {RegExp} re
* @param {Boolean} invert
* @return {Runner} for chaining
* @api public
* @param {RegExp} re
* @param {boolean} invert
Expand All @@ -113,6 +116,8 @@ Runner.prototype.grep = function(re, invert) {
* Returns the number of tests matching the grep search for the
* given suite.
*
* @param {Suite} suite
* @return {Number}
* @api public
* @param {Suite} suite
* @return {number}
Expand All @@ -137,8 +142,8 @@ Runner.prototype.grepTotal = function(suite) {
/**
* Return a list of global properties.
*
* @api private
* @return {Array}
* @api private
*/
Runner.prototype.globalProps = function() {
var props = keys(global);
Expand All @@ -157,6 +162,8 @@ Runner.prototype.globalProps = function() {
/**
* Allow the given `arr` of globals.
*
* @param {Array} arr
* @return {Runner} for chaining
* @api public
* @param {Array} arr
* @return {Runner} Runner instance.
Expand Down Expand Up @@ -264,6 +271,7 @@ Runner.prototype.failHook = function(hook, err) {
* @param {string} name
* @param {Function} fn
*/

Runner.prototype.hook = function(name, fn) {
var suite = this.suite;
var hooks = suite['_' + name];
Expand Down Expand Up @@ -349,9 +357,9 @@ Runner.prototype.hooks = function(name, suites, fn) {
/**
* Run hooks from the top level down.
*
* @api private
* @param {string} name
* @param {String} name
* @param {Function} fn
* @api private
*/
Runner.prototype.hookUp = function(name, fn) {
var suites = [this.suite].concat(this.parents()).reverse();
Expand All @@ -361,9 +369,9 @@ Runner.prototype.hookUp = function(name, fn) {
/**
* Run hooks from the bottom up.
*
* @api private
* @param {string} name
* @param {String} name
* @param {Function} fn
* @api private
*/
Runner.prototype.hookDown = function(name, fn) {
var suites = [this.suite].concat(this.parents());
Expand All @@ -374,8 +382,8 @@ Runner.prototype.hookDown = function(name, fn) {
* Return an array of parent Suites from
* closest to furthest.
*
* @api private
* @return {Array}
* @api private
*/
Runner.prototype.parents = function() {
var suite = this.suite;
Expand All @@ -389,8 +397,8 @@ Runner.prototype.parents = function() {
/**
* Run the current test and callback `fn(err)`.
*
* @api private
* @param {Function} fn
* @api private
*/
Runner.prototype.runTest = function(fn) {
var self = this;
Expand All @@ -400,6 +408,10 @@ Runner.prototype.runTest = function(fn) {
test.asyncOnly = true;
}

if (this.allowUncaught) {
test.allowUncaught = true;
return test.run(fn);
}
try {
test.on('error', function(err) {
self.fail(test, err);
Expand Down Expand Up @@ -627,8 +639,8 @@ Runner.prototype.runSuite = function(suite, fn) {
/**
* Handle uncaught exceptions.
*
* @api private
* @param {Error} err
* @api private
*/
Runner.prototype.uncaught = function(err) {
if (err) {
Expand Down Expand Up @@ -684,6 +696,8 @@ Runner.prototype.uncaught = function(err) {
* Run the root suite and invoke `fn(failures)`
* on completion.
*
* @param {Function} fn
* @return {Runner} for chaining
* @api public
* @param {Function} fn
* @return {Runner} Runner instance.
Expand Down Expand Up @@ -789,11 +803,11 @@ function filterLeaks(ok, globals) {
/**
* Array of globals dependent on the environment.
*
* @api private
* @return {Array}
* @api private
*/
function extraGlobals() {
if (!process.browser) {
if (typeof process === 'object' && typeof process.version === 'string') {
var nodeVersion = process.version.split('.').reduce(function(a, v) {
return a << 8 | v;
});
Expand Down

0 comments on commit 92dfdd4

Please sign in to comment.