From 71dfd899dfd2ffc625d9dd99c24db1a72806c67e Mon Sep 17 00:00:00 2001 From: Karthik Suresh <7954591+k21993@users.noreply.github.com> Date: Thu, 17 Sep 2026 01:55:00 -0700 Subject: [PATCH] pr_runtime: run vitest with its verbose reporter and parse it normalize_test_cmds_for_runtime appended `--verbose` to vitest commands, which vitest 3-5 reject with `CACError: Unknown option`, so no test ran. Its per-test output comes from `--reporter=verbose` instead. That output was not parseable either: vitest colors it whenever TERM is unset, and its bare ` 12ms` duration was left in the test name. Strip escape codes, then read vitest's fully qualified verbose lines, dropping the run suffixes and keeping project labels as part of the identity. Vitest's default reporter is ignored on purpose: it collapses a fully passing file to one line, so a patch that fixed more than the gold patch would lose its FAIL_TO_PASS tests from the log. Stripping escape codes also lets colored jest output parse, once its padded ` FAIL ` header is accepted. Closes #142 --- src/repo2rlenv/log_parsers/jest_parser.py | 59 ++- .../pipelines/_pr_runtime_verifier.py | 29 +- src/repo2rlenv/pipelines/pr_runtime.py | 16 +- tests/test_pipeline_pr_runtime.py | 15 + tests/test_vitest_output.py | 400 ++++++++++++++++++ 5 files changed, 511 insertions(+), 8 deletions(-) create mode 100644 tests/test_vitest_output.py diff --git a/src/repo2rlenv/log_parsers/jest_parser.py b/src/repo2rlenv/log_parsers/jest_parser.py index 935b2db6..fea280f0 100644 --- a/src/repo2rlenv/log_parsers/jest_parser.py +++ b/src/repo2rlenv/log_parsers/jest_parser.py @@ -30,8 +30,19 @@ ○ / "skipped" → SKIPPED Mocha (the other common JS runner) uses the same ✓/✕ glyphs, so this parser -covers most mocha output too. Vitest's default reporter is jest-compatible -by design — also covered. +covers most mocha output too. + +Vitest (3+) is detected by its ` RUN vX.Y.Z` / ` Test Files` markers and read +from `--reporter=verbose`, which prints one fully qualified line per test: + + ✓ src/foo.test.ts > Foo > returns 200 1ms + × |unit| src/foo.test.ts > Foo > returns 500 3ms + +The name is kept as printed, minus the duration and retry/heap/note suffixes. +Vitest's default reporter collapses fully passing files to one summary line, +so its per-test lines are ignored: a test would vanish from the log as soon as +a patch fixed the rest of its file. Vitest also colors output whenever TERM is +unset, as in a non-TTY `docker exec`, so escape codes are stripped first. Released under Apache-2.0. """ @@ -44,8 +55,9 @@ # File header: `PASS src/foo.test.ts (123 ms)` or `FAIL src/foo.test.ts`. # Captures the file path so we can prefix it onto test names. +# With color, the label is padded (` FAIL `), leaving one leading space. _JEST_FILE_RE = re.compile( - r"^(?:PASS|FAIL)\s+(?P\S+\.(?:ts|tsx|js|jsx|mjs|cjs))\b", + r"^ ?(?:PASS|FAIL)\s+(?P\S+\.(?:ts|tsx|js|jsx|mjs|cjs))\b", ) # Per-test glyph line. Indented arbitrarily; the glyph is the discriminator. @@ -65,6 +77,44 @@ "◯": "SKIPPED", } +# Terminal escape codes (colors), stripped before any matching. +_ANSI_RE = re.compile(r"\x1b\[[0-9;?]*[A-Za-z]") + +# ` RUN v5.0.1 /repo` starts a vitest run; ` Test Files 1 passed (1)` ends it. +_VITEST_MARKER_RE = re.compile(r"^\s*(?:RUN\s+v\d+\.\d+|Test Files\s+\d)", re.MULTILINE) + +# A `--reporter=verbose` test line. The optional project label is `|unit| ` +# without color and ` unit ` with it. Suffixes follow vitest's +# getTestCaseSuffix: duration, retries, repeats, heap usage, skip note. +_VITEST_TEST_RE = re.compile( + r"^\s*(?P[✓×↓□]) (?:\|(?P[^|]+)\| | (?P