52 lines
1.5 KiB
TypeScript
52 lines
1.5 KiB
TypeScript
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
import path from 'node:path';
|
|
import type { FullResult, Reporter } from '@playwright/test/reporter';
|
|
import { COVERAGE_DIR } from '../paths';
|
|
import { computeCoverage, formatCoverage } from './report';
|
|
|
|
/**
|
|
* Enforces endpoint coverage at the end of the run.
|
|
*
|
|
* API_COVERAGE=enforce (default) fail a passing run if any operation is uncovered
|
|
* API_COVERAGE=report print the summary only
|
|
* API_COVERAGE=off skip entirely
|
|
*/
|
|
class ApiCoverageReporter implements Reporter {
|
|
printsToStdio() {
|
|
return true;
|
|
}
|
|
|
|
async onEnd(result: FullResult) {
|
|
const mode = process.env.API_COVERAGE ?? 'enforce';
|
|
|
|
if (mode === 'off') {
|
|
return;
|
|
}
|
|
|
|
const summary = computeCoverage();
|
|
|
|
mkdirSync(COVERAGE_DIR, { recursive: true });
|
|
writeFileSync(path.join(COVERAGE_DIR, 'summary.json'), JSON.stringify(summary, null, 2));
|
|
|
|
console.log(formatCoverage(summary));
|
|
|
|
if (mode !== 'enforce' || summary.uncovered.length === 0) {
|
|
return;
|
|
}
|
|
|
|
if (result.status !== 'passed') {
|
|
console.log('API coverage not enforced because the run did not pass.\n');
|
|
return;
|
|
}
|
|
|
|
console.log(
|
|
`API coverage check failed: ${summary.uncovered.length} operation(s) were never called. ` +
|
|
'Add a spec that exercises them or an entry in tests/api/coverage/allowlist.ts.\n',
|
|
);
|
|
|
|
return { status: 'failed' as const };
|
|
}
|
|
}
|
|
|
|
export default ApiCoverageReporter;
|