#!/usr/bin/env node // Turns the per-test istanbul samples in .nyc_output into coverage/lcov.info for // SonarQube to import. // // The check below is the point of this script existing rather than the pipeline // just calling `nyc report`. The failure mode that matters here is not an error, // it is silence: if the dev server was started without COVERAGE, every test still // passes and every page reports no instrumentation, so .nyc_output stays empty // and nyc cheerfully writes a valid, empty report. SonarQube then shows frontend // coverage at 0%, which reads as "the tests stopped covering things" rather than // "collection was never switched on". // // This project has been bitten twice by tools succeeding while measuring nothing // — SonarQube skipping the entire frontend and still exiting EXECUTION SUCCESS // (#67), and an ESLint matcher silently matching no files (#60). Coverage has the // same shape, so it fails loudly instead. const { existsSync, readdirSync } = require('fs'); const { execFileSync } = require('child_process'); const path = require('path'); const root = path.resolve(__dirname, '..'); const nycOutput = path.join(root, '.nyc_output'); const samples = existsSync(nycOutput) ? readdirSync(nycOutput).filter((f) => f.endsWith('.json')) : []; if (samples.length === 0) { console.error('No coverage was collected — .nyc_output holds no samples.'); console.error(''); console.error('The tests may well have passed; that is the problem. Coverage is only'); console.error('gathered when the dev server is instrumented, which happens when COVERAGE=true'); console.error('reaches it. Run the suite with `npm run test:e2e:cov` rather than `npm run test:e2e`.'); console.error(''); console.error('If it was run that way, check that Playwright started its own dev server rather'); console.error('than reusing one you already had open — an uninstrumented server collects nothing.'); process.exit(1); } console.log(`Merging ${samples.length} coverage sample(s) from ${samples.length} test(s).`); // Invoked through nyc's own entry point rather than npx: on Windows, spawning // the npx.cmd shim fails with EINVAL under current Node unless a shell is used, // and running the JS directly avoids needing one at all. execFileSync( process.execPath, [ path.join(root, 'node_modules', 'nyc', 'bin', 'nyc.js'), 'report', '--reporter=lcov', '--reporter=text-summary', '--report-dir', 'coverage' ], { cwd: root, stdio: 'inherit' } ); const lcov = path.join(root, 'coverage', 'lcov.info'); if (!existsSync(lcov)) { console.error(`nyc reported success but ${lcov} was not written.`); process.exit(1); } console.log(`Wrote ${path.relative(root, lcov)}`);