$
This commit is contained in:
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
95
node_modules/mocha/lib/reporters/doc.js
generated
vendored
Normal file
95
node_modules/mocha/lib/reporters/doc.js
generated
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
'use strict';
|
||||
/**
|
||||
* @module Doc
|
||||
*/
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Base = require('./base');
|
||||
var utils = require('../utils');
|
||||
var constants = require('../runner').constants;
|
||||
var EVENT_TEST_PASS = constants.EVENT_TEST_PASS;
|
||||
var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL;
|
||||
var EVENT_SUITE_BEGIN = constants.EVENT_SUITE_BEGIN;
|
||||
var EVENT_SUITE_END = constants.EVENT_SUITE_END;
|
||||
|
||||
/**
|
||||
* Expose `Doc`.
|
||||
*/
|
||||
|
||||
exports = module.exports = Doc;
|
||||
|
||||
/**
|
||||
* Constructs a new `Doc` reporter instance.
|
||||
*
|
||||
* @public
|
||||
* @class
|
||||
* @memberof Mocha.reporters
|
||||
* @extends Mocha.reporters.Base
|
||||
* @param {Runner} runner - Instance triggers reporter actions.
|
||||
* @param {Object} [options] - runner options
|
||||
*/
|
||||
function Doc(runner, options) {
|
||||
Base.call(this, runner, options);
|
||||
|
||||
var indents = 2;
|
||||
|
||||
function indent() {
|
||||
return Array(indents).join(' ');
|
||||
}
|
||||
|
||||
runner.on(EVENT_SUITE_BEGIN, function(suite) {
|
||||
if (suite.root) {
|
||||
return;
|
||||
}
|
||||
++indents;
|
||||
Base.consoleLog('%s<section class="suite">', indent());
|
||||
++indents;
|
||||
Base.consoleLog('%s<h1>%s</h1>', indent(), utils.escape(suite.title));
|
||||
Base.consoleLog('%s<dl>', indent());
|
||||
});
|
||||
|
||||
runner.on(EVENT_SUITE_END, function(suite) {
|
||||
if (suite.root) {
|
||||
return;
|
||||
}
|
||||
Base.consoleLog('%s</dl>', indent());
|
||||
--indents;
|
||||
Base.consoleLog('%s</section>', indent());
|
||||
--indents;
|
||||
});
|
||||
|
||||
runner.on(EVENT_TEST_PASS, function(test) {
|
||||
Base.consoleLog('%s <dt>%s</dt>', indent(), utils.escape(test.title));
|
||||
Base.consoleLog('%s <dt>%s</dt>', indent(), utils.escape(test.file));
|
||||
var code = utils.escape(utils.clean(test.body));
|
||||
Base.consoleLog('%s <dd><pre><code>%s</code></pre></dd>', indent(), code);
|
||||
});
|
||||
|
||||
runner.on(EVENT_TEST_FAIL, function(test, err) {
|
||||
Base.consoleLog(
|
||||
'%s <dt class="error">%s</dt>',
|
||||
indent(),
|
||||
utils.escape(test.title)
|
||||
);
|
||||
Base.consoleLog(
|
||||
'%s <dt class="error">%s</dt>',
|
||||
indent(),
|
||||
utils.escape(test.file)
|
||||
);
|
||||
var code = utils.escape(utils.clean(test.body));
|
||||
Base.consoleLog(
|
||||
'%s <dd class="error"><pre><code>%s</code></pre></dd>',
|
||||
indent(),
|
||||
code
|
||||
);
|
||||
Base.consoleLog(
|
||||
'%s <dd class="error">%s</dd>',
|
||||
indent(),
|
||||
utils.escape(err)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Doc.description = 'HTML documentation';
|
81
node_modules/mocha/lib/reporters/dot.js
generated
vendored
Normal file
81
node_modules/mocha/lib/reporters/dot.js
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
'use strict';
|
||||
/**
|
||||
* @module Dot
|
||||
*/
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Base = require('./base');
|
||||
var inherits = require('../utils').inherits;
|
||||
var constants = require('../runner').constants;
|
||||
var EVENT_TEST_PASS = constants.EVENT_TEST_PASS;
|
||||
var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL;
|
||||
var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN;
|
||||
var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING;
|
||||
var EVENT_RUN_END = constants.EVENT_RUN_END;
|
||||
|
||||
/**
|
||||
* Expose `Dot`.
|
||||
*/
|
||||
|
||||
exports = module.exports = Dot;
|
||||
|
||||
/**
|
||||
* Constructs a new `Dot` reporter instance.
|
||||
*
|
||||
* @public
|
||||
* @class
|
||||
* @memberof Mocha.reporters
|
||||
* @extends Mocha.reporters.Base
|
||||
* @param {Runner} runner - Instance triggers reporter actions.
|
||||
* @param {Object} [options] - runner options
|
||||
*/
|
||||
function Dot(runner, options) {
|
||||
Base.call(this, runner, options);
|
||||
|
||||
var self = this;
|
||||
var width = (Base.window.width * 0.75) | 0;
|
||||
var n = -1;
|
||||
|
||||
runner.on(EVENT_RUN_BEGIN, function() {
|
||||
process.stdout.write('\n');
|
||||
});
|
||||
|
||||
runner.on(EVENT_TEST_PENDING, function() {
|
||||
if (++n % width === 0) {
|
||||
process.stdout.write('\n ');
|
||||
}
|
||||
process.stdout.write(Base.color('pending', Base.symbols.comma));
|
||||
});
|
||||
|
||||
runner.on(EVENT_TEST_PASS, function(test) {
|
||||
if (++n % width === 0) {
|
||||
process.stdout.write('\n ');
|
||||
}
|
||||
if (test.speed === 'slow') {
|
||||
process.stdout.write(Base.color('bright yellow', Base.symbols.dot));
|
||||
} else {
|
||||
process.stdout.write(Base.color(test.speed, Base.symbols.dot));
|
||||
}
|
||||
});
|
||||
|
||||
runner.on(EVENT_TEST_FAIL, function() {
|
||||
if (++n % width === 0) {
|
||||
process.stdout.write('\n ');
|
||||
}
|
||||
process.stdout.write(Base.color('fail', Base.symbols.bang));
|
||||
});
|
||||
|
||||
runner.once(EVENT_RUN_END, function() {
|
||||
process.stdout.write('\n');
|
||||
self.epilogue();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherit from `Base.prototype`.
|
||||
*/
|
||||
inherits(Dot, Base);
|
||||
|
||||
Dot.description = 'dot matrix representation';
|
390
node_modules/mocha/lib/reporters/html.js
generated
vendored
Normal file
390
node_modules/mocha/lib/reporters/html.js
generated
vendored
Normal file
@@ -0,0 +1,390 @@
|
||||
'use strict';
|
||||
|
||||
/* eslint-env browser */
|
||||
/**
|
||||
* @module HTML
|
||||
*/
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Base = require('./base');
|
||||
var utils = require('../utils');
|
||||
var Progress = require('../browser/progress');
|
||||
var escapeRe = require('escape-string-regexp');
|
||||
var constants = require('../runner').constants;
|
||||
var EVENT_TEST_PASS = constants.EVENT_TEST_PASS;
|
||||
var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL;
|
||||
var EVENT_SUITE_BEGIN = constants.EVENT_SUITE_BEGIN;
|
||||
var EVENT_SUITE_END = constants.EVENT_SUITE_END;
|
||||
var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING;
|
||||
var escape = utils.escape;
|
||||
|
||||
/**
|
||||
* Save timer references to avoid Sinon interfering (see GH-237).
|
||||
*/
|
||||
|
||||
var Date = global.Date;
|
||||
|
||||
/**
|
||||
* Expose `HTML`.
|
||||
*/
|
||||
|
||||
exports = module.exports = HTML;
|
||||
|
||||
/**
|
||||
* Stats template.
|
||||
*/
|
||||
|
||||
var statsTemplate =
|
||||
'<ul id="mocha-stats">' +
|
||||
'<li class="progress"><canvas width="40" height="40"></canvas></li>' +
|
||||
'<li class="passes"><a href="javascript:void(0);">passes:</a> <em>0</em></li>' +
|
||||
'<li class="failures"><a href="javascript:void(0);">failures:</a> <em>0</em></li>' +
|
||||
'<li class="duration">duration: <em>0</em>s</li>' +
|
||||
'</ul>';
|
||||
|
||||
var playIcon = '‣';
|
||||
|
||||
/**
|
||||
* Constructs a new `HTML` reporter instance.
|
||||
*
|
||||
* @public
|
||||
* @class
|
||||
* @memberof Mocha.reporters
|
||||
* @extends Mocha.reporters.Base
|
||||
* @param {Runner} runner - Instance triggers reporter actions.
|
||||
* @param {Object} [options] - runner options
|
||||
*/
|
||||
function HTML(runner, options) {
|
||||
Base.call(this, runner, options);
|
||||
|
||||
var self = this;
|
||||
var stats = this.stats;
|
||||
var stat = fragment(statsTemplate);
|
||||
var items = stat.getElementsByTagName('li');
|
||||
var passes = items[1].getElementsByTagName('em')[0];
|
||||
var passesLink = items[1].getElementsByTagName('a')[0];
|
||||
var failures = items[2].getElementsByTagName('em')[0];
|
||||
var failuresLink = items[2].getElementsByTagName('a')[0];
|
||||
var duration = items[3].getElementsByTagName('em')[0];
|
||||
var canvas = stat.getElementsByTagName('canvas')[0];
|
||||
var report = fragment('<ul id="mocha-report"></ul>');
|
||||
var stack = [report];
|
||||
var progress;
|
||||
var ctx;
|
||||
var root = document.getElementById('mocha');
|
||||
|
||||
if (canvas.getContext) {
|
||||
var ratio = window.devicePixelRatio || 1;
|
||||
canvas.style.width = canvas.width;
|
||||
canvas.style.height = canvas.height;
|
||||
canvas.width *= ratio;
|
||||
canvas.height *= ratio;
|
||||
ctx = canvas.getContext('2d');
|
||||
ctx.scale(ratio, ratio);
|
||||
progress = new Progress();
|
||||
}
|
||||
|
||||
if (!root) {
|
||||
return error('#mocha div missing, add it to your document');
|
||||
}
|
||||
|
||||
// pass toggle
|
||||
on(passesLink, 'click', function(evt) {
|
||||
evt.preventDefault();
|
||||
unhide();
|
||||
var name = /pass/.test(report.className) ? '' : ' pass';
|
||||
report.className = report.className.replace(/fail|pass/g, '') + name;
|
||||
if (report.className.trim()) {
|
||||
hideSuitesWithout('test pass');
|
||||
}
|
||||
});
|
||||
|
||||
// failure toggle
|
||||
on(failuresLink, 'click', function(evt) {
|
||||
evt.preventDefault();
|
||||
unhide();
|
||||
var name = /fail/.test(report.className) ? '' : ' fail';
|
||||
report.className = report.className.replace(/fail|pass/g, '') + name;
|
||||
if (report.className.trim()) {
|
||||
hideSuitesWithout('test fail');
|
||||
}
|
||||
});
|
||||
|
||||
root.appendChild(stat);
|
||||
root.appendChild(report);
|
||||
|
||||
if (progress) {
|
||||
progress.size(40);
|
||||
}
|
||||
|
||||
runner.on(EVENT_SUITE_BEGIN, function(suite) {
|
||||
if (suite.root) {
|
||||
return;
|
||||
}
|
||||
|
||||
// suite
|
||||
var url = self.suiteURL(suite);
|
||||
var el = fragment(
|
||||
'<li class="suite"><h1><a href="%s">%s</a></h1></li>',
|
||||
url,
|
||||
escape(suite.title)
|
||||
);
|
||||
|
||||
// container
|
||||
stack[0].appendChild(el);
|
||||
stack.unshift(document.createElement('ul'));
|
||||
el.appendChild(stack[0]);
|
||||
});
|
||||
|
||||
runner.on(EVENT_SUITE_END, function(suite) {
|
||||
if (suite.root) {
|
||||
updateStats();
|
||||
return;
|
||||
}
|
||||
stack.shift();
|
||||
});
|
||||
|
||||
runner.on(EVENT_TEST_PASS, function(test) {
|
||||
var url = self.testURL(test);
|
||||
var markup =
|
||||
'<li class="test pass %e"><h2>%e<span class="duration">%ems</span> ' +
|
||||
'<a href="%s" class="replay">' +
|
||||
playIcon +
|
||||
'</a></h2></li>';
|
||||
var el = fragment(markup, test.speed, test.title, test.duration, url);
|
||||
self.addCodeToggle(el, test.body);
|
||||
appendToStack(el);
|
||||
updateStats();
|
||||
});
|
||||
|
||||
runner.on(EVENT_TEST_FAIL, function(test) {
|
||||
var el = fragment(
|
||||
'<li class="test fail"><h2>%e <a href="%e" class="replay">' +
|
||||
playIcon +
|
||||
'</a></h2></li>',
|
||||
test.title,
|
||||
self.testURL(test)
|
||||
);
|
||||
var stackString; // Note: Includes leading newline
|
||||
var message = test.err.toString();
|
||||
|
||||
// <=IE7 stringifies to [Object Error]. Since it can be overloaded, we
|
||||
// check for the result of the stringifying.
|
||||
if (message === '[object Error]') {
|
||||
message = test.err.message;
|
||||
}
|
||||
|
||||
if (test.err.stack) {
|
||||
var indexOfMessage = test.err.stack.indexOf(test.err.message);
|
||||
if (indexOfMessage === -1) {
|
||||
stackString = test.err.stack;
|
||||
} else {
|
||||
stackString = test.err.stack.substr(
|
||||
test.err.message.length + indexOfMessage
|
||||
);
|
||||
}
|
||||
} else if (test.err.sourceURL && test.err.line !== undefined) {
|
||||
// Safari doesn't give you a stack. Let's at least provide a source line.
|
||||
stackString = '\n(' + test.err.sourceURL + ':' + test.err.line + ')';
|
||||
}
|
||||
|
||||
stackString = stackString || '';
|
||||
|
||||
if (test.err.htmlMessage && stackString) {
|
||||
el.appendChild(
|
||||
fragment(
|
||||
'<div class="html-error">%s\n<pre class="error">%e</pre></div>',
|
||||
test.err.htmlMessage,
|
||||
stackString
|
||||
)
|
||||
);
|
||||
} else if (test.err.htmlMessage) {
|
||||
el.appendChild(
|
||||
fragment('<div class="html-error">%s</div>', test.err.htmlMessage)
|
||||
);
|
||||
} else {
|
||||
el.appendChild(
|
||||
fragment('<pre class="error">%e%e</pre>', message, stackString)
|
||||
);
|
||||
}
|
||||
|
||||
self.addCodeToggle(el, test.body);
|
||||
appendToStack(el);
|
||||
updateStats();
|
||||
});
|
||||
|
||||
runner.on(EVENT_TEST_PENDING, function(test) {
|
||||
var el = fragment(
|
||||
'<li class="test pass pending"><h2>%e</h2></li>',
|
||||
test.title
|
||||
);
|
||||
appendToStack(el);
|
||||
updateStats();
|
||||
});
|
||||
|
||||
function appendToStack(el) {
|
||||
// Don't call .appendChild if #mocha-report was already .shift()'ed off the stack.
|
||||
if (stack[0]) {
|
||||
stack[0].appendChild(el);
|
||||
}
|
||||
}
|
||||
|
||||
function updateStats() {
|
||||
// TODO: add to stats
|
||||
var percent = ((stats.tests / runner.total) * 100) | 0;
|
||||
if (progress) {
|
||||
progress.update(percent).draw(ctx);
|
||||
}
|
||||
|
||||
// update stats
|
||||
var ms = new Date() - stats.start;
|
||||
text(passes, stats.passes);
|
||||
text(failures, stats.failures);
|
||||
text(duration, (ms / 1000).toFixed(2));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a URL, preserving querystring ("search") parameters.
|
||||
*
|
||||
* @param {string} s
|
||||
* @return {string} A new URL.
|
||||
*/
|
||||
function makeUrl(s) {
|
||||
var search = window.location.search;
|
||||
|
||||
// Remove previous grep query parameter if present
|
||||
if (search) {
|
||||
search = search.replace(/[?&]grep=[^&\s]*/g, '').replace(/^&/, '?');
|
||||
}
|
||||
|
||||
return (
|
||||
window.location.pathname +
|
||||
(search ? search + '&' : '?') +
|
||||
'grep=' +
|
||||
encodeURIComponent(escapeRe(s))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide suite URL.
|
||||
*
|
||||
* @param {Object} [suite]
|
||||
*/
|
||||
HTML.prototype.suiteURL = function(suite) {
|
||||
return makeUrl(suite.fullTitle());
|
||||
};
|
||||
|
||||
/**
|
||||
* Provide test URL.
|
||||
*
|
||||
* @param {Object} [test]
|
||||
*/
|
||||
HTML.prototype.testURL = function(test) {
|
||||
return makeUrl(test.fullTitle());
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds code toggle functionality for the provided test's list element.
|
||||
*
|
||||
* @param {HTMLLIElement} el
|
||||
* @param {string} contents
|
||||
*/
|
||||
HTML.prototype.addCodeToggle = function(el, contents) {
|
||||
var h2 = el.getElementsByTagName('h2')[0];
|
||||
|
||||
on(h2, 'click', function() {
|
||||
pre.style.display = pre.style.display === 'none' ? 'block' : 'none';
|
||||
});
|
||||
|
||||
var pre = fragment('<pre><code>%e</code></pre>', utils.clean(contents));
|
||||
el.appendChild(pre);
|
||||
pre.style.display = 'none';
|
||||
};
|
||||
|
||||
/**
|
||||
* Display error `msg`.
|
||||
*
|
||||
* @param {string} msg
|
||||
*/
|
||||
function error(msg) {
|
||||
document.body.appendChild(fragment('<div id="mocha-error">%s</div>', msg));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a DOM fragment from `html`.
|
||||
*
|
||||
* @param {string} html
|
||||
*/
|
||||
function fragment(html) {
|
||||
var args = arguments;
|
||||
var div = document.createElement('div');
|
||||
var i = 1;
|
||||
|
||||
div.innerHTML = html.replace(/%([se])/g, function(_, type) {
|
||||
switch (type) {
|
||||
case 's':
|
||||
return String(args[i++]);
|
||||
case 'e':
|
||||
return escape(args[i++]);
|
||||
// no default
|
||||
}
|
||||
});
|
||||
|
||||
return div.firstChild;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for suites that do not have elements
|
||||
* with `classname`, and hide them.
|
||||
*
|
||||
* @param {text} classname
|
||||
*/
|
||||
function hideSuitesWithout(classname) {
|
||||
var suites = document.getElementsByClassName('suite');
|
||||
for (var i = 0; i < suites.length; i++) {
|
||||
var els = suites[i].getElementsByClassName(classname);
|
||||
if (!els.length) {
|
||||
suites[i].className += ' hidden';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unhide .hidden suites.
|
||||
*/
|
||||
function unhide() {
|
||||
var els = document.getElementsByClassName('suite hidden');
|
||||
while (els.length > 0) {
|
||||
els[0].className = els[0].className.replace('suite hidden', 'suite');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an element's text contents.
|
||||
*
|
||||
* @param {HTMLElement} el
|
||||
* @param {string} contents
|
||||
*/
|
||||
function text(el, contents) {
|
||||
if (el.textContent) {
|
||||
el.textContent = contents;
|
||||
} else {
|
||||
el.innerText = contents;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen on `event` with callback `fn`.
|
||||
*/
|
||||
function on(el, event, fn) {
|
||||
if (el.addEventListener) {
|
||||
el.addEventListener(event, fn, false);
|
||||
} else {
|
||||
el.attachEvent('on' + event, fn);
|
||||
}
|
||||
}
|
||||
|
||||
HTML.browserOnly = true;
|
19
node_modules/mocha/lib/reporters/index.js
generated
vendored
Normal file
19
node_modules/mocha/lib/reporters/index.js
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
'use strict';
|
||||
|
||||
// Alias exports to a their normalized format Mocha#reporter to prevent a need
|
||||
// for dynamic (try/catch) requires, which Browserify doesn't handle.
|
||||
exports.Base = exports.base = require('./base');
|
||||
exports.Dot = exports.dot = require('./dot');
|
||||
exports.Doc = exports.doc = require('./doc');
|
||||
exports.TAP = exports.tap = require('./tap');
|
||||
exports.JSON = exports.json = require('./json');
|
||||
exports.HTML = exports.html = require('./html');
|
||||
exports.List = exports.list = require('./list');
|
||||
exports.Min = exports.min = require('./min');
|
||||
exports.Spec = exports.spec = require('./spec');
|
||||
exports.Nyan = exports.nyan = require('./nyan');
|
||||
exports.XUnit = exports.xunit = require('./xunit');
|
||||
exports.Markdown = exports.markdown = require('./markdown');
|
||||
exports.Progress = exports.progress = require('./progress');
|
||||
exports.Landing = exports.landing = require('./landing');
|
||||
exports.JSONStream = exports['json-stream'] = require('./json-stream');
|
92
node_modules/mocha/lib/reporters/json-stream.js
generated
vendored
Normal file
92
node_modules/mocha/lib/reporters/json-stream.js
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
'use strict';
|
||||
/**
|
||||
* @module JSONStream
|
||||
*/
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Base = require('./base');
|
||||
var constants = require('../runner').constants;
|
||||
var EVENT_TEST_PASS = constants.EVENT_TEST_PASS;
|
||||
var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL;
|
||||
var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN;
|
||||
var EVENT_RUN_END = constants.EVENT_RUN_END;
|
||||
|
||||
/**
|
||||
* Expose `JSONStream`.
|
||||
*/
|
||||
|
||||
exports = module.exports = JSONStream;
|
||||
|
||||
/**
|
||||
* Constructs a new `JSONStream` reporter instance.
|
||||
*
|
||||
* @public
|
||||
* @class
|
||||
* @memberof Mocha.reporters
|
||||
* @extends Mocha.reporters.Base
|
||||
* @param {Runner} runner - Instance triggers reporter actions.
|
||||
* @param {Object} [options] - runner options
|
||||
*/
|
||||
function JSONStream(runner, options) {
|
||||
Base.call(this, runner, options);
|
||||
|
||||
var self = this;
|
||||
var total = runner.total;
|
||||
|
||||
runner.once(EVENT_RUN_BEGIN, function() {
|
||||
writeEvent(['start', {total: total}]);
|
||||
});
|
||||
|
||||
runner.on(EVENT_TEST_PASS, function(test) {
|
||||
writeEvent(['pass', clean(test)]);
|
||||
});
|
||||
|
||||
runner.on(EVENT_TEST_FAIL, function(test, err) {
|
||||
test = clean(test);
|
||||
test.err = err.message;
|
||||
test.stack = err.stack || null;
|
||||
writeEvent(['fail', test]);
|
||||
});
|
||||
|
||||
runner.once(EVENT_RUN_END, function() {
|
||||
writeEvent(['end', self.stats]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Mocha event to be written to the output stream.
|
||||
* @typedef {Array} JSONStream~MochaEvent
|
||||
*/
|
||||
|
||||
/**
|
||||
* Writes Mocha event to reporter output stream.
|
||||
*
|
||||
* @private
|
||||
* @param {JSONStream~MochaEvent} event - Mocha event to be output.
|
||||
*/
|
||||
function writeEvent(event) {
|
||||
process.stdout.write(JSON.stringify(event) + '\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object literal representation of `test`
|
||||
* free of cyclic properties, etc.
|
||||
*
|
||||
* @private
|
||||
* @param {Test} test - Instance used as data source.
|
||||
* @return {Object} object containing pared-down test instance data
|
||||
*/
|
||||
function clean(test) {
|
||||
return {
|
||||
title: test.title,
|
||||
fullTitle: test.fullTitle(),
|
||||
file: test.file,
|
||||
duration: test.duration,
|
||||
currentRetry: test.currentRetry(),
|
||||
speed: test.speed
|
||||
};
|
||||
}
|
||||
|
||||
JSONStream.description = 'newline delimited JSON events';
|
137
node_modules/mocha/lib/reporters/json.js
generated
vendored
Normal file
137
node_modules/mocha/lib/reporters/json.js
generated
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
'use strict';
|
||||
/**
|
||||
* @module JSON
|
||||
*/
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Base = require('./base');
|
||||
var constants = require('../runner').constants;
|
||||
var EVENT_TEST_PASS = constants.EVENT_TEST_PASS;
|
||||
var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL;
|
||||
var EVENT_TEST_END = constants.EVENT_TEST_END;
|
||||
var EVENT_RUN_END = constants.EVENT_RUN_END;
|
||||
var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING;
|
||||
|
||||
/**
|
||||
* Expose `JSON`.
|
||||
*/
|
||||
|
||||
exports = module.exports = JSONReporter;
|
||||
|
||||
/**
|
||||
* Constructs a new `JSON` reporter instance.
|
||||
*
|
||||
* @public
|
||||
* @class JSON
|
||||
* @memberof Mocha.reporters
|
||||
* @extends Mocha.reporters.Base
|
||||
* @param {Runner} runner - Instance triggers reporter actions.
|
||||
* @param {Object} [options] - runner options
|
||||
*/
|
||||
function JSONReporter(runner, options) {
|
||||
Base.call(this, runner, options);
|
||||
|
||||
var self = this;
|
||||
var tests = [];
|
||||
var pending = [];
|
||||
var failures = [];
|
||||
var passes = [];
|
||||
|
||||
runner.on(EVENT_TEST_END, function(test) {
|
||||
tests.push(test);
|
||||
});
|
||||
|
||||
runner.on(EVENT_TEST_PASS, function(test) {
|
||||
passes.push(test);
|
||||
});
|
||||
|
||||
runner.on(EVENT_TEST_FAIL, function(test) {
|
||||
failures.push(test);
|
||||
});
|
||||
|
||||
runner.on(EVENT_TEST_PENDING, function(test) {
|
||||
pending.push(test);
|
||||
});
|
||||
|
||||
runner.once(EVENT_RUN_END, function() {
|
||||
var obj = {
|
||||
stats: self.stats,
|
||||
tests: tests.map(clean),
|
||||
pending: pending.map(clean),
|
||||
failures: failures.map(clean),
|
||||
passes: passes.map(clean)
|
||||
};
|
||||
|
||||
runner.testResults = obj;
|
||||
|
||||
process.stdout.write(JSON.stringify(obj, null, 2));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a plain-object representation of `test`
|
||||
* free of cyclic properties etc.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} test
|
||||
* @return {Object}
|
||||
*/
|
||||
function clean(test) {
|
||||
var err = test.err || {};
|
||||
if (err instanceof Error) {
|
||||
err = errorJSON(err);
|
||||
}
|
||||
|
||||
return {
|
||||
title: test.title,
|
||||
fullTitle: test.fullTitle(),
|
||||
file: test.file,
|
||||
duration: test.duration,
|
||||
currentRetry: test.currentRetry(),
|
||||
speed: test.speed,
|
||||
err: cleanCycles(err)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces any circular references inside `obj` with '[object Object]'
|
||||
*
|
||||
* @private
|
||||
* @param {Object} obj
|
||||
* @return {Object}
|
||||
*/
|
||||
function cleanCycles(obj) {
|
||||
var cache = [];
|
||||
return JSON.parse(
|
||||
JSON.stringify(obj, function(key, value) {
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
if (cache.indexOf(value) !== -1) {
|
||||
// Instead of going in a circle, we'll print [object Object]
|
||||
return '' + value;
|
||||
}
|
||||
cache.push(value);
|
||||
}
|
||||
|
||||
return value;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform an Error object into a JSON object.
|
||||
*
|
||||
* @private
|
||||
* @param {Error} err
|
||||
* @return {Object}
|
||||
*/
|
||||
function errorJSON(err) {
|
||||
var res = {};
|
||||
Object.getOwnPropertyNames(err).forEach(function(key) {
|
||||
res[key] = err[key];
|
||||
}, err);
|
||||
return res;
|
||||
}
|
||||
|
||||
JSONReporter.description = 'single JSON object';
|
116
node_modules/mocha/lib/reporters/landing.js
generated
vendored
Normal file
116
node_modules/mocha/lib/reporters/landing.js
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
'use strict';
|
||||
/**
|
||||
* @module Landing
|
||||
*/
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Base = require('./base');
|
||||
var inherits = require('../utils').inherits;
|
||||
var constants = require('../runner').constants;
|
||||
var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN;
|
||||
var EVENT_RUN_END = constants.EVENT_RUN_END;
|
||||
var EVENT_TEST_END = constants.EVENT_TEST_END;
|
||||
var STATE_FAILED = require('../runnable').constants.STATE_FAILED;
|
||||
|
||||
var cursor = Base.cursor;
|
||||
var color = Base.color;
|
||||
|
||||
/**
|
||||
* Expose `Landing`.
|
||||
*/
|
||||
|
||||
exports = module.exports = Landing;
|
||||
|
||||
/**
|
||||
* Airplane color.
|
||||
*/
|
||||
|
||||
Base.colors.plane = 0;
|
||||
|
||||
/**
|
||||
* Airplane crash color.
|
||||
*/
|
||||
|
||||
Base.colors['plane crash'] = 31;
|
||||
|
||||
/**
|
||||
* Runway color.
|
||||
*/
|
||||
|
||||
Base.colors.runway = 90;
|
||||
|
||||
/**
|
||||
* Constructs a new `Landing` reporter instance.
|
||||
*
|
||||
* @public
|
||||
* @class
|
||||
* @memberof Mocha.reporters
|
||||
* @extends Mocha.reporters.Base
|
||||
* @param {Runner} runner - Instance triggers reporter actions.
|
||||
* @param {Object} [options] - runner options
|
||||
*/
|
||||
function Landing(runner, options) {
|
||||
Base.call(this, runner, options);
|
||||
|
||||
var self = this;
|
||||
var width = (Base.window.width * 0.75) | 0;
|
||||
var stream = process.stdout;
|
||||
|
||||
var plane = color('plane', '✈');
|
||||
var crashed = -1;
|
||||
var n = 0;
|
||||
var total = 0;
|
||||
|
||||
function runway() {
|
||||
var buf = Array(width).join('-');
|
||||
return ' ' + color('runway', buf);
|
||||
}
|
||||
|
||||
runner.on(EVENT_RUN_BEGIN, function() {
|
||||
stream.write('\n\n\n ');
|
||||
cursor.hide();
|
||||
});
|
||||
|
||||
runner.on(EVENT_TEST_END, function(test) {
|
||||
// check if the plane crashed
|
||||
var col = crashed === -1 ? ((width * ++n) / ++total) | 0 : crashed;
|
||||
// show the crash
|
||||
if (test.state === STATE_FAILED) {
|
||||
plane = color('plane crash', '✈');
|
||||
crashed = col;
|
||||
}
|
||||
|
||||
// render landing strip
|
||||
stream.write('\u001b[' + (width + 1) + 'D\u001b[2A');
|
||||
stream.write(runway());
|
||||
stream.write('\n ');
|
||||
stream.write(color('runway', Array(col).join('⋅')));
|
||||
stream.write(plane);
|
||||
stream.write(color('runway', Array(width - col).join('⋅') + '\n'));
|
||||
stream.write(runway());
|
||||
stream.write('\u001b[0m');
|
||||
});
|
||||
|
||||
runner.once(EVENT_RUN_END, function() {
|
||||
cursor.show();
|
||||
process.stdout.write('\n');
|
||||
self.epilogue();
|
||||
});
|
||||
|
||||
// if cursor is hidden when we ctrl-C, then it will remain hidden unless...
|
||||
process.once('SIGINT', function() {
|
||||
cursor.show();
|
||||
process.nextTick(function() {
|
||||
process.kill(process.pid, 'SIGINT');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherit from `Base.prototype`.
|
||||
*/
|
||||
inherits(Landing, Base);
|
||||
|
||||
Landing.description = 'Unicode landing strip';
|
78
node_modules/mocha/lib/reporters/list.js
generated
vendored
Normal file
78
node_modules/mocha/lib/reporters/list.js
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
'use strict';
|
||||
/**
|
||||
* @module List
|
||||
*/
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Base = require('./base');
|
||||
var inherits = require('../utils').inherits;
|
||||
var constants = require('../runner').constants;
|
||||
var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN;
|
||||
var EVENT_RUN_END = constants.EVENT_RUN_END;
|
||||
var EVENT_TEST_BEGIN = constants.EVENT_TEST_BEGIN;
|
||||
var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL;
|
||||
var EVENT_TEST_PASS = constants.EVENT_TEST_PASS;
|
||||
var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING;
|
||||
var color = Base.color;
|
||||
var cursor = Base.cursor;
|
||||
|
||||
/**
|
||||
* Expose `List`.
|
||||
*/
|
||||
|
||||
exports = module.exports = List;
|
||||
|
||||
/**
|
||||
* Constructs a new `List` reporter instance.
|
||||
*
|
||||
* @public
|
||||
* @class
|
||||
* @memberof Mocha.reporters
|
||||
* @extends Mocha.reporters.Base
|
||||
* @param {Runner} runner - Instance triggers reporter actions.
|
||||
* @param {Object} [options] - runner options
|
||||
*/
|
||||
function List(runner, options) {
|
||||
Base.call(this, runner, options);
|
||||
|
||||
var self = this;
|
||||
var n = 0;
|
||||
|
||||
runner.on(EVENT_RUN_BEGIN, function() {
|
||||
Base.consoleLog();
|
||||
});
|
||||
|
||||
runner.on(EVENT_TEST_BEGIN, function(test) {
|
||||
process.stdout.write(color('pass', ' ' + test.fullTitle() + ': '));
|
||||
});
|
||||
|
||||
runner.on(EVENT_TEST_PENDING, function(test) {
|
||||
var fmt = color('checkmark', ' -') + color('pending', ' %s');
|
||||
Base.consoleLog(fmt, test.fullTitle());
|
||||
});
|
||||
|
||||
runner.on(EVENT_TEST_PASS, function(test) {
|
||||
var fmt =
|
||||
color('checkmark', ' ' + Base.symbols.ok) +
|
||||
color('pass', ' %s: ') +
|
||||
color(test.speed, '%dms');
|
||||
cursor.CR();
|
||||
Base.consoleLog(fmt, test.fullTitle(), test.duration);
|
||||
});
|
||||
|
||||
runner.on(EVENT_TEST_FAIL, function(test) {
|
||||
cursor.CR();
|
||||
Base.consoleLog(color('fail', ' %d) %s'), ++n, test.fullTitle());
|
||||
});
|
||||
|
||||
runner.once(EVENT_RUN_END, self.epilogue.bind(self));
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherit from `Base.prototype`.
|
||||
*/
|
||||
inherits(List, Base);
|
||||
|
||||
List.description = 'like "spec" reporter but flat';
|
112
node_modules/mocha/lib/reporters/markdown.js
generated
vendored
Normal file
112
node_modules/mocha/lib/reporters/markdown.js
generated
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
'use strict';
|
||||
/**
|
||||
* @module Markdown
|
||||
*/
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Base = require('./base');
|
||||
var utils = require('../utils');
|
||||
var constants = require('../runner').constants;
|
||||
var EVENT_RUN_END = constants.EVENT_RUN_END;
|
||||
var EVENT_SUITE_BEGIN = constants.EVENT_SUITE_BEGIN;
|
||||
var EVENT_SUITE_END = constants.EVENT_SUITE_END;
|
||||
var EVENT_TEST_PASS = constants.EVENT_TEST_PASS;
|
||||
|
||||
/**
|
||||
* Constants
|
||||
*/
|
||||
|
||||
var SUITE_PREFIX = '$';
|
||||
|
||||
/**
|
||||
* Expose `Markdown`.
|
||||
*/
|
||||
|
||||
exports = module.exports = Markdown;
|
||||
|
||||
/**
|
||||
* Constructs a new `Markdown` reporter instance.
|
||||
*
|
||||
* @public
|
||||
* @class
|
||||
* @memberof Mocha.reporters
|
||||
* @extends Mocha.reporters.Base
|
||||
* @param {Runner} runner - Instance triggers reporter actions.
|
||||
* @param {Object} [options] - runner options
|
||||
*/
|
||||
function Markdown(runner, options) {
|
||||
Base.call(this, runner, options);
|
||||
|
||||
var level = 0;
|
||||
var buf = '';
|
||||
|
||||
function title(str) {
|
||||
return Array(level).join('#') + ' ' + str;
|
||||
}
|
||||
|
||||
function mapTOC(suite, obj) {
|
||||
var ret = obj;
|
||||
var key = SUITE_PREFIX + suite.title;
|
||||
|
||||
obj = obj[key] = obj[key] || {suite: suite};
|
||||
suite.suites.forEach(function(suite) {
|
||||
mapTOC(suite, obj);
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function stringifyTOC(obj, level) {
|
||||
++level;
|
||||
var buf = '';
|
||||
var link;
|
||||
for (var key in obj) {
|
||||
if (key === 'suite') {
|
||||
continue;
|
||||
}
|
||||
if (key !== SUITE_PREFIX) {
|
||||
link = ' - [' + key.substring(1) + ']';
|
||||
link += '(#' + utils.slug(obj[key].suite.fullTitle()) + ')\n';
|
||||
buf += Array(level).join(' ') + link;
|
||||
}
|
||||
buf += stringifyTOC(obj[key], level);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
function generateTOC(suite) {
|
||||
var obj = mapTOC(suite, {});
|
||||
return stringifyTOC(obj, 0);
|
||||
}
|
||||
|
||||
generateTOC(runner.suite);
|
||||
|
||||
runner.on(EVENT_SUITE_BEGIN, function(suite) {
|
||||
++level;
|
||||
var slug = utils.slug(suite.fullTitle());
|
||||
buf += '<a name="' + slug + '"></a>' + '\n';
|
||||
buf += title(suite.title) + '\n';
|
||||
});
|
||||
|
||||
runner.on(EVENT_SUITE_END, function() {
|
||||
--level;
|
||||
});
|
||||
|
||||
runner.on(EVENT_TEST_PASS, function(test) {
|
||||
var code = utils.clean(test.body);
|
||||
buf += test.title + '.\n';
|
||||
buf += '\n```js\n';
|
||||
buf += code + '\n';
|
||||
buf += '```\n\n';
|
||||
});
|
||||
|
||||
runner.once(EVENT_RUN_END, function() {
|
||||
process.stdout.write('# TOC\n');
|
||||
process.stdout.write(generateTOC(runner.suite));
|
||||
process.stdout.write(buf);
|
||||
});
|
||||
}
|
||||
|
||||
Markdown.description = 'GitHub Flavored Markdown';
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user