mocha 6.1.0 → 6.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/CHANGELOG.md +1776 -1751
  2. package/LICENSE +22 -22
  3. package/README.md +105 -105
  4. package/bin/_mocha +10 -10
  5. package/bin/mocha +149 -149
  6. package/bin/options.js +10 -10
  7. package/browser-entry.js +191 -191
  8. package/index.js +3 -3
  9. package/lib/browser/growl.js +168 -168
  10. package/lib/browser/progress.js +119 -119
  11. package/lib/browser/template.html +18 -18
  12. package/lib/browser/tty.js +13 -13
  13. package/lib/cli/cli.js +69 -69
  14. package/lib/cli/commands.js +13 -13
  15. package/lib/cli/config.js +101 -101
  16. package/lib/cli/index.js +9 -9
  17. package/lib/cli/init.js +37 -37
  18. package/lib/cli/node-flags.js +86 -86
  19. package/lib/cli/one-and-dones.js +70 -70
  20. package/lib/cli/options.js +347 -347
  21. package/lib/cli/run-helpers.js +337 -337
  22. package/lib/cli/run-option-metadata.js +76 -76
  23. package/lib/cli/run.js +297 -297
  24. package/lib/context.js +101 -101
  25. package/lib/errors.js +141 -141
  26. package/lib/growl.js +136 -136
  27. package/lib/hook.js +46 -46
  28. package/lib/interfaces/bdd.js +118 -118
  29. package/lib/interfaces/common.js +191 -191
  30. package/lib/interfaces/exports.js +60 -60
  31. package/lib/interfaces/index.js +6 -6
  32. package/lib/interfaces/qunit.js +99 -99
  33. package/lib/interfaces/tdd.js +107 -107
  34. package/lib/mocha.js +843 -843
  35. package/lib/mocharc.json +10 -10
  36. package/lib/pending.js +12 -12
  37. package/lib/reporters/base.js +491 -491
  38. package/lib/reporters/doc.js +85 -85
  39. package/lib/reporters/dot.js +81 -81
  40. package/lib/reporters/html.js +390 -390
  41. package/lib/reporters/index.js +19 -19
  42. package/lib/reporters/json-stream.js +90 -90
  43. package/lib/reporters/json.js +135 -135
  44. package/lib/reporters/landing.js +108 -108
  45. package/lib/reporters/list.js +78 -78
  46. package/lib/reporters/markdown.js +112 -112
  47. package/lib/reporters/min.js +52 -52
  48. package/lib/reporters/nyan.js +276 -276
  49. package/lib/reporters/progress.js +104 -104
  50. package/lib/reporters/spec.js +99 -99
  51. package/lib/reporters/tap.js +294 -294
  52. package/lib/reporters/xunit.js +216 -216
  53. package/lib/runnable.js +496 -496
  54. package/lib/runner.js +1049 -1049
  55. package/lib/stats-collector.js +83 -83
  56. package/lib/suite.js +642 -642
  57. package/lib/test.js +51 -51
  58. package/lib/utils.js +897 -897
  59. package/mocha.css +326 -326
  60. package/mocha.js +8170 -8476
  61. package/package.json +630 -628
