From 0edb1bded66cc447afe9e9cf2581e45c2e00492c Mon Sep 17 00:00:00 2001 From: Vishnu Vardhan Date: Tue, 13 Oct 2026 12:52:55 +0530 Subject: [PATCH 1/2] docs: bring the adapter READMEs in line with the code and the website Several README statements had drifted from the code they describe, and the website pages (webdriverio/webdriverio#16071) now state the corrected behaviour, so the two sides would disagree. Correct Nightwatch Cucumber nesting (Feature to Scenario), its per-session screencast and output path, the Selenium import-order claim, the Python README's claim that per-test slicing does not exist, the service's fluent-ffmpeg step and removed trace JSON, which formats show-trace opens, the port default, TestLens, the action allow-list and the elements peer range. Add the Mobile (Appium) sections, the Nightwatch requirements and the webSocketUrl advice the website documents. Co-Authored-By: Claude Opus 5.5 --- CLAUDE.md | 2 +- README.md | 15 +++--- packages/elements/README.md | 5 +- packages/nightwatch-devtools/README.md | 63 ++++++++++++++++++------- packages/selenium-devtools-py/README.md | 25 ++++++++-- packages/selenium-devtools/README.md | 10 +++- packages/service/README.md | 14 ++---- 7 files changed, 91 insertions(+), 43 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 441236ad..ca16c55b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -27,7 +27,7 @@ Run from repo root unless noted. | `pnpm test:coverage` | Run vitest with v8 coverage. The thresholds in `vitest.config.ts` are aspirational, not a gate: that file states CI does not run this and the suite is currently below all four. CI runs `test`/`lint`/`test:ui`. | | `pnpm lint` | Lint all packages in parallel. Includes `eslint-plugin-security` for a subset of CodeQL findings; deeper taint-flow checks surface on the PR's CodeQL scan. | | `pnpm demo:wdio` / `pnpm demo:nightwatch` / `pnpm demo:selenium` | Run the per-framework example projects. Useful for manual verification of UI or runtime changes. **No package pins a browser driver**, deliberately: a pinned `chromedriver` rots against whatever Chrome the developer has, and because pnpm puts the package's `node_modules/.bin` on `PATH`, Selenium Manager finds it there, prefers it over resolving, and on a major mismatch only *warns* before returning it anyway. The session then fails to start. With nothing on `PATH` both adapters resolve a matching driver (Nightwatch's Chrome service builder sets `requiresDriverBinary: false` and passes an unset `server_path` through to the same resolver), and nightwatch declares `chromedriver` an optional peer. | -| `pnpm demo:wdio:mobile` / `:selenium:mobile` / `:nightwatch:mobile` / `:python:mobile` | The same, against Appium. All four build the same capability bag and drive the Clock app that ships with every Android system image — starting a timer, pausing it, clearing it — so a native example needs no `.apk`. `DEVTOOLS_MOBILE_PLATFORM=ios` runs the iOS spec instead — all four adapters — which drives Settings because the simulator ships no Clock, from a separate spec per platform rather than a branch. The simulator is chosen by udid and defaults to whichever is already booted, and an unmatched `IOS_DEVICE_NAME` is refused: naming one that does not exist makes the XCUITest driver create and boot it, every run, rather than fail. That refusal and the booted-simulator preflight are **local** policy — `xcrun simctl` enumerates local simulators and nothing else — so `IOS_UDID` and a non-local `APPIUM_HOST` both bypass them, or a real device and a cloud grid would be refused a run they were correctly configured for. `DEVTOOLS_MOBILE=web` drives the device's own browser on both platforms — Chrome on Android, Safari on iOS, which the XCUITest driver serves without a chromedriver. `examples/MOBILE.md` holds the prerequisites and the `DEVTOOLS_MOBILE` / `APPIUM_APP` switches; `DEVTOOLS_MODE=trace` flips any demo to trace mode. | +| `pnpm demo:wdio:mobile` / `:selenium:mobile` / `:nightwatch:mobile` / `:python:mobile` | The same, against Appium. All four build the same capability bag and drive the Clock app that ships with every Android system image (keying a duration on the timer keypad, never starting a timer), so a native example needs no `.apk`. `DEVTOOLS_MOBILE_PLATFORM=ios` runs the iOS spec instead — all four adapters — which drives Settings because the simulator ships no Clock, from a separate spec per platform rather than a branch. The simulator is chosen by udid and defaults to whichever is already booted, and an unmatched `IOS_DEVICE_NAME` is refused: naming one that does not exist makes the XCUITest driver create and boot it, every run, rather than fail. That refusal and the booted-simulator preflight are **local** policy — `xcrun simctl` enumerates local simulators and nothing else — so `IOS_UDID` and a non-local `APPIUM_HOST` both bypass them, or a real device and a cloud grid would be refused a run they were correctly configured for. `DEVTOOLS_MOBILE=web` drives the device's own browser on both platforms — Chrome on Android, Safari on iOS, which the XCUITest driver serves without a chromedriver. `examples/MOBILE.md` holds the prerequisites and the `DEVTOOLS_MOBILE` / `APPIUM_APP` switches; `DEVTOOLS_MODE=trace` flips any demo to trace mode. | | `pnpm dev` | Run all packages in parallel dev mode. | | `python3 packages/selenium-devtools-py/scripts/changes.py next-version` | The version a Python-adapter release would publish, from the fragments pending in `changes/`. `check --base ` is the CI gate; `apply` is what the release runs. | diff --git a/README.md b/README.md index 217f258b..3119fe6d 100644 --- a/README.md +++ b/README.md @@ -116,7 +116,7 @@ The trace **format and the player are identical** across those three, but **capt | Assertion capture (`captureAssertions`) | ✅ | ✅ | ✅ | | Auto BiDi capture | ✅ auto | ✅ auto | ⚠️ opt-in ⁷ | -¹ Selenium reconstructs the DOM per navigation; anchor timing is approximate (a navigation's snapshot can lag the command that triggered it). +¹ With BiDi, Selenium registers the page collector at document start, so every document anchors its own DOM as WebdriverIO's does. Without BiDi it falls back to injecting the collector after each navigation, and there a navigation's snapshot can lag the command that triggered it. ² Nightwatch's Cucumber and exports-object interfaces get real per-test slicing; the BDD `describe/it` interface collapses to a single session-scoped slice keyed to the first test. ³ Only `retain-on-failure` works; the other retry-aware policies degrade to it because Nightwatch's `--retries` re-runs a testcase internally without re-firing the per-test hooks. ⁴ WebdriverIO does not yet carry feature-level ancestry, so its Cucumber nesting is Scenario→Step. @@ -140,7 +140,7 @@ The trace artifact contains: - `resources/page@--snapshot.txt` — depth-indented accessibility-tree snapshot (AI-friendly) - `transcript.md` — human/LLM-readable Markdown transcript of the captured actions, with timing, selectors, and value annotations -What counts as a user-facing action is filtered through an allow-list in `@wdio/devtools-core/action-mapping.ts` (`url`, `click`, `setValue`, `sendKeys`, `get`, etc.). Internal commands like `findElement`/`waitUntil`/`executeScript` don't produce trace entries. +What counts as a user-facing action is filtered through the `ACTION_MAP` allow-list in [`packages/shared/src/trace-actions.ts`](packages/shared/src/trace-actions.ts) (`url`, `click`, `setValue`, `sendKeys`, `get`, explicit waits such as `waitUntil`, element reads, `execute`, etc.). Locator resolution (`$`, `$$`, `findElement(s)`), Selenium's `executeScript` polling and the `clearValue` that `setValue` fires internally don't produce trace entries. Trace mode and live mode are **mutually exclusive** — `screencast` options are ignored in trace mode (live-mode feature). Live and trace serve different audiences (humans debugging vs. agents diffing), and stacking them only costs perf. @@ -182,7 +182,7 @@ The `show-trace` bin is exposed by each JavaScript adapter (`@wdio/devtools-serv | `filmstrip` | `boolean` | `true` | Record a dense, continuous screencast *into* the trace for smooth scrubbing in the player (not just one frame per action). Dense frames sit alongside the per-action frames; thinned + content-addressed at export. Runs the screencast recorder (CDP push on Chrome, polling elsewhere). | | `emitArtifactsManifest` | `boolean` | auto | Write `devtools-artifacts-.json` next to the trace — the index reporters/CI read to discover produced artifacts. Off by default; auto-enabled when an Allure reporter is detected (Nightwatch stays opt-in). | -The per-test **`screenshot`** and **`video`** artifact options live on the WebdriverIO and Selenium adapters (not the shared base), are gated to `traceGranularity: 'test'`, and attach inline to Allure — see the [WebdriverIO](./packages/service/README.md#allure-integration) and [Selenium](./packages/selenium-devtools/README.md) READMEs for the full per-adapter option tables. +The per-test **`screenshot`** and **`video`** artifact options live on each adapter rather than the shared base, are gated to `traceGranularity: 'test'`, and attach inline to Allure on WebdriverIO and Selenium (Nightwatch writes them produce-only) — see the [WebdriverIO](./packages/service/README.md#allure-integration) and [Selenium](./packages/selenium-devtools/README.md) READMEs for the full per-adapter option tables. **Allure integration.** When an Allure reporter is present, per-test traces, screenshots, and videos attach to each test's card (`traceGranularity: 'test'`); coarser granularities write the artifacts to disk and list them in the manifest. Details and the report-noise settings are in the [WebdriverIO adapter README](./packages/service/README.md#allure-integration). @@ -222,7 +222,7 @@ A hybrid app is the case capabilities alone cannot answer, because the answer ch On a native session, element data and the A11y panel come from the platform's XML tree rather than the DOM, so locators read as `android=new UiSelector()…` / `-ios predicate string:…` rather than CSS or XPath over HTML. -A runnable example per adapter lives under [examples/](examples/) — `pnpm demo:wdio:mobile`, `:selenium:mobile`, `:nightwatch:mobile`, `:python:mobile`. Each drives the Clock app that ships with every Android system image — starting a timer, pausing it and clearing it — so none needs an `.apk`; `DEVTOOLS_MOBILE=web` drives the device's own browser instead — Chrome on Android, Safari on iOS. [examples/MOBILE.md](examples/MOBILE.md) states the prerequisites and switches in one place. A single-adapter WDIO variant also lives at [examples/wdio/mocha/wdio.native.conf.ts](examples/wdio/mocha/wdio.native.conf.ts) (`pnpm demo:wdio:native`), which reads its Appium endpoint from `APPIUM_HOST` / `APPIUM_PORT` / `APPIUM_DEVICE`. In short, for a local emulator: +A runnable example per adapter lives under [examples/](examples/) — `pnpm demo:wdio:mobile`, `:selenium:mobile`, `:nightwatch:mobile`, `:python:mobile`. Each drives the Clock app that ships with every Android system image, keying a duration on the timer keypad without ever starting a timer, so none needs an `.apk` (iOS drives Settings via `DEVTOOLS_MOBILE_PLATFORM=ios`); `DEVTOOLS_MOBILE=web` drives the device's own browser instead — Chrome on Android, Safari on iOS. [examples/MOBILE.md](examples/MOBILE.md) states the prerequisites and switches in one place. A single-adapter WDIO variant also lives at [examples/wdio/mocha/wdio.native.conf.ts](examples/wdio/mocha/wdio.native.conf.ts) (`pnpm demo:wdio:native`), which reads its Appium endpoint from `APPIUM_HOST` / `APPIUM_PORT` / `APPIUM_DEVICE`. In short, for a local emulator: 1. **Java JDK** — `brew install --cask temurin` 2. **Android SDK** — `brew install --cask android-commandlinetools` then `yes | sdkmanager --licenses && sdkmanager "platform-tools" "emulator" "system-images;android-34;google_apis_playstore;arm64-v8a"`. The brew cask installs sdkmanager under `/opt/homebrew/share/android-commandlinetools/`, and sdkmanager downloads other SDK pieces alongside it — set `ANDROID_HOME` to that path (not `~/Library/Android/sdk/`). @@ -234,10 +234,9 @@ A runnable example per adapter lives under [examples/](examples/) — `pnpm demo These are emulator-specific issues; on a physical phone with USB debugging only steps 1, 4, 6 (and the Chromedriver pin if Chrome on the device is old) apply. ### 🔍︎ TestLens -- **Code Intelligence**: View test definitions directly in your editor -- **Run/Debug Actions**: Execute individual tests or suites with inline CodeLens actions +- **Source tab**: Click any test or command to show its source at the exact line in the dashboard +- **Open in editor**: The Source tab links the file and line into VS Code (`vscode://file/...`) - **Quick Navigation**: Jump between test code and execution results seamlessly -- **Status Indicators**: Visual feedback for test pass/fail states in the editor ### 🏗️ Architecture - **Frontend**: Lit web components with reactive state management (`@lit/context`) @@ -298,7 +297,7 @@ npm install @wdio/selenium-devtools **Python (Selenium):** ```bash -pip install -e packages/selenium-devtools-py # or: pip install selenium-devtools-py (when published) +pip install selenium-devtools-py # or, from this repo: pip install -e packages/selenium-devtools-py selenium-devtools install-backend # once — pip cannot install a Node package ``` diff --git a/packages/elements/README.md b/packages/elements/README.md index c2c167eb..b188671c 100644 --- a/packages/elements/README.md +++ b/packages/elements/README.md @@ -8,7 +8,7 @@ Element detection and locator generation for WebdriverIO — browser DOM queryin npm install @wdio/elements ``` -Requires `webdriverio` as a peer dependency (^9.0.0). +Requires `webdriverio` as a peer dependency (`^9.0.0 || ^10.0.0`). ## Usage @@ -40,7 +40,8 @@ Selenium takes it via `By.xpath`, Nightwatch via its `'xpath'` locate strategy). The DevTools **per-action trace capture** runs the same scripts but asks for the locator in the recording runner's own dialect, so a WebdriverIO trace shows -`button*=Log in` while a Selenium or Nightwatch one shows the XPath above. Only +`button*=Log in` while a Selenium one shows the XPath above. Nightwatch prefers a +unique CSS selector and falls back to that XPath only when none exists. Only the text branch differs; every other branch is the same portable CSS. It auto-detects platform: web sessions walk the browser accessibility tree, mobile sessions parse the page-source XML. `@wdio/devtools-service` also registers a `browser.getSnapshot()` runtime accessor that calls this directly. diff --git a/packages/nightwatch-devtools/README.md b/packages/nightwatch-devtools/README.md index 9c4a7206..4ed40ddc 100644 --- a/packages/nightwatch-devtools/README.md +++ b/packages/nightwatch-devtools/README.md @@ -16,6 +16,8 @@ npm install @wdio/nightwatch-devtools npm install @wdio/nightwatch-devtools ``` +Requires Nightwatch 3 or later. For Cucumber, `@cucumber/cucumber` must be installed in your project; the plugin uses your copy rather than bundling one. + **2. Wire the plugin into your Nightwatch config** via the `globals` field: ```javascript @@ -132,7 +134,7 @@ globals: nightwatchDevtools({ ## Live Mode -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. +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. ### Preserve & Rerun (Compare) @@ -151,7 +153,7 @@ globals: nightwatchDevtools({ }) ``` -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. +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. Open a trace with the `show-trace` bin, shipped with this package (no extra dependency): @@ -191,15 +193,17 @@ desiredCapabilities: { } ``` -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. +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. + +**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. --- ## Screencast -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-.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. +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-.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. -**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. +**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. ### Quick start @@ -217,6 +221,7 @@ globals: nightwatchDevtools({ | `enabled` | `boolean` | `false` | Master switch. | | `pollIntervalMs` | `number` | `200` | Screenshot interval (ms). Lower = smoother video, more WebDriver round-trips. 200 ms ≈ 5 fps. | | `captureFormat` | `'jpeg' \| 'png'` | `'jpeg'` | Frame format. WebDriver screenshots are always PNG, so this only affects the encoded output. | +| `maxBufferFrames` | `number` | `2000` | Cap on frames held in memory; past it the buffer is thinned in place, keeping the first and last frames. | | `maxWidth` / `maxHeight` / `quality` | — | — | CDP-only options, ignored in polling mode. Listed for shape compatibility with the WDIO/Selenium adapters. | ### Prerequisites @@ -251,23 +256,47 @@ The encoded video is sent to the DevTools dashboard via the `screencast` WS scop --- -## Examples - -Working examples are included in this package: +## Mobile (Appium) -| Directory | Runner | Command | -|-----------|--------|---------| -| [`example/`](./example) | Nightwatch mocha-style | `pnpm example` | +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. -Build the package first: +```javascript +// nightwatch.mobile.conf.cjs +const nightwatchDevtools = require('@wdio/nightwatch-devtools').default -```bash -# From repo root -pnpm build --filter @wdio/nightwatch-devtools -cd packages/nightwatch-devtools -pnpm example +module.exports = { + selenium: { + start_process: false, + use_appium: true, + host: '127.0.0.1', + port: 4723, + default_path_prefix: '' + }, + test_settings: { + default: { + desiredCapabilities: { + browserName: null, + platformName: 'Android', + 'appium:automationName': 'UiAutomator2' + }, + globals: nightwatchDevtools({ mode: 'trace', bidi: false }) + } + } +} ``` +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). + +## Examples + +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: + +| Example | Runner | Command | +|-----------|--------|---------| +| `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`) | +| `examples/nightwatch/cucumber/` | Nightwatch + Cucumber | `pnpm --filter @wdio/nightwatch-devtools example:cucumber` | +| `examples/nightwatch/nightwatch.mobile.conf.cjs` | Appium (Android Clock app; iOS Settings with `DEVTOOLS_MOBILE_PLATFORM=ios`) | `pnpm demo:nightwatch:mobile` | + --- ## Limitations diff --git a/packages/selenium-devtools-py/README.md b/packages/selenium-devtools-py/README.md index da1ba538..29a4cd55 100644 --- a/packages/selenium-devtools-py/README.md +++ b/packages/selenium-devtools-py/README.md @@ -15,7 +15,7 @@ real headless Chrome. See [Trace mode](#trace-mode) and [Roadmap](#roadmap). ## Install (dev) ```bash -pip install -e packages/selenium-devtools-py # or: pip install selenium-devtools-py (when published) +pip install selenium-devtools-py # or, from this repo: pip install -e packages/selenium-devtools-py ``` The transport is **dependency-free** (stdlib WebSocket client). `selenium>=4.44` @@ -366,6 +366,22 @@ bugs: the Compare tab disappears after an ordinary rerun rather than diffing against something you did not ask to keep. +## Mobile (Appium) + +Appium sessions are captured in live and trace mode. A mobile-browser session is captured like a desktop one; a native app gets commands, screenshots and element data from the platform's XML tree, with no DOM. This adapter decides from the startup capabilities, so a hybrid app's webview half is captured as if it were still native (only the WebdriverIO service follows context switches). + +The demo runs bare `python3`, so from the root of a built checkout of this repo (`pnpm install && pnpm build`) install the adapter into that interpreter, plus the Appium client the desktop examples don't need: + +```bash +python3 -m venv .venv && source .venv/bin/activate +pip install -e packages/selenium-devtools-py +pip install -r examples/selenium-py/requirements-mobile.txt +pnpm demo:python:mobile # Android Clock app +DEVTOOLS_MOBILE_PLATFORM=ios pnpm demo:python:mobile # iOS Settings +``` + +`DEVTOOLS_MOBILE=web` drives the device's own browser instead. Prerequisites and the `DEVTOOLS_MOBILE` / `APPIUM_APP` switches are in [`examples/MOBILE.md`](../../examples/MOBILE.md); the full reference is the [Mobile Testing page](https://webdriver.io/docs/devtools/mobile). + ## Trace mode Instead of a live dashboard, write the run to a portable archive — the same @@ -403,9 +419,10 @@ per-command screenshots that both modes capture: | A11y tree + element overlay — read beside each action, two extra round trips per command | on | `DEVTOOLS_A11Y=0` | | DOM time-travel — the mutation stream the preview iframe already replays | on | — | -Two things worth knowing. **A run produces one archive**: the backend's -accumulator is run-scoped, so there is no per-session or per-test slicing and no -`traceGranularity` / `tracePolicy` equivalent yet. And **the export is requested +Two things worth knowing. **How many archives a run writes, and which are kept,** +is set by `--devtools-trace-granularity` and `--devtools-trace-policy` (see +[How many archives, and which ones to keep](#how-many-archives-and-which-ones-to-keep)). +And **the export is requested when the run finishes, not when the process exits** — pytest asks at `sessionfinish`, before it would park on a dashboard window, and a plain script's `disable()` exports before closing the transport. An artifact that depended on diff --git a/packages/selenium-devtools/README.md b/packages/selenium-devtools/README.md index 30829026..68685b3a 100644 --- a/packages/selenium-devtools/README.md +++ b/packages/selenium-devtools/README.md @@ -18,10 +18,10 @@ Works with **Mocha**, **Jest**, **Cucumber**, or plain `node script.js` — the npm install @wdio/selenium-devtools ``` -**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: +**2. Import it in your test file.** The import has a side effect: it patches Selenium's `Builder`, `WebDriver` and `WebElement` prototypes as it loads. Every import finishes before your test code runs, so its position relative to `selenium-webdriver` doesn't matter: ```javascript -import '@wdio/selenium-devtools' // <-- must be first +import '@wdio/selenium-devtools' import { Builder, By } from 'selenium-webdriver' ``` @@ -455,6 +455,12 @@ pnpm example:mocha --- +## Mobile (Appium) + +Appium sessions are captured in live and trace mode. A mobile-browser session is captured like a desktop one; a native app gets commands, screenshots and element data from the platform's XML tree, with no DOM. This adapter decides from the startup capabilities, so a hybrid app's webview half is captured as if it were still native (only the WebdriverIO service follows context switches). + +From the root of a built checkout of this repo (`pnpm install && pnpm build`), try it with `pnpm demo:selenium:mobile`, which drives the Android Clock app; `DEVTOOLS_MOBILE_PLATFORM=ios` runs the iOS spec against Settings, and `DEVTOOLS_MOBILE=web` drives the device's own browser. Prerequisites and the `DEVTOOLS_MOBILE` / `APPIUM_APP` switches are in [`examples/MOBILE.md`](../../examples/MOBILE.md); the full reference is the [Mobile Testing page](https://webdriver.io/docs/devtools/mobile). + ## How It Works The plugin patches `selenium-webdriver`'s `Builder`, `WebDriver`, and `WebElement` prototypes at import time: diff --git a/packages/service/README.md b/packages/service/README.md index e10a445f..75de9051 100644 --- a/packages/service/README.md +++ b/packages/service/README.md @@ -156,15 +156,12 @@ Available across all three adapters — WebdriverIO uses CDP push for Chrome (an ### Setup -Screencast encoding requires **ffmpeg** on `PATH` and the `fluent-ffmpeg` package: +Screencast encoding requires the **ffmpeg** binary on `PATH`; the `fluent-ffmpeg` wrapper installs with the service: ```sh -# Install ffmpeg — https://ffmpeg.org/download.html +# Install ffmpeg: https://ffmpeg.org/download.html brew install ffmpeg # macOS sudo apt install ffmpeg # Ubuntu/Debian - -# Install fluent-ffmpeg -npm install fluent-ffmpeg ``` ### Configuration @@ -214,13 +211,12 @@ No configuration change is needed to switch modes — the service detects browse - Leading blank frames (captured before the first URL navigation) are automatically trimmed so videos begin at the first meaningful page action. - If `browser.reloadSession()` is called mid-run, the service finalises the current recording and starts a fresh one for the new session. Each session produces its own `.webm` file. - When multiple recordings exist, the DevTools UI shows a **Recording N** dropdown to switch between them. -- Output files are written to the directory containing `wdio.conf.ts` (WDIO's `rootDir`) or `outputDir` if explicitly configured. +- Output files are written to a `test-results/` folder under `outputDir` if explicitly configured, otherwise under the directory containing `wdio.conf.ts` (WDIO's `rootDir`). ### Output files | File | Description | |---|---| -| `wdio-trace-{sessionId}.json` | Full trace: DOM mutations, commands, screenshots, console logs, network requests | | `wdio-video-{sessionId}.webm` | Screencast video (only produced when `screencast.enabled: true`) | ## Performance API capture @@ -235,12 +231,12 @@ services: [['devtools', options]] | Option | Type | Default | Description | |---|---|---|---| -| `port` | `number` | random | Port the DevTools UI server listens on | +| `port` | `number` | `3000` (a free port is used if it is taken) | Port the DevTools UI server listens on | | `hostname` | `string` | `'localhost'` | Hostname the DevTools UI server binds to | | `devtoolsCapabilities` | `Capabilities` | Chrome 1600×1200 | Capabilities used to open the DevTools UI window | | `screencast` | `ScreencastOptions` | — | Session video recording (live mode only — see below; for trace mode use `video`) | | `mode` | `'live' \| 'trace'` | `'live'` | `'live'` opens the DevTools UI window; `'trace'` skips the UI and writes a `trace-.zip` under a `test-results/` directory (base dir resolved from the test file's dir → config dir → cwd) at session end. See [Trace mode](../../README.md#-trace-mode-tracezip) | -| `traceFormat` | `'zip' \| 'ndjson-directory'` | `'zip'` | Trace mode only. Output layout — `'zip'` writes a single archive; `'ndjson-directory'` unpacks the same files into `trace-/` (one less unzip step for scripted/agentic consumers). Both open in `show-trace` and other compatible viewers. | +| `traceFormat` | `'zip' \| 'ndjson-directory'` | `'zip'` | Trace mode only. Output layout — `'zip'` writes a single archive; `'ndjson-directory'` unpacks the same files into `trace-/` (one less unzip step for scripted/agentic consumers). The `zip` opens in `show-trace`; a directory opens in other compatible viewers, or zip it first. | | `traceGranularity` | `'session' \| 'spec' \| 'test'` | `'session'` | Trace mode only. How traces are partitioned — one per worker session / spec file / test. `'test'` is required for per-test Allure attachments (trace, screenshot, video). | | `tracePolicy` | `TraceRetentionPolicy` | `'on'` | Trace mode only. Which traces to keep: `'on'` \| `'retain-on-failure'` \| `'retain-on-first-failure'` \| `'on-first-retry'` \| `'on-all-retries'` \| `'retain-on-failure-and-retries'`. The retry-aware policies pair best with `traceGranularity: 'test'`. | | `screenshot` | `'off' \| 'on' \| 'only-on-failure'` | `'off'` | Trace mode + `traceGranularity: 'test'`. Per-test screenshot, attached inline to Allure (`image/png`). WDIO-service-specific. | From 460e434fb0bbf4bcdda00dc2e3a1128336be3333 Mon Sep 17 00:00:00 2001 From: Vishnu Vardhan Date: Tue, 13 Oct 2026 13:04:51 +0530 Subject: [PATCH 2/2] Update README.md Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3119fe6d..198bfd86 100644 --- a/README.md +++ b/README.md @@ -234,7 +234,7 @@ A runnable example per adapter lives under [examples/](examples/) — `pnpm demo These are emulator-specific issues; on a physical phone with USB debugging only steps 1, 4, 6 (and the Chromedriver pin if Chrome on the device is old) apply. ### 🔍︎ TestLens -- **Source tab**: Click any test or command to show its source at the exact line in the dashboard +- **Source tab**: Click a test to open its source. Command clicks follow the source line in the background; open the Source tab to see it. Available when the source and its location were captured. - **Open in editor**: The Source tab links the file and line into VS Code (`vscode://file/...`) - **Quick Navigation**: Jump between test code and execution results seamlessly