Skip to content

Commit 2dfbc2e

Browse files
committed
Enhance coverage reporting with type coverage and summary
Show unified coverage summary with both v8 code coverage and TypeScript type coverage. Calculate cumulative percentage to provide holistic coverage metric. Clean output masks verbose test details and displays only summary statistics.
1 parent e5a2fb9 commit 2dfbc2e

File tree

1 file changed

+116
-0
lines changed

1 file changed

+116
-0
lines changed

scripts/cover.mjs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/**
2+
* @fileoverview Coverage script that runs tests with coverage reporting.
3+
* Masks test output and shows only the coverage summary.
4+
*/
5+
6+
import path from 'node:path'
7+
import { fileURLToPath } from 'node:url'
8+
9+
import { printError, printHeader, printSuccess } from './utils/cli-helpers.mjs'
10+
import { runCommandQuiet } from './utils/run-command.mjs'
11+
12+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
13+
const rootPath = path.join(__dirname, '..')
14+
15+
printHeader('Running Coverage')
16+
17+
// Run vitest with coverage enabled, capturing output
18+
const vitestArgs = [
19+
'exec',
20+
'vitest',
21+
'run',
22+
'--coverage',
23+
...process.argv.slice(2),
24+
]
25+
const typeCoverageArgs = ['exec', 'type-coverage']
26+
27+
try {
28+
const { exitCode, stdout, stderr } = await runCommandQuiet(
29+
'pnpm',
30+
vitestArgs,
31+
{
32+
cwd: rootPath,
33+
},
34+
)
35+
36+
// Run type coverage
37+
const typeCoverageResult = await runCommandQuiet('pnpm', typeCoverageArgs, {
38+
cwd: rootPath,
39+
})
40+
41+
// Combine and clean output - remove ANSI color codes and spinner artifacts
42+
const ansiRegex = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, 'g')
43+
const output = (stdout + stderr)
44+
.replace(ansiRegex, '') // Remove ANSI color codes
45+
.replace(/(?:||)\s*/g, '') // Remove spinner artifacts
46+
.trim()
47+
48+
// Extract test summary (Test Files ... Duration)
49+
const testSummaryMatch = output.match(
50+
/Test Files\s+\d+[^\n]*\n[\s\S]*?Duration\s+[\d.]+m?s[^\n]*/,
51+
)
52+
53+
// Extract coverage summary: header + All files row
54+
// Match from "% Coverage" header through the All files line and closing border
55+
const coverageHeaderMatch = output.match(
56+
/ % Coverage report from v8\n([-|]+)\n([^\n]+)\n\1/,
57+
)
58+
const allFilesMatch = output.match(/All files\s+\|\s+([\d.]+)\s+\|[^\n]*/)
59+
60+
// Extract type coverage percentage
61+
const typeCoverageOutput = (
62+
typeCoverageResult.stdout + typeCoverageResult.stderr
63+
).trim()
64+
const typeCoverageMatch = typeCoverageOutput.match(/\([\d\s/]+\)\s+([\d.]+)%/)
65+
66+
// Display clean output
67+
if (testSummaryMatch) {
68+
console.log()
69+
console.log(testSummaryMatch[0])
70+
console.log()
71+
}
72+
73+
if (coverageHeaderMatch && allFilesMatch) {
74+
console.log(' % Coverage report from v8')
75+
console.log(coverageHeaderMatch[1]) // Top border
76+
console.log(coverageHeaderMatch[2]) // Header row
77+
console.log(coverageHeaderMatch[1]) // Middle border
78+
console.log(allFilesMatch[0]) // All files row
79+
console.log(coverageHeaderMatch[1]) // Bottom border
80+
console.log()
81+
82+
// Display type coverage and cumulative summary
83+
if (typeCoverageMatch) {
84+
const codeCoveragePercent = Number.parseFloat(allFilesMatch[1])
85+
const typeCoveragePercent = Number.parseFloat(typeCoverageMatch[1])
86+
const cumulativePercent = (
87+
(codeCoveragePercent + typeCoveragePercent) /
88+
2
89+
).toFixed(2)
90+
91+
console.log(' Coverage Summary')
92+
console.log(' ───────────────────────────────')
93+
console.log(` Type Coverage: ${typeCoveragePercent.toFixed(2)}%`)
94+
console.log(` Code Coverage: ${codeCoveragePercent.toFixed(2)}%`)
95+
console.log(' ───────────────────────────────')
96+
console.log(` Cumulative: ${cumulativePercent}%`)
97+
console.log()
98+
}
99+
}
100+
101+
if (exitCode === 0) {
102+
printSuccess('Coverage completed successfully')
103+
} else {
104+
printError('Coverage failed')
105+
// Show relevant output on failure for debugging
106+
if (!testSummaryMatch && !coverageMatch) {
107+
console.log('\n--- Output ---')
108+
console.log(output)
109+
}
110+
}
111+
112+
process.exitCode = exitCode
113+
} catch (error) {
114+
printError(`Coverage script failed: ${error.message}`)
115+
process.exitCode = 1
116+
}

0 commit comments

Comments
 (0)