package/lib/runner.js CHANGED
@@ -1,1049 +1,1049 @@
1
- 'use strict';
2
-
3
- /**
4
- * Module dependencies.
5
- */
6
- var util = require('util');
7
- var EventEmitter = require('events').EventEmitter;
8
- var Pending = require('./pending');
9
- var utils = require('./utils');
10
- var inherits = utils.inherits;
11
- var debug = require('debug')('mocha:runner');
12
- var Runnable = require('./runnable');
13
- var Suite = require('./suite');
14
- var HOOK_TYPE_BEFORE_EACH = Suite.constants.HOOK_TYPE_BEFORE_EACH;
15
- var HOOK_TYPE_AFTER_EACH = Suite.constants.HOOK_TYPE_AFTER_EACH;
16
- var HOOK_TYPE_AFTER_ALL = Suite.constants.HOOK_TYPE_AFTER_ALL;
17
- var HOOK_TYPE_BEFORE_ALL = Suite.constants.HOOK_TYPE_BEFORE_ALL;
18
- var EVENT_ROOT_SUITE_RUN = Suite.constants.EVENT_ROOT_SUITE_RUN;
19
- var STATE_FAILED = Runnable.constants.STATE_FAILED;
20
- var STATE_PASSED = Runnable.constants.STATE_PASSED;
21
- var dQuote = utils.dQuote;
22
- var ngettext = utils.ngettext;
23
- var sQuote = utils.sQuote;
24
- var stackFilter = utils.stackTraceFilter();
25
- var stringify = utils.stringify;
26
- var type = utils.type;
27
- var createInvalidExceptionError = require('./errors')
28
- .createInvalidExceptionError;
29
-
30
- /**
31
- * Non-enumerable globals.
32
- * @readonly
33
- */
34
- var globals = [
35
- 'setTimeout',
36
- 'clearTimeout',
37
- 'setInterval',
38
- 'clearInterval',
39
- 'XMLHttpRequest',
40
- 'Date',
41
- 'setImmediate',
42
- 'clearImmediate'
43
- ];
44
-
45
- var constants = utils.defineConstants(
46
- /**
47
- * {@link Runner}-related constants.
48
- * @public
49
- * @memberof Runner
50
- * @readonly
51
- * @alias constants
52
- * @static
53
- * @enum {string}
54
- */
55
- {
56
- /**
57
- * Emitted when {@link Hook} execution begins
58
- */
59
- EVENT_HOOK_BEGIN: 'hook',
60
- /**
61
- * Emitted when {@link Hook} execution ends
62
- */
63
- EVENT_HOOK_END: 'hook end',
64
- /**
65
- * Emitted when Root {@link Suite} execution begins (all files have been parsed and hooks/tests are ready for execution)
66
- */
67
- EVENT_RUN_BEGIN: 'start',
68
- /**
69
- * Emitted when Root {@link Suite} execution has been delayed via `delay` option
70
- */
71
- EVENT_DELAY_BEGIN: 'waiting',
72
- /**
73
- * Emitted when delayed Root {@link Suite} execution is triggered by user via `global.run()`
74
- */
75
- EVENT_DELAY_END: 'ready',
76
- /**
77
- * Emitted when Root {@link Suite} execution ends
78
- */
79
- EVENT_RUN_END: 'end',
80
- /**
81
- * Emitted when {@link Suite} execution begins
82
- */
83
- EVENT_SUITE_BEGIN: 'suite',
84
- /**
85
- * Emitted when {@link Suite} execution ends
86
- */
87
- EVENT_SUITE_END: 'suite end',
88
- /**
89
- * Emitted when {@link Test} execution begins
90
- */
91
- EVENT_TEST_BEGIN: 'test',
92
- /**
93
- * Emitted when {@link Test} execution ends
94
- */
95
- EVENT_TEST_END: 'test end',
96
- /**
97
- * Emitted when {@link Test} execution fails
98
- */
99
- EVENT_TEST_FAIL: 'fail',
100
- /**
101
- * Emitted when {@link Test} execution succeeds
102
- */
103
- EVENT_TEST_PASS: 'pass',
104
- /**
105
- * Emitted when {@link Test} becomes pending
106
- */
107
- EVENT_TEST_PENDING: 'pending',
108
- /**
109
- * Emitted when {@link Test} execution has failed, but will retry
110
- */
111
- EVENT_TEST_RETRY: 'retry'
112
- }
113
- );
114
-
115
- module.exports = Runner;
116
-
117
- /**
118
- * Initialize a `Runner` at the Root {@link Suite}, which represents a hierarchy of {@link Suite|Suites} and {@link Test|Tests}.
119
- *
120
- * @extends external:EventEmitter
121
- * @public
122
- * @class
123
- * @param {Suite} suite Root suite
124
- * @param {boolean} [delay] Whether or not to delay execution of root suite
125
- * until ready.
126
- */
127
- function Runner(suite, delay) {
128
- var self = this;
129
- this._globals = [];
130
- this._abort = false;
131
- this._delay = delay;
132
- this.suite = suite;
133
- this.started = false;
134
- this.total = suite.total();
135
- this.failures = 0;
136
- this.on(constants.EVENT_TEST_END, function(test) {
137
- self.checkGlobals(test);
138
- });
139
- this.on(constants.EVENT_HOOK_END, function(hook) {
140
- self.checkGlobals(hook);
141
- });
142
- this._defaultGrep = /.*/;
143
- this.grep(this._defaultGrep);
144
- this.globals(this.globalProps().concat(extraGlobals()));
145
- }
146
-
147
- /**
148
- * Wrapper for setImmediate, process.nextTick, or browser polyfill.
149
- *
150
- * @param {Function} fn
151
- * @private
152
- */
153
- Runner.immediately = global.setImmediate || process.nextTick;
154
-
155
- /**
156
- * Inherit from `EventEmitter.prototype`.
157
- */
158
- inherits(Runner, EventEmitter);
159
-
160
- /**
161
- * Run tests with full titles matching `re`. Updates runner.total
162
- * with number of tests matched.
163
- *
164
- * @public
165
- * @memberof Runner
166
- * @param {RegExp} re
167
- * @param {boolean} invert
168
- * @return {Runner} Runner instance.
169
- */
170
- Runner.prototype.grep = function(re, invert) {
171
- debug('grep %s', re);
172
- this._grep = re;
173
- this._invert = invert;
174
- this.total = this.grepTotal(this.suite);
175
- return this;
176
- };
177
-
178
- /**
179
- * Returns the number of tests matching the grep search for the
180
- * given suite.
181
- *
182
- * @memberof Runner
183
- * @public
184
- * @param {Suite} suite
185
- * @return {number}
186
- */
187
- Runner.prototype.grepTotal = function(suite) {
188
- var self = this;
189
- var total = 0;
190
-
191
- suite.eachTest(function(test) {
192
- var match = self._grep.test(test.fullTitle());
193
- if (self._invert) {
194
- match = !match;
195
- }
196
- if (match) {
197
- total++;
198
- }
199
- });
200
-
201
- return total;
202
- };
203
-
204
- /**
205
- * Return a list of global properties.
206
- *
207
- * @return {Array}
208
- * @private
209
- */
210
- Runner.prototype.globalProps = function() {
211
- var props = Object.keys(global);
212
-
213
- // non-enumerables
214
- for (var i = 0; i < globals.length; ++i) {
215
- if (~props.indexOf(globals[i])) {
216
- continue;
217
- }
218
- props.push(globals[i]);
219
- }
220
-
221
- return props;
222
- };
223
-
224
- /**
225
- * Allow the given `arr` of globals.
226
- *
227
- * @public
228
- * @memberof Runner
229
- * @param {Array} arr
230
- * @return {Runner} Runner instance.
231
- */
232
- Runner.prototype.globals = function(arr) {
233
- if (!arguments.length) {
234
- return this._globals;
235
- }
236
- debug('globals %j', arr);
237
- this._globals = this._globals.concat(arr);
238
- return this;
239
- };
240
-
241
- /**
242
- * Check for global variable leaks.
243
- *
244
- * @private
245
- */
246
- Runner.prototype.checkGlobals = function(test) {
247
- if (this.ignoreLeaks) {
248
- return;
249
- }
250
- var ok = this._globals;
251
-
252
- var globals = this.globalProps();
253
- var leaks;
254
-
255
- if (test) {
256
- ok = ok.concat(test._allowedGlobals || []);
257
- }
258
-
259
- if (this.prevGlobalsLength === globals.length) {
260
- return;
261
- }
262
- this.prevGlobalsLength = globals.length;
263
-
264
- leaks = filterLeaks(ok, globals);
265
- this._globals = this._globals.concat(leaks);
266
-
267
- if (leaks.length) {
268
- var format = ngettext(
269
- leaks.length,
270
- 'global leak detected: %s',
271
- 'global leaks detected: %s'
272
- );
273
- var error = new Error(util.format(format, leaks.map(sQuote).join(', ')));
274
- this.fail(test, error);
275
- }
276
- };
277
-
278
- /**
279
- * Fail the given `test`.
280
- *
281
- * @private
282
- * @param {Test} test
283
- * @param {Error} err
284
- */
285
- Runner.prototype.fail = function(test, err) {
286
- if (test.isPending()) {
287
- return;
288
- }
289
-
290
- ++this.failures;
291
- test.state = STATE_FAILED;
292
-
293
- if (!isError(err)) {
294
- err = thrown2Error(err);
295
- }
296
-
297
- try {
298
- err.stack =
299
- this.fullStackTrace || !err.stack ? err.stack : stackFilter(err.stack);
300
- } catch (ignore) {
301
- // some environments do not take kindly to monkeying with the stack
302
- }
303
-
304
- this.emit(constants.EVENT_TEST_FAIL, test, err);
305
- };
306
-
307
- /**
308
- * Fail the given `hook` with `err`.
309
- *
310
- * Hook failures work in the following pattern:
311
- * - If bail, run corresponding `after each` and `after` hooks,
312
- * then exit
313
- * - Failed `before` hook skips all tests in a suite and subsuites,
314
- * but jumps to corresponding `after` hook
315
- * - Failed `before each` hook skips remaining tests in a
316
- * suite and jumps to corresponding `after each` hook,
317
- * which is run only once
318
- * - Failed `after` hook does not alter
319
- * execution order
320
- * - Failed `after each` hook skips remaining tests in a
321
- * suite and subsuites, but executes other `after each`
322
- * hooks
323
- *
324
- * @private
325
- * @param {Hook} hook
326
- * @param {Error} err
327
- */
328
- Runner.prototype.failHook = function(hook, err) {
329
- hook.originalTitle = hook.originalTitle || hook.title;
330
- if (hook.ctx && hook.ctx.currentTest) {
331
- hook.title =
332
- hook.originalTitle + ' for ' + dQuote(hook.ctx.currentTest.title);
333
- } else {
334
- var parentTitle;
335
- if (hook.parent.title) {
336
- parentTitle = hook.parent.title;
337
- } else {
338
- parentTitle = hook.parent.root ? '{root}' : '';
339
- }
340
- hook.title = hook.originalTitle + ' in ' + dQuote(parentTitle);
341
- }
342
-
343
- this.fail(hook, err);
344
- };
345
-
346
- /**
347
- * Run hook `name` callbacks and then invoke `fn()`.
348
- *
349
- * @private
350
- * @param {string} name
351
- * @param {Function} fn
352
- */
353
-
354
- Runner.prototype.hook = function(name, fn) {
355
- var suite = this.suite;
356
- var hooks = suite.getHooks(name);
357
- var self = this;
358
-
359
- function next(i) {
360
- var hook = hooks[i];
361
- if (!hook) {
362
- return fn();
363
- }
364
- self.currentRunnable = hook;
365
-
366
- if (name === HOOK_TYPE_BEFORE_ALL) {
367
- hook.ctx.currentTest = hook.parent.tests[0];
368
- } else if (name === HOOK_TYPE_AFTER_ALL) {
369
- hook.ctx.currentTest = hook.parent.tests[hook.parent.tests.length - 1];
370
- } else {
371
- hook.ctx.currentTest = self.test;
372
- }
373
-
374
- hook.allowUncaught = self.allowUncaught;
375
-
376
- self.emit(constants.EVENT_HOOK_BEGIN, hook);
377
-
378
- if (!hook.listeners('error').length) {
379
- hook.on('error', function(err) {
380
- self.failHook(hook, err);
381
- });
382
- }
383
-
384
- hook.run(function(err) {
385
- var testError = hook.error();
386
- if (testError) {
387
- self.fail(self.test, testError);
388
- }
389
- if (err) {
390
- if (err instanceof Pending) {
391
- if (name === HOOK_TYPE_AFTER_ALL) {
392
- utils.deprecate(
393
- 'Skipping a test within an "after all" hook is DEPRECATED and will throw an exception in a future version of Mocha. ' +
394
- 'Use a return statement or other means to abort hook execution.'
395
- );
396
- }
397
- if (name === HOOK_TYPE_BEFORE_EACH || name === HOOK_TYPE_AFTER_EACH) {
398
- if (self.test) {
399
- self.test.pending = true;
400
- }
401
- } else {
402
- suite.tests.forEach(function(test) {
403
- test.pending = true;
404
- });
405
- suite.suites.forEach(function(suite) {
406
- suite.pending = true;
407
- });
408
- // a pending hook won't be executed twice.
409
- hook.pending = true;
410
- }
411
- } else {
412
- self.failHook(hook, err);
413
-
414
- // stop executing hooks, notify callee of hook err
415
- return fn(err);
416
- }
417
- }
418
- self.emit(constants.EVENT_HOOK_END, hook);
419
- delete hook.ctx.currentTest;
420
- next(++i);
421
- });
422
- }
423
-
424
- Runner.immediately(function() {
425
- next(0);
426
- });
427
- };
428
-
429
- /**
430
- * Run hook `name` for the given array of `suites`
431
- * in order, and callback `fn(err, errSuite)`.
432
- *
433
- * @private
434
- * @param {string} name
435
- * @param {Array} suites
436
- * @param {Function} fn
437
- */
438
- Runner.prototype.hooks = function(name, suites, fn) {
439
- var self = this;
440
- var orig = this.suite;
441
-
442
- function next(suite) {
443
- self.suite = suite;
444
-
445
- if (!suite) {
446
- self.suite = orig;
447
- return fn();
448
- }
449
-
450
- self.hook(name, function(err) {
451
- if (err) {
452
- var errSuite = self.suite;
453
- self.suite = orig;
454
- return fn(err, errSuite);
455
- }
456
-
457
- next(suites.pop());
458
- });
459
- }
460
-
461
- next(suites.pop());
462
- };
463
-
464
- /**
465
- * Run hooks from the top level down.
466
- *
467
- * @param {String} name
468
- * @param {Function} fn
469
- * @private
470
- */
471
- Runner.prototype.hookUp = function(name, fn) {
472
- var suites = [this.suite].concat(this.parents()).reverse();
473
- this.hooks(name, suites, fn);
474
- };
475
-
476
- /**
477
- * Run hooks from the bottom up.
478
- *
479
- * @param {String} name
480
- * @param {Function} fn
481
- * @private
482
- */
483
- Runner.prototype.hookDown = function(name, fn) {
484
- var suites = [this.suite].concat(this.parents());
485
- this.hooks(name, suites, fn);
486
- };
487
-
488
- /**
489
- * Return an array of parent Suites from
490
- * closest to furthest.
491
- *
492
- * @return {Array}
493
- * @private
494
- */
495
- Runner.prototype.parents = function() {
496
- var suite = this.suite;
497
- var suites = [];
498
- while (suite.parent) {
499
- suite = suite.parent;
500
- suites.push(suite);
501
- }
502
- return suites;
503
- };
504
-
505
- /**
506
- * Run the current test and callback `fn(err)`.
507
- *
508
- * @param {Function} fn
509
- * @private
510
- */
511
- Runner.prototype.runTest = function(fn) {
512
- var self = this;
513
- var test = this.test;
514
-
515
- if (!test) {
516
- return;
517
- }
518
-
519
- var suite = this.parents().reverse()[0] || this.suite;
520
- if (this.forbidOnly && suite.hasOnly()) {
521
- fn(new Error('`.only` forbidden'));
522
- return;
523
- }
524
- if (this.asyncOnly) {
525
- test.asyncOnly = true;
526
- }
527
- test.on('error', function(err) {
528
- self.fail(test, err);
529
- });
530
- if (this.allowUncaught) {
531
- test.allowUncaught = true;
532
- return test.run(fn);
533
- }
534
- try {
535
- test.run(fn);
536
- } catch (err) {
537
- fn(err);
538
- }
539
- };
540
-
541
- /**
542
- * Run tests in the given `suite` and invoke the callback `fn()` when complete.
543
- *
544
- * @private
545
- * @param {Suite} suite
546
- * @param {Function} fn
547
- */
548
- Runner.prototype.runTests = function(suite, fn) {
549
- var self = this;
550
- var tests = suite.tests.slice();
551
- var test;
552
-
553
- function hookErr(_, errSuite, after) {
554
- // before/after Each hook for errSuite failed:
555
- var orig = self.suite;
556
-
557
- // for failed 'after each' hook start from errSuite parent,
558
- // otherwise start from errSuite itself
559
- self.suite = after ? errSuite.parent : errSuite;
560
-
561
- if (self.suite) {
562
- // call hookUp afterEach
563
- self.hookUp(HOOK_TYPE_AFTER_EACH, function(err2, errSuite2) {
564
- self.suite = orig;
565
- // some hooks may fail even now
566
- if (err2) {
567
- return hookErr(err2, errSuite2, true);
568
- }
569
- // report error suite
570
- fn(errSuite);
571
- });
572
- } else {
573
- // there is no need calling other 'after each' hooks
574
- self.suite = orig;
575
- fn(errSuite);
576
- }
577
- }
578
-
579
- function next(err, errSuite) {
580
- // if we bail after first err
581
- if (self.failures && suite._bail) {
582
- tests = [];
583
- }
584
-
585
- if (self._abort) {
586
- return fn();
587
- }
588
-
589
- if (err) {
590
- return hookErr(err, errSuite, true);
591
- }
592
-
593
- // next test
594
- test = tests.shift();
595
-
596
- // all done
597
- if (!test) {
598
- return fn();
599
- }
600
-
601
- // grep
602
- var match = self._grep.test(test.fullTitle());
603
- if (self._invert) {
604
- match = !match;
605
- }
606
- if (!match) {
607
- // Run immediately only if we have defined a grep. When we
608
- // define a grep — It can cause maximum callstack error if
609
- // the grep is doing a large recursive loop by neglecting
610
- // all tests. The run immediately function also comes with
611
- // a performance cost. So we don't want to run immediately
612
- // if we run the whole test suite, because running the whole
613
- // test suite don't do any immediate recursive loops. Thus,
614
- // allowing a JS runtime to breathe.
615
- if (self._grep !== self._defaultGrep) {
616
- Runner.immediately(next);
617
- } else {
618
- next();
619
- }
620
- return;
621
- }
622
-
623
- if (test.isPending()) {
624
- if (self.forbidPending) {
625
- test.isPending = alwaysFalse;
626
- self.fail(test, new Error('Pending test forbidden'));
627
- delete test.isPending;
628
- } else {
629
- self.emit(constants.EVENT_TEST_PENDING, test);
630
- }
631
- self.emit(constants.EVENT_TEST_END, test);
632
- return next();
633
- }
634
-
635
- // execute test and hook(s)
636
- self.emit(constants.EVENT_TEST_BEGIN, (self.test = test));
637
- self.hookDown(HOOK_TYPE_BEFORE_EACH, function(err, errSuite) {
638
- if (test.isPending()) {
639
- if (self.forbidPending) {
640
- test.isPending = alwaysFalse;
641
- self.fail(test, new Error('Pending test forbidden'));
642
- delete test.isPending;
643
- } else {
644
- self.emit(constants.EVENT_TEST_PENDING, test);
645
- }
646
- self.emit(constants.EVENT_TEST_END, test);
647
- return next();
648
- }
649
- if (err) {
650
- return hookErr(err, errSuite, false);
651
- }
652
- self.currentRunnable = self.test;
653
- self.runTest(function(err) {
654
- test = self.test;
655
- if (err) {
656
- var retry = test.currentRetry();
657
- if (err instanceof Pending && self.forbidPending) {
658
- self.fail(test, new Error('Pending test forbidden'));
659
- } else if (err instanceof Pending) {
660
- test.pending = true;
661
- self.emit(constants.EVENT_TEST_PENDING, test);
662
- } else if (retry < test.retries()) {
663
- var clonedTest = test.clone();
664
- clonedTest.currentRetry(retry + 1);
665
- tests.unshift(clonedTest);
666
-
667
- self.emit(constants.EVENT_TEST_RETRY, test, err);
668
-
669
- // Early return + hook trigger so that it doesn't
670
- // increment the count wrong
671
- return self.hookUp(HOOK_TYPE_AFTER_EACH, next);
672
- } else {
673
- self.fail(test, err);
674
- }
675
- self.emit(constants.EVENT_TEST_END, test);
676
-
677
- if (err instanceof Pending) {
678
- return next();
679
- }
680
-
681
- return self.hookUp(HOOK_TYPE_AFTER_EACH, next);
682
- }
683
-
684
- test.state = STATE_PASSED;
685
- self.emit(constants.EVENT_TEST_PASS, test);
686
- self.emit(constants.EVENT_TEST_END, test);
687
- self.hookUp(HOOK_TYPE_AFTER_EACH, next);
688
- });
689
- });
690
- }
691
-
692
- this.next = next;
693
- this.hookErr = hookErr;
694
- next();
695
- };
696
-
697
- function alwaysFalse() {
698
- return false;
699
- }
700
-
701
- /**
702
- * Run the given `suite` and invoke the callback `fn()` when complete.
703
- *
704
- * @private
705
- * @param {Suite} suite
706
- * @param {Function} fn
707
- */
708
- Runner.prototype.runSuite = function(suite, fn) {
709
- var i = 0;
710
- var self = this;
711
- var total = this.grepTotal(suite);
712
- var afterAllHookCalled = false;
713
-
714
- debug('run suite %s', suite.fullTitle());
715
-
716
- if (!total || (self.failures && suite._bail)) {
717
- return fn();
718
- }
719
-
720
- this.emit(constants.EVENT_SUITE_BEGIN, (this.suite = suite));
721
-
722
- function next(errSuite) {
723
- if (errSuite) {
724
- // current suite failed on a hook from errSuite
725
- if (errSuite === suite) {
726
- // if errSuite is current suite
727
- // continue to the next sibling suite
728
- return done();
729
- }
730
- // errSuite is among the parents of current suite
731
- // stop execution of errSuite and all sub-suites
732
- return done(errSuite);
733
- }
734
-
735
- if (self._abort) {
736
- return done();
737
- }
738
-
739
- var curr = suite.suites[i++];
740
- if (!curr) {
741
- return done();
742
- }
743
-
744
- // Avoid grep neglecting large number of tests causing a
745
- // huge recursive loop and thus a maximum call stack error.
746
- // See comment in `this.runTests()` for more information.
747
- if (self._grep !== self._defaultGrep) {
748
- Runner.immediately(function() {
749
- self.runSuite(curr, next);
750
- });
751
- } else {
752
- self.runSuite(curr, next);
753
- }
754
- }
755
-
756
- function done(errSuite) {
757
- self.suite = suite;
758
- self.nextSuite = next;
759
-
760
- if (afterAllHookCalled) {
761
- fn(errSuite);
762
- } else {
763
- // mark that the afterAll block has been called once
764
- // and so can be skipped if there is an error in it.
765
- afterAllHookCalled = true;
766
-
767
- // remove reference to test
768
- delete self.test;
769
-
770
- self.hook(HOOK_TYPE_AFTER_ALL, function() {
771
- self.emit(constants.EVENT_SUITE_END, suite);
772
- fn(errSuite);
773
- });
774
- }
775
- }
776
-
777
- this.nextSuite = next;
778
-
779
- this.hook(HOOK_TYPE_BEFORE_ALL, function(err) {
780
- if (err) {
781
- return done();
782
- }
783
- self.runTests(suite, next);
784
- });
785
- };
786
-
787
- /**
788
- * Handle uncaught exceptions.
789
- *
790
- * @param {Error} err
791
- * @private
792
- */
793
- Runner.prototype.uncaught = function(err) {
794
- if (err instanceof Pending) {
795
- return;
796
- }
797
- if (err) {
798
- debug('uncaught exception %O', err);
799
- } else {
800
- debug('uncaught undefined/falsy exception');
801
- err = createInvalidExceptionError(
802
- 'Caught falsy/undefined exception which would otherwise be uncaught. No stack trace found; try a debugger',
803
- err
804
- );
805
- }
806
-
807
- if (!isError(err)) {
808
- err = thrown2Error(err);
809
- }
810
- err.uncaught = true;
811
-
812
- var runnable = this.currentRunnable;
813
-
814
- if (!runnable) {
815
- runnable = new Runnable('Uncaught error outside test suite');
816
- runnable.parent = this.suite;
817
-
818
- if (this.started) {
819
- this.fail(runnable, err);
820
- } else {
821
- // Can't recover from this failure
822
- this.emit(constants.EVENT_RUN_BEGIN);
823
- this.fail(runnable, err);
824
- this.emit(constants.EVENT_RUN_END);
825
- }
826
-
827
- return;
828
- }
829
-
830
- runnable.clearTimeout();
831
-
832
- // Ignore errors if already failed or pending
833
- // See #3226
834
- if (runnable.isFailed() || runnable.isPending()) {
835
- return;
836
- }
837
- // we cannot recover gracefully if a Runnable has already passed
838
- // then fails asynchronously
839
- var alreadyPassed = runnable.isPassed();
840
- // this will change the state to "failed" regardless of the current value
841
- this.fail(runnable, err);
842
- if (!alreadyPassed) {
843
- // recover from test
844
- if (runnable.type === constants.EVENT_TEST_BEGIN) {
845
- this.emit(constants.EVENT_TEST_END, runnable);
846
- this.hookUp(HOOK_TYPE_AFTER_EACH, this.next);
847
- return;
848
- }
849
- debug(runnable);
850
-
851
- // recover from hooks
852
- var errSuite = this.suite;
853
-
854
- // XXX how about a less awful way to determine this?
855
- // if hook failure is in afterEach block
856
- if (runnable.fullTitle().indexOf('after each') > -1) {
857
- return this.hookErr(err, errSuite, true);
858
- }
859
- // if hook failure is in beforeEach block
860
- if (runnable.fullTitle().indexOf('before each') > -1) {
861
- return this.hookErr(err, errSuite, false);
862
- }
863
- // if hook failure is in after or before blocks
864
- return this.nextSuite(errSuite);
865
- }
866
-
867
- // bail
868
- this.emit(constants.EVENT_RUN_END);
869
- };
870
-
871
- /**
872
- * Run the root suite and invoke `fn(failures)`
873
- * on completion.
874
- *
875
- * @public
876
- * @memberof Runner
877
- * @param {Function} fn
878
- * @return {Runner} Runner instance.
879
- */
880
- Runner.prototype.run = function(fn) {
881
- var self = this;
882
- var rootSuite = this.suite;
883
-
884
- fn = fn || function() {};
885
-
886
- function uncaught(err) {
887
- self.uncaught(err);
888
- }
889
-
890
- function start() {
891
- // If there is an `only` filter
892
- if (rootSuite.hasOnly()) {
893
- rootSuite.filterOnly();
894
- }
895
- self.started = true;
896
- if (self._delay) {
897
- self.emit(constants.EVENT_DELAY_END);
898
- }
899
- self.emit(constants.EVENT_RUN_BEGIN);
900
-
901
- self.runSuite(rootSuite, function() {
902
- debug('finished running');
903
- self.emit(constants.EVENT_RUN_END);
904
- });
905
- }
906
-
907
- debug(constants.EVENT_RUN_BEGIN);
908
-
909
- // references cleanup to avoid memory leaks
910
- this.on(constants.EVENT_SUITE_END, function(suite) {
911
- suite.cleanReferences();
912
- });
913
-
914
- // callback
915
- this.on(constants.EVENT_RUN_END, function() {
916
- debug(constants.EVENT_RUN_END);
917
- process.removeListener('uncaughtException', uncaught);
918
- fn(self.failures);
919
- });
920
-
921
- // uncaught exception
922
- process.on('uncaughtException', uncaught);
923
-
924
- if (this._delay) {
925
- // for reporters, I guess.
926
- // might be nice to debounce some dots while we wait.
927
- this.emit(constants.EVENT_DELAY_BEGIN, rootSuite);
928
- rootSuite.once(EVENT_ROOT_SUITE_RUN, start);
929
- } else {
930
- start();
931
- }
932
-
933
- return this;
934
- };
935
-
936
- /**
937
- * Cleanly abort execution.
938
- *
939
- * @memberof Runner
940
- * @public
941
- * @return {Runner} Runner instance.
942
- */
943
- Runner.prototype.abort = function() {
944
- debug('aborting');
945
- this._abort = true;
946
-
947
- return this;
948
- };
949
-
950
- /**
951
- * Filter leaks with the given globals flagged as `ok`.
952
- *
953
- * @private
954
- * @param {Array} ok
955
- * @param {Array} globals
956
- * @return {Array}
957
- */
958
- function filterLeaks(ok, globals) {
959
- return globals.filter(function(key) {
960
- // Firefox and Chrome exposes iframes as index inside the window object
961
- if (/^\d+/.test(key)) {
962
- return false;
963
- }
964
-
965
- // in firefox
966
- // if runner runs in an iframe, this iframe's window.getInterface method
967
- // not init at first it is assigned in some seconds
968
- if (global.navigator && /^getInterface/.test(key)) {
969
- return false;
970
- }
971
-
972
- // an iframe could be approached by window[iframeIndex]
973
- // in ie6,7,8 and opera, iframeIndex is enumerable, this could cause leak
974
- if (global.navigator && /^\d+/.test(key)) {
975
- return false;
976
- }
977
-
978
- // Opera and IE expose global variables for HTML element IDs (issue #243)
979
- if (/^mocha-/.test(key)) {
980
- return false;
981
- }
982
-
983
- var matched = ok.filter(function(ok) {
984
- if (~ok.indexOf('*')) {
985
- return key.indexOf(ok.split('*')[0]) === 0;
986
- }
987
- return key === ok;
988
- });
989
- return !matched.length && (!global.navigator || key !== 'onerror');
990
- });
991
- }
992
-
993
- /**
994
- * Check if argument is an instance of Error object or a duck-typed equivalent.
995
- *
996
- * @private
997
- * @param {Object} err - object to check
998
- * @param {string} err.message - error message
999
- * @returns {boolean}
1000
- */
1001
- function isError(err) {
1002
- return err instanceof Error || (err && typeof err.message === 'string');
1003
- }
1004
-
1005
- /**
1006
- *
1007
- * Converts thrown non-extensible type into proper Error.
1008
- *
1009
- * @private
1010
- * @param {*} thrown - Non-extensible type thrown by code
1011
- * @return {Error}
1012
- */
1013
- function thrown2Error(err) {
1014
- return new Error(
1015
- 'the ' + type(err) + ' ' + stringify(err) + ' was thrown, throw an Error :)'
1016
- );
1017
- }
1018
-
1019
- /**
1020
- * Array of globals dependent on the environment.
1021
- *
1022
- * @return {Array}
1023
- * @deprecated
1024
- * @todo remove; long since unsupported
1025
- * @private
1026
- */
1027
- function extraGlobals() {
1028
- if (typeof process === 'object' && typeof process.version === 'string') {
1029
- var parts = process.version.split('.');
1030
- var nodeVersion = parts.reduce(function(a, v) {
1031
- return (a << 8) | v;
1032
- });
1033
-
1034
- // 'errno' was renamed to process._errno in v0.9.11.
1035
- if (nodeVersion < 0x00090b) {
1036
- return ['errno'];
1037
- }
1038
- }
1039
-
1040
- return [];
1041
- }
1042
-
1043
- Runner.constants = constants;
1044
-
1045
- /**
1046
- * Node.js' `EventEmitter`
1047
- * @external EventEmitter
1048
- * @see {@link https://nodejs.org/api/events.html#events_class_eventemitter}
1049
- */
1
+ 'use strict';
2
+
3
+ /**
4
+ * Module dependencies.
5
+ */
6
+ var util = require('util');
7
+ var EventEmitter = require('events').EventEmitter;
8
+ var Pending = require('./pending');
9
+ var utils = require('./utils');
10
+ var inherits = utils.inherits;
11
+ var debug = require('debug')('mocha:runner');
12
+ var Runnable = require('./runnable');
13
+ var Suite = require('./suite');
14
+ var HOOK_TYPE_BEFORE_EACH = Suite.constants.HOOK_TYPE_BEFORE_EACH;
15
+ var HOOK_TYPE_AFTER_EACH = Suite.constants.HOOK_TYPE_AFTER_EACH;
16
+ var HOOK_TYPE_AFTER_ALL = Suite.constants.HOOK_TYPE_AFTER_ALL;
17
+ var HOOK_TYPE_BEFORE_ALL = Suite.constants.HOOK_TYPE_BEFORE_ALL;
18
+ var EVENT_ROOT_SUITE_RUN = Suite.constants.EVENT_ROOT_SUITE_RUN;
19
+ var STATE_FAILED = Runnable.constants.STATE_FAILED;
20
+ var STATE_PASSED = Runnable.constants.STATE_PASSED;
21
+ var dQuote = utils.dQuote;
22
+ var ngettext = utils.ngettext;
23
+ var sQuote = utils.sQuote;
24
+ var stackFilter = utils.stackTraceFilter();
25
+ var stringify = utils.stringify;
26
+ var type = utils.type;
27
+ var createInvalidExceptionError = require('./errors')
28
+ .createInvalidExceptionError;
29
+
30
+ /**
31
+ * Non-enumerable globals.
32
+ * @readonly
33
+ */
34
+ var globals = [
35
+ 'setTimeout',
36
+ 'clearTimeout',
37
+ 'setInterval',
38
+ 'clearInterval',
39
+ 'XMLHttpRequest',
40
+ 'Date',
41
+ 'setImmediate',
42
+ 'clearImmediate'
43
+ ];
44
+
45
+ var constants = utils.defineConstants(
46
+ /**
47
+ * {@link Runner}-related constants.
48
+ * @public
49
+ * @memberof Runner
50
+ * @readonly
51
+ * @alias constants
52
+ * @static
53
+ * @enum {string}
54
+ */
55
+ {
56
+ /**
57
+ * Emitted when {@link Hook} execution begins
58
+ */
59
+ EVENT_HOOK_BEGIN: 'hook',
60
+ /**
61
+ * Emitted when {@link Hook} execution ends
62
+ */
63
+ EVENT_HOOK_END: 'hook end',
64
+ /**
65
+ * Emitted when Root {@link Suite} execution begins (all files have been parsed and hooks/tests are ready for execution)
66
+ */
67
+ EVENT_RUN_BEGIN: 'start',
68
+ /**
69
+ * Emitted when Root {@link Suite} execution has been delayed via `delay` option
70
+ */
71
+ EVENT_DELAY_BEGIN: 'waiting',
72
+ /**
73
+ * Emitted when delayed Root {@link Suite} execution is triggered by user via `global.run()`
74
+ */
75
+ EVENT_DELAY_END: 'ready',
76
+ /**
77
+ * Emitted when Root {@link Suite} execution ends
78
+ */
79
+ EVENT_RUN_END: 'end',
80
+ /**
81
+ * Emitted when {@link Suite} execution begins
82
+ */
83
+ EVENT_SUITE_BEGIN: 'suite',
84
+ /**
85
+ * Emitted when {@link Suite} execution ends
86
+ */
87
+ EVENT_SUITE_END: 'suite end',
88
+ /**
89
+ * Emitted when {@link Test} execution begins
90
+ */
91
+ EVENT_TEST_BEGIN: 'test',
92
+ /**
93
+ * Emitted when {@link Test} execution ends
94
+ */
95
+ EVENT_TEST_END: 'test end',
96
+ /**
97
+ * Emitted when {@link Test} execution fails
98
+ */
99
+ EVENT_TEST_FAIL: 'fail',
100
+ /**
101
+ * Emitted when {@link Test} execution succeeds
102
+ */
103
+ EVENT_TEST_PASS: 'pass',
104
+ /**
105
+ * Emitted when {@link Test} becomes pending
106
+ */
107
+ EVENT_TEST_PENDING: 'pending',
108
+ /**
109
+ * Emitted when {@link Test} execution has failed, but will retry
110
+ */
111
+ EVENT_TEST_RETRY: 'retry'
112
+ }
113
+ );
114
+
115
+ module.exports = Runner;
116
+
117
+ /**
118
+ * Initialize a `Runner` at the Root {@link Suite}, which represents a hierarchy of {@link Suite|Suites} and {@link Test|Tests}.
119
+ *
120
+ * @extends external:EventEmitter
121
+ * @public
122
+ * @class
123
+ * @param {Suite} suite Root suite
124
+ * @param {boolean} [delay] Whether or not to delay execution of root suite
125
+ * until ready.
126
+ */
127
+ function Runner(suite, delay) {
128
+ var self = this;
129
+ this._globals = [];
130
+ this._abort = false;
131
+ this._delay = delay;
132
+ this.suite = suite;
133
+ this.started = false;
134
+ this.total = suite.total();
135
+ this.failures = 0;
136
+ this.on(constants.EVENT_TEST_END, function(test) {
137
+ self.checkGlobals(test);
138
+ });
139
+ this.on(constants.EVENT_HOOK_END, function(hook) {
140
+ self.checkGlobals(hook);
141
+ });
142
+ this._defaultGrep = /.*/;
143
+ this.grep(this._defaultGrep);
144
+ this.globals(this.globalProps().concat(extraGlobals()));
145
+ }
146
+
147
+ /**
148
+ * Wrapper for setImmediate, process.nextTick, or browser polyfill.
149
+ *
150
+ * @param {Function} fn
151
+ * @private
152
+ */
153
+ Runner.immediately = global.setImmediate || process.nextTick;
154
+
155
+ /**
156
+ * Inherit from `EventEmitter.prototype`.
157
+ */
158
+ inherits(Runner, EventEmitter);
159
+
160
+ /**
161
+ * Run tests with full titles matching `re`. Updates runner.total
162
+ * with number of tests matched.
163
+ *
164
+ * @public
165
+ * @memberof Runner
166
+ * @param {RegExp} re
167
+ * @param {boolean} invert
168
+ * @return {Runner} Runner instance.
169
+ */
170
+ Runner.prototype.grep = function(re, invert) {
171
+ debug('grep %s', re);
172
+ this._grep = re;
173
+ this._invert = invert;
174
+ this.total = this.grepTotal(this.suite);
175
+ return this;
176
+ };
177
+
178
+ /**
179
+ * Returns the number of tests matching the grep search for the
180
+ * given suite.
181
+ *
182
+ * @memberof Runner
183
+ * @public
184
+ * @param {Suite} suite
185
+ * @return {number}
186
+ */
187
+ Runner.prototype.grepTotal = function(suite) {
188
+ var self = this;
189
+ var total = 0;
190
+
191
+ suite.eachTest(function(test) {
192
+ var match = self._grep.test(test.fullTitle());
193
+ if (self._invert) {
194
+ match = !match;
195
+ }
196
+ if (match) {
197
+ total++;
198
+ }
199
+ });
200
+
201
+ return total;
202
+ };
203
+
204
+ /**
205
+ * Return a list of global properties.
206
+ *
207
+ * @return {Array}
208
+ * @private
209
+ */
210
+ Runner.prototype.globalProps = function() {
211
+ var props = Object.keys(global);
212
+
213
+ // non-enumerables
214
+ for (var i = 0; i < globals.length; ++i) {
215
+ if (~props.indexOf(globals[i])) {
216
+ continue;
217
+ }
218
+ props.push(globals[i]);
219
+ }
220
+
221
+ return props;
222
+ };
223
+
224
+ /**
225
+ * Allow the given `arr` of globals.
226
+ *
227
+ * @public
228
+ * @memberof Runner
229
+ * @param {Array} arr
230
+ * @return {Runner} Runner instance.
231
+ */
232
+ Runner.prototype.globals = function(arr) {
233
+ if (!arguments.length) {
234
+ return this._globals;
235
+ }
236
+ debug('globals %j', arr);
237
+ this._globals = this._globals.concat(arr);
238
+ return this;
239
+ };
240
+
241
+ /**
242
+ * Check for global variable leaks.
243
+ *
244
+ * @private
245
+ */
246
+ Runner.prototype.checkGlobals = function(test) {
247
+ if (this.ignoreLeaks) {
248
+ return;
249
+ }
250
+ var ok = this._globals;
251
+
252
+ var globals = this.globalProps();
253
+ var leaks;
254
+
255
+ if (test) {
256
+ ok = ok.concat(test._allowedGlobals || []);
257
+ }
258
+
259
+ if (this.prevGlobalsLength === globals.length) {
260
+ return;
261
+ }
262
+ this.prevGlobalsLength = globals.length;
263
+
264
+ leaks = filterLeaks(ok, globals);
265
+ this._globals = this._globals.concat(leaks);
266
+
267
+ if (leaks.length) {
268
+ var format = ngettext(
269
+ leaks.length,
270
+ 'global leak detected: %s',
271
+ 'global leaks detected: %s'
272
+ );
273
+ var error = new Error(util.format(format, leaks.map(sQuote).join(', ')));
274
+ this.fail(test, error);
275
+ }
276
+ };
277
+
278
+ /**
279
+ * Fail the given `test`.
280
+ *
281
+ * @private
282
+ * @param {Test} test
283
+ * @param {Error} err
284
+ */
285
+ Runner.prototype.fail = function(test, err) {
286
+ if (test.isPending()) {
287
+ return;
288
+ }
289
+
290
+ ++this.failures;
291
+ test.state = STATE_FAILED;
292
+
293
+ if (!isError(err)) {
294
+ err = thrown2Error(err);
295
+ }
296
+
297
+ try {
298
+ err.stack =
299
+ this.fullStackTrace || !err.stack ? err.stack : stackFilter(err.stack);
300
+ } catch (ignore) {
301
+ // some environments do not take kindly to monkeying with the stack
302
+ }
303
+
304
+ this.emit(constants.EVENT_TEST_FAIL, test, err);
305
+ };
306
+
307
+ /**
308
+ * Fail the given `hook` with `err`.
309
+ *
310
+ * Hook failures work in the following pattern:
311
+ * - If bail, run corresponding `after each` and `after` hooks,
312
+ * then exit
313
+ * - Failed `before` hook skips all tests in a suite and subsuites,
314
+ * but jumps to corresponding `after` hook
315
+ * - Failed `before each` hook skips remaining tests in a
316
+ * suite and jumps to corresponding `after each` hook,
317
+ * which is run only once
318
+ * - Failed `after` hook does not alter
319
+ * execution order
320
+ * - Failed `after each` hook skips remaining tests in a
321
+ * suite and subsuites, but executes other `after each`
322
+ * hooks
323
+ *
324
+ * @private
325
+ * @param {Hook} hook
326
+ * @param {Error} err
327
+ */
328
+ Runner.prototype.failHook = function(hook, err) {
329
+ hook.originalTitle = hook.originalTitle || hook.title;
330
+ if (hook.ctx && hook.ctx.currentTest) {
331
+ hook.title =
332
+ hook.originalTitle + ' for ' + dQuote(hook.ctx.currentTest.title);
333
+ } else {
334
+ var parentTitle;
335
+ if (hook.parent.title) {
336
+ parentTitle = hook.parent.title;
337
+ } else {
338
+ parentTitle = hook.parent.root ? '{root}' : '';
339
+ }
340
+ hook.title = hook.originalTitle + ' in ' + dQuote(parentTitle);
341
+ }
342
+
343
+ this.fail(hook, err);
344
+ };
345
+
346
+ /**
347
+ * Run hook `name` callbacks and then invoke `fn()`.
348
+ *
349
+ * @private
350
+ * @param {string} name
351
+ * @param {Function} fn
352
+ */
353
+
354
+ Runner.prototype.hook = function(name, fn) {
355
+ var suite = this.suite;
356
+ var hooks = suite.getHooks(name);
357
+ var self = this;
358
+
359
+ function next(i) {
360
+ var hook = hooks[i];
361
+ if (!hook) {
362
+ return fn();
363
+ }
364
+ self.currentRunnable = hook;
365
+
366
+ if (name === HOOK_TYPE_BEFORE_ALL) {
367
+ hook.ctx.currentTest = hook.parent.tests[0];
368
+ } else if (name === HOOK_TYPE_AFTER_ALL) {
369
+ hook.ctx.currentTest = hook.parent.tests[hook.parent.tests.length - 1];
370
+ } else {
371
+ hook.ctx.currentTest = self.test;
372
+ }
373
+
374
+ hook.allowUncaught = self.allowUncaught;
375
+
376
+ self.emit(constants.EVENT_HOOK_BEGIN, hook);
377
+
378
+ if (!hook.listeners('error').length) {
379
+ hook.on('error', function(err) {
380
+ self.failHook(hook, err);
381
+ });
382
+ }
383
+
384
+ hook.run(function(err) {
385
+ var testError = hook.error();
386
+ if (testError) {
387
+ self.fail(self.test, testError);
388
+ }
389
+ if (err) {
390
+ if (err instanceof Pending) {
391
+ if (name === HOOK_TYPE_AFTER_ALL) {
392
+ utils.deprecate(
393
+ 'Skipping a test within an "after all" hook is DEPRECATED and will throw an exception in a future version of Mocha. ' +
394
+ 'Use a return statement or other means to abort hook execution.'
395
+ );
396
+ }
397
+ if (name === HOOK_TYPE_BEFORE_EACH || name === HOOK_TYPE_AFTER_EACH) {
398
+ if (self.test) {
399
+ self.test.pending = true;
400
+ }
401
+ } else {
402
+ suite.tests.forEach(function(test) {
403
+ test.pending = true;
404
+ });
405
+ suite.suites.forEach(function(suite) {
406
+ suite.pending = true;
407
+ });
408
+ // a pending hook won't be executed twice.
409
+ hook.pending = true;
410
+ }
411
+ } else {
412
+ self.failHook(hook, err);
413
+
414
+ // stop executing hooks, notify callee of hook err
415
+ return fn(err);
416
+ }
417
+ }
418
+ self.emit(constants.EVENT_HOOK_END, hook);
419
+ delete hook.ctx.currentTest;
420
+ next(++i);
421
+ });
422
+ }
423
+
424
+ Runner.immediately(function() {
425
+ next(0);
426
+ });
427
+ };
428
+
429
+ /**
430
+ * Run hook `name` for the given array of `suites`
431
+ * in order, and callback `fn(err, errSuite)`.
432
+ *
433
+ * @private
434
+ * @param {string} name
435
+ * @param {Array} suites
436
+ * @param {Function} fn
437
+ */
438
+ Runner.prototype.hooks = function(name, suites, fn) {
439
+ var self = this;
440
+ var orig = this.suite;
441
+
442
+ function next(suite) {
443
+ self.suite = suite;
444
+
445
+ if (!suite) {
446
+ self.suite = orig;
447
+ return fn();
448
+ }
449
+
450
+ self.hook(name, function(err) {
451
+ if (err) {
452
+ var errSuite = self.suite;
453
+ self.suite = orig;
454
+ return fn(err, errSuite);
455
+ }
456
+
457
+ next(suites.pop());
458
+ });
459
+ }
460
+
461
+ next(suites.pop());
462
+ };
463
+
464
+ /**
465
+ * Run hooks from the top level down.
466
+ *
467
+ * @param {String} name
468
+ * @param {Function} fn
469
+ * @private
470
+ */
471
+ Runner.prototype.hookUp = function(name, fn) {
472
+ var suites = [this.suite].concat(this.parents()).reverse();
473
+ this.hooks(name, suites, fn);
474
+ };
475
+
476
+ /**
477
+ * Run hooks from the bottom up.
478
+ *
479
+ * @param {String} name
480
+ * @param {Function} fn
481
+ * @private
482
+ */
483
+ Runner.prototype.hookDown = function(name, fn) {
484
+ var suites = [this.suite].concat(this.parents());
485
+ this.hooks(name, suites, fn);
486
+ };
487
+
488
+ /**
489
+ * Return an array of parent Suites from
490
+ * closest to furthest.
491
+ *
492
+ * @return {Array}
493
+ * @private
494
+ */
495
+ Runner.prototype.parents = function() {
496
+ var suite = this.suite;
497
+ var suites = [];
498
+ while (suite.parent) {
499
+ suite = suite.parent;
500
+ suites.push(suite);
501
+ }
502
+ return suites;
503
+ };
504
+
505
+ /**
506
+ * Run the current test and callback `fn(err)`.
507
+ *
508
+ * @param {Function} fn
509
+ * @private
510
+ */
511
+ Runner.prototype.runTest = function(fn) {
512
+ var self = this;
513
+ var test = this.test;
514
+
515
+ if (!test) {
516
+ return;
517
+ }
518
+
519
+ var suite = this.parents().reverse()[0] || this.suite;
520
+ if (this.forbidOnly && suite.hasOnly()) {
521
+ fn(new Error('`.only` forbidden'));
522
+ return;
523
+ }
524
+ if (this.asyncOnly) {
525
+ test.asyncOnly = true;
526
+ }
527
+ test.on('error', function(err) {
528
+ self.fail(test, err);
529
+ });
530
+ if (this.allowUncaught) {
531
+ test.allowUncaught = true;
532
+ return test.run(fn);
533
+ }
534
+ try {
535
+ test.run(fn);
536
+ } catch (err) {
537
+ fn(err);
538
+ }
539
+ };
540
+
541
+ /**
542
+ * Run tests in the given `suite` and invoke the callback `fn()` when complete.
543
+ *
544
+ * @private
545
+ * @param {Suite} suite
546
+ * @param {Function} fn
547
+ */
548
+ Runner.prototype.runTests = function(suite, fn) {
549
+ var self = this;
550
+ var tests = suite.tests.slice();
551
+ var test;
552
+
553
+ function hookErr(_, errSuite, after) {
554
+ // before/after Each hook for errSuite failed:
555
+ var orig = self.suite;
556
+
557
+ // for failed 'after each' hook start from errSuite parent,
558
+ // otherwise start from errSuite itself
559
+ self.suite = after ? errSuite.parent : errSuite;
560
+
561
+ if (self.suite) {
562
+ // call hookUp afterEach
563
+ self.hookUp(HOOK_TYPE_AFTER_EACH, function(err2, errSuite2) {
564
+ self.suite = orig;
565
+ // some hooks may fail even now
566
+ if (err2) {
567
+ return hookErr(err2, errSuite2, true);
568
+ }
569
+ // report error suite
570
+ fn(errSuite);
571
+ });
572
+ } else {
573
+ // there is no need calling other 'after each' hooks
574
+ self.suite = orig;
575
+ fn(errSuite);
576
+ }
577
+ }
578
+
579
+ function next(err, errSuite) {
580
+ // if we bail after first err
581
+ if (self.failures && suite._bail) {
582
+ tests = [];
583
+ }
584
+
585
+ if (self._abort) {
586
+ return fn();
587
+ }
588
+
589
+ if (err) {
590
+ return hookErr(err, errSuite, true);
591
+ }
592
+
593
+ // next test
594
+ test = tests.shift();
595
+
596
+ // all done
597
+ if (!test) {
598
+ return fn();
599
+ }
600
+
601
+ // grep
602
+ var match = self._grep.test(test.fullTitle());
603
+ if (self._invert) {
604
+ match = !match;
605
+ }
606
+ if (!match) {
607
+ // Run immediately only if we have defined a grep. When we
608
+ // define a grep — It can cause maximum callstack error if
609
+ // the grep is doing a large recursive loop by neglecting
610
+ // all tests. The run immediately function also comes with
611
+ // a performance cost. So we don't want to run immediately
612
+ // if we run the whole test suite, because running the whole
613
+ // test suite don't do any immediate recursive loops. Thus,
614
+ // allowing a JS runtime to breathe.
615
+ if (self._grep !== self._defaultGrep) {
616
+ Runner.immediately(next);
617
+ } else {
618
+ next();
619
+ }
620
+ return;
621
+ }
622
+
623
+ if (test.isPending()) {
624
+ if (self.forbidPending) {
625
+ test.isPending = alwaysFalse;
626
+ self.fail(test, new Error('Pending test forbidden'));
627
+ delete test.isPending;
628
+ } else {
629
+ self.emit(constants.EVENT_TEST_PENDING, test);
630
+ }
631
+ self.emit(constants.EVENT_TEST_END, test);
632
+ return next();
633
+ }
634
+
635
+ // execute test and hook(s)
636
+ self.emit(constants.EVENT_TEST_BEGIN, (self.test = test));
637
+ self.hookDown(HOOK_TYPE_BEFORE_EACH, function(err, errSuite) {
638
+ if (test.isPending()) {
639
+ if (self.forbidPending) {
640
+ test.isPending = alwaysFalse;
641
+ self.fail(test, new Error('Pending test forbidden'));
642
+ delete test.isPending;
643
+ } else {
644
+ self.emit(constants.EVENT_TEST_PENDING, test);
645
+ }
646
+ self.emit(constants.EVENT_TEST_END, test);
647
+ return next();
648
+ }
649
+ if (err) {
650
+ return hookErr(err, errSuite, false);
651
+ }
652
+ self.currentRunnable = self.test;
653
+ self.runTest(function(err) {
654
+ test = self.test;
655
+ if (err) {
656
+ var retry = test.currentRetry();
657
+ if (err instanceof Pending && self.forbidPending) {
658
+ self.fail(test, new Error('Pending test forbidden'));
659
+ } else if (err instanceof Pending) {
660
+ test.pending = true;
661
+ self.emit(constants.EVENT_TEST_PENDING, test);
662
+ } else if (retry < test.retries()) {
663
+ var clonedTest = test.clone();
664
+ clonedTest.currentRetry(retry + 1);
665
+ tests.unshift(clonedTest);
666
+
667
+ self.emit(constants.EVENT_TEST_RETRY, test, err);
668
+
669
+ // Early return + hook trigger so that it doesn't
670
+ // increment the count wrong
671
+ return self.hookUp(HOOK_TYPE_AFTER_EACH, next);
672
+ } else {
673
+ self.fail(test, err);
674
+ }
675
+ self.emit(constants.EVENT_TEST_END, test);
676
+
677
+ if (err instanceof Pending) {
678
+ return next();
679
+ }
680
+
681
+ return self.hookUp(HOOK_TYPE_AFTER_EACH, next);
682
+ }
683
+
684
+ test.state = STATE_PASSED;
685
+ self.emit(constants.EVENT_TEST_PASS, test);
686
+ self.emit(constants.EVENT_TEST_END, test);
687
+ self.hookUp(HOOK_TYPE_AFTER_EACH, next);
688
+ });
689
+ });
690
+ }
691
+
692
+ this.next = next;
693
+ this.hookErr = hookErr;
694
+ next();
695
+ };
696
+
697
+ function alwaysFalse() {
698
+ return false;
699
+ }
700
+
701
+ /**
702
+ * Run the given `suite` and invoke the callback `fn()` when complete.
703
+ *
704
+ * @private
705
+ * @param {Suite} suite
706
+ * @param {Function} fn
707
+ */
708
+ Runner.prototype.runSuite = function(suite, fn) {
709
+ var i = 0;
710
+ var self = this;
711
+ var total = this.grepTotal(suite);
712
+ var afterAllHookCalled = false;
713
+
714
+ debug('run suite %s', suite.fullTitle());
715
+
716
+ if (!total || (self.failures && suite._bail)) {
717
+ return fn();
718
+ }
719
+
720
+ this.emit(constants.EVENT_SUITE_BEGIN, (this.suite = suite));
721
+
722
+ function next(errSuite) {
723
+ if (errSuite) {
724
+ // current suite failed on a hook from errSuite
725
+ if (errSuite === suite) {
726
+ // if errSuite is current suite
727
+ // continue to the next sibling suite
728
+ return done();
729
+ }
730
+ // errSuite is among the parents of current suite
731
+ // stop execution of errSuite and all sub-suites
732
+ return done(errSuite);
733
+ }
734
+
735
+ if (self._abort) {
736
+ return done();
737
+ }
738
+
739
+ var curr = suite.suites[i++];
740
+ if (!curr) {
741
+ return done();
742
+ }
743
+
744
+ // Avoid grep neglecting large number of tests causing a
745
+ // huge recursive loop and thus a maximum call stack error.
746
+ // See comment in `this.runTests()` for more information.
747
+ if (self._grep !== self._defaultGrep) {
748
+ Runner.immediately(function() {
749
+ self.runSuite(curr, next);
750
+ });
751
+ } else {
752
+ self.runSuite(curr, next);
753
+ }
754
+ }
755
+
756
+ function done(errSuite) {
757
+ self.suite = suite;
758
+ self.nextSuite = next;
759
+
760
+ if (afterAllHookCalled) {
761
+ fn(errSuite);
762
+ } else {
763
+ // mark that the afterAll block has been called once
764
+ // and so can be skipped if there is an error in it.
765
+ afterAllHookCalled = true;
766
+
767
+ // remove reference to test
768
+ delete self.test;
769
+
770
+ self.hook(HOOK_TYPE_AFTER_ALL, function() {
771
+ self.emit(constants.EVENT_SUITE_END, suite);
772
+ fn(errSuite);
773
+ });
774
+ }
775
+ }
776
+
777
+ this.nextSuite = next;
778
+
779
+ this.hook(HOOK_TYPE_BEFORE_ALL, function(err) {
780
+ if (err) {
781
+ return done();
782
+ }
783
+ self.runTests(suite, next);
784
+ });
785
+ };
786
+
787
+ /**
788
+ * Handle uncaught exceptions.
789
+ *
790
+ * @param {Error} err
791
+ * @private
792
+ */
793
+ Runner.prototype.uncaught = function(err) {
794
+ if (err instanceof Pending) {
795
+ return;
796
+ }
797
+ if (err) {
798
+ debug('uncaught exception %O', err);
799
+ } else {
800
+ debug('uncaught undefined/falsy exception');
801
+ err = createInvalidExceptionError(
802
+ 'Caught falsy/undefined exception which would otherwise be uncaught. No stack trace found; try a debugger',
803
+ err
804
+ );
805
+ }
806
+
807
+ if (!isError(err)) {
808
+ err = thrown2Error(err);
809
+ }
810
+ err.uncaught = true;
811
+
812
+ var runnable = this.currentRunnable;
813
+
814
+ if (!runnable) {
815
+ runnable = new Runnable('Uncaught error outside test suite');
816
+ runnable.parent = this.suite;
817
+
818
+ if (this.started) {
819
+ this.fail(runnable, err);
820
+ } else {
821
+ // Can't recover from this failure
822
+ this.emit(constants.EVENT_RUN_BEGIN);
823
+ this.fail(runnable, err);
824
+ this.emit(constants.EVENT_RUN_END);
825
+ }
826
+
827
+ return;
828
+ }
829
+
830
+ runnable.clearTimeout();
831
+
832
+ // Ignore errors if already failed or pending
833
+ // See #3226
834
+ if (runnable.isFailed() || runnable.isPending()) {
835
+ return;
836
+ }
837
+ // we cannot recover gracefully if a Runnable has already passed
838
+ // then fails asynchronously
839
+ var alreadyPassed = runnable.isPassed();
840
+ // this will change the state to "failed" regardless of the current value
841
+ this.fail(runnable, err);
842
+ if (!alreadyPassed) {
843
+ // recover from test
844
+ if (runnable.type === constants.EVENT_TEST_BEGIN) {
845
+ this.emit(constants.EVENT_TEST_END, runnable);
846
+ this.hookUp(HOOK_TYPE_AFTER_EACH, this.next);
847
+ return;
848
+ }
849
+ debug(runnable);
850
+
851
+ // recover from hooks
852
+ var errSuite = this.suite;
853
+
854
+ // XXX how about a less awful way to determine this?
855
+ // if hook failure is in afterEach block
856
+ if (runnable.fullTitle().indexOf('after each') > -1) {
857
+ return this.hookErr(err, errSuite, true);
858
+ }
859
+ // if hook failure is in beforeEach block
860
+ if (runnable.fullTitle().indexOf('before each') > -1) {
861
+ return this.hookErr(err, errSuite, false);
862
+ }
863
+ // if hook failure is in after or before blocks
864
+ return this.nextSuite(errSuite);
865
+ }
866
+
867
+ // bail
868
+ this.emit(constants.EVENT_RUN_END);
869
+ };
870
+
871
+ /**
872
+ * Run the root suite and invoke `fn(failures)`
873
+ * on completion.
874
+ *
875
+ * @public
876
+ * @memberof Runner
877
+ * @param {Function} fn
878
+ * @return {Runner} Runner instance.
879
+ */
880
+ Runner.prototype.run = function(fn) {
881
+ var self = this;
882
+ var rootSuite = this.suite;
883
+
884
+ fn = fn || function() {};
885
+
886
+ function uncaught(err) {
887
+ self.uncaught(err);
888
+ }
889
+
890
+ function start() {
891
+ // If there is an `only` filter
892
+ if (rootSuite.hasOnly()) {
893
+ rootSuite.filterOnly();
894
+ }
895
+ self.started = true;
896
+ if (self._delay) {
897
+ self.emit(constants.EVENT_DELAY_END);
898
+ }
899
+ self.emit(constants.EVENT_RUN_BEGIN);
900
+
901
+ self.runSuite(rootSuite, function() {
902
+ debug('finished running');
903
+ self.emit(constants.EVENT_RUN_END);
904
+ });
905
+ }
906
+
907
+ debug(constants.EVENT_RUN_BEGIN);
908
+
909
+ // references cleanup to avoid memory leaks
910
+ this.on(constants.EVENT_SUITE_END, function(suite) {
911
+ suite.cleanReferences();
912
+ });
913
+
914
+ // callback
915
+ this.on(constants.EVENT_RUN_END, function() {
916
+ debug(constants.EVENT_RUN_END);
917
+ process.removeListener('uncaughtException', uncaught);
918
+ fn(self.failures);
919
+ });
920
+
921
+ // uncaught exception
922
+ process.on('uncaughtException', uncaught);
923
+
924
+ if (this._delay) {
925
+ // for reporters, I guess.
926
+ // might be nice to debounce some dots while we wait.
927
+ this.emit(constants.EVENT_DELAY_BEGIN, rootSuite);
928
+ rootSuite.once(EVENT_ROOT_SUITE_RUN, start);
929
+ } else {
930
+ start();
931
+ }
932
+
933
+ return this;
934
+ };
935
+
936
+ /**
937
+ * Cleanly abort execution.
938
+ *
939
+ * @memberof Runner
940
+ * @public
941
+ * @return {Runner} Runner instance.
942
+ */
943
+ Runner.prototype.abort = function() {
944
+ debug('aborting');
945
+ this._abort = true;
946
+
947
+ return this;
948
+ };
949
+
950
+ /**
951
+ * Filter leaks with the given globals flagged as `ok`.
952
+ *
953
+ * @private
954
+ * @param {Array} ok
955
+ * @param {Array} globals
956
+ * @return {Array}
957
+ */
958
+ function filterLeaks(ok, globals) {
959
+ return globals.filter(function(key) {
960
+ // Firefox and Chrome exposes iframes as index inside the window object
961
+ if (/^\d+/.test(key)) {
962
+ return false;
963
+ }
964
+
965
+ // in firefox
966
+ // if runner runs in an iframe, this iframe's window.getInterface method
967
+ // not init at first it is assigned in some seconds
968
+ if (global.navigator && /^getInterface/.test(key)) {
969
+ return false;
970
+ }
971
+
972
+ // an iframe could be approached by window[iframeIndex]
973
+ // in ie6,7,8 and opera, iframeIndex is enumerable, this could cause leak
974
+ if (global.navigator && /^\d+/.test(key)) {
975
+ return false;
976
+ }
977
+
978
+ // Opera and IE expose global variables for HTML element IDs (issue #243)
979
+ if (/^mocha-/.test(key)) {
980
+ return false;
981
+ }
982
+
983
+ var matched = ok.filter(function(ok) {
984
+ if (~ok.indexOf('*')) {
985
+ return key.indexOf(ok.split('*')[0]) === 0;
986
+ }
987
+ return key === ok;
988
+ });
989
+ return !matched.length && (!global.navigator || key !== 'onerror');
990
+ });
991
+ }
992
+
993
+ /**
994
+ * Check if argument is an instance of Error object or a duck-typed equivalent.
995
+ *
996
+ * @private
997
+ * @param {Object} err - object to check
998
+ * @param {string} err.message - error message
999
+ * @returns {boolean}
1000
+ */
1001
+ function isError(err) {
1002
+ return err instanceof Error || (err && typeof err.message === 'string');
1003
+ }
1004
+
1005
+ /**
1006
+ *
1007
+ * Converts thrown non-extensible type into proper Error.
1008
+ *
1009
+ * @private
1010
+ * @param {*} thrown - Non-extensible type thrown by code
1011
+ * @return {Error}
1012
+ */
1013
+ function thrown2Error(err) {
1014
+ return new Error(
1015
+ 'the ' + type(err) + ' ' + stringify(err) + ' was thrown, throw an Error :)'
1016
+ );
1017
+ }
1018
+
1019
+ /**
1020
+ * Array of globals dependent on the environment.
1021
+ *
1022
+ * @return {Array}
1023
+ * @deprecated
1024
+ * @todo remove; long since unsupported
1025
+ * @private
1026
+ */
1027
+ function extraGlobals() {
1028
+ if (typeof process === 'object' && typeof process.version === 'string') {
1029
+ var parts = process.version.split('.');
1030
+ var nodeVersion = parts.reduce(function(a, v) {
1031
+ return (a << 8) | v;
1032
+ });
1033
+
1034
+ // 'errno' was renamed to process._errno in v0.9.11.
1035
+ if (nodeVersion < 0x00090b) {
1036
+ return ['errno'];
1037
+ }
1038
+ }
1039
+
1040
+ return [];
1041
+ }
1042
+
1043
+ Runner.constants = constants;
1044
+
1045
+ /**
1046
+ * Node.js' `EventEmitter`
1047
+ * @external EventEmitter
1048
+ * @see {@link https://nodejs.org/api/events.html#events_class_eventemitter}
1049
+ */