Big Boy Updates

This commit is contained in:
Luke Hagar
2022-07-24 22:11:51 -05:00
parent 5942c95bc3
commit 480884abcd
1110 changed files with 178658 additions and 180 deletions

39
node_modules/mocha/lib/browser/highlight-tags.js generated vendored Normal file
View 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, '&lt;')
.replace(/>/g, '&gt;')
.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
View 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
View 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
View 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>

89
node_modules/mocha/lib/cli/cli.js generated vendored Executable file
View File

@@ -0,0 +1,89 @@
#!/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)`
* @param {object} [mochaArgs] - Object of already parsed Mocha arguments (by bin/mocha)
*/
exports.main = (argv = process.argv.slice(2), mochaArgs) => {
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 = mochaArgs || 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
View 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
View File

@@ -0,0 +1,13 @@
'use strict';
/**
* Exports Yargs commands
* @see https://github.com/yargs/yargs/blob/main/docs/advanced.md
* @private
* @module
*/
exports.init = require('./init');
// default command
exports.run = require('./run');

100
node_modules/mocha/lib/cli/config.js generated vendored Normal file
View File

@@ -0,0 +1,100 @@
'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'
];
/**
* 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 => {
let cwdFilepath;
try {
debug('parsers: load cwd-relative path: "%s"', path.resolve(filepath));
cwdFilepath = require.resolve(path.resolve(filepath)); // evtl. throws
return require(cwdFilepath);
} catch (err) {
if (cwdFilepath) throw err;
debug('parsers: retry load as module-relative path: "%s"', filepath);
return require(filepath);
}
},
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
View File

@@ -0,0 +1,3 @@
'use strict';
module.exports = require('./cli');

36
node_modules/mocha/lib/cli/init.js generated vendored Normal file
View 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);
};

145
node_modules/mocha/lib/cli/lookup-files.js generated vendored Normal file
View File

@@ -0,0 +1,145 @@
'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 errors = require('../errors');
var createNoFilesMatchPatternError = errors.createNoFilesMatchPatternError;
var createMissingArgumentError = errors.createMissingArgumentError;
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 "${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(
`Argument '${extensions}' required when argument '${filepath}' is a directory`,
'extensions',
'array'
);
}
if (
!stat.isFile() ||
!hasMatchingExtname(pathname, extensions) ||
isHiddenOnUnix(pathname)
) {
return;
}
files.push(pathname);
});
return files;
};

85
node_modules/mocha/lib/cli/node-flags.js generated vendored Normal file
View File

@@ -0,0 +1,85 @@
'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.
* 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 => (arg === ' ' ? '=' : arg))
.join('')
.split(' ')
: [];
};

69
node_modules/mocha/lib/cli/one-and-dones.js generated vendored Normal file
View File

@@ -0,0 +1,69 @@
'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 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(
` ${key.padEnd(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
View 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
View 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('../nodejs/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"|"ui"} pluginType - Type of plugin.
* @param {Object} [map] - Used as a cache of sorts;
* `Mocha.reporters` where each key corresponds to a reporter name,
* `Mocha.interfaces` where each key corresponds to an interface 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]) {
let foundId;
try {
foundId = require.resolve(pluginId);
map[pluginId] = require(foundId);
} catch (err) {
if (foundId) throw createUnknownError(err);
// Try to load reporters from a cwd-relative path
try {
map[pluginId] = require(path.resolve(pluginId));
} catch (e) {
throw createUnknownError(e);
}
}
}
};

115
node_modules/mocha/lib/cli/run-option-metadata.js generated vendored Normal file
View File

@@ -0,0 +1,115 @@
'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',
'node-option',
'reporter-option',
'require',
'spec',
'watch-files',
'watch-ignore'
],
boolean: [
'allow-uncaught',
'async-only',
'bail',
'check-leaks',
'color',
'delay',
'diff',
'dry-run',
'exit',
'fail-zero',
'forbid-only',
'forbid-pending',
'full-trace',
'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'],
ignore: ['exclude'],
invert: ['i'],
jobs: ['j'],
'no-colors': ['C'],
'node-option': ['n'],
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(/^--?/, ''));
};

375
node_modules/mocha/lib/cli/run.js generated vendored Normal file
View File

@@ -0,0 +1,375 @@
'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
},
'dry-run': {
description: 'Report tests without executing them',
group: GROUPS.RULES
},
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
},
'fail-zero': {
description: 'Fail test run if no test(s) encountered',
group: GROUPS.RULES
},
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
},
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
},
'node-option': {
description: 'Node or V8 option (no leading "--")',
group: GROUPS.CONFIG
},
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://github.com/mochajs/mocha/wiki/compilers-deprecation 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);
}
};

377
node_modules/mocha/lib/cli/watch-run.js generated vendored Normal file
View File

@@ -0,0 +1,377 @@
'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.
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.
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
View 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
View File

@@ -0,0 +1,563 @@
'use strict';
const {format} = require('util');
/**
* Contains error codes, factory functions to create throwable error objects,
* and warning/deprecation functions.
* @module
*/
/**
* process.emitWarning or a polyfill
* @see https://nodejs.org/api/process.html#process_process_emitwarning_warning_options
* @ignore
*/
const emitWarning = (msg, type) => {
if (process.emitWarning) {
process.emitWarning(msg, type);
} else {
/* istanbul ignore next */
process.nextTick(function () {
console.warn(type + ': ' + msg);
});
}
};
/**
* Show a deprecation warning. Each distinct message is only displayed once.
* Ignores empty messages.
*
* @param {string} [msg] - Warning to print
* @private
*/
const deprecate = msg => {
msg = String(msg);
if (msg && !deprecate.cache[msg]) {
deprecate.cache[msg] = true;
emitWarning(msg, 'DeprecationWarning');
}
};
deprecate.cache = {};
/**
* Show a generic warning.
* Ignores empty messages.
*
* @param {string} [msg] - Warning to print
* @private
*/
const warn = msg => {
if (msg) {
emitWarning(msg);
}
};
/**
* When Mocha throws exceptions (or rejects `Promise`s), it attempts to assign a `code` property to the `Error` object, for easier handling. These are the potential values of `code`.
* @public
* @namespace
* @memberof module:lib/errors
*/
var constants = {
/**
* An unrecoverable error.
* @constant
* @default
*/
FATAL: 'ERR_MOCHA_FATAL',
/**
* The type of an argument to a function call is invalid
* @constant
* @default
*/
INVALID_ARG_TYPE: 'ERR_MOCHA_INVALID_ARG_TYPE',
/**
* The value of an argument to a function call is invalid
* @constant
* @default
*/
INVALID_ARG_VALUE: 'ERR_MOCHA_INVALID_ARG_VALUE',
/**
* Something was thrown, but it wasn't an `Error`
* @constant
* @default
*/
INVALID_EXCEPTION: 'ERR_MOCHA_INVALID_EXCEPTION',
/**
* An interface (e.g., `Mocha.interfaces`) is unknown or invalid
* @constant
* @default
*/
INVALID_INTERFACE: 'ERR_MOCHA_INVALID_INTERFACE',
/**
* A reporter (.e.g, `Mocha.reporters`) is unknown or invalid
* @constant
* @default
*/
INVALID_REPORTER: 'ERR_MOCHA_INVALID_REPORTER',
/**
* `done()` was called twice in a `Test` or `Hook` callback
* @constant
* @default
*/
MULTIPLE_DONE: 'ERR_MOCHA_MULTIPLE_DONE',
/**
* No files matched the pattern provided by the user
* @constant
* @default
*/
NO_FILES_MATCH_PATTERN: 'ERR_MOCHA_NO_FILES_MATCH_PATTERN',
/**
* Known, but unsupported behavior of some kind
* @constant
* @default
*/
UNSUPPORTED: 'ERR_MOCHA_UNSUPPORTED',
/**
* Invalid state transition occurring in `Mocha` instance
* @constant
* @default
*/
INSTANCE_ALREADY_RUNNING: 'ERR_MOCHA_INSTANCE_ALREADY_RUNNING',
/**
* Invalid state transition occurring in `Mocha` instance
* @constant
* @default
*/
INSTANCE_ALREADY_DISPOSED: 'ERR_MOCHA_INSTANCE_ALREADY_DISPOSED',
/**
* Use of `only()` w/ `--forbid-only` results in this error.
* @constant
* @default
*/
FORBIDDEN_EXCLUSIVITY: 'ERR_MOCHA_FORBIDDEN_EXCLUSIVITY',
/**
* To be thrown when a user-defined plugin implementation (e.g., `mochaHooks`) is invalid
* @constant
* @default
*/
INVALID_PLUGIN_IMPLEMENTATION: 'ERR_MOCHA_INVALID_PLUGIN_IMPLEMENTATION',
/**
* To be thrown when a builtin or third-party plugin definition (the _definition_ of `mochaHooks`) is invalid
* @constant
* @default
*/
INVALID_PLUGIN_DEFINITION: 'ERR_MOCHA_INVALID_PLUGIN_DEFINITION',
/**
* When a runnable exceeds its allowed run time.
* @constant
* @default
*/
TIMEOUT: 'ERR_MOCHA_TIMEOUT',
/**
* Input file is not able to be parsed
* @constant
* @default
*/
UNPARSABLE_FILE: 'ERR_MOCHA_UNPARSABLE_FILE'
};
/**
* A set containing all string values of all Mocha error constants, for use by {@link isMochaError}.
* @private
*/
const MOCHA_ERRORS = new Set(Object.values(constants));
/**
* Creates an error object to be thrown when no files to be tested could be found using specified pattern.
*
* @public
* @static
* @param {string} message - Error message to be displayed.
* @param {string} pattern - User-specified argument value.
* @returns {Error} instance detailing the error condition
*/
function createNoFilesMatchPatternError(message, pattern) {
var err = new Error(message);
err.code = constants.NO_FILES_MATCH_PATTERN;
err.pattern = pattern;
return err;
}
/**
* Creates an error object to be thrown when the reporter specified in the options was not found.
*
* @public
* @param {string} message - Error message to be displayed.
* @param {string} reporter - User-specified reporter value.
* @returns {Error} instance detailing the error condition
*/
function createInvalidReporterError(message, reporter) {
var err = new TypeError(message);
err.code = constants.INVALID_REPORTER;
err.reporter = reporter;
return err;
}
/**
* Creates an error object to be thrown when the interface specified in the options was not found.
*
* @public
* @static
* @param {string} message - Error message to be displayed.
* @param {string} ui - User-specified interface value.
* @returns {Error} instance detailing the error condition
*/
function createInvalidInterfaceError(message, ui) {
var err = new Error(message);
err.code = constants.INVALID_INTERFACE;
err.interface = ui;
return err;
}
/**
* Creates an error object to be thrown when a behavior, option, or parameter is unsupported.
*
* @public
* @static
* @param {string} message - Error message to be displayed.
* @returns {Error} instance detailing the error condition
*/
function createUnsupportedError(message) {
var err = new Error(message);
err.code = constants.UNSUPPORTED;
return err;
}
/**
* Creates an error object to be thrown when an argument is missing.
*
* @public
* @static
* @param {string} message - Error message to be displayed.
* @param {string} argument - Argument name.
* @param {string} expected - Expected argument datatype.
* @returns {Error} instance detailing the error condition
*/
function createMissingArgumentError(message, argument, expected) {
return createInvalidArgumentTypeError(message, argument, expected);
}
/**
* Creates an error object to be thrown when an argument did not use the supported type
*
* @public
* @static
* @param {string} message - Error message to be displayed.
* @param {string} argument - Argument name.
* @param {string} expected - Expected argument datatype.
* @returns {Error} instance detailing the error condition
*/
function createInvalidArgumentTypeError(message, argument, expected) {
var err = new TypeError(message);
err.code = constants.INVALID_ARG_TYPE;
err.argument = argument;
err.expected = expected;
err.actual = typeof argument;
return err;
}
/**
* Creates an error object to be thrown when an argument did not use the supported value
*
* @public
* @static
* @param {string} message - Error message to be displayed.
* @param {string} argument - Argument name.
* @param {string} value - Argument value.
* @param {string} [reason] - Why value is invalid.
* @returns {Error} instance detailing the error condition
*/
function createInvalidArgumentValueError(message, argument, value, reason) {
var err = new TypeError(message);
err.code = constants.INVALID_ARG_VALUE;
err.argument = argument;
err.value = value;
err.reason = typeof reason !== 'undefined' ? reason : 'is invalid';
return err;
}
/**
* Creates an error object to be thrown when an exception was caught, but the `Error` is falsy or undefined.
*
* @public
* @static
* @param {string} message - Error message to be displayed.
* @returns {Error} instance detailing the error condition
*/
function createInvalidExceptionError(message, value) {
var err = new Error(message);
err.code = constants.INVALID_EXCEPTION;
err.valueType = typeof value;
err.value = value;
return err;
}
/**
* Creates an error object to be thrown when an unrecoverable error occurs.
*
* @public
* @static
* @param {string} message - Error message to be displayed.
* @returns {Error} instance detailing the error condition
*/
function createFatalError(message, value) {
var err = new Error(message);
err.code = constants.FATAL;
err.valueType = typeof value;
err.value = value;
return err;
}
/**
* Dynamically creates a plugin-type-specific error based on plugin type
* @param {string} message - Error message
* @param {"reporter"|"ui"} pluginType - Plugin type. Future: expand as needed
* @param {string} [pluginId] - Name/path of plugin, if any
* @throws When `pluginType` is not known
* @public
* @static
* @returns {Error}
*/
function createInvalidLegacyPluginError(message, pluginType, pluginId) {
switch (pluginType) {
case 'reporter':
return createInvalidReporterError(message, pluginId);
case 'ui':
return createInvalidInterfaceError(message, pluginId);
default:
throw new Error('unknown pluginType "' + pluginType + '"');
}
}
/**
* **DEPRECATED**. Use {@link createInvalidLegacyPluginError} instead Dynamically creates a plugin-type-specific error based on plugin type
* @deprecated
* @param {string} message - Error message
* @param {"reporter"|"interface"} pluginType - Plugin type. Future: expand as needed
* @param {string} [pluginId] - Name/path of plugin, if any
* @throws When `pluginType` is not known
* @public
* @static
* @returns {Error}
*/
function createInvalidPluginError(...args) {
deprecate('Use createInvalidLegacyPluginError() instead');
return createInvalidLegacyPluginError(...args);
}
/**
* Creates an error object to be thrown when a mocha object's `run` method is executed while it is already disposed.
* @param {string} message The error message to be displayed.
* @param {boolean} cleanReferencesAfterRun the value of `cleanReferencesAfterRun`
* @param {Mocha} instance the mocha instance that throw this error
* @static
*/
function createMochaInstanceAlreadyDisposedError(
message,
cleanReferencesAfterRun,
instance
) {
var err = new Error(message);
err.code = constants.INSTANCE_ALREADY_DISPOSED;
err.cleanReferencesAfterRun = cleanReferencesAfterRun;
err.instance = instance;
return err;
}
/**
* Creates an error object to be thrown when a mocha object's `run` method is called while a test run is in progress.
* @param {string} message The error message to be displayed.
* @static
* @public
*/
function createMochaInstanceAlreadyRunningError(message, instance) {
var err = new Error(message);
err.code = constants.INSTANCE_ALREADY_RUNNING;
err.instance = instance;
return err;
}
/**
* Creates an error object to be thrown when done() is called multiple times in a test
*
* @public
* @param {Runnable} runnable - Original runnable
* @param {Error} [originalErr] - Original error, if any
* @returns {Error} instance detailing the error condition
* @static
*/
function createMultipleDoneError(runnable, originalErr) {
var title;
try {
title = format('<%s>', runnable.fullTitle());
if (runnable.parent.root) {
title += ' (of root suite)';
}
} catch (ignored) {
title = format('<%s> (of unknown suite)', runnable.title);
}
var message = format(
'done() called multiple times in %s %s',
runnable.type ? runnable.type : 'unknown runnable',
title
);
if (runnable.file) {
message += format(' of file %s', runnable.file);
}
if (originalErr) {
message += format('; in addition, done() received error: %s', originalErr);
}
var err = new Error(message);
err.code = constants.MULTIPLE_DONE;
err.valueType = typeof originalErr;
err.value = originalErr;
return err;
}
/**
* Creates an error object to be thrown when `.only()` is used with
* `--forbid-only`.
* @static
* @public
* @param {Mocha} mocha - Mocha instance
* @returns {Error} Error with code {@link constants.FORBIDDEN_EXCLUSIVITY}
*/
function createForbiddenExclusivityError(mocha) {
var err = new Error(
mocha.isWorker
? '`.only` is not supported in parallel mode'
: '`.only` forbidden by --forbid-only'
);
err.code = constants.FORBIDDEN_EXCLUSIVITY;
return err;
}
/**
* Creates an error object to be thrown when a plugin definition is invalid
* @static
* @param {string} msg - Error message
* @param {PluginDefinition} [pluginDef] - Problematic plugin definition
* @public
* @returns {Error} Error with code {@link constants.INVALID_PLUGIN_DEFINITION}
*/
function createInvalidPluginDefinitionError(msg, pluginDef) {
const err = new Error(msg);
err.code = constants.INVALID_PLUGIN_DEFINITION;
err.pluginDef = pluginDef;
return err;
}
/**
* Creates an error object to be thrown when a plugin implementation (user code) is invalid
* @static
* @param {string} msg - Error message
* @param {Object} [opts] - Plugin definition and user-supplied implementation
* @param {PluginDefinition} [opts.pluginDef] - Plugin Definition
* @param {*} [opts.pluginImpl] - Plugin Implementation (user-supplied)
* @public
* @returns {Error} Error with code {@link constants.INVALID_PLUGIN_DEFINITION}
*/
function createInvalidPluginImplementationError(
msg,
{pluginDef, pluginImpl} = {}
) {
const err = new Error(msg);
err.code = constants.INVALID_PLUGIN_IMPLEMENTATION;
err.pluginDef = pluginDef;
err.pluginImpl = pluginImpl;
return err;
}
/**
* Creates an error object to be thrown when a runnable exceeds its allowed run time.
* @static
* @param {string} msg - Error message
* @param {number} [timeout] - Timeout in ms
* @param {string} [file] - File, if given
* @returns {MochaTimeoutError}
*/
function createTimeoutError(msg, timeout, file) {
const err = new Error(msg);
err.code = constants.TIMEOUT;
err.timeout = timeout;
err.file = file;
return err;
}
/**
* Creates an error object to be thrown when file is unparsable
* @public
* @static
* @param {string} message - Error message to be displayed.
* @param {string} filename - File name
* @returns {Error} Error with code {@link constants.UNPARSABLE_FILE}
*/
function createUnparsableFileError(message, filename) {
var err = new Error(message);
err.code = constants.UNPARSABLE_FILE;
return err;
}
/**
* Returns `true` if an error came out of Mocha.
* _Can suffer from false negatives, but not false positives._
* @static
* @public
* @param {*} err - Error, or anything
* @returns {boolean}
*/
const isMochaError = err =>
Boolean(err && typeof err === 'object' && MOCHA_ERRORS.has(err.code));
module.exports = {
constants,
createFatalError,
createForbiddenExclusivityError,
createInvalidArgumentTypeError,
createInvalidArgumentValueError,
createInvalidExceptionError,
createInvalidInterfaceError,
createInvalidLegacyPluginError,
createInvalidPluginDefinitionError,
createInvalidPluginError,
createInvalidPluginImplementationError,
createInvalidReporterError,
createMissingArgumentError,
createMochaInstanceAlreadyDisposedError,
createMochaInstanceAlreadyRunningError,
createMultipleDoneError,
createNoFilesMatchPatternError,
createTimeoutError,
createUnparsableFileError,
createUnsupportedError,
deprecate,
isMochaError,
warn
};
/**
* The error thrown when a Runnable times out
* @memberof module:lib/errors
* @typedef {Error} MochaTimeoutError
* @property {constants.TIMEOUT} code - Error code
* @property {number?} timeout Timeout in ms
* @property {string?} file Filepath, if given
*/

