mocha 8.1.1 → 8.2.1

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/lib/mocha.js CHANGED
@@ -12,21 +12,24 @@ var builtinReporters = require('./reporters');
12
12
  var growl = require('./nodejs/growl');
13
13
  var utils = require('./utils');
14
14
  var mocharc = require('./mocharc.json');
15
- var errors = require('./errors');
16
15
  var Suite = require('./suite');
17
16
  var esmUtils = utils.supportsEsModules(true)
18
17
  ? require('./esm-utils')
19
18
  : undefined;
20
19
  var createStatsCollector = require('./stats-collector');
21
- var createInvalidReporterError = errors.createInvalidReporterError;
22
- var createInvalidInterfaceError = errors.createInvalidInterfaceError;
23
- var createMochaInstanceAlreadyDisposedError =
24
- errors.createMochaInstanceAlreadyDisposedError;
25
- var createMochaInstanceAlreadyRunningError =
26
- errors.createMochaInstanceAlreadyRunningError;
27
- var EVENT_FILE_PRE_REQUIRE = Suite.constants.EVENT_FILE_PRE_REQUIRE;
28
- var EVENT_FILE_POST_REQUIRE = Suite.constants.EVENT_FILE_POST_REQUIRE;
29
- var EVENT_FILE_REQUIRE = Suite.constants.EVENT_FILE_REQUIRE;
20
+ const {
21
+ warn,
22
+ createInvalidReporterError,
23
+ createInvalidInterfaceError,
24
+ createMochaInstanceAlreadyDisposedError,
25
+ createMochaInstanceAlreadyRunningError,
26
+ createUnsupportedError
27
+ } = require('./errors');
28
+ const {
29
+ EVENT_FILE_PRE_REQUIRE,
30
+ EVENT_FILE_POST_REQUIRE,
31
+ EVENT_FILE_REQUIRE
32
+ } = Suite.constants;
30
33
  var sQuote = utils.sQuote;
31
34
  var debug = require('debug')('mocha:mocha');
32
35
 
@@ -128,8 +131,8 @@ exports.Test = require('./test');
128
131
  * suite with
129
132
  * @param {boolean} [options.isWorker] - Should be `true` if `Mocha` process is running in a worker process.
130
133
  */
