@motion-proto/live-tokens 0.97.0 → 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.
Files changed (191) hide show
  1. package/.claude/skills/live-tokens-check-compliance/SKILL.md +5 -0
  2. package/.claude/skills/live-tokens-create-component/SKILL.md +4 -3
  3. package/.claude/skills/live-tokens-create-component/references/contract-tests.md +71 -11
  4. package/CHANGELOG.md +339 -0
  5. package/README.md +2 -2
  6. package/bin/check-component.mjs +8 -0
  7. package/bin/check-page.mjs +6 -0
  8. package/bin/cli.mjs +36 -6
  9. package/bin/contractRunner.mjs +144 -19
  10. package/bin/lib/findings.mjs +20 -4
  11. package/bin/rules/a11y.mjs +53 -0
  12. package/dist-plugin/{chunk-A4TBP4MM.js → chunk-SLICH4IH.js} +2 -2
  13. package/dist-plugin/index.cjs +204 -3
  14. package/dist-plugin/index.js +306 -105
  15. package/dist-plugin/setColors/index.js +1 -1
  16. package/package.json +47 -2
  17. package/src/demo/LabeledSelect.svelte +1 -1
  18. package/src/editor/bootstrap.ts +5 -1
  19. package/src/editor/component-editor/BadgeEditor.svelte +13 -0
  20. package/src/editor/component-editor/ButtonEditor.svelte +33 -2
  21. package/src/editor/component-editor/CalloutEditor.svelte +13 -0
  22. package/src/editor/component-editor/CardEditor.svelte +12 -2
  23. package/src/editor/component-editor/CodeSnippetEditor.svelte +11 -1
  24. package/src/editor/component-editor/CollapsibleSectionEditor.svelte +18 -1
  25. package/src/editor/component-editor/CornerBadgeEditor.svelte +13 -0
  26. package/src/editor/component-editor/DialogEditor.svelte +15 -0
  27. package/src/editor/component-editor/IconButtonEditor.svelte +29 -1
  28. package/src/editor/component-editor/ImageEditor.svelte +20 -9
  29. package/src/editor/component-editor/ImageLightboxEditor.svelte +44 -0
  30. package/src/editor/component-editor/InlineEditActionsEditor.svelte +13 -1
  31. package/src/editor/component-editor/InputEditor.svelte +23 -0
  32. package/src/editor/component-editor/MenuSelectEditor.svelte +27 -1
  33. package/src/editor/component-editor/NotificationEditor.svelte +15 -0
  34. package/src/editor/component-editor/PanelEditor.svelte +1 -1
  35. package/src/editor/component-editor/ProgressBarEditor.svelte +13 -0
  36. package/src/editor/component-editor/RadioButtonEditor.svelte +20 -1
  37. package/src/editor/component-editor/SectionDividerEditor.svelte +40 -13
  38. package/src/editor/component-editor/SegmentedControlEditor.svelte +29 -1
  39. package/src/editor/component-editor/SideNavigationEditor.svelte +40 -1
  40. package/src/editor/component-editor/SlidePagerEditor.svelte +20 -0
  41. package/src/editor/component-editor/SliderEditor.svelte +33 -0
  42. package/src/editor/component-editor/TabBarEditor.svelte +23 -2
  43. package/src/editor/component-editor/TableEditor.svelte +14 -0
  44. package/src/editor/component-editor/ToggleEditor.svelte +17 -0
  45. package/src/editor/component-editor/TooltipEditor.svelte +11 -2
  46. package/src/editor/component-editor/VideoFrameEditor.svelte +8 -0
  47. package/src/editor/component-editor/VideoLightboxEditor.svelte +10 -0
  48. package/src/editor/component-editor/registration.ts +2 -1
  49. package/src/editor/component-editor/registry.ts +69 -27
  50. package/src/editor/component-editor/scaffolding/DemoHeader.svelte +2 -0
  51. package/src/editor/component-editor/scaffolding/StateBlock.svelte +19 -8
  52. package/src/editor/component-editor/scaffolding/TokenLayout.svelte +3 -0
  53. package/src/editor/component-editor/scaffolding/VariantGroup.svelte +70 -3
  54. package/src/editor/component-editor/scaffolding/editorContext.ts +7 -0
  55. package/src/editor/core/a11y/a11yReport.ts +164 -0
  56. package/src/editor/core/a11y/a11yReportTypes.ts +77 -0
  57. package/src/editor/core/a11y/contrastFailures.ts +22 -0
  58. package/src/editor/core/a11y/contrastFixes.ts +41 -0
  59. package/src/editor/core/components/contrastPairRegistry.ts +18 -0
  60. package/src/editor/core/palettes/contrast.ts +44 -0
  61. package/src/editor/core/palettes/contrastPairs.ts +82 -0
  62. package/src/editor/core/palettes/resolveColor.ts +77 -0
  63. package/src/editor/core/store/editorStore.ts +4 -0
  64. package/src/editor/core/store/editorViewStore.ts +2 -2
  65. package/src/editor/core/store/unsavedEdits.ts +10 -0
  66. package/src/editor/core/themes/liveStateStream.ts +7 -1
  67. package/src/editor/core/themes/parsers/gradient.ts +5 -2
  68. package/src/editor/docs/chapters.ts +1 -0
  69. package/src/editor/docs/content/accessibility.md +411 -0
  70. package/src/editor/docs/content/editing-tokens.md +32 -1
  71. package/src/editor/docs/content.generated.ts +2 -1
  72. package/src/editor/pages/ComponentEditorPage.svelte +11 -1
  73. package/src/editor/pages/EditorShell.svelte +98 -10
  74. package/src/editor/skill-atlas/skillSources.generated.ts +3 -3
  75. package/src/editor/skill-atlas/trees/check-compliance.ts +3 -3
  76. package/src/editor/skill-atlas/trees/create-component.ts +12 -12
  77. package/src/editor/styles/ui-editor.css +2 -0
  78. package/src/editor/ui/EditorViewSwitcher.svelte +15 -2
  79. package/src/editor/ui/GradientEditor.svelte +9 -1
  80. package/src/editor/ui/ThemePanel.svelte +4 -4
  81. package/src/editor/ui/UIContrastReadout.svelte +186 -0
  82. package/src/editor/ui/UIInfoPopover.svelte +16 -2
  83. package/src/editor/ui/UIPaletteSelector.svelte +9 -0
  84. package/src/editor/ui/UITokenSelector.svelte +26 -2
  85. package/src/editor/ui/a11y/A11yByEye.svelte +264 -0
  86. package/src/editor/ui/a11y/A11yComponentIssues.svelte +121 -0
  87. package/src/editor/ui/a11y/A11yFindingRow.svelte +119 -0
  88. package/src/editor/ui/a11y/A11yIssueRow.svelte +152 -0
  89. package/src/editor/ui/a11y/A11yOverview.svelte +246 -0
  90. package/src/editor/ui/a11y/A11yPanel.svelte +153 -0
  91. package/src/editor/ui/a11y/A11yPropertyMark.svelte +84 -0
  92. package/src/editor/ui/a11y/A11yRailMark.svelte +41 -0
  93. package/src/editor/ui/a11y/AccessibilityTab.svelte +321 -0
  94. package/src/editor/ui/a11y/a11yView.ts +257 -0
  95. package/src/editor/ui/contrastReadout.ts +45 -0
  96. package/src/live-tokens/data/colors-and-type/halloween.json +1 -1
  97. package/src/live-tokens/data/themes/autumn.json +35 -35
  98. package/src/live-tokens/data/themes/halloween.json +36 -36
  99. package/src/live-tokens/data/themes/midnight-study.json +35 -35
  100. package/src/live-tokens/data/themes/ocean.json +35 -35
  101. package/src/live-tokens/data/themes/royal-velvet.json +35 -35
  102. package/src/live-tokens/data/themes/sketchy.json +35 -35
  103. package/src/live-tokens/data/themes/spring-meadow.json +35 -35
  104. package/src/live-tokens/data/themes/sunset.json +35 -35
  105. package/src/system/components/Badge.svelte +3 -3
  106. package/src/system/components/Button.svelte +12 -6
  107. package/src/system/components/CodeSnippet.svelte +8 -1
  108. package/src/system/components/CornerBadge.svelte +3 -3
  109. package/src/system/components/Dialog.svelte +43 -14
  110. package/src/system/components/IconButton.svelte +1 -1
  111. package/src/system/components/Image.svelte +7 -0
  112. package/src/system/components/ImageLightbox.svelte +18 -25
  113. package/src/system/components/InlineEditActions.svelte +1 -1
  114. package/src/system/components/Input.svelte +9 -2
  115. package/src/system/components/MenuSelect.svelte +31 -3
  116. package/src/system/components/Notification.svelte +1 -1
  117. package/src/system/components/ProgressBar.svelte +7 -1
  118. package/src/system/components/RadioButton.svelte +36 -3
  119. package/src/system/components/SegmentedControl.svelte +36 -2
  120. package/src/system/components/SlidePager.svelte +3 -3
  121. package/src/system/components/Slider.svelte +19 -15
  122. package/src/system/components/TabBar.svelte +33 -3
  123. package/src/system/components/Toggle.svelte +3 -3
  124. package/src/system/components/VideoFrame.svelte +1 -1
  125. package/src/system/components/VideoLightbox.svelte +2 -2
  126. package/src/system/internal/focusTrap.ts +35 -0
  127. package/src/testing-js/__aria__/badge.yml +40 -0
  128. package/src/testing-js/__aria__/button.yml +88 -0
  129. package/src/testing-js/__aria__/callout.yml +12 -0
  130. package/src/testing-js/__aria__/card.yml +8 -0
  131. package/src/testing-js/__aria__/codesnippet.yml +21 -0
  132. package/src/testing-js/__aria__/collapsiblesection.yml +23 -0
  133. package/src/testing-js/__aria__/cornerbadge.yml +24 -0
  134. package/src/testing-js/__aria__/dialog.yml +42 -0
  135. package/src/testing-js/__aria__/iconbutton.yml +57 -0
  136. package/src/testing-js/__aria__/image.yml +10 -0
  137. package/src/testing-js/__aria__/imagelightbox.yml +50 -0
  138. package/src/testing-js/__aria__/inlineeditactions.yml +16 -0
  139. package/src/testing-js/__aria__/input.yml +61 -0
  140. package/src/testing-js/__aria__/menuselect.yml +30 -0
  141. package/src/testing-js/__aria__/notification.yml +30 -0
  142. package/src/testing-js/__aria__/panel.yml +2 -0
  143. package/src/testing-js/__aria__/progressbar.yml +2 -0
  144. package/src/testing-js/__aria__/radiobutton.yml +20 -0
  145. package/src/testing-js/__aria__/sectiondivider.yml +9 -0
  146. package/src/testing-js/__aria__/segmentedcontrol.yml +35 -0
  147. package/src/testing-js/__aria__/sidenavigation.yml +304 -0
  148. package/src/testing-js/__aria__/slidepager.yml +72 -0
  149. package/src/testing-js/__aria__/slider.yml +28 -0
  150. package/src/testing-js/__aria__/tabbar.yml +35 -0
  151. package/src/testing-js/__aria__/table.yml +144 -0
  152. package/src/testing-js/__aria__/toggle.yml +12 -0
  153. package/src/testing-js/__aria__/tooltip.yml +2 -0
  154. package/src/testing-js/__aria__/videoframe.yml +6 -0
  155. package/src/testing-js/__aria__/videolightbox.yml +12 -0
  156. package/src/testing-js/{chunk-WIZ6W7UT.js → chunk-3WCLMOM7.js} +2 -2
  157. package/src/testing-js/{chunk-NB3NZRBM.js → chunk-7CM5D7PF.js} +5 -1
  158. package/src/testing-js/chunk-7CM5D7PF.js.map +1 -0
  159. package/src/testing-js/{chunk-X6KHEVIG.js → chunk-7QXUJRSO.js} +97 -29
  160. package/src/testing-js/chunk-7QXUJRSO.js.map +1 -0
  161. package/src/testing-js/chunk-GZKUYLDW.js +193 -0
  162. package/src/testing-js/chunk-GZKUYLDW.js.map +1 -0
  163. package/src/testing-js/chunk-RTZQLW3F.js +649 -0
  164. package/src/testing-js/chunk-RTZQLW3F.js.map +1 -0
  165. package/src/testing-js/{chunk-SHBYZUPD.js → chunk-RWOT2XDB.js} +212 -15
  166. package/src/testing-js/chunk-RWOT2XDB.js.map +1 -0
  167. package/src/testing-js/{chunk-GKO27NGN.js → chunk-THWGMSRH.js} +2 -2
  168. package/src/testing-js/{chunk-3UKGXCDL.js → chunk-YVVR3GJZ.js} +1 -1
  169. package/src/testing-js/chunk-YVVR3GJZ.js.map +1 -0
  170. package/src/testing-js/component-a11y.contract.js +313 -0
  171. package/src/testing-js/component-a11y.contract.js.map +1 -0
  172. package/src/testing-js/component-alias.contract.js +2 -2
  173. package/src/testing-js/component-behavior.contract.js +2 -2
  174. package/src/testing-js/component-editor.contract.js +6 -5
  175. package/src/testing-js/component-editor.contract.js.map +1 -1
  176. package/src/testing-js/component-render.contract.js +10 -6
  177. package/src/testing-js/component-render.contract.js.map +1 -1
  178. package/src/testing-js/index.d.ts +44 -16
  179. package/src/testing-js/index.js +11 -11
  180. package/src/testing-js/index.js.map +1 -1
  181. package/src/testing-js/page-compliance.contract.js +132 -13
  182. package/src/testing-js/page-compliance.contract.js.map +1 -1
  183. package/src/testing-js/vitest.js +3 -3
  184. package/template/_gitignore +3 -0
  185. package/template/package.json +9 -0
  186. package/src/testing-js/chunk-3UKGXCDL.js.map +0 -1
  187. package/src/testing-js/chunk-NB3NZRBM.js.map +0 -1
  188. package/src/testing-js/chunk-SHBYZUPD.js.map +0 -1
  189. package/src/testing-js/chunk-X6KHEVIG.js.map +0 -1
  190. /package/src/testing-js/{chunk-WIZ6W7UT.js.map → chunk-3WCLMOM7.js.map} +0 -0
  191. /package/src/testing-js/{chunk-GKO27NGN.js.map → chunk-THWGMSRH.js.map} +0 -0
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  CONTRACTS_MODULE_ENV
3
- } from "./chunk-SHBYZUPD.js";
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-GKO27NGN.js.map
57
+ //# sourceMappingURL=chunk-THWGMSRH.js.map
@@ -45,4 +45,4 @@ export {
45
45
  requiresInteraction,
46
46
  partLocator
47
47
  };
