mocha 5.1.1 → 6.0.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 (61) hide show
  1. package/CHANGELOG.md +686 -984
  2. package/README.md +2 -1
  3. package/{images → assets/growl}/error.png +0 -0
  4. package/{images → assets/growl}/ok.png +0 -0
  5. package/bin/_mocha +4 -595
  6. package/bin/mocha +121 -61
  7. package/bin/options.js +6 -39
  8. package/browser-entry.js +21 -17
  9. package/lib/browser/growl.js +165 -2
  10. package/lib/browser/progress.js +11 -11
  11. package/lib/{template.html → browser/template.html} +0 -0
  12. package/lib/browser/tty.js +2 -2
  13. package/lib/cli/cli.js +68 -0
  14. package/lib/cli/commands.js +13 -0
  15. package/lib/cli/config.js +79 -0
  16. package/lib/cli/index.js +9 -0
  17. package/lib/cli/init.js +37 -0
  18. package/lib/cli/node-flags.js +69 -0
  19. package/lib/cli/one-and-dones.js +70 -0
  20. package/lib/cli/options.js +330 -0
  21. package/lib/cli/run-helpers.js +337 -0
  22. package/lib/cli/run-option-metadata.js +76 -0
  23. package/lib/cli/run.js +297 -0
  24. package/lib/context.js +14 -14
  25. package/lib/errors.js +141 -0
  26. package/lib/growl.js +136 -0
  27. package/lib/hook.js +5 -16
  28. package/lib/interfaces/bdd.js +16 -13
  29. package/lib/interfaces/common.js +62 -18
  30. package/lib/interfaces/exports.js +5 -8
  31. package/lib/interfaces/qunit.js +10 -10
  32. package/lib/interfaces/tdd.js +12 -11
  33. package/lib/mocha.js +477 -256
  34. package/lib/mocharc.json +10 -0
  35. package/lib/pending.js +1 -5
  36. package/lib/reporters/base.js +95 -117
  37. package/lib/reporters/doc.js +23 -9
  38. package/lib/reporters/dot.js +19 -13
  39. package/lib/reporters/html.js +82 -47
  40. package/lib/reporters/json-stream.js +43 -23
  41. package/lib/reporters/json.js +32 -23
  42. package/lib/reporters/landing.js +16 -9
  43. package/lib/reporters/list.js +19 -11
  44. package/lib/reporters/markdown.js +18 -12
  45. package/lib/reporters/min.js +8 -4
  46. package/lib/reporters/nyan.js +42 -35
  47. package/lib/reporters/progress.js +12 -7
  48. package/lib/reporters/spec.js +23 -12
  49. package/lib/reporters/tap.js +250 -32
  50. package/lib/reporters/xunit.js +61 -35
  51. package/lib/runnable.js +152 -95
  52. package/lib/runner.js +296 -248
  53. package/lib/stats-collector.js +83 -0
  54. package/lib/suite.js +294 -75
  55. package/lib/test.js +16 -15
  56. package/lib/utils.js +419 -146
  57. package/mocha.js +4589 -2228
  58. package/package.json +137 -38
  59. package/lib/ms.js +0 -94
  60. package/lib/reporters/base.js.orig +0 -498
  61. package/lib/reporters/json.js.orig +0 -128
