| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036 | 
							- 'use strict';
 
- /**
 
-  * Module dependencies.
 
-  */
 
- var util = require('util');
 
- var EventEmitter = require('events').EventEmitter;
 
- var Pending = require('./pending');
 
- var utils = require('./utils');
 
- var inherits = utils.inherits;
 
- var debug = require('debug')('mocha:runner');
 
- var Runnable = require('./runnable');
 
- var Suite = require('./suite');
 
- var HOOK_TYPE_BEFORE_EACH = Suite.constants.HOOK_TYPE_BEFORE_EACH;
 
- var HOOK_TYPE_AFTER_EACH = Suite.constants.HOOK_TYPE_AFTER_EACH;
 
- var HOOK_TYPE_AFTER_ALL = Suite.constants.HOOK_TYPE_AFTER_ALL;
 
- var HOOK_TYPE_BEFORE_ALL = Suite.constants.HOOK_TYPE_BEFORE_ALL;
 
- var EVENT_ROOT_SUITE_RUN = Suite.constants.EVENT_ROOT_SUITE_RUN;
 
- var STATE_FAILED = Runnable.constants.STATE_FAILED;
 
- var STATE_PASSED = Runnable.constants.STATE_PASSED;
 
- var dQuote = utils.dQuote;
 
- var ngettext = utils.ngettext;
 
- var sQuote = utils.sQuote;
 
- var stackFilter = utils.stackTraceFilter();
 
- var stringify = utils.stringify;
 
- var type = utils.type;
 
- var createInvalidExceptionError = require('./errors')
 
-   .createInvalidExceptionError;
 
- /**
 
-  * Non-enumerable globals.
 
-  * @readonly
 
-  */
 
- var globals = [
 
-   'setTimeout',
 
-   'clearTimeout',
 
-   'setInterval',
 
-   'clearInterval',
 
-   'XMLHttpRequest',
 
-   'Date',
 
-   'setImmediate',
 
-   'clearImmediate'
 
- ];
 
- var constants = utils.defineConstants(
 
-   /**
 
-    * {@link Runner}-related constants.
 
-    * @public
 
-    * @memberof Runner
 
-    * @readonly
 
-    * @alias constants
 
-    * @static
 
-    * @enum {string}
 
-    */
 
-   {
 
-     /**
 
-      * Emitted when {@link Hook} execution begins
 
-      */
 
-     EVENT_HOOK_BEGIN: 'hook',
 
-     /**
 
-      * Emitted when {@link Hook} execution ends
 
-      */
 
-     EVENT_HOOK_END: 'hook end',
 
-     /**
 
-      * Emitted when Root {@link Suite} execution begins (all files have been parsed and hooks/tests are ready for execution)
 
-      */
 
-     EVENT_RUN_BEGIN: 'start',
 
-     /**
 
-      * Emitted when Root {@link Suite} execution has been delayed via `delay` option
 
-      */
 
-     EVENT_DELAY_BEGIN: 'waiting',
 
-     /**
 
-      * Emitted when delayed Root {@link Suite} execution is triggered by user via `global.run()`
 
-      */
 
-     EVENT_DELAY_END: 'ready',
 
-     /**
 
-      * Emitted when Root {@link Suite} execution ends
 
-      */
 
-     EVENT_RUN_END: 'end',
 
-     /**
 
-      * Emitted when {@link Suite} execution begins
 
-      */
 
-     EVENT_SUITE_BEGIN: 'suite',
 
-     /**
 
-      * Emitted when {@link Suite} execution ends
 
-      */
 
-     EVENT_SUITE_END: 'suite end',
 
-     /**
 
-      * Emitted when {@link Test} execution begins
 
-      */
 
-     EVENT_TEST_BEGIN: 'test',
 
-     /**
 
-      * Emitted when {@link Test} execution ends
 
-      */
 
-     EVENT_TEST_END: 'test end',
 
-     /**
 
-      * Emitted when {@link Test} execution fails
 
-      */
 
-     EVENT_TEST_FAIL: 'fail',
 
-     /**
 
-      * Emitted when {@link Test} execution succeeds
 
-      */
 
-     EVENT_TEST_PASS: 'pass',
 
-     /**
 
-      * Emitted when {@link Test} becomes pending
 
-      */
 
-     EVENT_TEST_PENDING: 'pending',
 
-     /**
 
-      * Emitted when {@link Test} execution has failed, but will retry
 
-      */
 
-     EVENT_TEST_RETRY: 'retry'
 
-   }
 
- );
 
