chore: initialize from frontend template 4dc033c
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
import { readFile } from "node:fs/promises";
|
||||
|
||||
const evidencePath =
|
||||
"artifacts/tests/browser-capabilities/results.xml";
|
||||
const xml = await readFile(evidencePath, "utf8");
|
||||
const failures: string[] = [];
|
||||
const rootAttributes =
|
||||
/<testsuites\b([^>]*)>/u.exec(xml)?.[1] ?? "";
|
||||
const root = attributes(rootAttributes);
|
||||
|
||||
for (const field of ["failures", "errors", "skipped"] as const) {
|
||||
if (root[field] !== "0") {
|
||||
failures.push(`root ${field} must be zero`);
|
||||
}
|
||||
}
|
||||
if (!positiveInteger(root.tests)) {
|
||||
failures.push("root tests must be positive");
|
||||
}
|
||||
|
||||
const casesByEngine = new Map<string, Set<string>>();
|
||||
for (const match of xml.matchAll(
|
||||
/<testsuite\b([^>]*)>([\s\S]*?)<\/testsuite>/gu,
|
||||
)) {
|
||||
const suite = attributes(match[1] ?? "");
|
||||
const engine = suite.hostname;
|
||||
if (!engine) continue;
|
||||
const cases =
|
||||
casesByEngine.get(engine) ?? new Set<string>();
|
||||
for (const testCase of (match[2] ?? "").matchAll(
|
||||
/<testcase\b([^>]*)>/gu,
|
||||
)) {
|
||||
const data = attributes(testCase[1] ?? "");
|
||||
if (data.classname && data.name) {
|
||||
cases.add(`${data.classname}::${data.name}`);
|
||||
}
|
||||
}
|
||||
casesByEngine.set(engine, cases);
|
||||
}
|
||||
|
||||
const expectedEngines = ["chromium", "firefox", "webkit"] as const;
|
||||
const baseline = casesByEngine.get(expectedEngines[0]);
|
||||
for (const engine of expectedEngines) {
|
||||
const cases = casesByEngine.get(engine);
|
||||
if (!cases || cases.size === 0) {
|
||||
failures.push(`${engine} has no executed browser-capability cases`);
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
baseline &&
|
||||
(cases.size !== baseline.size ||
|
||||
[...baseline].some((testCase) => !cases.has(testCase)))
|
||||
) {
|
||||
failures.push(`${engine} case set differs from chromium`);
|
||||
}
|
||||
}
|
||||
for (const engine of casesByEngine.keys()) {
|
||||
if (!expectedEngines.includes(engine as (typeof expectedEngines)[number])) {
|
||||
failures.push(`unexpected browser project ${engine}`);
|
||||
}
|
||||
}
|
||||
if (/<skipped\b/u.test(xml) || /<failure\b/u.test(xml)) {
|
||||
failures.push("browser-capability evidence contains skipped/failure nodes");
|
||||
}
|
||||
|
||||
if (failures.length > 0) {
|
||||
process.stderr.write(
|
||||
`Browser capability evidence failed:\n- ${failures.join("\n- ")}\n`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
process.stdout.write(
|
||||
`Browser capability evidence: PASS (${baseline?.size ?? 0} cases x ${expectedEngines.length} engines, zero skipped)\n`,
|
||||
);
|
||||
|
||||
function attributes(source: string): Record<string, string> {
|
||||
return Object.fromEntries(
|
||||
[...source.matchAll(/([A-Za-z][A-Za-z0-9_-]*)="([^"]*)"/gu)].map(
|
||||
(match) => [match[1] ?? "", match[2] ?? ""],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function positiveInteger(value: string | undefined): boolean {
|
||||
return (
|
||||
typeof value === "string" &&
|
||||
/^\d+$/u.test(value) &&
|
||||
Number(value) > 0
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user