| 5 | import type { Counts, TestResult, TestResults } from './results.ts' |
| 6 | |
| 7 | export class FilesReporter implements Reporter { |
| 8 | #failures: { suiteName: string; name: string; error: TestResult['error'] }[] = [] |
| 9 | #files = new Set<string>() |
| 10 | #suites = new Set<string>() |
| 11 | |
| 12 | onSectionStart(_label: string) {} |
| 13 | |
| 14 | onResult(results: TestResults, env?: string) { |
| 15 | for (let test of results.tests) { |
| 16 | if (test.filePath) this.#files.add(test.filePath) |
| 17 | if (test.suiteName) this.#suites.add(test.suiteName) |
| 18 | } |
| 19 | |
| 20 | let filePath = results.tests[0]?.filePath |
| 21 | let fileName = filePath ? path.relative(process.cwd(), filePath) : '(unknown)' |
| 22 | let envLabel = env ? ` ${colors.dim(`[${env}]`)}` : '' |
| 23 | let totalDuration = results.tests.reduce((sum, t) => sum + t.duration, 0) |
| 24 | let hasFailed = results.tests.some((t) => t.status === 'failed') |
| 25 | |
| 26 | let fileColor = hasFailed ? colors.red : colors.green |
| 27 | let duration = hasFailed ? '' : ` (${totalDuration.toFixed(2)}ms)` |
| 28 | console.log(`${colors.dim('▶')} ${fileColor(fileName)}${duration}${envLabel}`) |
| 29 | |
| 30 | if (hasFailed) { |
| 31 | // Print failing tests with suite/test nesting using > separators |
| 32 | for (let test of results.tests) { |
| 33 | if (test.status !== 'failed') continue |
| 34 | let fullName = test.name ? `${test.suiteName} > ${test.name}` : test.suiteName |
| 35 | console.log(` ${colors.red('✗')} ${fullName}`) |
| 36 | if (test.error) { |
| 37 | console.log(` ${colors.red(`Error: ${test.error.message}`)}`) |
| 38 | if (test.error.stack) { |
| 39 | let stack = test.error.stack |
| 40 | .split('\n') |
| 41 | .map((line) => normalizeLine(line)) |
| 42 | .join('\n') |
| 43 | console.log(` ${stack.split('\n').slice(1, 5).join(`\n `)}`) |
| 44 | } |
| 45 | } |
| 46 | this.#failures.push({ suiteName: test.suiteName, name: test.name, error: test.error }) |
| 47 | } |
| 48 | } |
| 49 | |
| 50 | for (let test of results.tests) { |
| 51 | if ((test.status !== 'skipped' && test.status !== 'todo') || !test.reason) continue |
| 52 | let fullName = test.name ? `${test.suiteName} > ${test.name}` : test.suiteName |
| 53 | let marker = |
| 54 | test.status === 'skipped' |
| 55 | ? `${colors.dim('↓')} ${colors.dim(`${fullName} # skipped: ${test.reason}`)}` |
| 56 | : `${colors.yellow('…')} ${colors.yellow(`${fullName} # todo: ${test.reason}`)}` |
| 57 | console.log(` ${marker}`) |
| 58 | } |
| 59 | } |
| 60 | |
| 61 | onSummary(counts: Counts, durationMs: number) { |
| 62 | if (this.#failures.length > 0) { |
| 63 | console.log() |
| 64 | console.log(colors.red('Failed tests:')) |
nothing calls this directly
no outgoing calls
no test coverage detected
searching dependent graphs…