everywhere-qa 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/report.js ADDED
@@ -0,0 +1,176 @@
1
+ import { copyFile, mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { isAbsolute, join, relative, resolve, sep } from "node:path";
3
+ function escapeHtml(value) {
4
+ return value.replace(/[&<>"']/g, (char) => ({
5
+ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#039;"
6
+ })[char] ?? char);
7
+ }
8
+ function findingCard(item, advisory = false) {
9
+ const label = advisory ? "advisory" : item.severity;
10
+ const note = advisory ? " &middot; does not affect score" : "";
11
+ const page = item.pageId ? `${escapeHtml(item.pageId)} &middot; ` : "";
12
+ return `<article class="finding ${advisory ? "advisory" : item.severity}">
13
+ <div><span class="pill">${label}</span><span>${escapeHtml(item.pillar)}</span></div>
14
+ <h3>${escapeHtml(item.message)}</h3><code>${escapeHtml(item.selector)}</code>
15
+ <p>${escapeHtml(item.remediation)}</p>
16
+ <small>${page}${escapeHtml(item.scenario)} &middot; ${escapeHtml(item.ruleId)}${note}</small>
17
+ </article>`;
18
+ }
19
+ function reportHtml(report) {
20
+ const json = JSON.stringify(report).replaceAll("<", "\\u003c");
21
+ const cards = Object.entries(report.scores)
22
+ .map(([name, score]) => `<div class="score"><span>${escapeHtml(name)}</span><strong>${score}</strong><i style="--score:${score}%"></i></div>`)
23
+ .join("");
24
+ const pageCards = report.pageScores.length > 1 ? `<section class="page-scores">${report.pageScores.map((page) => `<article><span>${escapeHtml(page.pageId)}</span><strong>${page.scores.overall}</strong><small>${escapeHtml(page.pageUrl)}</small></article>`).join("")}</section>` : "";
25
+ const findings = report.findings.length
26
+ ? report.findings.map((item) => findingCard(item)).join("")
27
+ : `<div class="empty">No deterministic findings in the executed scenarios.</div>`;
28
+ const advisories = report.advisories.length
29
+ ? report.advisories.map((item) => findingCard(item, true)).join("")
30
+ : `<div class="empty">No heuristic source advisories.</div>`;
31
+ const scenarios = report.scenarios.map((scenario) => `<article class="scenario">
32
+ <img src="${escapeHtml(scenario.screenshot)}" alt="Evidence screenshot for ${escapeHtml(scenario.label)} on ${escapeHtml(scenario.pageId)}">
33
+ <div><span class="pill ${scenario.status === "passed" ? "pass" : ""}">${escapeHtml(scenario.status)}</span>
34
+ <h3>${escapeHtml(scenario.label)}</h3>
35
+ <p>${escapeHtml(scenario.pageId)} &middot; ${escapeHtml(scenario.locale)} &middot; ${scenario.viewport.width}&times;${scenario.viewport.height} &middot; ${(scenario.durationMs / 1000).toFixed(1)}s</p></div>
36
+ </article>`).join("");
37
+ const verification = report.verification;
38
+ const delta = verification
39
+ ? `<section class="delta"><div><strong>${verification.scoreChange >= 0 ? "+" : ""}${verification.scoreChange}</strong><span>score change</span></div><div><strong>${verification.fixed.length}</strong><span>fixed</span></div><div><strong>${verification.remaining.length}</strong><span>remaining</span></div><div><strong>${verification.introduced.length}</strong><span>introduced</span></div></section>`
40
+ : "";
41
+ const gate = verification && verification.gate.policy !== "off"
42
+ ? `<section class="gate ${verification.gate.status}"><strong>Regression gate: ${escapeHtml(verification.gate.status)}</strong>${verification.gate.reasons.length ? `<ul>${verification.gate.reasons.map((reason) => `<li>${escapeHtml(reason)}</li>`).join("")}</ul>` : `<p>No score drop or newly introduced serious/critical finding.</p>`}</section>`
43
+ : "";
44
+ const proof = verification?.visualComparisons.length
45
+ ? `<h2>Proof of repair</h2><p class="section-copy">The same route and browser profile before and after the repair.</p><section class="proof">${verification.visualComparisons.map((item) => `<article><div class="proof-head"><strong>${escapeHtml(item.pageId)}</strong><span>${escapeHtml(item.label)}</span></div><div class="proof-pair"><figure><figcaption>Before</figcaption><img src="${escapeHtml(item.beforeScreenshot)}" alt="Before repair: ${escapeHtml(item.label)}"></figure><figure><figcaption>Verified</figcaption><img src="${escapeHtml(item.afterScreenshot)}" alt="After repair: ${escapeHtml(item.label)}"></figure></div></article>`).join("")}</section>`
46
+ : "";
47
+ return `<!doctype html>
48
+ <html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Everywhere QA Global Passport</title>
49
+ <style>
50
+ :root{color-scheme:dark;--ink:#f5f1e8;--muted:#aaa9a2;--panel:#181b1a;--line:#323735;--mint:#9ce6bd;--coral:#ff806d;--yellow:#f3cf70}*{box-sizing:border-box}body{margin:0;background:#0d0f0e;color:var(--ink);font:15px/1.55 Inter,ui-sans-serif,system-ui,sans-serif}main{width:min(1120px,92vw);margin:auto;padding:56px 0 80px}header{display:flex;justify-content:space-between;gap:24px;align-items:end;border-bottom:1px solid var(--line);padding-bottom:28px}h1{font-size:clamp(38px,7vw,80px);line-height:.95;letter-spacing:-.06em;margin:8px 0}.eyebrow,.pill{font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--mint)}.tagline,.section-copy{color:var(--muted);max-width:60ch}.scores{display:grid;grid-template-columns:repeat(5,1fr);gap:12px;margin:28px 0}.score,.page-scores article{background:var(--panel);border:1px solid var(--line);border-radius:16px;padding:18px;overflow:hidden}.score span,.page-scores span,.page-scores small{display:block;color:var(--muted)}.score strong,.page-scores strong{font-size:34px}.score i{display:block;height:3px;background:linear-gradient(90deg,var(--mint) var(--score),var(--line) 0);margin-top:12px}.page-scores{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:12px;margin:20px 0}.page-scores small{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.delta{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin:20px 0}.delta div{border:1px solid var(--line);border-radius:16px;padding:18px}.delta strong,.delta span{display:block}.delta strong{font-size:28px;color:var(--mint)}.gate{border:1px solid var(--mint);background:#11271d;border-radius:14px;padding:18px;margin:20px 0}.gate.failed{border-color:var(--coral);background:#2c1714}.gate p,.gate ul{margin-bottom:0;color:var(--muted)}h2{margin:52px 0 8px;font-size:28px}.grid,.scenarios{display:grid;grid-template-columns:repeat(2,1fr);gap:12px}.finding{background:var(--panel);border:1px solid var(--line);border-left:3px solid var(--yellow);border-radius:14px;padding:20px}.finding.critical,.finding.serious{border-left-color:var(--coral)}.finding.advisory{border-left-color:#82a7ff}.finding h3{font-size:18px}.finding code{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--mint)}.finding p,.finding small,.scenario p{color:var(--muted)}.pill{display:inline-block;border:1px solid currentColor;border-radius:99px;padding:3px 8px;margin-right:8px}.pill.pass{color:var(--mint)}.scenario{overflow:hidden;background:var(--panel);border:1px solid var(--line);border-radius:16px}.scenario img{display:block;width:100%;aspect-ratio:16/10;object-fit:cover;object-position:top;background:#090b0a}.scenario div{padding:16px}.scenario h3{margin:12px 0 0}.scenario p{margin:5px 0 0}.proof{display:grid;gap:16px}.proof>article{background:var(--panel);border:1px solid var(--line);border-radius:18px;padding:18px}.proof-head{display:flex;justify-content:space-between;gap:16px;margin-bottom:14px}.proof-head span{color:var(--muted)}.proof-pair{display:grid;grid-template-columns:1fr 1fr;gap:12px}.proof figure{margin:0;min-width:0}.proof figcaption{font-size:11px;text-transform:uppercase;letter-spacing:.12em;color:var(--mint);margin-bottom:7px}.proof img{display:block;width:100%;aspect-ratio:16/10;object-fit:cover;object-position:top;border-radius:10px;border:1px solid var(--line)}.empty{padding:40px;border:1px dashed var(--line);border-radius:14px;color:var(--muted)}footer{margin-top:52px;border-top:1px solid var(--line);padding-top:24px;color:var(--muted)}@media(max-width:760px){header{display:block}.scores{grid-template-columns:repeat(2,1fr)}.grid,.scenarios,.proof-pair{grid-template-columns:1fr}.delta{grid-template-columns:repeat(2,1fr)}}
51
+ </style></head><body><main><header><div><span class="eyebrow">Global Passport &middot; ${escapeHtml(report.runId)}</span><h1>Everywhere<br>QA</h1></div><p class="tagline">${escapeHtml(report.target.url)}<br>${escapeHtml(report.generatedAt)}<br>${report.target.pages.length} page${report.target.pages.length === 1 ? "" : "s"} audited</p></header><section class="scores">${cards}</section>${pageCards}${delta}${gate}${proof}<h2>Executed scenarios</h2><section class="scenarios">${scenarios}</section><h2>Deterministic evidence</h2><section class="grid">${findings}</section><h2>Heuristic advisories</h2><section class="grid">${advisories}</section><footer>${escapeHtml(report.disclaimer)}</footer></main><script type="application/json" id="report-data">${json}</script></body></html>`;
52
+ }
53
+ export async function writeReport(report, outDir) {
54
+ await mkdir(outDir, { recursive: true });
55
+ const jsonPath = join(outDir, "report.json");
56
+ const htmlPath = join(outDir, "report.html");
57
+ await writeFile(jsonPath, `${JSON.stringify(report, null, 2)}\n`, "utf8");
58
+ await writeFile(htmlPath, reportHtml(report), "utf8");
59
+ return { jsonPath, htmlPath };
60
+ }
61
+ function scenarioKind(id) {
62
+ if (id.includes("accessibility"))
63
+ return "accessibility";
64
+ if (id.includes("rtl"))
65
+ return "rtl";
66
+ if (id.includes("pseudo") || id.includes("expansion"))
67
+ return "text-expansion";
68
+ return "resilience";
69
+ }
70
+ function normalizeLegacyFinding(item, targetUrl) {
71
+ if (item.confidence === "heuristic" || item.scenario === "source-review")
72
+ return item;
73
+ const kind = scenarioKind(item.scenario);
74
+ return { ...item, scenario: `target:${kind}`, pageId: "target", pageUrl: targetUrl };
75
+ }
76
+ function normalizeLegacy(value) {
77
+ const targetUrl = value.target.url;
78
+ const scenarios = value.scenarios.map((scenario) => {
79
+ const kind = scenarioKind(scenario.id);
80
+ return { ...scenario, id: `target:${kind}`, kind, pageId: "target", pageUrl: targetUrl };
81
+ });
82
+ const findings = value.findings.map((item) => normalizeLegacyFinding(item, targetUrl));
83
+ const advisories = value.advisories.map((item) => normalizeLegacyFinding(item, targetUrl));
84
+ return {
85
+ ...value,
86
+ schemaVersion: "1.1",
87
+ target: { ...value.target, pages: [{ id: "target", url: targetUrl }], profileSignature: "legacy-default" },
88
+ scenarios,
89
+ findings,
90
+ advisories,
91
+ pageScores: [{ pageId: "target", pageUrl: targetUrl, scores: value.scores }],
92
+ ...(value.verification ? {
93
+ verification: {
94
+ ...value.verification,
95
+ fixed: value.verification.fixed.map((item) => normalizeLegacyFinding(item, targetUrl)),
96
+ remaining: value.verification.remaining.map((item) => normalizeLegacyFinding(item, targetUrl)),
97
+ introduced: value.verification.introduced.map((item) => normalizeLegacyFinding(item, targetUrl)),
98
+ visualComparisons: [],
99
+ gate: { policy: "off", status: "not-run", reasons: [] }
100
+ }
101
+ } : {})
102
+ };
103
+ }
104
+ export async function readReport(path) {
105
+ let parsed;
106
+ try {
107
+ parsed = JSON.parse(await readFile(path, "utf8"));
108
+ }
109
+ catch (error) {
110
+ throw new Error(`Unable to read baseline report at ${path}: ${error instanceof Error ? error.message : String(error)}`);
111
+ }
112
+ if (!parsed || typeof parsed !== "object")
113
+ throw new Error("Baseline is not a report object.");
114
+ const value = parsed;
115
+ if (!["1.0", "1.1"].includes(value.schemaVersion) || typeof value.runId !== "string" || !Array.isArray(value.findings) || !Array.isArray(value.scenarios) || typeof value.scores?.overall !== "number" || typeof value.target?.url !== "string") {
116
+ throw new Error("Baseline report is malformed or uses an unsupported schema version.");
117
+ }
118
+ if (value.schemaVersion === "1.0")
119
+ return normalizeLegacy(value);
120
+ if (!Array.isArray(value.target.pages) || typeof value.target.profileSignature !== "string" || !Array.isArray(value.pageScores)) {
121
+ throw new Error("Baseline report is malformed or uses an unsupported schema version.");
122
+ }
123
+ return value;
124
+ }
125
+ export function assertBaselineCompatible(baseline, config) {
126
+ const beforeIds = baseline.target.pages.map((page) => page.id);
127
+ const afterIds = config.pages.map((page) => page.id);
128
+ if (beforeIds.length !== afterIds.length || beforeIds.some((page, index) => page !== afterIds[index])) {
129
+ throw new Error("Baseline routes do not match this verification run. Reuse the same --url and --config used for the audit.");
130
+ }
131
+ const isSingleTarget = config.isDefault && baseline.target.pages.length === 1 && baseline.target.pages[0]?.id === "target";
132
+ if (!isSingleTarget && baseline.target.pages.some((page, index) => page.url !== config.pages[index]?.url)) {
133
+ throw new Error("Baseline route URLs do not match this verification run. Reuse the same --url and --config used for the audit.");
134
+ }
135
+ if (baseline.target.profileSignature === "legacy-default") {
136
+ if (!config.isDefault)
137
+ throw new Error("A schema 1.0 baseline can only be verified with the built-in single-page profiles.");
138
+ }
139
+ else if (baseline.target.profileSignature !== config.profileSignature) {
140
+ throw new Error("Baseline profiles do not match this verification run. Reuse the same locale, viewport, expansion, and network configuration.");
141
+ }
142
+ }
143
+ function safeScreenshotPath(root, screenshot) {
144
+ const full = resolve(root, screenshot);
145
+ const back = relative(resolve(root), full);
146
+ if (!back || back.startsWith(`..${sep}`) || back === ".." || isAbsolute(back))
147
+ throw new Error(`Baseline contains an unsafe screenshot path: ${screenshot}`);
148
+ return full;
149
+ }
150
+ export async function copyBaselineScreenshots(baseline, current, baselineDir, outDir) {
151
+ const destination = join(outDir, "baseline-screenshots");
152
+ await mkdir(destination, { recursive: true });
153
+ const comparisons = [];
154
+ for (const after of current) {
155
+ const before = baseline.scenarios.find((scenario) => scenario.pageId === after.pageId && scenario.kind === after.kind);
156
+ if (!before)
157
+ throw new Error(`Baseline is missing scenario ${after.kind} for route ${after.pageId}.`);
158
+ const filename = `${after.pageId}--${after.kind}.png`;
159
+ try {
160
+ await copyFile(safeScreenshotPath(baselineDir, before.screenshot), join(destination, filename));
161
+ }
162
+ catch (error) {
163
+ throw new Error(`Unable to preserve baseline screenshot for ${after.pageId}/${after.kind}: ${error instanceof Error ? error.message : String(error)}`);
164
+ }
165
+ comparisons.push({
166
+ scenarioId: after.id,
167
+ kind: after.kind,
168
+ pageId: after.pageId,
169
+ label: after.label,
170
+ beforeScreenshot: `baseline-screenshots/${filename}`,
171
+ afterScreenshot: after.screenshot
172
+ });
173
+ }
174
+ return comparisons;
175
+ }
176
+ //# sourceMappingURL=report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.js","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { AuditOptions, AuditReport } from "./types.js";
2
+ export declare function runAudit(options: AuditOptions): Promise<AuditReport>;
package/dist/runner.js ADDED
@@ -0,0 +1,289 @@
1
+ import { AxeBuilder } from "@axe-core/playwright";
2
+ import { chromium } from "playwright";
3
+ import { mkdir } from "node:fs/promises";
4
+ import { join, resolve } from "node:path";
5
+ import { randomUUID } from "node:crypto";
6
+ import { createRequire } from "node:module";
7
+ import { dedupeFindings, finding } from "./findings.js";
8
+ import { calculatePageScores, compareReports, evaluateGate } from "./scoring.js";
9
+ import { scanSource } from "./source-scan.js";
10
+ import { assertBaselineCompatible, copyBaselineScreenshots, writeReport } from "./report.js";
11
+ const require = createRequire(import.meta.url);
12
+ const playwrightVersion = require("playwright/package.json").version;
13
+ const axeVersion = require("axe-core/package.json").version;
14
+ const ENGINE_VERSION = "0.2.0";
15
+ function impactSeverity(impact) {
16
+ if (impact === "critical")
17
+ return "critical";
18
+ if (impact === "serious")
19
+ return "serious";
20
+ if (impact === "minor")
21
+ return "minor";
22
+ return "moderate";
23
+ }
24
+ function scenarioId(page, kind) {
25
+ return `${page.id}:${kind}`;
26
+ }
27
+ function pageFinding(page, kind, input) {
28
+ return finding({ ...input, scenario: scenarioId(page, kind), pageId: page.id, pageUrl: page.url });
29
+ }
30
+ async function gotoTarget(browserPage, page, timeoutMs) {
31
+ const started = Date.now();
32
+ try {
33
+ const response = await browserPage.goto(page.url, { waitUntil: "load", timeout: timeoutMs });
34
+ if (response && response.status() >= 400)
35
+ throw new Error(`HTTP ${response.status()} ${response.statusText()}`);
36
+ }
37
+ catch (error) {
38
+ throw new Error(`Could not load ${page.url}. Start the app and confirm the URL is reachable. ${error instanceof Error ? error.message : String(error)}`);
39
+ }
40
+ return Date.now() - started;
41
+ }
42
+ async function layoutIssues(page) {
43
+ return page.evaluate(() => {
44
+ const selectorFor = (element) => {
45
+ if (element.id)
46
+ return `#${CSS.escape(element.id)}`;
47
+ const testId = element.getAttribute("data-testid");
48
+ if (testId)
49
+ return `[data-testid="${CSS.escape(testId)}"]`;
50
+ const parts = [];
51
+ let current = element;
52
+ while (current && current !== document.body && parts.length < 4) {
53
+ let part = current.tagName.toLowerCase();
54
+ const parentElement = current.parentElement;
55
+ if (parentElement) {
56
+ const siblings = [...parentElement.children].filter((item) => item.tagName === current?.tagName);
57
+ if (siblings.length > 1)
58
+ part += `:nth-of-type(${siblings.indexOf(current) + 1})`;
59
+ }
60
+ parts.unshift(part);
61
+ current = parentElement;
62
+ }
63
+ return parts.join(" > ") || element.tagName.toLowerCase();
64
+ };
65
+ const width = document.documentElement.clientWidth;
66
+ const issues = [];
67
+ if (document.documentElement.scrollWidth > width + 2) {
68
+ issues.push({ selector: "html", reason: `Document overflows horizontally by ${document.documentElement.scrollWidth - width}px.` });
69
+ }
70
+ const interactive = document.querySelectorAll("a,button,input,select,textarea,[tabindex]");
71
+ for (const element of interactive) {
72
+ const html = element;
73
+ const style = getComputedStyle(html);
74
+ if (style.display === "none" || style.visibility === "hidden")
75
+ continue;
76
+ const rect = html.getBoundingClientRect();
77
+ if (rect.width === 0 || rect.height === 0)
78
+ continue;
79
+ if (rect.left < -1 || rect.right > width + 1)
80
+ issues.push({ selector: selectorFor(element), reason: "Interactive control is partially outside the viewport." });
81
+ if (html.scrollWidth > html.clientWidth + 2 || html.scrollHeight > html.clientHeight + 2)
82
+ issues.push({ selector: selectorFor(element), reason: "Interactive control clips its content." });
83
+ if (issues.length >= 15)
84
+ break;
85
+ }
86
+ return issues;
87
+ });
88
+ }
89
+ async function capture(page, screenshotDir, id) {
90
+ const full = join(screenshotDir, `${id}.png`);
91
+ await page.screenshot({ path: full, fullPage: true, animations: "disabled", caret: "initial" });
92
+ return `screenshots/${id}.png`;
93
+ }
94
+ async function close(context) {
95
+ await context.close().catch(() => undefined);
96
+ }
97
+ async function runAccessibility(browser, target, profile, screenshotDir, timeoutMs) {
98
+ const kind = "accessibility";
99
+ const context = await browser.newContext({ locale: profile.locale, viewport: profile.viewport, reducedMotion: "reduce" });
100
+ try {
101
+ const page = await context.newPage();
102
+ const durationMs = await gotoTarget(page, target, timeoutMs);
103
+ const screenshot = await capture(page, screenshotDir, `${target.id}--accessibility`);
104
+ const results = await new AxeBuilder({ page }).analyze();
105
+ const findings = results.violations.map((violation) => pageFinding(target, kind, {
106
+ ruleId: `axe-${violation.id}`,
107
+ pillar: "accessibility",
108
+ severity: impactSeverity(violation.impact),
109
+ selector: String(violation.nodes[0]?.target[0] ?? "document"),
110
+ message: violation.help,
111
+ evidence: screenshot,
112
+ remediation: violation.nodes[0]?.failureSummary ?? violation.description,
113
+ helpUrl: violation.helpUrl
114
+ }));
115
+ const focus = await page.evaluate(() => ({
116
+ candidateCount: [...document.querySelectorAll("a[href],button,input,select,textarea,[tabindex]:not([tabindex='-1'])")].filter((element) => {
117
+ const style = getComputedStyle(element);
118
+ return style.display !== "none" && style.visibility !== "hidden" && !element.hasAttribute("disabled");
119
+ }).length
120
+ }));
121
+ const focused = new Set();
122
+ for (let index = 0; index < Math.min(12, focus.candidateCount + 2); index += 1) {
123
+ await page.keyboard.press("Tab");
124
+ focused.add(await page.evaluate(() => {
125
+ const active = document.activeElement;
126
+ return active?.id || active?.getAttribute("data-testid") || active?.tagName || "none";
127
+ }));
128
+ }
129
+ if (focus.candidateCount > 0 && [...focused].filter((value) => !["BODY", "HTML", "none"].includes(value)).length === 0) {
130
+ findings.push(pageFinding(target, kind, { ruleId: "keyboard-no-focus", pillar: "accessibility", severity: "serious", message: "Keyboard traversal did not reach an interactive element.", evidence: screenshot, remediation: "Use native interactive controls and preserve a visible, logical tab order." }));
131
+ }
132
+ return { scenario: { id: scenarioId(target, kind), kind, label: "Keyboard and assistive technology", pageId: target.id, pageUrl: target.url, locale: profile.locale, viewport: profile.viewport, durationMs, screenshot, status: findings.length ? "completed-with-findings" : "passed" }, findings };
133
+ }
134
+ finally {
135
+ await close(context);
136
+ }
137
+ }
138
+ async function runRtl(browser, target, profile, screenshotDir, timeoutMs) {
139
+ const kind = "rtl";
140
+ const context = await browser.newContext({ locale: profile.locale, timezoneId: profile.timezoneId, viewport: profile.viewport, isMobile: true, deviceScaleFactor: 1 });
141
+ try {
142
+ const page = await context.newPage();
143
+ const durationMs = await gotoTarget(page, target, timeoutMs);
144
+ await page.waitForTimeout(150);
145
+ const declaredDirection = await page.evaluate(() => document.documentElement.getAttribute("dir"));
146
+ await page.evaluate(() => { document.documentElement.dir = "rtl"; document.documentElement.lang = "ar"; });
147
+ await page.waitForTimeout(100);
148
+ const screenshot = await capture(page, screenshotDir, `${target.id}--rtl`);
149
+ const findings = (await layoutIssues(page)).map((issue) => pageFinding(target, kind, { ruleId: "rtl-layout-overflow", pillar: "rtl", severity: issue.selector === "html" ? "serious" : "moderate", selector: issue.selector, message: issue.reason, evidence: screenshot, remediation: `Use fluid sizing and CSS logical properties, then verify this control in a ${profile.viewport.width}px RTL viewport.` }));
150
+ if (declaredDirection !== "rtl")
151
+ findings.push(pageFinding(target, kind, { ruleId: "rtl-direction-incorrect", pillar: "rtl", severity: "minor", selector: "html", message: declaredDirection ? `The page declares dir="${declaredDirection}" for an Arabic browser context.` : "The page does not declare a text direction for an Arabic browser context.", evidence: screenshot, remediation: "Set the html dir attribute from the active locale and update it when the locale changes." }));
152
+ return { scenario: { id: scenarioId(target, kind), kind, label: "Right-to-left mobile", pageId: target.id, pageUrl: target.url, locale: profile.locale, viewport: profile.viewport, durationMs, screenshot, status: findings.length ? "completed-with-findings" : "passed" }, findings };
153
+ }
154
+ finally {
155
+ await close(context);
156
+ }
157
+ }
158
+ async function runPseudo(browser, target, profile, screenshotDir, timeoutMs) {
159
+ const kind = "text-expansion";
160
+ const context = await browser.newContext({ locale: profile.locale === "en-XA" ? "en-US" : profile.locale, viewport: profile.viewport, isMobile: true });
161
+ try {
162
+ const page = await context.newPage();
163
+ const durationMs = await gotoTarget(page, target, timeoutMs);
164
+ const before = new Set((await layoutIssues(page)).map((item) => `${item.selector}|${item.reason}`));
165
+ await page.evaluate((ratio) => {
166
+ const accents = { a: "á", b: "ƀ", c: "ç", d: "đ", e: "é", f: "ƒ", g: "ğ", h: "ħ", i: "í", j: "ĵ", k: "ķ", l: "ľ", m: "ɱ", n: "ñ", o: "ó", p: "þ", q: "ɋ", r: "ř", s: "š", t: "ŧ", u: "ú", v: "ṽ", w: "ŵ", x: "ẋ", y: "ý", z: "ž" };
167
+ const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, { acceptNode(node) {
168
+ const parent = node.parentElement;
169
+ if (!parent || !node.textContent?.trim() || /^(script|style|svg|code|pre|textarea)$/i.test(parent.tagName))
170
+ return NodeFilter.FILTER_REJECT;
171
+ return NodeFilter.FILTER_ACCEPT;
172
+ } });
173
+ const nodes = [];
174
+ while (walker.nextNode())
175
+ nodes.push(walker.currentNode);
176
+ for (const node of nodes) {
177
+ const text = node.textContent ?? "";
178
+ const transformed = [...text].map((char) => {
179
+ const replacement = accents[char.toLowerCase()];
180
+ return replacement ? (char === char.toLowerCase() ? replacement : replacement.toUpperCase()) : char;
181
+ }).join("");
182
+ node.textContent = `[${transformed} ${"~".repeat(Math.max(1, Math.ceil(text.length * ratio)))}]`;
183
+ }
184
+ }, profile.ratio);
185
+ await page.waitForTimeout(100);
186
+ const screenshot = await capture(page, screenshotDir, `${target.id}--text-expansion`);
187
+ const newIssues = (await layoutIssues(page)).filter((item) => !before.has(`${item.selector}|${item.reason}`));
188
+ const percentage = Math.round(profile.ratio * 100);
189
+ const findings = newIssues.map((issue) => pageFinding(target, kind, { ruleId: "text-expansion-layout", pillar: "rtl", severity: issue.selector === "html" ? "serious" : "moderate", selector: issue.selector, message: `${percentage}% text expansion caused a layout failure: ${issue.reason}`, evidence: screenshot, remediation: "Remove fixed text dimensions, allow wrapping, and retest with expanded labels." }));
190
+ return { scenario: { id: scenarioId(target, kind), kind, label: `${percentage}% pseudo-localized text expansion`, pageId: target.id, pageUrl: target.url, locale: profile.locale, viewport: profile.viewport, durationMs, screenshot, status: findings.length ? "completed-with-findings" : "passed" }, findings };
191
+ }
192
+ finally {
193
+ await close(context);
194
+ }
195
+ }
196
+ async function runResilience(browser, target, profile, screenshotDir, timeoutMs) {
197
+ const kind = "resilience";
198
+ const context = await browser.newContext({ locale: profile.locale, timezoneId: profile.timezoneId, viewport: profile.viewport, isMobile: true });
199
+ try {
200
+ const page = await context.newPage();
201
+ const failedRequests = [];
202
+ const consoleErrors = [];
203
+ page.on("requestfailed", (request) => failedRequests.push(request.url()));
204
+ page.on("console", (message) => { if (message.type() === "error")
205
+ consoleErrors.push(message.text()); });
206
+ const session = await context.newCDPSession(page);
207
+ await session.send("Network.enable");
208
+ await session.send("Network.emulateNetworkConditions", {
209
+ offline: false,
210
+ latency: profile.network.latencyMs,
211
+ downloadThroughput: profile.network.downloadKbps * 1024 / 8,
212
+ uploadThroughput: profile.network.uploadKbps * 1024 / 8,
213
+ connectionType: "cellular3g"
214
+ });
215
+ const durationMs = await gotoTarget(page, target, timeoutMs);
216
+ await page.waitForTimeout(200);
217
+ const screenshot = await capture(page, screenshotDir, `${target.id}--resilience`);
218
+ const findings = [];
219
+ if (durationMs > 5_000)
220
+ findings.push(pageFinding(target, kind, { ruleId: "slow-network-load", pillar: "resilience", severity: durationMs > 10_000 ? "serious" : "moderate", selector: "document", message: `The page took ${(durationMs / 1000).toFixed(1)}s to settle on the throttled mobile profile.`, evidence: screenshot, remediation: "Reduce blocking JavaScript and media, split bundles, and render useful content before optional resources." }));
221
+ if (failedRequests.length)
222
+ findings.push(pageFinding(target, kind, { ruleId: "slow-network-request-failure", pillar: "resilience", severity: "serious", selector: "network", message: `${failedRequests.length} request(s) failed on the throttled profile.`, evidence: failedRequests.slice(0, 3).join("\n"), remediation: "Handle transient failures, add retry boundaries, and avoid making core UI depend on optional resources." }));
223
+ if (consoleErrors.length)
224
+ findings.push(pageFinding(target, kind, { ruleId: "runtime-console-error", pillar: "resilience", severity: "moderate", selector: "console", message: `${consoleErrors.length} console error(s) occurred on the localized mobile profile.`, evidence: consoleErrors.slice(0, 3).join("\n"), remediation: "Resolve the reported runtime errors and ensure fallback UI remains usable." }));
225
+ const lang = await page.evaluate(() => document.documentElement.lang);
226
+ if (!lang)
227
+ findings.push(pageFinding(target, kind, { ruleId: "locale-language-missing", pillar: "locale", severity: "moderate", selector: "html", message: "The document language is missing.", evidence: screenshot, remediation: "Set a valid html lang attribute so assistive technology can choose the correct pronunciation rules." }));
228
+ return { scenario: { id: scenarioId(target, kind), kind, label: "Localized constrained mobile", pageId: target.id, pageUrl: target.url, locale: profile.locale, viewport: profile.viewport, durationMs, screenshot, status: findings.length ? "completed-with-findings" : "passed" }, findings };
229
+ }
230
+ finally {
231
+ await close(context);
232
+ }
233
+ }
234
+ export async function runAudit(options) {
235
+ const timeoutMs = options.timeoutMs ?? 30_000;
236
+ const outDir = resolve(options.outDir);
237
+ const screenshotDir = join(outDir, "screenshots");
238
+ if (options.baseline)
239
+ assertBaselineCompatible(options.baseline, options.config);
240
+ await mkdir(screenshotDir, { recursive: true });
241
+ let browser;
242
+ try {
243
+ browser = await chromium.launch({ headless: true });
244
+ }
245
+ catch (error) {
246
+ throw new Error(`Chromium is unavailable. Run "everywhere setup" and retry. ${error instanceof Error ? error.message : String(error)}`);
247
+ }
248
+ try {
249
+ const runs = [];
250
+ for (const target of options.config.pages) {
251
+ runs.push(await runAccessibility(browser, target, options.config.profiles.accessibility, screenshotDir, timeoutMs));
252
+ runs.push(await runRtl(browser, target, options.config.profiles.rtl, screenshotDir, timeoutMs));
253
+ runs.push(await runPseudo(browser, target, options.config.profiles.textExpansion, screenshotDir, timeoutMs));
254
+ runs.push(await runResilience(browser, target, options.config.profiles.resilience, screenshotDir, timeoutMs));
255
+ }
256
+ const browserFindings = dedupeFindings(runs.flatMap((run) => run.findings));
257
+ const sourceFindings = options.repo ? await scanSource(resolve(options.repo)) : [];
258
+ const findings = browserFindings.filter((item) => item.confidence === "deterministic");
259
+ const advisories = dedupeFindings([...browserFindings.filter((item) => item.confidence === "heuristic"), ...sourceFindings]);
260
+ const { scores, pageScores } = calculatePageScores(findings, options.config.pages);
261
+ const report = {
262
+ schemaVersion: "1.1",
263
+ runId: randomUUID().slice(0, 8),
264
+ target: { url: options.url, ...(options.repo ? { repo: resolve(options.repo) } : {}), pages: options.config.pages, profileSignature: options.config.profileSignature },
265
+ generatedAt: new Date().toISOString(),
266
+ tools: { everywhereQa: ENGINE_VERSION, playwright: playwrightVersion, axeCore: axeVersion },
267
+ scenarios: runs.map((run) => run.scenario),
268
+ findings,
269
+ advisories,
270
+ scores,
271
+ pageScores,
272
+ summary: { deterministic: findings.length, advisory: advisories.length, critical: findings.filter((item) => item.severity === "critical").length },
273
+ disclaimer: "Automated evidence is incomplete. Everywhere QA is not a WCAG certification, legal opinion, or translation-quality assessment. Human review remains required."
274
+ };
275
+ if (options.baseline) {
276
+ if (!options.baselineDir)
277
+ throw new Error("Verification requires the baseline report directory.");
278
+ report.verification = compareReports(options.baseline, findings, scores.overall);
279
+ report.verification.visualComparisons = await copyBaselineScreenshots(options.baseline, report.scenarios, options.baselineDir, outDir);
280
+ report.verification.gate = evaluateGate(report.verification, options.gatePolicy ?? "off");
281
+ }
282
+ await writeReport(report, outDir);
283
+ return report;
284
+ }
285
+ finally {
286
+ await browser.close();
287
+ }
288
+ }
289
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { AuditPage, AuditReport, Finding, GatePolicy, GateResult, PageScores, Scores, VerificationDelta } from "./types.js";
2
+ export declare function calculateScores(findings: Finding[]): Scores;
3
+ export declare function calculatePageScores(findings: Finding[], pages: AuditPage[]): {
4
+ scores: Scores;
5
+ pageScores: PageScores[];
6
+ };
7
+ export declare function compareReports(baseline: AuditReport, currentFindings: Finding[], currentOverall: number): VerificationDelta;
8
+ export declare function evaluateGate(verification: VerificationDelta, policy: GatePolicy): GateResult;
@@ -0,0 +1,73 @@
1
+ import { stableSignature } from "./findings.js";
2
+ const weights = {
3
+ accessibility: 0.35,
4
+ rtl: 0.25,
5
+ locale: 0.2,
6
+ resilience: 0.2
7
+ };
8
+ const penalties = { critical: 20, serious: 12, moderate: 7, minor: 3 };
9
+ function overall(scores) {
10
+ return Math.round(scores.accessibility * weights.accessibility +
11
+ scores.rtl * weights.rtl +
12
+ scores.locale * weights.locale +
13
+ scores.resilience * weights.resilience);
14
+ }
15
+ export function calculateScores(findings) {
16
+ const deterministic = findings.filter((item) => item.confidence === "deterministic");
17
+ const pillarScore = (pillar) => {
18
+ const deduction = deterministic
19
+ .filter((item) => item.pillar === pillar)
20
+ .reduce((sum, item) => sum + penalties[item.severity], 0);
21
+ return Math.max(0, 100 - deduction);
22
+ };
23
+ const scores = {
24
+ accessibility: pillarScore("accessibility"),
25
+ rtl: pillarScore("rtl"),
26
+ locale: pillarScore("locale"),
27
+ resilience: pillarScore("resilience")
28
+ };
29
+ return { overall: overall(scores), ...scores };
30
+ }
31
+ export function calculatePageScores(findings, pages) {
32
+ const pageScores = pages.map((page) => ({
33
+ pageId: page.id,
34
+ pageUrl: page.url,
35
+ scores: calculateScores(findings.filter((item) => item.pageId === page.id))
36
+ }));
37
+ const average = (pillar) => Math.round(pageScores.reduce((sum, page) => sum + page.scores[pillar], 0) / pageScores.length);
38
+ const scoresWithoutOverall = {
39
+ accessibility: average("accessibility"),
40
+ rtl: average("rtl"),
41
+ locale: average("locale"),
42
+ resilience: average("resilience")
43
+ };
44
+ return { scores: { overall: overall(scoresWithoutOverall), ...scoresWithoutOverall }, pageScores };
45
+ }
46
+ export function compareReports(baseline, currentFindings, currentOverall) {
47
+ const before = new Map(baseline.findings.map((item) => [stableSignature(item), item]));
48
+ const after = new Map(currentFindings.map((item) => [stableSignature(item), item]));
49
+ const fixed = [...before].filter(([key]) => !after.has(key)).map(([, item]) => item);
50
+ const remaining = [...after].filter(([key]) => before.has(key)).map(([, item]) => item);
51
+ const introduced = [...after].filter(([key]) => !before.has(key)).map(([, item]) => item);
52
+ return {
53
+ baselineRunId: baseline.runId,
54
+ fixed,
55
+ remaining,
56
+ introduced,
57
+ scoreChange: currentOverall - baseline.scores.overall,
58
+ visualComparisons: [],
59
+ gate: { policy: "off", status: "not-run", reasons: [] }
60
+ };
61
+ }
62
+ export function evaluateGate(verification, policy) {
63
+ if (policy === "off")
64
+ return { policy, status: "not-run", reasons: [] };
65
+ const reasons = [];
66
+ if (verification.scoreChange < 0)
67
+ reasons.push(`Overall score decreased by ${Math.abs(verification.scoreChange)} point(s).`);
68
+ const severe = verification.introduced.filter((item) => item.severity === "critical" || item.severity === "serious");
69
+ if (severe.length)
70
+ reasons.push(`${severe.length} new serious or critical deterministic finding(s) were introduced.`);
71
+ return { policy, status: reasons.length ? "failed" : "passed", reasons };
72
+ }
73
+ //# sourceMappingURL=scoring.js.map