mocha 9.1.2

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 (76) hide show
  1. package/CHANGELOG.md +1015 -0
  2. package/LICENSE +22 -0
  3. package/README.md +70 -0
  4. package/assets/growl/error.png +0 -0
  5. package/assets/growl/ok.png +0 -0
  6. package/bin/_mocha +10 -0
  7. package/bin/mocha +142 -0
  8. package/browser-entry.js +216 -0
  9. package/index.js +3 -0
  10. package/lib/browser/growl.js +169 -0
  11. package/lib/browser/highlight-tags.js +39 -0
  12. package/lib/browser/parse-query.js +24 -0
  13. package/lib/browser/progress.js +123 -0
  14. package/lib/browser/template.html +20 -0
  15. package/lib/cli/cli.js +89 -0
  16. package/lib/cli/collect-files.js +92 -0
  17. package/lib/cli/commands.js +13 -0
  18. package/lib/cli/config.js +105 -0
  19. package/lib/cli/index.js +3 -0
  20. package/lib/cli/init.js +36 -0
  21. package/lib/cli/lookup-files.js +145 -0
  22. package/lib/cli/node-flags.js +85 -0
  23. package/lib/cli/one-and-dones.js +69 -0
  24. package/lib/cli/options.js +261 -0
  25. package/lib/cli/run-helpers.js +243 -0
  26. package/lib/cli/run-option-metadata.js +117 -0
  27. package/lib/cli/run.js +379 -0
  28. package/lib/cli/watch-run.js +380 -0
  29. package/lib/context.js +86 -0
  30. package/lib/errors.js +563 -0
  31. package/lib/hook.js +89 -0
  32. package/lib/interfaces/bdd.js +111 -0
  33. package/lib/interfaces/common.js +193 -0
  34. package/lib/interfaces/exports.js +60 -0
  35. package/lib/interfaces/index.js +6 -0
  36. package/lib/interfaces/qunit.js +98 -0
  37. package/lib/interfaces/tdd.js +106 -0
  38. package/lib/mocha.js +1374 -0
  39. package/lib/mocharc.json +10 -0
  40. package/lib/nodejs/buffered-worker-pool.js +172 -0
  41. package/lib/nodejs/esm-utils.js +109 -0
  42. package/lib/nodejs/file-unloader.js +15 -0
  43. package/lib/nodejs/growl.js +137 -0
  44. package/lib/nodejs/parallel-buffered-runner.js +433 -0
  45. package/lib/nodejs/reporters/parallel-buffered.js +165 -0
  46. package/lib/nodejs/serializer.js +412 -0
  47. package/lib/nodejs/worker.js +151 -0
  48. package/lib/pending.js +16 -0
  49. package/lib/plugin-loader.js +286 -0
  50. package/lib/reporters/base.js +537 -0
  51. package/lib/reporters/doc.js +95 -0
  52. package/lib/reporters/dot.js +81 -0
  53. package/lib/reporters/html.js +390 -0
  54. package/lib/reporters/index.js +19 -0
  55. package/lib/reporters/json-stream.js +92 -0
  56. package/lib/reporters/json.js +162 -0
  57. package/lib/reporters/landing.js +116 -0
  58. package/lib/reporters/list.js +78 -0
  59. package/lib/reporters/markdown.js +112 -0
  60. package/lib/reporters/min.js +52 -0
  61. package/lib/reporters/nyan.js +276 -0
  62. package/lib/reporters/progress.js +104 -0
  63. package/lib/reporters/spec.js +99 -0
  64. package/lib/reporters/tap.js +293 -0
  65. package/lib/reporters/xunit.js +217 -0
  66. package/lib/runnable.js +476 -0
  67. package/lib/runner.js +1269 -0
  68. package/lib/stats-collector.js +83 -0
  69. package/lib/suite.js +695 -0
  70. package/lib/test.js +113 -0
  71. package/lib/utils.js +641 -0
  72. package/mocha-es2018.js +19816 -0
  73. package/mocha.css +325 -0
  74. package/mocha.js +30844 -0
  75. package/mocha.js.map +1 -0
  76. package/package.json +200 -0
