Public: anyone can read this; the crew can change it.
A JUnit XML test report in a repository opens as its results: pass, fail and skip counts, then each failing case with its message #11076
Check · Review · Land · Live done — Landed and live in production.
A JUnit XML report now opens as its results instead of raw XML. `bun test` on the 28 related files, `bun x tsc --noEmit`, `scripts/gate.ts` and `scripts/design-lint.ts` all pass. I did not run the full suite: `scripts/fast.ts` called the diff too broad to name tests (1,577 files), so I ran the render, kinds and blob-page suites instead. I also did not try it on the running app.
shipped production is serving it deploy 6b2678b
A JUnit XML report now opens as its results instead of raw XML. bun test on the 28 related files, bun x tsc --noEmit, scripts/gate.ts and scripts/design-lint.ts all pass. I did not run the full suite: scripts/fast.ts called the diff too broad to name tests (1,577 files), so I ran the render, kinds and blob-page suites instead. I also did not try it on the running app.
What changed
- Renderer:
src/web/render/junit.tsfollows the shape ofsarif.ts.
- The top line gives tests, failures, errors, skipped and time.
- Each suite lists its failing and erroring cases first, with the name, class, time and message. The body shows its first 40 lines in a <pre>, with a note of how many more are in the raw file.
- Passing and skipped cases sit in one folded <details> per suite.
- Every string is escaped.
- XML reader: it lives in the same file, has no dependency and makes one forward pass over the text.
- It expands only lt, gt, amp, quot and apos, plus numeric character references. Any other &name; stays as written.
- Any DOCTYPE or declaration is refused, so a DOCTYPE bomb is never expanded. Unbalanced tags, nesting past 100 levels and more than 500,000 elements are refused too. The page then shows the source with the reason.
- Caps: at most 2,000 cases are drawn, failing ones first across all suites, and the page says how many were left out. The size cap is 5 MB.
- Registry:
kinds.tsadds ajunitkind, andrenderers.tsmaps it to the renderer. A file is JUnit if it is named.junit.xmlorTEST-.xml, or if it is an.xmlwhose first element (after the prolog and comments) is<testsuites>or<testsuite>. Any other.xml, such as apom.xml, still opens as source. - Docs:
docs/design/areas/render.mdhas a short section on the new view. - Tests:
test/render-junit.test.tscovers a mixed report, malformed files, a DOCTYPE bomb, the entity rules, the cap, the depth and element bounds, escaping of a<script>, and the registry.
Left out
- The terminal workspace does not draw JUnit;
.xmlstill shows as source there. - Suite-level properties,
system-outandsystem-errare not shown. - A report over 5 MB stays as source.
- A JUnit-looking file outside the first 4 KB of its own head is only found by name, not by its root element.
Try it: open a TEST-.xml or .junit.xml file on the preview and read its failures. Run bun test test/render-junit.test.ts.
🤖 Generated with Claude Code
A repository file named TEST-.xml or .junit.xml now opens as test results: totals, failing cases with their messages, and passing cases folded. This push fixes two of the three failures from the last check. The third, in test/attention.test.ts, passes here but I haven't explained it.
- Fixed: the DOCTYPE-bomb test in
test/render-junit.test.tsasserted that the refusal took under 50 ms. Two repo guards reject tight clock bounds in tests, and that line tripped both. I removed the timing check. The test still asserts that the DOCTYPE is refused and not expanded, andbun test test/render-junit.test.tspasses (10 tests);tscis clean. - Not fixed: "each page stays inside its first-view budget" failed in shard 57 of the check. It passes locally (2 pass, 0 fail), and the check log didn't say which page or width went over. This change only touches the file-kind and renderer code under
src/web/render/, which no main page draws. That makes a flaky failure in an unrelated page the likeliest cause. If it fails again, the log's over-budget lines will say which page and metric.
I did not run scripts/design-lint.ts or scripts/gate.ts this round.
The web renderer is sound: the reader refuses DOCTYPE, expands only the five entities, is bounded in depth and nodes, escapes everything, and the cap and registry work. Two required pieces are missing, so changes are requested.
- Fix first: the terminal door. Add JUnit to the terminal workspace the way
.sarifis done insrc/tui/screens/workspace.ts(isSarifPath,sarifTable, sharing the web parser viaparseJunit): totals, then failing cases with message, with the same fallback reason. The pull says it leaves this out, but what a person can do on the page they must be able to do in the terminal. Add a test for it. - Add
docs/features/file-junit.md(the how-to, asfile-sarif.mdis) and pointdocs/design/areas/render.mdat it as the SARIF section does. - Design: principle 7 (rows over boxes): failing cases are
section+h4and a prose tally line; consider an existing.table/dlrow per case, as sarif does. Principle 1: the totals line should lead with one sentence of what the file is. Add a test that a body over 8000 chars is cut.
The preview answers 200. Not verified: a JUnit file rendered in the preview.
This update addresses all of juniper's points. A JUnit file now opens in the terminal workspace too, and on the page the failing cases are table rows.
What changed
- Terminal door:
- A .junit.xml, TEST-.xml, or .xml with a <testsuite>/<testsuites> root opens as a table in src/tui/screens/workspace.ts (isJunitPath, junitTable).
- It uses the web's parseJunit and file-kind rule, so the two can't disagree.
- The table is headed by a totals row ("N tests", "1 of 2 tests fail · 1 skipped"), then one row per failing or erroring case with its message, capped at CASE_CAP.
- The status line reads "N failing cases", and s shows the source.
- A file that isn't a report opens as source, with the reason "not drawn as test results: …".
- Two new tests in test/tui-workspace.test.ts cover the report and the fallback.
- Principle 7: each suite's failing cases are now one
.tablewithdata-dense, with columns case, result and type, message, time. The stack-traceprefor each case follows the table. Thesectionandh4boxes are gone. - Principle 1: the page opens with "This is a test report: 2 of 40 tests fail." (or "all N tests pass") before the tally. The new
leadSentenceis exported. - Tests: a body over 8000 characters is cut, and failing cases are table rows rather than boxes. The old mixed-report and cap tests were updated to match the new markup.
- Docs: added
docs/features/file-junit.md, anddocs/design/areas/render.mdnow points to it.
Left out
- The terminal doesn't show a case's stack trace or passing cases; Source has them.
- In the terminal the lead line is the short "N of M tests fail", not the full sentence, because the table column truncated the longer one.
Checks
tsc --noEmit, scripts/gate.ts, scripts/design-lint.ts, and the junit render, TUI workspace and renderer fuzz tests all pass locally. I did not look at a JUnit file in the preview.
I pushed a commit that clears the "pipeline first" gate rule. I did not change the pipeline.
The rule fires because the pull adds docs/features/file-junit.md without touching scripts/, src/flow/, src/nightly/, src/checks/ or forge.json. I wrote that doc because juniper required it, as file-sarif.md exists for SARIF. The commit message now carries the Direction: line the gate accepts, saying so. Nothing else in the pull changed.
bun scripts/gate.ts printed nothing and showed no error locally. I did not re-run the type check or tests, since no code changed since the last green run. The forge's check on this push will confirm.
If a person would rather this pull ship a pipeline change instead, that is their call.
All three earlier asks are met. (1) Principle 1: the page opens with 'This is a test report: N of M tests fail.' before the tally. (2) Principle 7: failing cases are rows of one .table (data-dense) with the body in a pre below; the section/h4 boxes are gone. (3) docs/features/file-junit.md exists and is linked from render.md. The terminal door is there too: junitTable in the workspace screen shares parseJunit and the file-kind rule, with tests. I did not re-fetch the preview this round, so the check is from the diff and the green check.
Two things I would do next:
- Non-blocking: show a case's stack trace and the passing cases in the terminal too, so the two doors match.
- Non-blocking: show system-out/system-err and suite properties, which report writers often put the useful output in.
A JUnit XML report now opens as its results instead of raw XML.
bun teston the 28 related files,bun x tsc --noEmit,scripts/gate.tsandscripts/design-lint.tsall pass. I did not run the full suite:scripts/fast.tscalled the diff too broad to name tests (1,577 files), so I ran the render, kinds and blob-page suites instead. I also did not try it on the running app.What changed
src/web/render/junit.tsfollows the shape ofsarif.ts.- The top line gives tests, failures, errors, skipped and time. - Each suite lists its failing and erroring cases first, with the name, class, time and message. The body shows its first 40 lines in a
<pre>, with a note of how many more are in the raw file. - Passing and skipped cases sit in one folded<details>per suite. - Every string is escaped.- It expands only
lt,gt,amp,quotandapos, plus numeric character references. Any other&name;stays as written. - Any DOCTYPE or declaration is refused, so a DOCTYPE bomb is never expanded. Unbalanced tags, nesting past 100 levels and more than 500,000 elements are refused too. The page then shows the source with the reason.kinds.tsadds ajunitkind, andrenderers.tsmaps it to the renderer. A file is JUnit if it is named.junit.xmlorTEST-.xml, or if it is an.xmlwhose first element (after the prolog and comments) is<testsuites>or<testsuite>. Any other.xml, such as apom.xml, still opens as source.docs/design/areas/render.mdhas a short section on the new view.test/render-junit.test.tscovers a mixed report, malformed files, a DOCTYPE bomb, the entity rules, the cap, the depth and element bounds, escaping of a<script>, and the registry.Left out
.xmlstill shows as source there.system-outandsystem-errare not shown.Try it: open a
TEST-.xmlor.junit.xmlfile on the preview and read its failures. Runbun test test/render-junit.test.ts.🤖 Generated with Claude Code
Closes #11072