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/suite.js CHANGED
@@ -1,642 +1,642 @@
1
- 'use strict';
2
-
3
- /**
4
- * Module dependencies.
5
- */
6
- var EventEmitter = require('events').EventEmitter;
7
- var Hook = require('./hook');
8
- var utils = require('./utils');
9
- var inherits = utils.inherits;
10
- var debug = require('debug')('mocha:suite');
11
- var milliseconds = require('ms');
12
- var errors = require('./errors');
13
- var createInvalidArgumentTypeError = errors.createInvalidArgumentTypeError;
14
-
15
- /**
16
- * Expose `Suite`.
17
- */
18
-
19
- exports = module.exports = Suite;
20
-
21
- /**
22
- * Create a new `Suite` with the given `title` and parent `Suite`.
23
- *
24
- * @public
25
- * @param {Suite} parent - Parent suite (required!)
26
- * @param {string} title - Title
27
- * @return {Suite}
28
- */
29
- Suite.create = function(parent, title) {
30
- var suite = new Suite(title, parent.ctx);
31
- suite.parent = parent;
32
- title = suite.fullTitle();
33
- parent.addSuite(suite);
34
- return suite;
35
- };
36
-
37
- /**
38
- * Constructs a new `Suite` instance with the given `title`, `ctx`, and `isRoot`.
39
- *
40
- * @public
41
- * @class
42
- * @extends EventEmitter
43
- * @see {@link https://nodejs.org/api/events.html#events_class_eventemitter|EventEmitter}
44
- * @param {string} title - Suite title.
45
- * @param {Context} parentContext - Parent context instance.
46
- * @param {boolean} [isRoot=false] - Whether this is the root suite.
47
- */
48
- function Suite(title, parentContext, isRoot) {
49
- if (!utils.isString(title)) {
50
- throw createInvalidArgumentTypeError(
51
- 'Suite argument "title" must be a string. Received type "' +
52
- typeof title +
53
- '"',
54
- 'title',
55
- 'string'
56
- );
57
- }
58
- this.title = title;
59
- function Context() {}
60
- Context.prototype = parentContext;
61
- this.ctx = new Context();
62
- this.suites = [];
63
- this.tests = [];
64
- this.pending = false;
65
- this._beforeEach = [];
66
- this._beforeAll = [];
67
- this._afterEach = [];
68
- this._afterAll = [];
69
- this.root = isRoot === true;
70
- this._timeout = 2000;
71
- this._enableTimeouts = true;
72
- this._slow = 75;
73
- this._bail = false;
74
- this._retries = -1;
75
- this._onlyTests = [];
76
- this._onlySuites = [];
77
- this.delayed = false;
78
-
79
- this.on('newListener', function(event) {
80
- if (deprecatedEvents[event]) {
81
- utils.deprecate(
82
- 'Event "' +
83
- event +
84
- '" is deprecated. Please let the Mocha team know about your use case: https://git.io/v6Lwm'
85
- );
86
- }
87
- });
88
- }
89
-
90
- /**
91
- * Inherit from `EventEmitter.prototype`.
92
- */
93
- inherits(Suite, EventEmitter);
94
-
95
- /**
96
- * Return a clone of this `Suite`.
97
- *
98
- * @private
99
- * @return {Suite}
100
- */
101
- Suite.prototype.clone = function() {
102
- var suite = new Suite(this.title);
103
- debug('clone');
104
- suite.ctx = this.ctx;
105
- suite.root = this.root;
106
- suite.timeout(this.timeout());
107
- suite.retries(this.retries());
108
- suite.enableTimeouts(this.enableTimeouts());
109
- suite.slow(this.slow());
110
- suite.bail(this.bail());
111
- return suite;
112
- };
113
-
114
- /**
115
- * Set or get timeout `ms` or short-hand such as "2s".
116
- *
117
- * @private
118
- * @todo Do not attempt to set value if `ms` is undefined
119
- * @param {number|string} ms
120
- * @return {Suite|number} for chaining
121
- */
122
- Suite.prototype.timeout = function(ms) {
123
- if (!arguments.length) {
124
- return this._timeout;
125
- }
126
- if (ms.toString() === '0') {
127
- this._enableTimeouts = false;
128
- }
129
- if (typeof ms === 'string') {
130
- ms = milliseconds(ms);
131
- }
132
- debug('timeout %d', ms);
133
- this._timeout = parseInt(ms, 10);
134
- return this;
135
- };
136
-
137
- /**
138
- * Set or get number of times to retry a failed test.
139
- *
140
- * @private
141
- * @param {number|string} n
142
- * @return {Suite|number} for chaining
143
- */
144
- Suite.prototype.retries = function(n) {
145
- if (!arguments.length) {
146
- return this._retries;
147
- }
148
- debug('retries %d', n);
149
- this._retries = parseInt(n, 10) || 0;
150
- return this;
151
- };
152
-
153
- /**
154
- * Set or get timeout to `enabled`.
155
- *
156
- * @private
157
- * @param {boolean} enabled
158
- * @return {Suite|boolean} self or enabled
159
- */
160
- Suite.prototype.enableTimeouts = function(enabled) {
161
- if (!arguments.length) {
162
- return this._enableTimeouts;
163
- }
164
- debug('enableTimeouts %s', enabled);
165
- this._enableTimeouts = enabled;
166
- return this;
167
- };
168
-
169
- /**
170
- * Set or get slow `ms` or short-hand such as "2s".
171
- *
172
- * @private
173
- * @param {number|string} ms
174
- * @return {Suite|number} for chaining
175
- */
176
- Suite.prototype.slow = function(ms) {
177
- if (!arguments.length) {
178
- return this._slow;
179
- }
180
- if (typeof ms === 'string') {
181
- ms = milliseconds(ms);
182
- }
183
- debug('slow %d', ms);
184
- this._slow = ms;
185
- return this;
186
- };
187
-
188
- /**
189
- * Set or get whether to bail after first error.
190
- *
191
- * @private
192
- * @param {boolean} bail
193
- * @return {Suite|number} for chaining
194
- */
195
- Suite.prototype.bail = function(bail) {
196
- if (!arguments.length) {
197
- return this._bail;
198
- }
199
- debug('bail %s', bail);
200
- this._bail = bail;
201
- return this;
202
- };
203
-
204
- /**
205
- * Check if this suite or its parent suite is marked as pending.
206
- *
207
- * @private
208
- */
209
- Suite.prototype.isPending = function() {
210
- return this.pending || (this.parent && this.parent.isPending());
211
- };
212
-
213
- /**
214
- * Generic hook-creator.
215
- * @private
216
- * @param {string} title - Title of hook
217
- * @param {Function} fn - Hook callback
218
- * @returns {Hook} A new hook
219
- */
220
- Suite.prototype._createHook = function(title, fn) {
221
- var hook = new Hook(title, fn);
222
- hook.parent = this;
223
- hook.timeout(this.timeout());
224
- hook.retries(this.retries());
225
- hook.enableTimeouts(this.enableTimeouts());
226
- hook.slow(this.slow());
227
- hook.ctx = this.ctx;
228
- hook.file = this.file;
229
- return hook;
230
- };
231
-
232
- /**
233
- * Run `fn(test[, done])` before running tests.
234
- *
235
- * @private
236
- * @param {string} title
237
- * @param {Function} fn
238
- * @return {Suite} for chaining
239
- */
240
- Suite.prototype.beforeAll = function(title, fn) {
241
- if (this.isPending()) {
242
- return this;
243
- }
244
- if (typeof title === 'function') {
245
- fn = title;
246
- title = fn.name;
247
- }
248
- title = '"before all" hook' + (title ? ': ' + title : '');
249
-
250
- var hook = this._createHook(title, fn);
251
- this._beforeAll.push(hook);
252
- this.emit(constants.EVENT_SUITE_ADD_HOOK_BEFORE_ALL, hook);
253
- return this;
254
- };
255
-
256
- /**
257
- * Run `fn(test[, done])` after running tests.
258
- *
259
- * @private
260
- * @param {string} title
261
- * @param {Function} fn
262
- * @return {Suite} for chaining
263
- */
264
- Suite.prototype.afterAll = function(title, fn) {
265
- if (this.isPending()) {
266
- return this;
267
- }
268
- if (typeof title === 'function') {
269
- fn = title;
270
- title = fn.name;
271
- }
272
- title = '"after all" hook' + (title ? ': ' + title : '');
273
-
274
- var hook = this._createHook(title, fn);
275
- this._afterAll.push(hook);
276
- this.emit(constants.EVENT_SUITE_ADD_HOOK_AFTER_ALL, hook);
277
- return this;
278
- };
279
-
280
- /**
281
- * Run `fn(test[, done])` before each test case.
282
- *
283
- * @private
284
- * @param {string} title
285
- * @param {Function} fn
286
- * @return {Suite} for chaining
287
- */
288
- Suite.prototype.beforeEach = function(title, fn) {
289
- if (this.isPending()) {
290
- return this;
291
- }
292
- if (typeof title === 'function') {
293
- fn = title;
294
- title = fn.name;
295
- }
296
- title = '"before each" hook' + (title ? ': ' + title : '');
297
-
298
- var hook = this._createHook(title, fn);
299
- this._beforeEach.push(hook);
300
- this.emit(constants.EVENT_SUITE_ADD_HOOK_BEFORE_EACH, hook);
301
- return this;
302
- };
303
-
304
- /**
305
- * Run `fn(test[, done])` after each test case.
306
- *
307
- * @private
308
- * @param {string} title
309
- * @param {Function} fn
310
- * @return {Suite} for chaining
311
- */
312
- Suite.prototype.afterEach = function(title, fn) {
313
- if (this.isPending()) {
314
- return this;
315
- }
316
- if (typeof title === 'function') {
317
- fn = title;
318
- title = fn.name;
319
- }
320
- title = '"after each" hook' + (title ? ': ' + title : '');
321
-
322
- var hook = this._createHook(title, fn);
323
- this._afterEach.push(hook);
324
- this.emit(constants.EVENT_SUITE_ADD_HOOK_AFTER_EACH, hook);
325
- return this;
326
- };
327
-
328
- /**
329
- * Add a test `suite`.
330
- *
331
- * @private
332
- * @param {Suite} suite
333
- * @return {Suite} for chaining
334
- */
335
- Suite.prototype.addSuite = function(suite) {
336
- suite.parent = this;
337
- suite.root = false;
338
- suite.timeout(this.timeout());
339
- suite.retries(this.retries());
340
- suite.enableTimeouts(this.enableTimeouts());
341
- suite.slow(this.slow());
342
- suite.bail(this.bail());
343
- this.suites.push(suite);
344
- this.emit(constants.EVENT_SUITE_ADD_SUITE, suite);
345
- return this;
346
- };
347
-
348
- /**
349
- * Add a `test` to this suite.
350
- *
351
- * @private
352
- * @param {Test} test
353
- * @return {Suite} for chaining
354
- */
355
- Suite.prototype.addTest = function(test) {
356
- test.parent = this;
357
- test.timeout(this.timeout());
358
- test.retries(this.retries());
359
- test.enableTimeouts(this.enableTimeouts());
360
- test.slow(this.slow());
361
- test.ctx = this.ctx;
362
- this.tests.push(test);
363
- this.emit(constants.EVENT_SUITE_ADD_TEST, test);
364
- return this;
365
- };
366
-
367
- /**
368
- * Return the full title generated by recursively concatenating the parent's
369
- * full title.
370
- *
371
- * @memberof Suite
372
- * @public
373
- * @return {string}
374
- */
375
- Suite.prototype.fullTitle = function() {
376
- return this.titlePath().join(' ');
377
- };
378
-
379
- /**
380
- * Return the title path generated by recursively concatenating the parent's
381
- * title path.
382
- *
383
- * @memberof Suite
384
- * @public
385
- * @return {string}
386
- */
387
- Suite.prototype.titlePath = function() {
388
- var result = [];
389
- if (this.parent) {
390
- result = result.concat(this.parent.titlePath());
391
- }
392
- if (!this.root) {
393
- result.push(this.title);
394
- }
395
- return result;
396
- };
397
-
398
- /**
399
- * Return the total number of tests.
400
- *
401
- * @memberof Suite
402
- * @public
403
- * @return {number}
404
- */
405
- Suite.prototype.total = function() {
406
- return (
407
- this.suites.reduce(function(sum, suite) {
408
- return sum + suite.total();
409
- }, 0) + this.tests.length
410
- );
411
- };
412
-
413
- /**
414
- * Iterates through each suite recursively to find all tests. Applies a
415
- * function in the format `fn(test)`.
416
- *
417
- * @private
418
- * @param {Function} fn
419
- * @return {Suite}
420
- */
421
- Suite.prototype.eachTest = function(fn) {
422
- this.tests.forEach(fn);
423
- this.suites.forEach(function(suite) {
424
- suite.eachTest(fn);
425
- });
426
- return this;
427
- };
428
-
429
- /**
430
- * This will run the root suite if we happen to be running in delayed mode.
431
- * @private
432
- */
433
- Suite.prototype.run = function run() {
434
- if (this.root) {
435
- this.emit(constants.EVENT_ROOT_SUITE_RUN);
436
- }
437
- };
438
-
439
- /**
440
- * Determines whether a suite has an `only` test or suite as a descendant.
441
- *
442
- * @private
443
- * @returns {Boolean}
444
- */
445
- Suite.prototype.hasOnly = function hasOnly() {
446
- return (
447
- this._onlyTests.length > 0 ||
448
- this._onlySuites.length > 0 ||
449
- this.suites.some(function(suite) {
450
- return suite.hasOnly();
451
- })
452
- );
453
- };
454
-
455
- /**
456
- * Filter suites based on `isOnly` logic.
457
- *
458
- * @private
459
- * @returns {Boolean}
460
- */
461
- Suite.prototype.filterOnly = function filterOnly() {
462
- if (this._onlyTests.length) {
463
- // If the suite contains `only` tests, run those and ignore any nested suites.
464
- this.tests = this._onlyTests;
465
- this.suites = [];
466
- } else {
467
- // Otherwise, do not run any of the tests in this suite.
468
- this.tests = [];
469
- this._onlySuites.forEach(function(onlySuite) {
470
- // If there are other `only` tests/suites nested in the current `only` suite, then filter that `only` suite.
471
- // Otherwise, all of the tests on this `only` suite should be run, so don't filter it.
472
- if (onlySuite.hasOnly()) {
473
- onlySuite.filterOnly();
474
- }
475
- });
476
- // Run the `only` suites, as well as any other suites that have `only` tests/suites as descendants.
477
- var onlySuites = this._onlySuites;
478
- this.suites = this.suites.filter(function(childSuite) {
479
- return onlySuites.indexOf(childSuite) !== -1 || childSuite.filterOnly();
480
- });
481
- }
482
- // Keep the suite only if there is something to run
483
- return this.tests.length > 0 || this.suites.length > 0;
484
- };
485
-
486
- /**
487
- * Adds a suite to the list of subsuites marked `only`.
488
- *
489
- * @private
490
- * @param {Suite} suite
491
- */
492
- Suite.prototype.appendOnlySuite = function(suite) {
493
- this._onlySuites.push(suite);
494
- };
495
-
496
- /**
497
- * Adds a test to the list of tests marked `only`.
498
- *
499
- * @private
500
- * @param {Test} test
501
- */
502
- Suite.prototype.appendOnlyTest = function(test) {
503
- this._onlyTests.push(test);
504
- };
505
-
506
- /**
507
- * Returns the array of hooks by hook name; see `HOOK_TYPE_*` constants.
508
- * @private
509
- */
510
- Suite.prototype.getHooks = function getHooks(name) {
511
- return this['_' + name];
512
- };
513
-
514
- /**
515
- * Cleans up the references to all the deferred functions
516
- * (before/after/beforeEach/afterEach) and tests of a Suite.
517
- * These must be deleted otherwise a memory leak can happen,
518
- * as those functions may reference variables from closures,
519
- * thus those variables can never be garbage collected as long
520
- * as the deferred functions exist.
521
- *
522
- * @private
523
- */
524
- Suite.prototype.cleanReferences = function cleanReferences() {
525
- function cleanArrReferences(arr) {
526
- for (var i = 0; i < arr.length; i++) {
527
- delete arr[i].fn;
528
- }
529
- }
530
-
531
- if (Array.isArray(this._beforeAll)) {
532
- cleanArrReferences(this._beforeAll);
533
- }
534
-
535
- if (Array.isArray(this._beforeEach)) {
536
- cleanArrReferences(this._beforeEach);
537
- }
538
-
539
- if (Array.isArray(this._afterAll)) {
540
- cleanArrReferences(this._afterAll);
541
- }
542
-
543
- if (Array.isArray(this._afterEach)) {
544
- cleanArrReferences(this._afterEach);
545
- }
546
-
547
- for (var i = 0; i < this.tests.length; i++) {
548
- delete this.tests[i].fn;
549
- }
550
- };
551
-
552
- var constants = utils.defineConstants(
553
- /**
554
- * {@link Suite}-related constants.
555
- * @public
556
- * @memberof Suite
557
- * @alias constants
558
- * @readonly
559
- * @static
560
- * @enum {string}
561
- */
562
- {
563
- /**
564
- * Event emitted after a test file has been loaded Not emitted in browser.
565
- */
566
- EVENT_FILE_POST_REQUIRE: 'post-require',
567
- /**
568
- * Event emitted before a test file has been loaded. In browser, this is emitted once an interface has been selected.
569
- */
570
- EVENT_FILE_PRE_REQUIRE: 'pre-require',
571
- /**
572
- * Event emitted immediately after a test file has been loaded. Not emitted in browser.
573
- */
574
- EVENT_FILE_REQUIRE: 'require',
575
- /**
576
- * Event emitted when `global.run()` is called (use with `delay` option)
577
- */
578
- EVENT_ROOT_SUITE_RUN: 'run',
579
-
580
- /**
581
- * Namespace for collection of a `Suite`'s "after all" hooks
582
- */
583
- HOOK_TYPE_AFTER_ALL: 'afterAll',
584
- /**
585
- * Namespace for collection of a `Suite`'s "after each" hooks
586
- */
587
- HOOK_TYPE_AFTER_EACH: 'afterEach',
588
- /**
589
- * Namespace for collection of a `Suite`'s "before all" hooks
590
- */
591
- HOOK_TYPE_BEFORE_ALL: 'beforeAll',
592
- /**
593
- * Namespace for collection of a `Suite`'s "before all" hooks
594
- */
595
- HOOK_TYPE_BEFORE_EACH: 'beforeEach',
596
-
597
- // the following events are all deprecated
598
-
599
- /**
600
- * Emitted after an "after all" `Hook` has been added to a `Suite`. Deprecated
601
- */
602
- EVENT_SUITE_ADD_HOOK_AFTER_ALL: 'afterAll',
603
- /**
604
- * Emitted after an "after each" `Hook` has been added to a `Suite` Deprecated
605
- */
606
- EVENT_SUITE_ADD_HOOK_AFTER_EACH: 'afterEach',
607
- /**
608
- * Emitted after an "before all" `Hook` has been added to a `Suite` Deprecated
609
- */
610
- EVENT_SUITE_ADD_HOOK_BEFORE_ALL: 'beforeAll',
611
- /**
612
- * Emitted after an "before each" `Hook` has been added to a `Suite` Deprecated
613
- */
614
- EVENT_SUITE_ADD_HOOK_BEFORE_EACH: 'beforeEach',
615
- /**
616
- * Emitted after a child `Suite` has been added to a `Suite`. Deprecated
617
- */
618
- EVENT_SUITE_ADD_SUITE: 'suite',
619
- /**
620
- * Emitted after a `Test` has been added to a `Suite`. Deprecated
621
- */
622
- EVENT_SUITE_ADD_TEST: 'test'
623
- }
624
- );
625
-
626
- /**
627
- * @summary There are no known use cases for these events.
628
- * @desc This is a `Set`-like object having all keys being the constant's string value and the value being `true`.
629
- * @todo Remove eventually
630
- * @type {Object<string,boolean>}
631
- * @ignore
632
- */
633
- var deprecatedEvents = Object.keys(constants)
634
- .filter(function(constant) {
635
- return constant.substring(0, 15) === 'EVENT_SUITE_ADD';
636
- })
637
- .reduce(function(acc, constant) {
638
- acc[constants[constant]] = true;
639
- return acc;
640
- }, utils.createMap());
641
-
642
- Suite.constants = constants;
1
+ 'use strict';
2
+
3
+ /**
4
+ * Module dependencies.
5
+ */
6
+ var EventEmitter = require('events').EventEmitter;
7
+ var Hook = require('./hook');
8
+ var utils = require('./utils');
9
+ var inherits = utils.inherits;
10
+ var debug = require('debug')('mocha:suite');
11
+ var milliseconds = require('ms');
12
+ var errors = require('./errors');
13
+ var createInvalidArgumentTypeError = errors.createInvalidArgumentTypeError;
14
+
15
+ /**
16
+ * Expose `Suite`.
17
+ */
18
+
19
+ exports = module.exports = Suite;
20
+
21
+ /**
22
+ * Create a new `Suite` with the given `title` and parent `Suite`.
23
+ *
24
+ * @public
25
+ * @param {Suite} parent - Parent suite (required!)
26
+ * @param {string} title - Title
27
+ * @return {Suite}
28
+ */
29
+ Suite.create = function(parent, title) {
30
+ var suite = new Suite(title, parent.ctx);
31
+ suite.parent = parent;
32
+ title = suite.fullTitle();
33
+ parent.addSuite(suite);
34
+ return suite;
35
+ };
36
+
37
+ /**
38
+ * Constructs a new `Suite` instance with the given `title`, `ctx`, and `isRoot`.
39
+ *
40
+ * @public
41
+ * @class
42
+ * @extends EventEmitter
43
+ * @see {@link https://nodejs.org/api/events.html#events_class_eventemitter|EventEmitter}
44
+ * @param {string} title - Suite title.
45
+ * @param {Context} parentContext - Parent context instance.
46
+ * @param {boolean} [isRoot=false] - Whether this is the root suite.
47
+ */
48
+ function Suite(title, parentContext, isRoot) {
49
+ if (!utils.isString(title)) {
50
+ throw createInvalidArgumentTypeError(
51
+ 'Suite argument "title" must be a string. Received type "' +
52
+ typeof title +
53
+ '"',
54
+ 'title',
55
+ 'string'
56
+ );
57
+ }
58
+ this.title = title;
59
+ function Context() {}
60
+ Context.prototype = parentContext;
61
+ this.ctx = new Context();
62
+ this.suites = [];
63
+ this.tests = [];
64
+ this.pending = false;
65
+ this._beforeEach = [];
66
+ this._beforeAll = [];
67
+ this._afterEach = [];
68
+ this._afterAll = [];
69
+ this.root = isRoot === true;
70
+ this._timeout = 2000;
71
+ this._enableTimeouts = true;
72
+ this._slow = 75;
73
+ this._bail = false;
74
+ this._retries = -1;
75
+ this._onlyTests = [];
76
+ this._onlySuites = [];
77
+ this.delayed = false;
78
+
79
+ this.on('newListener', function(event) {
80
+ if (deprecatedEvents[event]) {
81
+ utils.deprecate(
82
+ 'Event "' +
83
+ event +
84
+ '" is deprecated. Please let the Mocha team know about your use case: https://git.io/v6Lwm'
85
+ );
86
+ }
87
+ });
88
+ }
89
+
90
+ /**
91
+ * Inherit from `EventEmitter.prototype`.
92
+ */
93
+ inherits(Suite, EventEmitter);
94
+
95
+ /**
96
+ * Return a clone of this `Suite`.
97
+ *
98
+ * @private
99
+ * @return {Suite}
100
+ */
101
+ Suite.prototype.clone = function() {
102
+ var suite = new Suite(this.title);
103
+ debug('clone');
104
+ suite.ctx = this.ctx;
105
+ suite.root = this.root;
106
+ suite.timeout(this.timeout());
107
+ suite.retries(this.retries());
108
+ suite.enableTimeouts(this.enableTimeouts());
109
+ suite.slow(this.slow());
110
+ suite.bail(this.bail());
111
+ return suite;
112
+ };
113
+
114
+ /**
115
+ * Set or get timeout `ms` or short-hand such as "2s".
116
+ *
117
+ * @private
118
+ * @todo Do not attempt to set value if `ms` is undefined
119
+ * @param {number|string} ms
120
+ * @return {Suite|number} for chaining
121
+ */
122
+ Suite.prototype.timeout = function(ms) {
123
+ if (!arguments.length) {
124
+ return this._timeout;
125
+ }
126
+ if (ms.toString() === '0') {
127
+ this._enableTimeouts = false;
128
+ }
129
+ if (typeof ms === 'string') {
130
+ ms = milliseconds(ms);
131
+ }
132
+ debug('timeout %d', ms);
133
+ this._timeout = parseInt(ms, 10);
134
+ return this;
135
+ };
136
+
137
+ /**
138
+ * Set or get number of times to retry a failed test.
139
+ *
140
+ * @private
141
+ * @param {number|string} n
142
+ * @return {Suite|number} for chaining
143
+ */
144
+ Suite.prototype.retries = function(n) {
145
+ if (!arguments.length) {
146
+ return this._retries;
147
+ }
148
+ debug('retries %d', n);
149
+ this._retries = parseInt(n, 10) || 0;
150
+ return this;
151
+ };
152
+
153
+ /**
154
+ * Set or get timeout to `enabled`.
155
+ *
156
+ * @private
157
+ * @param {boolean} enabled
158
+ * @return {Suite|boolean} self or enabled
159
+ */
160
+ Suite.prototype.enableTimeouts = function(enabled) {
161
+ if (!arguments.length) {
162
+ return this._enableTimeouts;
163
+ }
164
+ debug('enableTimeouts %s', enabled);
165
+ this._enableTimeouts = enabled;
166
+ return this;
167
+ };
168
+
169
+ /**
170
+ * Set or get slow `ms` or short-hand such as "2s".
171
+ *
172
+ * @private
173
+ * @param {number|string} ms
174
+ * @return {Suite|number} for chaining
175
+ */
176
+ Suite.prototype.slow = function(ms) {
177
+ if (!arguments.length) {
178
+ return this._slow;
179
+ }
180
+ if (typeof ms === 'string') {
181
+ ms = milliseconds(ms);
182
+ }
183
+ debug('slow %d', ms);
184
+ this._slow = ms;
185
+ return this;
186
+ };
187
+
188
+ /**
189
+ * Set or get whether to bail after first error.
190
+ *
191
+ * @private
192
+ * @param {boolean} bail
193
+ * @return {Suite|number} for chaining
194
+ */
195
+ Suite.prototype.bail = function(bail) {
196
+ if (!arguments.length) {
197
+ return this._bail;
198
+ }
199
+ debug('bail %s', bail);
200
+ this._bail = bail;
201
+ return this;
202
+ };
203
+
204
+ /**
205
+ * Check if this suite or its parent suite is marked as pending.
206
+ *
207
+ * @private
208
+ */
209
+ Suite.prototype.isPending = function() {
210
+ return this.pending || (this.parent && this.parent.isPending());
211
+ };
212
+
213
+ /**
214
+ * Generic hook-creator.
215
+ * @private
216
+ * @param {string} title - Title of hook
217
+ * @param {Function} fn - Hook callback
218
+ * @returns {Hook} A new hook
219
+ */
220
+ Suite.prototype._createHook = function(title, fn) {
221
+ var hook = new Hook(title, fn);
222
+ hook.parent = this;
223
+ hook.timeout(this.timeout());
224
+ hook.retries(this.retries());
225
+ hook.enableTimeouts(this.enableTimeouts());
226
+ hook.slow(this.slow());
227
+ hook.ctx = this.ctx;
228
+ hook.file = this.file;
229
+ return hook;
230
+ };
231
+
232
+ /**
233
+ * Run `fn(test[, done])` before running tests.
234
+ *
235
+ * @private
236
+ * @param {string} title
237
+ * @param {Function} fn
238
+ * @return {Suite} for chaining
239
+ */
240
+ Suite.prototype.beforeAll = function(title, fn) {
241
+ if (this.isPending()) {
242
+ return this;
243
+ }
244
+ if (typeof title === 'function') {
245
+ fn = title;
246
+ title = fn.name;
247
+ }
248
+ title = '"before all" hook' + (title ? ': ' + title : '');
249
+
250
+ var hook = this._createHook(title, fn);
251
+ this._beforeAll.push(hook);
252
+ this.emit(constants.EVENT_SUITE_ADD_HOOK_BEFORE_ALL, hook);
253
+ return this;
254
+ };
255
+
256
+ /**
257
+ * Run `fn(test[, done])` after running tests.
258
+ *
259
+ * @private
260
+ * @param {string} title
261
+ * @param {Function} fn
262
+ * @return {Suite} for chaining
263
+ */
264
+ Suite.prototype.afterAll = function(title, fn) {
265
+ if (this.isPending()) {
266
+ return this;
267
+ }
268
+ if (typeof title === 'function') {
269
+ fn = title;
270
+ title = fn.name;
271
+ }
272
+ title = '"after all" hook' + (title ? ': ' + title : '');
273
+
274
+ var hook = this._createHook(title, fn);
275
+ this._afterAll.push(hook);
276
+ this.emit(constants.EVENT_SUITE_ADD_HOOK_AFTER_ALL, hook);
277
+ return this;
278
+ };
279
+
280
+ /**
281
+ * Run `fn(test[, done])` before each test case.
282
+ *
283
+ * @private
284
+ * @param {string} title
285
+ * @param {Function} fn
286
+ * @return {Suite} for chaining
287
+ */
288
+ Suite.prototype.beforeEach = function(title, fn) {
289
+ if (this.isPending()) {
290
+ return this;
291
+ }
292
+ if (typeof title === 'function') {
293
+ fn = title;
294
+ title = fn.name;
295
+ }
296
+ title = '"before each" hook' + (title ? ': ' + title : '');
297
+
298
+ var hook = this._createHook(title, fn);
299
+ this._beforeEach.push(hook);
300
+ this.emit(constants.EVENT_SUITE_ADD_HOOK_BEFORE_EACH, hook);
301
+ return this;
302
+ };
303
+
304
+ /**
305
+ * Run `fn(test[, done])` after each test case.
306
+ *
307
+ * @private
308
+ * @param {string} title
309
+ * @param {Function} fn
310
+ * @return {Suite} for chaining
311
+ */
312
+ Suite.prototype.afterEach = function(title, fn) {
313
+ if (this.isPending()) {
314
+ return this;
315
+ }
316
+ if (typeof title === 'function') {
317
+ fn = title;
318
+ title = fn.name;
319
+ }
320
+ title = '"after each" hook' + (title ? ': ' + title : '');
321
+
322
+ var hook = this._createHook(title, fn);
323
+ this._afterEach.push(hook);
324
+ this.emit(constants.EVENT_SUITE_ADD_HOOK_AFTER_EACH, hook);
325
+ return this;
326
+ };
327
+
328
+ /**
329
+ * Add a test `suite`.
330
+ *
331
+ * @private
332
+ * @param {Suite} suite
333
+ * @return {Suite} for chaining
334
+ */
335
+ Suite.prototype.addSuite = function(suite) {
336
+ suite.parent = this;
337
+ suite.root = false;
338
+ suite.timeout(this.timeout());
339
+ suite.retries(this.retries());
340
+ suite.enableTimeouts(this.enableTimeouts());
341
+ suite.slow(this.slow());
342
+ suite.bail(this.bail());
343
+ this.suites.push(suite);
344
+ this.emit(constants.EVENT_SUITE_ADD_SUITE, suite);
345
+ return this;
346
+ };
347
+
348
+ /**
349
+ * Add a `test` to this suite.
350
+ *
351
+ * @private
352
+ * @param {Test} test
353
+ * @return {Suite} for chaining
354
+ */
355
+ Suite.prototype.addTest = function(test) {
356
+ test.parent = this;
357
+ test.timeout(this.timeout());
358
+ test.retries(this.retries());
359
+ test.enableTimeouts(this.enableTimeouts());
360
+ test.slow(this.slow());
361
+ test.ctx = this.ctx;
362
+ this.tests.push(test);
363
+ this.emit(constants.EVENT_SUITE_ADD_TEST, test);
364
+ return this;
365
+ };
366
+
367
+ /**
368
+ * Return the full title generated by recursively concatenating the parent's
369
+ * full title.
370
+ *
371
+ * @memberof Suite
372
+ * @public
373
+ * @return {string}
374
+ */
375
+ Suite.prototype.fullTitle = function() {
376
+ return this.titlePath().join(' ');
377
+ };
378
+
379
+ /**
380
+ * Return the title path generated by recursively concatenating the parent's
381
+ * title path.
382
+ *
383
+ * @memberof Suite
384
+ * @public
385
+ * @return {string}
386
+ */
387
+ Suite.prototype.titlePath = function() {
388
+ var result = [];
389
+ if (this.parent) {
390
+ result = result.concat(this.parent.titlePath());
391
+ }
392
+ if (!this.root) {
393
+ result.push(this.title);
394
+ }
395
+ return result;
396
+ };
397
+
398
+ /**
399
+ * Return the total number of tests.
400
+ *
401
+ * @memberof Suite
402
+ * @public
403
+ * @return {number}
404
+ */
405
+ Suite.prototype.total = function() {
406
+ return (
407
+ this.suites.reduce(function(sum, suite) {
408
+ return sum + suite.total();
409
+ }, 0) + this.tests.length
410
+ );
411
+ };
412
+
413
+ /**
414
+ * Iterates through each suite recursively to find all tests. Applies a
415
+ * function in the format `fn(test)`.
416
+ *
417
+ * @private
418
+ * @param {Function} fn
419
+ * @return {Suite}
420
+ */
421
+ Suite.prototype.eachTest = function(fn) {
422
+ this.tests.forEach(fn);
423
+ this.suites.forEach(function(suite) {
424
+ suite.eachTest(fn);
425
+ });
426
+ return this;
427
+ };
428
+
429
+ /**
430
+ * This will run the root suite if we happen to be running in delayed mode.
431
+ * @private
432
+ */
433
+ Suite.prototype.run = function run() {
434
+ if (this.root) {
435
+ this.emit(constants.EVENT_ROOT_SUITE_RUN);
436
+ }
437
+ };
438
+
439
+ /**
440
+ * Determines whether a suite has an `only` test or suite as a descendant.
441
+ *
442
+ * @private
443
+ * @returns {Boolean}
444
+ */
445
+ Suite.prototype.hasOnly = function hasOnly() {
446
+ return (
447
+ this._onlyTests.length > 0 ||
448
+ this._onlySuites.length > 0 ||
449
+ this.suites.some(function(suite) {
450
+ return suite.hasOnly();
451
+ })
452
+ );
453
+ };
454
+
455
+ /**
456
+ * Filter suites based on `isOnly` logic.
457
+ *
458
+ * @private
459
+ * @returns {Boolean}
460
+ */
461
+ Suite.prototype.filterOnly = function filterOnly() {
462
+ if (this._onlyTests.length) {
463
+ // If the suite contains `only` tests, run those and ignore any nested suites.
464
+ this.tests = this._onlyTests;
465
+ this.suites = [];
466
+ } else {
467
+ // Otherwise, do not run any of the tests in this suite.
468
+ this.tests = [];
469
+ this._onlySuites.forEach(function(onlySuite) {
470
+ // If there are other `only` tests/suites nested in the current `only` suite, then filter that `only` suite.
471
+ // Otherwise, all of the tests on this `only` suite should be run, so don't filter it.
472
+ if (onlySuite.hasOnly()) {
473
+ onlySuite.filterOnly();
474
+ }
475
+ });
476
+ // Run the `only` suites, as well as any other suites that have `only` tests/suites as descendants.
477
+ var onlySuites = this._onlySuites;
478
+ this.suites = this.suites.filter(function(childSuite) {
479
+ return onlySuites.indexOf(childSuite) !== -1 || childSuite.filterOnly();
480
+ });
481
+ }
482
+ // Keep the suite only if there is something to run
483
+ return this.tests.length > 0 || this.suites.length > 0;
484
+ };
485
+
486
+ /**
487
+ * Adds a suite to the list of subsuites marked `only`.
488
+ *
489
+ * @private
490
+ * @param {Suite} suite
491
+ */
492
+ Suite.prototype.appendOnlySuite = function(suite) {
493
+ this._onlySuites.push(suite);
494
+ };
495
+
496
+ /**
497
+ * Adds a test to the list of tests marked `only`.
498
+ *
499
+ * @private
500
+ * @param {Test} test
501
+ */
502
+ Suite.prototype.appendOnlyTest = function(test) {
503
+ this._onlyTests.push(test);
504
+ };
505
+
506
+ /**
507
+ * Returns the array of hooks by hook name; see `HOOK_TYPE_*` constants.
508
+ * @private
509
+ */
510
+ Suite.prototype.getHooks = function getHooks(name) {
511
+ return this['_' + name];
512
+ };
513
+
514
+ /**
515
+ * Cleans up the references to all the deferred functions
516
+ * (before/after/beforeEach/afterEach) and tests of a Suite.
517
+ * These must be deleted otherwise a memory leak can happen,
518
+ * as those functions may reference variables from closures,
519
+ * thus those variables can never be garbage collected as long
520
+ * as the deferred functions exist.
521
+ *
522
+ * @private
523
+ */
524
+ Suite.prototype.cleanReferences = function cleanReferences() {
525
+ function cleanArrReferences(arr) {
526
+ for (var i = 0; i < arr.length; i++) {
527
+ delete arr[i].fn;
528
+ }
529
+ }
530
+
531
+ if (Array.isArray(this._beforeAll)) {
532
+ cleanArrReferences(this._beforeAll);
533
+ }
534
+
535
+ if (Array.isArray(this._beforeEach)) {
536
+ cleanArrReferences(this._beforeEach);
537
+ }
538
+
539
+ if (Array.isArray(this._afterAll)) {
540
+ cleanArrReferences(this._afterAll);
541
+ }
542
+
543
+ if (Array.isArray(this._afterEach)) {
544
+ cleanArrReferences(this._afterEach);
545
+ }
546
+
547
+ for (var i = 0; i < this.tests.length; i++) {
548
+ delete this.tests[i].fn;
549
+ }
550
+ };
551
+
552
+ var constants = utils.defineConstants(
553
+ /**
554
+ * {@link Suite}-related constants.
555
+ * @public
556
+ * @memberof Suite
557
+ * @alias constants
558
+ * @readonly
559
+ * @static
560
+ * @enum {string}
561
+ */
562
+ {
563
+ /**
564
+ * Event emitted after a test file has been loaded Not emitted in browser.
565
+ */
566
+ EVENT_FILE_POST_REQUIRE: 'post-require',
567
+ /**
568
+ * Event emitted before a test file has been loaded. In browser, this is emitted once an interface has been selected.
569
+ */
570
+ EVENT_FILE_PRE_REQUIRE: 'pre-require',
571
+ /**
572
+ * Event emitted immediately after a test file has been loaded. Not emitted in browser.
573
+ */
574
+ EVENT_FILE_REQUIRE: 'require',
575
+ /**
576
+ * Event emitted when `global.run()` is called (use with `delay` option)
577
+ */
578
+ EVENT_ROOT_SUITE_RUN: 'run',
579
+
580
+ /**
581
+ * Namespace for collection of a `Suite`'s "after all" hooks
582
+ */
583
+ HOOK_TYPE_AFTER_ALL: 'afterAll',
584
+ /**
585
+ * Namespace for collection of a `Suite`'s "after each" hooks
586
+ */
587
+ HOOK_TYPE_AFTER_EACH: 'afterEach',
588
+ /**
589
+ * Namespace for collection of a `Suite`'s "before all" hooks
590
+ */
591
+ HOOK_TYPE_BEFORE_ALL: 'beforeAll',
592
+ /**
593
+ * Namespace for collection of a `Suite`'s "before all" hooks
594
+ */
595
+ HOOK_TYPE_BEFORE_EACH: 'beforeEach',
596
+
597
+ // the following events are all deprecated
598
+
599
+ /**
600
+ * Emitted after an "after all" `Hook` has been added to a `Suite`. Deprecated
601
+ */
602
+ EVENT_SUITE_ADD_HOOK_AFTER_ALL: 'afterAll',
603
+ /**
604
+ * Emitted after an "after each" `Hook` has been added to a `Suite` Deprecated
605
+ */
606
+ EVENT_SUITE_ADD_HOOK_AFTER_EACH: 'afterEach',
607
+ /**
608
+ * Emitted after an "before all" `Hook` has been added to a `Suite` Deprecated
609
+ */
610
+ EVENT_SUITE_ADD_HOOK_BEFORE_ALL: 'beforeAll',
611
+ /**
612
+ * Emitted after an "before each" `Hook` has been added to a `Suite` Deprecated
613
+ */
614
+ EVENT_SUITE_ADD_HOOK_BEFORE_EACH: 'beforeEach',
615
+ /**
616
+ * Emitted after a child `Suite` has been added to a `Suite`. Deprecated
617
+ */
618
+ EVENT_SUITE_ADD_SUITE: 'suite',
619
+ /**
620
+ * Emitted after a `Test` has been added to a `Suite`. Deprecated
621
+ */
622
+ EVENT_SUITE_ADD_TEST: 'test'
623
+ }
624
+ );
625
+
626
+ /**
627
+ * @summary There are no known use cases for these events.
628
+ * @desc This is a `Set`-like object having all keys being the constant's string value and the value being `true`.
629
+ * @todo Remove eventually
630
+ * @type {Object<string,boolean>}
631
+ * @ignore
632
+ */
633
+ var deprecatedEvents = Object.keys(constants)
634
+ .filter(function(constant) {
635
+ return constant.substring(0, 15) === 'EVENT_SUITE_ADD';
636
+ })
637
+ .reduce(function(acc, constant) {
638
+ acc[constants[constant]] = true;
639
+ return acc;
640
+ }, utils.createMap());
641
+
642
+ Suite.constants = constants;