Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
e9a4b66
Phase 1: Introduce live and trace mode; Gate UI from launching for tr…
vishnuv688 Jun 4, 2026
e9bf75d
Phase 2: Incorporating @wdio/elements into devtools for the trace str…
vishnuv688 Jun 4, 2026
99ef6cb
Phase 3: Capture per-action accessibility/element snapshots in trace …
vishnuv688 Jun 4, 2026
c7f53e0
Phase 4: Export trace.zip from captured TraceLog (WDIO)
vishnuv688 Jun 4, 2026
f5abbf4
Phase 5: Emit trace.zip from Selenium + Nightwatch adapters via share…
vishnuv688 Jun 8, 2026
2d8f3a8
Phase 6: Per-action element/snapshot parity for Selenium and Nightwatch
vishnuv688 Jun 8, 2026
b756583
Update README
vishnuv688 Jun 8, 2026
4dc81e1
fix: CodeQl and lint fix
vishnuv688 Jun 8, 2026
60f76f9
Mobile-aware context-options, snapshot probe timeouts, chronological …
vishnuv688 Jun 9, 2026
7cda9ad
core: @wdio/elements → webdriverio peer dep leak through nightwatch/s…
vishnuv688 Jun 9, 2026
72a8894
core: Trace.zip viewer compat, traceFormat option, mobile example, Se…
vishnuv688 Jun 9, 2026
47225b3
Service+Nightwatch withTimeout timer leak
vishnuv688 Jun 10, 2026
40a910c
Bound polynomial regex in checkUniqueness xpath parser
vishnuv688 Jun 10, 2026
2880e4d
fix: Trace format with snapshots and elements for wdio examples
Winify Jun 10, 2026
2835c2d
fix: Network wallTime should be properly set with HAR format
Winify Jun 10, 2026
e1ff817
refactor: Use real start/end timestamps for actions
Winify Jun 10, 2026
c52941e
performance: Introduce guards on JS executions when native Mobile app…
Winify Jun 10, 2026
629b4c9
fix: Correctly chain commands' snapshot together
Winify Jun 10, 2026
55e8e5b
fix: INTERNAL_COMMAND leakage into trace
Winify Jun 10, 2026
8a5f959
fix: Cleaning up snapshot/screenshot mismatches
Winify Jun 10, 2026
579ba7e
chore: Patch packages/service by including necessary dependencies fro…
Winify Jun 10, 2026
08c35c6
chore: Address usability issues
Winify Jun 11, 2026
738eba4
refactor: Simplification across packages/core and service
Winify Jun 11, 2026
ebd5ba6
chore: Make @wdio/elements publishable; align xpath/xmldom deps acros…
vishnuv688 Jun 11, 2026
2dfe1b2
chore: Trace mode UI and screencast gaurd across all the devtools
vishnuv688 Jun 11, 2026
6745994
fix: Add @microsoft/api-extractor to @wdio/elements devDeps
vishnuv688 Jun 11, 2026
c332988
Version bump
vishnuv688 Jun 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,20 @@ dist-ssr
/*.ts
/*.json
*.tgz
example/wdio-*.json
examples/wdio/wdio-*.json
examples/wdio/wdio-*.webm
examples/nightwatch/logs/

# Adapter-encoded screencasts (written next to the project root by default)
selenium-video-*.webm
nightwatch-video-*.webm
packages/nightwatch-devtools/nightwatch-video-*.webm

# trace.zip output (mode: 'trace')
trace-*.zip

# vitest --coverage output
coverage/

# pnpm state, cache, logs, and debug files
/packages/**/*.mjs
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,27 @@ Real-time capture of browser-side events through the WebDriver BiDi protocol —

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.

### 📦 Trace mode (trace.zip)

Headless capture path — no DevTools UI window opens. At session end the adapter writes a `trace-<sessionId>.zip` next to the user's spec / config file, suitable for offline replay, AI-agent diffing, or any consumer that prefers a portable artifact over a live UI.

| Adapter | How to enable |
|---|---|
| **WebdriverIO** | `services: [['devtools', { mode: 'trace' }]]` |
| **Selenium** | `DevTools.configure({ mode: 'trace' })` (before importing `selenium-webdriver`) |
| **Nightwatch** | `globals: nightwatchDevtools({ mode: 'trace' })` |

The zip contains:
- `trace.trace` — NDJSON `context-options` + `before`/`after` action events
- `trace.network` — HAR-style network entries derived from the existing capture
- `resources/page@<id>-<ts>.jpeg` — screenshot per user-facing action
- `resources/elements-page@<id>-<ts>.json` — flat interactable element list from `@wdio/elements`
- `resources/snapshot-page@<id>-<ts>.txt` — depth-indented accessibility-tree snapshot (AI-friendly)

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.

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.

