mocha 7.2.0 → 8.1.1
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.
- package/CHANGELOG.md +116 -0
- package/bin/mocha +17 -2
- package/browser-entry.js +26 -9
- package/lib/browser/growl.js +2 -1
- package/lib/browser/highlight-tags.js +39 -0
- package/lib/browser/parse-query.js +24 -0
- package/lib/browser/progress.js +4 -0
- package/lib/browser/template.html +7 -5
- package/lib/cli/cli.js +2 -2
- package/lib/cli/collect-files.js +15 -9
- package/lib/cli/config.js +0 -1
- package/lib/cli/init.js +1 -2
- package/lib/cli/lookup-files.js +145 -0
- package/lib/cli/node-flags.js +2 -2
- package/lib/cli/options.js +11 -87
- package/lib/cli/run-helpers.js +54 -16
- package/lib/cli/run-option-metadata.js +4 -2
- package/lib/cli/run.js +61 -14
- package/lib/cli/watch-run.js +211 -51
- package/lib/context.js +0 -15
- package/lib/errors.js +26 -3
- package/lib/esm-utils.js +11 -6
- package/lib/hook.js +24 -0
- package/lib/interfaces/common.js +19 -11
- package/lib/mocha.js +137 -121
- package/lib/mocharc.json +0 -1
- package/lib/nodejs/buffered-worker-pool.js +174 -0
- package/lib/{growl.js → nodejs/growl.js} +3 -2
- package/lib/nodejs/parallel-buffered-runner.js +295 -0
- package/lib/nodejs/reporters/parallel-buffered.js +133 -0
- package/lib/nodejs/serializer.js +404 -0
- package/lib/nodejs/worker.js +154 -0
- package/lib/pending.js +4 -0
- package/lib/reporters/base.js +25 -12
- package/lib/reporters/landing.js +3 -3
- package/lib/reporters/tap.js +1 -2
- package/lib/reporters/xunit.js +3 -2
- package/lib/runnable.js +18 -30
- package/lib/runner.js +58 -64
- package/lib/suite.js +32 -24
- package/lib/test.js +28 -1
- package/lib/utils.js +19 -206
- package/mocha.js +25522 -18248
- package/mocha.js.map +1 -0
- package/package.json +52 -42
- package/lib/browser/tty.js +0 -13
package/lib/mocha.js
CHANGED
|
@@ -9,12 +9,14 @@
|
|
|
9
9
|
var escapeRe = require('escape-string-regexp');
|
|
10
10
|
var path = require('path');
|
|
11
11
|
var builtinReporters = require('./reporters');
|
|
12
|
-
var growl = require('./growl');
|
|
12
|
+
var growl = require('./nodejs/growl');
|
|
13
13
|
var utils = require('./utils');
|
|
14
14
|
var mocharc = require('./mocharc.json');
|
|
15
15
|
var errors = require('./errors');
|
|
16
16
|
var Suite = require('./suite');
|
|
17
|
-
var esmUtils = utils.supportsEsModules()
|
|
17
|
+
var esmUtils = utils.supportsEsModules(true)
|
|
18
|
+
? require('./esm-utils')
|
|
19
|
+
: undefined;
|
|
18
20
|
var createStatsCollector = require('./stats-collector');
|
|
19
21
|
var createInvalidReporterError = errors.createInvalidReporterError;
|
|
20
22
|
var createInvalidInterfaceError = errors.createInvalidInterfaceError;
|
|
@@ -26,29 +28,35 @@ var EVENT_FILE_PRE_REQUIRE = Suite.constants.EVENT_FILE_PRE_REQUIRE;
|
|
|
26
28
|
var EVENT_FILE_POST_REQUIRE = Suite.constants.EVENT_FILE_POST_REQUIRE;
|
|
27
29
|
var EVENT_FILE_REQUIRE = Suite.constants.EVENT_FILE_REQUIRE;
|
|
28
30
|
var sQuote = utils.sQuote;
|
|
31
|
+
var debug = require('debug')('mocha:mocha');
|
|
29
32
|
|
|
30
33
|
exports = module.exports = Mocha;
|
|
31
34
|
|
|
32
35
|
/**
|
|
33
36
|
* A Mocha instance is a finite state machine.
|
|
34
37
|
* These are the states it can be in.
|
|
38
|
+
* @private
|
|
35
39
|
*/
|
|
36
40
|
var mochaStates = utils.defineConstants({
|
|
37
41
|
/**
|
|
38
42
|
* Initial state of the mocha instance
|
|
43
|
+
* @private
|
|
39
44
|
*/
|
|
40
45
|
INIT: 'init',
|
|
41
46
|
/**
|
|
42
47
|
* Mocha instance is running tests
|
|
48
|
+
* @private
|
|
43
49
|
*/
|
|
44
50
|
RUNNING: 'running',
|
|
45
51
|
/**
|
|
46
52
|
* Mocha instance is done running tests and references to test functions and hooks are cleaned.
|
|
47
53
|
* You can reset this state by unloading the test files.
|
|
54
|
+
* @private
|
|
48
55
|
*/
|
|
49
56
|
REFERENCES_CLEANED: 'referencesCleaned',
|
|
50
57
|
/**
|
|
51
58
|
* Mocha instance is disposed and can no longer be used.
|
|
59
|
+
* @private
|
|
52
60
|
*/
|
|
53
61
|
DISPOSED: 'disposed'
|
|
54
62
|
});
|
|
@@ -57,20 +65,16 @@ var mochaStates = utils.defineConstants({
|
|
|
57
65
|
* To require local UIs and reporters when running in node.
|
|
58
66
|
*/
|
|
59
67
|
|
|
60
|
-
if (!
|
|
68
|
+
if (!utils.isBrowser() && typeof module.paths !== 'undefined') {
|
|
61
69
|
var cwd = utils.cwd();
|
|
62
70
|
module.paths.push(cwd, path.join(cwd, 'node_modules'));
|
|
63
71
|
}
|
|
64
72
|
|
|
65
73
|
/**
|
|
66
74
|
* Expose internals.
|
|
75
|
+
* @private
|
|
67
76
|
*/
|
|
68
77
|
|
|
69
|
-
/**
|
|
70
|
-
* @public
|
|
71
|
-
* @class utils
|
|
72
|
-
* @memberof Mocha
|
|
73
|
-
*/
|
|
74
78
|
exports.utils = utils;
|
|
75
79
|
exports.interfaces = require('./interfaces');
|
|
76
80
|
/**
|
|
@@ -118,8 +122,11 @@ exports.Test = require('./test');
|
|
|
118
122
|
* @param {number} [options.slow] - Slow threshold value.
|
|
119
123
|
* @param {number|string} [options.timeout] - Timeout threshold value.
|
|
120
124
|
* @param {string} [options.ui] - Interface name.
|
|
125
|
+
* @param {boolean} [options.parallel] - Run jobs in parallel
|
|
126
|
+
* @param {number} [options.jobs] - Max number of worker processes for parallel runs
|
|
121
127
|
* @param {MochaRootHookObject} [options.rootHooks] - Hooks to bootstrap the root
|
|
122
128
|
* suite with
|
|
129
|
+
* @param {boolean} [options.isWorker] - Should be `true` if `Mocha` process is running in a worker process.
|
|
123
130
|
*/
|
|
124
131
|
function Mocha(options) {
|
|
125
132
|
options = utils.assign({}, mocharc, options || {});
|
|
@@ -128,6 +135,7 @@ function Mocha(options) {
|
|
|
128
135
|
// root suite
|
|
129
136
|
this.suite = new exports.Suite('', new exports.Context(), true);
|
|
130
137
|
this._cleanReferencesAfterRun = true;
|
|
138
|
+
this._state = mochaStates.INIT;
|
|
131
139
|
|
|
132
140
|
this.grep(options.grep)
|
|
133
141
|
.fgrep(options.fgrep)
|
|
@@ -171,6 +179,39 @@ function Mocha(options) {
|
|
|
171
179
|
if (options.rootHooks) {
|
|
172
180
|
this.rootHooks(options.rootHooks);
|
|
173
181
|
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* The class which we'll instantiate in {@link Mocha#run}. Defaults to
|
|
185
|
+
* {@link Runner} in serial mode; changes in parallel mode.
|
|
186
|
+
* @memberof Mocha
|
|
187
|
+
* @private
|
|
188
|
+
*/
|
|
189
|
+
this._runnerClass = exports.Runner;
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Whether or not to call {@link Mocha#loadFiles} implicitly when calling
|
|
193
|
+
* {@link Mocha#run}. If this is `true`, then it's up to the consumer to call
|
|
194
|
+
* {@link Mocha#loadFiles} _or_ {@link Mocha#loadFilesAsync}.
|
|
195
|
+
* @private
|
|
196
|
+
* @memberof Mocha
|
|
197
|
+
*/
|
|
198
|
+
this._lazyLoadFiles = false;
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* It's useful for a Mocha instance to know if it's running in a worker process.
|
|
202
|
+
* We could derive this via other means, but it's helpful to have a flag to refer to.
|
|
203
|
+
* @memberof Mocha
|
|
204
|
+
* @private
|
|
205
|
+
*/
|
|
206
|
+
this.isWorker = Boolean(options.isWorker);
|
|
207
|
+
|
|
208
|
+
if (
|
|
209
|
+
options.parallel &&
|
|
210
|
+
(typeof options.jobs === 'undefined' || options.jobs > 1)
|
|
211
|
+
) {
|
|
212
|
+
debug('attempting to enable parallel mode');
|
|
213
|
+
this.parallelMode(true);
|
|
214
|
+
}
|
|
174
215
|
}
|
|
175
216
|
|
|
176
217
|
/**
|
|
@@ -211,7 +252,7 @@ Mocha.prototype.addFile = function(file) {
|
|
|
211
252
|
* @public
|
|
212
253
|
* @see [CLI option](../#-reporter-name-r-name)
|
|
213
254
|
* @see [Reporters](../#reporters)
|
|
214
|
-
* @param {String|Function}
|
|
255
|
+
* @param {String|Function} reporterName - Reporter name or constructor.
|
|
215
256
|
* @param {Object} [reporterOptions] - Options used to configure the reporter.
|
|
216
257
|
* @returns {Mocha} this
|
|
217
258
|
* @chainable
|
|
@@ -221,20 +262,20 @@ Mocha.prototype.addFile = function(file) {
|
|
|
221
262
|
* // Use XUnit reporter and direct its output to file
|
|
222
263
|
* mocha.reporter('xunit', { output: '/path/to/testspec.xunit.xml' });
|
|
223
264
|
*/
|
|
224
|
-
Mocha.prototype.reporter = function(
|
|
225
|
-
if (typeof
|
|
226
|
-
this._reporter =
|
|
265
|
+
Mocha.prototype.reporter = function(reporterName, reporterOptions) {
|
|
266
|
+
if (typeof reporterName === 'function') {
|
|
267
|
+
this._reporter = reporterName;
|
|
227
268
|
} else {
|
|
228
|
-
|
|
229
|
-
var
|
|
269
|
+
reporterName = reporterName || 'spec';
|
|
270
|
+
var reporter;
|
|
230
271
|
// Try to load a built-in reporter.
|
|
231
|
-
if (builtinReporters[
|
|
232
|
-
|
|
272
|
+
if (builtinReporters[reporterName]) {
|
|
273
|
+
reporter = builtinReporters[reporterName];
|
|
233
274
|
}
|
|
234
275
|
// Try to load reporters from process.cwd() and node_modules
|
|
235
|
-
if (!
|
|
276
|
+
if (!reporter) {
|
|
236
277
|
try {
|
|
237
|
-
|
|
278
|
+
reporter = require(reporterName);
|
|
238
279
|
} catch (err) {
|
|
239
280
|
if (
|
|
240
281
|
err.code === 'MODULE_NOT_FOUND' ||
|
|
@@ -242,31 +283,31 @@ Mocha.prototype.reporter = function(reporter, reporterOptions) {
|
|
|
242
283
|
) {
|
|
243
284
|
// Try to load reporters from a path (absolute or relative)
|
|
244
285
|
try {
|
|
245
|
-
|
|
286
|
+
reporter = require(path.resolve(utils.cwd(), reporterName));
|
|
246
287
|
} catch (_err) {
|
|
247
288
|
_err.code === 'MODULE_NOT_FOUND' ||
|
|
248
289
|
_err.message.indexOf('Cannot find module') >= 0
|
|
249
|
-
? utils.warn(sQuote(
|
|
290
|
+
? utils.warn(sQuote(reporterName) + ' reporter not found')
|
|
250
291
|
: utils.warn(
|
|
251
|
-
sQuote(
|
|
292
|
+
sQuote(reporterName) +
|
|
252
293
|
' reporter blew up with error:\n' +
|
|
253
294
|
err.stack
|
|
254
295
|
);
|
|
255
296
|
}
|
|
256
297
|
} else {
|
|
257
298
|
utils.warn(
|
|
258
|
-
sQuote(
|
|
299
|
+
sQuote(reporterName) + ' reporter blew up with error:\n' + err.stack
|
|
259
300
|
);
|
|
260
301
|
}
|
|
261
302
|
}
|
|
262
303
|
}
|
|
263
|
-
if (!
|
|
304
|
+
if (!reporter) {
|
|
264
305
|
throw createInvalidReporterError(
|
|
265
|
-
'invalid reporter ' + sQuote(
|
|
266
|
-
|
|
306
|
+
'invalid reporter ' + sQuote(reporterName),
|
|
307
|
+
reporterName
|
|
267
308
|
);
|
|
268
309
|
}
|
|
269
|
-
this._reporter =
|
|
310
|
+
this._reporter = reporter;
|
|
270
311
|
}
|
|
271
312
|
this.options.reporterOption = reporterOptions;
|
|
272
313
|
// alias option name is used in public reporters xunit/tap/progress
|
|
@@ -375,7 +416,7 @@ Mocha.prototype.loadFiles = function(fn) {
|
|
|
375
416
|
Mocha.prototype.loadFilesAsync = function() {
|
|
376
417
|
var self = this;
|
|
377
418
|
var suite = this.suite;
|
|
378
|
-
this.
|
|
419
|
+
this.lazyLoadFiles(true);
|
|
379
420
|
|
|
380
421
|
if (!esmUtils) {
|
|
381
422
|
return new Promise(function(resolve) {
|
|
@@ -520,24 +561,6 @@ Mocha.prototype.invert = function() {
|
|
|
520
561
|
return this;
|
|
521
562
|
};
|
|
522
563
|
|
|
523
|
-
/**
|
|
524
|
-
* Enables or disables ignoring global leaks.
|
|
525
|
-
*
|
|
526
|
-
* @deprecated since v7.0.0
|
|
527
|
-
* @public
|
|
528
|
-
* @see {@link Mocha#checkLeaks}
|
|
529
|
-
* @param {boolean} [ignoreLeaks=false] - Whether to ignore global leaks.
|
|
530
|
-
* @return {Mocha} this
|
|
531
|
-
* @chainable
|
|
532
|
-
*/
|
|
533
|
-
Mocha.prototype.ignoreLeaks = function(ignoreLeaks) {
|
|
534
|
-
utils.deprecate(
|
|
535
|
-
'"ignoreLeaks()" is DEPRECATED, please use "checkLeaks()" instead.'
|
|
536
|
-
);
|
|
537
|
-
this.options.checkLeaks = !ignoreLeaks;
|
|
538
|
-
return this;
|
|
539
|
-
};
|
|
540
|
-
|
|
541
564
|
/**
|
|
542
565
|
* Enables or disables checking for global variables leaked while running tests.
|
|
543
566
|
*
|
|
@@ -609,7 +632,7 @@ Mocha.prototype.fullTrace = function(fullTrace) {
|
|
|
609
632
|
Mocha.prototype.growl = function() {
|
|
610
633
|
this.options.growl = this.isGrowlCapable();
|
|
611
634
|
if (!this.options.growl) {
|
|
612
|
-
var detail =
|
|
635
|
+
var detail = utils.isBrowser()
|
|
613
636
|
? 'notification support not available in this browser...'
|
|
614
637
|
: 'notification support prerequisites not installed...';
|
|
615
638
|
console.error(detail + ' cannot enable!');
|
|
@@ -667,24 +690,6 @@ Mocha.prototype.global = function(global) {
|
|
|
667
690
|
// for backwards compability, 'globals' is an alias of 'global'
|
|
668
691
|
Mocha.prototype.globals = Mocha.prototype.global;
|
|
669
692
|
|
|
670
|
-
/**
|
|
671
|
-
* Enables or disables TTY color output by screen-oriented reporters.
|
|
672
|
-
*
|
|
673
|
-
* @deprecated since v7.0.0
|
|
674
|
-
* @public
|
|
675
|
-
* @see {@link Mocha#color}
|
|
676
|
-
* @param {boolean} colors - Whether to enable color output.
|
|
677
|
-
* @return {Mocha} this
|
|
678
|
-
* @chainable
|
|
679
|
-
*/
|
|
680
|
-
Mocha.prototype.useColors = function(colors) {
|
|
681
|
-
utils.deprecate('"useColors()" is DEPRECATED, please use "color()" instead.');
|
|
682
|
-
if (colors !== undefined) {
|
|
683
|
-
this.options.color = colors;
|
|
684
|
-
}
|
|
685
|
-
return this;
|
|
686
|
-
};
|
|
687
|
-
|
|
688
693
|
/**
|
|
689
694
|
* Enables or disables TTY color output by screen-oriented reporters.
|
|
690
695
|
*
|
|
@@ -699,25 +704,6 @@ Mocha.prototype.color = function(color) {
|
|
|
699
704
|
return this;
|
|
700
705
|
};
|
|
701
706
|
|
|
702
|
-
/**
|
|
703
|
-
* Determines if reporter should use inline diffs (rather than +/-)
|
|
704
|
-
* in test failure output.
|
|
705
|
-
*
|
|
706
|
-
* @deprecated since v7.0.0
|
|
707
|
-
* @public
|
|
708
|
-
* @see {@link Mocha#inlineDiffs}
|
|
709
|
-
* @param {boolean} [inlineDiffs=false] - Whether to use inline diffs.
|
|
710
|
-
* @return {Mocha} this
|
|
711
|
-
* @chainable
|
|
712
|
-
*/
|
|
713
|
-
Mocha.prototype.useInlineDiffs = function(inlineDiffs) {
|
|
714
|
-
utils.deprecate(
|
|
715
|
-
'"useInlineDiffs()" is DEPRECATED, please use "inlineDiffs()" instead.'
|
|
716
|
-
);
|
|
717
|
-
this.options.inlineDiffs = inlineDiffs !== undefined && inlineDiffs;
|
|
718
|
-
return this;
|
|
719
|
-
};
|
|
720
|
-
|
|
721
707
|
/**
|
|
722
708
|
* Enables or disables reporter to use inline diffs (rather than +/-)
|
|
723
709
|
* in test failure output.
|
|
@@ -733,22 +719,6 @@ Mocha.prototype.inlineDiffs = function(inlineDiffs) {
|
|
|
733
719
|
return this;
|
|
734
720
|
};
|
|
735
721
|
|
|
736
|
-
/**
|
|
737
|
-
* Determines if reporter should include diffs in test failure output.
|
|
738
|
-
*
|
|
739
|
-
* @deprecated since v7.0.0
|
|
740
|
-
* @public
|
|
741
|
-
* @see {@link Mocha#diff}
|
|
742
|
-
* @param {boolean} [hideDiff=false] - Whether to hide diffs.
|
|
743
|
-
* @return {Mocha} this
|
|
744
|
-
* @chainable
|
|
745
|
-
*/
|
|
746
|
-
Mocha.prototype.hideDiff = function(hideDiff) {
|
|
747
|
-
utils.deprecate('"hideDiff()" is DEPRECATED, please use "diff()" instead.');
|
|
748
|
-
this.options.diff = !(hideDiff === true);
|
|
749
|
-
return this;
|
|
750
|
-
};
|
|
751
|
-
|
|
752
722
|
/**
|
|
753
723
|
* Enables or disables reporter to include diff in test failure output.
|
|
754
724
|
*
|
|
@@ -774,7 +744,6 @@ Mocha.prototype.diff = function(diff) {
|
|
|
774
744
|
* @public
|
|
775
745
|
* @see [CLI option](../#-timeout-ms-t-ms)
|
|
776
746
|
* @see [Timeouts](../#timeouts)
|
|
777
|
-
* @see {@link Mocha#enableTimeouts}
|
|
778
747
|
* @param {number|string} msecs - Timeout threshold value.
|
|
779
748
|
* @return {Mocha} this
|
|
780
749
|
* @chainable
|
|
@@ -806,8 +775,8 @@ Mocha.prototype.timeout = function(msecs) {
|
|
|
806
775
|
* // Allow any failed test to retry one more time
|
|
807
776
|
* mocha.retries(1);
|
|
808
777
|
*/
|
|
809
|
-
Mocha.prototype.retries = function(
|
|
810
|
-
this.suite.retries(
|
|
778
|
+
Mocha.prototype.retries = function(retry) {
|
|
779
|
+
this.suite.retries(retry);
|
|
811
780
|
return this;
|
|
812
781
|
};
|
|
813
782
|
|
|
@@ -833,22 +802,6 @@ Mocha.prototype.slow = function(msecs) {
|
|
|
833
802
|
return this;
|
|
834
803
|
};
|
|
835
804
|
|
|
836
|
-
/**
|
|
837
|
-
* Enables or disables timeouts.
|
|
838
|
-
*
|
|
839
|
-
* @public
|
|
840
|
-
* @see [CLI option](../#-timeout-ms-t-ms)
|
|
841
|
-
* @param {boolean} enableTimeouts - Whether to enable timeouts.
|
|
842
|
-
* @return {Mocha} this
|
|
843
|
-
* @chainable
|
|
844
|
-
*/
|
|
845
|
-
Mocha.prototype.enableTimeouts = function(enableTimeouts) {
|
|
846
|
-
this.suite.enableTimeouts(
|
|
847
|
-
arguments.length && enableTimeouts !== undefined ? enableTimeouts : true
|
|
848
|
-
);
|
|
849
|
-
return this;
|
|
850
|
-
};
|
|
851
|
-
|
|
852
805
|
/**
|
|
853
806
|
* Forces all tests to either accept a `done` callback or return a promise.
|
|
854
807
|
*
|
|
@@ -936,6 +889,7 @@ Mocha.prototype.forbidPending = function(forbidPending) {
|
|
|
936
889
|
|
|
937
890
|
/**
|
|
938
891
|
* Throws an error if mocha is in the wrong state to be able to transition to a "running" state.
|
|
892
|
+
* @private
|
|
939
893
|
*/
|
|
940
894
|
Mocha.prototype._guardRunningStateTransition = function() {
|
|
941
895
|
if (this._state === mochaStates.RUNNING) {
|
|
@@ -973,6 +927,7 @@ Object.defineProperty(Mocha.prototype, 'version', {
|
|
|
973
927
|
/**
|
|
974
928
|
* Callback to be invoked when test execution is complete.
|
|
975
929
|
*
|
|
930
|
+
* @private
|
|
976
931
|
* @callback DoneCB
|
|
977
932
|
* @param {number} failures - Number of failures that occurred.
|
|
978
933
|
*/
|
|
@@ -1002,14 +957,14 @@ Mocha.prototype.run = function(fn) {
|
|
|
1002
957
|
this._previousRunner.dispose();
|
|
1003
958
|
this.suite.reset();
|
|
1004
959
|
}
|
|
1005
|
-
if (this.files.length && !this.
|
|
960
|
+
if (this.files.length && !this._lazyLoadFiles) {
|
|
1006
961
|
this.loadFiles();
|
|
1007
962
|
}
|
|
1008
963
|
var self = this;
|
|
1009
964
|
var suite = this.suite;
|
|
1010
965
|
var options = this.options;
|
|
1011
966
|
options.files = this.files;
|
|
1012
|
-
var runner = new
|
|
967
|
+
var runner = new this._runnerClass(suite, {
|
|
1013
968
|
delay: options.delay,
|
|
1014
969
|
cleanReferencesAfterRun: this._cleanReferencesAfterRun
|
|
1015
970
|
});
|
|
@@ -1051,7 +1006,7 @@ Mocha.prototype.run = function(fn) {
|
|
|
1051
1006
|
}
|
|
1052
1007
|
}
|
|
1053
1008
|
|
|
1054
|
-
return runner.run(done);
|
|
1009
|
+
return runner.run(done, {files: this.files, options: options});
|
|
1055
1010
|
};
|
|
1056
1011
|
|
|
1057
1012
|
/**
|
|
@@ -1082,8 +1037,68 @@ Mocha.prototype.rootHooks = function rootHooks(hooks) {
|
|
|
1082
1037
|
return this;
|
|
1083
1038
|
};
|
|
1084
1039
|
|
|
1040
|
+
/**
|
|
1041
|
+
* Toggles parallel mode.
|
|
1042
|
+
*
|
|
1043
|
+
* Must be run before calling {@link Mocha#run}. Changes the `Runner` class to
|
|
1044
|
+
* use; also enables lazy file loading if not already done so.
|
|
1045
|
+
* @param {boolean} [enable] - If `true`, enable; otherwise disable.
|
|
1046
|
+
* @throws If run in browser
|
|
1047
|
+
* @throws If Mocha not in "INIT" state
|
|
1048
|
+
* @returns {Mocha}
|
|
1049
|
+
* @chainable
|
|
1050
|
+
* @public
|
|
1051
|
+
*/
|
|
1052
|
+
Mocha.prototype.parallelMode = function parallelMode(enable) {
|
|
1053
|
+
if (utils.isBrowser()) {
|
|
1054
|
+
throw errors.createUnsupportedError(
|
|
1055
|
+
'parallel mode is only supported in Node.js'
|
|
1056
|
+
);
|
|
1057
|
+
}
|
|
1058
|
+
var parallel = enable === true;
|
|
1059
|
+
if (
|
|
1060
|
+
parallel === this.options.parallel &&
|
|
1061
|
+
this._lazyLoadFiles &&
|
|
1062
|
+
this._runnerClass !== exports.Runner
|
|
1063
|
+
) {
|
|
1064
|
+
return this;
|
|
1065
|
+
}
|
|
1066
|
+
if (this._state !== mochaStates.INIT) {
|
|
1067
|
+
throw errors.createUnsupportedError(
|
|
1068
|
+
'cannot change parallel mode after having called run()'
|
|
1069
|
+
);
|
|
1070
|
+
}
|
|
1071
|
+
this.options.parallel = parallel;
|
|
1072
|
+
|
|
1073
|
+
// swap Runner class
|
|
1074
|
+
this._runnerClass = parallel
|
|
1075
|
+
? require('./nodejs/parallel-buffered-runner')
|
|
1076
|
+
: exports.Runner;
|
|
1077
|
+
|
|
1078
|
+
// lazyLoadFiles may have been set `true` otherwise (for ESM loading),
|
|
1079
|
+
// so keep `true` if so.
|
|
1080
|
+
return this.lazyLoadFiles(this._lazyLoadFiles || parallel);
|
|
1081
|
+
};
|
|
1082
|
+
|
|
1083
|
+
/**
|
|
1084
|
+
* Disables implicit call to {@link Mocha#loadFiles} in {@link Mocha#run}. This
|
|
1085
|
+
* setting is used by watch mode, parallel mode, and for loading ESM files.
|
|
1086
|
+
* @todo This should throw if we've already loaded files; such behavior
|
|
1087
|
+
* necessitates adding a new state.
|
|
1088
|
+
* @param {boolean} [enable] - If `true`, disable eager loading of files in
|
|
1089
|
+
* {@link Mocha#run}
|
|
1090
|
+
* @chainable
|
|
1091
|
+
* @public
|
|
1092
|
+
*/
|
|
1093
|
+
Mocha.prototype.lazyLoadFiles = function lazyLoadFiles(enable) {
|
|
1094
|
+
this._lazyLoadFiles = enable === true;
|
|
1095
|
+
debug('set lazy load to %s', enable);
|
|
1096
|
+
return this;
|
|
1097
|
+
};
|
|
1098
|
+
|
|
1085
1099
|
/**
|
|
1086
1100
|
* An alternative way to define root hooks that works with parallel runs.
|
|
1101
|
+
* @private
|
|
1087
1102
|
* @typedef {Object} MochaRootHookObject
|
|
1088
1103
|
* @property {Function|Function[]} [beforeAll] - "Before all" hook(s)
|
|
1089
1104
|
* @property {Function|Function[]} [beforeEach] - "Before each" hook(s)
|
|
@@ -1093,6 +1108,7 @@ Mocha.prototype.rootHooks = function rootHooks(hooks) {
|
|
|
1093
1108
|
|
|
1094
1109
|
/**
|
|
1095
1110
|
* An function that returns a {@link MochaRootHookObject}, either sync or async.
|
|
1111
|
+
* @private
|
|
1096
1112
|
* @callback MochaRootHookFunction
|
|
1097
1113
|
* @returns {MochaRootHookObject|Promise<MochaRootHookObject>}
|
|
1098
1114
|
*/
|
package/lib/mocharc.json
CHANGED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A wrapper around a third-party child process worker pool implementation.
|
|
3
|
+
* Used by {@link module:buffered-runner}.
|
|
4
|
+
* @private
|
|
5
|
+
* @module buffered-worker-pool
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
const serializeJavascript = require('serialize-javascript');
|
|
11
|
+
const workerpool = require('workerpool');
|
|
12
|
+
const {deserialize} = require('./serializer');
|
|
13
|
+
const debug = require('debug')('mocha:parallel:buffered-worker-pool');
|
|
14
|
+
const {createInvalidArgumentTypeError} = require('../errors');
|
|
15
|
+
|
|
16
|
+
const WORKER_PATH = require.resolve('./worker.js');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* A mapping of Mocha `Options` objects to serialized values.
|
|
20
|
+
*
|
|
21
|
+
* This is helpful because we tend to same the same options over and over
|
|
22
|
+
* over IPC.
|
|
23
|
+
* @type {WeakMap<Options,string>}
|
|
24
|
+
*/
|
|
25
|
+
let optionsCache = new WeakMap();
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* These options are passed into the [workerpool](https://npm.im/workerpool) module.
|
|
29
|
+
* @type {Partial<WorkerPoolOptions>}
|
|
30
|
+
*/
|
|
31
|
+
const WORKER_POOL_DEFAULT_OPTS = {
|
|
32
|
+
// use child processes, not worker threads!
|
|
33
|
+
workerType: 'process',
|
|
34
|
+
// ensure the same flags sent to `node` for this `mocha` invocation are passed
|
|
35
|
+
// along to children
|
|
36
|
+
forkOpts: {execArgv: process.execArgv},
|
|
37
|
+
maxWorkers: workerpool.cpus - 1
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* A wrapper around a third-party worker pool implementation.
|
|
42
|
+
* @private
|
|
43
|
+
*/
|
|
44
|
+
class BufferedWorkerPool {
|
|
45
|
+
/**
|
|
46
|
+
* Creates an underlying worker pool instance; determines max worker count
|
|
47
|
+
* @param {Partial<WorkerPoolOptions>} [opts] - Options
|
|
48
|
+
*/
|
|
49
|
+
constructor(opts = {}) {
|
|
50
|
+
const maxWorkers = Math.max(
|
|
51
|
+
1,
|
|
52
|
+
typeof opts.maxWorkers === 'undefined'
|
|
53
|
+
? WORKER_POOL_DEFAULT_OPTS.maxWorkers
|
|
54
|
+
: opts.maxWorkers
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
/* istanbul ignore next */
|
|
58
|
+
if (workerpool.cpus < 2) {
|
|
59
|
+
// TODO: decide whether we should warn
|
|
60
|
+
debug(
|
|
61
|
+
'not enough CPU cores available to run multiple jobs; avoid --parallel on this machine'
|
|
62
|
+
);
|
|
63
|
+
} else if (maxWorkers >= workerpool.cpus) {
|
|
64
|
+
// TODO: decide whether we should warn
|
|
65
|
+
debug(
|
|
66
|
+
'%d concurrent job(s) requested, but only %d core(s) available',
|
|
67
|
+
maxWorkers,
|
|
68
|
+
workerpool.cpus
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
/* istanbul ignore next */
|
|
72
|
+
debug(
|
|
73
|
+
'run(): starting worker pool of max size %d, using node args: %s',
|
|
74
|
+
maxWorkers,
|
|
75
|
+
process.execArgv.join(' ')
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
this.options = Object.assign({}, WORKER_POOL_DEFAULT_OPTS, opts, {
|
|
79
|
+
maxWorkers
|
|
80
|
+
});
|
|
81
|
+
this._pool = workerpool.pool(WORKER_PATH, this.options);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Terminates all workers in the pool.
|
|
86
|
+
* @param {boolean} [force] - Whether to force-kill workers. By default, lets workers finish their current task before termination.
|
|
87
|
+
* @private
|
|
88
|
+
* @returns {Promise<void>}
|
|
89
|
+
*/
|
|
90
|
+
async terminate(force = false) {
|
|
91
|
+
/* istanbul ignore next */
|
|
92
|
+
debug('terminate(): terminating with force = %s', force);
|
|
93
|
+
return this._pool.terminate(force);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Adds a test file run to the worker pool queue for execution by a worker process.
|
|
98
|
+
*
|
|
99
|
+
* Handles serialization/deserialization.
|
|
100
|
+
*
|
|
101
|
+
* @param {string} filepath - Filepath of test
|
|
102
|
+
* @param {Options} [options] - Options for Mocha instance
|
|
103
|
+
* @private
|
|
104
|
+
* @returns {Promise<SerializedWorkerResult>}
|
|
105
|
+
*/
|
|
106
|
+
async run(filepath, options = {}) {
|
|
107
|
+
if (!filepath || typeof filepath !== 'string') {
|
|
108
|
+
throw createInvalidArgumentTypeError(
|
|
109
|
+
'Expected a non-empty filepath',
|
|
110
|
+
'filepath',
|
|
111
|
+
'string'
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
const serializedOptions = BufferedWorkerPool.serializeOptions(options);
|
|
115
|
+
const result = await this._pool.exec('run', [filepath, serializedOptions]);
|
|
116
|
+
return deserialize(result);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Returns stats about the state of the worker processes in the pool.
|
|
121
|
+
*
|
|
122
|
+
* Used for debugging.
|
|
123
|
+
*
|
|
124
|
+
* @private
|
|
125
|
+
*/
|
|
126
|
+
stats() {
|
|
127
|
+
return this._pool.stats();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Instantiates a {@link WorkerPool}.
|
|
132
|
+
* @private
|
|
133
|
+
*/
|
|
134
|
+
static create(...args) {
|
|
135
|
+
return new BufferedWorkerPool(...args);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Given Mocha options object `opts`, serialize into a format suitable for
|
|
140
|
+
* transmission over IPC.
|
|
141
|
+
*
|
|
142
|
+
* @param {Options} [opts] - Mocha options
|
|
143
|
+
* @private
|
|
144
|
+
* @returns {string} Serialized options
|
|
145
|
+
*/
|
|
146
|
+
static serializeOptions(opts = {}) {
|
|
147
|
+
if (!optionsCache.has(opts)) {
|
|
148
|
+
const serialized = serializeJavascript(opts, {
|
|
149
|
+
unsafe: true, // this means we don't care about XSS
|
|
150
|
+
ignoreFunction: true // do not serialize functions
|
|
151
|
+
});
|
|
152
|
+
optionsCache.set(opts, serialized);
|
|
153
|
+
/* istanbul ignore next */
|
|
154
|
+
debug(
|
|
155
|
+
'serializeOptions(): serialized options %O to: %s',
|
|
156
|
+
opts,
|
|
157
|
+
serialized
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
return optionsCache.get(opts);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Resets internal cache of serialized options objects.
|
|
165
|
+
*
|
|
166
|
+
* For testing/debugging
|
|
167
|
+
* @private
|
|
168
|
+
*/
|
|
169
|
+
static resetOptionsCache() {
|
|
170
|
+
optionsCache = new WeakMap();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
exports.BufferedWorkerPool = BufferedWorkerPool;
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
const os = require('os');
|
|
9
9
|
const path = require('path');
|
|
10
10
|
const {sync: which} = require('which');
|
|
11
|
-
const {EVENT_RUN_END} = require('
|
|
11
|
+
const {EVENT_RUN_END} = require('../runner').constants;
|
|
12
|
+
const {isBrowser} = require('../utils');
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* @summary
|
|
@@ -25,7 +26,7 @@ const {EVENT_RUN_END} = require('./runner').constants;
|
|
|
25
26
|
* @return {boolean} whether Growl notification support can be expected
|
|
26
27
|
*/
|
|
27
28
|
exports.isCapable = () => {
|
|
28
|
-
if (!
|
|
29
|
+
if (!isBrowser()) {
|
|
29
30
|
return getSupportBinaries().reduce(
|
|
30
31
|
(acc, binary) => acc || Boolean(which(binary, {nothrow: true})),
|
|
31
32
|
false
|