@cssints/apg-specs 0.0.0-stage → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (260) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +65 -2
  3. package/dist/apg/accordion.bridge.d.mts +35 -0
  4. package/dist/apg/accordion.bridge.mjs +326 -0
  5. package/dist/apg/accordion.model.json +8397 -0
  6. package/dist/apg/alert.bridge.d.mts +22 -0
  7. package/dist/apg/alert.bridge.mjs +86 -0
  8. package/dist/apg/alert.model.json +871 -0
  9. package/dist/apg/alertdialog.bridge.d.mts +21 -0
  10. package/dist/apg/alertdialog.bridge.mjs +221 -0
  11. package/dist/apg/alertdialog.model.json +5232 -0
  12. package/dist/apg/breadcrumb.bridge.d.mts +26 -0
  13. package/dist/apg/breadcrumb.bridge.mjs +120 -0
  14. package/dist/apg/breadcrumb.model.json +574 -0
  15. package/dist/apg/button.bridge.d.mts +35 -0
  16. package/dist/apg/button.bridge.mjs +208 -0
  17. package/dist/apg/button.model.json +5814 -0
  18. package/dist/apg/carousel.bridge.d.mts +44 -0
  19. package/dist/apg/carousel.bridge.mjs +262 -0
  20. package/dist/apg/carousel.model.json +8963 -0
  21. package/dist/apg/checkbox-group.bridge.d.mts +25 -0
  22. package/dist/apg/checkbox-group.bridge.mjs +276 -0
  23. package/dist/apg/checkbox-group.model.json +3863 -0
  24. package/dist/apg/checkbox.bridge.d.mts +34 -0
  25. package/dist/apg/checkbox.bridge.mjs +253 -0
  26. package/dist/apg/checkbox.model.json +3158 -0
  27. package/dist/apg/combobox-autocomplete.bridge.d.mts +14 -0
  28. package/dist/apg/combobox-autocomplete.bridge.mjs +326 -0
  29. package/dist/apg/combobox-autocomplete.model.json +11586 -0
  30. package/dist/apg/combobox-dialog.bridge.d.mts +17 -0
  31. package/dist/apg/combobox-dialog.bridge.mjs +250 -0
  32. package/dist/apg/combobox-dialog.model.json +8981 -0
  33. package/dist/apg/combobox-grid.bridge.d.mts +17 -0
  34. package/dist/apg/combobox-grid.bridge.mjs +227 -0
  35. package/dist/apg/combobox-grid.model.json +8554 -0
  36. package/dist/apg/combobox-select-only.bridge.d.mts +24 -0
  37. package/dist/apg/combobox-select-only.bridge.mjs +304 -0
  38. package/dist/apg/combobox-select-only.model.json +7846 -0
  39. package/dist/apg/combobox-tree.bridge.d.mts +17 -0
  40. package/dist/apg/combobox-tree.bridge.mjs +211 -0
  41. package/dist/apg/combobox-tree.model.json +9345 -0
  42. package/dist/apg/dialog-modal.bridge.d.mts +35 -0
  43. package/dist/apg/dialog-modal.bridge.mjs +186 -0
  44. package/dist/apg/dialog-modal.model.json +7580 -0
  45. package/dist/apg/disclosure.bridge.d.mts +25 -0
  46. package/dist/apg/disclosure.bridge.mjs +202 -0
  47. package/dist/apg/disclosure.model.json +4703 -0
  48. package/dist/apg/feed.bridge.d.mts +31 -0
  49. package/dist/apg/feed.bridge.mjs +246 -0
  50. package/dist/apg/feed.model.json +4523 -0
  51. package/dist/apg/grid-selection.bridge.d.mts +13 -0
  52. package/dist/apg/grid-selection.bridge.mjs +160 -0
  53. package/dist/apg/grid-selection.model.json +6974 -0
  54. package/dist/apg/grid-sort.bridge.d.mts +12 -0
  55. package/dist/apg/grid-sort.bridge.mjs +138 -0
  56. package/dist/apg/grid-sort.model.json +3799 -0
  57. package/dist/apg/grid.bridge.d.mts +54 -0
  58. package/dist/apg/grid.bridge.mjs +441 -0
  59. package/dist/apg/grid.model.json +3802 -0
  60. package/dist/apg/link.bridge.d.mts +24 -0
  61. package/dist/apg/link.bridge.mjs +126 -0
  62. package/dist/apg/link.model.json +694 -0
  63. package/dist/apg/listbox-multi.bridge.d.mts +10 -0
  64. package/dist/apg/listbox-multi.bridge.mjs +261 -0
  65. package/dist/apg/listbox-multi.model.json +10893 -0
  66. package/dist/apg/listbox-rearrangeable.bridge.d.mts +20 -0
  67. package/dist/apg/listbox-rearrangeable.bridge.mjs +237 -0
  68. package/dist/apg/listbox-rearrangeable.model.json +17592 -0
  69. package/dist/apg/listbox-shared.d.mts +58 -0
  70. package/dist/apg/listbox-shared.mjs +171 -0
  71. package/dist/apg/listbox-single.bridge.d.mts +10 -0
  72. package/dist/apg/listbox-single.bridge.mjs +196 -0
  73. package/dist/apg/listbox-single.model.json +8024 -0
  74. package/dist/apg/listbox-typeahead.bridge.d.mts +10 -0
  75. package/dist/apg/listbox-typeahead.bridge.mjs +153 -0
  76. package/dist/apg/listbox-typeahead.model.json +5615 -0
  77. package/dist/apg/menu-button.bridge.d.mts +29 -0
  78. package/dist/apg/menu-button.bridge.mjs +259 -0
  79. package/dist/apg/menu-button.model.json +5706 -0
  80. package/dist/apg/menu-checkable.bridge.d.mts +25 -0
  81. package/dist/apg/menu-checkable.bridge.mjs +226 -0
  82. package/dist/apg/menu-checkable.model.json +4277 -0
  83. package/dist/apg/menu-menubar.bridge.d.mts +27 -0
  84. package/dist/apg/menu-menubar.bridge.mjs +273 -0
  85. package/dist/apg/menu-menubar.model.json +9540 -0
  86. package/dist/apg/meter.bridge.d.mts +28 -0
  87. package/dist/apg/meter.bridge.mjs +159 -0
  88. package/dist/apg/meter.model.json +1185 -0
  89. package/dist/apg/player.bridge.d.mts +24 -0
  90. package/dist/apg/player.bridge.mjs +342 -0
  91. package/dist/apg/player.model.json +11013 -0
  92. package/dist/apg/radio.bridge.d.mts +35 -0
  93. package/dist/apg/radio.bridge.mjs +371 -0
  94. package/dist/apg/radio.model.json +5011 -0
  95. package/dist/apg/slider-multithumb.bridge.d.mts +34 -0
  96. package/dist/apg/slider-multithumb.bridge.mjs +365 -0
  97. package/dist/apg/slider-multithumb.model.json +5774 -0
  98. package/dist/apg/slider.bridge.d.mts +54 -0
  99. package/dist/apg/slider.bridge.mjs +359 -0
  100. package/dist/apg/slider.model.json +4881 -0
  101. package/dist/apg/spinbutton.bridge.d.mts +41 -0
  102. package/dist/apg/spinbutton.bridge.mjs +327 -0
  103. package/dist/apg/spinbutton.model.json +7571 -0
  104. package/dist/apg/switch.bridge.d.mts +30 -0
  105. package/dist/apg/switch.bridge.mjs +243 -0
  106. package/dist/apg/switch.model.json +3159 -0
  107. package/dist/apg/tabs.bridge.d.mts +41 -0
  108. package/dist/apg/tabs.bridge.mjs +294 -0
  109. package/dist/apg/tabs.model.json +7659 -0
  110. package/dist/apg/timeline.bridge.d.mts +36 -0
  111. package/dist/apg/timeline.bridge.mjs +265 -0
  112. package/dist/apg/timeline.model.json +6189 -0
  113. package/dist/apg/toolbar.bridge.d.mts +25 -0
  114. package/dist/apg/toolbar.bridge.mjs +307 -0
  115. package/dist/apg/toolbar.model.json +6616 -0
  116. package/dist/apg/tooltip.bridge.d.mts +36 -0
  117. package/dist/apg/tooltip.bridge.mjs +208 -0
  118. package/dist/apg/tooltip.model.json +8295 -0
  119. package/dist/apg/treegrid.bridge.d.mts +28 -0
  120. package/dist/apg/treegrid.bridge.mjs +311 -0
  121. package/dist/apg/treegrid.model.json +17358 -0
  122. package/dist/apg/treeview.bridge.d.mts +26 -0
  123. package/dist/apg/treeview.bridge.mjs +396 -0
  124. package/dist/apg/treeview.model.json +13745 -0
  125. package/dist/apg/windowsplitter.bridge.d.mts +36 -0
  126. package/dist/apg/windowsplitter.bridge.mjs +242 -0
  127. package/dist/apg/windowsplitter.model.json +6253 -0
  128. package/dist/canvas/camera.model.json +6568 -0
  129. package/dist/canvas/canvas.bridge.d.mts +190 -0
  130. package/dist/canvas/canvas.bridge.mjs +729 -0
  131. package/dist/canvas/pointer.model.json +17095 -0
  132. package/dist/scenario.d.mts +233 -0
  133. package/dist/scenario.mjs +313 -0
  134. package/package.json +57 -4
  135. package/scenarios/apg/README.md +221 -0
  136. package/scenarios/apg/accordion.base-ui.json +1005 -0
  137. package/scenarios/apg/accordion.react-aria.json +929 -0
  138. package/scenarios/apg/alert.base-ui.json +81 -0
  139. package/scenarios/apg/alert.react-aria.json +98 -0
  140. package/scenarios/apg/alertdialog.base-ui.json +331 -0
  141. package/scenarios/apg/alertdialog.react-aria.json +225 -0
  142. package/scenarios/apg/breadcrumb.react-aria.json +312 -0
  143. package/scenarios/apg/button.ariakit.json +136 -0
  144. package/scenarios/apg/button.base-ui.json +747 -0
  145. package/scenarios/apg/button.react-aria.json +443 -0
  146. package/scenarios/apg/carousel.apg.json +909 -0
  147. package/scenarios/apg/checkbox.ariakit.json +299 -0
  148. package/scenarios/apg/checkbox.base-ui.json +1126 -0
  149. package/scenarios/apg/checkbox.react-aria.json +518 -0
  150. package/scenarios/apg/combobox.ariakit.json +945 -0
  151. package/scenarios/apg/combobox.base-ui.json +2444 -0
  152. package/scenarios/apg/combobox.react-aria.json +845 -0
  153. package/scenarios/apg/dialog-modal.ariakit.json +1102 -0
  154. package/scenarios/apg/dialog-modal.base-ui.json +775 -0
  155. package/scenarios/apg/dialog-modal.react-aria.json +638 -0
  156. package/scenarios/apg/disclosure.apg.json +349 -0
  157. package/scenarios/apg/disclosure.ariakit.json +163 -0
  158. package/scenarios/apg/disclosure.base-ui.json +451 -0
  159. package/scenarios/apg/disclosure.react-aria.json +643 -0
  160. package/scenarios/apg/feed.apg.json +442 -0
  161. package/scenarios/apg/grid.ariakit.json +197 -0
  162. package/scenarios/apg/grid.base-ui.json +720 -0
  163. package/scenarios/apg/grid.react-aria.json +969 -0
  164. package/scenarios/apg/landmarks.ariakit.json +93 -0
  165. package/scenarios/apg/landmarks.react-aria.json +330 -0
  166. package/scenarios/apg/link.ariakit.json +83 -0
  167. package/scenarios/apg/link.base-ui.json +88 -0
  168. package/scenarios/apg/link.react-aria.json +186 -0
  169. package/scenarios/apg/listbox.ariakit.json +78 -0
  170. package/scenarios/apg/listbox.base-ui.json +163 -0
  171. package/scenarios/apg/listbox.react-aria.json +1118 -0
  172. package/scenarios/apg/menu-button.ariakit.json +924 -0
  173. package/scenarios/apg/menu-button.base-ui.json +1155 -0
  174. package/scenarios/apg/menu-button.react-aria.json +1080 -0
  175. package/scenarios/apg/menubar.ariakit.json +758 -0
  176. package/scenarios/apg/menubar.base-ui.json +673 -0
  177. package/scenarios/apg/meter.base-ui.json +243 -0
  178. package/scenarios/apg/meter.react-aria.json +201 -0
  179. package/scenarios/apg/radio.ariakit.json +459 -0
  180. package/scenarios/apg/radio.base-ui.json +581 -0
  181. package/scenarios/apg/radio.react-aria.json +576 -0
  182. package/scenarios/apg/slider-multithumb.base-ui.json +316 -0
  183. package/scenarios/apg/slider-multithumb.react-aria.json +363 -0
  184. package/scenarios/apg/slider.base-ui.json +544 -0
  185. package/scenarios/apg/slider.react-aria.json +456 -0
  186. package/scenarios/apg/spinbutton.base-ui.json +1688 -0
  187. package/scenarios/apg/spinbutton.react-aria.json +426 -0
  188. package/scenarios/apg/switch.base-ui.json +456 -0
  189. package/scenarios/apg/switch.react-aria.json +303 -0
  190. package/scenarios/apg/table.ariakit.json +65 -0
  191. package/scenarios/apg/table.react-aria.json +221 -0
  192. package/scenarios/apg/tabs.ariakit.json +329 -0
  193. package/scenarios/apg/tabs.base-ui.json +875 -0
  194. package/scenarios/apg/tabs.react-aria.json +644 -0
  195. package/scenarios/apg/toolbar.ariakit.json +595 -0
  196. package/scenarios/apg/toolbar.base-ui.json +1189 -0
  197. package/scenarios/apg/toolbar.react-aria.json +383 -0
  198. package/scenarios/apg/tooltip.ariakit.json +441 -0
  199. package/scenarios/apg/tooltip.base-ui.json +411 -0
  200. package/scenarios/apg/tooltip.react-aria.json +454 -0
  201. package/scenarios/apg/treegrid.apg.json +2558 -0
  202. package/scenarios/apg/treegrid.react-aria.json +5094 -0
  203. package/scenarios/apg/treeview.apg.json +928 -0
  204. package/scenarios/apg/windowsplitter.apg.json +557 -0
  205. package/scenarios/canvas/README.md +69 -0
  206. package/scenarios/canvas/camera.contract.json +237 -0
  207. package/scenarios/canvas/camera.excalidraw.json +57 -0
  208. package/scenarios/canvas/marquee.excalidraw.json +122 -0
  209. package/scenarios/canvas/pan.excalidraw.json +46 -0
  210. package/scenarios/canvas/pointer.contract.json +528 -0
  211. package/scenarios/parts.json +291 -0
  212. package/shared/aria.qnt +186 -0
  213. package/shared/details-group.qnt +50 -0
  214. package/shared/focusgroup.qnt +152 -0
  215. package/shared/invokers.qnt +81 -0
  216. package/shared/popover.qnt +71 -0
  217. package/specs/apg/STATIC.md +103 -0
  218. package/specs/apg/accordion.qnt +578 -0
  219. package/specs/apg/alert.qnt +131 -0
  220. package/specs/apg/alertdialog.qnt +455 -0
  221. package/specs/apg/breadcrumb.qnt +88 -0
  222. package/specs/apg/button.qnt +445 -0
  223. package/specs/apg/carousel.qnt +732 -0
  224. package/specs/apg/checkbox-group.qnt +388 -0
  225. package/specs/apg/checkbox.qnt +394 -0
  226. package/specs/apg/combobox-autocomplete.qnt +764 -0
  227. package/specs/apg/combobox-dialog.qnt +627 -0
  228. package/specs/apg/combobox-grid.qnt +632 -0
  229. package/specs/apg/combobox-select-only.qnt +626 -0
  230. package/specs/apg/combobox-tree.qnt +633 -0
  231. package/specs/apg/dialog-modal.qnt +499 -0
  232. package/specs/apg/disclosure.qnt +309 -0
  233. package/specs/apg/feed.qnt +485 -0
  234. package/specs/apg/grid-selection.qnt +512 -0
  235. package/specs/apg/grid-sort.qnt +330 -0
  236. package/specs/apg/grid.qnt +360 -0
  237. package/specs/apg/link.qnt +119 -0
  238. package/specs/apg/listbox-multi.qnt +799 -0
  239. package/specs/apg/listbox-rearrangeable.qnt +801 -0
  240. package/specs/apg/listbox-single.qnt +511 -0
  241. package/specs/apg/listbox-typeahead.qnt +396 -0
  242. package/specs/apg/menu-button.qnt +363 -0
  243. package/specs/apg/menu-checkable.qnt +304 -0
  244. package/specs/apg/menu-menubar.qnt +531 -0
  245. package/specs/apg/meter.qnt +129 -0
  246. package/specs/apg/player.qnt +1081 -0
  247. package/specs/apg/radio.qnt +394 -0
  248. package/specs/apg/slider-multithumb.qnt +441 -0
  249. package/specs/apg/slider.qnt +396 -0
  250. package/specs/apg/spinbutton.qnt +494 -0
  251. package/specs/apg/switch.qnt +389 -0
  252. package/specs/apg/tabs.qnt +412 -0
  253. package/specs/apg/timeline.qnt +612 -0
  254. package/specs/apg/toolbar.qnt +364 -0
  255. package/specs/apg/tooltip.qnt +553 -0
  256. package/specs/apg/treegrid.qnt +983 -0
  257. package/specs/apg/treeview.qnt +1004 -0
  258. package/specs/apg/windowsplitter.qnt +487 -0
  259. package/specs/canvas/camera.qnt +407 -0
  260. package/specs/canvas/pointer.qnt +841 -0
