Skip to content

Commit c7905ff

Browse files
committed
Find flag for faulty numeric args before yargs parser
Signed-off-by: Dinika Saxena <[email protected]>
1 parent 45519d2 commit c7905ff

File tree

6 files changed

+123
-25
lines changed

6 files changed

+123
-25
lines changed

Diff for: lib/cli/options.js

+20-4
Original file line numberDiff line numberDiff line change
@@ -114,15 +114,31 @@ const nargOpts = types.array
114114
const parse = (args = [], defaultValues = {}, ...configObjects) => {
115115
// save node-specific args for special handling.
116116
// 1. when these args have a "=" they should be considered to have values
117-
// 2. if they don't, they just boolean flags
117+
// 2. if they don't, they are just boolean flags
118118
// 3. to avoid explicitly defining the set of them, we tell yargs-parser they
119119
// are ALL boolean flags.
120120
// 4. we can then reapply the values after yargs-parser is done.
121121
const allArgs = Array.isArray(args) ? args : args.split(' ');
122-
const nodeArgs = allArgs.reduce((acc, arg) => {
123-
if (typeof arg !== 'string') {
124-
throw new Error(`Invalid option ${arg} passed to mocha cli`);
122+
const nodeArgs = allArgs.reduce((acc, arg, index, allArgs) => {
123+
const maybeFlag = allArgs[index - 1];
124+
125+
if (isNumeric(arg) && (!maybeFlag || !isMochaFlag(maybeFlag))) {
126+
throw createUnsupportedError(
127+
`Option ${arg} is unsupported by the mocha cli`
128+
);
125129
}
130+
if (
131+
isNumeric(arg) &&
132+
isMochaFlag(maybeFlag) &&
133+
expectedTypeForFlag(maybeFlag) !== 'number'
134+
) {
135+
throw createInvalidArgumentTypeError(
136+
`Mocha flag '--${maybeFlag}' given invalid option: '${arg}'`,
137+
Number(arg),
138+
expectedTypeForFlag(maybeFlag)
139+
);
140+
}
141+
126142
const pair = arg.split('=');
127143
let flag = pair[0];
128144
if (isNodeFlag(flag, false)) {

Diff for: lib/cli/run-option-metadata.js

+2-11
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,8 @@ const TYPES = (exports.types = {
5050
'sort',
5151
'watch'
5252
],
53-
number: ['retries', 'jobs'],
54-
string: [
55-
'config',
56-
'fgrep',
57-
'grep',
58-
'package',
59-
'reporter',
60-
'ui',
61-
'slow',
62-
'timeout'
63-
]
53+
number: ['retries', 'jobs', 'slow', 'timeout'],
54+
string: ['config', 'fgrep', 'grep', 'package', 'reporter', 'ui']
6455
});
6556

6657
/**

Diff for: package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
"test-coverage-clean": "rimraf .nyc_output coverage",
7171
"test-coverage-generate": "nyc report --reporter=lcov --reporter=text",
7272
"test-node-run-only": "nyc --no-clean --reporter=json node bin/mocha.js",
73-
"test-node-run": "nyc --no-clean --reporter=json node bin/mocha.js --forbid-only",
73+
"test-node-run": "nyc --no-clean --reporter=json node bin/mocha.js",
7474
"test-node:integration": "run-s clean build && npm run -s test-node-run -- --parallel --timeout 10000 --slow 3750 \"test/integration/**/*.spec.js\"",
7575
"test-node:interfaces:bdd": "npm run -s test-node-run -- --ui bdd test/interfaces/bdd.spec",
7676
"test-node:interfaces:exports": "npm run -s test-node-run -- --ui exports test/interfaces/exports.spec",

Diff for: test/node-unit/cli/mocha-flags.spec.js

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
'use strict';
2+
3+
const {
4+
types,
5+
expectedTypeForFlag
6+
} = require('../../../lib/cli/run-option-metadata');
7+
8+
describe('mocha-flags', function () {
9+
describe('expectedTypeForFlag()', function () {
10+
it('returns expected type for all mocha flags', function () {
11+
Object.entries(types).forEach(([dataType, flags]) => {
12+
flags.forEach(flag => {
13+
expect(expectedTypeForFlag(flag), 'to equal', dataType);
14+
});
15+
});
16+
});
17+
18+
it('returns undefined for node flags', function () {
19+
expect(expectedTypeForFlag('--throw-deprecation'), 'to equal', undefined);
20+
expect(expectedTypeForFlag('throw-deprecation'), 'to equal', undefined);
21+
});
22+
23+
it('returns undefined for unsupported flags', function () {
24+
expect(expectedTypeForFlag('--foo'), 'to equal', undefined);
25+
});
26+
});
27+
});

Diff for: test/node-unit/cli/options.spec.js

+62-9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
const sinon = require('sinon');
44
const rewiremock = require('rewiremock/node');
55
const {ONE_AND_DONE_ARGS} = require('../../../lib/cli/one-and-dones');
6+
const {constants} = require('../../../lib/errors');
67

78
const modulePath = require.resolve('../../../lib/cli/options');
89
const mocharcPath = require.resolve('../../../lib/mocharc.json');
@@ -524,7 +525,7 @@ describe('options', function () {
524525
loadOptions('--timeout 500'),
525526
'to have property',
526527
'timeout',
527-
'500'
528+
500
528529
);
529530
});
530531

@@ -678,6 +679,23 @@ describe('options', function () {
678679
describe('"numeric arguments"', function () {
679680
const numericArg = 123;
680681

682+
const unsupportedError = arg => {
683+
return {
684+
message: `Option ${arg} is unsupported by the mocha cli`,
685+
code: constants.UNSUPPORTED
686+
};
687+
};
688+
689+
const invalidArgError = (flag, arg, expectedType = 'string') => {
690+
return {
691+
message: `Mocha flag '--${flag}' given invalid option: '${arg}'`,
692+
code: constants.INVALID_ARG_TYPE,
693+
argument: arg,
694+
actual: 'number',
695+
expected: expectedType
696+
};
697+
};
698+
681699
beforeEach(function () {
682700
readFileSync = sinon.stub();
683701
findConfig = sinon.stub();
@@ -691,16 +709,51 @@ describe('options', function () {
691709
});
692710
});
693711

694-
it('throws error when numeric option is passed to cli', function () {
695-
expect(() => loadOptions(`${numericArg}`), 'to throw', {
696-
message: `Invalid option ${numericArg} passed to mocha cli`
697-
});
712+
it('throws UNSUPPORTED error when numeric option is passed to cli', function () {
713+
expect(
714+
() => loadOptions(`${numericArg}`),
715+
'to throw',
716+
unsupportedError(numericArg)
717+
);
698718
});
699719

700-
it('throws error when numeric argument is passed to mocha flag that does not accept numeric value', function () {
701-
expect(() => loadOptions(`--delay ${numericArg}`), 'to throw', {
702-
message: `Invalid option ${numericArg} passed to mocha cli`
703-
});
720+
it('throws INVALID_ARG_TYPE error when numeric argument is passed to mocha flag that does not accept numeric value', function () {
721+
const flag = '--delay';
722+
expect(
723+
() => loadOptions(`${flag} ${numericArg}`),
724+
'to throw',
725+
invalidArgError(flag, numericArg, 'boolean')
726+
);
727+
});
728+
729+
it('throws INVALID_ARG_TYPE error when incompatible flag does not have preceding "--"', function () {
730+
const flag = 'delay';
731+
expect(
732+
() => loadOptions(`${flag} ${numericArg}`),
733+
'to throw',
734+
invalidArgError(flag, numericArg, 'boolean')
735+
);
736+
});
737+
738+
it('shows correct flag in error when multiple mocha flags have numeric values', function () {
739+
const flag = '--delay';
740+
expect(
741+
() =>
742+
loadOptions(
743+
`--timeout ${numericArg} ${flag} ${numericArg} --retries ${numericArg}`
744+
),
745+
'to throw',
746+
invalidArgError(flag, numericArg, 'boolean')
747+
);
748+
});
749+
750+
it('throws UNSUPPORTED error when numeric arg is passed to unsupported flag', function () {
751+
const invalidFlag = 'foo';
752+
expect(
753+
() => loadOptions(`${invalidFlag} ${numericArg}`),
754+
'to throw',
755+
unsupportedError(numericArg)
756+
);
704757
});
705758

706759
it('does not throw error if numeric value is passed to a compatible mocha flag', function () {

Diff for: test/node-unit/utils.spec.js

+11
Original file line numberDiff line numberDiff line change
@@ -45,5 +45,16 @@ describe('utils', function () {
4545
);
4646
});
4747
});
48+
describe('isNumeric()', function () {
49+
it('returns true for a number type', function () {
50+
expect(utils.isNumeric(42), 'to equal', true);
51+
});
52+
it('returns true for a string that can be parsed as a number', function () {
53+
expect(utils.isNumeric('42'), 'to equal', true);
54+
});
55+
it('returns false for a string that cannot be parsed as a number', function () {
56+
expect(utils.isNumeric('foo'), 'to equal', false);
57+
});
58+
});
4859
});
4960
});

0 commit comments

Comments
 (0)