From 9678e128cfcab5a8d01e83b9215a544a22cd050d Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 31 Aug 2026 10:50:12 +0000 Subject: [PATCH] Collapse the all-tests table to a per-class summary when everything passes 3000+ individual pass rows blew past GitHub's job summary size limit, causing the whole summary to render as nothing. When there are no failures we now show test counts/time grouped by class instead of one row per test case; the full per-test table still renders when there are failures. --- dist/index.js | 34 +++++++++++++++++++++++++++------- src/main.test.ts | 9 +++++++++ src/main.ts | 39 ++++++++++++++++++++++++++++++++------- 3 files changed, 68 insertions(+), 14 deletions(-) diff --git a/dist/index.js b/dist/index.js index 4bc2c64..2319ae8 100644 --- a/dist/index.js +++ b/dist/index.js @@ -27782,16 +27782,36 @@ function buildMarkdown(title, reports) { lines.push("", ""); } } - lines.push("### All tests", ""); - lines.push("| Status | Test | Time |"); - lines.push("|--------|------|------|"); - for (const c of all) { - const icon = statusIcon(c.status); - const name = c.classname ? `${c.classname} \u203A ${c.name}` : c.name; - lines.push(`| ${icon} | ${escapeMd(name)} | ${c.time.toFixed(3)}s |`); + if (failed === 0) { + lines.push("### All tests", ""); + lines.push("| Class | Tests | Time |"); + lines.push("|-------|-------|------|"); + for (const { classname, count, time } of groupByClass(all)) { + lines.push(`| ${escapeMd(classname)} | ${count} | ${time.toFixed(3)}s |`); + } + } else { + lines.push("### All tests", ""); + lines.push("| Status | Test | Time |"); + lines.push("|--------|------|------|"); + for (const c of all) { + const icon = statusIcon(c.status); + const name = c.classname ? `${c.classname} \u203A ${c.name}` : c.name; + lines.push(`| ${icon} | ${escapeMd(name)} | ${c.time.toFixed(3)}s |`); + } } return lines.join("\n") + "\n"; } +function groupByClass(cases) { + const groups = /* @__PURE__ */ new Map(); + for (const c of cases) { + const key = c.classname || c.name; + const g = groups.get(key) ?? { count: 0, time: 0 }; + g.count += 1; + g.time += c.time; + groups.set(key, g); + } + return [...groups.entries()].map(([classname, g]) => ({ classname, ...g })).sort((a, b) => a.classname.localeCompare(b.classname)); +} function statusIcon(s) { switch (s) { case "passed": diff --git a/src/main.test.ts b/src/main.test.ts index 0242000..645ed8a 100644 --- a/src/main.test.ts +++ b/src/main.test.ts @@ -90,6 +90,15 @@ test("buildMarkdown: summarizes mixed results with failure detail blocks", () => assert.match(md, /\|\s*:fast_forward:\s*\|/); }); +test("buildMarkdown: collapses to a per-class summary when everything passes", () => { + const xml = fs.readFileSync(path.join(fixtures, "passing.xml"), "utf8"); + const cases = parseJunitXml(xml); + const md = buildMarkdown("Unit tests", [{ file: "passing.xml", cases }]); + assert.match(md, /### All tests/); + assert.match(md, /\| Class \| Tests \| Time \|/); + assert.doesNotMatch(md, /:white_check_mark:/); +}); + test("run: end-to-end over fixtures produces summary, outputs, and annotations", async () => { const outputs: Record = {}; const logLines: string[] = []; diff --git a/src/main.ts b/src/main.ts index 567f50b..ea8bfe8 100644 --- a/src/main.ts +++ b/src/main.ts @@ -199,17 +199,42 @@ export function buildMarkdown(title: string, reports: ParsedReport[]): string { } } - lines.push("### All tests", ""); - lines.push("| Status | Test | Time |"); - lines.push("|--------|------|------|"); - for (const c of all) { - const icon = statusIcon(c.status); - const name = c.classname ? `${c.classname} › ${c.name}` : c.name; - lines.push(`| ${icon} | ${escapeMd(name)} | ${c.time.toFixed(3)}s |`); + if (failed === 0) { + lines.push("### All tests", ""); + lines.push("| Class | Tests | Time |"); + lines.push("|-------|-------|------|"); + for (const { classname, count, time } of groupByClass(all)) { + lines.push(`| ${escapeMd(classname)} | ${count} | ${time.toFixed(3)}s |`); + } + } else { + lines.push("### All tests", ""); + lines.push("| Status | Test | Time |"); + lines.push("|--------|------|------|"); + for (const c of all) { + const icon = statusIcon(c.status); + const name = c.classname ? `${c.classname} › ${c.name}` : c.name; + lines.push(`| ${icon} | ${escapeMd(name)} | ${c.time.toFixed(3)}s |`); + } } return lines.join("\n") + "\n"; } +function groupByClass( + cases: TestCase[], +): { classname: string; count: number; time: number }[] { + const groups = new Map(); + for (const c of cases) { + const key = c.classname || c.name; + const g = groups.get(key) ?? { count: 0, time: 0 }; + g.count += 1; + g.time += c.time; + groups.set(key, g); + } + return [...groups.entries()] + .map(([classname, g]) => ({ classname, ...g })) + .sort((a, b) => a.classname.localeCompare(b.classname)); +} + function statusIcon(s: TestCase["status"]): string { switch (s) { case "passed":