@motion-proto/live-tokens 0.98.2 → 0.99.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +67 -0
- package/bin/contractRunner.mjs +2 -2
- package/bin/rules/a11y.mjs +1 -1
- package/dist-plugin/index.cjs +71 -15
- package/dist-plugin/index.js +71 -15
- package/package.json +1 -1
- package/src/editor/bootstrap.ts +3 -1
- package/src/editor/component-editor/SectionDividerEditor.svelte +9 -10
- package/src/editor/core/a11y/a11yReport.ts +85 -22
- package/src/editor/core/a11y/a11yReportTypes.ts +11 -3
- package/src/editor/core/a11y/contrastFixes.ts +41 -0
- package/src/editor/core/palettes/contrastPairs.ts +4 -1
- package/src/editor/docs/content/accessibility.md +67 -18
- package/src/editor/docs/content/editing-tokens.md +4 -0
- package/src/editor/docs/content.generated.ts +2 -2
- package/src/editor/pages/ComponentEditorPage.svelte +4 -3
- package/src/editor/pages/EditorShell.svelte +10 -8
- package/src/editor/styles/ui-editor.css +2 -0
- package/src/editor/ui/a11y/A11yByEye.svelte +264 -0
- package/src/editor/ui/a11y/A11yComponentIssues.svelte +34 -16
- package/src/editor/ui/a11y/A11yFindingRow.svelte +66 -4
- package/src/editor/ui/a11y/A11yIssueRow.svelte +55 -24
- package/src/editor/ui/a11y/A11yOverview.svelte +72 -68
- package/src/editor/ui/a11y/A11yPanel.svelte +29 -4
- package/src/editor/ui/a11y/A11yPropertyMark.svelte +8 -8
- package/src/editor/ui/a11y/A11yRailMark.svelte +11 -4
- package/src/editor/ui/a11y/AccessibilityTab.svelte +22 -35
- package/src/editor/ui/a11y/a11yView.ts +50 -11
- package/src/live-tokens/data/colors-and-type/halloween.json +1 -1
- package/src/live-tokens/data/themes/autumn.json +16 -16
- package/src/live-tokens/data/themes/halloween.json +17 -17
- package/src/live-tokens/data/themes/midnight-study.json +16 -16
- package/src/live-tokens/data/themes/ocean.json +16 -16
- package/src/live-tokens/data/themes/royal-velvet.json +16 -16
- package/src/live-tokens/data/themes/sketchy.json +16 -16
- package/src/live-tokens/data/themes/spring-meadow.json +16 -16
- package/src/live-tokens/data/themes/sunset.json +16 -16
- package/src/system/components/Button.svelte +3 -3
- package/src/system/components/InlineEditActions.svelte +1 -1
- package/src/system/components/Input.svelte +1 -1
- package/src/system/components/MenuSelect.svelte +1 -1
- package/src/system/components/ProgressBar.svelte +1 -1
- package/src/system/components/SlidePager.svelte +1 -1
- package/src/system/components/Slider.svelte +6 -6
- package/src/system/components/TabBar.svelte +1 -1
- package/src/system/components/Toggle.svelte +1 -1
- package/src/testing-js/{chunk-RWH46IU5.js → chunk-7QXUJRSO.js} +2 -2
- package/src/testing-js/chunk-GZKUYLDW.js +193 -0
- package/src/testing-js/chunk-GZKUYLDW.js.map +1 -0
- package/src/testing-js/{chunk-SHLPDNAQ.js → chunk-RTZQLW3F.js} +172 -181
- package/src/testing-js/chunk-RTZQLW3F.js.map +1 -0
- package/src/testing-js/{chunk-5HNZHIHG.js → chunk-RWOT2XDB.js} +2 -2
- package/src/testing-js/{chunk-5HNZHIHG.js.map → chunk-RWOT2XDB.js.map} +1 -1
- package/src/testing-js/{chunk-R54I7YL6.js → chunk-THWGMSRH.js} +2 -2
- package/src/testing-js/component-a11y.contract.js +53 -23
- package/src/testing-js/component-a11y.contract.js.map +1 -1
- package/src/testing-js/component-behavior.contract.js +1 -1
- package/src/testing-js/component-editor.contract.js +3 -3
- package/src/testing-js/component-render.contract.js +3 -3
- package/src/testing-js/index.js +4 -4
- package/src/testing-js/index.js.map +1 -1
- package/src/testing-js/page-compliance.contract.js +3 -3
- package/src/testing-js/vitest.js +2 -2
- package/template/_gitignore +3 -0
- package/src/testing-js/chunk-EWSGE27V.js +0 -168
- package/src/testing-js/chunk-EWSGE27V.js.map +0 -1
- package/src/testing-js/chunk-SHLPDNAQ.js.map +0 -1
- /package/src/testing-js/{chunk-RWH46IU5.js.map → chunk-7QXUJRSO.js.map} +0 -0
- /package/src/testing-js/{chunk-R54I7YL6.js.map → chunk-THWGMSRH.js.map} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CONTRACTS_MODULE_ENV
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-RWOT2XDB.js";
|
|
4
4
|
|
|
5
5
|
// src/testing/vitest.ts
|
|
6
6
|
import path from "path";
|
|
@@ -54,4 +54,4 @@ function createVitestConfig(viteConfig, options = {}) {
|
|
|
54
54
|
export {
|
|
55
55
|
createVitestConfig
|
|
56
56
|
};
|
|
57
|
-
//# sourceMappingURL=chunk-
|
|
57
|
+
//# sourceMappingURL=chunk-THWGMSRH.js.map
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ContractHarness
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-7QXUJRSO.js";
|
|
4
4
|
import "./chunk-3WCLMOM7.js";
|
|
5
5
|
import {
|
|
6
|
-
TRACE_AXES,
|
|
7
|
-
scanBoundaries,
|
|
8
|
-
scanFocusIndicators,
|
|
9
6
|
scanReducedMotion,
|
|
10
|
-
scanTextSpacing
|
|
11
|
-
|
|
12
|
-
} from "./chunk-SHLPDNAQ.js";
|
|
7
|
+
scanTextSpacing
|
|
8
|
+
} from "./chunk-GZKUYLDW.js";
|
|
13
9
|
import {
|
|
10
|
+
TRACE_AXES,
|
|
14
11
|
missingEngine,
|
|
15
12
|
recordA11y,
|
|
16
13
|
scanAxe,
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
scanBoundaries,
|
|
15
|
+
scanFocusIndicators,
|
|
16
|
+
scanFocusVisible,
|
|
17
|
+
traceProperties
|
|
18
|
+
} from "./chunk-RTZQLW3F.js";
|
|
19
19
|
import {
|
|
20
20
|
A11Y_THEMES_ENV,
|
|
21
21
|
UPDATE_ARIA_ENV
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
} from "./chunk-YVVR3GJZ.js";
|
|
27
27
|
import {
|
|
28
28
|
selectedContracts
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-RWOT2XDB.js";
|
|
30
30
|
|
|
31
31
|
// src/testing/component-a11y.contract.ts
|
|
32
32
|
import fs from "fs";
|
|
@@ -89,6 +89,34 @@ function viewGroups(contract) {
|
|
|
89
89
|
function portalSelectors(contract) {
|
|
90
90
|
return Object.keys(contract.parts).map((key) => partLocator(contract, key)).filter((part) => part.portal).map((part) => part.selector);
|
|
91
91
|
}
|
|
92
|
+
var LIFTED_ATTR = "data-lt-a11y-lifted";
|
|
93
|
+
function forcedClass(contract, view) {
|
|
94
|
+
if (isInapplicable(contract.states)) return void 0;
|
|
95
|
+
const variant = view.variant ?? contract.view?.variant;
|
|
96
|
+
return contract.states.find((s) => s.state === view.state && (s.variant ?? contract.view?.variant) === variant)?.forceClass;
|
|
97
|
+
}
|
|
98
|
+
async function withoutForcedState(page, forceClass, fn) {
|
|
99
|
+
if (!forceClass) return fn();
|
|
100
|
+
await page.evaluate(({ scope, forceClass: forceClass2, attr }) => {
|
|
101
|
+
for (const stage of document.querySelectorAll(scope)) {
|
|
102
|
+
for (const el of [stage, ...stage.querySelectorAll(`.${forceClass2}`)]) {
|
|
103
|
+
if (!el.classList.contains(forceClass2)) continue;
|
|
104
|
+
el.classList.remove(forceClass2);
|
|
105
|
+
el.setAttribute(attr, forceClass2);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}, { scope: SCOPE, forceClass, attr: LIFTED_ATTR });
|
|
109
|
+
try {
|
|
110
|
+
await fn();
|
|
111
|
+
} finally {
|
|
112
|
+
await page.evaluate((attr) => {
|
|
113
|
+
for (const el of document.querySelectorAll(`[${attr}]`)) {
|
|
114
|
+
el.classList.add(el.getAttribute(attr));
|
|
115
|
+
el.removeAttribute(attr);
|
|
116
|
+
}
|
|
117
|
+
}, LIFTED_ATTR);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
92
120
|
async function markStage(page, portals) {
|
|
93
121
|
await page.evaluate(({ stageAttr, treeAttr, portals: portals2 }) => {
|
|
94
122
|
for (const element of document.querySelectorAll(`[${stageAttr}], [${treeAttr}]`)) {
|
|
@@ -240,32 +268,34 @@ for (const contract of await selectedContracts()) {
|
|
|
240
268
|
const axes = TRACE_AXES[result.engineRule];
|
|
241
269
|
return axes ? traceProperties(page, { selector: result.target, scope: SCOPE, ...request }, axes, known) : [];
|
|
242
270
|
});
|
|
243
|
-
const
|
|
244
|
-
await found.add(
|
|
245
|
-
await found.add(
|
|
246
|
-
};
|
|
271
|
+
const scanFocus = (view, label, withAlfa) => withoutForcedState(page, forcedClass(contract, view), async () => {
|
|
272
|
+
await found.add(label, await scanFocusIndicators(page, SCOPE, EXCLUDE));
|
|
273
|
+
if (withAlfa) await found.add(label, await scanFocusVisible(page, SCOPE, EXCLUDE));
|
|
274
|
+
});
|
|
247
275
|
const trees = /* @__PURE__ */ new Map();
|
|
248
276
|
const scan = async ({ label, view }) => {
|
|
249
277
|
await harness.selectView(view);
|
|
250
278
|
await markStage(page, portals);
|
|
251
279
|
await found.add(label, await scanAxe(page, SCOPE, EXCLUDE, "a11y-component"));
|
|
252
|
-
await
|
|
253
|
-
await
|
|
280
|
+
await scanFocus(view, label, true);
|
|
281
|
+
await found.add(label, await scanBoundaries(page, SCOPE, EXCLUDE, boundaries, root));
|
|
254
282
|
trees.set(label, await readTree(page));
|
|
255
283
|
await found.add(label, await scanTextSpacing(page, SCOPE, EXCLUDE, own));
|
|
256
284
|
await found.add(label, await scanReducedMotion(page, SCOPE, EXCLUDE, own));
|
|
257
285
|
};
|
|
258
|
-
const scanContrast = async (label, theme) => {
|
|
286
|
+
const scanContrast = async ({ label, view }, theme) => {
|
|
287
|
+
const themed = `${label} under ${theme}`;
|
|
259
288
|
await markStage(page, portals);
|
|
260
|
-
await found.add(
|
|
261
|
-
await
|
|
289
|
+
await found.add(themed, await scanAxe(page, SCOPE, EXCLUDE, "a11y-component", PRESET_RULES));
|
|
290
|
+
await scanFocus(view, themed, false);
|
|
291
|
+
await found.add(themed, await scanBoundaries(page, SCOPE, EXCLUDE, boundaries, root));
|
|
262
292
|
};
|
|
263
293
|
for (const scanView of plain) await scan(scanView);
|
|
264
294
|
for (const theme of presets) {
|
|
265
295
|
await harness.previewThemeUncovered(theme);
|
|
266
|
-
for (const
|
|
267
|
-
await harness.selectView(view);
|
|
268
|
-
await scanContrast(
|
|
296
|
+
for (const scanView of plain) {
|
|
297
|
+
await harness.selectView(scanView.view);
|
|
298
|
+
await scanContrast(scanView, theme);
|
|
269
299
|
}
|
|
270
300
|
await harness.cancelUncoveredPreview();
|
|
271
301
|
}
|
|
@@ -273,7 +303,7 @@ for (const contract of await selectedContracts()) {
|
|
|
273
303
|
await scan(scanView);
|
|
274
304
|
for (const theme of presets) {
|
|
275
305
|
await harness.previewThemeUncovered(theme);
|
|
276
|
-
await scanContrast(scanView
|
|
306
|
+
await scanContrast(scanView, theme);
|
|
277
307
|
await harness.cancelUncoveredPreview();
|
|
278
308
|
}
|
|
279
309
|
}
|
|
@@ -1 +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"]}
|
|
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\nconst LIFTED_ATTR = 'data-lt-a11y-lifted';\n\n/** The class the editor puts on the preview for the view's state. A property\n * view shares its state tab with the contract's state entry, which names it. */\nfunction forcedClass(contract: ComponentContract, view: View): string | undefined {\n if (isInapplicable(contract.states)) return undefined;\n const variant = view.variant ?? contract.view?.variant;\n return contract.states.find((s) => s.state === view.state && (s.variant ?? contract.view?.variant) === variant)?.forceClass;\n}\n\n/** Runs `fn` with `forceClass` taken off the stage. A state such as Input's\n * focused one paints its look at rest, which would leave focus nothing to\n * change. */\nasync function withoutForcedState(page: Page, forceClass: string | undefined, fn: () => Promise<void>): Promise<void> {\n if (!forceClass) return fn();\n await page.evaluate(({ scope, forceClass, attr }) => {\n for (const stage of document.querySelectorAll(scope)) {\n for (const el of [stage, ...stage.querySelectorAll(`.${forceClass}`)]) {\n if (!el.classList.contains(forceClass)) continue;\n el.classList.remove(forceClass);\n el.setAttribute(attr, forceClass);\n }\n }\n }, { scope: SCOPE, forceClass, attr: LIFTED_ATTR });\n try {\n await fn();\n } finally {\n await page.evaluate((attr) => {\n for (const el of document.querySelectorAll(`[${attr}]`)) {\n el.classList.add(el.getAttribute(attr)!);\n el.removeAttribute(attr);\n }\n }, LIFTED_ATTR);\n }\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 // The focus probe runs before SIA-R65, which reads the marks it leaves on\n // elements whose ring a sibling draws.\n const scanFocus = (view: View, label: string, withAlfa: boolean) =>\n withoutForcedState(page, forcedClass(contract, view), async () => {\n await found.add(label, await scanFocusIndicators(page, SCOPE, EXCLUDE));\n if (withAlfa) await found.add(label, await scanFocusVisible(page, SCOPE, EXCLUDE));\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 scanFocus(view, label, true);\n await found.add(label, await scanBoundaries(page, SCOPE, EXCLUDE, boundaries, root));\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, view }: ScanView, theme: string) => {\n const themed = `${label} under ${theme}`;\n await markStage(page, portals);\n await found.add(themed, await scanAxe(page, SCOPE, EXCLUDE, 'a11y-component', PRESET_RULES));\n await scanFocus(view, themed, false);\n await found.add(themed, await scanBoundaries(page, SCOPE, EXCLUDE, boundaries, root));\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 scanView of plain) {\n await harness.selectView(scanView.view);\n await scanContrast(scanView, 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, 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":["forceClass","portals"]}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ContractHarness
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-7QXUJRSO.js";
|
|
4
4
|
import "./chunk-3WCLMOM7.js";
|
|
5
|
-
import "./chunk-
|
|
5
|
+
import "./chunk-RTZQLW3F.js";
|
|
6
6
|
import "./chunk-7CM5D7PF.js";
|
|
7
7
|
import {
|
|
8
8
|
ContractViolation,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
import {
|
|
12
12
|
allContracts,
|
|
13
13
|
selectedContracts
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-RWOT2XDB.js";
|
|
15
15
|
|
|
16
16
|
// src/testing/component-editor.contract.ts
|
|
17
17
|
import { test } from "@playwright/test";
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ContractHarness
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-7QXUJRSO.js";
|
|
4
4
|
import {
|
|
5
5
|
openComponentsEditor
|
|
6
6
|
} from "./chunk-3WCLMOM7.js";
|
|
7
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-RTZQLW3F.js";
|
|
8
8
|
import "./chunk-7CM5D7PF.js";
|
|
9
9
|
import "./chunk-YVVR3GJZ.js";
|
|
10
10
|
import {
|
|
11
11
|
selectedContracts
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-RWOT2XDB.js";
|
|
13
13
|
|
|
14
14
|
// src/testing/component-render.contract.ts
|
|
15
15
|
import fs from "fs";
|
package/src/testing-js/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createVitestConfig
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-THWGMSRH.js";
|
|
4
4
|
import {
|
|
5
5
|
COMPONENTS_PATH_ENV,
|
|
6
6
|
COMPONENT_ENV,
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
allContracts,
|
|
24
24
|
selectedContracts,
|
|
25
25
|
shippedContracts
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-RWOT2XDB.js";
|
|
27
27
|
|
|
28
28
|
// src/testing/playwright.ts
|
|
29
29
|
import path2 from "path";
|
|
@@ -39,7 +39,7 @@ import os from "os";
|
|
|
39
39
|
import path from "path";
|
|
40
40
|
var TEST_DATA_DIR_ENV = "LIVE_TOKENS_TEST_DATA_DIR";
|
|
41
41
|
var DATA_DIR_ENV = "LIVE_TOKENS_DATA_DIR";
|
|
42
|
-
var SESSION_FILES = /* @__PURE__ */ new Set(["_working.json", "_active.json", "_production.json"]);
|
|
42
|
+
var SESSION_FILES = /* @__PURE__ */ new Set(["_working.json", "_active.json", "_production.json", "_a11y-reports"]);
|
|
43
43
|
var removed = false;
|
|
44
44
|
function registerCleanup(dataDir) {
|
|
45
45
|
const remove = () => {
|
|
@@ -71,7 +71,7 @@ function isolateDataDir(sourceDataDir) {
|
|
|
71
71
|
fs.cpSync(source, dataDir, { recursive: true });
|
|
72
72
|
for (const entry of fs.readdirSync(dataDir, { recursive: true })) {
|
|
73
73
|
if (SESSION_FILES.has(path.basename(entry))) {
|
|
74
|
-
fs.rmSync(path.join(dataDir, entry), { force: true });
|
|
74
|
+
fs.rmSync(path.join(dataDir, entry), { recursive: true, force: true });
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
process.env[TEST_DATA_DIR_ENV] = dataDir;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../testing/playwright.ts","../testing/isolation.ts","../testing/port.ts"],"sourcesContent":["import path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport {\n defineConfig,\n devices,\n type PlaywrightTestConfig,\n type PlaywrightTestProject,\n} from '@playwright/test';\nimport {\n COMPONENT_ENV,\n COMPONENTS_PATH_ENV,\n PAGE_VIEWPORTS_ENV,\n devServerCommand,\n resolveTestingConfig,\n type LiveTokensTestingConfig,\n} from './config';\nimport { CONTRACTS_MODULE_ENV } from './contracts';\nimport { DATA_DIR_ENV, TEST_DATA_DIR_ENV, isolateDataDir } from './isolation';\nimport { resolvePort } from './port';\n\nconst HOST = '127.0.0.1';\n\n/** The shipped suites, wherever the package is installed. */\nconst CONTRACT_TEST_DIR = path.dirname(fileURLToPath(import.meta.url));\n\nexport interface PlaywrightConfigOptions extends LiveTokensTestingConfig {\n /** Project root the settings resolve against. Default `process.cwd()`. */\n root?: string;\n /** Projects that run beside the contract project. */\n extraProjects?: PlaywrightTestProject[];\n /** Where projects that declare no `testDir` of their own look. */\n testDir?: string;\n /** Bounds the whole run. Default fifteen minutes, the same bound\n * `check-component --tests` and `check-page --tests` hold their spawned\n * child to (`LIVE_TOKENS_TESTS_TIMEOUT` in `bin/contractRunner.mjs`), so a\n * hung run stops here too, for a caller that invokes this factory\n * directly rather than through the CLI. */\n globalTimeout?: number;\n}\n\n/** Mirrors `bin/contractRunner.mjs`'s own `DEFAULT_TESTS_TIMEOUT_MS`,\n * duplicated because that file is plain JS and cannot import this module\n * before `build:testing` compiles it (see `bin/engineLoadsLazily.test.ts`). */\nconst DEFAULT_GLOBAL_TIMEOUT_MS = 15 * 60_000;\n\n/**\n * The contract project, its dev server, and the data isolation the two share.\n *\n * Isolation runs here rather than in a global setup because the config is the\n * first thing every process in the run evaluates: the environment it leaves\n * behind reaches the workers and the dev server, and nothing in the run can\n * observe the state before it.\n */\nexport async function createPlaywrightConfig(\n options: PlaywrightConfigOptions = {},\n): Promise<PlaywrightTestConfig> {\n const settings = resolveTestingConfig(options, options.root);\n const { dataDir } = isolateDataDir(settings.dataDir);\n const port = resolvePort(settings.port);\n const baseURL = `http://${HOST}:${port}`;\n\n process.env[COMPONENTS_PATH_ENV] = settings.componentsPath;\n process.env[PAGE_VIEWPORTS_ENV] = JSON.stringify(settings.pageViewports);\n if (settings.contractsModule) process.env[CONTRACTS_MODULE_ENV] = settings.contractsModule;\n\n return defineConfig({\n testDir: options.testDir ?? CONTRACT_TEST_DIR,\n // The component suites carry their serial and parallel intent per file:\n // `component-editor.contract.ts` runs each component's save and reset\n // cycles in order against one data tree. Turning this on would interleave\n // them.\n fullyParallel: false,\n // One worker is the safe default: the specs share a dev server and a data\n // directory. A run that can afford more passes a worker count on the\n // command line.\n workers: 1,\n timeout: 30_000,\n globalTimeout: options.globalTimeout ?? DEFAULT_GLOBAL_TIMEOUT_MS,\n expect: { timeout: 5_000 },\n // A shared-server suite times out under CI load in ways it never does\n // locally, and without a retry budget one wobble aborts a tagged release.\n retries: process.env.CI ? 2 : 0,\n // Absolute and rooted at the consumer project, not the config file's own\n // directory: `check-component --tests`'s generated config lives in a\n // temporary directory that gets removed on completion, and a relative\n // `outputDir` would put every trace and screenshot in there too.\n outputDir: path.join(settings.root, 'test-results/playwright'),\n reporter: process.env.CI ? [['line'], ['html', { open: 'never' }]] : 'list',\n use: {\n baseURL,\n // Unset, both of these are unbounded: a locator that never resolves waits\n // out the whole test timeout and reports nothing about where it stopped.\n actionTimeout: 10_000,\n navigationTimeout: 30_000,\n trace: 'on-first-retry',\n screenshot: 'only-on-failure',\n // `retain-on-failure` still records every passing test and throws the\n // file away, which costs ~28% CPU. Match the trace policy instead.\n video: 'on-first-retry',\n },\n projects: [\n ...(options.extraProjects ?? []),\n {\n name: 'contract',\n testDir: CONTRACT_TEST_DIR,\n // `.ts` in this repo, `.js` once tsup compiles the shipped build into\n // `src/testing-js`; matching both here needs no build-time swap.\n testMatch: '**/component-*.contract.{ts,js}',\n // The behavior suite shares the prefix and runs under Vitest. Left in,\n // Playwright collects it, meets its Svelte imports and reports the\n // whole project as collecting nothing.\n testIgnore: '**/component-behavior.contract.{ts,js}',\n // `.tabs-preview` now caps its sticky band at 50vh, which is enough for\n // image, panel, card and sidenavigation to pass alone at 1280x720. A\n // full parallel run still fails panel's gradient radio there, so the\n // band can cover a control at some scroll positions. Until that is\n // fixed the suite runs taller. See docs/contract-test-defects.md.\n use: { ...devices['Desktop Chrome'], viewport: { width: 1280, height: 900 } },\n },\n {\n name: 'page',\n testDir: CONTRACT_TEST_DIR,\n testMatch: '**/page-*.contract.{ts,js}',\n // The suite sizes the window itself, once per viewport in\n // `pageViewports`, so the sizes stay one list rather than a project\n // per viewport whose name the reporter would have to decode.\n use: { ...devices['Desktop Chrome'] },\n },\n ],\n webServer: {\n command: devServerCommand(settings.devCommand, HOST, port),\n // Otherwise defaults to the config file's own directory. A generated\n // config that lives outside the consumer root (`check-component\n // --tests`'s temporary config) would run `npm run dev` from wherever\n // that happens to be instead.\n cwd: settings.root,\n url: baseURL,\n // A server this run did not start carries none of these variables, so it\n // would write the project's own data tree.\n reuseExistingServer: false,\n timeout: 120_000,\n env: {\n ...process.env,\n [TEST_DATA_DIR_ENV]: dataDir,\n [DATA_DIR_ENV]: dataDir,\n },\n },\n });\n}\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\n/**\n * The plugin reads this name ahead of its own options and\n * `live-tokens.config.json`, so setting it redirects every server-side write —\n * the data tree, `tokens.generated.css`, and `fonts.css` — into the copy.\n */\nexport const TEST_DATA_DIR_ENV = 'LIVE_TOKENS_TEST_DATA_DIR';\n\n/** What the suites and the harness read component configs from in the runner's\n * own process. It names the same directory and is a separate variable because\n * it answers a different question: where a reader looks, not where the server\n * is confined. */\nexport const DATA_DIR_ENV = 'LIVE_TOKENS_DATA_DIR';\n\n/**\n * A developer's current session rather than the document under test. Each one\n * is gitignored or dev-written, so leaving it in the copy would make the run\n * pass or fail on whichever theme the maintainer has open.\n *\n * _working.json an unsaved buffer, which reads as a dirty document and\n * can raise a destructive confirmation dialog\n * _active.json the theme the editor has open. One carrying a sketchstyle\n * boots the suite with the sketch layer on, which paints\n * every fill onto a pseudo-element and leaves the real\n * background transparent\n * _production.json the theme baked into tokens.generated.css\n *\n * A missing pointer resolves to \"default\" at runtime, so removing them is the\n * whole reset.\n */\nconst SESSION_FILES = new Set(['_working.json', '_active.json', '_production.json']);\n\nexport interface IsolatedData {\n dataDir: string;\n /** True when this process made the copy and owns removing it. */\n created: boolean;\n}\n\nlet removed = false;\n\nfunction registerCleanup(dataDir: string): void {\n const remove = () => {\n if (removed) return;\n removed = true;\n fs.rmSync(dataDir, { recursive: true, force: true });\n };\n process.once('exit', remove);\n // Playwright owns the exit path on a signal: it stops the workers and kills\n // the dev server's process group, then exits, which reaches `exit` above.\n // These two only make sure the copy is gone before that teardown starts.\n process.once('SIGINT', remove);\n process.once('SIGTERM', remove);\n}\n\n/**\n * Copy `sourceDataDir` into a unique temporary directory and point both the\n * server and the runner at it. Idempotent across processes: Playwright loads\n * the config again in every worker, and each inherits the parent's environment,\n * so only the first call copies anything.\n */\nexport function isolateDataDir(sourceDataDir: string): IsolatedData {\n const existing = process.env[TEST_DATA_DIR_ENV];\n if (existing) {\n const dataDir = path.resolve(existing);\n if (!fs.existsSync(dataDir)) {\n throw new Error(`${TEST_DATA_DIR_ENV} names ${dataDir}, which does not exist`);\n }\n process.env[DATA_DIR_ENV] = dataDir;\n return { dataDir, created: false };\n }\n\n const source = path.resolve(sourceDataDir);\n if (!fs.existsSync(source)) {\n throw new Error(\n `No data directory at ${source}. Set \\`dataDir\\` in live-tokens.testing.ts `\n + 'when the project keeps its live-tokens data somewhere else.',\n );\n }\n\n // realpath: macOS resolves os.tmpdir() through a symlink, and the plugin\n // compares resolved paths when it decides whether a write is in scope.\n const dataDir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'live-tokens-contract-')));\n fs.cpSync(source, dataDir, { recursive: true });\n for (const entry of fs.readdirSync(dataDir, { recursive: true }) as string[]) {\n if (SESSION_FILES.has(path.basename(entry))) {\n fs.rmSync(path.join(dataDir, entry), { force: true });\n }\n }\n\n process.env[TEST_DATA_DIR_ENV] = dataDir;\n process.env[DATA_DIR_ENV] = dataDir;\n registerCleanup(dataDir);\n return { dataDir, created: true };\n}\n","import { execFileSync } from 'node:child_process';\n\nexport const PORT_ENV = 'LIVE_TOKENS_TEST_PORT';\n\nconst PREFERRED_PORT = 4173;\n\n/** Bind the preferred port, fall back to whatever the OS hands out, print the\n * answer. A Playwright config is evaluated synchronously, so the check runs in\n * a child rather than on a listener callback. */\nconst PROBE = `\nconst net = require('net');\nconst listen = (port) => new Promise((resolve, reject) => {\n const server = net.createServer();\n server.once('error', reject);\n server.listen(port, '127.0.0.1', () => {\n const { port: bound } = server.address();\n server.close(() => resolve(bound));\n });\n});\n// process.stdout.write, not console.log: the parent's FORCE_COLOR makes\n// console.log wrap a number in ANSI escapes.\nlisten(Number(process.argv[1])).catch(() => listen(0)).then((port) => process.stdout.write(String(port)));\n`;\n\n/**\n * The port the dev server and `baseURL` share. Cached in the environment so\n * every Playwright worker, which loads the config again in its own process,\n * reads the port the main process settled on.\n */\nexport function resolvePort(preferred?: number): number {\n const cached = process.env[PORT_ENV];\n if (cached) return Number(cached);\n\n const port = preferred\n ?? Number(execFileSync(process.execPath, ['-e', PROBE, String(PREFERRED_PORT)], {\n encoding: 'utf-8',\n }).trim());\n if (!Number.isInteger(port) || port <= 0) {\n throw new Error(`Could not settle on a dev-server port (read ${port})`);\n }\n process.env[PORT_ENV] = String(port);\n return port;\n}\n"],"mappings":"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","names":["path","dataDir","path"]}
|
|
1
|
+
{"version":3,"sources":["../testing/playwright.ts","../testing/isolation.ts","../testing/port.ts"],"sourcesContent":["import path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport {\n defineConfig,\n devices,\n type PlaywrightTestConfig,\n type PlaywrightTestProject,\n} from '@playwright/test';\nimport {\n COMPONENT_ENV,\n COMPONENTS_PATH_ENV,\n PAGE_VIEWPORTS_ENV,\n devServerCommand,\n resolveTestingConfig,\n type LiveTokensTestingConfig,\n} from './config';\nimport { CONTRACTS_MODULE_ENV } from './contracts';\nimport { DATA_DIR_ENV, TEST_DATA_DIR_ENV, isolateDataDir } from './isolation';\nimport { resolvePort } from './port';\n\nconst HOST = '127.0.0.1';\n\n/** The shipped suites, wherever the package is installed. */\nconst CONTRACT_TEST_DIR = path.dirname(fileURLToPath(import.meta.url));\n\nexport interface PlaywrightConfigOptions extends LiveTokensTestingConfig {\n /** Project root the settings resolve against. Default `process.cwd()`. */\n root?: string;\n /** Projects that run beside the contract project. */\n extraProjects?: PlaywrightTestProject[];\n /** Where projects that declare no `testDir` of their own look. */\n testDir?: string;\n /** Bounds the whole run. Default fifteen minutes, the same bound\n * `check-component --tests` and `check-page --tests` hold their spawned\n * child to (`LIVE_TOKENS_TESTS_TIMEOUT` in `bin/contractRunner.mjs`), so a\n * hung run stops here too, for a caller that invokes this factory\n * directly rather than through the CLI. */\n globalTimeout?: number;\n}\n\n/** Mirrors `bin/contractRunner.mjs`'s own `DEFAULT_TESTS_TIMEOUT_MS`,\n * duplicated because that file is plain JS and cannot import this module\n * before `build:testing` compiles it (see `bin/engineLoadsLazily.test.ts`). */\nconst DEFAULT_GLOBAL_TIMEOUT_MS = 15 * 60_000;\n\n/**\n * The contract project, its dev server, and the data isolation the two share.\n *\n * Isolation runs here rather than in a global setup because the config is the\n * first thing every process in the run evaluates: the environment it leaves\n * behind reaches the workers and the dev server, and nothing in the run can\n * observe the state before it.\n */\nexport async function createPlaywrightConfig(\n options: PlaywrightConfigOptions = {},\n): Promise<PlaywrightTestConfig> {\n const settings = resolveTestingConfig(options, options.root);\n const { dataDir } = isolateDataDir(settings.dataDir);\n const port = resolvePort(settings.port);\n const baseURL = `http://${HOST}:${port}`;\n\n process.env[COMPONENTS_PATH_ENV] = settings.componentsPath;\n process.env[PAGE_VIEWPORTS_ENV] = JSON.stringify(settings.pageViewports);\n if (settings.contractsModule) process.env[CONTRACTS_MODULE_ENV] = settings.contractsModule;\n\n return defineConfig({\n testDir: options.testDir ?? CONTRACT_TEST_DIR,\n // The component suites carry their serial and parallel intent per file:\n // `component-editor.contract.ts` runs each component's save and reset\n // cycles in order against one data tree. Turning this on would interleave\n // them.\n fullyParallel: false,\n // One worker is the safe default: the specs share a dev server and a data\n // directory. A run that can afford more passes a worker count on the\n // command line.\n workers: 1,\n timeout: 30_000,\n globalTimeout: options.globalTimeout ?? DEFAULT_GLOBAL_TIMEOUT_MS,\n expect: { timeout: 5_000 },\n // A shared-server suite times out under CI load in ways it never does\n // locally, and without a retry budget one wobble aborts a tagged release.\n retries: process.env.CI ? 2 : 0,\n // Absolute and rooted at the consumer project, not the config file's own\n // directory: `check-component --tests`'s generated config lives in a\n // temporary directory that gets removed on completion, and a relative\n // `outputDir` would put every trace and screenshot in there too.\n outputDir: path.join(settings.root, 'test-results/playwright'),\n reporter: process.env.CI ? [['line'], ['html', { open: 'never' }]] : 'list',\n use: {\n baseURL,\n // Unset, both of these are unbounded: a locator that never resolves waits\n // out the whole test timeout and reports nothing about where it stopped.\n actionTimeout: 10_000,\n navigationTimeout: 30_000,\n trace: 'on-first-retry',\n screenshot: 'only-on-failure',\n // `retain-on-failure` still records every passing test and throws the\n // file away, which costs ~28% CPU. Match the trace policy instead.\n video: 'on-first-retry',\n },\n projects: [\n ...(options.extraProjects ?? []),\n {\n name: 'contract',\n testDir: CONTRACT_TEST_DIR,\n // `.ts` in this repo, `.js` once tsup compiles the shipped build into\n // `src/testing-js`; matching both here needs no build-time swap.\n testMatch: '**/component-*.contract.{ts,js}',\n // The behavior suite shares the prefix and runs under Vitest. Left in,\n // Playwright collects it, meets its Svelte imports and reports the\n // whole project as collecting nothing.\n testIgnore: '**/component-behavior.contract.{ts,js}',\n // `.tabs-preview` now caps its sticky band at 50vh, which is enough for\n // image, panel, card and sidenavigation to pass alone at 1280x720. A\n // full parallel run still fails panel's gradient radio there, so the\n // band can cover a control at some scroll positions. Until that is\n // fixed the suite runs taller. See docs/contract-test-defects.md.\n use: { ...devices['Desktop Chrome'], viewport: { width: 1280, height: 900 } },\n },\n {\n name: 'page',\n testDir: CONTRACT_TEST_DIR,\n testMatch: '**/page-*.contract.{ts,js}',\n // The suite sizes the window itself, once per viewport in\n // `pageViewports`, so the sizes stay one list rather than a project\n // per viewport whose name the reporter would have to decode.\n use: { ...devices['Desktop Chrome'] },\n },\n ],\n webServer: {\n command: devServerCommand(settings.devCommand, HOST, port),\n // Otherwise defaults to the config file's own directory. A generated\n // config that lives outside the consumer root (`check-component\n // --tests`'s temporary config) would run `npm run dev` from wherever\n // that happens to be instead.\n cwd: settings.root,\n url: baseURL,\n // A server this run did not start carries none of these variables, so it\n // would write the project's own data tree.\n reuseExistingServer: false,\n timeout: 120_000,\n env: {\n ...process.env,\n [TEST_DATA_DIR_ENV]: dataDir,\n [DATA_DIR_ENV]: dataDir,\n },\n },\n });\n}\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\n/**\n * The plugin reads this name ahead of its own options and\n * `live-tokens.config.json`, so setting it redirects every server-side write —\n * the data tree, `tokens.generated.css`, and `fonts.css` — into the copy.\n */\nexport const TEST_DATA_DIR_ENV = 'LIVE_TOKENS_TEST_DATA_DIR';\n\n/** What the suites and the harness read component configs from in the runner's\n * own process. It names the same directory and is a separate variable because\n * it answers a different question: where a reader looks, not where the server\n * is confined. */\nexport const DATA_DIR_ENV = 'LIVE_TOKENS_DATA_DIR';\n\n/**\n * A developer's current session rather than the document under test. Each one\n * is gitignored or dev-written, so leaving it in the copy would make the run\n * pass or fail on whichever theme the maintainer has open.\n *\n * _working.json an unsaved buffer, which reads as a dirty document and\n * can raise a destructive confirmation dialog\n * _active.json the theme the editor has open. One carrying a sketchstyle\n * boots the suite with the sketch layer on, which paints\n * every fill onto a pseudo-element and leaves the real\n * background transparent\n * _production.json the theme baked into tokens.generated.css\n * _a11y-reports/ the editor's last accessibility run per theme, which\n * would mark the editor under test\n *\n * A missing pointer resolves to \"default\" at runtime, so removing them is the\n * whole reset.\n */\nconst SESSION_FILES = new Set(['_working.json', '_active.json', '_production.json', '_a11y-reports']);\n\nexport interface IsolatedData {\n dataDir: string;\n /** True when this process made the copy and owns removing it. */\n created: boolean;\n}\n\nlet removed = false;\n\nfunction registerCleanup(dataDir: string): void {\n const remove = () => {\n if (removed) return;\n removed = true;\n fs.rmSync(dataDir, { recursive: true, force: true });\n };\n process.once('exit', remove);\n // Playwright owns the exit path on a signal: it stops the workers and kills\n // the dev server's process group, then exits, which reaches `exit` above.\n // These two only make sure the copy is gone before that teardown starts.\n process.once('SIGINT', remove);\n process.once('SIGTERM', remove);\n}\n\n/**\n * Copy `sourceDataDir` into a unique temporary directory and point both the\n * server and the runner at it. Idempotent across processes: Playwright loads\n * the config again in every worker, and each inherits the parent's environment,\n * so only the first call copies anything.\n */\nexport function isolateDataDir(sourceDataDir: string): IsolatedData {\n const existing = process.env[TEST_DATA_DIR_ENV];\n if (existing) {\n const dataDir = path.resolve(existing);\n if (!fs.existsSync(dataDir)) {\n throw new Error(`${TEST_DATA_DIR_ENV} names ${dataDir}, which does not exist`);\n }\n process.env[DATA_DIR_ENV] = dataDir;\n return { dataDir, created: false };\n }\n\n const source = path.resolve(sourceDataDir);\n if (!fs.existsSync(source)) {\n throw new Error(\n `No data directory at ${source}. Set \\`dataDir\\` in live-tokens.testing.ts `\n + 'when the project keeps its live-tokens data somewhere else.',\n );\n }\n\n // realpath: macOS resolves os.tmpdir() through a symlink, and the plugin\n // compares resolved paths when it decides whether a write is in scope.\n const dataDir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'live-tokens-contract-')));\n fs.cpSync(source, dataDir, { recursive: true });\n for (const entry of fs.readdirSync(dataDir, { recursive: true }) as string[]) {\n if (SESSION_FILES.has(path.basename(entry))) {\n fs.rmSync(path.join(dataDir, entry), { recursive: true, force: true });\n }\n }\n\n process.env[TEST_DATA_DIR_ENV] = dataDir;\n process.env[DATA_DIR_ENV] = dataDir;\n registerCleanup(dataDir);\n return { dataDir, created: true };\n}\n","import { execFileSync } from 'node:child_process';\n\nexport const PORT_ENV = 'LIVE_TOKENS_TEST_PORT';\n\nconst PREFERRED_PORT = 4173;\n\n/** Bind the preferred port, fall back to whatever the OS hands out, print the\n * answer. A Playwright config is evaluated synchronously, so the check runs in\n * a child rather than on a listener callback. */\nconst PROBE = `\nconst net = require('net');\nconst listen = (port) => new Promise((resolve, reject) => {\n const server = net.createServer();\n server.once('error', reject);\n server.listen(port, '127.0.0.1', () => {\n const { port: bound } = server.address();\n server.close(() => resolve(bound));\n });\n});\n// process.stdout.write, not console.log: the parent's FORCE_COLOR makes\n// console.log wrap a number in ANSI escapes.\nlisten(Number(process.argv[1])).catch(() => listen(0)).then((port) => process.stdout.write(String(port)));\n`;\n\n/**\n * The port the dev server and `baseURL` share. Cached in the environment so\n * every Playwright worker, which loads the config again in its own process,\n * reads the port the main process settled on.\n */\nexport function resolvePort(preferred?: number): number {\n const cached = process.env[PORT_ENV];\n if (cached) return Number(cached);\n\n const port = preferred\n ?? Number(execFileSync(process.execPath, ['-e', PROBE, String(PREFERRED_PORT)], {\n encoding: 'utf-8',\n }).trim());\n if (!Number.isInteger(port) || port <= 0) {\n throw new Error(`Could not settle on a dev-server port (read ${port})`);\n }\n process.env[PORT_ENV] = String(port);\n return port;\n}\n"],"mappings":"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","names":["path","dataDir","path"]}
|
|
@@ -4,12 +4,12 @@ import {
|
|
|
4
4
|
TEXT_SPACING_RULE,
|
|
5
5
|
TEXT_SPACING_URL,
|
|
6
6
|
newUnderTextSpacing
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-GZKUYLDW.js";
|
|
8
8
|
import {
|
|
9
9
|
missingEngine,
|
|
10
10
|
recordA11y,
|
|
11
11
|
scanAxe
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-RTZQLW3F.js";
|
|
13
13
|
import {
|
|
14
14
|
DEFAULT_PAGE_VIEWPORTS,
|
|
15
15
|
PAGES_ENV,
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
} from "./chunk-YVVR3GJZ.js";
|
|
21
21
|
import {
|
|
22
22
|
allContracts
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-RWOT2XDB.js";
|
|
24
24
|
|
|
25
25
|
// src/testing/page-compliance.contract.ts
|
|
26
26
|
import { test } from "@playwright/test";
|
package/src/testing-js/vitest.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createVitestConfig
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-THWGMSRH.js";
|
|
4
4
|
import {
|
|
5
5
|
defineTestingConfig,
|
|
6
6
|
resolveTestingConfig
|
|
7
7
|
} from "./chunk-7CM5D7PF.js";
|
|
8
|
-
import "./chunk-
|
|
8
|
+
import "./chunk-RWOT2XDB.js";
|
|
9
9
|
export {
|
|
10
10
|
createVitestConfig,
|
|
11
11
|
defineTestingConfig,
|
package/template/_gitignore
CHANGED
|
@@ -7,6 +7,9 @@ dist-ssr
|
|
|
7
7
|
playwright-report/
|
|
8
8
|
test-results/
|
|
9
9
|
|
|
10
|
+
# The live-tokens editor's last accessibility run per theme, kept in the data directory.
|
|
11
|
+
_a11y-reports/
|
|
12
|
+
|
|
10
13
|
# The live-tokens skills are a copy of what the package ships; postinstall
|
|
11
14
|
# refreshes them, so they are the package's to version, not this project's.
|
|
12
15
|
.claude/skills/live-tokens-*/
|