- module.exports = Runner;
 
- /**
 
-  * Initialize a `Runner` at the Root {@link Suite}, which represents a hierarchy of {@link Suite|Suites} and {@link Test|Tests}.
 
-  *
 
-  * @extends external:EventEmitter
 
-  * @public
 
-  * @class
 
-  * @param {Suite} suite Root suite
 
-  * @param {boolean} [delay] Whether or not to delay execution of root suite
 
-  * until ready.
 
-  */
 
- function Runner(suite, delay) {
 
-   var self = this;
 
-   this._globals = [];
 
-   this._abort = false;
 
-   this._delay = delay;
 
-   this.suite = suite;
 
-   this.started = false;
 
-   this.total = suite.total();
 
-   this.failures = 0;
 
-   this.on(constants.EVENT_TEST_END, function(test) {
 
-     self.checkGlobals(test);
 
-   });
 
-   this.on(constants.EVENT_HOOK_END, function(hook) {
 
-     self.checkGlobals(hook);
 
-   });
 
-   this._defaultGrep = /.*/;
 
-   this.grep(this._defaultGrep);
 
-   this.globals(this.globalProps().concat(extraGlobals()));
 
- }
 
- /**
 
-  * Wrapper for setImmediate, process.nextTick, or browser polyfill.
 
-  *
 
-  * @param {Function} fn
 
-  * @private
 
-  */
 
- Runner.immediately = global.setImmediate || process.nextTick;
 
- /**
 
-  * Inherit from `EventEmitter.prototype`.
 
-  */
 
- inherits(Runner, EventEmitter);
 
- /**
 
-  * Run tests with full titles matching `re`. Updates runner.total
 
-  * with number of tests matched.
 
-  *
 
-  * @public
 
-  * @memberof Runner
 
-  * @param {RegExp} re
 
-  * @param {boolean} invert
 
-  * @return {Runner} Runner instance.
 
-  */
 
- Runner.prototype.grep = function(re, invert) {
 
-   debug('grep %s', re);
 
-   this._grep = re;
 
-   this._invert = invert;
 
-   this.total = this.grepTotal(this.suite);
 
-   return this;
 
- };
 
- /**
 
-  * Returns the number of tests matching the grep search for the
 
-  * given suite.
 
-  *
 
-  * @memberof Runner
 
-  * @public
 
-  * @param {Suite} suite
 
-  * @return {number}
 
-  */
 
- Runner.prototype.grepTotal = function(suite) {
 
-   var self = this;
 
-   var total = 0;
 
-   suite.eachTest(function(test) {
 
-     var match = self._grep.test(test.fullTitle());
 
-     if (self._invert) {
 
-       match = !match;
 
-     }
 
-     if (match) {
 
-       total++;
 
-     }
 
-   });
 
-   return total;
 
- };
 
- /**
 
-  * Return a list of global properties.
 
-  *
 
-  * @return {Array}
 
-  * @private
 
-  */
 
- Runner.prototype.globalProps = function() {
 
-   var props = Object.keys(global);
 
-   // non-enumerables
 
-   for (var i = 0; i < globals.length; ++i) {
 
-     if (~props.indexOf(globals[i])) {
 
-       continue;
 
-     }
 
-     props.push(globals[i]);
 
-   }
 
-   return props;
 
- };
 
- /**
 
-  * Allow the given `arr` of globals.
 
-  *
 
-  * @public
 
-  * @memberof Runner
 
-  * @param {Array} arr
 
-  * @return {Runner} Runner instance.
 
-  */
 
