mocha 7.1.2 → 8.1.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 (49) hide show
  1. package/CHANGELOG.md +143 -2
  2. package/bin/mocha +24 -4
  3. package/browser-entry.js +37 -9
  4. package/lib/browser/growl.js +2 -1
  5. package/lib/browser/highlight-tags.js +39 -0
  6. package/lib/browser/parse-query.js +24 -0
  7. package/lib/browser/progress.js +4 -0
  8. package/lib/browser/template.html +7 -5
  9. package/lib/cli/cli.js +6 -3
  10. package/lib/cli/collect-files.js +17 -10
  11. package/lib/cli/config.js +6 -6
  12. package/lib/cli/init.js +1 -2
  13. package/lib/cli/lookup-files.js +145 -0
  14. package/lib/cli/node-flags.js +2 -2
  15. package/lib/cli/options.js +14 -90
  16. package/lib/cli/run-helpers.js +133 -36
  17. package/lib/cli/run-option-metadata.js +4 -2
  18. package/lib/cli/run.js +71 -11
  19. package/lib/cli/watch-run.js +211 -51
  20. package/lib/context.js +0 -15
  21. package/lib/errors.js +202 -9
  22. package/lib/esm-utils.js +11 -6
  23. package/lib/hook.js +32 -0
  24. package/lib/interfaces/common.js +21 -10
  25. package/lib/mocha.js +301 -126
  26. package/lib/mocharc.json +0 -1
  27. package/lib/nodejs/buffered-worker-pool.js +174 -0
  28. package/lib/{growl.js → nodejs/growl.js} +3 -2
  29. package/lib/nodejs/parallel-buffered-runner.js +295 -0
  30. package/lib/nodejs/reporters/parallel-buffered.js +133 -0
  31. package/lib/nodejs/serializer.js +404 -0
  32. package/lib/nodejs/worker.js +154 -0
  33. package/lib/pending.js +4 -0
  34. package/lib/reporters/base.js +25 -12
  35. package/lib/reporters/doc.js +6 -0
  36. package/lib/reporters/json-stream.js +1 -0
  37. package/lib/reporters/json.js +1 -0
  38. package/lib/reporters/landing.js +11 -3
  39. package/lib/reporters/tap.js +1 -2
  40. package/lib/reporters/xunit.js +3 -2
  41. package/lib/runnable.js +39 -47
  42. package/lib/runner.js +219 -118
  43. package/lib/suite.js +61 -27
  44. package/lib/test.js +48 -3
  45. package/lib/utils.js +33 -209
  46. package/mocha.js +25522 -17715
  47. package/mocha.js.map +1 -0
  48. package/package.json +87 -68
  49. package/lib/browser/tty.js +0 -13
package/lib/errors.js CHANGED
@@ -1,10 +1,78 @@
1
1
  'use strict';
2
+
3
+ var format = require('util').format;
4
+
2
5
  /**
6
+ * Factory functions to create throwable error objects
3
7
  * @module Errors
4
8
  */
9
+
5
10
  /**
6
- * Factory functions to create throwable error objects
11
+ * When Mocha throw exceptions (or otherwise errors), it attempts to assign a
12
+ * `code` property to the `Error` object, for easier handling. These are the
13
+ * potential values of `code`.
7
14
  */