89
node_modules/mocha/lib/hook.js generated vendored Normal file
View File

@@ -0,0 +1,89 @@
'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 {
$$currentRetry: this.currentRetry(),
$$fullTitle: this.fullTitle(),
$$isPending: Boolean(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
}
}
: {},
duration: this.duration,
file: this.file,
parent: {
$$fullTitle: this.parent.fullTitle(),
[MOCHA_ID_PROP_NAME]: this.parent.id
},
state: this.state,
title: this.title,
type: this.type,
[MOCHA_ID_PROP_NAME]: this.id
};
};

114
node_modules/mocha/lib/interfaces/bdd.js generated vendored Normal file
View File

@@ -0,0 +1,114 @@
'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
View 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
View 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
View 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
View 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
View 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"';

1313
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
View 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"]
}

188
node_modules/mocha/lib/nodejs/buffered-worker-pool.js generated vendored Normal file
View File

@@ -0,0 +1,188 @@
/**
* 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(' ')
);
let counter = 0;
const onCreateWorker = ({forkOpts}) => {
return {
forkOpts: {
...forkOpts,
// adds an incremental id to all workers, which can be useful to allocate resources for each process
env: {...process.env, MOCHA_WORKER_ID: counter++}
}
};
};
this.options = {
...WORKER_POOL_DEFAULT_OPTS,
...opts,
maxWorkers,
onCreateWorker
};
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;

94
node_modules/mocha/lib/nodejs/esm-utils.js generated vendored Normal file
View File

@@ -0,0 +1,94 @@
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.
// TODO: remove 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);
}
try {
return dealWithExports(await formattedImport(file));
} catch (err) {
if (
err.code === 'ERR_MODULE_NOT_FOUND' ||
err.code === 'ERR_UNKNOWN_FILE_EXTENSION' ||
err.code === 'ERR_UNSUPPORTED_DIR_IMPORT'
) {
try {
// Importing a file usually works, but the resolution of `import` is the ESM
// resolution algorithm, and not the CJS resolution algorithm. We may have
// failed because we tried the ESM resolution, so we try to `require` it.
return require(file);
} catch (requireErr) {
if (
requireErr.code === 'ERR_REQUIRE_ESM' ||
(requireErr instanceof SyntaxError &&
requireErr
.toString()
.includes('Cannot use import statement outside a module'))
) {
// ERR_REQUIRE_ESM happens when the test file is a JS file, but via type:module is actually ESM,
// AND has an import to a file that doesn't exist.
// This throws an `ERR_MODULE_NOT_FOUND` error above,
// and when we try to `require` it here, it throws an `ERR_REQUIRE_ESM`.
// What we want to do is throw the original error (the `ERR_MODULE_NOT_FOUND`),
// and not the `ERR_REQUIRE_ESM` error, which is a red herring.
//
// SyntaxError happens when in an edge case: when we're using an ESM loader that loads
// a `test.ts` file (i.e. unrecognized extension), and that file includes an unknown
// import (which throws an ERR_MODULE_NOT_FOUND). `require`-ing it will throw the
// syntax error, because we cannot require a file that has `import`-s.
throw err;
} else {
throw requireErr;
}
}
} else {
throw err;
}
}
};
function dealWithExports(module) {
if (module.default) {
return module.default;
} else {
return {...module, default: undefined};
}
}
exports.loadFilesAsync = async (files, preLoadFunc, postLoadFunc) => {
for (const file of files) {
preLoadFunc(file);
const result = await exports.requireOrImport(path.resolve(file));
postLoadFunc(file, result);
}
};

15
node_modules/mocha/lib/nodejs/file-unloader.js generated vendored Normal file
View 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)];
};

View File

@@ -0,0 +1,434 @@
/**
* 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 {Object} [opts] - options
* @param {string[]} opts.files - Files to run
* @param {Options} opts.options - command-line options
*/
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>}
*/

View 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
View 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
View 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
View 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
View 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
*/

550
node_modules/mocha/lib/reporters/base.js generated vendored Normal file
View File

