craftdriver 0.0.3 → 0.2.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.
Files changed (192) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/README.md +186 -10
  3. package/bin/craftdriver.mjs +26 -0
  4. package/dist/cli/client.d.ts +17 -0
  5. package/dist/cli/client.d.ts.map +1 -0
  6. package/dist/cli/client.js +88 -0
  7. package/dist/cli/client.js.map +1 -0
  8. package/dist/cli/daemon.d.ts +15 -0
  9. package/dist/cli/daemon.d.ts.map +1 -0
  10. package/dist/cli/daemon.js +145 -0
  11. package/dist/cli/daemon.js.map +1 -0
  12. package/dist/cli/defaults.d.ts +10 -0
  13. package/dist/cli/defaults.d.ts.map +1 -0
  14. package/dist/cli/defaults.js +42 -0
  15. package/dist/cli/defaults.js.map +1 -0
  16. package/dist/cli/dispatcher.d.ts +28 -0
  17. package/dist/cli/dispatcher.d.ts.map +1 -0
  18. package/dist/cli/dispatcher.js +319 -0
  19. package/dist/cli/dispatcher.js.map +1 -0
  20. package/dist/cli/index.d.ts +2 -0
  21. package/dist/cli/index.d.ts.map +1 -0
  22. package/dist/cli/index.js +404 -0
  23. package/dist/cli/index.js.map +1 -0
  24. package/dist/cli/init.d.ts +24 -0
  25. package/dist/cli/init.d.ts.map +1 -0
  26. package/dist/cli/init.js +192 -0
  27. package/dist/cli/init.js.map +1 -0
  28. package/dist/cli/mcp/artifacts.d.ts +43 -0
  29. package/dist/cli/mcp/artifacts.d.ts.map +1 -0
  30. package/dist/cli/mcp/artifacts.js +104 -0
  31. package/dist/cli/mcp/artifacts.js.map +1 -0
  32. package/dist/cli/mcp/server.d.ts +34 -0
  33. package/dist/cli/mcp/server.d.ts.map +1 -0
  34. package/dist/cli/mcp/server.js +214 -0
  35. package/dist/cli/mcp/server.js.map +1 -0
  36. package/dist/cli/mcp/tools.d.ts +34 -0
  37. package/dist/cli/mcp/tools.d.ts.map +1 -0
  38. package/dist/cli/mcp/tools.js +236 -0
  39. package/dist/cli/mcp/tools.js.map +1 -0
  40. package/dist/cli/parseArgs.d.ts +37 -0
  41. package/dist/cli/parseArgs.d.ts.map +1 -0
  42. package/dist/cli/parseArgs.js +215 -0
  43. package/dist/cli/parseArgs.js.map +1 -0
  44. package/dist/cli/protocol.d.ts +34 -0
  45. package/dist/cli/protocol.d.ts.map +1 -0
  46. package/dist/cli/protocol.js +2 -0
  47. package/dist/cli/protocol.js.map +1 -0
  48. package/dist/cli/selector.d.ts +29 -0
  49. package/dist/cli/selector.d.ts.map +1 -0
  50. package/dist/cli/selector.js +100 -0
  51. package/dist/cli/selector.js.map +1 -0
  52. package/dist/cli/snapshot.d.ts +48 -0
  53. package/dist/cli/snapshot.d.ts.map +1 -0
  54. package/dist/cli/snapshot.js +179 -0
  55. package/dist/cli/snapshot.js.map +1 -0
  56. package/dist/index.d.ts +14 -1
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +17 -1
  59. package/dist/index.js.map +1 -1
  60. package/dist/lib/a11y.d.ts +81 -0
  61. package/dist/lib/a11y.d.ts.map +1 -0
  62. package/dist/lib/a11y.js +171 -0
  63. package/dist/lib/a11y.js.map +1 -0
  64. package/dist/lib/bidi/connection.d.ts +62 -0
  65. package/dist/lib/bidi/connection.d.ts.map +1 -0
  66. package/dist/lib/bidi/connection.js +227 -0
  67. package/dist/lib/bidi/connection.js.map +1 -0
  68. package/dist/lib/bidi/index.d.ts +101 -0
  69. package/dist/lib/bidi/index.d.ts.map +1 -0
  70. package/dist/lib/bidi/index.js +163 -0
  71. package/dist/lib/bidi/index.js.map +1 -0
  72. package/dist/lib/bidi/logs.d.ts +104 -0
  73. package/dist/lib/bidi/logs.d.ts.map +1 -0
  74. package/dist/lib/bidi/logs.js +273 -0
  75. package/dist/lib/bidi/logs.js.map +1 -0
  76. package/dist/lib/bidi/network.d.ts +165 -0
  77. package/dist/lib/bidi/network.d.ts.map +1 -0
  78. package/dist/lib/bidi/network.js +477 -0
  79. package/dist/lib/bidi/network.js.map +1 -0
  80. package/dist/lib/bidi/storage.d.ts +83 -0
  81. package/dist/lib/bidi/storage.d.ts.map +1 -0
  82. package/dist/lib/bidi/storage.js +299 -0
  83. package/dist/lib/bidi/storage.js.map +1 -0
  84. package/dist/lib/bidi/types.d.ts +323 -0
  85. package/dist/lib/bidi/types.d.ts.map +1 -0
  86. package/dist/lib/bidi/types.js +6 -0
  87. package/dist/lib/bidi/types.js.map +1 -0
  88. package/dist/lib/browser.d.ts +776 -13
  89. package/dist/lib/browser.d.ts.map +1 -1
  90. package/dist/lib/browser.js +1547 -70
  91. package/dist/lib/browser.js.map +1 -1
  92. package/dist/lib/browserContext.d.ts +486 -0
  93. package/dist/lib/browserContext.d.ts.map +1 -0
  94. package/dist/lib/browserContext.js +937 -0
  95. package/dist/lib/browserContext.js.map +1 -0
  96. package/dist/lib/builder.d.ts +3 -0
  97. package/dist/lib/builder.d.ts.map +1 -1
  98. package/dist/lib/builder.js +31 -4
  99. package/dist/lib/builder.js.map +1 -1
  100. package/dist/lib/by.d.ts +18 -2
  101. package/dist/lib/by.d.ts.map +1 -1
  102. package/dist/lib/by.js +20 -2
  103. package/dist/lib/by.js.map +1 -1
  104. package/dist/lib/chrome.d.ts +13 -1
  105. package/dist/lib/chrome.d.ts.map +1 -1
  106. package/dist/lib/chrome.js +13 -17
  107. package/dist/lib/chrome.js.map +1 -1
  108. package/dist/lib/clock.d.ts +115 -0
  109. package/dist/lib/clock.d.ts.map +1 -0
  110. package/dist/lib/clock.js +407 -0
  111. package/dist/lib/clock.js.map +1 -0
  112. package/dist/lib/driver.d.ts +45 -0
  113. package/dist/lib/driver.d.ts.map +1 -1
  114. package/dist/lib/driver.js +129 -1
  115. package/dist/lib/driver.js.map +1 -1
  116. package/dist/lib/driverManager.d.ts +25 -0
  117. package/dist/lib/driverManager.d.ts.map +1 -0
  118. package/dist/lib/driverManager.js +399 -0
  119. package/dist/lib/driverManager.js.map +1 -0
  120. package/dist/lib/elementHandle.d.ts +69 -2
  121. package/dist/lib/elementHandle.d.ts.map +1 -1
  122. package/dist/lib/elementHandle.js +215 -40
  123. package/dist/lib/elementHandle.js.map +1 -1
  124. package/dist/lib/errors.d.ts +71 -0
  125. package/dist/lib/errors.d.ts.map +1 -0
  126. package/dist/lib/errors.js +73 -0
  127. package/dist/lib/errors.js.map +1 -0
  128. package/dist/lib/expect.d.ts +6 -1
  129. package/dist/lib/expect.d.ts.map +1 -1
  130. package/dist/lib/expect.js +55 -37
  131. package/dist/lib/expect.js.map +1 -1
  132. package/dist/lib/firefox.d.ts +18 -0
  133. package/dist/lib/firefox.d.ts.map +1 -0
  134. package/dist/lib/firefox.js +33 -0
  135. package/dist/lib/firefox.js.map +1 -0
  136. package/dist/lib/frame.d.ts +57 -0
  137. package/dist/lib/frame.d.ts.map +1 -0
  138. package/dist/lib/frame.js +277 -0
  139. package/dist/lib/frame.js.map +1 -0
  140. package/dist/lib/keyboard.d.ts +1 -1
  141. package/dist/lib/keyboard.d.ts.map +1 -1
  142. package/dist/lib/keyboard.js +1 -1
  143. package/dist/lib/keyboard.js.map +1 -1
  144. package/dist/lib/locator.d.ts +71 -2
  145. package/dist/lib/locator.d.ts.map +1 -1
  146. package/dist/lib/locator.js +289 -31
  147. package/dist/lib/locator.js.map +1 -1
  148. package/dist/lib/mouse.d.ts +1 -1
  149. package/dist/lib/mouse.d.ts.map +1 -1
  150. package/dist/lib/mouse.js +1 -1
  151. package/dist/lib/mouse.js.map +1 -1
  152. package/dist/lib/page.d.ts +107 -0
  153. package/dist/lib/page.d.ts.map +1 -0
  154. package/dist/lib/page.js +388 -0
  155. package/dist/lib/page.js.map +1 -0
  156. package/dist/lib/tracing.d.ts +128 -0
  157. package/dist/lib/tracing.d.ts.map +1 -0
  158. package/dist/lib/tracing.js +272 -0
  159. package/dist/lib/tracing.js.map +1 -0
  160. package/dist/lib/wait.d.ts.map +1 -1
  161. package/dist/lib/wait.js +14 -1
  162. package/dist/lib/wait.js.map +1 -1
  163. package/dist/lib/webelement.d.ts +2 -0
  164. package/dist/lib/webelement.d.ts.map +1 -1
  165. package/dist/lib/webelement.js +18 -0
  166. package/dist/lib/webelement.js.map +1 -1
  167. package/docs/accessibility.md +177 -0
  168. package/docs/api-reference.md +71 -0
  169. package/docs/assertions.md +182 -0
  170. package/docs/bidi-features.md +462 -0
  171. package/docs/browser-api.md +673 -0
  172. package/docs/browser-context.md +522 -0
  173. package/docs/cli.md +233 -0
  174. package/docs/clock.md +245 -0
  175. package/docs/dialogs.md +143 -0
  176. package/docs/driver-configuration.md +64 -0
  177. package/docs/element-api.md +219 -0
  178. package/docs/emulation.md +155 -0
  179. package/docs/error-codes.md +67 -0
  180. package/docs/getting-started.md +168 -0
  181. package/docs/keyboard-mouse.md +250 -0
  182. package/docs/mcp.md +250 -0
  183. package/docs/mobile-emulation.md +194 -0
  184. package/docs/screenshots.md +146 -0
  185. package/docs/selectors.md +382 -0
  186. package/docs/session-management.md +229 -0
  187. package/docs/tracing.md +282 -0
  188. package/package.json +25 -5
  189. package/skills/craftdriver/SKILL.md +84 -0
  190. package/skills/craftdriver/cheatsheet.md +194 -0
  191. package/skills/craftdriver/cli.md +104 -0
  192. package/skills/craftdriver/patterns.md +139 -0