- Runner.prototype.globals = function(arr) {
 
-   if (!arguments.length) {
 
-     return this._globals;
 
-   }
 
-   debug('globals %j', arr);
 
-   this._globals = this._globals.concat(arr);
 
-   return this;
 
- };
 
- /**
 
-  * Check for global variable leaks.
 
-  *
 
-  * @private
 
-  */
 
- Runner.prototype.checkGlobals = function(test) {
 
-   if (this.ignoreLeaks) {
 
-     return;
 
-   }
 
-   var ok = this._globals;
 
-   var globals = this.globalProps();
 
-   var leaks;
 
-   if (test) {
 
-     ok = ok.concat(test._allowedGlobals || []);
 
-   }
 
-   if (this.prevGlobalsLength === globals.length) {
 
-     return;
 
-   }
 
-   this.prevGlobalsLength = globals.length;
 
-   leaks = filterLeaks(ok, globals);
 
-   this._globals = this._globals.concat(leaks);
 
-   if (leaks.length) {
 
-     var format = ngettext(
 
-       leaks.length,
 
-       'global leak detected: %s',
 
-       'global leaks detected: %s'
 
-     );
 
-     var error = new Error(util.format(format, leaks.map(sQuote).join(', ')));
 
-     this.fail(test, error);
 
-   }
 
- };
 
- /**
 
-  * Fail the given `test`.
 
-  *
 
-  * @private
 
-  * @param {Test} test
 
-  * @param {Error} err
 
-  */
 
- Runner.prototype.fail = function(test, err) {
 
-   if (test.isPending()) {
 
-     return;
 
-   }
 
-   ++this.failures;
 
-   test.state = STATE_FAILED;
 
-   if (!isError(err)) {
 
-     err = thrown2Error(err);
 
-   }
 
-   try {
 
-     err.stack =
 
-       this.fullStackTrace || !err.stack ? err.stack : stackFilter(err.stack);
 
-   } catch (ignore) {
 
-     // some environments do not take kindly to monkeying with the stack
 
-   }
 
-   this.emit(constants.EVENT_TEST_FAIL, test, err);
 
- };
 
- /**
 
-  * Fail the given `hook` with `err`.
 
-  *
 
-  * Hook failures work in the following pattern:
 
-  * - If bail, run corresponding `after each` and `after` hooks,
 
-  *   then exit
 
-  * - Failed `before` hook skips all tests in a suite and subsuites,
 
-  *   but jumps to corresponding `after` hook
 
-  * - Failed `before each` hook skips remaining tests in a
 
-  *   suite and jumps to corresponding `after each` hook,
 
-  *   which is run only once
 
-  * - Failed `after` hook does not alter
 
-  *   execution order
 
-  * - Failed `after each` hook skips remaining tests in a
 
-  *   suite and subsuites, but executes other `after each`
 
-  *   hooks
 
-  *
 
-  * @private
 
-  * @param {Hook} hook
 
-  * @param {Error} err
 
-  */
 
- Runner.prototype.failHook = function(hook, err) {
 
-   hook.originalTitle = hook.originalTitle || hook.title;
 
-   if (hook.ctx && hook.ctx.currentTest) {
 
-     hook.title =
 
-       hook.originalTitle + ' for ' + dQuote(hook.ctx.currentTest.title);
 
-   } else {
 
-     var parentTitle;
 
-     if (hook.parent.title) {
 
-       parentTitle = hook.parent.title;
 
-     } else {
 
-       parentTitle = hook.parent.root ? '{root}' : '';
 
-     }
 
-     hook.title = hook.originalTitle + ' in ' + dQuote(parentTitle);
 
-   }
 
-   this.fail(hook, err);
 
- };
 
- /**
 
-  * Run hook `name` callbacks and then invoke `fn()`.
 
-  *
 
-  * @private
 
-  * @param {string} name
 
-  * @param {Function} fn
 
-  */
 
