mocha 9.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1015 -0
- package/LICENSE +22 -0
- package/README.md +70 -0
- package/assets/growl/error.png +0 -0
- package/assets/growl/ok.png +0 -0
- package/bin/_mocha +10 -0
- package/bin/mocha +142 -0
- package/browser-entry.js +216 -0
- package/index.js +3 -0
- package/lib/browser/growl.js +169 -0
- package/lib/browser/highlight-tags.js +39 -0
- package/lib/browser/parse-query.js +24 -0
- package/lib/browser/progress.js +123 -0
- package/lib/browser/template.html +20 -0
- package/lib/cli/cli.js +89 -0
- package/lib/cli/collect-files.js +92 -0
- package/lib/cli/commands.js +13 -0
- package/lib/cli/config.js +105 -0
- package/lib/cli/index.js +3 -0
- package/lib/cli/init.js +36 -0
- package/lib/cli/lookup-files.js +145 -0
- package/lib/cli/node-flags.js +85 -0
- package/lib/cli/one-and-dones.js +69 -0
- package/lib/cli/options.js +261 -0
- package/lib/cli/run-helpers.js +243 -0
- package/lib/cli/run-option-metadata.js +117 -0
- package/lib/cli/run.js +379 -0
- package/lib/cli/watch-run.js +380 -0
- package/lib/context.js +86 -0
- package/lib/errors.js +563 -0
- package/lib/hook.js +89 -0
- package/lib/interfaces/bdd.js +111 -0
- package/lib/interfaces/common.js +193 -0
- package/lib/interfaces/exports.js +60 -0
- package/lib/interfaces/index.js +6 -0
- package/lib/interfaces/qunit.js +98 -0
- package/lib/interfaces/tdd.js +106 -0
- package/lib/mocha.js +1374 -0
- package/lib/mocharc.json +10 -0
- package/lib/nodejs/buffered-worker-pool.js +172 -0
- package/lib/nodejs/esm-utils.js +109 -0
- package/lib/nodejs/file-unloader.js +15 -0
- package/lib/nodejs/growl.js +137 -0
- package/lib/nodejs/parallel-buffered-runner.js +433 -0
- package/lib/nodejs/reporters/parallel-buffered.js +165 -0
- package/lib/nodejs/serializer.js +412 -0
- package/lib/nodejs/worker.js +151 -0
- package/lib/pending.js +16 -0
- package/lib/plugin-loader.js +286 -0
- package/lib/reporters/base.js +537 -0
- package/lib/reporters/doc.js +95 -0
- package/lib/reporters/dot.js +81 -0
- package/lib/reporters/html.js +390 -0
- package/lib/reporters/index.js +19 -0
- package/lib/reporters/json-stream.js +92 -0
- package/lib/reporters/json.js +162 -0
- package/lib/reporters/landing.js +116 -0
- package/lib/reporters/list.js +78 -0
- package/lib/reporters/markdown.js +112 -0
- package/lib/reporters/min.js +52 -0
- package/lib/reporters/nyan.js +276 -0
- package/lib/reporters/progress.js +104 -0
- package/lib/reporters/spec.js +99 -0
- package/lib/reporters/tap.js +293 -0
- package/lib/reporters/xunit.js +217 -0
- package/lib/runnable.js +476 -0
- package/lib/runner.js +1269 -0
- package/lib/stats-collector.js +83 -0
- package/lib/suite.js +695 -0
- package/lib/test.js +113 -0
- package/lib/utils.js +641 -0
- package/mocha-es2018.js +19816 -0
- package/mocha.css +325 -0
- package/mocha.js +30844 -0
- package/mocha.js.map +1 -0
- package/package.json +200 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Parse the given `qs`.
|
|
5
|
+
*
|
|
6
|
+
* @private
|
|
7
|
+
* @param {string} qs
|
|
8
|
+
* @return {Object<string, string>}
|
|
9
|
+
*/
|
|
10
|
+
module.exports = function parseQuery(qs) {
|
|
11
|
+
return qs
|
|
12
|
+
.replace('?', '')
|
|
13
|
+
.split('&')
|
|
14
|
+
.reduce(function(obj, pair) {
|
|
15
|
+
var i = pair.indexOf('=');
|
|
16
|
+
var key = pair.slice(0, i);
|
|
17
|
+
var val = pair.slice(++i);
|
|
18
|
+
|
|
19
|
+
// Due to how the URLSearchParams API treats spaces
|
|
20
|
+
obj[key] = decodeURIComponent(val.replace(/\+/g, '%20'));
|
|
21
|
+
|
|
22
|
+
return obj;
|
|
23
|
+
}, {});
|
|
24
|
+
};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
@module browser/Progress
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Expose `Progress`.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
module.exports = Progress;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Initialize a new `Progress` indicator.
|
|
15
|
+
*/
|
|
16
|
+
function Progress() {
|
|
17
|
+
this.percent = 0;
|
|
18
|
+
this.size(0);
|
|
19
|
+
this.fontSize(11);
|
|
20
|
+
this.font('helvetica, arial, sans-serif');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Set progress size to `size`.
|
|
25
|
+
*
|
|
26
|
+
* @public
|
|
27
|
+
* @param {number} size
|
|
28
|
+
* @return {Progress} Progress instance.
|
|
29
|
+
*/
|
|
30
|
+
Progress.prototype.size = function(size) {
|
|
31
|
+
this._size = size;
|
|
32
|
+
return this;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Set text to `text`.
|
|
37
|
+
*
|
|
38
|
+
* @public
|
|
39
|
+
* @param {string} text
|
|
40
|
+
* @return {Progress} Progress instance.
|
|
41
|
+
*/
|
|
42
|
+
Progress.prototype.text = function(text) {
|
|
43
|
+
this._text = text;
|
|
44
|
+
return this;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Set font size to `size`.
|
|
49
|
+
*
|
|
50
|
+
* @public
|
|
51
|
+
* @param {number} size
|
|
52
|
+
* @return {Progress} Progress instance.
|
|
53
|
+
*/
|
|
54
|
+
Progress.prototype.fontSize = function(size) {
|
|
55
|
+
this._fontSize = size;
|
|
56
|
+
return this;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Set font to `family`.
|
|
61
|
+
*
|
|
62
|
+
* @param {string} family
|
|
63
|
+
* @return {Progress} Progress instance.
|
|
64
|
+
*/
|
|
65
|
+
Progress.prototype.font = function(family) {
|
|
66
|
+
this._font = family;
|
|
67
|
+
return this;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Update percentage to `n`.
|
|
72
|
+
*
|
|
73
|
+
* @param {number} n
|
|
74
|
+
* @return {Progress} Progress instance.
|
|
75
|
+
*/
|
|
76
|
+
Progress.prototype.update = function(n) {
|
|
77
|
+
this.percent = n;
|
|
78
|
+
return this;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Draw on `ctx`.
|
|
83
|
+
*
|
|
84
|
+
* @param {CanvasRenderingContext2d} ctx
|
|
85
|
+
* @return {Progress} Progress instance.
|
|
86
|
+
*/
|
|
87
|
+
Progress.prototype.draw = function(ctx) {
|
|
88
|
+
try {
|
|
89
|
+
var percent = Math.min(this.percent, 100);
|
|
90
|
+
var size = this._size;
|
|
91
|
+
var half = size / 2;
|
|
92
|
+
var x = half;
|
|
93
|
+
var y = half;
|
|
94
|
+
var rad = half - 1;
|
|
95
|
+
var fontSize = this._fontSize;
|
|
96
|
+
|
|
97
|
+
ctx.font = fontSize + 'px ' + this._font;
|
|
98
|
+
|
|
99
|
+
var angle = Math.PI * 2 * (percent / 100);
|
|
100
|
+
ctx.clearRect(0, 0, size, size);
|
|
101
|
+
|
|
102
|
+
// outer circle
|
|
103
|
+
ctx.strokeStyle = '#9f9f9f';
|
|
104
|
+
ctx.beginPath();
|
|
105
|
+
ctx.arc(x, y, rad, 0, angle, false);
|
|
106
|
+
ctx.stroke();
|
|
107
|
+
|
|
108
|
+
// inner circle
|
|
109
|
+
ctx.strokeStyle = '#eee';
|
|
110
|
+
ctx.beginPath();
|
|
111
|
+
ctx.arc(x, y, rad - 1, 0, angle, true);
|
|
112
|
+
ctx.stroke();
|
|
113
|
+
|
|
114
|
+
// text
|
|
115
|
+
var text = this._text || (percent | 0) + '%';
|
|
116
|
+
var w = ctx.measureText(text).width;
|
|
117
|
+
|
|
118
|
+
ctx.fillText(text, x - w / 2 + 1, y + fontSize / 2 - 1);
|
|
119
|
+
} catch (ignore) {
|
|
120
|
+
// don't fail if we can't render progress
|
|
121
|
+
}
|
|
122
|
+
return this;
|
|
123
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<title>Mocha</title>
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<link rel="stylesheet" href="mocha.css" />
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="mocha"></div>
|
|
11
|
+
<script src="mocha.js"></script>
|
|
12
|
+
<script>
|
|
13
|
+
mocha.setup('bdd');
|
|
14
|
+
</script>
|
|
15
|
+
<script src="tests.spec.js"></script>
|
|
16
|
+
<script>
|
|
17
|
+
mocha.run();
|
|
18
|
+
</script>
|
|
19
|
+
</body>
|
|
20
|
+
</html>
|
package/lib/cli/cli.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Contains CLI entry point and public API for programmatic usage in Node.js.
|
|
7
|
+
* - Option parsing is handled by {@link https://npm.im/yargs yargs}.
|
|
8
|
+
* - If executed via `node`, this module will run {@linkcode module:lib/cli.main main()}.
|
|
9
|
+
* @public
|
|
10
|
+
* @module lib/cli
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const debug = require('debug')('mocha:cli:cli');
|
|
14
|
+
const symbols = require('log-symbols');
|
|
15
|
+
const yargs = require('yargs/yargs');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
const {
|
|
18
|
+
loadRc,
|
|
19
|
+
loadPkgRc,
|
|
20
|
+
loadOptions,
|
|
21
|
+
YARGS_PARSER_CONFIG
|
|
22
|
+
} = require('./options');
|
|
23
|
+
const lookupFiles = require('./lookup-files');
|
|
24
|
+
const commands = require('./commands');
|
|
25
|
+
const ansi = require('ansi-colors');
|
|
26
|
+
const {repository, homepage, version, gitter} = require('../../package.json');
|
|
27
|
+
const {cwd} = require('../utils');
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* - Accepts an `Array` of arguments
|
|
31
|
+
* - Modifies {@link https://nodejs.org/api/modules.html#modules_module_paths Node.js' search path} for easy loading of consumer modules
|
|
32
|
+
* - Sets {@linkcode https://nodejs.org/api/errors.html#errors_error_stacktracelimit Error.stackTraceLimit} to `Infinity`
|
|
33
|
+
* @public
|
|
34
|
+
* @summary Mocha's main command-line entry-point.
|
|
35
|
+
* @param {string[]} argv - Array of arguments to parse, or by default the lovely `process.argv.slice(2)`
|
|
36
|
+
* @param {object} [mochaArgs] - Object of already parsed Mocha arguments (by bin/mocha)
|
|
37
|
+
*/
|
|
38
|
+
exports.main = (argv = process.argv.slice(2), mochaArgs) => {
|
|
39
|
+
debug('entered main with raw args', argv);
|
|
40
|
+
// ensure we can require() from current working directory
|
|
41
|
+
if (typeof module.paths !== 'undefined') {
|
|
42
|
+
module.paths.push(cwd(), path.resolve('node_modules'));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
Error.stackTraceLimit = Infinity; // configurable via --stack-trace-limit?
|
|
46
|
+
|
|
47
|
+
var args = mochaArgs || loadOptions(argv);
|
|
48
|
+
|
|
49
|
+
yargs()
|
|
50
|
+
.scriptName('mocha')
|
|
51
|
+
.command(commands.run)
|
|
52
|
+
.command(commands.init)
|
|
53
|
+
.updateStrings({
|
|
54
|
+
'Positionals:': 'Positional Arguments',
|
|
55
|
+
'Options:': 'Other Options',
|
|
56
|
+
'Commands:': 'Commands'
|
|
57
|
+
})
|
|
58
|
+
.fail((msg, err, yargs) => {
|
|
59
|
+
debug('caught error sometime before command handler: %O', err);
|
|
60
|
+
yargs.showHelp();
|
|
61
|
+
console.error(`\n${symbols.error} ${ansi.red('ERROR:')} ${msg}`);
|
|
62
|
+
process.exitCode = 1;
|
|
63
|
+
})
|
|
64
|
+
.help('help', 'Show usage information & exit')
|
|
65
|
+
.alias('help', 'h')
|
|
66
|
+
.version('version', 'Show version number & exit', version)
|
|
67
|
+
.alias('version', 'V')
|
|
68
|
+
.wrap(process.stdout.columns ? Math.min(process.stdout.columns, 80) : 80)
|
|
69
|
+
.epilog(
|
|
70
|
+
`Mocha Resources
|
|
71
|
+
Chat: ${ansi.magenta(gitter)}
|
|
72
|
+
GitHub: ${ansi.blue(repository.url)}
|
|
73
|
+
Docs: ${ansi.yellow(homepage)}
|
|
74
|
+
`
|
|
75
|
+
)
|
|
76
|
+
.parserConfiguration(YARGS_PARSER_CONFIG)
|
|
77
|
+
.config(args)
|
|
78
|
+
.parse(args._);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
exports.lookupFiles = lookupFiles;
|
|
82
|
+
exports.loadOptions = loadOptions;
|
|
83
|
+
exports.loadPkgRc = loadPkgRc;
|
|
84
|
+
exports.loadRc = loadRc;
|
|
85
|
+
|
|
86
|
+
// allow direct execution
|
|
87
|
+
if (require.main === module) {
|
|
88
|
+
exports.main();
|
|
89
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const ansi = require('ansi-colors');
|
|
5
|
+
const debug = require('debug')('mocha:cli:run:helpers');
|
|
6
|
+
const minimatch = require('minimatch');
|
|
7
|
+
const {NO_FILES_MATCH_PATTERN} = require('../errors').constants;
|
|
8
|
+
const lookupFiles = require('./lookup-files');
|
|
9
|
+
const {castArray} = require('../utils');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Exports a function that collects test files from CLI parameters.
|
|
13
|
+
* @see module:lib/cli/run-helpers
|
|
14
|
+
* @see module:lib/cli/watch-run
|
|
15
|
+
* @module
|
|
16
|
+
* @private
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Smash together an array of test files in the correct order
|
|
21
|
+
* @param {FileCollectionOptions} [opts] - Options
|
|
22
|
+
* @returns {string[]} List of files to test
|
|
23
|
+
* @private
|
|
24
|
+
*/
|
|
25
|
+
module.exports = ({
|
|
26
|
+
ignore,
|
|
27
|
+
extension,
|
|
28
|
+
file: fileArgs,
|
|
29
|
+
recursive,
|
|
30
|
+
sort,
|
|
31
|
+
spec
|
|
32
|
+
} = {}) => {
|
|
33
|
+
const unmatched = [];
|
|
34
|
+
const specFiles = spec.reduce((specFiles, arg) => {
|
|
35
|
+
try {
|
|
36
|
+
const moreSpecFiles = castArray(lookupFiles(arg, extension, recursive))
|
|
37
|
+
.filter(filename =>
|
|
38
|
+
ignore.every(pattern => !minimatch(filename, pattern))
|
|
39
|
+
)
|
|
40
|
+
.map(filename => path.resolve(filename));
|
|
41
|
+
return [...specFiles, ...moreSpecFiles];
|
|
42
|
+
} catch (err) {
|
|
43
|
+
if (err.code === NO_FILES_MATCH_PATTERN) {
|
|
44
|
+
unmatched.push({message: err.message, pattern: err.pattern});
|
|
45
|
+
return specFiles;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
throw err;
|
|
49
|
+
}
|
|
50
|
+
}, []);
|
|
51
|
+
|
|
52
|
+
// ensure we don't sort the stuff from fileArgs; order is important!
|
|
53
|
+
if (sort) {
|
|
54
|
+
specFiles.sort();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// add files given through --file to be ran first
|
|
58
|
+
const files = [
|
|
59
|
+
...fileArgs.map(filepath => path.resolve(filepath)),
|
|
60
|
+
...specFiles
|
|
61
|
+
];
|
|
62
|
+
debug('test files (in order): ', files);
|
|
63
|
+
|
|
64
|
+
if (!files.length) {
|
|
65
|
+
// give full message details when only 1 file is missing
|
|
66
|
+
const noneFoundMsg =
|
|
67
|
+
unmatched.length === 1
|
|
68
|
+
? `Error: No test files found: ${JSON.stringify(unmatched[0].pattern)}` // stringify to print escaped characters raw
|
|
69
|
+
: 'Error: No test files found';
|
|
70
|
+
console.error(ansi.red(noneFoundMsg));
|
|
71
|
+
process.exit(1);
|
|
72
|
+
} else {
|
|
73
|
+
// print messages as a warning
|
|
74
|
+
unmatched.forEach(warning => {
|
|
75
|
+
console.warn(ansi.yellow(`Warning: ${warning.message}`));
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return files;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* An object to configure how Mocha gathers test files
|
|
84
|
+
* @private
|
|
85
|
+
* @typedef {Object} FileCollectionOptions
|
|
86
|
+
* @property {string[]} extension - File extensions to use
|
|
87
|
+
* @property {string[]} spec - Files, dirs, globs to run
|
|
88
|
+
* @property {string[]} ignore - Files, dirs, globs to ignore
|
|
89
|
+
* @property {string[]} file - List of additional files to include
|
|
90
|
+
* @property {boolean} recursive - Find files recursively
|
|
91
|
+
* @property {boolean} sort - Sort test files
|
|
92
|
+
*/
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Responsible for loading / finding Mocha's "rc" files.
|
|
5
|
+
*
|
|
6
|
+
* @private
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const debug = require('debug')('mocha:cli:config');
|
|
13
|
+
const findUp = require('find-up');
|
|
14
|
+
const {createUnparsableFileError} = require('../errors');
|
|
15
|
+
const utils = require('../utils');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* These are the valid config files, in order of precedence;
|
|
19
|
+
* e.g., if `.mocharc.js` is present, then `.mocharc.yaml` and the rest
|
|
20
|
+
* will be ignored.
|
|
21
|
+
* The user should still be able to explicitly specify a file.
|
|
22
|
+
* @private
|
|
23
|
+
*/
|
|
24
|
+
exports.CONFIG_FILES = [
|
|
25
|
+
'.mocharc.cjs',
|
|
26
|
+
'.mocharc.js',
|
|
27
|
+
'.mocharc.yaml',
|
|
28
|
+
'.mocharc.yml',
|
|
29
|
+
'.mocharc.jsonc',
|
|
30
|
+
'.mocharc.json'
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
const isModuleNotFoundError = err =>
|
|
34
|
+
err.code === 'MODULE_NOT_FOUND' ||
|
|
35
|
+
err.message.indexOf('Cannot find module') !== -1;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Parsers for various config filetypes. Each accepts a filepath and
|
|
39
|
+
* returns an object (but could throw)
|
|
40
|
+
*/
|
|
41
|
+
const parsers = (exports.parsers = {
|
|
42
|
+
yaml: filepath => require('js-yaml').load(fs.readFileSync(filepath, 'utf8')),
|
|
43
|
+
js: filepath => {
|
|
44
|
+
const cwdFilepath = path.resolve(filepath);
|
|
45
|
+
try {
|
|
46
|
+
debug('parsers: load using cwd-relative path: "%s"', cwdFilepath);
|
|
47
|
+
return require(cwdFilepath);
|
|
48
|
+
} catch (err) {
|
|
49
|
+
if (isModuleNotFoundError(err)) {
|
|
50
|
+
debug('parsers: retry load as module-relative path: "%s"', filepath);
|
|
51
|
+
return require(filepath);
|
|
52
|
+
} else {
|
|
53
|
+
throw err; // rethrow
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
json: filepath =>
|
|
58
|
+
JSON.parse(
|
|
59
|
+
require('strip-json-comments')(fs.readFileSync(filepath, 'utf8'))
|
|
60
|
+
)
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Loads and parses, based on file extension, a config file.
|
|
65
|
+
* "JSON" files may have comments.
|
|
66
|
+
*
|
|
67
|
+
* @private
|
|
68
|
+
* @param {string} filepath - Config file path to load
|
|
69
|
+
* @returns {Object} Parsed config object
|
|
70
|
+
*/
|
|
71
|
+
exports.loadConfig = filepath => {
|
|
72
|
+
let config = {};
|
|
73
|
+
debug('loadConfig: trying to parse config at %s', filepath);
|
|
74
|
+
|
|
75
|
+
const ext = path.extname(filepath);
|
|
76
|
+
try {
|
|
77
|
+
if (ext === '.yml' || ext === '.yaml') {
|
|
78
|
+
config = parsers.yaml(filepath);
|
|
79
|
+
} else if (ext === '.js' || ext === '.cjs') {
|
|
80
|
+
config = parsers.js(filepath);
|
|
81
|
+
} else {
|
|
82
|
+
config = parsers.json(filepath);
|
|
83
|
+
}
|
|
84
|
+
} catch (err) {
|
|
85
|
+
throw createUnparsableFileError(
|
|
86
|
+
`Unable to read/parse ${filepath}: ${err}`,
|
|
87
|
+
filepath
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
return config;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Find ("find up") config file starting at `cwd`
|
|
95
|
+
*
|
|
96
|
+
* @param {string} [cwd] - Current working directory
|
|
97
|
+
* @returns {string|null} Filepath to config, if found
|
|
98
|
+
*/
|
|
99
|
+
exports.findConfig = (cwd = utils.cwd()) => {
|
|
100
|
+
const filepath = findUp.sync(exports.CONFIG_FILES, {cwd});
|
|
101
|
+
if (filepath) {
|
|
102
|
+
debug('findConfig: found config file %s', filepath);
|
|
103
|
+
}
|
|
104
|
+
return filepath;
|
|
105
|
+
};
|
package/lib/cli/index.js
ADDED
package/lib/cli/init.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Command module for "init" command
|
|
5
|
+
*
|
|
6
|
+
* @private
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
|
|
13
|
+
exports.command = 'init <path>';
|
|
14
|
+
|
|
15
|
+
exports.description = 'create a client-side Mocha setup at <path>';
|
|
16
|
+
|
|
17
|
+
exports.builder = yargs =>
|
|
18
|
+
yargs.positional('path', {
|
|
19
|
+
type: 'string',
|
|
20
|
+
normalize: true
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
exports.handler = argv => {
|
|
24
|
+
const destdir = argv.path;
|
|
25
|
+
const srcdir = path.join(__dirname, '..', '..');
|
|
26
|
+
fs.mkdirSync(destdir, {recursive: true});
|
|
27
|
+
const css = fs.readFileSync(path.join(srcdir, 'mocha.css'));
|
|
28
|
+
const js = fs.readFileSync(path.join(srcdir, 'mocha.js'));
|
|
29
|
+
const tmpl = fs.readFileSync(
|
|
30
|
+
path.join(srcdir, 'lib', 'browser', 'template.html')
|
|
31
|
+
);
|
|
32
|
+
fs.writeFileSync(path.join(destdir, 'mocha.css'), css);
|
|
33
|
+
fs.writeFileSync(path.join(destdir, 'mocha.js'), js);
|
|
34
|
+
fs.writeFileSync(path.join(destdir, 'tests.spec.js'), '');
|
|
35
|
+
fs.writeFileSync(path.join(destdir, 'index.html'), tmpl);
|
|
36
|
+
};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* Contains `lookupFiles`, which takes some globs/dirs/options and returns a list of files.
|
|
4
|
+
* @module
|
|
5
|
+
* @private
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
var fs = require('fs');
|
|
9
|
+
var path = require('path');
|
|
10
|
+
var glob = require('glob');
|
|
11
|
+
var errors = require('../errors');
|
|
12
|
+
var createNoFilesMatchPatternError = errors.createNoFilesMatchPatternError;
|
|
13
|
+
var createMissingArgumentError = errors.createMissingArgumentError;
|
|
14
|
+
const debug = require('debug')('mocha:cli:lookup-files');
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Determines if pathname would be a "hidden" file (or directory) on UN*X.
|
|
18
|
+
*
|
|
19
|
+
* @description
|
|
20
|
+
* On UN*X, pathnames beginning with a full stop (aka dot) are hidden during
|
|
21
|
+
* typical usage. Dotfiles, plain-text configuration files, are prime examples.
|
|
22
|
+
*
|
|
23
|
+
* @see {@link http://xahlee.info/UnixResource_dir/writ/unix_origin_of_dot_filename.html|Origin of Dot File Names}
|
|
24
|
+
*
|
|
25
|
+
* @private
|
|
26
|
+
* @param {string} pathname - Pathname to check for match.
|
|
27
|
+
* @return {boolean} whether pathname would be considered a hidden file.
|
|
28
|
+
* @example
|
|
29
|
+
* isHiddenOnUnix('.profile'); // => true
|
|
30
|
+
*/
|
|
31
|
+
const isHiddenOnUnix = pathname => path.basename(pathname).startsWith('.');
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Determines if pathname has a matching file extension.
|
|
35
|
+
*
|
|
36
|
+
* Supports multi-part extensions.
|
|
37
|
+
*
|
|
38
|
+
* @private
|
|
39
|
+
* @param {string} pathname - Pathname to check for match.
|
|
40
|
+
* @param {string[]} exts - List of file extensions, w/-or-w/o leading period
|
|
41
|
+
* @return {boolean} `true` if file extension matches.
|
|
42
|
+
* @example
|
|
43
|
+
* hasMatchingExtname('foo.html', ['js', 'css']); // false
|
|
44
|
+
* hasMatchingExtname('foo.js', ['.js']); // true
|
|
45
|
+
* hasMatchingExtname('foo.js', ['js']); // ture
|
|
46
|
+
*/
|
|
47
|
+
const hasMatchingExtname = (pathname, exts = []) =>
|
|
48
|
+
exts
|
|
49
|
+
.map(ext => (ext.startsWith('.') ? ext : `.${ext}`))
|
|
50
|
+
.some(ext => pathname.endsWith(ext));
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Lookup file names at the given `path`.
|
|
54
|
+
*
|
|
55
|
+
* @description
|
|
56
|
+
* Filenames are returned in _traversal_ order by the OS/filesystem.
|
|
57
|
+
* **Make no assumption that the names will be sorted in any fashion.**
|
|
58
|
+
*
|
|
59
|
+
* @public
|
|
60
|
+
* @alias module:lib/cli.lookupFiles
|
|
61
|
+
* @param {string} filepath - Base path to start searching from.
|
|
62
|
+
* @param {string[]} [extensions=[]] - File extensions to look for.
|
|
63
|
+
* @param {boolean} [recursive=false] - Whether to recurse into subdirectories.
|
|
64
|
+
* @return {string[]} An array of paths.
|
|
65
|
+
* @throws {Error} if no files match pattern.
|
|
66
|
+
* @throws {TypeError} if `filepath` is directory and `extensions` not provided.
|
|
67
|
+
*/
|
|
68
|
+
module.exports = function lookupFiles(
|
|
69
|
+
filepath,
|
|
70
|
+
extensions = [],
|
|
71
|
+
recursive = false
|
|
72
|
+
) {
|
|
73
|
+
const files = [];
|
|
74
|
+
let stat;
|
|
75
|
+
|
|
76
|
+
if (!fs.existsSync(filepath)) {
|
|
77
|
+
let pattern;
|
|
78
|
+
if (glob.hasMagic(filepath)) {
|
|
79
|
+
// Handle glob as is without extensions
|
|
80
|
+
pattern = filepath;
|
|
81
|
+
} else {
|
|
82
|
+
// glob pattern e.g. 'filepath+(.js|.ts)'
|
|
83
|
+
const strExtensions = extensions
|
|
84
|
+
.map(ext => (ext.startsWith('.') ? ext : `.${ext}`))
|
|
85
|
+
.join('|');
|
|
86
|
+
pattern = `${filepath}+(${strExtensions})`;
|
|
87
|
+
debug('looking for files using glob pattern: %s', pattern);
|
|
88
|
+
}
|
|
89
|
+
files.push(...glob.sync(pattern, {nodir: true}));
|
|
90
|
+
if (!files.length) {
|
|
91
|
+
throw createNoFilesMatchPatternError(
|
|
92
|
+
`Cannot find any files matching pattern "${filepath}"`,
|
|
93
|
+
filepath
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
return files;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Handle file
|
|
100
|
+
try {
|
|
101
|
+
stat = fs.statSync(filepath);
|
|
102
|
+
if (stat.isFile()) {
|
|
103
|
+
return filepath;
|
|
104
|
+
}
|
|
105
|
+
} catch (err) {
|
|
106
|
+
// ignore error
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Handle directory
|
|
111
|
+
fs.readdirSync(filepath).forEach(dirent => {
|
|
112
|
+
const pathname = path.join(filepath, dirent);
|
|
113
|
+
let stat;
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
stat = fs.statSync(pathname);
|
|
117
|
+
if (stat.isDirectory()) {
|
|
118
|
+
if (recursive) {
|
|
119
|
+
files.push(...lookupFiles(pathname, extensions, recursive));
|
|
120
|
+
}
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
} catch (ignored) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (!extensions.length) {
|
|
127
|
+
throw createMissingArgumentError(
|
|
128
|
+
`Argument '${extensions}' required when argument '${filepath}' is a directory`,
|
|
129
|
+
'extensions',
|
|
130
|
+
'array'
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (
|
|
135
|
+
!stat.isFile() ||
|
|
136
|
+
!hasMatchingExtname(pathname, extensions) ||
|
|
137
|
+
isHiddenOnUnix(pathname)
|
|
138
|
+
) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
files.push(pathname);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
return files;
|
|
145
|
+
};
|