@orkestrel/scaffold 0.0.73 → 0.0.74
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/main.js +75 -34
- package/dist/bin/main.js.map +1 -1
- package/dist/host/agents/skills/orkestrel-prove-journey/SKILL.md +175 -29
- package/dist/host/agents/skills/orkestrel-prove-journey/references/captures.md +42 -15
- package/dist/host/agents/skills/orkestrel-prove-journey/references/decide.md +34 -15
- package/dist/host/agents/skills/orkestrel-prove-journey/references/layer.md +248 -52
- package/dist/host/agents/skills/orkestrel-prove-journey/references/statechart.md +255 -49
- package/dist/host/agents/skills/orkestrel-prove-journey/references/styles.md +111 -23
- package/dist/host/agents/transports/codex.md +7 -0
- package/dist/host/claude/agents/orkestrel.md +2 -2
- package/dist/host/claude/rules/documentation.md +2 -0
- package/dist/host/claude/rules/tests.md +5 -3
- package/dist/host/claude/rules/workspace.md +26 -15
- package/dist/host/guides/README.md +5 -0
- package/dist/host/guides/scaffold.md +97 -13
- package/dist/host/guides/test.md +744 -184
- package/dist/host/manifest.json +18 -18
- package/dist/host/tests/config.test.ts +159 -64
- package/dist/host/tests/policy.test.ts +11 -1
- package/dist/host/tests/setupPolicy.ts +571 -7
- package/dist/src/core/index.cjs +109 -18
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +19 -6
- package/dist/src/core/index.d.ts +19 -6
- package/dist/src/core/index.js +109 -19
- package/dist/src/core/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -3,18 +3,92 @@
|
|
|
3
3
|
Route every journey step through the published layer. Treat a journey that works around a missing
|
|
4
4
|
capability by reaching for a selector as a layer defect.
|
|
5
5
|
|
|
6
|
+
## The vocabulary
|
|
7
|
+
|
|
8
|
+
Import the vocabulary from the entries that publish it: `@orkestrel/test` for what is
|
|
9
|
+
host-independent, and `@orkestrel/test/browser` for what drives a browser. Verify against the
|
|
10
|
+
installed entry any name this reference or its siblings do not carry in a fence.
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
import type { TextWaitOptions, WaitOptions } from '@orkestrel/test'
|
|
14
|
+
import { waitForText } from '@orkestrel/test'
|
|
15
|
+
import type { StateOptions, StorageOptions, WebStorageInterface } from '@orkestrel/test/browser'
|
|
16
|
+
import {
|
|
17
|
+
ACCESSIBLE_ROLES,
|
|
18
|
+
FOCUSABLE_SELECTOR,
|
|
19
|
+
build,
|
|
20
|
+
clearStorage,
|
|
21
|
+
clickAccessible,
|
|
22
|
+
clickAccessibleWithin,
|
|
23
|
+
clickDisclosure,
|
|
24
|
+
commitInput,
|
|
25
|
+
createDragEvent,
|
|
26
|
+
createJournal,
|
|
27
|
+
createPointerEvent,
|
|
28
|
+
createStorage,
|
|
29
|
+
describeFocus,
|
|
30
|
+
describeTree,
|
|
31
|
+
fillAccessible,
|
|
32
|
+
isOutsideViewport,
|
|
33
|
+
isReachable,
|
|
34
|
+
isRendered,
|
|
35
|
+
mount,
|
|
36
|
+
pressKeys,
|
|
37
|
+
readFocus,
|
|
38
|
+
readHit,
|
|
39
|
+
readName,
|
|
40
|
+
readPage,
|
|
41
|
+
readPerception,
|
|
42
|
+
readRefusal,
|
|
43
|
+
readRole,
|
|
44
|
+
readStates,
|
|
45
|
+
readText,
|
|
46
|
+
readValue,
|
|
47
|
+
removeDatabase,
|
|
48
|
+
render,
|
|
49
|
+
resolveAccessible,
|
|
50
|
+
resolveRendered,
|
|
51
|
+
traverseAccessible,
|
|
52
|
+
typeAccessible,
|
|
53
|
+
typeInput,
|
|
54
|
+
waitForAnimations,
|
|
55
|
+
waitForFrame,
|
|
56
|
+
waitForState,
|
|
57
|
+
} from '@orkestrel/test/browser'
|
|
58
|
+
```
|
|
59
|
+
|
|
6
60
|
## Import, never implement
|
|
7
61
|
|
|
8
|
-
Import every journey helper from `@orkestrel/test/browser
|
|
9
|
-
|
|
62
|
+
Import every journey helper from `@orkestrel/test/browser`, and every host-independent wait and
|
|
63
|
+
table type from `@orkestrel/test`. Write one of your own only where those entries publish none for
|
|
64
|
+
the act.
|
|
10
65
|
|
|
11
66
|
- Place a helper you write in the workspace's browser test setup module, export it from there, and
|
|
12
67
|
name it for the human act it performs.
|
|
13
|
-
- Read the package's own exports before writing anything
|
|
14
|
-
defect under `AGENTS.md`, and a second implementation of one drifts from the first.
|
|
68
|
+
- Read the package's own exports before writing anything, under `AGENTS.md` § Design laws.
|
|
15
69
|
- Code every journey against the vocabulary in this file, which is the published one. Diagnose a
|
|
16
70
|
target that stops resolving here, and fix it in the application.
|
|
17
71
|
|
|
72
|
+
Prove that setup module with `tests/setupBrowser.test.ts`. A workspace born with the browser setup
|
|
73
|
+
runtime carries the `setup:browser` project, the `test:setup:browser` script, and that script's
|
|
74
|
+
place in the `test` chain. A workspace that acquires the runtime later activates the project in this
|
|
75
|
+
order:
|
|
76
|
+
|
|
77
|
+
1. Write `tests/setupBrowser.test.ts`. Writing that file selects the browser setup runtime.
|
|
78
|
+
2. Add `npm run test:setup:browser` to the `test` chain.
|
|
79
|
+
3. Run `scaffold repair`, which registers the browser-enabled `setup:browser` project and appends
|
|
80
|
+
the `test:setup:browser` script.
|
|
81
|
+
|
|
82
|
+
Run `scaffold repair` before the chain invocation and it refuses the `configs` group:
|
|
83
|
+
|
|
84
|
+
```text
|
|
85
|
+
The configs group is blocked because the manifest at <target> does not reach a Vitest project the planned configuration registers: setup:browser. No chain from test invokes it. test:setup:browser is not declared, so the script is missing as well as the gate: declare it and invoke it by name from the test chain. Exclude configs from --groups to write another group.
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Add the invocation and run `scaffold repair` again. The Node `setup` project excludes
|
|
89
|
+
`tests/setupBrowser.test.ts`, so a proof of a browser helper placed anywhere else runs without a
|
|
90
|
+
browser.
|
|
91
|
+
|
|
18
92
|
## What it drives
|
|
19
93
|
|
|
20
94
|
- Drive the real browser through the installed Vitest browser provider. The published verbs import
|
|
@@ -23,8 +97,9 @@ package publishes none for the act.
|
|
|
23
97
|
- Never dispatch a constructed event from a journey. The published `createPointerEvent`,
|
|
24
98
|
`createDragEvent`, `typeInput`, and `commitInput` serve a unit test whose subject is the handler;
|
|
25
99
|
a journey drives input through `clickAccessible`, `clickAccessibleWithin`, `clickDisclosure`,
|
|
26
|
-
`typeAccessible`, `fillAccessible`,
|
|
27
|
-
|
|
100
|
+
`typeAccessible`, `fillAccessible`, `traverseAccessible`, and `pressKeys`.
|
|
101
|
+
- Send every key sequence — Enter, Escape, arrows, modifiers, and combinations — through
|
|
102
|
+
`pressKeys`, which is the published verb for it.
|
|
28
103
|
- Yield with `waitForFrame` where a step needs the browser to paint before the next reading. Never
|
|
29
104
|
guard a fact with a fixed delay.
|
|
30
105
|
|
|
@@ -34,15 +109,20 @@ A journey verb resolves its own target from role and accessible name, and refuse
|
|
|
34
109
|
component instance, or a selector from the caller. A reader, a fixture builder, and a capture each
|
|
35
110
|
take one, because their subject is a node the caller already holds.
|
|
36
111
|
|
|
37
|
-
| Population
|
|
38
|
-
|
|
|
39
|
-
| `resolveAccessible`, `resolveRendered`, `clickAccessible`, `clickAccessibleWithin`, `clickDisclosure`, `typeAccessible`, `fillAccessible`, `traverseAccessible`, `readPerception`, `readPage`, `readFocus`, `readValue` | No |
|
|
40
|
-
| `readText`, `readRole`, `readName`, `readStates`, `describeTree`, `describeFocus`, `isReachable`, `isRendered`
|
|
41
|
-
| `readContrast`, `readRing`, `readLayers`, `readBackdrop`, `readStyle`, `readToken`, `readPixels`, `readClasses`, `extractStyles`, `extractOrphans`, `readRows`
|
|
42
|
-
| `mount`, `typeInput`, `commitInput`, `captureFrame`, and a portfolio's `place`
|
|
43
|
-
|
|
44
|
-
Never pass an element to a verb from a journey step. Read a step that would pass one as a missing
|
|
45
|
-
verb, and add the verb instead.
|
|
112
|
+
| Population | Takes an element |
|
|
113
|
+
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- |
|
|
114
|
+
| `resolveAccessible`, `resolveRendered`, `clickAccessible`, `clickAccessibleWithin`, `clickDisclosure`, `typeAccessible`, `fillAccessible`, `traverseAccessible`, `pressKeys`, `waitForState`, `readRefusal`, `readPerception`, `readPage`, `readFocus`, `readValue` | No |
|
|
115
|
+
| `readText`, `readRole`, `readName`, `readStates`, `describeTree`, `describeFocus`, `isReachable`, `isRendered`, `readHit`, `waitForAnimations` | Yes |
|
|
116
|
+
| `readContrast`, `readRing`, `readLayers`, `readBackdrop`, `readStyle`, `readToken`, `readPixels`, `readClasses`, `readCensus`, `extractStyles`, `extractOrphans`, `readRows` | Yes |
|
|
117
|
+
| `mount`, `typeInput`, `commitInput`, `captureFrame`, and a portfolio's `place` | Yes |
|
|
118
|
+
|
|
119
|
+
- Never pass an element to a verb from a journey step. Read a step that would pass one as a missing
|
|
120
|
+
verb, and add the verb instead.
|
|
121
|
+
- `waitForState` resolves its control afresh on every reading, so it takes the role and the name
|
|
122
|
+
rather than a node. `waitForAnimations` takes the element, because an animation belongs to a
|
|
123
|
+
subtree rather than to a name.
|
|
124
|
+
- `createStorage` and `clearStorage` take neither: one returns a store the test hands the
|
|
125
|
+
application, and the other empties the browser's own surfaces.
|
|
46
126
|
|
|
47
127
|
## The resolver
|
|
48
128
|
|
|
@@ -59,35 +139,168 @@ act itself scrolls into view.
|
|
|
59
139
|
honours opacity and CSS, has a box with non-zero width and height, carries a `tabIndex` of at
|
|
60
140
|
least zero, matches neither `:disabled` nor `[aria-disabled="true"]`, and has no `[inert]`
|
|
61
141
|
ancestor.
|
|
142
|
+
- Read a `true` from either predicate for a subject inside a shadow root as the element's own
|
|
143
|
+
answer. Each asks `closest` for the `aria-hidden` ancestor and the `[inert]` ancestor, and
|
|
144
|
+
`closest` never crosses a shadow boundary, so ask the host separately where the ancestor attribute
|
|
145
|
+
is the subject.
|
|
62
146
|
- Count a control a person can scroll to as reachable, and one that stays outside the viewport after
|
|
63
|
-
the scroll as unreachable.
|
|
147
|
+
the scroll as unreachable. Reach for `isOutsideViewport` where the rectangle itself is the
|
|
148
|
+
subject.
|
|
149
|
+
- Ask `readHit` where the question is what paints at a control's centre. A node it returns is no
|
|
150
|
+
proof of a cover: a `pointer-events: none` cover is absent from the hit test, and an element in a
|
|
151
|
+
shadow tree retargets to its host.
|
|
64
152
|
|
|
65
153
|
### The failure voices
|
|
66
154
|
|
|
67
|
-
Assert the one voice the case means. Never write an assertion that accepts more than one.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
|
72
|
-
|
|
|
73
|
-
|
|
|
74
|
-
|
|
|
75
|
-
|
|
|
76
|
-
|
|
|
77
|
-
|
|
|
78
|
-
|
|
|
79
|
-
|
|
|
80
|
-
|
|
|
81
|
-
|
|
|
82
|
-
| The
|
|
155
|
+
Assert the one voice the case means. Never write an assertion that accepts more than one. This table
|
|
156
|
+
carries this layer's own voices. Read the statechart voices in [statechart.md](statechart.md) and
|
|
157
|
+
the capture voices in [captures.md](captures.md).
|
|
158
|
+
|
|
159
|
+
| Condition | The voice thrown | Thrown by |
|
|
160
|
+
| ------------------------------------------------------ | ---------------------------------------------------------------------------------------- | ----------------------- |
|
|
161
|
+
| No element carries the name | `No interactive element has the accessible name "<name>"` | `resolveRendered` |
|
|
162
|
+
| Every match fails a reachability condition | `Interactive target "<name>" is not visible and focus-reachable` | `resolveRendered` |
|
|
163
|
+
| Several matches are reachable | `Interactive target "<name>" is ambiguous across <n> elements` | `resolveRendered` |
|
|
164
|
+
| Still off-viewport after being scrolled to | `Interactive target "<name>" is unreachable after scrolling` | `resolveAccessible` |
|
|
165
|
+
| The region holds no reachable match | `Interactive target "<name>" is not reachable inside "<region>"` | `clickAccessibleWithin` |
|
|
166
|
+
| The region holds several reachable matches | `Interactive target "<name>" is ambiguous across <n> elements inside "<region>"` | `clickAccessibleWithin` |
|
|
167
|
+
| No native disclosure is reachable under the name | `Native disclosure "<name>" is not visible and focus-reachable` | `clickDisclosure` |
|
|
168
|
+
| Several native disclosures carry the name | `Native disclosure "<name>" is ambiguous across <n> elements` | `clickDisclosure` |
|
|
169
|
+
| Forward Tab never lands on the target | `Interactive target "<name>" is not reachable through forward Tab traversal: <trail>` | `traverseAccessible` |
|
|
170
|
+
| The named region is hidden | `Named region "<name>" is not visible` | `readPerception` |
|
|
171
|
+
| Several named regions carry the name | `Named region "<name>" is ambiguous across <n> elements` | `readPerception` |
|
|
172
|
+
| The resolved control renders no value | `Interactive target "<name>" does not carry a value` | `readValue` |
|
|
173
|
+
| A key sequence reached nothing | `Key sequence "<keys>" was sent with nothing focused` | `pressKeys` |
|
|
174
|
+
| The state never arrived or never left | `Condition "<subject>" did not hold within <n>ms (waited <n>ms) (last states: <states>)` | `waitForState` |
|
|
175
|
+
| The animation subject is in no document | `Animation subject is not connected` | `waitForAnimations` |
|
|
176
|
+
| The paint never stopped moving | `Animation "<subject>" did not settle within <n>ms (waited <n>ms): <animations>` | `waitForAnimations` |
|
|
177
|
+
| The sentence never arrived | `Condition "<description>" did not hold within <n>ms (waited <n>ms)` | `waitForText` |
|
|
178
|
+
| A host withholds `getItem`, `setItem`, or `removeItem` | `Access is denied for <operation> "<key>"` | `createStorage` |
|
|
179
|
+
| A host withholds `length`, `clear`, or `key` | `Access is denied for <operation>` | `createStorage` |
|
|
180
|
+
| A write ran past the declared quota | `No room is left for <key>` | `createStorage` |
|
|
181
|
+
| A style reading has no computed color | `Computed foreground color is unavailable` | `readContrast` |
|
|
182
|
+
| A class census walked nothing | `Class census walked no element` | `readCensus` |
|
|
183
|
+
| A contrast control cannot straddle its bar | `Contrast control cannot straddle the bar <bar>` | `buildContrast` |
|
|
184
|
+
| A database deletion was blocked | `IndexedDB database "<name>" is blocked by an open connection` | `removeDatabase` |
|
|
83
185
|
|
|
84
186
|
- Report an absent control and a present-but-unreachable one as different findings: absence names
|
|
85
187
|
a missing control, and unreachability names the interface gating one that exists.
|
|
86
188
|
- Report ambiguity as a finding about the surface. Quote the match count from the message, and
|
|
87
189
|
re-target the journey by role or region.
|
|
190
|
+
- Assert a `DOMException` on its `name` as well as its message. A withheld storage operation raises
|
|
191
|
+
`SecurityError` and a write past the quota raises `QuotaExceededError`, which is the pair a denied
|
|
192
|
+
or full origin raises.
|
|
193
|
+
- Never assert a `could not be resolved` sentence. Each one is narrowing the resolver needs under
|
|
194
|
+
`noUncheckedIndexedAccess`, and no surface reaches it.
|
|
195
|
+
- Read a refused bound as a test defect rather than a finding. The wait family validates its budget
|
|
196
|
+
and its interval and names the argument the caller passed, under the subject `Wait` or
|
|
197
|
+
`Animation`.
|
|
88
198
|
- Read the package's own voice table before asserting a message this file does not list. A workspace
|
|
89
199
|
that transcribes a voice by memory asserts a sentence the package does not throw.
|
|
90
200
|
|
|
201
|
+
## Input and traversal
|
|
202
|
+
|
|
203
|
+
| Verb | Contract |
|
|
204
|
+
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
205
|
+
| `typeAccessible(name, text)` | Focus the field, select all, delete, then send real keystrokes. Escape the provider's key syntax in the text. |
|
|
206
|
+
| `fillAccessible(name, text)` | Replace the value in one operation for text too long to type. The real element still publishes real input. |
|
|
207
|
+
| `pressKeys(keys)` | Send a key sequence to whatever holds focus, and refuse the sequence where the document body holds focus or nothing does. `{` opens a key name and `[` opens a code name. |
|
|
208
|
+
| `traverseAccessible(name)` | Move focus by forward Tab from wherever focus is, and return the target after focus lands on it. |
|
|
209
|
+
|
|
210
|
+
- Reach for `typeAccessible` where the keystrokes are part of what the journey claims, and
|
|
211
|
+
`fillAccessible` where the text is only a payload the person pastes.
|
|
212
|
+
- Bring focus about first — through `traverseAccessible`, `clickAccessible`, or `typeAccessible` —
|
|
213
|
+
and then press. The refusal is what `pressKeys` adds over the provider's own keyboard call: a key
|
|
214
|
+
sent to the body reaches no control, and every assertion after it reads a surface the key never
|
|
215
|
+
touched.
|
|
216
|
+
- Escape the sequence yourself where the sequence is the subject. Reach for `typeAccessible` where
|
|
217
|
+
the text is the subject and the key syntax is in the way.
|
|
218
|
+
- Let `traverseAccessible` end the walk. It counts a step only where focus lands, stops at one
|
|
219
|
+
complete cycle of the tab order, carries the trail of what focus reached in its voice, and holds a
|
|
220
|
+
cap above the cycle so a page with no tab order fails instead of hanging.
|
|
221
|
+
- Never call the browser's focus method to place focus, and never hold a node reference across
|
|
222
|
+
traversal steps. A framework may replace the node between resolution and focus arrival.
|
|
223
|
+
|
|
224
|
+
## The waits
|
|
225
|
+
|
|
226
|
+
| Wait | Parks on | Returns |
|
|
227
|
+
| ------------------------------------------------ | ------------------------------------------- | ----------------------------------- |
|
|
228
|
+
| `waitForText(description, read, text, options?)` | A reading the caller supplies | The first reading that satisfies it |
|
|
229
|
+
| `waitForState(name, state, options?)` | What one control announces | The states read at resolution |
|
|
230
|
+
| `waitForState(role, name, state, options?)` | The same, resolved within one exact role | The states read at resolution |
|
|
231
|
+
| `waitForAnimations(element, options?)` | Each running animation's `finished` promise | Nothing |
|
|
232
|
+
| `waitForFrame()` | One `requestAnimationFrame` | Nothing |
|
|
233
|
+
|
|
234
|
+
- Scope `waitForText`'s reader as narrowly as the claim. Read a named region through
|
|
235
|
+
`readPerception` for an arrival assertion, and reach for `readPage` only where the claim is about
|
|
236
|
+
the whole page. A wait over the page resolves on the sentence wherever it lands.
|
|
237
|
+
- Carry the replaced sentence in `absent` whenever a value replaces another. A screen swapping one
|
|
238
|
+
sentence for another passes through a frame carrying both, so a wait naming the arrival alone
|
|
239
|
+
resolves on the frame the departing sentence is still painted in.
|
|
240
|
+
- Pass `exact` where the reading must equal the sentence rather than contain it. An empty
|
|
241
|
+
expectation is refused rather than satisfied.
|
|
242
|
+
- Spell a state for `waitForState` the way `readStates` reports it: `expanded`, `collapsed`,
|
|
243
|
+
`disabled`, `current`, `invalid`, `checked`, `required`, `readonly`, `described`, `busy`, and the
|
|
244
|
+
valued forms `pressed=<value>`, `selected=<value>`, and `live=<value>`.
|
|
245
|
+
- Pass `absent: true` to `waitForState` where the claim is that the state went away, and read the
|
|
246
|
+
returned states rather than taking a second reading afterwards.
|
|
247
|
+
- Take `waitForState` as the published replacement for a settle keyed to a framework's own class
|
|
248
|
+
names. Where the control announces nothing, the finding is the surface's: give it `aria-expanded`,
|
|
249
|
+
`aria-pressed`, or `aria-busy` rather than reading the classes a stylesheet happens to use.
|
|
250
|
+
- Take every style, contrast, and capture reading after `waitForAnimations` on the element whose
|
|
251
|
+
paint was moving. A reading taken mid-transition reports an interpolated frame no state of the
|
|
252
|
+
interface paints.
|
|
253
|
+
- Read a `waitForAnimations` timeout naming an animation that never finishes as a finding about the
|
|
254
|
+
reading rather than a budget to lengthen. It excludes an animation declaring infinite iterations,
|
|
255
|
+
a finished one filling its target, and a paused one, so a wait that resolves is a claim about the
|
|
256
|
+
paint that was moving.
|
|
257
|
+
|
|
258
|
+
## Reading a refusal
|
|
259
|
+
|
|
260
|
+
`readRefusal(name)` and `readRefusal(role, name)` return the sentence `resolveRendered` raised, or
|
|
261
|
+
`undefined` where the target resolved. Each rethrows a value that is not an `Error`.
|
|
262
|
+
|
|
263
|
+
- Assert the exact sentence. A comparison against a substring passes for a refusal about a different
|
|
264
|
+
condition, and a comparison against `undefined` alone passes on every refusal the layer throws.
|
|
265
|
+
- Read `undefined` as the target being reachable to an acting verb, because this drives the same
|
|
266
|
+
resolver without acting.
|
|
267
|
+
- Never branch a journey on the reading. A journey performs its steps unconditionally and lets the
|
|
268
|
+
verb's own refusal name what the interface withheld.
|
|
269
|
+
|
|
270
|
+
## Disclosures
|
|
271
|
+
|
|
272
|
+
- Drive a native `<summary>` with `clickDisclosure`, and only a native one. It applies the
|
|
273
|
+
resolver's reachability conditions, throws its own voices, and resolves what the provider's role
|
|
274
|
+
locators do not, because the platform exposes a native disclosure rather than a role.
|
|
275
|
+
- Drive an ARIA disclosure as what it is: a button. Click it with `clickAccessible`, then settle it
|
|
276
|
+
with `waitForState` on the state it announces.
|
|
277
|
+
- Report a control that opens a panel and announces no state as a surface finding. Author
|
|
278
|
+
`aria-expanded` on it rather than settling on the classes its framework toggles.
|
|
279
|
+
- Read a native summary's expansion with `readStates`, which reads the parent `details` element's
|
|
280
|
+
own `open` where the summary declares no `aria-expanded`.
|
|
281
|
+
|
|
282
|
+
## The named bans
|
|
283
|
+
|
|
284
|
+
Never reach past the interface in any of the following ways. Reach for the published replacement
|
|
285
|
+
beside each.
|
|
286
|
+
|
|
287
|
+
| Never | Reach for |
|
|
288
|
+
| ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
|
|
289
|
+
| An element resolved by id or class | `resolveAccessible`, `resolveRendered`, and the acting verbs |
|
|
290
|
+
| A class-list read standing in for a settle | `waitForState`, `waitForAnimations` |
|
|
291
|
+
| `document.elementFromPoint` | `readHit` |
|
|
292
|
+
| `element.focus()` | `traverseAccessible`, `pressKeys` |
|
|
293
|
+
| A navigation a journey step performs by a router call | The visible link or control that navigates, through `clickAccessible` or `clickAccessibleWithin` |
|
|
294
|
+
| A store or route state read standing in for a rendered assertion | `readPerception`, `readValue`, `readStates`, `waitForText` |
|
|
295
|
+
|
|
296
|
+
- Admit a selector only for a population that carries no role, declared in the workspace's browser
|
|
297
|
+
test setup module with the reason beside it.
|
|
298
|
+
- Read a class-list assertion as proving nothing about the cascade. A class present in the markup
|
|
299
|
+
and absent from every loaded stylesheet resolves to nothing, and the assertion passes on it.
|
|
300
|
+
- Take a store read or a route state read as corroboration beside a rendered assertion, never in
|
|
301
|
+
place of one. Never take a router call as corroboration: it changes the route rather than
|
|
302
|
+
reporting it.
|
|
303
|
+
|
|
91
304
|
## Role vocabulary
|
|
92
305
|
|
|
93
306
|
Never infer a role from markup. Confirm the computed role in the browser with `readRole` whenever a
|
|
@@ -99,9 +312,6 @@ target stops resolving, and read the exposed name with `readName` and the expose
|
|
|
99
312
|
miss immediately after such a change as this before treating the element as missing.
|
|
100
313
|
- Always target a tab by its role. A tab and its panel collide on a bare name by construction,
|
|
101
314
|
because the panel is labelled by its tab.
|
|
102
|
-
- Drive a `<summary>` with `clickDisclosure`, which applies the same reachability conditions and
|
|
103
|
-
throws its own voices. The provider's role locators do not resolve it, because it is exposed as a
|
|
104
|
-
native disclosure rather than through a role.
|
|
105
315
|
|
|
106
316
|
## Region-scoped resolution
|
|
107
317
|
|
|
@@ -109,23 +319,6 @@ Reach for `clickAccessibleWithin(region, role, name)` where a short verb such as
|
|
|
109
319
|
across a page, and where a rendered status completes a control's accessible name. It applies the
|
|
110
320
|
same reachability conditions inside the region and names the region in every voice it throws.
|
|
111
321
|
|
|
112
|
-
## Input and traversal
|
|
113
|
-
|
|
114
|
-
| Verb | Contract |
|
|
115
|
-
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
116
|
-
| `typeAccessible(name, text)` | Focus the field, select all, delete, then send real keystrokes. Escape the provider's key syntax in the text. |
|
|
117
|
-
| `fillAccessible(name, text)` | Replace the value in one operation for text too long to type. The real element still publishes real input. |
|
|
118
|
-
| `userEvent.keyboard(keys)` | Send a key sequence to whatever holds focus, for Enter, Escape, arrows, modifiers, and combinations. Place focus with `traverseAccessible`, `clickAccessible`, or `typeAccessible` first. |
|
|
119
|
-
| `traverseAccessible(name)` | Move focus by forward Tab from wherever focus is, and return the target after focus lands on it. |
|
|
120
|
-
|
|
121
|
-
- Reach for `typeAccessible` where the keystrokes are part of what the journey claims, and
|
|
122
|
-
`fillAccessible` where the text is only a payload the person pastes.
|
|
123
|
-
- Let `traverseAccessible` end the walk. It counts a step only where focus lands, stops at one
|
|
124
|
-
complete cycle of the tab order, carries the trail of what focus reached in its voice, and holds a
|
|
125
|
-
cap above the cycle so a page with no tab order fails instead of hanging.
|
|
126
|
-
- Never call the browser's focus method to place focus, and never hold a node reference across
|
|
127
|
-
traversal steps. A framework may replace the node between resolution and focus arrival.
|
|
128
|
-
|
|
129
322
|
## Perception
|
|
130
323
|
|
|
131
324
|
`readPerception(name)` returns the normalized `innerText` of exactly one visible named region,
|
|
@@ -149,6 +342,9 @@ dialog, table, tab panel, alert, or status. Quote that text in assertions.
|
|
|
149
342
|
- Undo everything a journey changed after each test: unmount, destroy the session, reset the theme,
|
|
150
343
|
return the route to its entry, and clear what the application persisted with `clearStorage` and
|
|
151
344
|
`removeDatabase`. Never let a journey inherit the previous journey's state.
|
|
345
|
+
- Hand the application a `createStorage` store where the subject is what a host withholds, and reach
|
|
346
|
+
for `clearStorage` where the browser's own surfaces are the subject. The constructed store is
|
|
347
|
+
backed by a map of its own, patches neither browser surface, and dispatches no `storage` event.
|
|
152
348
|
- Record what a journey did with `createJournal`, started inside the journey and stopped in a
|
|
153
349
|
`finally`. Its `steps` and `output` are the evidence a failing journey hands back, and the input
|
|
154
350
|
the run's written artifact composes.
|