15
+ var constants = {
16
+ /**
17
+ * An unrecoverable error.
18
+ */
19
+ FATAL: 'ERR_MOCHA_FATAL',
20
+
21
+ /**
22
+ * The type of an argument to a function call is invalid
23
+ */
24
+ INVALID_ARG_TYPE: 'ERR_MOCHA_INVALID_ARG_TYPE',
25
+
26
+ /**
27
+ * The value of an argument to a function call is invalid
28
+ */
29
+ INVALID_ARG_VALUE: 'ERR_MOCHA_INVALID_ARG_VALUE',
30
+
31
+ /**
32
+ * Something was thrown, but it wasn't an `Error`
33
+ */
34
+ INVALID_EXCEPTION: 'ERR_MOCHA_INVALID_EXCEPTION',
35
+
36
+ /**
37
+ * An interface (e.g., `Mocha.interfaces`) is unknown or invalid
38
+ */
39
+ INVALID_INTERFACE: 'ERR_MOCHA_INVALID_INTERFACE',
40
+
41
+ /**
42
+ * A reporter (.e.g, `Mocha.reporters`) is unknown or invalid
43
+ */
44
+ INVALID_REPORTER: 'ERR_MOCHA_INVALID_REPORTER',
45
+
46
+ /**
47
+ * `done()` was called twice in a `Test` or `Hook` callback
48
+ */
49
+ MULTIPLE_DONE: 'ERR_MOCHA_MULTIPLE_DONE',
50
+
51
+ /**
52
+ * No files matched the pattern provided by the user
53
+ */
54
+ NO_FILES_MATCH_PATTERN: 'ERR_MOCHA_NO_FILES_MATCH_PATTERN',
55
+
56
+ /**
57
+ * Known, but unsupported behavior of some kind
58
+ */
59
+ UNSUPPORTED: 'ERR_MOCHA_UNSUPPORTED',
60
+
61
+ /**
62
+ * Invalid state transition occurring in `Mocha` instance
63
+ */
64
+ INSTANCE_ALREADY_RUNNING: 'ERR_MOCHA_INSTANCE_ALREADY_RUNNING',
65
+
66
+ /**
67
+ * Invalid state transition occurring in `Mocha` instance
68
+ */
69
+ INSTANCE_ALREADY_DISPOSED: 'ERR_MOCHA_INSTANCE_ALREADY_DISPOSED',
70
+
71
+ /**
72
+ * Use of `only()` w/ `--forbid-only` results in this error.
73
+ */
74
+ FORBIDDEN_EXCLUSIVITY: 'ERR_MOCHA_FORBIDDEN_EXCLUSIVITY'
75
+ };
8
76
 
9
77
  /**
10
78
  * Creates an error object to be thrown when no files to be tested could be found using specified pattern.
@@ -16,7 +84,7 @@
16
84
  */
17
85
  function createNoFilesMatchPatternError(message, pattern) {
18
86
  var err = new Error(message);
19
- err.code = 'ERR_MOCHA_NO_FILES_MATCH_PATTERN';
87
+ err.code = constants.NO_FILES_MATCH_PATTERN;
20
88
  err.pattern = pattern;
21
89
  return err;
22
90
  }
