From 912bdd18681d46fd255aa7523fe426d51bdf2511 Mon Sep 17 00:00:00 2001 From: Dinika Saxena Date: Thu, 5 Dec 2024 12:48:07 +0100 Subject: [PATCH] Throw error for numeric positional arg after yargs-parser has parsed args Signed-off-by: Dinika Saxena --- lib/cli/options.js | 66 +++++++++++++++++++++--------- test/node-unit/cli/options.spec.js | 8 ++++ 2 files changed, 55 insertions(+), 19 deletions(-) diff --git a/lib/cli/options.js b/lib/cli/options.js index 8ef5a28c6b..26199093be 100644 --- a/lib/cli/options.js +++ b/lib/cli/options.js @@ -103,6 +103,44 @@ const nargOpts = types.array .concat(types.string, types.number) .reduce((acc, arg) => Object.assign(acc, {[arg]: 1}), {}); +/** + * Throws either "UNSUPPORTED" error or "INVALID_ARG_TYPE" error for numeric positional arguments. + * @param {string[]} allArgs - Stringified args passed to mocha cli + * @param {number} numericArg - Numeric positional arg for which error must be thrown + * @param {Object} parsedResult - Result from `yargs-parser` + * @private + * @ignore + */ +const createErrorForNumericPositionalArg = ( + allArgs, + numericArg, + parsedResult +) => { + // A flag for `numericArg` exists if: + // 1. A mocha flag immediately preceeded the numericArg in `allArgs` array and + // 2. `numericArg` value could not be assigned to this flag by `yargs-parser` because of incompatible datatype. + const flag = allArgs.find((arg, index) => { + const normalizedArg = arg.replace(/^--?/, ''); + return ( + isMochaFlag(arg) && + allArgs[index + 1] === String(numericArg) && + parsedResult[normalizedArg] !== numericArg + ); + }); + + if (flag) { + throw createInvalidArgumentTypeError( + `Mocha flag '${flag}' given invalid option: '${numericArg}'`, + numericArg, + expectedTypeForFlag(flag) + ); + } else { + throw createUnsupportedError( + `Option ${numericArg} is unsupported by the mocha cli` + ); + } +}; + /** * Wrapper around `yargs-parser` which applies our settings * @param {string|string[]} args - Arguments to parse @@ -120,25 +158,6 @@ const parse = (args = [], defaultValues = {}, ...configObjects) => { // 4. we can then reapply the values after yargs-parser is done. const allArgs = Array.isArray(args) ? args : args.split(' '); const nodeArgs = allArgs.reduce((acc, arg, index, allArgs) => { - const maybeFlag = allArgs[index - 1]; - - if (isNumeric(arg) && (!maybeFlag || !isMochaFlag(maybeFlag))) { - throw createUnsupportedError( - `Option ${arg} is unsupported by the mocha cli` - ); - } - if ( - isNumeric(arg) && - isMochaFlag(maybeFlag) && - expectedTypeForFlag(maybeFlag) !== 'number' - ) { - throw createInvalidArgumentTypeError( - `Mocha flag '${maybeFlag}' given invalid option: '${arg}'`, - Number(arg), - expectedTypeForFlag(maybeFlag) - ); - } - const pair = arg.split('='); let flag = pair[0]; if (isNodeFlag(flag, false)) { @@ -165,6 +184,15 @@ const parse = (args = [], defaultValues = {}, ...configObjects) => { process.exit(1); } + const numericPositionalArg = result.argv._.find(arg => isNumeric(arg)); + if (numericPositionalArg) { + createErrorForNumericPositionalArg( + allArgs, + numericPositionalArg, + result.argv + ); + } + // reapply "=" arg values from above nodeArgs.forEach(([key, value]) => { result.argv[key] = value; diff --git a/test/node-unit/cli/options.spec.js b/test/node-unit/cli/options.spec.js index 2b6200fbcc..cb456869ae 100644 --- a/test/node-unit/cli/options.spec.js +++ b/test/node-unit/cli/options.spec.js @@ -771,6 +771,14 @@ describe('options', function () { 'not to throw' ); }); + + it('does not throw error if numeric value is passed to string flag', function () { + expect(() => loadOptions(`--grep ${numericArg}`), 'not to throw'); + }); + + it('does not throw error if numeric value is passed to an array flag', function () { + expect(() => loadOptions(`--spec ${numericArg}`), 'not to throw'); + }); }); }); });