@@ -1,20 +1,424 @@
1
1
  import { ChromeService } from './chrome.js';
2
- import { Driver } from './driver.js';
2
+ import { FirefoxService } from './firefox.js';
3
3
  import { By } from './by.js';
4
4
  import { Condition, WaitOptions } from './wait.js';
5
5
  import { ElementHandle } from './elementHandle.js';
6
- import { KeyboardController } from './keyboard.js';
7
- import { MouseController } from './mouse.js';
6
+ import { Locator } from './locator.js';
7
+ import { Keyboard } from './keyboard.js';
8
+ import { Mouse } from './mouse.js';
8
9
  import { ActionsBuilder } from './actions.js';
10
+ import { NetworkInterceptor, LogMonitor, SessionStateManager, type SessionState, type StorageStateOptions, type InterceptedRequest, type InterceptedResponse } from './bidi/index.js';
11
+ import { Frame } from './frame.js';
12
+ import { Page } from './page.js';
13
+ import { BrowserContext } from './browserContext.js';
14
+ import { type TraceStartOptions } from './tracing.js';
15
+ import { A11y } from './a11y.js';
16
+ import { Clock } from './clock.js';
17
+ /** Device metrics for custom mobile emulation */
18
+ export interface DeviceMetrics {
19
+ width: number;
20
+ height: number;
21
+ pixelRatio: number;
22
+ mobile?: boolean;
23
+ touch?: boolean;
24
+ }
25
+ /** Mobile emulation configuration */
26
+ export interface MobileEmulation {
27
+ /** Use a predefined device (e.g., 'iPhone 14', 'Pixel 7') */
28
+ deviceName?: string;
29
+ /** Custom device metrics */
30
+ deviceMetrics?: DeviceMetrics;
31
+ /** Custom user agent string */
32
+ userAgent?: string;
33
+ }
34
+ /**
35
+ * Options for {@link Browser.emulate}. Every field is independent; only the
36
+ * keys you pass are applied, others stay at their previous value. Passing
37
+ * `null` for a field clears the override for that field.
38
+ */
39
+ export interface EmulateOptions {
40
+ /** `prefers-color-scheme` media query value. Chromium only. */
41
+ colorScheme?: 'light' | 'dark' | 'no-preference' | null;
42
+ /** `prefers-reduced-motion` media query value. Chromium only. */
43
+ reducedMotion?: 'reduce' | 'no-preference' | null;
44
+ /** `forced-colors` media query value. Chromium only. */
45
+ forcedColors?: 'active' | 'none' | null;
46
+ /** BCP-47 language tag, e.g. `'de-DE'`. Affects `navigator.language` and `Intl.*`. */
47
+ locale?: string | null;
48
+ /** IANA timezone, e.g. `'Europe/Berlin'`. Affects `Intl.DateTimeFormat` and `Date`. */
49
+ timezoneId?: string | null;
50
+ /** When `true`, network requests fail and `navigator.onLine === false`. Chromium only. */
51
+ offline?: boolean;
52
+ }
53
+ /** Common mobile device presets */
54
+ export declare const devices: {
55
+ readonly 'iPhone 14': {
56
+ readonly deviceMetrics: {
57
+ readonly width: 390;
58
+ readonly height: 844;
59
+ readonly pixelRatio: 3;
60
+ readonly mobile: true;
61
+ readonly touch: true;
62
+ };
63
+ readonly userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1";
64
+ };
65
+ readonly 'iPhone 14 Pro Max': {
66
+ readonly deviceMetrics: {
67
+ readonly width: 430;
68
+ readonly height: 932;
69
+ readonly pixelRatio: 3;
70
+ readonly mobile: true;
71
+ readonly touch: true;
72
+ };
73
+ readonly userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1";
74
+ };
75
+ readonly 'iPhone SE': {
76
+ readonly deviceMetrics: {
77
+ readonly width: 375;
78
+ readonly height: 667;
79
+ readonly pixelRatio: 2;
80
+ readonly mobile: true;
81
+ readonly touch: true;
82
+ };
83
+ readonly userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1";
84
+ };
85
+ readonly 'Pixel 7': {
86
+ readonly deviceMetrics: {
87
+ readonly width: 412;
88
+ readonly height: 915;
89
+ readonly pixelRatio: 2.625;
90
+ readonly mobile: true;
91
+ readonly touch: true;
92
+ };
93
+ readonly userAgent: "Mozilla/5.0 (Linux; Android 13; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Mobile Safari/537.36";
94
+ };
95
+ readonly 'Pixel 7 Pro': {
96
+ readonly deviceMetrics: {
97
+ readonly width: 412;
98
+ readonly height: 892;
99
+ readonly pixelRatio: 3.5;
100
+ readonly mobile: true;
101
+ readonly touch: true;
102
+ };
103
+ readonly userAgent: "Mozilla/5.0 (Linux; Android 13; Pixel 7 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Mobile Safari/537.36";
104
+ };
105
+ readonly 'Samsung Galaxy S23': {
106
+ readonly deviceMetrics: {
107
+ readonly width: 360;
108
+ readonly height: 780;
109
+ readonly pixelRatio: 3;
110
+ readonly mobile: true;
111
+ readonly touch: true;
112
+ };
113
+ readonly userAgent: "Mozilla/5.0 (Linux; Android 13; SM-S911B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Mobile Safari/537.36";
114
+ };
115
+ readonly 'iPad Pro 11': {
116
+ readonly deviceMetrics: {
117
+ readonly width: 834;
118
+ readonly height: 1194;
119
+ readonly pixelRatio: 2;
120
+ readonly mobile: true;
121
+ readonly touch: true;
122
+ };
123
+ readonly userAgent: "Mozilla/5.0 (iPad; CPU OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1";
124
+ };
125
+ readonly 'iPad Mini': {
126
+ readonly deviceMetrics: {
127
+ readonly width: 768;
128
+ readonly height: 1024;
129
+ readonly pixelRatio: 2;
130
+ readonly mobile: true;
131
+ readonly touch: true;
132
+ };
133
+ readonly userAgent: "Mozilla/5.0 (iPad; CPU OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1";
134
+ };
135
+ };
136
+ export type DeviceName = keyof typeof devices;
9
137
  export interface LaunchOptions {
10
- browserName?: 'chrome' | 'chromium' | 'firefox' | 'edge' | 'safari';
138
+ browserName?: 'chrome' | 'chromium' | 'firefox';
11
139
  chromeService?: ChromeService;
140
+ firefoxService?: FirefoxService;
141
+ /**
142
+ * Enable WebDriver BiDi for network interception, logs, load events, etc.
143
+ * **Defaults to `true`** — BiDi is the primary protocol; Classic WebDriver
144
+ * is used only as a fallback when a capability is not yet available via BiDi.
145
+ * Set `false` only if your environment cannot negotiate a BiDi WebSocket.
146
+ */
147
+ enableBiDi?: boolean;
148
+ /** Load session state from file on launch. BiDi is always used when this is set. */
149
+ storageState?: string;
150
+ /** Enable mobile device emulation (Chrome/Chromium only) */
151
+ mobileEmulation?: MobileEmulation | DeviceName;
152
+ /**
153
+ * Directory where downloaded files are saved.
154
+ * Defaults to a temporary directory unique to this browser session.
155
+ */
156
+ downloadsDir?: string;
157
+ }
158
+ /**
159
+ * When to consider a navigation complete.
160
+ * - `'load'` — page `load` event has fired (default)
161
+ * - `'domcontentloaded'` — `DOMContentLoaded` has fired (faster, no waiting for images/fonts)
162
+ * - `'networkidle'` — `load` + no in-flight requests for 500 ms
163
+ * - `'none'` — do not wait; return as soon as the navigation is initiated
164
+ */
165
+ export type LoadState = 'load' | 'domcontentloaded' | 'networkidle' | 'none';
166
+ /** The type of a browser dialog. */
167
+ export type DialogType = 'alert' | 'confirm' | 'prompt' | 'beforeunload';
168
+ /**
169
+ * Represents an open browser dialog (alert / confirm / prompt).
170
+ * Passed to handlers registered with `browser.onDialog()`.
171
+ */
172
+ export interface Dialog {
173
+ /** Returns the dialog type: `'alert'`, `'confirm'`, `'prompt'`, or `'beforeunload'`. */
174
+ type(): DialogType;
175
+ /** Returns the message text shown in the dialog. */
176
+ message(): string;
177
+ /** Returns the default text pre-filled in a prompt dialog (empty string for non-prompt dialogs). */
178
+ defaultValue(): string;
179
+ /** Accept the dialog (OK button). For prompts, optionally pass `text` to fill the input first. */
180
+ accept(text?: string): Promise<void>;
181
+ /** Dismiss the dialog (Cancel button / close). */
182
+ dismiss(): Promise<void>;
183
+ }
184
+ /** A file downloaded during a `waitForDownload()` call. */
185
+ export interface Download {
186
+ /** Absolute path to the downloaded file on disk. */
187
+ path: string;
188
+ /** The filename as suggested by the server (from Content-Disposition or URL). */
189
+ suggestedFilename: string;
190
+ /** Copy the file to `targetPath`. */
191
+ saveAs(targetPath: string): Promise<void>;
12
192
  }