131
- function Mocha(options) {
132
- options = utils.assign({}, mocharc, options || {});
134
+ function Mocha(options = {}) {
135
+ options = {...mocharc, ...options};
133
136
  this.files = [];
134
137
  this.options = options;
135
138
  // root suite
@@ -205,6 +208,11 @@ function Mocha(options) {
205
208
  */
206
209
  this.isWorker = Boolean(options.isWorker);
207
210
 
211
+ this.globalSetup(options.globalSetup)
212
+ .globalTeardown(options.globalTeardown)
213
+ .enableGlobalSetup(options.enableGlobalSetup)
214
+ .enableGlobalTeardown(options.enableGlobalTeardown);
215
+
208
216
  if (
209
217
  options.parallel &&
210
218
  (typeof options.jobs === 'undefined' || options.jobs > 1)
@@ -277,25 +285,21 @@ Mocha.prototype.reporter = function(reporterName, reporterOptions) {
277
285
  try {
278
286
  reporter = require(reporterName);
279
287
  } catch (err) {
280
- if (
281
- err.code === 'MODULE_NOT_FOUND' ||
282
- err.message.indexOf('Cannot find module') >= 0
283
- ) {
288
+ if (err.code === 'MODULE_NOT_FOUND') {
284
289
  // Try to load reporters from a path (absolute or relative)
285
290
  try {
286
291
  reporter = require(path.resolve(utils.cwd(), reporterName));
287
292
  } catch (_err) {
288
- _err.code === 'MODULE_NOT_FOUND' ||
289
- _err.message.indexOf('Cannot find module') >= 0
290
- ? utils.warn(sQuote(reporterName) + ' reporter not found')
291
- : utils.warn(
293
+ _err.code === 'MODULE_NOT_FOUND'
294
+ ? warn(sQuote(reporterName) + ' reporter not found')
295
+ : warn(
292
296
  sQuote(reporterName) +
293
297
  ' reporter blew up with error:\n' +
294
298
  err.stack
295
299
  );
296
300
  }
297
301
  } else {
298
- utils.warn(
302
+ warn(
299
303
  sQuote(reporterName) + ' reporter blew up with error:\n' + err.stack
300
304
  );
301
305
  }
@@ -445,7 +449,12 @@ Mocha.prototype.loadFilesAsync = function() {
445
449
  * @param {string} file - Pathname of file to be unloaded.
446
450
  */
447
451
  Mocha.unloadFile = function(file) {
448
- delete require.cache[require.resolve(file)];
452
+ if (utils.isBrowser()) {
453
+ throw createUnsupportedError(
454
+ 'unloadFile() is only suported in a Node.js environment'
455
+ );
456
+ }
457
+ return require('./nodejs/file-unloader').unloadFile(file);
449
458
  };
450
459
 
451
460
  /**
@@ -807,7 +816,7 @@ Mocha.prototype.slow = function(msecs) {
807
816
  *
808
817
  * @public
809
818
  * @see [CLI option](../#-async-only-a)
810
- * @param {boolean} [asyncOnly=true] - Wether to force `done` callback or promise.
819
+ * @param {boolean} [asyncOnly=true] - Whether to force `done` callback or promise.
811
820
  * @return {Mocha} this
812
821
  * @chainable
813
822
  */
@@ -847,7 +856,7 @@ Mocha.prototype.allowUncaught = function(allowUncaught) {
847
856
  * Delays root suite execution.
848
857
  *
849
858
  * @description
850
- * Used to perform asynch operations before any suites are run.
859
+ * Used to perform async operations before any suites are run.
851
860
  *
852
861
  * @public
853
862
  * @see [delayed root suite](../#delayed-root-suite)
@@ -960,11 +969,10 @@ Mocha.prototype.run = function(fn) {
960
969
  if (this.files.length && !this._lazyLoadFiles) {
961
970
  this.loadFiles();
962
971
  }
963
- var self = this;
964
972
  var suite = this.suite;
965
973
  var options = this.options;
966
974
  options.files = this.files;
967
- var runner = new this._runnerClass(suite, {
975
+ const runner = new this._runnerClass(suite, {
968
976
  delay: options.delay,
969
977
  cleanReferencesAfterRun: this._cleanReferencesAfterRun
970
978
  });
@@ -991,22 +999,42 @@ Mocha.prototype.run = function(fn) {
991
999
  exports.reporters.Base.inlineDiffs = options.inlineDiffs;
992
1000
  exports.reporters.Base.hideDiff = !options.diff;
993
1001
 
994
- function done(failures) {
995
- self._previousRunner = runner;
996
- if (self._cleanReferencesAfterRun) {
997
- self._state = mochaStates.REFERENCES_CLEANED;
998
- } else {
999
- self._state = mochaStates.INIT;
1000
- }
1002
+ const done = failures => {
1003
+ this._previousRunner = runner;
1004
+ this._state = this._cleanReferencesAfterRun
1005
+ ? mochaStates.REFERENCES_CLEANED
1006
+ : mochaStates.INIT;
1001
1007
  fn = fn || utils.noop;
1002
- if (reporter.done) {
1008
+ if (typeof reporter.done === 'function') {
1003
1009
  reporter.done(failures, fn);
1004
1010
  } else {
1005
1011
  fn(failures);
1006
1012
  }
1007
- }
1013
+ };
1014
+
1015
+ const runAsync = async runner => {
1016
+ const context =
1017
+ this.options.enableGlobalSetup && this.hasGlobalSetupFixtures()
1018
+ ? await this.runGlobalSetup(runner)
1019
+ : {};
1020
+ const failureCount = await runner.runAsync({
1021
+ files: this.files,
1022
+ options
1023
+ });
1024
+ if (this.options.enableGlobalTeardown && this.hasGlobalTeardownFixtures()) {
1025
+ await this.runGlobalTeardown(runner, {context});
1026
+ }
1027
+ return failureCount;
1028
+ };
1029
+
1030
+ // no "catch" here is intentional. errors coming out of
1031
+ // Runner#run are considered uncaught/unhandled and caught
1032
+ // by the `process` event listeners.
1033
+ // also: returning anything other than `runner` would be a breaking
1034
+ // change
1035
+ runAsync(runner).then(done);
1008
1036
 
1009
- return runner.run(done, {files: this.files, options: options});
1037
+ return runner;
1010
1038
  };
1011
1039
 
1012
1040
  /**
@@ -1014,26 +1042,28 @@ Mocha.prototype.run = function(fn) {
1014
1042
  * @param {MochaRootHookObject} [hooks] - Hooks to assign to root suite
1015
1043
  * @chainable
1016
1044
  */
1017
- Mocha.prototype.rootHooks = function rootHooks(hooks) {
1018
- if (utils.type(hooks) === 'object') {
1019
- var beforeAll = [].concat(hooks.beforeAll || []);
1020
- var beforeEach = [].concat(hooks.beforeEach || []);
1021
- var afterAll = [].concat(hooks.afterAll || []);
1022
- var afterEach = [].concat(hooks.afterEach || []);
1023
- var rootSuite = this.suite;
1024
- beforeAll.forEach(function(hook) {
1025
- rootSuite.beforeAll(hook);
1026
- });
1027
- beforeEach.forEach(function(hook) {
1028
- rootSuite.beforeEach(hook);
1029
- });
1030
- afterAll.forEach(function(hook) {
1031
- rootSuite.afterAll(hook);
1032
- });
1033
- afterEach.forEach(function(hook) {
1034
- rootSuite.afterEach(hook);
1035
- });
1036
- }
1045
+ Mocha.prototype.rootHooks = function rootHooks({
1046
+ beforeAll = [],
1047
+ beforeEach = [],
1048
+ afterAll = [],
1049
+ afterEach = []
1050
+ } = {}) {
1051
+ beforeAll = utils.castArray(beforeAll);
1052
+ beforeEach = utils.castArray(beforeEach);
1053
+ afterAll = utils.castArray(afterAll);
1054
+ afterEach = utils.castArray(afterEach);
1055
+ beforeAll.forEach(hook => {
1056
+ this.suite.beforeAll(hook);
1057
+ });
1058
+ beforeEach.forEach(hook => {
1059
+ this.suite.beforeEach(hook);
1060
+ });
1061
+ afterAll.forEach(hook => {
1062
+ this.suite.afterAll(hook);
1063
+ });
1064
+ afterEach.forEach(hook => {
1065
+ this.suite.afterEach(hook);
1066
+ });
1037
1067
  return this;
1038
1068
  };
1039
1069
 
@@ -1042,20 +1072,21 @@ Mocha.prototype.rootHooks = function rootHooks(hooks) {
1042
1072
  *
1043
1073
  * Must be run before calling {@link Mocha#run}. Changes the `Runner` class to
1044
1074
  * use; also enables lazy file loading if not already done so.
1075
+ *
1076
+ * Warning: when passed `false` and lazy loading has been enabled _via any means_ (including calling `parallelMode(true)`), this method will _not_ disable lazy loading. Lazy loading is a prerequisite for parallel
1077
+ * mode, but parallel mode is _not_ a prerequisite for lazy loading!
1045
1078
  * @param {boolean} [enable] - If `true`, enable; otherwise disable.
1046
1079
  * @throws If run in browser
1047
- * @throws If Mocha not in "INIT" state
1080
+ * @throws If Mocha not in `INIT` state
1048
1081
  * @returns {Mocha}
1049
1082
  * @chainable
1050
1083
  * @public
1051
1084
  */
1052
- Mocha.prototype.parallelMode = function parallelMode(enable) {
1085
+ Mocha.prototype.parallelMode = function parallelMode(enable = true) {
1053
1086
  if (utils.isBrowser()) {
1054
- throw errors.createUnsupportedError(
1055
- 'parallel mode is only supported in Node.js'
1056
- );
1087
+ throw createUnsupportedError('parallel mode is only supported in Node.js');
1057
1088
  }
1058
- var parallel = enable === true;
1089
+ const parallel = Boolean(enable);
1059
1090
  if (
1060
1091
  parallel === this.options.parallel &&
1061
1092
  this._lazyLoadFiles &&
@@ -1064,7 +1095,7 @@ Mocha.prototype.parallelMode = function parallelMode(enable) {
1064
1095
  return this;
1065
1096
  }
1066
1097
  if (this._state !== mochaStates.INIT) {
1067
- throw errors.createUnsupportedError(
1098
+ throw createUnsupportedError(
1068
1099
  'cannot change parallel mode after having called run()'
1069
1100
  );
1070
1101
  }
@@ -1097,8 +1128,144 @@ Mocha.prototype.lazyLoadFiles = function lazyLoadFiles(enable) {
1097
1128
  };
1098
1129
 
1099
1130
  /**
1100
- * An alternative way to define root hooks that works with parallel runs.
1131
+ * Configures one or more global setup fixtures.
1132
+ *
1133
+ * If given no parameters, _unsets_ any previously-set fixtures.
1134
+ * @chainable
1135
+ * @public
1136
+ * @param {MochaGlobalFixture|MochaGlobalFixture[]} [setupFns] - Global setup fixture(s)
1137
+ * @returns {Mocha}
1138
+ */
1139
+ Mocha.prototype.globalSetup = function globalSetup(setupFns = []) {
1140
+ setupFns = utils.castArray(setupFns);
1141
+ this.options.globalSetup = setupFns;
1142
+ debug('configured %d global setup functions', setupFns.length);
1143
+ return this;
1144
+ };
1145
+
1146
+ /**
1147
+ * Configures one or more global teardown fixtures.
1148
+ *
1149
+ * If given no parameters, _unsets_ any previously-set fixtures.
1150
+ * @chainable
1151
+ * @public
1152
+ * @param {MochaGlobalFixture|MochaGlobalFixture[]} [teardownFns] - Global teardown fixture(s)
1153
+ * @returns {Mocha}
1154
+ */
1155
+ Mocha.prototype.globalTeardown = function globalTeardown(teardownFns = []) {
1156
+ teardownFns = utils.castArray(teardownFns);
1157
+ this.options.globalTeardown = teardownFns;
1158
+ debug('configured %d global teardown functions', teardownFns.length);
1159
+ return this;
1160
+ };
1161
+
1162
+ /**
1163
+ * Run any global setup fixtures sequentially, if any.
1164
+ *
1165
+ * This is _automatically called_ by {@link Mocha#run} _unless_ the `runGlobalSetup` option is `false`; see {@link Mocha#enableGlobalSetup}.
1166
+ *
1167
+ * The context object this function resolves with should be consumed by {@link Mocha#runGlobalTeardown}.
1168
+ * @param {object} [context] - Context object if already have one
1169
+ * @public
1170
+ * @returns {Promise<object>} Context object
1171
+ */
1172
+ Mocha.prototype.runGlobalSetup = async function runGlobalSetup(context = {}) {
1173
+ const {globalSetup} = this.options;
1174
+ if (globalSetup && globalSetup.length) {
1175
+ debug('run(): global setup starting');
1176
+ await this._runGlobalFixtures(globalSetup, context);
1177
+ debug('run(): global setup complete');
1178
+ }
1179
+ return context;
1180
+ };
1181
+
1182
+ /**
1183
+ * Run any global teardown fixtures sequentially, if any.
1184
+ *
1185
+ * This is _automatically called_ by {@link Mocha#run} _unless_ the `runGlobalTeardown` option is `false`; see {@link Mocha#enableGlobalTeardown}.
1186
+ *
1187
+ * Should be called with context object returned by {@link Mocha#runGlobalSetup}, if applicable.
1188
+ * @param {object} [context] - Context object if already have one
1189
+ * @public
1190
+ * @returns {Promise<object>} Context object
1191
+ */
1192
+ Mocha.prototype.runGlobalTeardown = async function runGlobalTeardown(
1193
+ context = {}
1194
+ ) {
1195
+ const {globalTeardown} = this.options;
1196
+ if (globalTeardown && globalTeardown.length) {
1197
+ debug('run(): global teardown starting');
1198
+ await this._runGlobalFixtures(globalTeardown, context);
1199
+ }
1200
+ debug('run(): global teardown complete');
1201
+ return context;
1202
+ };
1203
+
1204
+ /**
1205
+ * Run global fixtures sequentially with context `context`
1101
1206
  * @private
1207
+ * @param {MochaGlobalFixture[]} [fixtureFns] - Fixtures to run
1208
+ * @param {object} [context] - context object
1209
+ * @returns {Promise<object>} context object
1210
+ */
1211
+ Mocha.prototype._runGlobalFixtures = async function _runGlobalFixtures(
1212
+ fixtureFns = [],
1213
+ context = {}
1214
+ ) {
1215
+ for await (const fixtureFn of fixtureFns) {
1216
+ await fixtureFn.call(context);
1217
+ }
1218
+ return context;
1219
+ };
1220
+
1221
+ /**
1222
+ * Toggle execution of any global setup fixture(s)
1223
+ *
1224
+ * @chainable
1225
+ * @public
1226
+ * @param {boolean } [enabled=true] - If `false`, do not run global setup fixture
1227
+ * @returns {Mocha}
1228
+ */
1229
+ Mocha.prototype.enableGlobalSetup = function enableGlobalSetup(enabled = true) {
1230
+ this.options.enableGlobalSetup = Boolean(enabled);
1231
+ return this;
1232
+ };
1233
+
1234
+ /**
1235
+ * Toggle execution of any global teardown fixture(s)
1236
+ *
1237
+ * @chainable
1238
+ * @public
1239
+ * @param {boolean } [enabled=true] - If `false`, do not run global teardown fixture
1240
+ * @returns {Mocha}
1241
+ */
1242
+ Mocha.prototype.enableGlobalTeardown = function enableGlobalTeardown(
1243
+ enabled = true
1244
+ ) {
1245
+ this.options.enableGlobalTeardown = Boolean(enabled);
1246
+ return this;
1247
+ };
1248
+
1249
+ /**
1250
+ * Returns `true` if one or more global setup fixtures have been supplied.
1251
+ * @public
1252
+ * @returns {boolean}
1253
+ */
1254
+ Mocha.prototype.hasGlobalSetupFixtures = function hasGlobalSetupFixtures() {
1255
+ return Boolean(this.options.globalSetup.length);
1256
+ };
1257
+
1258
+ /**
1259
+ * Returns `true` if one or more global teardown fixtures have been supplied.
1260
+ * @public
1261
+ * @returns {boolean}
1262
+ */
1263
+ Mocha.prototype.hasGlobalTeardownFixtures = function hasGlobalTeardownFixtures() {
1264
+ return Boolean(this.options.globalTeardown.length);
1265
+ };
1266
+
1267
+ /**
1268
+ * An alternative way to define root hooks that works with parallel runs.
1102
1269
  * @typedef {Object} MochaRootHookObject
1103
1270
  * @property {Function|Function[]} [beforeAll] - "Before all" hook(s)
1104
1271
  * @property {Function|Function[]} [beforeEach] - "Before each" hook(s)
@@ -1108,7 +1275,40 @@ Mocha.prototype.lazyLoadFiles = function lazyLoadFiles(enable) {
1108
1275
 
1109
1276
  /**
1110
1277
  * An function that returns a {@link MochaRootHookObject}, either sync or async.
1111
- * @private
1112
- * @callback MochaRootHookFunction
1278
+ @callback MochaRootHookFunction
1113
1279
  * @returns {MochaRootHookObject|Promise<MochaRootHookObject>}
1114
1280
  */
1281
+
1282
+ /**
1283
+ * A function that's invoked _once_ which is either sync or async.
1284
+ * Can be a "teardown" or "setup". These will all share the same context.
1285
+ * @callback MochaGlobalFixture
1286
+ * @returns {void|Promise<void>}
1287
+ */
1288
+
1289
+ /**
1290
+ * An object making up all necessary parts of a plugin loader and aggregator
1291
+ * @typedef {Object} PluginDefinition
1292
+ * @property {string} exportName - Named export to use
1293
+ * @property {string} [optionName] - Option name for Mocha constructor (use `exportName` if omitted)
1294
+ * @property {PluginValidator} [validate] - Validator function
1295
+ * @property {PluginFinalizer} [finalize] - Finalizer/aggregator function
1296
+ */
1297
+
1298
+ /**
1299
+ * A (sync) function to assert a user-supplied plugin implementation is valid.
1300
+ *
1301
+ * Defined in a {@link PluginDefinition}.
1302
+
1303
+ * @callback PluginValidator
1304
+ * @param {*} value - Value to check
1305
+ * @this {PluginDefinition}
1306
+ * @returns {void}
1307
+ */
1308
+
1309
+ /**
1310
+ * A function to finalize plugins impls of a particular ilk
1311
+ * @callback PluginFinalizer
1312
+ * @param {Array<*>} impls - User-supplied implementations
1313
+ * @returns {Promise<*>|*}
1314
+ */
@@ -75,9 +75,7 @@ class BufferedWorkerPool {
75
75
  process.execArgv.join(' ')
76
76
  );
77
77
 
78
- this.options = Object.assign({}, WORKER_POOL_DEFAULT_OPTS, opts, {
79
- maxWorkers
80
- });
78
+ this.options = {...WORKER_POOL_DEFAULT_OPTS, opts, maxWorkers};
81
79
  this._pool = workerpool.pool(WORKER_PATH, this.options);
82
80
  }
83
81
 
@@ -0,0 +1,15 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * This module should not be in the browser bundle, so it's here.
5
+ * @private
6
+ * @module
7
+ */
8
+
9
+ /**
10
+ * Deletes a file from the `require` cache.
11
+ * @param {string} file - File
12
+ */
13
+ exports.unloadFile = file => {
14
+ delete require.cache[require.resolve(file)];
15
+ };