diff --git a/CHANGELOG.md b/CHANGELOG.md index ffa3830e0bfa..7c9cddc2c0bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -81,6 +81,7 @@ - `[jest-haste-map]` Remove legacy condition for duplicate module detection ([#7333](https://github.com/facebook/jest/pull/7333)) - `[jest-haste-map]` Fix `require` detection with trailing commas and ignore `import typeof` modules ([#7385](https://github.com/facebook/jest/pull/7385)) - `[jest-cli]` Fix to set prettierPath via config file ([#7412](https://github.com/facebook/jest/pull/7412)) +- `[jest-cli]` Support dashed args ([#7497](https://github.com/facebook/jest/pull/7497)) ### Chore & Maintenance diff --git a/docs/CLI.md b/docs/CLI.md index 7ca495392a35..52f74b160c03 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -81,6 +81,21 @@ you can use: npm test -- -u -t="ColorPicker" ``` +## Camelcase & dashed args support + +Jest supports both camelcase and dashed arg formats. The following examples will have equal result: + +```bash +jest --collect-coverage +jest --collectCoverage +``` + +Arguments can also be mixed: + +```bash +jest --update-snapshot --detectOpenHandles +``` + ## Options _Note: CLI options take precedence over values from the [Configuration](Configuration.md)._ diff --git a/e2e/__tests__/supports-dashed-args.js b/e2e/__tests__/supports-dashed-args.js new file mode 100644 index 000000000000..a0c18a22ebbe --- /dev/null +++ b/e2e/__tests__/supports-dashed-args.js @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2018-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +'use strict'; + +import path from 'path'; +import runJest from '../runJest'; + +const consoleDir = path.resolve(__dirname, '../console'); +const eachDir = path.resolve(__dirname, '../each'); + +expect.addSnapshotSerializer({ + print: value => value, + test: received => typeof received === 'string', +}); + +test('works with passing tests', () => { + const result = runJest(eachDir, [ + 'success.test.js', + '--runInBand', + '--collect-coverage', + '--coverageReporters', + 'text-summary', + '--clear-mocks', + '--useStderr', + ]); + if (result.status !== 0) { + console.error(result.stderr); + } + expect(result.status).toBe(0); +}); + +test('throws error for unknown dashed & camelcase args', () => { + const result = runJest(consoleDir, [ + 'success.test.js', + '--runInBand', + '--collect-coverage', + '--coverageReporters', + 'text-summary', + '--clear-mocks', + '--doesNotExist', + '--also-does-not-exist', + '--useStderr', + ]); + expect(result.stderr).toMatchInlineSnapshot(` +● Unrecognized CLI Parameters: + + Following options were not recognized: + ["doesNotExist", "also-does-not-exist"] + + CLI Options Documentation: + https://jestjs.io/docs/en/cli.html + + +`); + expect(result.status).toBe(1); +}); diff --git a/packages/jest-cli/src/__tests__/cli/args.test.js b/packages/jest-cli/src/__tests__/cli/args.test.js index 88f3faf1c4c5..88d16dc43591 100644 --- a/packages/jest-cli/src/__tests__/cli/args.test.js +++ b/packages/jest-cli/src/__tests__/cli/args.test.js @@ -10,6 +10,7 @@ import type {Argv} from 'types/Argv'; import {check} from '../../cli/args'; +import {buildArgv} from '../../cli'; describe('check', () => { it('returns true if the arguments are valid', () => { @@ -59,3 +60,15 @@ describe('check', () => { ); }); }); + +describe('buildArgv', () => { + it('should return only camelcased args ', () => { + const mockProcessArgv = jest + .spyOn(process.argv, 'slice') + .mockImplementation(() => ['--clear-mocks']); + const actual = buildArgv(null); + expect(actual).not.toHaveProperty('clear-mocks'); + expect(actual).toHaveProperty('clearMocks', true); + mockProcessArgv.mockRestore(); + }); +}); diff --git a/packages/jest-cli/src/cli/index.js b/packages/jest-cli/src/cli/index.js index 1738d86776b4..65de1406434e 100644 --- a/packages/jest-cli/src/cli/index.js +++ b/packages/jest-cli/src/cli/index.js @@ -35,6 +35,7 @@ import logDebugMessages from '../lib/log_debug_messages'; export async function run(maybeArgv?: Argv, project?: Path) { try { + // $FlowFixMe:`allow reduced return const argv: Argv = buildArgv(maybeArgv, project); if (argv.init) { @@ -174,8 +175,9 @@ const readResultsAndExit = ( } }; -const buildArgv = (maybeArgv: ?Argv, project: ?Path) => { - const argv: Argv = yargs(maybeArgv || process.argv.slice(2)) +export const buildArgv = (maybeArgv: ?Argv, project: ?Path) => { + const rawArgv: Argv | string[] = maybeArgv || process.argv.slice(2); + const argv: Argv = yargs(rawArgv) .usage(args.usage) .alias('help', 'h') .options(args.options) @@ -185,9 +187,20 @@ const buildArgv = (maybeArgv: ?Argv, project: ?Path) => { validateCLIOptions( argv, Object.assign({}, args.options, {deprecationEntries}), + // strip leading dashes + Array.isArray(rawArgv) + ? rawArgv.map(rawArgv => rawArgv.replace(/^--?/, '')) + : Object.keys(rawArgv), ); - return argv; + // strip dashed args + return Object.keys(argv).reduce((result, key) => { + if (!key.includes('-')) { + // $FlowFixMe:`allow reduced return + result[key] = argv[key]; + } + return result; + }, {}); }; const getProjectListFromCLIArgs = (argv, project: ?Path) => { diff --git a/packages/jest-validate/package.json b/packages/jest-validate/package.json index 33a9d90b4bb4..6ea5a6e5b68f 100644 --- a/packages/jest-validate/package.json +++ b/packages/jest-validate/package.json @@ -8,6 +8,7 @@ "license": "MIT", "main": "build/index.js", "dependencies": { + "camelcase": "^5.0.0", "chalk": "^2.0.1", "jest-get-type": "^22.1.0", "leven": "^2.1.0", diff --git a/packages/jest-validate/src/validateCLIOptions.js b/packages/jest-validate/src/validateCLIOptions.js index 255093959030..006af5162fcf 100644 --- a/packages/jest-validate/src/validateCLIOptions.js +++ b/packages/jest-validate/src/validateCLIOptions.js @@ -10,6 +10,7 @@ import type {Argv} from 'types/Argv'; import chalk from 'chalk'; +import camelcase from 'camelcase'; import {createDidYouMeanMessage, format, ValidationError} from './utils'; import {deprecationWarning} from './deprecated'; import defaultConfig from './defaultConfig'; @@ -65,7 +66,11 @@ const logDeprecatedOptions = ( }); }; -export default function validateCLIOptions(argv: Argv, options: Object) { +export default function validateCLIOptions( + argv: Argv, + options: Object, + rawArgv: string[] = [], +) { const yargsSpecialOptions = ['$0', '_', 'help', 'h']; const deprecationEntries = options.deprecationEntries || {}; const allowedOptions = Object.keys(options).reduce( @@ -73,7 +78,10 @@ export default function validateCLIOptions(argv: Argv, options: Object) { new Set(yargsSpecialOptions), ); const unrecognizedOptions = Object.keys(argv).filter( - arg => !allowedOptions.has(arg), + arg => + !allowedOptions.has(camelcase(arg)) && + (!rawArgv.length || rawArgv.includes(arg)), + [], ); if (unrecognizedOptions.length) { diff --git a/yarn.lock b/yarn.lock index e41514cbff50..191ce9c9d96f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3437,6 +3437,11 @@ camelcase@^4.1.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= +camelcase@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42" + integrity sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA== + caniuse-api@^1.5.2: version "1.6.1" resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.6.1.tgz#b534e7c734c4f81ec5fbe8aca2ad24354b962c6c"