runner.js 23 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009
  1. 'use strict';
  2. /**
  3. * @module Runner
  4. */
  5. /**
  6. * Module dependencies.
  7. */
  8. var EventEmitter = require('events').EventEmitter;
  9. var Pending = require('./pending');
  10. var utils = require('./utils');
  11. var inherits = utils.inherits;
  12. var debug = require('debug')('mocha:runner');
  13. var Runnable = require('./runnable');
  14. var stackFilter = utils.stackTraceFilter();
  15. var stringify = utils.stringify;
  16. var type = utils.type;
  17. var undefinedError = utils.undefinedError;
  18. /**
  19. * Non-enumerable globals.
  20. */
  21. var globals = [
  22. 'setTimeout',
  23. 'clearTimeout',
  24. 'setInterval',
  25. 'clearInterval',
  26. 'XMLHttpRequest',
  27. 'Date',
  28. 'setImmediate',
  29. 'clearImmediate'
  30. ];
  31. /**
  32. * Expose `Runner`.
  33. */
  34. module.exports = Runner;
  35. /**
  36. * Initialize a `Runner` for the given `suite`. Derived from [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter)
  37. *
  38. * Events:
  39. *
  40. * - `start` execution started
  41. * - `end` execution complete
  42. * - `suite` (suite) test suite execution started
  43. * - `suite end` (suite) all tests (and sub-suites) have finished
  44. * - `test` (test) test execution started
  45. * - `test end` (test) test completed
  46. * - `hook` (hook) hook execution started
  47. * - `hook end` (hook) hook complete
  48. * - `pass` (test) test passed
  49. * - `fail` (test, err) test failed
  50. * - `pending` (test) test pending
  51. *
  52. * @memberof Mocha
  53. * @public
  54. * @class
  55. * @api public
  56. * @param {Suite} [suite] Root suite
  57. * @param {boolean} [delay] Whether or not to delay execution of root suite
  58. * until ready.
  59. */
  60. function Runner(suite, delay) {
  61. var self = this;
  62. this._globals = [];
  63. this._abort = false;
  64. this._delay = delay;
  65. this.suite = suite;
  66. this.started = false;
  67. this.total = suite.total();
  68. this.failures = 0;
  69. this.on('test end', function(test) {
  70. self.checkGlobals(test);
  71. });
  72. this.on('hook end', function(hook) {
  73. self.checkGlobals(hook);
  74. });
  75. this._defaultGrep = /.*/;
  76. this.grep(this._defaultGrep);
  77. this.globals(this.globalProps().concat(extraGlobals()));
  78. }
  79. /**
  80. * Wrapper for setImmediate, process.nextTick, or browser polyfill.
  81. *
  82. * @param {Function} fn
  83. * @api private
  84. */
  85. Runner.immediately = global.setImmediate || process.nextTick;
  86. /**
  87. * Inherit from `EventEmitter.prototype`.
  88. */
  89. inherits(Runner, EventEmitter);
  90. /**
  91. * Run tests with full titles matching `re`. Updates runner.total
  92. * with number of tests matched.
  93. *
  94. * @api public
  95. * @public
  96. * @memberof Mocha.Runner
  97. * @param {RegExp} re
  98. * @param {boolean} invert
  99. * @return {Runner} Runner instance.
  100. */
  101. Runner.prototype.grep = function(re, invert) {
  102. debug('grep %s', re);
  103. this._grep = re;
  104. this._invert = invert;
  105. this.total = this.grepTotal(this.suite);
  106. return this;
  107. };
  108. /**
  109. * Returns the number of tests matching the grep search for the
  110. * given suite.
  111. *
  112. * @memberof Mocha.Runner
  113. * @api public
  114. * @public
  115. * @param {Suite} suite
  116. * @return {number}
  117. */
  118. Runner.prototype.grepTotal = function(suite) {
  119. var self = this;
  120. var total = 0;
  121. suite.eachTest(function(test) {
  122. var match = self._grep.test(test.fullTitle());
  123. if (self._invert) {
  124. match = !match;
  125. }
  126. if (match) {
  127. total++;
  128. }
  129. });
  130. return total;
  131. };
  132. /**
  133. * Return a list of global properties.
  134. *
  135. * @return {Array}
  136. * @api private
  137. */
  138. Runner.prototype.globalProps = function() {
  139. var props = Object.keys(global);
  140. // non-enumerables
  141. for (var i = 0; i < globals.length; ++i) {
  142. if (~props.indexOf(globals[i])) {
  143. continue;
  144. }
  145. props.push(globals[i]);
  146. }
  147. return props;
  148. };
  149. /**
  150. * Allow the given `arr` of globals.
  151. *
  152. * @api public
  153. * @public
  154. * @memberof Mocha.Runner
  155. * @param {Array} arr
  156. * @return {Runner} Runner instance.
  157. */
  158. Runner.prototype.globals = function(arr) {
  159. if (!arguments.length) {
  160. return this._globals;
  161. }
  162. debug('globals %j', arr);
  163. this._globals = this._globals.concat(arr);
  164. return this;
  165. };
  166. /**
  167. * Check for global variable leaks.
  168. *
  169. * @api private
  170. */
  171. Runner.prototype.checkGlobals = function(test) {
  172. if (this.ignoreLeaks) {
  173. return;
  174. }
  175. var ok = this._globals;
  176. var globals = this.globalProps();
  177. var leaks;
  178. if (test) {
  179. ok = ok.concat(test._allowedGlobals || []);
  180. }
  181. if (this.prevGlobalsLength === globals.length) {
  182. return;
  183. }
  184. this.prevGlobalsLength = globals.length;
  185. leaks = filterLeaks(ok, globals);
  186. this._globals = this._globals.concat(leaks);
  187. if (leaks.length > 1) {
  188. this.fail(
  189. test,
  190. new Error('global leaks detected: ' + leaks.join(', ') + '')
  191. );
  192. } else if (leaks.length) {
  193. this.fail(test, new Error('global leak detected: ' + leaks[0]));
  194. }
  195. };
  196. /**
  197. * Fail the given `test`.
  198. *
  199. * @api private
  200. * @param {Test} test
  201. * @param {Error} err
  202. */
  203. Runner.prototype.fail = function(test, err) {
  204. if (test.isPending()) {
  205. return;
  206. }
  207. ++this.failures;
  208. test.state = 'failed';
  209. if (!(err instanceof Error || (err && typeof err.message === 'string'))) {
  210. err = new Error(
  211. 'the ' +
  212. type(err) +
  213. ' ' +
  214. stringify(err) +
  215. ' was thrown, throw an Error :)'
  216. );
  217. }
  218. try {
  219. err.stack =
  220. this.fullStackTrace || !err.stack ? err.stack : stackFilter(err.stack);
  221. } catch (ignore) {
  222. // some environments do not take kindly to monkeying with the stack
  223. }
  224. this.emit('fail', test, err);
  225. if (this.suite.bail()) {
  226. this.emit('end');
  227. }
  228. };
  229. /**
  230. * Fail the given `hook` with `err`.
  231. *
  232. * Hook failures work in the following pattern:
  233. * - If bail, then exit
  234. * - Failed `before` hook skips all tests in a suite and subsuites,
  235. * but jumps to corresponding `after` hook
  236. * - Failed `before each` hook skips remaining tests in a
  237. * suite and jumps to corresponding `after each` hook,
  238. * which is run only once
  239. * - Failed `after` hook does not alter
  240. * execution order
  241. * - Failed `after each` hook skips remaining tests in a
  242. * suite and subsuites, but executes other `after each`
  243. * hooks
  244. *
  245. * @api private
  246. * @param {Hook} hook
  247. * @param {Error} err
  248. */
  249. Runner.prototype.failHook = function(hook, err) {
  250. if (hook.ctx && hook.ctx.currentTest) {
  251. hook.originalTitle = hook.originalTitle || hook.title;
  252. hook.title =
  253. hook.originalTitle + ' for "' + hook.ctx.currentTest.title + '"';
  254. }
  255. this.fail(hook, err);
  256. };
  257. /**
  258. * Run hook `name` callbacks and then invoke `fn()`.
  259. *
  260. * @api private
  261. * @param {string} name
  262. * @param {Function} fn
  263. */
  264. Runner.prototype.hook = function(name, fn) {
  265. var suite = this.suite;
  266. var hooks = suite['_' + name];
  267. var self = this;
  268. function next(i) {
  269. var hook = hooks[i];
  270. if (!hook) {
  271. return fn();
  272. }
  273. self.currentRunnable = hook;
  274. hook.ctx.currentTest = self.test;
  275. self.emit('hook', hook);
  276. if (!hook.listeners('error').length) {
  277. hook.on('error', function(err) {
  278. self.failHook(hook, err);
  279. });
  280. }
  281. hook.run(function(err) {
  282. var testError = hook.error();
  283. if (testError) {
  284. self.fail(self.test, testError);
  285. }
  286. if (err) {
  287. if (err instanceof Pending) {
  288. if (name === 'beforeEach' || name === 'afterEach') {
  289. self.test.pending = true;
  290. } else {
  291. suite.tests.forEach(function(test) {
  292. test.pending = true;
  293. });
  294. // a pending hook won't be executed twice.
  295. hook.pending = true;
  296. }
  297. } else {
  298. self.failHook(hook, err);
  299. // stop executing hooks, notify callee of hook err
  300. return fn(err);
  301. }
  302. }
  303. self.emit('hook end', hook);
  304. delete hook.ctx.currentTest;
  305. next(++i);
  306. });
  307. }
  308. Runner.immediately(function() {
  309. next(0);
  310. });
  311. };
  312. /**
  313. * Run hook `name` for the given array of `suites`
  314. * in order, and callback `fn(err, errSuite)`.
  315. *
  316. * @api private
  317. * @param {string} name
  318. * @param {Array} suites
  319. * @param {Function} fn
  320. */
  321. Runner.prototype.hooks = function(name, suites, fn) {
  322. var self = this;
  323. var orig = this.suite;
  324. function next(suite) {
  325. self.suite = suite;
  326. if (!suite) {
  327. self.suite = orig;
  328. return fn();
  329. }
  330. self.hook(name, function(err) {
  331. if (err) {
  332. var errSuite = self.suite;
  333. self.suite = orig;
  334. return fn(err, errSuite);
  335. }
  336. next(suites.pop());
  337. });
  338. }
  339. next(suites.pop());
  340. };
  341. /**
  342. * Run hooks from the top level down.
  343. *
  344. * @param {String} name
  345. * @param {Function} fn
  346. * @api private
  347. */
  348. Runner.prototype.hookUp = function(name, fn) {
  349. var suites = [this.suite].concat(this.parents()).reverse();
  350. this.hooks(name, suites, fn);
  351. };
  352. /**
  353. * Run hooks from the bottom up.
  354. *
  355. * @param {String} name
  356. * @param {Function} fn
  357. * @api private
  358. */
  359. Runner.prototype.hookDown = function(name, fn) {
  360. var suites = [this.suite].concat(this.parents());
  361. this.hooks(name, suites, fn);
  362. };
  363. /**
  364. * Return an array of parent Suites from
  365. * closest to furthest.
  366. *
  367. * @return {Array}
  368. * @api private
  369. */
  370. Runner.prototype.parents = function() {
  371. var suite = this.suite;
  372. var suites = [];
  373. while (suite.parent) {
  374. suite = suite.parent;
  375. suites.push(suite);
  376. }
  377. return suites;
  378. };
  379. /**
  380. * Run the current test and callback `fn(err)`.
  381. *
  382. * @param {Function} fn
  383. * @api private
  384. */
  385. Runner.prototype.runTest = function(fn) {
  386. var self = this;
  387. var test = this.test;
  388. if (!test) {
  389. return;
  390. }
  391. if (this.forbidOnly && hasOnly(this.parents().reverse()[0] || this.suite)) {
  392. fn(new Error('`.only` forbidden'));
  393. return;
  394. }
  395. if (this.asyncOnly) {
  396. test.asyncOnly = true;
  397. }
  398. test.on('error', function(err) {
  399. self.fail(test, err);
  400. });
  401. if (this.allowUncaught) {
  402. test.allowUncaught = true;
  403. return test.run(fn);
  404. }
  405. try {
  406. test.run(fn);
  407. } catch (err) {
  408. fn(err);
  409. }
  410. };
  411. /**
  412. * Run tests in the given `suite` and invoke the callback `fn()` when complete.
  413. *
  414. * @api private
  415. * @param {Suite} suite
  416. * @param {Function} fn
  417. */
  418. Runner.prototype.runTests = function(suite, fn) {
  419. var self = this;
  420. var tests = suite.tests.slice();
  421. var test;
  422. function hookErr(_, errSuite, after) {
  423. // before/after Each hook for errSuite failed:
  424. var orig = self.suite;
  425. // for failed 'after each' hook start from errSuite parent,
  426. // otherwise start from errSuite itself
  427. self.suite = after ? errSuite.parent : errSuite;
  428. if (self.suite) {
  429. // call hookUp afterEach
  430. self.hookUp('afterEach', function(err2, errSuite2) {
  431. self.suite = orig;
  432. // some hooks may fail even now
  433. if (err2) {
  434. return hookErr(err2, errSuite2, true);
  435. }
  436. // report error suite
  437. fn(errSuite);
  438. });
  439. } else {
  440. // there is no need calling other 'after each' hooks
  441. self.suite = orig;
  442. fn(errSuite);
  443. }
  444. }
  445. function next(err, errSuite) {
  446. // if we bail after first err
  447. if (self.failures && suite._bail) {
  448. return fn();
  449. }
  450. if (self._abort) {
  451. return fn();
  452. }
  453. if (err) {
  454. return hookErr(err, errSuite, true);
  455. }
  456. // next test
  457. test = tests.shift();
  458. // all done
  459. if (!test) {
  460. return fn();
  461. }
  462. // grep
  463. var match = self._grep.test(test.fullTitle());
  464. if (self._invert) {
  465. match = !match;
  466. }
  467. if (!match) {
  468. // Run immediately only if we have defined a grep. When we
  469. // define a grep — It can cause maximum callstack error if
  470. // the grep is doing a large recursive loop by neglecting
  471. // all tests. The run immediately function also comes with
  472. // a performance cost. So we don't want to run immediately
  473. // if we run the whole test suite, because running the whole
  474. // test suite don't do any immediate recursive loops. Thus,
  475. // allowing a JS runtime to breathe.
  476. if (self._grep !== self._defaultGrep) {
  477. Runner.immediately(next);
  478. } else {
  479. next();
  480. }
  481. return;
  482. }
  483. if (test.isPending()) {
  484. if (self.forbidPending) {
  485. test.isPending = alwaysFalse;
  486. self.fail(test, new Error('Pending test forbidden'));
  487. delete test.isPending;
  488. } else {
  489. self.emit('pending', test);
  490. }
  491. self.emit('test end', test);
  492. return next();
  493. }
  494. // execute test and hook(s)
  495. self.emit('test', (self.test = test));
  496. self.hookDown('beforeEach', function(err, errSuite) {
  497. if (test.isPending()) {
  498. if (self.forbidPending) {
  499. test.isPending = alwaysFalse;
  500. self.fail(test, new Error('Pending test forbidden'));
  501. delete test.isPending;
  502. } else {
  503. self.emit('pending', test);
  504. }
  505. self.emit('test end', test);
  506. return next();
  507. }
  508. if (err) {
  509. return hookErr(err, errSuite, false);
  510. }
  511. self.currentRunnable = self.test;
  512. self.runTest(function(err) {
  513. test = self.test;
  514. if (err) {
  515. var retry = test.currentRetry();
  516. if (err instanceof Pending && self.forbidPending) {
  517. self.fail(test, new Error('Pending test forbidden'));
  518. } else if (err instanceof Pending) {
  519. test.pending = true;
  520. self.emit('pending', test);
  521. } else if (retry < test.retries()) {
  522. var clonedTest = test.clone();
  523. clonedTest.currentRetry(retry + 1);
  524. tests.unshift(clonedTest);
  525. // Early return + hook trigger so that it doesn't
  526. // increment the count wrong
  527. return self.hookUp('afterEach', next);
  528. } else {
  529. self.fail(test, err);
  530. }
  531. self.emit('test end', test);
  532. if (err instanceof Pending) {
  533. return next();
  534. }
  535. return self.hookUp('afterEach', next);
  536. }
  537. test.state = 'passed';
  538. self.emit('pass', test);
  539. self.emit('test end', test);
  540. self.hookUp('afterEach', next);
  541. });
  542. });
  543. }
  544. this.next = next;
  545. this.hookErr = hookErr;
  546. next();
  547. };
  548. function alwaysFalse() {
  549. return false;
  550. }
  551. /**
  552. * Run the given `suite` and invoke the callback `fn()` when complete.
  553. *
  554. * @api private
  555. * @param {Suite} suite
  556. * @param {Function} fn
  557. */
  558. Runner.prototype.runSuite = function(suite, fn) {
  559. var i = 0;
  560. var self = this;
  561. var total = this.grepTotal(suite);
  562. var afterAllHookCalled = false;
  563. debug('run suite %s', suite.fullTitle());
  564. if (!total || (self.failures && suite._bail)) {
  565. return fn();
  566. }
  567. this.emit('suite', (this.suite = suite));
  568. function next(errSuite) {
  569. if (errSuite) {
  570. // current suite failed on a hook from errSuite
  571. if (errSuite === suite) {
  572. // if errSuite is current suite
  573. // continue to the next sibling suite
  574. return done();
  575. }
  576. // errSuite is among the parents of current suite
  577. // stop execution of errSuite and all sub-suites
  578. return done(errSuite);
  579. }
  580. if (self._abort) {
  581. return done();
  582. }
  583. var curr = suite.suites[i++];
  584. if (!curr) {
  585. return done();
  586. }
  587. // Avoid grep neglecting large number of tests causing a
  588. // huge recursive loop and thus a maximum call stack error.
  589. // See comment in `this.runTests()` for more information.
  590. if (self._grep !== self._defaultGrep) {
  591. Runner.immediately(function() {
  592. self.runSuite(curr, next);
  593. });
  594. } else {
  595. self.runSuite(curr, next);
  596. }
  597. }
  598. function done(errSuite) {
  599. self.suite = suite;
  600. self.nextSuite = next;
  601. if (afterAllHookCalled) {
  602. fn(errSuite);
  603. } else {
  604. // mark that the afterAll block has been called once
  605. // and so can be skipped if there is an error in it.
  606. afterAllHookCalled = true;
  607. // remove reference to test
  608. delete self.test;
  609. self.hook('afterAll', function() {
  610. self.emit('suite end', suite);
  611. fn(errSuite);
  612. });
  613. }
  614. }
  615. this.nextSuite = next;
  616. this.hook('beforeAll', function(err) {
  617. if (err) {
  618. return done();
  619. }
  620. self.runTests(suite, next);
  621. });
  622. };
  623. /**
  624. * Handle uncaught exceptions.
  625. *
  626. * @param {Error} err
  627. * @api private
  628. */
  629. Runner.prototype.uncaught = function(err) {
  630. if (err) {
  631. debug(
  632. 'uncaught exception %s',
  633. err ===
  634. function() {
  635. return this;
  636. }.call(err)
  637. ? err.message || err
  638. : err
  639. );
  640. } else {
  641. debug('uncaught undefined exception');
  642. err = undefinedError();
  643. }
  644. err.uncaught = true;
  645. var runnable = this.currentRunnable;
  646. if (!runnable) {
  647. runnable = new Runnable('Uncaught error outside test suite');
  648. runnable.parent = this.suite;
  649. if (this.started) {
  650. this.fail(runnable, err);
  651. } else {
  652. // Can't recover from this failure
  653. this.emit('start');
  654. this.fail(runnable, err);
  655. this.emit('end');
  656. }
  657. return;
  658. }
  659. runnable.clearTimeout();
  660. // Ignore errors if already failed or pending
  661. // See #3226
  662. if (runnable.isFailed() || runnable.isPending()) {
  663. return;
  664. }
  665. // we cannot recover gracefully if a Runnable has already passed
  666. // then fails asynchronously
  667. var alreadyPassed = runnable.isPassed();
  668. // this will change the state to "failed" regardless of the current value
  669. this.fail(runnable, err);
  670. if (!alreadyPassed) {
  671. // recover from test
  672. if (runnable.type === 'test') {
  673. this.emit('test end', runnable);
  674. this.hookUp('afterEach', this.next);
  675. return;
  676. }
  677. // recover from hooks
  678. var errSuite = this.suite;
  679. // if hook failure is in afterEach block
  680. if (runnable.fullTitle().indexOf('after each') > -1) {
  681. return this.hookErr(err, errSuite, true);
  682. }
  683. // if hook failure is in beforeEach block
  684. if (runnable.fullTitle().indexOf('before each') > -1) {
  685. return this.hookErr(err, errSuite, false);
  686. }
  687. // if hook failure is in after or before blocks
  688. return this.nextSuite(errSuite);
  689. }
  690. // bail
  691. this.emit('end');
  692. };
  693. /**
  694. * Cleans up the references to all the deferred functions
  695. * (before/after/beforeEach/afterEach) and tests of a Suite.
  696. * These must be deleted otherwise a memory leak can happen,
  697. * as those functions may reference variables from closures,
  698. * thus those variables can never be garbage collected as long
  699. * as the deferred functions exist.
  700. *
  701. * @param {Suite} suite
  702. */
  703. function cleanSuiteReferences(suite) {
  704. function cleanArrReferences(arr) {
  705. for (var i = 0; i < arr.length; i++) {
  706. delete arr[i].fn;
  707. }
  708. }
  709. if (Array.isArray(suite._beforeAll)) {
  710. cleanArrReferences(suite._beforeAll);
  711. }
  712. if (Array.isArray(suite._beforeEach)) {
  713. cleanArrReferences(suite._beforeEach);
  714. }
  715. if (Array.isArray(suite._afterAll)) {
  716. cleanArrReferences(suite._afterAll);
  717. }
  718. if (Array.isArray(suite._afterEach)) {
  719. cleanArrReferences(suite._afterEach);
  720. }
  721. for (var i = 0; i < suite.tests.length; i++) {
  722. delete suite.tests[i].fn;
  723. }
  724. }
  725. /**
  726. * Run the root suite and invoke `fn(failures)`
  727. * on completion.
  728. *
  729. * @api public
  730. * @public
  731. * @memberof Mocha.Runner
  732. * @param {Function} fn
  733. * @return {Runner} Runner instance.
  734. */
  735. Runner.prototype.run = function(fn) {
  736. var self = this;
  737. var rootSuite = this.suite;
  738. fn = fn || function() {};
  739. function uncaught(err) {
  740. self.uncaught(err);
  741. }
  742. function start() {
  743. // If there is an `only` filter
  744. if (hasOnly(rootSuite)) {
  745. filterOnly(rootSuite);
  746. }
  747. self.started = true;
  748. self.emit('start');
  749. self.runSuite(rootSuite, function() {
  750. debug('finished running');
  751. self.emit('end');
  752. });
  753. }
  754. debug('start');
  755. // references cleanup to avoid memory leaks
  756. this.on('suite end', cleanSuiteReferences);
  757. // callback
  758. this.on('end', function() {
  759. debug('end');
  760. process.removeListener('uncaughtException', uncaught);
  761. fn(self.failures);
  762. });
  763. // uncaught exception
  764. process.on('uncaughtException', uncaught);
  765. if (this._delay) {
  766. // for reporters, I guess.
  767. // might be nice to debounce some dots while we wait.
  768. this.emit('waiting', rootSuite);
  769. rootSuite.once('run', start);
  770. } else {
  771. start();
  772. }
  773. return this;
  774. };
  775. /**
  776. * Cleanly abort execution.
  777. *
  778. * @memberof Mocha.Runner
  779. * @public
  780. * @api public
  781. * @return {Runner} Runner instance.
  782. */
  783. Runner.prototype.abort = function() {
  784. debug('aborting');
  785. this._abort = true;
  786. return this;
  787. };
  788. /**
  789. * Filter suites based on `isOnly` logic.
  790. *
  791. * @param {Array} suite
  792. * @returns {Boolean}
  793. * @api private
  794. */
  795. function filterOnly(suite) {
  796. if (suite._onlyTests.length) {
  797. // If the suite contains `only` tests, run those and ignore any nested suites.
  798. suite.tests = suite._onlyTests;
  799. suite.suites = [];
  800. } else {
  801. // Otherwise, do not run any of the tests in this suite.
  802. suite.tests = [];
  803. suite._onlySuites.forEach(function(onlySuite) {
  804. // If there are other `only` tests/suites nested in the current `only` suite, then filter that `only` suite.
  805. // Otherwise, all of the tests on this `only` suite should be run, so don't filter it.
  806. if (hasOnly(onlySuite)) {
  807. filterOnly(onlySuite);
  808. }
  809. });
  810. // Run the `only` suites, as well as any other suites that have `only` tests/suites as descendants.
  811. suite.suites = suite.suites.filter(function(childSuite) {
  812. return (
  813. suite._onlySuites.indexOf(childSuite) !== -1 || filterOnly(childSuite)
  814. );
  815. });
  816. }
  817. // Keep the suite only if there is something to run
  818. return suite.tests.length || suite.suites.length;
  819. }
  820. /**
  821. * Determines whether a suite has an `only` test or suite as a descendant.
  822. *
  823. * @param {Array} suite
  824. * @returns {Boolean}
  825. * @api private
  826. */
  827. function hasOnly(suite) {
  828. return (
  829. suite._onlyTests.length ||
  830. suite._onlySuites.length ||
  831. suite.suites.some(hasOnly)
  832. );
  833. }
  834. /**
  835. * Filter leaks with the given globals flagged as `ok`.
  836. *
  837. * @api private
  838. * @param {Array} ok
  839. * @param {Array} globals
  840. * @return {Array}
  841. */
  842. function filterLeaks(ok, globals) {
  843. return globals.filter(function(key) {
  844. // Firefox and Chrome exposes iframes as index inside the window object
  845. if (/^\d+/.test(key)) {
  846. return false;
  847. }
  848. // in firefox
  849. // if runner runs in an iframe, this iframe's window.getInterface method
  850. // not init at first it is assigned in some seconds
  851. if (global.navigator && /^getInterface/.test(key)) {
  852. return false;
  853. }
  854. // an iframe could be approached by window[iframeIndex]
  855. // in ie6,7,8 and opera, iframeIndex is enumerable, this could cause leak
  856. if (global.navigator && /^\d+/.test(key)) {
  857. return false;
  858. }
  859. // Opera and IE expose global variables for HTML element IDs (issue #243)
  860. if (/^mocha-/.test(key)) {
  861. return false;
  862. }
  863. var matched = ok.filter(function(ok) {
  864. if (~ok.indexOf('*')) {
  865. return key.indexOf(ok.split('*')[0]) === 0;
  866. }
  867. return key === ok;
  868. });
  869. return !matched.length && (!global.navigator || key !== 'onerror');
  870. });
  871. }
  872. /**
  873. * Array of globals dependent on the environment.
  874. *
  875. * @return {Array}
  876. * @api private
  877. */
  878. function extraGlobals() {
  879. if (typeof process === 'object' && typeof process.version === 'string') {
  880. var parts = process.version.split('.');
  881. var nodeVersion = parts.reduce(function(a, v) {
  882. return (a << 8) | v;
  883. });
  884. // 'errno' was renamed to process._errno in v0.9.11.
  885. if (nodeVersion < 0x00090b) {
  886. return ['errno'];
  887. }
  888. }
  889. return [];
  890. }