// Minimal test reporter: silent on pass, prints failures in full, ends with one summary line. import type { TestEvent } from 'node:test/reporters'; interface Failure { name: string; reason: string; } export default async function* summaryReporter( source: AsyncIterable, ): AsyncGenerator { const failures: Failure[] = []; let passed = 0, failed = 0, totalMs = 0; for await (const event of source) { // Skip file-level wrapper events (name ends in .ts) — only count individual tests. if ((event.data as { name?: string })?.name?.endsWith('.ts')) continue; if (event.type === 'test:pass') { passed++; totalMs += (event.data as { details?: { duration_ms?: number } }).details?.duration_ms ?? 0; } else if (event.type === 'test:fail') { failed++; totalMs += (event.data as { details?: { duration_ms?: number } }).details?.duration_ms ?? 0; const err = ( event.data as { details?: { error?: { cause?: { message?: string }; message?: string } } } ).details?.error; failures.push({ name: (event.data as { name?: string }).name ?? 'unknown', reason: err?.cause?.message ?? err?.message ?? 'unknown', }); } } if (failures.length) { yield '\nFailed tests:\n'; for (const f of failures) yield ` ❌ ${f.name}\n ${f.reason}\n`; yield '\n'; } const status = failed === 0 ? '✅' : '❌'; const time = (totalMs / 1000).toFixed(2); yield `${status} ${passed + failed} tests: ${passed} passed`; if (failed) yield `, ${failed} failed`; yield ` (${time}s)\n`; }