- Runner.prototype.hook = function(name, fn) {
 
-   var suite = this.suite;
 
-   var hooks = suite.getHooks(name);
 
-   var self = this;
 
-   function next(i) {
 
-     var hook = hooks[i];
 
-     if (!hook) {
 
-       return fn();
 
-     }
 
-     self.currentRunnable = hook;
 
-     if (name === 'beforeAll') {
 
-       hook.ctx.currentTest = hook.parent.tests[0];
 
-     } else if (name === 'afterAll') {
 
-       hook.ctx.currentTest = hook.parent.tests[hook.parent.tests.length - 1];
 
-     } else {
 
-       hook.ctx.currentTest = self.test;
 
-     }
 
-     self.emit(constants.EVENT_HOOK_BEGIN, hook);
 
-     if (!hook.listeners('error').length) {
 
-       hook.on('error', function(err) {
 
-         self.failHook(hook, err);
 
-       });
 
-     }
 
-     hook.run(function(err) {
 
-       var testError = hook.error();
 
-       if (testError) {
 
-         self.fail(self.test, testError);
 
-       }
 
-       if (err) {
 
-         if (err instanceof Pending) {
 
-           if (name === HOOK_TYPE_BEFORE_EACH || name === HOOK_TYPE_AFTER_EACH) {
 
-             self.test.pending = true;
 
-           } else {
 
-             suite.tests.forEach(function(test) {
 
-               test.pending = true;
 
-             });
 
-             suite.suites.forEach(function(suite) {
 
-               suite.pending = true;
 
-             });
 
-             // a pending hook won't be executed twice.
 
-             hook.pending = true;
 
-           }
 
-         } else {
 
-           self.failHook(hook, err);
 
-           // stop executing hooks, notify callee of hook err
 
-           return fn(err);
 
-         }
 
-       }
 
-       self.emit(constants.EVENT_HOOK_END, hook);
 
-       delete hook.ctx.currentTest;
 
-       next(++i);
 
-     });
 
-   }
 
-   Runner.immediately(function() {
 
-     next(0);
 
-   });
 
- };
 
- /**
 
-  * Run hook `name` for the given array of `suites`
 
-  * in order, and callback `fn(err, errSuite)`.
 
-  *
 
-  * @private
 
-  * @param {string} name
 
-  * @param {Array} suites
 
-  * @param {Function} fn
 
-  */
 
- Runner.prototype.hooks = function(name, suites, fn) {
 
-   var self = this;
 
-   var orig = this.suite;
 
-   function next(suite) {
 
-     self.suite = suite;
 
-     if (!suite) {
 
-       self.suite = orig;
 
-       return fn();
 
-     }
 
-     self.hook(name, function(err) {
 
-       if (err) {
 
-         var errSuite = self.suite;
 
-         self.suite = orig;
 
-         return fn(err, errSuite);
 
-       }
 
-       next(suites.pop());
 
-     });
 
-   }
 
-   next(suites.pop());
 
- };
 
- /**
 
-  * Run hooks from the top level down.
 
-  *
 
-  * @param {String} name
 
-  * @param {Function} fn
 
-  * @private
 
-  */
 
- Runner.prototype.hookUp = function(name, fn) {
 
-   var suites = [this.suite].concat(this.parents()).reverse();
 
-   this.hooks(name, suites, fn);
 
- };
 
- /**
 
-  * Run hooks from the bottom up.
 
-  *
 
-  * @param {String} name
 
-  * @param {Function} fn
 
-  * @private
 
-  */
 
- Runner.prototype.hookDown = function(name, fn) {
 
-   var suites = [this.suite].concat(this.parents());
 
-   this.hooks(name, suites, fn);
 
- };
 
- /**
 
-  * Return an array of parent Suites from
 
-  * closest to furthest.
 
-  *
 
-  * @return {Array}
 
-  * @private
 
-  */
 
- Runner.prototype.parents = function() {
 
-   var suite = this.suite;
 
-   var suites = [];
 
-   while (suite.parent) {
 
-     suite = suite.parent;
 
-     suites.push(suite);
 
-   }
 
-   return suites;
 
- };
 
- /**
 
-  * Run the current test and callback `fn(err)`.
 
-  *
 
-  * @param {Function} fn
 
-  * @private
 
-  */
 