package/lib/cli/cli.js ADDED
@@ -0,0 +1,68 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * This is where we finally parse and handle arguments passed to the `mocha` executable.
5
+ * Option parsing is handled by {@link https://npm.im/yargs yargs}.
6
+ * If executed via `node`, this module will run {@linkcode module:lib/cli/cli.main main()}.
7
+ *
8
+ * @private
9
+ * @module
10
+ */
11
+
12
+ const debug = require('debug')('mocha:cli:cli');
13
+ const symbols = require('log-symbols');
14
+ const yargs = require('yargs');
15
+ const path = require('path');
16
+ const {loadOptions} = require('./options');
17
+ const commands = require('./commands');
18
+ const ansi = require('ansi-colors');
19
+ const {repository, homepage, version, gitter} = require('../../package.json');
20
+
21
+ /**
22
+ * - Accepts an `Array` of arguments
23
+ * - Modifies {@link https://nodejs.org/api/modules.html#modules_module_paths Node.js' search path} for easy loading of consumer modules
24
+ * - Sets {@linkcode https://nodejs.org/api/errors.html#errors_error_stacktracelimit Error.stackTraceLimit} to `Infinity`
25
+ * @summary Mocha's main entry point from the command-line.
26
+ * @param {string[]} argv - Array of arguments to parse, or by default the lovely `process.argv.slice(2)`
27
+ */
28
+ exports.main = (argv = process.argv.slice(2)) => {
29
+ debug('entered main with raw args', argv);
30
+ // ensure we can require() from current working directory
31
+ module.paths.push(process.cwd(), path.resolve('node_modules'));
32
+
33
+ Error.stackTraceLimit = Infinity; // configurable via --stack-trace-limit?
34
+
35
+ yargs
36
+ .scriptName('mocha')
37
+ .command(commands.run)
38
+ .command(commands.init)
39
+ .updateStrings({
40
+ 'Positionals:': 'Positional Arguments',
41
+ 'Options:': 'Other Options',
42
+ 'Commands:': 'Commands'
43
+ })
44
+ .fail((msg, err, yargs) => {
45
+ debug(err);
46
+ yargs.showHelp();
47
+ console.error(`\n${symbols.error} ${ansi.red('ERROR:')} ${msg}`);
48
+ process.exit(1);
49
+ })
50
+ .help('help', 'Show usage information & exit')
51
+ .alias('help', 'h')
52
+ .version('version', 'Show version number & exit', version)
53
+ .alias('version', 'V')
54
+ .wrap(process.stdout.columns ? Math.min(process.stdout.columns, 80) : 80)
55
+ .epilog(
56
+ `Mocha Resources
57
+ Chat: ${ansi.magenta(gitter)}
58
+ GitHub: ${ansi.blue(repository.url)}
59
+ Docs: ${ansi.yellow(homepage)}
60
+ `
61
+ )
62
+ .parse(argv, loadOptions(argv));
63
+ };
64
+
65
+ // allow direct execution
66
+ if (require.main === module) {
67
+ exports.main();
68
+ }
@@ -0,0 +1,13 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Exports Yargs commands
5
+ * @see https://git.io/fpJ0G
6
+ * @private
7
+ * @module
8
+ */
9
+
10
+ exports.init = require('./init');
11
+
12
+ // default command
13
+ exports.run = require('./run');
@@ -0,0 +1,79 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Responsible for loading / finding Mocha's "rc" files.
5
+ * This doesn't have anything to do with `mocha.opts`.
6
+ *
7
+ * @private
8
+ * @module
9
+ */
10
+
11
+ const fs = require('fs');
12
+ const findUp = require('findup-sync');
13
+ const path = require('path');
14
+ const debug = require('debug')('mocha:cli:config');
15
+
16
+ /**
17
+ * These are the valid config files, in order of precedence;
18
+ * e.g., if `.mocharc.js` is present, then `.mocharc.yaml` and the rest
19
+ * will be ignored.
20
+ * The user should still be able to explicitly specify a file.
21
+ * @private
22
+ */
23
+ exports.CONFIG_FILES = [
24
+ '.mocharc.js',
25
+ '.mocharc.yaml',
26
+ '.mocharc.yml',
27
+ '.mocharc.json'
28
+ ];
29
+
30
+ /**
31
+ * Parsers for various config filetypes. Each accepts a filepath and
32
+ * returns an object (but could throw)
33
+ */
34
+ const parsers = (exports.parsers = {
35
+ yaml: filepath =>
36
+ require('js-yaml').safeLoad(fs.readFileSync(filepath, 'utf8')),
37
+ js: filepath => require(filepath),
38
+ json: filepath =>
39
+ JSON.parse(
40
+ require('strip-json-comments')(fs.readFileSync(filepath, 'utf8'))
41
+ )
42
+ });
43
+
44
+ /**
45
+ * Loads and parses, based on file extension, a config file.
46
+ * "JSON" files may have comments.
47
+ * @param {string} filepath - Config file path to load
48
+ * @returns {Object} Parsed config object
49
+ * @private
50
+ */
51
+ exports.loadConfig = filepath => {
52
+ let config = {};
53
+ const ext = path.extname(filepath);
54
+ try {
55
+ if (/\.ya?ml/.test(ext)) {
56
+ config = parsers.yaml(filepath);
57
+ } else if (ext === '.js') {
58
+ config = parsers.js(filepath);
59
+ } else {
60
+ config = parsers.json(filepath);
61
+ }
62
+ } catch (err) {
63
+ throw new Error(`failed to parse ${filepath}: ${err}`);
64
+ }
65
+ return config;
66
+ };
67
+
68
+ /**
69
+ * Find ("find up") config file starting at `cwd`
70
+ * @param {string} [cwd] - Current working directory
71
+ * @returns {string|null} Filepath to config, if found
72
+ */
73
+ exports.findConfig = (cwd = process.cwd()) => {
74
+ const filepath = findUp(exports.CONFIG_FILES, {cwd});
75
+ if (filepath) {
76
+ debug(`found config at ${filepath}`);
77
+ }
78
+ return filepath;
79
+ };
@@ -0,0 +1,9 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Just exports {@link module:lib/cli/cli} for convenience.
5
+ * @private
6
+ * @module lib/cli
7
+ * @exports module:lib/cli/cli
8
+ */
9
+ module.exports = require('./cli');
@@ -0,0 +1,37 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Command module for "init" command
5
+ *
6
+ * @private
7
+ * @module
8
+ */
9
+
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+ const mkdirp = require('mkdirp');
13
+
14
+ exports.command = 'init <path>';
15
+
16
+ exports.description = 'create a client-side Mocha setup at <path>';
17
+
18
+ exports.builder = yargs =>
19
+ yargs.positional('path', {
20
+ type: 'string',
21
+ normalize: true
22
+ });
23
+
24
+ exports.handler = argv => {
25
+ const destdir = argv.path;
26
+ const srcdir = path.join(__dirname, '..', '..');
27
+ mkdirp.sync(destdir);
28
+ const css = fs.readFileSync(path.join(srcdir, 'mocha.css'));
29
+ const js = fs.readFileSync(path.join(srcdir, 'mocha.js'));
30
+ const tmpl = fs.readFileSync(
31
+ path.join(srcdir, 'lib', 'browser', 'template.html')
32
+ );
33
+ fs.writeFileSync(path.join(destdir, 'mocha.css'), css);
34
+ fs.writeFileSync(path.join(destdir, 'mocha.js'), js);
35
+ fs.writeFileSync(path.join(destdir, 'tests.spec.js'), '');
36
+ fs.writeFileSync(path.join(destdir, 'index.html'), tmpl);
37
+ };
@@ -0,0 +1,69 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Some settings and code related to Mocha's handling of Node.js/V8 flags.
5
+ * @private
6
+ * @module
7
+ */
8
+
9
+ const nodeFlags = require('node-environment-flags');
10
+ const unparse = require('yargs-unparser');
11
+
12
+ /**
13
+ * These flags are considered "debug" flags.
14
+ * @see {@link impliesNoTimeouts}
15
+ * @private
16
+ */
17
+ const debugFlags = new Set(['debug', 'debug-brk', 'inspect', 'inspect-brk']);
18
+
19
+ /**
20
+ * Mocha has historical support for various `node` and V8 flags which might not
21
+ * appear in `process.allowedNodeEnvironmentFlags`.
22
+ * These include:
23
+ * - `--preserve-symlinks`
24
+ * - `--harmony-*`
25
+ * - `--gc-global`
26
+ * - `--trace-*`
27
+ * - `--es-staging`
28
+ * - `--use-strict`
29
+ * - `--v8-*` (but *not* `--v8-options`)
30
+ * @summary Whether or not to pass a flag along to the `node` executable.
31
+ * @param {string} flag - Flag to test
32
+ * @returns {boolean}
33
+ * @private
34
+ */
35
+ exports.isNodeFlag = flag =>
36
+ !/^(?:require|r)$/.test(flag) &&
37
+ (nodeFlags.has(flag) ||
38
+ debugFlags.has(flag) ||
39
+ /(?:preserve-symlinks(?:-main)?|harmony(?:[_-]|$)|(?:trace[_-].+$)|gc(?:[_-]global)?$|es[_-]staging$|use[_-]strict$|v8[_-](?!options).+?$)/.test(
40
+ flag
41
+ ));
42
+
43
+ /**
44
+ * Returns `true` if the flag is a "debug-like" flag. These require timeouts
45
+ * to be suppressed, or pausing the debugger on breakpoints will cause test failures.
46
+ * @param {string} flag - Flag to test
47
+ * @returns {boolean}
48
+ * @private
49
+ */
50
+ exports.impliesNoTimeouts = flag => debugFlags.has(flag);
51
+
52
+ /**
53
+ * All non-strictly-boolean arguments to node--those with values--must specify those values using `=`, e.g., `--inspect=0.0.0.0`.
54
+ * Unparse these arguments using `yargs-unparser` (which would result in `--inspect 0.0.0.0`), then supply `=` where we have values.
55
+ * There's probably an easier or more robust way to do this; fixes welcome
56
+ * @param {Object} opts - Arguments object
57
+ * @returns {string[]} Unparsed arguments using `=` to specify values
58
+ * @private
59
+ */
60
+ exports.unparseNodeFlags = opts => {
61
+ var args = unparse(opts);
62
+ return args.length
63
+ ? args
64
+ .join(' ')
65
+ .split(/\b/)
66
+ .map(arg => (arg === ' ' ? '=' : arg))
67
+ .join('')
68
+ : [];
69
+ };
@@ -0,0 +1,70 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Contains "command" code for "one-and-dones"--options passed
5
+ * to Mocha which cause it to just dump some info and exit.
6
+ * See {@link module:lib/cli/one-and-dones.ONE_AND_DONE_ARGS ONE_AND_DONE_ARGS} for more info.
7
+ * @module
8
+ * @private
9
+ */
10
+
11
+ const align = require('wide-align');
12
+ const Mocha = require('../mocha');
13
+
14
+ /**
15
+ * Dumps a sorted list of the enumerable, lower-case keys of some object
16
+ * to `STDOUT`.
17
+ * @param {Object} obj - Object, ostensibly having some enumerable keys
18
+ * @ignore
19
+ * @private
20
+ */
21
+ const showKeys = obj => {
22
+ console.log();
23
+ const keys = Object.keys(obj);
24
+ const maxKeyLength = keys.reduce((max, key) => Math.max(max, key.length), 0);
25
+ keys
26
+ .filter(
27
+ key => /^[a-z]/.test(key) && !obj[key].browserOnly && !obj[key].abstract
28
+ )
29
+ .sort()
30
+ .forEach(key => {
31
+ const description = obj[key].description;
32
+ console.log(
33
+ ` ${align.left(key, maxKeyLength + 1)}${
34
+ description ? `- ${description}` : ''
35
+ }`
36
+ );
37
+ });
38
+ console.log();
39
+ };
40
+
41
+ /**
42
+ * Handlers for one-and-done options
43
+ * @namespace
44
+ * @private
45
+ */
46
+ exports.ONE_AND_DONES = {
47
+ /**
48
+ * Dump list of built-in interfaces
49
+ * @private
50
+ */
51
+ interfaces: () => {
52
+ showKeys(Mocha.interfaces);
53
+ },
54
+ /**
55
+ * Dump list of built-in reporters
56
+ * @private
57
+ */
58
+ reporters: () => {
59
+ showKeys(Mocha.reporters);
60
+ }
61
+ };
62
+
63
+ /**
64
+ * A Set of all one-and-done options
65
+ * @type Set<string>
66
+ * @private
67
+ */
68
+ exports.ONE_AND_DONE_ARGS = new Set(
69
+ ['help', 'h', 'version', 'V'].concat(Object.keys(exports.ONE_AND_DONES))
70
+ );
@@ -0,0 +1,330 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Main entry point for handling filesystem-based configuration,
5
+ * whether that's `mocha.opts` or a config file or `package.json` or whatever.
6
+ * @module
7
+ */
8
+
9
+ const fs = require('fs');
10
+ const yargsParser = require('yargs-parser');
11
+ const {types, aliases} = require('./run-option-metadata');
12
+ const {ONE_AND_DONE_ARGS} = require('./one-and-dones');
13
+ const mocharc = require('../mocharc.json');
14
+ const yargsParserConfig = require('../../package.json').yargs;
15
+ const {list} = require('./run-helpers');
16
+ const {loadConfig, findConfig} = require('./config');
17
+ const findup = require('findup-sync');
18
+ const {deprecate} = require('../utils');
19
+ const debug = require('debug')('mocha:cli:options');
20
+ const {createMissingArgumentError} = require('../errors');
21
+ const {isNodeFlag} = require('./node-flags');
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
+ * This is the config pulled from the `yargs` property of Mocha's
37
+ * `package.json`, but it also disables camel case expansion as to
38
+ * avoid outputting non-canonical keynames, as we need to do some
39
+ * lookups.
40
+ * @private
41
+ * @ignore
42
+ */
43
+ const configuration = Object.assign({}, yargsParserConfig, {
44
+ 'camel-case-expansion': false
45
+ });
46
+
47
+ /**
48
+ * This is a really fancy way to ensure unique values for `array`-type
49
+ * options.
50
+ * This is passed as the `coerce` option to `yargs-parser`
51
+ * @private
52
+ * @ignore
53
+ */
54
+ const coerceOpts = types.array.reduce(
55
+ (acc, arg) => Object.assign(acc, {[arg]: v => Array.from(new Set(list(v)))}),
56
+ {}
57
+ );
58
+
59
+ /**
60
+ * We do not have a case when multiple arguments are ever allowed after a flag
61
+ * (e.g., `--foo bar baz quux`), so we fix the number of arguments to 1 across
62
+ * the board of non-boolean options.
63
+ * This is passed as the `narg` option to `yargs-parser`
64
+ * @private
65
+ * @ignore
66
+ */
67
+ const nargOpts = types.array
68
+ .concat(types.string, types.number)
69
+ .reduce((acc, arg) => Object.assign(acc, {[arg]: 1}), {});
70
+
71
+ /**
72
+ * Wrapper around `yargs-parser` which applies our settings
73
+ * @param {string|string[]} args - Arguments to parse
74
+ * @param {...Object} configObjects - `configObjects` for yargs-parser
75
+ * @private
76
+ * @ignore
77
+ */
78
+ const parse = (args = [], ...configObjects) => {
79
+ // save node-specific args for special handling.
80
+ // 1. when these args have a "=" they should be considered to have values
81
+ // 2. if they don't, they just boolean flags
82
+ // 3. to avoid explicitly defining the set of them, we tell yargs-parser they
83
+ // are ALL boolean flags.
84
+ // 4. we can then reapply the values after yargs-parser is done.
85
+ const nodeArgs = (Array.isArray(args) ? args : args.split(' ')).reduce(
86
+ (acc, arg) => {
87
+ const pair = arg.split('=');
88
+ const flag = pair[0].replace(/^--?/, '');
89
+ if (isNodeFlag(flag)) {
90
+ return arg.includes('=')
91
+ ? acc.concat([[flag, pair[1]]])
92
+ : acc.concat([[flag, true]]);
93
+ }
94
+ return acc;
95
+ },
96
+ []
97
+ );
98
+
99
+ const result = yargsParser.detailed(args, {
100
+ configuration,
101
+ configObjects,
102
+ coerce: coerceOpts,
103
+ narg: nargOpts,
104
+ alias: aliases,
105
+ string: types.string,
106
+ array: types.array,
107
+ number: types.number,
108
+ boolean: types.boolean.concat(nodeArgs.map(pair => pair[0]))
109
+ });
110
+ if (result.error) {
111
+ throw createMissingArgumentError(result.error.message);
112
+ }
113
+
114
+ // reapply "=" arg values from above
115
+ nodeArgs.forEach(([key, value]) => {
116
+ result.argv[key] = value;
117
+ });
118
+
119
+ return result.argv;
120
+ };
121
+
122
+ /**
123
+ * - Replaces comments with empty strings
124
+ * - Replaces escaped spaces (e.g., 'xxx\ yyy') with HTML space
125
+ * - Splits on whitespace, creating array of substrings
126
+ * - Filters empty string elements from array
127
+ * - Replaces any HTML space with space
128
+ * @summary Parses options read from run-control file.
129
+ * @private
130
+ * @param {string} content - Content read from run-control file.
131
+ * @returns {string[]} cmdline options (and associated arguments)
132
+ * @ignore
133
+ */
134
+ const parseMochaOpts = content =>
135
+ content
136
+ .replace(/^#.*$/gm, '')
137
+ .replace(/\\\s/g, '%20')
138
+ .split(/\s/)
139
+ .filter(Boolean)
140
+ .map(value => value.replace(/%20/g, ' '));
141
+
142
+ /**
143
+ * Prepends options from run-control file to the command line arguments.
144
+ *
145
+ * @deprecated Deprecated in v6.0.0; This function is no longer used internally and will be removed in a future version.
146
+ * @public
147
+ * @alias module:lib/cli/options
148
+ * @see {@link https://mochajs.org/#mochaopts|mocha.opts}
149
+ */
150
+ module.exports = function getOptions() {
151
+ deprecate(
152
+ 'getOptions() is DEPRECATED and will be removed from a future version of Mocha. Use loadOptions() instead'
153
+ );
154
+ if (process.argv.length === 3 && ONE_AND_DONE_ARGS.has(process.argv[2])) {
155
+ return;
156
+ }
157
+
158
+ const optsPath =
159
+ process.argv.indexOf('--opts') === -1
160
+ ? mocharc.opts
161
+ : process.argv[process.argv.indexOf('--opts') + 1];
162
+
163
+ try {
164
+ const options = parseMochaOpts(fs.readFileSync(optsPath, 'utf8'));
165
+
166
+ process.argv = process.argv
167
+ .slice(0, 2)
168
+ .concat(options.concat(process.argv.slice(2)));
169
+ } catch (ignore) {
170
+ // NOTE: should console.error() and throw the error
171
+ }
172
+
173
+ process.env.LOADED_MOCHA_OPTS = true;
174
+ };
175
+
176
+ /**
177
+ * Given filepath in `args.opts`, attempt to load and parse a `mocha.opts` file.
178
+ * @param {Object} [args] - Arguments object
179
+ * @param {string|boolean} [args.opts] - Filepath to mocha.opts; defaults to whatever's in `mocharc.opts`, or `false` to skip
180
+ * @returns {external:yargsParser.Arguments|void} If read, object containing parsed arguments
181
+ * @memberof module:lib/cli/options
182
+ * @public
183
+ */
184
+ const loadMochaOpts = (args = {}) => {
185
+ let result;
186
+ let filepath = args.opts;
187
+ // /dev/null is backwards compat
188
+ if (filepath === false || filepath === '/dev/null') {
189
+ return result;
190
+ }
191
+ filepath = filepath || mocharc.opts;
192
+ result = {};
193
+ let mochaOpts;
194
+ try {
195
+ mochaOpts = fs.readFileSync(filepath, 'utf8');
196
+ debug(`read ${filepath}`);
197
+ } catch (err) {
198
+ if (args.opts) {
199
+ throw new Error(`Unable to read ${filepath}: ${err}`);
200
+ }
201
+ // ignore otherwise. we tried
202
+ debug(`No mocha.opts found at ${filepath}`);
203
+ }
204
+
205
+ // real args should override `mocha.opts` which should override defaults.
206
+ // if there's an exception to catch here, I'm not sure what it is.
207
+ // by attaching the `no-opts` arg, we avoid re-parsing of `mocha.opts`.
208
+ if (mochaOpts) {
209
+ result = parse(parseMochaOpts(mochaOpts));
210
+ debug(`${filepath} parsed succesfully`);
211
+ }
212
+ return result;
213
+ };
214
+
215
+ module.exports.loadMochaOpts = loadMochaOpts;
216
+
217
+ /**
218
+ * Given path to config file in `args.config`, attempt to load & parse config file.
219
+ * @param {Object} [args] - Arguments object
220
+ * @param {string|boolean} [args.config] - Path to config file or `false` to skip
221
+ * @public
222
+ * @memberof module:lib/cli/options
223
+ * @returns {external:yargsParser.Arguments|void} Parsed config, or nothing if `args.config` is `false`
224
+ */
225
+ const loadRc = (args = {}) => {
226
+ if (args.config !== false) {
227
+ const config = args.config || findConfig();
228
+ return config ? loadConfig(config) : {};
229
+ }
230
+ };
231
+
232
+ module.exports.loadRc = loadRc;
233
+
234
+ /**
235
+ * Given path to `package.json` in `args.package`, attempt to load config from `mocha` prop.
236
+ * @param {Object} [args] - Arguments object
237
+ * @param {string|boolean} [args.config] - Path to `package.json` or `false` to skip
238
+ * @public
239
+ * @memberof module:lib/cli/options
240
+ * @returns {external:yargsParser.Arguments|void} Parsed config, or nothing if `args.package` is `false`
241
+ */
242
+ const loadPkgRc = (args = {}) => {
243
+ let result;
244
+ if (args.package === false) {
245
+ return result;
246
+ }
247
+ result = {};
248
+ const filepath = args.package || findup(mocharc.package);
249
+ if (filepath) {
250
+ try {
251
+ const pkg = JSON.parse(fs.readFileSync(filepath, 'utf8'));
252
+ if (pkg.mocha) {
253
+ debug(`'mocha' prop of package.json parsed:`, pkg.mocha);
254
+ result = pkg.mocha;
255
+ } else {
256
+ debug(`no config found in ${filepath}`);
257
+ }
258
+ } catch (err) {
259
+ if (args.package) {
260
+ throw new Error(`Unable to read/parse ${filepath}: ${err}`);
261
+ }
262
+ debug(`failed to read default package.json at ${filepath}; ignoring`);
263
+ }
264
+ }
265
+ return result;
266
+ };
267
+
268
+ module.exports.loadPkgRc = loadPkgRc;
269
+
270
+ /**
271
+ * Priority list:
272
+ *
273
+ * 1. Command-line args
274
+ * 2. RC file (`.mocharc.js`, `.mocharc.ya?ml`, `mocharc.json`)
275
+ * 3. `mocha` prop of `package.json`
276
+ * 4. `mocha.opts`
277
+ * 5. default configuration (`lib/mocharc.json`)
278
+ *
279
+ * 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.
280
+ * @summary Parses options read from `mocha.opts`, `.mocharc.*` and `package.json`.
281
+ * @param {string|string[]} [argv] - Arguments to parse
282
+ * @public
283
+ * @memberof module:lib/cli/options
284
+ * @returns {external:yargsParser.Arguments} Parsed args from everything
285
+ */
286
+ const loadOptions = (argv = []) => {
287
+ let args = parse(argv);
288
+ // short-circuit: look for a flag that would abort loading of mocha.opts
289
+ if (
290
+ Array.from(ONE_AND_DONE_ARGS).reduce(
291
+ (acc, arg) => acc || arg in args,
292
+ false
293
+ )
294
+ ) {
295
+ return args;
296
+ }
297
+
298
+ const rcConfig = loadRc(args);
299
+ const pkgConfig = loadPkgRc(args);
300
+ const optsConfig = loadMochaOpts(args);
301
+
302
+ if (rcConfig) {
303
+ args.config = false;
304
+ }
305
+ if (pkgConfig) {
306
+ args.package = false;
307
+ }
308
+ if (optsConfig) {
309
+ args.opts = false;
310
+ }
311
+
312
+ args = parse(
313
+ args._,
314
+ args,
315
+ rcConfig || {},
316
+ pkgConfig || {},
317
+ optsConfig || {},
318
+ mocharc
319
+ );
320
+
321
+ // recombine positional arguments and "spec"
322
+ if (args.spec) {
323
+ args._ = args._.concat(args.spec);
324
+ delete args.spec;
325
+ }
326
+
327
+ return args;
328
+ };
329
+
330
+ module.exports.loadOptions = loadOptions;