@@ -0,0 +1,550 @@
'use strict';
/**
* @module Base
*/
/**
* Module dependencies.
*/
var diff = require('diff');
var milliseconds = require('ms');
var utils = require('../utils');
var supportsColor = require('supports-color');
var symbols = require('log-symbols');
var constants = require('../runner').constants;
var EVENT_TEST_PASS = constants.EVENT_TEST_PASS;
var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL;
const isBrowser = utils.isBrowser();
function getBrowserWindowSize() {
if ('innerHeight' in global) {
return [global.innerHeight, global.innerWidth];
}
// In a Web Worker, the DOM Window is not available.
return [640, 480];
}
/**
* Expose `Base`.
*/
exports = module.exports = Base;
/**
* Check if both stdio streams are associated with a tty.
*/
var isatty = isBrowser || (process.stdout.isTTY && process.stderr.isTTY);
/**
* Save log references to avoid tests interfering (see GH-3604).
*/
var consoleLog = console.log;
/**
* Enable coloring by default, except in the browser interface.
*/
exports.useColors =
!isBrowser &&
(supportsColor.stdout || process.env.MOCHA_COLORS !== undefined);
/**
* Inline diffs instead of +/-
*/
exports.inlineDiffs = false;
/**
* Truncate diffs longer than this value to avoid slow performance
*/
exports.maxDiffSize = 8192;
/**
* Default color map.
*/
exports.colors = {
pass: 90,
fail: 31,
'bright pass': 92,
'bright fail': 91,
'bright yellow': 93,
pending: 36,
suite: 0,
'error title': 0,
'error message': 31,
'error stack': 90,
checkmark: 32,
fast: 90,
medium: 33,
slow: 31,
green: 32,
light: 90,
'diff gutter': 90,
'diff added': 32,
'diff removed': 31,
'diff added inline': '30;42',
'diff removed inline': '30;41'
};
/**
* Default symbol map.
*/
exports.symbols = {
ok: symbols.success,
err: symbols.error,
dot: '.',
comma: ',',
bang: '!'
};
/**
* Color `str` with the given `type`,
* allowing colors to be disabled,
* as well as user-defined color
* schemes.
*
* @private
* @param {string} type
* @param {string} str
* @return {string}
*/
var color = (exports.color = function (type, str) {
if (!exports.useColors) {
return String(str);
}
return '\u001b[' + exports.colors[type] + 'm' + str + '\u001b[0m';
});
/**
* Expose term window size, with some defaults for when stderr is not a tty.
*/
exports.window = {
width: 75
};
if (isatty) {
if (isBrowser) {
exports.window.width = getBrowserWindowSize()[1];
} else {
exports.window.width = process.stdout.getWindowSize(1)[0];
}
}
/**
* Expose some basic cursor interactions that are common among reporters.
*/
exports.cursor = {
hide: function () {
isatty && process.stdout.write('\u001b[?25l');
},
show: function () {
isatty && process.stdout.write('\u001b[?25h');
},
deleteLine: function () {
isatty && process.stdout.write('\u001b[2K');
},
beginningOfLine: function () {
isatty && process.stdout.write('\u001b[0G');
},
CR: function () {
if (isatty) {
exports.cursor.deleteLine();
exports.cursor.beginningOfLine();
} else {
process.stdout.write('\r');
}
}
};
var showDiff = (exports.showDiff = function (err) {
return (
err &&
err.showDiff !== false &&
sameType(err.actual, err.expected) &&
err.expected !== undefined
);
});
function stringifyDiffObjs(err) {
if (!utils.isString(err.actual) || !utils.isString(err.expected)) {
err.actual = utils.stringify(err.actual);
err.expected = utils.stringify(err.expected);
}
}
/**
* Returns a diff between 2 strings with coloured ANSI output.
*
* @description
* The diff will be either inline or unified dependent on the value
* of `Base.inlineDiff`.
*
* @param {string} actual
* @param {string} expected
* @return {string} Diff
*/
var generateDiff = (exports.generateDiff = function (actual, expected) {
try {
var maxLen = exports.maxDiffSize;
var skipped = 0;
if (maxLen > 0) {
skipped = Math.max(actual.length - maxLen, expected.length - maxLen);
actual = actual.slice(0, maxLen);
expected = expected.slice(0, maxLen);
}
let result = exports.inlineDiffs
? inlineDiff(actual, expected)
: unifiedDiff(actual, expected);
if (skipped > 0) {
result = `${result}\n [mocha] output truncated to ${maxLen} characters, see "maxDiffSize" reporter-option\n`;
}
return result;
} catch (err) {
var msg =
'\n ' +
color('diff added', '+ expected') +
' ' +
color('diff removed', '- actual: failed to generate Mocha diff') +
'\n';
return msg;
}
});
/**
* Outputs the given `failures` as a list.
*
* @public
* @memberof Mocha.reporters.Base
* @variation 1
* @param {Object[]} failures - Each is Test instance with corresponding
* Error property
*/
exports.list = function (failures) {
var multipleErr, multipleTest;
Base.consoleLog();
failures.forEach(function (test, i) {
// format
var fmt =
color('error title', ' %s) %s:\n') +
color('error message', ' %s') +
color('error stack', '\n%s\n');
// msg
var msg;
var err;
if (test.err && test.err.multiple) {
if (multipleTest !== test) {
multipleTest = test;
multipleErr = [test.err].concat(test.err.multiple);
}
err = multipleErr.shift();
} else {
err = test.err;
}
var message;
if (typeof err.inspect === 'function') {
message = err.inspect() + '';
} else if (err.message && typeof err.message.toString === 'function') {
message = err.message + '';
} else {
message = '';
}
var stack = err.stack || message;
var index = message ? stack.indexOf(message) : -1;
if (index === -1) {
msg = message;
} else {
index += message.length;
msg = stack.slice(0, index);
// remove msg from stack
stack = stack.slice(index + 1);
}
// uncaught
if (err.uncaught) {
msg = 'Uncaught ' + msg;
}
// explicitly show diff
if (!exports.hideDiff && showDiff(err)) {
stringifyDiffObjs(err);
fmt =
color('error title', ' %s) %s:\n%s') + color('error stack', '\n%s\n');
var match = message.match(/^([^:]+): expected/);
msg = '\n ' + color('error message', match ? match[1] : msg);
msg += generateDiff(err.actual, err.expected);
}
// indent stack trace
stack = stack.replace(/^/gm, ' ');
// indented test title
var testTitle = '';
test.titlePath().forEach(function (str, index) {
if (index !== 0) {
testTitle += '\n ';
}
for (var i = 0; i < index; i++) {
testTitle += ' ';
}
testTitle += str;
});
Base.consoleLog(fmt, i + 1, testTitle, msg, stack);
});
};
/**
* Constructs a new `Base` reporter instance.
*
* @description
* All other reporters generally inherit from this reporter.
*
* @public
* @class
* @memberof Mocha.reporters
* @param {Runner} runner - Instance triggers reporter actions.
* @param {Object} [options] - runner options
*/
function Base(runner, options) {
var failures = (this.failures = []);
if (!runner) {
throw new TypeError('Missing runner argument');
}
this.options = options || {};
this.runner = runner;
this.stats = runner.stats; // assigned so Reporters keep a closer reference
var maxDiffSizeOpt =
this.options.reporterOption && this.options.reporterOption.maxDiffSize;
if (maxDiffSizeOpt !== undefined && !isNaN(Number(maxDiffSizeOpt))) {
exports.maxDiffSize = Number(maxDiffSizeOpt);
}
runner.on(EVENT_TEST_PASS, function (test) {
if (test.duration > test.slow()) {
test.speed = 'slow';
} else if (test.duration > test.slow() / 2) {
test.speed = 'medium';
} else {
test.speed = 'fast';
}
});
runner.on(EVENT_TEST_FAIL, function (test, err) {
if (showDiff(err)) {
stringifyDiffObjs(err);
}
// more than one error per test
if (test.err && err instanceof Error) {
test.err.multiple = (test.err.multiple || []).concat(err);
} else {
test.err = err;
}
failures.push(test);
});
}
/**
* Outputs common epilogue used by many of the bundled reporters.
*
* @public
* @memberof Mocha.reporters
*/
Base.prototype.epilogue = function () {
var stats = this.stats;
var fmt;
Base.consoleLog();
// passes
fmt =
color('bright pass', ' ') +
color('green', ' %d passing') +
color('light', ' (%s)');
Base.consoleLog(fmt, stats.passes || 0, milliseconds(stats.duration));
// pending
if (stats.pending) {
fmt = color('pending', ' ') + color('pending', ' %d pending');
Base.consoleLog(fmt, stats.pending);
}
// failures
if (stats.failures) {
fmt = color('fail', ' %d failing');
Base.consoleLog(fmt, stats.failures);
Base.list(this.failures);
Base.consoleLog();
}
Base.consoleLog();
};
/**
* Pads the given `str` to `len`.
*
* @private
* @param {string} str
* @param {string} len
* @return {string}
*/
function pad(str, len) {
str = String(str);
return Array(len - str.length + 1).join(' ') + str;
}
/**
* Returns inline diff between 2 strings with coloured ANSI output.
*
* @private
* @param {String} actual
* @param {String} expected
* @return {string} Diff
*/
function inlineDiff(actual, expected) {
var msg = errorDiff(actual, expected);
// linenos
var lines = msg.split('\n');
if (lines.length > 4) {
var width = String(lines.length).length;
msg = lines
.map(function (str, i) {
return pad(++i, width) + ' |' + ' ' + str;
})
.join('\n');
}
// legend
msg =
'\n' +
color('diff removed inline', 'actual') +
' ' +
color('diff added inline', 'expected') +
'\n\n' +
msg +
'\n';
// indent
msg = msg.replace(/^/gm, ' ');
return msg;
}
/**
* Returns unified diff between two strings with coloured ANSI output.
*
* @private
* @param {String} actual
* @param {String} expected
* @return {string} The diff.
*/
function unifiedDiff(actual, expected) {
var indent = ' ';
function cleanUp(line) {
if (line[0] === '+') {
return indent + colorLines('diff added', line);
}
if (line[0] === '-') {
return indent + colorLines('diff removed', line);
}
if (line.match(/@@/)) {
return '--';
}
if (line.match(/\\ No newline/)) {
return null;
}
return indent + line;
}
function notBlank(line) {
return typeof line !== 'undefined' && line !== null;
}
var msg = diff.createPatch('string', actual, expected);
var lines = msg.split('\n').splice(5);
return (
'\n ' +
colorLines('diff added', '+ expected') +
' ' +
colorLines('diff removed', '- actual') +
'\n\n' +
lines.map(cleanUp).filter(notBlank).join('\n')
);
}
/**
* Returns character diff for `err`.
*
* @private
* @param {String} actual
* @param {String} expected
* @return {string} the diff
*/
function errorDiff(actual, expected) {
return diff
.diffWordsWithSpace(actual, expected)
.map(function (str) {
if (str.added) {
return colorLines('diff added inline', str.value);
}
if (str.removed) {
return colorLines('diff removed inline', str.value);
}
return str.value;
})
.join('');
}
/**
* Colors lines for `str`, using the color `name`.
*
* @private
* @param {string} name
* @param {string} str
* @return {string}
*/
function colorLines(name, str) {
return str
.split('\n')
.map(function (str) {
return color(name, str);
})
.join('\n');
}
/**
* Object#toString reference.
*/
var objToString = Object.prototype.toString;
/**
* Checks that a / b have the same type.
*
* @private
* @param {Object} a
* @param {Object} b
* @return {boolean}
*/
function sameType(a, b) {
return objToString.call(a) === objToString.call(b);
}
Base.consoleLog = consoleLog;
Base.abstract = true;

95
node_modules/mocha/lib/reporters/doc.js generated vendored Normal file
View 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
View 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
View 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 = '&#x2023;';
/**
* 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.slice(
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
View 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
View 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';

162
node_modules/mocha/lib/reporters/json.js generated vendored Normal file
View File

@@ -0,0 +1,162 @@
'use strict';
/**
* @module JSON
*/
/**
* Module dependencies.
*/
var Base = require('./base');
var fs = require('fs');
var path = require('path');
const createUnsupportedError = require('../errors').createUnsupportedError;
const utils = require('../utils');
var constants = require('../runner').constants;
var EVENT_TEST_PASS = constants.EVENT_TEST_PASS;
var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING;
var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL;
var EVENT_TEST_END = constants.EVENT_TEST_END;
var EVENT_RUN_END = constants.EVENT_RUN_END;
/**
* 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 = [];
var output;
if (options.reporterOption && options.reporterOption.output) {
if (utils.isBrowser()) {
throw createUnsupportedError('file output not supported in browser');
}
output = options.reporterOption.output;
}
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;
var json = JSON.stringify(obj, null, 2);
if (output) {
try {
fs.mkdirSync(path.dirname(output), {recursive: true});
fs.writeFileSync(output, json);
} catch (err) {
console.error(
`${Base.symbols.err} [mocha] writing output to "${output}" failed: ${err.message}\n`
);
process.stdout.write(json);
}
} else {
process.stdout.write(json);
}
});
}
/**
* 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
View 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
View 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
View 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';

52
node_modules/mocha/lib/reporters/min.js generated vendored Normal file
View File

@@ -0,0 +1,52 @@
'use strict';
/**
* @module Min
*/
/**
* Module dependencies.
*/
var Base = require('./base');
var inherits = require('../utils').inherits;
var constants = require('../runner').constants;
var EVENT_RUN_END = constants.EVENT_RUN_END;
var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN;
/**
* Expose `Min`.
*/
exports = module.exports = Min;
/**
* Constructs a new `Min` reporter instance.
*
* @description
* This minimal test reporter is best used with '--watch'.
*
* @public
* @class
* @memberof Mocha.reporters
* @extends Mocha.reporters.Base
* @param {Runner} runner - Instance triggers reporter actions.
* @param {Object} [options] - runner options
*/
function Min(runner, options) {
Base.call(this, runner, options);
runner.on(EVENT_RUN_BEGIN, function () {
// clear screen
process.stdout.write('\u001b[2J');
// set cursor position
process.stdout.write('\u001b[1;3H');
});
runner.once(EVENT_RUN_END, this.epilogue.bind(this));
}
/**
* Inherit from `Base.prototype`.
*/
inherits(Min, Base);
Min.description = 'essentially just a summary';

276
node_modules/mocha/lib/reporters/nyan.js generated vendored Normal file
View File

