mocha 7.1.2 → 8.1.0

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 (49) hide show
  1. package/CHANGELOG.md +143 -2
  2. package/bin/mocha +24 -4
  3. package/browser-entry.js +37 -9
  4. package/lib/browser/growl.js +2 -1
  5. package/lib/browser/highlight-tags.js +39 -0
  6. package/lib/browser/parse-query.js +24 -0
  7. package/lib/browser/progress.js +4 -0
  8. package/lib/browser/template.html +7 -5
  9. package/lib/cli/cli.js +6 -3
  10. package/lib/cli/collect-files.js +17 -10
  11. package/lib/cli/config.js +6 -6
  12. package/lib/cli/init.js +1 -2
  13. package/lib/cli/lookup-files.js +145 -0
  14. package/lib/cli/node-flags.js +2 -2
  15. package/lib/cli/options.js +14 -90
  16. package/lib/cli/run-helpers.js +133 -36
  17. package/lib/cli/run-option-metadata.js +4 -2
  18. package/lib/cli/run.js +71 -11
  19. package/lib/cli/watch-run.js +211 -51
  20. package/lib/context.js +0 -15
  21. package/lib/errors.js +202 -9
  22. package/lib/esm-utils.js +11 -6
  23. package/lib/hook.js +32 -0
  24. package/lib/interfaces/common.js +21 -10
  25. package/lib/mocha.js +301 -126
  26. package/lib/mocharc.json +0 -1
  27. package/lib/nodejs/buffered-worker-pool.js +174 -0
  28. package/lib/{growl.js → nodejs/growl.js} +3 -2
  29. package/lib/nodejs/parallel-buffered-runner.js +295 -0
  30. package/lib/nodejs/reporters/parallel-buffered.js +133 -0
  31. package/lib/nodejs/serializer.js +404 -0
  32. package/lib/nodejs/worker.js +154 -0
  33. package/lib/pending.js +4 -0
  34. package/lib/reporters/base.js +25 -12
  35. package/lib/reporters/doc.js +6 -0
  36. package/lib/reporters/json-stream.js +1 -0
  37. package/lib/reporters/json.js +1 -0
  38. package/lib/reporters/landing.js +11 -3
  39. package/lib/reporters/tap.js +1 -2
  40. package/lib/reporters/xunit.js +3 -2
  41. package/lib/runnable.js +39 -47
  42. package/lib/runner.js +219 -118
  43. package/lib/suite.js +61 -27
  44. package/lib/test.js +48 -3
  45. package/lib/utils.js +33 -209
  46. package/mocha.js +25522 -17715
  47. package/mocha.js.map +1 -0
  48. package/package.json +87 -68
  49. package/lib/browser/tty.js +0 -13
package/lib/suite.js CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  /**
4
4
  * Module dependencies.
5
+ * @private
5
6
  */
6
7
  var EventEmitter = require('events').EventEmitter;
7
8
  var Hook = require('./hook');
