mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-09 09:26:14 -08:00
* add scoped coverage reporting * fix coverage review feedback --------- Co-authored-by: 4gray <fourgray@proton.me>
154 lines
4.2 KiB
JavaScript
154 lines
4.2 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
import { spawnSync } from 'node:child_process';
|
|
import {
|
|
existsSync,
|
|
mkdirSync,
|
|
readFileSync,
|
|
readdirSync,
|
|
statSync,
|
|
writeFileSync,
|
|
} from 'node:fs';
|
|
import path from 'node:path';
|
|
import process from 'node:process';
|
|
|
|
console.warn(
|
|
'Experimental: browser V8 coverage for existing Playwright specs requires Chromium and is intended for local investigation, not CI gating.'
|
|
);
|
|
|
|
const workspaceRoot = process.cwd();
|
|
|
|
const result = spawnSync(
|
|
'pnpm',
|
|
[
|
|
'nx',
|
|
'run',
|
|
'web-e2e:e2e',
|
|
'--',
|
|
'--project=chromium',
|
|
],
|
|
{
|
|
cwd: process.cwd(),
|
|
env: {
|
|
...process.env,
|
|
IPTVNATOR_E2E_V8_COVERAGE: '1',
|
|
},
|
|
stdio: 'inherit',
|
|
}
|
|
);
|
|
|
|
if (result.status !== 0) {
|
|
process.exit(result.status ?? 1);
|
|
}
|
|
|
|
function listFiles(directory, predicate) {
|
|
if (!existsSync(directory)) {
|
|
return [];
|
|
}
|
|
|
|
const files = [];
|
|
for (const entry of readdirSync(directory)) {
|
|
const fullPath = path.join(directory, entry);
|
|
const stats = statSync(fullPath);
|
|
if (stats.isDirectory()) {
|
|
files.push(...listFiles(fullPath, predicate));
|
|
} else if (predicate(fullPath)) {
|
|
files.push(fullPath);
|
|
}
|
|
}
|
|
return files;
|
|
}
|
|
|
|
function mergeRanges(ranges) {
|
|
const sorted = ranges
|
|
.filter((range) => range.count > 0)
|
|
.map((range) => [range.startOffset, range.endOffset])
|
|
.sort(([leftStart], [rightStart]) => leftStart - rightStart);
|
|
const merged = [];
|
|
|
|
for (const [start, end] of sorted) {
|
|
const previous = merged.at(-1);
|
|
if (previous && start <= previous[1]) {
|
|
previous[1] = Math.max(previous[1], end);
|
|
} else {
|
|
merged.push([start, end]);
|
|
}
|
|
}
|
|
|
|
return merged;
|
|
}
|
|
|
|
function summarizeJsEntry(entry) {
|
|
const sourceLength = entry.source?.length ?? 0;
|
|
const ranges = mergeRanges((entry.functions ?? []).flatMap((fn) => fn.ranges ?? []));
|
|
const usedBytes = ranges.reduce((total, [start, end]) => total + Math.max(0, end - start), 0);
|
|
return {
|
|
url: entry.url,
|
|
sourceLength,
|
|
usedBytes,
|
|
pct: sourceLength > 0 ? Math.round((usedBytes / sourceLength) * 10000) / 100 : 0,
|
|
};
|
|
}
|
|
|
|
const rawCoverageRoot = path.join(workspaceRoot, 'apps/web-e2e/test-results');
|
|
const rawFiles = listFiles(rawCoverageRoot, (file) =>
|
|
file.endsWith('.json') && file.includes(`${path.sep}v8-coverage${path.sep}`)
|
|
);
|
|
const entriesByUrl = new Map();
|
|
|
|
for (const file of rawFiles) {
|
|
const raw = JSON.parse(readFileSync(file, 'utf8'));
|
|
for (const entry of raw.js ?? []) {
|
|
if (!entry.url || entry.url.startsWith('extensions::')) {
|
|
continue;
|
|
}
|
|
const summary = summarizeJsEntry(entry);
|
|
const existing = entriesByUrl.get(summary.url);
|
|
if (!existing || summary.usedBytes > existing.usedBytes) {
|
|
entriesByUrl.set(summary.url, summary);
|
|
}
|
|
}
|
|
}
|
|
|
|
const summaries = Array.from(entriesByUrl.values()).sort((left, right) =>
|
|
left.url.localeCompare(right.url)
|
|
);
|
|
const outputDir = path.join(workspaceRoot, 'coverage/e2e-v8/web');
|
|
mkdirSync(outputDir, { recursive: true });
|
|
writeFileSync(
|
|
path.join(outputDir, 'summary.json'),
|
|
`${JSON.stringify(
|
|
{
|
|
rawCoverageRoot: path.relative(workspaceRoot, rawCoverageRoot),
|
|
rawFiles: rawFiles.length,
|
|
scripts: summaries,
|
|
},
|
|
null,
|
|
4
|
|
)}\n`
|
|
);
|
|
writeFileSync(
|
|
path.join(outputDir, 'summary.md'),
|
|
`# Web E2E V8 Coverage
|
|
|
|
Raw coverage root: ${path.relative(workspaceRoot, rawCoverageRoot)}
|
|
|
|
Raw coverage files: ${rawFiles.length}
|
|
|
|
This experimental report is Chromium/V8 byte coverage from Playwright's browser
|
|
coverage API. Use it for local investigation; rely on Tier A unit coverage and
|
|
E2E semantic coverage for CI gates.
|
|
|
|
| Script URL | Used bytes | Source bytes | Byte coverage |
|
|
| --- | ---: | ---: | ---: |
|
|
${summaries
|
|
.map(
|
|
(entry) =>
|
|
`| ${entry.url || '_anonymous_'} | ${entry.usedBytes} | ${entry.sourceLength} | ${entry.pct}% |`
|
|
)
|
|
.join('\n') || '| _none_ | 0 | 0 | 0% |'}
|
|
`
|
|
);
|
|
|
|
console.log(`Wrote coverage/e2e-v8/web/summary.md from ${rawFiles.length} raw coverage files.`);
|