added unit testing, and started implementing unit tests...phew

This commit is contained in:
Josh Burman
2019-03-12 22:28:02 -04:00
parent 74aad4a957
commit e8c2539f1b
3489 changed files with 464813 additions and 88 deletions

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

@ -0,0 +1,68 @@
'use strict';
/**
* This is where we finally parse and handle arguments passed to the `mocha` executable.
* Option parsing is handled by {@link https://npm.im/yargs yargs}.
* If executed via `node`, this module will run {@linkcode module:lib/cli/cli.main main()}.
*
* @private
* @module
*/
const debug = require('debug')('mocha:cli:cli');
const symbols = require('log-symbols');
const yargs = require('yargs');
const path = require('path');
const {loadOptions} = require('./options');
const commands = require('./commands');
const ansi = require('ansi-colors');
const {repository, homepage, version, gitter} = require('../../package.json');
/**
* - 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`
* @summary Mocha's main entry point from the command-line.
* @param {string[]} argv - Array of arguments to parse, or by default the lovely `process.argv.slice(2)`
*/
exports.main = (argv = process.argv.slice(2)) => {
debug('entered main with raw args', argv);
// ensure we can require() from current working directory
module.paths.push(process.cwd(), path.resolve('node_modules'));
Error.stackTraceLimit = Infinity; // configurable via --stack-trace-limit?
yargs
.scriptName('mocha')
.command(commands.run)
.command(commands.init)
.updateStrings({
'Positionals:': 'Positional Arguments',
'Options:': 'Other Options',
'Commands:': 'Commands'
})
.fail((msg, err, yargs) => {
debug(err);
yargs.showHelp();
console.error(`\n${symbols.error} ${ansi.red('ERROR:')} ${msg}`);
process.exit(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)}
`
)
.parse(argv, loadOptions(argv));
};
// allow direct execution
if (require.main === module) {
exports.main();
}

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://git.io/fpJ0G
* @private
* @module
*/
exports.init = require('./init');
// default command
exports.run = require('./run');

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

@ -0,0 +1,79 @@
'use strict';
/**
* Responsible for loading / finding Mocha's "rc" files.
* This doesn't have anything to do with `mocha.opts`.
*
* @private
* @module
*/
const fs = require('fs');
const findUp = require('findup-sync');
const path = require('path');
const debug = require('debug')('mocha:cli:config');
/**
* 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.js',
'.mocharc.yaml',
'.mocharc.yml',
'.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').safeLoad(fs.readFileSync(filepath, 'utf8')),
js: filepath => 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.
* @param {string} filepath - Config file path to load
* @returns {Object} Parsed config object
* @private
*/
exports.loadConfig = filepath => {
let config = {};
const ext = path.extname(filepath);
try {
if (/\.ya?ml/.test(ext)) {
config = parsers.yaml(filepath);
} else if (ext === '.js') {
config = parsers.js(filepath);
} else {
config = parsers.json(filepath);
}
} catch (err) {
throw new Error(`failed to parse ${filepath}: ${err}`);
}
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 = process.cwd()) => {
const filepath = findUp(exports.CONFIG_FILES, {cwd});
if (filepath) {
debug(`found config at ${filepath}`);
}
return filepath;
};

9
node_modules/mocha/lib/cli/index.js generated vendored Normal file
View File

@ -0,0 +1,9 @@
'use strict';
/**
* Just exports {@link module:lib/cli/cli} for convenience.
* @private
* @module lib/cli
* @exports module:lib/cli/cli
*/
module.exports = require('./cli');

37
node_modules/mocha/lib/cli/init.js generated vendored Normal file
View File

@ -0,0 +1,37 @@
'use strict';
/**
* Command module for "init" command
*
* @private
* @module
*/
const fs = require('fs');
const path = require('path');
const mkdirp = require('mkdirp');
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, '..', '..');
mkdirp.sync(destdir);
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);
};

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 = require('node-environment-flags');
const unparse = require('yargs-unparser');
/**
* These flags are considered "debug" flags.
* @see {@link impliesNoTimeouts}
* @private
*/
const debugFlags = new Set(['debug', 'debug-brk', '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 (
// treat --require/-r as Mocha flag even though it's also a node flag
!(flag === 'require' || flag === 'r') &&
// 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
(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('')
: [];
};

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

@ -0,0 +1,70 @@
'use strict';
/**
* Contains "command" code for "one-and-dones"--options passed
* to Mocha which cause it to just dump some info and exit.
* See {@link module:lib/cli/one-and-dones.ONE_AND_DONE_ARGS ONE_AND_DONE_ARGS} for more info.
* @module
* @private
*/
const align = require('wide-align');
const Mocha = require('../mocha');
/**
* Dumps a sorted list of the enumerable, lower-case keys of some object
* to `STDOUT`.
* @param {Object} obj - Object, ostensibly having some enumerable keys
* @ignore
* @private
*/
const showKeys = obj => {
console.log();
const keys = Object.keys(obj);
const maxKeyLength = keys.reduce((max, key) => Math.max(max, key.length), 0);
keys
.filter(
key => /^[a-z]/.test(key) && !obj[key].browserOnly && !obj[key].abstract
)
.sort()
.forEach(key => {
const description = obj[key].description;
console.log(
` ${align.left(key, maxKeyLength + 1)}${
description ? `- ${description}` : ''
}`
);
});
console.log();
};
/**
* Handlers for one-and-done options
* @namespace
* @private
*/
exports.ONE_AND_DONES = {
/**
* Dump list of built-in interfaces
* @private
*/
interfaces: () => {
showKeys(Mocha.interfaces);
},
/**
* Dump list of built-in reporters
* @private
*/
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))
);

337
node_modules/mocha/lib/cli/options.js generated vendored Normal file
View File

@ -0,0 +1,337 @@
'use strict';
/**
* Main entry point for handling filesystem-based configuration,
* whether that's `mocha.opts` or a config file or `package.json` or whatever.
* @module
*/
const fs = require('fs');
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 yargsParserConfig = require('../../package.json').yargs;
const {list} = require('./run-helpers');
const {loadConfig, findConfig} = require('./config');
const findup = require('findup-sync');
const {deprecate} = require('../utils');
const debug = require('debug')('mocha:cli:options');
const {createMissingArgumentError} = require('../errors');
const {isNodeFlag} = require('./node-flags');
/**
* 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
*/
/**
* 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({}, yargsParserConfig, {
'camel-case-expansion': false
});
/**
* This is a really fancy way to ensure unique values for `array`-type
* options.
* This is passed as the `coerce` option to `yargs-parser`
* @private
* @ignore
*/
const coerceOpts = types.array.reduce(
(acc, arg) => Object.assign(acc, {[arg]: v => Array.from(new Set(list(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} configObjects - `configObjects` for yargs-parser
* @private
* @ignore
*/
const parse = (args = [], ...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,
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) {
throw createMissingArgumentError(result.error.message);
}
// reapply "=" arg values from above
nodeArgs.forEach(([key, value]) => {
result.argv[key] = value;
});
return result.argv;
};
/**
* - Replaces comments with empty strings
* - Replaces escaped spaces (e.g., 'xxx\ yyy') with HTML space
* - Splits on whitespace, creating array of substrings
* - Filters empty string elements from array
* - Replaces any HTML space with space
* @summary Parses options read from run-control file.
* @private
* @param {string} content - Content read from run-control file.
* @returns {string[]} cmdline options (and associated arguments)
* @ignore
*/
const parseMochaOpts = content =>
content
.replace(/^#.*$/gm, '')
.replace(/\\\s/g, '%20')
.split(/\s/)
.filter(Boolean)
.map(value => value.replace(/%20/g, ' '));
/**
* Prepends options from run-control file to the command line arguments.
*
* @deprecated Deprecated in v6.0.0; This function is no longer used internally and will be removed in a future version.
* @public
* @alias module:lib/cli/options
* @see {@link https://mochajs.org/#mochaopts|mocha.opts}
*/
module.exports = function getOptions() {
deprecate(
'getOptions() is DEPRECATED and will be removed from a future version of Mocha. Use loadOptions() instead'
);
if (process.argv.length === 3 && ONE_AND_DONE_ARGS.has(process.argv[2])) {
return;
}
const optsPath =
process.argv.indexOf('--opts') === -1
? mocharc.opts
: process.argv[process.argv.indexOf('--opts') + 1];
try {
const options = parseMochaOpts(fs.readFileSync(optsPath, 'utf8'));
process.argv = process.argv
.slice(0, 2)
.concat(options.concat(process.argv.slice(2)));
} catch (ignore) {
// NOTE: should console.error() and throw the error
}
process.env.LOADED_MOCHA_OPTS = true;
};
/**
* Given filepath in `args.opts`, attempt to load and parse a `mocha.opts` file.
* @param {Object} [args] - Arguments object
* @param {string|boolean} [args.opts] - Filepath to mocha.opts; defaults to whatever's in `mocharc.opts`, or `false` to skip
* @returns {external:yargsParser.Arguments|void} If read, object containing parsed arguments
* @memberof module:lib/cli/options
* @public
*/
const loadMochaOpts = (args = {}) => {
let result;
let filepath = args.opts;
// /dev/null is backwards compat
if (filepath === false || filepath === '/dev/null') {
return result;
}
filepath = filepath || mocharc.opts;
result = {};
let mochaOpts;
try {
mochaOpts = fs.readFileSync(filepath, 'utf8');
debug(`read ${filepath}`);
} catch (err) {
if (args.opts) {
throw new Error(`Unable to read ${filepath}: ${err}`);
}
// ignore otherwise. we tried
debug(`No mocha.opts found at ${filepath}`);
}
// real args should override `mocha.opts` which should override defaults.
// if there's an exception to catch here, I'm not sure what it is.
// by attaching the `no-opts` arg, we avoid re-parsing of `mocha.opts`.
if (mochaOpts) {
result = parse(parseMochaOpts(mochaOpts));
debug(`${filepath} parsed succesfully`);
}
return result;
};
module.exports.loadMochaOpts = loadMochaOpts;
/**
* 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
* @memberof module:lib/cli/options
* @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
* @memberof module:lib/cli/options
* @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(mocharc.package);
if (filepath) {
try {
const pkg = JSON.parse(fs.readFileSync(filepath, 'utf8'));
if (pkg.mocha) {
debug(`'mocha' prop of package.json parsed:`, pkg.mocha);
result = pkg.mocha;
} else {
debug(`no config found in ${filepath}`);
}
} catch (err) {
if (args.package) {
throw new Error(`Unable to read/parse ${filepath}: ${err}`);
}
debug(`failed to read default package.json at ${filepath}; ignoring`);
}
}
return result;
};
module.exports.loadPkgRc = loadPkgRc;
/**
* Priority list:
*
* 1. Command-line args
* 2. RC file (`.mocharc.js`, `.mocharc.ya?ml`, `mocharc.json`)
* 3. `mocha` prop of `package.json`
* 4. `mocha.opts`
* 5. 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 `mocha.opts`, `.mocharc.*` and `package.json`.
* @param {string|string[]} [argv] - Arguments to parse
* @public
* @memberof module:lib/cli/options
* @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 mocha.opts
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);
const optsConfig = loadMochaOpts(args);
if (rcConfig) {
args.config = false;
args._ = args._.concat(rcConfig._ || []);
}
if (pkgConfig) {
args.package = false;
args._ = args._.concat(pkgConfig._ || []);
}
if (optsConfig) {
args.opts = false;
args._ = args._.concat(optsConfig._ || []);
}
args = parse(
args._,
args,
rcConfig || {},
pkgConfig || {},
optsConfig || {},
mocharc
);
// 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;

337
node_modules/mocha/lib/cli/run-helpers.js generated vendored Normal file
View File

@ -0,0 +1,337 @@
'use strict';
/**
* Helper scripts for the `run` command
* @see module:lib/cli/run
* @module
* @private
*/
const fs = require('fs');
const path = require('path');
const ansi = require('ansi-colors');
const debug = require('debug')('mocha:cli:run:helpers');
const minimatch = require('minimatch');
const Context = require('../context');
const Mocha = require('../mocha');
const utils = require('../utils');
const cwd = (exports.cwd = process.cwd());
/**
* 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();
};
/**
* Hide the cursor.
* @ignore
* @private
*/
const hideCursor = () => {
process.stdout.write('\u001b[?25l');
};
/**
* Show the cursor.
* @ignore
* @private
*/
const showCursor = () => {
process.stdout.write('\u001b[?25h');
};
/**
* Stop cursor business
* @private
*/
const stop = () => {
process.stdout.write('\u001b[2K');
};
/**
* 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>`
* @param {string[]} requires - Modules to require
* @private
*/
exports.handleRequires = (requires = []) => {
requires.forEach(mod => {
let modpath = mod;
if (fs.existsSync(mod, {cwd}) || fs.existsSync(`${mod}.js`, {cwd})) {
modpath = path.resolve(mod);
debug(`resolved ${mod} to ${modpath}`);
}
require(modpath);
debug(`loaded require "${mod}"`);
});
};
/**
* Smash together an array of test files in the correct order
* @param {Object} [opts] - Options
* @param {string[]} [opts.extension] - File extensions to use
* @param {string[]} [opts.spec] - Files, dirs, globs to run
* @param {string[]} [opts.exclude] - Files, dirs, globs to exclude
* @param {boolean} [opts.recursive=false] - Find files recursively
* @param {boolean} [opts.sort=false] - Sort test files
* @returns {string[]} List of files to test
* @private
*/
exports.handleFiles = ({
exclude = [],
extension = [],
file = [],
recursive = false,
sort = false,
spec = []
} = {}) => {
let files = [];
const unmatched = [];
spec.forEach(arg => {
let newFiles;
try {
newFiles = utils.lookupFiles(arg, extension, recursive);
} catch (err) {
if (err.code === 'ERR_MOCHA_NO_FILES_MATCH_PATTERN') {
unmatched.push({message: err.message, pattern: err.pattern});
return;
}
throw err;
}
if (typeof newFiles !== 'undefined') {
if (typeof newFiles === 'string') {
newFiles = [newFiles];
}
newFiles = newFiles.filter(fileName =>
exclude.every(pattern => !minimatch(fileName, pattern))
);
}
files = files.concat(newFiles);
});
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 an warning
unmatched.forEach(warning => {
console.warn(ansi.yellow(`Warning: ${warning.message}`));
});
}
const fileArgs = file.map(filepath => path.resolve(filepath));
files = files.map(filepath => path.resolve(filepath));
// ensure we don't sort the stuff from fileArgs; order is important!
if (sort) {
files.sort();
}
// add files given through --file to be ran first
files = fileArgs.concat(files);
debug('files (in order): ', files);
return files;
};
/**
* Give Mocha files and tell it to run
* @param {Mocha} mocha - Mocha instance
* @param {Options} [opts] - Options
* @param {string[]} [opts.files] - List of test files
* @param {boolean} [opts.exit] - Whether or not to force-exit after tests are complete
* @returns {Runner}
* @private
*/
exports.singleRun = (mocha, {files = [], exit = false} = {}) => {
mocha.files = files;
return mocha.run(exit ? exitMocha : exitMochaLater);
};
/**
* Run Mocha in "watch" mode
* @param {Mocha} mocha - Mocha instance
* @param {Object} [opts] - Options
* @param {string[]} [opts.extension] - List of extensions to watch
* @param {string|RegExp} [opts.grep] - Grep for test titles
* @param {string} [opts.ui=bdd] - User interface
* @param {string[]} [files] - Array of test files
* @private
*/
exports.watchRun = (
mocha,
{extension = ['js'], grep = '', ui = 'bdd', files = []} = {}
) => {
let runner;
console.log();
hideCursor();
process.on('SIGINT', () => {
showCursor();
console.log('\n');
process.exit(130);
});
const watchFiles = utils.files(cwd, extension);
let runAgain = false;
const loadAndRun = () => {
try {
mocha.files = files;
runAgain = false;
runner = mocha.run(() => {
runner = null;
if (runAgain) {
rerun();
}
});
} catch (e) {
console.log(e.stack);
}
};
const purge = () => {
watchFiles.forEach(Mocha.unloadFile);
};
loadAndRun();
const rerun = () => {
purge();
stop();
if (!grep) {
mocha.grep(null);
}
mocha.suite = mocha.suite.clone();
mocha.suite.ctx = new Context();
mocha.ui(ui);
loadAndRun();
};
utils.watch(watchFiles, () => {
runAgain = true;
if (runner) {
runner.abort();
} else {
rerun();
}
});
};
/**
* Actually run tests
* @param {Mocha} mocha - Mocha instance
* @param {Object} [opts] - Options
* @param {boolean} [opts.watch=false] - Enable watch mode
* @param {string[]} [opts.extension] - List of extensions to watch
* @param {string|RegExp} [opts.grep] - Grep for test titles
* @param {string} [opts.ui=bdd] - User interface
* @param {boolean} [opts.exit=false] - Force-exit Mocha when tests done
* @param {string[]} [files] - Array of test files
* @private
*/
exports.runMocha = (
mocha,
{watch = false, extension = ['js'], grep = '', ui = 'bdd', exit = false} = {},
files = []
) => {
if (watch) {
exports.watchRun(mocha, {extension, grep, ui, files});
} else {
exports.singleRun(mocha, {files, exit});
}
};
/**
* Used for `--reporter` and `--ui`. Ensures there's only one, and asserts
* that it actually exists.
* @todo XXX This must get run after requires are processed, as it'll prevent
* interfaces from loading.
* @param {Object} opts - Options object
* @param {string} key - Resolvable module name or path
* @param {Object} [map] - An object perhaps having key `key`
* @private
*/
exports.validatePlugin = (opts, key, map = {}) => {
if (Array.isArray(opts[key])) {
throw new TypeError(`"--${key} <${key}>" can only be specified once`);
}
const unknownError = () => new Error(`Unknown "${key}": ${opts[key]}`);
if (!map[opts[key]]) {
try {
opts[key] = require(opts[key]);
} catch (err) {
if (err.code === 'MODULE_NOT_FOUND') {
// Try to load reporters from a path (absolute or relative)
try {
opts[key] = require(path.resolve(process.cwd(), opts[key]));
} catch (err) {
throw unknownError();
}
} else {
throw unknownError();
}
}
}
};

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

@ -0,0 +1,76 @@
'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
*/
exports.types = {
array: [
'exclude',
'extension',
'file',
'global',
'require',
'reporter-option',
'spec'
],
boolean: [
'allow-uncaught',
'async-only',
'bail',
'check-leaks',
'color',
'delay',
'diff',
'exit',
'forbid-only',
'forbid-pending',
'full-trace',
'growl',
'inline-diffs',
'interfaces',
'invert',
'no-colors',
'recursive',
'reporters',
'sort',
'watch'
],
number: ['retries', 'slow', 'timeout'],
string: ['fgrep', 'grep', 'package', 'reporter', 'ui']
};
/**
* 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'],
extension: ['watch-extensions'],
fgrep: ['f'],
global: ['globals'],
grep: ['g'],
growl: ['G'],
invert: ['i'],
'no-colors': ['C'],
reporter: ['R'],
'reporter-option': ['reporter-options', 'O'],
require: ['r'],
slow: ['s'],
sort: ['S'],
timeout: ['t', 'timeouts'],
ui: ['u'],
watch: ['w']
};

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

@ -0,0 +1,297 @@
'use strict';
/**
* Definition for Mocha's default ("run tests") command
*
* @module
* @private
*/
const Mocha = require('../mocha');
const {
createUnsupportedError,
createInvalidArgumentValueError,
createMissingArgumentError
} = require('../errors');
const {
list,
handleFiles,
handleRequires,
validatePlugin,
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..]', 'debug [spec..]'];
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
},
exclude: {
defaultDescription: '(none)',
description: 'Ignore file(s) or glob pattern(s)',
group: GROUPS.FILES,
requiresArg: true
},
exit: {
description: 'Force Mocha to quit after tests complete',
group: GROUPS.RULES
},
extension: {
default: defaults.extension,
defaultDescription: 'js',
description: 'File extension(s) to load and/or watch',
group: GROUPS.FILES,
requiresArg: true,
coerce: list
},
fgrep: {
conflicts: 'grep',
description: 'Only run tests containing this string',
group: GROUPS.FILTERS,
requiresArg: true
},
file: {
defaultDescription: '(none)',
description:
'Specify file(s) to be loaded prior to root suite execution',
group: GROUPS.FILES,
normalize: true,
requiresArg: true
},
'forbid-only': {
description: 'Fail if exclusive test(s) encountered',
group: GROUPS.RULES
},
'forbid-pending': {
description: 'Fail if pending test(s) encountered',
group: GROUPS.RULES
},
'full-trace': {
description: 'Display full stack traces',
group: GROUPS.OUTPUT
},
global: {
coerce: list,
description: 'List of allowed global variables',
group: GROUPS.RULES,
requiresArg: true
},
grep: {
coerce: value => (!value ? null : value),
conflicts: 'fgrep',
description: 'Only run tests matching this string or regexp',
group: GROUPS.FILTERS,
requiresArg: true
},
growl: {
description: 'Enable Growl notifications',
group: GROUPS.OUTPUT
},
'inline-diffs': {
description:
'Display actual/expected differences inline within each string',
group: GROUPS.OUTPUT
},
interfaces: {
conflicts: Array.from(ONE_AND_DONE_ARGS),
description: 'List built-in user interfaces & exit'
},
invert: {
description: 'Inverts --grep and --fgrep matches',
group: GROUPS.FILTERS
},
'no-colors': {
description: 'Force-disable color output',
group: GROUPS.OUTPUT,
hidden: true
},
opts: {
default: defaults.opts,
description: 'Path to `mocha.opts`',
group: GROUPS.CONFIG,
normalize: true,
requiresArg: true
},
package: {
description: 'Path to package.json for config',
group: GROUPS.CONFIG,
normalize: true,
requiresArg: true
},
recursive: {
description: 'Look for tests in subdirectories',
group: GROUPS.FILES
},
reporter: {
default: defaults.reporter,
description: 'Specify reporter to use',
group: GROUPS.OUTPUT,
requiresArg: true
},
reporters: {
conflicts: Array.from(ONE_AND_DONE_ARGS),
description: 'List built-in reporters & exit'
},
'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
}
})
.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.compilers) {
throw createUnsupportedError(
`--compilers is DEPRECATED and no longer supported.
See https://git.io/vdcSr for migration information.`
);
}
// load requires first, because it can impact "plugin" validation
handleRequires(argv.require);
validatePlugin(argv, 'reporter', Mocha.reporters);
validatePlugin(argv, 'ui', Mocha.interfaces);
return true;
})
.array(types.array)
.boolean(types.boolean)
.string(types.string)
.number(types.number)
.alias(aliases);
exports.handler = argv => {
debug('post-yargs config', argv);
const mocha = new Mocha(argv);
const files = handleFiles(argv);
debug('running tests with files', files);
runMocha(mocha, argv, files);
};