@unotest/web 0.9.0 → 0.10.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.
@@ -69,6 +69,65 @@ Disambiguate multi-matches with `.filter({hasText: '…'})` or
69
69
  `.nth(N)`. The linter flags index-based picking
70
70
  (`lint:disambig-by-index`) because element order is brittle.
71
71
 
72
+ ## Discovery: snapshot mode vs ground mode
73
+
74
+ Which discovery tool exists depends on the server's mode — check the
75
+ tool list once. `get_page_snapshot` present — snapshot mode. Absent
76
+ (the server runs with `UNOTEST_SNAPSHOT=0`, ground mode) — it does not
77
+ exist, do NOT search or retry for it. In ground mode you normally need
78
+ no discovery call at all — act directly with an **intent locator**;
79
+ the runner grounds it inside the same call:
80
+
81
+ ```
82
+ locator: {kind:"locator", steps:[{kind:"intent",
83
+ intent:"<the element in the user's words>",
84
+ ordinal?: "first" | "last" | N}]}
85
+ ```
86
+
87
+ Positions go in `ordinal` (typed), never in the intent text. Name the
88
+ target itself, not its page region. Lookups for when you must look
89
+ before acting: `ground_element {intent, ordinal?}` (element line +
90
+ top-20 candidates) and `find_element {role, name, near?}` (exact ARIA
91
+ query). Any "re-`get_page_snapshot`" advice below reads as "retry the
92
+ intent step / re-ground via `ground_element`" in ground mode.
93
+
94
+ ## Batching: `explore_steps`
95
+
96
+ When the next several steps are known in advance, send ONE
97
+ `explore_steps {explorationId?, steps:[…]}` call — up to 25 steps, each
98
+ with the same shape as `explore_step`. Sequential; stops at the first
99
+ failure; the reply carries per-step status + `firstError`; only the
100
+ steps that executed are recorded.
101
+
102
+ Every step whose locator was an intent replies with `groundedTo` — the
103
+ element line the intent resolved to. Check the target there rather than
104
+ pre-probing with `ground_element`. A step that CHANGED the address also
105
+ replies with `url`: the last one in the batch is where you ended up, so
106
+ `get_active_context` / `get_url` afterwards re-reads what you have.
107
+
108
+ Record checks as assert steps — `assert_text {locator, text, options?:
109
+ {exact}}`, `assert_visible {locator}`, `assert_hidden {locator}`,
110
+ `assert_value {locator, value}`, `assert_count {locator, count}`,
111
+ `assert_url {pattern}`. When the brief names no check, verify with the
112
+ text it DID give you — the last thing you were told to click is on the
113
+ page you land on, so assert that name inside the same batch, quoting
114
+ the text alone (`"<name>"`, not `heading "<name>"` — the words often
115
+ live in a row or a link, and a role word then grounds to nothing);
116
+ don't spend calls hunting for something assertable.
117
+ `assert_url` is for briefs that name the destination URL. On a checkbox
118
+ / radio / switch `assert_value`
119
+ takes `"true"` / `"false"` and asserts the checked state — no CSS
120
+ `:checked` selector. They execute live, so a recorded assert has
121
+ already passed against the real page; never hand-edit assertions into
122
+ the generated file. The final batch of a scenario can pass `autoRun:
123
+ true`: when every step succeeds, a verification step is recorded and
124
+ the draft has no blocking warnings (FRAGILE_LOCATOR passes and is
125
+ reported in the reply; DYNAMIC_TEXT / NO_DSL_PRIMITIVE block), the same
126
+ call saves the scenario, resets the context and runs it
127
+ (`autoRun.run.next.outcome === "completed"` is the pass signal);
128
+ otherwise `autoRun.status: "skipped"` + reason and the manual
129
+ generate → save → run flow applies.
130
+
72
131
  ## Recording: name the `section` like a test-plan line
73
132
 
74
133
  Each `explore_step`/`explore_record` carries a `section` that becomes a
@@ -99,24 +158,36 @@ completion signal).
99
158
  ## When a scenario fails
100
159
 
101
160
  The MCP server keeps a paused-failure runtime alive (default
102
- `pauseOnFailure: true`) the browser context is still open. Use that:
161
+ `pauseOnFailure: true`) and `run_test` auto-attaches you to that live
162
+ page — your `get_page_snapshot` / `find_element` / `check_locator`
163
+ already target the page being debugged (no manual
164
+ `attach_debug_session`). Use that:
103
165
 
104
166
  1. **`inspect_runtime {runtimeId}`** — shows `lastFailure` (error +
105
167
  line/col + AST node) and `vars` (every assignment up to the pause).
106
168
  This tells you what was observed vs expected, and what intermediate
107
169
  state the scenario built up.
108
- 2. **`get_page_snapshot`** — compact outline of the active page in
170
+ 2. **`check_locator {locator}`** — paste the EXACT locator from the
171
+ failing line (e.g. `getByRole('row').filter({hasText: 'Uma Quinn'}).first()`).
172
+ It parses that string with the same DSL parser the runner uses and
173
+ evaluates it against the live paused page: `count` of matches, and —
174
+ when the chain ends in `.first()`/`.last()`/`.nth()` — `pinned.ofCount`,
175
+ how many elements that index op silently collapsed (a `.first()`
176
+ hiding 16 identical rows is the classic break), plus per-match
177
+ role/name/text/testId so you see WHERE it points. Verify the selector
178
+ at runtime instead of guessing or re-running the whole test.
179
+ 3. **`get_page_snapshot`** — compact outline of the active page in
109
180
  its failed state: regions grouped by kind (navigation / form /
110
181
  main / dialog / cmp / overlay / …), interactive actions per
111
182
  region, off-screen actions partitioned by side (top / bottom /
112
183
  left / right). Look for whether the locator is reachable, or
113
184
  whether the page navigated somewhere unexpected. The `_meta`
114
185
  section carries viewport + totals.
115
- 3. **`list_failures` + `get_failure_*`** — read from the failure bundle
186
+ 4. **`list_failures` + `get_failure_*`** — read from the failure bundle
116
187
  (failure.json, console.json, snapshot.json, screenshot.png).
117
188
  `get_failure_console` is often the giveaway when the failure is
118
189
  actually a runtime error in the app.
119
- 4. **`agent_fix {runId}`** — composes a structured prompt with the
190
+ 5. **`agent_fix {runId}`** — composes a structured prompt with the
120
191
  failure, the relevant scenario excerpt, and a classifier
121
192
  suggestion (`edit-locator` / `edit-assertion` / `edit-wait` /
122
193
  `edit-flow` / `investigate-app`). Use it as a starting hypothesis.