@@ -61,20 +62,19 @@ function Suite(title, parentContext, isRoot) {
61
62
  this.ctx = new Context();
62
63
  this.suites = [];
63
64
  this.tests = [];
65
+ this.root = isRoot === true;
64
66
  this.pending = false;
67
+ this._retries = -1;
65
68
  this._beforeEach = [];
66
69
  this._beforeAll = [];
67
70
  this._afterEach = [];
68
71
  this._afterAll = [];
69
- this.root = isRoot === true;
70
72
  this._timeout = 2000;
71
- this._enableTimeouts = true;
72
73
  this._slow = 75;
73
74
  this._bail = false;
74
- this._retries = -1;
75
75
  this._onlyTests = [];
76
76
  this._onlySuites = [];
77
- this.delayed = false;
77
+ this.reset();
78
78
 
79
79
  this.on('newListener', function(event) {
80
80
  if (deprecatedEvents[event]) {
@@ -92,6 +92,22 @@ function Suite(title, parentContext, isRoot) {
92
92
  */
93
93
  inherits(Suite, EventEmitter);
94
94
 
95
+ /**
96
+ * Resets the state initially or for a next run.
97
+ */
98
+ Suite.prototype.reset = function() {
99
+ this.delayed = false;
100
+ function doReset(thingToReset) {
101
+ thingToReset.reset();
102
+ }
103
+ this.suites.forEach(doReset);
104
+ this.tests.forEach(doReset);
105
+ this._beforeEach.forEach(doReset);
106
+ this._afterEach.forEach(doReset);
107
+ this._beforeAll.forEach(doReset);
108
+ this._afterAll.forEach(doReset);
109
+ };
110
+
95
111
  /**
96
112
  * Return a clone of this `Suite`.
97
113
  *
@@ -105,7 +121,6 @@ Suite.prototype.clone = function() {
105
121
  suite.root = this.root;
106
122
  suite.timeout(this.timeout());
107
123
  suite.retries(this.retries());
108
- suite.enableTimeouts(this.enableTimeouts());
109
124
  suite.slow(this.slow());
110
125
  suite.bail(this.bail());
111
126
  return suite;
@@ -123,12 +138,15 @@ Suite.prototype.timeout = function(ms) {
123
138
  if (!arguments.length) {
124
139
  return this._timeout;
125
140
  }
126
- if (ms.toString() === '0') {
127
- this._enableTimeouts = false;
128
- }
129
141
  if (typeof ms === 'string') {
130
142
  ms = milliseconds(ms);
131
143
  }
144
+
145
+ // Clamp to range
146
+ var INT_MAX = Math.pow(2, 31) - 1;
147
+ var range = [0, INT_MAX];
148
+ ms = utils.clamp(ms, range);
149
+
132
150
  debug('timeout %d', ms);
133
151
  this._timeout = parseInt(ms, 10);
134
152
  return this;
@@ -150,22 +168,6 @@ Suite.prototype.retries = function(n) {
150
168
  return this;
151
169
  };
152
170
 
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
171
  /**
170
172
  * Set or get slow `ms` or short-hand such as "2s".
171
173
  *
@@ -222,7 +224,6 @@ Suite.prototype._createHook = function(title, fn) {
222
224
  hook.parent = this;
223
225
  hook.timeout(this.timeout());
224
226
  hook.retries(this.retries());
225
- hook.enableTimeouts(this.enableTimeouts());
226
227
  hook.slow(this.slow());
227
228
  hook.ctx = this.ctx;
228
229
  hook.file = this.file;
@@ -337,7 +338,6 @@ Suite.prototype.addSuite = function(suite) {
337
338
  suite.root = false;
338
339
  suite.timeout(this.timeout());
339
340
  suite.retries(this.retries());
340
- suite.enableTimeouts(this.enableTimeouts());
341
341
  suite.slow(this.slow());
342
342
  suite.bail(this.bail());
343
343
  this.suites.push(suite);
@@ -356,7 +356,6 @@ Suite.prototype.addTest = function(test) {
356
356
  test.parent = this;
357
357
  test.timeout(this.timeout());
358
358
  test.retries(this.retries());
359
- test.enableTimeouts(this.enableTimeouts());
360
359
  test.slow(this.slow());
361
360
  test.ctx = this.ctx;
362
361
  this.tests.push(test);
@@ -493,6 +492,15 @@ Suite.prototype.appendOnlySuite = function(suite) {
493
492
  this._onlySuites.push(suite);
494
493
  };
495
494
 
495
+ /**
496
+ * Marks a suite to be `only`.
497
+ *
498
+ * @private
499
+ */
500
+ Suite.prototype.markOnly = function() {
501
+ this.parent && this.parent.appendOnlySuite(this);
502
+ };
503
+
496
504
  /**
497
505
  * Adds a test to the list of tests marked `only`.
498
506
  *
@@ -511,6 +519,16 @@ Suite.prototype.getHooks = function getHooks(name) {
511
519
  return this['_' + name];
512
520
  };
513
521
 
522
+ /**
523
+ * cleans all references from this suite and all child suites.
524
+ */
525
+ Suite.prototype.dispose = function() {
526
+ this.suites.forEach(function(suite) {
527
+ suite.dispose();
528
+ });
529
+ this.cleanReferences();
530
+ };
531
+
514
532
  /**
515
533
  * Cleans up the references to all the deferred functions
516
534
  * (before/after/beforeEach/afterEach) and tests of a Suite.
@@ -549,6 +567,22 @@ Suite.prototype.cleanReferences = function cleanReferences() {
549
567
  }
550
568
  };
551
569
 
570
+ /**
571
+ * Returns an object suitable for IPC.
572
+ * Functions are represented by keys beginning with `$$`.
573
+ * @private
574
+ * @returns {Object}
575
+ */
576
+ Suite.prototype.serialize = function serialize() {
577
+ return {
578
+ _bail: this._bail,
579
+ $$fullTitle: this.fullTitle(),
580
+ $$isPending: this.isPending(),
581
+ root: this.root,
582
+ title: this.title
583
+ };
584
+ };
585
+
552
586
  var constants = utils.defineConstants(
553
587
  /**
554
588
  * {@link Suite}-related constants.
package/lib/test.js CHANGED
@@ -26,9 +26,9 @@ function Test(title, fn) {
26
26
  'string'
27
27
  );
28
28
  }
29
- Runnable.call(this, title, fn);
30
- this.pending = !fn;
31
29
  this.type = 'test';
30
+ Runnable.call(this, title, fn);
31
+ this.reset();
32
32
  }
33
33
 
34
34
  /**
@@ -36,6 +36,15 @@ function Test(title, fn) {
36
36
  */
37
37
  utils.inherits(Test, Runnable);
38
38
 
39
+ /**
40
+ * Resets the state initially or for a next run.
41
+ */
42
+ Test.prototype.reset = function() {
43
+ Runnable.prototype.reset.call(this);
44
+ this.pending = !this.fn;
45
+ delete this.state;
46
+ };
47
+
39
48
  /**
40
49
  * Set or get retried test
41
50
  *
@@ -48,11 +57,19 @@ Test.prototype.retriedTest = function(n) {
48
57
  this._retriedTest = n;
49
58
  };
50
59
 
60
+ /**
61
+ * Add test to the list of tests marked `only`.
62
+ *
63
+ * @private
64
+ */
65
+ Test.prototype.markOnly = function() {
66
+ this.parent.appendOnlyTest(this);
67
+ };
68
+
51
69
  Test.prototype.clone = function() {
52
70
  var test = new Test(this.title, this.fn);
53
71
  test.timeout(this.timeout());
54
72
  test.slow(this.slow());
55
- test.enableTimeouts(this.enableTimeouts());
56
73
  test.retries(this.retries());
57
74
  test.currentRetry(this.currentRetry());
58
75
  test.retriedTest(this.retriedTest() || this);
@@ -62,3 +79,31 @@ Test.prototype.clone = function() {
62
79
  test.ctx = this.ctx;
63
80
  return test;
64
81
  };
82
+
83
+ /**
84
+ * Returns an minimal object suitable for transmission over IPC.
85
+ * Functions are represented by keys beginning with `$$`.
86
+ * @private
87
+ * @returns {Object}
88
+ */
89
+ Test.prototype.serialize = function serialize() {
90
+ return {
91
+ $$currentRetry: this._currentRetry,
92
+ $$fullTitle: this.fullTitle(),
93
+ $$isPending: this.pending,
94
+ $$retriedTest: this._retriedTest || null,
95
+ $$slow: this._slow,
96
+ $$titlePath: this.titlePath(),
97
+ body: this.body,
98
+ duration: this.duration,
99
+ err: this.err,
100
+ parent: {
101
+ $$fullTitle: this.parent.fullTitle()
102
+ },
103
+ speed: this.speed,
104
+ state: this.state,
105
+ title: this.title,
106
+ type: this.type,
107
+ file: this.file
108
+ };
109
+ };
package/lib/utils.js CHANGED
@@ -9,14 +9,9 @@
9
9
  * Module dependencies.
10
10
  */
11
11
 
12
- var fs = require('fs');
13
12
  var path = require('path');
14
13
  var util = require('util');
15
- var glob = require('glob');
16
14
  var he = require('he');
17
- var errors = require('./errors');
18
- var createNoFilesMatchPatternError = errors.createNoFilesMatchPatternError;
19
- var createMissingArgumentError = errors.createMissingArgumentError;
20
15
 
21
16
  var assign = (exports.assign = require('object.assign').getPolyfill());
22
17
 
@@ -63,8 +58,9 @@ exports.isString = function(obj) {
63
58
  exports.slug = function(str) {
64
59
  return str
65
60
  .toLowerCase()
66
- .replace(/ +/g, '-')
67
- .replace(/[^-\w]/g, '');
61
+ .replace(/\s+/g, '-')
62
+ .replace(/[^-\w]/g, '')
63
+ .replace(/-{2,}/g, '-');
68
64
  };
69
65
 
70
66
  /**
@@ -95,67 +91,6 @@ exports.clean = function(str) {
95
91
  return str.trim();
96
92
  };
97
93
 
98
- /**
99
- * Parse the given `qs`.
100
- *
101
- * @private
102
- * @param {string} qs
103
- * @return {Object}
104
- */
105
- exports.parseQuery = function(qs) {
106
- return qs
107
- .replace('?', '')
108
- .split('&')
109
- .reduce(function(obj, pair) {
110
- var i = pair.indexOf('=');
111
- var key = pair.slice(0, i);
112
- var val = pair.slice(++i);
113
-
114
- // Due to how the URLSearchParams API treats spaces
115
- obj[key] = decodeURIComponent(val.replace(/\+/g, '%20'));
116
-
117
- return obj;
118
- }, {});
119
- };
120
-
121
- /**
122
- * Highlight the given string of `js`.
123
- *
124
- * @private
125
- * @param {string} js
126
- * @return {string}
127
- */
128
- function highlight(js) {
129
- return js
130
- .replace(/</g, '&lt;')
131
- .replace(/>/g, '&gt;')
132
- .replace(/\/\/(.*)/gm, '<span class="comment">//$1</span>')
133
- .replace(/('.*?')/gm, '<span class="string">$1</span>')
134
- .replace(/(\d+\.\d+)/gm, '<span class="number">$1</span>')
135
- .replace(/(\d+)/gm, '<span class="number">$1</span>')
136
- .replace(
137
- /\bnew[ \t]+(\w+)/gm,
138
- '<span class="keyword">new</span> <span class="init">$1</span>'
139
- )
140
- .replace(
141
- /\b(function|new|throw|return|var|if|else)\b/gm,
142
- '<span class="keyword">$1</span>'
143
- );
144
- }
145
-
146
- /**
147
- * Highlight the contents of tag `name`.
148
- *
149
- * @private
150
- * @param {string} name
151
- */
152
- exports.highlightTags = function(name) {
153
- var code = document.getElementById('mocha').getElementsByTagName(name);
154
- for (var i = 0, len = code.length; i < len; ++i) {
155
- code[i].innerHTML = highlight(code[i].innerHTML);
156
- }
157
- };
158
-
159
94
  /**
160
95
  * If a value could have properties, and has none, this function is called,
161
96
  * which returns a string representation of the empty value.
@@ -443,141 +378,6 @@ exports.canonicalize = function canonicalize(value, stack, typeHint) {
443
378
  return canonicalizedObj;
444
379
  };
445
380
 
446
- /**
447
- * Determines if pathname has a matching file extension.
448
- *
449
- * @private
450
- * @param {string} pathname - Pathname to check for match.
451
- * @param {string[]} exts - List of file extensions (sans period).
452
- * @return {boolean} whether file extension matches.
453
- * @example
454
- * hasMatchingExtname('foo.html', ['js', 'css']); // => false
455
- */
456
- function hasMatchingExtname(pathname, exts) {
457
- var suffix = path.extname(pathname).slice(1);
458
- return exts.some(function(element) {
459
- return suffix === element;
460
- });
461
- }
462
-
463
- /**
464
- * Determines if pathname would be a "hidden" file (or directory) on UN*X.
465
- *
466
- * @description
467
- * On UN*X, pathnames beginning with a full stop (aka dot) are hidden during
468
- * typical usage. Dotfiles, plain-text configuration files, are prime examples.
469
- *
470
- * @see {@link http://xahlee.info/UnixResource_dir/writ/unix_origin_of_dot_filename.html|Origin of Dot File Names}
471
- *
472
- * @private
473
- * @param {string} pathname - Pathname to check for match.
474
- * @return {boolean} whether pathname would be considered a hidden file.
475
- * @example
476
- * isHiddenOnUnix('.profile'); // => true
477
- */
478
- function isHiddenOnUnix(pathname) {
479
- return path.basename(pathname)[0] === '.';
480
- }
481
-
482
- /**
483
- * Lookup file names at the given `path`.
484
- *
485
- * @description
486
- * Filenames are returned in _traversal_ order by the OS/filesystem.
487
- * **Make no assumption that the names will be sorted in any fashion.**
488
- *
489
- * @public
490
- * @memberof Mocha.utils
491
- * @param {string} filepath - Base path to start searching from.
492
- * @param {string[]} [extensions=[]] - File extensions to look for.
493
- * @param {boolean} [recursive=false] - Whether to recurse into subdirectories.
494
- * @return {string[]} An array of paths.
495
- * @throws {Error} if no files match pattern.
496
- * @throws {TypeError} if `filepath` is directory and `extensions` not provided.
497
- */
498
- exports.lookupFiles = function lookupFiles(filepath, extensions, recursive) {
499
- extensions = extensions || [];
500
- recursive = recursive || false;
501
- var files = [];
502
- var stat;
503
-
504
- if (!fs.existsSync(filepath)) {
505
- var pattern;
506
- if (glob.hasMagic(filepath)) {
507
- // Handle glob as is without extensions
508
- pattern = filepath;
509
- } else {
510
- // glob pattern e.g. 'filepath+(.js|.ts)'
511
- var strExtensions = extensions
512
- .map(function(v) {
513
- return '.' + v;
514
- })
515
- .join('|');
516
- pattern = filepath + '+(' + strExtensions + ')';
517
- }
518
- files = glob.sync(pattern, {nodir: true});
519
- if (!files.length) {
520
- throw createNoFilesMatchPatternError(
521
- 'Cannot find any files matching pattern ' + exports.dQuote(filepath),
522
- filepath
523
- );
524
- }
525
- return files;
526
- }
527
-
528
- // Handle file
529
- try {
530
- stat = fs.statSync(filepath);
531
- if (stat.isFile()) {
532
- return filepath;
533
- }
534
- } catch (err) {
535
- // ignore error
536
- return;
537
- }
538
-
539
- // Handle directory
540
- fs.readdirSync(filepath).forEach(function(dirent) {
541
- var pathname = path.join(filepath, dirent);
542
- var stat;
543
-
544
- try {
545
- stat = fs.statSync(pathname);
546
- if (stat.isDirectory()) {
547
- if (recursive) {
548
- files = files.concat(lookupFiles(pathname, extensions, recursive));
549
- }
550
- return;
551
- }
552
- } catch (err) {
553
- // ignore error
554
- return;
555
- }
556
- if (!extensions.length) {
557
- throw createMissingArgumentError(
558
- util.format(
559
- 'Argument %s required when argument %s is a directory',
560
- exports.sQuote('extensions'),
561
- exports.sQuote('filepath')
562
- ),
563
- 'extensions',
564
- 'array'
565
- );
566
- }
567
-
568
- if (
569
- !stat.isFile() ||
570
- !hasMatchingExtname(pathname, extensions) ||
571
- isHiddenOnUnix(pathname)
572
- ) {
573
- return;
574
- }
575
- files.push(pathname);
576
- });
577
-
578
- return files;
579
- };
580
-
581
381
  /**
582
382
  * process.emitWarning or a polyfill
583
383
  * @see https://nodejs.org/api/process.html#process_process_emitwarning_warning_options
@@ -637,7 +437,7 @@ exports.stackTraceFilter = function() {
637
437
  var slash = path.sep;
638
438
  var cwd;
639
439
  if (is.node) {
640
- cwd = process.cwd() + slash;
440
+ cwd = exports.cwd() + slash;
641
441
  } else {
642
442
  cwd = (typeof location === 'undefined'
643
443
  ? window.location
@@ -804,20 +604,44 @@ exports.defineConstants = function(obj) {
804
604
  * Whether current version of Node support ES modules
805
605
  *
806
606
  * @description
807
- * Versions prior to 10 did not support ES Modules, and version 10 has an old incompatibile version of ESM.
607
+ * Versions prior to 10 did not support ES Modules, and version 10 has an old incompatible version of ESM.
808
608
  * This function returns whether Node.JS has ES Module supports that is compatible with Mocha's needs,
809
609
  * which is version >=12.11.
810
610
  *
611
+ * @param {partialSupport} whether the full Node.js ESM support is available (>= 12) or just something that supports the runtime (>= 10)
612
+ *
811
613
  * @returns {Boolean} whether the current version of Node.JS supports ES Modules in a way that is compatible with Mocha
812
614
  */
813
- exports.supportsEsModules = function() {
814
- if (!process.browser && process.versions && process.versions.node) {
615
+ exports.supportsEsModules = function(partialSupport) {
616
+ if (!exports.isBrowser() && process.versions && process.versions.node) {
815
617
  var versionFields = process.versions.node.split('.');
816
618
  var major = +versionFields[0];
817
619
  var minor = +versionFields[1];
818
620
 
819
- if (major >= 13 || (major === 12 && minor >= 11)) {
820
- return true;
621
+ if (!partialSupport) {
622
+ return major >= 13 || (major === 12 && minor >= 11);
623
+ } else {
624
+ return major >= 10;
821
625
  }
822
626
  }
823
627
  };
628
+
629
+ /**
630
+ * Returns current working directory
631
+ *
632
+ * Wrapper around `process.cwd()` for isolation
633
+ * @private
634
+ */
635
+ exports.cwd = function cwd() {
636
+ return process.cwd();
637
+ };
638
+
639
+ /**
640
+ * Returns `true` if Mocha is running in a browser.
641
+ * Checks for `process.browser`.
642
+ * @returns {boolean}
643
+ * @private
644
+ */
645
+ exports.isBrowser = function isBrowser() {
646
+ return Boolean(process.browser);
647
+ };