From d532576abf3ebe0135a79f076b304adf93ab1795 Mon Sep 17 00:00:00 2001 From: "Sakamoto, Kazunori" Date: Thu, 25 Dec 2025 17:37:34 +0900 Subject: [PATCH 1/4] feat: add quick option to wb test Co-authored-by: WillBooster (Codex CLI) --- packages/wb/src/commands/test.ts | 4 ++++ .../wb/src/scripts/execution/baseScripts.ts | 24 ++++++++++++++----- .../scripts/execution/httpServerScripts.ts | 3 ++- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/packages/wb/src/commands/test.ts b/packages/wb/src/commands/test.ts index 7ae121a1..44e53aee 100644 --- a/packages/wb/src/commands/test.ts +++ b/packages/wb/src/commands/test.ts @@ -42,6 +42,10 @@ const builder = { description: 'Reduce redundant outputs', type: 'boolean', }, + quick: { + description: 'Stop tests after the first failure', + type: 'boolean', + }, 'unit-timeout': { description: 'Timeout for unit tests', type: 'number', diff --git a/packages/wb/src/scripts/execution/baseScripts.ts b/packages/wb/src/scripts/execution/baseScripts.ts index ca1d417f..10be4df3 100644 --- a/packages/wb/src/scripts/execution/baseScripts.ts +++ b/packages/wb/src/scripts/execution/baseScripts.ts @@ -101,7 +101,7 @@ export abstract class BaseScripts { ): Promise { const port = await checkAndKillPortProcess(project.env.PORT, project); const suffix = project.packageJson.scripts?.['test/e2e-additional'] ? ' && YARN test/e2e-additional' : ''; - const playwrightCommand = buildPlaywrightCommand(playwrightArgs, argv.targets); + const playwrightCommand = buildPlaywrightCommand(playwrightArgs, argv.targets, argv.quick); if (project.skipLaunchingServerForPlaywright) { return `${playwrightCommand}${suffix}`; } @@ -115,11 +115,13 @@ export abstract class BaseScripts { testUnit(project: Project, argv: TestArgv): string { const testTarget = argv.targets?.join(' ') || 'test/unit/'; + const quickOptionForVitest = argv.quick ? ' --bail=1' : ''; + const quickOptionForBun = argv.quick ? ' --bail' : ''; if (project.hasVitest) { // Since this command is referred from other commands, we have to use "vitest run" (non-interactive mode). - return `YARN vitest run ${testTarget} --color --passWithNoTests --allowOnly`; + return `YARN vitest run ${testTarget} --color --passWithNoTests --allowOnly${quickOptionForVitest}`; } else if (project.isBunAvailable) { - return `bun test ${testTarget}`; + return `bun test ${testTarget}${quickOptionForBun}`; } return 'echo "No tests."'; } @@ -151,11 +153,11 @@ function findEcosystemConfigPath(project: Project): string | undefined { } } -function buildPlaywrightCommand(playwrightArgs: string, targets: TestArgv['targets']): string { +function buildPlaywrightCommand(playwrightArgs: string, targets: TestArgv['targets'], quick?: boolean): string { const base = 'BUN playwright'; const target = targets?.join(' ') || 'test/e2e/'; if (!playwrightArgs.startsWith('test ') || !targets?.length) { - return `${base} ${playwrightArgs}`; + return appendPlaywrightQuickOption(`${base} ${playwrightArgs}`, quick); } const rest = playwrightArgs.slice('test '.length).trim(); @@ -165,5 +167,15 @@ function buildPlaywrightCommand(playwrightArgs: string, targets: TestArgv['targe } else { parts[0] = target; } - return `${base} test ${parts.join(' ')}`; + return appendPlaywrightQuickOption(`${base} test ${parts.join(' ')}`, quick); +} + +function appendPlaywrightQuickOption(command: string, quick?: boolean): string { + if (!quick || !command.includes('playwright test')) { + return command; + } + if (/--max-failures(?:=|\s)/.test(command)) { + return command; + } + return `${command} --max-failures=1`; } diff --git a/packages/wb/src/scripts/execution/httpServerScripts.ts b/packages/wb/src/scripts/execution/httpServerScripts.ts index 7ec608d0..3b7fc97d 100644 --- a/packages/wb/src/scripts/execution/httpServerScripts.ts +++ b/packages/wb/src/scripts/execution/httpServerScripts.ts @@ -22,11 +22,12 @@ class HttpServerScripts extends BaseScripts { const port = await checkAndKillPortProcess(project.env.PORT, project); const suffix = project.packageJson.scripts?.['test/e2e-additional'] ? ' && YARN test/e2e-additional' : ''; const testTarget = argv.targets && argv.targets.length > 0 ? argv.targets.join(' ') : 'test/e2e/'; + const quickOption = argv.quick ? ' --bail=1' : ''; return `YARN concurrently --kill-others --raw --success first "${startCommand} && exit 1" "wait-on -t 600000 -i 2000 http-get://127.0.0.1:${port} - && vitest run ${testTarget} --color --passWithNoTests --allowOnly${suffix}"`; + && vitest run ${testTarget} --color --passWithNoTests --allowOnly${quickOption}${suffix}"`; } } From 98a841bd1bedb921abf45999f7730b5f97c04fa1 Mon Sep 17 00:00:00 2001 From: "Sakamoto, Kazunori" Date: Thu, 25 Dec 2025 17:45:55 +0900 Subject: [PATCH 2/4] feat: rename quick to bail for wb test Co-authored-by: WillBooster (Codex CLI) --- packages/wb/src/commands/test.ts | 2 +- packages/wb/src/scripts/execution/baseScripts.ts | 16 ++++++++-------- .../src/scripts/execution/httpServerScripts.ts | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/wb/src/commands/test.ts b/packages/wb/src/commands/test.ts index 44e53aee..42871977 100644 --- a/packages/wb/src/commands/test.ts +++ b/packages/wb/src/commands/test.ts @@ -42,7 +42,7 @@ const builder = { description: 'Reduce redundant outputs', type: 'boolean', }, - quick: { + bail: { description: 'Stop tests after the first failure', type: 'boolean', }, diff --git a/packages/wb/src/scripts/execution/baseScripts.ts b/packages/wb/src/scripts/execution/baseScripts.ts index 10be4df3..d9575f1b 100644 --- a/packages/wb/src/scripts/execution/baseScripts.ts +++ b/packages/wb/src/scripts/execution/baseScripts.ts @@ -101,7 +101,7 @@ export abstract class BaseScripts { ): Promise { const port = await checkAndKillPortProcess(project.env.PORT, project); const suffix = project.packageJson.scripts?.['test/e2e-additional'] ? ' && YARN test/e2e-additional' : ''; - const playwrightCommand = buildPlaywrightCommand(playwrightArgs, argv.targets, argv.quick); + const playwrightCommand = buildPlaywrightCommand(playwrightArgs, argv.targets, argv.bail); if (project.skipLaunchingServerForPlaywright) { return `${playwrightCommand}${suffix}`; } @@ -115,8 +115,8 @@ export abstract class BaseScripts { testUnit(project: Project, argv: TestArgv): string { const testTarget = argv.targets?.join(' ') || 'test/unit/'; - const quickOptionForVitest = argv.quick ? ' --bail=1' : ''; - const quickOptionForBun = argv.quick ? ' --bail' : ''; + const quickOptionForVitest = argv.bail ? ' --bail=1' : ''; + const quickOptionForBun = argv.bail ? ' --bail' : ''; if (project.hasVitest) { // Since this command is referred from other commands, we have to use "vitest run" (non-interactive mode). return `YARN vitest run ${testTarget} --color --passWithNoTests --allowOnly${quickOptionForVitest}`; @@ -153,11 +153,11 @@ function findEcosystemConfigPath(project: Project): string | undefined { } } -function buildPlaywrightCommand(playwrightArgs: string, targets: TestArgv['targets'], quick?: boolean): string { +function buildPlaywrightCommand(playwrightArgs: string, targets: TestArgv['targets'], bail?: boolean): string { const base = 'BUN playwright'; const target = targets?.join(' ') || 'test/e2e/'; if (!playwrightArgs.startsWith('test ') || !targets?.length) { - return appendPlaywrightQuickOption(`${base} ${playwrightArgs}`, quick); + return appendPlaywrightBailOption(`${base} ${playwrightArgs}`, bail); } const rest = playwrightArgs.slice('test '.length).trim(); @@ -167,11 +167,11 @@ function buildPlaywrightCommand(playwrightArgs: string, targets: TestArgv['targe } else { parts[0] = target; } - return appendPlaywrightQuickOption(`${base} test ${parts.join(' ')}`, quick); + return appendPlaywrightBailOption(`${base} test ${parts.join(' ')}`, bail); } -function appendPlaywrightQuickOption(command: string, quick?: boolean): string { - if (!quick || !command.includes('playwright test')) { +function appendPlaywrightBailOption(command: string, bail?: boolean): string { + if (!bail || !command.includes('playwright test')) { return command; } if (/--max-failures(?:=|\s)/.test(command)) { diff --git a/packages/wb/src/scripts/execution/httpServerScripts.ts b/packages/wb/src/scripts/execution/httpServerScripts.ts index 3b7fc97d..c02f98e4 100644 --- a/packages/wb/src/scripts/execution/httpServerScripts.ts +++ b/packages/wb/src/scripts/execution/httpServerScripts.ts @@ -22,7 +22,7 @@ class HttpServerScripts extends BaseScripts { const port = await checkAndKillPortProcess(project.env.PORT, project); const suffix = project.packageJson.scripts?.['test/e2e-additional'] ? ' && YARN test/e2e-additional' : ''; const testTarget = argv.targets && argv.targets.length > 0 ? argv.targets.join(' ') : 'test/e2e/'; - const quickOption = argv.quick ? ' --bail=1' : ''; + const quickOption = argv.bail ? ' --bail=1' : ''; return `YARN concurrently --kill-others --raw --success first "${startCommand} && exit 1" From b9efab44035db17a669863cb56aa1a765db13b7d Mon Sep 17 00:00:00 2001 From: "Sakamoto, Kazunori" Date: Thu, 25 Dec 2025 17:47:15 +0900 Subject: [PATCH 3/4] chore: rename quick identifiers to bail Co-authored-by: WillBooster (Codex CLI) --- packages/wb/src/scripts/execution/baseScripts.ts | 8 ++++---- packages/wb/src/scripts/execution/httpServerScripts.ts | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/wb/src/scripts/execution/baseScripts.ts b/packages/wb/src/scripts/execution/baseScripts.ts index d9575f1b..ddc49ca6 100644 --- a/packages/wb/src/scripts/execution/baseScripts.ts +++ b/packages/wb/src/scripts/execution/baseScripts.ts @@ -115,13 +115,13 @@ export abstract class BaseScripts { testUnit(project: Project, argv: TestArgv): string { const testTarget = argv.targets?.join(' ') || 'test/unit/'; - const quickOptionForVitest = argv.bail ? ' --bail=1' : ''; - const quickOptionForBun = argv.bail ? ' --bail' : ''; + const bailOptionForVitest = argv.bail ? ' --bail=1' : ''; + const bailOptionForBun = argv.bail ? ' --bail' : ''; if (project.hasVitest) { // Since this command is referred from other commands, we have to use "vitest run" (non-interactive mode). - return `YARN vitest run ${testTarget} --color --passWithNoTests --allowOnly${quickOptionForVitest}`; + return `YARN vitest run ${testTarget} --color --passWithNoTests --allowOnly${bailOptionForVitest}`; } else if (project.isBunAvailable) { - return `bun test ${testTarget}${quickOptionForBun}`; + return `bun test ${testTarget}${bailOptionForBun}`; } return 'echo "No tests."'; } diff --git a/packages/wb/src/scripts/execution/httpServerScripts.ts b/packages/wb/src/scripts/execution/httpServerScripts.ts index c02f98e4..6743ef34 100644 --- a/packages/wb/src/scripts/execution/httpServerScripts.ts +++ b/packages/wb/src/scripts/execution/httpServerScripts.ts @@ -22,12 +22,12 @@ class HttpServerScripts extends BaseScripts { const port = await checkAndKillPortProcess(project.env.PORT, project); const suffix = project.packageJson.scripts?.['test/e2e-additional'] ? ' && YARN test/e2e-additional' : ''; const testTarget = argv.targets && argv.targets.length > 0 ? argv.targets.join(' ') : 'test/e2e/'; - const quickOption = argv.bail ? ' --bail=1' : ''; + const bailOption = argv.bail ? ' --bail=1' : ''; return `YARN concurrently --kill-others --raw --success first "${startCommand} && exit 1" "wait-on -t 600000 -i 2000 http-get://127.0.0.1:${port} - && vitest run ${testTarget} --color --passWithNoTests --allowOnly${quickOption}${suffix}"`; + && vitest run ${testTarget} --color --passWithNoTests --allowOnly${bailOption}${suffix}"`; } } From 66a2170fd0beefabc6256575388997a20e26f3bd Mon Sep 17 00:00:00 2001 From: "Sakamoto, Kazunori" Date: Thu, 25 Dec 2025 19:13:30 +0900 Subject: [PATCH 4/4] style: scope bail options in testUnit Co-authored-by: WillBooster (Codex CLI) --- packages/wb/src/scripts/execution/baseScripts.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/wb/src/scripts/execution/baseScripts.ts b/packages/wb/src/scripts/execution/baseScripts.ts index ddc49ca6..5cb9f3b3 100644 --- a/packages/wb/src/scripts/execution/baseScripts.ts +++ b/packages/wb/src/scripts/execution/baseScripts.ts @@ -115,13 +115,13 @@ export abstract class BaseScripts { testUnit(project: Project, argv: TestArgv): string { const testTarget = argv.targets?.join(' ') || 'test/unit/'; - const bailOptionForVitest = argv.bail ? ' --bail=1' : ''; - const bailOptionForBun = argv.bail ? ' --bail' : ''; if (project.hasVitest) { + const bailOption = argv.bail ? ' --bail=1' : ''; // Since this command is referred from other commands, we have to use "vitest run" (non-interactive mode). - return `YARN vitest run ${testTarget} --color --passWithNoTests --allowOnly${bailOptionForVitest}`; + return `YARN vitest run ${testTarget} --color --passWithNoTests --allowOnly${bailOption}`; } else if (project.isBunAvailable) { - return `bun test ${testTarget}${bailOptionForBun}`; + const bailOption = argv.bail ? ' --bail' : ''; + return `bun test ${testTarget}${bailOption}`; } return 'echo "No tests."'; }