Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/detect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const FRAMEWORK_NAMES: Record<FrameworkId, string> = {
playwright: "Playwright",
"go-test": "Go test",
"cargo-test": "Cargo test",
tap: "TAP",
node: "Node/npm generic",
unknown: "Unknown"
};
Expand Down Expand Up @@ -76,6 +77,10 @@ export function detectFramework(command: string[] = [], output = ""): DetectedFr
add("cargo-test", 3, "output contains Rust failure references");
}

if (/^TAP version \d+/m.test(output)) add("tap", 4, "output contains TAP version header");
if (/^not ok \d+\b/m.test(output)) add("tap", 4, "output contains TAP failing test lines");
if (/^# (?:tests|pass|fail|skip|todo)\b/im.test(output)) add("tap", 2, "output contains TAP summary lines");

if (/\b(?:npm|pnpm|yarn|bun)\s+(?:run\s+)?(?:test|unit|e2e|spec|check)\b/.test(commandText)) {
add("node", 2, "command is a Node package script");
}
Expand Down
12 changes: 9 additions & 3 deletions src/extract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ function findAnchorLines(lines: string[], framework: DetectedFramework): number[
pytest: [/=+\s*FAILURES\s*=+/i, /^_{3,}\s+.+\s+_{3,}$/],
playwright: [/^\s*\d+\)\s+\[.*\]\s+.+\s+›\s+/, /Error:\s+expect\(.+\)\./],
"go-test": [/^FAIL\t/, /^\s+.+\.go:\d+:/],
"cargo-test": [/^----\s+.+\s+stdout\s+----/, /^failures:\s*$/i]
"cargo-test": [/^----\s+.+\s+stdout\s+----/, /^failures:\s*$/i],
tap: [/^not ok \d+\b/, /^\s{2,}---\s*$/, /^\s{2,}\.\.\.\s*$/]
};

const allPatterns = [...patterns, ...(frameworkPatterns[framework.id] ?? [])];
Expand Down Expand Up @@ -142,7 +143,9 @@ function collectAssertionMessages(lines: string[]): string[] {
/Error:\s+expect\(.*/i,
/^\s*E\s+AssertionError:.*/i,
/thread '.*' panicked at.*/i,
/\bpanic:.*/i
/\bpanic:.*/i,
/^not ok \d+\b.*/i,
/^\s{4}(?:message|severity|operator|expected|actual):\s+.*/i
];

for (const line of lines) {
Expand Down Expand Up @@ -243,7 +246,10 @@ function collectSummaryLines(lines: string[]): string[] {
/^FAIL\s+.+/i,
/^test result: FAILED\./i,
/^error: test failed/i,
/^failures:\s*$/i
/^failures:\s*$/i,
/^TAP version \d+/i,
/^not ok \d+\b/i,
/^# (?:tests|pass|fail|skip|todo)\b/i
];

return uniqueStrings(
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export type FrameworkId =
| "playwright"
| "go-test"
| "cargo-test"
| "tap"
| "node"
| "unknown";

Expand Down
6 changes: 6 additions & 0 deletions test/detect.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,10 @@ describe("detectFramework", () => {
expect(framework.id).toBe("pytest");
expect(framework.confidence).not.toBe("low");
});

it("detects TAP from version header and failing test lines", () => {
const framework = detectFramework([], "TAP version 13\nnot ok 2 should validate payload\n# fail 1");
expect(framework.id).toBe("tap");
expect(framework.confidence).toBe("high");
});
});
30 changes: 30 additions & 0 deletions test/extract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,27 @@ const playwrightLog = `
attachment #3: trace (application/zip) - test-results/login-works-chromium/trace.zip
`;

const tapLog = `
TAP version 13
# Subtest: api validation
ok 1 accepts valid payload
not ok 2 rejects missing user id
---
message: "expected validation error"
severity: fail
operator: equal
expected: true
actual: false
...
ok 3 accepts optional metadata
not ok 1 api validation
ok 2 config loads
1..2
# tests 2
# pass 1
# fail 1
`;

describe("extractFailures", () => {
it("extracts blocks, assertion messages, summaries, and file references", () => {
const extraction = extractFailures(vitestLog, detectFramework(["vitest"], vitestLog));
Expand All @@ -41,4 +62,13 @@ describe("extractFailures", () => {
expect.objectContaining({ kind: "trace", path: "test-results/login-works-chromium/trace.zip" })
]);
});

it("extracts TAP failing tests, diagnostics, and summary counts", () => {
const extraction = extractFailures(tapLog, detectFramework([], tapLog));

expect(extraction.blocks[0]?.text).toContain("not ok 2 rejects missing user id");
expect(extraction.assertionMessages).toContain("not ok 2 rejects missing user id");
expect(extraction.assertionMessages).toContain("message: \"expected validation error\"");
expect(extraction.summaryLines).toEqual(expect.arrayContaining(["TAP version 13", "# tests 2", "# fail 1"]));
});
});