@@ -0,0 +1,276 @@
'use strict';
/**
* @module Nyan
*/
/**
* Module dependencies.
*/
var Base = require('./base');
var constants = require('../runner').constants;
var inherits = require('../utils').inherits;
var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN;
var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING;
var EVENT_TEST_PASS = constants.EVENT_TEST_PASS;
var EVENT_RUN_END = constants.EVENT_RUN_END;
var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL;
/**
* Expose `Dot`.
*/
exports = module.exports = NyanCat;
/**
* Constructs a new `Nyan` reporter instance.
*
* @public
* @class Nyan
* @memberof Mocha.reporters
* @extends Mocha.reporters.Base
* @param {Runner} runner - Instance triggers reporter actions.
* @param {Object} [options] - runner options
*/
function NyanCat(runner, options) {
Base.call(this, runner, options);
var self = this;
var width = (Base.window.width * 0.75) | 0;
var nyanCatWidth = (this.nyanCatWidth = 11);
this.colorIndex = 0;
this.numberOfLines = 4;
this.rainbowColors = self.generateColors();
this.scoreboardWidth = 5;
this.tick = 0;
this.trajectories = [[], [], [], []];
this.trajectoryWidthMax = width - nyanCatWidth;
runner.on(EVENT_RUN_BEGIN, function () {
Base.cursor.hide();
self.draw();
});
runner.on(EVENT_TEST_PENDING, function () {
self.draw();
});
runner.on(EVENT_TEST_PASS, function () {
self.draw();
});
runner.on(EVENT_TEST_FAIL, function () {
self.draw();
});
runner.once(EVENT_RUN_END, function () {
Base.cursor.show();
for (var i = 0; i < self.numberOfLines; i++) {
write('\n');
}
self.epilogue();
});
}
/**
* Inherit from `Base.prototype`.
*/
inherits(NyanCat, Base);
/**
* Draw the nyan cat
*
* @private
*/
NyanCat.prototype.draw = function () {
this.appendRainbow();
this.drawScoreboard();
this.drawRainbow();
this.drawNyanCat();
this.tick = !this.tick;
};
/**
* Draw the "scoreboard" showing the number
* of passes, failures and pending tests.
*
* @private
*/
NyanCat.prototype.drawScoreboard = function () {
var stats = this.stats;
function draw(type, n) {
write(' ');
write(Base.color(type, n));
write('\n');
}
draw('green', stats.passes);
draw('fail', stats.failures);
draw('pending', stats.pending);
write('\n');
this.cursorUp(this.numberOfLines);
};
/**
* Append the rainbow.
*
* @private
*/
NyanCat.prototype.appendRainbow = function () {
var segment = this.tick ? '_' : '-';
var rainbowified = this.rainbowify(segment);
for (var index = 0; index < this.numberOfLines; index++) {
var trajectory = this.trajectories[index];
if (trajectory.length >= this.trajectoryWidthMax) {
trajectory.shift();
}
trajectory.push(rainbowified);
}
};
/**
* Draw the rainbow.
*
* @private
*/
NyanCat.prototype.drawRainbow = function () {
var self = this;
this.trajectories.forEach(function (line) {
write('\u001b[' + self.scoreboardWidth + 'C');
write(line.join(''));
write('\n');
});
this.cursorUp(this.numberOfLines);
};
/**
* Draw the nyan cat
*
* @private
*/
NyanCat.prototype.drawNyanCat = function () {
var self = this;
var startWidth = this.scoreboardWidth + this.trajectories[0].length;
var dist = '\u001b[' + startWidth + 'C';
var padding = '';
write(dist);
write('_,------,');
write('\n');
write(dist);
padding = self.tick ? ' ' : ' ';
write('_|' + padding + '/\\_/\\ ');
write('\n');
write(dist);
padding = self.tick ? '_' : '__';
var tail = self.tick ? '~' : '^';
write(tail + '|' + padding + this.face() + ' ');
write('\n');
write(dist);
padding = self.tick ? ' ' : ' ';
write(padding + '"" "" ');
write('\n');
this.cursorUp(this.numberOfLines);
};
/**
* Draw nyan cat face.
*
* @private
* @return {string}
*/
NyanCat.prototype.face = function () {
var stats = this.stats;
if (stats.failures) {
return '( x .x)';
} else if (stats.pending) {
return '( o .o)';
} else if (stats.passes) {
return '( ^ .^)';
}
return '( - .-)';
};
/**
* Move cursor up `n`.
*
* @private
* @param {number} n
*/
NyanCat.prototype.cursorUp = function (n) {
write('\u001b[' + n + 'A');
};
/**
* Move cursor down `n`.
*
* @private
* @param {number} n
*/
NyanCat.prototype.cursorDown = function (n) {
write('\u001b[' + n + 'B');
};
/**
* Generate rainbow colors.
*
* @private
* @return {Array}
*/
NyanCat.prototype.generateColors = function () {
var colors = [];
for (var i = 0; i < 6 * 7; i++) {
var pi3 = Math.floor(Math.PI / 3);
var n = i * (1.0 / 6);
var r = Math.floor(3 * Math.sin(n) + 3);
var g = Math.floor(3 * Math.sin(n + 2 * pi3) + 3);
var b = Math.floor(3 * Math.sin(n + 4 * pi3) + 3);
colors.push(36 * r + 6 * g + b + 16);
}
return colors;
};
/**
* Apply rainbow to the given `str`.
*
* @private
* @param {string} str
* @return {string}
*/
NyanCat.prototype.rainbowify = function (str) {
if (!Base.useColors) {
return str;
}
var color = this.rainbowColors[this.colorIndex % this.rainbowColors.length];
this.colorIndex += 1;
return '\u001b[38;5;' + color + 'm' + str + '\u001b[0m';
};
/**
* Stdout helper.
*
* @param {string} string A message to write to stdout.
*/
function write(string) {
process.stdout.write(string);
}
NyanCat.description = '"nyan cat"';

104
node_modules/mocha/lib/reporters/progress.js generated vendored Normal file
View File

@@ -0,0 +1,104 @@
'use strict';
/**
* @module Progress
*/
/**
* Module dependencies.
*/
var Base = require('./base');
var constants = require('../runner').constants;
var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN;
var EVENT_TEST_END = constants.EVENT_TEST_END;
var EVENT_RUN_END = constants.EVENT_RUN_END;
var inherits = require('../utils').inherits;
var color = Base.color;
var cursor = Base.cursor;
/**
* Expose `Progress`.
*/
exports = module.exports = Progress;
/**
* General progress bar color.
*/
Base.colors.progress = 90;
/**
* Constructs a new `Progress` reporter instance.
*
* @public
* @class
* @memberof Mocha.reporters
* @extends Mocha.reporters.Base
* @param {Runner} runner - Instance triggers reporter actions.
* @param {Object} [options] - runner options
*/
function Progress(runner, options) {
Base.call(this, runner, options);
var self = this;
var width = (Base.window.width * 0.5) | 0;
var total = runner.total;
var complete = 0;
var lastN = -1;
// default chars
options = options || {};
var reporterOptions = options.reporterOptions || {};
options.open = reporterOptions.open || '[';
options.complete = reporterOptions.complete || '▬';
options.incomplete = reporterOptions.incomplete || Base.symbols.dot;
options.close = reporterOptions.close || ']';
options.verbose = reporterOptions.verbose || false;
// tests started
runner.on(EVENT_RUN_BEGIN, function () {
process.stdout.write('\n');
cursor.hide();
});
// tests complete
runner.on(EVENT_TEST_END, function () {
complete++;
var percent = complete / total;
var n = (width * percent) | 0;
var i = width - n;
if (n === lastN && !options.verbose) {
// Don't re-render the line if it hasn't changed
return;
}
lastN = n;
cursor.CR();
process.stdout.write('\u001b[J');
process.stdout.write(color('progress', ' ' + options.open));
process.stdout.write(Array(n).join(options.complete));
process.stdout.write(Array(i).join(options.incomplete));
process.stdout.write(color('progress', options.close));
if (options.verbose) {
process.stdout.write(color('progress', ' ' + complete + ' of ' + total));
}
});
// tests are complete, output some stats
// and the failures if any
runner.once(EVENT_RUN_END, function () {
cursor.show();
process.stdout.write('\n');
self.epilogue();
});
}
/**
* Inherit from `Base.prototype`.
*/
inherits(Progress, Base);
Progress.description = 'a progress bar';

99
node_modules/mocha/lib/reporters/spec.js generated vendored Normal file
View File

@@ -0,0 +1,99 @@
'use strict';
/**
* @module Spec
*/
/**
* Module dependencies.
*/
var Base = require('./base');
var constants = require('../runner').constants;
var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN;
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_FAIL = constants.EVENT_TEST_FAIL;
var EVENT_TEST_PASS = constants.EVENT_TEST_PASS;
var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING;
var inherits = require('../utils').inherits;
var color = Base.color;
/**
* Expose `Spec`.
*/
exports = module.exports = Spec;
/**
* Constructs a new `Spec` reporter instance.
*
* @public
* @class
* @memberof Mocha.reporters
* @extends Mocha.reporters.Base
* @param {Runner} runner - Instance triggers reporter actions.
* @param {Object} [options] - runner options
*/
function Spec(runner, options) {
Base.call(this, runner, options);
var self = this;
var indents = 0;
var n = 0;
function indent() {
return Array(indents).join(' ');
}
runner.on(EVENT_RUN_BEGIN, function () {
Base.consoleLog();
});
runner.on(EVENT_SUITE_BEGIN, function (suite) {
++indents;
Base.consoleLog(color('suite', '%s%s'), indent(), suite.title);
});
runner.on(EVENT_SUITE_END, function () {
--indents;
if (indents === 1) {
Base.consoleLog();
}
});
runner.on(EVENT_TEST_PENDING, function (test) {
var fmt = indent() + color('pending', ' - %s');
Base.consoleLog(fmt, test.title);
});
runner.on(EVENT_TEST_PASS, function (test) {
var fmt;
if (test.speed === 'fast') {
fmt =
indent() +
color('checkmark', ' ' + Base.symbols.ok) +
color('pass', ' %s');
Base.consoleLog(fmt, test.title);
} else {
fmt =
indent() +
color('checkmark', ' ' + Base.symbols.ok) +
color('pass', ' %s') +
color(test.speed, ' (%dms)');
Base.consoleLog(fmt, test.title, test.duration);
}
});
runner.on(EVENT_TEST_FAIL, function (test) {
Base.consoleLog(indent() + color('fail', ' %d) %s'), ++n, test.title);
});
runner.once(EVENT_RUN_END, self.epilogue.bind(self));
}
/**
* Inherit from `Base.prototype`.
*/
inherits(Spec, Base);
Spec.description = 'hierarchical & verbose [default]';

293
node_modules/mocha/lib/reporters/tap.js generated vendored Normal file
View File