48
- //# sourceMappingURL=chunk-3UKGXCDL.js.map
48
+ //# sourceMappingURL=chunk-YVVR3GJZ.js.map
@@ -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,313 @@
1
+ import {
2
+ ContractHarness
3
+ } from "./chunk-7QXUJRSO.js";
4
+ import "./chunk-3WCLMOM7.js";
5
+ import {
6
+ scanReducedMotion,
7
+ scanTextSpacing
8
+ } from "./chunk-GZKUYLDW.js";
9
+ import {
10
+ TRACE_AXES,
11
+ missingEngine,
12
+ recordA11y,
13
+ scanAxe,
14
+ scanBoundaries,
15
+ scanFocusIndicators,
16
+ scanFocusVisible,
17
+ traceProperties
18
+ } from "./chunk-RTZQLW3F.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-RWOT2XDB.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
+ 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
+ }
120
+ async function markStage(page, portals) {
121
+ await page.evaluate(({ stageAttr, treeAttr, portals: portals2 }) => {
122
+ for (const element of document.querySelectorAll(`[${stageAttr}], [${treeAttr}]`)) {
123
+ element.removeAttribute(stageAttr);
124
+ element.removeAttribute(treeAttr);
125
+ }
126
+ const group = [...document.querySelectorAll(".variant-group")].find((g) => g.checkVisibility());
127
+ const stage = group?.querySelector(".preview-stage, .sketch-scope");
128
+ if (!group || !stage) throw new Error("the visible variant group has no preview stage");
129
+ stage.setAttribute(stageAttr, "");
130
+ stage.scrollIntoView({ block: "nearest" });
131
+ group.querySelector(".sketch-scope")?.setAttribute(treeAttr, "");
132
+ for (const selector of portals2) {
133
+ for (const element of document.querySelectorAll(selector)) {
134
+ if (element.closest(`[${stageAttr}]`)) continue;
135
+ element.setAttribute(stageAttr, "");
136
+ element.setAttribute(treeAttr, "");
137
+ }
138
+ }
139
+ }, { stageAttr: STAGE_ATTR, treeAttr: TREE_ATTR, portals });
140
+ }
141
+ async function readTree(page) {
142
+ const roots = page.locator(`[${TREE_ATTR}]`);
143
+ const trees = [];
144
+ for (let i = 0; i < await roots.count(); i++) trees.push(await roots.nth(i).ariaSnapshot());
145
+ return trees.filter(Boolean).join("\n");
146
+ }
147
+ function serializeTrees(trees) {
148
+ return [...trees].map(([label, tree]) => tree ? `${JSON.stringify(label)}: |
149
+ ${tree.split("\n").map((line) => ` ${line}`).join("\n")}
150
+ ` : `${JSON.stringify(label)}: ""
151
+ `).join("");
152
+ }
153
+ function parseTrees(text) {
154
+ const trees = /* @__PURE__ */ new Map();
155
+ let label = null;
156
+ let lines = [];
157
+ const flush = () => {
158
+ if (label !== null) trees.set(label, lines.join("\n"));
159
+ };
160
+ for (const line of text.split("\n")) {
161
+ const key = /^(".*"): (?:\||"")$/.exec(line);
162
+ if (key) {
163
+ flush();
164
+ label = JSON.parse(key[1]);
165
+ lines = [];
166
+ } else if (line.startsWith(" ")) {
167
+ lines.push(line.slice(2));
168
+ }
169
+ }
170
+ flush();
171
+ return trees;
172
+ }
173
+ function lineChanges(before, after) {
174
+ const added = after.split("\n");
175
+ const removed = [];
176
+ for (const line of before.split("\n")) {
177
+ const index = added.indexOf(line);
178
+ if (index >= 0) added.splice(index, 1);
179
+ else removed.push(line);
180
+ }
181
+ return { removed, added };
182
+ }
183
+ function describeChange(before, after) {
184
+ if (before === void 0) return "The view has no recorded accessibility tree";
185
+ if (after === void 0) return "The recorded accessibility tree names a view the contract no longer declares";
186
+ const { removed, added } = lineChanges(before, after);
187
+ const quote = (lines) => lines.length === 0 ? "nothing" : lines.slice(0, 2).map((line) => `\`${line.trim()}\``).join(", ") + (lines.length > 2 ? ` and ${lines.length - 2} more` : "");
188
+ return `The accessibility tree differs from the recorded one: recorded ${quote(removed)}, now ${quote(added)}`;
189
+ }
190
+ function listViews(views) {
191
+ return [...views.slice(0, VIEW_LIMIT), ...views.length > VIEW_LIMIT ? [`and ${views.length - VIEW_LIMIT} more`] : []].join("; ");
192
+ }
193
+ function compareTrees(id, trees) {
194
+ const file = path.join(ARIA_DIR, `${id}.yml`);
195
+ if (process.env[UPDATE_ARIA_ENV] === "1" || !fs.existsSync(file)) {
196
+ if (ARIA_WRITABLE) {
197
+ fs.mkdirSync(ARIA_DIR, { recursive: true });
198
+ fs.writeFileSync(file, serializeTrees(trees));
199
+ }
200
+ return [];
201
+ }
202
+ const recorded = parseTrees(fs.readFileSync(file, "utf8"));
203
+ const changes = /* @__PURE__ */ new Map();
204
+ for (const label of /* @__PURE__ */ new Set([...recorded.keys(), ...trees.keys()])) {
205
+ const before = recorded.get(label);
206
+ const after = trees.get(label);
207
+ if (before === after) continue;
208
+ const help = describeChange(before, after);
209
+ const { removed, added } = lineChanges(before ?? "", after ?? "");
210
+ let change = changes.get(help);
211
+ if (!change) {
212
+ const diff = [...removed.map((line) => `-${line}`), ...added.map((line) => `+${line}`)].join("\n");
213
+ changes.set(help, change = { diff, views: [] });
214
+ }
215
+ change.views.push(label);
216
+ }
217
+ return [...changes].map(([help, { diff, views }]) => ({
218
+ rule: "a11y-component",
219
+ engineRule: "aria-snapshot",
220
+ impact: null,
221
+ target: `__aria__/${id}.yml`,
222
+ html: diff.slice(0, DIFF_LIMIT),
223
+ help: `${help}. Rerun with --update-aria once the change is deliberate`,
224
+ helpUrl: ARIA_HELP_URL,
225
+ view: listViews(views)
226
+ }));
227
+ }
228
+ var Occurrences = class {
229
+ constructor(trace) {
230
+ this.trace = trace;
231
+ }
232
+ trace;
233
+ entries = /* @__PURE__ */ new Map();
234
+ async add(view, results) {
235
+ for (const item of results) {
236
+ const { result, trace } = "result" in item ? item : { result: item, trace: void 0 };
237
+ const key = [result.rule, result.engineRule, result.target].join("\n");
238
+ let entry = this.entries.get(key);
239
+ if (!entry) {
240
+ const variables = await this.trace(result, trace);
241
+ this.entries.set(key, entry = { result: variables.length > 0 ? { ...result, variables } : result, views: [] });
242
+ }
243
+ if (!entry.views.includes(view)) entry.views.push(view);
244
+ }
245
+ }
246
+ results() {
247
+ return [...this.entries.values()].map(({ result, views }) => ({ ...result, view: listViews(views) }));
248
+ }
249
+ };
250
+ for (const contract of await selectedContracts()) {
251
+ test(`${contract.id} records its accessibility findings in every variant and state`, async ({ page }) => {
252
+ test.setTimeout(10 * 6e4);
253
+ const missing = await missingEngine(["axe", "alfa"]);
254
+ if (missing) {
255
+ test.info().annotations.push({ type: "inapplicable", description: missing });
256
+ return;
257
+ }
258
+ const harness = await ContractHarness.open(page, contract);
259
+ await page.addStyleTag({ content: STAGE_CSS });
260
+ const { plain, setUp } = viewGroups(contract);
261
+ const portals = portalSelectors(contract);
262
+ const presets = process.env[A11Y_THEMES_ENV] === "all" ? PRESETS : [];
263
+ const known = await harness.propertyVariables();
264
+ const boundaries = (contract.boundaries ?? []).map(({ part, label, when }) => ({ selector: partLocator(contract, part).selector, label, when }));
265
+ const root = partLocator(contract, contract.root).selector;
266
+ const own = [root, ...portals];
267
+ const found = new Occurrences(async (result, request) => {
268
+ const axes = TRACE_AXES[result.engineRule];
269
+ return axes ? traceProperties(page, { selector: result.target, scope: SCOPE, ...request }, axes, known) : [];
270
+ });
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
+ });
275
+ const trees = /* @__PURE__ */ new Map();
276
+ const scan = async ({ label, view }) => {
277
+ await harness.selectView(view);
278
+ await markStage(page, portals);
279
+ await found.add(label, await scanAxe(page, SCOPE, EXCLUDE, "a11y-component"));
280
+ await scanFocus(view, label, true);
281
+ await found.add(label, await scanBoundaries(page, SCOPE, EXCLUDE, boundaries, root));
282
+ trees.set(label, await readTree(page));
283
+ await found.add(label, await scanTextSpacing(page, SCOPE, EXCLUDE, own));
284
+ await found.add(label, await scanReducedMotion(page, SCOPE, EXCLUDE, own));
285
+ };
286
+ const scanContrast = async ({ label, view }, theme) => {
287
+ const themed = `${label} under ${theme}`;
288
+ await markStage(page, portals);
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));
292
+ };
293
+ for (const scanView of plain) await scan(scanView);
294
+ for (const theme of presets) {
295
+ await harness.previewThemeUncovered(theme);
296
+ for (const scanView of plain) {
297
+ await harness.selectView(scanView.view);
298
+ await scanContrast(scanView, theme);
299
+ }
300
+ await harness.cancelUncoveredPreview();
301
+ }
302
+ for (const scanView of setUp) {
303
+ await scan(scanView);
304
+ for (const theme of presets) {
305
+ await harness.previewThemeUncovered(theme);
306
+ await scanContrast(scanView, theme);
307
+ await harness.cancelUncoveredPreview();
308
+ }
309
+ }
310
+ recordA11y([...found.results(), ...compareTrees(contract.id, trees)]);
311
+ });
312
+ }
313
+ //# 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\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,7 +1,7 @@
1
1
  import {
2
2
  openComponentsEditor
3
- } from "./chunk-WIZ6W7UT.js";
4
- import "./chunk-NB3NZRBM.js";
3
+ } from "./chunk-3WCLMOM7.js";
4
+ import "./chunk-7CM5D7PF.js";
5
5
 
