mocha 6.2.3 → 7.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.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  (The MIT License)
2
2
 
3
- Copyright (c) 2011-2018 JS Foundation and contributors, https://js.foundation
3
+ Copyright (c) 2011-2020 OpenJS Foundation and contributors, https://openjsf.org
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining
6
6
  a copy of this software and associated documentation files (the
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
  <img src="https://cldup.com/xFVFxOioAU.svg" alt="Mocha test framework"/>
3
3
  </p>
4
4
 
5
- <p align="center">:coffee: Simple, flexible, fun JavaScript test framework for Node.js & The Browser :coffee:</p>
5
+ <p align="center">☕️ Simple, flexible, fun JavaScript test framework for Node.js & The Browser ☕️</p>
6
6
 
7
7
  <p align="center"><a href="http://travis-ci.org/mochajs/mocha"><img src="https://api.travis-ci.org/mochajs/mocha.svg?branch=master" alt="Build Status"></a> <a href="https://coveralls.io/github/mochajs/mocha"><img src="https://coveralls.io/repos/github/mochajs/mocha/badge.svg" alt="Coverage Status"></a> <a href="https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmochajs%2Fmocha?ref=badge_shield"><img src="https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmochajs%2Fmocha.svg?type=shield" alt="FOSSA Status"></a> <a href="https://gitter.im/mochajs/mocha?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge"><img src="https://badges.gitter.im/Join%20Chat.svg" alt="Gitter"></a> <a href="https://github.com/mochajs/mocha#backers"><img src="https://opencollective.com/mochajs/backers/badge.svg" alt="OpenCollective"></a> <a href="https://github.com/mochajs/mocha#sponsors"><img src="https://opencollective.com/mochajs/sponsors/badge.svg" alt="OpenCollective"></a>
8
8
  </p>
@@ -56,7 +56,7 @@
56
56
 
57
57
  ## Sponsors
58
58
 
59
- Does your company use Mocha? Ask your manager or marketing team if your company would be interested in supporting our project. Support will allow the maintainers to dedicate more time for maintenance and new features for everyone. Also, your company's logo will show [on GitHub](https://github.com/mochajs/mocha#readme) and on [our site](https://mochajs.org) - who doesn't want a little extra exposure? [Here's the info](https://opencollective.com/mochajs#sponsor).
59
+ Does your company use Mocha? Ask your manager or marketing team if your company would be interested in supporting our project. Support will allow the maintainers to dedicate more time for maintenance and new features for everyone. Also, your company's logo will show [on GitHub](https://github.com/mochajs/mocha#readme) and on [our site](https://mochajs.org) - who doesn't want a little extra exposure? [Here's the info](https://opencollective.com/mochajs#sponsor).
60
60
 
61
61
  [![MochaJS Sponsor](https://opencollective.com/mochajs/sponsor/0/avatar)](https://opencollective.com/mochajs/sponsor/0/website)
62
62
  [![MochaJS Sponsor](https://opencollective.com/mochajs/sponsor/1/avatar)](https://opencollective.com/mochajs/sponsor/1/website)
@@ -83,12 +83,12 @@ Does your company use Mocha? Ask your manager or marketing team if your company
83
83
 
84
84
  You might want to know that:
85
85
 
86
- - Mocha is the *most-depended-upon* module on npm (source: [libraries.io](https://libraries.io/search?order=desc&platforms=NPM&sort=dependents_count)), and
87
- - Mocha is an *independent* open-source project, maintained exclusively by volunteers.
86
+ - Mocha is the _most-depended-upon_ module on npm (source: [libraries.io](https://libraries.io/search?order=desc&platforms=NPM&sort=dependents_count)), and
87
+ - Mocha is an _independent_ open-source project, maintained exclusively by volunteers.
88
88
 
89
89
  You might want to help:
90
90
 
91
- - New to contributing to Mocha? Check out this list of [good first issues](https://github.com/mochajs/mocha/issues?q=is%3Aissue+is%3Aopen+label%3Agood-first-issue)
91
+ - New to contributing to Mocha? Check out this list of [good first issues](https://github.com/mochajs/mocha/issues?q=is%3Aissue+is%3Aopen+label%3Agood-first-issue)
92
92
  - Mocha could use a hand with [these issues](https://github.com/mochajs/mocha/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22)
93
93
  - The [maintainer's handbook](https://github.com/mochajs/mocha/blob/master/MAINTAINERS.md) explains how things get done
94
94
 
@@ -100,6 +100,6 @@ Finally, come [chat with the maintainers](https://gitter.im/mochajs/contributors
100
100
 
101
101
  ## License
102
102
 
103
- [MIT](LICENSE)
103
+ Copyright 2011-2020 OpenJS Foundation and contributors. Licensed [MIT](https://github.com/mochajs/mocha/blob/master/LICENSE).
104
104
 
105
105
  [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmochajs%2Fmocha.svg?type=large)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmochajs%2Fmocha?ref=badge_large)
package/bin/mocha CHANGED
@@ -10,7 +10,7 @@
10
10
  * @private
11
11
  */
12
12
 
13
- const {deprecate, warn} = require('../lib/utils');
13
+ const {deprecate} = require('../lib/utils');
14
14
  const {loadOptions} = require('../lib/cli/options');
15
15
  const {
16
16
  unparseNodeFlags,
@@ -20,7 +20,6 @@ const {
20
20
  const unparse = require('yargs-unparser');
21
21
  const debug = require('debug')('mocha:cli:mocha');
22
22
  const {aliases} = require('../lib/cli/run-option-metadata');
23
- const nodeEnv = require('node-environment-flags');
24
23
 
25
24
  const mochaArgs = {};
26
25
  const nodeArgs = {};
@@ -55,57 +54,29 @@ const trimV8Option = value =>
55
54
  Object.keys(opts).forEach(opt => {
56
55
  if (isNodeFlag(opt)) {
57
56
  nodeArgs[trimV8Option(opt)] = opts[opt];
58
- disableTimeouts(opt);
59
57
  } else {
60
58
  mochaArgs[opt] = opts[opt];
61
59
  }
62
60
  });
63
61
 
62
+ // disable 'timeout' for debugFlags
63
+ Object.keys(nodeArgs).forEach(opt => disableTimeouts(opt));
64
+
64
65
  // Native debugger handling
65
66
  // see https://nodejs.org/api/debugger.html#debugger_debugger
66
- // look for 'debug' or 'inspect' that would launch this debugger,
67
+ // look for 'inspect' or 'debug' that would launch this debugger,
67
68
  // remove it from Mocha's opts and prepend it to Node's opts.
68
- // also coerce depending on Node.js version.
69
69
  // A deprecation warning will be printed by node, if applicable.
70
70
  // (mochaArgs._ are "positional" arguments, not prefixed with - or --)
71
- if (/^(debug|inspect)$/.test(mochaArgs._[0])) {
72
- const command = mochaArgs._.shift();
73
- disableTimeouts(command);
74
- // don't conflict with inspector
75
- ['debug', 'inspect', 'debug-brk', 'inspect-brk']
76
- .filter(opt => opt in nodeArgs || opt in mochaArgs)
77
- .forEach(opt => {
78
- warn(`command "${command}" provided; --${opt} ignored`);
79
- delete nodeArgs[opt];
80
- delete mochaArgs[opt];
81
- });
82
- nodeArgs._ = [
83
- parseInt(
84
- process.version
85
- .slice(1)
86
- .split('.')
87
- .shift(),
88
- 10
89
- ) >= 8
90
- ? 'inspect'
91
- : 'debug'
92
- ];
71
+ if (mochaArgs._) {
72
+ const i = mochaArgs._.findIndex(val => val === 'inspect' || val === 'debug');
73
+ if (i > -1) {
74
+ const [command] = mochaArgs._.splice(i, 1);
75
+ disableTimeouts('inspect');
76
+ nodeArgs._ = [command];
77
+ }
93
78
  }
94
79
 
95
- // allow --debug to invoke --inspect on Node.js v8 or newer.
96
- ['debug', 'debug-brk']
97
- .filter(opt => opt in nodeArgs && !nodeEnv.has(opt))
98
- .forEach(opt => {
99
- const newOpt = opt === 'debug' ? 'inspect' : 'inspect-brk';
100
- warn(
101
- `"--${opt}" is not available in Node.js ${process.version}; use "--${newOpt}" instead.`
102
- );
103
- nodeArgs[newOpt] = nodeArgs[opt];
104
- mochaArgs.timeout = false;
105
- debug(`--${opt} -> ${newOpt}`);
106
- delete nodeArgs[opt];
107
- });
108
-
109
80
  // historical
110
81
  if (nodeArgs.gc) {
111
82
  deprecate(
package/browser-entry.js CHANGED
@@ -60,7 +60,7 @@ process.on = function(e, fn) {
60
60
  if (e === 'uncaughtException') {
61
61
  global.onerror = function(err, url, line) {
62
62
  fn(new Error(err + ' (' + url + ':' + line + ')'));
63
- return !mocha.allowUncaught;
63
+ return !mocha.options.allowUncaught;
64
64
  };
65
65
  uncaughtExceptionHandlers.push(fn);
66
66
  }
@@ -129,7 +129,7 @@ mocha.setup = function(opts) {
129
129
  opts = {ui: opts};
130
130
  }
131
131
  for (var opt in opts) {
132
- if (opts.hasOwnProperty(opt)) {
132
+ if (Object.prototype.hasOwnProperty.call(opts, opt)) {
133
133
  this[opt](opts[opt]);
134
134
  }
135
135
  }
package/lib/cli/config.js CHANGED
@@ -21,6 +21,7 @@ const findUp = require('find-up');
21
21
  * @private
22
22
  */
23
23
  exports.CONFIG_FILES = [
24
+ '.mocharc.cjs',
24
25
  '.mocharc.js',
25
26
  '.mocharc.yaml',
26
27
  '.mocharc.yml',
@@ -75,7 +76,7 @@ exports.loadConfig = filepath => {
75
76
  try {
76
77
  if (ext === '.yml' || ext === '.yaml') {
77
78
  config = parsers.yaml(filepath);
78
- } else if (ext === '.js') {
79
+ } else if (ext === '.js' || ext === '.cjs') {
79
80
  config = parsers.js(filepath);
80
81
  } else {
81
82
  config = parsers.json(filepath);
@@ -14,7 +14,7 @@ const unparse = require('yargs-unparser');
14
14
  * @see {@link impliesNoTimeouts}
15
15
  * @private
16
16
  */
17
- const debugFlags = new Set(['debug', 'debug-brk', 'inspect', 'inspect-brk']);
17
+ const debugFlags = new Set(['inspect', 'inspect-brk']);
18
18
 
19
19
  /**
20
20
  * Mocha has historical support for various `node` and V8 flags which might not
@@ -48,14 +48,14 @@ exports.ONE_AND_DONES = {
48
48
  * Dump list of built-in interfaces
49
49
  * @private
50
50
  */
51
- interfaces: () => {
51
+ 'list-interfaces': () => {
52
52
  showKeys(Mocha.interfaces);
53
53
  },
54
54
  /**
55
55
  * Dump list of built-in reporters
56
56
  * @private
57
57
  */
58
- reporters: () => {
58
+ 'list-reporters': () => {
59
59
  showKeys(Mocha.reporters);
60
60
  }
61
61
  };
@@ -163,40 +163,6 @@ const parseMochaOpts = content =>
163
163
  .filter(Boolean)
164
164
  .map(value => value.replace(/%20/g, ' '));
165
165
 
166
- /**
167
- * Prepends options from run-control file to the command line arguments.
168
- *
169
- * @deprecated Deprecated in v6.0.0; This function is no longer used internally and will be removed in a future version.
170
- * @public
171
- * @alias module:lib/cli/options
172
- * @see {@link https://mochajs.org/#mochaopts|mocha.opts}
173
- */
174
- module.exports = function getOptions() {
175
- deprecate(
176
- 'getOptions() is DEPRECATED and will be removed from a future version of Mocha. Use loadOptions() instead'
177
- );
178
- if (process.argv.length === 3 && ONE_AND_DONE_ARGS.has(process.argv[2])) {
179
- return;
180
- }
181
-
182
- const optsPath =
183
- process.argv.indexOf('--opts') === -1
184
- ? mocharc.opts
185
- : process.argv[process.argv.indexOf('--opts') + 1];
186
-
187
- try {
188
- const options = parseMochaOpts(fs.readFileSync(optsPath, 'utf8'));
189
-
190
- process.argv = process.argv
191
- .slice(0, 2)
192
- .concat(options.concat(process.argv.slice(2)));
193
- } catch (ignore) {
194
- // NOTE: should console.error() and throw the error
195
- }
196
-
197
- process.env.LOADED_MOCHA_OPTS = true;
198
- };
199
-
200
166
  /**
201
167
  * Given filepath in `args.opts`, attempt to load and parse a `mocha.opts` file.
202
168
  * @param {Object} [args] - Arguments object
@@ -231,6 +197,9 @@ const loadMochaOpts = (args = {}) => {
231
197
  // if there's an exception to catch here, I'm not sure what it is.
232
198
  // by attaching the `no-opts` arg, we avoid re-parsing of `mocha.opts`.
233
199
  if (mochaOpts) {
200
+ deprecate(
201
+ 'Configuration via mocha.opts is DEPRECATED and will be removed from a future version of Mocha. Use RC files or package.json instead.'
202
+ );
234
203
  result = parse(parseMochaOpts(mochaOpts));
235
204
  debug(`${filepath} parsed succesfully`);
236
205
  }
@@ -296,7 +265,7 @@ module.exports.loadPkgRc = loadPkgRc;
296
265
  * Priority list:
297
266
  *
298
267
  * 1. Command-line args
299
- * 2. RC file (`.mocharc.js`, `.mocharc.ya?ml`, `mocharc.json`)
268
+ * 2. RC file (`.mocharc.c?js`, `.mocharc.ya?ml`, `mocharc.json`)
300
269
  * 3. `mocha` prop of `package.json`
301
270
  * 4. `mocha.opts`
302
271
  * 5. default configuration (`lib/mocharc.json`)
@@ -15,8 +15,6 @@ const collectFiles = require('./collect-files');
15
15
 
16
16
  const cwd = (exports.cwd = process.cwd());
17
17
 
18
- exports.watchRun = watchRun;
19
-
20
18
  /**
21
19
  * Exits Mocha when tests + code under test has finished execution (default)
22
20
  * @param {number} code - Exit code; typically # of failures
@@ -92,19 +90,21 @@ exports.handleRequires = (requires = []) => {
92
90
  };
93
91
 
94
92
  /**
95
- * Collect test files and run mocha instance.
93
+ * Collect and load test files, then run mocha instance.
96
94
  * @param {Mocha} mocha - Mocha instance
97
95
  * @param {Options} [opts] - Command line options
98
96
  * @param {boolean} [opts.exit] - Whether or not to force-exit after tests are complete
99
97
  * @param {Object} fileCollectParams - Parameters that control test
100
98
  * file collection. See `lib/cli/collect-files.js`.
101
- * @returns {Runner}
99
+ * @returns {Promise<Runner>}
102
100
  * @private
103
101
  */
104
- exports.singleRun = (mocha, {exit}, fileCollectParams) => {
102
+ const singleRun = async (mocha, {exit}, fileCollectParams) => {
105
103
  const files = collectFiles(fileCollectParams);
106
104
  debug('running tests with files', files);
107
105
  mocha.files = files;
106
+
107
+ await mocha.loadFilesAsync();
108
108
  return mocha.run(exit ? exitMocha : exitMochaLater);
109
109
  };
110
110
 
@@ -113,18 +113,20 @@ exports.singleRun = (mocha, {exit}, fileCollectParams) => {
113
113
  * @param {Mocha} mocha - Mocha instance
114
114
  * @param {Object} opts - Command line options
115
115
  * @private
116
+ * @returns {Promise}
116
117
  */
117
- exports.runMocha = (mocha, options) => {
118
+ exports.runMocha = async (mocha, options) => {
118
119
  const {
119
120
  watch = false,
120
121
  extension = [],
121
- ui = 'bdd',
122
122
  exit = false,
123
123
  ignore = [],
124
124
  file = [],
125
125
  recursive = false,
126
126
  sort = false,
127
- spec = []
127
+ spec = [],
128
+ watchFiles,
129
+ watchIgnore
128
130
  } = options;
129
131
 
130
132
  const fileCollectParams = {
@@ -137,9 +139,9 @@ exports.runMocha = (mocha, options) => {
137
139
  };
138
140
 
139
141
  if (watch) {
140
- watchRun(mocha, {ui}, fileCollectParams);
142
+ watchRun(mocha, {watchFiles, watchIgnore}, fileCollectParams);
141
143
  } else {
142
- exports.singleRun(mocha, {exit}, fileCollectParams);
144
+ await singleRun(mocha, {exit}, fileCollectParams);
143
145
  }
144
146
  };
145
147
 
@@ -18,9 +18,11 @@ exports.types = {
18
18
  'file',
19
19
  'global',
20
20
  'ignore',
21
- 'require',
22
21
  'reporter-option',
23
- 'spec'
22
+ 'require',
23
+ 'spec',
24
+ 'watch-files',
25
+ 'watch-ignore'
24
26
  ],
25
27
  boolean: [
26
28
  'allow-uncaught',
@@ -36,11 +38,11 @@ exports.types = {
36
38
  'full-trace',
37
39
  'growl',
38
40
  'inline-diffs',
39
- 'interfaces',
40
41
  'invert',
42
+ 'list-interfaces',
43
+ 'list-reporters',
41
44
  'no-colors',
42
45
  'recursive',
43
- 'reporters',
44
46
  'sort',
45
47
  'watch'
46
48
  ],
@@ -68,7 +70,6 @@ exports.aliases = {
68
70
  'async-only': ['A'],
69
71
  bail: ['b'],
70
72
  color: ['c', 'colors'],
71
- extension: ['watch-extensions'],
72
73
  fgrep: ['f'],
73
74
  global: ['globals'],
74
75
  grep: ['g'],
package/lib/cli/run.js CHANGED
@@ -38,7 +38,7 @@ const GROUPS = {
38
38
  CONFIG: 'Configuration'
39
39
  };
40
40
 
41
- exports.command = ['$0 [spec..]', 'debug [spec..]'];
41
+ exports.command = ['$0 [spec..]', 'inspect'];
42
42
 
43
43
  exports.describe = 'Run tests with Mocha';
44
44
 
@@ -87,8 +87,7 @@ exports.builder = yargs =>
87
87
  },
88
88
  extension: {
89
89
  default: defaults.extension,
90
- defaultDescription: 'js',
91
- description: 'File extension(s) to load and/or watch',
90
+ description: 'File extension(s) to load',
92
91
  group: GROUPS.FILES,
93
92
  requiresArg: true,
94
93
  coerce: list
@@ -147,14 +146,18 @@ exports.builder = yargs =>
147
146
  'Display actual/expected differences inline within each string',
148
147
  group: GROUPS.OUTPUT
149
148
  },
150
- interfaces: {
151
- conflicts: Array.from(ONE_AND_DONE_ARGS),
152
- description: 'List built-in user interfaces & exit'
153
- },
154
149
  invert: {
155
150
  description: 'Inverts --grep and --fgrep matches',
156
151
  group: GROUPS.FILTERS
157
152
  },
153
+ 'list-interfaces': {
154
+ conflicts: Array.from(ONE_AND_DONE_ARGS),
155
+ description: 'List built-in user interfaces & exit'
156
+ },
157
+ 'list-reporters': {
158
+ conflicts: Array.from(ONE_AND_DONE_ARGS),
159
+ description: 'List built-in reporters & exit'
160
+ },
158
161
  'no-colors': {
159
162
  description: 'Force-disable color output',
160
163
  group: GROUPS.OUTPUT,
@@ -162,7 +165,7 @@ exports.builder = yargs =>
162
165
  },
163
166
  opts: {
164
167
  default: defaults.opts,
165
- description: 'Path to `mocha.opts`',
168
+ description: 'Path to `mocha.opts` (DEPRECATED)',
166
169
  group: GROUPS.CONFIG,
167
170
  normalize: true,
168
171
  requiresArg: true
@@ -183,10 +186,6 @@ exports.builder = yargs =>
183
186
  group: GROUPS.OUTPUT,
184
187
  requiresArg: true
185
188
  },
186
- reporters: {
187
- conflicts: Array.from(ONE_AND_DONE_ARGS),
188
- description: 'List built-in reporters & exit'
189
- },
190
189
  'reporter-option': {
191
190
  coerce: opts =>
192
191
  list(opts).reduce((acc, opt) => {
@@ -241,6 +240,19 @@ exports.builder = yargs =>
241
240
  watch: {
242
241
  description: 'Watch files in the current working directory for changes',
243
242
  group: GROUPS.FILES
243
+ },
244
+ 'watch-files': {
245
+ description: 'List of paths or globs to watch',
246
+ group: GROUPS.FILES,
247
+ requiresArg: true,
248
+ coerce: list
249
+ },
250
+ 'watch-ignore': {
251
+ description: 'List of paths or globs to exclude from watching',
252
+ group: GROUPS.FILES,
253
+ requiresArg: true,
254
+ coerce: list,
255
+ default: defaults['watch-ignore']
244
256
  }
245
257
  })
246
258
  .positional('spec', {
@@ -286,8 +298,14 @@ exports.builder = yargs =>
286
298
  .number(types.number)
287
299
  .alias(aliases);
288
300
 
289
- exports.handler = argv => {
301
+ exports.handler = async function(argv) {
290
302
  debug('post-yargs config', argv);
291
303
  const mocha = new Mocha(argv);
292
- runMocha(mocha, argv);
304
+
305
+ try {
306
+ await runMocha(mocha, argv);
307
+ } catch (err) {
308
+ console.error('\n' + (err.stack || `Error: ${err.message || err}`));
309
+ process.exit(1);
310
+ }
293
311
  };
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
- const utils = require('../utils');
3
+ const path = require('path');
4
+ const chokidar = require('chokidar');
4
5
  const Context = require('../context');
5
- const Mocha = require('../mocha');
6
6
  const collectFiles = require('./collect-files');
7
7
 
8
8
  /**
@@ -16,36 +16,88 @@ const collectFiles = require('./collect-files');
16
16
  * Run Mocha in "watch" mode
17
17
  * @param {Mocha} mocha - Mocha instance
18
18
  * @param {Object} opts - Options
19
- * @param {string} opts.ui - User interface
19
+ * @param {string[]} [opts.watchFiles] - List of paths and patterns to
20
+ * watch. If not provided all files with an extension included in
21
+ * `fileColletionParams.extension` are watched. See first argument of
22
+ * `chokidar.watch`.
23
+ * @param {string[]} opts.watchIgnore - List of paths and patterns to
24
+ * exclude from watching. See `ignored` option of `chokidar`.
20
25
  * @param {Object} fileCollectParams - Parameters that control test
21
26
  * file collection. See `lib/cli/collect-files.js`.
22
- * @param {string[]} fileCollectParams.extension - List of extensions to watch
27
+ * @param {string[]} fileCollectParams.extension - List of extensions
28
+ * to watch if `opts.watchFiles` is not given.
23
29
  * @private
24
30
  */
25
- module.exports = (mocha, {ui}, fileCollectParams) => {
26
- let runner;
27
- const files = collectFiles(fileCollectParams);
31
+ module.exports = (mocha, {watchFiles, watchIgnore}, fileCollectParams) => {
32
+ if (!watchFiles) {
33
+ watchFiles = fileCollectParams.extension.map(ext => `**/*.${ext}`);
34
+ }
35
+
36
+ const watcher = chokidar.watch(watchFiles, {
37
+ ignored: watchIgnore,
38
+ ignoreInitial: true
39
+ });
40
+
41
+ const rerunner = createRerunner(mocha, () => {
42
+ getWatchedFiles(watcher).forEach(file => {
43
+ delete require.cache[file];
44
+ });
45
+ mocha.files = collectFiles(fileCollectParams);
46
+ });
47
+
48
+ watcher.on('ready', () => {
49
+ rerunner.run();
50
+ });
51
+
52
+ watcher.on('all', () => {
53
+ rerunner.scheduleRun();
54
+ });
28
55
 
29
56
  console.log();
30
57
  hideCursor();
58
+ process.on('exit', () => {
59
+ showCursor();
60
+ });
31
61
  process.on('SIGINT', () => {
32
62
  showCursor();
33
63
  console.log('\n');
34
- // By UNIX/Posix convention this indicates that the process was
35
- // killed by SIGINT which has portable number 2.
36
64
  process.exit(128 + 2);
37
65
  });
38
66
 
39
- const watchFiles = utils.files(process.cwd(), fileCollectParams.extension);
40
- let runAgain = false;
67
+ // Keyboard shortcut for restarting when "rs\n" is typed (ala Nodemon)
68
+ process.stdin.resume();
69
+ process.stdin.setEncoding('utf8');
70
+ process.stdin.on('data', data => {
71
+ const str = data
72
+ .toString()
73
+ .trim()
74
+ .toLowerCase();
75
+ if (str === 'rs') rerunner.scheduleRun();
76
+ });
77
+ };
78
+
79
+ /**
80
+ * Create an object that allows you to rerun tests on the mocha
81
+ * instance. `beforeRun` is called everytime before `mocha.run()` is
82
+ * called.
83
+ *
84
+ * @param {Mocha} mocha - Mocha instance
85
+ * @param {function} beforeRun - Called just before `mocha.run()`
86
+ */
87
+ const createRerunner = (mocha, beforeRun) => {
88
+ // Set to a `Runner` when mocha is running. Set to `null` when mocha is not
89
+ // running.
90
+ let runner = null;
41
91
 
42
- const loadAndRun = () => {
92
+ let rerunScheduled = false;
93
+
94
+ const run = () => {
43
95
  try {
44
- mocha.files = files;
45
- runAgain = false;
96
+ beforeRun();
97
+ resetMocha(mocha);
46
98
  runner = mocha.run(() => {
47
99
  runner = null;
48
- if (runAgain) {
100
+ if (rerunScheduled) {
49
101
  rerun();
50
102
  }
51
103
  });
@@ -54,29 +106,62 @@ module.exports = (mocha, {ui}, fileCollectParams) => {
54
106
  }
55
107
  };
56
108
 
57
- const purge = () => {
58
- watchFiles.forEach(Mocha.unloadFile);
59
- };
60
-
61
- loadAndRun();
62
-
63
- const rerun = () => {
64
- purge();
65
- eraseLine();
66
- mocha.suite = mocha.suite.clone();
67
- mocha.suite.ctx = new Context();
68
- mocha.ui(ui);
69
- loadAndRun();
70
- };
109
+ const scheduleRun = () => {
110
+ if (rerunScheduled) {
111
+ return;
112
+ }
71
113
 
72
- utils.watch(watchFiles, () => {
73
- runAgain = true;
114
+ rerunScheduled = true;
74
115
  if (runner) {
75
116
  runner.abort();
76
117
  } else {
77
118
  rerun();
78
119
  }
120
+ };
121
+
122
+ const rerun = () => {
123
+ rerunScheduled = false;
124
+ eraseLine();
125
+ run();
126
+ };
127
+
128
+ return {
129
+ scheduleRun,
130
+ run
131
+ };
132
+ };
133
+
134
+ /**
135
+ * Return the list of absolute paths watched by a chokidar watcher.
136
+ *
137
+ * @param watcher - Instance of a chokidar watcher
138
+ * @return {string[]} - List of absolute paths
139
+ */
140
+ const getWatchedFiles = watcher => {
141
+ const watchedDirs = watcher.getWatched();
142
+ let watchedFiles = [];
143
+ Object.keys(watchedDirs).forEach(dir => {
144
+ watchedFiles = watchedFiles.concat(
145
+ watchedDirs[dir].map(file => path.join(dir, file))
146
+ );
79
147
  });
148
+ return watchedFiles;
149
+ };
150
+
151
+ /**
152
+ * Reset the internal state of the mocha instance so that tests can be rerun.
153
+ *
154
+ * @param {Mocha} mocha - Mocha instance
155
+ * @private
156
+ */
157
+ const resetMocha = mocha => {
158
+ mocha.unloadFiles();
159
+ mocha.suite = mocha.suite.clone();
160
+ mocha.suite.ctx = new Context();
161
+ // Registers a callback on `mocha.suite` that wires new context to the DSL
162
+ // (e.g. `describe`) that is exposed as globals when the test files are
163
+ // reloaded.
164
+ mocha.ui(mocha.options.ui);
80
165
  };
81
166
 
82
167
  /**