From 93fbd02d89f71412e76fe6ddb8cd47693b105527 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ari=20Perkki=C3=B6?= Date: Thu, 4 May 2023 16:26:14 +0300 Subject: [PATCH] fix(cli): improve cac errors when mixing boolean and dot notation (#3302) --- packages/vitest/src/node/cli.ts | 28 +++++++++++++++++++++++++++- test/config/test/failures.test.ts | 14 ++++++++++++++ test/config/test/utils.ts | 2 +- test/config/vitest.config.ts | 3 +++ 4 files changed, 45 insertions(+), 2 deletions(-) diff --git a/packages/vitest/src/node/cli.ts b/packages/vitest/src/node/cli.ts index b756e44cb4e..260fb0d8abd 100644 --- a/packages/vitest/src/node/cli.ts +++ b/packages/vitest/src/node/cli.ts @@ -77,7 +77,33 @@ cli .command('[...filters]') .action((filters, options) => start('test', filters, options)) -cli.parse() +try { + cli.parse() +} +catch (originalError) { + // CAC may fail to parse arguments when boolean flags and dot notation are mixed + // e.g. "--coverage --coverage.reporter text" will fail, when "--coverage.enabled --coverage.reporter text" will pass + const fullArguments = cli.rawArgs.join(' ') + const conflictingArgs: { arg: string; dotArgs: string[] }[] = [] + + for (const arg of cli.rawArgs) { + if (arg.startsWith('--') && !arg.includes('.') && fullArguments.includes(`${arg}.`)) { + const dotArgs = cli.rawArgs.filter(rawArg => rawArg.startsWith(arg) && rawArg.includes('.')) + conflictingArgs.push({ arg, dotArgs }) + } + } + + if (conflictingArgs.length === 0) + throw originalError + + const error = conflictingArgs + .map(({ arg, dotArgs }) => + `A boolean argument "${arg}" was used with dot notation arguments "${dotArgs.join(' ')}".` + + `\nPlease specify the "${arg}" argument with dot notation as well: "${arg}.enabled"`) + .join('\n') + + throw new Error(error) +} async function runRelated(relatedFiles: string[] | string, argv: CliOptions): Promise { argv.related = relatedFiles diff --git a/test/config/test/failures.test.ts b/test/config/test/failures.test.ts index f72b49ac87c..5abde7b08cb 100644 --- a/test/config/test/failures.test.ts +++ b/test/config/test/failures.test.ts @@ -43,3 +43,17 @@ test('c8 coverage provider cannot be used with browser', async () => { expect(error).toMatch('Error: @vitest/coverage-c8 does not work with --browser. Use @vitest/coverage-istanbul instead') }) + +test('boolean coverage flag without dot notation, with more dot notation options', async () => { + const { error } = await runVitest('run', ['--coverage', '--coverage.reporter', 'text']) + + expect(error).toMatch('Error: A boolean argument "--coverage" was used with dot notation arguments "--coverage.reporter".') + expect(error).toMatch('Please specify the "--coverage" argument with dot notation as well: "--coverage.enabled"') +}) + +test('boolean browser flag without dot notation, with more dot notation options', async () => { + const { error } = await runVitest('run', ['--browser', '--browser.name', 'chrome']) + + expect(error).toMatch('Error: A boolean argument "--browser" was used with dot notation arguments "--browser.name".') + expect(error).toMatch('Please specify the "--browser" argument with dot notation as well: "--browser.enabled"') +}) diff --git a/test/config/test/utils.ts b/test/config/test/utils.ts index 574cd02fd8d..51e8b492249 100644 --- a/test/config/test/utils.ts +++ b/test/config/test/utils.ts @@ -6,7 +6,7 @@ export async function runVitest(mode: 'run' | 'watch', cliArguments: string[]) { let error = '' subprocess.stderr?.on('data', (data) => { - error = stripAnsi(data.toString()) + error += stripAnsi(data.toString()) // Sometimes on Windows CI execa doesn't exit properly. Force exit when stderr is caught. subprocess.kill() diff --git a/test/config/vitest.config.ts b/test/config/vitest.config.ts index 765b3be8a8d..a3f5c3ce3f0 100644 --- a/test/config/vitest.config.ts +++ b/test/config/vitest.config.ts @@ -3,5 +3,8 @@ import { defineConfig } from 'vitest/config' export default defineConfig({ test: { testTimeout: 60_000, + chaiConfig: { + truncateThreshold: 999, + }, }, })