6
6
  // src/testing/component-alias.contract.ts
7
7
  import fs from "fs";
@@ -2,10 +2,10 @@ import {
2
2
  ContractViolation,
3
3
  isInapplicable,
4
4
  partLocator
5
- } from "./chunk-3UKGXCDL.js";
5
+ } from "./chunk-YVVR3GJZ.js";
6
6
  import {
7
7
  selectedContracts
8
- } from "./chunk-SHBYZUPD.js";
8
+ } from "./chunk-RWOT2XDB.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-X6KHEVIG.js";
4
- import "./chunk-WIZ6W7UT.js";
3
+ } from "./chunk-7QXUJRSO.js";
4
+ import "./chunk-3WCLMOM7.js";
5
+ import "./chunk-RTZQLW3F.js";
6
+ import "./chunk-7CM5D7PF.js";
5
7
  import {
6
8
  ContractViolation,
7
9
  isInapplicable
8
- } from "./chunk-3UKGXCDL.js";
9
- import "./chunk-NB3NZRBM.js";
10
+ } from "./chunk-YVVR3GJZ.js";
10
11
  import {
11
12
  allContracts,
12
13
  selectedContracts
13
- } from "./chunk-SHBYZUPD.js";
14
+ } from "./chunk-RWOT2XDB.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":";;;;;;;;;;;;;;;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":[]}
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":[]}
@@ -1,14 +1,15 @@
1
1
  import {
2
2
  ContractHarness
3
- } from "./chunk-X6KHEVIG.js";
3
+ } from "./chunk-7QXUJRSO.js";
4
4
  import {
5
5
  openComponentsEditor
6
- } from "./chunk-WIZ6W7UT.js";
7
- import "./chunk-3UKGXCDL.js";
8
- import "./chunk-NB3NZRBM.js";
6
+ } from "./chunk-3WCLMOM7.js";
7
+ import "./chunk-RTZQLW3F.js";
8
+ import "./chunk-7CM5D7PF.js";
9
+ import "./chunk-YVVR3GJZ.js";
9
10
  import {
10
11
  selectedContracts
11
- } from "./chunk-SHBYZUPD.js";
12
+ } from "./chunk-RWOT2XDB.js";
12
13
 