@@ -0,0 +1,293 @@
'use strict';
/**
* @module TAP
*/
/**
* Module dependencies.
*/
var util = require('util');
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;
var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING;
var EVENT_TEST_END = constants.EVENT_TEST_END;
var inherits = require('../utils').inherits;
var sprintf = util.format;
/**
* Expose `TAP`.
*/
exports = module.exports = TAP;
/**
* Constructs a new `TAP` reporter instance.
*
* @public
* @class
* @memberof Mocha.reporters
* @extends Mocha.reporters.Base
* @param {Runner} runner - Instance triggers reporter actions.
* @param {Object} [options] - runner options
*/
function TAP(runner, options) {
Base.call(this, runner, options);
var self = this;
var n = 1;
var tapVersion = '12';
if (options && options.reporterOptions) {
if (options.reporterOptions.tapVersion) {
tapVersion = options.reporterOptions.tapVersion.toString();
}
}
this._producer = createProducer(tapVersion);
runner.once(EVENT_RUN_BEGIN, function () {
self._producer.writeVersion();
});
runner.on(EVENT_TEST_END, function () {
++n;
});
runner.on(EVENT_TEST_PENDING, function (test) {
self._producer.writePending(n, test);
});
runner.on(EVENT_TEST_PASS, function (test) {
self._producer.writePass(n, test);
});
runner.on(EVENT_TEST_FAIL, function (test, err) {
self._producer.writeFail(n, test, err);
});
runner.once(EVENT_RUN_END, function () {
self._producer.writeEpilogue(runner.stats);
});
}
/**
* Inherit from `Base.prototype`.
*/
inherits(TAP, Base);
/**
* Returns a TAP-safe title of `test`.
*
* @private
* @param {Test} test - Test instance.
* @return {String} title with any hash character removed
*/
function title(test) {
return test.fullTitle().replace(/#/g, '');
}
/**
* Writes newline-terminated formatted string to reporter output stream.
*
* @private
* @param {string} format - `printf`-like format string
* @param {...*} [varArgs] - Format string arguments
*/
function println(format, varArgs) {
var vargs = Array.from(arguments);
vargs[0] += '\n';
process.stdout.write(sprintf.apply(null, vargs));
}
/**
* Returns a `tapVersion`-appropriate TAP producer instance, if possible.
*
* @private
* @param {string} tapVersion - Version of TAP specification to produce.
* @returns {TAPProducer} specification-appropriate instance
* @throws {Error} if specification version has no associated producer.
*/
function createProducer(tapVersion) {
var producers = {
12: new TAP12Producer(),
13: new TAP13Producer()
};
var producer = producers[tapVersion];
if (!producer) {
throw new Error(
'invalid or unsupported TAP version: ' + JSON.stringify(tapVersion)
);
}
return producer;
}
/**
* @summary
* Constructs a new TAPProducer.
*
* @description
* <em>Only</em> to be used as an abstract base class.
*
* @private
* @constructor
*/
function TAPProducer() {}
/**
* Writes the TAP version to reporter output stream.
*
* @abstract
*/
TAPProducer.prototype.writeVersion = function () {};
/**
* Writes the plan to reporter output stream.
*
* @abstract
* @param {number} ntests - Number of tests that are planned to run.
*/
TAPProducer.prototype.writePlan = function (ntests) {
println('%d..%d', 1, ntests);
};
/**
* Writes that test passed to reporter output stream.
*
* @abstract
* @param {number} n - Index of test that passed.
* @param {Test} test - Instance containing test information.
*/
TAPProducer.prototype.writePass = function (n, test) {
println('ok %d %s', n, title(test));
};
/**
* Writes that test was skipped to reporter output stream.
*
* @abstract
* @param {number} n - Index of test that was skipped.
* @param {Test} test - Instance containing test information.
*/
TAPProducer.prototype.writePending = function (n, test) {
println('ok %d %s # SKIP -', n, title(test));
};
/**
* Writes that test failed to reporter output stream.
*
* @abstract
* @param {number} n - Index of test that failed.
* @param {Test} test - Instance containing test information.
* @param {Error} err - Reason the test failed.
*/
TAPProducer.prototype.writeFail = function (n, test, err) {
println('not ok %d %s', n, title(test));
};
/**
* Writes the summary epilogue to reporter output stream.
*
* @abstract
* @param {Object} stats - Object containing run statistics.
*/
TAPProducer.prototype.writeEpilogue = function (stats) {
// :TBD: Why is this not counting pending tests?
println('# tests ' + (stats.passes + stats.failures));
println('# pass ' + stats.passes);
// :TBD: Why are we not showing pending results?
println('# fail ' + stats.failures);
this.writePlan(stats.passes + stats.failures + stats.pending);
};
/**
* @summary
* Constructs a new TAP12Producer.
*
* @description
* Produces output conforming to the TAP12 specification.
*
* @private
* @constructor
* @extends TAPProducer
* @see {@link https://testanything.org/tap-specification.html|Specification}
*/
function TAP12Producer() {
/**
* Writes that test failed to reporter output stream, with error formatting.
* @override
*/
this.writeFail = function (n, test, err) {
TAPProducer.prototype.writeFail.call(this, n, test, err);
if (err.message) {
println(err.message.replace(/^/gm, ' '));
}
if (err.stack) {
println(err.stack.replace(/^/gm, ' '));
}
};
}
/**
* Inherit from `TAPProducer.prototype`.
*/
inherits(TAP12Producer, TAPProducer);
/**
* @summary
* Constructs a new TAP13Producer.
*
* @description
* Produces output conforming to the TAP13 specification.
*
* @private
* @constructor
* @extends TAPProducer
* @see {@link https://testanything.org/tap-version-13-specification.html|Specification}
*/
function TAP13Producer() {
/**
* Writes the TAP version to reporter output stream.
* @override
*/
this.writeVersion = function () {
println('TAP version 13');
};
/**
* Writes that test failed to reporter output stream, with error formatting.
* @override
*/
this.writeFail = function (n, test, err) {
TAPProducer.prototype.writeFail.call(this, n, test, err);
var emitYamlBlock = err.message != null || err.stack != null;
if (emitYamlBlock) {
println(indent(1) + '---');
if (err.message) {
println(indent(2) + 'message: |-');
println(err.message.replace(/^/gm, indent(3)));
}
if (err.stack) {
println(indent(2) + 'stack: |-');
println(err.stack.replace(/^/gm, indent(3)));
}
println(indent(1) + '...');
}
};
function indent(level) {
return Array(level + 1).join(' ');
}
}
/**
* Inherit from `TAPProducer.prototype`.
*/
inherits(TAP13Producer, TAPProducer);
TAP.description = 'TAP-compatible output';

217
node_modules/mocha/lib/reporters/xunit.js generated vendored Normal file
View File

@@ -0,0 +1,217 @@
'use strict';
/**
* @module XUnit
*/
/**
* Module dependencies.
*/
var Base = require('./base');
var utils = require('../utils');
var fs = require('fs');
var path = require('path');
var errors = require('../errors');
var createUnsupportedError = errors.createUnsupportedError;
var constants = require('../runner').constants;
var EVENT_TEST_PASS = constants.EVENT_TEST_PASS;
var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL;
var EVENT_RUN_END = constants.EVENT_RUN_END;
var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING;
var STATE_FAILED = require('../runnable').constants.STATE_FAILED;
var inherits = utils.inherits;
var escape = utils.escape;
/**
* Save timer references to avoid Sinon interfering (see GH-237).
*/
var Date = global.Date;
/**
* Expose `XUnit`.
*/
exports = module.exports = XUnit;
/**
* Constructs a new `XUnit` reporter instance.
*
* @public
* @class
* @memberof Mocha.reporters
* @extends Mocha.reporters.Base
* @param {Runner} runner - Instance triggers reporter actions.
* @param {Object} [options] - runner options
*/
function XUnit(runner, options) {
Base.call(this, runner, options);
var stats = this.stats;
var tests = [];
var self = this;
// the name of the test suite, as it will appear in the resulting XML file
var suiteName;
// the default name of the test suite if none is provided
var DEFAULT_SUITE_NAME = 'Mocha Tests';
if (options && options.reporterOptions) {
if (options.reporterOptions.output) {
if (!fs.createWriteStream) {
throw createUnsupportedError('file output not supported in browser');
}
fs.mkdirSync(path.dirname(options.reporterOptions.output), {
recursive: true
});
self.fileStream = fs.createWriteStream(options.reporterOptions.output);
}
// get the suite name from the reporter options (if provided)
suiteName = options.reporterOptions.suiteName;
}
// fall back to the default suite name
suiteName = suiteName || DEFAULT_SUITE_NAME;
runner.on(EVENT_TEST_PENDING, function (test) {
tests.push(test);
});
runner.on(EVENT_TEST_PASS, function (test) {
tests.push(test);
});
runner.on(EVENT_TEST_FAIL, function (test) {
tests.push(test);
});
runner.once(EVENT_RUN_END, function () {
self.write(
tag(
'testsuite',
{
name: suiteName,
tests: stats.tests,
failures: 0,
errors: stats.failures,
skipped: stats.tests - stats.failures - stats.passes,
timestamp: new Date().toUTCString(),
time: stats.duration / 1000 || 0
},
false
)
);
tests.forEach(function (t) {
self.test(t);
});
self.write('</testsuite>');
});
}
/**
* Inherit from `Base.prototype`.
*/
inherits(XUnit, Base);
/**
* Override done to close the stream (if it's a file).
*
* @param failures
* @param {Function} fn
*/
XUnit.prototype.done = function (failures, fn) {
if (this.fileStream) {
this.fileStream.end(function () {
fn(failures);
});
} else {
fn(failures);
}
};
/**
* Write out the given line.
*
* @param {string} line
*/
XUnit.prototype.write = function (line) {
if (this.fileStream) {
this.fileStream.write(line + '\n');
} else if (typeof process === 'object' && process.stdout) {
process.stdout.write(line + '\n');
} else {
Base.consoleLog(line);
}
};
/**
* Output tag for the given `test.`
*
* @param {Test} test
*/
XUnit.prototype.test = function (test) {
Base.useColors = false;
var attrs = {
classname: test.parent.fullTitle(),
name: test.title,
time: test.duration / 1000 || 0
};
if (test.state === STATE_FAILED) {
var err = test.err;
var diff =
!Base.hideDiff && Base.showDiff(err)
? '\n' + Base.generateDiff(err.actual, err.expected)
: '';
this.write(
tag(
'testcase',
attrs,
false,
tag(
'failure',
{},
false,
escape(err.message) + escape(diff) + '\n' + escape(err.stack)
)
)
);
} else if (test.isPending()) {
this.write(tag('testcase', attrs, false, tag('skipped', {}, true)));
} else {
this.write(tag('testcase', attrs, true));
}
};
/**
* HTML tag helper.
*
* @param name
* @param attrs
* @param close
* @param content
* @return {string}
*/
function tag(name, attrs, close, content) {
var end = close ? '/>' : '>';
var pairs = [];
var tag;
for (var key in attrs) {
if (Object.prototype.hasOwnProperty.call(attrs, key)) {
pairs.push(key + '="' + escape(attrs[key]) + '"');
}
}
tag = '<' + name + (pairs.length ? ' ' + pairs.join(' ') : '') + end;
if (content) {
tag += content + '</' + name + end;
}
return tag;
}
XUnit.description = 'XUnit-compatible XML output';

476
node_modules/mocha/lib/runnable.js generated vendored Normal file
View File

@@ -0,0 +1,476 @@
'use strict';
var EventEmitter = require('events').EventEmitter;
var Pending = require('./pending');
var debug = require('debug')('mocha:runnable');
var milliseconds = require('ms');
var utils = require('./utils');
const {
createInvalidExceptionError,
createMultipleDoneError,
createTimeoutError
} = require('./errors');
/**
* Save timer references to avoid Sinon interfering (see GH-237).
* @private
*/
var Date = global.Date;
var setTimeout = global.setTimeout;
var clearTimeout = global.clearTimeout;
var toString = Object.prototype.toString;
module.exports = Runnable;
/**
* Initialize a new `Runnable` with the given `title` and callback `fn`.
*
* @class
* @extends external:EventEmitter
* @public
* @param {String} title
* @param {Function} fn
*/
function Runnable(title, fn) {
this.title = title;
this.fn = fn;
this.body = (fn || '').toString();
this.async = fn && fn.length;
this.sync = !this.async;
this._timeout = 2000;
this._slow = 75;
this._retries = -1;
utils.assignNewMochaID(this);
Object.defineProperty(this, 'id', {
get() {
return utils.getMochaID(this);
}
});
this.reset();
}
/**
* Inherit from `EventEmitter.prototype`.
*/
utils.inherits(Runnable, EventEmitter);
/**
* Resets the state initially or for a next run.
*/
Runnable.prototype.reset = function () {
this.timedOut = false;
this._currentRetry = 0;
this.pending = false;
delete this.state;
delete this.err;
};
/**
* Get current timeout value in msecs.
*
* @private
* @returns {number} current timeout threshold value
*/
/**
* @summary
* Set timeout threshold value (msecs).
*
* @description
* A string argument can use shorthand (e.g., "2s") and will be converted.
* The value will be clamped to range [<code>0</code>, <code>2^<sup>31</sup>-1</code>].
* If clamped value matches either range endpoint, timeouts will be disabled.
*
* @private
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout#Maximum_delay_value}
* @param {number|string} ms - Timeout threshold value.
* @returns {Runnable} this
* @chainable
*/
Runnable.prototype.timeout = function (ms) {
if (!arguments.length) {
return this._timeout;
}
if (typeof ms === 'string') {
ms = milliseconds(ms);
}
// Clamp to range
var INT_MAX = Math.pow(2, 31) - 1;
var range = [0, INT_MAX];
ms = utils.clamp(ms, range);
// see #1652 for reasoning
if (ms === range[0] || ms === range[1]) {
this._timeout = 0;
} else {
this._timeout = ms;
}
debug('timeout %d', this._timeout);
if (this.timer) {
this.resetTimeout();
}
return this;
};
/**
* Set or get slow `ms`.
*
* @private
* @param {number|string} ms
* @return {Runnable|number} ms or Runnable instance.
*/
Runnable.prototype.slow = function (ms) {
if (!arguments.length || typeof ms === 'undefined') {
return this._slow;
}
if (typeof ms === 'string') {
ms = milliseconds(ms);
}
debug('slow %d', ms);
this._slow = ms;
return this;
};
/**
* Halt and mark as pending.
*
* @memberof Mocha.Runnable
* @public
*/
Runnable.prototype.skip = function () {
this.pending = true;
throw new Pending('sync skip; aborting execution');
};
/**
* Check if this runnable or its parent suite is marked as pending.
*
* @private
*/
Runnable.prototype.isPending = function () {
return this.pending || (this.parent && this.parent.isPending());
};
/**
* Return `true` if this Runnable has failed.
* @return {boolean}
* @private
*/
Runnable.prototype.isFailed = function () {
return !this.isPending() && this.state === constants.STATE_FAILED;
};
/**
* Return `true` if this Runnable has passed.
* @return {boolean}
* @private
*/
Runnable.prototype.isPassed = function () {
return !this.isPending() && this.state === constants.STATE_PASSED;
};
/**
* Set or get number of retries.
*
* @private
*/
Runnable.prototype.retries = function (n) {
if (!arguments.length) {
return this._retries;
}
this._retries = n;
};
/**
* Set or get current retry
*
* @private
*/
Runnable.prototype.currentRetry = function (n) {
if (!arguments.length) {
return this._currentRetry;
}
this._currentRetry = n;
};
/**
* Return the full title generated by recursively concatenating the parent's
* full title.
*
* @memberof Mocha.Runnable
* @public
* @return {string}
*/
Runnable.prototype.fullTitle = function () {
return this.titlePath().join(' ');
};
/**
* Return the title path generated by concatenating the parent's title path with the title.
*
* @memberof Mocha.Runnable
* @public
* @return {string}
*/
Runnable.prototype.titlePath = function () {
return this.parent.titlePath().concat([this.title]);
};
/**
* Clear the timeout.
*
* @private
*/
Runnable.prototype.clearTimeout = function () {
clearTimeout(this.timer);
};
/**
* Reset the timeout.
*
* @private
*/
Runnable.prototype.resetTimeout = function () {
var self = this;
var ms = this.timeout();
if (ms === 0) {
return;
}
this.clearTimeout();
this.timer = setTimeout(function () {
if (self.timeout() === 0) {
return;
}
self.callback(self._timeoutError(ms));
self.timedOut = true;
}, ms);
};
/**
* Set or get a list of whitelisted globals for this test run.
*
* @private
* @param {string[]} globals
*/
Runnable.prototype.globals = function (globals) {
if (!arguments.length) {
return this._allowedGlobals;
}
this._allowedGlobals = globals;
};
/**
* Run the test and invoke `fn(err)`.
*
* @param {Function} fn
* @private
*/
Runnable.prototype.run = function (fn) {
var self = this;
var start = new Date();
var ctx = this.ctx;
var finished;
var errorWasHandled = false;
if (this.isPending()) return fn();
// Sometimes the ctx exists, but it is not runnable
if (ctx && ctx.runnable) {
ctx.runnable(this);
}
// called multiple times
function multiple(err) {
if (errorWasHandled) {
return;
}
errorWasHandled = true;
self.emit('error', createMultipleDoneError(self, err));
}
// finished
function done(err) {
var ms = self.timeout();
if (self.timedOut) {
return;
}
if (finished) {
return multiple(err);
}
self.clearTimeout();
self.duration = new Date() - start;
finished = true;
if (!err && self.duration > ms && ms > 0) {
err = self._timeoutError(ms);
}
fn(err);
}
// for .resetTimeout() and Runner#uncaught()
this.callback = done;
if (this.fn && typeof this.fn.call !== 'function') {
done(
new TypeError(
'A runnable must be passed a function as its second argument.'
)
);
return;
}
// explicit async with `done` argument
if (this.async) {
this.resetTimeout();
// allows skip() to be used in an explicit async context
this.skip = function asyncSkip() {
this.pending = true;
done();
// halt execution, the uncaught handler will ignore the failure.
throw new Pending('async skip; aborting execution');
};
try {
callFnAsync(this.fn);
} catch (err) {
// handles async runnables which actually run synchronously
errorWasHandled = true;
if (err instanceof Pending) {
return; // done() is already called in this.skip()
} else if (this.allowUncaught) {
throw err;
}
done(Runnable.toValueOrError(err));
}
return;
}
// sync or promise-returning
try {
callFn(this.fn);
} catch (err) {
errorWasHandled = true;
if (err instanceof Pending) {
return done();
} else if (this.allowUncaught) {
throw err;
}
done(Runnable.toValueOrError(err));
}
function callFn(fn) {
var result = fn.call(ctx);
if (result && typeof result.then === 'function') {
self.resetTimeout();
result.then(
function () {
done();
// Return null so libraries like bluebird do not warn about
// subsequently constructed Promises.
return null;
},
function (reason) {
done(reason || new Error('Promise rejected with no or falsy reason'));
}
);
} else {
if (self.asyncOnly) {
return done(
new Error(
'--async-only option in use without declaring `done()` or returning a promise'
)
);
}
done();
}
}
function callFnAsync(fn) {
var result = fn.call(ctx, function (err) {
if (err instanceof Error || toString.call(err) === '[object Error]') {
return done(err);
}
if (err) {
if (Object.prototype.toString.call(err) === '[object Object]') {
return done(
new Error('done() invoked with non-Error: ' + JSON.stringify(err))
);
}
return done(new Error('done() invoked with non-Error: ' + err));
}
if (result && utils.isPromise(result)) {
return done(
new Error(
'Resolution method is overspecified. Specify a callback *or* return a Promise; not both.'
)
);
}
done();
});
}
};
/**
* Instantiates a "timeout" error
*
* @param {number} ms - Timeout (in milliseconds)
* @returns {Error} a "timeout" error
* @private
*/
Runnable.prototype._timeoutError = function (ms) {
let msg = `Timeout of ${ms}ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.`;
if (this.file) {
msg += ' (' + this.file + ')';
}
return createTimeoutError(msg, ms, this.file);
};
var constants = utils.defineConstants(
/**
* {@link Runnable}-related constants.
* @public
* @memberof Runnable
* @readonly
* @static
* @alias constants
* @enum {string}
*/
{
/**
* Value of `state` prop when a `Runnable` has failed
*/
STATE_FAILED: 'failed',
/**
* Value of `state` prop when a `Runnable` has passed
*/
STATE_PASSED: 'passed',
/**
* Value of `state` prop when a `Runnable` has been skipped by user
*/
STATE_PENDING: 'pending'
}
);
/**
* Given `value`, return identity if truthy, otherwise create an "invalid exception" error and return that.
* @param {*} [value] - Value to return, if present
* @returns {*|Error} `value`, otherwise an `Error`
* @private
*/
Runnable.toValueOrError = function (value) {
return (
value ||
createInvalidExceptionError(
'Runnable failed with falsy or undefined exception. Please throw an Error instead.',
value
)
);
};
Runnable.constants = constants;

1256
node_modules/mocha/lib/runner.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

83
node_modules/mocha/lib/stats-collector.js generated vendored Normal file
View File

@@ -0,0 +1,83 @@
'use strict';
/**
* Provides a factory function for a {@link StatsCollector} object.
* @module
*/
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_RUN_BEGIN = constants.EVENT_RUN_BEGIN;
var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING;
var EVENT_RUN_END = constants.EVENT_RUN_END;
var EVENT_TEST_END = constants.EVENT_TEST_END;
/**
* Test statistics collector.
*
* @public
* @typedef {Object} StatsCollector
* @property {number} suites - integer count of suites run.
* @property {number} tests - integer count of tests run.
* @property {number} passes - integer count of passing tests.
* @property {number} pending - integer count of pending tests.
* @property {number} failures - integer count of failed tests.
* @property {Date} start - time when testing began.
* @property {Date} end - time when testing concluded.
* @property {number} duration - number of msecs that testing took.
*/
var Date = global.Date;
/**
* Provides stats such as test duration, number of tests passed / failed etc., by listening for events emitted by `runner`.
*
* @private
* @param {Runner} runner - Runner instance
* @throws {TypeError} If falsy `runner`
*/
function createStatsCollector(runner) {
/**
* @type StatsCollector
*/
var stats = {
suites: 0,
tests: 0,
passes: 0,
pending: 0,
failures: 0
};
if (!runner) {
throw new TypeError('Missing runner argument');
}
runner.stats = stats;
runner.once(EVENT_RUN_BEGIN, function () {
stats.start = new Date();
});
runner.on(EVENT_SUITE_BEGIN, function (suite) {
suite.root || stats.suites++;
});
runner.on(EVENT_TEST_PASS, function () {
stats.passes++;
});
runner.on(EVENT_TEST_FAIL, function () {
stats.failures++;
});
runner.on(EVENT_TEST_PENDING, function () {
stats.pending++;
});
runner.on(EVENT_TEST_END, function () {
stats.tests++;
});
runner.once(EVENT_RUN_END, function () {
stats.end = new Date();
stats.duration = stats.end - stats.start;
});
}
module.exports = createStatsCollector;

666
node_modules/mocha/lib/suite.js generated vendored Normal file
View File

@@ -0,0 +1,666 @@
'use strict';
/**
* Module dependencies.
* @private
*/
const {EventEmitter} = require('events');
const Hook = require('./hook');
var {
assignNewMochaID,
clamp,
constants: utilsConstants,
defineConstants,
getMochaID,
inherits,
isString
} = require('./utils');
const debug = require('debug')('mocha:suite');
const milliseconds = require('ms');
const errors = require('./errors');
const {MOCHA_ID_PROP_NAME} = utilsConstants;
/**
* Expose `Suite`.
*/
exports = module.exports = Suite;
/**
* Create a new `Suite` with the given `title` and parent `Suite`.
*
* @public
* @param {Suite} parent - Parent suite (required!)
* @param {string} title - Title
* @return {Suite}
*/
Suite.create = function (parent, title) {
var suite = new Suite(title, parent.ctx);
suite.parent = parent;
title = suite.fullTitle();
parent.addSuite(suite);
return suite;
};
/**
* Constructs a new `Suite` instance with the given `title`, `ctx`, and `isRoot`.
*
* @public
* @class
* @extends EventEmitter
* @see {@link https://nodejs.org/api/events.html#events_class_eventemitter|EventEmitter}
* @param {string} title - Suite title.
* @param {Context} parentContext - Parent context instance.
* @param {boolean} [isRoot=false] - Whether this is the root suite.
*/
function Suite(title, parentContext, isRoot) {
if (!isString(title)) {
throw errors.createInvalidArgumentTypeError(
'Suite argument "title" must be a string. Received type "' +
typeof title +
'"',
'title',
'string'
);
}
this.title = title;
function Context() {}
Context.prototype = parentContext;
this.ctx = new Context();
this.suites = [];
this.tests = [];
this.root = isRoot === true;
this.pending = false;
this._retries = -1;
this._beforeEach = [];
this._beforeAll = [];
this._afterEach = [];
this._afterAll = [];
this._timeout = 2000;
this._slow = 75;
this._bail = false;
this._onlyTests = [];
this._onlySuites = [];
assignNewMochaID(this);
Object.defineProperty(this, 'id', {
get() {
return getMochaID(this);
}
});
this.reset();
}
/**
* Inherit from `EventEmitter.prototype`.
*/
inherits(Suite, EventEmitter);
/**
* Resets the state initially or for a next run.
*/
Suite.prototype.reset = function () {
this.delayed = false;
function doReset(thingToReset) {
thingToReset.reset();
}
this.suites.forEach(doReset);
this.tests.forEach(doReset);
this._beforeEach.forEach(doReset);
this._afterEach.forEach(doReset);
this._beforeAll.forEach(doReset);
this._afterAll.forEach(doReset);
};
/**
* Return a clone of this `Suite`.
*
* @private
* @return {Suite}
*/
Suite.prototype.clone = function () {
var suite = new Suite(this.title);
debug('clone');
suite.ctx = this.ctx;
suite.root = this.root;
suite.timeout(this.timeout());
suite.retries(this.retries());
suite.slow(this.slow());
suite.bail(this.bail());
return suite;
};
/**
* Set or get timeout `ms` or short-hand such as "2s".
*
* @private
* @todo Do not attempt to set value if `ms` is undefined
* @param {number|string} ms
* @return {Suite|number} for chaining
*/
Suite.prototype.timeout = function (ms) {
if (!arguments.length) {
return this._timeout;
}
if (typeof ms === 'string') {
ms = milliseconds(ms);
}
// Clamp to range
var INT_MAX = Math.pow(2, 31) - 1;
var range = [0, INT_MAX];
ms = clamp(ms, range);
debug('timeout %d', ms);
this._timeout = parseInt(ms, 10);
return this;
};
/**
* Set or get number of times to retry a failed test.
*
* @private
* @param {number|string} n
* @return {Suite|number} for chaining
*/
Suite.prototype.retries = function (n) {
if (!arguments.length) {
return this._retries;
}
debug('retries %d', n);
this._retries = parseInt(n, 10) || 0;
return this;
};
/**
* Set or get slow `ms` or short-hand such as "2s".
*
* @private
* @param {number|string} ms
* @return {Suite|number} for chaining
*/
Suite.prototype.slow = function (ms) {
if (!arguments.length) {
return this._slow;
}
if (typeof ms === 'string') {
ms = milliseconds(ms);
}
debug('slow %d', ms);
this._slow = ms;
return this;
};
/**
* Set or get whether to bail after first error.
*
* @private
* @param {boolean} bail
* @return {Suite|number} for chaining
*/
Suite.prototype.bail = function (bail) {
if (!arguments.length) {
return this._bail;
}
debug('bail %s', bail);
this._bail = bail;
return this;
};
/**
* Check if this suite or its parent suite is marked as pending.
*
* @private
*/
Suite.prototype.isPending = function () {
return this.pending || (this.parent && this.parent.isPending());
};
/**
* Generic hook-creator.
* @private
* @param {string} title - Title of hook
* @param {Function} fn - Hook callback
* @returns {Hook} A new hook
*/
Suite.prototype._createHook = function (title, fn) {
var hook = new Hook(title, fn);
hook.parent = this;
hook.timeout(this.timeout());
hook.retries(this.retries());
hook.slow(this.slow());
hook.ctx = this.ctx;
hook.file = this.file;
return hook;
};
/**
* Run `fn(test[, done])` before running tests.
*
* @private
* @param {string} title
* @param {Function} fn
* @return {Suite} for chaining
*/
Suite.prototype.beforeAll = function (title, fn) {
if (this.isPending()) {
return this;
}
if (typeof title === 'function') {
fn = title;
title = fn.name;
}
title = '"before all" hook' + (title ? ': ' + title : '');
var hook = this._createHook(title, fn);
this._beforeAll.push(hook);
this.emit(constants.EVENT_SUITE_ADD_HOOK_BEFORE_ALL, hook);
return this;
};
/**
* Run `fn(test[, done])` after running tests.
*
* @private
* @param {string} title
* @param {Function} fn
* @return {Suite} for chaining
*/
Suite.prototype.afterAll = function (title, fn) {
if (this.isPending()) {
return this;
}
if (typeof title === 'function') {
fn = title;
title = fn.name;
}
title = '"after all" hook' + (title ? ': ' + title : '');
var hook = this._createHook(title, fn);
this._afterAll.push(hook);
this.emit(constants.EVENT_SUITE_ADD_HOOK_AFTER_ALL, hook);
return this;
};
/**
* Run `fn(test[, done])` before each test case.
*
* @private
* @param {string} title
* @param {Function} fn
* @return {Suite} for chaining
*/
Suite.prototype.beforeEach = function (title, fn) {
if (this.isPending()) {
return this;
}
if (typeof title === 'function') {
fn = title;
title = fn.name;
}
title = '"before each" hook' + (title ? ': ' + title : '');
var hook = this._createHook(title, fn);
this._beforeEach.push(hook);
this.emit(constants.EVENT_SUITE_ADD_HOOK_BEFORE_EACH, hook);
return this;
};
/**
* Run `fn(test[, done])` after each test case.
*
* @private
* @param {string} title
* @param {Function} fn
* @return {Suite} for chaining
*/
Suite.prototype.afterEach = function (title, fn) {
if (this.isPending()) {
return this;
}
if (typeof title === 'function') {
fn = title;
title = fn.name;
}
title = '"after each" hook' + (title ? ': ' + title : '');
var hook = this._createHook(title, fn);
this._afterEach.push(hook);
this.emit(constants.EVENT_SUITE_ADD_HOOK_AFTER_EACH, hook);
return this;
};
/**
* Add a test `suite`.
*
* @private
* @param {Suite} suite
* @return {Suite} for chaining
*/
Suite.prototype.addSuite = function (suite) {
suite.parent = this;
suite.root = false;
suite.timeout(this.timeout());
suite.retries(this.retries());
suite.slow(this.slow());
suite.bail(this.bail());
this.suites.push(suite);
this.emit(constants.EVENT_SUITE_ADD_SUITE, suite);
return this;
};
/**
* Add a `test` to this suite.
*
* @private
* @param {Test} test
* @return {Suite} for chaining
*/
Suite.prototype.addTest = function (test) {
test.parent = this;
test.timeout(this.timeout());
test.retries(this.retries());
test.slow(this.slow());
test.ctx = this.ctx;
this.tests.push(test);
this.emit(constants.EVENT_SUITE_ADD_TEST, test);
return this;
};
/**
* Return the full title generated by recursively concatenating the parent's
* full title.
*
* @memberof Suite
* @public
* @return {string}
*/
Suite.prototype.fullTitle = function () {
return this.titlePath().join(' ');
};
/**
* Return the title path generated by recursively concatenating the parent's
* title path.
*
* @memberof Suite
* @public
* @return {string}
*/
Suite.prototype.titlePath = function () {
var result = [];
if (this.parent) {
result = result.concat(this.parent.titlePath());
}
if (!this.root) {
result.push(this.title);
}
return result;
};
/**
* Return the total number of tests.
*
* @memberof Suite
* @public
* @return {number}
*/
Suite.prototype.total = function () {
return (
this.suites.reduce(function (sum, suite) {
return sum + suite.total();
}, 0) + this.tests.length
);
};
/**
* Iterates through each suite recursively to find all tests. Applies a
* function in the format `fn(test)`.
*
* @private
* @param {Function} fn
* @return {Suite}
*/
Suite.prototype.eachTest = function (fn) {
this.tests.forEach(fn);
this.suites.forEach(function (suite) {
suite.eachTest(fn);
});
return this;
};
/**
* This will run the root suite if we happen to be running in delayed mode.
* @private
*/
Suite.prototype.run = function run() {
if (this.root) {
this.emit(constants.EVENT_ROOT_SUITE_RUN);
}
};
/**
* Determines whether a suite has an `only` test or suite as a descendant.
*
* @private
* @returns {Boolean}
*/
Suite.prototype.hasOnly = function hasOnly() {
return (
this._onlyTests.length > 0 ||
this._onlySuites.length > 0 ||
this.suites.some(function (suite) {
return suite.hasOnly();
})
);
};
/**
* Filter suites based on `isOnly` logic.
*
* @private
* @returns {Boolean}
*/
Suite.prototype.filterOnly = function filterOnly() {
if (this._onlyTests.length) {
// If the suite contains `only` tests, run those and ignore any nested suites.
this.tests = this._onlyTests;
this.suites = [];
} else {
// Otherwise, do not run any of the tests in this suite.
this.tests = [];
this._onlySuites.forEach(function (onlySuite) {
// If there are other `only` tests/suites nested in the current `only` suite, then filter that `only` suite.
// Otherwise, all of the tests on this `only` suite should be run, so don't filter it.
if (onlySuite.hasOnly()) {
onlySuite.filterOnly();
}
});
// Run the `only` suites, as well as any other suites that have `only` tests/suites as descendants.
var onlySuites = this._onlySuites;
this.suites = this.suites.filter(function (childSuite) {
return onlySuites.indexOf(childSuite) !== -1 || childSuite.filterOnly();
});
}
// Keep the suite only if there is something to run
return this.tests.length > 0 || this.suites.length > 0;
};
/**
* Adds a suite to the list of subsuites marked `only`.
*
* @private
* @param {Suite} suite
*/
Suite.prototype.appendOnlySuite = function (suite) {
this._onlySuites.push(suite);
};
/**
* Marks a suite to be `only`.
*
* @private
*/
Suite.prototype.markOnly = function () {
this.parent && this.parent.appendOnlySuite(this);
};
/**
* Adds a test to the list of tests marked `only`.
*
* @private
* @param {Test} test
*/
Suite.prototype.appendOnlyTest = function (test) {
this._onlyTests.push(test);
};
/**
* Returns the array of hooks by hook name; see `HOOK_TYPE_*` constants.
* @private
*/
Suite.prototype.getHooks = function getHooks(name) {
return this['_' + name];
};
/**
* cleans all references from this suite and all child suites.
*/
Suite.prototype.dispose = function () {
this.suites.forEach(function (suite) {
suite.dispose();
});
this.cleanReferences();
};
/**
* Cleans up the references to all the deferred functions
* (before/after/beforeEach/afterEach) and tests of a Suite.
* These must be deleted otherwise a memory leak can happen,
* as those functions may reference variables from closures,
* thus those variables can never be garbage collected as long
* as the deferred functions exist.
*
* @private
*/
Suite.prototype.cleanReferences = function cleanReferences() {
function cleanArrReferences(arr) {
for (var i = 0; i < arr.length; i++) {
delete arr[i].fn;
}
}
if (Array.isArray(this._beforeAll)) {
cleanArrReferences(this._beforeAll);
}
if (Array.isArray(this._beforeEach)) {
cleanArrReferences(this._beforeEach);
}
if (Array.isArray(this._afterAll)) {
cleanArrReferences(this._afterAll);
}
if (Array.isArray(this._afterEach)) {
cleanArrReferences(this._afterEach);
}
for (var i = 0; i < this.tests.length; i++) {
delete this.tests[i].fn;
}
};
/**
* Returns an object suitable for IPC.
* Functions are represented by keys beginning with `$$`.
* @private
* @returns {Object}
*/
Suite.prototype.serialize = function serialize() {
return {
_bail: this._bail,
$$fullTitle: this.fullTitle(),
$$isPending: Boolean(this.isPending()),
root: this.root,
title: this.title,
[MOCHA_ID_PROP_NAME]: this.id,
parent: this.parent ? {[MOCHA_ID_PROP_NAME]: this.parent.id} : null
};
};
var constants = defineConstants(
/**
* {@link Suite}-related constants.
* @public
* @memberof Suite
* @alias constants
* @readonly
* @static
* @enum {string}
*/
{
/**
* Event emitted after a test file has been loaded. Not emitted in browser.
*/
EVENT_FILE_POST_REQUIRE: 'post-require',
/**
* Event emitted before a test file has been loaded. In browser, this is emitted once an interface has been selected.
*/
EVENT_FILE_PRE_REQUIRE: 'pre-require',
/**
* Event emitted immediately after a test file has been loaded. Not emitted in browser.
*/
EVENT_FILE_REQUIRE: 'require',
/**
* Event emitted when `global.run()` is called (use with `delay` option).
*/
EVENT_ROOT_SUITE_RUN: 'run',
/**
* Namespace for collection of a `Suite`'s "after all" hooks.
*/
HOOK_TYPE_AFTER_ALL: 'afterAll',
/**
* Namespace for collection of a `Suite`'s "after each" hooks.
*/
HOOK_TYPE_AFTER_EACH: 'afterEach',
/**
* Namespace for collection of a `Suite`'s "before all" hooks.
*/
HOOK_TYPE_BEFORE_ALL: 'beforeAll',
/**
* Namespace for collection of a `Suite`'s "before each" hooks.
*/
HOOK_TYPE_BEFORE_EACH: 'beforeEach',
/**
* Emitted after a child `Suite` has been added to a `Suite`.
*/
EVENT_SUITE_ADD_SUITE: 'suite',
/**
* Emitted after an "after all" `Hook` has been added to a `Suite`.
*/
EVENT_SUITE_ADD_HOOK_AFTER_ALL: 'afterAll',
/**
* Emitted after an "after each" `Hook` has been added to a `Suite`.
*/
EVENT_SUITE_ADD_HOOK_AFTER_EACH: 'afterEach',
/**
* Emitted after an "before all" `Hook` has been added to a `Suite`.
*/
EVENT_SUITE_ADD_HOOK_BEFORE_ALL: 'beforeAll',
/**
* Emitted after an "before each" `Hook` has been added to a `Suite`.
*/
EVENT_SUITE_ADD_HOOK_BEFORE_EACH: 'beforeEach',
/**
* Emitted after a `Test` has been added to a `Suite`.
*/
EVENT_SUITE_ADD_TEST: 'test'
}
);
Suite.constants = constants;

113
node_modules/mocha/lib/test.js generated vendored Normal file
View File

@@ -0,0 +1,113 @@
'use strict';
var Runnable = require('./runnable');
var utils = require('./utils');
var errors = require('./errors');
var createInvalidArgumentTypeError = errors.createInvalidArgumentTypeError;
var isString = utils.isString;
const {MOCHA_ID_PROP_NAME} = utils.constants;
module.exports = Test;
/**
* Initialize a new `Test` with the given `title` and callback `fn`.
*
* @public
* @class
* @extends Runnable
* @param {String} title - Test title (required)
* @param {Function} [fn] - Test callback. If omitted, the Test is considered "pending"
*/
function Test(title, fn) {
if (!isString(title)) {
throw createInvalidArgumentTypeError(
'Test argument "title" should be a string. Received type "' +
typeof title +
'"',
'title',
'string'
);
}
this.type = 'test';
Runnable.call(this, title, fn);
this.reset();
}
/**
* Inherit from `Runnable.prototype`.
*/
utils.inherits(Test, Runnable);
/**
* Resets the state initially or for a next run.
*/
Test.prototype.reset = function () {
Runnable.prototype.reset.call(this);
this.pending = !this.fn;
delete this.state;
};
/**
* Set or get retried test
*
* @private
*/
Test.prototype.retriedTest = function (n) {
if (!arguments.length) {
return this._retriedTest;
}
this._retriedTest = n;
};
/**
* Add test to the list of tests marked `only`.
*
* @private
*/
Test.prototype.markOnly = function () {
this.parent.appendOnlyTest(this);
};
Test.prototype.clone = function () {
var test = new Test(this.title, this.fn);
test.timeout(this.timeout());
test.slow(this.slow());
test.retries(this.retries());
test.currentRetry(this.currentRetry());
test.retriedTest(this.retriedTest() || this);
test.globals(this.globals());
test.parent = this.parent;
test.file = this.file;
test.ctx = this.ctx;
return test;
};
/**
* Returns an minimal object suitable for transmission over IPC.
* Functions are represented by keys beginning with `$$`.
* @private
* @returns {Object}
*/
Test.prototype.serialize = function serialize() {
return {
$$currentRetry: this._currentRetry,
$$fullTitle: this.fullTitle(),
$$isPending: Boolean(this.pending),
$$retriedTest: this._retriedTest || null,
$$slow: this._slow,
$$titlePath: this.titlePath(),
body: this.body,
duration: this.duration,
err: this.err,
parent: {
$$fullTitle: this.parent.fullTitle(),
[MOCHA_ID_PROP_NAME]: this.parent.id
},
speed: this.speed,
state: this.state,
title: this.title,
type: this.type,
file: this.file,
[MOCHA_ID_PROP_NAME]: this.id
};
};

640
node_modules/mocha/lib/utils.js generated vendored Normal file
View File

@@ -0,0 +1,640 @@
'use strict';
/**
* Various utility functions used throughout Mocha's codebase.
* @module utils
*/
/**
* Module dependencies.
*/
const {nanoid} = require('nanoid/non-secure');
var path = require('path');
var util = require('util');
var he = require('he');
const MOCHA_ID_PROP_NAME = '__mocha_id__';
/**
* Inherit the prototype methods from one constructor into another.
*
* @param {function} ctor - Constructor function which needs to inherit the
* prototype.
* @param {function} superCtor - Constructor function to inherit prototype from.
* @throws {TypeError} if either constructor is null, or if super constructor
* lacks a prototype.
*/
exports.inherits = util.inherits;
/**
* Escape special characters in the given string of html.
*
* @private
* @param {string} html
* @return {string}
*/
exports.escape = function (html) {
return he.encode(String(html), {useNamedReferences: false});
};
/**
* Test if the given obj is type of string.
*
* @private
* @param {Object} obj
* @return {boolean}
*/
exports.isString = function (obj) {
return typeof obj === 'string';
};
/**
* Compute a slug from the given `str`.
*
* @private
* @param {string} str
* @return {string}
*/
exports.slug = function (str) {
return str
.toLowerCase()
.replace(/\s+/g, '-')
.replace(/[^-\w]/g, '')
.replace(/-{2,}/g, '-');
};
/**
* Strip the function definition from `str`, and re-indent for pre whitespace.
*
* @param {string} str
* @return {string}
*/
exports.clean = function (str) {
str = str
.replace(/\r\n?|[\n\u2028\u2029]/g, '\n')
.replace(/^\uFEFF/, '')
// (traditional)-> space/name parameters body (lambda)-> parameters body multi-statement/single keep body content
.replace(
/^function(?:\s*|\s+[^(]*)\([^)]*\)\s*\{((?:.|\n)*?)\s*\}$|^\([^)]*\)\s*=>\s*(?:\{((?:.|\n)*?)\s*\}|((?:.|\n)*))$/,
'$1$2$3'
);
var spaces = str.match(/^\n?( *)/)[1].length;
var tabs = str.match(/^\n?(\t*)/)[1].length;
var re = new RegExp(
'^\n?' + (tabs ? '\t' : ' ') + '{' + (tabs || spaces) + '}',
'gm'
);
str = str.replace(re, '');
return str.trim();
};
/**
* If a value could have properties, and has none, this function is called,
* which returns a string representation of the empty value.
*
* Functions w/ no properties return `'[Function]'`
* Arrays w/ length === 0 return `'[]'`
* Objects w/ no properties return `'{}'`
* All else: return result of `value.toString()`
*
* @private
* @param {*} value The value to inspect.
* @param {string} typeHint The type of the value
* @returns {string}
*/
function emptyRepresentation(value, typeHint) {
switch (typeHint) {
case 'function':
return '[Function]';
case 'object':
return '{}';
case 'array':
return '[]';
default:
return value.toString();
}
}
/**
* Takes some variable and asks `Object.prototype.toString()` what it thinks it
* is.
*
* @private
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString
* @param {*} value The value to test.
* @returns {string} Computed type
* @example
* canonicalType({}) // 'object'
* canonicalType([]) // 'array'
* canonicalType(1) // 'number'
* canonicalType(false) // 'boolean'
* canonicalType(Infinity) // 'number'
* canonicalType(null) // 'null'
* canonicalType(new Date()) // 'date'
* canonicalType(/foo/) // 'regexp'
* canonicalType('type') // 'string'
* canonicalType(global) // 'global'
* canonicalType(new String('foo') // 'object'
* canonicalType(async function() {}) // 'asyncfunction'
* canonicalType(await import(name)) // 'module'
*/
var canonicalType = (exports.canonicalType = function canonicalType(value) {
if (value === undefined) {
return 'undefined';
} else if (value === null) {
return 'null';
} else if (Buffer.isBuffer(value)) {
return 'buffer';
}
return Object.prototype.toString
.call(value)
.replace(/^\[.+\s(.+?)]$/, '$1')
.toLowerCase();
});
/**
*
* Returns a general type or data structure of a variable
* @private
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures
* @param {*} value The value to test.
* @returns {string} One of undefined, boolean, number, string, bigint, symbol, object
* @example
* type({}) // 'object'
* type([]) // 'array'
* type(1) // 'number'
* type(false) // 'boolean'
* type(Infinity) // 'number'
* type(null) // 'null'
* type(new Date()) // 'object'
* type(/foo/) // 'object'
* type('type') // 'string'
* type(global) // 'object'
* type(new String('foo') // 'string'
*/
exports.type = function type(value) {
// Null is special
if (value === null) return 'null';
const primitives = new Set([
'undefined',
'boolean',
'number',
'string',
'bigint',
'symbol'
]);
const _type = typeof value;
if (_type === 'function') return _type;
if (primitives.has(_type)) return _type;
if (value instanceof String) return 'string';
if (value instanceof Error) return 'error';
if (Array.isArray(value)) return 'array';
return _type;
};
/**
* Stringify `value`. Different behavior depending on type of value:
*
* - If `value` is undefined or null, return `'[undefined]'` or `'[null]'`, respectively.
* - If `value` is not an object, function or array, return result of `value.toString()` wrapped in double-quotes.
* - If `value` is an *empty* object, function, or array, return result of function
* {@link emptyRepresentation}.
* - If `value` has properties, call {@link exports.canonicalize} on it, then return result of
* JSON.stringify().
*
* @private
* @see exports.type
* @param {*} value
* @return {string}
*/
exports.stringify = function (value) {
var typeHint = canonicalType(value);
if (!~['object', 'array', 'function'].indexOf(typeHint)) {
if (typeHint === 'buffer') {
var json = Buffer.prototype.toJSON.call(value);
// Based on the toJSON result
return jsonStringify(
json.data && json.type ? json.data : json,
2
).replace(/,(\n|$)/g, '$1');
}
// IE7/IE8 has a bizarre String constructor; needs to be coerced
// into an array and back to obj.
if (typeHint === 'string' && typeof value === 'object') {
value = value.split('').reduce(function (acc, char, idx) {
acc[idx] = char;
return acc;
}, {});
typeHint = 'object';
} else {
return jsonStringify(value);
}
}
for (var prop in value) {
if (Object.prototype.hasOwnProperty.call(value, prop)) {
return jsonStringify(
exports.canonicalize(value, null, typeHint),
2
).replace(/,(\n|$)/g, '$1');
}
}
return emptyRepresentation(value, typeHint);
};
/**
* like JSON.stringify but more sense.
*
* @private
* @param {Object} object
* @param {number=} spaces
* @param {number=} depth
* @returns {*}
*/
function jsonStringify(object, spaces, depth) {
if (typeof spaces === 'undefined') {
// primitive types
return _stringify(object);
}
depth = depth || 1;
var space = spaces * depth;
var str = Array.isArray(object) ? '[' : '{';
var end = Array.isArray(object) ? ']' : '}';
var length =
typeof object.length === 'number'
? object.length
: Object.keys(object).length;
// `.repeat()` polyfill
function repeat(s, n) {
return new Array(n).join(s);
}
function _stringify(val) {
switch (canonicalType(val)) {
case 'null':
case 'undefined':
val = '[' + val + ']';
break;
case 'array':
case 'object':
val = jsonStringify(val, spaces, depth + 1);
break;
case 'boolean':
case 'regexp':
case 'symbol':
case 'number':
val =
val === 0 && 1 / val === -Infinity // `-0`
? '-0'
: val.toString();
break;
case 'bigint':
val = val.toString() + 'n';
break;
case 'date':
var sDate = isNaN(val.getTime()) ? val.toString() : val.toISOString();
val = '[Date: ' + sDate + ']';
break;
case 'buffer':
var json = val.toJSON();
// Based on the toJSON result
json = json.data && json.type ? json.data : json;
val = '[Buffer: ' + jsonStringify(json, 2, depth + 1) + ']';
break;
default:
val =
val === '[Function]' || val === '[Circular]'
? val
: JSON.stringify(val); // string
}
return val;
}
for (var i in object) {
if (!Object.prototype.hasOwnProperty.call(object, i)) {
continue; // not my business
}
--length;
str +=
'\n ' +
repeat(' ', space) +
(Array.isArray(object) ? '' : '"' + i + '": ') + // key
_stringify(object[i]) + // value
(length ? ',' : ''); // comma
}
return (
str +
// [], {}
(str.length !== 1 ? '\n' + repeat(' ', --space) + end : end)
);
}
/**
* Return a new Thing that has the keys in sorted order. Recursive.
*
* If the Thing...
* - has already been seen, return string `'[Circular]'`
* - is `undefined`, return string `'[undefined]'`
* - is `null`, return value `null`
* - is some other primitive, return the value
* - is not a primitive or an `Array`, `Object`, or `Function`, return the value of the Thing's `toString()` method
* - is a non-empty `Array`, `Object`, or `Function`, return the result of calling this function again.
* - is an empty `Array`, `Object`, or `Function`, return the result of calling `emptyRepresentation()`
*
* @private
* @see {@link exports.stringify}
* @param {*} value Thing to inspect. May or may not have properties.
* @param {Array} [stack=[]] Stack of seen values
* @param {string} [typeHint] Type hint
* @return {(Object|Array|Function|string|undefined)}
*/
exports.canonicalize = function canonicalize(value, stack, typeHint) {
var canonicalizedObj;
/* eslint-disable no-unused-vars */
var prop;
/* eslint-enable no-unused-vars */
typeHint = typeHint || canonicalType(value);
function withStack(value, fn) {
stack.push(value);
fn();
stack.pop();
}
stack = stack || [];
if (stack.indexOf(value) !== -1) {
return '[Circular]';
}
switch (typeHint) {
case 'undefined':
case 'buffer':
case 'null':
canonicalizedObj = value;
break;
case 'array':
withStack(value, function () {
canonicalizedObj = value.map(function (item) {
return exports.canonicalize(item, stack);
});
});
break;
case 'function':
/* eslint-disable-next-line no-unused-vars, no-unreachable-loop */
for (prop in value) {
canonicalizedObj = {};
break;
}
/* eslint-enable guard-for-in */
if (!canonicalizedObj) {
canonicalizedObj = emptyRepresentation(value, typeHint);
break;
}
/* falls through */
case 'object':
canonicalizedObj = canonicalizedObj || {};
withStack(value, function () {
Object.keys(value)
.sort()
.forEach(function (key) {
canonicalizedObj[key] = exports.canonicalize(value[key], stack);
});
});
break;
case 'date':
case 'number':
case 'regexp':
case 'boolean':
case 'symbol':
canonicalizedObj = value;
break;
default:
canonicalizedObj = value + '';
}
return canonicalizedObj;
};
/**
* @summary
* This Filter based on `mocha-clean` module.(see: `github.com/rstacruz/mocha-clean`)
* @description
* When invoking this function you get a filter function that get the Error.stack as an input,
* and return a prettify output.
* (i.e: strip Mocha and internal node functions from stack trace).
* @returns {Function}
*/
exports.stackTraceFilter = function () {
// TODO: Replace with `process.browser`
var is = typeof document === 'undefined' ? {node: true} : {browser: true};
var slash = path.sep;
var cwd;
if (is.node) {
cwd = exports.cwd() + slash;
} else {
cwd = (
typeof location === 'undefined' ? window.location : location
).href.replace(/\/[^/]*$/, '/');
slash = '/';
}
function isMochaInternal(line) {
return (
~line.indexOf('node_modules' + slash + 'mocha' + slash) ||
~line.indexOf(slash + 'mocha.js') ||
~line.indexOf(slash + 'mocha.min.js')
);
}
function isNodeInternal(line) {
return (
~line.indexOf('(timers.js:') ||
~line.indexOf('(events.js:') ||
~line.indexOf('(node.js:') ||
~line.indexOf('(module.js:') ||
~line.indexOf('GeneratorFunctionPrototype.next (native)') ||
false
);
}
return function (stack) {
stack = stack.split('\n');
stack = stack.reduce(function (list, line) {
if (isMochaInternal(line)) {
return list;
}
if (is.node && isNodeInternal(line)) {
return list;
}
// Clean up cwd(absolute)
if (/:\d+:\d+\)?$/.test(line)) {
line = line.replace('(' + cwd, '(');
}
list.push(line);
return list;
}, []);
return stack.join('\n');
};
};
/**
* Crude, but effective.
* @public
* @param {*} value
* @returns {boolean} Whether or not `value` is a Promise
*/
exports.isPromise = function isPromise(value) {
return (
typeof value === 'object' &&
value !== null &&
typeof value.then === 'function'
);
};
/**
* Clamps a numeric value to an inclusive range.
*
* @param {number} value - Value to be clamped.
* @param {number[]} range - Two element array specifying [min, max] range.
* @returns {number} clamped value
*/
exports.clamp = function clamp(value, range) {
return Math.min(Math.max(value, range[0]), range[1]);
};
/**
* It's a noop.
* @public
*/
exports.noop = function () {};
/**
* Creates a map-like object.
*
* @description
* A "map" is an object with no prototype, for our purposes. In some cases
* this would be more appropriate than a `Map`, especially if your environment
* doesn't support it. Recommended for use in Mocha's public APIs.
*
* @public
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map#Custom_and_Null_objects|MDN:Map}
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Custom_and_Null_objects|MDN:Object.create - Custom objects}
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Custom_and_Null_objects|MDN:Object.assign}
* @param {...*} [obj] - Arguments to `Object.assign()`.
* @returns {Object} An object with no prototype, having `...obj` properties
*/
exports.createMap = function (obj) {
return Object.assign.apply(
null,
[Object.create(null)].concat(Array.prototype.slice.call(arguments))
);
};
/**
* Creates a read-only map-like object.
*
* @description
* This differs from {@link module:utils.createMap createMap} only in that
* the argument must be non-empty, because the result is frozen.
*
* @see {@link module:utils.createMap createMap}
* @param {...*} [obj] - Arguments to `Object.assign()`.
* @returns {Object} A frozen object with no prototype, having `...obj` properties
* @throws {TypeError} if argument is not a non-empty object.
*/
exports.defineConstants = function (obj) {
if (canonicalType(obj) !== 'object' || !Object.keys(obj).length) {
throw new TypeError('Invalid argument; expected a non-empty object');
}
return Object.freeze(exports.createMap(obj));
};
/**
* Returns current working directory
*
* Wrapper around `process.cwd()` for isolation
* @private
*/
exports.cwd = function cwd() {
return process.cwd();
};
/**
* Returns `true` if Mocha is running in a browser.
* Checks for `process.browser`.
* @returns {boolean}
* @private
*/
exports.isBrowser = function isBrowser() {
return Boolean(process.browser);
};
/*
* Casts `value` to an array; useful for optionally accepting array parameters
*
* It follows these rules, depending on `value`. If `value` is...
* 1. `undefined`: return an empty Array
* 2. `null`: return an array with a single `null` element
* 3. Any other object: return the value of `Array.from()` _if_ the object is iterable
* 4. otherwise: return an array with a single element, `value`
* @param {*} value - Something to cast to an Array
* @returns {Array<*>}
*/
exports.castArray = function castArray(value) {
if (value === undefined) {
return [];
}
if (value === null) {
return [null];
}
if (
typeof value === 'object' &&
(typeof value[Symbol.iterator] === 'function' || value.length !== undefined)
) {
return Array.from(value);
}
return [value];
};
exports.constants = exports.defineConstants({
MOCHA_ID_PROP_NAME
});
/**
* Creates a new unique identifier
* @returns {string} Unique identifier
*/
exports.uniqueID = () => nanoid();
exports.assignNewMochaID = obj => {
const id = exports.uniqueID();
Object.defineProperty(obj, MOCHA_ID_PROP_NAME, {
get() {
return id;
}
});
return obj;
};
/**
* Retrieves a Mocha ID from an object, if present.
* @param {*} [obj] - Object
* @returns {string|void}
*/
exports.getMochaID = obj =>
obj && typeof obj === 'object' ? obj[MOCHA_ID_PROP_NAME] : undefined;