@@ -31,7 +99,7 @@ function createNoFilesMatchPatternError(message, pattern) {
31
99
  */
32
100
  function createInvalidReporterError(message, reporter) {
33
101
  var err = new TypeError(message);
34
- err.code = 'ERR_MOCHA_INVALID_REPORTER';
102
+ err.code = constants.INVALID_REPORTER;
35
103
  err.reporter = reporter;
36
104
  return err;
37
105
  }
@@ -46,7 +114,7 @@ function createInvalidReporterError(message, reporter) {
46
114
  */
47
115
  function createInvalidInterfaceError(message, ui) {
48
116
  var err = new Error(message);
49
- err.code = 'ERR_MOCHA_INVALID_INTERFACE';
117
+ err.code = constants.INVALID_INTERFACE;
50
118
  err.interface = ui;
51
119
  return err;
52
120
  }
@@ -60,7 +128,7 @@ function createInvalidInterfaceError(message, ui) {
60
128
  */
61
129
  function createUnsupportedError(message) {
62
130
  var err = new Error(message);
63
- err.code = 'ERR_MOCHA_UNSUPPORTED';
131
+ err.code = constants.UNSUPPORTED;
64
132
  return err;
65
133
  }
66
134
 
@@ -88,7 +156,7 @@ function createMissingArgumentError(message, argument, expected) {
88
156
  */
89
157
  function createInvalidArgumentTypeError(message, argument, expected) {
90
158
  var err = new TypeError(message);
91
- err.code = 'ERR_MOCHA_INVALID_ARG_TYPE';
159
+ err.code = constants.INVALID_ARG_TYPE;
92
160
  err.argument = argument;
93
161
  err.expected = expected;
94
162
  err.actual = typeof argument;
@@ -107,7 +175,7 @@ function createInvalidArgumentTypeError(message, argument, expected) {
107
175
  */
108
176
  function createInvalidArgumentValueError(message, argument, value, reason) {
109
177
  var err = new TypeError(message);
110
- err.code = 'ERR_MOCHA_INVALID_ARG_VALUE';
178
+ err.code = constants.INVALID_ARG_VALUE;
111
179
  err.argument = argument;
112
180
  err.value = value;
113
181
  err.reason = typeof reason !== 'undefined' ? reason : 'is invalid';
@@ -123,12 +191,130 @@ function createInvalidArgumentValueError(message, argument, value, reason) {
123
191
  */
124
192
  function createInvalidExceptionError(message, value) {
125
193
  var err = new Error(message);
126
- err.code = 'ERR_MOCHA_INVALID_EXCEPTION';
194
+ err.code = constants.INVALID_EXCEPTION;
127
195
  err.valueType = typeof value;
128
196
  err.value = value;
129
197
  return err;
130
198
  }
131
199
 
200
+ /**
201
+ * Creates an error object to be thrown when an unrecoverable error occurs.
202
+ *
203
+ * @public
204
+ * @param {string} message - Error message to be displayed.
205
+ * @returns {Error} instance detailing the error condition
206
+ */
207
+ function createFatalError(message, value) {
208
+ var err = new Error(message);
209
+ err.code = constants.FATAL;
210
+ err.valueType = typeof value;
211
+ err.value = value;
212
+ return err;
213
+ }
214
+
215
+ /**
216
+ * Dynamically creates a plugin-type-specific error based on plugin type
217
+ * @param {string} message - Error message
218
+ * @param {"reporter"|"interface"} pluginType - Plugin type. Future: expand as needed
219
+ * @param {string} [pluginId] - Name/path of plugin, if any
220
+ * @throws When `pluginType` is not known
221
+ * @public
222
+ * @returns {Error}
223
+ */
224
+ function createInvalidPluginError(message, pluginType, pluginId) {
225
+ switch (pluginType) {
226
+ case 'reporter':
227
+ return createInvalidReporterError(message, pluginId);
228
+ case 'interface':
229
+ return createInvalidInterfaceError(message, pluginId);
230
+ default:
231
+ throw new Error('unknown pluginType "' + pluginType + '"');
232
+ }
233
+ }
234
+
235
+ /**
236
+ * Creates an error object to be thrown when a mocha object's `run` method is executed while it is already disposed.
237
+ * @param {string} message The error message to be displayed.
238
+ * @param {boolean} cleanReferencesAfterRun the value of `cleanReferencesAfterRun`
239
+ * @param {Mocha} instance the mocha instance that throw this error
240
+ */
241
+ function createMochaInstanceAlreadyDisposedError(
242
+ message,
243
+ cleanReferencesAfterRun,
244
+ instance
245
+ ) {
246
+ var err = new Error(message);
247
+ err.code = constants.INSTANCE_ALREADY_DISPOSED;
248
+ err.cleanReferencesAfterRun = cleanReferencesAfterRun;
249
+ err.instance = instance;
250
+ return err;
251
+ }
252
+
253
+ /**
254
+ * Creates an error object to be thrown when a mocha object's `run` method is called while a test run is in progress.
255
+ * @param {string} message The error message to be displayed.
256
+ */
257
+ function createMochaInstanceAlreadyRunningError(message, instance) {
258
+ var err = new Error(message);
259
+ err.code = constants.INSTANCE_ALREADY_RUNNING;
260
+ err.instance = instance;
261
+ return err;
262
+ }
263
+
264
+ /*
265
+ * Creates an error object to be thrown when done() is called multiple times in a test
266
+ *
267
+ * @public
268
+ * @param {Runnable} runnable - Original runnable
269
+ * @param {Error} [originalErr] - Original error, if any
270
+ * @returns {Error} instance detailing the error condition
271
+ */
272
+ function createMultipleDoneError(runnable, originalErr) {
273
+ var title;
274
+ try {
275
+ title = format('<%s>', runnable.fullTitle());
276
+ if (runnable.parent.root) {
277
+ title += ' (of root suite)';
278
+ }
279
+ } catch (ignored) {
280
+ title = format('<%s> (of unknown suite)', runnable.title);
281
+ }
282
+ var message = format(
283
+ 'done() called multiple times in %s %s',
284
+ runnable.type ? runnable.type : 'unknown runnable',
285
+ title
286
+ );
287
+ if (runnable.file) {
288
+ message += format(' of file %s', runnable.file);
289
+ }
290
+ if (originalErr) {
291
+ message += format('; in addition, done() received error: %s', originalErr);
292
+ }
293
+
294
+ var err = new Error(message);
295
+ err.code = constants.MULTIPLE_DONE;
296
+ err.valueType = typeof originalErr;
297
+ err.value = originalErr;
298
+ return err;
299
+ }
300
+
301
+ /**
302
+ * Creates an error object to be thrown when `.only()` is used with
303
+ * `--forbid-only`.
304
+ * @public
305
+ * @param {Mocha} mocha - Mocha instance
306
+ * @returns {Error} Error with code {@link constants.FORBIDDEN_EXCLUSIVITY}
307
+ */
308
+ function createForbiddenExclusivityError(mocha) {
309
+ var err = new Error(
310
+ mocha.isWorker
311
+ ? '`.only` is not supported in parallel mode'
312
+ : '`.only` forbidden by --forbid-only'
313
+ );
314
+ err.code = constants.FORBIDDEN_EXCLUSIVITY;
315
+ return err;
316
+ }
317
+
132
318
  module.exports = {
133
319
  createInvalidArgumentTypeError: createInvalidArgumentTypeError,
134
320
  createInvalidArgumentValueError: createInvalidArgumentValueError,
@@ -137,5 +323,12 @@ module.exports = {
137
323
  createInvalidReporterError: createInvalidReporterError,
138
324
  createMissingArgumentError: createMissingArgumentError,
139
325
  createNoFilesMatchPatternError: createNoFilesMatchPatternError,
140
- createUnsupportedError: createUnsupportedError
326
+ createUnsupportedError: createUnsupportedError,
327
+ createInvalidPluginError: createInvalidPluginError,
328
+ createMochaInstanceAlreadyDisposedError: createMochaInstanceAlreadyDisposedError,
329
+ createMochaInstanceAlreadyRunningError: createMochaInstanceAlreadyRunningError,
330
+ createFatalError: createFatalError,
331
+ createMultipleDoneError: createMultipleDoneError,
332
+ createForbiddenExclusivityError: createForbiddenExclusivityError,
333
+ constants: constants
141
334
  };
package/lib/esm-utils.js CHANGED
@@ -1,11 +1,16 @@
1
- const url = require('url');
2
1
  const path = require('path');
2
+ const url = require('url');
3
3
 
4
- const requireOrImport = async file => {
5
- file = path.resolve(file);
4
+ const formattedImport = async file => {
5
+ if (path.isAbsolute(file)) {
6
+ return import(url.pathToFileURL(file));
7
+ }
8
+ return import(file);
9
+ };
6
10
 
11
+ exports.requireOrImport = async file => {
7
12
  if (path.extname(file) === '.mjs') {
8
- return import(url.pathToFileURL(file));
13
+ return formattedImport(file);
9
14
  }
10
15
  // This is currently the only known way of figuring out whether a file is CJS or ESM.
11
16
  // If Node.js or the community establish a better procedure for that, we can fix this code.
@@ -15,7 +20,7 @@ const requireOrImport = async file => {
15
20
  return require(file);
16
21
  } catch (err) {
17
22
  if (err.code === 'ERR_REQUIRE_ESM') {
18
- return import(url.pathToFileURL(file));
23
+ return formattedImport(file);
19
24
  } else {
20
25
  throw err;
21
26
  }
@@ -25,7 +30,7 @@ const requireOrImport = async file => {
25
30
  exports.loadFilesAsync = async (files, preLoadFunc, postLoadFunc) => {
26
31
  for (const file of files) {
27
32
  preLoadFunc(file);
28
- const result = await requireOrImport(file);
33
+ const result = await exports.requireOrImport(path.resolve(file));
29
34
  postLoadFunc(file, result);
30
35
  }
31
36
  };
package/lib/hook.js CHANGED
@@ -27,6 +27,14 @@ function Hook(title, fn) {
27
27
  */
28
28
  inherits(Hook, Runnable);
29
29
 
30
+ /**
31
+ * Resets the state for a next run.
32
+ */
33
+ Hook.prototype.reset = function() {
34
+ Runnable.prototype.reset.call(this);
35
+ delete this._error;
36
+ };
37
+
30
38
  /**
31
39
  * Get or set the test `err`.
32
40
  *
@@ -44,3 +52,27 @@ Hook.prototype.error = function(err) {
44
52
 
45
53
  this._error = err;
46
54
  };
55
+
56
+ /**
57
+ * Returns an object suitable for IPC.
58
+ * Functions are represented by keys beginning with `$$`.
59
+ * @private
60
+ * @returns {Object}
61
+ */
62
+ Hook.prototype.serialize = function serialize() {
63
+ return {
64
+ $$isPending: this.isPending(),
65
+ $$titlePath: this.titlePath(),
66
+ ctx: {
67
+ currentTest: {
68
+ title: this.ctx && this.ctx.currentTest && this.ctx.currentTest.title
69
+ }
70
+ },
71
+ parent: {
72
+ root: this.parent.root,
73
+ title: this.parent.title
74
+ },
75
+ title: this.title,
76
+ type: this.type
77
+ };
78
+ };
@@ -1,12 +1,19 @@
1
1
  'use strict';
2
2
 
3
+ /**
4
+ @module interfaces/common
5
+ */
6
+
3
7
  var Suite = require('../suite');
4
8
  var errors = require('../errors');
5
9
  var createMissingArgumentError = errors.createMissingArgumentError;
10
+ var createUnsupportedError = errors.createUnsupportedError;
11
+ var createForbiddenExclusivityError = errors.createForbiddenExclusivityError;
6
12
 
7
13
  /**
8
14
  * Functions common to more than one interface.
9
15
  *
16
+ * @private
10
17
  * @param {Suite[]} suites
11
18
  * @param {Context} context
12
19
  * @param {Mocha} mocha
@@ -92,6 +99,9 @@ module.exports = function(suites, context, mocha) {
92
99
  * @returns {Suite}
93
100
  */
94
101
  only: function only(opts) {
102
+ if (mocha.options.forbidOnly) {
103
+ throw createForbiddenExclusivityError(mocha);
104
+ }
95
105
  opts.isOnly = true;
96
106
  return this.create(opts);
97
107
  },
@@ -125,16 +135,14 @@ module.exports = function(suites, context, mocha) {
125
135
  suite.file = opts.file;
126
136
  suites.unshift(suite);
127
137
  if (opts.isOnly) {
128
- if (mocha.options.forbidOnly && shouldBeTested(suite)) {
129
- throw new Error('`.only` forbidden');
130
- }
131
-
132
- suite.parent.appendOnlySuite(suite);
138
+ suite.markOnly();
133
139
  }
134
- if (suite.pending) {
135
- if (mocha.options.forbidPending && shouldBeTested(suite)) {
136
- throw new Error('Pending test forbidden');
137
- }
140
+ if (
141
+ suite.pending &&
142
+ mocha.options.forbidPending &&
143
+ shouldBeTested(suite)
144
+ ) {
145
+ throw createUnsupportedError('Pending test forbidden');
138
146
  }
139
147
  if (typeof opts.fn === 'function') {
140
148
  opts.fn.call(suite);
@@ -165,7 +173,10 @@ module.exports = function(suites, context, mocha) {
165
173
  * @returns {*}
166
174
  */
167
175
  only: function(mocha, test) {
168
- test.parent.appendOnlyTest(test);
176
+ if (mocha.options.forbidOnly) {
177
+ throw createForbiddenExclusivityError(mocha);
178
+ }
179
+ test.markOnly();
169
180
  return test;
170
181
  },
171
182