mocha 7.0.1 → 7.2.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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,87 @@
1
+ # 7.2.0 / 2020-05-22
2
+
3
+ ## :tada: Enhancements
4
+
5
+ - [#4234](https://github.com/mochajs/mocha/issues/4234): Add ability to run tests in a mocha instance multiple times ([**@nicojs**](https://github.com/nicojs))
6
+ - [#4219](https://github.com/mochajs/mocha/issues/4219): Exposing filename in JSON, doc, and json-stream reporters ([**@Daniel0113**](https://github.com/Daniel0113))
7
+ - [#4244](https://github.com/mochajs/mocha/issues/4244): Add Root Hook Plugins ([**@boneskull**](https://github.com/boneskull))
8
+
9
+ ## :bug: Fixes
10
+
11
+ - [#4258](https://github.com/mochajs/mocha/issues/4258): Fix missing dot in name of configuration file ([**@sonicdoe**](https://github.com/sonicdoe))
12
+ - [#4194](https://github.com/mochajs/mocha/issues/4194): Check if module.paths really exists ([**@ematipico**](https://github.com/ematipico))
13
+ - [#4256](https://github.com/mochajs/mocha/issues/4256): `--forbid-only` does not recognize `it.only` when `before` crashes ([**@arvidOtt**](https://github.com/arvidOtt))
14
+ - [#4152](https://github.com/mochajs/mocha/issues/4152): Bug with multiple async done() calls ([**@boneskull**](https://github.com/boneskull))
15
+ - [#4275](https://github.com/mochajs/mocha/issues/4275): Improper warnings for invalid reporters ([**@boneskull**](https://github.com/boneskull))
16
+ - [#4288](https://github.com/mochajs/mocha/issues/4288): Broken hook.spec.js test for IE11 ([**@boneskull**](https://github.com/boneskull))
17
+
18
+ ## :book: Documentation
19
+
20
+ - [#4081](https://github.com/mochajs/mocha/issues/4081): Insufficient white space for API docs in view on mobile ([**@HyunSangHan**](https://github.com/HyunSangHan))
21
+ - [#4255](https://github.com/mochajs/mocha/issues/4255): Update mocha-docdash for UI fixes on API docs ([**@craigtaub**](https://github.com/craigtaub))
22
+ - [#4235](https://github.com/mochajs/mocha/issues/4235): Enable emoji on website; enable normal ul elements ([**@boneskull**](https://github.com/boneskull))
23
+ - [#4272](https://github.com/mochajs/mocha/issues/4272): Fetch sponsors at build time, show ALL non-skeevy sponsors ([**@boneskull**](https://github.com/boneskull))
24
+
25
+ ## :nut_and_bolt: Other
26
+
27
+ - [#4249](https://github.com/mochajs/mocha/issues/4249): Refactoring improving encapsulation ([**@arvidOtt**](https://github.com/arvidOtt))
28
+ - [#4242](https://github.com/mochajs/mocha/issues/4242): CI add job names, add Node.js v14 to matrix ([**@boneskull**](https://github.com/boneskull))
29
+ - [#4237](https://github.com/mochajs/mocha/issues/4237): Refactor validatePlugins to throw coded errors ([**@boneskull**](https://github.com/boneskull))
30
+ - [#4236](https://github.com/mochajs/mocha/issues/4236): Better debug output ([**@boneskull**](https://github.com/boneskull))
31
+
32
+ # 7.1.2 / 2020-04-26
33
+
34
+ ## :nut_and_bolt: Other
35
+
36
+ - [#4251](https://github.com/mochajs/mocha/issues/4251): Prevent karma-mocha from stalling ([**@juergba**](https://github.com/juergba))
37
+ - [#4222](https://github.com/mochajs/mocha/issues/4222): Update dependency mkdirp to v0.5.5 ([**@outsideris**](https://github.com/outsideris))
38
+
39
+ ## :book: Documentation
40
+
41
+ - [#4208](https://github.com/mochajs/mocha/issues/4208): Add Wallaby logo to site ([**@boneskull**](https://github.com/boneskull))
42
+
43
+ # 7.1.1 / 2020-03-18
44
+
45
+ ## :lock: Security Fixes
46
+
47
+ - [#4204](https://github.com/mochajs/mocha/issues/4204): Update dependencies mkdirp, yargs-parser and yargs ([**@juergba**](https://github.com/juergba))
48
+
49
+ ## :bug: Fixes
50
+
51
+ - [#3660](https://github.com/mochajs/mocha/issues/3660): Fix `runner` listening to `start` and `end` events ([**@juergba**](https://github.com/juergba))
52
+
53
+ ## :book: Documentation
54
+
55
+ - [#4190](https://github.com/mochajs/mocha/issues/4190): Show Netlify badge on footer ([**@outsideris**](https://github.com/outsideris))
56
+
57
+ # 7.1.0 / 2020-02-26
58
+
59
+ ## :tada: Enhancements
60
+
61
+ [#4038](https://github.com/mochajs/mocha/issues/4038): Add Node.js native ESM support ([**@giltayar**](https://github.com/giltayar))
62
+
63
+ Mocha supports writing your test files as ES modules:
64
+
65
+ - Node.js only v12.11.0 and above
66
+ - Node.js below v13.2.0, you must set `--experimental-modules` option
67
+ - current limitations: please check our [documentation](https://mochajs.org/#nodejs-native-esm-support)
68
+ - for programmatic usage: see [API: loadFilesAsync()](https://mochajs.org/api/mocha#loadFilesAsync)
69
+
70
+ **Note:** Node.JS native [ECMAScript Modules](https://nodejs.org/api/esm.html) implementation has status: **Stability: 1 - Experimental**
71
+
72
+ ## :bug: Fixes
73
+
74
+ - [#4181](https://github.com/mochajs/mocha/issues/4181): Programmatic API cannot access retried test objects ([**@juergba**](https://github.com/juergba))
75
+ - [#4174](https://github.com/mochajs/mocha/issues/4174): Browser: fix `allowUncaught` option ([**@juergba**](https://github.com/juergba))
76
+
77
+ ## :book: Documentation
78
+
79
+ - [#4058](https://github.com/mochajs/mocha/issues/4058): Manage author list in AUTHORS instead of `package.json` ([**@outsideris**](https://github.com/outsideris))
80
+
81
+ ## :nut_and_bolt: Other
82
+
83
+ - [#4138](https://github.com/mochajs/mocha/issues/4138): Upgrade ESLint v6.8 ([**@kaicataldo**](https://github.com/kaicataldo))
84
+
1
85
  # 7.0.1 / 2020-01-25
2
86
 
3
87
  ## :bug: Fixes
@@ -493,7 +577,7 @@ This release fixes a class of tests which report as _false positives_. **Certain
493
577
 
494
578
  - [#3226](https://github.com/mochajs/mocha/issues/3226): Do not swallow errors that are thrown asynchronously from passing tests ([@boneskull](https://github.com/boneskull)). Example:
495
579
 
496
- \```js
580
+ \`\`\`js
497
581
  it('should actually fail, sorry!', function (done) {
498
582
  // passing assertion
499
583
  assert(true === true);
@@ -506,7 +590,7 @@ This release fixes a class of tests which report as _false positives_. **Certain
506
590
  throw new Error('chaos!');
507
591
  }, 100);
508
592
  });
509
- \```
593
+ \`\`\`
510
594
 
511
595
  Previously to this version, Mocha would have _silently swallowed_ the `chaos!` exception, and you wouldn't know. Well, _now you know_. Mocha cannot recover from this gracefully, so it will exit with a nonzero code.
512
596
 
package/bin/mocha CHANGED
@@ -34,7 +34,7 @@ debug('loaded opts', opts);
34
34
  */
35
35
  const disableTimeouts = value => {
36
36
  if (impliesNoTimeouts(value)) {
37
- debug(`option "${value}" disabled timeouts`);
37
+ debug('option %s disabled timeouts', value);
38
38
  mochaArgs.timeout = 0;
39
39
  delete mochaArgs.timeouts;
40
40
  delete mochaArgs.t;
@@ -108,7 +108,11 @@ if (Object.keys(nodeArgs).length) {
108
108
  unparse(mochaArgs, {alias: aliases})
109
109
  );
110
110
 
111
- debug(`exec ${process.execPath} w/ args:`, args);
111
+ debug(
112
+ 'forking child process via command: %s %s',
113
+ process.execPath,
114
+ args.join(' ')
115
+ );
112
116
 
113
117
  const proc = spawn(process.execPath, args, {
114
118
  stdio: 'inherit'
@@ -130,5 +134,6 @@ if (Object.keys(nodeArgs).length) {
130
134
  proc.kill('SIGTERM'); // if that didn't work, we're probably in an infinite loop, so make it die.
131
135
  });
132
136
  } else {
137
+ debug('running Mocha in-process');
133
138
  require('../lib/cli/cli').main(unparse(mochaArgs, {alias: aliases}));
134
139
  }
package/browser-entry.js CHANGED
@@ -52,6 +52,17 @@ process.removeListener = function(e, fn) {
52
52
  }
53
53
  };
54
54
 
55
+ /**
56
+ * Implements listenerCount for 'uncaughtException'.
57
+ */
58
+
59
+ process.listenerCount = function(name) {
60
+ if (name === 'uncaughtException') {
61
+ return uncaughtExceptionHandlers.length;
62
+ }
63
+ return 0;
64
+ };
65
+
55
66
  /**
56
67
  * Implements uncaughtException listener.
57
68
  */
@@ -60,7 +71,7 @@ process.on = function(e, fn) {
60
71
  if (e === 'uncaughtException') {
61
72
  global.onerror = function(err, url, line) {
62
73
  fn(new Error(err + ' (' + url + ':' + line + ')'));
63
- return !mocha.allowUncaught;
74
+ return !mocha.options.allowUncaught;
64
75
  };
65
76
  uncaughtExceptionHandlers.push(fn);
66
77
  }
@@ -129,7 +140,7 @@ mocha.setup = function(opts) {
129
140
  opts = {ui: opts};
130
141
  }
131
142
  for (var opt in opts) {
132
- if (opts.hasOwnProperty(opt)) {
143
+ if (Object.prototype.hasOwnProperty.call(opts, opt)) {
133
144
  this[opt](opts[opt]);
134
145
  }
135
146
  }
package/lib/cli/cli.js CHANGED
@@ -19,6 +19,7 @@ const {loadOptions, YARGS_PARSER_CONFIG} = require('./options');
19
19
  const commands = require('./commands');
20
20
  const ansi = require('ansi-colors');
21
21
  const {repository, homepage, version, gitter} = require('../../package.json');
22
+ const {cwd} = require('../utils');
22
23
 
23
24
  /**
24
25
  * - Accepts an `Array` of arguments
@@ -30,7 +31,9 @@ const {repository, homepage, version, gitter} = require('../../package.json');
30
31
  exports.main = (argv = process.argv.slice(2)) => {
31
32
  debug('entered main with raw args', argv);
32
33
  // ensure we can require() from current working directory
33
- module.paths.push(process.cwd(), path.resolve('node_modules'));
34
+ if (typeof module.paths !== 'undefined') {
35
+ module.paths.push(cwd(), path.resolve('node_modules'));
36
+ }
34
37
 
35
38
  Error.stackTraceLimit = Infinity; // configurable via --stack-trace-limit?
36
39
 
@@ -5,6 +5,7 @@ const ansi = require('ansi-colors');
5
5
  const debug = require('debug')('mocha:cli:run:helpers');
6
6
  const minimatch = require('minimatch');
7
7
  const utils = require('../utils');
8
+ const {NO_FILES_MATCH_PATTERN} = require('../errors').constants;
8
9
 
9
10
  /**
10
11
  * Exports a function that collects test files from CLI parameters.
@@ -34,7 +35,7 @@ module.exports = ({ignore, extension, file, recursive, sort, spec} = {}) => {
34
35
  try {
35
36
  newFiles = utils.lookupFiles(arg, extension, recursive);
36
37
  } catch (err) {
37
- if (err.code === 'ERR_MOCHA_NO_FILES_MATCH_PATTERN') {
38
+ if (err.code === NO_FILES_MATCH_PATTERN) {
38
39
  unmatched.push({message: err.message, pattern: err.pattern});
39
40
  return;
40
41
  }
package/lib/cli/config.js CHANGED
@@ -12,6 +12,7 @@ const fs = require('fs');
12
12
  const path = require('path');
13
13
  const debug = require('debug')('mocha:cli:config');
14
14
  const findUp = require('find-up');
15
+ const utils = require('../utils');
15
16
 
16
17
  /**
17
18
  * These are the valid config files, in order of precedence;
@@ -21,6 +22,7 @@ const findUp = require('find-up');
21
22
  * @private
22
23
  */
23
24
  exports.CONFIG_FILES = [
25
+ '.mocharc.cjs',
24
26
  '.mocharc.js',
25
27
  '.mocharc.yaml',
26
28
  '.mocharc.yml',
@@ -42,11 +44,11 @@ const parsers = (exports.parsers = {
42
44
  js: filepath => {
43
45
  const cwdFilepath = path.resolve(filepath);
44
46
  try {
45
- debug(`parsers: load using cwd-relative path: "${cwdFilepath}"`);
47
+ debug('parsers: load using cwd-relative path: "%s"', cwdFilepath);
46
48
  return require(cwdFilepath);
47
49
  } catch (err) {
48
50
  if (isModuleNotFoundError(err)) {
49
- debug(`parsers: retry load as module-relative path: "${filepath}"`);
51
+ debug('parsers: retry load as module-relative path: "%s"', filepath);
50
52
  return require(filepath);
51
53
  } else {
52
54
  throw err; // rethrow
@@ -69,13 +71,13 @@ const parsers = (exports.parsers = {
69
71
  */
70
72
  exports.loadConfig = filepath => {
71
73
  let config = {};
72
- debug(`loadConfig: "${filepath}"`);
74
+ debug('loadConfig: trying to parse config at %s', filepath);
73
75
 
74
76
  const ext = path.extname(filepath);
75
77
  try {
76
78
  if (ext === '.yml' || ext === '.yaml') {
77
79
  config = parsers.yaml(filepath);
78
- } else if (ext === '.js') {
80
+ } else if (ext === '.js' || ext === '.cjs') {
79
81
  config = parsers.js(filepath);
80
82
  } else {
81
83
  config = parsers.json(filepath);
@@ -92,10 +94,10 @@ exports.loadConfig = filepath => {
92
94
  * @param {string} [cwd] - Current working directory
93
95
  * @returns {string|null} Filepath to config, if found
94
96
  */
95
- exports.findConfig = (cwd = process.cwd()) => {
97
+ exports.findConfig = (cwd = utils.cwd()) => {
96
98
  const filepath = findUp.sync(exports.CONFIG_FILES, {cwd});
97
99
  if (filepath) {
98
- debug(`findConfig: found "${filepath}"`);
100
+ debug('findConfig: found config file %s', filepath);
99
101
  }
100
102
  return filepath;
101
103
  };
@@ -244,16 +244,16 @@ const loadPkgRc = (args = {}) => {
244
244
  try {
245
245
  const pkg = JSON.parse(fs.readFileSync(filepath, 'utf8'));
246
246
  if (pkg.mocha) {
247
- debug(`'mocha' prop of package.json parsed:`, pkg.mocha);
247
+ debug('`mocha` prop of package.json parsed: %O', pkg.mocha);
248
248
  result = pkg.mocha;
249
249
  } else {
250
- debug(`no config found in ${filepath}`);
250
+ debug('no config found in %s', filepath);
251
251
  }
252
252
  } catch (err) {
253
253
  if (args.package) {
254
254
  throw new Error(`Unable to read/parse ${filepath}: ${err}`);
255
255
  }
256
- debug(`failed to read default package.json at ${filepath}; ignoring`);
256
+ debug('failed to read default package.json at %s; ignoring', filepath);
257
257
  }
258
258
  }
259
259
  return result;
@@ -265,7 +265,7 @@ module.exports.loadPkgRc = loadPkgRc;
265
265
  * Priority list:
266
266
  *
267
267
  * 1. Command-line args
268
- * 2. RC file (`.mocharc.js`, `.mocharc.ya?ml`, `mocharc.json`)
268
+ * 2. RC file (`.mocharc.c?js`, `.mocharc.ya?ml`, `mocharc.json`)
269
269
  * 3. `mocha` prop of `package.json`
270
270
  * 4. `mocha.opts`
271
271
  * 5. default configuration (`lib/mocharc.json`)
@@ -12,10 +12,9 @@ const path = require('path');
12
12
  const debug = require('debug')('mocha:cli:run:helpers');
13
13
  const watchRun = require('./watch-run');
14
14
  const collectFiles = require('./collect-files');
15
-
16
- const cwd = (exports.cwd = process.cwd());
17
-
18
- exports.watchRun = watchRun;
15
+ const {type} = require('../utils');
16
+ const {format} = require('util');
17
+ const {createInvalidPluginError, createUnsupportedError} = require('../errors');
19
18
 
20
19
  /**
21
20
  * Exits Mocha when tests + code under test has finished execution (default)
@@ -75,36 +74,79 @@ exports.list = str =>
75
74
  Array.isArray(str) ? exports.list(str.join(',')) : str.split(/ *, */);
76
75
 
77
76
  /**
78
- * `require()` the modules as required by `--require <require>`
77
+ * `require()` the modules as required by `--require <require>`.
78
+ *
79
+ * Returns array of `mochaHooks` exports, if any.
79
80
  * @param {string[]} requires - Modules to require
81
+ * @returns {Promise<MochaRootHookObject|MochaRootHookFunction>} Any root hooks
80
82
  * @private
81
83
  */
82
- exports.handleRequires = (requires = []) => {
83
- requires.forEach(mod => {
84
+ exports.handleRequires = async (requires = []) =>
85
+ requires.reduce((acc, mod) => {
84
86
  let modpath = mod;
85
- if (fs.existsSync(mod, {cwd}) || fs.existsSync(`${mod}.js`, {cwd})) {
87
+ // this is relative to cwd
88
+ if (fs.existsSync(mod) || fs.existsSync(`${mod}.js`)) {
86
89
  modpath = path.resolve(mod);
87
- debug(`resolved ${mod} to ${modpath}`);
90
+ debug('resolved required file %s to %s', mod, modpath);
88
91
  }
89
- require(modpath);
90
- debug(`loaded require "${mod}"`);
91
- });
92
+ const requiredModule = require(modpath);
93
+ if (type(requiredModule) === 'object' && requiredModule.mochaHooks) {
94
+ const mochaHooksType = type(requiredModule.mochaHooks);
95
+ if (/function$/.test(mochaHooksType) || mochaHooksType === 'object') {
96
+ debug('found root hooks in required file %s', mod);
97
+ acc.push(requiredModule.mochaHooks);
98
+ } else {
99
+ throw createUnsupportedError(
100
+ 'mochaHooks must be an object or a function returning (or fulfilling with) an object'
101
+ );
102
+ }
103
+ }
104
+ debug('loaded required module "%s"', mod);
105
+ return acc;
106
+ }, []);
107
+
108
+ /**
109
+ * Loads root hooks as exported via `mochaHooks` from required files.
110
+ * These can be sync/async functions returning objects, or just objects.
111
+ * Flattens to a single object.
112
+ * @param {Array<MochaRootHookObject|MochaRootHookFunction>} rootHooks - Array of root hooks
113
+ * @private
114
+ * @returns {MochaRootHookObject}
115
+ */
116
+ exports.loadRootHooks = async rootHooks => {
117
+ const rootHookObjects = await Promise.all(
118
+ rootHooks.map(async hook => (/function$/.test(type(hook)) ? hook() : hook))
119
+ );
120
+
121
+ return rootHookObjects.reduce(
122
+ (acc, hook) => {
123
+ acc.beforeAll = acc.beforeAll.concat(hook.beforeAll || []);
124
+ acc.beforeEach = acc.beforeEach.concat(hook.beforeEach || []);
125
+ acc.afterAll = acc.afterAll.concat(hook.afterAll || []);
126
+ acc.afterEach = acc.afterEach.concat(hook.afterEach || []);
127
+ return acc;
128
+ },
129
+ {beforeAll: [], beforeEach: [], afterAll: [], afterEach: []}
130
+ );
92
131
  };
93
132
 
94
133
  /**
95
- * Collect test files and run mocha instance.
134
+ * Collect and load test files, then run mocha instance.
96
135
  * @param {Mocha} mocha - Mocha instance
97
136
  * @param {Options} [opts] - Command line options
98
137
  * @param {boolean} [opts.exit] - Whether or not to force-exit after tests are complete
99
138
  * @param {Object} fileCollectParams - Parameters that control test
100
139
  * file collection. See `lib/cli/collect-files.js`.
101
- * @returns {Runner}
140
+ * @returns {Promise<Runner>}
102
141
  * @private
103
142
  */
104
- exports.singleRun = (mocha, {exit}, fileCollectParams) => {
143
+ const singleRun = async (mocha, {exit}, fileCollectParams) => {
105
144
  const files = collectFiles(fileCollectParams);
106
- debug('running tests with files', files);
145
+ debug('single run with %d file(s)', files.length);
107
146
  mocha.files = files;
147
+
148
+ // handles ESM modules
149
+ await mocha.loadFilesAsync();
108
150
  return mocha.run(exit ? exitMocha : exitMochaLater);
109
151
  };
110
152
 
@@ -113,8 +155,9 @@ exports.singleRun = (mocha, {exit}, fileCollectParams) => {
113
155
  * @param {Mocha} mocha - Mocha instance
114
156
  * @param {Object} opts - Command line options
115
157
  * @private
158
+ * @returns {Promise}
116
159
  */
117
- exports.runMocha = (mocha, options) => {
160
+ exports.runMocha = async (mocha, options) => {
118
161
  const {
119
162
  watch = false,
120
163
  extension = [],
@@ -140,40 +183,57 @@ exports.runMocha = (mocha, options) => {
140
183
  if (watch) {
141
184
  watchRun(mocha, {watchFiles, watchIgnore}, fileCollectParams);
142
185
  } else {
143
- exports.singleRun(mocha, {exit}, fileCollectParams);
186
+ await singleRun(mocha, {exit}, fileCollectParams);
144
187
  }
145
188
  };
146
189
 
147
190
  /**
148
- * Used for `--reporter` and `--ui`. Ensures there's only one, and asserts
149
- * that it actually exists.
150
- * @todo XXX This must get run after requires are processed, as it'll prevent
151
- * interfaces from loading.
191
+ * Used for `--reporter` and `--ui`. Ensures there's only one, and asserts that
192
+ * it actually exists. This must be run _after_ requires are processed (see
193
+ * {@link handleRequires}), as it'll prevent interfaces from loading otherwise.
152
194
  * @param {Object} opts - Options object
153
- * @param {string} key - Resolvable module name or path
154
- * @param {Object} [map] - An object perhaps having key `key`
195
+ * @param {"reporter"|"interface"} pluginType - Type of plugin.
196
+ * @param {Object} [map] - An object perhaps having key `key`. Used as a cache
197
+ * of sorts; `Mocha.reporters` is one, where each key corresponds to a reporter
198
+ * name
155
199
  * @private
156
200
  */
157
- exports.validatePlugin = (opts, key, map = {}) => {
158
- if (Array.isArray(opts[key])) {
159
- throw new TypeError(`"--${key} <${key}>" can only be specified once`);
201
+ exports.validatePlugin = (opts, pluginType, map = {}) => {
202
+ /**
203
+ * This should be a unique identifier; either a string (present in `map`),
204
+ * or a resolvable (via `require.resolve`) module ID/path.
205
+ * @type {string}
206
+ */
207
+ const pluginId = opts[pluginType];
208
+
209
+ if (Array.isArray(pluginId)) {
210
+ throw createInvalidPluginError(
211
+ `"--${pluginType}" can only be specified once`,
212
+ pluginType
213
+ );
160
214
  }
161
215
 
162
- const unknownError = () => new Error(`Unknown "${key}": ${opts[key]}`);
216
+ const unknownError = err =>
217
+ createInvalidPluginError(
218
+ format('Could not load %s "%s":\n\n %O', pluginType, pluginId, err),
219
+ pluginType,
220
+ pluginId
221
+ );
163
222
 
164
- if (!map[opts[key]]) {
223
+ // if this exists, then it's already loaded, so nothing more to do.
224
+ if (!map[pluginId]) {
165
225
  try {
166
- opts[key] = require(opts[key]);
226
+ opts[pluginType] = require(pluginId);
167
227
  } catch (err) {
168
228
  if (err.code === 'MODULE_NOT_FOUND') {
169
229
  // Try to load reporters from a path (absolute or relative)
170
230
  try {
171
- opts[key] = require(path.resolve(process.cwd(), opts[key]));
231
+ opts[pluginType] = require(path.resolve(pluginId));
172
232
  } catch (err) {
173
- throw unknownError();
233
+ throw unknownError(err);
174
234
  }
175
235
  } else {
176
- throw unknownError();
236
+ throw unknownError(err);
177
237
  }
178
238
  }
179
239
  }
package/lib/cli/run.js CHANGED
@@ -18,6 +18,7 @@ const {
18
18
  list,
19
19
  handleRequires,
20
20
  validatePlugin,
21
+ loadRootHooks,
21
22
  runMocha
22
23
  } = require('./run-helpers');
23
24
  const {ONE_AND_DONES, ONE_AND_DONE_ARGS} = require('./one-and-dones');
@@ -87,7 +88,6 @@ exports.builder = yargs =>
87
88
  },
88
89
  extension: {
89
90
  default: defaults.extension,
90
- defaultDescription: 'js',
91
91
  description: 'File extension(s) to load',
92
92
  group: GROUPS.FILES,
93
93
  requiresArg: true,
@@ -286,12 +286,24 @@ exports.builder = yargs =>
286
286
  );
287
287
  }
288
288
 
289
+ if (argv.opts) {
290
+ throw createUnsupportedError(
291
+ `--opts: configuring Mocha via 'mocha.opts' is DEPRECATED and no longer supported.
292
+ Please use a configuration file instead.`
293
+ );
294
+ }
295
+
296
+ return true;
297
+ })
298
+ .middleware(async argv => {
289
299
  // load requires first, because it can impact "plugin" validation
290
- handleRequires(argv.require);
300
+ const rawRootHooks = await handleRequires(argv.require);
291
301
  validatePlugin(argv, 'reporter', Mocha.reporters);
292
302
  validatePlugin(argv, 'ui', Mocha.interfaces);
293
303
 
294
- return true;
304
+ if (rawRootHooks.length) {
305
+ argv.rootHooks = await loadRootHooks(rawRootHooks);
306
+ }
295
307
  })
296
308
  .array(types.array)
297
309
  .boolean(types.boolean)
@@ -299,8 +311,14 @@ exports.builder = yargs =>
299
311
  .number(types.number)
300
312
  .alias(aliases);
301
313
 
302
- exports.handler = argv => {
314
+ exports.handler = async function(argv) {
303
315
  debug('post-yargs config', argv);
304
316
  const mocha = new Mocha(argv);
305
- runMocha(mocha, argv);
317
+
318
+ try {
319
+ await runMocha(mocha, argv);
320
+ } catch (err) {
321
+ console.error('\n' + (err.stack || `Error: ${err.message || err}`));
322
+ process.exit(1);
323
+ }
306
324
  };