@wdio/selenium-devtools 0.0.1

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 (56) hide show
  1. package/README.md +411 -0
  2. package/dist/assertPatcher.d.ts +11 -0
  3. package/dist/assertPatcher.js +123 -0
  4. package/dist/assertPatcher.js.map +1 -0
  5. package/dist/bidi.d.ts +6 -0
  6. package/dist/bidi.js +222 -0
  7. package/dist/bidi.js.map +1 -0
  8. package/dist/constants.d.ts +75 -0
  9. package/dist/constants.js +146 -0
  10. package/dist/constants.js.map +1 -0
  11. package/dist/driverPatcher.d.ts +4 -0
  12. package/dist/driverPatcher.js +256 -0
  13. package/dist/driverPatcher.js.map +1 -0
  14. package/dist/helpers/detachedBackend.d.ts +7 -0
  15. package/dist/helpers/detachedBackend.js +34 -0
  16. package/dist/helpers/detachedBackend.js.map +1 -0
  17. package/dist/helpers/runtime.d.ts +3 -0
  18. package/dist/helpers/runtime.js +47 -0
  19. package/dist/helpers/runtime.js.map +1 -0
  20. package/dist/helpers/suiteManager.d.ts +19 -0
  21. package/dist/helpers/suiteManager.js +131 -0
  22. package/dist/helpers/suiteManager.js.map +1 -0
  23. package/dist/helpers/testManager.d.ts +47 -0
  24. package/dist/helpers/testManager.js +158 -0
  25. package/dist/helpers/testManager.js.map +1 -0
  26. package/dist/helpers/utils.d.ts +26 -0
  27. package/dist/helpers/utils.js +187 -0
  28. package/dist/helpers/utils.js.map +1 -0
  29. package/dist/helpers/videoEncoder.d.ts +2 -0
  30. package/dist/helpers/videoEncoder.js +89 -0
  31. package/dist/helpers/videoEncoder.js.map +1 -0
  32. package/dist/index.d.ts +15 -0
  33. package/dist/index.js +801 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/reporter.d.ts +18 -0
  36. package/dist/reporter.js +72 -0
  37. package/dist/reporter.js.map +1 -0
  38. package/dist/rerunManager.d.ts +8 -0
  39. package/dist/rerunManager.js +78 -0
  40. package/dist/rerunManager.js.map +1 -0
  41. package/dist/runnerHooks.d.ts +6 -0
  42. package/dist/runnerHooks.js +594 -0
  43. package/dist/runnerHooks.js.map +1 -0
  44. package/dist/screencast.d.ts +11 -0
  45. package/dist/screencast.js +179 -0
  46. package/dist/screencast.js.map +1 -0
  47. package/dist/session.d.ts +48 -0
  48. package/dist/session.js +480 -0
  49. package/dist/session.js.map +1 -0
  50. package/dist/setupConsole.d.ts +1 -0
  51. package/dist/setupConsole.js +13 -0
  52. package/dist/setupConsole.js.map +1 -0
  53. package/dist/types.d.ts +235 -0
  54. package/dist/types.js +5 -0
  55. package/dist/types.js.map +1 -0
  56. package/package.json +68 -0