- Runner.prototype.runTest = function(fn) {
 
-   var self = this;
 
-   var test = this.test;
 
-   if (!test) {
 
-     return;
 
-   }
 
-   var suite = this.parents().reverse()[0] || this.suite;
 
-   if (this.forbidOnly && suite.hasOnly()) {
 
-     fn(new Error('`.only` forbidden'));
 
-     return;
 
-   }
 
-   if (this.asyncOnly) {
 
-     test.asyncOnly = true;
 
-   }
 
-   test.on('error', function(err) {
 
-     self.fail(test, err);
 
-   });
 
-   if (this.allowUncaught) {
 
-     test.allowUncaught = true;
 
-     return test.run(fn);
 
-   }
 
-   try {
 
-     test.run(fn);
 
-   } catch (err) {
 
-     fn(err);
 
-   }
 
- };
 
- /**
 
-  * Run tests in the given `suite` and invoke the callback `fn()` when complete.
 
-  *
 
-  * @private
 
-  * @param {Suite} suite
 
-  * @param {Function} fn
 
-  */
 
- Runner.prototype.runTests = function(suite, fn) {
 
-   var self = this;
 
-   var tests = suite.tests.slice();
 
-   var test;
 
-   function hookErr(_, errSuite, after) {
 
-     // before/after Each hook for errSuite failed:
 
-     var orig = self.suite;
 
-     // for failed 'after each' hook start from errSuite parent,
 
-     // otherwise start from errSuite itself
 
-     self.suite = after ? errSuite.parent : errSuite;
 
-     if (self.suite) {
 
-       // call hookUp afterEach
 
-       self.hookUp(HOOK_TYPE_AFTER_EACH, function(err2, errSuite2) {
 
-         self.suite = orig;
 
-         // some hooks may fail even now
 
-         if (err2) {
 
-           return hookErr(err2, errSuite2, true);
 
-         }
 
-         // report error suite
 
-         fn(errSuite);
 
-       });
 
-     } else {
 
-       // there is no need calling other 'after each' hooks
 
-       self.suite = orig;
 
-       fn(errSuite);
 
-     }
 
-   }
 
-   function next(err, errSuite) {
 
-     // if we bail after first err
 
-     if (self.failures && suite._bail) {
 
-       tests = [];
 
-     }
 
-     if (self._abort) {
 
-       return fn();
 
-     }
 
-     if (err) {
 
-       return hookErr(err, errSuite, true);
 
-     }
 
-     // next test
 
-     test = tests.shift();
 
-     // all done
 
-     if (!test) {
 
-       return fn();
 
-     }
 
-     // grep
 
-     var match = self._grep.test(test.fullTitle());
 
-     if (self._invert) {
 
-       match = !match;
 
-     }
 
-     if (!match) {
 
-       // Run immediately only if we have defined a grep. When we
 
-       // define a grep — It can cause maximum callstack error if
 
-       // the grep is doing a large recursive loop by neglecting
 
-       // all tests. The run immediately function also comes with
 
-       // a performance cost. So we don't want to run immediately
 
-       // if we run the whole test suite, because running the whole
 
-       // test suite don't do any immediate recursive loops. Thus,
 
-       // allowing a JS runtime to breathe.
 
-       if (self._grep !== self._defaultGrep) {
 
-         Runner.immediately(next);
 
-       } else {
 
-         next();
 
-       }
 
-       return;
 
-     }
 
-     if (test.isPending()) {
 
-       if (self.forbidPending) {
 
-         test.isPending = alwaysFalse;
 
-         self.fail(test, new Error('Pending test forbidden'));
 
-         delete test.isPending;
 
-       } else {
 
-         self.emit(constants.EVENT_TEST_PENDING, test);
 
-       }
 
-       self.emit(constants.EVENT_TEST_END, test);
 
-       return next();
 
-     }
 
-     // execute test and hook(s)
 
-     self.emit(constants.EVENT_TEST_BEGIN, (self.test = test));
 
-     self.hookDown(HOOK_TYPE_BEFORE_EACH, function(err, errSuite) {
 
-       if (test.isPending()) {
 
-         if (self.forbidPending) {
 
-           test.isPending = alwaysFalse;
 
-           self.fail(test, new Error('Pending test forbidden'));
 
-           delete test.isPending;
 
-         } else {
 
-           self.emit(constants.EVENT_TEST_PENDING, test);
 
-         }
 
-         self.emit(constants.EVENT_TEST_END, test);
 
-         return next();
 
-       }
 
-       if (err) {
 
-         return hookErr(err, errSuite, false);
 
-       }
 
-       self.currentRunnable = self.test;
 
-       self.runTest(function(err) {
 
-         test = self.test;
 
-         if (err) {
 
-           var retry = test.currentRetry();
 
-           if (err instanceof Pending && self.forbidPending) {
 
-             self.fail(test, new Error('Pending test forbidden'));
 
-           } else if (err instanceof Pending) {
 
-             test.pending = true;
 
-             self.emit(constants.EVENT_TEST_PENDING, test);
 
-           } else if (retry < test.retries()) {
 
-             var clonedTest = test.clone();
 
-             clonedTest.currentRetry(retry + 1);
 
-             tests.unshift(clonedTest);
 
-             self.emit(constants.EVENT_TEST_RETRY, test, err);
 
-             // Early return + hook trigger so that it doesn't
 
-             // increment the count wrong
 
-             return self.hookUp(HOOK_TYPE_AFTER_EACH, next);
 
-           } else {
 
-             self.fail(test, err);
 
-           }
 
-           self.emit(constants.EVENT_TEST_END, test);
 
-           if (err instanceof Pending) {
 
-             return next();
 
-           }
 
-           return self.hookUp(HOOK_TYPE_AFTER_EACH, next);
 
-         }
 
-         test.state = STATE_PASSED;
 
-         self.emit(constants.EVENT_TEST_PASS, test);
 
-         self.emit(constants.EVENT_TEST_END, test);
 
-         self.hookUp(HOOK_TYPE_AFTER_EACH, next);
 
-       });
 
-     });
 
-   }
 
-   this.next = next;
 
-   this.hookErr = hookErr;
 
-   next();
 
- };
 
