@unotest/web 0.5.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/.claude/skills/write-e2e-test.md +551 -0
- package/CHANGELOG.md +158 -0
- package/LICENSE +21 -0
- package/README.md +146 -0
- package/bin/unotest-web.js +197 -0
- package/dist/config/schema.d.ts +356 -0
- package/dist/config/schema.js +1 -0
- package/dist/driver/index.d.ts +74 -0
- package/dist/driver/index.js +1 -0
- package/dist/dsl/index.d.ts +338 -0
- package/dist/dsl/index.js +1 -0
- package/dist/inspection/page-inject.d.ts +395 -0
- package/dist/inspection/page-inject.js +3533 -0
- package/dist/interfaces-iTfjd1zT.d.ts +407 -0
- package/dist/mcp/server.d.ts +2 -0
- package/dist/mcp/server.js +1 -0
- package/dist/runner/cli.d.ts +22 -0
- package/dist/runner/cli.js +1 -0
- package/dist/runner/init.d.ts +6 -0
- package/dist/runner/init.js +1 -0
- package/dist/runner/install-chromium.d.ts +9 -0
- package/dist/runner/install-chromium.js +1 -0
- package/dist/runner/prepare-fix.d.ts +8 -0
- package/dist/runner/prepare-fix.js +1 -0
- package/dist/runner/serve-fixture.d.ts +2 -0
- package/dist/runner/serve-fixture.js +1 -0
- package/dist/runner/web-runner-adapter.d.ts +8 -0
- package/dist/runner/web-runner-adapter.js +1 -0
- package/examples/fixtures/break-fix-app/README.md +45 -0
- package/examples/fixtures/break-fix-app/index.html +101 -0
- package/examples/fixtures/break-fix-app/serve.mjs +95 -0
- package/package.json +115 -0
- package/src/mcp/prompts/agent-test-author.md +208 -0
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
import { Matcher } from '@unotest/protocol';
|
|
2
|
+
import { Browser, BrowserChannel } from './config/schema.js';
|
|
3
|
+
|
|
4
|
+
type LocatorStep = {
|
|
5
|
+
kind: 'css';
|
|
6
|
+
selector: string;
|
|
7
|
+
} | {
|
|
8
|
+
kind: 'getByRole';
|
|
9
|
+
role: string;
|
|
10
|
+
name?: Matcher;
|
|
11
|
+
exact?: boolean;
|
|
12
|
+
} | {
|
|
13
|
+
kind: 'getByText';
|
|
14
|
+
text: Matcher;
|
|
15
|
+
exact?: boolean;
|
|
16
|
+
} | {
|
|
17
|
+
kind: 'getByLabel';
|
|
18
|
+
text: Matcher;
|
|
19
|
+
exact?: boolean;
|
|
20
|
+
} | {
|
|
21
|
+
kind: 'getByPlaceholder';
|
|
22
|
+
text: Matcher;
|
|
23
|
+
exact?: boolean;
|
|
24
|
+
} | {
|
|
25
|
+
kind: 'getByTestId';
|
|
26
|
+
id: string;
|
|
27
|
+
} | {
|
|
28
|
+
kind: 'getByAltText';
|
|
29
|
+
text: Matcher;
|
|
30
|
+
exact?: boolean;
|
|
31
|
+
} | {
|
|
32
|
+
kind: 'getByTitle';
|
|
33
|
+
text: Matcher;
|
|
34
|
+
exact?: boolean;
|
|
35
|
+
} | {
|
|
36
|
+
kind: 'filter';
|
|
37
|
+
hasText?: Matcher;
|
|
38
|
+
hasNotText?: Matcher;
|
|
39
|
+
has?: LocatorValue;
|
|
40
|
+
hasNot?: LocatorValue;
|
|
41
|
+
} | {
|
|
42
|
+
kind: 'first';
|
|
43
|
+
} | {
|
|
44
|
+
kind: 'last';
|
|
45
|
+
} | {
|
|
46
|
+
kind: 'nth';
|
|
47
|
+
index: number;
|
|
48
|
+
} | {
|
|
49
|
+
kind: 'contentFrame';
|
|
50
|
+
} | {
|
|
51
|
+
kind: 'ref';
|
|
52
|
+
ref: string;
|
|
53
|
+
};
|
|
54
|
+
interface LocatorValue {
|
|
55
|
+
readonly kind: 'locator';
|
|
56
|
+
readonly steps: readonly LocatorStep[];
|
|
57
|
+
}
|
|
58
|
+
declare function isLocatorValue(value: unknown): value is LocatorValue;
|
|
59
|
+
/** Internal: build a new LocatorValue by appending one step. Immutable. */
|
|
60
|
+
declare function appendStep(loc: LocatorValue, step: LocatorStep): LocatorValue;
|
|
61
|
+
/** Internal: construct a fresh LocatorValue rooted at one step. */
|
|
62
|
+
declare function rootedAt(step: LocatorStep): LocatorValue;
|
|
63
|
+
/** Debug-friendly string repr. NOT a selector — for logs / errors only. */
|
|
64
|
+
declare function describeLocator(loc: LocatorValue): string;
|
|
65
|
+
|
|
66
|
+
interface LaunchOptions {
|
|
67
|
+
browser: Browser;
|
|
68
|
+
/** Playwright channel — `'chrome'` / `'msedge'` to use a system-
|
|
69
|
+
* installed browser, `null`/undefined to fall back to bundled
|
|
70
|
+
* Chromium. Only meaningful when `browser === 'chromium'`. */
|
|
71
|
+
channel?: BrowserChannel;
|
|
72
|
+
/** Run with visible window. Default: false in CI, true in dev. */
|
|
73
|
+
headless?: boolean;
|
|
74
|
+
/** Slow each operation by N ms — for dev debugging. Default: 0. */
|
|
75
|
+
slowMo?: number;
|
|
76
|
+
}
|
|
77
|
+
interface Viewport {
|
|
78
|
+
width: number;
|
|
79
|
+
height: number;
|
|
80
|
+
}
|
|
81
|
+
interface ContextOptions {
|
|
82
|
+
baseUrl?: string;
|
|
83
|
+
viewport?: Viewport;
|
|
84
|
+
/** Path to storageState JSON (D-23 caching, optional). */
|
|
85
|
+
storageState?: string;
|
|
86
|
+
/** Per-context override for dialog policy. Defaults to config.dialogPolicy. */
|
|
87
|
+
dialogPolicy?: "accept" | "dismiss" | "manual";
|
|
88
|
+
/** User-Agent string override. */
|
|
89
|
+
userAgent?: string;
|
|
90
|
+
/** Locale, e.g. "en-US". */
|
|
91
|
+
locale?: string;
|
|
92
|
+
/** Timezone, e.g. "Europe/Moscow". */
|
|
93
|
+
timezoneId?: string;
|
|
94
|
+
/**
|
|
95
|
+
* Default action timeout in milliseconds — applied to every Playwright
|
|
96
|
+
* `click` / `fill` / `waitFor` / etc. via `context.setDefaultTimeout`.
|
|
97
|
+
* Single source of truth lives in `UnotestConfigSchema.defaultTimeoutMs`
|
|
98
|
+
* (default 3000ms); `UNOTEST_DEFAULT_TIMEOUT_MS` env overrides.
|
|
99
|
+
*/
|
|
100
|
+
defaultTimeoutMs?: number;
|
|
101
|
+
/**
|
|
102
|
+
* Default navigation timeout in milliseconds — applied to `goto` /
|
|
103
|
+
* `reload` / `waitForUrl` via `context.setDefaultNavigationTimeout`.
|
|
104
|
+
* Action timeout (3s) is too aggressive for real-world page loads;
|
|
105
|
+
* keep navigation at Playwright stock 30000ms by default.
|
|
106
|
+
* Single source of truth lives in `UnotestConfigSchema.defaultNavigationTimeoutMs`;
|
|
107
|
+
* `UNOTEST_DEFAULT_NAVIGATION_TIMEOUT_MS` env overrides.
|
|
108
|
+
*/
|
|
109
|
+
defaultNavigationTimeoutMs?: number;
|
|
110
|
+
}
|
|
111
|
+
interface ActionTimeout {
|
|
112
|
+
/** Override default action timeout (D-7 actionability wait). Milliseconds. */
|
|
113
|
+
timeout?: number;
|
|
114
|
+
}
|
|
115
|
+
interface ClickOptions extends ActionTimeout {
|
|
116
|
+
/** Bypass actionability checks (D-5 — replaces testoid5 `forceClick`). */
|
|
117
|
+
force?: boolean;
|
|
118
|
+
/** Click at specific offset within element. */
|
|
119
|
+
position?: {
|
|
120
|
+
x: number;
|
|
121
|
+
y: number;
|
|
122
|
+
};
|
|
123
|
+
/** Hold these modifiers during click. */
|
|
124
|
+
modifiers?: ReadonlyArray<"Alt" | "Control" | "Meta" | "Shift">;
|
|
125
|
+
/** Mouse button. Default: 'left'. */
|
|
126
|
+
button?: "left" | "right" | "middle";
|
|
127
|
+
/** Number of clicks. Default: 1. */
|
|
128
|
+
clickCount?: number;
|
|
129
|
+
}
|
|
130
|
+
interface FillOptions extends ActionTimeout {
|
|
131
|
+
force?: boolean;
|
|
132
|
+
}
|
|
133
|
+
interface PressOptions extends ActionTimeout {
|
|
134
|
+
/** Delay between keydown and keyup, in ms. */
|
|
135
|
+
delay?: number;
|
|
136
|
+
}
|
|
137
|
+
interface CheckOptions extends ActionTimeout {
|
|
138
|
+
force?: boolean;
|
|
139
|
+
}
|
|
140
|
+
interface SelectOptions extends ActionTimeout {
|
|
141
|
+
force?: boolean;
|
|
142
|
+
}
|
|
143
|
+
interface HoverOptions extends ActionTimeout {
|
|
144
|
+
force?: boolean;
|
|
145
|
+
position?: {
|
|
146
|
+
x: number;
|
|
147
|
+
y: number;
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* State to wait for. Default: 'visible'.
|
|
152
|
+
* - 'attached' — element exists in DOM
|
|
153
|
+
* - 'detached' — element removed from DOM
|
|
154
|
+
* - 'visible' — exists + visible (non-zero size, not hidden)
|
|
155
|
+
* - 'hidden' — exists with zero size OR not in DOM
|
|
156
|
+
*/
|
|
157
|
+
type WaitState = "attached" | "detached" | "visible" | "hidden";
|
|
158
|
+
interface WaitOptions extends ActionTimeout {
|
|
159
|
+
state?: WaitState;
|
|
160
|
+
}
|
|
161
|
+
interface NavigationOptions extends ActionTimeout {
|
|
162
|
+
/**
|
|
163
|
+
* When to consider navigation done. Default: 'load'.
|
|
164
|
+
* - 'load' — `load` event fired
|
|
165
|
+
* - 'domcontentloaded' — `DOMContentLoaded` fired
|
|
166
|
+
* - 'networkidle' — no network requests for 500ms
|
|
167
|
+
* - 'commit' — initial response received
|
|
168
|
+
*/
|
|
169
|
+
waitUntil?: "load" | "domcontentloaded" | "networkidle" | "commit";
|
|
170
|
+
}
|
|
171
|
+
type GotoOptions = NavigationOptions;
|
|
172
|
+
type ReloadOptions = NavigationOptions;
|
|
173
|
+
interface ScreenshotOptions {
|
|
174
|
+
/** Capture full page (scroll), not just viewport. Default: false. */
|
|
175
|
+
fullPage?: boolean;
|
|
176
|
+
/** Crop to a specific element. */
|
|
177
|
+
clip?: {
|
|
178
|
+
x: number;
|
|
179
|
+
y: number;
|
|
180
|
+
width: number;
|
|
181
|
+
height: number;
|
|
182
|
+
};
|
|
183
|
+
/** PNG quality (1-100). Only honoured for JPEG. */
|
|
184
|
+
quality?: number;
|
|
185
|
+
}
|
|
186
|
+
interface CookieOptions {
|
|
187
|
+
domain?: string;
|
|
188
|
+
path?: string;
|
|
189
|
+
expires?: number;
|
|
190
|
+
httpOnly?: boolean;
|
|
191
|
+
secure?: boolean;
|
|
192
|
+
sameSite?: "Strict" | "Lax" | "None";
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
interface WebDriver {
|
|
196
|
+
/**
|
|
197
|
+
* Launch the underlying browser process. Idempotent — subsequent calls
|
|
198
|
+
* resolve immediately if already launched. Must be called before
|
|
199
|
+
* newContext().
|
|
200
|
+
*/
|
|
201
|
+
launch(opts: LaunchOptions): Promise<void>;
|
|
202
|
+
/**
|
|
203
|
+
* Create a fresh context (D-23). Cookies / localStorage / sessionStorage
|
|
204
|
+
* / tabs / frames start clean. Per-test isolation lives at this layer —
|
|
205
|
+
* each test calls newContext() at start and ctx.close() at end.
|
|
206
|
+
*/
|
|
207
|
+
newContext(opts?: ContextOptions): Promise<DriverContext>;
|
|
208
|
+
/**
|
|
209
|
+
* Tear down all open contexts and the browser process. Idempotent.
|
|
210
|
+
* Errors logged but not re-thrown (must not mask test failures in
|
|
211
|
+
* `finally` blocks).
|
|
212
|
+
*/
|
|
213
|
+
close(): Promise<void>;
|
|
214
|
+
}
|
|
215
|
+
interface DriverContext {
|
|
216
|
+
/** Currently-open tabs/windows in this context. Index 0 = first opened. */
|
|
217
|
+
pages(): ReadonlyArray<DriverPage>;
|
|
218
|
+
/** The page that subsequent operations target. */
|
|
219
|
+
activePage(): DriverPage;
|
|
220
|
+
/** Switch the active page index (D-4 setPage). 0 = first opened, etc.
|
|
221
|
+
* Implementations MUST also reset the frame stack — a frame scope
|
|
222
|
+
* established on tab A is not valid on tab B. */
|
|
223
|
+
setActivePage(index: number): Promise<void>;
|
|
224
|
+
/**
|
|
225
|
+
* Subscribe to new-page events. Listener fires when a tab/window opens
|
|
226
|
+
* in this context (`target="_blank"`, `window.open`, etc.). Returns an
|
|
227
|
+
* unsubscribe function — call it to detach the listener. Cross-browser:
|
|
228
|
+
* Playwright abstracts the underlying CDP / Juggler / WebKit signal so
|
|
229
|
+
* this works in Chromium, Firefox, and WebKit identically.
|
|
230
|
+
*
|
|
231
|
+
* Used by the explore tool's popup-watcher; tests can fan out
|
|
232
|
+
* synchronously into a buffer.
|
|
233
|
+
*/
|
|
234
|
+
onNewPage(listener: (info: {
|
|
235
|
+
index: number;
|
|
236
|
+
url: string;
|
|
237
|
+
}) => void): () => void;
|
|
238
|
+
/**
|
|
239
|
+
* Enter a frame scope (D-4 stateful pair). All subsequent locators
|
|
240
|
+
* resolve inside this frame until `exitFrame()` is called. Stacks (calling
|
|
241
|
+
* enterFrame twice nests, exitFrame pops one level).
|
|
242
|
+
*/
|
|
243
|
+
enterFrame(locator: LocatorValue): Promise<void>;
|
|
244
|
+
exitFrame(): Promise<void>;
|
|
245
|
+
/** Current frame-stack depth — 0 when no `enterFrame` is active. Used
|
|
246
|
+
* by the popup-watcher to know how many `exit_frame` entries to emit
|
|
247
|
+
* before auto-switching to a popup (frame scope does not survive tab
|
|
248
|
+
* changes). */
|
|
249
|
+
frameDepth(): number;
|
|
250
|
+
/** Typed storage shortcuts (D-6). */
|
|
251
|
+
setLocalStorage(key: string, value: string): Promise<void>;
|
|
252
|
+
getLocalStorage(key: string): Promise<string | null>;
|
|
253
|
+
setCookie(name: string, value: string, opts?: CookieOptions): Promise<void>;
|
|
254
|
+
getCookie(name: string): Promise<string | null>;
|
|
255
|
+
/**
|
|
256
|
+
* Persist current storage state to disk (D-23 opt-in caching). Used by
|
|
257
|
+
* project helpers like loginAndCacheState().
|
|
258
|
+
*/
|
|
259
|
+
saveStorageState(path: string): Promise<void>;
|
|
260
|
+
/** Run JS in the page context (D-6 escape hatch). */
|
|
261
|
+
evaluate<T = unknown>(js: string): Promise<T>;
|
|
262
|
+
/**
|
|
263
|
+
* Snapshot of console entries captured since context creation, oldest
|
|
264
|
+
* first. Used by failure-bundle capture (D-16 tier 1). The driver wires
|
|
265
|
+
* a `page.on('console', …)` listener at newContext() time so the
|
|
266
|
+
* runner can pull the tail when a test fails.
|
|
267
|
+
*/
|
|
268
|
+
consoleEntries(): ReadonlyArray<{
|
|
269
|
+
level: "log" | "info" | "warn" | "error" | "debug";
|
|
270
|
+
text: string;
|
|
271
|
+
timestamp: string;
|
|
272
|
+
}>;
|
|
273
|
+
/** Close all pages in this context. Idempotent. */
|
|
274
|
+
close(): Promise<void>;
|
|
275
|
+
}
|
|
276
|
+
interface DriverPage {
|
|
277
|
+
/** Current URL. Synchronous — reads cached state. */
|
|
278
|
+
url(): string;
|
|
279
|
+
/** Current document title. */
|
|
280
|
+
title(): Promise<string>;
|
|
281
|
+
click(loc: LocatorValue, opts?: ClickOptions): Promise<void>;
|
|
282
|
+
doubleClick(loc: LocatorValue, opts?: ClickOptions): Promise<void>;
|
|
283
|
+
fill(loc: LocatorValue, value: string, opts?: FillOptions): Promise<void>;
|
|
284
|
+
press(loc: LocatorValue, key: string, opts?: PressOptions): Promise<void>;
|
|
285
|
+
check(loc: LocatorValue, opts?: CheckOptions): Promise<void>;
|
|
286
|
+
uncheck(loc: LocatorValue, opts?: CheckOptions): Promise<void>;
|
|
287
|
+
selectOption(loc: LocatorValue, value: string | ReadonlyArray<string>, opts?: SelectOptions): Promise<void>;
|
|
288
|
+
hover(loc: LocatorValue, opts?: HoverOptions): Promise<void>;
|
|
289
|
+
scrollIntoView(loc: LocatorValue): Promise<void>;
|
|
290
|
+
/** Drag the source element onto the target. */
|
|
291
|
+
dragAndDrop(from: LocatorValue, to: LocatorValue, opts?: ClickOptions): Promise<void>;
|
|
292
|
+
/** Set the input element's selected files. Accepts one or many paths. */
|
|
293
|
+
uploadFile(loc: LocatorValue, files: string | ReadonlyArray<string>): Promise<void>;
|
|
294
|
+
/** Focus the locator then dispatch a paste-like input with `text`. */
|
|
295
|
+
clipboardPaste(loc: LocatorValue, text: string): Promise<void>;
|
|
296
|
+
count(loc: LocatorValue): Promise<number>;
|
|
297
|
+
textContent(loc: LocatorValue): Promise<string | null>;
|
|
298
|
+
inputValue(loc: LocatorValue): Promise<string>;
|
|
299
|
+
isVisible(loc: LocatorValue): Promise<boolean>;
|
|
300
|
+
/** Read a DOM attribute on the matched element. Returns null if absent. */
|
|
301
|
+
getAttribute(loc: LocatorValue, name: string): Promise<string | null>;
|
|
302
|
+
waitFor(loc: LocatorValue, opts?: WaitOptions): Promise<void>;
|
|
303
|
+
waitForUrl(pattern: string | RegExp, opts?: NavigationOptions): Promise<void>;
|
|
304
|
+
waitForText(text: string, opts?: WaitOptions): Promise<void>;
|
|
305
|
+
waitForNavigation(opts?: NavigationOptions): Promise<void>;
|
|
306
|
+
goto(url: string, opts?: GotoOptions): Promise<void>;
|
|
307
|
+
reload(opts?: ReloadOptions): Promise<void>;
|
|
308
|
+
goBack(opts?: NavigationOptions): Promise<void>;
|
|
309
|
+
goForward(opts?: NavigationOptions): Promise<void>;
|
|
310
|
+
evaluate<T = unknown>(js: string): Promise<T>;
|
|
311
|
+
evaluateOnLocator<T = unknown>(loc: LocatorValue, js: string): Promise<T>;
|
|
312
|
+
screenshot(opts?: ScreenshotOptions): Promise<Buffer>;
|
|
313
|
+
inspectElement(loc: LocatorValue): Promise<ElementHint | null>;
|
|
314
|
+
/** outerHTML of the element the locator matches (first match), or null
|
|
315
|
+
* when it resolves to zero elements. Used by debug logging to persist
|
|
316
|
+
* "what did this locator actually resolve to" as agent-readable proof —
|
|
317
|
+
* NOT part of the DSL / action surface. */
|
|
318
|
+
outerHTML(loc: LocatorValue): Promise<string | null>;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Identifying attributes of a single DOM element. Populated by
|
|
322
|
+
* `DriverPage.inspectElement`. Consumers (RefResolver) pick the highest-
|
|
323
|
+
* priority signal to synthesise a stable LocatorValue. All fields are
|
|
324
|
+
* optional — an element might have only a role+name, or only a testId,
|
|
325
|
+
* or nothing stable at all (in which case the resolver throws).
|
|
326
|
+
*/
|
|
327
|
+
interface ElementHint {
|
|
328
|
+
/** The wire-only ref the resolver is acting on. Echoed back so the
|
|
329
|
+
* resolver can verify the resolved locator still points at the same
|
|
330
|
+
* element. */
|
|
331
|
+
ref: string;
|
|
332
|
+
/** data-testid / data-qa value, if present. Highest-priority signal. */
|
|
333
|
+
testId?: string;
|
|
334
|
+
/** Computed ARIA role (Playwright's locator role inference). */
|
|
335
|
+
role?: string;
|
|
336
|
+
/** Computed accessible name (W3C accname algorithm). */
|
|
337
|
+
name?: string;
|
|
338
|
+
/** aria-label attribute when the accessible name path is not desired
|
|
339
|
+
* (e.g. icon-only buttons where role+name would still match). */
|
|
340
|
+
ariaLabel?: string;
|
|
341
|
+
/** placeholder attribute on `<input>`/`<textarea>`. Crucial for form
|
|
342
|
+
* fields that lack `<label>` or `aria-label` (common pattern in
|
|
343
|
+
* non-English UIs where placeholder doubles as the visible label).
|
|
344
|
+
* Resolver emits `getByPlaceholder(text)`. */
|
|
345
|
+
placeholder?: string;
|
|
346
|
+
/** `alt` on `<img>` / `<area>` / `<input type="image">`. Resolver
|
|
347
|
+
* emits `getByAltText(text)`. */
|
|
348
|
+
alt?: string;
|
|
349
|
+
/** `title` attribute — tooltip fallback for icon-only buttons / links
|
|
350
|
+
* that have no other label. Named `titleAttr` to avoid collision
|
|
351
|
+
* with `page.title()`. Resolver emits `getByTitle(text)`. */
|
|
352
|
+
titleAttr?: string;
|
|
353
|
+
/** textContent (trimmed, clipped). Last-resort semantic fallback
|
|
354
|
+
* before the CSS escape hatch. */
|
|
355
|
+
text?: string;
|
|
356
|
+
/** href attribute for `<a>` elements lacking any of the above.
|
|
357
|
+
* Resolver emits `locator('a[href="..."]')`. */
|
|
358
|
+
href?: string;
|
|
359
|
+
/** `id` attribute when it looks stable (not framework-generated).
|
|
360
|
+
* Heuristic filters out `:r0:`, `__next_xxx`, long random-hash
|
|
361
|
+
* patterns. Resolver emits `locator('#id')` as a CSS fallback. */
|
|
362
|
+
elementId?: string;
|
|
363
|
+
/** `name` attribute on form fields — stable selector for inputs
|
|
364
|
+
* without testId/label/placeholder. Resolver emits
|
|
365
|
+
* `locator('[name="..."]')`. */
|
|
366
|
+
nameAttr?: string;
|
|
367
|
+
/** A stable, app-authored `data-*` identifier (e.g. `data-id`, `data-guid`,
|
|
368
|
+
* `data-row-id`) — NOT framework noise (`data-v-*`, `data-reactid`) and NOT
|
|
369
|
+
* our own `data-unotest-*`. The single highest-value fallback for grid rows
|
|
370
|
+
* / custom widgets that carry no role+name: the resolver emits
|
|
371
|
+
* `locator('[data-x="..."]')`, turning an otherwise-unaddressable element
|
|
372
|
+
* into a durable locator. Detected generically (name ends in id/guid/uid/
|
|
373
|
+
* key/code, value looks stable). */
|
|
374
|
+
dataAttr?: {
|
|
375
|
+
name: string;
|
|
376
|
+
value: string;
|
|
377
|
+
};
|
|
378
|
+
/** A custom tooltip/label `data-*` attribute (name ends in `-title` /
|
|
379
|
+
* `-tooltip` / `-label`: `data-shadow-title`, `data-original-title`,
|
|
380
|
+
* `data-tooltip`, …). Carries the visible label of icon-only controls
|
|
381
|
+
* that the W3C accname can't see. Generic — matched by suffix, not by a
|
|
382
|
+
* hardcoded attribute name. The resolver emits `locator('[data-x="…"]')`. */
|
|
383
|
+
tooltipAttr?: {
|
|
384
|
+
name: string;
|
|
385
|
+
value: string;
|
|
386
|
+
};
|
|
387
|
+
/** Identifying signals of the element's ancestors (nearest first, ≤6) used
|
|
388
|
+
* to DISAMBIGUATE when the target's own locator is ambiguous: the resolver
|
|
389
|
+
* anchors to the nearest uniquely-locatable ancestor and scopes the
|
|
390
|
+
* candidate within it (`ancestor.locator(candidate)`) — the robust
|
|
391
|
+
* alternative to a positional `nth` (Playwright codegen / Robula+ approach). */
|
|
392
|
+
ancestors?: AncestorHint[];
|
|
393
|
+
}
|
|
394
|
+
/** Compact, cheap-to-compute identifying signals of an ancestor — enough to
|
|
395
|
+
* synthesise a scope anchor. No accname/role (too costly per ancestor). */
|
|
396
|
+
interface AncestorHint {
|
|
397
|
+
testId?: string;
|
|
398
|
+
dataAttr?: {
|
|
399
|
+
name: string;
|
|
400
|
+
value: string;
|
|
401
|
+
};
|
|
402
|
+
elementId?: string;
|
|
403
|
+
/** A single stable, semantic class token (not a hash / utility class). */
|
|
404
|
+
className?: string;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
export { type ActionTimeout as A, type ContextOptions as C, type DriverContext as D, type ElementHint as E, type FillOptions as F, type GotoOptions as G, type HoverOptions as H, type LaunchOptions as L, type NavigationOptions as N, type PressOptions as P, type ReloadOptions as R, type ScreenshotOptions as S, type Viewport as V, type WebDriver as W, type CheckOptions as a, type ClickOptions as b, type CookieOptions as c, type DriverPage as d, type SelectOptions as e, type WaitOptions as f, type WaitState as g, type LocatorValue as h, type LocatorStep as i, appendStep as j, describeLocator as k, isLocatorValue as l, rootedAt as r };
|