mocha 10.5.0 → 10.5.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.
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ const fs = require('fs');
3
4
  const path = require('path');
4
5
  const ansi = require('ansi-colors');
5
6
  const debug = require('debug')('mocha:cli:run:helpers');
@@ -19,7 +20,7 @@ const {castArray} = require('../utils');
19
20
  /**
20
21
  * Smash together an array of test files in the correct order
21
22
  * @param {FileCollectionOptions} [opts] - Options
22
- * @returns {string[]} List of files to test
23
+ * @returns {FileCollectionResponse} An object containing a list of files to test and unmatched files.
23
24
  * @private
24
25
  */
25
26
  module.exports = ({
@@ -30,7 +31,7 @@ module.exports = ({
30
31
  sort,
31
32
  spec
32
33
  } = {}) => {
33
- const unmatched = [];
34
+ const unmatchedSpecFiles = [];
34
35
  const specFiles = spec.reduce((specFiles, arg) => {
35
36
  try {
36
37
  const moreSpecFiles = castArray(lookupFiles(arg, extension, recursive))
@@ -44,7 +45,7 @@ module.exports = ({
44
45
  return [...specFiles, ...moreSpecFiles];
45
46
  } catch (err) {
46
47
  if (err.code === NO_FILES_MATCH_PATTERN) {
47
- unmatched.push({message: err.message, pattern: err.pattern});
48
+ unmatchedSpecFiles.push({message: err.message, pattern: err.pattern});
48
49
  return specFiles;
49
50
  }
50
51
 
@@ -52,6 +53,27 @@ module.exports = ({
52
53
  }
53
54
  }, []);
54
55
 
56
+ // check that each file passed in to --file exists
57
+
58
+ const unmatchedFiles = [];
59
+ fileArgs.forEach(file => {
60
+ const fileAbsolutePath = path.resolve(file);
61
+ try {
62
+ // Used instead of fs.existsSync to ensure that file-ending less files are still resolved correctly
63
+ require.resolve(fileAbsolutePath);
64
+ } catch (err) {
65
+ if (err.code === 'MODULE_NOT_FOUND') {
66
+ unmatchedFiles.push({
67
+ pattern: file,
68
+ absolutePath: fileAbsolutePath
69
+ });
70
+ return;
71
+ }
72
+
73
+ throw err;
74
+ }
75
+ });
76
+
55
77
  // ensure we don't sort the stuff from fileArgs; order is important!
56
78
  if (sort) {
57
79
  specFiles.sort();
@@ -67,19 +89,24 @@ module.exports = ({
67
89
  if (!files.length) {
68
90
  // give full message details when only 1 file is missing
69
91
  const noneFoundMsg =
70
- unmatched.length === 1
71
- ? `Error: No test files found: ${JSON.stringify(unmatched[0].pattern)}` // stringify to print escaped characters raw
92
+ unmatchedSpecFiles.length === 1
93
+ ? `Error: No test files found: ${JSON.stringify(
94
+ unmatchedSpecFiles[0].pattern
95
+ )}` // stringify to print escaped characters raw
72
96
  : 'Error: No test files found';
73
97
  console.error(ansi.red(noneFoundMsg));
74
98
  process.exit(1);
75
99
  } else {
76
100
  // print messages as a warning
77
- unmatched.forEach(warning => {
101
+ unmatchedSpecFiles.forEach(warning => {
78
102
  console.warn(ansi.yellow(`Warning: ${warning.message}`));
79
103
  });
80
104
  }
81
105
 
82
- return files;
106
+ return {
107
+ files,
108
+ unmatchedFiles
109
+ };
83
110
  };
84
111
 
85
112
  /**
@@ -93,3 +120,18 @@ module.exports = ({
93
120
  * @property {boolean} recursive - Find files recursively
94
121
  * @property {boolean} sort - Sort test files
95
122
  */
123
+
124
+ /**
125
+ * Diagnostic object containing unmatched files
126
+ * @typedef {Object} UnmatchedFile -
127
+ * @property {string} absolutePath - A list of unmatched files derived from the file arguments passed in.
128
+ * @property {string} pattern - A list of unmatched files derived from the file arguments passed in.
129
+ *
130
+ */
131
+
132
+ /**
133
+ * Response object containing a list of files to test and unmatched files.
134
+ * @typedef {Object} FileCollectionResponse
135
+ * @property {string[]} files - A list of files to test
136
+ * @property {UnmatchedFile[]} unmatchedFiles - A list of unmatched files derived from the file arguments passed in.
137
+ */
@@ -9,6 +9,7 @@
9
9
 
10
10
  const fs = require('fs');
11
11
  const path = require('path');
12
+ const ansi = require('ansi-colors');
12
13
  const debug = require('debug')('mocha:cli:run:helpers');
13
14
  const {watchRun, watchParallelRun} = require('./watch-run');
14
15
  const collectFiles = require('./collect-files');
@@ -16,6 +17,7 @@ const {format} = require('util');
16
17
  const {createInvalidLegacyPluginError} = require('../errors');
17
18
  const {requireOrImport} = require('../nodejs/esm-utils');
18
19
  const PluginLoader = require('../plugin-loader');
20
+ const {UnmatchedFile} = require('./collect-files');
19
21
 
20
22
  /**
21
23
  * Exits Mocha when tests + code under test has finished execution (default)
@@ -106,6 +108,32 @@ exports.handleRequires = async (requires = [], {ignoredPlugins = []} = {}) => {
106
108
  return plugins;
107
109
  };
108
110
 
111
+ /**
112
+ * Logs errors and exits the app if unmatched files exist
113
+ * @param {Mocha} mocha - Mocha instance
114
+ * @param {UnmatchedFile} unmatchedFiles - object containing unmatched file paths
115
+ * @returns {Promise<Runner>}
116
+ * @private
117
+ */
118
+ const handleUnmatchedFiles = (mocha, unmatchedFiles) => {
119
+ if (unmatchedFiles.length === 0) {
120
+ return;
121
+ }
122
+
123
+ unmatchedFiles.forEach(({pattern, absolutePath}) => {
124
+ console.error(
125
+ ansi.yellow(
126
+ `Warning: Cannot find any files matching pattern "${pattern}" at the absolute path "${absolutePath}"`
127
+ )
128
+ );
129
+ });
130
+ console.log(
131
+ 'No test file(s) found with the given pattern, exiting with code 1'
132
+ );
133
+
134
+ return mocha.run(exitMocha(1));
135
+ };
136
+
109
137
  /**
110
138
  * Collect and load test files, then run mocha instance.
111
139
  * @param {Mocha} mocha - Mocha instance
@@ -117,9 +145,14 @@ exports.handleRequires = async (requires = [], {ignoredPlugins = []} = {}) => {
117
145
  * @private
118
146
  */
119
147
  const singleRun = async (mocha, {exit}, fileCollectParams) => {
120
- const files = collectFiles(fileCollectParams);
121
- debug('single run with %d file(s)', files.length);
122
- mocha.files = files;
148
+ const fileCollectionObj = collectFiles(fileCollectParams);
149
+
150
+ if (fileCollectionObj.unmatchedFiles.length > 0) {
151
+ return handleUnmatchedFiles(mocha, fileCollectionObj.unmatchedFiles);
152
+ }
153
+
154
+ debug('single run with %d file(s)', fileCollectionObj.files.length);
155
+ mocha.files = fileCollectionObj.files;
123
156
 
124
157
  // handles ESM modules
125
158
  await mocha.loadFilesAsync();
@@ -140,9 +173,17 @@ const singleRun = async (mocha, {exit}, fileCollectParams) => {
140
173
  * @private
141
174
  */
142
175
  const parallelRun = async (mocha, options, fileCollectParams) => {
143
- const files = collectFiles(fileCollectParams);
144
- debug('executing %d test file(s) in parallel mode', files.length);
145
- mocha.files = files;
176
+ const fileCollectionObj = collectFiles(fileCollectParams);
177
+
178
+ if (fileCollectionObj.unmatchedFiles.length > 0) {
179
+ return handleUnmatchedFiles(mocha, fileCollectionObj.unmatchedFiles);
180
+ }
181
+
182
+ debug(
183
+ 'executing %d test file(s) in parallel mode',
184
+ fileCollectionObj.files.length
185
+ );
186
+ mocha.files = fileCollectionObj.files;
146
187
 
147
188
  // note that we DO NOT load any files here; this is handled by the worker
148
189
  return mocha.run(options.exit ? exitMocha : exitMochaLater);
@@ -58,7 +58,7 @@ exports.watchParallelRun = (
58
58
  newMocha.suite.ctx = new Context();
59
59
 
60
60
  // reset the list of files
61
- newMocha.files = collectFiles(fileCollectParams);
61
+ newMocha.files = collectFiles(fileCollectParams).files;
62
62
 
63
63
  // because we've swapped out the root suite (see the `run` inner function
64
64
  // in `createRerunner`), we need to call `mocha.ui()` again to set up the context/globals.
@@ -120,7 +120,7 @@ exports.watchRun = (mocha, {watchFiles, watchIgnore}, fileCollectParams) => {
120
120
  newMocha.suite.ctx = new Context();
121
121
 
122
122
  // reset the list of files
123
- newMocha.files = collectFiles(fileCollectParams);
123
+ newMocha.files = collectFiles(fileCollectParams).files;
124
124
 
125
125
  // because we've swapped out the root suite (see the `run` inner function
126
126
  // in `createRerunner`), we need to call `mocha.ui()` again to set up the context/globals.
@@ -6,7 +6,7 @@
6
6
 
7
7
  'use strict';
8
8
 
9
- const {type} = require('../utils');
9
+ const {type, breakCircularDeps} = require('../utils');
10
10
  const {createInvalidArgumentTypeError} = require('../errors');
11
11
  // this is not named `mocha:parallel:serializer` because it's noisy and it's
12
12
  // helpful to be able to write `DEBUG=mocha:parallel*` and get everything else.
@@ -188,14 +188,9 @@ class SerializableEvent {
188
188
  * @param {Array<object|string>} pairs - List of parent/key tuples to process; modified in-place. This JSDoc type is an approximation
189
189
  * @param {object} parent - Some parent object
190
190
  * @param {string} key - Key to inspect
191
- * @param {WeakSet<Object>} seenObjects - For avoiding circular references
192
191
  */
193
- static _serialize(pairs, parent, key, seenObjects) {
192
+ static _serialize(pairs, parent, key) {
194
193
  let value = parent[key];
195
- if (seenObjects.has(value)) {
196
- parent[key] = Object.create(null);
197
- return;
198
- }
199
194
  let _type = type(value);
200
195
  if (_type === 'error') {
201
196
  // we need to reference the stack prop b/c it's lazily-loaded.
@@ -263,13 +258,14 @@ class SerializableEvent {
263
258
  error: this.originalError
264
259
  });
265
260
 
261
+ // mutates the object
262
+ breakCircularDeps(result);
263
+
266
264
  const pairs = Object.keys(result).map(key => [result, key]);
267
- const seenObjects = new WeakSet();
268
265
 
269
266
  let pair;
270
267
  while ((pair = pairs.shift())) {
271
- SerializableEvent._serialize(pairs, ...pair, seenObjects);
272
- seenObjects.add(pair[0]);
268
+ SerializableEvent._serialize(pairs, ...pair);
273
269
  }
274
270
 
275
271
  this.data = result.data;
package/lib/utils.js CHANGED
@@ -647,3 +647,36 @@ exports.assignNewMochaID = obj => {
647
647
  */
648
648
  exports.getMochaID = obj =>
649
649
  obj && typeof obj === 'object' ? obj[MOCHA_ID_PROP_NAME] : undefined;
650
+
651
+ /**
652
+ * Replaces any detected circular dependency with the string '[Circular]'
653
+ * Mutates original object
654
+ * @param inputObj {*}
655
+ * @returns {*}
656
+ */
657
+ exports.breakCircularDeps = inputObj => {
658
+ const seen = new Set();
659
+
660
+ function _breakCircularDeps(obj) {
661
+ if (obj && typeof obj !== 'object') {
662
+ return obj;
663
+ }
664
+
665
+ if (seen.has(obj)) {
666
+ return '[Circular]';
667
+ }
668
+
669
+ seen.add(obj);
670
+ for (const k in obj) {
671
+ if (Object.prototype.hasOwnProperty.call(obj, k)) {
672
+ obj[k] = _breakCircularDeps(obj[k], k);
673
+ }
674
+ }
675
+
676
+ // deleting means only a seen object that is its own child will be detected
677
+ seen.delete(obj);
678
+ return obj;
679
+ }
680
+
681
+ return _breakCircularDeps(inputObj);
682
+ };
package/mocha.js CHANGED
@@ -1,4 +1,4 @@
1
- // mocha@10.5.0 in javascript ES2018
1
+ // mocha@10.5.2 in javascript ES2018
2
2
  (function (global, factory) {
3
3
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
4
4
  typeof define === 'function' && define.amd ? define(factory) :
@@ -11460,6 +11460,39 @@
11460
11460
  */
11461
11461
  exports.getMochaID = obj =>
11462
11462
  obj && typeof obj === 'object' ? obj[MOCHA_ID_PROP_NAME] : undefined;
11463
+
11464
+ /**
11465
+ * Replaces any detected circular dependency with the string '[Circular]'
11466
+ * Mutates original object
11467
+ * @param inputObj {*}
11468
+ * @returns {*}
11469
+ */
11470
+ exports.breakCircularDeps = inputObj => {
11471
+ const seen = new Set();
11472
+
11473
+ function _breakCircularDeps(obj) {
11474
+ if (obj && typeof obj !== 'object') {
11475
+ return obj;
11476
+ }
11477
+
11478
+ if (seen.has(obj)) {
11479
+ return '[Circular]';
11480
+ }
11481
+
11482
+ seen.add(obj);
11483
+ for (const k in obj) {
11484
+ if (Object.prototype.hasOwnProperty.call(obj, k)) {
11485
+ obj[k] = _breakCircularDeps(obj[k]);
11486
+ }
11487
+ }
11488
+
11489
+ // deleting means only a seen object that is its own child will be detected
11490
+ seen.delete(obj);
11491
+ return obj;
11492
+ }
11493
+
11494
+ return _breakCircularDeps(inputObj);
11495
+ };
11463
11496
  }(utils$3));
11464
11497
 
11465
11498
  var _nodeResolve_empty = {};
@@ -18986,7 +19019,7 @@
18986
19019
  };
18987
19020
 
18988
19021
  var name = "mocha";
18989
- var version = "10.5.0";
19022
+ var version = "10.5.2";
18990
19023
  var homepage = "https://mochajs.org/";
18991
19024
  var notifyLogo = "https://ibin.co/4QuRuGjXvl36.png";
18992
19025
  var require$$17 = {