- function alwaysFalse() {
 
-   return false;
 
- }
 
- /**
 
-  * Run the given `suite` and invoke the callback `fn()` when complete.
 
-  *
 
-  * @private
 
-  * @param {Suite} suite
 
-  * @param {Function} fn
 
-  */
 
- Runner.prototype.runSuite = function(suite, fn) {
 
-   var i = 0;
 
-   var self = this;
 
-   var total = this.grepTotal(suite);
 
-   var afterAllHookCalled = false;
 
-   debug('run suite %s', suite.fullTitle());
 
-   if (!total || (self.failures && suite._bail)) {
 
-     return fn();
 
-   }
 
-   this.emit(constants.EVENT_SUITE_BEGIN, (this.suite = suite));
 
-   function next(errSuite) {
 
-     if (errSuite) {
 
-       // current suite failed on a hook from errSuite
 
-       if (errSuite === suite) {
 
-         // if errSuite is current suite
 
-         // continue to the next sibling suite
 
-         return done();
 
-       }
 
-       // errSuite is among the parents of current suite
 
-       // stop execution of errSuite and all sub-suites
 
-       return done(errSuite);
 
-     }
 
-     if (self._abort) {
 
-       return done();
 
-     }
 
-     var curr = suite.suites[i++];
 
-     if (!curr) {
 
-       return done();
 
-     }
 
-     // Avoid grep neglecting large number of tests causing a
 
-     // huge recursive loop and thus a maximum call stack error.
 
-     // See comment in `this.runTests()` for more information.
 
-     if (self._grep !== self._defaultGrep) {
 
-       Runner.immediately(function() {
 
-         self.runSuite(curr, next);
 
-       });
 
-     } else {
 
-       self.runSuite(curr, next);
 
-     }
 
-   }
 
-   function done(errSuite) {
 
-     self.suite = suite;
 
-     self.nextSuite = next;
 
-     if (afterAllHookCalled) {
 
-       fn(errSuite);
 
-     } else {
 
-       // mark that the afterAll block has been called once
 
-       // and so can be skipped if there is an error in it.
 
-       afterAllHookCalled = true;
 
-       // remove reference to test
 
-       delete self.test;
 
-       self.hook(HOOK_TYPE_AFTER_ALL, function() {
 
-         self.emit(constants.EVENT_SUITE_END, suite);
 
-         fn(errSuite);
 
-       });
 
-     }
 
-   }
 
-   this.nextSuite = next;
 
-   this.hook(HOOK_TYPE_BEFORE_ALL, function(err) {
 
-     if (err) {
 
-       return done();
 
-     }
 
-     self.runTests(suite, next);
 
-   });
 
- };
 