13
193
  export declare class Browser {
14
194
  private driver;
195
+ private bidiSession?;
196
+ private _network?;
197
+ private _defaultContext?;
198
+ /** Cache of {@link BrowserContext} wrappers keyed by user-context id. */
199
+ private _contextsById;
200
+ private _logs?;
201
+ private _tracer?;
202
+ private _storage?;
203
+ private _a11y?;
204
+ private _clock?;
205
+ private _downloadsDir?;
206
+ private _driverService?;
207
+ private _browserName;
208
+ /** Active emulation overrides, re-applied to new top-level contexts. */
209
+ private _emulation;
210
+ /** Mutable browser-level defaults. Use setDefaultTimeout() / setDefaultNavigationTimeout() to change. */
211
+ private defaults;
15
212
  private constructor();
213
+ /**
214
+ * Set the default timeout (ms) used by all element actions, waits, and assertions
215
+ * when no per-call `{ timeout }` option is provided. Default: 5000.
216
+ */
217
+ setDefaultTimeout(ms: number): void;
218
+ /**
219
+ * Set the default navigation timeout (ms) used by navigateTo and load-state waits.
220
+ * Default: 30000.
221
+ */
222
+ setDefaultNavigationTimeout(ms: number): void;
223
+ /** Returns the current default timeout. Used as a live getter passed to ElementHandle / expectSelector. */
224
+ private getDefaultTimeout;
16
225
  static launch(options?: LaunchOptions): Promise<Browser>;
17
- navigateTo(url: string): Promise<void>;
226
+ /**
227
+ * Initialize BiDi WebSocket connection
228
+ */
229
+ private initBiDi;
230
+ /**
231
+ * Check if BiDi features are available
232
+ */
233
+ isBiDiEnabled(): boolean;
234
+ /**
235
+ * Network interception API (BiDi)
236
+ * Mock, intercept, and modify network requests
237
+ */
238
+ get network(): NetworkInterceptor;
239
+ /**
240
+ * Console/error log monitoring (BiDi)
241
+ */
242
+ get logs(): LogMonitor;
243
+ /**
244
+ * Session state management (cookies, localStorage)
245
+ * Works with both BiDi and Classic WebDriver
246
+ */
247
+ get storage(): SessionStateManager;
248
+ /**
249
+ * Virtual clock control — freeze or advance `Date`, `setTimeout`, and
250
+ * `setInterval` inside the page. Requires BiDi (enabled by default).
251
+ *
252
+ * ```ts
253
+ * await browser.clock.install({ time: '2026-01-01T09:00:00Z' });
254
+ * await browser.clock.fastForward('15:01');
255
+ * ```
256
+ */
257
+ get clock(): Clock;
258
+ /**
259
+ * Accessibility audits via axe-core (optional peer dependency).
260
+ *
261
+ * ```ts
262
+ * await browser.a11y.check({ disableRules: ['color-contrast'] });
263
+ * ```
264
+ *
265
+ * Requires `axe-core` to be installed: `npm install --save-dev axe-core`.
266
+ */
267
+ get a11y(): A11y;
268
+ /**
269
+ * Save current session state (cookies + localStorage) to file
270
+ */
271
+ saveState(path: string, options?: StorageStateOptions): Promise<SessionState>;
272
+ /**
273
+ * Load session state from file
274
+ */
275
+ loadState(path: string): Promise<void>;
276
+ navigateTo(url: string, opts?: {
277
+ waitUntil?: LoadState;
278
+ }): Promise<void>;
279
+ /**
280
+ * Navigate the active page back one step in its session history.
281
+ * Proxies to {@link Page.goBack} on {@link activePage}.
282
+ */
283
+ goBack(): Promise<void>;
284
+ /**
285
+ * Navigate the active page forward one step in its session history.
286
+ * Proxies to {@link Page.goForward} on {@link activePage}.
287
+ */
288
+ goForward(): Promise<void>;
289
+ /**
290
+ * Reload the active page. Proxies to {@link Page.reload} on
291
+ * {@link activePage}.
292
+ */
293
+ reload(): Promise<void>;
294
+ /**
295
+ * Return the full HTML serialization of the active page.
296
+ * Proxies to {@link Page.content}.
297
+ */
298
+ content(): Promise<string>;
299
+ /**
300
+ * Replace the active page contents with the given HTML.
301
+ * Proxies to {@link Page.setContent}.
302
+ */
303
+ setContent(html: string, opts?: {
304
+ waitUntil?: Exclude<LoadState, 'networkidle'>;
305
+ }): Promise<void>;
306
+ /**
307
+ * Resize the viewport at runtime.
308
+ *
309
+ * - **BiDi** path uses `browsingContext.setViewport` on the active page,
310
+ * which resizes the *layout* viewport without changing the OS window.
311
+ * - **Classic** fallback uses `POST /session/{id}/window/rect`, which
312
+ * resizes the OS window — the inner viewport may end up a few pixels
313
+ * smaller than `width`/`height` because of browser chrome.
314
+ *
315
+ * For full mobile emulation (DPR, user-agent, touch), pass
316
+ * `mobileEmulation` to {@link Browser.launch}; this method only changes
317
+ * the viewport box.
318
+ */
319
+ setViewportSize(size: {
320
+ width: number;
321
+ height: number;
322
+ }): Promise<void>;
323
+ /**
324
+ * Override one or more permissions for an origin (or for every origin
325
+ * if `origin` is omitted).
326
+ *
327
+ * Uses BiDi `permissions.setPermission` (W3C Permissions module).
328
+ * Each name in `permissions` corresponds to a `PermissionDescriptor.name`,
329
+ * for example: `'geolocation'`, `'notifications'`, `'clipboard-read'`,
330
+ * `'clipboard-write'`, `'camera'`, `'microphone'`, `'midi'`,
331
+ * `'background-sync'`, `'persistent-storage'`.
332
+ *
333
+ * @example
334
+ * await browser.grantPermissions(['geolocation', 'notifications']);
335
+ * await browser.grantPermissions(['clipboard-read'], { origin: 'https://example.com' });
336
+ */
337
+ grantPermissions(permissions: string[], opts?: {
338
+ origin?: string;
339
+ state?: 'granted' | 'denied' | 'prompt';
340
+ }): Promise<void>;
341
+ /**
342
+ * Reset all permissions for an origin (or every origin) back to the
343
+ * browser default of `'prompt'`. Convenience wrapper around
344
+ * {@link grantPermissions} with `state: 'prompt'`.
345
+ */
346
+ clearPermissions(permissions: string[], opts?: {
347
+ origin?: string;
348
+ }): Promise<void>;
349
+ /**
350
+ * Override the geolocation reported by `navigator.geolocation`.
351
+ *
352
+ * Uses BiDi `emulation.setGeolocationOverride` (W3C BiDi
353
+ * `emulation` module). Pass `null` to clear the override and return
354
+ * to the real device location.
355
+ *
356
+ * @example
357
+ * await browser.setGeolocation({ latitude: 51.5074, longitude: -0.1278 });
358
+ * await browser.setGeolocation(null); // clear
359
+ */
360
+ setGeolocation(coords: {
361
+ latitude: number;
362
+ longitude: number;
363
+ accuracy?: number;
364
+ } | null): Promise<void>;
365
+ /**
366
+ * Override what the page sees from `matchMedia`, `navigator.language`,
367
+ * `Intl.*`, and the network stack.
368
+ *
369
+ * Pass `null` for any field to clear that override; pass `{}` for a no-op.
370
+ * Settings persist across navigations and are re-applied to new top-level
371
+ * pages opened in the same session.
372
+ *
373
+ * | Field | Transport | Cross-browser |
374
+ * |---|---|---|
375
+ * | `locale` | BiDi `emulation.setLocaleOverride` | yes |
376
+ * | `timezoneId` | BiDi `emulation.setTimezoneOverride` | yes |
377
+ * | `colorScheme` / `reducedMotion` / `forcedColors` | CDP `Emulation.setEmulatedMedia` | Chromium only |
378
+ * | `offline` | CDP `Network.emulateNetworkConditions` | Chromium only |
379
+ *
380
+ * Throws with a clear message if a Chromium-only field is requested on
381
+ * Firefox, or if BiDi negotiation failed at launch.
382
+ *
383
+ * @example
384
+ * ```ts
385
+ * // Dark mode + German formatting + Berlin time
386
+ * await browser.emulate({
387
+ * colorScheme: 'dark',
388
+ * locale: 'de-DE',
389
+ * timezoneId: 'Europe/Berlin',
390
+ * });
391
+ *
392
+ * // Offline PWA UI
393
+ * await browser.emulate({ offline: true });
394
+ * await browser.click('#refresh');
395
+ *
396
+ * // Clear a single override
397
+ * await browser.emulate({ locale: null });
398
+ * ```
399
+ */
400
+ emulate(options: EmulateOptions): Promise<void>;
401
+ /**
402
+ * Send a CDP command scoped to a single top-level browsing context.
403
+ * Uses the chromium-bidi `goog:cdp` vendor extension. Wraps protocol
404
+ * errors so callers see an actionable message.
405
+ */
406
+ private _cdpForContext;
407
+ /** Best-effort: derive the active page's origin (`scheme://host[:port]`). */
408
+ private _currentOrigin;
409
+ /**
410
+ * Wait for the page to reach a given load state.
411
+ * Call this after an action that triggers navigation (clicking a link, submitting a form).
412
+ *
413
+ * - `'load'` — page `load` event (default)
414
+ * - `'domcontentloaded'` — DOMContentLoaded (faster)
415
+ * - `'networkidle'` — load + no in-flight requests for 500 ms
416
+ *
417
+ * Uses BiDi events when available, falls back to polling `document.readyState`.
418
+ */
419
+ waitForLoadState(state?: Exclude<LoadState, 'none'>, opts?: {
420
+ timeout?: number;
421
+ }): Promise<void>;
18
422
  url(): Promise<string>;
19
423
  title(): Promise<string>;
20
424
  wait<T>(condition: Condition<T>, options?: WaitOptions & {
@@ -22,7 +426,324 @@ export declare class Browser {
22
426
  }): Promise<T>;
23
427
  wait<T>(condition: Condition<T>, timeoutMs?: number, intervalMs?: number, message?: string): Promise<T>;
24
428
  quit(): Promise<void>;
25
- click(selector: string | By): Promise<void>;
429
+ /**
430
+ * Start a streaming trace session. Writes every event synchronously to
431
+ * `<outDir>/trace.ndjson` so a thrown `expect` on the next line cannot
432
+ * lose data. Screenshots, when enabled, land in `<outDir>/screenshots/`.
433
+ *
434
+ * **BiDi-only.** Call `stopTrace()` to write the closing marker and
435
+ * close the file.
436
+ *
437
+ * @example
438
+ * await browser.startTrace({ outDir: './artefacts/login' });
439
+ * try {
440
+ * await browser.navigateTo('/checkout');
441
+ * await browser.click('#pay');
442
+ * } finally {
443
+ * await browser.stopTrace();
444
+ * }
445
+ */
446
+ startTrace(opts: TraceStartOptions): Promise<void>;
447
+ /**
448
+ * Stop the active trace: drain in-flight screenshots, write the closing
449
+ * `meta` line, and close the file. If your test threw and never reached
450
+ * here, the file is still valid NDJSON — just without the closing line.
451
+ */
452
+ stopTrace(): Promise<void>;
453
+ /**
454
+ * Execute JavaScript in the page and return the result.
455
+ *
456
+ * Accepts a function (with optional args) or a script string:
457
+ * ```ts
458
+ * await browser.evaluate(() => document.title);
459
+ * await browser.evaluate((a, b) => a + b, 2, 3); // 5
460
+ * await browser.evaluate('return document.title');
461
+ * ```
462
+ *
463
+ * Uses BiDi `script.callFunction` / `script.evaluate` when available,
464
+ * falls back to Classic WebDriver `executeScript`.
465
+ * Only JSON-serializable return values are supported.
466
+ */
467
+ evaluate<T = unknown>(fn: ((...args: unknown[]) => T) | string, ...args: unknown[]): Promise<T>;
468
+ /**
469
+ * Register a script that runs before any page script on every navigation.
470
+ * Returns a handle with a `remove()` method to unregister it.
471
+ *
472
+ * Requires BiDi (enabled by default). Throws a clear error otherwise.
473
+ *
474
+ * ```ts
475
+ * const script = await browser.addInitScript(() => {
476
+ * window.__flags = { darkMode: true };
477
+ * });
478
+ * await browser.navigateTo(url); // __flags is available
479
+ * await script.remove(); // unregister
480
+ * ```
481
+ */
482
+ addInitScript(fnOrSrc: ((...args: unknown[]) => unknown) | string): Promise<{
483
+ remove(): Promise<void>;
484
+ }>;
485
+ /**
486
+ * Wait for the first network request matching a URL glob or predicate.
487
+ * Register **before** the action that triggers the request.
488
+ */
489
+ waitForRequest(pattern: string | ((req: InterceptedRequest) => boolean), opts?: {
490
+ timeout?: number;
491
+ }): Promise<InterceptedRequest>;
492
+ /**
493
+ * Wait for the first completed response matching a URL glob or predicate.
494
+ * Register **before** the action that triggers the request.
495
+ *
496
+ * @example
497
+ * ```ts
498
+ * const [res] = await Promise.all([
499
+ * browser.waitForResponse('/api/users'),
500
+ * browser.click('#load-btn'),
501
+ * ]);
502
+ * expect(res.status).toBe(200);
503
+ * ```
504
+ */
505
+ waitForResponse(pattern: string | ((res: InterceptedResponse) => boolean), opts?: {
506
+ timeout?: number;
507
+ }): Promise<InterceptedResponse>;
508
+ /**
509
+ * Subscribe to **every** network request or response. Returns an
510
+ * `off()` function that unsubscribes the listener.
511
+ *
512
+ * Use this for fire-and-forget logging, tracing, or assertion-on-every
513
+ * patterns where you do not know in advance which request you care
514
+ * about. For one-shot waits prefer {@link waitForRequest} /
515
+ * {@link waitForResponse}.
516
+ *
517
+ * @example
518
+ * ```ts
519
+ * const off = browser.on('response', (res) => {
520
+ * console.log(res.status, res.url);
521
+ * });
522
+ * await browser.click('#load');
523
+ * off();
524
+ * ```
525
+ */
526
+ on(event: 'request', listener: (req: InterceptedRequest) => void): () => void;
527
+ on(event: 'response', listener: (res: InterceptedResponse) => void): () => void;
528
+ /**
529
+ * Run `action`, then wait until a new file appears in the downloads directory.
530
+ * Returns a `Download` handle with `path`, `suggestedFilename`, and `saveAs()`.
531
+ *
532
+ * @example
533
+ * const dl = await browser.waitForDownload(() => browser.click('#download-btn'));
534
+ * expect(dl.suggestedFilename).toBe('report.csv');
535
+ * await dl.saveAs('/tmp/report.csv');
536
+ */
537
+ waitForDownload(action: () => Promise<void> | void, opts?: {
538
+ timeout?: number;
539
+ }): Promise<Download>;
540
+ /**
541
+ * Register a handler called every time a browser dialog opens (alert / confirm / prompt).
542
+ * The handler receives a `Dialog` object with `accept()` and `dismiss()` methods.
543
+ * Returns an unsubscribe function — call it to stop listening.
544
+ *
545
+ * If no handler is registered, dialogs are auto-dismissed so they don't hang the test.
546
+ *
547
+ * @example
548
+ * const off = browser.onDialog(async dialog => {
549
+ * expect(dialog.message).toBe('Are you sure?');
550
+ * await dialog.accept();
551
+ * });
552
+ * await browser.click('#confirm-btn');
553
+ * off(); // stop listening
554
+ */
555
+ onDialog(handler: (dialog: Dialog) => Promise<void> | void): () => void;
556
+ /**
557
+ * Accept the currently open dialog (OK / confirm).
558
+ * For prompt dialogs, pass `text` to type into the input first.
559
+ * Uses BiDi when available, Classic WebDriver otherwise.
560
+ */
561
+ acceptDialog(text?: string): Promise<void>;
562
+ /**
563
+ * Dismiss the currently open dialog (Cancel / close).
564
+ * Uses BiDi when available, Classic WebDriver otherwise.
565
+ */
566
+ dismissDialog(): Promise<void>;
567
+ /**
568
+ * Get the message text of the currently open dialog.
569
+ * Uses BiDi when available, Classic WebDriver otherwise.
570
+ */
571
+ getDialogMessage(): Promise<string>;
572
+ /**
573
+ * Wait for the next dialog (alert / confirm / prompt) to open and return it.
574
+ * The dialog object has `accept()` and `dismiss()` methods — call one of them
575
+ * or the page will be frozen waiting for the dialog.
576
+ *
577
+ * Matches Playwright's `page.waitForEvent('dialog')` pattern:
578
+ * ```ts
579
+ * const [, dialog] = await Promise.all([
580
+ * browser.click('#confirm-btn'),
581
+ * browser.waitForDialog(),
582
+ * ]);
583
+ * await dialog.accept();
584
+ * ```
585
+ */
586
+ waitForDialog(opts?: {
587
+ timeout?: number;
588
+ }): Promise<Dialog>;
589
+ private _buildDialog;
590
+ /**
591
+ * Return a `Frame` object scoped to the first iframe matching `selector`.
592
+ * All element methods on the returned `Frame` are automatically targeted
593
+ * inside that iframe's browsing context.
594
+ *
595
+ * @example
596
+ * const frame = await browser.frame('#stripe-iframe');
597
+ * await frame.fill('#card-number', '4242 4242 4242 4242');
598
+ */
599
+ frame(selector: string | By, opts?: {
600
+ timeout?: number;
601
+ }): Promise<Frame>;
602
+ /**
603
+ * Return `Frame` objects for all iframes on the current page.
604
+ */
605
+ frames(opts?: {
606
+ timeout?: number;
607
+ }): Promise<Frame[]>;
608
+ /**
609
+ * Return `Page` objects for all open top-level browsing contexts
610
+ * (tabs and popup windows).
611
+ */
612
+ pages(): Promise<Page[]>;
613
+ /**
614
+ * Open a new top-level browsing context (a tab or a window).
615
+ *
616
+ * Maps to BiDi `browsingContext.create`. **BiDi-only** — throws in Classic
617
+ * mode because WebDriver Classic has no spec-level command for creating
618
+ * top-level browsing contexts.
619
+ *
620
+ * @example
621
+ * const page = await browser.openPage({ url: 'https://example.com', type: 'tab' });
622
+ * await page.waitForLoadState();
623
+ */
624
+ openPage(opts?: {
625
+ url?: string;
626
+ type?: 'tab' | 'window';
627
+ }): Promise<Page>;
628
+ /**
629
+ * Run `action` and wait for a new top-level browsing context (tab / popup)
630
+ * to open. Returns a `Page` bound to the new context.
631
+ *
632
+ * @example
633
+ * const popup = await browser.waitForPage(() => browser.click('#open-popup'));
634
+ * await popup.waitForLoadState();
635
+ * const title = await popup.title();
636
+ */
637
+ waitForPage(action: () => Promise<void>, opts?: {
638
+ timeout?: number;
639
+ }): Promise<Page>;
640
+ /**
641
+ * Create a new isolated user context (an "incognito profile"). Each
642
+ * context has its own cookies, localStorage, and IndexedDB.
643
+ *
644
+ * Maps to BiDi `browser.createUserContext`. **BiDi-only** — throws in
645
+ * Classic mode because WebDriver Classic has no equivalent.
646
+ *
647
+ * Optionally pre-seed the context with a previously-saved
648
+ * `storageState` snapshot (object or path to JSON) — cookies are
649
+ * applied immediately and localStorage entries land on first
650
+ * navigation to each captured origin.
651
+ *
652
+ * @example
653
+ * const alice = await browser.newContext();
654
+ * const bob = await browser.newContext();
655
+ * const aPage = await alice.newPage({ url: 'https://app.example.com/login' });
656
+ * const bPage = await bob.newPage({ url: 'https://app.example.com/login' });
657
+ * // logging in as Alice in aPage does not leak into bPage.
658
+ *
659
+ * @example
660
+ * // Skip the login UI in every test by reusing a saved session.
661
+ * const ctx = await browser.newContext({ storageState: 'auth/alice.json' });
662
+ * const page = await ctx.newPage({ url: 'https://app.example.com/dashboard' });
663
+ *
664
+ * @example
665
+ * // Pre-configure a context for a staging tenant: every relative URL
666
+ * // resolves against staging, every request carries the tenant header.
667
+ * const ctx = await browser.newContext({
668
+ * baseURL: 'https://staging.example.com',
669
+ * extraHTTPHeaders: { 'X-Tenant': 'acme' },
670
+ * });
671
+ * const page = await ctx.newPage({ url: '/dashboard' }); // → staging.example.com/dashboard
672
+ */
673
+ /**
674
+ * Return the {@link BrowserContext} wrapper for a given BiDi user-context
675
+ * id, constructing it on first access and caching it thereafter. Stable
676
+ * across {@link defaultContext}, {@link newContext}, and {@link contexts}
677
+ * so route handlers, event listeners, and init scripts always live on
678
+ * the same instance the user holds.
679
+ *
680
+ * Caller must guarantee BiDi is connected (`bidiSession.isConnected()`).
681
+ */
682
+ private _wrapContext;
683
+ newContext(opts?: {
684
+ storageState?: SessionState | string;
685
+ baseURL?: string;
686
+ extraHTTPHeaders?: Record<string, string>;
687
+ /**
688
+ * Locale reported by `navigator.language`, `Intl.*`, and the
689
+ * `Accept-Language` header. Cross-browser via BiDi
690
+ * `emulation.setLocaleOverride`. Applies to every page in this
691
+ * context, including pages opened later.
692
+ */
693
+ locale?: string;
694
+ /**
695
+ * IANA timezone applied to `Date` and `Intl.DateTimeFormat`.
696
+ * Cross-browser via BiDi `emulation.setTimezoneOverride`. Applies to
697
+ * every page in this context, including pages opened later.
698
+ */
699
+ timezoneId?: string;
700
+ /**
701
+ * Coordinates returned by `navigator.geolocation`. Cross-browser via
702
+ * BiDi `emulation.setGeolocationOverride`. The page still needs the
703
+ * `geolocation` permission \u2014 use {@link BrowserContext.grantPermissions}
704
+ * once the origin is known.
705
+ */
706
+ geolocation?: {
707
+ latitude: number;
708
+ longitude: number;
709
+ accuracy?: number;
710
+ };
711
+ }): Promise<BrowserContext>;
712
+ /**
713
+ * Return all open user contexts, including the default one.
714
+ *
715
+ * Maps to BiDi `browser.getUserContexts`. **BiDi-only.**
716
+ */
717
+ contexts(): Promise<BrowserContext[]>;
718
+ /**
719
+ * The implicit default user context the browser started in (id `'default'`).
720
+ * Pages opened via `browser.openPage()` / `browser.waitForPage()` belong
721
+ * to this context. **BiDi-only.**
722
+ */
723
+ get defaultContext(): BrowserContext;
724
+ /**
725
+ * Return the currently focused top-level page in `defaultContext`.
726
+ *
727
+ * This is the page that `browser.click()`, `browser.find()`,
728
+ * `browser.evaluate()` and the other `Browser`-level DOM shortcuts
729
+ * implicitly target. It **never** crosses into pages inside a
730
+ * non-default `BrowserContext`.
731
+ *
732
+ * Use it to make multi-tab tests explicit:
733
+ *
734
+ * @example
735
+ * const page = await browser.activePage();
736
+ * await page.click('#submit'); // identical to browser.click('#submit')
737
+ *
738
+ * @example
739
+ * const popup = await browser.openPage({ url: '/help' });
740
+ * // browser.activePage() still returns the original page —
741
+ * // openPage() does not steal focus.
742
+ */
743
+ activePage(): Promise<Page>;
744
+ click(selector: string | By, opts?: {
745
+ timeout?: number;
746
+ }): Promise<void>;
26
747
  fill(selector: string | By, text: string, opts?: {
27
748
  timeout?: number;
28
749
  }): Promise<void>;
@@ -48,10 +769,41 @@ export declare class Browser {
48
769
  durationMs?: number;
49
770
  }) => Promise<void>;
50
771
  };