package/lib/runner.js ADDED
@@ -0,0 +1,1269 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Module dependencies.
5
+ * @private
6
+ */
7
+ var EventEmitter = require('events').EventEmitter;
8
+ var Pending = require('./pending');
9
+ var utils = require('./utils');
10
+ var debug = require('debug')('mocha:runner');
11
+ var Runnable = require('./runnable');
12
+ var Suite = require('./suite');
13
+ var HOOK_TYPE_BEFORE_EACH = Suite.constants.HOOK_TYPE_BEFORE_EACH;
14
+ var HOOK_TYPE_AFTER_EACH = Suite.constants.HOOK_TYPE_AFTER_EACH;
15
+ var HOOK_TYPE_AFTER_ALL = Suite.constants.HOOK_TYPE_AFTER_ALL;
16
+ var HOOK_TYPE_BEFORE_ALL = Suite.constants.HOOK_TYPE_BEFORE_ALL;
17
+ var EVENT_ROOT_SUITE_RUN = Suite.constants.EVENT_ROOT_SUITE_RUN;
18
+ var STATE_FAILED = Runnable.constants.STATE_FAILED;
19
+ var STATE_PASSED = Runnable.constants.STATE_PASSED;
20
+ var STATE_PENDING = Runnable.constants.STATE_PENDING;
21
+ var stackFilter = utils.stackTraceFilter();
22
+ var stringify = utils.stringify;
23
+
24
+ const {
25
+ createInvalidExceptionError,
26
+ createUnsupportedError,
27
+ createFatalError,
28
+ isMochaError,
29
+ constants: errorConstants
30
+ } = require('./errors');
31
+
32
+ /**
33
+ * Non-enumerable globals.
34
+ * @private
35
+ * @readonly
36
+ */
37
+ var globals = [
38
+ 'setTimeout',
39
+ 'clearTimeout',
40
+ 'setInterval',
41
+ 'clearInterval',
42
+ 'XMLHttpRequest',
43
+ 'Date',
44
+ 'setImmediate',
45
+ 'clearImmediate'
46
+ ];
47
+
48
+ var constants = utils.defineConstants(
49
+ /**
50
+ * {@link Runner}-related constants.
51
+ * @public
52
+ * @memberof Runner
53
+ * @readonly
54
+ * @alias constants
55
+ * @static
56
+ * @enum {string}
57
+ */
58
+ {
59
+ /**
60
+ * Emitted when {@link Hook} execution begins
61
+ */
62
+ EVENT_HOOK_BEGIN: 'hook',
63
+ /**
64
+ * Emitted when {@link Hook} execution ends
65
+ */
66
+ EVENT_HOOK_END: 'hook end',
67
+ /**
68
+ * Emitted when Root {@link Suite} execution begins (all files have been parsed and hooks/tests are ready for execution)
69
+ */
70
+ EVENT_RUN_BEGIN: 'start',
71
+ /**
72
+ * Emitted when Root {@link Suite} execution has been delayed via `delay` option
73
+ */
74
+ EVENT_DELAY_BEGIN: 'waiting',
75
+ /**
76
+ * Emitted when delayed Root {@link Suite} execution is triggered by user via `global.run()`
77
+ */
78
+ EVENT_DELAY_END: 'ready',
79
+ /**
80
+ * Emitted when Root {@link Suite} execution ends
81
+ */
82
+ EVENT_RUN_END: 'end',
83
+ /**
84
+ * Emitted when {@link Suite} execution begins
85
+ */
86
+ EVENT_SUITE_BEGIN: 'suite',
87
+ /**
88
+ * Emitted when {@link Suite} execution ends
89
+ */
90
+ EVENT_SUITE_END: 'suite end',
91
+ /**
92
+ * Emitted when {@link Test} execution begins
93
+ */
94
+ EVENT_TEST_BEGIN: 'test',
95
+ /**
96
+ * Emitted when {@link Test} execution ends
97
+ */
98
+ EVENT_TEST_END: 'test end',
99
+ /**
100
+ * Emitted when {@link Test} execution fails
101
+ */
102
+ EVENT_TEST_FAIL: 'fail',
103
+ /**
104
+ * Emitted when {@link Test} execution succeeds
105
+ */
106
+ EVENT_TEST_PASS: 'pass',
107
+ /**
108
+ * Emitted when {@link Test} becomes pending
109
+ */
110
+ EVENT_TEST_PENDING: 'pending',
111
+ /**
112
+ * Emitted when {@link Test} execution has failed, but will retry
113
+ */
114
+ EVENT_TEST_RETRY: 'retry',
115
+ /**
116
+ * Initial state of Runner
117
+ */
118
+ STATE_IDLE: 'idle',
119
+ /**
120
+ * State set to this value when the Runner has started running
121
+ */
122
+ STATE_RUNNING: 'running',
123
+ /**
124
+ * State set to this value when the Runner has stopped
125
+ */
126
+ STATE_STOPPED: 'stopped'
127
+ }
128
+ );
129
+
130
+ class Runner extends EventEmitter {
131
+ /**
132
+ * Initialize a `Runner` at the Root {@link Suite}, which represents a hierarchy of {@link Suite|Suites} and {@link Test|Tests}.
133
+ *
134
+ * @extends external:EventEmitter
135
+ * @public
136
+ * @class
137
+ * @param {Suite} suite - Root suite
138
+ * @param {Object|boolean} [opts] - Options. If `boolean` (deprecated), whether to delay execution of root suite until ready.
139
+ * @param {boolean} [opts.cleanReferencesAfterRun] - Whether to clean references to test fns and hooks when a suite is done.
140
+ * @param {boolean} [opts.delay] - Whether to delay execution of root suite until ready.
141
+ * @param {boolean} [opts.dryRun] - Whether to report tests without running them.
142
+ * @param {boolean} [options.failZero] - Whether to fail test run if zero tests encountered.
143
+ */
144
+ constructor(suite, opts) {
145
+ super();
146
+ if (opts === undefined) {
147
+ opts = {};
148
+ }
149
+ if (typeof opts === 'boolean') {
150
+ // TODO: remove this
151
+ require('./errors').deprecate(
152
+ '"Runner(suite: Suite, delay: boolean)" is deprecated. Use "Runner(suite: Suite, {delay: boolean})" instead.'
153
+ );
154
+ this._delay = opts;
155
+ opts = {};
156
+ } else {
157
+ this._delay = opts.delay;
158
+ }
159
+ var self = this;
160
+ this._globals = [];
161
+ this._abort = false;
162
+ this.suite = suite;
163
+ this._opts = opts;
164
+ this.state = constants.STATE_IDLE;
165
+ this.total = suite.total();
166
+ this.failures = 0;
167
+ /**
168
+ * @type {Map<EventEmitter,Map<string,Set<EventListener>>>}
169
+ */
170
+ this._eventListeners = new Map();
171
+ this.on(constants.EVENT_TEST_END, function(test) {
172
+ if (test.type === 'test' && test.retriedTest() && test.parent) {
173
+ var idx =
174
+ test.parent.tests && test.parent.tests.indexOf(test.retriedTest());
175
+ if (idx > -1) test.parent.tests[idx] = test;
176
+ }
177
+ self.checkGlobals(test);
178
+ });
179
+ this.on(constants.EVENT_HOOK_END, function(hook) {
180
+ self.checkGlobals(hook);
181
+ });
182
+ this._defaultGrep = /.*/;
183
+ this.grep(this._defaultGrep);
184
+ this.globals(this.globalProps());
185
+
186
+ this.uncaught = this._uncaught.bind(this);
187
+ this.unhandled = (reason, promise) => {
188
+ if (isMochaError(reason)) {
189
+ debug(
190
+ 'trapped unhandled rejection coming out of Mocha; forwarding to uncaught handler:',
191
+ reason
192
+ );
193
+ this.uncaught(reason);
194
+ } else {
195
+ debug(
196
+ 'trapped unhandled rejection from (probably) user code; re-emitting on process'
197
+ );
198
+ this._removeEventListener(
199
+ process,
200
+ 'unhandledRejection',
201
+ this.unhandled
202
+ );
203
+ try {
204
+ process.emit('unhandledRejection', reason, promise);
205
+ } finally {
206
+ this._addEventListener(process, 'unhandledRejection', this.unhandled);
207
+ }
208
+ }
209
+ };
210
+ }
211
+ }
212
+
213
+ /**
214
+ * Wrapper for setImmediate, process.nextTick, or browser polyfill.
215
+ *
216
+ * @param {Function} fn
217
+ * @private
218
+ */
219
+ Runner.immediately = global.setImmediate || process.nextTick;
220
+
221
+ /**
222
+ * Replacement for `target.on(eventName, listener)` that does bookkeeping to remove them when this runner instance is disposed.
223
+ * @param {EventEmitter} target - The `EventEmitter`
224
+ * @param {string} eventName - The event name
225
+ * @param {string} fn - Listener function
226
+ * @private
227
+ */
228
+ Runner.prototype._addEventListener = function(target, eventName, listener) {
229
+ debug(
230
+ '_addEventListener(): adding for event %s; %d current listeners',
231
+ eventName,
232
+ target.listenerCount(eventName)
233
+ );
234
+ /* istanbul ignore next */
235
+ if (
236
+ this._eventListeners.has(target) &&
237
+ this._eventListeners.get(target).has(eventName) &&
238
+ this._eventListeners
239
+ .get(target)
240
+ .get(eventName)
241
+ .has(listener)
242
+ ) {
243
+ debug(
244
+ 'warning: tried to attach duplicate event listener for %s',
245
+ eventName
246
+ );
247
+ return;
248
+ }
249
+ target.on(eventName, listener);
250
+ const targetListeners = this._eventListeners.has(target)
251
+ ? this._eventListeners.get(target)
252
+ : new Map();
253
+ const targetEventListeners = targetListeners.has(eventName)
254
+ ? targetListeners.get(eventName)
255
+ : new Set();
256
+ targetEventListeners.add(listener);
257
+ targetListeners.set(eventName, targetEventListeners);
258
+ this._eventListeners.set(target, targetListeners);
259
+ };
260
+
261
+ /**
262
+ * Replacement for `target.removeListener(eventName, listener)` that also updates the bookkeeping.
263
+ * @param {EventEmitter} target - The `EventEmitter`
264
+ * @param {string} eventName - The event name
265
+ * @param {function} listener - Listener function
266
+ * @private
267
+ */
268
+ Runner.prototype._removeEventListener = function(target, eventName, listener) {
269
+ target.removeListener(eventName, listener);
270
+
271
+ if (this._eventListeners.has(target)) {
272
+ const targetListeners = this._eventListeners.get(target);
273
+ if (targetListeners.has(eventName)) {
274
+ const targetEventListeners = targetListeners.get(eventName);
275
+ targetEventListeners.delete(listener);
276
+ if (!targetEventListeners.size) {
277
+ targetListeners.delete(eventName);
278
+ }
279
+ }
280
+ if (!targetListeners.size) {
281
+ this._eventListeners.delete(target);
282
+ }
283
+ } else {
284
+ debug('trying to remove listener for untracked object %s', target);
285
+ }
286
+ };
287
+
288
+ /**
289
+ * Removes all event handlers set during a run on this instance.
290
+ * Remark: this does _not_ clean/dispose the tests or suites themselves.
291
+ */
292
+ Runner.prototype.dispose = function() {
293
+ this.removeAllListeners();
294
+ this._eventListeners.forEach((targetListeners, target) => {
295
+ targetListeners.forEach((targetEventListeners, eventName) => {
296
+ targetEventListeners.forEach(listener => {
297
+ target.removeListener(eventName, listener);
298
+ });
299
+ });
300
+ });
301
+ this._eventListeners.clear();
302
+ };
303
+
304
+ /**
305
+ * Run tests with full titles matching `re`. Updates runner.total
306
+ * with number of tests matched.
307
+ *
308
+ * @public
309
+ * @memberof Runner
310
+ * @param {RegExp} re
311
+ * @param {boolean} invert
312
+ * @return {Runner} Runner instance.
313
+ */
314
+ Runner.prototype.grep = function(re, invert) {
315
+ debug('grep(): setting to %s', re);
316
+ this._grep = re;
317
+ this._invert = invert;
318
+ this.total = this.grepTotal(this.suite);
319
+ return this;
320
+ };
321
+
322
+ /**
323
+ * Returns the number of tests matching the grep search for the
324
+ * given suite.
325
+ *
326
+ * @memberof Runner
327
+ * @public
328
+ * @param {Suite} suite
329
+ * @return {number}
330
+ */
331
+ Runner.prototype.grepTotal = function(suite) {
332
+ var self = this;
333
+ var total = 0;
334
+
335
+ suite.eachTest(function(test) {
336
+ var match = self._grep.test(test.fullTitle());
337
+ if (self._invert) {
338
+ match = !match;
339
+ }
340
+ if (match) {
341
+ total++;
342
+ }
343
+ });
344
+
345
+ return total;
346
+ };
347
+
348
+ /**
349
+ * Return a list of global properties.
350
+ *
351
+ * @return {Array}
352
+ * @private
353
+ */
354
+ Runner.prototype.globalProps = function() {
355
+ var props = Object.keys(global);
356
+
357
+ // non-enumerables
358
+ for (var i = 0; i < globals.length; ++i) {
359
+ if (~props.indexOf(globals[i])) {
360
+ continue;
361
+ }
362
+ props.push(globals[i]);
363
+ }
364
+
365
+ return props;
366
+ };
367
+
368
+ /**
369
+ * Allow the given `arr` of globals.
370
+ *
371
+ * @public
372
+ * @memberof Runner
373
+ * @param {Array} arr
374
+ * @return {Runner} Runner instance.
375
+ */
376
+ Runner.prototype.globals = function(arr) {
377
+ if (!arguments.length) {
378
+ return this._globals;
379
+ }
380
+ debug('globals(): setting to %O', arr);
381
+ this._globals = this._globals.concat(arr);
382
+ return this;
383
+ };
384
+
385
+ /**
386
+ * Check for global variable leaks.
387
+ *
388
+ * @private
389
+ */
390
+ Runner.prototype.checkGlobals = function(test) {
391
+ if (!this.checkLeaks) {
392
+ return;
393
+ }
394
+ var ok = this._globals;
395
+
396
+ var globals = this.globalProps();
397
+ var leaks;
398
+
399
+ if (test) {
400
+ ok = ok.concat(test._allowedGlobals || []);
401
+ }
402
+
403
+ if (this.prevGlobalsLength === globals.length) {
404
+ return;
405
+ }
406
+ this.prevGlobalsLength = globals.length;
407
+
408
+ leaks = filterLeaks(ok, globals);
409
+ this._globals = this._globals.concat(leaks);
410
+
411
+ if (leaks.length) {
412
+ var msg = `global leak(s) detected: ${leaks.map(e => `'${e}'`).join(', ')}`;
413
+ this.fail(test, new Error(msg));
414
+ }
415
+ };
416
+
417
+ /**
418
+ * Fail the given `test`.
419
+ *
420
+ * If `test` is a hook, failures work in the following pattern:
421
+ * - If bail, run corresponding `after each` and `after` hooks,
422
+ * then exit
423
+ * - Failed `before` hook skips all tests in a suite and subsuites,
424
+ * but jumps to corresponding `after` hook
425
+ * - Failed `before each` hook skips remaining tests in a
426
+ * suite and jumps to corresponding `after each` hook,
427
+ * which is run only once
428
+ * - Failed `after` hook does not alter execution order
429
+ * - Failed `after each` hook skips remaining tests in a
430
+ * suite and subsuites, but executes other `after each`
431
+ * hooks
432
+ *
433
+ * @private
434
+ * @param {Runnable} test
435
+ * @param {Error} err
436
+ * @param {boolean} [force=false] - Whether to fail a pending test.
437
+ */
438
+ Runner.prototype.fail = function(test, err, force) {
439
+ force = force === true;
440
+ if (test.isPending() && !force) {
441
+ return;
442
+ }
443
+ if (this.state === constants.STATE_STOPPED) {
444
+ if (err.code === errorConstants.MULTIPLE_DONE) {
445
+ throw err;
446
+ }
447
+ throw createFatalError(
448
+ 'Test failed after root suite execution completed!',
449
+ err
450
+ );
451
+ }
452
+
453
+ ++this.failures;
454
+ debug('total number of failures: %d', this.failures);
455
+ test.state = STATE_FAILED;
456
+
457
+ if (!isError(err)) {
458
+ err = thrown2Error(err);
459
+ }
460
+
461
+ try {
462
+ err.stack =
463
+ this.fullStackTrace || !err.stack ? err.stack : stackFilter(err.stack);
464
+ } catch (ignore) {
465
+ // some environments do not take kindly to monkeying with the stack
466
+ }
467
+
468
+ this.emit(constants.EVENT_TEST_FAIL, test, err);
469
+ };
470
+
471
+ /**
472
+ * Run hook `name` callbacks and then invoke `fn()`.
473
+ *
474
+ * @private
475
+ * @param {string} name
476
+ * @param {Function} fn
477
+ */
478
+
479
+ Runner.prototype.hook = function(name, fn) {
480
+ if (this._opts.dryRun) return fn();
481
+
482
+ var suite = this.suite;
483
+ var hooks = suite.getHooks(name);
484
+ var self = this;
485
+
486
+ function next(i) {
487
+ var hook = hooks[i];
488
+ if (!hook) {
489
+ return fn();
490
+ }
491
+ self.currentRunnable = hook;
492
+
493
+ if (name === HOOK_TYPE_BEFORE_ALL) {
494
+ hook.ctx.currentTest = hook.parent.tests[0];
495
+ } else if (name === HOOK_TYPE_AFTER_ALL) {
496
+ hook.ctx.currentTest = hook.parent.tests[hook.parent.tests.length - 1];
497
+ } else {
498
+ hook.ctx.currentTest = self.test;
499
+ }
500
+
501
+ setHookTitle(hook);
502
+
503
+ hook.allowUncaught = self.allowUncaught;
504
+
505
+ self.emit(constants.EVENT_HOOK_BEGIN, hook);
506
+
507
+ if (!hook.listeners('error').length) {
508
+ self._addEventListener(hook, 'error', function(err) {
509
+ self.fail(hook, err);
510
+ });
511
+ }
512
+
513
+ hook.run(function cbHookRun(err) {
514
+ var testError = hook.error();
515
+ if (testError) {
516
+ self.fail(self.test, testError);
517
+ }
518
+ // conditional skip
519
+ if (hook.pending) {
520
+ if (name === HOOK_TYPE_AFTER_EACH) {
521
+ // TODO define and implement use case
522
+ if (self.test) {
523
+ self.test.pending = true;
524
+ }
525
+ } else if (name === HOOK_TYPE_BEFORE_EACH) {
526
+ if (self.test) {
527
+ self.test.pending = true;
528
+ }
529
+ self.emit(constants.EVENT_HOOK_END, hook);
530
+ hook.pending = false; // activates hook for next test
531
+ return fn(new Error('abort hookDown'));
532
+ } else if (name === HOOK_TYPE_BEFORE_ALL) {
533
+ suite.tests.forEach(function(test) {
534
+ test.pending = true;
535
+ });
536
+ suite.suites.forEach(function(suite) {
537
+ suite.pending = true;
538
+ });
539
+ hooks = [];
540
+ } else {
541
+ hook.pending = false;
542
+ var errForbid = createUnsupportedError('`this.skip` forbidden');
543
+ self.fail(hook, errForbid);
544
+ return fn(errForbid);
545
+ }
546
+ } else if (err) {
547
+ self.fail(hook, err);
548
+ // stop executing hooks, notify callee of hook err
549
+ return fn(err);
550
+ }
551
+ self.emit(constants.EVENT_HOOK_END, hook);
552
+ delete hook.ctx.currentTest;
553
+ setHookTitle(hook);
554
+ next(++i);
555
+ });
556
+
557
+ function setHookTitle(hook) {
558
+ hook.originalTitle = hook.originalTitle || hook.title;
559
+ if (hook.ctx && hook.ctx.currentTest) {
560
+ hook.title = `${hook.originalTitle} for "${hook.ctx.currentTest.title}"`;
561
+ } else {
562
+ var parentTitle;
563
+ if (hook.parent.title) {
564
+ parentTitle = hook.parent.title;
565
+ } else {
566
+ parentTitle = hook.parent.root ? '{root}' : '';
567
+ }
568
+ hook.title = `${hook.originalTitle} in "${parentTitle}"`;
569
+ }
570
+ }
571
+ }
572
+
573
+ Runner.immediately(function() {
574
+ next(0);
575
+ });
576
+ };
577
+
578
+ /**
579
+ * Run hook `name` for the given array of `suites`
580
+ * in order, and callback `fn(err, errSuite)`.
581
+ *
582
+ * @private
583
+ * @param {string} name
584
+ * @param {Array} suites
585
+ * @param {Function} fn
586
+ */
587
+ Runner.prototype.hooks = function(name, suites, fn) {
588
+ var self = this;
589
+ var orig = this.suite;
590
+
591
+ function next(suite) {
592
+ self.suite = suite;
593
+
594
+ if (!suite) {
595
+ self.suite = orig;
596
+ return fn();
597
+ }
598
+
599
+ self.hook(name, function(err) {
600
+ if (err) {
601
+ var errSuite = self.suite;
602
+ self.suite = orig;
603
+ return fn(err, errSuite);
604
+ }
605
+
606
+ next(suites.pop());
607
+ });
608
+ }
609
+
610
+ next(suites.pop());
611
+ };
612
+
613
+ /**
614
+ * Run 'afterEach' hooks from bottom up.
615
+ *
616
+ * @param {String} name
617
+ * @param {Function} fn
618
+ * @private
619
+ */
620
+ Runner.prototype.hookUp = function(name, fn) {
621
+ var suites = [this.suite].concat(this.parents()).reverse();
622
+ this.hooks(name, suites, fn);
623
+ };
624
+
625
+ /**
626
+ * Run 'beforeEach' hooks from top level down.
627
+ *
628
+ * @param {String} name
629
+ * @param {Function} fn
630
+ * @private
631
+ */
632
+ Runner.prototype.hookDown = function(name, fn) {
633
+ var suites = [this.suite].concat(this.parents());
634
+ this.hooks(name, suites, fn);
635
+ };
636
+
637
+ /**
638
+ * Return an array of parent Suites from
639
+ * closest to furthest.
640
+ *
641
+ * @return {Array}
642
+ * @private
643
+ */
644
+ Runner.prototype.parents = function() {
645
+ var suite = this.suite;
646
+ var suites = [];
647
+ while (suite.parent) {
648
+ suite = suite.parent;
649
+ suites.push(suite);
650
+ }
651
+ return suites;
652
+ };
653
+
654
+ /**
655
+ * Run the current test and callback `fn(err)`.
656
+ *
657
+ * @param {Function} fn
658
+ * @private
659
+ */
660
+ Runner.prototype.runTest = function(fn) {
661
+ if (this._opts.dryRun) return fn();
662
+
663
+ var self = this;
664
+ var test = this.test;
665
+
666
+ if (!test) {
667
+ return;
668
+ }
669
+
670
+ if (this.asyncOnly) {
671
+ test.asyncOnly = true;
672
+ }
673
+ this._addEventListener(test, 'error', function(err) {
674
+ self.fail(test, err);
675
+ });
676
+ if (this.allowUncaught) {
677
+ test.allowUncaught = true;
678
+ return test.run(fn);
679
+ }
680
+ try {
681
+ test.run(fn);
682
+ } catch (err) {
683
+ fn(err);
684
+ }
685
+ };
686
+
687
+ /**
688
+ * Run tests in the given `suite` and invoke the callback `fn()` when complete.
689
+ *
690
+ * @private
691
+ * @param {Suite} suite
692
+ * @param {Function} fn
693
+ */
694
+ Runner.prototype.runTests = function(suite, fn) {
695
+ var self = this;
696
+ var tests = suite.tests.slice();
697
+ var test;
698
+
699
+ function hookErr(_, errSuite, after) {
700
+ // before/after Each hook for errSuite failed:
701
+ var orig = self.suite;
702
+
703
+ // for failed 'after each' hook start from errSuite parent,
704
+ // otherwise start from errSuite itself
705
+ self.suite = after ? errSuite.parent : errSuite;
706
+
707
+ if (self.suite) {
708
+ self.hookUp(HOOK_TYPE_AFTER_EACH, function(err2, errSuite2) {
709
+ self.suite = orig;
710
+ // some hooks may fail even now
711
+ if (err2) {
712
+ return hookErr(err2, errSuite2, true);
713
+ }
714
+ // report error suite
715
+ fn(errSuite);
716
+ });
717
+ } else {
718
+ // there is no need calling other 'after each' hooks
719
+ self.suite = orig;
720
+ fn(errSuite);
721
+ }
722
+ }
723
+
724
+ function next(err, errSuite) {
725
+ // if we bail after first err
726
+ if (self.failures && suite._bail) {
727
+ tests = [];
728
+ }
729
+
730
+ if (self._abort) {
731
+ return fn();
732
+ }
733
+
734
+ if (err) {
735
+ return hookErr(err, errSuite, true);
736
+ }
737
+
738
+ // next test
739
+ test = tests.shift();
740
+
741
+ // all done
742
+ if (!test) {
743
+ return fn();
744
+ }
745
+
746
+ // grep
747
+ var match = self._grep.test(test.fullTitle());
748
+ if (self._invert) {
749
+ match = !match;
750
+ }
751
+ if (!match) {
752
+ // Run immediately only if we have defined a grep. When we
753
+ // define a grep — It can cause maximum callstack error if
754
+ // the grep is doing a large recursive loop by neglecting
755
+ // all tests. The run immediately function also comes with
756
+ // a performance cost. So we don't want to run immediately
757
+ // if we run the whole test suite, because running the whole
758
+ // test suite don't do any immediate recursive loops. Thus,
759
+ // allowing a JS runtime to breathe.
760
+ if (self._grep !== self._defaultGrep) {
761
+ Runner.immediately(next);
762
+ } else {
763
+ next();
764
+ }
765
+ return;
766
+ }
767
+
768
+ // static skip, no hooks are executed
769
+ if (test.isPending()) {
770
+ if (self.forbidPending) {
771
+ self.fail(test, new Error('Pending test forbidden'), true);
772
+ } else {
773
+ test.state = STATE_PENDING;
774
+ self.emit(constants.EVENT_TEST_PENDING, test);
775
+ }
776
+ self.emit(constants.EVENT_TEST_END, test);
777
+ return next();
778
+ }
779
+
780
+ // execute test and hook(s)
781
+ self.emit(constants.EVENT_TEST_BEGIN, (self.test = test));
782
+ self.hookDown(HOOK_TYPE_BEFORE_EACH, function(err, errSuite) {
783
+ // conditional skip within beforeEach
784
+ if (test.isPending()) {
785
+ if (self.forbidPending) {
786
+ self.fail(test, new Error('Pending test forbidden'), true);
787
+ } else {
788
+ test.state = STATE_PENDING;
789
+ self.emit(constants.EVENT_TEST_PENDING, test);
790
+ }
791
+ self.emit(constants.EVENT_TEST_END, test);
792
+ // skip inner afterEach hooks below errSuite level
793
+ var origSuite = self.suite;
794
+ self.suite = errSuite || self.suite;
795
+ return self.hookUp(HOOK_TYPE_AFTER_EACH, function(e, eSuite) {
796
+ self.suite = origSuite;
797
+ next(e, eSuite);
798
+ });
799
+ }
800
+ if (err) {
801
+ return hookErr(err, errSuite, false);
802
+ }
803
+ self.currentRunnable = self.test;
804
+ self.runTest(function(err) {
805
+ test = self.test;
806
+ // conditional skip within it
807
+ if (test.pending) {
808
+ if (self.forbidPending) {
809
+ self.fail(test, new Error('Pending test forbidden'), true);
810
+ } else {
811
+ test.state = STATE_PENDING;
812
+ self.emit(constants.EVENT_TEST_PENDING, test);
813
+ }
814
+ self.emit(constants.EVENT_TEST_END, test);
815
+ return self.hookUp(HOOK_TYPE_AFTER_EACH, next);
816
+ } else if (err) {
817
+ var retry = test.currentRetry();
818
+ if (retry < test.retries()) {
819
+ var clonedTest = test.clone();
820
+ clonedTest.currentRetry(retry + 1);
821
+ tests.unshift(clonedTest);
822
+
823
+ self.emit(constants.EVENT_TEST_RETRY, test, err);
824
+
825
+ // Early return + hook trigger so that it doesn't
826
+ // increment the count wrong
827
+ return self.hookUp(HOOK_TYPE_AFTER_EACH, next);
828
+ } else {
829
+ self.fail(test, err);
830
+ }
831
+ self.emit(constants.EVENT_TEST_END, test);
832
+ return self.hookUp(HOOK_TYPE_AFTER_EACH, next);
833
+ }
834
+
835
+ test.state = STATE_PASSED;
836
+ self.emit(constants.EVENT_TEST_PASS, test);
837
+ self.emit(constants.EVENT_TEST_END, test);
838
+ self.hookUp(HOOK_TYPE_AFTER_EACH, next);
839
+ });
840
+ });
841
+ }
842
+
843
+ this.next = next;
844
+ this.hookErr = hookErr;
845
+ next();
846
+ };
847
+
848
+ /**
849
+ * Run the given `suite` and invoke the callback `fn()` when complete.
850
+ *
851
+ * @private
852
+ * @param {Suite} suite
853
+ * @param {Function} fn
854
+ */
855
+ Runner.prototype.runSuite = function(suite, fn) {
856
+ var i = 0;
857
+ var self = this;
858
+ var total = this.grepTotal(suite);
859
+
860
+ debug('runSuite(): running %s', suite.fullTitle());
861
+
862
+ if (!total || (self.failures && suite._bail)) {
863
+ debug('runSuite(): bailing');
864
+ return fn();
865
+ }
866
+
867
+ this.emit(constants.EVENT_SUITE_BEGIN, (this.suite = suite));
868
+
869
+ function next(errSuite) {
870
+ if (errSuite) {
871
+ // current suite failed on a hook from errSuite
872
+ if (errSuite === suite) {
873
+ // if errSuite is current suite
874
+ // continue to the next sibling suite
875
+ return done();
876
+ }
877
+ // errSuite is among the parents of current suite
878
+ // stop execution of errSuite and all sub-suites
879
+ return done(errSuite);
880
+ }
881
+
882
+ if (self._abort) {
883
+ return done();
884
+ }
885
+
886
+ var curr = suite.suites[i++];
887
+ if (!curr) {
888
+ return done();
889
+ }
890
+
891
+ // Avoid grep neglecting large number of tests causing a
892
+ // huge recursive loop and thus a maximum call stack error.
893
+ // See comment in `this.runTests()` for more information.
894
+ if (self._grep !== self._defaultGrep) {
895
+ Runner.immediately(function() {
896
+ self.runSuite(curr, next);
897
+ });
898
+ } else {
899
+ self.runSuite(curr, next);
900
+ }
901
+ }
902
+
903
+ function done(errSuite) {
904
+ self.suite = suite;
905
+ self.nextSuite = next;
906
+
907
+ // remove reference to test
908
+ delete self.test;
909
+
910
+ self.hook(HOOK_TYPE_AFTER_ALL, function() {
911
+ self.emit(constants.EVENT_SUITE_END, suite);
912
+ fn(errSuite);
913
+ });
914
+ }
915
+
916
+ this.nextSuite = next;
917
+
918
+ this.hook(HOOK_TYPE_BEFORE_ALL, function(err) {
919
+ if (err) {
920
+ return done();
921
+ }
922
+ self.runTests(suite, next);
923
+ });
924
+ };
925
+
926
+ /**
927
+ * Handle uncaught exceptions within runner.
928
+ *
929
+ * This function is bound to the instance as `Runner#uncaught` at instantiation
930
+ * time. It's intended to be listening on the `Process.uncaughtException` event.
931
+ * In order to not leak EE listeners, we need to ensure no more than a single
932
+ * `uncaughtException` listener exists per `Runner`. The only way to do
933
+ * this--because this function needs the context (and we don't have lambdas)--is
934
+ * to use `Function.prototype.bind`. We need strict equality to unregister and
935
+ * _only_ unregister the _one_ listener we set from the
936
+ * `Process.uncaughtException` event; would be poor form to just remove
937
+ * everything. See {@link Runner#run} for where the event listener is registered
938
+ * and unregistered.
939
+ * @param {Error} err - Some uncaught error
940
+ * @private
941
+ */
942
+ Runner.prototype._uncaught = function(err) {
943
+ // this is defensive to prevent future developers from mis-calling this function.
944
+ // it's more likely that it'd be called with the incorrect context--say, the global
945
+ // `process` object--than it would to be called with a context that is not a "subclass"
946
+ // of `Runner`.
947
+ if (!(this instanceof Runner)) {
948
+ throw createFatalError(
949
+ 'Runner#uncaught() called with invalid context',
950
+ this
951
+ );
952
+ }
953
+ if (err instanceof Pending) {
954
+ debug('uncaught(): caught a Pending');
955
+ return;
956
+ }
957
+ // browser does not exit script when throwing in global.onerror()
958
+ if (this.allowUncaught && !utils.isBrowser()) {
959
+ debug('uncaught(): bubbling exception due to --allow-uncaught');
960
+ throw err;
961
+ }
962
+
963
+ if (this.state === constants.STATE_STOPPED) {
964
+ debug('uncaught(): throwing after run has completed!');
965
+ throw err;
966
+ }
967
+
968
+ if (err) {
969
+ debug('uncaught(): got truthy exception %O', err);
970
+ } else {
971
+ debug('uncaught(): undefined/falsy exception');
972
+ err = createInvalidExceptionError(
973
+ 'Caught falsy/undefined exception which would otherwise be uncaught. No stack trace found; try a debugger',
974
+ err
975
+ );
976
+ }
977
+
978
+ if (!isError(err)) {
979
+ err = thrown2Error(err);
980
+ debug('uncaught(): converted "error" %o to Error', err);
981
+ }
982
+ err.uncaught = true;
983
+
984
+ var runnable = this.currentRunnable;
985
+
986
+ if (!runnable) {
987
+ runnable = new Runnable('Uncaught error outside test suite');
988
+ debug('uncaught(): no current Runnable; created a phony one');
989
+ runnable.parent = this.suite;
990
+
991
+ if (this.state === constants.STATE_RUNNING) {
992
+ debug('uncaught(): failing gracefully');
993
+ this.fail(runnable, err);
994
+ } else {
995
+ // Can't recover from this failure
996
+ debug('uncaught(): test run has not yet started; unrecoverable');
997
+ this.emit(constants.EVENT_RUN_BEGIN);
998
+ this.fail(runnable, err);
999
+ this.emit(constants.EVENT_RUN_END);
1000
+ }
1001
+
1002
+ return;
1003
+ }
1004
+
1005
+ runnable.clearTimeout();
1006
+
1007
+ if (runnable.isFailed()) {
1008
+ debug('uncaught(): Runnable has already failed');
1009
+ // Ignore error if already failed
1010
+ return;
1011
+ } else if (runnable.isPending()) {
1012
+ debug('uncaught(): pending Runnable wound up failing!');
1013
+ // report 'pending test' retrospectively as failed
1014
+ this.fail(runnable, err, true);
1015
+ return;
1016
+ }
1017
+
1018
+ // we cannot recover gracefully if a Runnable has already passed
1019
+ // then fails asynchronously
1020
+ if (runnable.isPassed()) {
1021
+ debug('uncaught(): Runnable has already passed; bailing gracefully');
1022
+ this.fail(runnable, err);
1023
+ this.abort();
1024
+ } else {
1025
+ debug('uncaught(): forcing Runnable to complete with Error');
1026
+ return runnable.callback(err);
1027
+ }
1028
+ };
1029
+
1030
+ /**
1031
+ * Run the root suite and invoke `fn(failures)`
1032
+ * on completion.
1033
+ *
1034
+ * @public
1035
+ * @memberof Runner
1036
+ * @param {Function} fn - Callback when finished
1037
+ * @param {{files: string[], options: Options}} [opts] - For subclasses
1038
+ * @returns {Runner} Runner instance.
1039
+ */
1040
+ Runner.prototype.run = function(fn, opts = {}) {
1041
+ var rootSuite = this.suite;
1042
+ var options = opts.options || {};
1043
+
1044
+ debug('run(): got options: %O', options);
1045
+ fn = fn || function() {};
1046
+
1047
+ const end = () => {
1048
+ if (!this.total && this._opts.failZero) this.failures = 1;
1049
+
1050
+ debug('run(): root suite completed; emitting %s', constants.EVENT_RUN_END);
1051
+ this.emit(constants.EVENT_RUN_END);
1052
+ };
1053
+
1054
+ const begin = () => {
1055
+ debug('run(): emitting %s', constants.EVENT_RUN_BEGIN);
1056
+ this.emit(constants.EVENT_RUN_BEGIN);
1057
+ debug('run(): emitted %s', constants.EVENT_RUN_BEGIN);
1058
+
1059
+ this.runSuite(rootSuite, end);
1060
+ };
1061
+
1062
+ const prepare = () => {
1063
+ debug('run(): starting');
1064
+ // If there is an `only` filter
1065
+ if (rootSuite.hasOnly()) {
1066
+ rootSuite.filterOnly();
1067
+ debug('run(): filtered exclusive Runnables');
1068
+ }
1069
+ this.state = constants.STATE_RUNNING;
1070
+ if (this._delay) {
1071
+ this.emit(constants.EVENT_DELAY_END);
1072
+ debug('run(): "delay" ended');
1073
+ }
1074
+
1075
+ return begin();
1076
+ };
1077
+
1078
+ // references cleanup to avoid memory leaks
1079
+ if (this._opts.cleanReferencesAfterRun) {
1080
+ this.on(constants.EVENT_SUITE_END, suite => {
1081
+ suite.cleanReferences();
1082
+ });
1083
+ }
1084
+
1085
+ // callback
1086
+ this.on(constants.EVENT_RUN_END, function() {
1087
+ this.state = constants.STATE_STOPPED;
1088
+ debug('run(): emitted %s', constants.EVENT_RUN_END);
1089
+ fn(this.failures);
1090
+ });
1091
+
1092
+ this._removeEventListener(process, 'uncaughtException', this.uncaught);
1093
+ this._removeEventListener(process, 'unhandledRejection', this.unhandled);
1094
+ this._addEventListener(process, 'uncaughtException', this.uncaught);
1095
+ this._addEventListener(process, 'unhandledRejection', this.unhandled);
1096
+
1097
+ if (this._delay) {
1098
+ // for reporters, I guess.
1099
+ // might be nice to debounce some dots while we wait.
1100
+ this.emit(constants.EVENT_DELAY_BEGIN, rootSuite);
1101
+ rootSuite.once(EVENT_ROOT_SUITE_RUN, prepare);
1102
+ debug('run(): waiting for green light due to --delay');
1103
+ } else {
1104
+ Runner.immediately(prepare);
1105
+ }
1106
+
1107
+ return this;
1108
+ };
1109
+
1110
+ /**
1111
+ * Toggle partial object linking behavior; used for building object references from
1112
+ * unique ID's. Does nothing in serial mode, because the object references already exist.
1113
+ * Subclasses can implement this (e.g., `ParallelBufferedRunner`)
1114
+ * @abstract
1115
+ * @param {boolean} [value] - If `true`, enable partial object linking, otherwise disable
1116
+ * @returns {Runner}
1117
+ * @chainable
1118
+ * @public
1119
+ * @example
1120
+ * // this reporter needs proper object references when run in parallel mode
1121
+ * class MyReporter() {
1122
+ * constructor(runner) {
1123
+ * this.runner.linkPartialObjects(true)
1124
+ * .on(EVENT_SUITE_BEGIN, suite => {
1125
+ // this Suite may be the same object...
1126
+ * })
1127
+ * .on(EVENT_TEST_BEGIN, test => {
1128
+ * // ...as the `test.parent` property
1129
+ * });
1130
+ * }
1131
+ * }
1132
+ */
1133
+ Runner.prototype.linkPartialObjects = function(value) {
1134
+ return this;
1135
+ };
1136
+
1137
+ /*
1138
+ * Like {@link Runner#run}, but does not accept a callback and returns a `Promise` instead of a `Runner`.
1139
+ * This function cannot reject; an `unhandledRejection` event will bubble up to the `process` object instead.
1140
+ * @public
1141
+ * @memberof Runner
1142
+ * @param {Object} [opts] - Options for {@link Runner#run}
1143
+ * @returns {Promise<number>} Failure count
1144
+ */
1145
+ Runner.prototype.runAsync = async function runAsync(opts = {}) {
1146
+ return new Promise(resolve => {
1147
+ this.run(resolve, opts);
1148
+ });
1149
+ };
1150
+
1151
+ /**
1152
+ * Cleanly abort execution.
1153
+ *
1154
+ * @memberof Runner
1155
+ * @public
1156
+ * @return {Runner} Runner instance.
1157
+ */
1158
+ Runner.prototype.abort = function() {
1159
+ debug('abort(): aborting');
1160
+ this._abort = true;
1161
+
1162
+ return this;
1163
+ };
1164
+
1165
+ /**
1166
+ * Returns `true` if Mocha is running in parallel mode. For reporters.
1167
+ *
1168
+ * Subclasses should return an appropriate value.
1169
+ * @public
1170
+ * @returns {false}
1171
+ */
1172
+ Runner.prototype.isParallelMode = function isParallelMode() {
1173
+ return false;
1174
+ };
1175
+
1176
+ /**
1177
+ * Configures an alternate reporter for worker processes to use. Subclasses
1178
+ * using worker processes should implement this.
1179
+ * @public
1180
+ * @param {string} path - Absolute path to alternate reporter for worker processes to use
1181
+ * @returns {Runner}
1182
+ * @throws When in serial mode
1183
+ * @chainable
1184
+ * @abstract
1185
+ */
1186
+ Runner.prototype.workerReporter = function() {
1187
+ throw createUnsupportedError('workerReporter() not supported in serial mode');
1188
+ };
1189
+
1190
+ /**
1191
+ * Filter leaks with the given globals flagged as `ok`.
1192
+ *
1193
+ * @private
1194
+ * @param {Array} ok
1195
+ * @param {Array} globals
1196
+ * @return {Array}
1197
+ */
1198
+ function filterLeaks(ok, globals) {
1199
+ return globals.filter(function(key) {
1200
+ // Firefox and Chrome exposes iframes as index inside the window object
1201
+ if (/^\d+/.test(key)) {
1202
+ return false;
1203
+ }
1204
+
1205
+ // in firefox
1206
+ // if runner runs in an iframe, this iframe's window.getInterface method
1207
+ // not init at first it is assigned in some seconds
1208
+ if (global.navigator && /^getInterface/.test(key)) {
1209
+ return false;
1210
+ }
1211
+
1212
+ // an iframe could be approached by window[iframeIndex]
1213
+ // in ie6,7,8 and opera, iframeIndex is enumerable, this could cause leak
1214
+ if (global.navigator && /^\d+/.test(key)) {
1215
+ return false;
1216
+ }
1217
+
1218
+ // Opera and IE expose global variables for HTML element IDs (issue #243)
1219
+ if (/^mocha-/.test(key)) {
1220
+ return false;
1221
+ }
1222
+
1223
+ var matched = ok.filter(function(ok) {
1224
+ if (~ok.indexOf('*')) {
1225
+ return key.indexOf(ok.split('*')[0]) === 0;
1226
+ }
1227
+ return key === ok;
1228
+ });
1229
+ return !matched.length && (!global.navigator || key !== 'onerror');
1230
+ });
1231
+ }
1232
+
1233
+ /**
1234
+ * Check if argument is an instance of Error object or a duck-typed equivalent.
1235
+ *
1236
+ * @private
1237
+ * @param {Object} err - object to check
1238
+ * @param {string} err.message - error message
1239
+ * @returns {boolean}
1240
+ */
1241
+ function isError(err) {
1242
+ return err instanceof Error || (err && typeof err.message === 'string');
1243
+ }
1244
+
1245
+ /**
1246
+ *
1247
+ * Converts thrown non-extensible type into proper Error.
1248
+ *
1249
+ * @private
1250
+ * @param {*} thrown - Non-extensible type thrown by code
1251
+ * @return {Error}
1252
+ */
1253
+ function thrown2Error(err) {
1254
+ return new Error(
1255
+ `the ${utils.canonicalType(err)} ${stringify(
1256
+ err
1257
+ )} was thrown, throw an Error :)`
1258
+ );
1259
+ }
1260
+
1261
+ Runner.constants = constants;
1262
+
1263
+ /**
1264
+ * Node.js' `EventEmitter`
1265
+ * @external EventEmitter
1266
+ * @see {@link https://nodejs.org/api/events.html#events_class_eventemitter}
1267
+ */
1268
+
1269
+ module.exports = Runner;