- /**
 
-  * Handle uncaught exceptions.
 
-  *
 
-  * @param {Error} err
 
-  * @private
 
-  */
 
- Runner.prototype.uncaught = function(err) {
 
-   if (err) {
 
-     debug('uncaught exception %O', err);
 
-   } else {
 
-     debug('uncaught undefined/falsy exception');
 
-     err = createInvalidExceptionError(
 
-       'Caught falsy/undefined exception which would otherwise be uncaught. No stack trace found; try a debugger',
 
-       err
 
-     );
 
-   }
 
-   if (!isError(err)) {
 
-     err = thrown2Error(err);
 
-   }
 
-   err.uncaught = true;
 
-   var runnable = this.currentRunnable;
 
-   if (!runnable) {
 
-     runnable = new Runnable('Uncaught error outside test suite');
 
-     runnable.parent = this.suite;
 
-     if (this.started) {
 
-       this.fail(runnable, err);
 
-     } else {
 
-       // Can't recover from this failure
 
-       this.emit(constants.EVENT_RUN_BEGIN);
 
-       this.fail(runnable, err);
 
-       this.emit(constants.EVENT_RUN_END);
 
-     }
 
-     return;
 
-   }
 
-   runnable.clearTimeout();
 
-   // Ignore errors if already failed or pending
 
-   // See #3226
 
-   if (runnable.isFailed() || runnable.isPending()) {
 
-     return;
 
-   }
 
-   // we cannot recover gracefully if a Runnable has already passed
 
-   // then fails asynchronously
 
-   var alreadyPassed = runnable.isPassed();
 
-   // this will change the state to "failed" regardless of the current value
 
-   this.fail(runnable, err);
 
-   if (!alreadyPassed) {
 
-     // recover from test
 
-     if (runnable.type === constants.EVENT_TEST_BEGIN) {
 
-       this.emit(constants.EVENT_TEST_END, runnable);
 
-       this.hookUp(HOOK_TYPE_AFTER_EACH, this.next);
 
-       return;
 
-     }
 
-     debug(runnable);
 
-     // recover from hooks
 
-     var errSuite = this.suite;
 
-     // XXX how about a less awful way to determine this?
 
-     // if hook failure is in afterEach block
 
-     if (runnable.fullTitle().indexOf('after each') > -1) {
 
-       return this.hookErr(err, errSuite, true);
 
-     }
 
-     // if hook failure is in beforeEach block
 
-     if (runnable.fullTitle().indexOf('before each') > -1) {
 
-       return this.hookErr(err, errSuite, false);
 
-     }
 
-     // if hook failure is in after or before blocks
 
-     return this.nextSuite(errSuite);
 
-   }
 
-   // bail
 
-   this.emit(constants.EVENT_RUN_END);
 
- };
 
- /**
 
-  * Run the root suite and invoke `fn(failures)`
 
-  * on completion.
 
-  *
 
-  * @public
 
-  * @memberof Runner
 
-  * @param {Function} fn
 
-  * @return {Runner} Runner instance.
 
-  */
 