51
- screenshot(selectorOrBy?: string | By): Promise<Buffer>;
52
- saveScreenshot(path: string, selectorOrBy?: string | By): Promise<Buffer>;
772
+ /**
773
+ * Capture a screenshot of the active page (viewport by default), the
774
+ * full scrollable document (`fullPage: true`), or an element matching
775
+ * `selector`. Optionally write the PNG to `path`. Returns the raw PNG
776
+ * buffer.
777
+ *
778
+ * Full-page capture uses BiDi `browsingContext.captureScreenshot` with
779
+ * `origin: 'document'` and therefore requires `enableBiDi: true`
780
+ * (the default). Element capture composes auto-waiting via the same
781
+ * resolution as `find()`.
782
+ *
783
+ * @example
784
+ * const buf = await browser.screenshot();
785
+ * await browser.screenshot({ path: 'viewport.png' });
786
+ * await browser.screenshot({ fullPage: true, path: 'full.png' });
787
+ * await browser.screenshot({ selector: '#chart', path: 'chart.png' });
788
+ */
789
+ screenshot(opts?: {
790
+ path?: string;
791
+ selector?: string | By;
792
+ fullPage?: boolean;
793
+ timeout?: number;
794
+ }): Promise<Buffer>;
53
795
  expect(selector: string | By): import("./expect.js").ExpectApi;
