@motion-proto/live-tokens 0.97.0 → 0.98.2
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/.claude/skills/live-tokens-check-compliance/SKILL.md +5 -0
- package/.claude/skills/live-tokens-create-component/SKILL.md +4 -3
- package/.claude/skills/live-tokens-create-component/references/contract-tests.md +71 -11
- package/CHANGELOG.md +272 -0
- package/README.md +2 -2
- package/bin/check-component.mjs +8 -0
- package/bin/check-page.mjs +6 -0
- package/bin/cli.mjs +36 -6
- package/bin/contractRunner.mjs +142 -17
- package/bin/lib/findings.mjs +20 -4
- package/bin/rules/a11y.mjs +53 -0
- package/dist-plugin/{chunk-A4TBP4MM.js → chunk-SLICH4IH.js} +2 -2
- package/dist-plugin/index.cjs +148 -3
- package/dist-plugin/index.js +250 -105
- package/dist-plugin/setColors/index.js +1 -1
- package/package.json +47 -2
- package/src/demo/LabeledSelect.svelte +1 -1
- package/src/editor/bootstrap.ts +3 -1
- package/src/editor/component-editor/BadgeEditor.svelte +13 -0
- package/src/editor/component-editor/ButtonEditor.svelte +33 -2
- package/src/editor/component-editor/CalloutEditor.svelte +13 -0
- package/src/editor/component-editor/CardEditor.svelte +12 -2
- package/src/editor/component-editor/CodeSnippetEditor.svelte +11 -1
- package/src/editor/component-editor/CollapsibleSectionEditor.svelte +18 -1
- package/src/editor/component-editor/CornerBadgeEditor.svelte +13 -0
- package/src/editor/component-editor/DialogEditor.svelte +15 -0
- package/src/editor/component-editor/IconButtonEditor.svelte +29 -1
- package/src/editor/component-editor/ImageEditor.svelte +20 -9
- package/src/editor/component-editor/ImageLightboxEditor.svelte +44 -0
- package/src/editor/component-editor/InlineEditActionsEditor.svelte +13 -1
- package/src/editor/component-editor/InputEditor.svelte +23 -0
- package/src/editor/component-editor/MenuSelectEditor.svelte +27 -1
- package/src/editor/component-editor/NotificationEditor.svelte +15 -0
- package/src/editor/component-editor/PanelEditor.svelte +1 -1
- package/src/editor/component-editor/ProgressBarEditor.svelte +13 -0
- package/src/editor/component-editor/RadioButtonEditor.svelte +20 -1
- package/src/editor/component-editor/SectionDividerEditor.svelte +41 -13
- package/src/editor/component-editor/SegmentedControlEditor.svelte +29 -1
- package/src/editor/component-editor/SideNavigationEditor.svelte +40 -1
- package/src/editor/component-editor/SlidePagerEditor.svelte +20 -0
- package/src/editor/component-editor/SliderEditor.svelte +33 -0
- package/src/editor/component-editor/TabBarEditor.svelte +23 -2
- package/src/editor/component-editor/TableEditor.svelte +14 -0
- package/src/editor/component-editor/ToggleEditor.svelte +17 -0
- package/src/editor/component-editor/TooltipEditor.svelte +11 -2
- package/src/editor/component-editor/VideoFrameEditor.svelte +8 -0
- package/src/editor/component-editor/VideoLightboxEditor.svelte +10 -0
- package/src/editor/component-editor/registration.ts +2 -1
- package/src/editor/component-editor/registry.ts +69 -27
- package/src/editor/component-editor/scaffolding/DemoHeader.svelte +2 -0
- package/src/editor/component-editor/scaffolding/StateBlock.svelte +19 -8
- package/src/editor/component-editor/scaffolding/TokenLayout.svelte +3 -0
- package/src/editor/component-editor/scaffolding/VariantGroup.svelte +70 -3
- package/src/editor/component-editor/scaffolding/editorContext.ts +7 -0
- package/src/editor/core/a11y/a11yReport.ts +101 -0
- package/src/editor/core/a11y/a11yReportTypes.ts +69 -0
- package/src/editor/core/a11y/contrastFailures.ts +22 -0
- package/src/editor/core/components/contrastPairRegistry.ts +18 -0
- package/src/editor/core/palettes/contrast.ts +44 -0
- package/src/editor/core/palettes/contrastPairs.ts +79 -0
- package/src/editor/core/palettes/resolveColor.ts +77 -0
- package/src/editor/core/store/editorStore.ts +4 -0
- package/src/editor/core/store/editorViewStore.ts +2 -2
- package/src/editor/core/store/unsavedEdits.ts +10 -0
- package/src/editor/core/themes/liveStateStream.ts +7 -1
- package/src/editor/core/themes/parsers/gradient.ts +5 -2
- package/src/editor/docs/chapters.ts +1 -0
- package/src/editor/docs/content/accessibility.md +362 -0
- package/src/editor/docs/content/editing-tokens.md +28 -1
- package/src/editor/docs/content.generated.ts +2 -1
- package/src/editor/pages/ComponentEditorPage.svelte +10 -1
- package/src/editor/pages/EditorShell.svelte +96 -10
- package/src/editor/skill-atlas/skillSources.generated.ts +3 -3
- package/src/editor/skill-atlas/trees/check-compliance.ts +3 -3
- package/src/editor/skill-atlas/trees/create-component.ts +12 -12
- package/src/editor/ui/EditorViewSwitcher.svelte +15 -2
- package/src/editor/ui/GradientEditor.svelte +9 -1
- package/src/editor/ui/ThemePanel.svelte +4 -4
- package/src/editor/ui/UIContrastReadout.svelte +186 -0
- package/src/editor/ui/UIInfoPopover.svelte +16 -2
- package/src/editor/ui/UIPaletteSelector.svelte +9 -0
- package/src/editor/ui/UITokenSelector.svelte +26 -2
- package/src/editor/ui/a11y/A11yComponentIssues.svelte +103 -0
- package/src/editor/ui/a11y/A11yFindingRow.svelte +57 -0
- package/src/editor/ui/a11y/A11yIssueRow.svelte +121 -0
- package/src/editor/ui/a11y/A11yOverview.svelte +242 -0
- package/src/editor/ui/a11y/A11yPanel.svelte +128 -0
- package/src/editor/ui/a11y/A11yPropertyMark.svelte +84 -0
- package/src/editor/ui/a11y/A11yRailMark.svelte +34 -0
- package/src/editor/ui/a11y/AccessibilityTab.svelte +334 -0
- package/src/editor/ui/a11y/a11yView.ts +218 -0
- package/src/editor/ui/contrastReadout.ts +45 -0
- package/src/live-tokens/data/themes/autumn.json +20 -20
- package/src/live-tokens/data/themes/halloween.json +20 -20
- package/src/live-tokens/data/themes/midnight-study.json +20 -20
- package/src/live-tokens/data/themes/ocean.json +20 -20
- package/src/live-tokens/data/themes/royal-velvet.json +20 -20
- package/src/live-tokens/data/themes/sketchy.json +20 -20
- package/src/live-tokens/data/themes/spring-meadow.json +20 -20
- package/src/live-tokens/data/themes/sunset.json +20 -20
- package/src/system/components/Badge.svelte +3 -3
- package/src/system/components/Button.svelte +9 -3
- package/src/system/components/CodeSnippet.svelte +8 -1
- package/src/system/components/CornerBadge.svelte +3 -3
- package/src/system/components/Dialog.svelte +43 -14
- package/src/system/components/IconButton.svelte +1 -1
- package/src/system/components/Image.svelte +7 -0
- package/src/system/components/ImageLightbox.svelte +18 -25
- package/src/system/components/Input.svelte +8 -1
- package/src/system/components/MenuSelect.svelte +30 -2
- package/src/system/components/Notification.svelte +1 -1
- package/src/system/components/ProgressBar.svelte +6 -0
- package/src/system/components/RadioButton.svelte +36 -3
- package/src/system/components/SegmentedControl.svelte +36 -2
- package/src/system/components/SlidePager.svelte +2 -2
- package/src/system/components/Slider.svelte +13 -9
- package/src/system/components/TabBar.svelte +33 -3
- package/src/system/components/Toggle.svelte +2 -2
- package/src/system/components/VideoFrame.svelte +1 -1
- package/src/system/components/VideoLightbox.svelte +2 -2
- package/src/system/internal/focusTrap.ts +35 -0
- package/src/testing-js/__aria__/badge.yml +40 -0
- package/src/testing-js/__aria__/button.yml +88 -0
- package/src/testing-js/__aria__/callout.yml +12 -0
- package/src/testing-js/__aria__/card.yml +8 -0
- package/src/testing-js/__aria__/codesnippet.yml +21 -0
- package/src/testing-js/__aria__/collapsiblesection.yml +23 -0
- package/src/testing-js/__aria__/cornerbadge.yml +24 -0
- package/src/testing-js/__aria__/dialog.yml +42 -0
- package/src/testing-js/__aria__/iconbutton.yml +57 -0
- package/src/testing-js/__aria__/image.yml +10 -0
- package/src/testing-js/__aria__/imagelightbox.yml +50 -0
- package/src/testing-js/__aria__/inlineeditactions.yml +16 -0
- package/src/testing-js/__aria__/input.yml +61 -0
- package/src/testing-js/__aria__/menuselect.yml +30 -0
- package/src/testing-js/__aria__/notification.yml +30 -0
- package/src/testing-js/__aria__/panel.yml +2 -0
- package/src/testing-js/__aria__/progressbar.yml +2 -0
- package/src/testing-js/__aria__/radiobutton.yml +20 -0
- package/src/testing-js/__aria__/sectiondivider.yml +9 -0
- package/src/testing-js/__aria__/segmentedcontrol.yml +35 -0
- package/src/testing-js/__aria__/sidenavigation.yml +304 -0
- package/src/testing-js/__aria__/slidepager.yml +72 -0
- package/src/testing-js/__aria__/slider.yml +28 -0
- package/src/testing-js/__aria__/tabbar.yml +35 -0
- package/src/testing-js/__aria__/table.yml +144 -0
- package/src/testing-js/__aria__/toggle.yml +12 -0
- package/src/testing-js/__aria__/tooltip.yml +2 -0
- package/src/testing-js/__aria__/videoframe.yml +6 -0
- package/src/testing-js/__aria__/videolightbox.yml +12 -0
- package/src/testing-js/{chunk-WIZ6W7UT.js → chunk-3WCLMOM7.js} +2 -2
- package/src/testing-js/{chunk-SHBYZUPD.js → chunk-5HNZHIHG.js} +211 -14
- package/src/testing-js/chunk-5HNZHIHG.js.map +1 -0
- package/src/testing-js/{chunk-NB3NZRBM.js → chunk-7CM5D7PF.js} +5 -1
- package/src/testing-js/chunk-7CM5D7PF.js.map +1 -0
- package/src/testing-js/chunk-EWSGE27V.js +168 -0
- package/src/testing-js/chunk-EWSGE27V.js.map +1 -0
- package/src/testing-js/{chunk-GKO27NGN.js → chunk-R54I7YL6.js} +2 -2
- package/src/testing-js/{chunk-X6KHEVIG.js → chunk-RWH46IU5.js} +97 -29
- package/src/testing-js/chunk-RWH46IU5.js.map +1 -0
- package/src/testing-js/chunk-SHLPDNAQ.js +658 -0
- package/src/testing-js/chunk-SHLPDNAQ.js.map +1 -0
- package/src/testing-js/{chunk-3UKGXCDL.js → chunk-YVVR3GJZ.js} +1 -1
- package/src/testing-js/chunk-YVVR3GJZ.js.map +1 -0
- package/src/testing-js/component-a11y.contract.js +283 -0
- package/src/testing-js/component-a11y.contract.js.map +1 -0
- package/src/testing-js/component-alias.contract.js +2 -2
- package/src/testing-js/component-behavior.contract.js +2 -2
- package/src/testing-js/component-editor.contract.js +6 -5
- package/src/testing-js/component-editor.contract.js.map +1 -1
- package/src/testing-js/component-render.contract.js +10 -6
- package/src/testing-js/component-render.contract.js.map +1 -1
- package/src/testing-js/index.d.ts +44 -16
- package/src/testing-js/index.js +9 -9
- package/src/testing-js/page-compliance.contract.js +132 -13
- package/src/testing-js/page-compliance.contract.js.map +1 -1
- package/src/testing-js/vitest.js +3 -3
- package/template/package.json +9 -0
- package/src/testing-js/chunk-3UKGXCDL.js.map +0 -1
- package/src/testing-js/chunk-NB3NZRBM.js.map +0 -1
- package/src/testing-js/chunk-SHBYZUPD.js.map +0 -1
- package/src/testing-js/chunk-X6KHEVIG.js.map +0 -1
- /package/src/testing-js/{chunk-WIZ6W7UT.js.map → chunk-3WCLMOM7.js.map} +0 -0
- /package/src/testing-js/{chunk-GKO27NGN.js.map → chunk-R54I7YL6.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../testing/support/traceProperties.ts","../testing/support/nonTextContrast.ts","../testing/support/textSpacingAndMotion.ts"],"sourcesContent":["import type { CDPSession, Page } from '@playwright/test';\n\n/** The CSS properties a result's verdict reads, per element. A name ending in\n * `*` matches every property that starts with the rest; `*` alone matches\n * every property. */\nexport interface TraceAxes {\n target: string[];\n pseudoElements: string[];\n ancestors: string[];\n /** Pseudo-classes forced on the target while its styles are read. The\n * target then keeps only the declarations the force brings in or changes. */\n force?: string[];\n}\n\nconst BOX_SIZE = ['width', 'height', 'min-*', 'inline-size', 'block-size', 'padding*'];\nconst FOCUS = ['focus', 'focus-visible'];\n// Every border property but the radii, which carry no colour or width.\nconst BORDER_PAINT = ['border-block*', 'border-inline*', ...['', '-top', '-right', '-bottom', '-left']\n .flatMap((side) => [`border${side}`, `border${side}-color`, `border${side}-width`, `border${side}-style`])];\n\n// What sets how long a motion lasts; easing and fill shape it without lengthening it.\nconst MOTION = ['animation', 'animation-name', 'animation-duration', 'animation-delay', 'animation-iteration-count',\n 'transition', 'transition-property', 'transition-duration', 'transition-delay'];\n\n/** Decision 2 of docs/plans/a11y-property-marks.md, keyed by engine rule. An\n * engine rule absent here traces nothing. */\nexport const TRACE_AXES: Readonly<Record<string, TraceAxes>> = {\n 'color-contrast': {\n target: ['color', 'background*', 'opacity', 'text-shadow'],\n pseudoElements: ['*'],\n ancestors: ['background*', 'opacity'],\n },\n 'SIA-R65': {\n target: ['outline*', 'box-shadow', 'border*', 'background*', 'color', 'text-decoration*'],\n pseudoElements: [],\n ancestors: [],\n force: FOCUS,\n },\n 'target-size': { target: BOX_SIZE, pseudoElements: BOX_SIZE, ancestors: [] },\n 'focus-indicator-contrast': {\n target: ['outline*', 'box-shadow', ...BORDER_PAINT, 'background*', 'color'],\n pseudoElements: [],\n ancestors: ['background*'],\n force: FOCUS,\n },\n 'control-boundary-contrast': {\n target: [...BORDER_PAINT, 'outline', 'outline-color', 'outline-width', 'outline-style', 'background*'],\n pseudoElements: [],\n ancestors: ['background*'],\n },\n // Every element's line height is 1.5 under the override, so the theme's own cannot cause the overflow.\n 'text-spacing': {\n target: ['height', 'max-height', 'width', 'max-width', 'padding*', 'overflow*', 'white-space'],\n pseudoElements: [],\n ancestors: [],\n },\n // A pseudo-element that does not display does not move, so its display gates the motion, as Button's shimmer does.\n 'prefers-reduced-motion': { target: MOTION, pseudoElements: [...MOTION, 'display'], ancestors: [] },\n};\n\ninterface Declaration {\n name: string;\n value: string;\n text?: string;\n important?: boolean;\n}\n\ninterface Style {\n cssProperties: Declaration[];\n}\n\ninterface RuleMatch {\n rule: {\n origin: string;\n style: Style & { range?: unknown };\n styleSheetId?: string;\n selectorList: { text: string };\n };\n}\n\ninterface ElementStyles {\n inlineStyle?: Style;\n matchedCSSRules?: RuleMatch[];\n}\n\nconst sessions = new WeakMap<Page, Promise<CDPSession>>();\n\n/** One DevTools session per page, with `DOM` and `CSS` enabled. */\nexport function cdpSession(page: Page): Promise<CDPSession> {\n let session = sessions.get(page);\n if (!session) {\n session = (async () => {\n const cdp = await page.context().newCDPSession(page);\n await cdp.send('DOM.enable');\n await cdp.send('CSS.enable');\n return cdp;\n })();\n sessions.set(page, session);\n }\n return session;\n}\n\nfunction onAxis(property: string, axis: string[]): boolean {\n return axis.some((name) => (name.endsWith('*') ? property.startsWith(name.slice(0, -1)) : property === name));\n}\n\n/** Authored declarations only: Chromium also lists each shorthand's longhands\n * without text, which would repeat every reference. */\nfunction authored(style: Style | undefined): Declaration[] {\n return (style?.cssProperties ?? []).filter((declaration) => declaration.text);\n}\n\nfunction regularRules(styles: ElementStyles): RuleMatch['rule'][] {\n return (styles.matchedCSSRules ?? []).filter(({ rule }) => rule.origin === 'regular').map(({ rule }) => rule);\n}\n\nfunction declarations(styles: ElementStyles): Declaration[] {\n return [authored(styles.inlineStyle), ...regularRules(styles).map((rule) => authored(rule.style))].flat();\n}\n\n/** One element's declarations from lowest to highest precedence: its rules\n * in cascade order, then its inline style. */\nfunction cascadeOrder(styles: ElementStyles): Declaration[] {\n return [...regularRules(styles).flatMap((rule) => authored(rule.style)), ...authored(styles.inlineStyle)];\n}\n\n/** The declaration of `name` that wins among `ordered`, or none. */\nfunction winning(ordered: Declaration[], name: string): Declaration | undefined {\n const all = ordered.filter((d) => d.name === name);\n // Chromium flags `important` on standard properties and leaves it in a custom property's value.\n return all.filter((d) => d.important || /!\\s*important\\s*$/i.test(d.value)).at(-1) ?? all.at(-1);\n}\n\nfunction ruleKey(rule: RuleMatch['rule']): string {\n return JSON.stringify([rule.styleSheetId, rule.selectorList.text, rule.style.range]);\n}\n\ntype Longhands = (names: string[]) => Promise<Map<string, string[]>>;\n\n/** The longhands each property sets, read from the page's CSSOM, so that\n * `outline` and `outline-color` count as the same property. */\nfunction longhandsIn(page: Page): Longhands {\n return async (names) => new Map(await page.evaluate((names) => names.map((name): [string, string[]] => {\n if (name.startsWith('--')) return [name, [name]];\n const style = document.createElement('div').style;\n style.setProperty(name, 'initial');\n return [name, style.length ? Array.from({ length: style.length }, (_, i) => style.item(i)) : [name]];\n }), names));\n}\n\n/**\n * The target's declarations under `forced` that the force brings in or\n * changes: every declaration of a rule that matches only under the force,\n * and every declaration of a later rule, or of the inline style, that sets a\n * property one of those rules sets. Rules that match in every state drop, so\n * a field's resting border and surface never name a focus finding.\n */\nasync function forcedDeclarations(plain: ElementStyles, forced: ElementStyles, longhands: Longhands): Promise<Declaration[]> {\n const before = new Set(regularRules(plain).map(ruleKey));\n const cascade = [\n ...regularRules(forced).map((rule) => ({ forcedOnly: !before.has(ruleKey(rule)), declarations: authored(rule.style) })),\n { forcedOnly: false, declarations: authored(forced.inlineStyle) },\n ];\n const expanded = await longhands([...new Set(cascade.flatMap((entry) => entry.declarations.map((d) => d.name)))]);\n const changed = new Set<string>();\n const kept: Declaration[] = [];\n for (const { forcedOnly, declarations } of cascade) {\n for (const d of declarations) {\n const sets = expanded.get(d.name) ?? [d.name];\n if (forcedOnly) sets.forEach((name) => changed.add(name));\n if (forcedOnly || sets.some((name) => changed.has(name))) kept.push(d);\n }\n }\n return kept;\n}\n\nfunction references(value: string): string[] {\n return [...value.matchAll(/var\\(\\s*(--[\\w-]+)/g)].map((match) => match[1]);\n}\n\n/** How many ancestors separate each element `selector` matches from the\n * nearest element `scope` matches, in document order, or -1 outside it. */\nfunction scopeDepths(page: Page, selector: string, scope: string): Promise<number[]> {\n return page.evaluate(({ selector, scope }) => [...document.querySelectorAll(selector)].map((element) => {\n let depth = 0;\n for (let node: Element | null = element; node; node = node.parentElement, depth++) {\n if (node.matches(scope)) return depth;\n }\n return -1;\n }), { selector, scope });\n}\n\nasync function matchedStyles(cdp: CDPSession, nodeId: number, force: string[] = [], forceNodeId = nodeId) {\n const forcePseudoState = (forcedPseudoClasses: string[]) => cdp.send('CSS.forcePseudoState', { nodeId: forceNodeId, forcedPseudoClasses });\n if (force.length === 0) return cdp.send('CSS.getMatchedStylesForNode', { nodeId });\n await forcePseudoState(force);\n return cdp.send('CSS.getMatchedStylesForNode', { nodeId }).finally(() => forcePseudoState([]));\n}\n\ninterface TraceNode {\n nodeId: number;\n depth: number;\n forceNodeId?: number;\n ancestorsFrom: number;\n}\n\nasync function traceNode(\n cdp: CDPSession,\n node: TraceNode,\n axes: TraceAxes,\n known: ReadonlySet<string>,\n longhands: Longhands,\n): Promise<string[]> {\n const matched = await matchedStyles(cdp, node.nodeId, axes.force, node.forceNodeId);\n const own = axes.force\n ? await forcedDeclarations(await matchedStyles(cdp, node.nodeId), matched, longhands)\n : declarations(matched);\n const pseudo = (matched.pseudoElements ?? []).flatMap(({ matches }) => declarations({ matchedCSSRules: matches }));\n const chain = (matched.inherited ?? []).slice(0, node.depth);\n\n // A reference resolves on the element whose declaration holds it: level 0\n // is the target, level n its nth ancestor, and PSEUDO its pseudo-elements.\n // Only the winning declaration of a private property counts there, so a\n // selected tab traces its selected binding and leaves the default one out.\n const PSEUDO = -1;\n const levels = [cascadeOrder(matched), ...chain.map(cascadeOrder)];\n const pseudoCustom = pseudo.filter((d) => d.name.startsWith('--'));\n const from = Math.max(node.ancestorsFrom - 1, 0);\n const pending: { name: string; level: number }[] = [\n ...own.filter((d) => onAxis(d.name, axes.target)).map((d) => ({ d, level: 0 })),\n ...pseudo.filter((d) => onAxis(d.name, axes.pseudoElements)).map((d) => ({ d, level: PSEUDO })),\n ...chain.slice(from).flatMap((entry, i) => declarations(entry)\n .filter((d) => onAxis(d.name, axes.ancestors))\n .map((d) => ({ d, level: from + i + 1 }))),\n ].flatMap(({ d, level }) => references(d.value).map((name) => ({ name, level })));\n\n const resolve = (name: string, level: number): { declarations: Declaration[]; level: number } => {\n if (level === PSEUDO) {\n const own = pseudoCustom.filter((d) => d.name === name);\n if (own.length > 0) return { declarations: own, level: PSEUDO };\n }\n for (let at = Math.max(level, 0); at < levels.length; at++) {\n const won = winning(levels[at], name);\n if (won) return { declarations: [won], level: at };\n }\n return { declarations: [], level };\n };\n\n const found = new Set<string>();\n const followed = new Set<string>();\n for (let next = pending.pop(); next !== undefined; next = pending.pop()) {\n const { name, level } = next;\n const key = `${name}@${level}`;\n if (known.has(name)) found.add(name);\n else if (!followed.has(key)) {\n followed.add(key);\n const resolved = resolve(name, level);\n for (const d of resolved.declarations) pending.push(...references(d.value).map((ref) => ({ name: ref, level: resolved.level })));\n }\n }\n return [...found];\n}\n\n/** The element a trace reads, inside `scope`. `forceOn`, when set, names the\n * element `axes.force` applies to, for a focus style drawn on another\n * element. `ancestorsFrom` is the nearest ancestor whose `axes.ancestors`\n * declarations count, 1 being the parent and the default. */\nexport interface TraceTarget {\n selector: string;\n scope: string;\n forceOn?: string;\n ancestorsFrom?: number;\n}\n\n/**\n * The component properties `axes` reads on the element `target.selector`\n * names inside `target.scope` (decisions 1 to 3): each matched declaration on\n * an axis, narrowed under `axes.force` to the ones the force brings in or\n * changes, its `var()` references followed through the winning declaration\n * of each private custom property on the target or an in-scope ancestor,\n * kept where a name is in `known`. Sorted and unique.\n */\nexport async function traceProperties(\n page: Page,\n target: TraceTarget,\n axes: TraceAxes,\n known: ReadonlySet<string>,\n): Promise<string[]> {\n let depths: number[];\n let forceDepths: number[] = [];\n try {\n depths = await scopeDepths(page, target.selector, target.scope);\n if (target.forceOn) forceDepths = await scopeDepths(page, target.forceOn, target.scope);\n } catch {\n // An Alfa label or an axe frame path need not parse as one selector.\n return [];\n }\n const cdp = await cdpSession(page);\n const { root } = await cdp.send('DOM.getDocument', { depth: 0 });\n const query = async (selector: string) => (await cdp.send('DOM.querySelectorAll', { nodeId: root.nodeId, selector })).nodeIds;\n const nodeIds = await query(target.selector);\n const forceNodeId = target.forceOn\n ? (await query(target.forceOn)).find((_, index) => forceDepths[index] >= 0)\n : undefined;\n if (target.forceOn && forceNodeId === undefined) return [];\n const longhands = longhandsIn(page);\n const found = new Set<string>();\n for (const [index, nodeId] of nodeIds.entries()) {\n if (depths[index] < 0) continue;\n const node = { nodeId, depth: depths[index], forceNodeId, ancestorsFrom: target.ancestorsFrom ?? 1 };\n for (const name of await traceNode(cdp, node, axes, known, longhands)) found.add(name);\n }\n return [...found].sort();\n}\n","import type { Page } from '@playwright/test';\nimport type { A11yResult } from './a11y';\nimport { cdpSession } from './traceProperties';\n\nexport const FOCUS_INDICATOR_RULE = 'focus-indicator-contrast';\nexport const BOUNDARY_RULE = 'control-boundary-contrast';\n\nconst NON_TEXT_URL = 'https://www.w3.org/WAI/WCAG22/Understanding/non-text-contrast.html';\nconst FOCUS_ATTR = 'data-lt-focus-probe';\nconst NO_TRANSITION_ID = 'lt-focus-probe-no-transition';\nconst FORCE = ['focus', 'focus-visible'];\n\n/** Where the trace reads a result's properties: `selector` names the element\n * that paints the colour, `forceOn` the element focus is forced on, and\n * `ancestorsFrom` the nearest ancestor (1 is the parent, 0 the element\n * itself) whose surface lies behind the measured edge. */\nexport interface TraceRequest {\n selector: string;\n forceOn?: string;\n ancestorsFrom?: number;\n}\n\nexport interface CheckedResult {\n result: A11yResult;\n trace: TraceRequest;\n}\n\n/** A part whose border, outline or surface draws a control's visible edge.\n * `when` narrows it to the elements that also match, such as a state class. */\nexport interface BoundaryPart {\n selector: string;\n label: string;\n when?: string;\n}\n\ntype Snapshot = string[];\n\ntype FocusOutcome =\n | { kind: 'none' | 'browser' | 'pass' }\n | {\n kind: 'fail' | 'review';\n channel: string;\n ratio?: number;\n target: string;\n indicator: string;\n html: string;\n ancestorsFrom: number;\n };\n\ntype BoundaryOutcome = {\n kind: 'fail' | 'review';\n label: string;\n target: string;\n html: string;\n edge: { channel: 'border' | 'outline'; ratio: number } | null;\n surface: number | null;\n ancestorsFrom: number;\n};\n\ntype PageRequest =\n | { kind: 'focusables'; scope: string; exclude: string[]; attr: string; styleId: string }\n | { kind: 'snapshot'; attr: string; index: number; scope: string; exclude: string[] }\n | { kind: 'measure'; attr: string; index: number; scope: string; exclude: string[]; before: Snapshot }\n | { kind: 'cleanup'; attr: string; styleId: string }\n | { kind: 'boundaries'; scope: string; exclude: string[]; parts: BoundaryPart[]; root: string }\n | { kind: 'describe'; scope: string; exclude: string[]; attr: string; values: string[] };\n\n/**\n * Runs in the page, so it carries every helper it needs. Colours resolve and\n * composite on a 1px canvas, which reads any CSS colour syntax the computed\n * style returns and blends alpha the way the page paints it.\n */\nfunction inPage(request: PageRequest): unknown {\n const scopes = [...document.querySelectorAll<HTMLElement>(('scope' in request && request.scope) || ':not(*)')];\n const excluded = (el: Element) => 'exclude' in request && request.exclude.some((selector) => el.closest(selector));\n const inScope = (el: Element) => scopes.some((scope) => scope.contains(el)) && !excluded(el);\n\n const canvas = document.createElement('canvas');\n canvas.width = canvas.height = 1;\n const ctx = canvas.getContext('2d', { willReadFrequently: true })!;\n const paint = (layers: string[]): number[] => {\n ctx.clearRect(0, 0, 1, 1);\n for (const layer of ['#fff', ...layers]) {\n ctx.fillStyle = 'transparent';\n ctx.fillStyle = layer;\n ctx.fillRect(0, 0, 1, 1);\n }\n return [...ctx.getImageData(0, 0, 1, 1).data.slice(0, 3)];\n };\n const alpha = (color: string): number => {\n ctx.clearRect(0, 0, 1, 1);\n ctx.fillStyle = 'transparent';\n ctx.fillStyle = color;\n ctx.fillRect(0, 0, 1, 1);\n return ctx.getImageData(0, 0, 1, 1).data[3] / 255;\n };\n const rgb = (channels: number[]) => `rgb(${channels.join(' ')})`;\n const luminance = (channels: number[]) => {\n const [r, g, b] = channels.map((c) => {\n const s = c / 255;\n return s <= 0.04045 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;\n });\n return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n };\n const contrast = (a: number[], b: number[]) => {\n const [hi, lo] = [luminance(a), luminance(b)].sort((x, y) => y - x);\n return (hi + 0.05) / (lo + 0.05);\n };\n\n const selectorFor = (el: Element): string => {\n const count = (selector: string) => scopes.reduce((n, scope) => n + (scope.matches(selector) ? 1 : 0) + scope.querySelectorAll(selector).length, 0);\n const own = (e: Element) => e.tagName.toLowerCase()\n + [...e.classList].filter((name) => !name.startsWith('svelte-') && /^[\\w-]+$/.test(name)).map((name) => `.${name}`).join('');\n const path: string[] = [];\n for (let e: Element | null = el; e; e = e.parentElement) {\n const step = own(e);\n path.unshift(step);\n if (count(path.join(' > ')) === 1) break;\n const same = [...(e.parentElement?.children ?? [])].filter((c) => c.tagName === e!.tagName);\n if (same.length > 1) path[0] = `${step}:nth-of-type(${same.indexOf(e) + 1})`;\n if (count(path.join(' > ')) === 1 || scopes.includes(e as HTMLElement)) break;\n }\n return path.join(' > ');\n };\n\n /** The surfaces behind `ring`, from the nearest box that contains it up to\n * the first opaque one, composited over white. A gradient or image among\n * them leaves the colour unknown. */\n const backdrop = (from: Element, ring: DOMRect) => {\n const layers: string[] = [];\n let level = 0;\n let first = -1;\n for (let e: Element | null = from; e; e = e.parentElement, level++) {\n const box = e.getBoundingClientRect();\n const contains = box.left <= ring.left + 0.5 && box.top <= ring.top + 0.5\n && box.right >= ring.right - 0.5 && box.bottom >= ring.bottom - 0.5;\n if (!contains) continue;\n if (first < 0) first = level;\n const style = getComputedStyle(e);\n if (style.backgroundImage !== 'none') return { color: null, first };\n const a = alpha(style.backgroundColor);\n if (a > 0) layers.unshift(style.backgroundColor);\n if (a >= 1) break;\n }\n return { color: paint(layers), first: Math.max(first, 0) };\n };\n const inflate = (box: DOMRect, by: number) => new DOMRect(box.x - by, box.y - by, box.width + 2 * by, box.height + 2 * by);\n const px = (value: string) => parseFloat(value) || 0;\n const html = (el: Element) => el.outerHTML.replace(/\\s+/g, ' ').slice(0, 300);\n\n const SIDES = ['Top', 'Right', 'Bottom', 'Left'] as const;\n const visibleSide = (style: CSSStyleDeclaration, side: string) => {\n const s = style as unknown as Record<string, string>;\n return !['none', 'hidden'].includes(s[`border${side}Style`]) && px(s[`border${side}Width`]) >= 0.99\n ? s[`border${side}Color`] : null;\n };\n\n const candidates = (target: Element): Element[] => {\n const list = [target, ...target.querySelectorAll('*')];\n for (const sibling of target.parentElement?.children ?? []) {\n if (sibling !== target) list.push(sibling, ...sibling.querySelectorAll('*'));\n }\n return list.filter(inScope);\n };\n const signature = (el: Element) => {\n const s = getComputedStyle(el);\n return JSON.stringify([\n [s.outlineStyle, s.outlineWidth, s.outlineColor, s.outlineOffset],\n s.boxShadow,\n SIDES.map((side) => visibleSide(s, side)),\n ]);\n };\n\n /** The colour focus brings in on `el`, by decision 8's order: an outline,\n * else the first outer box shadow, else a border. A channel focus leaves\n * alone, or paints fully transparent, is no indicator. */\n const indicatorOf = (el: Element, before: string) => {\n const [outlineBefore, shadowBefore, bordersBefore] = JSON.parse(before);\n const s = getComputedStyle(el);\n const outline = [s.outlineStyle, s.outlineWidth, s.outlineColor, s.outlineOffset];\n if (JSON.stringify(outline) !== JSON.stringify(outlineBefore) && s.outlineStyle !== 'none' && px(s.outlineWidth) > 0) {\n if (s.outlineStyle === 'auto') return { channel: 'browser', color: '', extent: 0 };\n if (alpha(s.outlineColor) > 0) return { channel: 'outline', color: s.outlineColor, extent: px(s.outlineOffset) + px(s.outlineWidth) };\n }\n if (s.boxShadow !== shadowBefore && s.boxShadow !== 'none') {\n const shadows = s.boxShadow.split(/,(?![^(]*\\))/).map((part) => part.trim());\n for (const shadow of shadows) {\n if (/\\binset\\b/.test(shadow)) continue;\n const color = /^(?:[a-z-]+\\([^)]*\\)|#[\\da-f]+|[a-z]+)/i.exec(shadow)?.[0] ?? '';\n if (!color || alpha(color) === 0) continue;\n const [x = 0, y = 0, blur = 0, spread = 0] = (shadow.slice(color.length).match(/-?[\\d.]+px/g) ?? []).map(px);\n return { channel: 'shadow', color, extent: Math.max(Math.abs(x), Math.abs(y)) + blur + spread };\n }\n }\n const borders = SIDES.map((side) => visibleSide(s, side));\n for (const [i, color] of borders.entries()) {\n if (color && color !== bordersBefore[i] && alpha(color) > 0) return { channel: 'border', color, extent: 0 };\n }\n return null;\n };\n\n switch (request.kind) {\n case 'focusables': {\n // A focus transition would leave the computed colour at its start.\n const style = document.createElement('style');\n style.id = request.styleId;\n style.textContent = '*, *::before, *::after { transition: none !important; }';\n document.head.append(style);\n const focusable = 'a[href], area[href], button, input:not([type=\"hidden\"]), select, textarea, summary, iframe, [tabindex], [contenteditable]:not([contenteditable=\"false\"])';\n const found = scopes.flatMap((scope) => [scope, ...scope.querySelectorAll(focusable)])\n .filter((el, i, all) => all.indexOf(el) === i && el.matches(focusable) && inScope(el))\n .filter((el) => (el as HTMLElement).tabIndex >= 0 && !el.matches(':disabled') && el.checkVisibility({ visibilityProperty: true }));\n found.forEach((el, i) => el.setAttribute(request.attr, String(i)));\n return found.length;\n }\n case 'snapshot': {\n const target = document.querySelector(`[${request.attr}=\"${request.index}\"]`)!;\n return candidates(target).map(signature);\n }\n case 'measure': {\n const target = document.querySelector(`[${request.attr}=\"${request.index}\"]`)!;\n const list = candidates(target);\n for (const [i, el] of list.entries()) {\n if (request.before[i] === undefined || request.before[i] === signature(el)) continue;\n const found = indicatorOf(el, request.before[i]);\n if (!found) continue;\n if (found.channel === 'browser') return { kind: 'browser' };\n const ring = inflate(el.getBoundingClientRect(), Math.max(found.extent, 0));\n const behind = backdrop(found.channel === 'border' ? el.parentElement! : el, found.extent < 0 ? el.getBoundingClientRect() : ring);\n const offset = found.channel === 'border' ? 1 : 0;\n const common = {\n channel: found.channel,\n target: selectorFor(target),\n indicator: selectorFor(el),\n html: html(target),\n ancestorsFrom: behind.first + offset,\n };\n if (!behind.color) return { kind: 'review', ...common };\n const ratio = contrast(paint([rgb(behind.color), found.color]), behind.color);\n return ratio < 3 ? { kind: 'fail', ratio, ...common } : { kind: 'pass' };\n }\n return { kind: 'none' };\n }\n case 'cleanup': {\n document.getElementById(request.styleId)?.remove();\n for (const el of document.querySelectorAll(`[${request.attr}]`)) el.removeAttribute(request.attr);\n return null;\n }\n case 'boundaries': {\n const outcomes: BoundaryOutcome[] = [];\n const control = 'button, input, select, textarea, [aria-disabled]';\n const inactive = (el: Element) => {\n const root = el.closest(request.root) ?? el;\n const controls = [root, ...root.querySelectorAll(control)].filter((c) => c.matches(control));\n return controls.length > 0 && controls.every((c) => c.matches(':disabled') || c.getAttribute('aria-disabled') === 'true');\n };\n for (const part of request.parts) {\n const elements = scopes.flatMap((scope) => [scope, ...scope.querySelectorAll(part.selector)])\n .filter((el, i, all) => all.indexOf(el) === i && el.matches(part.selector) && inScope(el))\n .filter((el) => !part.when || el.matches(part.when))\n .filter((el) => el.checkVisibility({ visibilityProperty: true }) && el.getBoundingClientRect().width > 0 && !inactive(el));\n for (const el of elements) {\n const style = getComputedStyle(el);\n const behind = backdrop(el.parentElement!, el.getBoundingClientRect());\n const common = { label: part.label, target: selectorFor(el), html: html(el), ancestorsFrom: behind.first + 1 };\n if (!behind.color) {\n outcomes.push({ kind: 'review', edge: null, surface: null, ...common });\n continue;\n }\n const surfaceColor = paint([rgb(behind.color), style.backgroundColor]);\n const surface = style.backgroundImage === 'none' ? contrast(surfaceColor, behind.color) : null;\n // A state indicator may draw one side, as TabBar's underline does, or\n // an inset outline, as SegmentedControl's selected option does.\n const edges: { channel: 'border' | 'outline'; color: string; under: number[] }[] = [];\n const borderUnder = style.backgroundClip === 'border-box' ? surfaceColor : behind.color;\n for (const side of SIDES) {\n const color = visibleSide(style, side);\n if (color) edges.push({ channel: 'border', color, under: borderUnder });\n }\n if (!['none', 'auto'].includes(style.outlineStyle) && px(style.outlineWidth) >= 0.99) {\n edges.push({ channel: 'outline', color: style.outlineColor, under: px(style.outlineOffset) < 0 ? surfaceColor : behind.color });\n }\n const edge = edges\n .map(({ channel, color, under }) => ({ channel, ratio: contrast(paint([rgb(under), color]), behind.color) }))\n .reduce<BoundaryOutcome['edge']>((best, next) => (best && best.ratio >= next.ratio ? best : next), null);\n if ((edge?.ratio ?? 0) >= 3 || (surface ?? 0) >= 3) continue;\n outcomes.push({ kind: surface === null ? 'review' : 'fail', edge, surface, ...common });\n }\n }\n return outcomes;\n }\n case 'describe': {\n return request.values.map((value) => {\n const el = document.querySelector(`[${request.attr}=\"${value}\"]`)!;\n return { target: selectorFor(el), html: html(el) };\n });\n }\n }\n}\n\nfunction run<T>(page: Page, request: PageRequest): Promise<T> {\n return page.evaluate(inPage, request) as Promise<T>;\n}\n\n/** The selector and markup a result names for each element in `scope` whose\n * `attr` holds one of `values`, in that order. The selector is unique in\n * `scope`, which the trace needs. */\nexport function describeTagged(\n page: Page,\n scope: string,\n exclude: string[],\n attr: string,\n values: string[],\n): Promise<{ target: string; html: string }[]> {\n return run(page, { kind: 'describe', scope, exclude, attr, values });\n}\n\n/** Below 3:1 fails; WCAG does not round, so neither does the reading. */\nfunction ratioText(ratio: number): string {\n return `${Math.floor(ratio * 100) / 100}:1`;\n}\n\n/**\n * Decision 8: focus forced on each focusable element in `scope`, the colour\n * it brings in measured against the surfaces behind it. The indicator is the\n * first of the element, its descendants and its siblings whose outline, box\n * shadow or border focus changes, so a ring drawn on a sibling, as Slider\n * draws it, counts. Chromium's own `auto` ring pairs a dark and a light band,\n * one of which contrasts with any backdrop, so it passes. An element focus\n * leaves unchanged is SIA-R65's finding and adds none here.\n */\nexport async function scanFocusIndicators(page: Page, scope: string, exclude: string[]): Promise<CheckedResult[]> {\n const cdp = await cdpSession(page);\n const count = await run<number>(page, { kind: 'focusables', scope, exclude, attr: FOCUS_ATTR, styleId: NO_TRANSITION_ID });\n const checked: CheckedResult[] = [];\n try {\n const { root } = await cdp.send('DOM.getDocument', { depth: 0 });\n for (let index = 0; index < count; index++) {\n const { nodeId } = await cdp.send('DOM.querySelector', { nodeId: root.nodeId, selector: `[${FOCUS_ATTR}=\"${index}\"]` });\n const before = await run<Snapshot>(page, { kind: 'snapshot', attr: FOCUS_ATTR, index, scope, exclude });\n await cdp.send('CSS.forcePseudoState', { nodeId, forcedPseudoClasses: FORCE });\n let outcome: FocusOutcome;\n try {\n outcome = await run<FocusOutcome>(page, { kind: 'measure', attr: FOCUS_ATTR, index, scope, exclude, before });\n } finally {\n await cdp.send('CSS.forcePseudoState', { nodeId, forcedPseudoClasses: [] });\n }\n if (outcome.kind !== 'fail' && outcome.kind !== 'review') continue;\n const where = outcome.indicator === outcome.target ? '' : ` on ${outcome.indicator}`;\n checked.push({\n result: {\n rule: outcome.kind === 'fail' ? 'a11y-component' : 'a11y-review',\n engineRule: FOCUS_INDICATOR_RULE,\n impact: null,\n target: outcome.target,\n html: outcome.html,\n help: outcome.kind === 'fail'\n ? `Focus ${outcome.channel}${where} ${ratioText(outcome.ratio!)} against the backdrop, needs 3:1`\n : `Focus ${outcome.channel}${where} sits over a gradient or image. Check by eye that it contrasts 3:1 with the backdrop`,\n helpUrl: NON_TEXT_URL,\n },\n trace: { selector: outcome.indicator, forceOn: outcome.target, ancestorsFrom: outcome.ancestorsFrom },\n });\n }\n } finally {\n await run(page, { kind: 'cleanup', attr: FOCUS_ATTR, styleId: NO_TRANSITION_ID });\n }\n return checked;\n}\n\n/**\n * Decision 9: each visible element of each boundary part in `scope` passes\n * when its best border side or outline, 1px or wider, or its surface\n * contrasts 3:1 with the surfaces behind it. A part of a control whose every\n * form control is disabled is inactive, which SC 1.4.11 exempts. `root` names\n * the component's root part, the extent of the control.\n */\nexport async function scanBoundaries(\n page: Page,\n scope: string,\n exclude: string[],\n parts: BoundaryPart[],\n root: string,\n): Promise<CheckedResult[]> {\n if (parts.length === 0) return [];\n const outcomes = await run<BoundaryOutcome[]>(page, { kind: 'boundaries', scope, exclude, parts, root });\n return outcomes.map((outcome) => {\n const edge = outcome.edge === null ? 'no border' : `${outcome.edge.channel} ${ratioText(outcome.edge.ratio)}`;\n return {\n result: {\n rule: outcome.kind === 'fail' ? 'a11y-component' : 'a11y-review',\n engineRule: BOUNDARY_RULE,\n impact: null,\n target: outcome.target,\n html: outcome.html,\n help: outcome.kind === 'fail'\n ? `${outcome.label} ${edge} and surface ${ratioText(outcome.surface!)} against the backdrop, needs 3:1`\n : `${outcome.label} sits on or carries a gradient or image. Check by eye that its edge contrasts 3:1 with the backdrop`,\n helpUrl: NON_TEXT_URL,\n },\n trace: { selector: outcome.target, ancestorsFrom: outcome.ancestorsFrom },\n };\n });\n}\n","import type { Page } from '@playwright/test';\nimport type { A11yResult } from './a11y';\nimport { describeTagged, type CheckedResult } from './nonTextContrast';\n\nexport const TEXT_SPACING_URL = 'https://www.w3.org/WAI/WCAG22/Understanding/text-spacing.html';\n\n/** An animation that ends within this many milliseconds of its start reads as\n * a fade or a state change, which reduced motion leaves alone. */\nexport const MOTION_MS = 200;\nexport const MOTION_URL = 'https://www.w3.org/WAI/WCAG22/Techniques/css/C39';\n\nexport const TEXT_SPACING_RULE = 'text-spacing';\nexport const REDUCED_MOTION_RULE = 'prefers-reduced-motion';\n\nconst SPACING_ATTR = 'data-lt-spacing-probe';\nconst MOTION_ATTR = 'data-lt-motion-probe';\nconst STILL_ID = 'lt-spacing-probe-still';\nconst TOLERANCE = 1;\n\n/** The override SC 1.4.12 names, as the W3C's own text spacing bookmarklet\n * applies it, over the whole page or inside `scope`. */\nexport function textSpacingCss(scope?: string): string {\n const every = scope ? `${scope}, ${scope} *` : '*';\n const paragraphs = scope ? `${scope} p` : 'p';\n return `\n${every} { line-height: 1.5 !important; letter-spacing: 0.12em !important; word-spacing: 0.16em !important; }\n${paragraphs} { margin-bottom: 2em !important; }\n`;\n}\n\n/**\n * What `observe` finds under the SC 1.4.12 override and not without it. The\n * pixel counts in a failure move under the override, so `key` names a failure\n * by what fails and where; a key found n times without the override excuses n\n * under it.\n */\nexport async function newUnderTextSpacing<T>(\n page: Page,\n observe: () => Promise<T[]>,\n key: (failure: T) => string,\n scope?: string,\n): Promise<T[]> {\n const before = await observe();\n const style = await page.addStyleTag({ content: textSpacingCss(scope) });\n let after: T[];\n try {\n after = await observe();\n } finally {\n await style.evaluate((node) => (node as Element).remove());\n }\n const known = new Map<string, number>();\n for (const failure of before) known.set(key(failure), (known.get(key(failure)) ?? 0) + 1);\n return after.filter((failure) => {\n const left = known.get(key(failure)) ?? 0;\n if (left > 0) known.set(key(failure), left - 1);\n return left === 0;\n });\n}\n\n/** Tags with its index under `attr` each element inside `scope`, outside\n * `exclude`, that is or sits inside one of `own`, and returns how many it\n * tagged. `own` names the component's root and portaled parts, so the\n * editor's scaffolding around the preview stays out. */\nfunction tagInScope(page: Page, scope: string, exclude: string[], own: string[], attr: string): Promise<number> {\n return page.evaluate(({ scope, exclude, own, attr }) => {\n const found = [...document.querySelectorAll(scope)].flatMap((root) => [root, ...root.querySelectorAll('*')])\n .filter((el, i, all) => all.indexOf(el) === i && el.closest(own) && !exclude.some((selector) => el.closest(selector)));\n found.forEach((el, i) => el.setAttribute(attr, String(i)));\n return found.length;\n }, { scope, exclude, own: own.join(', '), attr });\n}\n\nfunction untag(page: Page, attr: string, styleId?: string): Promise<void> {\n return page.evaluate(({ attr, styleId }) => {\n if (styleId) document.getElementById(styleId)?.remove();\n for (const el of document.querySelectorAll(`[${attr}]`)) el.removeAttribute(attr);\n }, { attr, styleId });\n}\n\ninterface Overflow {\n index: string;\n axis: 'x' | 'y';\n tag: string;\n content: number;\n box: number;\n overflow: string;\n}\n\n/** Each tagged element whose content runs past its box on an axis it does not\n * scroll. */\nfunction observeOverflow(page: Page): Promise<Overflow[]> {\n return page.evaluate(({ attr, tolerance }) => [...document.querySelectorAll(`[${attr}]`)].flatMap((el) => {\n const style = getComputedStyle(el);\n if (style.display === 'none' || style.display === 'contents') return [];\n // An inline box reports 0 for both, so it would read as overflowing its own zero size.\n if (el.clientWidth === 0 && el.clientHeight === 0) return [];\n // A box with no text under it holds icon glyphs, which letter spacing pads with nothing visible.\n if (!el.textContent?.trim()) return [];\n const index = el.getAttribute(attr)!;\n const tag = el.tagName.toLowerCase();\n const found: Overflow[] = [];\n const axes = [\n ['x', el.scrollWidth, el.clientWidth, style.overflowX],\n ['y', el.scrollHeight, el.clientHeight, style.overflowY],\n ] as const;\n for (const [axis, content, box, overflow] of axes) {\n if (overflow === 'auto' || overflow === 'scroll' || content <= box + tolerance) continue;\n found.push({ index, axis, tag, content, box, overflow });\n }\n return found;\n }), { attr: SPACING_ATTR, tolerance: TOLERANCE });\n}\n\nfunction groupBy<T>(items: T[], key: (item: T) => string): Map<string, T[]> {\n const groups = new Map<string, T[]>();\n for (const item of items) groups.set(key(item), [...(groups.get(key(item)) ?? []), item]);\n return groups;\n}\n\n/**\n * Decision 10: each element of the component in `scope` that overflows or\n * clips under the SC 1.4.12 override, applied inside `scope`, and did not\n * without it. A transition would leave the box at its start size, so\n * transitions stay off while the check reads.\n */\nexport async function scanTextSpacing(page: Page, scope: string, exclude: string[], own: string[]): Promise<A11yResult[]> {\n await page.evaluate(({ id, scope }) => {\n const style = document.createElement('style');\n style.id = id;\n style.textContent = `${scope} *, ${scope} *::before, ${scope} *::after { transition: none !important; }`;\n document.head.append(style);\n }, { id: STILL_ID, scope });\n try {\n if (await tagInScope(page, scope, exclude, own, SPACING_ATTR) === 0) return [];\n const found = await newUnderTextSpacing(page, () => observeOverflow(page), (o) => `${o.index}|${o.axis}`, scope);\n if (found.length === 0) return [];\n const byElement = groupBy(found, (o) => o.index);\n const described = await describeTagged(page, scope, exclude, SPACING_ATTR, [...byElement.keys()]);\n return [...byElement.values()].map((overflows, i) => ({\n rule: 'a11y-text-spacing',\n engineRule: TEXT_SPACING_RULE,\n impact: null,\n target: described[i].target,\n html: described[i].html,\n help: `Under the text spacing override, ${overflows.map((o) =>\n `<${o.tag}> holds ${o.content}px of content in ${o.box}px of ${o.axis === 'x' ? 'width' : 'height'}, `\n + `and its overflow-${o.axis} is ${o.overflow}`).join('; ')}`,\n helpUrl: TEXT_SPACING_URL,\n }));\n } finally {\n await untag(page, SPACING_ATTR, STILL_ID);\n }\n}\n\ninterface Motion {\n index: string;\n pseudo: string;\n kind: 'animation' | 'transition' | 'script';\n name: string;\n end: number;\n}\n\n/** Each tagged element's animations and transitions, and its pseudo-elements',\n * that last past `MOTION_MS`. The harness finishes every finite animation\n * when it selects a view, so the read takes each declared one at its length,\n * run or not. A script animation counts while it runs. */\nfunction observeMotion(page: Page): Promise<Motion[]> {\n return page.evaluate(({ attr, limit }) => {\n const ms = (time: string) => parseFloat(time) * (time.trim().endsWith('ms') ? 1 : 1000) || 0;\n const list = (value: string) => value.split(/,(?![^(]*\\))/).map((part) => part.trim());\n const at = (values: string[], i: number) => values[i % values.length];\n const found: Motion[] = [];\n const tagged = [...document.querySelectorAll(`[${attr}]`)].filter((el) => el.checkVisibility());\n for (const el of tagged) {\n const index = el.getAttribute(attr)!;\n for (const pseudo of ['', '::before', '::after']) {\n const style = getComputedStyle(el, pseudo || null);\n if (pseudo && (style.content === 'none' || style.content === 'normal' || style.display === 'none')) continue;\n const durations = list(style.animationDuration);\n const counts = list(style.animationIterationCount);\n const delays = list(style.animationDelay);\n list(style.animationName).forEach((name, i) => {\n if (name === 'none') return;\n const count = at(counts, i);\n const end = ms(at(delays, i)) + (count === 'infinite' ? Infinity : ms(at(durations, i)) * parseFloat(count));\n // Svelte scopes a component's keyframes with a hash its source never spells.\n if (end > limit) found.push({ index, pseudo, kind: 'animation', name: name.replace(/^svelte-[a-z0-9]+-/, ''), end });\n });\n const transitionDurations = list(style.transitionDuration);\n const transitionDelays = list(style.transitionDelay);\n list(style.transitionProperty).forEach((name, i) => {\n const end = ms(at(transitionDurations, i)) + ms(at(transitionDelays, i));\n if (name !== 'none' && end > limit) found.push({ index, pseudo, kind: 'transition', name, end });\n });\n }\n }\n for (const animation of document.getAnimations()) {\n if (animation instanceof CSSAnimation || animation instanceof CSSTransition || animation.playState !== 'running') continue;\n const effect = animation.effect;\n if (!(effect instanceof KeyframeEffect) || !effect.target?.hasAttribute(attr)) continue;\n const end = Number(effect.getComputedTiming().endTime ?? 0);\n if (end > limit) {\n found.push({ index: effect.target.getAttribute(attr)!, pseudo: effect.pseudoElement ?? '', kind: 'script', name: animation.id, end });\n }\n }\n return found;\n }, { attr: MOTION_ATTR, limit: MOTION_MS });\n}\n\nconst capitalize = (text: string) => text.charAt(0).toUpperCase() + text.slice(1);\n\nfunction describeMotion({ kind, name, end }: Motion): string {\n const what = kind === 'animation' ? `the animation ${name}`\n : kind === 'transition' ? `a transition of ${name}` : `a script animation${name ? ` ${name}` : ''}`;\n return `${what} ${Number.isFinite(end) ? `lasts ${Math.round(end)} ms` : 'runs without end'}`;\n}\n\n/**\n * Decision 11: the animations and transitions of the component in `scope`\n * that last past `MOTION_MS`, read again under `prefers-reduced-motion:\n * reduce`. Each one still that long reports. A view with none skips the\n * second read, and the emulation lifts before the check returns.\n */\nexport async function scanReducedMotion(page: Page, scope: string, exclude: string[], own: string[]): Promise<CheckedResult[]> {\n try {\n if (await tagInScope(page, scope, exclude, own, MOTION_ATTR) === 0) return [];\n if ((await observeMotion(page)).length === 0) return [];\n let reduced: Motion[];\n await page.emulateMedia({ reducedMotion: 'reduce' });\n try {\n // A script that listens for the preference reacts on a later frame.\n await page.evaluate(() => new Promise<void>((resolve) => requestAnimationFrame(() => requestAnimationFrame(() => resolve()))));\n reduced = await observeMotion(page);\n } finally {\n await page.emulateMedia({ reducedMotion: null });\n }\n if (reduced.length === 0) return [];\n const byTarget = groupBy(reduced, (m) => `${m.index}\\n${m.pseudo}`);\n const groups = [...byTarget.values()];\n const described = await describeTagged(page, scope, exclude, MOTION_ATTR, groups.map((motions) => motions[0].index));\n return groups.map((motions, i) => ({\n result: {\n rule: 'a11y-reduced-motion',\n engineRule: REDUCED_MOTION_RULE,\n impact: null,\n target: `${described[i].target}${motions[0].pseudo}`,\n html: described[i].html,\n help: capitalize(`${motions.map(describeMotion).join('; ')} under prefers-reduced-motion: reduce`),\n helpUrl: MOTION_URL,\n },\n trace: { selector: described[i].target },\n }));\n } finally {\n await untag(page, MOTION_ATTR);\n }\n}\n"],"mappings":"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","names":["names","declarations","selector","scope","own","scope","exclude","own","attr","styleId"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../testing/componentContract.ts"],"sourcesContent":["export type ContractRule =\n | 'contract-listed'\n | 'contract-render'\n | 'contract-alias'\n | 'contract-persist'\n | 'contract-theme'\n | 'contract-states'\n | 'contract-interaction'\n | 'contract-behavior'\n | 'contract-sketch'\n | 'contract-missing';\n\n/**\n * A failed obligation, carrying the rule id the CLI reports it under. A defect\n * fixture names the rule it expects, and `check-component --tests` maps a\n * reporter error onto a finding.\n */\nexport class ContractViolation extends Error {\n constructor(\n readonly rule: ContractRule,\n readonly component: string,\n message: string,\n ) {\n super(`[${rule}] ${component}: ${message}`);\n this.name = 'ContractViolation';\n }\n}\n\nexport interface Inapplicable {\n applicable: false;\n reason: string;\n}\n\nexport function isInapplicable(value: unknown): value is Inapplicable {\n return typeof value === 'object' && value !== null && (value as Inapplicable).applicable === false;\n}\n\nexport interface PartLocator {\n /** Resolved inside the preview stage unless `portal` is set. */\n selector: string;\n /** Read computed style from this pseudo-element of the part, e.g.\n * `'::after'`. Chromium's `getComputedStyle` only tolerates the bare form\n * (`'after'`) for `::before`/`::after`; use the full `::` syntax so a\n * future pseudo-element (`::placeholder`, `::marker`, ...) works the same\n * way without a silent fallback to the host element's own style. */\n pseudo?: string;\n /** Rendered outside the preview stage. Resolved against the document. */\n portal?: boolean;\n}\n\nexport type PartDeclaration = string | PartLocator;\n\n/**\n * An editor control the preview is gated behind: a canvas-toolbar checkbox, an\n * optional-content switch. `check` sets a checkbox to a state and is safe to\n * repeat; `value` picks a select option; neither clicks once.\n */\nexport interface ControlStep {\n kind: 'control';\n /** Selector inside the visible variant group. */\n selector: string;\n check?: boolean;\n value?: string;\n}\n\nexport type SetupStep = InteractionAction | ControlStep;\n\n/** Which VariantGroup, which state tab, and what has to happen first. */\nexport interface View {\n /** Variant tab label. Omitted when the editor renders one variant. */\n variant?: string;\n /** State tab label. Omitted when the editor renders no state strip. */\n state?: string;\n /** Run after the tabs are selected. Portaled and toolbar-gated parts exist\n * only once these have run. */\n setup?: SetupStep[];\n}\n\n/** Part key -> CSS property -> the design token that must drive it. */\nexport type PaintMap = Record<string, Record<string, string>>;\n\nexport interface PropertyExpectation extends View {\n paints: PaintMap;\n}\n\nexport interface StateExpectation extends View {\n state: string;\n /** Class the root part carries while the editor previews this state. */\n forceClass?: string;\n /** Part key -> attributes it must carry. A null value requires the absence. */\n attributes?: Record<string, Record<string, string | null>>;\n /** What the state repaints, as computed-value equality against its token. */\n paints?: PaintMap;\n}\n\n/**\n * `token` and `opacity` drive the token selector for `variable`. `gradient`\n * drives the gradient editor for `variable`. `literal` drives `control` and\n * reads `variable` back off the root, which is where an intrinsic lands: the\n * editors write those into the alias bucket. `config` drives `control` and\n * reads `configKey` out of the config bucket, which holds editor metadata that\n * never reaches `:root`.\n */\nexport type PersistenceShape = 'token' | 'literal' | 'opacity' | 'gradient' | 'config';\n\nexport interface PersistenceCase extends View {\n shape: PersistenceShape;\n /** The alias the value is read back from. Omitted for `config`. */\n variable?: string;\n /** Selector inside the visible variant group, for `literal` and `config`. */\n control?: string;\n /** The config-bucket key, for `config`. */\n configKey?: string;\n /** Where the persisted value must land once the page has reloaded. */\n observe: { part: string; css: string };\n}\n\nexport interface PersistenceExpectation {\n cases: PersistenceCase[];\n /** The alias Reset must return to the value the server holds for it. It has\n * to be one a case above moves, so the saved value and the value the\n * component booted with are different and Reset can be seen to choose. */\n resetVariable: string;\n}\n\nexport interface ThemeExpectation extends View {\n /** Theme slug shown in the Theme Picker, e.g. `ocean`. */\n theme: string;\n /** Variables whose root value the theme must move. */\n changed: string[];\n /**\n * Variables the theme must leave exactly as they were. A theme moves colors,\n * shadows, font families, and type sizes, and it resolves many color tokens\n * from its own palette even where two theme files name the same alias, so\n * compare a candidate's resolved value under both themes before trusting\n * it. The spacing, radius, and border-width steps (`--space-*`, `--radius-*`,\n * `--border-width-*`) hold one value in every theme, and a theme assigns\n * them only to the components it holds a block for, so a new component's\n * geometry stays put.\n */\n unchanged: string[];\n /** Variables, and the design token each must resolve to under the theme. */\n aliasedTo: Record<string, string>;\n /** Where the theme's value must land in the rendered preview. */\n observe: { part: string; css: string; variable: string };\n}\n\nexport type InteractionAction =\n /** Without a part the key goes to the element holding focus, such as an\n * overlay that took focus as it opened. */\n | { kind: 'press'; part?: string; key: string }\n | { kind: 'click'; part: string }\n | { kind: 'type'; part: string; text: string }\n | { kind: 'dragTo'; part: string; along: string; fraction: number };\n\nexport type InteractionOutcome =\n | { kind: 'attribute'; part: string; name: string; value: string }\n | { kind: 'valueMoves'; part: string; direction: 'up' | 'down' }\n | { kind: 'valueBecomes'; part: string; value: string }\n | { kind: 'valueChanges'; part: string }\n | { kind: 'valueHolds'; part: string }\n /** Whether the part takes focus. The only outcome a button with no state\n * attribute has: a disabled one refuses it and an enabled one takes it. */\n | { kind: 'focused'; part: string; value: boolean }\n /** Whether the action fired a click on the part, the event a native button\n * sends when Enter or Space activates it. A preview that never feeds a\n * controlled component's callback back into its props shows activation\n * only this way. */\n | { kind: 'activated'; part: string }\n /** Whether the part is in the document, for an overlay a key closes. */\n | { kind: 'present'; part: string; value: boolean };\n\nexport interface InteractionCase extends View {\n name: string;\n action: InteractionAction;\n expect: InteractionOutcome;\n /** `a11y-keyboard` records a failure as a keyboard finding at `warn` and\n * lets the test pass. Without it a failure is a `contract-interaction`\n * error. */\n rule?: 'a11y-keyboard';\n}\n\nexport interface InteractionExpectation {\n /** The part carrying the interactive role. */\n part: string;\n role: string;\n cases: InteractionCase[];\n}\n\n/**\n * One mounted case under happy-dom: props in, a DOM event, one assertion. It\n * carries what a browser cannot reach, the callback a controlled component\n * calls instead of moving its own state.\n */\nexport interface BehaviorCase {\n name: string;\n /** Props the fixture mounts with. A snippet-valued prop is not\n * expressible; a component whose behavior needs one records that case\n * as inapplicable with the reason. */\n props?: Record<string, unknown>;\n action?: { kind: 'click' | 'keydown' | 'input'; part: string; key?: string; value?: string };\n expect:\n | { kind: 'callback'; prop: string; args: unknown[] }\n | { kind: 'no-callback'; prop: string }\n | { kind: 'attribute'; part: string; name: string; value: string | null }\n | { kind: 'text'; part: string; value: string };\n}\n\nexport interface BehaviorExpectation {\n cases: BehaviorCase[];\n}\n\nexport interface SketchPartExpectation {\n part: string;\n /** The design token `--sketch-fill` must resolve to on this part. */\n fill?: string;\n /** The design token `--sketch-stroke` must resolve to on this part. */\n stroke?: string;\n}\n\nexport interface SketchExpectation extends View {\n /** Sketchstyle id the paint is asserted under. */\n style: string;\n parts: SketchPartExpectation[];\n}\n\nexport interface BoundaryExpectation {\n part: string;\n label: string;\n /** A selector the part's element must also match to be measured, such as a\n * state class a part shared across states carries in one of them. */\n when?: string;\n}\n\nexport interface ComponentContract {\n id: string;\n origin: 'system' | 'custom';\n parts: Record<string, PartDeclaration>;\n /** Part key of the component's own root element in the preview. */\n root: string;\n /** The view every obligation opens with. */\n view?: View;\n states: StateExpectation[] | Inapplicable;\n properties: PropertyExpectation[];\n /**\n * Aliases no paint map can pin, each with the reason. A token consumed\n * inside `calc()` or handed to a gradient function never appears verbatim in\n * a computed style, so the probe cannot read it back — that is the only\n * class this belongs to. A token a sibling alias merely happens to carry\n * the same rendered value through (a base padding token behind four\n * per-side overrides, say) is not in this class: the per-side entries pin\n * their own aliases, not the base one, and a paint map that drives the base\n * token directly and observes the per-side CSS property can pin it too. Put\n * that here and Wave 4's coverage JSON records the base token as covered\n * when nothing asserts the link between it and what actually painted.\n */\n uncovered?: Record<string, string>;\n persistence: PersistenceExpectation;\n theme: ThemeExpectation;\n interaction: InteractionExpectation | Inapplicable;\n /** The parts whose border, outline or surface draws the control's visible\n * edge or marks its selected state. The accessibility suite measures each\n * against the backdrop behind it, at 3:1 under WCAG 2.2 SC 1.4.11. `label`\n * names the part in a finding. */\n boundaries?: BoundaryExpectation[];\n /** What the component does when it is driven, proven under happy-dom. */\n behavior: BehaviorExpectation | Inapplicable;\n sketch: SketchExpectation | Inapplicable;\n}\n\n/** Roles whose components must declare interaction cases. */\nconst INTERACTIVE_ROLES = new Set([\n 'button', 'switch', 'checkbox', 'radio', 'slider', 'spinbutton', 'textbox',\n 'combobox', 'listbox', 'menuitem', 'tab', 'link', 'searchbox', 'option',\n]);\n\nexport function requiresInteraction(role: string | null): boolean {\n return role !== null && INTERACTIVE_ROLES.has(role);\n}\n\nexport function partLocator(contract: ComponentContract, key: string): PartLocator {\n const declared = contract.parts[key];\n if (declared === undefined) {\n throw new ContractViolation('contract-render', contract.id, `no part named \"${key}\" is declared`);\n }\n return typeof declared === 'string' ? { selector: declared } : declared;\n}\n"],"mappings":";AAiBO,IAAM,oBAAN,cAAgC,MAAM;AAAA,EAC3C,YACW,MACA,WACT,SACA;AACA,UAAM,IAAI,IAAI,KAAK,SAAS,KAAK,OAAO,EAAE;AAJjC;AACA;AAIT,SAAK,OAAO;AAAA,EACd;AAAA,EANW;AAAA,EACA;AAMb;AAOO,SAAS,eAAe,OAAuC;AACpE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAS,MAAuB,eAAe;AAC/F;AA4OA,IAAM,oBAAoB,oBAAI,IAAI;AAAA,EAChC;AAAA,EAAU;AAAA,EAAU;AAAA,EAAY;AAAA,EAAS;AAAA,EAAU;AAAA,EAAc;AAAA,EACjE;AAAA,EAAY;AAAA,EAAW;AAAA,EAAY;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAa;AACjE,CAAC;AAEM,SAAS,oBAAoB,MAA8B;AAChE,SAAO,SAAS,QAAQ,kBAAkB,IAAI,IAAI;AACpD;AAEO,SAAS,YAAY,UAA6B,KAA0B;AACjF,QAAM,WAAW,SAAS,MAAM,GAAG;AACnC,MAAI,aAAa,QAAW;AAC1B,UAAM,IAAI,kBAAkB,mBAAmB,SAAS,IAAI,kBAAkB,GAAG,eAAe;AAAA,EAClG;AACA,SAAO,OAAO,aAAa,WAAW,EAAE,UAAU,SAAS,IAAI;AACjE;","names":[]}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ContractHarness
|
|
3
|
+
} from "./chunk-RWH46IU5.js";
|
|
4
|
+
import "./chunk-3WCLMOM7.js";
|
|
5
|
+
import {
|
|
6
|
+
TRACE_AXES,
|
|
7
|
+
scanBoundaries,
|
|
8
|
+
scanFocusIndicators,
|
|
9
|
+
scanReducedMotion,
|
|
10
|
+
scanTextSpacing,
|
|
11
|
+
traceProperties
|
|
12
|
+
} from "./chunk-SHLPDNAQ.js";
|
|
13
|
+
import {
|
|
14
|
+
missingEngine,
|
|
15
|
+
recordA11y,
|
|
16
|
+
scanAxe,
|
|
17
|
+
scanFocusVisible
|
|
18
|
+
} from "./chunk-EWSGE27V.js";
|
|
19
|
+
import {
|
|
20
|
+
A11Y_THEMES_ENV,
|
|
21
|
+
UPDATE_ARIA_ENV
|
|
22
|
+
} from "./chunk-7CM5D7PF.js";
|
|
23
|
+
import {
|
|
24
|
+
isInapplicable,
|
|
25
|
+
partLocator
|
|
26
|
+
} from "./chunk-YVVR3GJZ.js";
|
|
27
|
+
import {
|
|
28
|
+
selectedContracts
|
|
29
|
+
} from "./chunk-5HNZHIHG.js";
|
|
30
|
+
|
|
31
|
+
// src/testing/component-a11y.contract.ts
|
|
32
|
+
import fs from "fs";
|
|
33
|
+
import path from "path";
|
|
34
|
+
import { fileURLToPath } from "url";
|
|
35
|
+
import { test } from "@playwright/test";
|
|
36
|
+
test.describe.configure({ mode: "parallel" });
|
|
37
|
+
var PRESETS = ["autumn", "halloween", "midnight-study", "ocean", "royal-velvet", "sketchy", "spring-meadow", "sunset"];
|
|
38
|
+
var STAGE_ATTR = "data-lt-a11y-stage";
|
|
39
|
+
var TREE_ATTR = "data-lt-a11y-tree";
|
|
40
|
+
var SCOPE = `[${STAGE_ATTR}]`;
|
|
41
|
+
var EXCLUDE = [`${SCOPE} .canvas-toolbar`, "[data-preview-demo]"];
|
|
42
|
+
var PRESET_RULES = ["color-contrast"];
|
|
43
|
+
var VIEW_LIMIT = 4;
|
|
44
|
+
var DIFF_LIMIT = 600;
|
|
45
|
+
var ARIA_HELP_URL = "https://playwright.dev/docs/aria-snapshots";
|
|
46
|
+
var STAGE_CSS = `
|
|
47
|
+
.tabs-preview::after { display: none !important; }
|
|
48
|
+
.preview-stage, .preview-stage .shadow-backdrop { background: var(--page-bg) !important; }
|
|
49
|
+
`;
|
|
50
|
+
var here = path.dirname(fileURLToPath(import.meta.url));
|
|
51
|
+
var compiled = path.basename(here) === "testing-js";
|
|
52
|
+
var sourceAria = path.resolve(here, compiled ? "../testing/contracts/__aria__" : "contracts/__aria__");
|
|
53
|
+
var ARIA_DIR = !compiled || fs.existsSync(sourceAria) ? sourceAria : path.join(here, "__aria__");
|
|
54
|
+
var ARIA_WRITABLE = !ARIA_DIR.split(path.sep).includes("node_modules");
|
|
55
|
+
function describeStep(step) {
|
|
56
|
+
if (step.kind === "control") {
|
|
57
|
+
if (step.value !== void 0) return step.value;
|
|
58
|
+
const named = /:has-text\("([^"]+)"\)/.exec(step.selector)?.[1] ?? step.selector;
|
|
59
|
+
if (step.check !== void 0) return `${named} ${step.check ? "on" : "off"}`;
|
|
60
|
+
return named;
|
|
61
|
+
}
|
|
62
|
+
if (step.kind === "press") return step.part ? `${step.key} on ${step.part}` : step.key;
|
|
63
|
+
return `${step.kind} ${step.part}`;
|
|
64
|
+
}
|
|
65
|
+
function viewsOf(contract) {
|
|
66
|
+
const declared = [...contract.properties, ...isInapplicable(contract.states) ? [] : contract.states];
|
|
67
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
68
|
+
const labels = /* @__PURE__ */ new Set();
|
|
69
|
+
for (const view of declared.length > 0 ? declared : [{}]) {
|
|
70
|
+
const merged = { ...contract.view, ...view };
|
|
71
|
+
const key = JSON.stringify([merged.variant ?? "", merged.state ?? "", merged.setup ?? []]);
|
|
72
|
+
if (byKey.has(key)) continue;
|
|
73
|
+
const own = view.setup?.length ? ` (${view.setup.map(describeStep).join(", ")})` : "";
|
|
74
|
+
const base = `${[merged.variant, merged.state].filter(Boolean).join(" / ") || "preview"}${own}`;
|
|
75
|
+
let label = base;
|
|
76
|
+
for (let n = 2; labels.has(label); n++) label = `${base} #${n}`;
|
|
77
|
+
labels.add(label);
|
|
78
|
+
byKey.set(key, { label, view });
|
|
79
|
+
}
|
|
80
|
+
return [...byKey.values()];
|
|
81
|
+
}
|
|
82
|
+
function viewGroups(contract) {
|
|
83
|
+
const views = viewsOf(contract);
|
|
84
|
+
return {
|
|
85
|
+
plain: views.filter(({ view }) => !view.setup?.length),
|
|
86
|
+
setUp: views.filter(({ view }) => view.setup?.length)
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function portalSelectors(contract) {
|
|
90
|
+
return Object.keys(contract.parts).map((key) => partLocator(contract, key)).filter((part) => part.portal).map((part) => part.selector);
|
|
91
|
+
}
|
|
92
|
+
async function markStage(page, portals) {
|
|
93
|
+
await page.evaluate(({ stageAttr, treeAttr, portals: portals2 }) => {
|
|
94
|
+
for (const element of document.querySelectorAll(`[${stageAttr}], [${treeAttr}]`)) {
|
|
95
|
+
element.removeAttribute(stageAttr);
|
|
96
|
+
element.removeAttribute(treeAttr);
|
|
97
|
+
}
|
|
98
|
+
const group = [...document.querySelectorAll(".variant-group")].find((g) => g.checkVisibility());
|
|
99
|
+
const stage = group?.querySelector(".preview-stage, .sketch-scope");
|
|
100
|
+
if (!group || !stage) throw new Error("the visible variant group has no preview stage");
|
|
101
|
+
stage.setAttribute(stageAttr, "");
|
|
102
|
+
stage.scrollIntoView({ block: "nearest" });
|
|
103
|
+
group.querySelector(".sketch-scope")?.setAttribute(treeAttr, "");
|
|
104
|
+
for (const selector of portals2) {
|
|
105
|
+
for (const element of document.querySelectorAll(selector)) {
|
|
106
|
+
if (element.closest(`[${stageAttr}]`)) continue;
|
|
107
|
+
element.setAttribute(stageAttr, "");
|
|
108
|
+
element.setAttribute(treeAttr, "");
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}, { stageAttr: STAGE_ATTR, treeAttr: TREE_ATTR, portals });
|
|
112
|
+
}
|
|
113
|
+
async function readTree(page) {
|
|
114
|
+
const roots = page.locator(`[${TREE_ATTR}]`);
|
|
115
|
+
const trees = [];
|
|
116
|
+
for (let i = 0; i < await roots.count(); i++) trees.push(await roots.nth(i).ariaSnapshot());
|
|
117
|
+
return trees.filter(Boolean).join("\n");
|
|
118
|
+
}
|
|
119
|
+
function serializeTrees(trees) {
|
|
120
|
+
return [...trees].map(([label, tree]) => tree ? `${JSON.stringify(label)}: |
|
|
121
|
+
${tree.split("\n").map((line) => ` ${line}`).join("\n")}
|
|
122
|
+
` : `${JSON.stringify(label)}: ""
|
|
123
|
+
`).join("");
|
|
124
|
+
}
|
|
125
|
+
function parseTrees(text) {
|
|
126
|
+
const trees = /* @__PURE__ */ new Map();
|
|
127
|
+
let label = null;
|
|
128
|
+
let lines = [];
|
|
129
|
+
const flush = () => {
|
|
130
|
+
if (label !== null) trees.set(label, lines.join("\n"));
|
|
131
|
+
};
|
|
132
|
+
for (const line of text.split("\n")) {
|
|
133
|
+
const key = /^(".*"): (?:\||"")$/.exec(line);
|
|
134
|
+
if (key) {
|
|
135
|
+
flush();
|
|
136
|
+
label = JSON.parse(key[1]);
|
|
137
|
+
lines = [];
|
|
138
|
+
} else if (line.startsWith(" ")) {
|
|
139
|
+
lines.push(line.slice(2));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
flush();
|
|
143
|
+
return trees;
|
|
144
|
+
}
|
|
145
|
+
function lineChanges(before, after) {
|
|
146
|
+
const added = after.split("\n");
|
|
147
|
+
const removed = [];
|
|
148
|
+
for (const line of before.split("\n")) {
|
|
149
|
+
const index = added.indexOf(line);
|
|
150
|
+
if (index >= 0) added.splice(index, 1);
|
|
151
|
+
else removed.push(line);
|
|
152
|
+
}
|
|
153
|
+
return { removed, added };
|
|
154
|
+
}
|
|
155
|
+
function describeChange(before, after) {
|
|
156
|
+
if (before === void 0) return "The view has no recorded accessibility tree";
|
|
157
|
+
if (after === void 0) return "The recorded accessibility tree names a view the contract no longer declares";
|
|
158
|
+
const { removed, added } = lineChanges(before, after);
|
|
159
|
+
const quote = (lines) => lines.length === 0 ? "nothing" : lines.slice(0, 2).map((line) => `\`${line.trim()}\``).join(", ") + (lines.length > 2 ? ` and ${lines.length - 2} more` : "");
|
|
160
|
+
return `The accessibility tree differs from the recorded one: recorded ${quote(removed)}, now ${quote(added)}`;
|
|
161
|
+
}
|
|
162
|
+
function listViews(views) {
|
|
163
|
+
return [...views.slice(0, VIEW_LIMIT), ...views.length > VIEW_LIMIT ? [`and ${views.length - VIEW_LIMIT} more`] : []].join("; ");
|
|
164
|
+
}
|
|
165
|
+
function compareTrees(id, trees) {
|
|
166
|
+
const file = path.join(ARIA_DIR, `${id}.yml`);
|
|
167
|
+
if (process.env[UPDATE_ARIA_ENV] === "1" || !fs.existsSync(file)) {
|
|
168
|
+
if (ARIA_WRITABLE) {
|
|
169
|
+
fs.mkdirSync(ARIA_DIR, { recursive: true });
|
|
170
|
+
fs.writeFileSync(file, serializeTrees(trees));
|
|
171
|
+
}
|
|
172
|
+
return [];
|
|
173
|
+
}
|
|
174
|
+
const recorded = parseTrees(fs.readFileSync(file, "utf8"));
|
|
175
|
+
const changes = /* @__PURE__ */ new Map();
|
|
176
|
+
for (const label of /* @__PURE__ */ new Set([...recorded.keys(), ...trees.keys()])) {
|
|
177
|
+
const before = recorded.get(label);
|
|
178
|
+
const after = trees.get(label);
|
|
179
|
+
if (before === after) continue;
|
|
180
|
+
const help = describeChange(before, after);
|
|
181
|
+
const { removed, added } = lineChanges(before ?? "", after ?? "");
|
|
182
|
+
let change = changes.get(help);
|
|
183
|
+
if (!change) {
|
|
184
|
+
const diff = [...removed.map((line) => `-${line}`), ...added.map((line) => `+${line}`)].join("\n");
|
|
185
|
+
changes.set(help, change = { diff, views: [] });
|
|
186
|
+
}
|
|
187
|
+
change.views.push(label);
|
|
188
|
+
}
|
|
189
|
+
return [...changes].map(([help, { diff, views }]) => ({
|
|
190
|
+
rule: "a11y-component",
|
|
191
|
+
engineRule: "aria-snapshot",
|
|
192
|
+
impact: null,
|
|
193
|
+
target: `__aria__/${id}.yml`,
|
|
194
|
+
html: diff.slice(0, DIFF_LIMIT),
|
|
195
|
+
help: `${help}. Rerun with --update-aria once the change is deliberate`,
|
|
196
|
+
helpUrl: ARIA_HELP_URL,
|
|
197
|
+
view: listViews(views)
|
|
198
|
+
}));
|
|
199
|
+
}
|
|
200
|
+
var Occurrences = class {
|
|
201
|
+
constructor(trace) {
|
|
202
|
+
this.trace = trace;
|
|
203
|
+
}
|
|
204
|
+
trace;
|
|
205
|
+
entries = /* @__PURE__ */ new Map();
|
|
206
|
+
async add(view, results) {
|
|
207
|
+
for (const item of results) {
|
|
208
|
+
const { result, trace } = "result" in item ? item : { result: item, trace: void 0 };
|
|
209
|
+
const key = [result.rule, result.engineRule, result.target].join("\n");
|
|
210
|
+
let entry = this.entries.get(key);
|
|
211
|
+
if (!entry) {
|
|
212
|
+
const variables = await this.trace(result, trace);
|
|
213
|
+
this.entries.set(key, entry = { result: variables.length > 0 ? { ...result, variables } : result, views: [] });
|
|
214
|
+
}
|
|
215
|
+
if (!entry.views.includes(view)) entry.views.push(view);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
results() {
|
|
219
|
+
return [...this.entries.values()].map(({ result, views }) => ({ ...result, view: listViews(views) }));
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
for (const contract of await selectedContracts()) {
|
|
223
|
+
test(`${contract.id} records its accessibility findings in every variant and state`, async ({ page }) => {
|
|
224
|
+
test.setTimeout(10 * 6e4);
|
|
225
|
+
const missing = await missingEngine(["axe", "alfa"]);
|
|
226
|
+
if (missing) {
|
|
227
|
+
test.info().annotations.push({ type: "inapplicable", description: missing });
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
const harness = await ContractHarness.open(page, contract);
|
|
231
|
+
await page.addStyleTag({ content: STAGE_CSS });
|
|
232
|
+
const { plain, setUp } = viewGroups(contract);
|
|
233
|
+
const portals = portalSelectors(contract);
|
|
234
|
+
const presets = process.env[A11Y_THEMES_ENV] === "all" ? PRESETS : [];
|
|
235
|
+
const known = await harness.propertyVariables();
|
|
236
|
+
const boundaries = (contract.boundaries ?? []).map(({ part, label, when }) => ({ selector: partLocator(contract, part).selector, label, when }));
|
|
237
|
+
const root = partLocator(contract, contract.root).selector;
|
|
238
|
+
const own = [root, ...portals];
|
|
239
|
+
const found = new Occurrences(async (result, request) => {
|
|
240
|
+
const axes = TRACE_AXES[result.engineRule];
|
|
241
|
+
return axes ? traceProperties(page, { selector: result.target, scope: SCOPE, ...request }, axes, known) : [];
|
|
242
|
+
});
|
|
243
|
+
const scanNonText = async (view) => {
|
|
244
|
+
await found.add(view, await scanFocusIndicators(page, SCOPE, EXCLUDE));
|
|
245
|
+
await found.add(view, await scanBoundaries(page, SCOPE, EXCLUDE, boundaries, root));
|
|
246
|
+
};
|
|
247
|
+
const trees = /* @__PURE__ */ new Map();
|
|
248
|
+
const scan = async ({ label, view }) => {
|
|
249
|
+
await harness.selectView(view);
|
|
250
|
+
await markStage(page, portals);
|
|
251
|
+
await found.add(label, await scanAxe(page, SCOPE, EXCLUDE, "a11y-component"));
|
|
252
|
+
await found.add(label, await scanFocusVisible(page, SCOPE, EXCLUDE));
|
|
253
|
+
await scanNonText(label);
|
|
254
|
+
trees.set(label, await readTree(page));
|
|
255
|
+
await found.add(label, await scanTextSpacing(page, SCOPE, EXCLUDE, own));
|
|
256
|
+
await found.add(label, await scanReducedMotion(page, SCOPE, EXCLUDE, own));
|
|
257
|
+
};
|
|
258
|
+
const scanContrast = async (label, theme) => {
|
|
259
|
+
await markStage(page, portals);
|
|
260
|
+
await found.add(`${label} under ${theme}`, await scanAxe(page, SCOPE, EXCLUDE, "a11y-component", PRESET_RULES));
|
|
261
|
+
await scanNonText(`${label} under ${theme}`);
|
|
262
|
+
};
|
|
263
|
+
for (const scanView of plain) await scan(scanView);
|
|
264
|
+
for (const theme of presets) {
|
|
265
|
+
await harness.previewThemeUncovered(theme);
|
|
266
|
+
for (const { label, view } of plain) {
|
|
267
|
+
await harness.selectView(view);
|
|
268
|
+
await scanContrast(label, theme);
|
|
269
|
+
}
|
|
270
|
+
await harness.cancelUncoveredPreview();
|
|
271
|
+
}
|
|
272
|
+
for (const scanView of setUp) {
|
|
273
|
+
await scan(scanView);
|
|
274
|
+
for (const theme of presets) {
|
|
275
|
+
await harness.previewThemeUncovered(theme);
|
|
276
|
+
await scanContrast(scanView.label, theme);
|
|
277
|
+
await harness.cancelUncoveredPreview();
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
recordA11y([...found.results(), ...compareTrees(contract.id, trees)]);
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
//# sourceMappingURL=component-a11y.contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../testing/component-a11y.contract.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { test, type Page } from '@playwright/test';\nimport {\n isInapplicable,\n partLocator,\n type ComponentContract,\n type SetupStep,\n type View,\n} from './componentContract';\nimport { A11Y_THEMES_ENV, UPDATE_ARIA_ENV } from './config';\nimport { selectedContracts } from './contracts';\nimport { missingEngine, recordA11y, scanAxe, scanFocusVisible, type A11yResult } from './support/a11y';\nimport { ContractHarness } from './support/contractHarness';\nimport { scanBoundaries, scanFocusIndicators, type CheckedResult, type TraceRequest } from './support/nonTextContrast';\nimport { scanReducedMotion, scanTextSpacing } from './support/textSpacingAndMotion';\nimport { TRACE_AXES, traceProperties } from './support/traceProperties';\n\n// Each test opens its own editor and only reads the preview, so the\n// components scan side by side.\ntest.describe.configure({ mode: 'parallel' });\n\nconst PRESETS = ['autumn', 'halloween', 'midnight-study', 'ocean', 'royal-velvet', 'sketchy', 'spring-meadow', 'sunset'];\n\nconst STAGE_ATTR = 'data-lt-a11y-stage';\nconst TREE_ATTR = 'data-lt-a11y-tree';\nconst SCOPE = `[${STAGE_ATTR}]`;\nconst EXCLUDE = [`${SCOPE} .canvas-toolbar`, '[data-preview-demo]'];\n\n// The Wave 0 probe found every other axe rule, and SIA-R65, reporting the\n// same nodes under all nine themes. The non-text contrast checks measure\n// colour, so they run under each preset too.\nconst PRESET_RULES = ['color-contrast'];\n\nconst VIEW_LIMIT = 4;\nconst DIFF_LIMIT = 600;\nconst ARIA_HELP_URL = 'https://playwright.dev/docs/aria-snapshots';\n\n// axe returns every text node under the sticky band's full-size fade as\n// incomplete, and the default backdrop paints an editor colour no theme moves.\nconst STAGE_CSS = `\n.tabs-preview::after { display: none !important; }\n.preview-stage, .preview-stage .shadow-backdrop { background: var(--page-bg) !important; }\n`;\n\nconst here = path.dirname(fileURLToPath(import.meta.url));\nconst compiled = path.basename(here) === 'testing-js';\nconst sourceAria = path.resolve(here, compiled ? '../testing/contracts/__aria__' : 'contracts/__aria__');\n// A tarball ships the compiled suite alone, with the recorded trees copied\n// beside it; the repository's compiled suite still reads and writes the source.\nconst ARIA_DIR = !compiled || fs.existsSync(sourceAria) ? sourceAria : path.join(here, '__aria__');\nconst ARIA_WRITABLE = !ARIA_DIR.split(path.sep).includes('node_modules');\n\ninterface ScanView {\n label: string;\n view: View;\n}\n\nfunction describeStep(step: SetupStep): string {\n if (step.kind === 'control') {\n if (step.value !== undefined) return step.value;\n const named = /:has-text\\(\"([^\"]+)\"\\)/.exec(step.selector)?.[1] ?? step.selector;\n if (step.check !== undefined) return `${named} ${step.check ? 'on' : 'off'}`;\n return named;\n }\n if (step.kind === 'press') return step.part ? `${step.key} on ${step.part}` : step.key;\n return `${step.kind} ${step.part}`;\n}\n\n/** Every variant and state pair the contract's properties and states name,\n * once each, in the order the contract declares them. */\nfunction viewsOf(contract: ComponentContract): ScanView[] {\n const declared: View[] = [...contract.properties, ...(isInapplicable(contract.states) ? [] : contract.states)];\n const byKey = new Map<string, ScanView>();\n const labels = new Set<string>();\n for (const view of declared.length > 0 ? declared : [{}]) {\n const merged = { ...contract.view, ...view };\n const key = JSON.stringify([merged.variant ?? '', merged.state ?? '', merged.setup ?? []]);\n if (byKey.has(key)) continue;\n const own = view.setup?.length ? ` (${view.setup.map(describeStep).join(', ')})` : '';\n const base = `${[merged.variant, merged.state].filter(Boolean).join(' / ') || 'preview'}${own}`;\n let label = base;\n for (let n = 2; labels.has(label); n++) label = `${base} #${n}`;\n labels.add(label);\n byKey.set(key, { label, view });\n }\n return [...byKey.values()];\n}\n\n/** Plain views select their tabs again under each preset. A view with a\n * setup of its own is selected once and scanned with every preset previewed\n * over it: a toolbar control the setup sets, such as Button's Small size,\n * stays set and hides the tabs a second selection would click. */\nfunction viewGroups(contract: ComponentContract): { plain: ScanView[]; setUp: ScanView[] } {\n const views = viewsOf(contract);\n return {\n plain: views.filter(({ view }) => !view.setup?.length),\n setUp: views.filter(({ view }) => view.setup?.length),\n };\n}\n\nfunction portalSelectors(contract: ComponentContract): string[] {\n return Object.keys(contract.parts)\n .map((key) => partLocator(contract, key))\n .filter((part) => part.portal)\n .map((part) => part.selector);\n}\n\n/** Marks the visible stage and every portaled part, so both engines scope to\n * one selector, and the component's own tree for the snapshot. */\nasync function markStage(page: Page, portals: string[]): Promise<void> {\n await page.evaluate(({ stageAttr, treeAttr, portals }) => {\n for (const element of document.querySelectorAll(`[${stageAttr}], [${treeAttr}]`)) {\n element.removeAttribute(stageAttr);\n element.removeAttribute(treeAttr);\n }\n const group = [...document.querySelectorAll<HTMLElement>('.variant-group')].find((g) => g.checkVisibility());\n const stage = group?.querySelector('.preview-stage, .sketch-scope');\n if (!group || !stage) throw new Error('the visible variant group has no preview stage');\n stage.setAttribute(stageAttr, '');\n stage.scrollIntoView({ block: 'nearest' });\n group.querySelector('.sketch-scope')?.setAttribute(treeAttr, '');\n for (const selector of portals) {\n for (const element of document.querySelectorAll(selector)) {\n if (element.closest(`[${stageAttr}]`)) continue;\n element.setAttribute(stageAttr, '');\n element.setAttribute(treeAttr, '');\n }\n }\n }, { stageAttr: STAGE_ATTR, treeAttr: TREE_ATTR, portals });\n}\n\nasync function readTree(page: Page): Promise<string> {\n const roots = page.locator(`[${TREE_ATTR}]`);\n const trees: string[] = [];\n for (let i = 0; i < await roots.count(); i++) trees.push(await roots.nth(i).ariaSnapshot());\n return trees.filter(Boolean).join('\\n');\n}\n\nfunction serializeTrees(trees: Map<string, string>): string {\n return [...trees].map(([label, tree]) => tree\n ? `${JSON.stringify(label)}: |\\n${tree.split('\\n').map((line) => ` ${line}`).join('\\n')}\\n`\n : `${JSON.stringify(label)}: \"\"\\n`).join('');\n}\n\nfunction parseTrees(text: string): Map<string, string> {\n const trees = new Map<string, string>();\n let label: string | null = null;\n let lines: string[] = [];\n const flush = () => {\n if (label !== null) trees.set(label, lines.join('\\n'));\n };\n for (const line of text.split('\\n')) {\n const key = /^(\".*\"): (?:\\||\"\")$/.exec(line);\n if (key) {\n flush();\n label = JSON.parse(key[1]) as string;\n lines = [];\n } else if (line.startsWith(' ')) {\n lines.push(line.slice(2));\n }\n }\n flush();\n return trees;\n}\n\nfunction lineChanges(before: string, after: string): { removed: string[]; added: string[] } {\n const added = after.split('\\n');\n const removed: string[] = [];\n for (const line of before.split('\\n')) {\n const index = added.indexOf(line);\n if (index >= 0) added.splice(index, 1);\n else removed.push(line);\n }\n return { removed, added };\n}\n\nfunction describeChange(before: string | undefined, after: string | undefined): string {\n if (before === undefined) return 'The view has no recorded accessibility tree';\n if (after === undefined) return 'The recorded accessibility tree names a view the contract no longer declares';\n const { removed, added } = lineChanges(before, after);\n const quote = (lines: string[]) => lines.length === 0 ? 'nothing' : lines.slice(0, 2).map((line) => `\\`${line.trim()}\\``).join(', ')\n + (lines.length > 2 ? ` and ${lines.length - 2} more` : '');\n return `The accessibility tree differs from the recorded one: recorded ${quote(removed)}, now ${quote(added)}`;\n}\n\nfunction listViews(views: string[]): string {\n return [...views.slice(0, VIEW_LIMIT), ...(views.length > VIEW_LIMIT ? [`and ${views.length - VIEW_LIMIT} more`] : [])]\n .join('; ');\n}\n\n/** Decision 12: each view's tree beside the one recorded for it, one result\n * per distinct change. A missing file, or `--update-aria`, records the trees\n * instead. */\nfunction compareTrees(id: string, trees: Map<string, string>): A11yResult[] {\n const file = path.join(ARIA_DIR, `${id}.yml`);\n if (process.env[UPDATE_ARIA_ENV] === '1' || !fs.existsSync(file)) {\n if (ARIA_WRITABLE) {\n fs.mkdirSync(ARIA_DIR, { recursive: true });\n fs.writeFileSync(file, serializeTrees(trees));\n }\n return [];\n }\n const recorded = parseTrees(fs.readFileSync(file, 'utf8'));\n const changes = new Map<string, { diff: string; views: string[] }>();\n for (const label of new Set([...recorded.keys(), ...trees.keys()])) {\n const before = recorded.get(label);\n const after = trees.get(label);\n if (before === after) continue;\n const help = describeChange(before, after);\n const { removed, added } = lineChanges(before ?? '', after ?? '');\n let change = changes.get(help);\n if (!change) {\n const diff = [...removed.map((line) => `-${line}`), ...added.map((line) => `+${line}`)].join('\\n');\n changes.set(help, (change = { diff, views: [] }));\n }\n change.views.push(label);\n }\n return [...changes].map(([help, { diff, views }]) => ({\n rule: 'a11y-component',\n engineRule: 'aria-snapshot',\n impact: null,\n target: `__aria__/${id}.yml`,\n html: diff.slice(0, DIFF_LIMIT),\n help: `${help}. Rerun with --update-aria once the change is deliberate`,\n helpUrl: ARIA_HELP_URL,\n view: listViews(views),\n }));\n}\n\n/** One result per element and engine rule, naming every view it failed in.\n * A result traces once, the first time it appears, while its view is on\n * screen (decision 4). */\nclass Occurrences {\n private readonly entries = new Map<string, { result: A11yResult; views: string[] }>();\n\n constructor(private readonly trace: (result: A11yResult, request?: TraceRequest) => Promise<string[]>) {}\n\n async add(view: string, results: (A11yResult | CheckedResult)[]): Promise<void> {\n for (const item of results) {\n const { result, trace } = 'result' in item ? item : { result: item, trace: undefined };\n const key = [result.rule, result.engineRule, result.target].join('\\n');\n let entry = this.entries.get(key);\n if (!entry) {\n const variables = await this.trace(result, trace);\n this.entries.set(key, (entry = { result: variables.length > 0 ? { ...result, variables } : result, views: [] }));\n }\n if (!entry.views.includes(view)) entry.views.push(view);\n }\n }\n\n results(): A11yResult[] {\n return [...this.entries.values()].map(({ result, views }) => ({ ...result, view: listViews(views) }));\n }\n}\n\nfor (const contract of await selectedContracts()) {\n test(`${contract.id} records its accessibility findings in every variant and state`, async ({ page }) => {\n test.setTimeout(10 * 60_000);\n const missing = await missingEngine(['axe', 'alfa']);\n if (missing) {\n test.info().annotations.push({ type: 'inapplicable', description: missing });\n return;\n }\n\n const harness = await ContractHarness.open(page, contract);\n await page.addStyleTag({ content: STAGE_CSS });\n const { plain, setUp } = viewGroups(contract);\n const portals = portalSelectors(contract);\n const presets = process.env[A11Y_THEMES_ENV] === 'all' ? PRESETS : [];\n const known = await harness.propertyVariables();\n const boundaries = (contract.boundaries ?? []).map(({ part, label, when }) => ({ selector: partLocator(contract, part).selector, label, when }));\n const root = partLocator(contract, contract.root).selector;\n const own = [root, ...portals];\n const found = new Occurrences(async (result, request) => {\n const axes = TRACE_AXES[result.engineRule];\n return axes ? traceProperties(page, { selector: result.target, scope: SCOPE, ...request }, axes, known) : [];\n });\n const scanNonText = async (view: string) => {\n await found.add(view, await scanFocusIndicators(page, SCOPE, EXCLUDE));\n await found.add(view, await scanBoundaries(page, SCOPE, EXCLUDE, boundaries, root));\n };\n const trees = new Map<string, string>();\n\n const scan = async ({ label, view }: ScanView) => {\n await harness.selectView(view);\n await markStage(page, portals);\n await found.add(label, await scanAxe(page, SCOPE, EXCLUDE, 'a11y-component'));\n await found.add(label, await scanFocusVisible(page, SCOPE, EXCLUDE));\n await scanNonText(label);\n trees.set(label, await readTree(page));\n await found.add(label, await scanTextSpacing(page, SCOPE, EXCLUDE, own));\n await found.add(label, await scanReducedMotion(page, SCOPE, EXCLUDE, own));\n };\n const scanContrast = async (label: string, theme: string) => {\n await markStage(page, portals);\n await found.add(`${label} under ${theme}`, await scanAxe(page, SCOPE, EXCLUDE, 'a11y-component', PRESET_RULES));\n await scanNonText(`${label} under ${theme}`);\n };\n\n for (const scanView of plain) await scan(scanView);\n for (const theme of presets) {\n await harness.previewThemeUncovered(theme);\n for (const { label, view } of plain) {\n await harness.selectView(view);\n await scanContrast(label, theme);\n }\n await harness.cancelUncoveredPreview();\n }\n for (const scanView of setUp) {\n await scan(scanView);\n for (const theme of presets) {\n await harness.previewThemeUncovered(theme);\n await scanContrast(scanView.label, theme);\n await harness.cancelUncoveredPreview();\n }\n }\n\n recordA11y([...found.results(), ...compareTrees(contract.id, trees)]);\n });\n}\n"],"mappings":"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","names":["portals"]}
|
|
@@ -2,10 +2,10 @@ import {
|
|
|
2
2
|
ContractViolation,
|
|
3
3
|
isInapplicable,
|
|
4
4
|
partLocator
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-YVVR3GJZ.js";
|
|
6
6
|
import {
|
|
7
7
|
selectedContracts
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-5HNZHIHG.js";
|
|
9
9
|
|
|
10
10
|
// src/testing/component-behavior.contract.ts
|
|
11
11
|
import { existsSync } from "fs";
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ContractHarness
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-RWH46IU5.js";
|
|
4
|
+
import "./chunk-3WCLMOM7.js";
|
|
5
|
+
import "./chunk-EWSGE27V.js";
|
|
6
|
+
import "./chunk-7CM5D7PF.js";
|
|
5
7
|
import {
|
|
6
8
|
ContractViolation,
|
|
7
9
|
isInapplicable
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-NB3NZRBM.js";
|
|
10
|
+
} from "./chunk-YVVR3GJZ.js";
|
|
10
11
|
import {
|
|
11
12
|
allContracts,
|
|
12
13
|
selectedContracts
|
|
13
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-5HNZHIHG.js";
|
|
14
15
|
|
|
15
16
|
// src/testing/component-editor.contract.ts
|
|
16
17
|
import { test } from "@playwright/test";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../testing/component-editor.contract.ts"],"sourcesContent":["import { test } from '@playwright/test';\nimport { ContractViolation, isInapplicable } from './componentContract';\nimport { allContracts, selectedContracts } from './contracts';\nimport { ContractHarness } from './support/contractHarness';\n\nconst selected = await selectedContracts();\nconst requested = process.env.LIVE_TOKENS_COMPONENT;\n\n// A failing test rather than a throw from the config: the JSON report then\n// carries the rule, and the runner maps it like any other violation instead\n// of reading a stderr tail.\nif (requested && selected.length === 0) {\n const declared = (await allContracts()).map((contract) => contract.id).sort();\n test.describe.serial(requested, () => {\n test(`${requested} has a component contract`, () => {\n throw new ContractViolation(\n 'contract-missing',\n requested,\n 'no component contract is declared for it. Export one from the module '\n + `\\`contractsModule\\` names in live-tokens.testing.ts. Declared: ${declared.join(', ')}`,\n );\n });\n });\n}\n\nfor (const contract of selected) {\n // Serial, so a component that is not registered reports that and stops rather\n // than reporting six preview failures caused by the missing entry.\n test.describe.serial(contract.id, () => {\n test(`${contract.id} is listed in its registry group`, async ({ page }) => {\n const harness = await ContractHarness.attach(page, contract);\n await harness.assertListed();\n });\n\n test(`${contract.id} declares every part and every shipped alias`, async ({ page }) => {\n const harness = await ContractHarness.open(page, contract);\n await harness.assertInventory();\n });\n\n test(`${contract.id} resolves every alias it paints with`, async ({ page }) => {\n const harness = await ContractHarness.open(page, contract);\n await harness.assertAliasesResolve();\n });\n\n test(`${contract.id} previews the state being edited`, async ({ page }) => {\n const harness = await ContractHarness.open(page, contract);\n if (isInapplicable(contract.states)) {\n test.info().annotations.push({ type: 'inapplicable', description: contract.states.reason });\n }\n await harness.assertStates();\n });\n\n test(`${contract.id} answers the pointer and the keyboard`, async ({ page }) => {\n const harness = await ContractHarness.open(page, contract);\n if (isInapplicable(contract.interaction)) {\n test.info().annotations.push({ type: 'inapplicable', description: contract.interaction.reason });\n }\n await harness.assertInteraction();\n });\n\n test(`${contract.id} persists an edit and resets to the saved config`, async ({ page }) => {\n test.setTimeout(180_000);\n const harness = await ContractHarness.open(page, contract);\n await harness.assertPersistence();\n });\n\n test(`${contract.id} takes the theme's values and gives them back`, async ({ page }) => {\n const harness = await ContractHarness.open(page, contract);\n await harness.assertThemeProjection(contract.theme);\n });\n\n test(`${contract.id} draws every painted part in Sketch mode`, async ({ page }) => {\n const harness = await ContractHarness.open(page, contract);\n const sketch = contract.sketch;\n if (isInapplicable(sketch)) {\n test.info().annotations.push({ type: 'inapplicable', description: sketch.reason });\n await harness.assertNoSketchPaint('pencil');\n return;\n }\n await harness.assertSketchPaint(sketch);\n });\n });\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../testing/component-editor.contract.ts"],"sourcesContent":["import { test } from '@playwright/test';\nimport { ContractViolation, isInapplicable } from './componentContract';\nimport { allContracts, selectedContracts } from './contracts';\nimport { ContractHarness } from './support/contractHarness';\n\nconst selected = await selectedContracts();\nconst requested = process.env.LIVE_TOKENS_COMPONENT;\n\n// A failing test rather than a throw from the config: the JSON report then\n// carries the rule, and the runner maps it like any other violation instead\n// of reading a stderr tail.\nif (requested && selected.length === 0) {\n const declared = (await allContracts()).map((contract) => contract.id).sort();\n test.describe.serial(requested, () => {\n test(`${requested} has a component contract`, () => {\n throw new ContractViolation(\n 'contract-missing',\n requested,\n 'no component contract is declared for it. Export one from the module '\n + `\\`contractsModule\\` names in live-tokens.testing.ts. Declared: ${declared.join(', ')}`,\n );\n });\n });\n}\n\nfor (const contract of selected) {\n // Serial, so a component that is not registered reports that and stops rather\n // than reporting six preview failures caused by the missing entry.\n test.describe.serial(contract.id, () => {\n test(`${contract.id} is listed in its registry group`, async ({ page }) => {\n const harness = await ContractHarness.attach(page, contract);\n await harness.assertListed();\n });\n\n test(`${contract.id} declares every part and every shipped alias`, async ({ page }) => {\n const harness = await ContractHarness.open(page, contract);\n await harness.assertInventory();\n });\n\n test(`${contract.id} resolves every alias it paints with`, async ({ page }) => {\n const harness = await ContractHarness.open(page, contract);\n await harness.assertAliasesResolve();\n });\n\n test(`${contract.id} previews the state being edited`, async ({ page }) => {\n const harness = await ContractHarness.open(page, contract);\n if (isInapplicable(contract.states)) {\n test.info().annotations.push({ type: 'inapplicable', description: contract.states.reason });\n }\n await harness.assertStates();\n });\n\n test(`${contract.id} answers the pointer and the keyboard`, async ({ page }) => {\n const harness = await ContractHarness.open(page, contract);\n if (isInapplicable(contract.interaction)) {\n test.info().annotations.push({ type: 'inapplicable', description: contract.interaction.reason });\n }\n await harness.assertInteraction();\n });\n\n test(`${contract.id} persists an edit and resets to the saved config`, async ({ page }) => {\n test.setTimeout(180_000);\n const harness = await ContractHarness.open(page, contract);\n await harness.assertPersistence();\n });\n\n test(`${contract.id} takes the theme's values and gives them back`, async ({ page }) => {\n const harness = await ContractHarness.open(page, contract);\n await harness.assertThemeProjection(contract.theme);\n });\n\n test(`${contract.id} draws every painted part in Sketch mode`, async ({ page }) => {\n const harness = await ContractHarness.open(page, contract);\n const sketch = contract.sketch;\n if (isInapplicable(sketch)) {\n test.info().annotations.push({ type: 'inapplicable', description: sketch.reason });\n await harness.assertNoSketchPaint('pencil');\n return;\n }\n await harness.assertSketchPaint(sketch);\n });\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAAA,SAAS,YAAY;AAKrB,IAAM,WAAW,MAAM,kBAAkB;AACzC,IAAM,YAAY,QAAQ,IAAI;AAK9B,IAAI,aAAa,SAAS,WAAW,GAAG;AACtC,QAAM,YAAY,MAAM,aAAa,GAAG,IAAI,CAAC,aAAa,SAAS,EAAE,EAAE,KAAK;AAC5E,OAAK,SAAS,OAAO,WAAW,MAAM;AACpC,SAAK,GAAG,SAAS,6BAA6B,MAAM;AAClD,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA,uIACoE,SAAS,KAAK,IAAI,CAAC;AAAA,MACzF;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACH;AAEA,WAAW,YAAY,UAAU;AAG/B,OAAK,SAAS,OAAO,SAAS,IAAI,MAAM;AACtC,SAAK,GAAG,SAAS,EAAE,oCAAoC,OAAO,EAAE,KAAK,MAAM;AACzE,YAAM,UAAU,MAAM,gBAAgB,OAAO,MAAM,QAAQ;AAC3D,YAAM,QAAQ,aAAa;AAAA,IAC7B,CAAC;AAED,SAAK,GAAG,SAAS,EAAE,gDAAgD,OAAO,EAAE,KAAK,MAAM;AACrF,YAAM,UAAU,MAAM,gBAAgB,KAAK,MAAM,QAAQ;AACzD,YAAM,QAAQ,gBAAgB;AAAA,IAChC,CAAC;AAED,SAAK,GAAG,SAAS,EAAE,wCAAwC,OAAO,EAAE,KAAK,MAAM;AAC7E,YAAM,UAAU,MAAM,gBAAgB,KAAK,MAAM,QAAQ;AACzD,YAAM,QAAQ,qBAAqB;AAAA,IACrC,CAAC;AAED,SAAK,GAAG,SAAS,EAAE,oCAAoC,OAAO,EAAE,KAAK,MAAM;AACzE,YAAM,UAAU,MAAM,gBAAgB,KAAK,MAAM,QAAQ;AACzD,UAAI,eAAe,SAAS,MAAM,GAAG;AACnC,aAAK,KAAK,EAAE,YAAY,KAAK,EAAE,MAAM,gBAAgB,aAAa,SAAS,OAAO,OAAO,CAAC;AAAA,MAC5F;AACA,YAAM,QAAQ,aAAa;AAAA,IAC7B,CAAC;AAED,SAAK,GAAG,SAAS,EAAE,yCAAyC,OAAO,EAAE,KAAK,MAAM;AAC9E,YAAM,UAAU,MAAM,gBAAgB,KAAK,MAAM,QAAQ;AACzD,UAAI,eAAe,SAAS,WAAW,GAAG;AACxC,aAAK,KAAK,EAAE,YAAY,KAAK,EAAE,MAAM,gBAAgB,aAAa,SAAS,YAAY,OAAO,CAAC;AAAA,MACjG;AACA,YAAM,QAAQ,kBAAkB;AAAA,IAClC,CAAC;AAED,SAAK,GAAG,SAAS,EAAE,oDAAoD,OAAO,EAAE,KAAK,MAAM;AACzF,WAAK,WAAW,IAAO;AACvB,YAAM,UAAU,MAAM,gBAAgB,KAAK,MAAM,QAAQ;AACzD,YAAM,QAAQ,kBAAkB;AAAA,IAClC,CAAC;AAED,SAAK,GAAG,SAAS,EAAE,iDAAiD,OAAO,EAAE,KAAK,MAAM;AACtF,YAAM,UAAU,MAAM,gBAAgB,KAAK,MAAM,QAAQ;AACzD,YAAM,QAAQ,sBAAsB,SAAS,KAAK;AAAA,IACpD,CAAC;AAED,SAAK,GAAG,SAAS,EAAE,4CAA4C,OAAO,EAAE,KAAK,MAAM;AACjF,YAAM,UAAU,MAAM,gBAAgB,KAAK,MAAM,QAAQ;AACzD,YAAM,SAAS,SAAS;AACxB,UAAI,eAAe,MAAM,GAAG;AAC1B,aAAK,KAAK,EAAE,YAAY,KAAK,EAAE,MAAM,gBAAgB,aAAa,OAAO,OAAO,CAAC;AACjF,cAAM,QAAQ,oBAAoB,QAAQ;AAC1C;AAAA,MACF;AACA,YAAM,QAAQ,kBAAkB,MAAM;AAAA,IACxC,CAAC;AAAA,EACH,CAAC;AACH;","names":[]}
|