- Runner.prototype.run = function(fn) {
 
-   var self = this;
 
-   var rootSuite = this.suite;
 
-   fn = fn || function() {};
 
-   function uncaught(err) {
 
-     self.uncaught(err);
 
-   }
 
-   function start() {
 
-     // If there is an `only` filter
 
-     if (rootSuite.hasOnly()) {
 
-       rootSuite.filterOnly();
 
-     }
 
-     self.started = true;
 
-     if (self._delay) {
 
-       self.emit(constants.EVENT_DELAY_END);
 
-     }
 
-     self.emit(constants.EVENT_RUN_BEGIN);
 
-     self.runSuite(rootSuite, function() {
 
-       debug('finished running');
 
-       self.emit(constants.EVENT_RUN_END);
 
-     });
 
-   }
 
-   debug(constants.EVENT_RUN_BEGIN);
 
-   // references cleanup to avoid memory leaks
 
-   this.on(constants.EVENT_SUITE_END, function(suite) {
 
-     suite.cleanReferences();
 
-   });
 
-   // callback
 
-   this.on(constants.EVENT_RUN_END, function() {
 
-     debug(constants.EVENT_RUN_END);
 
-     process.removeListener('uncaughtException', uncaught);
 
-     fn(self.failures);
 
-   });
 
-   // uncaught exception
 
-   process.on('uncaughtException', uncaught);
 
-   if (this._delay) {
 
-     // for reporters, I guess.
 
-     // might be nice to debounce some dots while we wait.
 
-     this.emit(constants.EVENT_DELAY_BEGIN, rootSuite);
 
-     rootSuite.once(EVENT_ROOT_SUITE_RUN, start);
 
-   } else {
 
-     start();
 
-   }
 
-   return this;
 
- };
 
- /**
 
-  * Cleanly abort execution.
 
-  *
 
-  * @memberof Runner
 
-  * @public
 
-  * @return {Runner} Runner instance.
 
-  */
 
- Runner.prototype.abort = function() {
 
-   debug('aborting');
 
-   this._abort = true;
 
-   return this;
 
- };
 
- /**
 
-  * Filter leaks with the given globals flagged as `ok`.
 
-  *
 
-  * @private
 
-  * @param {Array} ok
 
-  * @param {Array} globals
 
-  * @return {Array}
 
-  */
 
- function filterLeaks(ok, globals) {
 
-   return globals.filter(function(key) {
 
-     // Firefox and Chrome exposes iframes as index inside the window object
 
-     if (/^\d+/.test(key)) {
 
-       return false;
 
-     }
 
-     // in firefox
 
-     // if runner runs in an iframe, this iframe's window.getInterface method
 
-     // not init at first it is assigned in some seconds
 
-     if (global.navigator && /^getInterface/.test(key)) {
 
-       return false;
 
-     }
 
-     // an iframe could be approached by window[iframeIndex]
 
-     // in ie6,7,8 and opera, iframeIndex is enumerable, this could cause leak
 
-     if (global.navigator && /^\d+/.test(key)) {
 
-       return false;
 
-     }
 
-     // Opera and IE expose global variables for HTML element IDs (issue #243)
 
-     if (/^mocha-/.test(key)) {
 
-       return false;
 
-     }
 
-     var matched = ok.filter(function(ok) {
 
-       if (~ok.indexOf('*')) {
 
-         return key.indexOf(ok.split('*')[0]) === 0;
 
-       }
 
-       return key === ok;
 
-     });
 
-     return !matched.length && (!global.navigator || key !== 'onerror');
 
-   });
 
- }
 
- /**
 
-  * 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.
 
-  *
 
-  * @return {Array}
 
-  * @deprecated
 
-  * @todo remove; long since unsupported
 
-  * @private
 
-  */
 
- function extraGlobals() {
 
-   if (typeof process === 'object' && typeof process.version === 'string') {
 
-     var parts = process.version.split('.');
 
-     var nodeVersion = parts.reduce(function(a, v) {
 
-       return (a << 8) | v;
 
-     });
 
-     // 'errno' was renamed to process._errno in v0.9.11.
 
-     if (nodeVersion < 0x00090b) {
 
-       return ['errno'];
 
-     }
 
-   }
 
-   return [];
 
- }
 
- Runner.constants = constants;
 
- /**
 
-  * Node.js' `EventEmitter`
 
-  * @external EventEmitter
 
-  * @see {@link https://nodejs.org/api/events.html#events_class_eventemitter}
 
-  */
 
 
  |