UNPKG

8.65 kBJavaScriptView Raw
1'use strict';
2
3/**
4 * Definition for Mocha's default ("run tests") command
5 *
6 * @module
7 * @private
8 */
9
10const Mocha = require('../mocha');
11const {
12 createUnsupportedError,
13 createInvalidArgumentValueError,
14 createMissingArgumentError
15} = require('../errors');
16
17const {
18 list,
19 handleRequires,
20 validatePlugin,
21 runMocha
22} = require('./run-helpers');
23const {ONE_AND_DONES, ONE_AND_DONE_ARGS} = require('./one-and-dones');
24const debug = require('debug')('mocha:cli:run');
25const defaults = require('../mocharc');
26const {types, aliases} = require('./run-option-metadata');
27
28/**
29 * Logical option groups
30 * @constant
31 */
32const GROUPS = {
33 FILES: 'File Handling',
34 FILTERS: 'Test Filters',
35 NODEJS: 'Node.js & V8',
36 OUTPUT: 'Reporting & Output',
37 RULES: 'Rules & Behavior',
38 CONFIG: 'Configuration'
39};
40
41exports.command = ['$0 [spec..]', 'inspect'];
42
43exports.describe = 'Run tests with Mocha';
44
45exports.builder = yargs =>
46 yargs
47 .options({
48 'allow-uncaught': {
49 description: 'Allow uncaught errors to propagate',
50 group: GROUPS.RULES
51 },
52 'async-only': {
53 description:
54 'Require all tests to use a callback (async) or return a Promise',
55 group: GROUPS.RULES
56 },
57 bail: {
58 description: 'Abort ("bail") after first test failure',
59 group: GROUPS.RULES
60 },
61 'check-leaks': {
62 description: 'Check for global variable leaks',
63 group: GROUPS.RULES
64 },
65 color: {
66 description: 'Force-enable color output',
67 group: GROUPS.OUTPUT
68 },
69 config: {
70 config: true,
71 defaultDescription: '(nearest rc file)',
72 description: 'Path to config file',
73 group: GROUPS.CONFIG
74 },
75 delay: {
76 description: 'Delay initial execution of root suite',
77 group: GROUPS.RULES
78 },
79 diff: {
80 default: true,
81 description: 'Show diff on failure',
82 group: GROUPS.OUTPUT
83 },
84 exit: {
85 description: 'Force Mocha to quit after tests complete',
86 group: GROUPS.RULES
87 },
88 extension: {
89 default: defaults.extension,
90 defaultDescription: 'js',
91 description: 'File extension(s) to load',
92 group: GROUPS.FILES,
93 requiresArg: true,
94 coerce: list
95 },
96 fgrep: {
97 conflicts: 'grep',
98 description: 'Only run tests containing this string',
99 group: GROUPS.FILTERS,
100 requiresArg: true
101 },
102 file: {
103 defaultDescription: '(none)',
104 description:
105 'Specify file(s) to be loaded prior to root suite execution',
106 group: GROUPS.FILES,
107 normalize: true,
108 requiresArg: true
109 },
110 'forbid-only': {
111 description: 'Fail if exclusive test(s) encountered',
112 group: GROUPS.RULES
113 },
114 'forbid-pending': {
115 description: 'Fail if pending test(s) encountered',
116 group: GROUPS.RULES
117 },
118 'full-trace': {
119 description: 'Display full stack traces',
120 group: GROUPS.OUTPUT
121 },
122 global: {
123 coerce: list,
124 description: 'List of allowed global variables',
125 group: GROUPS.RULES,
126 requiresArg: true
127 },
128 grep: {
129 coerce: value => (!value ? null : value),
130 conflicts: 'fgrep',
131 description: 'Only run tests matching this string or regexp',
132 group: GROUPS.FILTERS,
133 requiresArg: true
134 },
135 growl: {
136 description: 'Enable Growl notifications',
137 group: GROUPS.OUTPUT
138 },
139 ignore: {
140 defaultDescription: '(none)',
141 description: 'Ignore file(s) or glob pattern(s)',
142 group: GROUPS.FILES,
143 requiresArg: true
144 },
145 'inline-diffs': {
146 description:
147 'Display actual/expected differences inline within each string',
148 group: GROUPS.OUTPUT
149 },
150 invert: {
151 description: 'Inverts --grep and --fgrep matches',
152 group: GROUPS.FILTERS
153 },
154 'list-interfaces': {
155 conflicts: Array.from(ONE_AND_DONE_ARGS),
156 description: 'List built-in user interfaces & exit'
157 },
158 'list-reporters': {
159 conflicts: Array.from(ONE_AND_DONE_ARGS),
160 description: 'List built-in reporters & exit'
161 },
162 'no-colors': {
163 description: 'Force-disable color output',
164 group: GROUPS.OUTPUT,
165 hidden: true
166 },
167 opts: {
168 default: defaults.opts,
169 description: 'Path to `mocha.opts` (DEPRECATED)',
170 group: GROUPS.CONFIG,
171 normalize: true,
172 requiresArg: true
173 },
174 package: {
175 description: 'Path to package.json for config',
176 group: GROUPS.CONFIG,
177 normalize: true,
178 requiresArg: true
179 },
180 recursive: {
181 description: 'Look for tests in subdirectories',
182 group: GROUPS.FILES
183 },
184 reporter: {
185 default: defaults.reporter,
186 description: 'Specify reporter to use',
187 group: GROUPS.OUTPUT,
188 requiresArg: true
189 },
190 'reporter-option': {
191 coerce: opts =>
192 list(opts).reduce((acc, opt) => {
193 const pair = opt.split('=');
194
195 if (pair.length > 2 || !pair.length) {
196 throw createInvalidArgumentValueError(
197 `invalid reporter option '${opt}'`,
198 '--reporter-option',
199 opt,
200 'expected "key=value" format'
201 );
202 }
203
204 acc[pair[0]] = pair.length === 2 ? pair[1] : true;
205 return acc;
206 }, {}),
207 description: 'Reporter-specific options (<k=v,[k1=v1,..]>)',
208 group: GROUPS.OUTPUT,
209 requiresArg: true
210 },
211 require: {
212 defaultDescription: '(none)',
213 description: 'Require module',
214 group: GROUPS.FILES,
215 requiresArg: true
216 },
217 retries: {
218 description: 'Retry failed tests this many times',
219 group: GROUPS.RULES
220 },
221 slow: {
222 default: defaults.slow,
223 description: 'Specify "slow" test threshold (in milliseconds)',
224 group: GROUPS.RULES
225 },
226 sort: {
227 description: 'Sort test files',
228 group: GROUPS.FILES
229 },
230 timeout: {
231 default: defaults.timeout,
232 description: 'Specify test timeout threshold (in milliseconds)',
233 group: GROUPS.RULES
234 },
235 ui: {
236 default: defaults.ui,
237 description: 'Specify user interface',
238 group: GROUPS.RULES,
239 requiresArg: true
240 },
241 watch: {
242 description: 'Watch files in the current working directory for changes',
243 group: GROUPS.FILES
244 },
245 'watch-files': {
246 description: 'List of paths or globs to watch',
247 group: GROUPS.FILES,
248 requiresArg: true,
249 coerce: list
250 },
251 'watch-ignore': {
252 description: 'List of paths or globs to exclude from watching',
253 group: GROUPS.FILES,
254 requiresArg: true,
255 coerce: list,
256 default: defaults['watch-ignore']
257 }
258 })
259 .positional('spec', {
260 default: ['test'],
261 description: 'One or more files, directories, or globs to test',
262 type: 'array'
263 })
264 .check(argv => {
265 // "one-and-dones"; let yargs handle help and version
266 Object.keys(ONE_AND_DONES).forEach(opt => {
267 if (argv[opt]) {
268 ONE_AND_DONES[opt].call(null, yargs);
269 process.exit();
270 }
271 });
272
273 // yargs.implies() isn't flexible enough to handle this
274 if (argv.invert && !('fgrep' in argv || 'grep' in argv)) {
275 throw createMissingArgumentError(
276 '"--invert" requires one of "--fgrep <str>" or "--grep <regexp>"',
277 '--fgrep|--grep',
278 'string|regexp'
279 );
280 }
281
282 if (argv.compilers) {
283 throw createUnsupportedError(
284 `--compilers is DEPRECATED and no longer supported.
285 See https://git.io/vdcSr for migration information.`
286 );
287 }
288
289 // load requires first, because it can impact "plugin" validation
290 handleRequires(argv.require);
291 validatePlugin(argv, 'reporter', Mocha.reporters);
292 validatePlugin(argv, 'ui', Mocha.interfaces);
293
294 return true;
295 })
296 .array(types.array)
297 .boolean(types.boolean)
298 .string(types.string)
299 .number(types.number)
300 .alias(aliases);
301
302exports.handler = async function(argv) {
303 debug('post-yargs config', argv);
304 const mocha = new Mocha(argv);
305
306 try {
307 await runMocha(mocha, argv);
308 } catch (err) {
309 console.error(err.stack || `Error: ${err.message || err}`);
310 process.exit(1);
311 }
312};