From f5903ee88cae22b33bcc812c9bd32fd312dfe596 Mon Sep 17 00:00:00 2001 From: Vince Graics Date: Tue, 16 Jun 2026 15:12:20 +0200 Subject: [PATCH 1/2] chore: Introduce bundling config file and type exports with tsup for @wdio/elements - Adding README --- packages/elements/.npmignore | 7 -- packages/elements/README.md | 145 +++++++++++++++++++++++++++++++ packages/elements/package.json | 12 ++- packages/elements/tsup.config.ts | 12 +++ 4 files changed, 167 insertions(+), 9 deletions(-) delete mode 100644 packages/elements/.npmignore create mode 100644 packages/elements/README.md create mode 100644 packages/elements/tsup.config.ts diff --git a/packages/elements/.npmignore b/packages/elements/.npmignore deleted file mode 100644 index 344b429f..00000000 --- a/packages/elements/.npmignore +++ /dev/null @@ -1,7 +0,0 @@ -src -node_modules -tests -index.html -tsconfig.json -vite.config.ts -*.tgz diff --git a/packages/elements/README.md b/packages/elements/README.md new file mode 100644 index 00000000..c1534289 --- /dev/null +++ b/packages/elements/README.md @@ -0,0 +1,145 @@ +# @wdio/elements + +Element detection and locator generation for WebdriverIO — browser DOM querying, mobile page-source parsing, accessibility tree extraction, and AI-readable snapshots. + +## Install + +```bash +npm install @wdio/elements +``` + +Requires `webdriverio` as a peer dependency (^9.0.0). + +## Usage + +### Unified entry point (auto-detects browser vs mobile) + +```ts +import { getElements } from '@wdio/elements' + +const result = await getElements(browser, { limit: 50, inViewportOnly: true }) +// { total, showing, hasMore, elements, tree? } +``` + +### Browser + +```ts +import { getInteractableBrowserElements } from '@wdio/elements' + +const elements = await getInteractableBrowserElements(browser, { + includeBounds: true, + inViewportOnly: true +}) +``` + +```ts +import { getBrowserAccessibilityTree } from '@wdio/elements' + +const nodes = await getBrowserAccessibilityTree(browser, { inViewportOnly: true }) +``` + +### Mobile + +```ts +import { getMobileVisibleElements } from '@wdio/elements' + +const elements = await getMobileVisibleElements(browser, 'ios', { + includeBounds: true, + includeContainers: false +}) +``` + +For the raw JSON element tree alongside the flat list: + +```ts +import { getMobileVisibleElementsWithTree } from '@wdio/elements' + +const { elements, tree } = await getMobileVisibleElementsWithTree(browser, 'android') +``` + +### AI-readable snapshots + +```ts +import { serializeWebSnapshot, serializeMobileSnapshot } from '@wdio/elements' + +const snapshot = await serializeWebSnapshot(browser, { includeBounds: true }) +// or +const snapshot = await serializeMobileSnapshot(browser, 'android', { includeLocators: true }) +``` + +### Locator generation + +```ts +import { generateAllElementLocators, xmlToJSON } from '@wdio/elements/locators' + +const locators = generateAllElementLocators(pageSource, { + platform: 'android', + viewportSize: { width: 1080, height: 2340 } +}) +``` + +## API + +### `getElements(browser, params)` + +Auto-detects platform and returns a unified result. + +| Param | Type | Default | Description | +|---|---|---|---| +| `inViewportOnly` | `boolean` | `true` | Skip off-screen elements | +| `includeContainers` | `boolean` | `false` | Include layout containers (mobile only) | +| `includeBounds` | `boolean` | `false` | Include element bounding boxes | +| `limit` | `number` | `0` | Max elements (0 = no limit) | +| `offset` | `number` | `0` | Pagination offset | + +### `getInteractableBrowserElements(browser, options)` + +Single `querySelectorAll` walk — returns flat list of interactable elements. + +### `getBrowserAccessibilityTree(browser, options)` + +Single DOM walk returning the accessibility tree as a flat `AccessibilityNode[]`. + +### `getMobileVisibleElements(browser, platform, options)` + +Parses page source XML (2 HTTP calls total) and returns elements with generated locators. + +### `getMobileVisibleElementsWithTree(browser, platform, options)` + +Same as above but also returns the raw `JSONElement` tree. + +### `serializeWebSnapshot(browser, options)` / `serializeMobileSnapshot(browser, platform, options)` + +Generate AI-readable (TOON-format) snapshots for LLM consumption. + +### `@wdio/elements/locators` + +Re-exports the full locator generation pipeline from `@wdio/devtools-core`: + +- `xmlToJSON`, `xmlToDOM`, `evaluateXPath`, `checkXPathUniqueness` +- `findDOMNodeByPath`, `parseAndroidBounds`, `parseIOSBounds` +- `flattenElementTree`, `countAttributeOccurrences`, `isAttributeUnique` +- `isInteractableElement`, `isLayoutContainer`, `hasMeaningfulContent` +- `shouldIncludeElement`, `getDefaultFilters` +- `getSuggestedLocators`, `getBestLocator`, `locatorsToObject` +- `generateAllElementLocators` + +## Types + +```ts +export type { + BrowserElementInfo, GetBrowserElementsOptions, + MobileElementInfo, GetMobileElementsOptions, + AccessibilityNode, + VisibleElementsResult, + WebSnapshotOptions, MobileSnapshotOptions +} from '@wdio/elements' + +// From @wdio/elements/locators: +export type { + ElementAttributes, JSONElement, Bounds, + FilterOptions, UniquenessResult, + LocatorStrategy, LocatorContext, + ElementWithLocators, GenerateLocatorsOptions +} from '@wdio/elements/locators' +``` diff --git a/packages/elements/package.json b/packages/elements/package.json index 3bdf2589..ae40c27c 100644 --- a/packages/elements/package.json +++ b/packages/elements/package.json @@ -3,6 +3,9 @@ "version": "1.0.0", "description": "Element detection scripts for WebdriverIO", "author": "Vince Graics", + "publishConfig": { + "access": "public" + }, "license": "MIT", "type": "module", "exports": { @@ -16,14 +19,19 @@ } }, "types": "./dist/index.d.ts", + "files": [ + "dist", + "README.md" + ], "repository": { "type": "git", "url": "git+https://github.com/webdriverio/devtools.git", "directory": "packages/elements" }, "scripts": { - "build": "tsup src/index.ts src/locators/index.ts --format esm --experimental-dts --sourcemap --clean --out-dir dist", - "watch": "tsup src/index.ts src/locators/index.ts --format esm --experimental-dts --sourcemap --watch --out-dir dist", + "build": "tsup", + "postbuild": "pnpm pack", + "watch": "tsup --watch", "lint": "eslint . --fix", "test": "vitest run", "prepublishOnly": "pnpm build" diff --git a/packages/elements/tsup.config.ts b/packages/elements/tsup.config.ts new file mode 100644 index 00000000..7e5c76ec --- /dev/null +++ b/packages/elements/tsup.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from 'tsup' + +export default defineConfig({ + entry: ['src/index.ts', 'src/locators/index.ts'], + format: ['esm'], + experimentalDts: true, + splitting: false, + sourcemap: true, + clean: true, + platform: 'node', + outDir: 'dist' +}) From 00262811c35dc8584f5083821e1caf9abffa8356 Mon Sep 17 00:00:00 2001 From: Vince Graics Date: Tue, 16 Jun 2026 15:59:32 +0200 Subject: [PATCH 2/2] refactor: Address simplifications in capturing consoleLogs across adapters - Removing postbuild: pnpm pack - Merging ROADMAP with SIMPLIFY_DEBT --- .../core/src/locators/locator-generation.ts | 14 +- packages/core/src/session-capturer.ts | 14 +- packages/core/src/trace-exporter.ts | 8 +- packages/elements/ROADMAP.md | 202 +++++++++++++----- packages/elements/package.json | 1 - packages/nightwatch-devtools/src/session.ts | 19 +- packages/selenium-devtools/src/session.ts | 20 +- packages/service/src/session.ts | 20 +- 8 files changed, 168 insertions(+), 130 deletions(-) diff --git a/packages/core/src/locators/locator-generation.ts b/packages/core/src/locators/locator-generation.ts index a66bdf49..8586ff66 100644 --- a/packages/core/src/locators/locator-generation.ts +++ b/packages/core/src/locators/locator-generation.ts @@ -418,14 +418,12 @@ function getSimpleSuggestedLocators( element: JSONElement, ctx: LocatorContext, automationName: string, + inUiAutomatorScope: boolean, targetNode?: XMLNode ): [LocatorStrategy, string][] { const results: [LocatorStrategy, string][] = [] const isAndroid = automationName.toLowerCase().includes('uiautomator') const attrs = element.attributes - const inUiAutomatorScope = isAndroid - ? isInUiAutomatorScope(element, ctx.parsedDOM) - : true if (isAndroid) { // Resource ID @@ -526,13 +524,11 @@ function getComplexSuggestedLocators( element: JSONElement, ctx: LocatorContext, automationName: string, + inUiAutomatorScope: boolean, targetNode?: XMLNode ): [LocatorStrategy, string][] { const results: [LocatorStrategy, string][] = [] const isAndroid = automationName.toLowerCase().includes('uiautomator') - const inUiAutomatorScope = isAndroid - ? isInUiAutomatorScope(element, ctx.parsedDOM) - : true if (isAndroid) { if (inUiAutomatorScope) { @@ -594,16 +590,22 @@ export function getSuggestedLocators( isAndroid: automationName.toLowerCase().includes('uiautomator') } + const inUiAutomatorScope = locatorCtx.isAndroid + ? isInUiAutomatorScope(element, locatorCtx.parsedDOM) + : true + const simpleLocators = getSimpleSuggestedLocators( element, locatorCtx, automationName, + inUiAutomatorScope, targetNode ) const complexLocators = getComplexSuggestedLocators( element, locatorCtx, automationName, + inUiAutomatorScope, targetNode ) diff --git a/packages/core/src/session-capturer.ts b/packages/core/src/session-capturer.ts index 115e8360..25505840 100644 --- a/packages/core/src/session-capturer.ts +++ b/packages/core/src/session-capturer.ts @@ -455,17 +455,17 @@ export abstract class SessionCapturerBase { // ── Hooks (subclasses override) ───────────────────────────────────────── /** - * Default: forward a single ConsoleLog via the `consoleLogs` scope. - * Args is passed as an array (matching the original console.* call shape: - * `console.log('a', 'b')` → `args = ['a', 'b']`) so subclasses can preserve - * the multi-argument structure for the UI. + * Build a ConsoleLog entry from the captured line, push it into the local + * `consoleLogs` array (so it ends up in any future trace export), and + * broadcast it live via the `consoleLogs` WS scope. * - * Subclasses that need to maintain local capture state (for the rerun/ - * replay flow) should override to also push the entry into their own - * array — see service's onLine override. + * Args is passed as an array (matching the original console.* call shape: + * `console.log('a', 'b')` → `args = ['a', 'b']`) so the multi-argument + * structure is preserved for the UI. */ protected onLine(type: LogLevel, args: string[], source: LogSource): void { const entry = createConsoleLogEntry(type, args, source) + this.consoleLogs.push(entry) this.sendUpstream('consoleLogs', [entry]) } diff --git a/packages/core/src/trace-exporter.ts b/packages/core/src/trace-exporter.ts index 8280895e..a7d1cb8a 100644 --- a/packages/core/src/trace-exporter.ts +++ b/packages/core/src/trace-exporter.ts @@ -402,7 +402,8 @@ function buildTraceBundle( const pageId = `page@${idPrefix}` const viewport = trace.metadata.viewport ?? { width: 1280, height: 720 } const snapshots = trace.actionSnapshots ?? [] - const events: TraceEvent[] = [buildContextOptions(trace, contextId, wallTime)] + const ctxOptions = buildContextOptions(trace, contextId, wallTime) + const events: TraceEvent[] = [ctxOptions] // Emit initial screencast-frame (timestamp=0) using the first snapshot's // resources so trace viewers show the page state before any interaction. @@ -438,10 +439,7 @@ function buildTraceBundle( ...buildActionEvents(trace.commands, pageId, wallTime) ) events.sort(compareEvents) - const caps = trace.metadata.capabilities as - | Record - | undefined - const ctxBName = resolveContextNaming(caps).title + const ctxBName = ctxOptions.title return { traceNdjson: events.map((e) => JSON.stringify(e)).join('\n') + '\n', networkNdjson: buildNetworkNdjson(trace.networkRequests, wallTime, pageId), diff --git a/packages/elements/ROADMAP.md b/packages/elements/ROADMAP.md index f387fb4b..e1eeeff6 100644 --- a/packages/elements/ROADMAP.md +++ b/packages/elements/ROADMAP.md @@ -1,81 +1,169 @@ -# @wdio/elements Roadmap +# Roadmap -## Current state (May 2026) +Prioritized work items for the devtools monorepo. Items are ordered by priority +within each phase; dependencies are noted inline. -The package delivers LLM-readable element snapshots for both web and mobile: +--- -| Capability | Web | Mobile | -|---|---|---| -| Interactable element list | `getInteractableBrowserElements()` | `getMobileVisibleElements()` | -| Semantic tree | `getBrowserAccessibilityTree()` | *(raw `JSONElement` only)* | -| Snapshot serialization | `serializeWebSnapshot()` | `serializeMobileSnapshot()` | -| Unified API | `getElements()` returns both | `getElements()` returns both | -| Viewport filtering | `inViewportOnly` (default true) | `inViewportOnly` (default true) | -| Role classification | Computed in-browser from tag/ARIA | `ANDROID_ROLE_MAP` / `IOS_ROLE_MAP` in snapshot.ts | -| Locator generation | CSS selectors in browser script | `getSuggestedLocators()` from locator-generation.ts | -| Context disambiguation | `∈` via `inferPurpose()` | `∈` via `mobileInferPurpose()` | -| Duplicate selector indexing | N/A (selectors are unique) | `.instance(N)` suffix | +## Phase 1 — Foundation (tests + safe extractions) -## Architectural concerns +Before touching the hot paths, add coverage and extract non-breaking helpers. -### 1. Two independent mobile pipelines +### 1.1 Locator-generation unit tests -`serializeMobileSnapshot` in `snapshot.ts` has its own copies of: +`packages/core/src/locators/` has zero tests. Every item downstream (XML parse +dedup, interactive-element merge, `isInUiAutomatorScope` fix) needs this safety +net first. -- **Role classification** — `ANDROID_ROLE_MAP` / `IOS_ROLE_MAP` duplicate logic from `locators/constants.ts` and `locators/element-filter.ts`. -- **Interactivity detection** — `isMobileInteractive()` shadows `isInteractableElement()` from `element-filter.ts`. They use different criteria (tag-based vs attribute-based) and can disagree. -- **Locator generation** — `getBestAndroidLocator()` / `getBestIOSLocator()` are simplified fallbacks. The full pipeline (`getSuggestedLocators()`) is now wired in when source XML is available, but the fallback still exists and the two paths can produce different selectors for the same element. +**Effort:** ~3 hours. **Depends on:** nothing. -These should be collapsed: `serializeMobileSnapshot` should consume pre-computed roles, interactivity flags, and selectors from the locator pipeline, not recompute them. +### 1.2 Snapshot-output golden tests -### 2. No mobile equivalent of `getBrowserAccessibilityTree()` +`serializeMobileSnapshot` and `serializeWebSnapshot` need output-fidelity tests +before the interactive-element dedup or mobile pipeline unification. Without +them, any change to the snapshot format is a blind regression risk for LLM +consumers. -The web path returns a flat `AccessibilityNode[]` with roles, names, selectors, depths, and state. The mobile path returns a raw `JSONElement` tree — the snapshot does all enrichment internally via `collectMobileNodes()` → `MobileFlatNode[]` (a private interface). There is no public function to get an enriched flat node list for mobile. +**Effort:** ~2 hours. **Depends on:** nothing. -**Proposal:** Extract `collectMobileNodes()` into a public `getMobileAccessibilityTree()` that returns `MobileFlatNode[]` (or a shared type). `serializeMobileSnapshot()` becomes a pure formatting pass — like `serializeWebSnapshot()` already is. +### 1.3 Extract `getMobileAccessibilityTree()` -### 3. Layout noise in mobile snapshots +Expose `collectMobileNodes()` as a public `getMobileAccessibilityTree()` +returning `MobileFlatNode[]`. `serializeMobileSnapshot()` becomes a pure +formatting pass — matching `serializeWebSnapshot()`'s shape. -The Android view hierarchy includes every layout container (`FrameLayout`, `LinearLayout`, `ViewGroup`, etc.). The current noise filter (`NOISY_ROLES`) collapses anonymous containers at depth ≥ 2, but named containers and depth 0-1 scaffolding still appear. The web a11y tree doesn't have this problem because the browser's accessibility computation already skips layout-only `
`s. +**Effort:** ~2 hours. **Depends on:** 1.2 (golden tests). **Unblocks:** 2.1, 2.2. -**Proposal:** A `collapseContainers` option on the snapshot (default `true`) that skips any container without an interactive descendant. Alternatively, the tree collection pass could flag "informative" vs "structural" containers and let the renderer decide. +--- -### 4. Selector format for mobile +## Phase 2 — Core dedup & unification -Mobile selectors are Appium/WDIO-specific strings (`~Accessibility`, `android=new UiSelector()...`, `id:com.example:id/foo`). The web path outputs CSS selectors (`a*=Highlights`, `#cart-icon-bubble`). An LLM/agent needs different selector parsing logic per platform. There's no common selector abstraction. +### 2.1 Merge interactive-element checks -**Proposal:** A `SelectorString` type with platform-aware parsing, or at minimum consistent prefix conventions documented for LLM consumption. +`element-snapshot.ts` duplicates `locators/element-filter.ts`: +- `isMobileInteractive` / `isExplicitlyInteractive` → use + `isInteractableElement` +- `isMobileInViewport` → use shared `isWithinViewport` +- `inferPurpose` / `mobileInferPurpose` → parameterize the role-skip + predicate +- Static-text echo dedup → extract shared helper -### 5. The raw tree doesn't carry locators unless processed +**Effort:** ~3 hours. **Depends on:** 1.2, 1.3. -`getMobileVisibleElementsWithTree()` returns `{ elements, tree }` where `tree` is the raw `xmlToJSON()` output. Locators are only on `elements` (from `generateAllElementLocators()`). The snapshot reads locators by running `getSuggestedLocators()` again (or falling back). If a consumer wants to annotate the tree themselves, they must re-run the locator pipeline. +### 2.2 Unify mobile pipeline role classification -**Proposal:** Enrich the tree in-place during `generateAllElementLocators()` — attach `_selector`, `_role`, and `_interactive` attributes to each `JSONElement` node that passes the filter. The raw tree becomes self-describing. +`serializeMobileSnapshot` has its own copies of `ANDROID_ROLE_MAP` / +`IOS_ROLE_MAP`, interactivity detection, and locator fallbacks +(`getBestAndroidLocator` / `getBestIOSLocator`). Thread `_role`, `_interactive`, +and `_selector` through via the shared tree so `serializeMobileSnapshot` doesn't +re-classify what `generateAllElementLocators` already computed. -## Improvement backlog +**Effort:** ~4 hours. **Depends on:** 1.3, 2.1. **Unblocks:** 2.3. -| Priority | What | Effort | -|---|---|---| -| P0 | Merge `isMobileInteractive` + role classification into `generateAllElementLocators` — one source of truth | Medium | -| P1 | Extract `getMobileAccessibilityTree()` as a public API returning enriched flat nodes | Medium | -| P1 | Enrich `JSONElement` tree nodes with locators during `generateAllElementLocators()` | Small | -| P2 | `collapseContainers` option on `serializeMobileSnapshot` | Small | -| P2 | Unify web + mobile serialization into a single `serializeSnapshot()` function | Large | -| P3 | Document selector format conventions for LLM consumption | Small | -| P3 | Add `checked`/`selected`/`expanded` state rendering to mobile snapshot (parity with web) | Small | +### 2.3 Thread parsed XML through to both consumers -## Verified capabilities +`pageSource` XML is parsed 3× per mobile snapshot (`xmlToJSON` twice, +`xmlToDOM` once). Pass the initial `jsonTree` through to both +`serializeMobileSnapshot` and `generateAllElementLocators`. Also removes the +redundant second `getSuggestedLocators` call per element. -- [x] Web: viewport-only snapshot with semantic roles and unique CSS selectors -- [x] Web: `∈` disambiguation for duplicate selectors (6 "Add to Wishlist" buttons → each with book title context) -- [x] Web: `statictext` role capturing visible text (book titles, promo copy, cookie text) -- [x] Web: deduplication of echoed text (child text already in parent name → skipped) -- [x] Mobile: semantic role mapping (TextView→statictext, ImageView→img, Button→button, etc.) -- [x] Mobile: full-pipeline selectors via `getSuggestedLocators()` wired into snapshot -- [x] Mobile: `~` prefix for accessibility-id, `id:` for resource-id, `android=new UiSelector()...` for compound -- [x] Mobile: `.instance(N)` indexing for duplicate selectors -- [x] Mobile: explicit tap-target promotion (clickable parent carries `→`, label children provide `∈` context) -- [x] Mobile: layout noise collapse for anonymous containers -- [x] Mobile: `∈` context from actual parent, not previous list-item sibling -- [x] Unified `getElements()` API returning `{ elements, tree }` for both platforms -- [x] `inViewportOnly` default `true` across all entry points with per-function toggles +**Effort:** ~1 day. **Depends on:** 1.1, 2.2. + +--- + +## Phase 3 — Hot-path & trace fixes + +### 3.1 `isInUiAutomatorScope` computed once per element + +Both `getSimpleSuggestedLocators` and `getComplexSuggestedLocators` call it for +the same element. Compute once in `getSuggestedLocators`, pass as parameter. + +**Effort:** ~15 min. **Depends on:** 1.1. + +### 3.2 `resolveContextNaming(caps)` called once + +`buildContextOptions` and `buildTraceBundle` both call it with the same args. +The title is already available on `events[0]`. Thread it through. + +**Effort:** ~15 min. **Depends on:** nothing. + +### 3.3 Extract `makeScreencastFrame` helper + +The initial t=0 frame manually duplicates `buildScreencastFrames`'s +construction logic. Extract a shared `makeScreencastFrame` helper; use for both +t=0 and subsequent frames. + +**Effort:** ~20 min. **Depends on:** nothing. + +### 3.4 Mobile layout-noise filter made configurable + +Add `collapseContainers` option to `MobileSnapshotOptions` so consumers can +adjust the `NOISY_ROLES` threshold or disable the filter. + +**Effort:** ~30 min. **Depends on:** nothing. + +### 3.5 `selectBestLocators` in elements wrapper delegates to core + +`mobile-elements.ts`'s `LOCATOR_PRIORITY` and `selectBestLocators` duplicate +`getBestLocator` from core. Route through the re-export. Verify no external +consumers break. + +**Effort:** ~30 min. **Depends on:** nothing. + +--- + +## Phase 4 — Adapter convergence + +### 4.1 `onLine` override unified in base class + +All three adapters duplicate the same `onLine` override. Move the +`consoleLogs.push` into `SessionCapturerBase.onLine` after resolving the type +mismatch between `createConsoleLogEntry`'s return type and the stored array. + +**Effort:** ~30 min. **Depends on:** nothing. + +### 4.2 `CommandLog.startTime` populated in nightwatch + selenium + +Nightwatch and selenium never set `startTime`, so command durations are always +0ms in their traces. Add start-timestamp capture to the nightwatch +`browserProxy` and selenium `onCommand` hooks. + +**Effort:** ~2 hours. **Depends on:** nothing (framework-specific wiring). + +### 4.3 Nightwatch `traceMode` moved off capturer field + +`SessionCapturer.traceMode` is a mutable public field set from +`session-init.ts`. Neither service nor selenium store it on the capturer. Thread +`mode` through an options bag to `captureCommand()`. Watch for breaking changes +to the nightwatch browser proxy signature. + +**Effort:** ~1 hour. **Depends on:** nothing. + +### 4.4 Unify `replaceCommand` logic + +Nightwatch and selenium both implement ~35 lines of entry-find, error-serialize, +command-counter-increment, and push logic. Unify under +`SessionCapturerBase` with a parameterized splice-vs-mutate policy. + +**Effort:** ~1.5 hours. **Depends on:** nothing. + +--- + +## Completed + +- **Snapshot promise drain** — Nightwatch and selenium now drain + `snapshotCaptures` via `Promise.allSettled` before writing the trace zip + (2026-06-16). + +--- + +## Deferred + +Items considered but not prioritized: + +- **`INTERNAL_COMMANDS` extraction** — only `'execute'` overlaps across all + three adapters. Not worth extracting. +- **`SelectorString` type** — no concrete consumer exists. Revisit if an LLM + consumer needs to parse selector strings. +- **Unify web + mobile serialization** — blocked on 1.3. The renderers are too + different in input types and passes to unify directly; revisit after Phase 2. diff --git a/packages/elements/package.json b/packages/elements/package.json index ae40c27c..3373e05e 100644 --- a/packages/elements/package.json +++ b/packages/elements/package.json @@ -30,7 +30,6 @@ }, "scripts": { "build": "tsup", - "postbuild": "pnpm pack", "watch": "tsup --watch", "lint": "eslint . --fix", "test": "vitest run", diff --git a/packages/nightwatch-devtools/src/session.ts b/packages/nightwatch-devtools/src/session.ts index 7fc70e42..9b5d0eda 100644 --- a/packages/nightwatch-devtools/src/session.ts +++ b/packages/nightwatch-devtools/src/session.ts @@ -2,13 +2,11 @@ import http from 'node:http' import logger from '@wdio/logger' import { SessionCapturerBase, - createConsoleLogEntry, errorMessage, loadInjectableScript, mapChromeBrowserLogs, pollUntilReady, - serializeError, - type LogSource + serializeError } from '@wdio/devtools-core' import { mapCommandToAction } from '@wdio/devtools-core' import { captureActionSnapshot } from './action-snapshot.js' @@ -28,7 +26,6 @@ import type { ActionSnapshot, CommandLog, DevToolsMode, - LogLevel, NightwatchBrowser } from './types.js' @@ -140,20 +137,6 @@ export class SessionCapturer extends SessionCapturerBase { log.info('Worker WebSocket disconnected') } - /** - * Push every captured line into the local `consoleLogs` array so it ends up - * in any future trace export, in addition to the live WS broadcast. - */ - protected override onLine( - type: LogLevel, - args: string[], - source: LogSource - ): void { - const entry = createConsoleLogEntry(type, args, source) - this.consoleLogs.push(entry) - this.sendUpstream('consoleLogs', [entry]) - } - async captureCommand( command: string, args: unknown[], diff --git a/packages/selenium-devtools/src/session.ts b/packages/selenium-devtools/src/session.ts index 70cbe98c..5bb749a9 100644 --- a/packages/selenium-devtools/src/session.ts +++ b/packages/selenium-devtools/src/session.ts @@ -1,18 +1,16 @@ import logger from '@wdio/logger' import { SessionCapturerBase, - createConsoleLogEntry, errorMessage, loadInjectableScript, mapChromeBrowserLogs, pollUntilReady, - serializeError, - type LogSource + serializeError } from '@wdio/devtools-core' import { WS_SCOPE } from '@wdio/devtools-shared' import { NAVIGATION_COMMANDS } from './constants.js' import { getDriverOriginals } from './driverPatcher.js' -import type { CommandLog, LogLevel, SeleniumDriverLike } from './types.js' +import type { CommandLog, SeleniumDriverLike } from './types.js' const log = logger('@wdio/selenium-devtools:SessionCapturer') @@ -77,20 +75,6 @@ export class SessionCapturer extends SessionCapturerBase { } } - /** - * Push every captured line into the local `consoleLogs` array so it ends up - * in any future trace export, in addition to the live WS broadcast. - */ - protected override onLine( - type: LogLevel, - args: string[], - source: LogSource - ): void { - const entry = createConsoleLogEntry(type, args, source) - this.consoleLogs.push(entry) - this.sendUpstream('consoleLogs', [entry]) - } - setDriver(driver: SeleniumDriverLike) { this.#driver = driver } diff --git a/packages/service/src/session.ts b/packages/service/src/session.ts index cc684234..dd58e083 100644 --- a/packages/service/src/session.ts +++ b/packages/service/src/session.ts @@ -14,13 +14,11 @@ import { LOG_SOURCES, SessionCapturerBase, applyPerformanceData, - createConsoleLogEntry, errorMessage, getRequestType, - type CapturedPerformancePayload, - type LogSource + type CapturedPerformancePayload } from '@wdio/devtools-core' -import type { CommandLog, LogLevel } from './types.js' +import type { CommandLog } from './types.js' const log = logger('@wdio/devtools-service:SessionCapturer') @@ -51,20 +49,6 @@ export class SessionCapturer extends SessionCapturerBase { log.error(`Couldn't connect to devtools backend: ${errorMessage(err)}`) } - /** - * Push every captured line into the local `consoleLogs` array so it ends up - * in the final trace payload, in addition to the live WS broadcast. - */ - protected override onLine( - type: LogLevel, - args: string[], - source: LogSource - ): void { - const entry = createConsoleLogEntry(type, args, source) - this.consoleLogs.push(entry as ConsoleLogs) - this.sendUpstream('consoleLogs', [entry]) - } - // Cucumber step files never appear on the WebDriver call stack; // the reporter feeds their paths here so the Source tab can resolve them. async ensureSourceLoaded(location?: string): Promise {