### 🔍︎ TestLens
- **Code Intelligence**: View test definitions directly in your editor
- **Run/Debug Actions**: Execute individual tests or suites with inline CodeLens actions
Expand Down
1 change: 1 addition & 0 deletions examples/nightwatch/nightwatch.conf.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ module.exports = {
// off to avoid duplicate entries.
globals: nightwatchDevtools({
port: 3000,
mode: 'trace',
screencast: { enabled: true, pollIntervalMs: 200 },
bidi: true
})
Expand Down
2 changes: 1 addition & 1 deletion examples/selenium/jest-test/test/example.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const VALID_USERNAME = 'tomsmith'
const VALID_PASSWORD = 'SuperSecretPassword!'

DevTools.configure({
screencast: { enabled: true, quality: 70, maxWidth: 1280, maxHeight: 720 },
mode: 'trace',
headless: true
})

Expand Down
16 changes: 1 addition & 15 deletions examples/wdio/wdio.conf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,21 +127,7 @@ export const config: Options.Testrunner = {
// Services take over a specific job you don't want to take care of. They enhance
// your test setup with almost no effort. Unlike plugins, they don't add new
// commands. Instead, they hook themselves up into the test process.
services: [
'devtools'
// [
// 'devtools',
// {
// screencast: {
// enabled: true,
// captureFormat: 'jpeg', // 'jpeg' or 'png' — frame format sent by Chrome over CDP
// quality: 70, // JPEG quality 0–100
// maxWidth: 1280, // max frame width in px
// maxHeight: 720 // max frame height in px
// }
// }
// ]
],
services: [['devtools', { mode: 'trace' as const }]],
//
// Framework you want to run your specs with.
// The following are supported: Mocha, Jasmine, and Cucumber
Expand Down
4 changes: 3 additions & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@
"license": "MIT",
"devDependencies": {
"@types/ws": "^8.18.1",
"@types/yazl": "^2.4.6",
"@wdio/devtools-script": "workspace:*",
"@wdio/devtools-shared": "workspace:^",
"stacktrace-parser": "^0.1.11",
"ws": "^8.21.0"
"ws": "^8.21.0",
"yazl": "^2.5.1"
}
}
65 changes: 65 additions & 0 deletions packages/core/src/action-mapping.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Allow-list mapping from runner-native command names to trace
// vocabulary. Ported from Vince Graics' PR #209 (`@wdio/tracing-service`); the
// existing devtools UI uses its own denylist (`INTERNAL_COMMANDS`) — this map
// is for the trace.zip exporter to filter + rename in one step.

export interface TraceAction {
class: string
method: string
}

const ACTION_MAP: Record<string, TraceAction> = {
// WDIO browser-level
url: { class: 'Page', method: 'navigate' },
navigateTo: { class: 'Page', method: 'navigate' },
back: { class: 'Page', method: 'goBack' },
forward: { class: 'Page', method: 'goForward' },
refresh: { class: 'Page', method: 'reload' },
newWindow: { class: 'Page', method: 'goto' },
// Selenium WebDriver navigation (driver.get, driver.navigate().to/back/forward/refresh)
get: { class: 'Page', method: 'navigate' },
to: { class: 'Page', method: 'navigate' },
// WDIO element-level
click: { class: 'Element', method: 'click' },
doubleClick: { class: 'Element', method: 'dblclick' },
setValue: { class: 'Element', method: 'fill' },
selectByVisibleText: { class: 'Element', method: 'selectOption' },
moveTo: { class: 'Element', method: 'hover' },
scrollIntoView: { class: 'Element', method: 'scrollIntoViewIfNeeded' },
dragAndDrop: { class: 'Element', method: 'dragTo' },
// Selenium WebElement actions
sendKeys: { class: 'Element', method: 'fill' },
clear: { class: 'Element', method: 'clear' },
submit: { class: 'Element', method: 'submit' },
// Cross-runner
keys: { class: 'Keyboard', method: 'press' },
execute: { class: 'Page', method: 'evaluate' },
executeAsync: { class: 'Page', method: 'evaluate' },
switchToFrame: { class: 'Frame', method: 'goto' },
touchAction: { class: 'Element', method: 'tap' }
}

// Excluded by design:
// clearValue / addValue — WDIO fires these inside setValue (duplicate events).
// executeScript — Selenium's `until` polling fires it ~50ms; also recurses
// because @wdio/elements uses executeScript inside captureActionSnapshot.
// WDIO's user-facing `execute`/`executeAsync` are still captured.

export function mapCommandToAction(command: string): TraceAction | null {
return ACTION_MAP[command] ?? null
}

export function formatActionTitle(
action: TraceAction,
args: unknown[],
params?: Record<string, unknown>
): string {
const firstArg = args[0] ?? params?.selector
if (firstArg === undefined) {
return `${action.class}.${action.method}()`
}
const label = (
typeof firstArg === 'object' ? JSON.stringify(firstArg) : String(firstArg)
).slice(0, 80)
return `${action.class}.${action.method}("${label}")`
}
4 changes: 4 additions & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
// Framework-agnostic capture/reporter logic shared by @wdio/devtools-*
// adapters. See ARCHITECTURE.md §2 and CLAUDE.md §2.2.

export * from './action-mapping.js'
export * from './assert-patcher.js'
export * from './trace-exporter.js'
export * from './trace-har.js'
export * from './trace-zip-writer.js'
export * from './bidi.js'
export * from './console.js'
export * from './uid.js'
Expand Down
Loading
Loading