From db38adbe12181e8e2f73907f8abc679d41099056 Mon Sep 17 00:00:00 2001 From: kurihada Date: Tue, 24 Mar 2026 18:00:20 +0800 Subject: [PATCH] fix(harness): filter known storybook axe false positives Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- scripts/harness/run-storybook-a11y.mjs | 67 +++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 8 deletions(-) diff --git a/scripts/harness/run-storybook-a11y.mjs b/scripts/harness/run-storybook-a11y.mjs index b7dae1b..90c08ff 100644 --- a/scripts/harness/run-storybook-a11y.mjs +++ b/scripts/harness/run-storybook-a11y.mjs @@ -34,7 +34,7 @@ const stories = [ label: "Date picker playground", prepare: async (page) => { await page.getByRole("combobox", { name: "Launch date" }).click(); - await page.getByRole("grid").waitFor({ state: "visible" }); + await page.getByRole("dialog", { name: "Launch date calendar" }).waitFor({ state: "visible" }); } }, { @@ -96,8 +96,8 @@ async function gotoStory(page, story) { await page.waitForLoadState("domcontentloaded"); await page.waitForFunction( (storyId) => { - const title = document.title.toLowerCase(); - const root = document.getElementById("storybook-root"); + const title = globalThis.document.title.toLowerCase(); + const root = globalThis.document.getElementById("storybook-root"); return ( title.includes(String(storyId).toLowerCase()) || @@ -125,12 +125,63 @@ function summarizeResults(results) { })); } +function isKnownIncompleteFalsePositive(result, node) { + const failureSummary = node.failureSummary ?? ""; + + return ( + (result.id === "aria-valid-attr-value" && + failureSummary.includes( + "Unable to determine if aria-controls referenced ID exists on the page while using aria-haspopup" + )) || + (result.id === "color-contrast" && + (failureSummary.includes("Could not parse color string") || + failureSummary.includes("Axe encountered an error; test the page for this type of problem manually"))) + ); +} + +function summarizeIncompleteResults(results) { + return results + .map((result) => { + const nodes = result.nodes + .filter((node) => !isKnownIncompleteFalsePositive(result, node)) + .map((node) => ({ + failureSummary: node.failureSummary, + html: node.html, + target: node.target + })); + + if (nodes.length === 0) { + return null; + } + + return { + description: result.description, + help: result.help, + helpUrl: result.helpUrl, + id: result.id, + impact: result.impact, + nodeCount: nodes.length, + nodes + }; + }) + .filter(Boolean); +} + async function runAxe(page) { return page.evaluate(async () => { - const root = document.getElementById("storybook-root") ?? document.body; - return window.axe.run(root, { - resultTypes: ["violations", "incomplete"] - }); + return globalThis.window.axe.run( + { + exclude: [ + ["#storybook-root[aria-hidden='true']"], + ["#storybook-root[data-aria-hidden='true']"], + ["[data-radix-focus-guard]"] + ], + include: [["body"]] + }, + { + resultTypes: ["violations", "incomplete"] + } + ); }); } @@ -170,7 +221,7 @@ async function main() { const result = await runAxe(page); const violations = summarizeResults(result.violations); - const incomplete = summarizeResults(result.incomplete); + const incomplete = summarizeIncompleteResults(result.incomplete); report.violationCount += violations.length; report.incompleteCount += incomplete.length;