$
This commit is contained in:
931
node_modules/mocha/CHANGELOG.md
generated
vendored
Normal file
931
node_modules/mocha/CHANGELOG.md
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
22
node_modules/mocha/LICENSE
generated
vendored
Normal file
22
node_modules/mocha/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2011-2021 OpenJS Foundation and contributors, https://openjsf.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
70
node_modules/mocha/README.md
generated
vendored
Normal file
70
node_modules/mocha/README.md
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
<p align="center">
|
||||
<img src="https://cldup.com/xFVFxOioAU.svg" alt="Mocha test framework"/>
|
||||
</p>
|
||||
|
||||
<p align="center">☕️ Simple, flexible, fun JavaScript test framework for Node.js & The Browser ☕️</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/mochajs/mocha/actions?query=workflow%3ATests+branch%3Amaster"><img src="https://github.com/mochajs/mocha/workflows/Tests/badge.svg?branch=master" alt="GitHub Actions 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#sponsors"><img src="https://opencollective.com/mochajs/tiers/sponsors/badge.svg" alt="OpenCollective"></a>
|
||||
<a href="https://github.com/mochajs/mocha#backers"><img src="https://opencollective.com/mochajs/tiers/backers/badge.svg" alt="OpenCollective"></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://www.npmjs.com/package/mocha"><img src="https://img.shields.io/npm/v/mocha.svg" alt="NPM Version"></a>
|
||||
<a href="https://github.com/mochajs/mocha"><img src="https://img.shields.io/node/v/mocha.svg" alt="Node Version"></a>
|
||||
</p>
|
||||
|
||||
<p align="center"><br><img alt="Mocha Browser Support h/t SauceLabs" src="https://saucelabs.com/browser-matrix/mochajs.svg" width="354"></p>
|
||||
|
||||
## Links
|
||||
|
||||
- **[Documentation](https://mochajs.org/)**
|
||||
- **[Release Notes / History / Changes](https://github.com/mochajs/mocha/blob/master/CHANGELOG.md)**
|
||||
- [Code of Conduct](https://github.com/mochajs/mocha/blob/master/.github/CODE_OF_CONDUCT.md)
|
||||
- [Contributing](https://github.com/mochajs/mocha/blob/master/.github/CONTRIBUTING.md)
|
||||
- [Gitter Chatroom](https://gitter.im/mochajs/mocha) (ask questions here!)
|
||||
- [Issue Tracker](https://github.com/mochajs/mocha/issues)
|
||||
|
||||
## Backers
|
||||
|
||||
[Become a backer](https://opencollective.com/mochajs) and show your support to our open source project on [our site](https://mochajs.org/#backers).
|
||||
|
||||
<a href="https://opencollective.com/mochajs"><img src="https://opencollective.com/mochajs/tiers/backers.svg?limit=30&button=false&avatarHeight=46&width=750"></a>
|
||||
|
||||
## Sponsors
|
||||
|
||||
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#sponsors) - who doesn't want a little extra exposure? [Here's the info](https://opencollective.com/mochajs).
|
||||
|
||||
[](https://opencollective.com/mochajs/tiers/sponsors/0/website)
|
||||
[](https://opencollective.com/mochajs/tiers/sponsors/1/website)
|
||||
[](https://opencollective.com/mochajs/tiers/sponsors/2/website)
|
||||
[](https://opencollective.com/mochajs/tiers/sponsors/3/website)
|
||||
|
||||
## Development
|
||||
|
||||
You might want to know that:
|
||||
|
||||
- Mocha is one of the _most-depended-upon_ modules on npm (source: [libraries.io](https://libraries.io/search?order=desc&platforms=NPM&sort=dependents_count)), and
|
||||
- Mocha is an _independent_ open-source project, maintained exclusively by volunteers.
|
||||
|
||||
You might want to help:
|
||||
|
||||
- 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)
|
||||
- Mocha could use a hand with [these issues](https://github.com/mochajs/mocha/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22)
|
||||
- The [maintainer's handbook](https://github.com/mochajs/mocha/blob/master/MAINTAINERS.md) explains how things get done
|
||||
|
||||
Finally, come [chat with the maintainers](https://gitter.im/mochajs/contributors) on Gitter if you want to help with:
|
||||
|
||||
- Triaging issues, answering questions
|
||||
- Review, merging, and closing pull requests
|
||||
- Other project-maintenance-y things
|
||||
|
||||
## License
|
||||
|
||||
Copyright 2011-2021 OpenJS Foundation and contributors. Licensed [MIT](https://github.com/mochajs/mocha/blob/master/LICENSE).
|
||||
|
||||
[](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmochajs%2Fmocha?ref=badge_large)
|
BIN
node_modules/mocha/assets/growl/error.png
generated
vendored
Normal file
BIN
node_modules/mocha/assets/growl/error.png
generated
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 412 B |
BIN
node_modules/mocha/assets/growl/ok.png
generated
vendored
Normal file
BIN
node_modules/mocha/assets/growl/ok.png
generated
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 388 B |
10
node_modules/mocha/bin/_mocha
generated
vendored
Normal file
10
node_modules/mocha/bin/_mocha
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* This file remains for backwards compatibility only.
|
||||
* Don't put stuff in this file.
|
||||
* @see module:lib/cli
|
||||
*/
|
||||
|
||||
require('../lib/cli').main();
|
151
node_modules/mocha/bin/mocha
generated
vendored
Normal file
151
node_modules/mocha/bin/mocha
generated
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* This wrapper executable checks for known node flags and appends them when found,
|
||||
* before invoking the "real" executable (`lib/cli/cli.js`)
|
||||
*
|
||||
* @module bin/mocha
|
||||
* @private
|
||||
*/
|
||||
|
||||
const {deprecate} = require('../lib/utils');
|
||||
const {loadOptions} = require('../lib/cli/options');
|
||||
const {
|
||||
unparseNodeFlags,
|
||||
isNodeFlag,
|
||||
impliesNoTimeouts
|
||||
} = require('../lib/cli/node-flags');
|
||||
const unparse = require('yargs-unparser');
|
||||
const debug = require('debug')('mocha:cli:mocha');
|
||||
const {aliases} = require('../lib/cli/run-option-metadata');
|
||||
|
||||
const mochaArgs = {};
|
||||
const nodeArgs = {};
|
||||
|
||||
const opts = loadOptions(process.argv.slice(2));
|
||||
debug('loaded opts', opts);
|
||||
|
||||
/**
|
||||
* Given option/command `value`, disable timeouts if applicable
|
||||
* @param {string} [value] - Value to check
|
||||
* @ignore
|
||||
*/
|
||||
const disableTimeouts = value => {
|
||||
if (impliesNoTimeouts(value)) {
|
||||
debug('option %s disabled timeouts', value);
|
||||
mochaArgs.timeout = 0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* If `value` begins with `v8-` and is not explicitly `v8-options`, remove prefix
|
||||
* @param {string} [value] - Value to check
|
||||
* @returns {string} `value` with prefix (maybe) removed
|
||||
* @ignore
|
||||
*/
|
||||
const trimV8Option = value =>
|
||||
value !== 'v8-options' && /^v8-/.test(value) ? value.slice(3) : value;
|
||||
|
||||
// sort options into "node" and "mocha" buckets
|
||||
Object.keys(opts).forEach(opt => {
|
||||
if (isNodeFlag(opt)) {
|
||||
nodeArgs[trimV8Option(opt)] = opts[opt];
|
||||
} else {
|
||||
mochaArgs[opt] = opts[opt];
|
||||
}
|
||||
});
|
||||
|
||||
// disable 'timeout' for debugFlags
|
||||
Object.keys(nodeArgs).forEach(opt => disableTimeouts(opt));
|
||||
|
||||
// Native debugger handling
|
||||
// see https://nodejs.org/api/debugger.html#debugger_debugger
|
||||
// look for 'inspect' or 'debug' that would launch this debugger,
|
||||
// remove it from Mocha's opts and prepend it to Node's opts.
|
||||
// A deprecation warning will be printed by node, if applicable.
|
||||
// (mochaArgs._ are "positional" arguments, not prefixed with - or --)
|
||||
if (mochaArgs._) {
|
||||
const i = mochaArgs._.findIndex(val => val === 'inspect' || val === 'debug');
|
||||
if (i > -1) {
|
||||
const [command] = mochaArgs._.splice(i, 1);
|
||||
disableTimeouts('inspect');
|
||||
nodeArgs._ = [command];
|
||||
}
|
||||
}
|
||||
|
||||
// historical
|
||||
if (nodeArgs.gc) {
|
||||
deprecate(
|
||||
'"-gc" is deprecated and will be removed from a future version of Mocha. Use "--gc-global" instead.'
|
||||
);
|
||||
nodeArgs['gc-global'] = nodeArgs.gc;
|
||||
delete nodeArgs.gc;
|
||||
}
|
||||
|
||||
// --require/-r is treated as Mocha flag except when 'esm' is preloaded
|
||||
if (mochaArgs.require && mochaArgs.require.includes('esm')) {
|
||||
nodeArgs.require = ['esm'];
|
||||
mochaArgs.require = mochaArgs.require.filter(mod => mod !== 'esm');
|
||||
if (!mochaArgs.require.length) {
|
||||
delete mochaArgs.require;
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(nodeArgs).length) {
|
||||
const {spawn} = require('child_process');
|
||||
const mochaPath = require.resolve('../lib/cli/cli.js');
|
||||
|
||||
debug('final node args', nodeArgs);
|
||||
|
||||
const args = [].concat(
|
||||
unparseNodeFlags(nodeArgs),
|
||||
mochaPath,
|
||||
unparse(mochaArgs, {alias: aliases})
|
||||
);
|
||||
|
||||
debug(
|
||||
'forking child process via command: %s %s',
|
||||
process.execPath,
|
||||
args.join(' ')
|
||||
);
|
||||
|
||||
const proc = spawn(process.execPath, args, {
|
||||
stdio: 'inherit'
|
||||
});
|
||||
|
||||
proc.on('exit', (code, signal) => {
|
||||
process.on('exit', () => {
|
||||
if (signal) {
|
||||
process.kill(process.pid, signal);
|
||||
} else {
|
||||
process.exit(code);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// terminate children.
|
||||
process.on('SIGINT', () => {
|
||||
// XXX: a previous comment said this would abort the runner, but I can't see that it does
|
||||
// anything with the default runner.
|
||||
debug('main process caught SIGINT');
|
||||
proc.kill('SIGINT');
|
||||
// if running in parallel mode, we will have a proper SIGINT handler, so the below won't
|
||||
// be needed.
|
||||
if (!args.parallel || args.jobs < 2) {
|
||||
// win32 does not support SIGTERM, so use next best thing.
|
||||
if (require('os').platform() === 'win32') {
|
||||
proc.kill('SIGKILL');
|
||||
} else {
|
||||
// using SIGKILL won't cleanly close the output streams, which can result
|
||||
// in cut-off text or a befouled terminal.
|
||||
debug('sending SIGTERM to child process');
|
||||
proc.kill('SIGTERM');
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
debug('running Mocha in-process');
|
||||
require('../lib/cli/cli').main(unparse(mochaArgs, {alias: aliases}));
|
||||
}
|
219
node_modules/mocha/browser-entry.js
generated
vendored
Normal file
219
node_modules/mocha/browser-entry.js
generated
vendored
Normal file
@@ -0,0 +1,219 @@
|
||||
'use strict';
|
||||
|
||||
/* eslint no-unused-vars: off */
|
||||
/* eslint-env commonjs */
|
||||
|
||||
/**
|
||||
* Shim process.stdout.
|
||||
*/
|
||||
|
||||
process.stdout = require('browser-stdout')({label: false});
|
||||
|
||||
var parseQuery = require('./lib/browser/parse-query');
|
||||
var highlightTags = require('./lib/browser/highlight-tags');
|
||||
var Mocha = require('./lib/mocha');
|
||||
|
||||
/**
|
||||
* Create a Mocha instance.
|
||||
*
|
||||
* @return {undefined}
|
||||
*/
|
||||
|
||||
var mocha = new Mocha({reporter: 'html'});
|
||||
|
||||
/**
|
||||
* Save timer references to avoid Sinon interfering (see GH-237).
|
||||
*/
|
||||
|
||||
var Date = global.Date;
|
||||
var setTimeout = global.setTimeout;
|
||||
var setInterval = global.setInterval;
|
||||
var clearTimeout = global.clearTimeout;
|
||||
var clearInterval = global.clearInterval;
|
||||
|
||||
var uncaughtExceptionHandlers = [];
|
||||
|
||||
var originalOnerrorHandler = global.onerror;
|
||||
|
||||
/**
|
||||
* Remove uncaughtException listener.
|
||||
* Revert to original onerror handler if previously defined.
|
||||
*/
|
||||
|
||||
process.removeListener = function(e, fn) {
|
||||
if (e === 'uncaughtException') {
|
||||
if (originalOnerrorHandler) {
|
||||
global.onerror = originalOnerrorHandler;
|
||||
} else {
|
||||
global.onerror = function() {};
|
||||
}
|
||||
var i = uncaughtExceptionHandlers.indexOf(fn);
|
||||
if (i !== -1) {
|
||||
uncaughtExceptionHandlers.splice(i, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements listenerCount for 'uncaughtException'.
|
||||
*/
|
||||
|
||||
process.listenerCount = function(name) {
|
||||
if (name === 'uncaughtException') {
|
||||
return uncaughtExceptionHandlers.length;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements uncaughtException listener.
|
||||
*/
|
||||
|
||||
process.on = function(e, fn) {
|
||||
if (e === 'uncaughtException') {
|
||||
global.onerror = function(err, url, line) {
|
||||
fn(new Error(err + ' (' + url + ':' + line + ')'));
|
||||
return !mocha.options.allowUncaught;
|
||||
};
|
||||
uncaughtExceptionHandlers.push(fn);
|
||||
}
|
||||
};
|
||||
|
||||
process.listeners = function(e) {
|
||||
if (e === 'uncaughtException') {
|
||||
return uncaughtExceptionHandlers;
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
// The BDD UI is registered by default, but no UI will be functional in the
|
||||
// browser without an explicit call to the overridden `mocha.ui` (see below).
|
||||
// Ensure that this default UI does not expose its methods to the global scope.
|
||||
mocha.suite.removeAllListeners('pre-require');
|
||||
|
||||
var immediateQueue = [];
|
||||
var immediateTimeout;
|
||||
|
||||
function timeslice() {
|
||||
var immediateStart = new Date().getTime();
|
||||
while (immediateQueue.length && new Date().getTime() - immediateStart < 100) {
|
||||
immediateQueue.shift()();
|
||||
}
|
||||
if (immediateQueue.length) {
|
||||
immediateTimeout = setTimeout(timeslice, 0);
|
||||
} else {
|
||||
immediateTimeout = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* High-performance override of Runner.immediately.
|
||||
*/
|
||||
|
||||
Mocha.Runner.immediately = function(callback) {
|
||||
immediateQueue.push(callback);
|
||||
if (!immediateTimeout) {
|
||||
immediateTimeout = setTimeout(timeslice, 0);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Function to allow assertion libraries to throw errors directly into mocha.
|
||||
* This is useful when running tests in a browser because window.onerror will
|
||||
* only receive the 'message' attribute of the Error.
|
||||
*/
|
||||
mocha.throwError = function(err) {
|
||||
uncaughtExceptionHandlers.forEach(function(fn) {
|
||||
fn(err);
|
||||
});
|
||||
throw err;
|
||||
};
|
||||
|
||||
/**
|
||||
* Override ui to ensure that the ui functions are initialized.
|
||||
* Normally this would happen in Mocha.prototype.loadFiles.
|
||||
*/
|
||||
|
||||
mocha.ui = function(ui) {
|
||||
Mocha.prototype.ui.call(this, ui);
|
||||
this.suite.emit('pre-require', global, null, this);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Setup mocha with the given setting options.
|
||||
*/
|
||||
|
||||
mocha.setup = function(opts) {
|
||||
if (typeof opts === 'string') {
|
||||
opts = {ui: opts};
|
||||
}
|
||||
if (opts.delay === true) {
|
||||
this.delay();
|
||||
}
|
||||
var self = this;
|
||||
Object.keys(opts)
|
||||
.filter(function(opt) {
|
||||
return opt !== 'delay';
|
||||
})
|
||||
.forEach(function(opt) {
|
||||
if (Object.prototype.hasOwnProperty.call(opts, opt)) {
|
||||
self[opt](opts[opt]);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Run mocha, returning the Runner.
|
||||
*/
|
||||
|
||||
mocha.run = function(fn) {
|
||||
var options = mocha.options;
|
||||
mocha.globals('location');
|
||||
|
||||
var query = parseQuery(global.location.search || '');
|
||||
if (query.grep) {
|
||||
mocha.grep(query.grep);
|
||||
}
|
||||
if (query.fgrep) {
|
||||
mocha.fgrep(query.fgrep);
|
||||
}
|
||||
if (query.invert) {
|
||||
mocha.invert();
|
||||
}
|
||||
|
||||
return Mocha.prototype.run.call(mocha, function(err) {
|
||||
// The DOM Document is not available in Web Workers.
|
||||
var document = global.document;
|
||||
if (
|
||||
document &&
|
||||
document.getElementById('mocha') &&
|
||||
options.noHighlighting !== true
|
||||
) {
|
||||
highlightTags('code');
|
||||
}
|
||||
if (fn) {
|
||||
fn(err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Expose the process shim.
|
||||
* https://github.com/mochajs/mocha/pull/916
|
||||
*/
|
||||
|
||||
Mocha.process = process;
|
||||
|
||||
/**
|
||||
* Expose mocha.
|
||||
*/
|
||||
|
||||
global.Mocha = Mocha;
|
||||
global.mocha = mocha;
|
||||
|
||||
// this allows test/acceptance/required-tokens.js to pass; thus,
|
||||
// you can now do `const describe = require('mocha').describe` in a
|
||||
// browser context (assuming browserification). should fix #880
|
||||
module.exports = Object.assign(mocha, global);
|
3
node_modules/mocha/index.js
generated
vendored
Normal file
3
node_modules/mocha/index.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = require('./lib/mocha');
|
169
node_modules/mocha/lib/browser/growl.js
generated
vendored
Normal file
169
node_modules/mocha/lib/browser/growl.js
generated
vendored
Normal file
@@ -0,0 +1,169 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Web Notifications module.
|
||||
* @module Growl
|
||||
*/
|
||||
|
||||
/**
|
||||
* Save timer references to avoid Sinon interfering (see GH-237).
|
||||
*/
|
||||
var Date = global.Date;
|
||||
var setTimeout = global.setTimeout;
|
||||
var EVENT_RUN_END = require('../runner').constants.EVENT_RUN_END;
|
||||
var isBrowser = require('../utils').isBrowser;
|
||||
|
||||
/**
|
||||
* Checks if browser notification support exists.
|
||||
*
|
||||
* @public
|
||||
* @see {@link https://caniuse.com/#feat=notifications|Browser support (notifications)}
|
||||
* @see {@link https://caniuse.com/#feat=promises|Browser support (promises)}
|
||||
* @see {@link Mocha#growl}
|
||||
* @see {@link Mocha#isGrowlCapable}
|
||||
* @return {boolean} whether browser notification support exists
|
||||
*/
|
||||
exports.isCapable = function() {
|
||||
var hasNotificationSupport = 'Notification' in window;
|
||||
var hasPromiseSupport = typeof Promise === 'function';
|
||||
return isBrowser() && hasNotificationSupport && hasPromiseSupport;
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements browser notifications as a pseudo-reporter.
|
||||
*
|
||||
* @public
|
||||
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/notification|Notification API}
|
||||
* @see {@link https://developers.google.com/web/fundamentals/push-notifications/display-a-notification|Displaying a Notification}
|
||||
* @see {@link Growl#isPermitted}
|
||||
* @see {@link Mocha#_growl}
|
||||
* @param {Runner} runner - Runner instance.
|
||||
*/
|
||||
exports.notify = function(runner) {
|
||||
var promise = isPermitted();
|
||||
|
||||
/**
|
||||
* Attempt notification.
|
||||
*/
|
||||
var sendNotification = function() {
|
||||
// If user hasn't responded yet... "No notification for you!" (Seinfeld)
|
||||
Promise.race([promise, Promise.resolve(undefined)])
|
||||
.then(canNotify)
|
||||
.then(function() {
|
||||
display(runner);
|
||||
})
|
||||
.catch(notPermitted);
|
||||
};
|
||||
|
||||
runner.once(EVENT_RUN_END, sendNotification);
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if browser notification is permitted by user.
|
||||
*
|
||||
* @private
|
||||
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Notification/permission|Notification.permission}
|
||||
* @see {@link Mocha#growl}
|
||||
* @see {@link Mocha#isGrowlPermitted}
|
||||
* @returns {Promise<boolean>} promise determining if browser notification
|
||||
* permissible when fulfilled.
|
||||
*/
|
||||
function isPermitted() {
|
||||
var permitted = {
|
||||
granted: function allow() {
|
||||
return Promise.resolve(true);
|
||||
},
|
||||
denied: function deny() {
|
||||
return Promise.resolve(false);
|
||||
},
|
||||
default: function ask() {
|
||||
return Notification.requestPermission().then(function(permission) {
|
||||
return permission === 'granted';
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return permitted[Notification.permission]();
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary
|
||||
* Determines if notification should proceed.
|
||||
*
|
||||
* @description
|
||||
* Notification shall <strong>not</strong> proceed unless `value` is true.
|
||||
*
|
||||
* `value` will equal one of:
|
||||
* <ul>
|
||||
* <li><code>true</code> (from `isPermitted`)</li>
|
||||
* <li><code>false</code> (from `isPermitted`)</li>
|
||||
* <li><code>undefined</code> (from `Promise.race`)</li>
|
||||
* </ul>
|
||||
*
|
||||
* @private
|
||||
* @param {boolean|undefined} value - Determines if notification permissible.
|
||||
* @returns {Promise<undefined>} Notification can proceed
|
||||
*/
|
||||
function canNotify(value) {
|
||||
if (!value) {
|
||||
var why = value === false ? 'blocked' : 'unacknowledged';
|
||||
var reason = 'not permitted by user (' + why + ')';
|
||||
return Promise.reject(new Error(reason));
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the notification.
|
||||
*
|
||||
* @private
|
||||
* @param {Runner} runner - Runner instance.
|
||||
*/
|
||||
function display(runner) {
|
||||
var stats = runner.stats;
|
||||
var symbol = {
|
||||
cross: '\u274C',
|
||||
tick: '\u2705'
|
||||
};
|
||||
var logo = require('../../package.json').notifyLogo;
|
||||
var _message;
|
||||
var message;
|
||||
var title;
|
||||
|
||||
if (stats.failures) {
|
||||
_message = stats.failures + ' of ' + stats.tests + ' tests failed';
|
||||
message = symbol.cross + ' ' + _message;
|
||||
title = 'Failed';
|
||||
} else {
|
||||
_message = stats.passes + ' tests passed in ' + stats.duration + 'ms';
|
||||
message = symbol.tick + ' ' + _message;
|
||||
title = 'Passed';
|
||||
}
|
||||
|
||||
// Send notification
|
||||
var options = {
|
||||
badge: logo,
|
||||
body: message,
|
||||
dir: 'ltr',
|
||||
icon: logo,
|
||||
lang: 'en-US',
|
||||
name: 'mocha',
|
||||
requireInteraction: false,
|
||||
timestamp: Date.now()
|
||||
};
|
||||
var notification = new Notification(title, options);
|
||||
|
||||
// Autoclose after brief delay (makes various browsers act same)
|
||||
var FORCE_DURATION = 4000;
|
||||
setTimeout(notification.close.bind(notification), FORCE_DURATION);
|
||||
}
|
||||
|
||||
/**
|
||||
* As notifications are tangential to our purpose, just log the error.
|
||||
*
|
||||
* @private
|
||||
* @param {Error} err - Why notification didn't happen.
|
||||
*/
|
||||
function notPermitted(err) {
|
||||
console.error('notification error:', err.message);
|
||||
}
|
39
node_modules/mocha/lib/browser/highlight-tags.js
generated
vendored
Normal file
39
node_modules/mocha/lib/browser/highlight-tags.js
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Highlight the given string of `js`.
|
||||
*
|
||||
* @private
|
||||
* @param {string} js
|
||||
* @return {string}
|
||||
*/
|
||||
function highlight(js) {
|
||||
return js
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/\/\/(.*)/gm, '<span class="comment">//$1</span>')
|
||||
.replace(/('.*?')/gm, '<span class="string">$1</span>')
|
||||
.replace(/(\d+\.\d+)/gm, '<span class="number">$1</span>')
|
||||
.replace(/(\d+)/gm, '<span class="number">$1</span>')
|
||||
.replace(
|
||||
/\bnew[ \t]+(\w+)/gm,
|
||||
'<span class="keyword">new</span> <span class="init">$1</span>'
|
||||
)
|
||||
.replace(
|
||||
/\b(function|new|throw|return|var|if|else)\b/gm,
|
||||
'<span class="keyword">$1</span>'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlight the contents of tag `name`.
|
||||
*
|
||||
* @private
|
||||
* @param {string} name
|
||||
*/
|
||||
module.exports = function highlightTags(name) {
|
||||
var code = document.getElementById('mocha').getElementsByTagName(name);
|
||||
for (var i = 0, len = code.length; i < len; ++i) {
|
||||
code[i].innerHTML = highlight(code[i].innerHTML);
|
||||
}
|
||||
};
|
24
node_modules/mocha/lib/browser/parse-query.js
generated
vendored
Normal file
24
node_modules/mocha/lib/browser/parse-query.js
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Parse the given `qs`.
|
||||
*
|
||||
* @private
|
||||
* @param {string} qs
|
||||
* @return {Object<string, string>}
|
||||
*/
|
||||
module.exports = function parseQuery(qs) {
|
||||
return qs
|
||||
.replace('?', '')
|
||||
.split('&')
|
||||
.reduce(function(obj, pair) {
|
||||
var i = pair.indexOf('=');
|
||||
var key = pair.slice(0, i);
|
||||
var val = pair.slice(++i);
|
||||
|
||||
// Due to how the URLSearchParams API treats spaces
|
||||
obj[key] = decodeURIComponent(val.replace(/\+/g, '%20'));
|
||||
|
||||
return obj;
|
||||
}, {});
|
||||
};
|
123
node_modules/mocha/lib/browser/progress.js
generated
vendored
Normal file
123
node_modules/mocha/lib/browser/progress.js
generated
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
@module browser/Progress
|
||||
*/
|
||||
|
||||
/**
|
||||
* Expose `Progress`.
|
||||
*/
|
||||
|
||||
module.exports = Progress;
|
||||
|
||||
/**
|
||||
* Initialize a new `Progress` indicator.
|
||||
*/
|
||||
function Progress() {
|
||||
this.percent = 0;
|
||||
this.size(0);
|
||||
this.fontSize(11);
|
||||
this.font('helvetica, arial, sans-serif');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set progress size to `size`.
|
||||
*
|
||||
* @public
|
||||
* @param {number} size
|
||||
* @return {Progress} Progress instance.
|
||||
*/
|
||||
Progress.prototype.size = function(size) {
|
||||
this._size = size;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set text to `text`.
|
||||
*
|
||||
* @public
|
||||
* @param {string} text
|
||||
* @return {Progress} Progress instance.
|
||||
*/
|
||||
Progress.prototype.text = function(text) {
|
||||
this._text = text;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set font size to `size`.
|
||||
*
|
||||
* @public
|
||||
* @param {number} size
|
||||
* @return {Progress} Progress instance.
|
||||
*/
|
||||
Progress.prototype.fontSize = function(size) {
|
||||
this._fontSize = size;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set font to `family`.
|
||||
*
|
||||
* @param {string} family
|
||||
* @return {Progress} Progress instance.
|
||||
*/
|
||||
Progress.prototype.font = function(family) {
|
||||
this._font = family;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update percentage to `n`.
|
||||
*
|
||||
* @param {number} n
|
||||
* @return {Progress} Progress instance.
|
||||
*/
|
||||
Progress.prototype.update = function(n) {
|
||||
this.percent = n;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Draw on `ctx`.
|
||||
*
|
||||
* @param {CanvasRenderingContext2d} ctx
|
||||
* @return {Progress} Progress instance.
|
||||
*/
|
||||
Progress.prototype.draw = function(ctx) {
|
||||
try {
|
||||
var percent = Math.min(this.percent, 100);
|
||||
var size = this._size;
|
||||
var half = size / 2;
|
||||
var x = half;
|
||||
var y = half;
|
||||
var rad = half - 1;
|
||||
var fontSize = this._fontSize;
|
||||
|
||||
ctx.font = fontSize + 'px ' + this._font;
|
||||
|
||||
var angle = Math.PI * 2 * (percent / 100);
|
||||
ctx.clearRect(0, 0, size, size);
|
||||
|
||||
// outer circle
|
||||
ctx.strokeStyle = '#9f9f9f';
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, rad, 0, angle, false);
|
||||
ctx.stroke();
|
||||
|
||||
// inner circle
|
||||
ctx.strokeStyle = '#eee';
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, rad - 1, 0, angle, true);
|
||||
ctx.stroke();
|
||||
|
||||
// text
|
||||
var text = this._text || (percent | 0) + '%';
|
||||
var w = ctx.measureText(text).width;
|
||||
|
||||
ctx.fillText(text, x - w / 2 + 1, y + fontSize / 2 - 1);
|
||||
} catch (ignore) {
|
||||
// don't fail if we can't render progress
|
||||
}
|
||||
return this;
|
||||
};
|
20
node_modules/mocha/lib/browser/template.html
generated
vendored
Normal file
20
node_modules/mocha/lib/browser/template.html
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Mocha</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="mocha.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="mocha"></div>
|
||||
<script src="mocha.js"></script>
|
||||
<script>
|
||||
mocha.setup('bdd');
|
||||
</script>
|
||||
<script src="tests.spec.js"></script>
|
||||
<script>
|
||||
mocha.run();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
88
node_modules/mocha/lib/cli/cli.js
generated
vendored
Normal file
88
node_modules/mocha/lib/cli/cli.js
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Contains CLI entry point and public API for programmatic usage in Node.js.
|
||||
* - Option parsing is handled by {@link https://npm.im/yargs yargs}.
|
||||
* - If executed via `node`, this module will run {@linkcode module:lib/cli.main main()}.
|
||||
* @public
|
||||
* @module lib/cli
|
||||
*/
|
||||
|
||||
const debug = require('debug')('mocha:cli:cli');
|
||||
const symbols = require('log-symbols');
|
||||
const yargs = require('yargs/yargs');
|
||||
const path = require('path');
|
||||
const {
|
||||
loadRc,
|
||||
loadPkgRc,
|
||||
loadOptions,
|
||||
YARGS_PARSER_CONFIG
|
||||
} = require('./options');
|
||||
const lookupFiles = require('./lookup-files');
|
||||
const commands = require('./commands');
|
||||
const ansi = require('ansi-colors');
|
||||
const {repository, homepage, version, gitter} = require('../../package.json');
|
||||
const {cwd} = require('../utils');
|
||||
|
||||
/**
|
||||
* - Accepts an `Array` of arguments
|
||||
* - Modifies {@link https://nodejs.org/api/modules.html#modules_module_paths Node.js' search path} for easy loading of consumer modules
|
||||
* - Sets {@linkcode https://nodejs.org/api/errors.html#errors_error_stacktracelimit Error.stackTraceLimit} to `Infinity`
|
||||
* @public
|
||||
* @summary Mocha's main command-line entry-point.
|
||||
* @param {string[]} argv - Array of arguments to parse, or by default the lovely `process.argv.slice(2)`
|
||||
*/
|
||||
exports.main = (argv = process.argv.slice(2)) => {
|
||||
debug('entered main with raw args', argv);
|
||||
// ensure we can require() from current working directory
|
||||
if (typeof module.paths !== 'undefined') {
|
||||
module.paths.push(cwd(), path.resolve('node_modules'));
|
||||
}
|
||||
|
||||
Error.stackTraceLimit = Infinity; // configurable via --stack-trace-limit?
|
||||
|
||||
var args = loadOptions(argv);
|
||||
|
||||
yargs()
|
||||
.scriptName('mocha')
|
||||
.command(commands.run)
|
||||
.command(commands.init)
|
||||
.updateStrings({
|
||||
'Positionals:': 'Positional Arguments',
|
||||
'Options:': 'Other Options',
|
||||
'Commands:': 'Commands'
|
||||
})
|
||||
.fail((msg, err, yargs) => {
|
||||
debug('caught error sometime before command handler: %O', err);
|
||||
yargs.showHelp();
|
||||
console.error(`\n${symbols.error} ${ansi.red('ERROR:')} ${msg}`);
|
||||
process.exitCode = 1;
|
||||
})
|
||||
.help('help', 'Show usage information & exit')
|
||||
.alias('help', 'h')
|
||||
.version('version', 'Show version number & exit', version)
|
||||
.alias('version', 'V')
|
||||
.wrap(process.stdout.columns ? Math.min(process.stdout.columns, 80) : 80)
|
||||
.epilog(
|
||||
`Mocha Resources
|
||||
Chat: ${ansi.magenta(gitter)}
|
||||
GitHub: ${ansi.blue(repository.url)}
|
||||
Docs: ${ansi.yellow(homepage)}
|
||||
`
|
||||
)
|
||||
.parserConfiguration(YARGS_PARSER_CONFIG)
|
||||
.config(args)
|
||||
.parse(args._);
|
||||
};
|
||||
|
||||
exports.lookupFiles = lookupFiles;
|
||||
exports.loadOptions = loadOptions;
|
||||
exports.loadPkgRc = loadPkgRc;
|
||||
exports.loadRc = loadRc;
|
||||
|
||||
// allow direct execution
|
||||
if (require.main === module) {
|
||||
exports.main();
|
||||
}
|
92
node_modules/mocha/lib/cli/collect-files.js
generated
vendored
Normal file
92
node_modules/mocha/lib/cli/collect-files.js
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const ansi = require('ansi-colors');
|
||||
const debug = require('debug')('mocha:cli:run:helpers');
|
||||
const minimatch = require('minimatch');
|
||||
const {NO_FILES_MATCH_PATTERN} = require('../errors').constants;
|
||||
const lookupFiles = require('./lookup-files');
|
||||
const {castArray} = require('../utils');
|
||||
|
||||
/**
|
||||
* Exports a function that collects test files from CLI parameters.
|
||||
* @see module:lib/cli/run-helpers
|
||||
* @see module:lib/cli/watch-run
|
||||
* @module
|
||||
* @private
|
||||
*/
|
||||
|
||||
/**
|
||||
* Smash together an array of test files in the correct order
|
||||
* @param {FileCollectionOptions} [opts] - Options
|
||||
* @returns {string[]} List of files to test
|
||||
* @private
|
||||
*/
|
||||
module.exports = ({
|
||||
ignore,
|
||||
extension,
|
||||
file: fileArgs,
|
||||
recursive,
|
||||
sort,
|
||||
spec
|
||||
} = {}) => {
|
||||
const unmatched = [];
|
||||
const specFiles = spec.reduce((specFiles, arg) => {
|
||||
try {
|
||||
const moreSpecFiles = castArray(lookupFiles(arg, extension, recursive))
|
||||
.filter(filename =>
|
||||
ignore.every(pattern => !minimatch(filename, pattern))
|
||||
)
|
||||
.map(filename => path.resolve(filename));
|
||||
return [...specFiles, ...moreSpecFiles];
|
||||
} catch (err) {
|
||||
if (err.code === NO_FILES_MATCH_PATTERN) {
|
||||
unmatched.push({message: err.message, pattern: err.pattern});
|
||||
return specFiles;
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
}, []);
|
||||
|
||||
// ensure we don't sort the stuff from fileArgs; order is important!
|
||||
if (sort) {
|
||||
specFiles.sort();
|
||||
}
|
||||
|
||||
// add files given through --file to be ran first
|
||||
const files = [
|
||||
...fileArgs.map(filepath => path.resolve(filepath)),
|
||||
...specFiles
|
||||
];
|
||||
debug('test files (in order): ', files);
|
||||
|
||||
if (!files.length) {
|
||||
// give full message details when only 1 file is missing
|
||||
const noneFoundMsg =
|
||||
unmatched.length === 1
|
||||
? `Error: No test files found: ${JSON.stringify(unmatched[0].pattern)}` // stringify to print escaped characters raw
|
||||
: 'Error: No test files found';
|
||||
console.error(ansi.red(noneFoundMsg));
|
||||
process.exit(1);
|
||||
} else {
|
||||
// print messages as a warning
|
||||
unmatched.forEach(warning => {
|
||||
console.warn(ansi.yellow(`Warning: ${warning.message}`));
|
||||
});
|
||||
}
|
||||
|
||||
return files;
|
||||
};
|
||||
|
||||
/**
|
||||
* An object to configure how Mocha gathers test files
|
||||
* @private
|
||||
* @typedef {Object} FileCollectionOptions
|
||||
* @property {string[]} extension - File extensions to use
|
||||
* @property {string[]} spec - Files, dirs, globs to run
|
||||
* @property {string[]} ignore - Files, dirs, globs to ignore
|
||||
* @property {string[]} file - List of additional files to include
|
||||
* @property {boolean} recursive - Find files recursively
|
||||
* @property {boolean} sort - Sort test files
|
||||
*/
|
13
node_modules/mocha/lib/cli/commands.js
generated
vendored
Normal file
13
node_modules/mocha/lib/cli/commands.js
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Exports Yargs commands
|
||||
* @see https://git.io/fpJ0G
|
||||
* @private
|
||||
* @module
|
||||
*/
|
||||
|
||||
exports.init = require('./init');
|
||||
|
||||
// default command
|
||||
exports.run = require('./run');
|
105
node_modules/mocha/lib/cli/config.js
generated
vendored
Normal file
105
node_modules/mocha/lib/cli/config.js
generated
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Responsible for loading / finding Mocha's "rc" files.
|
||||
*
|
||||
* @private
|
||||
* @module
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const debug = require('debug')('mocha:cli:config');
|
||||
const findUp = require('find-up');
|
||||
const {createUnparsableFileError} = require('../errors');
|
||||
const utils = require('../utils');
|
||||
|
||||
/**
|
||||
* These are the valid config files, in order of precedence;
|
||||
* e.g., if `.mocharc.js` is present, then `.mocharc.yaml` and the rest
|
||||
* will be ignored.
|
||||
* The user should still be able to explicitly specify a file.
|
||||
* @private
|
||||
*/
|
||||
exports.CONFIG_FILES = [
|
||||
'.mocharc.cjs',
|
||||
'.mocharc.js',
|
||||
'.mocharc.yaml',
|
||||
'.mocharc.yml',
|
||||
'.mocharc.jsonc',
|
||||
'.mocharc.json'
|
||||
];
|
||||
|
||||
const isModuleNotFoundError = err =>
|
||||
err.code !== 'MODULE_NOT_FOUND' ||
|
||||
err.message.indexOf('Cannot find module') !== -1;
|
||||
|
||||
/**
|
||||
* Parsers for various config filetypes. Each accepts a filepath and
|
||||
* returns an object (but could throw)
|
||||
*/
|
||||
const parsers = (exports.parsers = {
|
||||
yaml: filepath => require('js-yaml').load(fs.readFileSync(filepath, 'utf8')),
|
||||
js: filepath => {
|
||||
const cwdFilepath = path.resolve(filepath);
|
||||
try {
|
||||
debug('parsers: load using cwd-relative path: "%s"', cwdFilepath);
|
||||
return require(cwdFilepath);
|
||||
} catch (err) {
|
||||
if (isModuleNotFoundError(err)) {
|
||||
debug('parsers: retry load as module-relative path: "%s"', filepath);
|
||||
return require(filepath);
|
||||
} else {
|
||||
throw err; // rethrow
|
||||
}
|
||||
}
|
||||
},
|
||||
json: filepath =>
|
||||
JSON.parse(
|
||||
require('strip-json-comments')(fs.readFileSync(filepath, 'utf8'))
|
||||
)
|
||||
});
|
||||
|
||||
/**
|
||||
* Loads and parses, based on file extension, a config file.
|
||||
* "JSON" files may have comments.
|
||||
*
|
||||
* @private
|
||||
* @param {string} filepath - Config file path to load
|
||||
* @returns {Object} Parsed config object
|
||||
*/
|
||||
exports.loadConfig = filepath => {
|
||||
let config = {};
|
||||
debug('loadConfig: trying to parse config at %s', filepath);
|
||||
|
||||
const ext = path.extname(filepath);
|
||||
try {
|
||||
if (ext === '.yml' || ext === '.yaml') {
|
||||
config = parsers.yaml(filepath);
|
||||
} else if (ext === '.js' || ext === '.cjs') {
|
||||
config = parsers.js(filepath);
|
||||
} else {
|
||||
config = parsers.json(filepath);
|
||||
}
|
||||
} catch (err) {
|
||||
throw createUnparsableFileError(
|
||||
`Unable to read/parse ${filepath}: ${err}`,
|
||||
filepath
|
||||
);
|
||||
}
|
||||
return config;
|
||||
};
|
||||
|
||||
/**
|
||||
* Find ("find up") config file starting at `cwd`
|
||||
*
|
||||
* @param {string} [cwd] - Current working directory
|
||||
* @returns {string|null} Filepath to config, if found
|
||||
*/
|
||||
exports.findConfig = (cwd = utils.cwd()) => {
|
||||
const filepath = findUp.sync(exports.CONFIG_FILES, {cwd});
|
||||
if (filepath) {
|
||||
debug('findConfig: found config file %s', filepath);
|
||||
}
|
||||
return filepath;
|
||||
};
|
3
node_modules/mocha/lib/cli/index.js
generated
vendored
Normal file
3
node_modules/mocha/lib/cli/index.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = require('./cli');
|
36
node_modules/mocha/lib/cli/init.js
generated
vendored
Normal file
36
node_modules/mocha/lib/cli/init.js
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Command module for "init" command
|
||||
*
|
||||
* @private
|
||||
* @module
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
exports.command = 'init <path>';
|
||||
|
||||
exports.description = 'create a client-side Mocha setup at <path>';
|
||||
|
||||
exports.builder = yargs =>
|
||||
yargs.positional('path', {
|
||||
type: 'string',
|
||||
normalize: true
|
||||
});
|
||||
|
||||
exports.handler = argv => {
|
||||
const destdir = argv.path;
|
||||
const srcdir = path.join(__dirname, '..', '..');
|
||||
fs.mkdirSync(destdir, {recursive: true});
|
||||
const css = fs.readFileSync(path.join(srcdir, 'mocha.css'));
|
||||
const js = fs.readFileSync(path.join(srcdir, 'mocha.js'));
|
||||
const tmpl = fs.readFileSync(
|
||||
path.join(srcdir, 'lib', 'browser', 'template.html')
|
||||
);
|
||||
fs.writeFileSync(path.join(destdir, 'mocha.css'), css);
|
||||
fs.writeFileSync(path.join(destdir, 'mocha.js'), js);
|
||||
fs.writeFileSync(path.join(destdir, 'tests.spec.js'), '');
|
||||
fs.writeFileSync(path.join(destdir, 'index.html'), tmpl);
|
||||
};
|
151
node_modules/mocha/lib/cli/lookup-files.js
generated
vendored
Normal file
151
node_modules/mocha/lib/cli/lookup-files.js
generated
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
'use strict';
|
||||
/**
|
||||
* Contains `lookupFiles`, which takes some globs/dirs/options and returns a list of files.
|
||||
* @module
|
||||
* @private
|
||||
*/
|
||||
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var glob = require('glob');
|
||||
var {format} = require('util');
|
||||
var errors = require('../errors');
|
||||
var createNoFilesMatchPatternError = errors.createNoFilesMatchPatternError;
|
||||
var createMissingArgumentError = errors.createMissingArgumentError;
|
||||
var {sQuote, dQuote} = require('../utils');
|
||||
const debug = require('debug')('mocha:cli:lookup-files');
|
||||
|
||||
/**
|
||||
* Determines if pathname would be a "hidden" file (or directory) on UN*X.
|
||||
*
|
||||
* @description
|
||||
* On UN*X, pathnames beginning with a full stop (aka dot) are hidden during
|
||||
* typical usage. Dotfiles, plain-text configuration files, are prime examples.
|
||||
*
|
||||
* @see {@link http://xahlee.info/UnixResource_dir/writ/unix_origin_of_dot_filename.html|Origin of Dot File Names}
|
||||
*
|
||||
* @private
|
||||
* @param {string} pathname - Pathname to check for match.
|
||||
* @return {boolean} whether pathname would be considered a hidden file.
|
||||
* @example
|
||||
* isHiddenOnUnix('.profile'); // => true
|
||||
*/
|
||||
const isHiddenOnUnix = pathname => path.basename(pathname).startsWith('.');
|
||||
|
||||
/**
|
||||
* Determines if pathname has a matching file extension.
|
||||
*
|
||||
* Supports multi-part extensions.
|
||||
*
|
||||
* @private
|
||||
* @param {string} pathname - Pathname to check for match.
|
||||
* @param {string[]} exts - List of file extensions, w/-or-w/o leading period
|
||||
* @return {boolean} `true` if file extension matches.
|
||||
* @example
|
||||
* hasMatchingExtname('foo.html', ['js', 'css']); // false
|
||||
* hasMatchingExtname('foo.js', ['.js']); // true
|
||||
* hasMatchingExtname('foo.js', ['js']); // ture
|
||||
*/
|
||||
const hasMatchingExtname = (pathname, exts = []) =>
|
||||
exts
|
||||
.map(ext => (ext.startsWith('.') ? ext : `.${ext}`))
|
||||
.some(ext => pathname.endsWith(ext));
|
||||
|
||||
/**
|
||||
* Lookup file names at the given `path`.
|
||||
*
|
||||
* @description
|
||||
* Filenames are returned in _traversal_ order by the OS/filesystem.
|
||||
* **Make no assumption that the names will be sorted in any fashion.**
|
||||
*
|
||||
* @public
|
||||
* @alias module:lib/cli.lookupFiles
|
||||
* @param {string} filepath - Base path to start searching from.
|
||||
* @param {string[]} [extensions=[]] - File extensions to look for.
|
||||
* @param {boolean} [recursive=false] - Whether to recurse into subdirectories.
|
||||
* @return {string[]} An array of paths.
|
||||
* @throws {Error} if no files match pattern.
|
||||
* @throws {TypeError} if `filepath` is directory and `extensions` not provided.
|
||||
*/
|
||||
module.exports = function lookupFiles(
|
||||
filepath,
|
||||
extensions = [],
|
||||
recursive = false
|
||||
) {
|
||||
const files = [];
|
||||
let stat;
|
||||
|
||||
if (!fs.existsSync(filepath)) {
|
||||
let pattern;
|
||||
if (glob.hasMagic(filepath)) {
|
||||
// Handle glob as is without extensions
|
||||
pattern = filepath;
|
||||
} else {
|
||||
// glob pattern e.g. 'filepath+(.js|.ts)'
|
||||
const strExtensions = extensions
|
||||
.map(ext => (ext.startsWith('.') ? ext : `.${ext}`))
|
||||
.join('|');
|
||||
pattern = `${filepath}+(${strExtensions})`;
|
||||
debug('looking for files using glob pattern: %s', pattern);
|
||||
}
|
||||
files.push(...glob.sync(pattern, {nodir: true}));
|
||||
if (!files.length) {
|
||||
throw createNoFilesMatchPatternError(
|
||||
'Cannot find any files matching pattern ' + dQuote(filepath),
|
||||
filepath
|
||||
);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
// Handle file
|
||||
try {
|
||||
stat = fs.statSync(filepath);
|
||||
if (stat.isFile()) {
|
||||
return filepath;
|
||||
}
|
||||
} catch (err) {
|
||||
// ignore error
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle directory
|
||||
fs.readdirSync(filepath).forEach(dirent => {
|
||||
const pathname = path.join(filepath, dirent);
|
||||
let stat;
|
||||
|
||||
try {
|
||||
stat = fs.statSync(pathname);
|
||||
if (stat.isDirectory()) {
|
||||
if (recursive) {
|
||||
files.push(...lookupFiles(pathname, extensions, recursive));
|
||||
}
|
||||
return;
|
||||
}
|
||||
} catch (ignored) {
|
||||
return;
|
||||
}
|
||||
if (!extensions.length) {
|
||||
throw createMissingArgumentError(
|
||||
format(
|
||||
'Argument %s required when argument %s is a directory',
|
||||
sQuote('extensions'),
|
||||
sQuote('filepath')
|
||||
),
|
||||
'extensions',
|
||||
'array'
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
!stat.isFile() ||
|
||||
!hasMatchingExtname(pathname, extensions) ||
|
||||
isHiddenOnUnix(pathname)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
files.push(pathname);
|
||||
});
|
||||
|
||||
return files;
|
||||
};
|
88
node_modules/mocha/lib/cli/node-flags.js
generated
vendored
Normal file
88
node_modules/mocha/lib/cli/node-flags.js
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Some settings and code related to Mocha's handling of Node.js/V8 flags.
|
||||
* @private
|
||||
* @module
|
||||
*/
|
||||
|
||||
const nodeFlags = process.allowedNodeEnvironmentFlags;
|
||||
const {isMochaFlag} = require('./run-option-metadata');
|
||||
const unparse = require('yargs-unparser');
|
||||
|
||||
/**
|
||||
* These flags are considered "debug" flags.
|
||||
* @see {@link impliesNoTimeouts}
|
||||
* @private
|
||||
*/
|
||||
const debugFlags = new Set(['inspect', 'inspect-brk']);
|
||||
|
||||
/**
|
||||
* Mocha has historical support for various `node` and V8 flags which might not
|
||||
* appear in `process.allowedNodeEnvironmentFlags`.
|
||||
* These include:
|
||||
* - `--preserve-symlinks`
|
||||
* - `--harmony-*`
|
||||
* - `--gc-global`
|
||||
* - `--trace-*`
|
||||
* - `--es-staging`
|
||||
* - `--use-strict`
|
||||
* - `--v8-*` (but *not* `--v8-options`)
|
||||
* @summary Whether or not to pass a flag along to the `node` executable.
|
||||
* @param {string} flag - Flag to test
|
||||
* @param {boolean} [bareword=true] - If `false`, we expect `flag` to have one or two leading dashes.
|
||||
* @returns {boolean} If the flag is considered a "Node" flag.
|
||||
* @private
|
||||
*/
|
||||
exports.isNodeFlag = (flag, bareword = true) => {
|
||||
if (!bareword) {
|
||||
// check if the flag begins with dashes; if not, not a node flag.
|
||||
if (!/^--?/.test(flag)) {
|
||||
return false;
|
||||
}
|
||||
// strip the leading dashes to match against subsequent checks
|
||||
flag = flag.replace(/^--?/, '');
|
||||
}
|
||||
return (
|
||||
// check actual node flags from `process.allowedNodeEnvironmentFlags`,
|
||||
// then historical support for various V8 and non-`NODE_OPTIONS` flags
|
||||
// and also any V8 flags with `--v8-` prefix
|
||||
(!isMochaFlag(flag) && nodeFlags && nodeFlags.has(flag)) ||
|
||||
debugFlags.has(flag) ||
|
||||
/(?:preserve-symlinks(?:-main)?|harmony(?:[_-]|$)|(?:trace[_-].+$)|gc(?:[_-]global)?$|es[_-]staging$|use[_-]strict$|v8[_-](?!options).+?$)/.test(
|
||||
flag
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns `true` if the flag is a "debug-like" flag. These require timeouts
|
||||
* to be suppressed, or pausing the debugger on breakpoints will cause test failures.
|
||||
* @param {string} flag - Flag to test
|
||||
* @returns {boolean}
|
||||
* @private
|
||||
*/
|
||||
exports.impliesNoTimeouts = flag => debugFlags.has(flag);
|
||||
|
||||
/**
|
||||
* All non-strictly-boolean arguments to node--those with values--must specify those values using `=`, e.g., `--inspect=0.0.0.0`.
|
||||
* Unparse these arguments using `yargs-unparser` (which would result in `--inspect 0.0.0.0`), then supply `=` where we have values.
|
||||
* Apparently --require in Node.js v8 does NOT want `=`.
|
||||
* There's probably an easier or more robust way to do this; fixes welcome
|
||||
* @param {Object} opts - Arguments object
|
||||
* @returns {string[]} Unparsed arguments using `=` to specify values
|
||||
* @private
|
||||
*/
|
||||
exports.unparseNodeFlags = opts => {
|
||||
var args = unparse(opts);
|
||||
return args.length
|
||||
? args
|
||||
.join(' ')
|
||||
.split(/\b/)
|
||||
.map((arg, index, args) =>
|
||||
arg === ' ' && args[index - 1] !== 'require' ? '=' : arg
|
||||
)
|
||||
.join('')
|
||||
.split(' ')
|
||||
: [];
|
||||
};
|
70
node_modules/mocha/lib/cli/one-and-dones.js
generated
vendored
Normal file
70
node_modules/mocha/lib/cli/one-and-dones.js
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Contains "command" code for "one-and-dones"--options passed
|
||||
* to Mocha which cause it to just dump some info and exit.
|
||||
* See {@link module:lib/cli/one-and-dones.ONE_AND_DONE_ARGS ONE_AND_DONE_ARGS} for more info.
|
||||
* @module
|
||||
* @private
|
||||
*/
|
||||
|
||||
const align = require('wide-align');
|
||||
const Mocha = require('../mocha');
|
||||
|
||||
/**
|
||||
* Dumps a sorted list of the enumerable, lower-case keys of some object
|
||||
* to `STDOUT`.
|
||||
* @param {Object} obj - Object, ostensibly having some enumerable keys
|
||||
* @ignore
|
||||
* @private
|
||||
*/
|
||||
const showKeys = obj => {
|
||||
console.log();
|
||||
const keys = Object.keys(obj);
|
||||
const maxKeyLength = keys.reduce((max, key) => Math.max(max, key.length), 0);
|
||||
keys
|
||||
.filter(
|
||||
key => /^[a-z]/.test(key) && !obj[key].browserOnly && !obj[key].abstract
|
||||
)
|
||||
.sort()
|
||||
.forEach(key => {
|
||||
const description = obj[key].description;
|
||||
console.log(
|
||||
` ${align.left(key, maxKeyLength + 1)}${
|
||||
description ? `- ${description}` : ''
|
||||
}`
|
||||
);
|
||||
});
|
||||
console.log();
|
||||
};
|
||||
|
||||
/**
|
||||
* Handlers for one-and-done options
|
||||
* @namespace
|
||||
* @private
|
||||
*/
|
||||
exports.ONE_AND_DONES = {
|
||||
/**
|
||||
* Dump list of built-in interfaces
|
||||
* @private
|
||||
*/
|
||||
'list-interfaces': () => {
|
||||
showKeys(Mocha.interfaces);
|
||||
},
|
||||
/**
|
||||
* Dump list of built-in reporters
|
||||
* @private
|
||||
*/
|
||||
'list-reporters': () => {
|
||||
showKeys(Mocha.reporters);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A Set of all one-and-done options
|
||||
* @type Set<string>
|
||||
* @private
|
||||
*/
|
||||
exports.ONE_AND_DONE_ARGS = new Set(
|
||||
['help', 'h', 'version', 'V'].concat(Object.keys(exports.ONE_AND_DONES))
|
||||
);
|
261
node_modules/mocha/lib/cli/options.js
generated
vendored
Normal file
261
node_modules/mocha/lib/cli/options.js
generated
vendored
Normal file
@@ -0,0 +1,261 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Main entry point for handling filesystem-based configuration,
|
||||
* whether that's a config file or `package.json` or whatever.
|
||||
* @module lib/cli/options
|
||||
* @private
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const ansi = require('ansi-colors');
|
||||
const yargsParser = require('yargs-parser');
|
||||
const {types, aliases} = require('./run-option-metadata');
|
||||
const {ONE_AND_DONE_ARGS} = require('./one-and-dones');
|
||||
const mocharc = require('../mocharc.json');
|
||||
const {list} = require('./run-helpers');
|
||||
const {loadConfig, findConfig} = require('./config');
|
||||
const findUp = require('find-up');
|
||||
const debug = require('debug')('mocha:cli:options');
|
||||
const {isNodeFlag} = require('./node-flags');
|
||||
const {createUnparsableFileError} = require('../errors');
|
||||
|
||||
/**
|
||||
* The `yargs-parser` namespace
|
||||
* @external yargsParser
|
||||
* @see {@link https://npm.im/yargs-parser}
|
||||
*/
|
||||
|
||||
/**
|
||||
* An object returned by a configured `yargs-parser` representing arguments
|
||||
* @memberof external:yargsParser
|
||||
* @interface Arguments
|
||||
*/
|
||||
|
||||
/**
|
||||
* Base yargs parser configuration
|
||||
* @private
|
||||
*/
|
||||
const YARGS_PARSER_CONFIG = {
|
||||
'combine-arrays': true,
|
||||
'short-option-groups': false,
|
||||
'dot-notation': false,
|
||||
'strip-aliased': true
|
||||
};
|
||||
|
||||
/**
|
||||
* This is the config pulled from the `yargs` property of Mocha's
|
||||
* `package.json`, but it also disables camel case expansion as to
|
||||
* avoid outputting non-canonical keynames, as we need to do some
|
||||
* lookups.
|
||||
* @private
|
||||
* @ignore
|
||||
*/
|
||||
const configuration = Object.assign({}, YARGS_PARSER_CONFIG, {
|
||||
'camel-case-expansion': false
|
||||
});
|
||||
|
||||
/**
|
||||
* This is a really fancy way to:
|
||||
* - `array`-type options: ensure unique values and evtl. split comma-delimited lists
|
||||
* - `boolean`/`number`/`string`- options: use last element when given multiple times
|
||||
* This is passed as the `coerce` option to `yargs-parser`
|
||||
* @private
|
||||
* @ignore
|
||||
*/
|
||||
const globOptions = ['spec', 'ignore'];
|
||||
const coerceOpts = Object.assign(
|
||||
types.array.reduce(
|
||||
(acc, arg) =>
|
||||
Object.assign(acc, {
|
||||
[arg]: v => Array.from(new Set(globOptions.includes(arg) ? v : list(v)))
|
||||
}),
|
||||
{}
|
||||
),
|
||||
types.boolean
|
||||
.concat(types.string, types.number)
|
||||
.reduce(
|
||||
(acc, arg) =>
|
||||
Object.assign(acc, {[arg]: v => (Array.isArray(v) ? v.pop() : v)}),
|
||||
{}
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* We do not have a case when multiple arguments are ever allowed after a flag
|
||||
* (e.g., `--foo bar baz quux`), so we fix the number of arguments to 1 across
|
||||
* the board of non-boolean options.
|
||||
* This is passed as the `narg` option to `yargs-parser`
|
||||
* @private
|
||||
* @ignore
|
||||
*/
|
||||
const nargOpts = types.array
|
||||
.concat(types.string, types.number)
|
||||
.reduce((acc, arg) => Object.assign(acc, {[arg]: 1}), {});
|
||||
|
||||
/**
|
||||
* Wrapper around `yargs-parser` which applies our settings
|
||||
* @param {string|string[]} args - Arguments to parse
|
||||
* @param {Object} defaultValues - Default values of mocharc.json
|
||||
* @param {...Object} configObjects - `configObjects` for yargs-parser
|
||||
* @private
|
||||
* @ignore
|
||||
*/
|
||||
const parse = (args = [], defaultValues = {}, ...configObjects) => {
|
||||
// save node-specific args for special handling.
|
||||
// 1. when these args have a "=" they should be considered to have values
|
||||
// 2. if they don't, they just boolean flags
|
||||
// 3. to avoid explicitly defining the set of them, we tell yargs-parser they
|
||||
// are ALL boolean flags.
|
||||
// 4. we can then reapply the values after yargs-parser is done.
|
||||
const nodeArgs = (Array.isArray(args) ? args : args.split(' ')).reduce(
|
||||
(acc, arg) => {
|
||||
const pair = arg.split('=');
|
||||
let flag = pair[0];
|
||||
if (isNodeFlag(flag, false)) {
|
||||
flag = flag.replace(/^--?/, '');
|
||||
return arg.includes('=')
|
||||
? acc.concat([[flag, pair[1]]])
|
||||
: acc.concat([[flag, true]]);
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const result = yargsParser.detailed(args, {
|
||||
configuration,
|
||||
configObjects,
|
||||
default: defaultValues,
|
||||
coerce: coerceOpts,
|
||||
narg: nargOpts,
|
||||
alias: aliases,
|
||||
string: types.string,
|
||||
array: types.array,
|
||||
number: types.number,
|
||||
boolean: types.boolean.concat(nodeArgs.map(pair => pair[0]))
|
||||
});
|
||||
if (result.error) {
|
||||
console.error(ansi.red(`Error: ${result.error.message}`));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// reapply "=" arg values from above
|
||||
nodeArgs.forEach(([key, value]) => {
|
||||
result.argv[key] = value;
|
||||
});
|
||||
|
||||
return result.argv;
|
||||
};
|
||||
|
||||
/**
|
||||
* Given path to config file in `args.config`, attempt to load & parse config file.
|
||||
* @param {Object} [args] - Arguments object
|
||||
* @param {string|boolean} [args.config] - Path to config file or `false` to skip
|
||||
* @public
|
||||
* @alias module:lib/cli.loadRc
|
||||
* @returns {external:yargsParser.Arguments|void} Parsed config, or nothing if `args.config` is `false`
|
||||
*/
|
||||
const loadRc = (args = {}) => {
|
||||
if (args.config !== false) {
|
||||
const config = args.config || findConfig();
|
||||
return config ? loadConfig(config) : {};
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.loadRc = loadRc;
|
||||
|
||||
/**
|
||||
* Given path to `package.json` in `args.package`, attempt to load config from `mocha` prop.
|
||||
* @param {Object} [args] - Arguments object
|
||||
* @param {string|boolean} [args.config] - Path to `package.json` or `false` to skip
|
||||
* @public
|
||||
* @alias module:lib/cli.loadPkgRc
|
||||
* @returns {external:yargsParser.Arguments|void} Parsed config, or nothing if `args.package` is `false`
|
||||
*/
|
||||
const loadPkgRc = (args = {}) => {
|
||||
let result;
|
||||
if (args.package === false) {
|
||||
return result;
|
||||
}
|
||||
result = {};
|
||||
const filepath = args.package || findUp.sync(mocharc.package);
|
||||
if (filepath) {
|
||||
try {
|
||||
const pkg = JSON.parse(fs.readFileSync(filepath, 'utf8'));
|
||||
if (pkg.mocha) {
|
||||
debug('`mocha` prop of package.json parsed: %O', pkg.mocha);
|
||||
result = pkg.mocha;
|
||||
} else {
|
||||
debug('no config found in %s', filepath);
|
||||
}
|
||||
} catch (err) {
|
||||
if (args.package) {
|
||||
throw createUnparsableFileError(
|
||||
`Unable to read/parse ${filepath}: ${err}`,
|
||||
filepath
|
||||
);
|
||||
}
|
||||
debug('failed to read default package.json at %s; ignoring', filepath);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
module.exports.loadPkgRc = loadPkgRc;
|
||||
|
||||
/**
|
||||
* Priority list:
|
||||
*
|
||||
* 1. Command-line args
|
||||
* 2. RC file (`.mocharc.c?js`, `.mocharc.ya?ml`, `mocharc.json`)
|
||||
* 3. `mocha` prop of `package.json`
|
||||
* 4. default configuration (`lib/mocharc.json`)
|
||||
*
|
||||
* If a {@link module:lib/cli/one-and-dones.ONE_AND_DONE_ARGS "one-and-done" option} is present in the `argv` array, no external config files will be read.
|
||||
* @summary Parses options read from `.mocharc.*` and `package.json`.
|
||||
* @param {string|string[]} [argv] - Arguments to parse
|
||||
* @public
|
||||
* @alias module:lib/cli.loadOptions
|
||||
* @returns {external:yargsParser.Arguments} Parsed args from everything
|
||||
*/
|
||||
const loadOptions = (argv = []) => {
|
||||
let args = parse(argv);
|
||||
// short-circuit: look for a flag that would abort loading of options
|
||||
if (
|
||||
Array.from(ONE_AND_DONE_ARGS).reduce(
|
||||
(acc, arg) => acc || arg in args,
|
||||
false
|
||||
)
|
||||
) {
|
||||
return args;
|
||||
}
|
||||
|
||||
const rcConfig = loadRc(args);
|
||||
const pkgConfig = loadPkgRc(args);
|
||||
|
||||
if (rcConfig) {
|
||||
args.config = false;
|
||||
args._ = args._.concat(rcConfig._ || []);
|
||||
}
|
||||
if (pkgConfig) {
|
||||
args.package = false;
|
||||
args._ = args._.concat(pkgConfig._ || []);
|
||||
}
|
||||
|
||||
args = parse(args._, mocharc, args, rcConfig || {}, pkgConfig || {});
|
||||
|
||||
// recombine positional arguments and "spec"
|
||||
if (args.spec) {
|
||||
args._ = args._.concat(args.spec);
|
||||
delete args.spec;
|
||||
}
|
||||
|
||||
// make unique
|
||||
args._ = Array.from(new Set(args._));
|
||||
|
||||
return args;
|
||||
};
|
||||
|
||||
module.exports.loadOptions = loadOptions;
|
||||
module.exports.YARGS_PARSER_CONFIG = YARGS_PARSER_CONFIG;
|
243
node_modules/mocha/lib/cli/run-helpers.js
generated
vendored
Normal file
243
node_modules/mocha/lib/cli/run-helpers.js
generated
vendored
Normal file
@@ -0,0 +1,243 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Helper scripts for the `run` command
|
||||
* @see module:lib/cli/run
|
||||
* @module
|
||||
* @private
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const debug = require('debug')('mocha:cli:run:helpers');
|
||||
const {watchRun, watchParallelRun} = require('./watch-run');
|
||||
const collectFiles = require('./collect-files');
|
||||
const {format} = require('util');
|
||||
const {createInvalidLegacyPluginError} = require('../errors');
|
||||
const {requireOrImport} = require('../esm-utils');
|
||||
const PluginLoader = require('../plugin-loader');
|
||||
|
||||
/**
|
||||
* Exits Mocha when tests + code under test has finished execution (default)
|
||||
* @param {number} code - Exit code; typically # of failures
|
||||
* @ignore
|
||||
* @private
|
||||
*/
|
||||
const exitMochaLater = code => {
|
||||
process.on('exit', () => {
|
||||
process.exitCode = Math.min(code, 255);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Exits Mocha when Mocha itself has finished execution, regardless of
|
||||
* what the tests or code under test is doing.
|
||||
* @param {number} code - Exit code; typically # of failures
|
||||
* @ignore
|
||||
* @private
|
||||
*/
|
||||
const exitMocha = code => {
|
||||
const clampedCode = Math.min(code, 255);
|
||||
let draining = 0;
|
||||
|
||||
// Eagerly set the process's exit code in case stream.write doesn't
|
||||
// execute its callback before the process terminates.
|
||||
process.exitCode = clampedCode;
|
||||
|
||||
// flush output for Node.js Windows pipe bug
|
||||
// https://github.com/joyent/node/issues/6247 is just one bug example
|
||||
// https://github.com/visionmedia/mocha/issues/333 has a good discussion
|
||||
const done = () => {
|
||||
if (!draining--) {
|
||||
process.exit(clampedCode);
|
||||
}
|
||||
};
|
||||
|
||||
const streams = [process.stdout, process.stderr];
|
||||
|
||||
streams.forEach(stream => {
|
||||
// submit empty write request and wait for completion
|
||||
draining += 1;
|
||||
stream.write('', done);
|
||||
});
|
||||
|
||||
done();
|
||||
};
|
||||
|
||||
/**
|
||||
* Coerce a comma-delimited string (or array thereof) into a flattened array of
|
||||
* strings
|
||||
* @param {string|string[]} str - Value to coerce
|
||||
* @returns {string[]} Array of strings
|
||||
* @private
|
||||
*/
|
||||
exports.list = str =>
|
||||
Array.isArray(str) ? exports.list(str.join(',')) : str.split(/ *, */);
|
||||
|
||||
/**
|
||||
* `require()` the modules as required by `--require <require>`.
|
||||
*
|
||||
* Returns array of `mochaHooks` exports, if any.
|
||||
* @param {string[]} requires - Modules to require
|
||||
* @returns {Promise<object>} Plugin implementations
|
||||
* @private
|
||||
*/
|
||||
exports.handleRequires = async (requires = [], {ignoredPlugins = []} = {}) => {
|
||||
const pluginLoader = PluginLoader.create({ignore: ignoredPlugins});
|
||||
for await (const mod of requires) {
|
||||
let modpath = mod;
|
||||
// this is relative to cwd
|
||||
if (fs.existsSync(mod) || fs.existsSync(`${mod}.js`)) {
|
||||
modpath = path.resolve(mod);
|
||||
debug('resolved required file %s to %s', mod, modpath);
|
||||
}
|
||||
const requiredModule = await requireOrImport(modpath);
|
||||
if (requiredModule && typeof requiredModule === 'object') {
|
||||
if (pluginLoader.load(requiredModule)) {
|
||||
debug('found one or more plugin implementations in %s', modpath);
|
||||
}
|
||||
}
|
||||
debug('loaded required module "%s"', mod);
|
||||
}
|
||||
const plugins = await pluginLoader.finalize();
|
||||
if (Object.keys(plugins).length) {
|
||||
debug('finalized plugin implementations: %O', plugins);
|
||||
}
|
||||
return plugins;
|
||||
};
|
||||
|
||||
/**
|
||||
* Collect and load test files, then run mocha instance.
|
||||
* @param {Mocha} mocha - Mocha instance
|
||||
* @param {Options} [opts] - Command line options
|
||||
* @param {boolean} [opts.exit] - Whether or not to force-exit after tests are complete
|
||||
* @param {Object} fileCollectParams - Parameters that control test
|
||||
* file collection. See `lib/cli/collect-files.js`.
|
||||
* @returns {Promise<Runner>}
|
||||
* @private
|
||||
*/
|
||||
const singleRun = async (mocha, {exit}, fileCollectParams) => {
|
||||
const files = collectFiles(fileCollectParams);
|
||||
debug('single run with %d file(s)', files.length);
|
||||
mocha.files = files;
|
||||
|
||||
// handles ESM modules
|
||||
await mocha.loadFilesAsync();
|
||||
return mocha.run(exit ? exitMocha : exitMochaLater);
|
||||
};
|
||||
|
||||
/**
|
||||
* Collect files and run tests (using `BufferedRunner`).
|
||||
*
|
||||
* This is `async` for consistency.
|
||||
*
|
||||
* @param {Mocha} mocha - Mocha instance
|
||||
* @param {Options} options - Command line options
|
||||
* @param {Object} fileCollectParams - Parameters that control test
|
||||
* file collection. See `lib/cli/collect-files.js`.
|
||||
* @returns {Promise<BufferedRunner>}
|
||||
* @ignore
|
||||
* @private
|
||||
*/
|
||||
const parallelRun = async (mocha, options, fileCollectParams) => {
|
||||
const files = collectFiles(fileCollectParams);
|
||||
debug('executing %d test file(s) in parallel mode', files.length);
|
||||
mocha.files = files;
|
||||
|
||||
// note that we DO NOT load any files here; this is handled by the worker
|
||||
return mocha.run(options.exit ? exitMocha : exitMochaLater);
|
||||
};
|
||||
|
||||
/**
|
||||
* Actually run tests. Delegates to one of four different functions:
|
||||
* - `singleRun`: run tests in serial & exit
|
||||
* - `watchRun`: run tests in serial, rerunning as files change
|
||||
* - `parallelRun`: run tests in parallel & exit
|
||||
* - `watchParallelRun`: run tests in parallel, rerunning as files change
|
||||
* @param {Mocha} mocha - Mocha instance
|
||||
* @param {Options} opts - Command line options
|
||||
* @private
|
||||
* @returns {Promise<Runner>}
|
||||
*/
|
||||
exports.runMocha = async (mocha, options) => {
|
||||
const {
|
||||
watch = false,
|
||||
extension = [],
|
||||
ignore = [],
|
||||
file = [],
|
||||
parallel = false,
|
||||
recursive = false,
|
||||
sort = false,
|
||||
spec = []
|
||||
} = options;
|
||||
|
||||
const fileCollectParams = {
|
||||
ignore,
|
||||
extension,
|
||||
file,
|
||||
recursive,
|
||||
sort,
|
||||
spec
|
||||
};
|
||||
|
||||
let run;
|
||||
if (watch) {
|
||||
run = parallel ? watchParallelRun : watchRun;
|
||||
} else {
|
||||
run = parallel ? parallelRun : singleRun;
|
||||
}
|
||||
|
||||
return run(mocha, options, fileCollectParams);
|
||||
};
|
||||
|
||||
/**
|
||||
* Used for `--reporter` and `--ui`. Ensures there's only one, and asserts that
|
||||
* it actually exists. This must be run _after_ requires are processed (see
|
||||
* {@link handleRequires}), as it'll prevent interfaces from loading otherwise.
|
||||
* @param {Object} opts - Options object
|
||||
* @param {"reporter"|"interface"} pluginType - Type of plugin.
|
||||
* @param {Object} [map] - An object perhaps having key `key`. Used as a cache
|
||||
* of sorts; `Mocha.reporters` is one, where each key corresponds to a reporter
|
||||
* name
|
||||
* @private
|
||||
*/
|
||||
exports.validateLegacyPlugin = (opts, pluginType, map = {}) => {
|
||||
/**
|
||||
* This should be a unique identifier; either a string (present in `map`),
|
||||
* or a resolvable (via `require.resolve`) module ID/path.
|
||||
* @type {string}
|
||||
*/
|
||||
const pluginId = opts[pluginType];
|
||||
|
||||
if (Array.isArray(pluginId)) {
|
||||
throw createInvalidLegacyPluginError(
|
||||
`"--${pluginType}" can only be specified once`,
|
||||
pluginType
|
||||
);
|
||||
}
|
||||
|
||||
const createUnknownError = err =>
|
||||
createInvalidLegacyPluginError(
|
||||
format('Could not load %s "%s":\n\n %O', pluginType, pluginId, err),
|
||||
pluginType,
|
||||
pluginId
|
||||
);
|
||||
|
||||
// if this exists, then it's already loaded, so nothing more to do.
|
||||
if (!map[pluginId]) {
|
||||
try {
|
||||
opts[pluginType] = require(pluginId);
|
||||
} catch (err) {
|
||||
if (err.code === 'MODULE_NOT_FOUND') {
|
||||
// Try to load reporters from a path (absolute or relative)
|
||||
try {
|
||||
opts[pluginType] = require(path.resolve(pluginId));
|
||||
} catch (err) {
|
||||
throw createUnknownError(err);
|
||||
}
|
||||
} else {
|
||||
throw createUnknownError(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
113
node_modules/mocha/lib/cli/run-option-metadata.js
generated
vendored
Normal file
113
node_modules/mocha/lib/cli/run-option-metadata.js
generated
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Metadata about various options of the `run` command
|
||||
* @see module:lib/cli/run
|
||||
* @module
|
||||
* @private
|
||||
*/
|
||||
|
||||
/**
|
||||
* Dictionary of yargs option types to list of options having said type
|
||||
* @type {{string:string[]}}
|
||||
* @private
|
||||
*/
|
||||
const TYPES = (exports.types = {
|
||||
array: [
|
||||
'extension',
|
||||
'file',
|
||||
'global',
|
||||
'ignore',
|
||||
'reporter-option',
|
||||
'require',
|
||||
'spec',
|
||||
'watch-files',
|
||||
'watch-ignore'
|
||||
],
|
||||
boolean: [
|
||||
'allow-uncaught',
|
||||
'async-only',
|
||||
'bail',
|
||||
'check-leaks',
|
||||
'color',
|
||||
'delay',
|
||||
'diff',
|
||||
'exit',
|
||||
'forbid-only',
|
||||
'forbid-pending',
|
||||
'full-trace',
|
||||
'growl',
|
||||
'inline-diffs',
|
||||
'invert',
|
||||
'list-interfaces',
|
||||
'list-reporters',
|
||||
'no-colors',
|
||||
'parallel',
|
||||
'recursive',
|
||||
'sort',
|
||||
'watch'
|
||||
],
|
||||
number: ['retries', 'jobs'],
|
||||
string: [
|
||||
'config',
|
||||
'fgrep',
|
||||
'grep',
|
||||
'package',
|
||||
'reporter',
|
||||
'ui',
|
||||
'slow',
|
||||
'timeout'
|
||||
]
|
||||
});
|
||||
|
||||
/**
|
||||
* Option aliases keyed by canonical option name.
|
||||
* Arrays used to reduce
|
||||
* @type {{string:string[]}}
|
||||
* @private
|
||||
*/
|
||||
exports.aliases = {
|
||||
'async-only': ['A'],
|
||||
bail: ['b'],
|
||||
color: ['c', 'colors'],
|
||||
fgrep: ['f'],
|
||||
global: ['globals'],
|
||||
grep: ['g'],
|
||||
growl: ['G'],
|
||||
ignore: ['exclude'],
|
||||
invert: ['i'],
|
||||
jobs: ['j'],
|
||||
'no-colors': ['C'],
|
||||
parallel: ['p'],
|
||||
reporter: ['R'],
|
||||
'reporter-option': ['reporter-options', 'O'],
|
||||
require: ['r'],
|
||||
slow: ['s'],
|
||||
sort: ['S'],
|
||||
timeout: ['t', 'timeouts'],
|
||||
ui: ['u'],
|
||||
watch: ['w']
|
||||
};
|
||||
|
||||
const ALL_MOCHA_FLAGS = Object.keys(TYPES).reduce((acc, key) => {
|
||||
// gets all flags from each of the fields in `types`, adds those,
|
||||
// then adds aliases of each flag (if any)
|
||||
TYPES[key].forEach(flag => {
|
||||
acc.add(flag);
|
||||
const aliases = exports.aliases[flag] || [];
|
||||
aliases.forEach(alias => {
|
||||
acc.add(alias);
|
||||
});
|
||||
});
|
||||
return acc;
|
||||
}, new Set());
|
||||
|
||||
/**
|
||||
* Returns `true` if the provided `flag` is known to Mocha.
|
||||
* @param {string} flag - Flag to check
|
||||
* @returns {boolean} If `true`, this is a Mocha flag
|
||||
* @private
|
||||
*/
|
||||
exports.isMochaFlag = flag => {
|
||||
return ALL_MOCHA_FLAGS.has(flag.replace(/^--?/, ''));
|
||||
};
|
367
node_modules/mocha/lib/cli/run.js
generated
vendored
Normal file
367
node_modules/mocha/lib/cli/run.js
generated
vendored
Normal file
@@ -0,0 +1,367 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Definition for Mocha's default ("run tests") command
|
||||
*
|
||||
* @module
|
||||
* @private
|
||||
*/
|
||||
|
||||
const symbols = require('log-symbols');
|
||||
const ansi = require('ansi-colors');
|
||||
const Mocha = require('../mocha');
|
||||
const {
|
||||
createUnsupportedError,
|
||||
createInvalidArgumentValueError,
|
||||
createMissingArgumentError
|
||||
} = require('../errors');
|
||||
|
||||
const {
|
||||
list,
|
||||
handleRequires,
|
||||
validateLegacyPlugin,
|
||||
runMocha
|
||||
} = require('./run-helpers');
|
||||
const {ONE_AND_DONES, ONE_AND_DONE_ARGS} = require('./one-and-dones');
|
||||
const debug = require('debug')('mocha:cli:run');
|
||||
const defaults = require('../mocharc');
|
||||
const {types, aliases} = require('./run-option-metadata');
|
||||
|
||||
/**
|
||||
* Logical option groups
|
||||
* @constant
|
||||
*/
|
||||
const GROUPS = {
|
||||
FILES: 'File Handling',
|
||||
FILTERS: 'Test Filters',
|
||||
NODEJS: 'Node.js & V8',
|
||||
OUTPUT: 'Reporting & Output',
|
||||
RULES: 'Rules & Behavior',
|
||||
CONFIG: 'Configuration'
|
||||
};
|
||||
|
||||
exports.command = ['$0 [spec..]', 'inspect'];
|
||||
|
||||
exports.describe = 'Run tests with Mocha';
|
||||
|
||||
exports.builder = yargs =>
|
||||
yargs
|
||||
.options({
|
||||
'allow-uncaught': {
|
||||
description: 'Allow uncaught errors to propagate',
|
||||
group: GROUPS.RULES
|
||||
},
|
||||
'async-only': {
|
||||
description:
|
||||
'Require all tests to use a callback (async) or return a Promise',
|
||||
group: GROUPS.RULES
|
||||
},
|
||||
bail: {
|
||||
description: 'Abort ("bail") after first test failure',
|
||||
group: GROUPS.RULES
|
||||
},
|
||||
'check-leaks': {
|
||||
description: 'Check for global variable leaks',
|
||||
group: GROUPS.RULES
|
||||
},
|
||||
color: {
|
||||
description: 'Force-enable color output',
|
||||
group: GROUPS.OUTPUT
|
||||
},
|
||||
config: {
|
||||
config: true,
|
||||
defaultDescription: '(nearest rc file)',
|
||||
description: 'Path to config file',
|
||||
group: GROUPS.CONFIG
|
||||
},
|
||||
delay: {
|
||||
description: 'Delay initial execution of root suite',
|
||||
group: GROUPS.RULES
|
||||
},
|
||||
diff: {
|
||||
default: true,
|
||||
description: 'Show diff on failure',
|
||||
group: GROUPS.OUTPUT
|
||||
},
|
||||
exit: {
|
||||
description: 'Force Mocha to quit after tests complete',
|
||||
group: GROUPS.RULES
|
||||
},
|
||||
extension: {
|
||||
default: defaults.extension,
|
||||
description: 'File extension(s) to load',
|
||||
group: GROUPS.FILES,
|
||||
requiresArg: true,
|
||||
coerce: list
|
||||
},
|
||||
fgrep: {
|
||||
conflicts: 'grep',
|
||||
description: 'Only run tests containing this string',
|
||||
group: GROUPS.FILTERS,
|
||||
requiresArg: true
|
||||
},
|
||||
file: {
|
||||
defaultDescription: '(none)',
|
||||
description:
|
||||
'Specify file(s) to be loaded prior to root suite execution',
|
||||
group: GROUPS.FILES,
|
||||
normalize: true,
|
||||
requiresArg: true
|
||||
},
|
||||
'forbid-only': {
|
||||
description: 'Fail if exclusive test(s) encountered',
|
||||
group: GROUPS.RULES
|
||||
},
|
||||
'forbid-pending': {
|
||||
description: 'Fail if pending test(s) encountered',
|
||||
group: GROUPS.RULES
|
||||
},
|
||||
'full-trace': {
|
||||
description: 'Display full stack traces',
|
||||
group: GROUPS.OUTPUT
|
||||
},
|
||||
global: {
|
||||
coerce: list,
|
||||
description: 'List of allowed global variables',
|
||||
group: GROUPS.RULES,
|
||||
requiresArg: true
|
||||
},
|
||||
grep: {
|
||||
coerce: value => (!value ? null : value),
|
||||
conflicts: 'fgrep',
|
||||
description: 'Only run tests matching this string or regexp',
|
||||
group: GROUPS.FILTERS,
|
||||
requiresArg: true
|
||||
},
|
||||
growl: {
|
||||
description: 'Enable Growl notifications',
|
||||
group: GROUPS.OUTPUT
|
||||
},
|
||||
ignore: {
|
||||
defaultDescription: '(none)',
|
||||
description: 'Ignore file(s) or glob pattern(s)',
|
||||
group: GROUPS.FILES,
|
||||
requiresArg: true
|
||||
},
|
||||
'inline-diffs': {
|
||||
description:
|
||||
'Display actual/expected differences inline within each string',
|
||||
group: GROUPS.OUTPUT
|
||||
},
|
||||
invert: {
|
||||
description: 'Inverts --grep and --fgrep matches',
|
||||
group: GROUPS.FILTERS
|
||||
},
|
||||
jobs: {
|
||||
description:
|
||||
'Number of concurrent jobs for --parallel; use 1 to run in serial',
|
||||
defaultDescription: '(number of CPU cores - 1)',
|
||||
requiresArg: true,
|
||||
group: GROUPS.RULES
|
||||
},
|
||||
'list-interfaces': {
|
||||
conflicts: Array.from(ONE_AND_DONE_ARGS),
|
||||
description: 'List built-in user interfaces & exit'
|
||||
},
|
||||
'list-reporters': {
|
||||
conflicts: Array.from(ONE_AND_DONE_ARGS),
|
||||
description: 'List built-in reporters & exit'
|
||||
},
|
||||
'no-colors': {
|
||||
description: 'Force-disable color output',
|
||||
group: GROUPS.OUTPUT,
|
||||
hidden: true
|
||||
},
|
||||
package: {
|
||||
description: 'Path to package.json for config',
|
||||
group: GROUPS.CONFIG,
|
||||
normalize: true,
|
||||
requiresArg: true
|
||||
},
|
||||
parallel: {
|
||||
description: 'Run tests in parallel',
|
||||
group: GROUPS.RULES
|
||||
},
|
||||
recursive: {
|
||||
description: 'Look for tests in subdirectories',
|
||||
group: GROUPS.FILES
|
||||
},
|
||||
reporter: {
|
||||
default: defaults.reporter,
|
||||
description: 'Specify reporter to use',
|
||||
group: GROUPS.OUTPUT,
|
||||
requiresArg: true
|
||||
},
|
||||
'reporter-option': {
|
||||
coerce: opts =>
|
||||
list(opts).reduce((acc, opt) => {
|
||||
const pair = opt.split('=');
|
||||
|
||||
if (pair.length > 2 || !pair.length) {
|
||||
throw createInvalidArgumentValueError(
|
||||
`invalid reporter option '${opt}'`,
|
||||
'--reporter-option',
|
||||
opt,
|
||||
'expected "key=value" format'
|
||||
);
|
||||
}
|
||||
|
||||
acc[pair[0]] = pair.length === 2 ? pair[1] : true;
|
||||
return acc;
|
||||
}, {}),
|
||||
description: 'Reporter-specific options (<k=v,[k1=v1,..]>)',
|
||||
group: GROUPS.OUTPUT,
|
||||
requiresArg: true
|
||||
},
|
||||
require: {
|
||||
defaultDescription: '(none)',
|
||||
description: 'Require module',
|
||||
group: GROUPS.FILES,
|
||||
requiresArg: true
|
||||
},
|
||||
retries: {
|
||||
description: 'Retry failed tests this many times',
|
||||
group: GROUPS.RULES
|
||||
},
|
||||
slow: {
|
||||
default: defaults.slow,
|
||||
description: 'Specify "slow" test threshold (in milliseconds)',
|
||||
group: GROUPS.RULES
|
||||
},
|
||||
sort: {
|
||||
description: 'Sort test files',
|
||||
group: GROUPS.FILES
|
||||
},
|
||||
timeout: {
|
||||
default: defaults.timeout,
|
||||
description: 'Specify test timeout threshold (in milliseconds)',
|
||||
group: GROUPS.RULES
|
||||
},
|
||||
ui: {
|
||||
default: defaults.ui,
|
||||
description: 'Specify user interface',
|
||||
group: GROUPS.RULES,
|
||||
requiresArg: true
|
||||
},
|
||||
watch: {
|
||||
description: 'Watch files in the current working directory for changes',
|
||||
group: GROUPS.FILES
|
||||
},
|
||||
'watch-files': {
|
||||
description: 'List of paths or globs to watch',
|
||||
group: GROUPS.FILES,
|
||||
requiresArg: true,
|
||||
coerce: list
|
||||
},
|
||||
'watch-ignore': {
|
||||
description: 'List of paths or globs to exclude from watching',
|
||||
group: GROUPS.FILES,
|
||||
requiresArg: true,
|
||||
coerce: list,
|
||||
default: defaults['watch-ignore']
|
||||
}
|
||||
})
|
||||
.positional('spec', {
|
||||
default: ['test'],
|
||||
description: 'One or more files, directories, or globs to test',
|
||||
type: 'array'
|
||||
})
|
||||
.check(argv => {
|
||||
// "one-and-dones"; let yargs handle help and version
|
||||
Object.keys(ONE_AND_DONES).forEach(opt => {
|
||||
if (argv[opt]) {
|
||||
ONE_AND_DONES[opt].call(null, yargs);
|
||||
process.exit();
|
||||
}
|
||||
});
|
||||
|
||||
// yargs.implies() isn't flexible enough to handle this
|
||||
if (argv.invert && !('fgrep' in argv || 'grep' in argv)) {
|
||||
throw createMissingArgumentError(
|
||||
'"--invert" requires one of "--fgrep <str>" or "--grep <regexp>"',
|
||||
'--fgrep|--grep',
|
||||
'string|regexp'
|
||||
);
|
||||
}
|
||||
|
||||
if (argv.parallel) {
|
||||
// yargs.conflicts() can't deal with `--file foo.js --no-parallel`, either
|
||||
if (argv.file) {
|
||||
throw createUnsupportedError(
|
||||
'--parallel runs test files in a non-deterministic order, and is mutually exclusive with --file'
|
||||
);
|
||||
}
|
||||
|
||||
// or this
|
||||
if (argv.sort) {
|
||||
throw createUnsupportedError(
|
||||
'--parallel runs test files in a non-deterministic order, and is mutually exclusive with --sort'
|
||||
);
|
||||
}
|
||||
|
||||
if (argv.reporter === 'progress') {
|
||||
throw createUnsupportedError(
|
||||
'--reporter=progress is mutually exclusive with --parallel'
|
||||
);
|
||||
}
|
||||
|
||||
if (argv.reporter === 'markdown') {
|
||||
throw createUnsupportedError(
|
||||
'--reporter=markdown is mutually exclusive with --parallel'
|
||||
);
|
||||
}
|
||||
|
||||
if (argv.reporter === 'json-stream') {
|
||||
throw createUnsupportedError(
|
||||
'--reporter=json-stream is mutually exclusive with --parallel'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (argv.compilers) {
|
||||
throw createUnsupportedError(
|
||||
`--compilers is DEPRECATED and no longer supported.
|
||||
See https://git.io/vdcSr for migration information.`
|
||||
);
|
||||
}
|
||||
|
||||
if (argv.opts) {
|
||||
throw createUnsupportedError(
|
||||
`--opts: configuring Mocha via 'mocha.opts' is DEPRECATED and no longer supported.
|
||||
Please use a configuration file instead.`
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
.middleware(async (argv, yargs) => {
|
||||
// currently a failing middleware does not work nicely with yargs' `fail()`.
|
||||
try {
|
||||
// load requires first, because it can impact "plugin" validation
|
||||
const plugins = await handleRequires(argv.require);
|
||||
validateLegacyPlugin(argv, 'reporter', Mocha.reporters);
|
||||
validateLegacyPlugin(argv, 'ui', Mocha.interfaces);
|
||||
Object.assign(argv, plugins);
|
||||
} catch (err) {
|
||||
// this could be a bad --require, bad reporter, ui, etc.
|
||||
console.error(`\n${symbols.error} ${ansi.red('ERROR:')}`, err);
|
||||
yargs.exit(1);
|
||||
}
|
||||
})
|
||||
.array(types.array)
|
||||
.boolean(types.boolean)
|
||||
.string(types.string)
|
||||
.number(types.number)
|
||||
.alias(aliases);
|
||||
|
||||
exports.handler = async function(argv) {
|
||||
debug('post-yargs config', argv);
|
||||
const mocha = new Mocha(argv);
|
||||
|
||||
try {
|
||||
await runMocha(mocha, argv);
|
||||
} catch (err) {
|
||||
console.error('\n' + (err.stack || `Error: ${err.message || err}`));
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
380
node_modules/mocha/lib/cli/watch-run.js
generated
vendored
Normal file
380
node_modules/mocha/lib/cli/watch-run.js
generated
vendored
Normal file
@@ -0,0 +1,380 @@
|
||||
'use strict';
|
||||
|
||||
const logSymbols = require('log-symbols');
|
||||
const debug = require('debug')('mocha:cli:watch');
|
||||
const path = require('path');
|
||||
const chokidar = require('chokidar');
|
||||
const Context = require('../context');
|
||||
const collectFiles = require('./collect-files');
|
||||
|
||||
/**
|
||||
* Exports the `watchRun` function that runs mocha in "watch" mode.
|
||||
* @see module:lib/cli/run-helpers
|
||||
* @module
|
||||
* @private
|
||||
*/
|
||||
|
||||
/**
|
||||
* Run Mocha in parallel "watch" mode
|
||||
* @param {Mocha} mocha - Mocha instance
|
||||
* @param {Object} opts - Options
|
||||
* @param {string[]} [opts.watchFiles] - List of paths and patterns to
|
||||
* watch. If not provided all files with an extension included in
|
||||
* `fileCollectionParams.extension` are watched. See first argument of
|
||||
* `chokidar.watch`.
|
||||
* @param {string[]} opts.watchIgnore - List of paths and patterns to
|
||||
* exclude from watching. See `ignored` option of `chokidar`.
|
||||
* @param {FileCollectionOptions} fileCollectParams - Parameters that control test
|
||||
* @private
|
||||
*/
|
||||
exports.watchParallelRun = (
|
||||
mocha,
|
||||
{watchFiles, watchIgnore},
|
||||
fileCollectParams
|
||||
) => {
|
||||
debug('creating parallel watcher');
|
||||
|
||||
return createWatcher(mocha, {
|
||||
watchFiles,
|
||||
watchIgnore,
|
||||
beforeRun({mocha}) {
|
||||
// I don't know why we're cloning the root suite.
|
||||
const rootSuite = mocha.suite.clone();
|
||||
|
||||
// ensure we aren't leaking event listeners
|
||||
mocha.dispose();
|
||||
|
||||
// this `require` is needed because the require cache has been cleared. the dynamic
|
||||
// exports set via the below call to `mocha.ui()` won't work properly if a
|
||||
// test depends on this module (see `required-tokens.spec.js`).
|
||||
const Mocha = require('../mocha');
|
||||
|
||||
// ... and now that we've gotten a new module, we need to use it again due
|
||||
// to `mocha.ui()` call
|
||||
const newMocha = new Mocha(mocha.options);
|
||||
// don't know why this is needed
|
||||
newMocha.suite = rootSuite;
|
||||
// nor this
|
||||
newMocha.suite.ctx = new Context();
|
||||
|
||||
// reset the list of files
|
||||
newMocha.files = collectFiles(fileCollectParams);
|
||||
|
||||
// because we've swapped out the root suite (see the `run` inner function
|
||||
// in `createRerunner`), we need to call `mocha.ui()` again to set up the context/globals.
|
||||
newMocha.ui(newMocha.options.ui);
|
||||
|
||||
// we need to call `newMocha.rootHooks` to set up rootHooks for the new
|
||||
// suite
|
||||
newMocha.rootHooks(newMocha.options.rootHooks);
|
||||
|
||||
// in parallel mode, the main Mocha process doesn't actually load the
|
||||
// files. this flag prevents `mocha.run()` from autoloading.
|
||||
newMocha.lazyLoadFiles(true);
|
||||
return newMocha;
|
||||
},
|
||||
fileCollectParams
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Run Mocha in "watch" mode
|
||||
* @param {Mocha} mocha - Mocha instance
|
||||
* @param {Object} opts - Options
|
||||
* @param {string[]} [opts.watchFiles] - List of paths and patterns to
|
||||
* watch. If not provided all files with an extension included in
|
||||
* `fileCollectionParams.extension` are watched. See first argument of
|
||||
* `chokidar.watch`.
|
||||
* @param {string[]} opts.watchIgnore - List of paths and patterns to
|
||||
* exclude from watching. See `ignored` option of `chokidar`.
|
||||
* @param {FileCollectionOptions} fileCollectParams - Parameters that control test
|
||||
* file collection. See `lib/cli/collect-files.js`.
|
||||
* @private
|
||||
*/
|
||||
exports.watchRun = (mocha, {watchFiles, watchIgnore}, fileCollectParams) => {
|
||||
debug('creating serial watcher');
|
||||
|
||||
return createWatcher(mocha, {
|
||||
watchFiles,
|
||||
watchIgnore,
|
||||
beforeRun({mocha}) {
|
||||
mocha.unloadFiles();
|
||||
|
||||
// I don't know why we're cloning the root suite.
|
||||
const rootSuite = mocha.suite.clone();
|
||||
|
||||
// ensure we aren't leaking event listeners
|
||||
mocha.dispose();
|
||||
|
||||
// this `require` is needed because the require cache has been cleared. the dynamic
|
||||
// exports set via the below call to `mocha.ui()` won't work properly if a
|
||||
// test depends on this module (see `required-tokens.spec.js`).
|
||||
const Mocha = require('../mocha');
|
||||
|
||||
// ... and now that we've gotten a new module, we need to use it again due
|
||||
// to `mocha.ui()` call
|
||||
const newMocha = new Mocha(mocha.options);
|
||||
// don't know why this is needed
|
||||
newMocha.suite = rootSuite;
|
||||
// nor this
|
||||
newMocha.suite.ctx = new Context();
|
||||
|
||||
// reset the list of files
|
||||
newMocha.files = collectFiles(fileCollectParams);
|
||||
|
||||
// because we've swapped out the root suite (see the `run` inner function
|
||||
// in `createRerunner`), we need to call `mocha.ui()` again to set up the context/globals.
|
||||
newMocha.ui(newMocha.options.ui);
|
||||
|
||||
// we need to call `newMocha.rootHooks` to set up rootHooks for the new
|
||||
// suite
|
||||
newMocha.rootHooks(newMocha.options.rootHooks);
|
||||
|
||||
return newMocha;
|
||||
},
|
||||
fileCollectParams
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Bootstraps a chokidar watcher. Handles keyboard input & signals
|
||||
* @param {Mocha} mocha - Mocha instance
|
||||
* @param {Object} opts
|
||||
* @param {BeforeWatchRun} [opts.beforeRun] - Function to call before
|
||||
* `mocha.run()`
|
||||
* @param {string[]} [opts.watchFiles] - List of paths and patterns to watch. If
|
||||
* not provided all files with an extension included in
|
||||
* `fileCollectionParams.extension` are watched. See first argument of
|
||||
* `chokidar.watch`.
|
||||
* @param {string[]} [opts.watchIgnore] - List of paths and patterns to exclude
|
||||
* from watching. See `ignored` option of `chokidar`.
|
||||
* @param {FileCollectionOptions} opts.fileCollectParams - List of extensions to watch if `opts.watchFiles` is not given.
|
||||
* @returns {FSWatcher}
|
||||
* @ignore
|
||||
* @private
|
||||
*/
|
||||
const createWatcher = (
|
||||
mocha,
|
||||
{watchFiles, watchIgnore, beforeRun, fileCollectParams}
|
||||
) => {
|
||||
if (!watchFiles) {
|
||||
watchFiles = fileCollectParams.extension.map(ext => `**/*.${ext}`);
|
||||
}
|
||||
|
||||
debug('ignoring files matching: %s', watchIgnore);
|
||||
let globalFixtureContext;
|
||||
|
||||
// we handle global fixtures manually
|
||||
mocha.enableGlobalSetup(false).enableGlobalTeardown(false);
|
||||
|
||||
const watcher = chokidar.watch(watchFiles, {
|
||||
ignored: watchIgnore,
|
||||
ignoreInitial: true
|
||||
});
|
||||
|
||||
const rerunner = createRerunner(mocha, watcher, {
|
||||
beforeRun
|
||||
});
|
||||
|
||||
watcher.on('ready', async () => {
|
||||
if (!globalFixtureContext) {
|
||||
debug('triggering global setup');
|
||||
globalFixtureContext = await mocha.runGlobalSetup();
|
||||
}
|
||||
rerunner.run();
|
||||
});
|
||||
|
||||
watcher.on('all', () => {
|
||||
rerunner.scheduleRun();
|
||||
});
|
||||
|
||||
hideCursor();
|
||||
process.on('exit', () => {
|
||||
showCursor();
|
||||
});
|
||||
|
||||
// this is for testing.
|
||||
// win32 cannot gracefully shutdown via a signal from a parent
|
||||
// process; a `SIGINT` from a parent will cause the process
|
||||
// to immediately exit. during normal course of operation, a user
|
||||
// will type Ctrl-C and the listener will be invoked, but this
|
||||
// is not possible in automated testing.
|
||||
// there may be another way to solve this, but it too will be a hack.
|
||||
// for our watch tests on win32 we must _fork_ mocha with an IPC channel
|
||||
if (process.connected) {
|
||||
process.on('message', msg => {
|
||||
if (msg === 'SIGINT') {
|
||||
process.emit('SIGINT');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let exiting = false;
|
||||
process.on('SIGINT', async () => {
|
||||
showCursor();
|
||||
console.error(`${logSymbols.warning} [mocha] cleaning up, please wait...`);
|
||||
if (!exiting) {
|
||||
exiting = true;
|
||||
if (mocha.hasGlobalTeardownFixtures()) {
|
||||
debug('running global teardown');
|
||||
try {
|
||||
await mocha.runGlobalTeardown(globalFixtureContext);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
process.exit(130);
|
||||
}
|
||||
});
|
||||
|
||||
// Keyboard shortcut for restarting when "rs\n" is typed (ala Nodemon)
|
||||
process.stdin.resume();
|
||||
process.stdin.setEncoding('utf8');
|
||||
process.stdin.on('data', data => {
|
||||
const str = data
|
||||
.toString()
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
if (str === 'rs') rerunner.scheduleRun();
|
||||
});
|
||||
|
||||
return watcher;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an object that allows you to rerun tests on the mocha instance.
|
||||
*
|
||||
* @param {Mocha} mocha - Mocha instance
|
||||
* @param {FSWatcher} watcher - chokidar `FSWatcher` instance
|
||||
* @param {Object} [opts] - Options!
|
||||
* @param {BeforeWatchRun} [opts.beforeRun] - Function to call before `mocha.run()`
|
||||
* @returns {Rerunner}
|
||||
* @ignore
|
||||
* @private
|
||||
*/
|
||||
const createRerunner = (mocha, watcher, {beforeRun} = {}) => {
|
||||
// Set to a `Runner` when mocha is running. Set to `null` when mocha is not
|
||||
// running.
|
||||
let runner = null;
|
||||
|
||||
// true if a file has changed during a test run
|
||||
let rerunScheduled = false;
|
||||
|
||||
const run = () => {
|
||||
try {
|
||||
mocha = beforeRun ? beforeRun({mocha, watcher}) || mocha : mocha;
|
||||
runner = mocha.run(() => {
|
||||
debug('finished watch run');
|
||||
runner = null;
|
||||
blastCache(watcher);
|
||||
if (rerunScheduled) {
|
||||
rerun();
|
||||
} else {
|
||||
console.error(`${logSymbols.info} [mocha] waiting for changes...`);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e.stack);
|
||||
}
|
||||
};
|
||||
|
||||
const scheduleRun = () => {
|
||||
if (rerunScheduled) {
|
||||
return;
|
||||
}
|
||||
|
||||
rerunScheduled = true;
|
||||
if (runner) {
|
||||
runner.abort();
|
||||
} else {
|
||||
rerun();
|
||||
}
|
||||
};
|
||||
|
||||
const rerun = () => {
|
||||
rerunScheduled = false;
|
||||
eraseLine();
|
||||
run();
|
||||
};
|
||||
|
||||
return {
|
||||
scheduleRun,
|
||||
run
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the list of absolute paths watched by a chokidar watcher.
|
||||
*
|
||||
* @param watcher - Instance of a chokidar watcher
|
||||
* @return {string[]} - List of absolute paths
|
||||
* @ignore
|
||||
* @private
|
||||
*/
|
||||
const getWatchedFiles = watcher => {
|
||||
const watchedDirs = watcher.getWatched();
|
||||
return Object.keys(watchedDirs).reduce(
|
||||
(acc, dir) => [
|
||||
...acc,
|
||||
...watchedDirs[dir].map(file => path.join(dir, file))
|
||||
],
|
||||
[]
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Hide the cursor.
|
||||
* @ignore
|
||||
* @private
|
||||
*/
|
||||
const hideCursor = () => {
|
||||
process.stdout.write('\u001b[?25l');
|
||||
};
|
||||
|
||||
/**
|
||||
* Show the cursor.
|
||||
* @ignore
|
||||
* @private
|
||||
*/
|
||||
const showCursor = () => {
|
||||
process.stdout.write('\u001b[?25h');
|
||||
};
|
||||
|
||||
/**
|
||||
* Erases the line on stdout
|
||||
* @private
|
||||
*/
|
||||
const eraseLine = () => {
|
||||
process.stdout.write('\u001b[2K');
|
||||
};
|
||||
|
||||
/**
|
||||
* Blast all of the watched files out of `require.cache`
|
||||
* @param {FSWatcher} watcher - chokidar FSWatcher
|
||||
* @ignore
|
||||
* @private
|
||||
*/
|
||||
const blastCache = watcher => {
|
||||
const files = getWatchedFiles(watcher);
|
||||
files.forEach(file => {
|
||||
delete require.cache[file];
|
||||
});
|
||||
debug('deleted %d file(s) from the require cache', files.length);
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback to be run before `mocha.run()` is called.
|
||||
* Optionally, it can return a new `Mocha` instance.
|
||||
* @callback BeforeWatchRun
|
||||
* @private
|
||||
* @param {{mocha: Mocha, watcher: FSWatcher}} options
|
||||
* @returns {Mocha}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Object containing run control methods
|
||||
* @typedef {Object} Rerunner
|
||||
* @private
|
||||
* @property {Function} run - Calls `mocha.run()`
|
||||
* @property {Function} scheduleRun - Schedules another call to `run`
|
||||
*/
|
86
node_modules/mocha/lib/context.js
generated
vendored
Normal file
86
node_modules/mocha/lib/context.js
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
'use strict';
|
||||
/**
|
||||
* @module Context
|
||||
*/
|
||||
/**
|
||||
* Expose `Context`.
|
||||
*/
|
||||
|
||||
module.exports = Context;
|
||||
|
||||
/**
|
||||
* Initialize a new `Context`.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function Context() {}
|
||||
|
||||
/**
|
||||
* Set or get the context `Runnable` to `runnable`.
|
||||
*
|
||||
* @private
|
||||
* @param {Runnable} runnable
|
||||
* @return {Context} context
|
||||
*/
|
||||
Context.prototype.runnable = function(runnable) {
|
||||
if (!arguments.length) {
|
||||
return this._runnable;
|
||||
}
|
||||
this.test = this._runnable = runnable;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set or get test timeout `ms`.
|
||||
*
|
||||
* @private
|
||||
* @param {number} ms
|
||||
* @return {Context} self
|
||||
*/
|
||||
Context.prototype.timeout = function(ms) {
|
||||
if (!arguments.length) {
|
||||
return this.runnable().timeout();
|
||||
}
|
||||
this.runnable().timeout(ms);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set or get test slowness threshold `ms`.
|
||||
*
|
||||
* @private
|
||||
* @param {number} ms
|
||||
* @return {Context} self
|
||||
*/
|
||||
Context.prototype.slow = function(ms) {
|
||||
if (!arguments.length) {
|
||||
return this.runnable().slow();
|
||||
}
|
||||
this.runnable().slow(ms);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Mark a test as skipped.
|
||||
*
|
||||
* @private
|
||||
* @throws Pending
|
||||
*/
|
||||
Context.prototype.skip = function() {
|
||||
this.runnable().skip();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set or get a number of allowed retries on failed tests
|
||||
*
|
||||
* @private
|
||||
* @param {number} n
|
||||
* @return {Context} self
|
||||
*/
|
||||
Context.prototype.retries = function(n) {
|
||||
if (!arguments.length) {
|
||||
return this.runnable().retries();
|
||||
}
|
||||
this.runnable().retries(n);
|
||||
return this;
|
||||
};
|
563
node_modules/mocha/lib/errors.js
generated
vendored
Normal file
563
node_modules/mocha/lib/errors.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
58
node_modules/mocha/lib/esm-utils.js
generated
vendored
Normal file
58
node_modules/mocha/lib/esm-utils.js
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
const path = require('path');
|
||||
const url = require('url');
|
||||
|
||||
const formattedImport = async file => {
|
||||
if (path.isAbsolute(file)) {
|
||||
try {
|
||||
return await import(url.pathToFileURL(file));
|
||||
} catch (err) {
|
||||
// This is a hack created because ESM in Node.js (at least in Node v15.5.1) does not emit
|
||||
// the location of the syntax error in the error thrown.
|
||||
// This is problematic because the user can't see what file has the problem,
|
||||
// so we add the file location to the error.
|
||||
// This `if` should be removed once Node.js fixes the problem.
|
||||
if (
|
||||
err instanceof SyntaxError &&
|
||||
err.message &&
|
||||
err.stack &&
|
||||
!err.stack.includes(file)
|
||||
) {
|
||||
const newErrorWithFilename = new SyntaxError(err.message);
|
||||
newErrorWithFilename.stack = err.stack.replace(
|
||||
/^SyntaxError/,
|
||||
`SyntaxError[ @${file} ]`
|
||||
);
|
||||
throw newErrorWithFilename;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
return import(file);
|
||||
};
|
||||
|
||||
exports.requireOrImport = async file => {
|
||||
if (path.extname(file) === '.mjs') {
|
||||
return formattedImport(file);
|
||||
}
|
||||
// This is currently the only known way of figuring out whether a file is CJS or ESM.
|
||||
// If Node.js or the community establish a better procedure for that, we can fix this code.
|
||||
// Another option here would be to always use `import()`, as this also supports CJS, but I would be
|
||||
// wary of using it for _all_ existing test files, till ESM is fully stable.
|
||||
try {
|
||||
return require(file);
|
||||
} catch (err) {
|
||||
if (err.code === 'ERR_REQUIRE_ESM') {
|
||||
return formattedImport(file);
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
exports.loadFilesAsync = async (files, preLoadFunc, postLoadFunc) => {
|
||||
for (const file of files) {
|
||||
preLoadFunc(file);
|
||||
const result = await exports.requireOrImport(path.resolve(file));
|
||||
postLoadFunc(file, result);
|
||||
}
|
||||
};
|
83
node_modules/mocha/lib/hook.js
generated
vendored
Normal file
83
node_modules/mocha/lib/hook.js
generated
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
'use strict';
|
||||
|
||||
var Runnable = require('./runnable');
|
||||
const {inherits, constants} = require('./utils');
|
||||
const {MOCHA_ID_PROP_NAME} = constants;
|
||||
|
||||
/**
|
||||
* Expose `Hook`.
|
||||
*/
|
||||
|
||||
module.exports = Hook;
|
||||
|
||||
/**
|
||||
* Initialize a new `Hook` with the given `title` and callback `fn`
|
||||
*
|
||||
* @class
|
||||
* @extends Runnable
|
||||
* @param {String} title
|
||||
* @param {Function} fn
|
||||
*/
|
||||
function Hook(title, fn) {
|
||||
Runnable.call(this, title, fn);
|
||||
this.type = 'hook';
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherit from `Runnable.prototype`.
|
||||
*/
|
||||
inherits(Hook, Runnable);
|
||||
|
||||
/**
|
||||
* Resets the state for a next run.
|
||||
*/
|
||||
Hook.prototype.reset = function() {
|
||||
Runnable.prototype.reset.call(this);
|
||||
delete this._error;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get or set the test `err`.
|
||||
*
|
||||
* @memberof Hook
|
||||
* @public
|
||||
* @param {Error} err
|
||||
* @return {Error}
|
||||
*/
|
||||
Hook.prototype.error = function(err) {
|
||||
if (!arguments.length) {
|
||||
err = this._error;
|
||||
this._error = null;
|
||||
return err;
|
||||
}
|
||||
|
||||
this._error = err;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns an object suitable for IPC.
|
||||
* Functions are represented by keys beginning with `$$`.
|
||||
* @private
|
||||
* @returns {Object}
|
||||
*/
|
||||
Hook.prototype.serialize = function serialize() {
|
||||
return {
|
||||
$$isPending: this.isPending(),
|
||||
$$titlePath: this.titlePath(),
|
||||
ctx:
|
||||
this.ctx && this.ctx.currentTest
|
||||
? {
|
||||
currentTest: {
|
||||
title: this.ctx.currentTest.title,
|
||||
[MOCHA_ID_PROP_NAME]: this.ctx.currentTest.id
|
||||
}
|
||||
}
|
||||
: {},
|
||||
parent: {
|
||||
[MOCHA_ID_PROP_NAME]: this.parent.id
|
||||
},
|
||||
title: this.title,
|
||||
type: this.type,
|
||||
[MOCHA_ID_PROP_NAME]: this.id
|
||||
};
|
||||
};
|
111
node_modules/mocha/lib/interfaces/bdd.js
generated
vendored
Normal file
111
node_modules/mocha/lib/interfaces/bdd.js
generated
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
'use strict';
|
||||
|
||||
var Test = require('../test');
|
||||
var EVENT_FILE_PRE_REQUIRE = require('../suite').constants
|
||||
.EVENT_FILE_PRE_REQUIRE;
|
||||
|
||||
/**
|
||||
* BDD-style interface:
|
||||
*
|
||||
* describe('Array', function() {
|
||||
* describe('#indexOf()', function() {
|
||||
* it('should return -1 when not present', function() {
|
||||
* // ...
|
||||
* });
|
||||
*
|
||||
* it('should return the index when present', function() {
|
||||
* // ...
|
||||
* });
|
||||
* });
|
||||
* });
|
||||
*
|
||||
* @param {Suite} suite Root suite.
|
||||
*/
|
||||
module.exports = function bddInterface(suite) {
|
||||
var suites = [suite];
|
||||
|
||||
suite.on(EVENT_FILE_PRE_REQUIRE, function(context, file, mocha) {
|
||||
var common = require('./common')(suites, context, mocha);
|
||||
|
||||
context.before = common.before;
|
||||
context.after = common.after;
|
||||
context.beforeEach = common.beforeEach;
|
||||
context.afterEach = common.afterEach;
|
||||
context.run = mocha.options.delay && common.runWithSuite(suite);
|
||||
/**
|
||||
* Describe a "suite" with the given `title`
|
||||
* and callback `fn` containing nested suites
|
||||
* and/or tests.
|
||||
*/
|
||||
|
||||
context.describe = context.context = function(title, fn) {
|
||||
return common.suite.create({
|
||||
title: title,
|
||||
file: file,
|
||||
fn: fn
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Pending describe.
|
||||
*/
|
||||
|
||||
context.xdescribe = context.xcontext = context.describe.skip = function(
|
||||
title,
|
||||
fn
|
||||
) {
|
||||
return common.suite.skip({
|
||||
title: title,
|
||||
file: file,
|
||||
fn: fn
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Exclusive suite.
|
||||
*/
|
||||
|
||||
context.describe.only = function(title, fn) {
|
||||
return common.suite.only({
|
||||
title: title,
|
||||
file: file,
|
||||
fn: fn
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Describe a specification or test-case
|
||||
* with the given `title` and callback `fn`
|
||||
* acting as a thunk.
|
||||
*/
|
||||
|
||||
context.it = context.specify = function(title, fn) {
|
||||
var suite = suites[0];
|
||||
if (suite.isPending()) {
|
||||
fn = null;
|
||||
}
|
||||
var test = new Test(title, fn);
|
||||
test.file = file;
|
||||
suite.addTest(test);
|
||||
return test;
|
||||
};
|
||||
|
||||
/**
|
||||
* Exclusive test-case.
|
||||
*/
|
||||
|
||||
context.it.only = function(title, fn) {
|
||||
return common.test.only(mocha, context.it(title, fn));
|
||||
};
|
||||
|
||||
/**
|
||||
* Pending test case.
|
||||
*/
|
||||
|
||||
context.xit = context.xspecify = context.it.skip = function(title) {
|
||||
return context.it(title);
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.description = 'BDD or RSpec style [default]';
|
193
node_modules/mocha/lib/interfaces/common.js
generated
vendored
Normal file
193
node_modules/mocha/lib/interfaces/common.js
generated
vendored
Normal file
@@ -0,0 +1,193 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
@module interfaces/common
|
||||
*/
|
||||
|
||||
var Suite = require('../suite');
|
||||
var errors = require('../errors');
|
||||
var createMissingArgumentError = errors.createMissingArgumentError;
|
||||
var createUnsupportedError = errors.createUnsupportedError;
|
||||
var createForbiddenExclusivityError = errors.createForbiddenExclusivityError;
|
||||
|
||||
/**
|
||||
* Functions common to more than one interface.
|
||||
*
|
||||
* @private
|
||||
* @param {Suite[]} suites
|
||||
* @param {Context} context
|
||||
* @param {Mocha} mocha
|
||||
* @return {Object} An object containing common functions.
|
||||
*/
|
||||
module.exports = function(suites, context, mocha) {
|
||||
/**
|
||||
* Check if the suite should be tested.
|
||||
*
|
||||
* @private
|
||||
* @param {Suite} suite - suite to check
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function shouldBeTested(suite) {
|
||||
return (
|
||||
!mocha.options.grep ||
|
||||
(mocha.options.grep &&
|
||||
mocha.options.grep.test(suite.fullTitle()) &&
|
||||
!mocha.options.invert)
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
/**
|
||||
* This is only present if flag --delay is passed into Mocha. It triggers
|
||||
* root suite execution.
|
||||
*
|
||||
* @param {Suite} suite The root suite.
|
||||
* @return {Function} A function which runs the root suite
|
||||
*/
|
||||
runWithSuite: function runWithSuite(suite) {
|
||||
return function run() {
|
||||
suite.run();
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Execute before running tests.
|
||||
*
|
||||
* @param {string} name
|
||||
* @param {Function} fn
|
||||
*/
|
||||
before: function(name, fn) {
|
||||
suites[0].beforeAll(name, fn);
|
||||
},
|
||||
|
||||
/**
|
||||
* Execute after running tests.
|
||||
*
|
||||
* @param {string} name
|
||||
* @param {Function} fn
|
||||
*/
|
||||
after: function(name, fn) {
|
||||
suites[0].afterAll(name, fn);
|
||||
},
|
||||
|
||||
/**
|
||||
* Execute before each test case.
|
||||
*
|
||||
* @param {string} name
|
||||
* @param {Function} fn
|
||||
*/
|
||||
beforeEach: function(name, fn) {
|
||||
suites[0].beforeEach(name, fn);
|
||||
},
|
||||
|
||||
/**
|
||||
* Execute after each test case.
|
||||
*
|
||||
* @param {string} name
|
||||
* @param {Function} fn
|
||||
*/
|
||||
afterEach: function(name, fn) {
|
||||
suites[0].afterEach(name, fn);
|
||||
},
|
||||
|
||||
suite: {
|
||||
/**
|
||||
* Create an exclusive Suite; convenience function
|
||||
* See docstring for create() below.
|
||||
*
|
||||
* @param {Object} opts
|
||||
* @returns {Suite}
|
||||
*/
|
||||
only: function only(opts) {
|
||||
if (mocha.options.forbidOnly) {
|
||||
throw createForbiddenExclusivityError(mocha);
|
||||
}
|
||||
opts.isOnly = true;
|
||||
return this.create(opts);
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a Suite, but skip it; convenience function
|
||||
* See docstring for create() below.
|
||||
*
|
||||
* @param {Object} opts
|
||||
* @returns {Suite}
|
||||
*/
|
||||
skip: function skip(opts) {
|
||||
opts.pending = true;
|
||||
return this.create(opts);
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a suite.
|
||||
*
|
||||
* @param {Object} opts Options
|
||||
* @param {string} opts.title Title of Suite
|
||||
* @param {Function} [opts.fn] Suite Function (not always applicable)
|
||||
* @param {boolean} [opts.pending] Is Suite pending?
|
||||
* @param {string} [opts.file] Filepath where this Suite resides
|
||||
* @param {boolean} [opts.isOnly] Is Suite exclusive?
|
||||
* @returns {Suite}
|
||||
*/
|
||||
create: function create(opts) {
|
||||
var suite = Suite.create(suites[0], opts.title);
|
||||
suite.pending = Boolean(opts.pending);
|
||||
suite.file = opts.file;
|
||||
suites.unshift(suite);
|
||||
if (opts.isOnly) {
|
||||
suite.markOnly();
|
||||
}
|
||||
if (
|
||||
suite.pending &&
|
||||
mocha.options.forbidPending &&
|
||||
shouldBeTested(suite)
|
||||
) {
|
||||
throw createUnsupportedError('Pending test forbidden');
|
||||
}
|
||||
if (typeof opts.fn === 'function') {
|
||||
opts.fn.call(suite);
|
||||
suites.shift();
|
||||
} else if (typeof opts.fn === 'undefined' && !suite.pending) {
|
||||
throw createMissingArgumentError(
|
||||
'Suite "' +
|
||||
suite.fullTitle() +
|
||||
'" was defined but no callback was supplied. ' +
|
||||
'Supply a callback or explicitly skip the suite.',
|
||||
'callback',
|
||||
'function'
|
||||
);
|
||||
} else if (!opts.fn && suite.pending) {
|
||||
suites.shift();
|
||||
}
|
||||
|
||||
return suite;
|
||||
}
|
||||
},
|
||||
|
||||
test: {
|
||||
/**
|
||||
* Exclusive test-case.
|
||||
*
|
||||
* @param {Object} mocha
|
||||
* @param {Function} test
|
||||
* @returns {*}
|
||||
*/
|
||||
only: function(mocha, test) {
|
||||
if (mocha.options.forbidOnly) {
|
||||
throw createForbiddenExclusivityError(mocha);
|
||||
}
|
||||
test.markOnly();
|
||||
return test;
|
||||
},
|
||||
|
||||
/**
|
||||
* Pending test case.
|
||||
*
|
||||
* @param {string} title
|
||||
*/
|
||||
skip: function(title) {
|
||||
context.test(title);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
60
node_modules/mocha/lib/interfaces/exports.js
generated
vendored
Normal file
60
node_modules/mocha/lib/interfaces/exports.js
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
'use strict';
|
||||
var Suite = require('../suite');
|
||||
var Test = require('../test');
|
||||
|
||||
/**
|
||||
* Exports-style (as Node.js module) interface:
|
||||
*
|
||||
* exports.Array = {
|
||||
* '#indexOf()': {
|
||||
* 'should return -1 when the value is not present': function() {
|
||||
*
|
||||
* },
|
||||
*
|
||||
* 'should return the correct index when the value is present': function() {
|
||||
*
|
||||
* }
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* @param {Suite} suite Root suite.
|
||||
*/
|
||||
module.exports = function(suite) {
|
||||
var suites = [suite];
|
||||
|
||||
suite.on(Suite.constants.EVENT_FILE_REQUIRE, visit);
|
||||
|
||||
function visit(obj, file) {
|
||||
var suite;
|
||||
for (var key in obj) {
|
||||
if (typeof obj[key] === 'function') {
|
||||
var fn = obj[key];
|
||||
switch (key) {
|
||||
case 'before':
|
||||
suites[0].beforeAll(fn);
|
||||
break;
|
||||
case 'after':
|
||||
suites[0].afterAll(fn);
|
||||
break;
|
||||
case 'beforeEach':
|
||||
suites[0].beforeEach(fn);
|
||||
break;
|
||||
case 'afterEach':
|
||||
suites[0].afterEach(fn);
|
||||
break;
|
||||
default:
|
||||
var test = new Test(key, fn);
|
||||
test.file = file;
|
||||
suites[0].addTest(test);
|
||||
}
|
||||
} else {
|
||||
suite = Suite.create(suites[0], key);
|
||||
suites.unshift(suite);
|
||||
visit(obj[key], file);
|
||||
suites.shift();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.description = 'Node.js module ("exports") style';
|
6
node_modules/mocha/lib/interfaces/index.js
generated
vendored
Normal file
6
node_modules/mocha/lib/interfaces/index.js
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
exports.bdd = require('./bdd');
|
||||
exports.tdd = require('./tdd');
|
||||
exports.qunit = require('./qunit');
|
||||
exports.exports = require('./exports');
|
98
node_modules/mocha/lib/interfaces/qunit.js
generated
vendored
Normal file
98
node_modules/mocha/lib/interfaces/qunit.js
generated
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
'use strict';
|
||||
|
||||
var Test = require('../test');
|
||||
var EVENT_FILE_PRE_REQUIRE = require('../suite').constants
|
||||
.EVENT_FILE_PRE_REQUIRE;
|
||||
|
||||
/**
|
||||
* QUnit-style interface:
|
||||
*
|
||||
* suite('Array');
|
||||
*
|
||||
* test('#length', function() {
|
||||
* var arr = [1,2,3];
|
||||
* ok(arr.length == 3);
|
||||
* });
|
||||
*
|
||||
* test('#indexOf()', function() {
|
||||
* var arr = [1,2,3];
|
||||
* ok(arr.indexOf(1) == 0);
|
||||
* ok(arr.indexOf(2) == 1);
|
||||
* ok(arr.indexOf(3) == 2);
|
||||
* });
|
||||
*
|
||||
* suite('String');
|
||||
*
|
||||
* test('#length', function() {
|
||||
* ok('foo'.length == 3);
|
||||
* });
|
||||
*
|
||||
* @param {Suite} suite Root suite.
|
||||
*/
|
||||
module.exports = function qUnitInterface(suite) {
|
||||
var suites = [suite];
|
||||
|
||||
suite.on(EVENT_FILE_PRE_REQUIRE, function(context, file, mocha) {
|
||||
var common = require('./common')(suites, context, mocha);
|
||||
|
||||
context.before = common.before;
|
||||
context.after = common.after;
|
||||
context.beforeEach = common.beforeEach;
|
||||
context.afterEach = common.afterEach;
|
||||
context.run = mocha.options.delay && common.runWithSuite(suite);
|
||||
/**
|
||||
* Describe a "suite" with the given `title`.
|
||||
*/
|
||||
|
||||
context.suite = function(title) {
|
||||
if (suites.length > 1) {
|
||||
suites.shift();
|
||||
}
|
||||
return common.suite.create({
|
||||
title: title,
|
||||
file: file,
|
||||
fn: false
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Exclusive Suite.
|
||||
*/
|
||||
|
||||
context.suite.only = function(title) {
|
||||
if (suites.length > 1) {
|
||||
suites.shift();
|
||||
}
|
||||
return common.suite.only({
|
||||
title: title,
|
||||
file: file,
|
||||
fn: false
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Describe a specification or test-case
|
||||
* with the given `title` and callback `fn`
|
||||
* acting as a thunk.
|
||||
*/
|
||||
|
||||
context.test = function(title, fn) {
|
||||
var test = new Test(title, fn);
|
||||
test.file = file;
|
||||
suites[0].addTest(test);
|
||||
return test;
|
||||
};
|
||||
|
||||
/**
|
||||
* Exclusive test-case.
|
||||
*/
|
||||
|
||||
context.test.only = function(title, fn) {
|
||||
return common.test.only(mocha, context.test(title, fn));
|
||||
};
|
||||
|
||||
context.test.skip = common.test.skip;
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.description = 'QUnit style';
|
106
node_modules/mocha/lib/interfaces/tdd.js
generated
vendored
Normal file
106
node_modules/mocha/lib/interfaces/tdd.js
generated
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
'use strict';
|
||||
|
||||
var Test = require('../test');
|
||||
var EVENT_FILE_PRE_REQUIRE = require('../suite').constants
|
||||
.EVENT_FILE_PRE_REQUIRE;
|
||||
|
||||
/**
|
||||
* TDD-style interface:
|
||||
*
|
||||
* suite('Array', function() {
|
||||
* suite('#indexOf()', function() {
|
||||
* suiteSetup(function() {
|
||||
*
|
||||
* });
|
||||
*
|
||||
* test('should return -1 when not present', function() {
|
||||
*
|
||||
* });
|
||||
*
|
||||
* test('should return the index when present', function() {
|
||||
*
|
||||
* });
|
||||
*
|
||||
* suiteTeardown(function() {
|
||||
*
|
||||
* });
|
||||
* });
|
||||
* });
|
||||
*
|
||||
* @param {Suite} suite Root suite.
|
||||
*/
|
||||
module.exports = function(suite) {
|
||||
var suites = [suite];
|
||||
|
||||
suite.on(EVENT_FILE_PRE_REQUIRE, function(context, file, mocha) {
|
||||
var common = require('./common')(suites, context, mocha);
|
||||
|
||||
context.setup = common.beforeEach;
|
||||
context.teardown = common.afterEach;
|
||||
context.suiteSetup = common.before;
|
||||
context.suiteTeardown = common.after;
|
||||
context.run = mocha.options.delay && common.runWithSuite(suite);
|
||||
|
||||
/**
|
||||
* Describe a "suite" with the given `title` and callback `fn` containing
|
||||
* nested suites and/or tests.
|
||||
*/
|
||||
context.suite = function(title, fn) {
|
||||
return common.suite.create({
|
||||
title: title,
|
||||
file: file,
|
||||
fn: fn
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Pending suite.
|
||||
*/
|
||||
context.suite.skip = function(title, fn) {
|
||||
return common.suite.skip({
|
||||
title: title,
|
||||
file: file,
|
||||
fn: fn
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Exclusive test-case.
|
||||
*/
|
||||
context.suite.only = function(title, fn) {
|
||||
return common.suite.only({
|
||||
title: title,
|
||||
file: file,
|
||||
fn: fn
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Describe a specification or test-case with the given `title` and
|
||||
* callback `fn` acting as a thunk.
|
||||
*/
|
||||
context.test = function(title, fn) {
|
||||
var suite = suites[0];
|
||||
if (suite.isPending()) {
|
||||
fn = null;
|
||||
}
|
||||
var test = new Test(title, fn);
|
||||
test.file = file;
|
||||
suite.addTest(test);
|
||||
return test;
|
||||
};
|
||||
|
||||
/**
|
||||
* Exclusive test-case.
|
||||
*/
|
||||
|
||||
context.test.only = function(title, fn) {
|
||||
return common.test.only(mocha, context.test(title, fn));
|
||||
};
|
||||
|
||||
context.test.skip = common.test.skip;
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.description =
|
||||
'traditional "suite"/"test" instead of BDD\'s "describe"/"it"';
|
1356
node_modules/mocha/lib/mocha.js
generated
vendored
Normal file
1356
node_modules/mocha/lib/mocha.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
10
node_modules/mocha/lib/mocharc.json
generated
vendored
Normal file
10
node_modules/mocha/lib/mocharc.json
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"diff": true,
|
||||
"extension": ["js", "cjs", "mjs"],
|
||||
"package": "./package.json",
|
||||
"reporter": "spec",
|
||||
"slow": 75,
|
||||
"timeout": 2000,
|
||||
"ui": "bdd",
|
||||
"watch-ignore": ["node_modules", ".git"]
|
||||
}
|
172
node_modules/mocha/lib/nodejs/buffered-worker-pool.js
generated
vendored
Normal file
172
node_modules/mocha/lib/nodejs/buffered-worker-pool.js
generated
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
/**
|
||||
* A wrapper around a third-party child process worker pool implementation.
|
||||
* Used by {@link module:buffered-runner}.
|
||||
* @private
|
||||
* @module buffered-worker-pool
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const serializeJavascript = require('serialize-javascript');
|
||||
const workerpool = require('workerpool');
|
||||
const {deserialize} = require('./serializer');
|
||||
const debug = require('debug')('mocha:parallel:buffered-worker-pool');
|
||||
const {createInvalidArgumentTypeError} = require('../errors');
|
||||
|
||||
const WORKER_PATH = require.resolve('./worker.js');
|
||||
|
||||
/**
|
||||
* A mapping of Mocha `Options` objects to serialized values.
|
||||
*
|
||||
* This is helpful because we tend to same the same options over and over
|
||||
* over IPC.
|
||||
* @type {WeakMap<Options,string>}
|
||||
*/
|
||||
let optionsCache = new WeakMap();
|
||||
|
||||
/**
|
||||
* These options are passed into the [workerpool](https://npm.im/workerpool) module.
|
||||
* @type {Partial<WorkerPoolOptions>}
|
||||
*/
|
||||
const WORKER_POOL_DEFAULT_OPTS = {
|
||||
// use child processes, not worker threads!
|
||||
workerType: 'process',
|
||||
// ensure the same flags sent to `node` for this `mocha` invocation are passed
|
||||
// along to children
|
||||
forkOpts: {execArgv: process.execArgv},
|
||||
maxWorkers: workerpool.cpus - 1
|
||||
};
|
||||
|
||||
/**
|
||||
* A wrapper around a third-party worker pool implementation.
|
||||
* @private
|
||||
*/
|
||||
class BufferedWorkerPool {
|
||||
/**
|
||||
* Creates an underlying worker pool instance; determines max worker count
|
||||
* @param {Partial<WorkerPoolOptions>} [opts] - Options
|
||||
*/
|
||||
constructor(opts = {}) {
|
||||
const maxWorkers = Math.max(
|
||||
1,
|
||||
typeof opts.maxWorkers === 'undefined'
|
||||
? WORKER_POOL_DEFAULT_OPTS.maxWorkers
|
||||
: opts.maxWorkers
|
||||
);
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (workerpool.cpus < 2) {
|
||||
// TODO: decide whether we should warn
|
||||
debug(
|
||||
'not enough CPU cores available to run multiple jobs; avoid --parallel on this machine'
|
||||
);
|
||||
} else if (maxWorkers >= workerpool.cpus) {
|
||||
// TODO: decide whether we should warn
|
||||
debug(
|
||||
'%d concurrent job(s) requested, but only %d core(s) available',
|
||||
maxWorkers,
|
||||
workerpool.cpus
|
||||
);
|
||||
}
|
||||
/* istanbul ignore next */
|
||||
debug(
|
||||
'run(): starting worker pool of max size %d, using node args: %s',
|
||||
maxWorkers,
|
||||
process.execArgv.join(' ')
|
||||
);
|
||||
|
||||
this.options = {...WORKER_POOL_DEFAULT_OPTS, opts, maxWorkers};
|
||||
this._pool = workerpool.pool(WORKER_PATH, this.options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminates all workers in the pool.
|
||||
* @param {boolean} [force] - Whether to force-kill workers. By default, lets workers finish their current task before termination.
|
||||
* @private
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async terminate(force = false) {
|
||||
/* istanbul ignore next */
|
||||
debug('terminate(): terminating with force = %s', force);
|
||||
return this._pool.terminate(force);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a test file run to the worker pool queue for execution by a worker process.
|
||||
*
|
||||
* Handles serialization/deserialization.
|
||||
*
|
||||
* @param {string} filepath - Filepath of test
|
||||
* @param {Options} [options] - Options for Mocha instance
|
||||
* @private
|
||||
* @returns {Promise<SerializedWorkerResult>}
|
||||
*/
|
||||
async run(filepath, options = {}) {
|
||||
if (!filepath || typeof filepath !== 'string') {
|
||||
throw createInvalidArgumentTypeError(
|
||||
'Expected a non-empty filepath',
|
||||
'filepath',
|
||||
'string'
|
||||
);
|
||||
}
|
||||
const serializedOptions = BufferedWorkerPool.serializeOptions(options);
|
||||
const result = await this._pool.exec('run', [filepath, serializedOptions]);
|
||||
return deserialize(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns stats about the state of the worker processes in the pool.
|
||||
*
|
||||
* Used for debugging.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
stats() {
|
||||
return this._pool.stats();
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a {@link WorkerPool}.
|
||||
* @private
|
||||
*/
|
||||
static create(...args) {
|
||||
return new BufferedWorkerPool(...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given Mocha options object `opts`, serialize into a format suitable for
|
||||
* transmission over IPC.
|
||||
*
|
||||
* @param {Options} [opts] - Mocha options
|
||||
* @private
|
||||
* @returns {string} Serialized options
|
||||
*/
|
||||
static serializeOptions(opts = {}) {
|
||||
if (!optionsCache.has(opts)) {
|
||||
const serialized = serializeJavascript(opts, {
|
||||
unsafe: true, // this means we don't care about XSS
|
||||
ignoreFunction: true // do not serialize functions
|
||||
});
|
||||
optionsCache.set(opts, serialized);
|
||||
/* istanbul ignore next */
|
||||
debug(
|
||||
'serializeOptions(): serialized options %O to: %s',
|
||||
opts,
|
||||
serialized
|
||||
);
|
||||
}
|
||||
return optionsCache.get(opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets internal cache of serialized options objects.
|
||||
*
|
||||
* For testing/debugging
|
||||
* @private
|
||||
*/
|
||||
static resetOptionsCache() {
|
||||
optionsCache = new WeakMap();
|
||||
}
|
||||
}
|
||||
|
||||
exports.BufferedWorkerPool = BufferedWorkerPool;
|
15
node_modules/mocha/lib/nodejs/file-unloader.js
generated
vendored
Normal file
15
node_modules/mocha/lib/nodejs/file-unloader.js
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* This module should not be in the browser bundle, so it's here.
|
||||
* @private
|
||||
* @module
|
||||
*/
|
||||
|
||||
/**
|
||||
* Deletes a file from the `require` cache.
|
||||
* @param {string} file - File
|
||||
*/
|
||||
exports.unloadFile = file => {
|
||||
delete require.cache[require.resolve(file)];
|
||||
};
|
137
node_modules/mocha/lib/nodejs/growl.js
generated
vendored
Normal file
137
node_modules/mocha/lib/nodejs/growl.js
generated
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Desktop Notifications module.
|
||||
* @module Growl
|
||||
*/
|
||||
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
const {sync: which} = require('which');
|
||||
const {EVENT_RUN_END} = require('../runner').constants;
|
||||
const {isBrowser} = require('../utils');
|
||||
|
||||
/**
|
||||
* @summary
|
||||
* Checks if Growl notification support seems likely.
|
||||
*
|
||||
* @description
|
||||
* Glosses over the distinction between an unsupported platform
|
||||
* and one that lacks prerequisite software installations.
|
||||
*
|
||||
* @public
|
||||
* @see {@link https://github.com/tj/node-growl/blob/master/README.md|Prerequisite Installs}
|
||||
* @see {@link Mocha#growl}
|
||||
* @see {@link Mocha#isGrowlCapable}
|
||||
* @return {boolean} whether Growl notification support can be expected
|
||||
*/
|
||||
exports.isCapable = () => {
|
||||
if (!isBrowser()) {
|
||||
return getSupportBinaries().reduce(
|
||||
(acc, binary) => acc || Boolean(which(binary, {nothrow: true})),
|
||||
false
|
||||
);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements desktop notifications as a pseudo-reporter.
|
||||
*
|
||||
* @public
|
||||
* @see {@link Mocha#_growl}
|
||||
* @param {Runner} runner - Runner instance.
|
||||
*/
|
||||
exports.notify = runner => {
|
||||
runner.once(EVENT_RUN_END, () => {
|
||||
display(runner);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Displays the notification.
|
||||
*
|
||||
* @private
|
||||
* @param {Runner} runner - Runner instance.
|
||||
*/
|
||||
const display = runner => {
|
||||
const growl = require('growl');
|
||||
const stats = runner.stats;
|
||||
const symbol = {
|
||||
cross: '\u274C',
|
||||
tick: '\u2705'
|
||||
};
|
||||
let _message;
|
||||
let message;
|
||||
let title;
|
||||
|
||||
if (stats.failures) {
|
||||
_message = `${stats.failures} of ${stats.tests} tests failed`;
|
||||
message = `${symbol.cross} ${_message}`;
|
||||
title = 'Failed';
|
||||
} else {
|
||||
_message = `${stats.passes} tests passed in ${stats.duration}ms`;
|
||||
message = `${symbol.tick} ${_message}`;
|
||||
title = 'Passed';
|
||||
}
|
||||
|
||||
// Send notification
|
||||
const options = {
|
||||
image: logo(),
|
||||
name: 'mocha',
|
||||
title
|
||||
};
|
||||
growl(message, options, onCompletion);
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary
|
||||
* Callback for result of attempted Growl notification.
|
||||
*
|
||||
* @description
|
||||
* Despite its appearance, this is <strong>not</strong> an Error-first
|
||||
* callback -- all parameters are populated regardless of success.
|
||||
*
|
||||
* @private
|
||||
* @callback Growl~growlCB
|
||||
* @param {*} err - Error object, or <code>null</code> if successful.
|
||||
*/
|
||||
function onCompletion(err) {
|
||||
if (err) {
|
||||
// As notifications are tangential to our purpose, just log the error.
|
||||
const message =
|
||||
err.code === 'ENOENT' ? 'prerequisite software not found' : err.message;
|
||||
console.error('notification error:', message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Mocha logo image path.
|
||||
*
|
||||
* @private
|
||||
* @return {string} Pathname of Mocha logo
|
||||
*/
|
||||
const logo = () => {
|
||||
return path.join(__dirname, '..', 'assets', 'mocha-logo-96.png');
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary
|
||||
* Gets platform-specific Growl support binaries.
|
||||
*
|
||||
* @description
|
||||
* Somewhat brittle dependency on `growl` package implementation, but it
|
||||
* rarely changes.
|
||||
*
|
||||
* @private
|
||||
* @see {@link https://github.com/tj/node-growl/blob/master/lib/growl.js#L28-L126|setupCmd}
|
||||
* @return {string[]} names of Growl support binaries
|
||||
*/
|
||||
const getSupportBinaries = () => {
|
||||
const binaries = {
|
||||
Darwin: ['terminal-notifier', 'growlnotify'],
|
||||
Linux: ['notify-send', 'growl'],
|
||||
Windows_NT: ['growlnotify.exe']
|
||||
};
|
||||
return binaries[os.type()] || [];
|
||||
};
|
433
node_modules/mocha/lib/nodejs/parallel-buffered-runner.js
generated
vendored
Normal file
433
node_modules/mocha/lib/nodejs/parallel-buffered-runner.js
generated
vendored
Normal file
@@ -0,0 +1,433 @@
|
||||
/**
|
||||
* A test Runner that uses a {@link module:buffered-worker-pool}.
|
||||
* @module parallel-buffered-runner
|
||||
* @private
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const allSettled = require('@ungap/promise-all-settled').bind(Promise);
|
||||
const Runner = require('../runner');
|
||||
const {EVENT_RUN_BEGIN, EVENT_RUN_END} = Runner.constants;
|
||||
const debug = require('debug')('mocha:parallel:parallel-buffered-runner');
|
||||
const {BufferedWorkerPool} = require('./buffered-worker-pool');
|
||||
const {setInterval, clearInterval} = global;
|
||||
const {createMap, constants} = require('../utils');
|
||||
const {MOCHA_ID_PROP_NAME} = constants;
|
||||
const {createFatalError} = require('../errors');
|
||||
|
||||
const DEFAULT_WORKER_REPORTER = require.resolve(
|
||||
'./reporters/parallel-buffered'
|
||||
);
|
||||
|
||||
/**
|
||||
* List of options to _not_ serialize for transmission to workers
|
||||
*/
|
||||
const DENY_OPTIONS = [
|
||||
'globalSetup',
|
||||
'globalTeardown',
|
||||
'parallel',
|
||||
'p',
|
||||
'jobs',
|
||||
'j'
|
||||
];
|
||||
|
||||
/**
|
||||
* Outputs a debug statement with worker stats
|
||||
* @param {BufferedWorkerPool} pool - Worker pool
|
||||
*/
|
||||
/* istanbul ignore next */
|
||||
const debugStats = pool => {
|
||||
const {totalWorkers, busyWorkers, idleWorkers, pendingTasks} = pool.stats();
|
||||
debug(
|
||||
'%d/%d busy workers; %d idle; %d tasks queued',
|
||||
busyWorkers,
|
||||
totalWorkers,
|
||||
idleWorkers,
|
||||
pendingTasks
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* The interval at which we will display stats for worker processes in debug mode
|
||||
*/
|
||||
const DEBUG_STATS_INTERVAL = 5000;
|
||||
|
||||
const ABORTED = 'ABORTED';
|
||||
const IDLE = 'IDLE';
|
||||
const ABORTING = 'ABORTING';
|
||||
const RUNNING = 'RUNNING';
|
||||
const BAILING = 'BAILING';
|
||||
const BAILED = 'BAILED';
|
||||
const COMPLETE = 'COMPLETE';
|
||||
|
||||
const states = createMap({
|
||||
[IDLE]: new Set([RUNNING, ABORTING]),
|
||||
[RUNNING]: new Set([COMPLETE, BAILING, ABORTING]),
|
||||
[COMPLETE]: new Set(),
|
||||
[ABORTED]: new Set(),
|
||||
[ABORTING]: new Set([ABORTED]),
|
||||
[BAILING]: new Set([BAILED, ABORTING]),
|
||||
[BAILED]: new Set([COMPLETE, ABORTING])
|
||||
});
|
||||
|
||||
/**
|
||||
* This `Runner` delegates tests runs to worker threads. Does not execute any
|
||||
* {@link Runnable}s by itself!
|
||||
* @public
|
||||
*/
|
||||
class ParallelBufferedRunner extends Runner {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
|
||||
let state = IDLE;
|
||||
Object.defineProperty(this, '_state', {
|
||||
get() {
|
||||
return state;
|
||||
},
|
||||
set(newState) {
|
||||
if (states[state].has(newState)) {
|
||||
state = newState;
|
||||
} else {
|
||||
throw new Error(`invalid state transition: ${state} => ${newState}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this._workerReporter = DEFAULT_WORKER_REPORTER;
|
||||
this._linkPartialObjects = false;
|
||||
this._linkedObjectMap = new Map();
|
||||
|
||||
this.once(Runner.constants.EVENT_RUN_END, () => {
|
||||
this._state = COMPLETE;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a mapping function to enqueue a file in the worker pool and return results of its execution.
|
||||
* @param {BufferedWorkerPool} pool - Worker pool
|
||||
* @param {Options} options - Mocha options
|
||||
* @returns {FileRunner} Mapping function
|
||||
* @private
|
||||
*/
|
||||
_createFileRunner(pool, options) {
|
||||
/**
|
||||
* Emits event and sets `BAILING` state, if necessary.
|
||||
* @param {Object} event - Event having `eventName`, maybe `data` and maybe `error`
|
||||
* @param {number} failureCount - Failure count
|
||||
*/
|
||||
const emitEvent = (event, failureCount) => {
|
||||
this.emit(event.eventName, event.data, event.error);
|
||||
if (
|
||||
this._state !== BAILING &&
|
||||
event.data &&
|
||||
event.data._bail &&
|
||||
(failureCount || event.error)
|
||||
) {
|
||||
debug('run(): nonzero failure count & found bail flag');
|
||||
// we need to let the events complete for this file, as the worker
|
||||
// should run any cleanup hooks
|
||||
this._state = BAILING;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Given an event, recursively find any objects in its data that have ID's, and create object references to already-seen objects.
|
||||
* @param {Object} event - Event having `eventName`, maybe `data` and maybe `error`
|
||||
*/
|
||||
const linkEvent = event => {
|
||||
const stack = [{parent: event, prop: 'data'}];
|
||||
while (stack.length) {
|
||||
const {parent, prop} = stack.pop();
|
||||
const obj = parent[prop];
|
||||
let newObj;
|
||||
if (obj && typeof obj === 'object') {
|
||||
if (obj[MOCHA_ID_PROP_NAME]) {
|
||||
const id = obj[MOCHA_ID_PROP_NAME];
|
||||
newObj = this._linkedObjectMap.has(id)
|
||||
? Object.assign(this._linkedObjectMap.get(id), obj)
|
||||
: obj;
|
||||
this._linkedObjectMap.set(id, newObj);
|
||||
parent[prop] = newObj;
|
||||
} else {
|
||||
throw createFatalError(
|
||||
'Object missing ID received in event data',
|
||||
obj
|
||||
);
|
||||
}
|
||||
}
|
||||
Object.keys(newObj).forEach(key => {
|
||||
const value = obj[key];
|
||||
if (value && typeof value === 'object' && value[MOCHA_ID_PROP_NAME]) {
|
||||
stack.push({obj: value, parent: newObj, prop: key});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return async file => {
|
||||
debug('run(): enqueueing test file %s', file);
|
||||
try {
|
||||
const {failureCount, events} = await pool.run(file, options);
|
||||
|
||||
if (this._state === BAILED) {
|
||||
// short-circuit after a graceful bail. if this happens,
|
||||
// some other worker has bailed.
|
||||
// TODO: determine if this is the desired behavior, or if we
|
||||
// should report the events of this run anyway.
|
||||
return;
|
||||
}
|
||||
debug(
|
||||
'run(): completed run of file %s; %d failures / %d events',
|
||||
file,
|
||||
failureCount,
|
||||
events.length
|
||||
);
|
||||
this.failures += failureCount; // can this ever be non-numeric?
|
||||
let event = events.shift();
|
||||
|
||||
if (this._linkPartialObjects) {
|
||||
while (event) {
|
||||
linkEvent(event);
|
||||
emitEvent(event, failureCount);
|
||||
event = events.shift();
|
||||
}
|
||||
} else {
|
||||
while (event) {
|
||||
emitEvent(event, failureCount);
|
||||
event = events.shift();
|
||||
}
|
||||
}
|
||||
if (this._state === BAILING) {
|
||||
debug('run(): terminating pool due to "bail" flag');
|
||||
this._state = BAILED;
|
||||
await pool.terminate();
|
||||
}
|
||||
} catch (err) {
|
||||
if (this._state === BAILED || this._state === ABORTING) {
|
||||
debug(
|
||||
'run(): worker pool terminated with intent; skipping file %s',
|
||||
file
|
||||
);
|
||||
} else {
|
||||
// this is an uncaught exception
|
||||
debug('run(): encountered uncaught exception: %O', err);
|
||||
if (this.allowUncaught) {
|
||||
// still have to clean up
|
||||
this._state = ABORTING;
|
||||
await pool.terminate(true);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
} finally {
|
||||
debug('run(): done running file %s', file);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen on `Process.SIGINT`; terminate pool if caught.
|
||||
* Returns the listener for later call to `process.removeListener()`.
|
||||
* @param {BufferedWorkerPool} pool - Worker pool
|
||||
* @returns {SigIntListener} Listener
|
||||
* @private
|
||||
*/
|
||||
_bindSigIntListener(pool) {
|
||||
const sigIntListener = async () => {
|
||||
debug('run(): caught a SIGINT');
|
||||
this._state = ABORTING;
|
||||
|
||||
try {
|
||||
debug('run(): force-terminating worker pool');
|
||||
await pool.terminate(true);
|
||||
} catch (err) {
|
||||
console.error(
|
||||
`Error while attempting to force-terminate worker pool: ${err}`
|
||||
);
|
||||
process.exitCode = 1;
|
||||
} finally {
|
||||
process.nextTick(() => {
|
||||
debug('run(): imminent death');
|
||||
this._state = ABORTED;
|
||||
process.kill(process.pid, 'SIGINT');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
process.once('SIGINT', sigIntListener);
|
||||
|
||||
return sigIntListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs Mocha tests by creating a thread pool, then delegating work to the
|
||||
* worker threads.
|
||||
*
|
||||
* Each worker receives one file, and as workers become available, they take a
|
||||
* file from the queue and run it. The worker thread execution is treated like
|
||||
* an RPC--it returns a `Promise` containing serialized information about the
|
||||
* run. The information is processed as it's received, and emitted to a
|
||||
* {@link Reporter}, which is likely listening for these events.
|
||||
*
|
||||
* @param {Function} callback - Called with an exit code corresponding to
|
||||
* number of test failures.
|
||||
* @param {{files: string[], options: Options}} opts - Files to run and
|
||||
* command-line options, respectively.
|
||||
*/
|
||||
run(callback, {files, options = {}} = {}) {
|
||||
/**
|
||||
* Listener on `Process.SIGINT` which tries to cleanly terminate the worker pool.
|
||||
*/
|
||||
let sigIntListener;
|
||||
|
||||
// assign the reporter the worker will use, which will be different than the
|
||||
// main process' reporter
|
||||
options = {...options, reporter: this._workerReporter};
|
||||
|
||||
// This function should _not_ return a `Promise`; its parent (`Runner#run`)
|
||||
// returns this instance, so this should do the same. However, we want to make
|
||||
// use of `async`/`await`, so we use this IIFE.
|
||||
(async () => {
|
||||
/**
|
||||
* This is an interval that outputs stats about the worker pool every so often
|
||||
*/
|
||||
let debugInterval;
|
||||
|
||||
/**
|
||||
* @type {BufferedWorkerPool}
|
||||
*/
|
||||
let pool;
|
||||
|
||||
try {
|
||||
pool = BufferedWorkerPool.create({maxWorkers: options.jobs});
|
||||
|
||||
sigIntListener = this._bindSigIntListener(pool);
|
||||
|
||||
/* istanbul ignore next */
|
||||
debugInterval = setInterval(
|
||||
() => debugStats(pool),
|
||||
DEBUG_STATS_INTERVAL
|
||||
).unref();
|
||||
|
||||
// this is set for uncaught exception handling in `Runner#uncaught`
|
||||
// TODO: `Runner` should be using a state machine instead.
|
||||
this.started = true;
|
||||
this._state = RUNNING;
|
||||
|
||||
this.emit(EVENT_RUN_BEGIN);
|
||||
|
||||
options = {...options};
|
||||
DENY_OPTIONS.forEach(opt => {
|
||||
delete options[opt];
|
||||
});
|
||||
|
||||
const results = await allSettled(
|
||||
files.map(this._createFileRunner(pool, options))
|
||||
);
|
||||
|
||||
// note that pool may already be terminated due to --bail
|
||||
await pool.terminate();
|
||||
|
||||
results
|
||||
.filter(({status}) => status === 'rejected')
|
||||
.forEach(({reason}) => {
|
||||
if (this.allowUncaught) {
|
||||
// yep, just the first one.
|
||||
throw reason;
|
||||
}
|
||||
// "rejected" will correspond to uncaught exceptions.
|
||||
// unlike the serial runner, the parallel runner can always recover.
|
||||
this.uncaught(reason);
|
||||
});
|
||||
|
||||
if (this._state === ABORTING) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.emit(EVENT_RUN_END);
|
||||
debug('run(): completing with failure count %d', this.failures);
|
||||
callback(this.failures);
|
||||
} catch (err) {
|
||||
// this `nextTick` takes us out of the `Promise` scope, so the
|
||||
// exception will not be caught and returned as a rejected `Promise`,
|
||||
// which would lead to an `unhandledRejection` event.
|
||||
process.nextTick(() => {
|
||||
debug('run(): re-throwing uncaught exception');
|
||||
throw err;
|
||||
});
|
||||
} finally {
|
||||
clearInterval(debugInterval);
|
||||
process.removeListener('SIGINT', sigIntListener);
|
||||
}
|
||||
})();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle partial object linking behavior; used for building object references from
|
||||
* unique ID's.
|
||||
* @param {boolean} [value] - If `true`, enable partial object linking, otherwise disable
|
||||
* @returns {Runner}
|
||||
* @chainable
|
||||
* @public
|
||||
* @example
|
||||
* // this reporter needs proper object references when run in parallel mode
|
||||
* class MyReporter() {
|
||||
* constructor(runner) {
|
||||
* this.runner.linkPartialObjects(true)
|
||||
* .on(EVENT_SUITE_BEGIN, suite => {
|
||||
// this Suite may be the same object...
|
||||
* })
|
||||
* .on(EVENT_TEST_BEGIN, test => {
|
||||
* // ...as the `test.parent` property
|
||||
* });
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
linkPartialObjects(value) {
|
||||
this._linkPartialObjects = Boolean(value);
|
||||
return super.linkPartialObjects(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* If this class is the `Runner` in use, then this is going to return `true`.
|
||||
*
|
||||
* For use by reporters.
|
||||
* @returns {true}
|
||||
* @public
|
||||
*/
|
||||
isParallelMode() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures an alternate reporter for worker processes to use. Subclasses
|
||||
* using worker processes should implement this.
|
||||
* @public
|
||||
* @param {string} path - Absolute path to alternate reporter for worker processes to use
|
||||
* @returns {Runner}
|
||||
* @throws When in serial mode
|
||||
* @chainable
|
||||
*/
|
||||
workerReporter(reporter) {
|
||||
this._workerReporter = reporter;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ParallelBufferedRunner;
|
||||
|
||||
/**
|
||||
* Listener function intended to be bound to `Process.SIGINT` event
|
||||
* @private
|
||||
* @callback SigIntListener
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
|
||||
/**
|
||||
* A function accepting a test file path and returning the results of a test run
|
||||
* @private
|
||||
* @callback FileRunner
|
||||
* @param {string} filename - File to run
|
||||
* @returns {Promise<SerializedWorkerResult>}
|
||||
*/
|
165
node_modules/mocha/lib/nodejs/reporters/parallel-buffered.js
generated
vendored
Normal file
165
node_modules/mocha/lib/nodejs/reporters/parallel-buffered.js
generated
vendored
Normal file
@@ -0,0 +1,165 @@
|
||||
/**
|
||||
* "Buffered" reporter used internally by a worker process when running in parallel mode.
|
||||
* @module nodejs/reporters/parallel-buffered
|
||||
* @public
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
const {
|
||||
EVENT_SUITE_BEGIN,
|
||||
EVENT_SUITE_END,
|
||||
EVENT_TEST_FAIL,
|
||||
EVENT_TEST_PASS,
|
||||
EVENT_TEST_PENDING,
|
||||
EVENT_TEST_BEGIN,
|
||||
EVENT_TEST_END,
|
||||
EVENT_TEST_RETRY,
|
||||
EVENT_DELAY_BEGIN,
|
||||
EVENT_DELAY_END,
|
||||
EVENT_HOOK_BEGIN,
|
||||
EVENT_HOOK_END,
|
||||
EVENT_RUN_END
|
||||
} = require('../../runner').constants;
|
||||
const {SerializableEvent, SerializableWorkerResult} = require('../serializer');
|
||||
const debug = require('debug')('mocha:reporters:buffered');
|
||||
const Base = require('../../reporters/base');
|
||||
|
||||
/**
|
||||
* List of events to listen to; these will be buffered and sent
|
||||
* when `Mocha#run` is complete (via {@link ParallelBuffered#done}).
|
||||
*/
|
||||
const EVENT_NAMES = [
|
||||
EVENT_SUITE_BEGIN,
|
||||
EVENT_SUITE_END,
|
||||
EVENT_TEST_BEGIN,
|
||||
EVENT_TEST_PENDING,
|
||||
EVENT_TEST_FAIL,
|
||||
EVENT_TEST_PASS,
|
||||
EVENT_TEST_RETRY,
|
||||
EVENT_TEST_END,
|
||||
EVENT_HOOK_BEGIN,
|
||||
EVENT_HOOK_END
|
||||
];
|
||||
|
||||
/**
|
||||
* Like {@link EVENT_NAMES}, except we expect these events to only be emitted
|
||||
* by the `Runner` once.
|
||||
*/
|
||||
const ONCE_EVENT_NAMES = [EVENT_DELAY_BEGIN, EVENT_DELAY_END];
|
||||
|
||||
/**
|
||||
* The `ParallelBuffered` reporter is used by each worker process in "parallel"
|
||||
* mode, by default. Instead of reporting to to `STDOUT`, etc., it retains a
|
||||
* list of events it receives and hands these off to the callback passed into
|
||||
* {@link Mocha#run}. That callback will then return the data to the main
|
||||
* process.
|
||||
* @public
|
||||
*/
|
||||
class ParallelBuffered extends Base {
|
||||
/**
|
||||
* Calls {@link ParallelBuffered#createListeners}
|
||||
* @param {Runner} runner
|
||||
*/
|
||||
constructor(runner, opts) {
|
||||
super(runner, opts);
|
||||
|
||||
/**
|
||||
* Retained list of events emitted from the {@link Runner} instance.
|
||||
* @type {BufferedEvent[]}
|
||||
* @public
|
||||
*/
|
||||
this.events = [];
|
||||
|
||||
/**
|
||||
* Map of `Runner` event names to listeners (for later teardown)
|
||||
* @public
|
||||
* @type {Map<string,EventListener>}
|
||||
*/
|
||||
this.listeners = new Map();
|
||||
|
||||
this.createListeners(runner);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new listener which saves event data in memory to
|
||||
* {@link ParallelBuffered#events}. Listeners are indexed by `eventName` and stored
|
||||
* in {@link ParallelBuffered#listeners}. This is a defensive measure, so that we
|
||||
* don't a) leak memory or b) remove _other_ listeners that may not be
|
||||
* associated with this reporter.
|
||||
*
|
||||
* Subclasses could override this behavior.
|
||||
*
|
||||
* @public
|
||||
* @param {string} eventName - Name of event to create listener for
|
||||
* @returns {EventListener}
|
||||
*/
|
||||
createListener(eventName) {
|
||||
const listener = (runnable, err) => {
|
||||
this.events.push(SerializableEvent.create(eventName, runnable, err));
|
||||
};
|
||||
return this.listeners.set(eventName, listener).get(eventName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates event listeners (using {@link ParallelBuffered#createListener}) for each
|
||||
* reporter-relevant event emitted by a {@link Runner}. This array is drained when
|
||||
* {@link ParallelBuffered#done} is called by {@link Runner#run}.
|
||||
*
|
||||
* Subclasses could override this behavior.
|
||||
* @public
|
||||
* @param {Runner} runner - Runner instance
|
||||
* @returns {ParallelBuffered}
|
||||
* @chainable
|
||||
*/
|
||||
createListeners(runner) {
|
||||
EVENT_NAMES.forEach(evt => {
|
||||
runner.on(evt, this.createListener(evt));
|
||||
});
|
||||
ONCE_EVENT_NAMES.forEach(evt => {
|
||||
runner.once(evt, this.createListener(evt));
|
||||
});
|
||||
|
||||
runner.once(EVENT_RUN_END, () => {
|
||||
debug('received EVENT_RUN_END');
|
||||
this.listeners.forEach((listener, evt) => {
|
||||
runner.removeListener(evt, listener);
|
||||
this.listeners.delete(evt);
|
||||
});
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the {@link Mocha#run} callback (`callback`) with the test failure
|
||||
* count and the array of {@link BufferedEvent} objects. Resets the array.
|
||||
*
|
||||
* This is called directly by `Runner#run` and should not be called by any other consumer.
|
||||
*
|
||||
* Subclasses could override this.
|
||||
*
|
||||
* @param {number} failures - Number of failed tests
|
||||
* @param {Function} callback - The callback passed to {@link Mocha#run}.
|
||||
* @public
|
||||
*/
|
||||
done(failures, callback) {
|
||||
callback(SerializableWorkerResult.create(this.events, failures));
|
||||
this.events = []; // defensive
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializable event data from a `Runner`. Keys of the `data` property
|
||||
* beginning with `__` will be converted into a function which returns the value
|
||||
* upon deserialization.
|
||||
* @typedef {Object} BufferedEvent
|
||||
* @property {string} name - Event name
|
||||
* @property {object} data - Event parameters
|
||||
*/
|
||||
|
||||
module.exports = ParallelBuffered;
|
412
node_modules/mocha/lib/nodejs/serializer.js
generated
vendored
Normal file
412
node_modules/mocha/lib/nodejs/serializer.js
generated
vendored
Normal file
@@ -0,0 +1,412 @@
|
||||
/**
|
||||
* Serialization/deserialization classes and functions for communication between a main Mocha process and worker processes.
|
||||
* @module serializer
|
||||
* @private
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const {type} = require('../utils');
|
||||
const {createInvalidArgumentTypeError} = require('../errors');
|
||||
// this is not named `mocha:parallel:serializer` because it's noisy and it's
|
||||
// helpful to be able to write `DEBUG=mocha:parallel*` and get everything else.
|
||||
const debug = require('debug')('mocha:serializer');
|
||||
|
||||
const SERIALIZABLE_RESULT_NAME = 'SerializableWorkerResult';
|
||||
const SERIALIZABLE_TYPES = new Set(['object', 'array', 'function', 'error']);
|
||||
|
||||
/**
|
||||
* The serializable result of a test file run from a worker.
|
||||
* @private
|
||||
*/
|
||||
class SerializableWorkerResult {
|
||||
/**
|
||||
* Creates instance props; of note, the `__type` prop.
|
||||
*
|
||||
* Note that the failure count is _redundant_ and could be derived from the
|
||||
* list of events; but since we're already doing the work, might as well use
|
||||
* it.
|
||||
* @param {SerializableEvent[]} [events=[]] - Events to eventually serialize
|
||||
* @param {number} [failureCount=0] - Failure count
|
||||
*/
|
||||
constructor(events = [], failureCount = 0) {
|
||||
/**
|
||||
* The number of failures in this run
|
||||
* @type {number}
|
||||
*/
|
||||
this.failureCount = failureCount;
|
||||
/**
|
||||
* All relevant events emitted from the {@link Runner}.
|
||||
* @type {SerializableEvent[]}
|
||||
*/
|
||||
this.events = events;
|
||||
|
||||
/**
|
||||
* Symbol-like value needed to distinguish when attempting to deserialize
|
||||
* this object (once it's been received over IPC).
|
||||
* @type {Readonly<"SerializableWorkerResult">}
|
||||
*/
|
||||
Object.defineProperty(this, '__type', {
|
||||
value: SERIALIZABLE_RESULT_NAME,
|
||||
enumerable: true,
|
||||
writable: false
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new {@link SerializableWorkerResult}.
|
||||
* @param {...any} args - Args to constructor
|
||||
* @returns {SerializableWorkerResult}
|
||||
*/
|
||||
static create(...args) {
|
||||
return new SerializableWorkerResult(...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes each {@link SerializableEvent} in our `events` prop;
|
||||
* makes this object read-only.
|
||||
* @returns {Readonly<SerializableWorkerResult>}
|
||||
*/
|
||||
serialize() {
|
||||
this.events.forEach(event => {
|
||||
event.serialize();
|
||||
});
|
||||
return Object.freeze(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes a {@link SerializedWorkerResult} into something reporters can
|
||||
* use; calls {@link SerializableEvent.deserialize} on each item in its
|
||||
* `events` prop.
|
||||
* @param {SerializedWorkerResult} obj
|
||||
* @returns {SerializedWorkerResult}
|
||||
*/
|
||||
static deserialize(obj) {
|
||||
obj.events.forEach(event => {
|
||||
SerializableEvent.deserialize(event);
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if this is a {@link SerializedWorkerResult} or a
|
||||
* {@link SerializableWorkerResult}.
|
||||
* @param {*} value - A value to check
|
||||
* @returns {boolean} If true, it's deserializable
|
||||
*/
|
||||
static isSerializedWorkerResult(value) {
|
||||
return (
|
||||
value instanceof SerializableWorkerResult ||
|
||||
(type(value) === 'object' && value.__type === SERIALIZABLE_RESULT_NAME)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an event, emitted by a {@link Runner}, which is to be transmitted
|
||||
* over IPC.
|
||||
*
|
||||
* Due to the contents of the event data, it's not possible to send them
|
||||
* verbatim. When received by the main process--and handled by reporters--these
|
||||
* objects are expected to contain {@link Runnable} instances. This class
|
||||
* provides facilities to perform the translation via serialization and
|
||||
* deserialization.
|
||||
* @private
|
||||
*/
|
||||
class SerializableEvent {
|
||||
/**
|
||||
* Constructs a `SerializableEvent`, throwing if we receive unexpected data.
|
||||
*
|
||||
* Practically, events emitted from `Runner` have a minumum of zero (0)
|
||||
* arguments-- (for example, {@link Runnable.constants.EVENT_RUN_BEGIN}) and a
|
||||
* maximum of two (2) (for example,
|
||||
* {@link Runnable.constants.EVENT_TEST_FAIL}, where the second argument is an
|
||||
* `Error`). The first argument, if present, is a {@link Runnable}. This
|
||||
* constructor's arguments adhere to this convention.
|
||||
* @param {string} eventName - A non-empty event name.
|
||||
* @param {any} [originalValue] - Some data. Corresponds to extra arguments
|
||||
* passed to `EventEmitter#emit`.
|
||||
* @param {Error} [originalError] - An error, if there's an error.
|
||||
* @throws If `eventName` is empty, or `originalValue` is a non-object.
|
||||
*/
|
||||
constructor(eventName, originalValue, originalError) {
|
||||
if (!eventName) {
|
||||
throw createInvalidArgumentTypeError(
|
||||
'Empty `eventName` string argument',
|
||||
'eventName',
|
||||
'string'
|
||||
);
|
||||
}
|
||||
/**
|
||||
* The event name.
|
||||
* @memberof SerializableEvent
|
||||
*/
|
||||
this.eventName = eventName;
|
||||
const originalValueType = type(originalValue);
|
||||
if (originalValueType !== 'object' && originalValueType !== 'undefined') {
|
||||
throw createInvalidArgumentTypeError(
|
||||
`Expected object but received ${originalValueType}`,
|
||||
'originalValue',
|
||||
'object'
|
||||
);
|
||||
}
|
||||
/**
|
||||
* An error, if present.
|
||||
* @memberof SerializableEvent
|
||||
*/
|
||||
Object.defineProperty(this, 'originalError', {
|
||||
value: originalError,
|
||||
enumerable: false
|
||||
});
|
||||
|
||||
/**
|
||||
* The raw value.
|
||||
*
|
||||
* We don't want this value sent via IPC; making it non-enumerable will do that.
|
||||
*
|
||||
* @memberof SerializableEvent
|
||||
*/
|
||||
Object.defineProperty(this, 'originalValue', {
|
||||
value: originalValue,
|
||||
enumerable: false
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* In case you hated using `new` (I do).
|
||||
*
|
||||
* @param {...any} args - Args for {@link SerializableEvent#constructor}.
|
||||
* @returns {SerializableEvent} A new `SerializableEvent`
|
||||
*/
|
||||
static create(...args) {
|
||||
return new SerializableEvent(...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used internally by {@link SerializableEvent#serialize}.
|
||||
* @ignore
|
||||
* @param {Array<object|string>} pairs - List of parent/key tuples to process; modified in-place. This JSDoc type is an approximation
|
||||
* @param {object} parent - Some parent object
|
||||
* @param {string} key - Key to inspect
|
||||
* @param {WeakSet<Object>} seenObjects - For avoiding circular references
|
||||
*/
|
||||
static _serialize(pairs, parent, key, seenObjects) {
|
||||
let value = parent[key];
|
||||
if (seenObjects.has(value)) {
|
||||
parent[key] = Object.create(null);
|
||||
return;
|
||||
}
|
||||
let _type = type(value);
|
||||
if (_type === 'error') {
|
||||
// we need to reference the stack prop b/c it's lazily-loaded.
|
||||
// `__type` is necessary for deserialization to create an `Error` later.
|
||||
// `message` is apparently not enumerable, so we must handle it specifically.
|
||||
value = Object.assign(Object.create(null), value, {
|
||||
stack: value.stack,
|
||||
message: value.message,
|
||||
__type: 'Error'
|
||||
});
|
||||
parent[key] = value;
|
||||
// after this, set the result of type(value) to be `object`, and we'll throw
|
||||
// whatever other junk is in the original error into the new `value`.
|
||||
_type = 'object';
|
||||
}
|
||||
switch (_type) {
|
||||
case 'object':
|
||||
if (type(value.serialize) === 'function') {
|
||||
parent[key] = value.serialize();
|
||||
} else {
|
||||
// by adding props to the `pairs` array, we will process it further
|
||||
pairs.push(
|
||||
...Object.keys(value)
|
||||
.filter(key => SERIALIZABLE_TYPES.has(type(value[key])))
|
||||
.map(key => [value, key])
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'function':
|
||||
// we _may_ want to dig in to functions for some assertion libraries
|
||||
// that might put a usable property on a function.
|
||||
// for now, just zap it.
|
||||
delete parent[key];
|
||||
break;
|
||||
case 'array':
|
||||
pairs.push(
|
||||
...value
|
||||
.filter(value => SERIALIZABLE_TYPES.has(type(value)))
|
||||
.map((value, index) => [value, index])
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies this object *in place* (for theoretical memory consumption &
|
||||
* performance reasons); serializes `SerializableEvent#originalValue` (placing
|
||||
* the result in `SerializableEvent#data`) and `SerializableEvent#error`.
|
||||
* Freezes this object. The result is an object that can be transmitted over
|
||||
* IPC.
|
||||
* If this quickly becomes unmaintainable, we will want to move towards immutable
|
||||
* objects post-haste.
|
||||
*/
|
||||
serialize() {
|
||||
// given a parent object and a key, inspect the value and decide whether
|
||||
// to replace it, remove it, or add it to our `pairs` array to further process.
|
||||
// this is recursion in loop form.
|
||||
const originalValue = this.originalValue;
|
||||
const result = Object.assign(Object.create(null), {
|
||||
data:
|
||||
type(originalValue) === 'object' &&
|
||||
type(originalValue.serialize) === 'function'
|
||||
? originalValue.serialize()
|
||||
: originalValue,
|
||||
error: this.originalError
|
||||
});
|
||||
|
||||
const pairs = Object.keys(result).map(key => [result, key]);
|
||||
const seenObjects = new WeakSet();
|
||||
|
||||
let pair;
|
||||
while ((pair = pairs.shift())) {
|
||||
SerializableEvent._serialize(pairs, ...pair, seenObjects);
|
||||
seenObjects.add(pair[0]);
|
||||
}
|
||||
|
||||
this.data = result.data;
|
||||
this.error = result.error;
|
||||
|
||||
return Object.freeze(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used internally by {@link SerializableEvent.deserialize}; creates an `Error`
|
||||
* from an `Error`-like (serialized) object
|
||||
* @ignore
|
||||
* @param {Object} value - An Error-like value
|
||||
* @returns {Error} Real error
|
||||
*/
|
||||
static _deserializeError(value) {
|
||||
const error = new Error(value.message);
|
||||
error.stack = value.stack;
|
||||
Object.assign(error, value);
|
||||
delete error.__type;
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used internally by {@link SerializableEvent.deserialize}; recursively
|
||||
* deserializes an object in-place.
|
||||
* @param {object|Array} parent - Some object or array
|
||||
* @param {string|number} key - Some prop name or array index within `parent`
|
||||
*/
|
||||
static _deserializeObject(parent, key) {
|
||||
if (key === '__proto__') {
|
||||
delete parent[key];
|
||||
return;
|
||||
}
|
||||
const value = parent[key];
|
||||
// keys beginning with `$$` are converted into functions returning the value
|
||||
// and renamed, stripping the `$$` prefix.
|
||||
// functions defined this way cannot be array members!
|
||||
if (type(key) === 'string' && key.startsWith('$$')) {
|
||||
const newKey = key.slice(2);
|
||||
parent[newKey] = () => value;
|
||||
delete parent[key];
|
||||
key = newKey;
|
||||
}
|
||||
if (type(value) === 'array') {
|
||||
value.forEach((_, idx) => {
|
||||
SerializableEvent._deserializeObject(value, idx);
|
||||
});
|
||||
} else if (type(value) === 'object') {
|
||||
if (value.__type === 'Error') {
|
||||
parent[key] = SerializableEvent._deserializeError(value);
|
||||
} else {
|
||||
Object.keys(value).forEach(key => {
|
||||
SerializableEvent._deserializeObject(value, key);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize value returned from a worker into something more useful.
|
||||
* Does not return the same object.
|
||||
* @todo do this in a loop instead of with recursion (if necessary)
|
||||
* @param {SerializedEvent} obj - Object returned from worker
|
||||
* @returns {SerializedEvent} Deserialized result
|
||||
*/
|
||||
static deserialize(obj) {
|
||||
if (!obj) {
|
||||
throw createInvalidArgumentTypeError('Expected value', obj);
|
||||
}
|
||||
|
||||
obj = Object.assign(Object.create(null), obj);
|
||||
|
||||
if (obj.data) {
|
||||
Object.keys(obj.data).forEach(key => {
|
||||
SerializableEvent._deserializeObject(obj.data, key);
|
||||
});
|
||||
}
|
||||
|
||||
if (obj.error) {
|
||||
obj.error = SerializableEvent._deserializeError(obj.error);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* "Serializes" a value for transmission over IPC as a message.
|
||||
*
|
||||
* If value is an object and has a `serialize()` method, call that method; otherwise return the object and hope for the best.
|
||||
*
|
||||
* @param {*} [value] - A value to serialize
|
||||
*/
|
||||
exports.serialize = function serialize(value) {
|
||||
const result =
|
||||
type(value) === 'object' && type(value.serialize) === 'function'
|
||||
? value.serialize()
|
||||
: value;
|
||||
debug('serialized: %O', result);
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* "Deserializes" a "message" received over IPC.
|
||||
*
|
||||
* This could be expanded with other objects that need deserialization,
|
||||
* but at present time we only care about {@link SerializableWorkerResult} objects.
|
||||
*
|
||||
* @param {*} [value] - A "message" to deserialize
|
||||
*/
|
||||
exports.deserialize = function deserialize(value) {
|
||||
const result = SerializableWorkerResult.isSerializedWorkerResult(value)
|
||||
? SerializableWorkerResult.deserialize(value)
|
||||
: value;
|
||||
debug('deserialized: %O', result);
|
||||
return result;
|
||||
};
|
||||
|
||||
exports.SerializableEvent = SerializableEvent;
|
||||
exports.SerializableWorkerResult = SerializableWorkerResult;
|
||||
|
||||
/**
|
||||
* The result of calling `SerializableEvent.serialize`, as received
|
||||
* by the deserializer.
|
||||
* @private
|
||||
* @typedef {Object} SerializedEvent
|
||||
* @property {object?} data - Optional serialized data
|
||||
* @property {object?} error - Optional serialized `Error`
|
||||
*/
|
||||
|
||||
/**
|
||||
* The result of calling `SerializableWorkerResult.serialize` as received
|
||||
* by the deserializer.
|
||||
* @private
|
||||
* @typedef {Object} SerializedWorkerResult
|
||||
* @property {number} failureCount - Number of failures
|
||||
* @property {SerializedEvent[]} events - Serialized events
|
||||
* @property {"SerializedWorkerResult"} __type - Symbol-like to denote the type of object this is
|
||||
*/
|
151
node_modules/mocha/lib/nodejs/worker.js
generated
vendored
Normal file
151
node_modules/mocha/lib/nodejs/worker.js
generated
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
/**
|
||||
* A worker process. Consumes {@link module:reporters/parallel-buffered} reporter.
|
||||
* @module worker
|
||||
* @private
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const {
|
||||
createInvalidArgumentTypeError,
|
||||
createInvalidArgumentValueError
|
||||
} = require('../errors');
|
||||
const workerpool = require('workerpool');
|
||||
const Mocha = require('../mocha');
|
||||
const {handleRequires, validateLegacyPlugin} = require('../cli/run-helpers');
|
||||
const d = require('debug');
|
||||
const debug = d.debug(`mocha:parallel:worker:${process.pid}`);
|
||||
const isDebugEnabled = d.enabled(`mocha:parallel:worker:${process.pid}`);
|
||||
const {serialize} = require('./serializer');
|
||||
const {setInterval, clearInterval} = global;
|
||||
|
||||
let rootHooks;
|
||||
|
||||
if (workerpool.isMainThread) {
|
||||
throw new Error(
|
||||
'This script is intended to be run as a worker (by the `workerpool` package).'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes some stuff on the first call to {@link run}.
|
||||
*
|
||||
* Handles `--require` and `--ui`. Does _not_ handle `--reporter`,
|
||||
* as only the `Buffered` reporter is used.
|
||||
*
|
||||
* **This function only runs once per worker**; it overwrites itself with a no-op
|
||||
* before returning.
|
||||
*
|
||||
* @param {Options} argv - Command-line options
|
||||
*/
|
||||
let bootstrap = async argv => {
|
||||
// globalSetup and globalTeardown do not run in workers
|
||||
const plugins = await handleRequires(argv.require, {
|
||||
ignoredPlugins: ['mochaGlobalSetup', 'mochaGlobalTeardown']
|
||||
});
|
||||
validateLegacyPlugin(argv, 'ui', Mocha.interfaces);
|
||||
|
||||
rootHooks = plugins.rootHooks;
|
||||
bootstrap = () => {};
|
||||
debug('bootstrap(): finished with args: %O', argv);
|
||||
};
|
||||
|
||||
/**
|
||||
* Runs a single test file in a worker thread.
|
||||
* @param {string} filepath - Filepath of test file
|
||||
* @param {string} [serializedOptions] - **Serialized** options. This string will be eval'd!
|
||||
* @see https://npm.im/serialize-javascript
|
||||
* @returns {Promise<{failures: number, events: BufferedEvent[]}>} - Test
|
||||
* failure count and list of events.
|
||||
*/
|
||||
async function run(filepath, serializedOptions = '{}') {
|
||||
if (!filepath) {
|
||||
throw createInvalidArgumentTypeError(
|
||||
'Expected a non-empty "filepath" argument',
|
||||
'file',
|
||||
'string'
|
||||
);
|
||||
}
|
||||
|
||||
debug('run(): running test file %s', filepath);
|
||||
|
||||
if (typeof serializedOptions !== 'string') {
|
||||
throw createInvalidArgumentTypeError(
|
||||
'run() expects second parameter to be a string which was serialized by the `serialize-javascript` module',
|
||||
'serializedOptions',
|
||||
'string'
|
||||
);
|
||||
}
|
||||
let argv;
|
||||
try {
|
||||
// eslint-disable-next-line no-eval
|
||||
argv = eval('(' + serializedOptions + ')');
|
||||
} catch (err) {
|
||||
throw createInvalidArgumentValueError(
|
||||
'run() was unable to deserialize the options',
|
||||
'serializedOptions',
|
||||
serializedOptions
|
||||
);
|
||||
}
|
||||
|
||||
const opts = Object.assign({ui: 'bdd'}, argv, {
|
||||
// if this was true, it would cause infinite recursion.
|
||||
parallel: false,
|
||||
// this doesn't work in parallel mode
|
||||
forbidOnly: true,
|
||||
// it's useful for a Mocha instance to know if it's running in a worker process.
|
||||
isWorker: true
|
||||
});
|
||||
|
||||
await bootstrap(opts);
|
||||
|
||||
opts.rootHooks = rootHooks;
|
||||
|
||||
const mocha = new Mocha(opts).addFile(filepath);
|
||||
|
||||
try {
|
||||
await mocha.loadFilesAsync();
|
||||
} catch (err) {
|
||||
debug('run(): could not load file %s: %s', filepath, err);
|
||||
throw err;
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let debugInterval;
|
||||
/* istanbul ignore next */
|
||||
if (isDebugEnabled) {
|
||||
debugInterval = setInterval(() => {
|
||||
debug('run(): still running %s...', filepath);
|
||||
}, 5000).unref();
|
||||
}
|
||||
mocha.run(result => {
|
||||
// Runner adds these; if we don't remove them, we'll get a leak.
|
||||
process.removeAllListeners('uncaughtException');
|
||||
process.removeAllListeners('unhandledRejection');
|
||||
|
||||
try {
|
||||
const serialized = serialize(result);
|
||||
debug(
|
||||
'run(): completed run with %d test failures; returning to main process',
|
||||
typeof result.failures === 'number' ? result.failures : 0
|
||||
);
|
||||
resolve(serialized);
|
||||
} catch (err) {
|
||||
// TODO: figure out exactly what the sad path looks like here.
|
||||
// rejection should only happen if an error is "unrecoverable"
|
||||
debug('run(): serialization failed; rejecting: %O', err);
|
||||
reject(err);
|
||||
} finally {
|
||||
clearInterval(debugInterval);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// this registers the `run` function.
|
||||
workerpool.worker({run});
|
||||
|
||||
debug('started worker process');
|
||||
|
||||
// for testing
|
||||
exports.run = run;
|
16
node_modules/mocha/lib/pending.js
generated
vendored
Normal file
16
node_modules/mocha/lib/pending.js
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
@module Pending
|
||||
*/
|
||||
|
||||
module.exports = Pending;
|
||||
|
||||
/**
|
||||
* Initialize a new `Pending` error with the given message.
|
||||
*
|
||||
* @param {string} message
|
||||
*/
|
||||
function Pending(message) {
|
||||
this.message = message;
|
||||
}
|
286
node_modules/mocha/lib/plugin-loader.js
generated
vendored
Normal file
286
node_modules/mocha/lib/plugin-loader.js
generated
vendored
Normal file
@@ -0,0 +1,286 @@
|
||||
/**
|
||||
* Provides a way to load "plugins" as provided by the user.
|
||||
*
|
||||
* Currently supports:
|
||||
*
|
||||
* - Root hooks
|
||||
* - Global fixtures (setup/teardown)
|
||||
* @private
|
||||
* @module plugin
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const debug = require('debug')('mocha:plugin-loader');
|
||||
const {
|
||||
createInvalidPluginDefinitionError,
|
||||
createInvalidPluginImplementationError
|
||||
} = require('./errors');
|
||||
const {castArray} = require('./utils');
|
||||
|
||||
/**
|
||||
* Built-in plugin definitions.
|
||||
*/
|
||||
const MochaPlugins = [
|
||||
/**
|
||||
* Root hook plugin definition
|
||||
* @type {PluginDefinition}
|
||||
*/
|
||||
{
|
||||
exportName: 'mochaHooks',
|
||||
optionName: 'rootHooks',
|
||||
validate(value) {
|
||||
if (
|
||||
Array.isArray(value) ||
|
||||
(typeof value !== 'function' && typeof value !== 'object')
|
||||
) {
|
||||
throw createInvalidPluginImplementationError(
|
||||
`mochaHooks must be an object or a function returning (or fulfilling with) an object`
|
||||
);
|
||||
}
|
||||
},
|
||||
async finalize(rootHooks) {
|
||||
if (rootHooks.length) {
|
||||
const rootHookObjects = await Promise.all(
|
||||
rootHooks.map(async hook =>
|
||||
typeof hook === 'function' ? hook() : hook
|
||||
)
|
||||
);
|
||||
|
||||
return rootHookObjects.reduce(
|
||||
(acc, hook) => {
|
||||
hook = {
|
||||
beforeAll: [],
|
||||
beforeEach: [],
|
||||
afterAll: [],
|
||||
afterEach: [],
|
||||
...hook
|
||||
};
|
||||
return {
|
||||
beforeAll: [...acc.beforeAll, ...castArray(hook.beforeAll)],
|
||||
beforeEach: [...acc.beforeEach, ...castArray(hook.beforeEach)],
|
||||
afterAll: [...acc.afterAll, ...castArray(hook.afterAll)],
|
||||
afterEach: [...acc.afterEach, ...castArray(hook.afterEach)]
|
||||
};
|
||||
},
|
||||
{beforeAll: [], beforeEach: [], afterAll: [], afterEach: []}
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Global setup fixture plugin definition
|
||||
* @type {PluginDefinition}
|
||||
*/
|
||||
{
|
||||
exportName: 'mochaGlobalSetup',
|
||||
optionName: 'globalSetup',
|
||||
validate(value) {
|
||||
let isValid = true;
|
||||
if (Array.isArray(value)) {
|
||||
if (value.some(item => typeof item !== 'function')) {
|
||||
isValid = false;
|
||||
}
|
||||
} else if (typeof value !== 'function') {
|
||||
isValid = false;
|
||||
}
|
||||
if (!isValid) {
|
||||
throw createInvalidPluginImplementationError(
|
||||
`mochaGlobalSetup must be a function or an array of functions`,
|
||||
{pluginDef: this, pluginImpl: value}
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Global teardown fixture plugin definition
|
||||
* @type {PluginDefinition}
|
||||
*/
|
||||
{
|
||||
exportName: 'mochaGlobalTeardown',
|
||||
optionName: 'globalTeardown',
|
||||
validate(value) {
|
||||
let isValid = true;
|
||||
if (Array.isArray(value)) {
|
||||
if (value.some(item => typeof item !== 'function')) {
|
||||
isValid = false;
|
||||
}
|
||||
} else if (typeof value !== 'function') {
|
||||
isValid = false;
|
||||
}
|
||||
if (!isValid) {
|
||||
throw createInvalidPluginImplementationError(
|
||||
`mochaGlobalTeardown must be a function or an array of functions`,
|
||||
{pluginDef: this, pluginImpl: value}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
/**
|
||||
* Contains a registry of [plugin definitions]{@link PluginDefinition} and discovers plugin implementations in user-supplied code.
|
||||
*
|
||||
* - [load()]{@link #load} should be called for all required modules
|
||||
* - The result of [finalize()]{@link #finalize} should be merged into the options for the [Mocha]{@link Mocha} constructor.
|
||||
* @private
|
||||
*/
|
||||
class PluginLoader {
|
||||
/**
|
||||
* Initializes plugin names, plugin map, etc.
|
||||
* @param {PluginLoaderOptions} [opts] - Options
|
||||
*/
|
||||
constructor({pluginDefs = MochaPlugins, ignore = []} = {}) {
|
||||
/**
|
||||
* Map of registered plugin defs
|
||||
* @type {Map<string,PluginDefinition>}
|
||||
*/
|
||||
this.registered = new Map();
|
||||
|
||||
/**
|
||||
* Cache of known `optionName` values for checking conflicts
|
||||
* @type {Set<string>}
|
||||
*/
|
||||
this.knownOptionNames = new Set();
|
||||
|
||||
/**
|
||||
* Cache of known `exportName` values for checking conflicts
|
||||
* @type {Set<string>}
|
||||
*/
|
||||
this.knownExportNames = new Set();
|
||||
|
||||
/**
|
||||
* Map of user-supplied plugin implementations
|
||||
* @type {Map<string,Array<*>>}
|
||||
*/
|
||||
this.loaded = new Map();
|
||||
|
||||
/**
|
||||
* Set of ignored plugins by export name
|
||||
* @type {Set<string>}
|
||||
*/
|
||||
this.ignoredExportNames = new Set(castArray(ignore));
|
||||
|
||||
castArray(pluginDefs).forEach(pluginDef => {
|
||||
this.register(pluginDef);
|
||||
});
|
||||
|
||||
debug(
|
||||
'registered %d plugin defs (%d ignored)',
|
||||
this.registered.size,
|
||||
this.ignoredExportNames.size
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a plugin
|
||||
* @param {PluginDefinition} pluginDef - Plugin definition
|
||||
*/
|
||||
register(pluginDef) {
|
||||
if (!pluginDef || typeof pluginDef !== 'object') {
|
||||
throw createInvalidPluginDefinitionError(
|
||||
'pluginDef is non-object or falsy',
|
||||
pluginDef
|
||||
);
|
||||
}
|
||||
if (!pluginDef.exportName) {
|
||||
throw createInvalidPluginDefinitionError(
|
||||
`exportName is expected to be a non-empty string`,
|
||||
pluginDef
|
||||
);
|
||||
}
|
||||
let {exportName} = pluginDef;
|
||||
if (this.ignoredExportNames.has(exportName)) {
|
||||
debug(
|
||||
'refusing to register ignored plugin with export name "%s"',
|
||||
exportName
|
||||
);
|
||||
return;
|
||||
}
|
||||
exportName = String(exportName);
|
||||
pluginDef.optionName = String(pluginDef.optionName || exportName);
|
||||
if (this.knownExportNames.has(exportName)) {
|
||||
throw createInvalidPluginDefinitionError(
|
||||
`Plugin definition conflict: ${exportName}; exportName must be unique`,
|
||||
pluginDef
|
||||
);
|
||||
}
|
||||
this.loaded.set(exportName, []);
|
||||
this.registered.set(exportName, pluginDef);
|
||||
this.knownExportNames.add(exportName);
|
||||
this.knownOptionNames.add(pluginDef.optionName);
|
||||
debug('registered plugin def "%s"', exportName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inspects a module's exports for known plugins and keeps them in memory.
|
||||
*
|
||||
* @param {*} requiredModule - The exports of a module loaded via `--require`
|
||||
* @returns {boolean} If one or more plugins was found, return `true`.
|
||||
*/
|
||||
load(requiredModule) {
|
||||
// we should explicitly NOT fail if other stuff is exported.
|
||||
// we only care about the plugins we know about.
|
||||
if (requiredModule && typeof requiredModule === 'object') {
|
||||
return Array.from(this.knownExportNames).reduce(
|
||||
(pluginImplFound, pluginName) => {
|
||||
const pluginImpl = requiredModule[pluginName];
|
||||
if (pluginImpl) {
|
||||
const plugin = this.registered.get(pluginName);
|
||||
if (typeof plugin.validate === 'function') {
|
||||
plugin.validate(pluginImpl);
|
||||
}
|
||||
this.loaded.set(pluginName, [
|
||||
...this.loaded.get(pluginName),
|
||||
...castArray(pluginImpl)
|
||||
]);
|
||||
return true;
|
||||
}
|
||||
return pluginImplFound;
|
||||
},
|
||||
false
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the `finalize()` function of each known plugin definition on the plugins found by [load()]{@link PluginLoader#load}.
|
||||
*
|
||||
* Output suitable for passing as input into {@link Mocha} constructor.
|
||||
* @returns {Promise<object>} Object having keys corresponding to registered plugin definitions' `optionName` prop (or `exportName`, if none), and the values are the implementations as provided by a user.
|
||||
*/
|
||||
async finalize() {
|
||||
const finalizedPlugins = Object.create(null);
|
||||
|
||||
for await (const [exportName, pluginImpls] of this.loaded.entries()) {
|
||||
if (pluginImpls.length) {
|
||||
const plugin = this.registered.get(exportName);
|
||||
finalizedPlugins[plugin.optionName] =
|
||||
typeof plugin.finalize === 'function'
|
||||
? await plugin.finalize(pluginImpls)
|
||||
: pluginImpls;
|
||||
}
|
||||
}
|
||||
|
||||
debug('finalized plugins: %O', finalizedPlugins);
|
||||
return finalizedPlugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@link PluginLoader}
|
||||
* @param {PluginLoaderOptions} [opts] - Plugin loader options
|
||||
*/
|
||||
static create({pluginDefs = MochaPlugins, ignore = []} = {}) {
|
||||
return new PluginLoader({pluginDefs, ignore});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PluginLoader;
|
||||
|
||||
/**
|
||||
* Options for {@link PluginLoader}
|
||||
* @typedef {Object} PluginLoaderOptions
|
||||
* @property {PluginDefinition[]} [pluginDefs] - Plugin definitions
|
||||
* @property {string[]} [ignore] - A list of plugins to ignore when loading
|
||||
*/
|
536
node_modules/mocha/lib/reporters/base.js
generated
vendored
Normal file
536
node_modules/mocha/lib/reporters/base.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user