13
14
  // src/testing/component-render.contract.ts
14
15
  import fs from "fs";
@@ -154,6 +155,7 @@ async function exerciseVisibleGradientControls(page, componentAliases, exercised
154
155
  const variable = await editor.getAttribute("data-token-variable");
155
156
  if (!variable || !componentAliases.includes(variable) || exercised.has(variable)) continue;
156
157
  const before = await page.evaluate((name) => document.documentElement.style.getPropertyValue(name).trim(), variable);
158
+ await editor.evaluate((el) => el.scrollIntoView({ block: "center" }));
157
159
  const solid = editor.getByRole("radio", { name: "Solid" });
158
160
  if (await solid.count() && !await solid.isChecked()) await solid.check({ force: true });
159
161
  const picker = editor.locator(".picker-slot .ui-token-selector").first();
@@ -476,7 +478,9 @@ for (const [component, aliases] of aliasesByComponent) {
476
478
  await unchecked.nth(index).click({ force: true });
477
479
  }
478
480
  }
479
- const dialogAction = page.locator('[role="dialog"] button:not(:disabled)').first();
481
+ const dialogAction = page.locator(
482
+ '[role="dialog"]:not(.tabs-preview [role="dialog"]) button:not(:disabled)'
483
+ ).first();
480
484
  if (await dialogAction.count()) await dialogAction.hover({ force: true });
481
485
  await exerciseVisibleTokenControls(page, componentAliases, controlExercised, proof);
482
486
  const gates = await exerciseVisibleCompositeControls(page, componentAliases, controlExercised);