package/README.md ADDED
@@ -0,0 +1,411 @@
1
+ # @wdio/selenium-devtools
2
+
3
+ > Selenium WebDriver adapter for [WebdriverIO DevTools](../../README.md) — runner-agnostic visual debugging UI for any `selenium-webdriver` test, regardless of the test runner.
4
+
5
+ ```bash
6
+ npm install @wdio/selenium-devtools
7
+ ```
8
+
9
+ Works with **Mocha**, **Jest**, **Cucumber**, or plain `node script.js` — the plugin auto-detects the runner and wires test boundaries accordingly.
10
+
11
+ ---
12
+
13
+ ## Quick start (3 steps)
14
+
15
+ **1. Install the package** in your Selenium project:
16
+
17
+ ```bash
18
+ npm install @wdio/selenium-devtools
19
+ ```
20
+
21
+ **2. Import it at the top of your test file**, BEFORE `selenium-webdriver`. The import has a side effect that hooks into Selenium, so the order matters:
22
+
23
+ ```javascript
24
+ import '@wdio/selenium-devtools' // <-- must be first
25
+ import { Builder, By } from 'selenium-webdriver'
26
+ ```
27
+
28
+ **3. Run your tests as you normally do** — `mocha`, `jest`, `npm test`, whatever you use today. A new Chrome window opens automatically with the DevTools UI showing your test's commands, screenshots, console logs, and network activity in real time.
29
+
30
+ That's it. No other code changes required for Mocha / Jest / Cucumber.
31
+
32
+ ---
33
+
34
+ ## Setup per runner
35
+
36
+ Each block below is a **complete, copy-paste-ready example** including the `DevTools.configure(...)` call. Pick the runner you use, drop the snippet into your project, and run it. These mirror the working examples in [`example/`](./example).
37
+
38
+ ### Mocha
39
+
40
+ ```javascript
41
+ // tests/example.test.js
42
+ import { strict as assert } from 'node:assert'
43
+ import { Builder, By, until } from 'selenium-webdriver'
44
+ import { DevTools } from '@wdio/selenium-devtools'
45
+
46
+ DevTools.configure({
47
+ screencast: { enabled: true, quality: 70, maxWidth: 1280, maxHeight: 720 },
48
+ headless: true
49
+ })
50
+
51
+ describe('smoke test', function () {
52
+ let driver
53
+
54
+ before(async function () {
55
+ driver = await new Builder().forBrowser('chrome').build()
56
+ })
57
+
58
+ after(async function () {
59
+ if (driver) {
60
+ await driver.quit()
61
+ }
62
+ })
63
+
64
+ it('loads example.com and reads the heading', async function () {
65
+ await driver.get('https://example.com')
66
+ const heading = await driver.wait(until.elementLocated(By.css('h1')), 10000)
67
+ assert.equal(await heading.getText(), 'Example Domain')
68
+ })
69
+ })
70
+ ```
71
+
72
+ Run it:
73
+
74
+ ```bash
75
+ mocha --timeout 60000 tests/example.test.js
76
+ ```
77
+
78
+ > Alternative: skip the per-file import and use `mocha --require @wdio/selenium-devtools` to load the plugin once for the whole run. You'll still need a separate one-time `DevTools.configure(...)` call somewhere if you want non-default options.
79
+
80
+ ### Jest
81
+
82
+ ```javascript
83
+ // test/example.js
84
+ import { DevTools } from '@wdio/selenium-devtools'
85
+ import { Builder, By, until } from 'selenium-webdriver'
86
+
87
+ DevTools.configure({
88
+ screencast: { enabled: true, quality: 70, maxWidth: 1280, maxHeight: 720 },
89
+ headless: true
90
+ })
91
+
92
+ describe('login flow', () => {
93
+ let driver
94
+
95
+ beforeEach(async () => {
96
+ driver = await new Builder().forBrowser('chrome').build()
97
+ }, 60000)
98
+
99
+ afterEach(async () => {
100
+ if (driver) {
101
+ await driver.quit()
102
+ }
103
+ })
104
+
105
+ test('logs in with valid credentials', async () => {
106
+ await driver.get('https://the-internet.herokuapp.com/login')
107
+ await driver.findElement(By.id('username')).sendKeys('tomsmith')
108
+ await driver.findElement(By.id('password')).sendKeys('SuperSecretPassword!')
109
+ await driver.findElement(By.css('button[type="submit"]')).click()
110
+
111
+ await driver.wait(until.urlContains('/secure'), 10000)
112
+ const flash = await driver.findElement(By.id('flash'))
113
+ expect(await flash.getText()).toMatch(/You logged into a secure area/i)
114
+ }, 60000)
115
+ })
116
+ ```
117
+
118
+ `jest.config.json`:
119
+
120
+ ```json
121
+ {
122
+ "testEnvironment": "node",
123
+ "testMatch": ["<rootDir>/test/example.js"],
124
+ "testTimeout": 60000,
125
+ "transform": {}
126
+ }
127
+ ```
128
+
129
+ Run it (ESM needs the experimental flag):
130
+
131
+ ```bash
132
+ NODE_OPTIONS=--experimental-vm-modules jest --config jest.config.json
133
+ ```
134
+
135
+ ### Cucumber
136
+
137
+ Cucumber's split layout means three small files — one to configure the plugin, one for World/hooks, and one for step definitions. They mirror [`example/cucumber-test/`](./example/cucumber-test).
138
+
139
+ `features/support/setup.js` — load the plugin and configure once:
140
+
141
+ ```javascript
142
+ import { DevTools } from '@wdio/selenium-devtools'
143
+
144
+ DevTools.configure({
145
+ screencast: { enabled: true, quality: 70, maxWidth: 1280, maxHeight: 720 },
146
+ headless: true
147
+ })
148
+ ```
149
+
150
+ `features/support/world.js` — driver lifecycle (Before / After):
151
+
152
+ ```javascript
153
+ import {
154
+ setWorldConstructor,
155
+ World,
156
+ Before,
157
+ After,
158
+ setDefaultTimeout
159
+ } from '@cucumber/cucumber'
160
+ import { Builder } from 'selenium-webdriver'
161
+
162
+ setDefaultTimeout(60000)
163
+
164
+ class CustomWorld extends World {
165
+ constructor (options) {
166
+ super(options)
167
+ this.driver = null
168
+ }
169
+ }
170
+
171
+ setWorldConstructor(CustomWorld)
172
+
173
+ Before(async function () {
174
+ this.driver = await new Builder().forBrowser('chrome').build()
175
+ })
176
+
177
+ After(async function () {
178
+ if (this.driver) {
179
+ await this.driver.quit()
180
+ this.driver = null
181
+ }
182
+ })
183
+ ```
184
+
185
+ `cucumber.json` — wire the setup file in first so the plugin patches Selenium before any step runs:
186
+
187
+ ```json
188
+ {
189
+ "default": {
190
+ "import": [
191
+ "features/support/setup.js",
192
+ "features/support/world.js",
193
+ "features/support/steps.js"
194
+ ],
195
+ "paths": ["features/*.feature"],
196
+ "format": ["progress"]
197
+ }
198
+ }
199
+ ```
200
+
201
+ Run it:
202
+
203
+ ```bash
204
+ cucumber-js --config cucumber.json
205
+ ```
206
+
207
+ ### Plain Node script (no test runner)
208
+
209
+ If you run `node tests/google.test.js` directly — no Mocha, no Jest — there's no runner for the plugin to auto-hook. You get a single "Selenium Session" row in the dashboard by default. To get a named test boundary instead, call `DevTools.startTest` / `endTest` around your work:
210
+
211
+ ```javascript
212
+ // tests/google.test.js
213
+ import { DevTools } from '@wdio/selenium-devtools'
214
+ import { Builder, By, until, Key } from 'selenium-webdriver'
215
+
216
+ DevTools.configure({
217
+ screencast: { enabled: true, quality: 70, maxWidth: 1280, maxHeight: 720 },
218
+ headless: false
219
+ })
220
+
221
+ async function run () {
222
+ DevTools.startTest('search Google for Selenium') // optional — names the test row
223
+
224
+ const driver = await new Builder().forBrowser('chrome').build()
225
+ try {
226
+ await driver.get('https://www.google.com')
227
+ const searchBox = await driver.findElement(By.name('q'))
228
+ await searchBox.sendKeys('Selenium WebDriver JavaScript', Key.ENTER)
229
+ await driver.wait(until.titleContains('Selenium'), 10000)
230
+
231
+ DevTools.endTest('passed')
232
+ } catch (err) {
233
+ DevTools.endTest('failed')
234
+ throw err
235
+ } finally {
236
+ await driver.quit()
237
+ }
238
+ }
239
+
240
+ run()
241
+ ```
242
+
243
+ Run it:
244
+
245
+ ```bash
246
+ node tests/google.test.js
247
+ ```
248
+
249
+ > Only use `startTest` / `endTest` for plain Node scripts. Under Mocha / Jest / Cucumber the plugin already knows when each test starts and ends — calling these manually would create duplicate rows in the dashboard.
250
+
251
+ ---
252
+
253
+ ## Configuration options explained
254
+
255
+ The runner snippets above use a typical config:
256
+
257
+ ```javascript
258
+ DevTools.configure({
259
+ screencast: { enabled: true, quality: 70, maxWidth: 1280, maxHeight: 720 },
260
+ headless: true
261
+ })
262
+ ```
263
+
264
+ Here's what every option does, in plain language. **All are optional** — the plugin runs fine with `DevTools.configure({})` or no configure call at all.
265
+
266
+ #### `screencast` — record a video of the browser
267
+ **Default:** off. Set `{ enabled: true }` to record a `.webm` video for every browser session. Watch it back in the "Screencast" tab in the dashboard.
268
+
269
+ ```javascript
270
+ DevTools.configure({
271
+ screencast: { enabled: true, quality: 70 }
272
+ })
273
+ ```
274
+
275
+ Detailed sub-options: `quality` (0–100 JPEG quality, default 70), `maxWidth`/`maxHeight` (frame size, default 1280×720), `captureFormat` (`'jpeg'` or `'png'`), `pollIntervalMs` (used for non-Chrome browsers; default 200ms).
276
+
277
+ Uses Chrome DevTools Protocol push mode where available; falls back to screenshot polling for Firefox / Safari with no config change.
278
+
279
+ #### `headless` — hide the test browser window
280
+ **Default:** `false` (the test browser is visible). Set to `true` to run the **test** browser without a window — useful for CI servers or when the popping window is annoying. The dashboard window is unaffected and still opens.
281
+
282
+ ```javascript
283
+ DevTools.configure({ headless: true })
284
+ ```
285
+
286
+ > Caveat: this injects `--headless=old` into Chrome options. `--headless=new` (Chrome's newer headless mode) is intentionally **not** used because it produces all-black frames in the video recording.
287
+
288
+ #### `openUi` — should the dashboard auto-open?
289
+ **Default:** `true`. Set to `false` if you don't want the plugin to launch a Chrome window for the dashboard — handy for CI where there's no display. The backend still runs at `http://localhost:3000`; you can open it manually if you want.
290
+
291
+ ```javascript
292
+ DevTools.configure({ openUi: false })
293
+ ```
294
+
295
+ #### `port` and `hostname` — change where the dashboard runs
296
+ **Defaults:** port `3000`, hostname `'localhost'`. If port 3000 is already taken, the plugin automatically tries 3001, 3002, etc., so you usually don't need to touch these.
297
+
298
+ ```javascript
299
+ DevTools.configure({ port: 4000, hostname: '0.0.0.0' })
300
+ ```
301
+
302
+ #### `captureScreenshots` — turn off per-command screenshots
303
+ **Default:** `true` (a screenshot is taken after every Selenium command). Set to `false` for faster tests on long suites where you don't need visual debugging.
304
+
305
+ ```javascript
306
+ DevTools.configure({ captureScreenshots: false })
307
+ ```
308
+
309
+ #### `rerunCommand` — customize the dashboard's "rerun this test" button
310
+ **Default:** auto-detected from your `npm`/`pnpm`/`yarn` script + the runner's filter flag (e.g. Mocha's `--grep`, Jest's `--testNamePattern`). Override if your invocation needs something special. Use `{{testName}}` where the test name should be substituted.
311
+
312
+ ```javascript
313
+ DevTools.configure({ rerunCommand: 'npm test -- --grep "{{testName}}"' })
314
+ ```
315
+
316
+ ---
317
+
318
+ ## Common recipes
319
+
320
+ | I want to… | Configuration |
321
+ |---|---|
322
+ | Record a video of every test | `DevTools.configure({ screencast: { enabled: true } })` |
323
+ | Run in CI without opening the dashboard window | `DevTools.configure({ openUi: false })` |
324
+ | Hide the test browser (CI / headless) | `DevTools.configure({ headless: true })` |
325
+ | Faster tests; skip screenshots | `DevTools.configure({ captureScreenshots: false })` |
326
+ | Move the dashboard off port 3000 | `DevTools.configure({ port: 4000 })` |
327
+ | All of the above for CI | `DevTools.configure({ headless: true, openUi: false, screencast: { enabled: true } })` |
328
+
329
+ ---
330
+
331
+ ## Reference — all options
332
+
333
+ | Option | Type | Default | Description |
334
+ |--------|------|---------|-------------|
335
+ | `port` | `number` | `3000` | Port for the DevTools backend server. Auto-incremented if already in use. |
336
+ | `hostname` | `string` | `'localhost'` | Hostname the backend server binds to. |
337
+ | `openUi` | `boolean` | `true` | Auto-open the DevTools UI in a new Chrome window. Set `false` for CI. |
338
+ | `captureScreenshots` | `boolean` | `true` | Capture a screenshot after every WebDriver command. |
339
+ | `headless` | `boolean` | `false` | Run the **test** browser headless (injects `--headless=old`). The DevTools UI window is unaffected. |
340
+ | `screencast` | `ScreencastOptions` | `{ enabled: false }` | Per-session `.webm` video recording. See sub-options below. |
341
+ | `rerunCommand` | `string` | auto | Command template for per-test rerun. `{{testName}}` is substituted. Auto-derived from runner argv if omitted. |
342
+
343
+ `ScreencastOptions`:
344
+
345
+ | Option | Type | Default | Description |
346
+ |--------|------|---------|-------------|
347
+ | `enabled` | `boolean` | `false` | Enable per-session recording. |
348
+ | `captureFormat` | `'jpeg' \| 'png'` | `'jpeg'` | Frame format. Chromium-only. |
349
+ | `quality` | `number` | `70` | JPEG quality 0–100. Chromium-only. |
350
+ | `maxWidth` | `number` | `1280` | Max frame width pushed over CDP. Chromium-only. |
351
+ | `maxHeight` | `number` | `720` | Max frame height pushed over CDP. Chromium-only. |
352
+ | `pollIntervalMs` | `number` | `200` | Fallback `takeScreenshot` poll interval for non-Chromium browsers. |
353
+
354
+ ---
355
+
356
+ ## Public API
357
+
358
+ ```javascript
359
+ import { DevTools } from '@wdio/selenium-devtools'
360
+
361
+ DevTools.configure(opts) // set runtime options (see above)
362
+ DevTools.startTest(name, meta?) // mark a named test boundary (plain Node scripts only)
363
+ DevTools.endTest('passed'|'failed'|'skipped'|'pending')
364
+ ```
365
+
366
+ ---
367
+
368
+ ## Examples
369
+
370
+ Working smoke tests are included for each supported runner:
371
+
372
+ | Directory | Runner | Command |
373
+ |-----------|--------|---------|
374
+ | [`example/mocha-test/`](./example/mocha-test) | Mocha | `pnpm example:mocha` |
375
+ | [`example/jest-test/`](./example/jest-test) | Jest | `pnpm example:jest` |
376
+ | [`example/cucumber-test/`](./example/cucumber-test) | Cucumber | `pnpm example:cucumber` |
377
+
378
+ Build the package first:
379
+
380
+ ```bash
381
+ # From repo root
382
+ pnpm build --filter @wdio/selenium-devtools
383
+ cd packages/selenium-devtools
384
+ pnpm example:mocha
385
+ ```
386
+
387
+ ---
388
+
389
+ ## How it works
390
+
391
+ The plugin patches `selenium-webdriver`'s `Builder`, `WebDriver`, and `WebElement` prototypes at import time:
392
+
393
+ - **`Builder.build()`** → after construction, the driver instance is registered with the session capturer and the DevTools backend is started in a detached child process.
394
+ - **Every public `WebDriver` / `WebElement` method** → wrapped with command capture (args + result + screenshot + call source).
395
+ - **`WebDriver.quit()`** → awaited cleanup hook flushes screencast encoding, WebSocket buffer, and final metadata before the original quit runs.
396
+
397
+ When BiDi is available (Chrome ≥114), console logs, JavaScript exceptions, and network events stream directly via the Selenium BiDi handlers. Otherwise the plugin falls back to an injected browser-side collector script.
398
+
399
+ ---
400
+
401
+ ## Limitations
402
+
403
+ | Limitation | Detail |
404
+ |-----------|--------|
405
+ | Cucumber leaf-step rerun | Cucumber's `--name` filter targets scenarios, not individual Gherkin steps. The dashboard's per-step rerun is disabled under Cucumber. |
406
+ | Headless mode caveat | `headless: true` injects `--headless=old`; `--headless=new` produces all-black CDP frames. |
407
+ | Initial viewport | The dashboard's snapshot iframe falls back to 1280×800 until the first navigation completes and the browser-side collector reports the real viewport. |
408
+
409
+ ## :page_facing_up: License
410
+
411
+ [MIT](/LICENSE)
@@ -0,0 +1,11 @@
1
+ import type { CapturedCommand } from './types.js';
2
+ /**
3
+ * Patch `node:assert` so each tracked method emits a `CapturedCommand` to
4
+ * the supplied hook. Idempotent — calling twice doesn't double-wrap.
5
+ *
6
+ * Note: we patch BOTH the function-form (`assert(...)`) and the namespace
7
+ * methods (`assert.equal(...)`). User code that imported the methods BEFORE
8
+ * this patcher loaded will already have stale references — to be safe,
9
+ * the plugin's main entry imports node:assert before the user's test files.
10
+ */
11
+ export declare function patchNodeAssert(onCommand: (cmd: CapturedCommand) => void): boolean;
@@ -0,0 +1,123 @@
1
+ import { createRequire } from 'node:module';
2
+ import logger from '@wdio/logger';
3
+ import { ASSERT_PATCHED_SYMBOL, TRACKED_ASSERT_METHODS } from './constants.js';
4
+ import { getCallSourceFromStack } from './helpers/utils.js';
5
+ const log = logger('@wdio/selenium-devtools:assertPatcher');
6
+ const require = createRequire(import.meta.url);
7
+ function safeSerialize(value) {
8
+ if (value === null || value === undefined) {
9
+ return value;
10
+ }
11
+ if (value instanceof RegExp) {
12
+ return value.toString();
13
+ }
14
+ if (typeof value === 'function') {
15
+ return '[Function]';
16
+ }
17
+ if (typeof value === 'object') {
18
+ try {
19
+ return JSON.parse(JSON.stringify(value));
20
+ }
21
+ catch {
22
+ return String(value);
23
+ }
24
+ }
25
+ return value;
26
+ }
27
+ /**
28
+ * Patch `node:assert` so each tracked method emits a `CapturedCommand` to
29
+ * the supplied hook. Idempotent — calling twice doesn't double-wrap.
30
+ *
31
+ * Note: we patch BOTH the function-form (`assert(...)`) and the namespace
32
+ * methods (`assert.equal(...)`). User code that imported the methods BEFORE
33
+ * this patcher loaded will already have stale references — to be safe,
34
+ * the plugin's main entry imports node:assert before the user's test files.
35
+ */
36
+ export function patchNodeAssert(onCommand) {
37
+ let assertModule;
38
+ try {
39
+ assertModule = require('node:assert');
40
+ }
41
+ catch {
42
+ log.warn('node:assert not available — skipping assertion capture');
43
+ return false;
44
+ }
45
+ if (assertModule[ASSERT_PATCHED_SYMBOL]) {
46
+ return true;
47
+ }
48
+ ;
49
+ assertModule[ASSERT_PATCHED_SYMBOL] = true;
50
+ // Wrap each tracked method on `assert` and `assert.strict`. We don't
51
+ // overwrite `assert.strict.equal` separately because Node's strict
52
+ // namespace shares method bodies internally — patching the surface is
53
+ // enough.
54
+ const wrapMethod = (methodName) => {
55
+ const original = assertModule[methodName];
56
+ if (typeof original !== 'function') {
57
+ return;
58
+ }
59
+ ;
60
+ assertModule[methodName] = function patchedAssert(...args) {
61
+ const callInfo = getCallSourceFromStack();
62
+ const startedAt = Date.now();
63
+ const sanitizedArgs = args.map(safeSerialize);
64
+ try {
65
+ const result = original.apply(this, args);
66
+ // Async assert methods (rejects/doesNotReject) return a Promise.
67
+ if (result && typeof result.then === 'function') {
68
+ return result.then((v) => {
69
+ onCommand({
70
+ command: `assert.${methodName}`,
71
+ args: sanitizedArgs,
72
+ result: 'passed',
73
+ error: undefined,
74
+ callSource: callInfo.callSource,
75
+ timestamp: startedAt,
76
+ fromElement: false
77
+ });
78
+ return v;
79
+ }, (err) => {
80
+ onCommand({
81
+ command: `assert.${methodName}`,
82
+ args: sanitizedArgs,
83
+ result: undefined,
84
+ error: err instanceof Error ? err : new Error(String(err)),
85
+ callSource: callInfo.callSource,
86
+ timestamp: startedAt,
87
+ fromElement: false
88
+ });
89
+ throw err;
90
+ });
91
+ }
92
+ onCommand({
93
+ command: `assert.${methodName}`,
94
+ args: sanitizedArgs,
95
+ result: 'passed',
96
+ error: undefined,
97
+ callSource: callInfo.callSource,
98
+ timestamp: startedAt,
99
+ fromElement: false
100
+ });
101
+ return result;
102
+ }
103
+ catch (err) {
104
+ onCommand({
105
+ command: `assert.${methodName}`,
106
+ args: sanitizedArgs,
107
+ result: undefined,
108
+ error: err instanceof Error ? err : new Error(String(err)),
109
+ callSource: callInfo.callSource,
110
+ timestamp: startedAt,
111
+ fromElement: false
112
+ });
113
+ throw err;
114
+ }
115
+ };
116
+ };
117
+ for (const m of TRACKED_ASSERT_METHODS) {
118
+ wrapMethod(m);
119
+ }
120
+ log.info(`Patched ${TRACKED_ASSERT_METHODS.length} node:assert method(s)`);
121
+ return true;
122
+ }
123
+ //# sourceMappingURL=assertPatcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assertPatcher.js","sourceRoot":"","sources":["../src/assertPatcher.ts"],"names":[],"mappings":"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"}
package/dist/bidi.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import type { BidiHandlerSinks } from './types.js';
2
+ import type { SessionCapturer } from './session.js';
3
+ export declare function ensureBidiCapability(builder: any): void;
4
+ export declare function ensureHeadlessChrome(builder: any): void;
5
+ export declare function attachBidiHandlers(driver: any, sinks: BidiHandlerSinks): Promise<boolean>;
6
+ export declare function buildBidiSinks(capturer: SessionCapturer): BidiHandlerSinks;