From fe0045736d63b685ea36d3a32af3b23a92c76941 Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Tue, 8 Sep 2026 04:52:58 +0000 Subject: [PATCH] bun test: honor --pass-with-no-tests when a single path argument does not exist bun test ./missing --pass-with-no-tests printed 'Test filter "./missing" had no matches' and exited 1. The single-path DoesNotExist branch exits before the flag is consulted, while two missing paths, an empty directory, and -t with no match all reach the shared no-tests path and exit 0 with the flag. Skip the early exit when the flag is set so the single-path case takes the same path. --- src/runtime/cli/test_command.rs | 6 ++-- test/cli/test/pass-with-no-tests.test.ts | 38 ++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/runtime/cli/test_command.rs b/src/runtime/cli/test_command.rs index cd5264d8837b..74ce5307551d 100644 --- a/src/runtime/cli/test_command.rs +++ b/src/runtime/cli/test_command.rs @@ -2039,9 +2039,11 @@ impl TestCommand { Ok(()) => {} Err(scanner::ScanError::OutOfMemory) => bun::out_of_memory(), // don't error if multiple are passed; one might fail - // but the others may not + // but the others may not. With --pass-with-no-tests, fall + // through to the shared "did not match any test files" + // path, which reports and exits 0. Err(scanner::ScanError::DoesNotExist) => { - if file_or_dirnames.len() == 1 { + if file_or_dirnames.len() == 1 && !ctx.test_options.pass_with_no_tests { if Output::is_ai_agent() { pretty_errorln!( "Test filter {} had no matches in --cwd={}", diff --git a/test/cli/test/pass-with-no-tests.test.ts b/test/cli/test/pass-with-no-tests.test.ts index 5b39edc57c38..65a0c6158add 100644 --- a/test/cli/test/pass-with-no-tests.test.ts +++ b/test/cli/test/pass-with-no-tests.test.ts @@ -40,6 +40,44 @@ test("--pass-with-no-tests exits with 0 when filters match no tests", async () = expect(exitCode).toBe(0); }); +test.each([ + ["a path that does not exist", ["./does-not-exist"]], + ["several paths that do not exist", ["./does-not-exist", "./also-missing"]], + ["a directory with no test files", ["./empty"]], +])("--pass-with-no-tests exits with 0 when given %s", async (_label, paths) => { + using dir = tempDir("pass-with-no-tests-path", { + "some.test.ts": `import { test } from "bun:test"; test("example", () => {});`, + "empty/not-a-test.ts": `console.log("hello");`, + }); + + await using failing = Bun.spawn({ + cmd: [bunExe(), "test", ...paths], + cwd: String(dir), + stdout: "pipe", + stderr: "pipe", + stdin: "ignore", + env: bunEnv, + }); + const [failErr, failExitCode] = await Promise.all([failing.stderr.text(), failing.exited]); + expect(failErr).toContain(paths[0]); + expect(failErr).not.toContain("example"); + expect(failExitCode).toBe(1); + + await using passing = Bun.spawn({ + cmd: [bunExe(), "test", ...paths, "--pass-with-no-tests"], + cwd: String(dir), + stdout: "pipe", + stderr: "pipe", + stdin: "ignore", + env: bunEnv, + }); + const [passErr, passExitCode] = await Promise.all([passing.stderr.text(), passing.exited]); + expect(passErr).toContain("The following filters did not match any test files:"); + expect(passErr).toContain(paths[0]); + expect(passErr).not.toContain("example"); + expect(passExitCode).toBe(0); +}); + test("without --pass-with-no-tests, exits with 1 when no test files found", async () => { using dir = tempDir("fail-with-no-tests", { "not-a-test.ts": `console.log("hello");`,