mocha-compat 3.6.4 → 10.8.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.
- package/LICENSE +1 -1
- package/README.md +61 -110
- package/bin/_mocha +10 -0
- package/bin/mocha.js +142 -0
- package/browser-entry.js +61 -22
- package/lib/browser/highlight-tags.js +39 -0
- package/lib/browser/parse-query.js +24 -0
- package/lib/{template.html → browser/template.html} +7 -5
- package/lib/cli/cli.js +89 -0
- package/lib/cli/collect-files.js +137 -0
- package/lib/cli/commands.js +14 -0
- package/lib/cli/config.js +100 -0
- package/lib/cli/index.js +3 -0
- package/lib/cli/init.js +36 -0
- package/lib/cli/lookup-files.js +150 -0
- package/lib/cli/node-flags.js +85 -0
- package/lib/cli/one-and-dones.js +69 -0
- package/lib/cli/options.js +284 -0
- package/lib/cli/run-helpers.js +304 -0
- package/lib/cli/run-option-metadata.js +116 -0
- package/lib/cli/run.js +380 -0
- package/lib/cli/watch-run.js +377 -0
- package/lib/context.js +16 -42
- package/lib/errors.js +563 -0
- package/lib/hook.js +50 -9
- package/lib/interfaces/bdd.js +28 -29
- package/lib/interfaces/common.js +56 -21
- package/lib/interfaces/exports.js +4 -7
- package/lib/interfaces/qunit.js +10 -11
- package/lib/interfaces/tdd.js +15 -15
- package/lib/mocha.js +1073 -292
- package/lib/mocharc.json +10 -0
- package/lib/nodejs/buffered-worker-pool.js +188 -0
- package/lib/nodejs/esm-utils.js +106 -0
- package/lib/nodejs/file-unloader.js +15 -0
- package/lib/nodejs/parallel-buffered-runner.js +433 -0
- package/lib/nodejs/reporters/parallel-buffered.js +165 -0
- package/lib/nodejs/serializer.js +414 -0
- package/lib/nodejs/worker.js +151 -0
- package/lib/pending.js +3 -3
- package/lib/plugin-loader.js +286 -0
- package/lib/reporters/base.js +305 -205
- package/lib/reporters/doc.js +52 -21
- package/lib/reporters/dot.js +31 -18
- package/lib/reporters/html.js +153 -81
- package/lib/reporters/json-stream.js +53 -24
- package/lib/reporters/json.js +88 -18
- package/lib/reporters/landing.js +38 -16
- package/lib/reporters/list.js +34 -19
- package/lib/reporters/markdown.js +28 -15
- package/lib/reporters/min.js +22 -8
- package/lib/reporters/nyan.js +62 -58
- package/lib/reporters/progress.js +32 -19
- package/lib/reporters/spec.js +41 -23
- package/lib/reporters/tap.js +255 -32
- package/lib/reporters/xunit.js +89 -39
- package/lib/runnable.js +233 -148
- package/lib/runner.js +679 -380
- package/lib/stats-collector.js +83 -0
- package/lib/suite.js +376 -112
- package/lib/test.js +82 -21
- package/lib/utils.js +364 -477
- package/mocha.css +183 -54
- package/mocha.js +19840 -15846
- package/mocha.js.map +1 -0
- package/package.json +163 -336
- package/{lib/to-iso-string → vendor/serialize-javascript}/LICENSE +8 -6
- package/vendor/serialize-javascript/README.md +10 -0
- package/vendor/serialize-javascript/index.js +349 -0
- package/bin/_mocha-compat +0 -572
- package/bin/mocha-compat +0 -87
- package/bin/options.js +0 -41
- package/bower.json +0 -38
- package/images/error.png +0 -0
- package/images/ok.png +0 -0
- package/lib/browser/.eslintrc.yaml +0 -4
- package/lib/browser/debug.js +0 -7
- package/lib/browser/events.js +0 -195
- package/lib/browser/progress.js +0 -119
- package/lib/browser/tty.js +0 -13
- package/lib/ms.js +0 -130
- package/lib/to-iso-string/index.js +0 -37
- package/vendor/glob/LICENSE +0 -15
- package/vendor/glob/README.md +0 -399
- package/vendor/glob/common.js +0 -244
- package/vendor/glob/glob.js +0 -788
- package/vendor/glob/package.json +0 -55
- package/vendor/glob/sync.js +0 -486
- package/vendor/inflight/LICENSE +0 -15
- package/vendor/inflight/README.md +0 -37
- package/vendor/inflight/inflight.js +0 -54
- package/vendor/inflight/package.json +0 -29
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Main entry point for handling filesystem-based configuration,
|
|
5
|
+
* whether that's a config file or `package.json` or whatever.
|
|
6
|
+
* @module lib/cli/options
|
|
7
|
+
* @private
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const ansi = require('ansi-colors');
|
|
12
|
+
const yargsParser = require('yargs-parser');
|
|
13
|
+
const {types, aliases} = require('./run-option-metadata');
|
|
14
|
+
const {ONE_AND_DONE_ARGS} = require('./one-and-dones');
|
|
15
|
+
const mocharc = require('../mocharc.json');
|
|
16
|
+
const {list} = require('./run-helpers');
|
|
17
|
+
const {loadConfig, findConfig} = require('./config');
|
|
18
|
+
const findUp = require('find-up');
|
|
19
|
+
const debug = require('debug')('mocha:cli:options');
|
|
20
|
+
const {isNodeFlag} = require('./node-flags');
|
|
21
|
+
const {createUnparsableFileError} = require('../errors');
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The `yargs-parser` namespace
|
|
25
|
+
* @external yargsParser
|
|
26
|
+
* @see {@link https://npm.im/yargs-parser}
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* An object returned by a configured `yargs-parser` representing arguments
|
|
31
|
+
* @memberof external:yargsParser
|
|
32
|
+
* @interface Arguments
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Base yargs parser configuration
|
|
37
|
+
* @private
|
|
38
|
+
*/
|
|
39
|
+
const YARGS_PARSER_CONFIG = {
|
|
40
|
+
'combine-arrays': true,
|
|
41
|
+
'short-option-groups': false,
|
|
42
|
+
'dot-notation': false,
|
|
43
|
+
'strip-aliased': true
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* This is the config pulled from the `yargs` property of Mocha's
|
|
48
|
+
* `package.json`, but it also disables camel case expansion as to
|
|
49
|
+
* avoid outputting non-canonical keynames, as we need to do some
|
|
50
|
+
* lookups.
|
|
51
|
+
* @private
|
|
52
|
+
* @ignore
|
|
53
|
+
*/
|
|
54
|
+
const configuration = Object.assign({}, YARGS_PARSER_CONFIG, {
|
|
55
|
+
'camel-case-expansion': false
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* This is a really fancy way to:
|
|
60
|
+
* - `array`-type options: ensure unique values and evtl. split comma-delimited lists
|
|
61
|
+
* - `boolean`/`number`/`string`- options: use last element when given multiple times
|
|
62
|
+
* This is passed as the `coerce` option to `yargs-parser`
|
|
63
|
+
* @private
|
|
64
|
+
* @ignore
|
|
65
|
+
*/
|
|
66
|
+
const globOptions = ['spec', 'ignore'];
|
|
67
|
+
const coerceOpts = Object.assign(
|
|
68
|
+
types.array.reduce(
|
|
69
|
+
(acc, arg) =>
|
|
70
|
+
Object.assign(acc, {
|
|
71
|
+
[arg]: v => Array.from(new Set(globOptions.includes(arg) ? v : list(v)))
|
|
72
|
+
}),
|
|
73
|
+
{}
|
|
74
|
+
),
|
|
75
|
+
types.boolean
|
|
76
|
+
.concat(types.string, types.number)
|
|
77
|
+
.reduce(
|
|
78
|
+
(acc, arg) =>
|
|
79
|
+
Object.assign(acc, {[arg]: v => (Array.isArray(v) ? v.pop() : v)}),
|
|
80
|
+
{}
|
|
81
|
+
)
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* We do not have a case when multiple arguments are ever allowed after a flag
|
|
86
|
+
* (e.g., `--foo bar baz quux`), so we fix the number of arguments to 1 across
|
|
87
|
+
* the board of non-boolean options.
|
|
88
|
+
* This is passed as the `narg` option to `yargs-parser`
|
|
89
|
+
* @private
|
|
90
|
+
* @ignore
|
|
91
|
+
*/
|
|
92
|
+
const nargOpts = types.array
|
|
93
|
+
.concat(types.string, types.number)
|
|
94
|
+
.reduce((acc, arg) => Object.assign(acc, {[arg]: 1}), {});
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Wrapper around `yargs-parser` which applies our settings
|
|
98
|
+
* @param {string|string[]} args - Arguments to parse
|
|
99
|
+
* @param {Object} defaultValues - Default values of mocharc.json
|
|
100
|
+
* @param {...Object} configObjects - `configObjects` for yargs-parser
|
|
101
|
+
* @private
|
|
102
|
+
* @ignore
|
|
103
|
+
*/
|
|
104
|
+
const parse = (args = [], defaultValues = {}, ...configObjects) => {
|
|
105
|
+
// save node-specific args for special handling.
|
|
106
|
+
// 1. when these args have a "=" they should be considered to have values
|
|
107
|
+
// 2. if they don't, they just boolean flags
|
|
108
|
+
// 3. to avoid explicitly defining the set of them, we tell yargs-parser they
|
|
109
|
+
// are ALL boolean flags.
|
|
110
|
+
// 4. we can then reapply the values after yargs-parser is done.
|
|
111
|
+
const nodeArgs = (Array.isArray(args) ? args : args.split(' ')).reduce(
|
|
112
|
+
(acc, arg) => {
|
|
113
|
+
const pair = arg.split('=');
|
|
114
|
+
let flag = pair[0];
|
|
115
|
+
if (isNodeFlag(flag, false)) {
|
|
116
|
+
flag = flag.replace(/^--?/, '');
|
|
117
|
+
return arg.includes('=')
|
|
118
|
+
? acc.concat([[flag, pair[1]]])
|
|
119
|
+
: acc.concat([[flag, true]]);
|
|
120
|
+
}
|
|
121
|
+
return acc;
|
|
122
|
+
},
|
|
123
|
+
[]
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
const result = yargsParser.detailed(args, {
|
|
127
|
+
configuration,
|
|
128
|
+
configObjects,
|
|
129
|
+
default: defaultValues,
|
|
130
|
+
coerce: coerceOpts,
|
|
131
|
+
narg: nargOpts,
|
|
132
|
+
alias: aliases,
|
|
133
|
+
string: types.string,
|
|
134
|
+
array: types.array,
|
|
135
|
+
number: types.number,
|
|
136
|
+
boolean: types.boolean.concat(nodeArgs.map(pair => pair[0]))
|
|
137
|
+
});
|
|
138
|
+
if (result.error) {
|
|
139
|
+
console.error(ansi.red(`Error: ${result.error.message}`));
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// reapply "=" arg values from above
|
|
144
|
+
nodeArgs.forEach(([key, value]) => {
|
|
145
|
+
result.argv[key] = value;
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
return result.argv;
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Given path to config file in `args.config`, attempt to load & parse config file.
|
|
153
|
+
* @param {Object} [args] - Arguments object
|
|
154
|
+
* @param {string|boolean} [args.config] - Path to config file or `false` to skip
|
|
155
|
+
* @public
|
|
156
|
+
* @alias module:lib/cli.loadRc
|
|
157
|
+
* @returns {external:yargsParser.Arguments|void} Parsed config, or nothing if `args.config` is `false`
|
|
158
|
+
*/
|
|
159
|
+
const loadRc = (args = {}) => {
|
|
160
|
+
if (args.config !== false) {
|
|
161
|
+
const config = args.config || findConfig();
|
|
162
|
+
return config ? loadConfig(config) : {};
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
module.exports.loadRc = loadRc;
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Given path to `package.json` in `args.package`, attempt to load config from `mocha` prop.
|
|
170
|
+
* @param {Object} [args] - Arguments object
|
|
171
|
+
* @param {string|boolean} [args.config] - Path to `package.json` or `false` to skip
|
|
172
|
+
* @public
|
|
173
|
+
* @alias module:lib/cli.loadPkgRc
|
|
174
|
+
* @returns {external:yargsParser.Arguments|void} Parsed config, or nothing if `args.package` is `false`
|
|
175
|
+
*/
|
|
176
|
+
const loadPkgRc = (args = {}) => {
|
|
177
|
+
let result;
|
|
178
|
+
if (args.package === false) {
|
|
179
|
+
return result;
|
|
180
|
+
}
|
|
181
|
+
result = {};
|
|
182
|
+
const filepath = args.package || findUp.sync(mocharc.package);
|
|
183
|
+
if (filepath) {
|
|
184
|
+
let configData;
|
|
185
|
+
try {
|
|
186
|
+
configData = fs.readFileSync(filepath, 'utf8');
|
|
187
|
+
} catch (err) {
|
|
188
|
+
// If `args.package` was explicitly specified, throw an error
|
|
189
|
+
if (filepath == args.package) {
|
|
190
|
+
throw createUnparsableFileError(
|
|
191
|
+
`Unable to read ${filepath}: ${err}`,
|
|
192
|
+
filepath
|
|
193
|
+
);
|
|
194
|
+
} else {
|
|
195
|
+
debug('failed to read default package.json at %s; ignoring',
|
|
196
|
+
filepath);
|
|
197
|
+
return result;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
try {
|
|
201
|
+
const pkg = JSON.parse(configData);
|
|
202
|
+
if (pkg.mocha) {
|
|
203
|
+
debug('`mocha` prop of package.json parsed: %O', pkg.mocha);
|
|
204
|
+
result = pkg.mocha;
|
|
205
|
+
} else {
|
|
206
|
+
debug('no config found in %s', filepath);
|
|
207
|
+
}
|
|
208
|
+
} catch (err) {
|
|
209
|
+
// If JSON failed to parse, throw an error.
|
|
210
|
+
throw createUnparsableFileError(
|
|
211
|
+
`Unable to parse ${filepath}: ${err}`,
|
|
212
|
+
filepath
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return result;
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
module.exports.loadPkgRc = loadPkgRc;
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Priority list:
|
|
223
|
+
*
|
|
224
|
+
* 1. Command-line args
|
|
225
|
+
* 2. `MOCHA_OPTIONS` environment variable.
|
|
226
|
+
* 3. RC file (`.mocharc.c?js`, `.mocharc.ya?ml`, `mocharc.json`)
|
|
227
|
+
* 4. `mocha` prop of `package.json`
|
|
228
|
+
* 5. default configuration (`lib/mocharc.json`)
|
|
229
|
+
*
|
|
230
|
+
* If a {@link module:lib/cli/one-and-dones.ONE_AND_DONE_ARGS "one-and-done" option} is present in the `argv` array, no external config files will be read.
|
|
231
|
+
* @summary Parses options read from `.mocharc.*` and `package.json`.
|
|
232
|
+
* @param {string|string[]} [argv] - Arguments to parse
|
|
233
|
+
* @public
|
|
234
|
+
* @alias module:lib/cli.loadOptions
|
|
235
|
+
* @returns {external:yargsParser.Arguments} Parsed args from everything
|
|
236
|
+
*/
|
|
237
|
+
const loadOptions = (argv = []) => {
|
|
238
|
+
let args = parse(argv);
|
|
239
|
+
// short-circuit: look for a flag that would abort loading of options
|
|
240
|
+
if (
|
|
241
|
+
Array.from(ONE_AND_DONE_ARGS).reduce(
|
|
242
|
+
(acc, arg) => acc || arg in args,
|
|
243
|
+
false
|
|
244
|
+
)
|
|
245
|
+
) {
|
|
246
|
+
return args;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const envConfig = parse(process.env.MOCHA_OPTIONS || '');
|
|
250
|
+
const rcConfig = loadRc(args);
|
|
251
|
+
const pkgConfig = loadPkgRc(args);
|
|
252
|
+
|
|
253
|
+
if (rcConfig) {
|
|
254
|
+
args.config = false;
|
|
255
|
+
args._ = args._.concat(rcConfig._ || []);
|
|
256
|
+
}
|
|
257
|
+
if (pkgConfig) {
|
|
258
|
+
args.package = false;
|
|
259
|
+
args._ = args._.concat(pkgConfig._ || []);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
args = parse(
|
|
263
|
+
args._,
|
|
264
|
+
mocharc,
|
|
265
|
+
args,
|
|
266
|
+
envConfig,
|
|
267
|
+
rcConfig || {},
|
|
268
|
+
pkgConfig || {}
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
// recombine positional arguments and "spec"
|
|
272
|
+
if (args.spec) {
|
|
273
|
+
args._ = args._.concat(args.spec);
|
|
274
|
+
delete args.spec;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// make unique
|
|
278
|
+
args._ = Array.from(new Set(args._));
|
|
279
|
+
|
|
280
|
+
return args;
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
module.exports.loadOptions = loadOptions;
|
|
284
|
+
module.exports.YARGS_PARSER_CONFIG = YARGS_PARSER_CONFIG;
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Helper scripts for the `run` command
|
|
5
|
+
* @see module:lib/cli/run
|
|
6
|
+
* @module
|
|
7
|
+
* @private
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const ansi = require('ansi-colors');
|
|
13
|
+
const debug = require('debug')('mocha:cli:run:helpers');
|
|
14
|
+
const {watchRun, watchParallelRun} = require('./watch-run');
|
|
15
|
+
const collectFiles = require('./collect-files');
|
|
16
|
+
const {format} = require('util');
|
|
17
|
+
const {createInvalidLegacyPluginError} = require('../errors');
|
|
18
|
+
const {requireOrImport} = require('../nodejs/esm-utils');
|
|
19
|
+
const PluginLoader = require('../plugin-loader');
|
|
20
|
+
const {UnmatchedFile} = require('./collect-files');
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Exits Mocha when tests + code under test has finished execution (default)
|
|
24
|
+
* @param {number} clampedCode - Exit code; typically # of failures
|
|
25
|
+
* @ignore
|
|
26
|
+
* @private
|
|
27
|
+
*/
|
|
28
|
+
const exitMochaLater = clampedCode => {
|
|
29
|
+
process.on('exit', () => {
|
|
30
|
+
process.exitCode = clampedCode;
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Exits Mocha when Mocha itself has finished execution, regardless of
|
|
36
|
+
* what the tests or code under test is doing.
|
|
37
|
+
* @param {number} clampedCode - Exit code; typically # of failures
|
|
38
|
+
* @ignore
|
|
39
|
+
* @private
|
|
40
|
+
*/
|
|
41
|
+
const exitMocha = clampedCode => {
|
|
42
|
+
let draining = 0;
|
|
43
|
+
|
|
44
|
+
// Eagerly set the process's exit code in case stream.write doesn't
|
|
45
|
+
// execute its callback before the process terminates.
|
|
46
|
+
process.exitCode = clampedCode;
|
|
47
|
+
|
|
48
|
+
// flush output for Node.js Windows pipe bug
|
|
49
|
+
// https://github.com/joyent/node/issues/6247 is just one bug example
|
|
50
|
+
// https://github.com/visionmedia/mocha/issues/333 has a good discussion
|
|
51
|
+
const done = () => {
|
|
52
|
+
if (!draining--) {
|
|
53
|
+
process.exit(clampedCode);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const streams = [process.stdout, process.stderr];
|
|
58
|
+
|
|
59
|
+
streams.forEach(stream => {
|
|
60
|
+
// submit empty write request and wait for completion
|
|
61
|
+
draining += 1;
|
|
62
|
+
stream.write('', done);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
done();
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Coerce a comma-delimited string (or array thereof) into a flattened array of
|
|
70
|
+
* strings
|
|
71
|
+
* @param {string|string[]} str - Value to coerce
|
|
72
|
+
* @returns {string[]} Array of strings
|
|
73
|
+
* @private
|
|
74
|
+
*/
|
|
75
|
+
exports.list = str =>
|
|
76
|
+
Array.isArray(str) ? exports.list(str.join(',')) : str.split(/ *, */);
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* `require()` the modules as required by `--require <require>`.
|
|
80
|
+
*
|
|
81
|
+
* Returns array of `mochaHooks` exports, if any.
|
|
82
|
+
* @param {string[]} requires - Modules to require
|
|
83
|
+
* @returns {Promise<object>} Plugin implementations
|
|
84
|
+
* @private
|
|
85
|
+
*/
|
|
86
|
+
exports.handleRequires = async (requires = [], {ignoredPlugins = []} = {}) => {
|
|
87
|
+
const pluginLoader = PluginLoader.create({ignore: ignoredPlugins});
|
|
88
|
+
for await (const mod of requires) {
|
|
89
|
+
let modpath = mod;
|
|
90
|
+
// this is relative to cwd
|
|
91
|
+
if (fs.existsSync(mod) || fs.existsSync(`${mod}.js`)) {
|
|
92
|
+
modpath = path.resolve(mod);
|
|
93
|
+
debug('resolved required file %s to %s', mod, modpath);
|
|
94
|
+
}
|
|
95
|
+
const requiredModule = await requireOrImport(modpath);
|
|
96
|
+
if (requiredModule && typeof requiredModule === 'object') {
|
|
97
|
+
if (pluginLoader.load(requiredModule)) {
|
|
98
|
+
debug('found one or more plugin implementations in %s', modpath);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
debug('loaded required module "%s"', mod);
|
|
102
|
+
}
|
|
103
|
+
const plugins = await pluginLoader.finalize();
|
|
104
|
+
if (Object.keys(plugins).length) {
|
|
105
|
+
debug('finalized plugin implementations: %O', plugins);
|
|
106
|
+
}
|
|
107
|
+
return plugins;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Logs errors and exits the app if unmatched files exist
|
|
112
|
+
* @param {Mocha} mocha - Mocha instance
|
|
113
|
+
* @param {UnmatchedFile} unmatchedFiles - object containing unmatched file paths
|
|
114
|
+
* @returns {Promise<Runner>}
|
|
115
|
+
* @private
|
|
116
|
+
*/
|
|
117
|
+
const handleUnmatchedFiles = (mocha, unmatchedFiles) => {
|
|
118
|
+
if (unmatchedFiles.length === 0) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
unmatchedFiles.forEach(({pattern, absolutePath}) => {
|
|
123
|
+
console.error(
|
|
124
|
+
ansi.yellow(
|
|
125
|
+
`Warning: Cannot find any files matching pattern "${pattern}" at the absolute path "${absolutePath}"`
|
|
126
|
+
)
|
|
127
|
+
);
|
|
128
|
+
});
|
|
129
|
+
console.log(
|
|
130
|
+
'No test file(s) found with the given pattern, exiting with code 1'
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
return mocha.run(exitMocha(1));
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Collect and load test files, then run mocha instance.
|
|
138
|
+
* @param {Mocha} mocha - Mocha instance
|
|
139
|
+
* @param {Options} [opts] - Command line options
|
|
140
|
+
* @param {boolean} [opts.exit] - Whether or not to force-exit after tests are complete
|
|
141
|
+
* @param {boolean} [opts.passOnFailingTestSuite] - Whether or not to fail test run if tests were failed
|
|
142
|
+
* @param {Object} fileCollectParams - Parameters that control test
|
|
143
|
+
* file collection. See `lib/cli/collect-files.js`.
|
|
144
|
+
* @returns {Promise<Runner>}
|
|
145
|
+
* @private
|
|
146
|
+
*/
|
|
147
|
+
const singleRun = async (
|
|
148
|
+
mocha,
|
|
149
|
+
{exit, passOnFailingTestSuite},
|
|
150
|
+
fileCollectParams
|
|
151
|
+
) => {
|
|
152
|
+
const fileCollectionObj = collectFiles(fileCollectParams);
|
|
153
|
+
|
|
154
|
+
if (fileCollectionObj.unmatchedFiles.length > 0) {
|
|
155
|
+
return handleUnmatchedFiles(mocha, fileCollectionObj.unmatchedFiles);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
debug('single run with %d file(s)', fileCollectionObj.files.length);
|
|
159
|
+
mocha.files = fileCollectionObj.files;
|
|
160
|
+
|
|
161
|
+
// handles ESM modules
|
|
162
|
+
await mocha.loadFilesAsync();
|
|
163
|
+
return mocha.run(
|
|
164
|
+
createExitHandler({exit, passOnFailingTestSuite})
|
|
165
|
+
);
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Collect files and run tests (using `BufferedRunner`).
|
|
170
|
+
*
|
|
171
|
+
* This is `async` for consistency.
|
|
172
|
+
*
|
|
173
|
+
* @param {Mocha} mocha - Mocha instance
|
|
174
|
+
* @param {Options} options - Command line options
|
|
175
|
+
* @param {Object} fileCollectParams - Parameters that control test
|
|
176
|
+
* file collection. See `lib/cli/collect-files.js`.
|
|
177
|
+
* @returns {Promise<BufferedRunner>}
|
|
178
|
+
* @ignore
|
|
179
|
+
* @private
|
|
180
|
+
*/
|
|
181
|
+
const parallelRun = async (mocha, options, fileCollectParams) => {
|
|
182
|
+
const fileCollectionObj = collectFiles(fileCollectParams);
|
|
183
|
+
|
|
184
|
+
if (fileCollectionObj.unmatchedFiles.length > 0) {
|
|
185
|
+
return handleUnmatchedFiles(mocha, fileCollectionObj.unmatchedFiles);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
debug(
|
|
189
|
+
'executing %d test file(s) in parallel mode',
|
|
190
|
+
fileCollectionObj.files.length
|
|
191
|
+
);
|
|
192
|
+
mocha.files = fileCollectionObj.files;
|
|
193
|
+
|
|
194
|
+
// note that we DO NOT load any files here; this is handled by the worker
|
|
195
|
+
return mocha.run(
|
|
196
|
+
createExitHandler(options)
|
|
197
|
+
);
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Actually run tests. Delegates to one of four different functions:
|
|
202
|
+
* - `singleRun`: run tests in serial & exit
|
|
203
|
+
* - `watchRun`: run tests in serial, rerunning as files change
|
|
204
|
+
* - `parallelRun`: run tests in parallel & exit
|
|
205
|
+
* - `watchParallelRun`: run tests in parallel, rerunning as files change
|
|
206
|
+
* @param {Mocha} mocha - Mocha instance
|
|
207
|
+
* @param {Options} opts - Command line options
|
|
208
|
+
* @private
|
|
209
|
+
* @returns {Promise<Runner>}
|
|
210
|
+
*/
|
|
211
|
+
exports.runMocha = async (mocha, options) => {
|
|
212
|
+
const {
|
|
213
|
+
watch = false,
|
|
214
|
+
extension = [],
|
|
215
|
+
ignore = [],
|
|
216
|
+
file = [],
|
|
217
|
+
parallel = false,
|
|
218
|
+
recursive = false,
|
|
219
|
+
sort = false,
|
|
220
|
+
spec = []
|
|
221
|
+
} = options;
|
|
222
|
+
|
|
223
|
+
const fileCollectParams = {
|
|
224
|
+
ignore,
|
|
225
|
+
extension,
|
|
226
|
+
file,
|
|
227
|
+
recursive,
|
|
228
|
+
sort,
|
|
229
|
+
spec
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
let run;
|
|
233
|
+
if (watch) {
|
|
234
|
+
run = parallel ? watchParallelRun : watchRun;
|
|
235
|
+
} else {
|
|
236
|
+
run = parallel ? parallelRun : singleRun;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return run(mocha, options, fileCollectParams);
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Used for `--reporter` and `--ui`. Ensures there's only one, and asserts that
|
|
244
|
+
* it actually exists. This must be run _after_ requires are processed (see
|
|
245
|
+
* {@link handleRequires}), as it'll prevent interfaces from loading otherwise.
|
|
246
|
+
* @param {Object} opts - Options object
|
|
247
|
+
* @param {"reporter"|"ui"} pluginType - Type of plugin.
|
|
248
|
+
* @param {Object} [map] - Used as a cache of sorts;
|
|
249
|
+
* `Mocha.reporters` where each key corresponds to a reporter name,
|
|
250
|
+
* `Mocha.interfaces` where each key corresponds to an interface name.
|
|
251
|
+
* @private
|
|
252
|
+
*/
|
|
253
|
+
exports.validateLegacyPlugin = (opts, pluginType, map = {}) => {
|
|
254
|
+
/**
|
|
255
|
+
* This should be a unique identifier; either a string (present in `map`),
|
|
256
|
+
* or a resolvable (via `require.resolve`) module ID/path.
|
|
257
|
+
* @type {string}
|
|
258
|
+
*/
|
|
259
|
+
const pluginId = opts[pluginType];
|
|
260
|
+
|
|
261
|
+
if (Array.isArray(pluginId)) {
|
|
262
|
+
throw createInvalidLegacyPluginError(
|
|
263
|
+
`"--${pluginType}" can only be specified once`,
|
|
264
|
+
pluginType
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const createUnknownError = err =>
|
|
269
|
+
createInvalidLegacyPluginError(
|
|
270
|
+
format('Could not load %s "%s":\n\n %O', pluginType, pluginId, err),
|
|
271
|
+
pluginType,
|
|
272
|
+
pluginId
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
// if this exists, then it's already loaded, so nothing more to do.
|
|
276
|
+
if (!map[pluginId]) {
|
|
277
|
+
let foundId;
|
|
278
|
+
try {
|
|
279
|
+
foundId = require.resolve(pluginId);
|
|
280
|
+
map[pluginId] = require(foundId);
|
|
281
|
+
} catch (err) {
|
|
282
|
+
if (foundId) throw createUnknownError(err);
|
|
283
|
+
|
|
284
|
+
// Try to load reporters from a cwd-relative path
|
|
285
|
+
try {
|
|
286
|
+
map[pluginId] = require(path.resolve(pluginId));
|
|
287
|
+
} catch (e) {
|
|
288
|
+
throw createUnknownError(e);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
const createExitHandler = ({ exit, passOnFailingTestSuite }) => {
|
|
295
|
+
return code => {
|
|
296
|
+
const clampedCode = passOnFailingTestSuite
|
|
297
|
+
? 0
|
|
298
|
+
: Math.min(code, 255);
|
|
299
|
+
|
|
300
|
+
return exit
|
|
301
|
+
? exitMocha(clampedCode)
|
|
302
|
+
: exitMochaLater(clampedCode);
|
|
303
|
+
};
|
|
304
|
+
};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Metadata about various options of the `run` command
|
|
5
|
+
* @see module:lib/cli/run
|
|
6
|
+
* @module
|
|
7
|
+
* @private
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Dictionary of yargs option types to list of options having said type
|
|
12
|
+
* @type {{string:string[]}}
|
|
13
|
+
* @private
|
|
14
|
+
*/
|
|
15
|
+
const TYPES = (exports.types = {
|
|
16
|
+
array: [
|
|
17
|
+
'extension',
|
|
18
|
+
'file',
|
|
19
|
+
'global',
|
|
20
|
+
'ignore',
|
|
21
|
+
'node-option',
|
|
22
|
+
'reporter-option',
|
|
23
|
+
'require',
|
|
24
|
+
'spec',
|
|
25
|
+
'watch-files',
|
|
26
|
+
'watch-ignore'
|
|
27
|
+
],
|
|
28
|
+
boolean: [
|
|
29
|
+
'allow-uncaught',
|
|
30
|
+
'async-only',
|
|
31
|
+
'bail',
|
|
32
|
+
'check-leaks',
|
|
33
|
+
'color',
|
|
34
|
+
'delay',
|
|
35
|
+
'diff',
|
|
36
|
+
'dry-run',
|
|
37
|
+
'exit',
|
|
38
|
+
'pass-on-failing-test-suite',
|
|
39
|
+
'fail-zero',
|
|
40
|
+
'forbid-only',
|
|
41
|
+
'forbid-pending',
|
|
42
|
+
'full-trace',
|
|
43
|
+
'inline-diffs',
|
|
44
|
+
'invert',
|
|
45
|
+
'list-interfaces',
|
|
46
|
+
'list-reporters',
|
|
47
|
+
'no-colors',
|
|
48
|
+
'parallel',
|
|
49
|
+
'recursive',
|
|
50
|
+
'sort',
|
|
51
|
+
'watch'
|
|
52
|
+
],
|
|
53
|
+
number: ['retries', 'jobs'],
|
|
54
|
+
string: [
|
|
55
|
+
'config',
|
|
56
|
+
'fgrep',
|
|
57
|
+
'grep',
|
|
58
|
+
'package',
|
|
59
|
+
'reporter',
|
|
60
|
+
'ui',
|
|
61
|
+
'slow',
|
|
62
|
+
'timeout'
|
|
63
|
+
]
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Option aliases keyed by canonical option name.
|
|
68
|
+
* Arrays used to reduce
|
|
69
|
+
* @type {{string:string[]}}
|
|
70
|
+
* @private
|
|
71
|
+
*/
|
|
72
|
+
exports.aliases = {
|
|
73
|
+
'async-only': ['A'],
|
|
74
|
+
bail: ['b'],
|
|
75
|
+
color: ['c', 'colors'],
|
|
76
|
+
fgrep: ['f'],
|
|
77
|
+
global: ['globals'],
|
|
78
|
+
grep: ['g'],
|
|
79
|
+
ignore: ['exclude'],
|
|
80
|
+
invert: ['i'],
|
|
81
|
+
jobs: ['j'],
|
|
82
|
+
'no-colors': ['C'],
|
|
83
|
+
'node-option': ['n'],
|
|
84
|
+
parallel: ['p'],
|
|
85
|
+
reporter: ['R'],
|
|
86
|
+
'reporter-option': ['reporter-options', 'O'],
|
|
87
|
+
require: ['r'],
|
|
88
|
+
slow: ['s'],
|
|
89
|
+
sort: ['S'],
|
|
90
|
+
timeout: ['t', 'timeouts'],
|
|
91
|
+
ui: ['u'],
|
|
92
|
+
watch: ['w']
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const ALL_MOCHA_FLAGS = Object.keys(TYPES).reduce((acc, key) => {
|
|
96
|
+
// gets all flags from each of the fields in `types`, adds those,
|
|
97
|
+
// then adds aliases of each flag (if any)
|
|
98
|
+
TYPES[key].forEach(flag => {
|
|
99
|
+
acc.add(flag);
|
|
100
|
+
const aliases = exports.aliases[flag] || [];
|
|
101
|
+
aliases.forEach(alias => {
|
|
102
|
+
acc.add(alias);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
return acc;
|
|
106
|
+
}, new Set());
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Returns `true` if the provided `flag` is known to Mocha.
|
|
110
|
+
* @param {string} flag - Flag to check
|
|
111
|
+
* @returns {boolean} If `true`, this is a Mocha flag
|
|
112
|
+
* @private
|
|
113
|
+
*/
|
|
114
|
+
exports.isMochaFlag = flag => {
|
|
115
|
+
return ALL_MOCHA_FLAGS.has(flag.replace(/^--?/, ''));
|
|
116
|
+
};
|