DevTools (Test Debugging & Visualization) Service
@wdio/devtools-service is a 3rd party package, for more information please see GitHub | npm
A powerful browser devtools extension for debugging, visualizing, and controlling test executions in real-time.
Works with WebdriverIO, Nightwatch.js, Selenium WebDriver (any test runner), and Python Selenium — same backend, same UI, same capture infrastructure.
It runs in two modes: live — an interactive dashboard that opens as your tests run — and trace — a portable trace.zip artifact for offline replay, CI, and AI-agent diffing.
Quick Start
Install the adapter for your framework, add it to your config, and run — the DevTools dashboard opens automatically.
npm install @wdio/devtools-service --save-dev # WebdriverIO (Nightwatch / Selenium below)
// wdio.conf.js
export const config = {
services: ['devtools'] // live mode — opens the dashboard on run
}
npx wdio run wdio.conf.js
Want a portable artifact instead of a live UI (CI / agent diffing)? Switch to trace mode — see Configuration and Usage. Full setup for each framework: Installation · Nightwatch · Selenium.
Features
🎯 Interactive Test Execution
- Selective Test Rerun: Click play buttons on individual test cases, test suites, or Cucumber scenario examples to re-execute them instantly
- Smart Browser Reuse: Tests rerun in the same browser window without opening new tabs, improving performance and user experience
- Stop Test Execution: Terminate running tests with proper process cleanup using the stop button
- Test List Preservation: All tests remain visible in the sidebar during reruns, maintaining full context
🎭 Multi-Framework Support
- Mocha: Full support with grep-based filtering for test/suite execution
- Jasmine: Complete integration with grep-based filtering
- Cucumber: Scenario-level and example-specific execution with feature:line targeting
Dashboard and options by adapter:
| Feature | WebdriverIO | Selenium | Nightwatch | Python |
|---|---|---|---|---|
| One row per command in the Actions list | ✅ | ✅ | ✅ | ✅ |
| Waits show as one row with what they issued nested under it | ✅ | ✅ | ⚠️ no element-wait polls ¹ | ✅ |
| Screenshot on every command row | ✅ | ✅ | ✅ | ✅ |
| Browser console logs | ✅ | ✅ | ✅ | ✅ |
| Source tab opens the test line that ran the command | ✅ | ✅ | ✅ | ✅ |
| Metadata tab (session, capabilities, device) | ✅ | ✅ | ✅ | ✅ |
| Performance timings on navigation rows | ✅ | ✅ | ✅ | ✅ |
| Failed command in the Errors tab, with the browser's message | ✅ | ✅ | ✅ | ✅ |
| Run, Rerun and Run all from the dashboard | ⚠️ no Cucumber step rerun ² | ⚠️ no Cucumber step rerun ² | ⚠️ no Cucumber step rerun ² | ✅ |
| Preserve & Rerun (compare a failed run with its rerun) | ✅ | ✅ | ✅ | ✅ |
| Dashboard window opens by itself in live mode, and can be turned off | ⚠️ always opens ³ | ✅ openUi: false | ⚠️ always opens ⁴ | ✅ DEVTOOLS_OPEN=0 |
Turn off per-command screenshots (captureScreenshots) | ❌ ⁵ | ✅ | ❌ ⁶ | ❌ ⁶ |
Force the test browser headless (headless) | ❌ ⁷ | ⚠️ Chrome only ⁸ | ❌ ⁷ | ❌ ⁷ |
| Safari kept frontmost so input lands (live mode) | ⚠️ once ⁹ | ✅ before each element input | ⚠️ once ⁹ | ⚠️ once ⁹ |
✅ supported. ⚠️ partial, with the limit in the note below. ❌ not supported. Each supported cell is backed by the cross-adapter conformance suite, a named test or a manual check, listed in the feature list.
¹ A waitUntil row holds the commands its condition issued; a waitForElement* row has nothing under it, because Nightwatch polls for the element through its own transport rather than browser.
² Under Cucumber a Gherkin step has no CLI filter of its own, so the dashboard reruns scenarios and features, not single steps.
³ The service always opens the dashboard in live mode; devtoolsCapabilities picks its browser but cannot skip it.
⁴ Nightwatch always opens the dashboard in live mode.
⁵ No option: every command row takes a screenshot, except on Appium, where only native sessions in live mode do.
⁶ No option: every command row takes a screenshot.
⁷ No option: set headless in the browser capabilities, as for any run.
⁸ The option adds --headless=old to goog:chromeOptions only, so other browsers stay headed; set their own headless capability.
⁹ Brings Safari to the front once after the session starts (live mode, local macOS), so a click issued after another window took the front can still land nowhere.
📊 Real-Time Visualization
- Live Browser Preview: View the application under test in a scaled iframe with automatic screenshot updates
- Actions Timeline: Command-by-command execution log with timestamps and parameters
- Test Hierarchy: Nested test suite and test case tree view with status indicators
- Live Status Updates: Immediate spinner icons and visual feedback when tests start/stop
🧐 Debugging Capabilities
- Command Logging: Detailed capture of all WebDriver commands with arguments and results
- Screenshot Capture: Automatic screenshots after each command for visual debugging
- Source Code Mapping: View the exact line of code that triggered each command
- Console Logs: Capture and display application console output with timestamps and log levels
- Network Logs: Monitor and inspect HTTP requests/responses including headers, payloads, timing, and status codes. Where the driver exposes no BiDi (Safari, Appium), the page's own Resource Timing still lists every request with its timing and size
- Error Tracking: Full error messages and stack traces for failed tests
🎮 Execution Controls
- Global Test Running State: All play buttons automatically disable during test execution to prevent conflicts
- Immediate Feedback: Spinner icons update instantly when tests start
- Actions Tab Auto-Clear: Execution data automatically clears and refreshes on reruns
- Metadata Tracking: Test duration, status, and execution timestamps
🎬 Session Screencast
- Automatic Video Recording: Captures a continuous
.webmvideo of the browser session alongside the existing snapshot and DOM mutation views - Per-framework modes:
- WebdriverIO: CDP push mode for Chrome/Chromium (efficient, no per-command overhead); polling fallback for other browsers
- Selenium WebDriver: CDP push mode via
selenium-webdriver/bidi; polling fallback otherwise - Nightwatch.js: Polling mode (Nightwatch doesn't expose a stable CDP escape hatch); works on every browser Nightwatch supports
- Per-Session Videos: Each browser session (including sessions created by
browser.reloadSession()) produces its own recording, selectable from a dropdown in the UI - Smart Trimming: Leading blank frames before the first URL navigation are automatically removed so videos start at the first meaningful page action
For setup, configuration options, and prerequisites see each adapter's README: WebdriverIO · Selenium · Nightwatch.
🐞 Preserve & Rerun (Compare)
- When the bug icon appears: Only on test/suite rows in a
failedstate and the icon sits next to ▶ on hover, available wherever a plain rerun is supported (e.g. Cucumber scenarios at the scenario row, Mocha tests at the test or suite row) - Side-by-side diff: Click the bug-play icon on a failed test to snapshot the failing run and rerun in one action and the Compare tab shows the two runs aligned by command, with the failure point and assertion error (Expected vs Received) called out
- Diagnose flaky tests: See exactly which command differed between a pass and a fail without re-reading logs
- Pop out: Open the comparison in a separate, themed window for a roomier view
Available across WebdriverIO, Selenium WebDriver, and Nightwatch.js. The rerun mechanism differs per framework (WDIO uses
--spec+ grep, Selenium substitutes a runner-specific filter flag like--grep/--testNamePattern, Nightwatch readsDEVTOOLS_RERUN_LABEL); the dashboard contract is identical.
🌐 BiDi capture (browser console + JS exceptions + network)
Real-time capture of browser-side events through the WebDriver BiDi protocol — entries arrive in the dashboard as they happen instead of being scraped after each command.
| Adapter | BiDi source | Default | How to enable |
|---|---|---|---|
| WebdriverIO | WDIO's native browser.on('log.entryAdded' | 'network.*') | On | Automatic when the driver advertises BiDi (Chrome ≥114) |
| Selenium WebDriver | selenium-webdriver/bidi/{logInspector, networkInspector} | On when available | Automatic; ensureBidiCapability sets webSocketUrl=true on the Builder |
| Nightwatch.js | Same selenium-webdriver/bidi inspectors (Nightwatch ships selenium-webdriver internally) | Opt-in | globals: nightwatchDevtools({ bidi: true }) + desiredCapabilities: { webSocketUrl: true } |
When BiDi is active in Selenium or Nightwatch, the per-command Chrome performance-log network-capture path is gated off so requests don't appear twice in the dashboard. The attach + sink logic lives in @wdio/devtools-core's bidi.ts — same module both adapters consume.
Without BiDi: Safari, and every session Appium serves. Neither driver exposes BiDi, so there the Network tab is filled by the page itself, through the same injected collector that records the DOM. It patches fetch / XMLHttpRequest (method, headers, status and body of each call the page makes) and reads the document's Resource Timing buffer: every load the browser made (the document, scripts, styles, images, fonts) with its timing phases and size, plus a status on Chromium. What Resource Timing cannot see are headers and bodies, and on Safari the status; each such row says so in its detail card, and its trace.network entry carries _source: "resource-timing". When BiDi is serving the session, meaning it has delivered a network event and not merely accepted a subscription (Appium's BiDi shim accepts one and sends nothing), the Resource Timing rows are dropped at ingest so a request is listed once; Nightwatch's performance log, read after the page is drained, upgrades those rows in place with the headers and status it has. A native app session has no document, so it has no network capture at all. Only a device- or proxy-level capture outside the adapters can see that traffic.