54
796
  find(selector: string | By): ElementHandle;
797
+ /**
798
+ * Return a lazy, chainable `Locator` for the given selector.
799
+ * Prefer `locator()` over `find()` when you need composition, filtering, or indexed access.
800
+ */
801
+ locator(selector: string | By): Locator;
802
+ /**
803
+ * Return snapshot `ElementHandle`s for all elements matching `selector` at call time.
804
+ * Use `locator().all()` when you need filtering; use `findAll()` for the simple array case.
805
+ */
806
+ findAll(selector: string | By): Promise<ElementHandle[]>;
55
807
  getByRole(role: string, opts?: {
56
808
  name?: string;
57
809
  exact?: boolean;
@@ -67,8 +819,8 @@ export declare class Browser {
67
819
  exact?: boolean;
68
820
  }): ElementHandle;
69
821
  getByTestId(id: string): ElementHandle;
70
- keyboard: KeyboardController;
71
- mouse: MouseController;
822
+ keyboard: Keyboard;
823
+ mouse: Mouse;
72
824
  static Key: {
73
825
  readonly Null: string;
74
826
  readonly Cancel: string;
@@ -108,9 +860,6 @@ export declare class Browser {
108
860
  readonly F12: string;
109
861
  readonly Meta: string;
110
862
  };
111
- waitFor(check: ((driver: Driver) => Promise<any> | any) | (() => Promise<any> | any), options?: WaitOptions & {
112
- message?: string;
113
- }): Promise<any>;
114
863
  waitForVisible(selector: string | By, opts?: {
115
864
  timeout?: number;
116
865
  }): Promise<void>;
@@ -123,6 +872,20 @@ export declare class Browser {
123
872
  waitForDetached(selector: string | By, opts?: {
124
873
  timeout?: number;
125
874
  }): Promise<void>;
875
+ /**
876
+ * Wait for an element to reach the given state.
877
+ *
878
+ * Canonical wait API; the four `waitForVisible/Hidden/Attached/Detached`
879
+ * methods are kept as one-line shortcuts.
880
+ *
881
+ * @example
882
+ * await browser.waitFor('#spinner', { state: 'hidden' });
883
+ * await browser.waitFor('#toast', { state: 'visible', timeout: 2000 });
884
+ */
885
+ waitFor(selector: string | By, opts: {
886
+ state: 'visible' | 'hidden' | 'attached' | 'detached';
887
+ timeout?: number;
888
+ }): Promise<void>;
126
889
  actions(): ActionsBuilder;
127
890
  pause(ms: number): Promise<void>;
128
891
  }