Skip to content

Commit f0f34af

Browse files
kylehgcclaude
andcommitted
fix(jest): equals-form --reporters is greedy too; respect -- terminator
Preclear review findings, verified against real jest 30: - --reporters=<v> stays greedy under yargs: following non-flag tokens are reporter values and must be consumed like the space form. - Singular --reporter (not a jest flag) binds at most one value under yargs unknown-option rules; consume exactly one, keeping the test filter after it. - Everything after -- is positional to jest; forward it verbatim instead of applying strip rules past the terminator. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent b4aceb6 commit f0f34af

1 file changed

Lines changed: 66 additions & 11 deletions

File tree

‎src/cmds/js/vitest_cmd.rs‎

Lines changed: 66 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -314,27 +314,45 @@ fn should_skip_vitest_arg(arg: &str) -> bool {
314314
arg == "run" || arg.starts_with("--json") || arg.starts_with("--watch")
315315
}
316316

317-
/// Strip jest args that conflict with the injected `--no-watch --json`.
318-
/// `--reporters` is a yargs array flag: the space-separated form consumes
319-
/// every following non-flag token, so those values must be dropped with the
320-
/// flag — leaving them behind turns reporter names (`default`, ...) into
321-
/// positional test-name filters and jest silently runs the wrong test set.
322-
/// `--watch`/`--watchAll` are boolean and matched exactly, so value-carrying
323-
/// flags like `--watchPathIgnorePatterns` are forwarded intact.
317+
/// Strip jest args that conflict with the injected `--no-watch --json`,
318+
/// consuming exactly the tokens jest's own yargs parsing would bind to them:
319+
/// - `--reporters` and `--reporters=<v>` are greedy array flags: every
320+
/// following token up to the next `-`-prefixed one is a reporter value and
321+
/// is dropped with the flag — left behind, it becomes a positional
322+
/// test-name filter and jest silently runs the wrong test set.
323+
/// - `--reporter` (not a jest flag) binds at most one following non-flag
324+
/// token under yargs unknown-option rules; only that token is dropped.
325+
/// - `--reporter=<v>` binds no following token; only the flag is dropped.
326+
/// - `run`, `--json`, `--watch`, `--watchAll` (and their `=` forms) are
327+
/// boolean/bare and dropped alone; every other token — including
328+
/// value-carrying flags like `--watchPathIgnorePatterns` — is forwarded
329+
/// intact.
330+
/// - `--` and everything after it are forwarded verbatim: jest treats all
331+
/// following tokens as positionals, so no strip rule may apply to them.
324332
fn strip_jest_conflicting_args(args: &[String]) -> Vec<String> {
325333
let mut out = Vec::new();
326334
let mut iter = args.iter().peekable();
327335
while let Some(arg) = iter.next() {
328-
if arg == "--reporters" || arg == "--reporter" {
336+
if arg == "--" {
337+
out.push(arg.clone());
338+
out.extend(iter.cloned());
339+
break;
340+
}
341+
if arg == "--reporters" || arg.starts_with("--reporters=") {
329342
while iter.peek().is_some_and(|next| !next.starts_with('-')) {
330343
iter.next();
331344
}
332345
continue;
333346
}
347+
if arg == "--reporter" {
348+
if iter.peek().is_some_and(|next| !next.starts_with('-')) {
349+
iter.next();
350+
}
351+
continue;
352+
}
334353
if arg == "run"
335354
|| arg == "--json"
336355
|| arg.starts_with("--json=")
337-
|| arg.starts_with("--reporters=")
338356
|| arg.starts_with("--reporter=")
339357
|| arg == "--watch"
340358
|| arg.starts_with("--watch=")
@@ -674,11 +692,48 @@ Scope: all 6 workspace projects
674692
}
675693

676694
#[test]
677-
fn test_strip_jest_reporters_equals_form() {
678-
let result = strip_jest_conflicting_args(&args(&["--reporters=default", "sum.test.ts"]));
695+
fn test_strip_jest_reporters_equals_form_is_greedy_too() {
696+
// yargs array flags stay greedy in the equals form: jest binds
697+
// `jest-junit` (and any following non-flag token) to --reporters,
698+
// so forwarding it would turn it into a test-name filter.
699+
let result = strip_jest_conflicting_args(&args(&[
700+
"--reporters=default",
701+
"jest-junit",
702+
"--coverage",
703+
]));
704+
assert_eq!(result, args(&["--coverage"]));
705+
}
706+
707+
#[test]
708+
fn test_strip_jest_singular_reporter_consumes_at_most_one_value() {
709+
// --reporter is not a jest flag; yargs binds at most one value to an
710+
// unknown option, so the test filter after it must survive.
711+
let result =
712+
strip_jest_conflicting_args(&args(&["--reporter", "default", "sum.test.ts"]));
713+
assert_eq!(result, args(&["sum.test.ts"]));
714+
}
715+
716+
#[test]
717+
fn test_strip_jest_singular_reporter_equals_form_binds_no_value() {
718+
let result = strip_jest_conflicting_args(&args(&["--reporter=verbose", "sum.test.ts"]));
679719
assert_eq!(result, args(&["sum.test.ts"]));
680720
}
681721

722+
#[test]
723+
fn test_strip_jest_reporters_stops_at_double_dash_terminator() {
724+
let result =
725+
strip_jest_conflicting_args(&args(&["--reporters", "default", "--", "sum.test.ts"]));
726+
assert_eq!(result, args(&["--", "sum.test.ts"]));
727+
}
728+
729+
#[test]
730+
fn test_strip_jest_everything_after_double_dash_is_positional() {
731+
// jest treats all tokens after `--` as positionals — even ones that
732+
// look like strippable flags must be forwarded verbatim.
733+
let result = strip_jest_conflicting_args(&args(&["--watch", "--", "run", "--json"]));
734+
assert_eq!(result, args(&["--", "run", "--json"]));
735+
}
736+
682737
#[test]
683738
fn test_strip_jest_watch_flags_are_boolean() {
684739
// --watch/--watchAll never consume a value; the following token is a

0 commit comments

Comments
 (0)