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
@@ -0,0 +1,337 @@
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 minimatch = require('minimatch');
15
+ const Context = require('../context');
16
+ const Mocha = require('../mocha');
17
+ const utils = require('../utils');
18
+
19
+ const cwd = (exports.cwd = process.cwd());
20
+
21
+ /**
22
+ * Exits Mocha when tests + code under test has finished execution (default)
23
+ * @param {number} code - Exit code; typically # of failures
24
+ * @ignore
25
+ * @private
26
+ */
27
+ const exitMochaLater = code => {
28
+ process.on('exit', () => {
29
+ process.exitCode = Math.min(code, 255);
30
+ });
31
+ };
32
+
33
+ /**
34
+ * Exits Mocha when Mocha itself has finished execution, regardless of
35
+ * what the tests or code under test is doing.
36
+ * @param {number} code - Exit code; typically # of failures
37
+ * @ignore
38
+ * @private
39
+ */
40
+ const exitMocha = code => {
41
+ const clampedCode = Math.min(code, 255);
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
+ * Hide the cursor.
70
+ * @ignore
71
+ * @private
72
+ */
73
+ const hideCursor = () => {
74
+ process.stdout.write('\u001b[?25l');
75
+ };
76
+
77
+ /**
78
+ * Show the cursor.
79
+ * @ignore
80
+ * @private
81
+ */
82
+ const showCursor = () => {
83
+ process.stdout.write('\u001b[?25h');
84
+ };
85
+
86
+ /**
87
+ * Stop cursor business
88
+ * @private
89
+ */
90
+ const stop = () => {
91
+ process.stdout.write('\u001b[2K');
92
+ };
93
+
94
+ /**
95
+ * Coerce a comma-delimited string (or array thereof) into a flattened array of
96
+ * strings
97
+ * @param {string|string[]} str - Value to coerce
98
+ * @returns {string[]} Array of strings
99
+ * @private
100
+ */
101
+ exports.list = str =>
102
+ Array.isArray(str) ? exports.list(str.join(',')) : str.split(/ *, */);
103
+
104
+ /**
105
+ * `require()` the modules as required by `--require <require>`
106
+ * @param {string[]} requires - Modules to require
107
+ * @private
108
+ */
109
+ exports.handleRequires = (requires = []) => {
110
+ requires.forEach(mod => {
111
+ let modpath = mod;
112
+ if (fs.existsSync(mod, {cwd}) || fs.existsSync(`${mod}.js`, {cwd})) {
113
+ modpath = path.resolve(mod);
114
+ debug(`resolved ${mod} to ${modpath}`);
115
+ }
116
+ require(modpath);
117
+ debug(`loaded require "${mod}"`);
118
+ });
119
+ };
120
+
121
+ /**
122
+ * Smash together an array of test files in the correct order
123
+ * @param {Object} [opts] - Options
124
+ * @param {string[]} [opts.extension] - File extensions to use
125
+ * @param {string[]} [opts.spec] - Files, dirs, globs to run
126
+ * @param {string[]} [opts.exclude] - Files, dirs, globs to exclude
127
+ * @param {boolean} [opts.recursive=false] - Find files recursively
128
+ * @param {boolean} [opts.sort=false] - Sort test files
129
+ * @returns {string[]} List of files to test
130
+ * @private
131
+ */
132
+ exports.handleFiles = ({
133
+ exclude = [],
134
+ extension = [],
135
+ file = [],
136
+ recursive = false,
137
+ sort = false,
138
+ spec = []
139
+ } = {}) => {
140
+ let files = [];
141
+ const unmatched = [];
142
+ spec.forEach(arg => {
143
+ let newFiles;
144
+ try {
145
+ newFiles = utils.lookupFiles(arg, extension, recursive);
146
+ } catch (err) {
147
+ if (err.code === 'ERR_MOCHA_NO_FILES_MATCH_PATTERN') {
148
+ unmatched.push({message: err.message, pattern: err.pattern});
149
+ return;
150
+ }
151
+
152
+ throw err;
153
+ }
154
+
155
+ if (typeof newFiles !== 'undefined') {
156
+ if (typeof newFiles === 'string') {
157
+ newFiles = [newFiles];
158
+ }
159
+ newFiles = newFiles.filter(fileName =>
160
+ exclude.every(pattern => !minimatch(fileName, pattern))
161
+ );
162
+ }
163
+
164
+ files = files.concat(newFiles);
165
+ });
166
+
167
+ if (!files.length) {
168
+ // give full message details when only 1 file is missing
169
+ const noneFoundMsg =
170
+ unmatched.length === 1
171
+ ? `Error: No test files found: ${JSON.stringify(unmatched[0].pattern)}` // stringify to print escaped characters raw
172
+ : 'Error: No test files found';
173
+ console.error(ansi.red(noneFoundMsg));
174
+ process.exit(1);
175
+ } else {
176
+ // print messages as an warning
177
+ unmatched.forEach(warning => {
178
+ console.warn(ansi.yellow(`Warning: ${warning.message}`));
179
+ });
180
+ }
181
+
182
+ const fileArgs = file.map(filepath => path.resolve(filepath));
183
+ files = files.map(filepath => path.resolve(filepath));
184
+
185
+ // ensure we don't sort the stuff from fileArgs; order is important!
186
+ if (sort) {
187
+ files.sort();
188
+ }
189
+
190
+ // add files given through --file to be ran first
191
+ files = fileArgs.concat(files);
192
+ debug('files (in order): ', files);
193
+ return files;
194
+ };
195
+
196
+ /**
197
+ * Give Mocha files and tell it to run
198
+ * @param {Mocha} mocha - Mocha instance
199
+ * @param {Options} [opts] - Options
200
+ * @param {string[]} [opts.files] - List of test files
201
+ * @param {boolean} [opts.exit] - Whether or not to force-exit after tests are complete
202
+ * @returns {Runner}
203
+ * @private
204
+ */
205
+ exports.singleRun = (mocha, {files = [], exit = false} = {}) => {
206
+ mocha.files = files;
207
+ return mocha.run(exit ? exitMocha : exitMochaLater);
208
+ };
209
+
210
+ /**
211
+ * Run Mocha in "watch" mode
212
+ * @param {Mocha} mocha - Mocha instance
213
+ * @param {Object} [opts] - Options
214
+ * @param {string[]} [opts.extension] - List of extensions to watch
215
+ * @param {string|RegExp} [opts.grep] - Grep for test titles
216
+ * @param {string} [opts.ui=bdd] - User interface
217
+ * @param {string[]} [files] - Array of test files
218
+ * @private
219
+ */
220
+ exports.watchRun = (
221
+ mocha,
222
+ {extension = ['js'], grep = '', ui = 'bdd', files = []} = {}
223
+ ) => {
224
+ let runner;
225
+
226
+ console.log();
227
+ hideCursor();
228
+ process.on('SIGINT', () => {
229
+ showCursor();
230
+ console.log('\n');
231
+ process.exit(130);
232
+ });
233
+
234
+ const watchFiles = utils.files(cwd, extension);
235
+ let runAgain = false;
236
+
237
+ const loadAndRun = () => {
238
+ try {
239
+ mocha.files = files;
240
+ runAgain = false;
241
+ runner = mocha.run(() => {
242
+ runner = null;
243
+ if (runAgain) {
244
+ rerun();
245
+ }
246
+ });
247
+ } catch (e) {
248
+ console.log(e.stack);
249
+ }
250
+ };
251
+
252
+ const purge = () => {
253
+ watchFiles.forEach(Mocha.unloadFile);
254
+ };
255
+
256
+ loadAndRun();
257
+
258
+ const rerun = () => {
259
+ purge();
260
+ stop();
261
+ if (!grep) {
262
+ mocha.grep(null);
263
+ }
264
+ mocha.suite = mocha.suite.clone();
265
+ mocha.suite.ctx = new Context();
266
+ mocha.ui(ui);
267
+ loadAndRun();
268
+ };
269
+
270
+ utils.watch(watchFiles, () => {
271
+ runAgain = true;
272
+ if (runner) {
273
+ runner.abort();
274
+ } else {
275
+ rerun();
276
+ }
277
+ });
278
+ };
279
+
280
+ /**
281
+ * Actually run tests
282
+ * @param {Mocha} mocha - Mocha instance
283
+ * @param {Object} [opts] - Options
284
+ * @param {boolean} [opts.watch=false] - Enable watch mode
285
+ * @param {string[]} [opts.extension] - List of extensions to watch
286
+ * @param {string|RegExp} [opts.grep] - Grep for test titles
287
+ * @param {string} [opts.ui=bdd] - User interface
288
+ * @param {boolean} [opts.exit=false] - Force-exit Mocha when tests done
289
+ * @param {string[]} [files] - Array of test files
290
+ * @private
291
+ */
292
+ exports.runMocha = (
293
+ mocha,
294
+ {watch = false, extension = ['js'], grep = '', ui = 'bdd', exit = false} = {},
295
+ files = []
296
+ ) => {
297
+ if (watch) {
298
+ exports.watchRun(mocha, {extension, grep, ui});
299
+ } else {
300
+ exports.singleRun(mocha, {files, exit});
301
+ }
302
+ };
303
+
304
+ /**
305
+ * Used for `--reporter` and `--ui`. Ensures there's only one, and asserts
306
+ * that it actually exists.
307
+ * @todo XXX This must get run after requires are processed, as it'll prevent
308
+ * interfaces from loading.
309
+ * @param {Object} opts - Options object
310
+ * @param {string} key - Resolvable module name or path
311
+ * @param {Object} [map] - An object perhaps having key `key`
312
+ * @private
313
+ */
314
+ exports.validatePlugin = (opts, key, map = {}) => {
315
+ if (Array.isArray(opts[key])) {
316
+ throw new TypeError(`"--${key} <${key}>" can only be specified once`);
317
+ }
318
+
319
+ const unknownError = () => new Error(`Unknown "${key}": ${opts[key]}`);
320
+
321
+ if (!map[opts[key]]) {
322
+ try {
323
+ opts[key] = require(opts[key]);
324
+ } catch (err) {
325
+ if (err.code === 'MODULE_NOT_FOUND') {
326
+ // Try to load reporters from a path (absolute or relative)
327
+ try {
328
+ opts[key] = require(path.resolve(process.cwd(), opts[key]));
329
+ } catch (err) {
330
+ throw unknownError();
331
+ }
332
+ } else {
333
+ throw unknownError();
334
+ }
335
+ }
336
+ }
337
+ };
@@ -0,0 +1,76 @@
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
+ exports.types = {
16
+ array: [
17
+ 'exclude',
18
+ 'extension',
19
+ 'file',
20
+ 'global',
21
+ 'require',
22
+ 'reporter-option',
23
+ 'spec'
24
+ ],
25
+ boolean: [
26
+ 'allow-uncaught',
27
+ 'async-only',
28
+ 'bail',
29
+ 'check-leaks',
30
+ 'color',
31
+ 'delay',
32
+ 'diff',
33
+ 'exit',
34
+ 'forbid-only',
35
+ 'forbid-pending',
36
+ 'full-trace',
37
+ 'growl',
38
+ 'inline-diffs',
39
+ 'interfaces',
40
+ 'invert',
41
+ 'no-colors',
42
+ 'recursive',
43
+ 'reporters',
44
+ 'sort',
45
+ 'watch'
46
+ ],
47
+ number: ['retries', 'slow', 'timeout'],
48
+ string: ['fgrep', 'grep', 'package', 'reporter', 'ui']
49
+ };
50
+
51
+ /**
52
+ * Option aliases keyed by canonical option name.
53
+ * Arrays used to reduce
54
+ * @type {{string:string[]}}
55
+ * @private
56
+ */
57
+ exports.aliases = {
58
+ 'async-only': ['A'],
59
+ bail: ['b'],
60
+ color: ['c', 'colors'],
61
+ extension: ['watch-extensions'],
62
+ fgrep: ['f'],
63
+ global: ['globals'],
64
+ grep: ['g'],
65
+ growl: ['G'],
66
+ invert: ['i'],
67
+ 'no-colors': ['C'],
68
+ reporter: ['R'],
69
+ 'reporter-option': ['reporter-options', 'O'],
70
+ require: ['r'],
71
+ slow: ['s'],
72
+ sort: ['S'],
73
+ timeout: ['t', 'timeouts'],
74
+ ui: ['u'],
75
+ watch: ['w']
76
+ };
package/lib/cli/run.js ADDED
@@ -0,0 +1,297 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Definition for Mocha's default ("run tests") command
5
+ *
6
+ * @module
7
+ * @private
8
+ */
9
+
10
+ const Mocha = require('../mocha');
11
+ const {
12
+ createUnsupportedError,
13
+ createInvalidArgumentValueError,
14
+ createMissingArgumentError
15
+ } = require('../errors');
16
+
17
+ const {
18
+ list,
19
+ handleFiles,
20
+ handleRequires,
21
+ validatePlugin,
22
+ runMocha
23
+ } = require('./run-helpers');
24
+ const {ONE_AND_DONES, ONE_AND_DONE_ARGS} = require('./one-and-dones');
25
+ const debug = require('debug')('mocha:cli:run');
26
+ const defaults = require('../mocharc');
27
+ const {types, aliases} = require('./run-option-metadata');
28
+
29
+ /**
30
+ * Logical option groups
31
+ * @constant
32
+ */
33
+ const GROUPS = {
34
+ FILES: 'File Handling',
35
+ FILTERS: 'Test Filters',
36
+ NODEJS: 'Node.js & V8',
37
+ OUTPUT: 'Reporting & Output',
38
+ RULES: 'Rules & Behavior',
39
+ CONFIG: 'Configuration'
40
+ };
41
+
42
+ exports.command = ['$0 [spec..]', 'debug [spec..]'];
43
+
44
+ exports.describe = 'Run tests with Mocha';
45
+
46
+ exports.builder = yargs =>
47
+ yargs
48
+ .options({
49
+ 'allow-uncaught': {
50
+ description: 'Allow uncaught errors to propagate',
51
+ group: GROUPS.RULES
52
+ },
53
+ 'async-only': {
54
+ description:
55
+ 'Require all tests to use a callback (async) or return a Promise',
56
+ group: GROUPS.RULES
57
+ },
58
+ bail: {
59
+ description: 'Abort ("bail") after first test failure',
60
+ group: GROUPS.RULES
61
+ },
62
+ 'check-leaks': {
63
+ description: 'Check for global variable leaks',
64
+ group: GROUPS.RULES
65
+ },
66
+ color: {
67
+ description: 'Force-enable color output',
68
+ group: GROUPS.OUTPUT
69
+ },
70
+ config: {
71
+ config: true,
72
+ defaultDescription: '(nearest rc file)',
73
+ description: 'Path to config file',
74
+ group: GROUPS.CONFIG
75
+ },
76
+ delay: {
77
+ description: 'Delay initial execution of root suite',
78
+ group: GROUPS.RULES
79
+ },
80
+ diff: {
81
+ default: true,
82
+ description: 'Show diff on failure',
83
+ group: GROUPS.OUTPUT
84
+ },
85
+ exclude: {
86
+ defaultDescription: '(none)',
87
+ description: 'Ignore file(s) or glob pattern(s)',
88
+ group: GROUPS.FILES,
89
+ requiresArg: true
90
+ },
91
+ exit: {
92
+ description: 'Force Mocha to quit after tests complete',
93
+ group: GROUPS.RULES
94
+ },
95
+ extension: {
96
+ default: defaults.extension,
97
+ defaultDescription: 'js',
98
+ description: 'File extension(s) to load and/or watch',
99
+ group: GROUPS.FILES,
100
+ requiresArg: true,
101
+ coerce: list
102
+ },
103
+ fgrep: {
104
+ conflicts: 'grep',
105
+ description: 'Only run tests containing this string',
106
+ group: GROUPS.FILTERS,
107
+ requiresArg: true
108
+ },
109
+ file: {
110
+ defaultDescription: '(none)',
111
+ description:
112
+ 'Specify file(s) to be loaded prior to root suite execution',
113
+ group: GROUPS.FILES,
114
+ normalize: true,
115
+ requiresArg: true
116
+ },
117
+ 'forbid-only': {
118
+ description: 'Fail if exclusive test(s) encountered',
119
+ group: GROUPS.RULES
120
+ },
121
+ 'forbid-pending': {
122
+ description: 'Fail if pending test(s) encountered',
123
+ group: GROUPS.RULES
124
+ },
125
+ 'full-trace': {
126
+ description: 'Display full stack traces',
127
+ group: GROUPS.OUTPUT
128
+ },
129
+ global: {
130
+ coerce: list,
131
+ description: 'List of allowed global variables',
132
+ group: GROUPS.RULES,
133
+ requiresArg: true
134
+ },
135
+ grep: {
136
+ coerce: value => (!value ? null : value),
137
+ conflicts: 'fgrep',
138
+ description: 'Only run tests matching this string or regexp',
139
+ group: GROUPS.FILTERS,
140
+ requiresArg: true
141
+ },
142
+ growl: {
143
+ description: 'Enable Growl notifications',
144
+ group: GROUPS.OUTPUT
145
+ },
146
+ 'inline-diffs': {
147
+ description:
148
+ 'Display actual/expected differences inline within each string',
149
+ group: GROUPS.OUTPUT
150
+ },
151
+ interfaces: {
152
+ conflicts: Array.from(ONE_AND_DONE_ARGS),
153
+ description: 'List built-in user interfaces & exit'
154
+ },
155
+ invert: {
156
+ description: 'Inverts --grep and --fgrep matches',
157
+ group: GROUPS.FILTERS
158
+ },
159
+ 'no-colors': {
160
+ description: 'Force-disable color output',
161
+ group: GROUPS.OUTPUT,
162
+ hidden: true
163
+ },
164
+ opts: {
165
+ default: defaults.opts,
166
+ description: 'Path to `mocha.opts`',
167
+ group: GROUPS.CONFIG,
168
+ normalize: true,
169
+ requiresArg: true
170
+ },
171
+ package: {
172
+ description: 'Path to package.json for config',
173
+ group: GROUPS.CONFIG,
174
+ normalize: true,
175
+ requiresArg: true
176
+ },
177
+ recursive: {
178
+ description: 'Look for tests in subdirectories',
179
+ group: GROUPS.FILES
180
+ },
181
+ reporter: {
182
+ default: defaults.reporter,
183
+ description: 'Specify reporter to use',
184
+ group: GROUPS.OUTPUT,
185
+ requiresArg: true
186
+ },
187
+ reporters: {
188
+ conflicts: Array.from(ONE_AND_DONE_ARGS),
189
+ description: 'List built-in reporters & exit'
190
+ },
191
+ 'reporter-option': {
192
+ coerce: opts =>
193
+ list(opts).reduce((acc, opt) => {
194
+ const pair = opt.split('=');
195
+
196
+ if (pair.length > 2 || !pair.length) {
197
+ throw createInvalidArgumentValueError(
198
+ `invalid reporter option '${opt}'`,
199
+ '--reporter-option',
200
+ opt,
201
+ 'expected "key=value" format'
202
+ );
203
+ }
204
+
205
+ acc[pair[0]] = pair.length === 2 ? pair[1] : true;
206
+ return acc;
207
+ }, {}),
208
+ description: 'Reporter-specific options (<k=v,[k1=v1,..]>)',
209
+ group: GROUPS.OUTPUT,
210
+ requiresArg: true
211
+ },
212
+ require: {
213
+ defaultDescription: '(none)',
214
+ description: 'Require module',
215
+ group: GROUPS.FILES,
216
+ requiresArg: true
217
+ },
218
+ retries: {
219
+ description: 'Retry failed tests this many times',
220
+ group: GROUPS.RULES
221
+ },
222
+ slow: {
223
+ default: defaults.slow,
224
+ description: 'Specify "slow" test threshold (in milliseconds)',
225
+ group: GROUPS.RULES
226
+ },
227
+ sort: {
228
+ description: 'Sort test files',
229
+ group: GROUPS.FILES
230
+ },
231
+ timeout: {
232
+ default: defaults.timeout,
233
+ description: 'Specify test timeout threshold (in milliseconds)',
234
+ group: GROUPS.RULES
235
+ },
236
+ ui: {
237
+ default: defaults.ui,
238
+ description: 'Specify user interface',
239
+ group: GROUPS.RULES,
240
+ requiresArg: true
241
+ },
242
+ watch: {
243
+ description: 'Watch files in the current working directory for changes',
244
+ group: GROUPS.FILES
245
+ }
246
+ })
247
+ .positional('spec', {
248
+ default: ['test/'],
249
+ description: 'One or more files, directories, or globs to test',
250
+ type: 'array'
251
+ })
252
+ .check(argv => {
253
+ // "one-and-dones"; let yargs handle help and version
254
+ Object.keys(ONE_AND_DONES).forEach(opt => {
255
+ if (argv[opt]) {
256
+ ONE_AND_DONES[opt].call(null, yargs);
257
+ process.exit();
258
+ }
259
+ });
260
+
261
+ // yargs.implies() isn't flexible enough to handle this
262
+ if (argv.invert && !('fgrep' in argv || 'grep' in argv)) {
263
+ throw createMissingArgumentError(
264
+ '"--invert" requires one of "--fgrep <str>" or "--grep <regexp>"',
265
+ '--fgrep|--grep',
266
+ 'string|regexp'
267
+ );
268
+ }
269
+
270
+ if (argv.compilers) {
271
+ throw createUnsupportedError(
272
+ `--compilers is DEPRECATED and no longer supported.
273
+ See https://git.io/vdcSr for migration information.`
274
+ );
275
+ }
276
+
277
+ // load requires first, because it can impact "plugin" validation
278
+ handleRequires(argv.require);
279
+ validatePlugin(argv, 'reporter', Mocha.reporters);
280
+ validatePlugin(argv, 'ui', Mocha.interfaces);
281
+
282
+ return true;
283
+ })
284
+ .array(types.array)
285
+ .boolean(types.boolean)
286
+ .string(types.string)
287
+ .number(types.number)
288
+ .alias(aliases);
289
+
290
+ exports.handler = argv => {
291
+ debug('post-yargs config', argv);
292
+ const mocha = new Mocha(argv);
293
+ const files = handleFiles(argv);
294
+
295
+ debug('running tests with files', files);
296
+ runMocha(mocha, argv, files);
297
+ };