@wdio/nightwatch-devtools 1.8.2 → 2.0.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/README.md CHANGED
@@ -16,6 +16,8 @@ npm install @wdio/nightwatch-devtools
16
16
  npm install @wdio/nightwatch-devtools
17
17
  ```
18
18
 
19
+ Requires Nightwatch 3 or later. For Cucumber, `@cucumber/cucumber` must be installed in your project; the plugin uses your copy rather than bundling one.
20
+
19
21
  **2. Wire the plugin into your Nightwatch config** via the `globals` field:
20
22
 
21
23
  ```javascript
@@ -132,7 +134,7 @@ globals: nightwatchDevtools({
132
134
 
133
135
  ## Live Mode
134
136
 
135
- In the default `mode: 'live'`, the plugin launches the DevTools dashboard in a new browser window when your tests run, streaming each test's commands, screenshots, console logs, and network activity in real time. See the [dashboard docs](https://webdriver.io/docs/devtools/dashboard) for a walkthrough of the UI.
137
+ In the default `mode: 'live'`, the plugin launches the DevTools dashboard in a new browser window when your tests run, streaming each test's commands, screenshots, console logs, and network activity in real time. The run stays open after your tests finish so you can review the results: close the DevTools window to end it, or press Ctrl+C to exit and leave the window open. See the [dashboard docs](https://webdriver.io/docs/devtools/dashboard) for a walkthrough of the UI.
136
138
 
137
139
  ### Preserve & Rerun (Compare)
138
140
 
@@ -151,7 +153,7 @@ globals: nightwatchDevtools({
151
153
  })
152
154
  ```
153
155
 
154
- Nightwatch emits the **same normalized trace** as the WebdriverIO and Selenium adapters (all three share `@wdio/devtools-core`), so the archive format and the `show-trace` player are identical no matter which adapter produced it. A Nightwatch trace carries the full per-action capture — a screenshot, the depth-indented accessibility-tree snapshot, the interactable-element list, and the Markdown transcript — so it opens in the player with DOM/snapshot time-travel, the **A11y** and **Transcript** tabs, an **Errors** tab surfacing expect/step failures with jump-to-source, the pick-locator element overlay, and (for Cucumber) **Feature → Scenario → Step** nesting.
156
+ Nightwatch emits the **same normalized trace** as the WebdriverIO and Selenium adapters (all three share `@wdio/devtools-trace`), so the archive format and the `show-trace` player are identical no matter which adapter produced it. A Nightwatch trace carries the full per-action capture — a screenshot, the depth-indented accessibility-tree snapshot, the interactable-element list, and the Markdown transcript — so it opens in the player with DOM/snapshot time-travel, the **A11y** and **Transcript** tabs, an **Errors** tab surfacing expect/step failures with jump-to-source, the pick-locator element overlay, and (for Cucumber) **Feature → Scenario** nesting. Steps are tracked in the live dashboard but not yet nested as their own groups in the trace. Copied locators prefer plain CSS, which Nightwatch reads by default, and fall back to XPath (captioned `useXpath()` / `locateStrategy: 'xpath'`) only when no unique CSS selector exists.
155
157
 
156
158
  Open a trace with the `show-trace` bin, shipped with this package (no extra dependency):
157
159
 
@@ -191,15 +193,17 @@ desiredCapabilities: {
191
193
  }
192
194
  ```
193
195
 
194
- When attached, the per-command Chrome performance-log network capture path is gated off so requests don't appear twice in the dashboard. If `webSocketUrl` is missing or the chromedriver version doesn't expose BiDi, the attach silently fails and the perf-log fallback continues to work.
196
+ When attached, the per-command Chrome performance-log network capture path is gated off so requests don't appear twice in the dashboard. If `webSocketUrl` is missing or the chromedriver version doesn't expose BiDi, the attach logs a warning and the perf-log fallback continues to work. The injected collector lists every request as well, from two sources of the page's own: a `fetch` / `XMLHttpRequest` call goes through its patch and carries method, headers, status and body, and every other load comes from the page's Resource Timing buffer with its timing and size. On Chrome the performance log then upgrades those Resource Timing rows in place with headers and status; on Safari, or any session Appium serves, they stay as the page saw them, without headers or bodies, and on Safari without a status.
197
+
198
+ **Set `webSocketUrl: true` even without `bidi: true`.** With it, the plugin registers its page collector at document start on every page, so DOM time-travel stays accurate across navigations. Without it, the collector is injected after each navigation instead, which can miss the first moments of a new page.
195
199
 
196
200
  ---
197
201
 
198
202
  ## Screencast
199
203
 
200
- Record a continuous `.webm` video of the browser session. The recording starts on the first session the plugin sees and is finalized in Nightwatch's `after()` hook, writing `nightwatch-video-<sessionId>.webm` to the directory of the test file that just ran. Falls back to the directory containing `nightwatch.conf.*` if the test file path isn't known, and to `process.cwd()` as a last resort. Directories under `node_modules/` are skipped.
204
+ Record a continuous `.webm` video of the browser session, live mode only (in trace mode use `filmstrip` / `video`). Each WebDriver session gets its own recording: a mid-run `browser.end()`, or the per-scenario browser quit in Cucumber, finalizes the current video and starts a new one. The last recording is finalized in Nightwatch's `after()` hook. Each is written as `nightwatch-video-<sessionId>.webm` to a `test-results/` folder next to the test file that just ran, falling back to the directory containing `nightwatch.conf.*`, then `process.cwd()`. Directories under `node_modules/` are skipped, and an unwritable directory falls back to the OS temp directory.
201
205
 
202
- **Polling mode only.** Nightwatch doesn't expose a stable CDP escape hatch the way WebdriverIO (`browser.getPuppeteer()`) and Selenium (`driver.createCDPConnection`) do, so the screencast captures frames by calling `browser.takeScreenshot()` at a fixed interval. This works on every browser Nightwatch supports.
206
+ **Polling mode only.** Nightwatch doesn't expose a stable CDP escape hatch the way WebdriverIO (`browser.getPuppeteer()`) and Selenium (`driver.createCDPConnection`) do, so the screencast captures frames by requesting a WebDriver screenshot at a fixed interval, straight over the WebDriver HTTP transport so it never waits behind your test's queued commands. This works on every browser Nightwatch supports.
203
207
 
204
208
  ### Quick start
205
209
 
@@ -217,6 +221,7 @@ globals: nightwatchDevtools({
217
221
  | `enabled` | `boolean` | `false` | Master switch. |
218
222
  | `pollIntervalMs` | `number` | `200` | Screenshot interval (ms). Lower = smoother video, more WebDriver round-trips. 200 ms ≈ 5 fps. |
219
223
  | `captureFormat` | `'jpeg' \| 'png'` | `'jpeg'` | Frame format. WebDriver screenshots are always PNG, so this only affects the encoded output. |
224
+ | `maxBufferFrames` | `number` | `2000` | Cap on frames held in memory; past it the buffer is thinned in place, keeping the first and last frames. |
220
225
  | `maxWidth` / `maxHeight` / `quality` | — | — | CDP-only options, ignored in polling mode. Listed for shape compatibility with the WDIO/Selenium adapters. |
221
226
 
222
227
  ### Prerequisites
@@ -251,23 +256,72 @@ The encoded video is sent to the DevTools dashboard via the `screencast` WS scop
251
256
 
252
257
  ---
253
258
 
254
- ## Examples
259
+ ## Mobile (Appium)
255
260
 
256
- Working examples are included in this package:
261
+ Appium sessions are captured in live and trace mode. Declare Appium under `selenium` with `use_appium: true`, `start_process: false` and `default_path_prefix: ''`. For a native app set `browserName: null`; otherwise Nightwatch fills in Firefox and the plugin treats the session as having a web page. Leave `bidi` off, since Appium serves no BiDi.
257
262
 
258
- | Directory | Runner | Command |
259
- |-----------|--------|---------|
260
- | [`example/`](./example) | Nightwatch mocha-style | `pnpm example` |
263
+ ```javascript
264
+ // nightwatch.mobile.conf.cjs
265
+ const nightwatchDevtools = require('@wdio/nightwatch-devtools').default
266
+
267
+ module.exports = {
268
+ selenium: {
269
+ start_process: false,
270
+ use_appium: true,
271
+ host: '127.0.0.1',
272
+ port: 4723,
273
+ default_path_prefix: ''
274
+ },
275
+ test_settings: {
276
+ default: {
277
+ desiredCapabilities: {
278
+ browserName: null,
279
+ platformName: 'Android',
280
+ 'appium:automationName': 'UiAutomator2'
281
+ },
282
+ globals: nightwatchDevtools({ mode: 'trace', bidi: false })
283
+ }
284
+ }
285
+ }
286
+ ```
261
287
 
262
- Build the package first:
288
+ Native sessions record commands, screenshots and element data from the platform's XML tree, with no DOM. A hybrid app's webview is captured as if it were still native. From the root of a built checkout of this repo (`pnpm install && pnpm build`), try it with `pnpm demo:nightwatch:mobile`; prerequisites are in [`examples/MOBILE.md`](../../examples/MOBILE.md), and the full reference is the [Mobile Testing page](https://webdriver.io/docs/devtools/mobile).
263
289
 
264
- ```bash
265
- # From repo root
266
- pnpm build --filter @wdio/nightwatch-devtools
267
- cd packages/nightwatch-devtools
268
- pnpm example
290
+ ### Native app network
291
+
292
+ A native app has no document and Appium serves no BiDi network events, so by default the Network tab of a native session is empty. The plugin can read the app's traffic from the [appium-interceptor](https://github.com/AppiumTestDistribution/appium-interceptor-plugin) plugin instead, which routes the device through a proxy of its own:
293
+
294
+ ```sh
295
+ appium plugin install --source=npm appium-interceptor
296
+ appium --use-plugins=appium-interceptor
297
+ ```
298
+
299
+ Then ask for the proxy in `desiredCapabilities`:
300
+
301
+ ```js
302
+ desiredCapabilities: {
303
+ browserName: null,
304
+ platformName: 'Android',
305
+ 'appium:automationName': 'UiAutomator2',
306
+ 'appium:app': '/path/to/app.apk',
307
+ 'appium:intercept': true
308
+ }
269
309
  ```
270
310
 
311
+ The plugin starts a listener for each session, including one that replaces it mid-run, and reads what the proxy recorded at most every 2 seconds between commands (each read returns the whole history), and always before each test's trace slice is written, in both live and trace mode. Each request shows its method, URL, status, headers and bodies. The plugin records no timing, so a request is placed at the moment it was first read and shows no duration. Without the plugin a warning is logged and the session runs as usual. This applies to native sessions only; a mobile browser session already captures its network through the page.
312
+
313
+ The plugin supports Android. For HTTPS traffic the app has to trust the certificate the plugin installs on the device, which a release build usually does not (see Android's network security configuration).
314
+
315
+ ## Examples
316
+
317
+ Working examples live in the repo's top-level [`examples/nightwatch/`](../../examples/nightwatch). Build the workspace once (`pnpm install && pnpm build`), then run from the repo root:
318
+
319
+ | Example | Runner | Command |
320
+ |-----------|--------|---------|
321
+ | `examples/nightwatch/` | Nightwatch BDD `describe/it` (trace mode by default; `DEVTOOLS_MODE=live` for the UI) | `pnpm demo:nightwatch` (`pnpm demo:nightwatch:retry` adds `--retries 1`) |
322
+ | `examples/nightwatch/cucumber/` | Nightwatch + Cucumber | `pnpm --filter @wdio/nightwatch-devtools example:cucumber` |
323
+ | `examples/nightwatch/nightwatch.mobile.conf.cjs` | Appium (Android Clock app; iOS Settings with `DEVTOOLS_MOBILE_PLATFORM=ios`) | `pnpm demo:nightwatch:mobile` |
324
+
271
325
  ---
272
326
 
273
327
  ## Limitations
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  attachBidiHandlers
3
- } from "./chunk-DM4FCAIJ.js";
3
+ } from "./chunk-TUK7ZLQI.js";
4
4
 
5
5
  // src/bidi.ts
6
6
  import logger from "@wdio/logger";
@@ -19,10 +19,12 @@ function buildBidiSinks(capturer) {
19
19
  capturer.sendUpstream("consoleLogs", [entry]);
20
20
  },
21
21
  pushNetworkRequest: (entry) => {
22
+ capturer.bidiNetworkObserved = true;
22
23
  capturer.networkRequests.push(entry);
23
24
  capturer.sendUpstream("networkRequests", [entry]);
24
25
  },
25
26
  replaceNetworkRequest: (id, entry) => {
27
+ capturer.bidiNetworkObserved = true;
26
28
  const idx = capturer.networkRequests.findIndex(
27
29
  (r) => r.id === id
28
30
  );
@@ -39,4 +41,4 @@ export {
39
41
  attachBidiHandlers2 as attachBidiHandlers,
40
42
  buildBidiSinks
41
43
  };
42
- //# sourceMappingURL=bidi-WNZOXX5X.js.map
44
+ //# sourceMappingURL=bidi-AZX3WRGJ.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/bidi.ts"],"sourcesContent":["import logger from '@wdio/logger'\nimport {\n type BidiHandlerSinks,\n attachBidiHandlers as attachBidiHandlersCore\n} from '@wdio/devtools-core'\nimport type { SessionCapturer } from './session.js'\n\nconst log = logger('@wdio/nightwatch-devtools:bidi')\n\n/**\n * Nightwatch wrapper around the core BiDi attach helper. Nightwatch ships\n * selenium-webdriver under the hood (via chromedriver), so the same\n * `selenium-webdriver/bidi` inspectors selenium-devtools uses are available\n * whenever the user has set `webSocketUrl: true` in their capabilities.\n *\n * Opt-in via the plugin's `bidi: true` option. When attached, the per-command\n * Chrome performance-log network capture is gated off to avoid duplicate\n * entries in the dashboard.\n */\nexport async function attachBidiHandlers(\n driver: unknown,\n sinks: BidiHandlerSinks\n): Promise<boolean> {\n return attachBidiHandlersCore(driver, sinks, (level, message) =>\n log[level](message)\n )\n}\n\n/**\n * Build sinks that route BiDi events into the SessionCapturer's local arrays\n * and broadcast them upstream. Mirrors selenium-devtools' `buildBidiSinks` —\n * separate per-adapter because the SessionCapturer concrete types differ.\n */\nexport function buildBidiSinks(capturer: SessionCapturer): BidiHandlerSinks {\n return {\n pushConsoleLog: (entry) => {\n capturer.consoleLogs.push(entry)\n capturer.sendUpstream('consoleLogs', [entry])\n },\n pushNetworkRequest: (entry) => {\n capturer.networkRequests.push(entry)\n capturer.sendUpstream('networkRequests', [entry])\n },\n replaceNetworkRequest: (id, entry) => {\n const idx = capturer.networkRequests.findIndex(\n (r: { id?: string }) => r.id === id\n )\n if (idx !== -1) {\n capturer.networkRequests[idx] = entry\n } else {\n capturer.networkRequests.push(entry)\n }\n capturer.sendUpstream('networkRequests', [entry])\n }\n }\n}\n"],"mappings":";;;;;AAAA,OAAO,YAAY;AAOnB,IAAM,MAAM,OAAO,gCAAgC;AAYnD,eAAsBA,oBACpB,QACA,OACkB;AAClB,SAAO;AAAA,IAAuB;AAAA,IAAQ;AAAA,IAAO,CAAC,OAAO,YACnD,IAAI,KAAK,EAAE,OAAO;AAAA,EACpB;AACF;AAOO,SAAS,eAAe,UAA6C;AAC1E,SAAO;AAAA,IACL,gBAAgB,CAAC,UAAU;AACzB,eAAS,YAAY,KAAK,KAAK;AAC/B,eAAS,aAAa,eAAe,CAAC,KAAK,CAAC;AAAA,IAC9C;AAAA,IACA,oBAAoB,CAAC,UAAU;AAC7B,eAAS,gBAAgB,KAAK,KAAK;AACnC,eAAS,aAAa,mBAAmB,CAAC,KAAK,CAAC;AAAA,IAClD;AAAA,IACA,uBAAuB,CAAC,IAAI,UAAU;AACpC,YAAM,MAAM,SAAS,gBAAgB;AAAA,QACnC,CAAC,MAAuB,EAAE,OAAO;AAAA,MACnC;AACA,UAAI,QAAQ,IAAI;AACd,iBAAS,gBAAgB,GAAG,IAAI;AAAA,MAClC,OAAO;AACL,iBAAS,gBAAgB,KAAK,KAAK;AAAA,MACrC;AACA,eAAS,aAAa,mBAAmB,CAAC,KAAK,CAAC;AAAA,IAClD;AAAA,EACF;AACF;","names":["attachBidiHandlers"]}
1
+ {"version":3,"sources":["../src/bidi.ts"],"sourcesContent":["import logger from '@wdio/logger'\nimport {\n type BidiHandlerSinks,\n attachBidiHandlers as attachBidiHandlersCore\n} from '@wdio/devtools-core'\nimport type { SessionCapturer } from './session.js'\n\nconst log = logger('@wdio/nightwatch-devtools:bidi')\n\n/**\n * Nightwatch wrapper around the core BiDi attach helper. Nightwatch ships\n * selenium-webdriver under the hood (via chromedriver), so the same\n * `selenium-webdriver/bidi` inspectors selenium-devtools uses are available\n * whenever the user has set `webSocketUrl: true` in their capabilities.\n *\n * Opt-in via the plugin's `bidi: true` option. When attached, the per-command\n * Chrome performance-log network capture is gated off to avoid duplicate\n * entries in the dashboard.\n */\nexport async function attachBidiHandlers(\n driver: unknown,\n sinks: BidiHandlerSinks\n): Promise<boolean> {\n return attachBidiHandlersCore(driver, sinks, (level, message) =>\n log[level](message)\n )\n}\n\n/**\n * Build sinks that route BiDi events into the SessionCapturer's local arrays\n * and broadcast them upstream. Mirrors selenium-devtools' `buildBidiSinks` —\n * separate per-adapter because the SessionCapturer concrete types differ.\n */\nexport function buildBidiSinks(capturer: SessionCapturer): BidiHandlerSinks {\n return {\n pushConsoleLog: (entry) => {\n capturer.consoleLogs.push(entry)\n capturer.sendUpstream('consoleLogs', [entry])\n },\n pushNetworkRequest: (entry) => {\n capturer.bidiNetworkObserved = true\n capturer.networkRequests.push(entry)\n capturer.sendUpstream('networkRequests', [entry])\n },\n replaceNetworkRequest: (id, entry) => {\n capturer.bidiNetworkObserved = true\n const idx = capturer.networkRequests.findIndex(\n (r: { id?: string }) => r.id === id\n )\n if (idx !== -1) {\n capturer.networkRequests[idx] = entry\n } else {\n capturer.networkRequests.push(entry)\n }\n capturer.sendUpstream('networkRequests', [entry])\n }\n }\n}\n"],"mappings":";;;;;AAAA,OAAO,YAAY;AAOnB,IAAM,MAAM,OAAO,gCAAgC;AAYnD,eAAsBA,oBACpB,QACA,OACkB;AAClB,SAAO;AAAA,IAAuB;AAAA,IAAQ;AAAA,IAAO,CAAC,OAAO,YACnD,IAAI,KAAK,EAAE,OAAO;AAAA,EACpB;AACF;AAOO,SAAS,eAAe,UAA6C;AAC1E,SAAO;AAAA,IACL,gBAAgB,CAAC,UAAU;AACzB,eAAS,YAAY,KAAK,KAAK;AAC/B,eAAS,aAAa,eAAe,CAAC,KAAK,CAAC;AAAA,IAC9C;AAAA,IACA,oBAAoB,CAAC,UAAU;AAC7B,eAAS,sBAAsB;AAC/B,eAAS,gBAAgB,KAAK,KAAK;AACnC,eAAS,aAAa,mBAAmB,CAAC,KAAK,CAAC;AAAA,IAClD;AAAA,IACA,uBAAuB,CAAC,IAAI,UAAU;AACpC,eAAS,sBAAsB;AAC/B,YAAM,MAAM,SAAS,gBAAgB;AAAA,QACnC,CAAC,MAAuB,EAAE,OAAO;AAAA,MACnC;AACA,UAAI,QAAQ,IAAI;AACd,iBAAS,gBAAgB,GAAG,IAAI;AAAA,MAClC,OAAO;AACL,iBAAS,gBAAgB,KAAK,KAAK;AAAA,MACrC;AACA,eAAS,aAAa,mBAAmB,CAAC,KAAK,CAAC;AAAA,IAClD;AAAA,EACF;AACF;","names":["attachBidiHandlers"]}