@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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,216 @@ All notable changes to `@unotest/web` are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.10.0] - 2026-08-06
9
+
10
+ ### Minor Changes
11
+
12
+ - 7ed750c: API-setup primitives for pure-`_helpers/` test setup (multi-host RAG
13
+ smoke flows and similar):
14
+
15
+ - `apiCall(method, path, body?, headers?, options?)` gains
16
+ `{base: 'API_BASE_X'}` — the NAME of a `unotest/.env` variable holding
17
+ an http(s) base URL. Default behavior (path against
18
+ `sandbox.apiBaseUrl`) is unchanged; URLs still never appear in
19
+ scenarios.
20
+ - `apiCall` multipart upload: a body of the exact shape
21
+ `{file, field?, fields?}` posts `multipart/form-data` via
22
+ `fetch`+`FormData`. The path is relative and confined to
23
+ `sandbox.uploadDir` (new optional config field; defaults to the
24
+ project root) — absolute paths and `../` escapes throw, and a manual
25
+ `Content-Type` header with a multipart body is rejected.
26
+ - DSL validator now allows `break` inside loop bodies
27
+ (`break-outside-loop` error elsewhere) and index access on data
28
+ variables (`docs[0].objectsCount`); indexing a Locator is rejected as
29
+ `locator-index-access`, and `while` / `do…while` / `continue` /
30
+ `arr[i] = v` stay unsupported. Parser and executor already supported
31
+ these forms; only validation changed.
32
+
33
+ - b5b9710: Assert steps ground as whole units. `Intent.prefer` gains `"unit"`: the
34
+ grounder returns the semantic unit itself (heading / record / text) and
35
+ suppresses the record→control hop — the profile for read-only targets.
36
+ Web assert actions (`assert_text/visible/hidden/value/count`) declare
37
+ `intentTarget: "unit"` on their plugins, and the intent resolver
38
+ materializes a DOM ref for unit resolutions via the new
39
+ `materializeRefScript(nodeId)` (grounder-client dom-walker): one lazy
40
+ attribute write on the unit's own element, reused by later captures.
41
+ Previously an assert intent like "the page heading" was rejected with a
42
+ RECORD error and forced a `find_element` detour. Control-target errors
43
+ now name the unit kind correctly (heading / record / text block) and
44
+ point at assert steps for check-not-click intents.
45
+
46
+ Materialized record containers now resolve to a stable locator. A record
47
+ row / card / tree-item (`<li>`, `<tr>`, `<article>`) carries no accessible
48
+ name of its own — its text lives in children — so `RefResolver` gains a
49
+ container strategy: `getByRole(role).filter({hasText})`, keyed off the
50
+ container's short descendant text (capped so a whole table is never
51
+ captured) and verified for uniqueness + ref identity. Fixes `assert_visible`
52
+ on a grounded tree item failing with `no stable identifier`.
53
+
54
+ - c7b7b50: `assertValue` on a checkbox / radio / switch now asserts its **checked
55
+ state** instead of the DOM `value`. Their value is the submitted payload
56
+ (`"on"` by default) and is identical whether the box is ticked or not, so
57
+ the old behaviour made a correct test fail on DOM trivia (live agent run:
58
+ `assertValue(checkbox, "true")` → `expected "true", got "on"`).
59
+
60
+ Pass `'true'` / `'false'`; any other expectation on a checkable element is
61
+ rejected with a usage error naming the rule. Text inputs, textareas and
62
+ selects are unchanged. Applies to the `assert_value` MCP action too.
63
+
64
+ **Migration:** a test asserting the literal payload —
65
+ `assertValue(checkbox, 'on')` — now fails with that usage error. Assert
66
+ the state (`'true'`) instead; to read the raw attribute use
67
+ `getAttribute(loc, 'value')`.
68
+
69
+ The `WebDriver` page contract gains `checkedState(loc)` (`boolean | null`,
70
+ null for non-checkable elements) — relevant to custom driver
71
+ implementations.
72
+
73
+ `assert_value` also grounds intent locators as a **control** now
74
+ (`intentTarget: "control"`). Under the previous `"unit"` profile an
75
+ intent like "selection checkbox of the Pending row" resolved to the ROW,
76
+ materialized a ref on the `<tr>`, and failed with `Node is not an
77
+ <input>`. The read-only asserts (`assert_text` / `assert_visible` /
78
+ `assert_hidden` / `assert_count`) keep the unit profile.
79
+
80
+ - 7ed750c: Chat-feed-friendly wait primitives (ai-support plan, tasks 5–6):
81
+
82
+ - `waitForText` now waits for ≥1 VISIBLE occurrence instead of failing
83
+ strict mode on duplicated text; `{first: true}` becomes a no-op.
84
+ New `{exact: true}` option and regex matchers
85
+ (`waitForText(/\bhi\b/)`) fix substring-inside-a-word false matches.
86
+ - New `waitForCount(loc, n, {timeout, exact})` DSL primitive and
87
+ `wait_for_count` explore action: polls until the locator matches at
88
+ least `n` elements (exactly `n` with `{exact: true}`) — the "wait for
89
+ reply №N" pattern without index locators; counts as a verification
90
+ step for autoRun / NO_VERIFICATION.
91
+
92
+ - 65eb77c: Shared injected DOM helpers: one `createDomHelpers()` factory (visibility, ARIA roles, W3C accname, state flags, ref minting) now backs the grounder-client DOM walker and the web snapshot/find algorithms, composed into the page by `composeInjectedScript` — replaces four hand-maintained copies of the same in-page logic.
93
+
94
+ **Breaking (`@unotest/grounder-client`):** `captureRawTree` takes the helpers object as its first parameter, so passing it straight to `page.evaluate(captureRawTree)` no longer works (evaluate arguments are JSON-serialized and cannot carry the helpers). Migration: inject the serialized script instead — `page.evaluate(domWalkerScript())` — which composes the helpers automatically. New exports: `createDomHelpers`, `composeInjectedScript`, `InjectedDomHelpers`, `DomHelperOptions`, `InjectedScriptOptions`.
95
+
96
+ `@unotest/web` behavior deltas from unifying on the walker's ARIA mapping: `get_aria_snapshot` now maps `dl` to `list` (`dt`/`dd` stay name-only) and `tbody`/`thead`/`tfoot` to `rowgroup`, and never names structural containers from their text content; `find_element` matches against the full W3C accname (label-for lookup, composed multi-child names) instead of a reduced approximation.
97
+
98
+ - 0d31a8e: Recorded assertions and one-call test authoring in exploration mode. Six new recordable actions — `assert_text`, `assert_visible`, `assert_hidden`, `assert_value`, `assert_count`, `assert_url` — available in `explore_step`, `explore_steps` and `explore_record`; they execute live through the same poll-until-timeout cores as the runtime `assertText(...)` family (extracted to `dsl/assert-core.ts`), so a recorded assert has already passed against the real page and the generated DSL carries the matching assert lines. Assert steps count as verification for the `NO_VERIFICATION` save gate. New `autoRun: true` flag on `explore_steps` (requires `explorationId`): when every step succeeds, a verification step is recorded, the draft has no blocking warnings (`FRAGILE_LOCATOR` is informational — it passes and rides along in the reply; `DYNAMIC_TEXT` / `NO_DSL_PRIMITIVE` block, mirroring save's policy) and the target file is new, the same call stops the session, saves `unotest/e2e/<scenarioName>.js`, resets the browser context and runs the test with `run_test` semantics (`autoRun.run.next.outcome`); any gate failure degrades to `autoRun.status: "skipped"` + reason with the session left active. The green happy-path shrinks to `new_context` → `explore_start` → `explore_steps {…, autoRun: true}`. `UNOTEST_TOOLSET=core` unchanged — asserts are actions, not tools.
99
+ - 2b2c957: Two MCP round-trips removed from every authoring flow.
100
+
101
+ `explore_step` / `explore_steps` now reply with `url` whenever the step
102
+ CHANGED the page address — the same cure `groundedTo` gave intents, applied to
103
+ navigation: after a batch the agent reads where it ended up instead of spending
104
+ a call on `get_active_context` / `get_url`. Unchanged addresses print nothing,
105
+ so a batch of ten clicks on one page adds no lines.
106
+
107
+ `explore_start` opens the browser context itself when none is open, so the
108
+ `new_context` prologue is gone from the happy path. `new_context` keeps its
109
+ role as the explicit RESET (between the recording and the first run, and inside
110
+ `autoRun`); an existing or attached collaborative context is never re-created.
111
+
112
+ - 9f80b38: Steps whose locator was an intent now report `groundedTo` — the element
113
+ line the grounder resolved it to (`checkbox "Select row R-00147" (in row
114
+ "Row R-00147: … Pending …")`), per locator argument. Present on
115
+ `explore_step`, every step of an `explore_steps` batch, and
116
+ `explore_record`.
117
+
118
+ Until now a step's reply said only `executed / recorded / success`, so an
119
+ agent could not tell WHICH element a positional intent hit without a
120
+ separate `ground_element` probe — grounding the same intent twice. A live
121
+ run spent 4 extra calls and doubled grounder time doing exactly that. The
122
+ skill and the tool descriptions now point at `groundedTo` instead.
123
+
124
+ - 7ed750c: New `screenshot(name?, {fullPage?})` DSL command — on-demand PNG capture
125
+ of the active page into `unotest/.runs/<runId>/screenshot/<NNN>-<name>.png`
126
+ (returns the project-relative path). Evidence tool for green runs: fixes
127
+ what the page actually looked like at a chosen point. Each capture is
128
+ also mirrored as a `screenshot` RunArtifact event, so the viewer's event
129
+ stream links to it. In runtimes with no artifact dir (exploration) the
130
+ call logs a warning and returns `null` instead of failing the run.
131
+
132
+ ### Patch Changes
133
+
134
+ - 30c491b: The `write-e2e-test-ground` skill now records batch-first: when the task brief already spells out the step sequence, Phase 2 sends it as one `explore_steps` call (goto included, intent locators ground at execution time) instead of one `explore_step` per action, with a repair recipe for mid-batch failures. A/B across four eval tasks showed equal-or-better scores with fewer calls, lower cost, and 9–38% less agent time. The snapshot-mode skill is unchanged — its ref discipline requires a snapshot before locator steps, so pre-discovery batching does not apply.
135
+ - 574841f: Dedup pass across the tree (jscpd 0 clones, ratchet threshold 0). New public export in `@unotest/grounder-client`: `intEnvInRange(env, name, def, min, max)` — the `UNOTEST_GROUNDER_*` integer-env parser shared by the consumer side and the grounder server. Everything else is internal refactoring with no behavior change: protocol gains a shared fs-safe-name validator behind `validateCollectionName` / `validateSegmentName` and a shared env-file assignment iterator; core's `RuntimeInspection` becomes a `Pick` of `RuntimeStateFile`; dsl's `ExecutionWalker` threads one `WalkCtx` object instead of an eight-argument clump and AST nodes use constructor parameter properties; web deduplicates the MCP tool scaffolds, action plugins, DSL contracts, perception LRU caches, and semantic-dom grouping passes.
136
+ - 080ea5a: Grounder infra resilience. The MCP server now runs a cheap grounder
137
+ reachability ping at startup when `UNOTEST_GROUNDER_MODE` is `local` or
138
+ `remote` (`/api/tags` for local ollama, `/health` for a remote grounding
139
+ server) — non-blocking, warning-only. Network-level failures on the
140
+ intent / `ground_element` path are wrapped into a typed
141
+ `GrounderUnreachableError` citing the backend URL, a mode-specific fix
142
+ hint and the boot-time health verdict, instead of surfacing undici's raw
143
+ `fetch failed`. The ground-mode skill now spells out the exact fallback
144
+ recipe: a single `{kind:"intent"}` step, the `ref` from `find_element`,
145
+ or the ready-made `locator` object from `ground_element` — hand-assembled
146
+ `getByRole`/`css` locators are rejected in recording mode.
147
+ - Updated dependencies [7324966]
148
+ - Updated dependencies [7ed750c]
149
+ - Updated dependencies [b5b9710]
150
+ - Updated dependencies [574841f]
151
+ - Updated dependencies [65eb77c]
152
+ - Updated dependencies [f106cf8]
153
+ - Updated dependencies [856f087]
154
+ - Updated dependencies [78f850f]
155
+ - Updated dependencies [1bd55ba]
156
+ - Updated dependencies [337a31c]
157
+ - @unotest/grounder-client@0.10.0
158
+ - @unotest/dsl@0.10.0
159
+ - @unotest/protocol@0.10.0
160
+ - @unotest/core@0.10.0
161
+ - @unotest/viewer@0.10.0
162
+
163
+ ## [0.9.2] - 2026-06-16
164
+
165
+ ### Added
166
+
167
+ - **Run snapshot export.** A finished run can be exported as a portable
168
+ `*.unotest.zip` bundle (events + captured artifacts) for a teammate to open in
169
+ their own viewer. The runner now records `console` / `network` capture flags in
170
+ the run manifest so a re-rendered snapshot shows "capture was off" rather than an
171
+ empty console, and `unotest/.snapshots/` is added to the generated `.gitignore`.
172
+
173
+ ### Fixed
174
+
175
+ - **Locator picker survives navigation and reaches into shadow DOM.** A navigation
176
+ that tore down the JS context mid-drain now surfaces as a typed
177
+ `ExecutionContextDestroyedError`, so the picker overlay reports not-alive and
178
+ self-heals (re-stamp refs + re-inject) instead of logging a dead end. Hit-testing
179
+ descends through open shadow roots and ref-lookup climbs back out across shadow
180
+ boundaries, keeping the picked element and its stamped ref symmetric with the
181
+ snapshot capture.
182
+
183
+ ## [0.9.1] - 2026-06-14
184
+
185
+ ### Added
186
+
187
+ - **`check_locator` MCP tool + runtime locator probe.** Evaluate a DSL locator
188
+ string against the live page and see what it actually matches — count, the N a
189
+ trailing `.first()`/`.last()`/`.nth()` silently collapsed, and where each match
190
+ points — instead of guessing or re-running the whole test. Same parser/core
191
+ (`probe-locator`) the runner uses, so a result here equals the runtime locator.
192
+ - **Interactive debug + auto-attach.** `run_test --debug` auto-attaches the MCP
193
+ session to the paused run's shared browser, so `get_page_snapshot` /
194
+ `find_element` / `check_locator` hit the page being debugged. Collaborative
195
+ `probe` command (the human's `check_locator` twin) is handled by the runner.
196
+ - **Agent tool calls in the viewer's System console.** A new `StepsToolEventSink`
197
+ publishes each MCP tool call + result (secret-redacted, capped) into the
198
+ attached run's `steps.jsonl`, so the human sees what the agent is doing.
199
+ - **Run breakpoints published to `runtime.json`** (`.debugger.json` seed +
200
+ `--breakpoints` override), so the viewer paints gutter dots for non-persisted
201
+ breakpoints.
202
+
203
+ ### Changed
204
+
205
+ - **Shared runner machinery extracted to `@unotest/core`** (run-artifact /
206
+ run-manifest / runtime-state writers, debug-commands watcher, env reader) and
207
+ consumed via thin web bindings.
208
+
209
+ ### Fixed
210
+
211
+ - **`abort` now actually terminates a paused run.** A `{cmd:abort}` on a run
212
+ paused mid-statement could not unwind the suspended executor, leaving the
213
+ process + headed browser alive with a fresh heartbeat (the viewer showed a
214
+ ghost "active" session). Abort now force-terminates the runner the same clean
215
+ way Ctrl-C / SIGTERM does, emitting a terminal `run:finished` and closing the
216
+ browser.
217
+
8
218
  ## [0.9.0] - 2026-06-11
9
219
 
10
220
  ### Added
@@ -90,19 +90,19 @@ declare const FailureBundleConfigSchema: z.ZodObject<{
90
90
  type FailureBundleConfig = z.infer<typeof FailureBundleConfigSchema>;
91
91
  declare const DialogPolicySchema: z.ZodEnum<["accept", "dismiss", "manual"]>;
92
92
  type DialogPolicy = z.infer<typeof DialogPolicySchema>;
93
- declare const LinterRuleIdSchema: z.ZodEnum<["lint:deep-css", "lint:xpath", "lint:obfuscated-class", "lint:pause-explicit", "lint:disambig-by-index", "lint:evaluate-discouraged", "lint:scenario-in-root", "lint:mustache-in-dsl", "validator:unknown-function", "validator:dsl"]>;
93
+ declare const LinterRuleIdSchema: z.ZodEnum<["lint:deep-css", "lint:xpath", "lint:obfuscated-class", "lint:pause-explicit", "lint:disambig-by-index", "lint:evaluate-discouraged", "lint:scenario-in-root", "lint:mustache-in-dsl", "validator:unknown-function", "validator:arity", "validator:arg-kind", "validator:step-coverage", "validator:step-shape", "validator:meta-shape", "validator:function-shape", "validator:for-shape", "validator:if-shape", "validator:var-shape", "validator:unsupported-statement", "validator:unsupported-expression", "validator:string-concat", "validator:semantic-loss", "validator:list-arg", "validator:wrapped-format"]>;
94
94
  type LinterRuleId = z.infer<typeof LinterRuleIdSchema>;
95
95
  declare const LinterSeveritySchema: z.ZodEnum<["off", "warn", "error"]>;
96
96
  type LinterSeverity = z.infer<typeof LinterSeveritySchema>;
97
97
  declare const LinterConfigSchema: z.ZodObject<{
98
98
  enabled: z.ZodBoolean;
99
- rules: z.ZodRecord<z.ZodEnum<["lint:deep-css", "lint:xpath", "lint:obfuscated-class", "lint:pause-explicit", "lint:disambig-by-index", "lint:evaluate-discouraged", "lint:scenario-in-root", "lint:mustache-in-dsl", "validator:unknown-function", "validator:dsl"]>, z.ZodEnum<["off", "warn", "error"]>>;
99
+ rules: z.ZodRecord<z.ZodEnum<["lint:deep-css", "lint:xpath", "lint:obfuscated-class", "lint:pause-explicit", "lint:disambig-by-index", "lint:evaluate-discouraged", "lint:scenario-in-root", "lint:mustache-in-dsl", "validator:unknown-function", "validator:arity", "validator:arg-kind", "validator:step-coverage", "validator:step-shape", "validator:meta-shape", "validator:function-shape", "validator:for-shape", "validator:if-shape", "validator:var-shape", "validator:unsupported-statement", "validator:unsupported-expression", "validator:string-concat", "validator:semantic-loss", "validator:list-arg", "validator:wrapped-format"]>, z.ZodEnum<["off", "warn", "error"]>>;
100
100
  }, "strip", z.ZodTypeAny, {
101
101
  enabled: boolean;
102
- rules: Partial<Record<"lint:deep-css" | "lint:xpath" | "lint:obfuscated-class" | "lint:pause-explicit" | "lint:disambig-by-index" | "lint:evaluate-discouraged" | "lint:scenario-in-root" | "lint:mustache-in-dsl" | "validator:unknown-function" | "validator:dsl", "off" | "warn" | "error">>;
102
+ rules: Partial<Record<"lint:deep-css" | "lint:xpath" | "lint:obfuscated-class" | "lint:pause-explicit" | "lint:disambig-by-index" | "lint:evaluate-discouraged" | "lint:scenario-in-root" | "lint:mustache-in-dsl" | "validator:unknown-function" | "validator:arity" | "validator:arg-kind" | "validator:step-coverage" | "validator:step-shape" | "validator:meta-shape" | "validator:function-shape" | "validator:for-shape" | "validator:if-shape" | "validator:var-shape" | "validator:unsupported-statement" | "validator:unsupported-expression" | "validator:string-concat" | "validator:semantic-loss" | "validator:list-arg" | "validator:wrapped-format", "off" | "warn" | "error">>;
103
103
  }, {
104
104
  enabled: boolean;
105
- rules: Partial<Record<"lint:deep-css" | "lint:xpath" | "lint:obfuscated-class" | "lint:pause-explicit" | "lint:disambig-by-index" | "lint:evaluate-discouraged" | "lint:scenario-in-root" | "lint:mustache-in-dsl" | "validator:unknown-function" | "validator:dsl", "off" | "warn" | "error">>;
105
+ rules: Partial<Record<"lint:deep-css" | "lint:xpath" | "lint:obfuscated-class" | "lint:pause-explicit" | "lint:disambig-by-index" | "lint:evaluate-discouraged" | "lint:scenario-in-root" | "lint:mustache-in-dsl" | "validator:unknown-function" | "validator:arity" | "validator:arg-kind" | "validator:step-coverage" | "validator:step-shape" | "validator:meta-shape" | "validator:function-shape" | "validator:for-shape" | "validator:if-shape" | "validator:var-shape" | "validator:unsupported-statement" | "validator:unsupported-expression" | "validator:string-concat" | "validator:semantic-loss" | "validator:list-arg" | "validator:wrapped-format", "off" | "warn" | "error">>;
106
106
  }>;
107
107
  type LinterConfig = z.infer<typeof LinterConfigSchema>;
108
108
  declare const McpConfigSchema: z.ZodObject<{
@@ -121,20 +121,86 @@ declare const SandboxConfigSchema: z.ZodObject<{
121
121
  database: z.ZodOptional<z.ZodString>;
122
122
  /** Base URL for `apiCall(method, path, body?, headers?)`. The path
123
123
  * passed to `apiCall` MUST be relative (starts with `/`); absolute
124
- * URLs are rejected at runtime. */
124
+ * URLs are rejected at runtime. Additional bases come from env
125
+ * variables via `apiCall(..., {base: "API_BASE_X"})`. */
125
126
  apiBaseUrl: z.ZodOptional<z.ZodString>;
127
+ /** Root for `apiCall` multipart `{file}` fixtures. Relative upload
128
+ * paths resolve against it and may not escape it. Defaults to the
129
+ * project root (the runner's cwd). */
130
+ uploadDir: z.ZodOptional<z.ZodString>;
126
131
  }, "strip", z.ZodTypeAny, {
127
132
  shellCwd?: string | undefined;
128
133
  database?: string | undefined;
129
134
  apiBaseUrl?: string | undefined;
135
+ uploadDir?: string | undefined;
130
136
  }, {
131
137
  shellCwd?: string | undefined;
132
138
  database?: string | undefined;
133
139
  apiBaseUrl?: string | undefined;
140
+ uploadDir?: string | undefined;
134
141
  }>;
135
142
  type SandboxConfig = z.infer<typeof SandboxConfigSchema>;
143
+ declare const WebServerConfigSchema: z.ZodObject<{
144
+ /** Shell command that starts the app under test (`npm run dev`,
145
+ * `python manage.py runserver`, …). Runs in the user's shell from the
146
+ * project root. */
147
+ command: z.ZodString;
148
+ /** URL probed for readiness — any HTTP response counts as up. Usually
149
+ * equals `baseUrl`; declared explicitly because a dev server may
150
+ * answer on a different path/port than the tests navigate to. */
151
+ url: z.ZodString;
152
+ /** When the URL is already up, reuse that server instead of spawning a
153
+ * second one (default true — covers a dev server the user or the
154
+ * viewer already started). `false` makes an occupied URL an error,
155
+ * Playwright-style for CI strictness. */
156
+ reuseExistingServer: z.ZodDefault<z.ZodBoolean>;
157
+ /** How long to wait for the URL to come up after spawning. */
158
+ timeoutMs: z.ZodDefault<z.ZodNumber>;
159
+ }, "strip", z.ZodTypeAny, {
160
+ url: string;
161
+ command: string;
162
+ reuseExistingServer: boolean;
163
+ timeoutMs: number;
164
+ }, {
165
+ url: string;
166
+ command: string;
167
+ reuseExistingServer?: boolean | undefined;
168
+ timeoutMs?: number | undefined;
169
+ }>;
170
+ type WebServerConfig = z.infer<typeof WebServerConfigSchema>;
136
171
  declare const UnotestConfigSchema: z.ZodObject<{
137
172
  baseUrl: z.ZodOptional<z.ZodString>;
173
+ /** Dev-server lifecycle for the app under test (M-12). Unset → the app
174
+ * is expected to be running already (current behaviour). When set, the
175
+ * CLI ensures it per run; `npx @unotest/web app-server` / the viewer
176
+ * hold a long-lived instance and runs then reuse it. */
177
+ webServer: z.ZodOptional<z.ZodObject<{
178
+ /** Shell command that starts the app under test (`npm run dev`,
179
+ * `python manage.py runserver`, …). Runs in the user's shell from the
180
+ * project root. */
181
+ command: z.ZodString;
182
+ /** URL probed for readiness — any HTTP response counts as up. Usually
183
+ * equals `baseUrl`; declared explicitly because a dev server may
184
+ * answer on a different path/port than the tests navigate to. */
185
+ url: z.ZodString;
186
+ /** When the URL is already up, reuse that server instead of spawning a
187
+ * second one (default true — covers a dev server the user or the
188
+ * viewer already started). `false` makes an occupied URL an error,
189
+ * Playwright-style for CI strictness. */
190
+ reuseExistingServer: z.ZodDefault<z.ZodBoolean>;
191
+ /** How long to wait for the URL to come up after spawning. */
192
+ timeoutMs: z.ZodDefault<z.ZodNumber>;
193
+ }, "strip", z.ZodTypeAny, {
194
+ url: string;
195
+ command: string;
196
+ reuseExistingServer: boolean;
197
+ timeoutMs: number;
198
+ }, {
199
+ url: string;
200
+ command: string;
201
+ reuseExistingServer?: boolean | undefined;
202
+ timeoutMs?: number | undefined;
203
+ }>>;
138
204
  browsers: z.ZodArray<z.ZodEnum<["chromium", "firefox", "webkit"]>, "many">;
139
205
  channel: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"chrome">, z.ZodLiteral<"msedge">, z.ZodLiteral<"chrome-beta">, z.ZodNull]>>;
140
206
  viewport: z.ZodObject<{
@@ -222,19 +288,22 @@ declare const UnotestConfigSchema: z.ZodObject<{
222
288
  defaultTimeoutMs: z.ZodNumber;
223
289
  /** Default timeout for navigation (goto / reload / waitForUrl) AND
224
290
  * for click/doubleClick's post-action navigation wait. Real web
225
- * pages routinely take 5–10s to fire `load`; 15000ms is the sweet
226
- * spot between reliability and time-to-failure on a misclick.
291
+ * pages routinely take 5–10s to fire `load`, and heavy legacy sites
292
+ * (bricklink-class) blow through 15s on a cold load — two live-run
293
+ * failures 2026-07-16 were correct waits that just ran out of budget.
294
+ * 30000ms trades slower failure on a misclick for not flaking on
295
+ * slow-but-correct navigation.
227
296
  * Override via `UNOTEST_DEFAULT_NAVIGATION_TIMEOUT_MS`. */
228
297
  defaultNavigationTimeoutMs: z.ZodNumber;
229
298
  linter: z.ZodObject<{
230
299
  enabled: z.ZodBoolean;
231
- rules: z.ZodRecord<z.ZodEnum<["lint:deep-css", "lint:xpath", "lint:obfuscated-class", "lint:pause-explicit", "lint:disambig-by-index", "lint:evaluate-discouraged", "lint:scenario-in-root", "lint:mustache-in-dsl", "validator:unknown-function", "validator:dsl"]>, z.ZodEnum<["off", "warn", "error"]>>;
300
+ rules: z.ZodRecord<z.ZodEnum<["lint:deep-css", "lint:xpath", "lint:obfuscated-class", "lint:pause-explicit", "lint:disambig-by-index", "lint:evaluate-discouraged", "lint:scenario-in-root", "lint:mustache-in-dsl", "validator:unknown-function", "validator:arity", "validator:arg-kind", "validator:step-coverage", "validator:step-shape", "validator:meta-shape", "validator:function-shape", "validator:for-shape", "validator:if-shape", "validator:var-shape", "validator:unsupported-statement", "validator:unsupported-expression", "validator:string-concat", "validator:semantic-loss", "validator:list-arg", "validator:wrapped-format"]>, z.ZodEnum<["off", "warn", "error"]>>;
232
301
  }, "strip", z.ZodTypeAny, {
233
302
  enabled: boolean;
234
- rules: Partial<Record<"lint:deep-css" | "lint:xpath" | "lint:obfuscated-class" | "lint:pause-explicit" | "lint:disambig-by-index" | "lint:evaluate-discouraged" | "lint:scenario-in-root" | "lint:mustache-in-dsl" | "validator:unknown-function" | "validator:dsl", "off" | "warn" | "error">>;
303
+ rules: Partial<Record<"lint:deep-css" | "lint:xpath" | "lint:obfuscated-class" | "lint:pause-explicit" | "lint:disambig-by-index" | "lint:evaluate-discouraged" | "lint:scenario-in-root" | "lint:mustache-in-dsl" | "validator:unknown-function" | "validator:arity" | "validator:arg-kind" | "validator:step-coverage" | "validator:step-shape" | "validator:meta-shape" | "validator:function-shape" | "validator:for-shape" | "validator:if-shape" | "validator:var-shape" | "validator:unsupported-statement" | "validator:unsupported-expression" | "validator:string-concat" | "validator:semantic-loss" | "validator:list-arg" | "validator:wrapped-format", "off" | "warn" | "error">>;
235
304
  }, {
236
305
  enabled: boolean;
237
- rules: Partial<Record<"lint:deep-css" | "lint:xpath" | "lint:obfuscated-class" | "lint:pause-explicit" | "lint:disambig-by-index" | "lint:evaluate-discouraged" | "lint:scenario-in-root" | "lint:mustache-in-dsl" | "validator:unknown-function" | "validator:dsl", "off" | "warn" | "error">>;
306
+ rules: Partial<Record<"lint:deep-css" | "lint:xpath" | "lint:obfuscated-class" | "lint:pause-explicit" | "lint:disambig-by-index" | "lint:evaluate-discouraged" | "lint:scenario-in-root" | "lint:mustache-in-dsl" | "validator:unknown-function" | "validator:arity" | "validator:arg-kind" | "validator:step-coverage" | "validator:step-shape" | "validator:meta-shape" | "validator:function-shape" | "validator:for-shape" | "validator:if-shape" | "validator:var-shape" | "validator:unsupported-statement" | "validator:unsupported-expression" | "validator:string-concat" | "validator:semantic-loss" | "validator:list-arg" | "validator:wrapped-format", "off" | "warn" | "error">>;
238
307
  }>;
239
308
  mcp: z.ZodObject<{
240
309
  transport: z.ZodLiteral<"stdio">;
@@ -251,16 +320,23 @@ declare const UnotestConfigSchema: z.ZodObject<{
251
320
  database: z.ZodOptional<z.ZodString>;
252
321
  /** Base URL for `apiCall(method, path, body?, headers?)`. The path
253
322
  * passed to `apiCall` MUST be relative (starts with `/`); absolute
254
- * URLs are rejected at runtime. */
323
+ * URLs are rejected at runtime. Additional bases come from env
324
+ * variables via `apiCall(..., {base: "API_BASE_X"})`. */
255
325
  apiBaseUrl: z.ZodOptional<z.ZodString>;
326
+ /** Root for `apiCall` multipart `{file}` fixtures. Relative upload
327
+ * paths resolve against it and may not escape it. Defaults to the
328
+ * project root (the runner's cwd). */
329
+ uploadDir: z.ZodOptional<z.ZodString>;
256
330
  }, "strip", z.ZodTypeAny, {
257
331
  shellCwd?: string | undefined;
258
332
  database?: string | undefined;
259
333
  apiBaseUrl?: string | undefined;
334
+ uploadDir?: string | undefined;
260
335
  }, {
261
336
  shellCwd?: string | undefined;
262
337
  database?: string | undefined;
263
338
  apiBaseUrl?: string | undefined;
339
+ uploadDir?: string | undefined;
264
340
  }>;
265
341
  }, "strip", z.ZodTypeAny, {
266
342
  browsers: ("chromium" | "firefox" | "webkit")[];
@@ -292,7 +368,7 @@ declare const UnotestConfigSchema: z.ZodObject<{
292
368
  defaultNavigationTimeoutMs: number;
293
369
  linter: {
294
370
  enabled: boolean;
295
- rules: Partial<Record<"lint:deep-css" | "lint:xpath" | "lint:obfuscated-class" | "lint:pause-explicit" | "lint:disambig-by-index" | "lint:evaluate-discouraged" | "lint:scenario-in-root" | "lint:mustache-in-dsl" | "validator:unknown-function" | "validator:dsl", "off" | "warn" | "error">>;
371
+ rules: Partial<Record<"lint:deep-css" | "lint:xpath" | "lint:obfuscated-class" | "lint:pause-explicit" | "lint:disambig-by-index" | "lint:evaluate-discouraged" | "lint:scenario-in-root" | "lint:mustache-in-dsl" | "validator:unknown-function" | "validator:arity" | "validator:arg-kind" | "validator:step-coverage" | "validator:step-shape" | "validator:meta-shape" | "validator:function-shape" | "validator:for-shape" | "validator:if-shape" | "validator:var-shape" | "validator:unsupported-statement" | "validator:unsupported-expression" | "validator:string-concat" | "validator:semantic-loss" | "validator:list-arg" | "validator:wrapped-format", "off" | "warn" | "error">>;
296
372
  };
297
373
  mcp: {
298
374
  transport: "stdio";
@@ -301,8 +377,15 @@ declare const UnotestConfigSchema: z.ZodObject<{
301
377
  shellCwd?: string | undefined;
302
378
  database?: string | undefined;
303
379
  apiBaseUrl?: string | undefined;
380
+ uploadDir?: string | undefined;
304
381
  };
305
382
  baseUrl?: string | undefined;
383
+ webServer?: {
384
+ url: string;
385
+ command: string;
386
+ reuseExistingServer: boolean;
387
+ timeoutMs: number;
388
+ } | undefined;
306
389
  channel?: "chrome" | "msedge" | "chrome-beta" | null | undefined;
307
390
  storageState?: string | undefined;
308
391
  }, {
@@ -335,7 +418,7 @@ declare const UnotestConfigSchema: z.ZodObject<{
335
418
  defaultNavigationTimeoutMs: number;
336
419
  linter: {
337
420
  enabled: boolean;
338
- rules: Partial<Record<"lint:deep-css" | "lint:xpath" | "lint:obfuscated-class" | "lint:pause-explicit" | "lint:disambig-by-index" | "lint:evaluate-discouraged" | "lint:scenario-in-root" | "lint:mustache-in-dsl" | "validator:unknown-function" | "validator:dsl", "off" | "warn" | "error">>;
421
+ rules: Partial<Record<"lint:deep-css" | "lint:xpath" | "lint:obfuscated-class" | "lint:pause-explicit" | "lint:disambig-by-index" | "lint:evaluate-discouraged" | "lint:scenario-in-root" | "lint:mustache-in-dsl" | "validator:unknown-function" | "validator:arity" | "validator:arg-kind" | "validator:step-coverage" | "validator:step-shape" | "validator:meta-shape" | "validator:function-shape" | "validator:for-shape" | "validator:if-shape" | "validator:var-shape" | "validator:unsupported-statement" | "validator:unsupported-expression" | "validator:string-concat" | "validator:semantic-loss" | "validator:list-arg" | "validator:wrapped-format", "off" | "warn" | "error">>;
339
422
  };
340
423
  mcp: {
341
424
  transport: "stdio";
@@ -344,8 +427,15 @@ declare const UnotestConfigSchema: z.ZodObject<{
344
427
  shellCwd?: string | undefined;
345
428
  database?: string | undefined;
346
429
  apiBaseUrl?: string | undefined;
430
+ uploadDir?: string | undefined;
347
431
  };
348
432
  baseUrl?: string | undefined;
433
+ webServer?: {
434
+ url: string;
435
+ command: string;
436
+ reuseExistingServer?: boolean | undefined;
437
+ timeoutMs?: number | undefined;
438
+ } | undefined;
349
439
  channel?: "chrome" | "msedge" | "chrome-beta" | null | undefined;
350
440
  storageState?: string | undefined;
351
441
  }>;
@@ -353,4 +443,4 @@ type UnotestConfig = z.infer<typeof UnotestConfigSchema>;
353
443
  type UnotestConfigInput = z.input<typeof UnotestConfigSchema>;
354
444
  declare const DEFAULT_CONFIG: UnotestConfig;
355
445
 
356
- export { type Browser, type BrowserChannel, BrowserChannelSchema, BrowserSchema, DEFAULT_CONFIG, type DialogPolicy, DialogPolicySchema, type FailureBundleConfig, FailureBundleConfigSchema, type LinterConfig, LinterConfigSchema, type LinterRuleId, LinterRuleIdSchema, type LinterSeverity, LinterSeveritySchema, type McpConfig, McpConfigSchema, type RetryConfig, RetryConfigSchema, type RetryReason, RetryReasonSchema, type SandboxConfig, SandboxConfigSchema, type UnotestConfig, type UnotestConfigInput, UnotestConfigSchema, type Viewport, ViewportSchema };
446
+ export { type Browser, type BrowserChannel, BrowserChannelSchema, BrowserSchema, DEFAULT_CONFIG, type DialogPolicy, DialogPolicySchema, type FailureBundleConfig, FailureBundleConfigSchema, type LinterConfig, LinterConfigSchema, type LinterRuleId, LinterRuleIdSchema, type LinterSeverity, LinterSeveritySchema, type McpConfig, McpConfigSchema, type RetryConfig, RetryConfigSchema, type RetryReason, RetryReasonSchema, type SandboxConfig, SandboxConfigSchema, type UnotestConfig, type UnotestConfigInput, UnotestConfigSchema, type Viewport, ViewportSchema, type WebServerConfig, WebServerConfigSchema };
@@ -1 +1 @@
1
- var _0x2f4e9c=_0x5088;function _0x5088(_0x35ba0f,_0x4f1556){_0x35ba0f=_0x35ba0f-0x16f;var _0x5f0c45=_0x5f0c();var _0x5088d1=_0x5f0c45[_0x35ba0f];if(_0x5088['NVKkTr']===undefined){var _0x16a1af=function(_0x451af3){var _0x5b4805='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';var _0x5b9d04='',_0x2f88a3='';for(var _0x2a8880=0x0,_0x47feb8,_0x17d054,_0xf51d57=0x0;_0x17d054=_0x451af3['charAt'](_0xf51d57++);~_0x17d054&&(_0x47feb8=_0x2a8880%0x4?_0x47feb8*0x40+_0x17d054:_0x17d054,_0x2a8880++%0x4)?_0x5b9d04+=String['fromCharCode'](0xff&_0x47feb8>>(-0x2*_0x2a8880&0x6)):0x0){_0x17d054=_0x5b4805['indexOf'](_0x17d054);}for(var _0x541710=0x0,_0x483552=_0x5b9d04['length'];_0x541710<_0x483552;_0x541710++){_0x2f88a3+='%'+('00'+_0x5b9d04['charCodeAt'](_0x541710)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x2f88a3);};_0x5088['FAqgnd']=_0x16a1af,_0x5088['wLKUZH']={},_0x5088['NVKkTr']=!![];}var _0x3990bf=_0x5f0c45[0x0],_0xd91f61=_0x35ba0f+_0x3990bf,_0x242047=_0x5088['wLKUZH'][_0xd91f61];return!_0x242047?(_0x5088d1=_0x5088['FAqgnd'](_0x5088d1),_0x5088['wLKUZH'][_0xd91f61]=_0x5088d1):_0x5088d1=_0x242047,_0x5088d1;}(function(_0x3cce67,_0x192c32){var _0x1e21ad={_0x2d54de:0x19d,_0x7f5c28:0x18f,_0x24a64e:0x191,_0x3b1c32:0x189,_0x1cd0d0:0x170,_0x3f11e4:0x17d,_0x1737a5:0x198},_0x44a598=_0x5088,_0x4ef498=_0x3cce67();while(!![]){try{var _0x21ddfd=parseInt(_0x44a598(_0x1e21ad._0x2d54de))/0x1*(-parseInt(_0x44a598(0x18b))/0x2)+parseInt(_0x44a598(_0x1e21ad._0x7f5c28))/0x3*(-parseInt(_0x44a598(_0x1e21ad._0x24a64e))/0x4)+parseInt(_0x44a598(_0x1e21ad._0x3b1c32))/0x5+-parseInt(_0x44a598(0x184))/0x6*(parseInt(_0x44a598(0x180))/0x7)+-parseInt(_0x44a598(_0x1e21ad._0x1cd0d0))/0x8+parseInt(_0x44a598(0x188))/0x9+-parseInt(_0x44a598(_0x1e21ad._0x3f11e4))/0xa*(-parseInt(_0x44a598(_0x1e21ad._0x1737a5))/0xb);if(_0x21ddfd===_0x192c32)break;else _0x4ef498['push'](_0x4ef498['shift']());}catch(_0x4efa28){_0x4ef498['push'](_0x4ef498['shift']());}}}(_0x5f0c,0xeb7d9));import{z}from'zod';function _0x5f0c(){var _0x49077b=['BgLUDdPZy2vUyxjPBY1PBI1YB290','mta0nZC0mdbxCfHszhO','BNvTyMvY','BNvSBa','C3rYAw5N','Cg9ZAxrPDMu','zw51Bq','yxjYyxK','B2jQzwn0','BgLUDdP4Cgf0Aa','zxjYB3i','lNvUB3rLC3qVzMfPBhvYzxm','ywnJzxb0','zMLYzwzVEa','mtmZmtCWvMH4rfDe','BxnLzgDL','CMvJB3jK','mteYnJK5m0rszvHAuW','Aw50','zgLZBwLZCW','DxjS','mZztA3rfyNC','D2vIA2L0','DMfSAwrHDg9YoMrZBa','BgLUDdPKzwvWlwnZCW','mtmYntu2mtrrv2zcEwW','ndKXnZmZmfDKy296yq','Bwf4','nde3nfjdzuDMBa','y2HYB21L','BwfUDwfS','zgvMyxvSDa','nMjNv2TREa','y2HYB21PDw0','nde3mtC2DenLrgPw','y3jHC2G','Dw5PB24','BgLUDdPLDMfSDwf0zs1KAxnJB3vYywDLza','DMfSAwrHDg9YoNvUA25VD24TzNvUy3rPB24','BgLUDdPKAxnHBwjPzY1IEs1PBMrLEa','BgLUDdPTDxn0ywnOzs1PBI1KC2W','odi1tLndwK1n','B2zM','D2fYBG','DhjHBNnPzw50','BMv0D29YAW','m1nYDxnfAa','C3rKAw8','BgL0zxjHBa','BwLU','B3b0Aw9UywW','yM9VBgvHBG','BgLUDdPVyMz1C2nHDgvKlwnSyxnZ','Dw5VDgvZDc9LmMuVx2HLBhbLCNm'];_0x5f0c=function(){return _0x49077b;};return _0x5f0c();}var BrowserSchema=z[_0x2f4e9c(0x175)]([_0x2f4e9c(0x190),_0x2f4e9c(0x17c),_0x2f4e9c(0x185)]),BrowserChannelSchema=z[_0x2f4e9c(0x193)]([z[_0x2f4e9c(0x19f)](_0x2f4e9c(0x18c)),z[_0x2f4e9c(0x19f)](_0x2f4e9c(0x17e)),z[_0x2f4e9c(0x19f)]('chrome-beta'),z[_0x2f4e9c(0x172)]()])[_0x2f4e9c(0x1a1)](),ViewportSchema=z[_0x2f4e9c(0x177)]({'width':z[_0x2f4e9c(0x171)]()[_0x2f4e9c(0x181)]()[_0x2f4e9c(0x174)](),'height':z[_0x2f4e9c(0x171)]()[_0x2f4e9c(0x181)]()[_0x2f4e9c(0x174)]()}),RetryReasonSchema=z[_0x2f4e9c(0x175)]([_0x2f4e9c(0x19b),_0x2f4e9c(0x19c),_0x2f4e9c(0x192)]),RetryConfigSchema=z[_0x2f4e9c(0x177)]({'count':z[_0x2f4e9c(0x171)]()[_0x2f4e9c(0x181)]()['min'](0x0)[_0x2f4e9c(0x18a)](0xa),'on':z[_0x2f4e9c(0x176)](RetryReasonSchema)}),FailureBundleConfigSchema=z['object']({'tier1':z[_0x2f4e9c(0x19f)](!![])[_0x2f4e9c(0x18e)](!![]),'tier2':z['boolean'](),'tier3':z[_0x2f4e9c(0x177)]({'network':z[_0x2f4e9c(0x1a2)](),'video':z['boolean']()}),'retention':z[_0x2f4e9c(0x177)]({'runs':z['number']()[_0x2f4e9c(0x181)]()[_0x2f4e9c(0x174)](),'days':z[_0x2f4e9c(0x171)]()[_0x2f4e9c(0x181)]()['positive']()}),'storageDir':z[_0x2f4e9c(0x173)]()[_0x2f4e9c(0x1a0)](0x1)}),DialogPolicySchema=z['enum']([_0x2f4e9c(0x17b),_0x2f4e9c(0x182),_0x2f4e9c(0x18d)]),LinterRuleIdSchema=z['enum']([_0x2f4e9c(0x187),_0x2f4e9c(0x178),_0x2f4e9c(0x1a3),'lint:pause-explicit',_0x2f4e9c(0x196),_0x2f4e9c(0x194),_0x2f4e9c(0x16f),_0x2f4e9c(0x197),_0x2f4e9c(0x195),_0x2f4e9c(0x186)]),LinterSeveritySchema=z[_0x2f4e9c(0x175)](['off',_0x2f4e9c(0x19a),_0x2f4e9c(0x179)]),LinterConfigSchema=z[_0x2f4e9c(0x177)]({'enabled':z[_0x2f4e9c(0x1a2)](),'rules':z[_0x2f4e9c(0x17f)](LinterRuleIdSchema,LinterSeveritySchema)}),McpConfigSchema=z[_0x2f4e9c(0x177)]({'transport':z[_0x2f4e9c(0x19f)](_0x2f4e9c(0x19e))}),SandboxConfigSchema=z[_0x2f4e9c(0x177)]({'shellCwd':z[_0x2f4e9c(0x173)]()['optional'](),'database':z[_0x2f4e9c(0x173)]()[_0x2f4e9c(0x1a1)](),'apiBaseUrl':z[_0x2f4e9c(0x173)]()[_0x2f4e9c(0x183)]()[_0x2f4e9c(0x1a1)]()}),UnotestConfigSchema=z[_0x2f4e9c(0x177)]({'baseUrl':z[_0x2f4e9c(0x173)]()[_0x2f4e9c(0x183)]()[_0x2f4e9c(0x1a1)](),'browsers':z['array'](BrowserSchema)[_0x2f4e9c(0x1a0)](0x1),'channel':BrowserChannelSchema,'viewport':ViewportSchema,'retry':RetryConfigSchema,'failureBundle':FailureBundleConfigSchema,'dialogPolicy':DialogPolicySchema,'storageState':z['string']()[_0x2f4e9c(0x1a1)](),'testDir':z['string']()['min'](0x1),'helpersDir':z[_0x2f4e9c(0x173)]()[_0x2f4e9c(0x1a0)](0x1),'defaultTimeoutMs':z[_0x2f4e9c(0x171)]()[_0x2f4e9c(0x181)]()[_0x2f4e9c(0x174)](),'defaultNavigationTimeoutMs':z[_0x2f4e9c(0x171)]()[_0x2f4e9c(0x181)]()[_0x2f4e9c(0x174)](),'linter':LinterConfigSchema,'mcp':McpConfigSchema,'sandbox':SandboxConfigSchema}),DEFAULT_CONFIG={'browsers':['chromium'],'viewport':{'width':0x500,'height':0x2d0},'retry':{'count':0x0,'on':[_0x2f4e9c(0x19b)]},'failureBundle':{'tier1':!![],'tier2':!![],'tier3':{'network':![],'video':![]},'retention':{'runs':0x14,'days':0x7},'storageDir':_0x2f4e9c(0x17a)},'dialogPolicy':_0x2f4e9c(0x17b),'testDir':'unotest/e2e','helpersDir':_0x2f4e9c(0x1a4),'defaultTimeoutMs':0xbb8,'defaultNavigationTimeoutMs':0x3a98,'linter':{'enabled':!![],'rules':{'lint:deep-css':_0x2f4e9c(0x19a),'lint:xpath':'warn','lint:obfuscated-class':'warn','lint:pause-explicit':_0x2f4e9c(0x19a),'lint:disambig-by-index':_0x2f4e9c(0x199),'lint:evaluate-discouraged':_0x2f4e9c(0x19a),'lint:scenario-in-root':_0x2f4e9c(0x179),'lint:mustache-in-dsl':_0x2f4e9c(0x179),'validator:unknown-function':_0x2f4e9c(0x179),'validator:dsl':_0x2f4e9c(0x179)}},'mcp':{'transport':'stdio'},'sandbox':{}};export{BrowserChannelSchema,BrowserSchema,DEFAULT_CONFIG,DialogPolicySchema,FailureBundleConfigSchema,LinterConfigSchema,LinterRuleIdSchema,LinterSeveritySchema,McpConfigSchema,RetryConfigSchema,RetryReasonSchema,SandboxConfigSchema,UnotestConfigSchema,ViewportSchema};
1
+ var _0x2e344b=_0x8977;(function(_0x274a7d,_0xfc8b9d){var _0x244ac2={_0x4ac5bb:0xdd,_0x4ff88e:0xe4,_0x286156:0xe0,_0x4f3327:0xe8,_0x58371b:0xe1},_0x41182f=_0x8977,_0x5b93a8=_0x274a7d();while(!![]){try{var _0x1d56a7=-parseInt(_0x41182f(0xdf))/0x1*(-parseInt(_0x41182f(_0x244ac2._0x4ac5bb))/0x2)+-parseInt(_0x41182f(_0x244ac2._0x4ff88e))/0x3*(-parseInt(_0x41182f(0xc7))/0x4)+parseInt(_0x41182f(0xea))/0x5+parseInt(_0x41182f(_0x244ac2._0x286156))/0x6+-parseInt(_0x41182f(0xcc))/0x7+parseInt(_0x41182f(_0x244ac2._0x4f3327))/0x8*(parseInt(_0x41182f(0xfc))/0x9)+-parseInt(_0x41182f(_0x244ac2._0x58371b))/0xa;if(_0x1d56a7===_0xfc8b9d)break;else _0x5b93a8['push'](_0x5b93a8['shift']());}catch(_0x35c23d){_0x5b93a8['push'](_0x5b93a8['shift']());}}}(_0x19a6,0x37d44));import{z}from'zod';var BrowserSchema=z[_0x2e344b(0xfa)]([_0x2e344b(0xe3),_0x2e344b(0xcf),_0x2e344b(0xf9)]),BrowserChannelSchema=z[_0x2e344b(0xf3)]([z['literal'](_0x2e344b(0xca)),z[_0x2e344b(0xc6)](_0x2e344b(0xc9)),z[_0x2e344b(0xc6)](_0x2e344b(0xf5)),z[_0x2e344b(0xe9)]()])[_0x2e344b(0xec)](),ViewportSchema=z[_0x2e344b(0xd7)]({'width':z['number']()[_0x2e344b(0xee)]()[_0x2e344b(0xcb)](),'height':z[_0x2e344b(0xd1)]()[_0x2e344b(0xee)]()[_0x2e344b(0xcb)]()}),RetryReasonSchema=z[_0x2e344b(0xfa)](['transient',_0x2e344b(0xd6),_0x2e344b(0xef)]),RetryConfigSchema=z[_0x2e344b(0xd7)]({'count':z[_0x2e344b(0xd1)]()[_0x2e344b(0xee)]()[_0x2e344b(0x103)](0x0)['max'](0xa),'on':z[_0x2e344b(0xed)](RetryReasonSchema)}),FailureBundleConfigSchema=z[_0x2e344b(0xd7)]({'tier1':z[_0x2e344b(0xc6)](!![])[_0x2e344b(0xc5)](!![]),'tier2':z[_0x2e344b(0xdc)](),'tier3':z[_0x2e344b(0xd7)]({'network':z[_0x2e344b(0xdc)](),'video':z[_0x2e344b(0xdc)]()}),'retention':z['object']({'runs':z[_0x2e344b(0xd1)]()['int']()[_0x2e344b(0xcb)](),'days':z['number']()[_0x2e344b(0xee)]()['positive']()}),'storageDir':z[_0x2e344b(0x104)]()[_0x2e344b(0x103)](0x1)}),DialogPolicySchema=z[_0x2e344b(0xfa)]([_0x2e344b(0xd0),_0x2e344b(0xf1),_0x2e344b(0xde)]),LinterRuleIdSchema=z[_0x2e344b(0xfa)]([_0x2e344b(0xd5),'lint:xpath',_0x2e344b(0xf6),_0x2e344b(0xff),_0x2e344b(0xeb),_0x2e344b(0xfd),_0x2e344b(0xfb),_0x2e344b(0xe5),_0x2e344b(0xd4),_0x2e344b(0xf2),_0x2e344b(0xd2),_0x2e344b(0xe2),_0x2e344b(0xd3),'validator:meta-shape',_0x2e344b(0xc8),_0x2e344b(0xf0),_0x2e344b(0x100),_0x2e344b(0x101),_0x2e344b(0x102),_0x2e344b(0xcd),'validator:string-concat',_0x2e344b(0xc4),_0x2e344b(0xf7),'validator:wrapped-format']),LinterSeveritySchema=z[_0x2e344b(0xfa)]([_0x2e344b(0xe6),'warn','error']),LinterConfigSchema=z[_0x2e344b(0xd7)]({'enabled':z[_0x2e344b(0xdc)](),'rules':z[_0x2e344b(0xf4)](LinterRuleIdSchema,LinterSeveritySchema)}),McpConfigSchema=z[_0x2e344b(0xd7)]({'transport':z[_0x2e344b(0xc6)](_0x2e344b(0xce))}),SandboxConfigSchema=z[_0x2e344b(0xd7)]({'shellCwd':z[_0x2e344b(0x104)]()[_0x2e344b(0xec)](),'database':z['string']()['optional'](),'apiBaseUrl':z[_0x2e344b(0x104)]()[_0x2e344b(0xda)]()[_0x2e344b(0xec)](),'uploadDir':z['string']()[_0x2e344b(0xec)]()}),WebServerConfigSchema=z[_0x2e344b(0xd7)]({'command':z[_0x2e344b(0x104)]()[_0x2e344b(0x103)](0x1),'url':z[_0x2e344b(0x104)]()['url'](),'reuseExistingServer':z[_0x2e344b(0xdc)]()[_0x2e344b(0xc5)](!![]),'timeoutMs':z[_0x2e344b(0xd1)]()[_0x2e344b(0xee)]()[_0x2e344b(0xcb)]()[_0x2e344b(0xc5)](0xea60)}),UnotestConfigSchema=z['object']({'baseUrl':z[_0x2e344b(0x104)]()['url']()[_0x2e344b(0xec)](),'webServer':WebServerConfigSchema[_0x2e344b(0xec)](),'browsers':z[_0x2e344b(0xed)](BrowserSchema)[_0x2e344b(0x103)](0x1),'channel':BrowserChannelSchema,'viewport':ViewportSchema,'retry':RetryConfigSchema,'failureBundle':FailureBundleConfigSchema,'dialogPolicy':DialogPolicySchema,'storageState':z[_0x2e344b(0x104)]()[_0x2e344b(0xec)](),'testDir':z[_0x2e344b(0x104)]()['min'](0x1),'helpersDir':z['string']()[_0x2e344b(0x103)](0x1),'defaultTimeoutMs':z[_0x2e344b(0xd1)]()[_0x2e344b(0xee)]()['positive'](),'defaultNavigationTimeoutMs':z['number']()[_0x2e344b(0xee)]()[_0x2e344b(0xcb)](),'linter':LinterConfigSchema,'mcp':McpConfigSchema,'sandbox':SandboxConfigSchema}),DEFAULT_CONFIG={'browsers':['chromium'],'viewport':{'width':0x500,'height':0x2d0},'retry':{'count':0x0,'on':[_0x2e344b(0xdb)]},'failureBundle':{'tier1':!![],'tier2':!![],'tier3':{'network':![],'video':![]},'retention':{'runs':0x14,'days':0x7},'storageDir':_0x2e344b(0xd9)},'dialogPolicy':'accept','testDir':_0x2e344b(0xe7),'helpersDir':_0x2e344b(0xf8),'defaultTimeoutMs':0xbb8,'defaultNavigationTimeoutMs':0x7530,'linter':{'enabled':!![],'rules':{'lint:deep-css':_0x2e344b(0xd8),'lint:xpath':'warn','lint:obfuscated-class':_0x2e344b(0xd8),'lint:pause-explicit':'warn','lint:disambig-by-index':_0x2e344b(0xe6),'lint:evaluate-discouraged':_0x2e344b(0xd8),'lint:scenario-in-root':_0x2e344b(0xfe),'lint:mustache-in-dsl':_0x2e344b(0xfe),'validator:unknown-function':_0x2e344b(0xfe)}},'mcp':{'transport':_0x2e344b(0xce)},'sandbox':{}};function _0x8977(_0x3a774f,_0x1b39aa){_0x3a774f=_0x3a774f-0xc4;var _0x19a653=_0x19a6();var _0x89776=_0x19a653[_0x3a774f];if(_0x8977['fRpWih']===undefined){var _0x38d7ef=function(_0x26bdbc){var _0x3caffa='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';var _0xda4aa0='',_0x563661='';for(var _0x305f13=0x0,_0x3d29c0,_0x481c1e,_0x19b0ec=0x0;_0x481c1e=_0x26bdbc['charAt'](_0x19b0ec++);~_0x481c1e&&(_0x3d29c0=_0x305f13%0x4?_0x3d29c0*0x40+_0x481c1e:_0x481c1e,_0x305f13++%0x4)?_0xda4aa0+=String['fromCharCode'](0xff&_0x3d29c0>>(-0x2*_0x305f13&0x6)):0x0){_0x481c1e=_0x3caffa['indexOf'](_0x481c1e);}for(var _0x4792b1=0x0,_0x49dbb6=_0xda4aa0['length'];_0x4792b1<_0x49dbb6;_0x4792b1++){_0x563661+='%'+('00'+_0xda4aa0['charCodeAt'](_0x4792b1)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x563661);};_0x8977['wyEdtR']=_0x38d7ef,_0x8977['cHKdQs']={},_0x8977['fRpWih']=!![];}var _0x1354e5=_0x19a653[0x0],_0x16a4bb=_0x3a774f+_0x1354e5,_0x9c7c53=_0x8977['cHKdQs'][_0x16a4bb];return!_0x9c7c53?(_0x89776=_0x8977['wyEdtR'](_0x89776),_0x8977['cHKdQs'][_0x16a4bb]=_0x89776):_0x89776=_0x9c7c53,_0x89776;}export{BrowserChannelSchema,BrowserSchema,DEFAULT_CONFIG,DialogPolicySchema,FailureBundleConfigSchema,LinterConfigSchema,LinterRuleIdSchema,LinterSeveritySchema,McpConfigSchema,RetryConfigSchema,RetryReasonSchema,SandboxConfigSchema,UnotestConfigSchema,ViewportSchema,WebServerConfigSchema};function _0x19a6(){var _0x1434f7=['DMfSAwrHDg9YoMXPC3qTyxjN','Dw5VDgvZDc9LmMuVx2HLBhbLCNm','D2vIA2L0','zw51Bq','BgLUDdPZy2vUyxjPBY1PBI1YB290','mtG3nduYCKHJsuHS','BgLUDdPLDMfSDwf0zs1KAxnJB3vYywDLza','zxjYB3i','BgLUDdPWyxvZzs1LEhbSAwnPDa','DMfSAwrHDg9YoMLMlxnOyxbL','DMfSAwrHDg9YoNzHCI1ZAgfWzq','DMfSAwrHDg9YoNvUC3vWCg9YDgvKlxn0yxrLBwvUDa','BwLU','C3rYAw5N','DMfSAwrHDg9YoNnLBwfUDgLJlwXVC3m','zgvMyxvSDa','BgL0zxjHBa','mJyYmdrzrev4q0K','DMfSAwrHDg9YoMz1BMn0Aw9UlxnOyxbL','BxnLzgDL','y2HYB21L','Cg9ZAxrPDMu','mtq3nZa0mLfMrgnerW','DMfSAwrHDg9YoNvUC3vWCg9YDgvKlwv4ChjLC3nPB24','C3rKAw8','zMLYzwzVEa','ywnJzxb0','BNvTyMvY','DMfSAwrHDg9YoMfYzY1RAw5K','DMfSAwrHDg9YoNn0zxaTC2HHCgu','DMfSAwrHDg9YoNvUA25VD24TzNvUy3rPB24','BgLUDdPKzwvWlwnZCW','BMv0D29YAW','B2jQzwn0','D2fYBG','lNvUB3rLC3qVzMfPBhvYzxm','DxjS','DhjHBNnPzw50','yM9VBgvHBG','mLPiC0XWDq','BwfUDwfS','mZy5ntm1tgPYrKfu','mtqWntu0offSrgvtBG','nti5mdiZmgLwwwnRDG','DMfSAwrHDg9YoNn0zxaTy292zxjHz2u','y2HYB21PDw0','m1LeDg1LzG','BgLUDdPTDxn0ywnOzs1PBI1KC2W','B2zM','Dw5VDgvZDc9LmMu','odHXD3bdzNq','BNvSBa','nJq2mJy1seL0CvP3','BgLUDdPKAxnHBwjPzY1IEs1PBMrLEa','B3b0Aw9UywW','yxjYyxK','Aw50','y3jHC2G','DMfSAwrHDg9YoMzVCI1ZAgfWzq','zgLZBwLZCW','DMfSAwrHDg9YoMfYAxr5','Dw5PB24','CMvJB3jK','y2HYB21LlwjLDge','BgLUDdPVyMz1C2nHDgvKlwnSyxnZ'];_0x19a6=function(){return _0x1434f7;};return _0x19a6();}
@@ -1,5 +1,5 @@
1
- import { E as ElementHint, N as NetworkEntry, W as WebDriver, L as LaunchOptions, C as ContextOptions, D as DriverContext } from '../interfaces-dbZG10RS.js';
2
- export { A as ActionTimeout, a as CheckOptions, b as ClickOptions, c as CookieOptions, d as DriverPage, F as FillOptions, G as GotoOptions, H as HoverOptions, e as NavigationOptions, P as PressOptions, R as ReloadOptions, S as ScreenshotOptions, f as SelectOptions, V as Viewport, g as WaitOptions, h as WaitState } from '../interfaces-dbZG10RS.js';
1
+ import { E as ElementHint, N as NetworkEntry, W as WebDriver, L as LaunchOptions, C as ContextOptions, D as DriverContext } from '../interfaces-BxviHxnr.js';
2
+ export { A as ActionTimeout, a as CheckOptions, b as ClickOptions, c as CookieOptions, d as DriverPage, F as FillOptions, G as GotoOptions, H as HoverOptions, e as NavigationOptions, P as PressOptions, R as ReloadOptions, S as ScreenshotOptions, f as SelectOptions, V as Viewport, g as WaitOptions, h as WaitState } from '../interfaces-BxviHxnr.js';
3
3
  import '@unotest/protocol';
4
4
  import '../config/schema.js';
5
5
  import 'zod';
@@ -27,6 +27,9 @@ interface FakeBehavior {
27
27
  inputValueByDefault?: string;
28
28
  /** Default isVisible() response. Default: true. */
29
29
  isVisibleByDefault?: boolean;
30
+ /** Default checkedState() response — null = "not a checkable element",
31
+ * the shape a plain <input type=text> reports. Default: null. */
32
+ checkedStateByDefault?: boolean | null;
30
33
  /** Default URL after newContext. Default: 'about:blank'. */
31
34
  initialUrl?: string;
32
35
  /** If set, every action throws this error on next call (then clears). */