@@ -0,0 +1,221 @@
1
+ # APG scenarios — upstream tests ported as library-neutral data
2
+
3
+ One file per pattern and source: `<pattern>.<source>.json`, where `<source>` is
4
+ `ariakit`, `base-ui`, `react-aria` or `apg` (scenarios written by us from the APG
5
+ page). Each scenario is a port of one upstream test (no code copied; behaviour
6
+ restated) and is replayed against the Quint spec (`specs/apg/<pattern>*.qnt`) and
7
+ the Solid implementation by `src/scenario.ts`.
8
+
9
+ **This file is the one format spec (format v2).** The canvas scenarios
10
+ (`../canvas/README.md`) share the envelope and the `skipped` shape below but have
11
+ their own step/expectation vocabulary (pointer geometry, camera) and are not
12
+ decoded by `src/scenario.ts`; that README says so. `src/scenario.ts` is the
13
+ executable version of this document: every field named here is decoded and typed
14
+ there, and `test/apg/scenario-format.test.ts` checks every file in this
15
+ directory against it.
16
+
17
+ Pinned upstreams (shallow clones in the session temp dir `upstream/`):
18
+
19
+ | source | repo | commit | licence |
20
+ | ---------- | -------------------- | ------------------------------------------ | ---------- |
21
+ | ariakit | ariakit/ariakit | `404c72257b879569feb08465da4ff9915fd873da` | MIT |
22
+ | base-ui | mui/base-ui | `19511bb171f3b360b006c94cf6d07e53cb446505` | MIT |
23
+ | react-aria | adobe/react-spectrum | `99e610236887da619ad9d54a1cd87983166c043d` | Apache-2.0 |
24
+
25
+ ## Envelope
26
+
27
+ ```jsonc
28
+ {
29
+ "pattern": "tabs", // APG pattern slug (w3.org/WAI/ARIA/apg/patterns/<slug>/)
30
+ "source": "react-aria",
31
+ "commit": "99e6102…", // null for source "apg"
32
+ "license": "Apache-2.0",
33
+ "upstream": "https://…", // source "apg" only: the APG page
34
+ "retrieved": "2026-10-03", // source "apg" only
35
+ "fixture": {}, // optional, informational (shared fixture description); never passed to a driver
36
+ "notes": "optional, file level",
37
+ "skipped": [{ "upstream": "…", "upstreamTest": "…", "reason": "…" }],
38
+ "scenarios": [
39
+ {
40
+ "id": "tabs.react-aria.arrow-right-moves-focus", // unique, kebab-case, `<pattern>.<source>.<slug>`
41
+ "upstream": "https://github.com/adobe/react-spectrum/blob/<commit>/<path>#L<line>",
42
+ "upstreamTest": "Tabs > should focus the next tab on ArrowRight",
43
+ "apg": "keyboard", // APG section the behaviour comes from, or "extension" if not in APG
44
+ "config": {
45
+ "orientation": "horizontal",
46
+ "activation": "automatic",
47
+ "items": 3,
48
+ },
49
+ "steps": [
50
+ { "event": "focus", "target": "tab:0" },
51
+ { "event": "key", "key": "ArrowRight", "target": "tab:0" },
52
+ {
53
+ "expect": {
54
+ "focus": "tab:1",
55
+ "attrs": { "tab:1": { "aria-selected": "true" } },
56
+ },
57
+ },
58
+ ],
59
+ "notes": "optional: divergence from APG, platform-specific behaviour",
60
+ },
61
+ ],
62
+ }
63
+ ```
64
+
65
+ `skipped` is always present (use `[]`) and always the **object form**
66
+ `{ upstream?, upstreamTest?, reason, id? }`: `reason` is required; `upstream` /
67
+ `upstreamTest` say which upstream test was not ported when there is one; `id` names
68
+ the would-be scenario for `apg`-source files. Bare strings are not allowed.
69
+
70
+ ## Steps
71
+
72
+ A step is **either** an action (has `event`) **or** an expectation (has only
73
+ `expect`). Never both: a step carrying `event` and `expect` is read as an action
74
+ with an unknown field and the scenario becomes unsupported, so write the check as
75
+ the following step.
76
+
77
+ Action fields (all decoded and typed in `ActionStep`):
78
+
79
+ | field | type | meaning |
80
+ | ------------------ | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
81
+ | `event` | string | `focus` `blur` `click` `pointerdown` `pointermove` `pointerup` `hover` `unhover` `key` `type` `prop` `wait` … |
82
+ | `target` | target (grammar below) | the part the event is delivered to; `key`/`type` first focuses it, as a user would (a `phase: "up"` key does not) |
83
+ | `key` | string | `KeyboardEvent.key` (`Enter`, `ArrowDown`, `Tab`, `" "`; `Space` is accepted for `" "`) |
84
+ | `text` | string | text typed by a `type` event |
85
+ | `prop` | string | prop name for a `prop` event; with no `value` it is a trigger (`beforematch` fires the `beforematch` event on the target) |
86
+ | `value` | JSON | the new value for `prop`, or the value an event carries |
87
+ | `set` | object | `prop` event changing several props at once: `{ "event": "prop", "set": { "open": true } }` |
88
+ | `repeat` | `true` \| integer | `true`: this keydown is an auto-repeat (`KeyboardEvent.repeat`); `N`: the key is held for `N` repeat keydowns |
89
+ | `ms` | number | duration of a `wait` event (fake-timer milliseconds) |
90
+ | `position` | number 0..1 | where on the target track a pointer event lands, as a fraction of its length |
91
+ | `modifiers` | `Alt`\|`Control`\|`Meta`\|`Shift` array | held modifiers (a set; write them sorted). Never `shiftKey`, `ctrl`, `altKey`, … |
92
+ | `phase` | `down` \| `up` | which half of a `key` press (default: the full press); `down` holds the key for a later `up` step, so a blur can land between |
93
+ | `button` | number | `MouseEvent.button` (0 primary, 1 auxiliary, 2 secondary) |
94
+ | `buttons` | number | `MouseEvent.buttons` bitmask |
95
+ | `pointerType` | `mouse` \| `pen` \| `touch` | `PointerEvent.pointerType` (default `mouse`) |
96
+ | `pointerId` | number | `PointerEvent.pointerId` |
97
+ | `detail` | number | `UIEvent.detail` of a click |
98
+ | `count` | number | click count (`2` = double click) |
99
+ | `canceled` | boolean | the event is canceled (`preventDefault`) before the widget sees it |
100
+ | `defaultPrevented` | boolean | the event is expected to end with its default prevented (`true`) or not (`false`) |
101
+ | `isComposing` | boolean | `KeyboardEvent.isComposing` |
102
+ | `inputEvent` | string | origin of a `type` event (`paste`, `programmatic`) |
103
+
104
+ Any other field is **unknown**: it is preserved on decode and makes the scenario
105
+ `unsupported` (skipped, never silently replayed as a different step).
106
+
107
+ Typed is not the same as supported. The core fields (`event` `target` `key`
108
+ `text` `prop` `value` `repeat` `ms`) are understood by every bridge. The rest
109
+ (`EXTENDED_STEP_FIELDS` in `src/scenario.ts`) are decoded for everyone, but a
110
+ scenario using one is `unsupported` until the bridge lists it in
111
+ `SpecBridge.stepFields`, because a bridge that ignored `modifiers` would replay a
112
+ different step. A bridge that honours a field declares it; nothing else changes.
113
+
114
+ ## Expectations
115
+
116
+ `{ "expect": { … } }` checks only what the upstream test checks:
117
+
118
+ | key | type | meaning |
119
+ | --------- | -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
120
+ | `focus` | target \| `null` | the part holding focus (`null` = nothing in the widget) |
121
+ | `attrs` | `{ target: { name: string \| null } }` | attribute (or reflected property) values as strings; `null` = absent; booleans are `"true"`/`"false"` |
122
+ | `visible` | target[] | exactly these parts are visible |
123
+ | `hidden` | target[] | these parts are not visible (the others are unconstrained) |
124
+ | `value` | JSON | the pattern's observable value |
125
+ | `events` | `{ callback: payload[] }` | callbacks fired since mount, in order, one payload per call: `{ "onSelectionChange": ["tab:1"] }`; a sentinel is still a one-element list (`["all"]`) |
126
+ | `checked` | target \| `null` | the checked item of a single-selection group (radio) |
127
+
128
+ The engine currently **checks** `focus`, `attrs`, `visible`, `value`, `events`.
129
+ `hidden` and `checked` are decoded and typed but reported as skipped
130
+ (`expectation not supported by the replay engine`) until the engine implements
131
+ them. Unknown expectation keys are preserved and reported the same way.
132
+
133
+ **Symbolic relationship refs.** For the IDREF attributes (`aria-controls`,
134
+ `aria-labelledby`, `aria-describedby`, `aria-activedescendant`, `aria-owns`,
135
+ `aria-details`, `aria-errormessage`, `aria-flowto`) an `attrs` value of the form
136
+ `<part>:<index>` (a whitespace-separated list is allowed) is a reference to that
137
+ part, not a literal id: `"aria-controls": "tabpanel:1"`,
138
+ `"aria-activedescendant": "option:0"`, `"aria-describedby": "description:0 error:0"`.
139
+ The driver resolves it to the part's real id. Anything that is not a target is a
140
+ literal id (`"aria-labelledby": "custom-trigger-id"`).
141
+
142
+ ## Target grammar
143
+
144
+ ```
145
+ target = part [ ":" locator ] { "/" child }
146
+ part = name tab tabpanel popup nested-dialog
147
+ locator = index tab:1 option:0
148
+ | path row:0/1/2 treeitem:0/1 menuitem:1/0 (indices joined by "/")
149
+ | path "," index cell:<row>,<col> cell:0/3,2 (row may be a path)
150
+ | name checkbox:parent button:disabled input:hidden (non-indexed variant)
151
+ child = name [ ":" index ] /checkbox /chevron /link /child:0 (n-th child element of the part)
152
+ name = [A-Za-z][A-Za-z0-9-]*
153
+ ```
154
+
155
+ A segment after `/` that starts with a digit continues the path; one that starts
156
+ with a letter is a child. `tab` and `tab:0` name the same single part. Paths are
157
+ always written with their part prefix (`row:0/1`, never a bare `0/1`).
158
+
159
+ ## Part names
160
+
161
+ `target` uses **scenario names**; the specs use their own. The table maps them
162
+ (the machine-readable source is [`../parts.json`](../parts.json), which the bridges
163
+ should read instead of hard-coding aliases; the `.qnt` files are not renamed).
164
+ `← spec x` means the spec calls the part `x`. Names in the last column are
165
+ page/fixture parts of the scenarios (the page around the widget, helper controls);
166
+ they may appear in targets but carry no spec meaning. `test/apg/scenario-format.test.ts`
167
+ fails when a scenario uses a name that is in neither column.
168
+
169
+ | pattern | spec | parts (scenario name ← spec name) | fixture parts |
170
+ | ------------------- | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------- |
171
+ | `accordion` | `accordion.qnt` | `trigger` ← spec `header`, `panel` ← spec `content`, `heading` | `menu`, `menuTrigger` |
172
+ | `alert` | `alert.qnt` | `alert`, `close` ← spec `dismiss` | `add`, `add-high`, `content`, `region`, `root`, `toast` |
173
+ | `alertdialog` | `alertdialog.qnt` | `trigger` ← spec `discard`, `dialog` ← spec `alertdialog`, `cancel` ← spec `no`, `confirm` ← spec `yes` | `backdrop`, `dismiss`, `secondary` |
174
+ | `breadcrumb` | `breadcrumb.qnt` | `item`, `link`, `nav` (no spec name), `breadcrumb` (no spec name) | — |
175
+ | `button` | `button.qnt` | `button`, `group` (no spec name), `text` (no spec name) | — |
176
+ | `carousel` | `carousel.qnt` | `carousel` (no spec name), `rotation`, `prev`, `next`, `tab`, `slide` | — |
177
+ | `checkbox` | `checkbox.qnt`, `checkbox-group.qnt` | `checkbox`, `group` (no spec name) | `button`, `input`, `label` |
178
+ | `combobox` | `combobox-autocomplete.qnt`, `combobox-dialog.qnt`, `combobox-grid.qnt`, `combobox-select-only.qnt`, `combobox-tree.qnt` | `input`, `trigger` ← spec `button`, `popup`, `option`, `gridcell` ← spec `cell`, `treeitem` ← spec `node` | `body`, `final-focus-button`, `label`, `next-button` |
179
+ | `dialog-modal` | `dialog-modal.qnt` | `trigger` ← spec `invoker`, `dialog`, `backdrop` (no spec name) | any kebab-case name |
180
+ | `disclosure` | `disclosure.qnt` | `trigger`, `panel`, `heading` (no spec name) | `disclosure` |
181
+ | `feed` | `feed.qnt` | `feed`, `article`, `before-feed` ← spec `before`, `after-feed` ← spec `after` | — |
182
+ | `grid` | `grid.qnt`, `grid-selection.qnt`, `grid-sort.qnt` | `cell`, `row`, `rowgroup` (no spec name), `columnheader` ← spec `header` | `after`, `body`, `checkbox`, `combobox`, `dialog`, `rowheader` |
183
+ | `landmarks` | — | `main` (no spec name), `navigation` (no spec name), `region` (no spec name), `button` (no spec name), `link` (no spec name), `listitem` (no spec name) | — |
184
+ | `link` | `link.qnt` | `link`, `button` (no spec name) | — |
185
+ | `listbox` | `listbox-single.qnt`, `listbox-multi.qnt`, `listbox-typeahead.qnt`, `listbox-rearrangeable.qnt` | `listbox`, `option` | `trigger` |
186
+ | `menu-button` | `menu-button.qnt`, `menu-checkable.qnt` | `trigger` ← spec `button`, `menu`, `menuitem` ← spec `item`, `submenu` (no spec name) | `after`, `body`, `dialog`, `dismiss`, `group`, `outside` |
187
+ | `menubar` | `menu-menubar.qnt` | `menubar`, `menu`, `menuitem` ← spec `item`, `submenu` | `body`, `outside` |
188
+ | `meter` | `meter.qnt` | `meter` | — |
189
+ | `radio` | `radio.qnt` | `radiogroup`, `radio` | — |
190
+ | `slider` | `slider.qnt` | `thumb`, `track` ← spec `rail` | `body` |
191
+ | `slider-multithumb` | `slider-multithumb.qnt` | `thumb`, `track` ← spec `rail` | `body` |
192
+ | `spinbutton` | `spinbutton.qnt` | `input`, `increment`, `decrement` | `description`, `error`, `group`, `label` |
193
+ | `switch` | `switch.qnt` | `switch` | `button`, `input`, `label` |
194
+ | `table` | — | `table` (no spec name), `rowgroup` (no spec name), `row` (no spec name), `columnheader` (no spec name), `rowheader` (no spec name), `gridcell` (no spec name), `caption` (no spec name), `colindex-cell` (no spec name), `colspan-cell` (no spec name) | — |
195
+ | `tabs` | `tabs.qnt` | `tablist`, `tab`, `tabpanel` | `link` |
196
+ | `toolbar` | `toolbar.qnt` | `toolbar`, `item` | `dialog`, `menuitem`, `option`, `popup` |
197
+ | `tooltip` | `tooltip.qnt` | `trigger`, `tooltip` | — |
198
+ | `treegrid` | `treegrid.qnt` | `treegrid` ← spec `grid`, `row`, `cell`, `chevron` ← spec `expander` | `checkbox`, `document`, `info`, `link`, `menu` |
199
+ | `treeview` | `treeview.qnt` | `tree`, `treeitem` ← spec `node` | — |
200
+ | `windowsplitter` | `windowsplitter.qnt` | `separator` ← spec `splitter`, `primary`, `secondary` | — |
201
+
202
+ Spec names are the names the `.qnt` header comments and action names use
203
+ (`click_header`, `click_button`, `focusSplitter`, …); where a spec encodes parts
204
+ as numbers (alertdialog/dialog-modal focus slots) the name is the role the
205
+ comment gives. `gridcell` and `treeitem` under `combobox` are reserved for the
206
+ grid/tree popups (no scenario uses them yet; the bridges already accept them).
207
+ `treegrid`'s `chevron` is a child part (`row:0/chevron`) that the spec calls
208
+ `expander`.
209
+
210
+ ## Rules
211
+
212
+ - `target` uses APG part names (see the table). Rendering details, styling and
213
+ React-only APIs are out of scope; list them under `skipped`.
214
+ - Do not invent a field: if a step needs something not listed here, add it to
215
+ this file and to `ActionStep` in the same change.
216
+ - Units for `position` (fraction of the track) and `ms` (fake-timer time) are
217
+ fixed; `button`/`buttons`/`pointerId`/`detail` are the DOM values.
218
+ - When a scenario's expectation and a primitive disagree, the
219
+ [divergence policy](../../headless/README.md#divergence-policy) decides which
220
+ wins; a deliberate miss stays a negative test in the primitive's `DIVERGENCES`
221
+ map, and `notes` records why.