From 7f5360103aca7198bc89189402ffec9cb8918c25 Mon Sep 17 00:00:00 2001 From: Vince Graics Date: Mon, 19 Oct 2026 18:13:08 +0200 Subject: [PATCH 01/12] refactor(elements): produce the element snapshot with @wdio/snapshot The hand-rolled snapshot builder drifted from what the wdio CLI prints for the same page. Delegating to @wdio/snapshot makes getSnapshot() output match `wdio session snapshot`. @wdio/snapshot requires webdriverio 10, so the peer range narrows to ^10.0.0; the service's getSnapshot command follows the new options/result shape. Co-Authored-By: Claude Opus 5.5 --- .changeset/snapshot-matches-the-wdio-cli.md | 9 + packages/core/package.json | 4 - packages/core/src/element-snapshot.ts | 238 +----- packages/core/src/element-types.ts | 32 - packages/core/tests/element-snapshot.test.ts | 35 - packages/core/tests/mobile-snapshot.test.ts | 100 +++ packages/elements/README.md | 59 +- packages/elements/package.json | 6 +- packages/elements/src/get-snapshot.ts | 203 ++--- packages/elements/src/index.ts | 12 +- packages/elements/src/mobile-elements.ts | 6 - packages/elements/src/snapshot.ts | 20 - packages/elements/tests/get-snapshot.test.ts | 199 +++++ .../tests/locators/locator-generation.test.ts | 2 +- packages/elements/tests/snapshot.test.ts | 697 ------------------ packages/elements/tsconfig.json | 2 +- packages/service/src/index.ts | 13 +- packages/service/src/types.ts | 4 +- packages/shared/src/locator-dialect.ts | 6 - packages/shared/src/snapshot-format.ts | 3 +- packages/shared/tests/locator-dialect.test.ts | 16 +- pnpm-lock.yaml | 20 + 22 files changed, 444 insertions(+), 1242 deletions(-) create mode 100644 .changeset/snapshot-matches-the-wdio-cli.md delete mode 100644 packages/core/tests/element-snapshot.test.ts create mode 100644 packages/core/tests/mobile-snapshot.test.ts delete mode 100644 packages/elements/src/snapshot.ts create mode 100644 packages/elements/tests/get-snapshot.test.ts delete mode 100644 packages/elements/tests/snapshot.test.ts diff --git a/.changeset/snapshot-matches-the-wdio-cli.md b/.changeset/snapshot-matches-the-wdio-cli.md new file mode 100644 index 00000000..19c4c38b --- /dev/null +++ b/.changeset/snapshot-matches-the-wdio-cli.md @@ -0,0 +1,9 @@ +--- +'@wdio/elements': major +--- + +`getSnapshot` now produces the same text as `wdio session snapshot --selectors`, by delegating to `@wdio/snapshot` instead of keeping a second renderer that drifted from the CLI's. Agents that read the CLI's output and agents that call this function now see one format. + +Breaking: it returns `{ text, refs }` rather than `{ text, elements }`, takes `{ viewport }` (default `false`, the full page) in place of `{ inViewportOnly }` (default `true`), no longer prefixes a `[Page: …]` header, and throws on failure instead of returning a placeholder. `serializeWebSnapshot`, `serializeMobileSnapshot` and their option and result types are removed. Requires `webdriverio` ^10. + +`@wdio/devtools-service` now consumes this version, so its `browser.getSnapshot()` returns the same `{ text, refs }` (defaulting to `{ viewport: true }`). diff --git a/packages/core/package.json b/packages/core/package.json index 7f63b1bd..5f1c44b8 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -24,10 +24,6 @@ "types": "./src/locators/index.ts", "default": "./src/locators/index.ts" }, - "./element-snapshot": { - "types": "./src/element-snapshot.ts", - "default": "./src/element-snapshot.ts" - }, "./element-scripts": { "types": "./src/element-scripts.ts", "default": "./src/element-scripts.ts" diff --git a/packages/core/src/element-snapshot.ts b/packages/core/src/element-snapshot.ts index 1f627724..153ca01b 100644 --- a/packages/core/src/element-snapshot.ts +++ b/packages/core/src/element-snapshot.ts @@ -5,13 +5,6 @@ * text files that LLMs can consume without any parsing. */ -import type { - AccessibilityNode, - SnapshotNode, - SnapshotElement, - SnapshotResult -} from './element-types.js' - // The web serializer lives in `trace` because the backend builds the trace for // an adapter that cannot, and §2.2 bars the backend from importing core. export { @@ -21,11 +14,8 @@ export { import { SNAPSHOT_INDENT_UNIT, - INTERACTIVE_ROLES, - isStatictextEchoedByParent, SNAPSHOT_LOCATOR_DELIM, - SNAPSHOT_PURPOSE_TOKEN, - xpathLocatorTag + SNAPSHOT_PURPOSE_TOKEN } from '@wdio/devtools-shared' import type { JSONElement } from './locators/types.js' @@ -675,229 +665,3 @@ export function serializeMobileSnapshot( const lines = renderMobileNodes(nodes) return [header, ...lines].join('\n') } - -// --------------------------------------------------------------------------- -// Unified snapshot formatter — web + mobile share the same render pass. -// --------------------------------------------------------------------------- - -/** Derive a tag name from a locator prefix (e.g. `//button[contains(., "Submit")]` - * or "button*=Submit" → "button"). */ -function extractTagFromSelector(selector: string, fallback: string): string { - const xpathTag = xpathLocatorTag(selector) - if (xpathTag) { - return xpathTag - } - // Matches tag name followed by a CSS selector combinator or operator. - // Supports hyphenated custom elements (my-component) and pseudo-classes (:nth-of-type). - const match = selector.match( - /^([a-z][a-z0-9]*(?:-[a-z][a-z0-9]*)*)[*.#\[:=(^$~]/ - ) - if (match) { - return match[1] - } - const spaceMatch = selector.match(/^([a-z][a-z0-9]*(?:-[a-z][a-z0-9]*)*)\s/) - if (spaceMatch) { - return spaceMatch[1] - } - return fallback -} - -/** Walk backwards to find the nearest structural container name for ∈ context. */ -function findContextName( - nodes: SnapshotNode[], - index: number -): string | undefined { - const myDepth = nodes[index].depth - for (let i = index - 1; i >= 0; i--) { - if (nodes[i].depth <= myDepth && nodes[i].name) { - if (nodes[i].depth === myDepth && nodes[i].isInteractive) { - continue - } - // Suppressed tag-interactive nodes (isInteractive=false but role is a - // mobile-interactable label like 'statictext') shouldn't provide context - // at same depth — their name is label text, not a container identity. - // Mirrors mobileInferPurpose which skips same-depth nodes not in - // MOBILE_STRUCTURAL_ROLES (which excludes 'statictext'). - if (nodes[i].depth === myDepth && nodes[i].role === 'statictext') { - continue - } - return nodes[i].name - } - } - return undefined -} - -/** - * Core formatter — converts a flat SnapshotNode[] into a text tree with - * eN virtual IDs and an elements map for selector resolution. - * - * Platform-agnostic: both web and mobile pipelines feed into this function. - */ -export function buildSnapshot( - header: string, - nodes: SnapshotNode[] -): SnapshotResult { - const selectorCounts = new Map() - for (const node of nodes) { - if (node.isInteractive && node.selector) { - selectorCounts.set( - node.selector, - (selectorCounts.get(node.selector) ?? 0) + 1 - ) - } - } - - const lines: string[] = [header] - const elements: Record = {} - const selectorIndex = new Map() - let counter = 1 - - for (let i = 0; i < nodes.length; i++) { - const node = nodes[i] - const indent = ' '.repeat(node.depth + 1) - - if (node.isInteractive && node.selector) { - let selector = node.selector - const total = selectorCounts.get(selector) ?? 1 - if (total > 1) { - const idx = selectorIndex.get(selector) ?? 0 - selectorIndex.set(selector, idx + 1) - selector = `${selector}.instance(${idx})` - } - - const eId = `e${counter++}` - - const roleLabel = - node.role === 'heading' && node.level - ? `heading[${node.level}]` - : node.role - - const context = findContextName(nodes, i) - if (node.name && context) { - lines.push( - `${indent}${eId} ${roleLabel} "${node.name}" ∈ "${context}" → ${selector}` - ) - } else if (node.name) { - lines.push( - `${indent}${eId} ${roleLabel} "${node.name}" → ${selector}` - ) - } else if (context) { - lines.push( - `${indent}${eId} ${roleLabel} ∈ "${context}" → ${selector}` - ) - } else { - lines.push(`${indent}${eId} ${roleLabel} → ${selector}`) - } - - elements[eId] = { - selector: node.selector, - ...(selector !== node.selector ? { qualifiedSelector: selector } : {}), - tagName: node.tagName, - role: node.role, - text: node.name - } - } else { - const roleLabel = - node.role === 'heading' && node.level - ? `heading[${node.level}]` - : node.role - lines.push( - node.name - ? `${indent}${roleLabel} "${node.name}"` - : `${indent}${roleLabel}` - ) - } - } - - return { text: lines.join('\n'), elements } -} - -// --------------------------------------------------------------------------- -// Web adapter — AccessibilityNode[] → SnapshotNode[] -// --------------------------------------------------------------------------- - -export function accessibilityNodesToSnapshotNodes( - nodes: AccessibilityNode[], - options?: { inViewportOnly?: boolean } -): SnapshotNode[] { - const { inViewportOnly = true } = options ?? {} - - const result: SnapshotNode[] = [] - - for (let i = 0; i < nodes.length; i++) { - const node = nodes[i] - - if (inViewportOnly && node.isInViewport === false) { - continue - } - - const isInteractive = INTERACTIVE_ROLES.has(node.role) - - if (isStatictextEchoedByParent(nodes, i)) { - continue - } - - // Interactive nodes without a selector can't be acted on — skip them - // so they don't leak as non-actionable entries in the text tree. - // Matches the guard in serializeWebSnapshot (line 141). - if (isInteractive && !node.selector) { - continue - } - - const tagName = - isInteractive && node.selector - ? extractTagFromSelector(node.selector, node.role) - : node.role - - result.push({ - role: node.role, - name: node.name, - selector: node.selector, - depth: node.depth, - isInteractive, - tagName, - level: node.level || undefined - }) - } - - return result -} - -// --------------------------------------------------------------------------- -// Mobile adapter — JSONElement tree → SnapshotNode[] -// --------------------------------------------------------------------------- - -export function jsonElementToSnapshotNodes( - root: JSONElement, - platform: 'android' | 'ios', - options?: { - inViewportOnly?: boolean - viewport?: { width: number; height: number } - sourceXML?: string - } -): SnapshotNode[] { - const { inViewportOnly = true } = options ?? {} - const effectiveViewport = options?.viewport ?? { width: 9999, height: 9999 } - const automationName = platform === 'android' ? 'uiautomator2' : 'xcuitest' - const effectiveXML = options?.sourceXML || root.attributes._sourceXML - - const mobileNodes: MobileFlatNode[] = [] - collectMobileNodes(root, platform, 0, mobileNodes, { - inViewportOnly, - viewport: effectiveViewport, - sourceXML: effectiveXML, - automationName: effectiveXML ? automationName : undefined - }) - - suppressTagOnlyChildren(mobileNodes) - - return mobileNodes.map((m) => ({ - role: m.role, - name: m.name, - selector: m.selector, - depth: m.depth, - isInteractive: m.isInteractive, - tagName: m.tagName, - level: undefined - })) -} diff --git a/packages/core/src/element-types.ts b/packages/core/src/element-types.ts index 694b6735..1e2a269f 100644 --- a/packages/core/src/element-types.ts +++ b/packages/core/src/element-types.ts @@ -17,35 +17,3 @@ export type { // Re-export mobile types from locators for convenience. // Downstream consumers can also import directly from @wdio/devtools-core/locators. export type { JSONElement } from './locators/types.js' - -/** - * Flat intermediate node shared by both web and mobile snapshot pipelines. - * Both adapters (web: AccessibilityNode[], mobile: JSONElement tree) convert - * to this shape before buildSnapshot() renders them. - */ -export interface SnapshotNode { - role: string - name: string - selector: string - depth: number - isInteractive: boolean - tagName: string - level?: number | string -} - -/** Entry in the elements map returned by getSnapshot(). */ -export interface SnapshotElement { - /** Raw selector — may need .instance(N) on mobile when duplicates exist. */ - selector: string - /** Selector with .instance(N) suffix — only set when it differs from `selector` (duplicate disambiguation). */ - qualifiedSelector?: string - tagName: string - role: string - text: string -} - -/** Return type of getSnapshot(). */ -export interface SnapshotResult { - text: string - elements: Record -} diff --git a/packages/core/tests/element-snapshot.test.ts b/packages/core/tests/element-snapshot.test.ts deleted file mode 100644 index 245e42da..00000000 --- a/packages/core/tests/element-snapshot.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// @vitest-environment happy-dom -import { beforeEach, describe, expect, it } from 'vitest' - -import { accessibilityTreeScript } from '@wdio/devtools-shared' -import type { AccessibilityNode } from '@wdio/devtools-shared' -import { accessibilityNodesToSnapshotNodes } from '../src/element-snapshot.js' - -/** The script is injectable source, so it is run the way the adapters run it. */ -function a11yNodes(html: string, runner: 'mocha' | 'nightwatch') { - document.body.innerHTML = html - return new Function( - `return ${accessibilityTreeScript(false, runner)}` - )() as AccessibilityNode[] -} - -beforeEach(() => { - // happy-dom has no layout engine, so the script's visibility gate would reject - // every element and the walk would return nothing. - Element.prototype.checkVisibility = () => true -}) - -describe('accessibilityNodesToSnapshotNodes', () => { - it('yields its tag from a captured locator in either dialect', () => { - // The serializer reads the tag back out of the locator it was handed, so a - // dialect it can't parse would report the ARIA role as the tag instead. - for (const runner of ['mocha', 'nightwatch'] as const) { - const nodes = accessibilityNodesToSnapshotNodes( - a11yNodes(' Logout', runner), - { inViewportOnly: false } - ) - - expect(nodes.find((n) => n.name === 'Logout')?.tagName).toBe('a') - } - }) -}) diff --git a/packages/core/tests/mobile-snapshot.test.ts b/packages/core/tests/mobile-snapshot.test.ts new file mode 100644 index 00000000..ef8a4fc6 --- /dev/null +++ b/packages/core/tests/mobile-snapshot.test.ts @@ -0,0 +1,100 @@ +import { describe, expect, it } from 'vitest' + +import { serializeMobileSnapshot } from '../src/element-snapshot.js' +import type { JSONElement } from '../src/locators/types.js' + +function mobileEl( + tagName: string, + attrs: JSONElement['attributes'], + children: JSONElement[] = [] +): JSONElement { + return { tagName, attributes: attrs, children, path: '' } +} + +describe('serializeMobileSnapshot', () => { + it('produces a platform header with device and viewport', () => { + const root = mobileEl('hierarchy', {}) + const out = serializeMobileSnapshot(root, { + platform: 'android', + deviceName: 'Pixel 7', + viewport: { width: 412, height: 915 } + }) + expect(out).toMatch('[android — Pixel 7 (412×915)]') + }) + + it('renders interactive Android element with accessibility-id locator', () => { + const root = mobileEl('hierarchy', {}, [ + mobileEl('android.widget.Button', { + clickable: 'true', + 'content-desc': 'Skip', + text: '' + }) + ]) + const out = serializeMobileSnapshot(root, { platform: 'android' }) + expect(out).toContain('button "Skip" → ~Skip') + }) + + it('falls back to resource-id when no content-desc', () => { + const root = mobileEl('hierarchy', {}, [ + mobileEl('android.widget.EditText', { + clickable: 'true', + 'content-desc': '', + 'resource-id': 'com.example:id/search', + text: '' + }) + ]) + const out = serializeMobileSnapshot(root, { platform: 'android' }) + expect(out).toContain('textbox "search" → id:com.example:id/search') + }) + + it('renders ∈ ancestor context when element has no identity', () => { + const root = mobileEl('hierarchy', {}, [ + mobileEl( + 'android.widget.LinearLayout', + { 'content-desc': 'Search section' }, + [ + mobileEl('android.widget.EditText', { + clickable: 'true', + 'content-desc': '', + 'resource-id': 'com.example:id/search', + text: '' + }) + ] + ) + ]) + const out = serializeMobileSnapshot(root, { platform: 'android' }) + expect(out).toContain( + 'textbox "search" ∈ "Search section" → id:com.example:id/search' + ) + }) + + it('renders iOS element with accessibility-id', () => { + const root = mobileEl('XCUIElementTypeApplication', {}, [ + mobileEl('XCUIElementTypeButton', { + accessible: 'true', + name: 'Accept All Cookies', + label: 'Accept All Cookies' + }) + ]) + const out = serializeMobileSnapshot(root, { platform: 'ios' }) + expect(out).toContain('button "Accept All Cookies" → ~Accept All Cookies') + }) + + it('simplifies iOS XCUIElementType prefix', () => { + const root = mobileEl('XCUIElementTypeApplication', {}, [ + mobileEl('XCUIElementTypeScrollView', {}) + ]) + const out = serializeMobileSnapshot(root, { platform: 'ios' }) + expect(out).toContain('ScrollView') + expect(out).not.toContain('XCUIElementType') + }) + + it('shows container without selector', () => { + const root = mobileEl('hierarchy', {}, [ + mobileEl('android.widget.FrameLayout', { 'content-desc': '' }) + ]) + const out = serializeMobileSnapshot(root, { platform: 'android' }) + expect(out).toContain('FrameLayout') + expect(out).not.toContain('→') + }) +}) diff --git a/packages/elements/README.md b/packages/elements/README.md index b188671c..967ff75b 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 || ^10.0.0`). +Requires `webdriverio` as a peer dependency (`^10.0.0`). ## Usage @@ -23,20 +23,18 @@ const result = await getElements(browser, { limit: 50, inViewportOnly: true }) ### Accessibility snapshot (`getSnapshot`) -A single call for web and mobile that returns an AI-readable accessibility/DOM snapshot as a `SnapshotResult` — a text tree with `e1`, `e2`, … virtual element IDs baked in, plus an `elements` map that resolves each ID to a real selector. No post-processing required. +A single call for web and native sessions that returns the same text as the WebdriverIO CLI's `wdio session snapshot --selectors` (add `--viewport` for `{ viewport: true }`). It is built on `@wdio/snapshot`: each actionable element gets an `eN` ref, and its line ends in `→ `. The default walks the whole document, so on long pages `{ viewport: true }` is much smaller and faster. ```ts import { getSnapshot } from '@wdio/elements' -const { text, elements } = await getSnapshot(browser, { inViewportOnly: true }) -// text: '[Page: Login — https://…]\n button "Log in" → //button[contains(., "Log in")]\n …' -// elements: { e1: { selector, qualifiedSelector?, tagName, role, text }, … } +const { text, refs } = await getSnapshot(browser) // full page +const visible = await getSnapshot(browser, { viewport: true }) +// text: '- button "start" [ref=e1] → ~start' (one line per node) +// refs: SnapshotRef[] — { id, candidates, … } for every ref in the text ``` -Every locator this API returns is portable across WebdriverIO, Selenium and -Nightwatch: CSS where an id/attribute/class identifies the element uniquely, -XPath where its text does (WebdriverIO resolves a leading `//` as XPath; -Selenium takes it via `By.xpath`, Nightwatch via its `'xpath'` locate strategy). +Web sessions walk the DOM in the page; Android and iOS sessions parse the page source. Errors (`SnapshotError`) propagate instead of returning a placeholder. 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 @@ -44,9 +42,13 @@ locator in the recording runner's own dialect, so a WebdriverIO trace shows 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. +Differences from the CLI, which keeps session state this function does not have: -This is the snapshot the DevTools **trace player** consumes: the per-action accessibility tree feeds the player's **A11y tab** (roles + accessible names, hover to highlight, click to copy a locator), and the resolved `elements` back the player's **element overlay / pick-locator** (click-to-copy boxes drawn over each interacted element, cross-linked to the A11y rows). +- No wait for a freshly loaded page to settle before collecting. +- Iframes are not inlined over BiDi. A cut or cross-origin frame renders as the frame-hint note (`wdio session frame `). +- Refs are numbered from `e1` on every call. + +`@wdio/devtools-service`'s `browser.getSnapshot()` calls this function and returns its `{ text, refs }`. Unlike the default here, it passes `{ viewport: true }` when called without options, so it reads the visible viewport; pass `{ viewport: false }` for the full page. ### Browser @@ -76,24 +78,6 @@ const elements = await getMobileVisibleElements(browser, 'ios', { }) ``` -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 @@ -121,11 +105,11 @@ Auto-detects platform and returns a unified result. ### `getSnapshot(browser, options)` -Auto-detects platform and returns a `SnapshotResult` — `{ text, elements }` — where `text` is the rendered accessibility tree with embedded `e1`, `e2`, … IDs and `elements` maps each ID to a `SnapshotElement` (`selector`, optional `qualifiedSelector` for `.instance(N)` disambiguation, `tagName`, `role`, `text`). +Returns `GetSnapshotResult` — `{ text, refs }` — where `text` is the rendered tree with `eN` refs and selectors, and `refs` is the `SnapshotRef[]` behind them. | Param | Type | Default | Description | |---|---|---|---| -| `inViewportOnly` | `boolean` | `true` | Only include elements whose bounds intersect the viewport | +| `viewport` | `boolean` | `false` | Only include what intersects the viewport; `false` snapshots the full page | ### `getInteractableBrowserElements(browser, options)` @@ -139,14 +123,6 @@ Single DOM walk returning the accessibility tree as a flat `AccessibilityNode[]` 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`: @@ -167,9 +143,8 @@ export type { MobileElementInfo, GetMobileElementsOptions, AccessibilityNode, VisibleElementsResult, - WebSnapshotOptions, MobileSnapshotOptions, - GetSnapshotOptions, - SnapshotResult, SnapshotElement + GetSnapshotOptions, GetSnapshotResult, + CandidateKind, SnapshotCandidate, SnapshotNode, SnapshotRef } from '@wdio/elements' // From @wdio/elements/locators: diff --git a/packages/elements/package.json b/packages/elements/package.json index 483af22b..2f82bc83 100644 --- a/packages/elements/package.json +++ b/packages/elements/package.json @@ -35,7 +35,11 @@ "test": "vitest run", "prepublishOnly": "pnpm build" }, + "engines": { + "node": ">=22.19.0" + }, "dependencies": { + "@wdio/snapshot": "10.0.2", "@xmldom/xmldom": "^0.9.12", "xpath": "^0.0.34" }, @@ -50,6 +54,6 @@ "webdriverio": "^10.0.2" }, "peerDependencies": { - "webdriverio": "^9.0.0 || ^10.0.0" + "webdriverio": "^10.0.0" } } diff --git a/packages/elements/src/get-snapshot.ts b/packages/elements/src/get-snapshot.ts index 9f003a94..23b22d5f 100644 --- a/packages/elements/src/get-snapshot.ts +++ b/packages/elements/src/get-snapshot.ts @@ -1,155 +1,92 @@ -/** - * Unified getSnapshot() — single call for web and mobile. - * - * Auto-detects platform, fetches the accessibility tree (web) or page source - * (mobile), converts to a flat SnapshotNode[], and renders a text tree with - * e1, e2, … virtual IDs baked in plus an elements map for direct selector - * resolution. - */ - -import type { SnapshotResult } from '@wdio/devtools-core/element-types' import { - buildSnapshot, - accessibilityNodesToSnapshotNodes, - jsonElementToSnapshotNodes -} from '@wdio/devtools-core/element-snapshot' -import { getBrowserAccessibilityTree } from './accessibility-tree.js' -import { xmlToJSON } from './locators/index.js' + attachSelectors, + collectWeb, + formatSnapshot, + inViewport, + nativePlatform, + parseNativeSource +} from '@wdio/snapshot' +import type { NativePlatform, SnapshotNode, SnapshotRef } from '@wdio/snapshot' export interface GetSnapshotOptions { - /** Only include elements whose bounds intersect the viewport (default true). */ - inViewportOnly?: boolean + /** Only include what intersects the viewport (default false: the full page). */ + viewport?: boolean } -/** - * Take a snapshot of the current page/app state. - * - * Returns a text tree with eN virtual IDs for every interactive element - * and an elements map so consumers can resolve e1, e2, … to real selectors - * without any post-processing. - */ -export async function getSnapshot( - browser: WebdriverIO.Browser, - options?: GetSnapshotOptions -): Promise { - const { inViewportOnly = true } = options ?? {} - - if (browser.isAndroid || browser.isIOS) { - return getMobileSnapshot(browser, inViewportOnly) - } - return getWebSnapshot(browser, inViewportOnly) +export interface GetSnapshotResult { + /** Accessibility tree as text, one `eN` ref per actionable element, each ending in `→ `. */ + text: string + refs: SnapshotRef[] } -// --------------------------------------------------------------------------- -// Web -// --------------------------------------------------------------------------- +const frameHint = (ref: string) => `wdio session frame ${ref}` -async function getWebSnapshot( - browser: WebdriverIO.Browser, - inViewportOnly: boolean -): Promise { - const [url, title, nodes] = await Promise.all([ - safeCall(() => browser.getUrl()), - safeCall(() => browser.getTitle()), - getBrowserAccessibilityTree(browser, { inViewportOnly }) - ]) - - const header = buildWebHeader(title, url) - const snapshotNodes = accessibilityNodesToSnapshotNodes(nodes, { - inViewportOnly - }) - return buildSnapshot(header, snapshotNodes) -} +const NATIVE_PLATFORMS = new Set(['android', 'ios', 'mac', 'windows']) -function buildWebHeader(title?: string, url?: string): string { - let h = '[Page' - if (title) { - h += `: ${title}` - } - if (url) { - h += ` — ${url}` - } - h += ']' - return h +// @wdio/session's `classify`: a browserName makes a mobile session web. Desktop +// browsers also report mac/windows here, which their browserName excludes too. +function nativePlatformName(caps: Record): string | undefined { + const platformName = String( + caps.platformName ?? caps['appium:platformName'] ?? '' + ).toLowerCase() + return NATIVE_PLATFORMS.has(platformName) && !caps.browserName + ? platformName + : undefined } -// --------------------------------------------------------------------------- -// Mobile -// --------------------------------------------------------------------------- - -async function getMobileSnapshot( +/** + * Same text as `wdio session snapshot --selectors [--viewport]`, for web and + * native sessions alike. Throws SnapshotError rather than returning a partial tree. + */ +export async function getSnapshot( browser: WebdriverIO.Browser, - inViewportOnly: boolean -): Promise { - const platform: 'android' | 'ios' = browser.isAndroid ? 'android' : 'ios' - - const [viewportSize, pageSource] = await Promise.all([ - safeCall(() => browser.getWindowSize()), - safeCall(() => browser.getPageSource()) - ]) - - if (!pageSource) { - return { text: '[No elements found]', elements: {} } + options: GetSnapshotOptions = {} +): Promise { + const { viewport = false } = options + const caps = browser.capabilities as Record + const platformName = nativePlatformName(caps) + const { tree, refs } = platformName + ? await collectNative(browser, nativePlatform(caps, platformName), viewport) + : await collectWebTree(browser, viewport) + + attachSelectors(tree, refs) + return { + text: formatSnapshot(tree, { selectors: true, frameHint }), + refs } - - const root = xmlToJSON(pageSource) - if (!root) { - return { text: '[No elements found]', elements: {} } - } - - // A zero-dimension viewport (minimized app, broken ADB connection) would - // silently clip every element. Treat it as unavailable instead. - const safeViewport = - viewportSize?.width && viewportSize?.height ? viewportSize : undefined - - const deviceName = getDeviceName(browser) - - const header = buildMobileHeader(platform, deviceName, safeViewport) - - const snapshotNodes = jsonElementToSnapshotNodes(root, platform, { - inViewportOnly, - viewport: safeViewport, - sourceXML: pageSource - }) - - return buildSnapshot(header, snapshotNodes) } -function buildMobileHeader( - platform: string, - deviceName: string, - viewport?: { width: number; height: number } -): string { - let h = `[${platform}` - if (deviceName) { - h += ` — ${deviceName}` - } - if (viewport) { - h += ` (${viewport.width}×${viewport.height})` - } - h += ']' - return h +async function collectWebTree(browser: WebdriverIO.Browser, viewport: boolean) { + return collectWeb( + browser, + { counter: 0, all: false, boxes: false, viewport, assignRefs: 'ephemeral' }, + // Same rule as collectTop in @wdio/session; `bidi` is the path for a classic session. + { transport: browser.isBidi ? 'classic-first' : 'bidi' } + ) } -// --------------------------------------------------------------------------- -// Helpers -// --------------------------------------------------------------------------- - -async function safeCall(fn: () => Promise): Promise { - try { - return await fn() - } catch { - return undefined +async function collectNative( + browser: WebdriverIO.Browser, + platform: NativePlatform, + viewport: boolean +): Promise<{ tree: SnapshotNode; refs: SnapshotRef[] }> { + const [xml, size] = await Promise.all([ + browser.getPageSource(), + viewport ? browser.getWindowSize() : undefined + ]) + const parsed = parseNativeSource(xml, platform, { all: false, counter: 0 }) + if (!size) { + return parsed } + const tree = inViewport(parsed.tree, size.width, size.height) ?? parsed.tree + const shown = refIds(tree) + return { tree, refs: parsed.refs.filter((ref) => shown.has(ref.id)) } } -function getDeviceName(browser: WebdriverIO.Browser): string { - try { - const caps = browser.capabilities as Record - return ( - (caps['appium:deviceName'] as string) ?? (caps.deviceName as string) ?? '' - ) - } catch { - return '' +function refIds(node: SnapshotNode, ids = new Set()): Set { + if (node.ref) { + ids.add(node.ref) } + node.children?.forEach((child) => refIds(child, ids)) + return ids } diff --git a/packages/elements/src/index.ts b/packages/elements/src/index.ts index d1b1d9ca..a19f934b 100644 --- a/packages/elements/src/index.ts +++ b/packages/elements/src/index.ts @@ -20,10 +20,14 @@ export type { export { getElements } from './get-elements.js' export type { VisibleElementsResult } from './get-elements.js' -export { serializeWebSnapshot, serializeMobileSnapshot } from './snapshot.js' -export type { WebSnapshotOptions, MobileSnapshotOptions } from './snapshot.js' export type { JSONElement } from './locators/index.js' export { getSnapshot } from './get-snapshot.js' -export type { GetSnapshotOptions } from './get-snapshot.js' -export type { SnapshotResult, SnapshotElement } from './snapshot.js' +export type { GetSnapshotOptions, GetSnapshotResult } from './get-snapshot.js' +export { SnapshotError } from '@wdio/snapshot' +export type { + CandidateKind, + SnapshotCandidate, + SnapshotNode, + SnapshotRef +} from '@wdio/snapshot' diff --git a/packages/elements/src/mobile-elements.ts b/packages/elements/src/mobile-elements.ts index 1eabbc53..e57f3cf2 100644 --- a/packages/elements/src/mobile-elements.ts +++ b/packages/elements/src/mobile-elements.ts @@ -161,12 +161,6 @@ export async function getMobileVisibleElementsWithTree( const tree = xmlToJSON(pageSource) - // Stash the source XML on the root element so serializeMobileSnapshot - // can use the full locator pipeline without requiring it as a separate arg. - if (tree) { - tree.attributes._sourceXML = pageSource - } - const elementLocators = generateAllElementLocators(pageSource, { platform, viewportSize, diff --git a/packages/elements/src/snapshot.ts b/packages/elements/src/snapshot.ts deleted file mode 100644 index e1d30489..00000000 --- a/packages/elements/src/snapshot.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * AI-readable snapshot serializers — re-exported from @wdio/devtools-core. - */ - -export { - serializeWebSnapshot, - serializeMobileSnapshot, - buildSnapshot, - accessibilityNodesToSnapshotNodes, - jsonElementToSnapshotNodes -} from '@wdio/devtools-core/element-snapshot' -export type { - WebSnapshotOptions, - MobileSnapshotOptions -} from '@wdio/devtools-core/element-snapshot' -export type { - SnapshotNode, - SnapshotElement, - SnapshotResult -} from '@wdio/devtools-core/element-types' diff --git a/packages/elements/tests/get-snapshot.test.ts b/packages/elements/tests/get-snapshot.test.ts new file mode 100644 index 00000000..5ae8048a --- /dev/null +++ b/packages/elements/tests/get-snapshot.test.ts @@ -0,0 +1,199 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest' + +vi.mock('@wdio/snapshot', async (importActual) => { + const actual = await importActual() + return { + ...actual, + collectWeb: vi.fn(), + attachSelectors: vi.fn(), + formatSnapshot: vi.fn(() => 'TEXT'), + inViewport: vi.fn(), + parseNativeSource: vi.fn(actual.parseNativeSource) + } +}) + +import * as snap from '@wdio/snapshot' +import { getSnapshot } from '../src/get-snapshot.js' + +const TREE = { role: 'document', children: [] } as unknown as snap.SnapshotNode +const REFS = [{ id: 'e1', candidates: [] }] as unknown as snap.SnapshotRef[] + +function browser(overrides: Record) { + return { + capabilities: { browserName: 'chrome' }, + ...overrides + } as unknown as WebdriverIO.Browser +} + +beforeEach(() => { + vi.clearAllMocks() + vi.mocked(snap.collectWeb).mockResolvedValue({ + tree: TREE, + refs: REFS, + counter: 1 + } as never) +}) + +describe('getSnapshot (web)', () => { + it('collects the full page and renders with selectors', async () => { + const b = browser({ isBidi: true }) + const result = await getSnapshot(b) + + expect(snap.collectWeb).toHaveBeenCalledWith( + b, + { + counter: 0, + all: false, + boxes: false, + viewport: false, + assignRefs: 'ephemeral' + }, + { transport: 'classic-first' } + ) + expect(snap.attachSelectors).toHaveBeenCalledWith(TREE, REFS) + expect(snap.formatSnapshot).toHaveBeenCalledWith(TREE, { + selectors: true, + frameHint: expect.any(Function) + }) + expect( + vi.mocked(snap.formatSnapshot).mock.calls[0][1]?.frameHint?.('e3') + ).toBe('wdio session frame e3') + expect(result).toEqual({ text: 'TEXT', refs: REFS }) + }) + + it('passes viewport through, and uses the bidi transport on a classic session', async () => { + const b = browser({ isBidi: false }) + await getSnapshot(b, { viewport: true }) + + expect(snap.collectWeb).toHaveBeenCalledWith( + b, + expect.objectContaining({ viewport: true, assignRefs: 'ephemeral' }), + { transport: 'bidi' } + ) + }) +}) + +const ANDROID_XML = ` + + + + +` + +function nativeBrowser() { + return browser({ + capabilities: { platformName: 'Android' }, + getPageSource: vi.fn().mockResolvedValue(ANDROID_XML), + getWindowSize: vi.fn().mockResolvedValue({ width: 1080, height: 2400 }) + }) +} + +describe('getSnapshot web/native routing (same as wdio session)', () => { + it.each([ + ['Android app', { platformName: 'Android' }, 'native'], + ['iOS app via appium: prefix', { 'appium:platformName': 'iOS' }, 'native'], + ['macOS app', { platformName: 'mac' }, 'native'], + ['Windows app', { platformName: 'Windows' }, 'native'], + [ + 'Chrome on Android', + { platformName: 'Android', browserName: 'chrome' }, + 'web' + ], + ['Safari on iOS', { platformName: 'iOS', browserName: 'Safari' }, 'web'], + [ + 'desktop Chrome on mac', + { platformName: 'mac', browserName: 'chrome' }, + 'web' + ], + [ + 'desktop Chrome on linux', + { platformName: 'linux', browserName: 'chrome' }, + 'web' + ] + ])('%s is %s', async (_label, capabilities, expected) => { + await getSnapshot( + browser({ + capabilities, + getPageSource: vi.fn().mockResolvedValue(ANDROID_XML) + }) + ) + expect(vi.mocked(snap.collectWeb).mock.calls.length > 0).toBe( + expected === 'web' + ) + }) +}) + +describe('getSnapshot (native)', () => { + it('parses the page source without a viewport cut by default', async () => { + const b = nativeBrowser() + await getSnapshot(b) + + expect(snap.parseNativeSource).toHaveBeenCalledWith( + ANDROID_XML, + 'android', + { + all: false, + counter: 0 + } + ) + expect(b.getWindowSize).not.toHaveBeenCalled() + expect(snap.inViewport).not.toHaveBeenCalled() + expect(snap.collectWeb).not.toHaveBeenCalled() + }) + + it('cuts to the window size when viewport is set, keeping the tree if nothing remains', async () => { + const b = nativeBrowser() + vi.mocked(snap.inViewport).mockReturnValue(undefined) + await getSnapshot(b, { viewport: true }) + + expect(snap.inViewport).toHaveBeenCalledWith(expect.anything(), 1080, 2400) + const rendered = vi.mocked(snap.formatSnapshot).mock.calls[0][0] + expect(rendered).toBe(vi.mocked(snap.attachSelectors).mock.calls[0][0]) + }) + + it('renders refs with selectors from a real parse', async () => { + const actual = await vi.importActual('@wdio/snapshot') + vi.mocked(snap.attachSelectors).mockImplementation(actual.attachSelectors) + vi.mocked(snap.formatSnapshot).mockImplementation(actual.formatSnapshot) + + const { text, refs } = await getSnapshot(nativeBrowser()) + + expect(refs.length).toBeGreaterThan(0) + expect(text).toMatch(/\be1\b/) + expect(text).toContain('→') + expect(text).toContain('start') + }) + + it('parses an appium:-prefixed iOS session as iOS, not the android fallback', async () => { + await getSnapshot( + browser({ + capabilities: { 'appium:platformName': 'iOS' }, + getPageSource: vi + .fn() + .mockResolvedValue('') + }) + ) + expect(vi.mocked(snap.parseNativeSource).mock.calls[0][1]).toBe('ios') + }) + + it('returns only the refs left in the tree after a viewport cut', async () => { + const parsedRefs = [ + { id: 'e1', role: 'button', candidates: [] }, + { id: 'e2', role: 'button', candidates: [] } + ] + vi.mocked(snap.parseNativeSource).mockReturnValueOnce({ + tree: TREE, + refs: parsedRefs, + located: [], + counter: 2 + }) + vi.mocked(snap.inViewport).mockReturnValue({ + role: 'application', + children: [{ role: 'button', ref: 'e2' }] + }) + + const { refs } = await getSnapshot(nativeBrowser(), { viewport: true }) + + expect(refs.map((ref) => ref.id)).toEqual(['e2']) + }) +}) diff --git a/packages/elements/tests/locators/locator-generation.test.ts b/packages/elements/tests/locators/locator-generation.test.ts index c5d2de4b..d8d58bf7 100644 --- a/packages/elements/tests/locators/locator-generation.test.ts +++ b/packages/elements/tests/locators/locator-generation.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest' -import { locatorsToObject } from '@wdio/elements/locators' +import { locatorsToObject } from '../../src/locators/index.js' describe('locatorsToObject', () => { it('converts locator array to object', () => { diff --git a/packages/elements/tests/snapshot.test.ts b/packages/elements/tests/snapshot.test.ts deleted file mode 100644 index d739c7ef..00000000 --- a/packages/elements/tests/snapshot.test.ts +++ /dev/null @@ -1,697 +0,0 @@ -import { describe, it, expect } from 'vitest' -import { - serializeWebSnapshot, - serializeMobileSnapshot, - buildSnapshot, - accessibilityNodesToSnapshotNodes, - jsonElementToSnapshotNodes -} from '../src/snapshot.js' -import type { AccessibilityNode } from '../src/accessibility-tree.js' -import type { JSONElement } from '../src/locators/index.js' -import type { SnapshotNode } from '@wdio/devtools-core/element-types' - -// --------------------------------------------------------------------------- -// serializeWebSnapshot -// --------------------------------------------------------------------------- - -function node( - overrides: Partial & { role: string; depth: number } -): AccessibilityNode { - return { - name: '', - selector: '', - level: '', - disabled: '', - checked: '', - expanded: '', - selected: '', - pressed: '', - required: '', - readonly: '', - ...overrides - } -} - -describe('serializeWebSnapshot', () => { - it('produces a page header', () => { - const out = serializeWebSnapshot([]) - expect(out).toBe('[Page]') - }) - - it('includes title and url in header', () => { - const out = serializeWebSnapshot([], { - title: 'Login', - url: 'https://example.com/login' - }) - expect(out).toMatch('[Page: Login — https://example.com/login]') - }) - - it('renders interactive role with name and selector', () => { - const nodes = [ - node({ - role: 'button', - depth: 0, - name: 'Submit', - selector: 'button*=Submit' - }) - ] - const out = serializeWebSnapshot(nodes) - expect(out).toContain('button "Submit" → button*=Submit') - }) - - it('renders interactive role with ∈ ancestor name when self has no name', () => { - const nodes = [ - node({ role: 'form', depth: 0, name: 'Login form' }), - node({ role: 'checkbox', depth: 1, name: '', selector: '#remember' }) - ] - const out = serializeWebSnapshot(nodes) - expect(out).toContain('checkbox ∈ "Login form" → #remember') - }) - - it('omits interactive node with no selector regardless of name', () => { - const nodes = [ - node({ role: 'button', depth: 0, name: '', selector: '' }), - node({ - role: 'button', - depth: 0, - name: 'Named but unselector', - selector: '' - }) - ] - const out = serializeWebSnapshot(nodes) - // Only the header — both nodes skipped due to missing selector - expect(out.split('\n').length).toBe(1) - }) - - it('omits interactive node with ∈ context but no selector', () => { - const nodes = [ - node({ role: 'form', depth: 0, name: 'Login form' }), - node({ role: 'combobox', depth: 1, name: '', selector: '' }) - ] - const out = serializeWebSnapshot(nodes) - // combobox has ancestor context but no selector — must be dropped - expect(out).not.toContain('combobox') - expect(out).not.toContain('→') - }) - - it('renders container role without selector', () => { - const nodes = [node({ role: 'navigation', depth: 0, name: 'Main' })] - const out = serializeWebSnapshot(nodes) - expect(out).toContain('navigation "Main"') - expect(out).not.toContain('→') - }) - - it('renders heading with level suffix', () => { - const nodes = [ - node({ role: 'heading', depth: 0, name: 'Sign in', level: 1 }) - ] - const out = serializeWebSnapshot(nodes) - expect(out).toContain('heading[1] "Sign in"') - }) - - it('indents nodes according to depth', () => { - const nodes = [ - node({ role: 'navigation', depth: 0, name: 'Nav' }), - node({ role: 'link', depth: 1, name: 'Home', selector: 'a*=Home' }) - ] - const lines = serializeWebSnapshot(nodes).split('\n') - // depth 0 → 1 level of indent (' ' × 1), depth 1 → 2 levels (' ' × 2) - expect(lines[1]).toMatch(/^ navigation/) - expect(lines[2]).toMatch(/^ link/) - }) - - it('renders full login page example correctly', () => { - const nodes: AccessibilityNode[] = [ - node({ role: 'navigation', depth: 0, name: 'Main' }), - node({ role: 'link', depth: 1, name: 'Home', selector: 'a*=Home' }), - node({ role: 'main', depth: 0, name: '' }), - node({ role: 'heading', depth: 1, name: 'Sign in', level: 1 }), - node({ role: 'form', depth: 1, name: 'Login' }), - node({ - role: 'textbox', - depth: 2, - name: 'Email address', - selector: '#email' - }), - node({ - role: 'button', - depth: 2, - name: 'Sign in', - selector: 'button*=Sign in' - }) - ] - const out = serializeWebSnapshot(nodes, { - title: 'Login', - url: 'https://example.com/login' - }) - expect(out).toContain('[Page: Login — https://example.com/login]') - expect(out).toContain('navigation "Main"') - expect(out).toContain('link "Home" ∈ "Main" → a*=Home') - expect(out).toContain('heading[1] "Sign in"') - expect(out).toContain('textbox "Email address" ∈ "Login" → #email') - expect(out).toContain('button "Sign in" ∈ "Login" → button*=Sign in') - }) -}) - -// --------------------------------------------------------------------------- -// serializeMobileSnapshot -// --------------------------------------------------------------------------- - -function mobileEl( - tagName: string, - attrs: JSONElement['attributes'], - children: JSONElement[] = [] -): JSONElement { - return { tagName, attributes: attrs, children, path: '' } -} - -describe('serializeMobileSnapshot', () => { - it('produces a platform header with device and viewport', () => { - const root = mobileEl('hierarchy', {}) - const out = serializeMobileSnapshot(root, { - platform: 'android', - deviceName: 'Pixel 7', - viewport: { width: 412, height: 915 } - }) - expect(out).toMatch('[android — Pixel 7 (412×915)]') - }) - - it('renders interactive Android element with accessibility-id locator', () => { - const root = mobileEl('hierarchy', {}, [ - mobileEl('android.widget.Button', { - clickable: 'true', - 'content-desc': 'Skip', - text: '' - }) - ]) - const out = serializeMobileSnapshot(root, { platform: 'android' }) - expect(out).toContain('button "Skip" → ~Skip') - }) - - it('falls back to resource-id when no content-desc', () => { - const root = mobileEl('hierarchy', {}, [ - mobileEl('android.widget.EditText', { - clickable: 'true', - 'content-desc': '', - 'resource-id': 'com.example:id/search', - text: '' - }) - ]) - const out = serializeMobileSnapshot(root, { platform: 'android' }) - expect(out).toContain('textbox "search" → id:com.example:id/search') - }) - - it('renders ∈ ancestor context when element has no identity', () => { - const root = mobileEl('hierarchy', {}, [ - mobileEl( - 'android.widget.LinearLayout', - { 'content-desc': 'Search section' }, - [ - mobileEl('android.widget.EditText', { - clickable: 'true', - 'content-desc': '', - 'resource-id': 'com.example:id/search', - text: '' - }) - ] - ) - ]) - const out = serializeMobileSnapshot(root, { platform: 'android' }) - expect(out).toContain( - 'textbox "search" ∈ "Search section" → id:com.example:id/search' - ) - }) - - it('renders iOS element with accessibility-id', () => { - const root = mobileEl('XCUIElementTypeApplication', {}, [ - mobileEl('XCUIElementTypeButton', { - accessible: 'true', - name: 'Accept All Cookies', - label: 'Accept All Cookies' - }) - ]) - const out = serializeMobileSnapshot(root, { platform: 'ios' }) - expect(out).toContain('button "Accept All Cookies" → ~Accept All Cookies') - }) - - it('simplifies iOS XCUIElementType prefix', () => { - const root = mobileEl('XCUIElementTypeApplication', {}, [ - mobileEl('XCUIElementTypeScrollView', {}) - ]) - const out = serializeMobileSnapshot(root, { platform: 'ios' }) - expect(out).toContain('ScrollView') - expect(out).not.toContain('XCUIElementType') - }) - - it('shows container without selector', () => { - const root = mobileEl('hierarchy', {}, [ - mobileEl('android.widget.FrameLayout', { 'content-desc': '' }) - ]) - const out = serializeMobileSnapshot(root, { platform: 'android' }) - expect(out).toContain('FrameLayout') - expect(out).not.toContain('→') - }) -}) - -// --------------------------------------------------------------------------- -// buildSnapshot -// --------------------------------------------------------------------------- - -function snapNode(overrides: Partial): SnapshotNode { - return { - role: 'button', - name: '', - selector: '', - depth: 0, - isInteractive: false, - tagName: 'button', - ...overrides - } -} - -describe('buildSnapshot', () => { - it('returns empty elements map for empty nodes', () => { - const result = buildSnapshot('[Page]', []) - expect(result.text).toBe('[Page]') - expect(result.elements).toEqual({}) - }) - - it('renders structural nodes without eN IDs', () => { - const nodes = [ - snapNode({ - role: 'navigation', - depth: 0, - name: 'Main', - isInteractive: false, - tagName: 'nav' - }) - ] - const result = buildSnapshot('[Page]', nodes) - expect(result.text).toContain('navigation "Main"') - expect(result.text).not.toContain('e1') - expect(result.text).not.toContain('→') - expect(result.elements).toEqual({}) - }) - - it('assigns e1 to first interactive element', () => { - const nodes = [ - snapNode({ - role: 'button', - depth: 0, - name: 'Submit', - selector: 'button*=Submit', - isInteractive: true, - tagName: 'button' - }) - ] - const result = buildSnapshot('[Page]', nodes) - expect(result.text).toContain('e1 button "Submit" → button*=Submit') - expect(result.elements).toEqual({ - e1: { - selector: 'button*=Submit', - tagName: 'button', - role: 'button', - text: 'Submit' - } - }) - }) - - it('skips eN for non-interactive nodes and continues numbering', () => { - const nodes = [ - snapNode({ - role: 'navigation', - depth: 0, - name: 'Nav', - isInteractive: false, - tagName: 'nav' - }), - snapNode({ - role: 'link', - depth: 1, - name: 'Home', - selector: 'a*=Home', - isInteractive: true, - tagName: 'a' - }), - snapNode({ - role: 'button', - depth: 0, - name: 'Submit', - selector: 'button*=Submit', - isInteractive: true, - tagName: 'button' - }) - ] - const result = buildSnapshot('[Page]', nodes) - expect(result.text).toContain('navigation "Nav"') - // link at depth 1 has "Nav" as its structural ancestor → ∈ context - expect(result.text).toContain('e1 link "Home" ∈ "Nav" → a*=Home') - // button at depth 0 also gets ∈ "Nav" — Nav is a same-depth structural sibling - expect(result.text).toContain( - 'e2 button "Submit" ∈ "Nav" → button*=Submit' - ) - expect(Object.keys(result.elements)).toEqual(['e1', 'e2']) - }) - - it('renders heading with level suffix', () => { - const nodes = [ - snapNode({ - role: 'heading', - depth: 0, - name: 'Welcome', - level: 2, - isInteractive: false, - tagName: 'h2' - }) - ] - const result = buildSnapshot('[Page]', nodes) - expect(result.text).toContain('heading[2] "Welcome"') - }) - - it('renders ∈ ancestor context for interactive nodes', () => { - const nodes = [ - snapNode({ - role: 'form', - depth: 0, - name: 'Login', - isInteractive: false, - tagName: 'form' - }), - snapNode({ - role: 'textbox', - depth: 1, - name: 'Email', - selector: '#email', - isInteractive: true, - tagName: 'input' - }) - ] - const result = buildSnapshot('[Page]', nodes) - expect(result.text).toContain('e1 textbox "Email" ∈ "Login" → #email') - }) - - it('omits ∈ context when same-depth sibling is interactive', () => { - const nodes = [ - snapNode({ - role: 'button', - depth: 0, - name: 'Cancel', - selector: 'button*=Cancel', - isInteractive: true, - tagName: 'button' - }), - snapNode({ - role: 'button', - depth: 0, - name: 'Submit', - selector: 'button*=Submit', - isInteractive: true, - tagName: 'button' - }) - ] - const result = buildSnapshot('[Page]', nodes) - // Second button should NOT have ∈ "Cancel" context (same-depth interactive isn't context) - expect(result.text).toContain('e2 button "Submit" → button*=Submit') - expect(result.text).not.toContain('∈ "Cancel"') - }) - - it('appends .instance(N) for duplicate selectors', () => { - const nodes = [ - snapNode({ - role: 'button', - depth: 0, - name: 'Add', - selector: 'button*=Add', - isInteractive: true, - tagName: 'button' - }), - snapNode({ - role: 'button', - depth: 0, - name: 'Add', - selector: 'button*=Add', - isInteractive: true, - tagName: 'button' - }) - ] - const result = buildSnapshot('[Page]', nodes) - expect(result.text).toContain('button*=Add.instance(0)') - expect(result.text).toContain('button*=Add.instance(1)') - // Elements map stores the raw selector without .instance(N) - expect(result.elements['e1']!.selector).toBe('button*=Add') - expect(result.elements['e2']!.selector).toBe('button*=Add') - }) - - it('handles interactive node with no name but context ancestor', () => { - const nodes = [ - snapNode({ - role: 'form', - depth: 0, - name: 'Search', - isInteractive: false, - tagName: 'form' - }), - snapNode({ - role: 'textbox', - depth: 1, - name: '', - selector: '#q', - isInteractive: true, - tagName: 'input' - }) - ] - const result = buildSnapshot('[Page]', nodes) - expect(result.text).toContain('e1 textbox ∈ "Search" → #q') - expect(result.elements['e1']!.text).toBe('') - }) - - it('indents according to depth', () => { - const nodes = [ - snapNode({ - role: 'main', - depth: 0, - name: '', - isInteractive: false, - tagName: 'main' - }), - snapNode({ - role: 'button', - depth: 1, - name: 'Click', - selector: '#btn', - isInteractive: true, - tagName: 'button' - }) - ] - const lines = buildSnapshot('[Page]', nodes).text.split('\n') - expect(lines[1]).toMatch(/^ main/) - expect(lines[2]).toMatch(/^ e1/) - }) -}) - -// --------------------------------------------------------------------------- -// accessibilityNodesToSnapshotNodes -// --------------------------------------------------------------------------- - -describe('accessibilityNodesToSnapshotNodes', () => { - it('converts interactive node and derives tagName from selector', () => { - const input = [ - node({ - role: 'button', - depth: 0, - name: 'Submit', - selector: 'button*=Submit' - }) - ] - const result = accessibilityNodesToSnapshotNodes(input) - expect(result).toHaveLength(1) - expect(result[0]).toMatchObject({ - role: 'button', - name: 'Submit', - selector: 'button*=Submit', - isInteractive: true, - tagName: 'button' - }) - }) - - it('filters out off-screen nodes when inViewportOnly is true', () => { - const input = [ - node({ - role: 'button', - depth: 0, - name: 'Submit', - selector: 'button*=Submit', - isInViewport: false - }) - ] - const result = accessibilityNodesToSnapshotNodes(input, { - inViewportOnly: true - }) - expect(result).toHaveLength(0) - }) - - it('keeps off-screen nodes when inViewportOnly is false', () => { - const input = [ - node({ - role: 'button', - depth: 0, - name: 'Submit', - selector: 'button*=Submit', - isInViewport: false - }) - ] - const result = accessibilityNodesToSnapshotNodes(input, { - inViewportOnly: false - }) - expect(result).toHaveLength(1) - }) - - it('sets isInteractive false for structural roles', () => { - const input = [node({ role: 'navigation', depth: 0, name: 'Nav' })] - const result = accessibilityNodesToSnapshotNodes(input) - expect(result[0]!.isInteractive).toBe(false) - expect(result[0]!.tagName).toBe('navigation') // fallback to role - }) - - it('suppresses statictext echoed by interactive parent', () => { - const input = [ - node({ - role: 'button', - depth: 0, - name: 'Submit', - selector: 'button*=Submit' - }), - node({ role: 'statictext', depth: 1, name: 'Submit' }) - ] - const result = accessibilityNodesToSnapshotNodes(input) - expect(result).toHaveLength(1) // statictext suppressed - expect(result[0]!.role).toBe('button') - }) - - it('derives tagName from link selector', () => { - const input = [ - node({ role: 'link', depth: 0, name: 'Home', selector: 'a*=Home' }) - ] - const result = accessibilityNodesToSnapshotNodes(input) - expect(result[0]!.tagName).toBe('a') - }) - - it('derives tagName from the xpath the capture emits for a text match', () => { - const input = [ - node({ - role: 'link', - depth: 0, - name: 'Home', - selector: '//a[contains(., "Home")]' - }), - node({ - role: 'button', - depth: 0, - name: 'Go', - selector: '//my-button[contains(., "Go")]' - }) - ] - const result = accessibilityNodesToSnapshotNodes(input) - expect(result.map((n) => n.tagName)).toEqual(['a', 'my-button']) - }) - - it('falls back to role for selectorless tag extraction', () => { - const input = [ - node({ role: 'textbox', depth: 0, name: 'Email', selector: '#email' }) - ] - const result = accessibilityNodesToSnapshotNodes(input) - // #email doesn't start with a tag prefix → falls back to role - expect(result[0]!.tagName).toBe('textbox') - }) -}) - -// --------------------------------------------------------------------------- -// jsonElementToSnapshotNodes -// --------------------------------------------------------------------------- - -describe('jsonElementToSnapshotNodes', () => { - it('flattens a simple tree', () => { - const root = mobileEl('hierarchy', {}, [ - mobileEl('android.widget.Button', { - clickable: 'true', - 'content-desc': 'Submit', - text: '' - }) - ]) - const result = jsonElementToSnapshotNodes(root, 'android') - // Root hierarchy element + one button = 2 nodes - expect(result.length).toBeGreaterThanOrEqual(2) - const button = result.find((n) => n.role === 'button') - expect(button).toBeDefined() - expect(button).toMatchObject({ - role: 'button', - name: 'Submit', - isInteractive: true, - tagName: 'android.widget.Button' - }) - }) - - it('includes structural containers', () => { - const root = mobileEl('hierarchy', {}, [ - mobileEl( - 'android.widget.LinearLayout', - { - 'content-desc': '' - }, - [ - mobileEl('android.widget.Button', { - clickable: 'true', - 'content-desc': 'OK', - text: '' - }) - ] - ) - ]) - const result = jsonElementToSnapshotNodes(root, 'android') - // LinearLayout is not in INTERACTIVE_ROLES, not clickable → structural - expect(result.length).toBeGreaterThanOrEqual(2) - const structNode = result.find((n) => n.role === 'LinearLayout') - expect(structNode).toBeDefined() - expect(structNode!.isInteractive).toBe(false) - }) - - it('suppresses tag-only-interactive children of explicit parents', () => { - const root = mobileEl('hierarchy', {}, [ - mobileEl( - 'android.widget.LinearLayout', - { - clickable: 'true', - 'content-desc': 'Row' - }, - [ - // TextView is in ANDROID_INTERACTABLE_TAGS (tag-interactive), - // but not explicitly clickable → suppressed by suppressTagOnlyChildren - mobileEl('android.widget.TextView', { - clickable: 'false', - 'content-desc': '', - text: 'Label' - }) - ] - ) - ]) - const result = jsonElementToSnapshotNodes(root, 'android') - const textView = result.find((n) => n.role === 'statictext') - expect(textView).toBeDefined() - expect(textView!.isInteractive).toBe(false) // suppressed - }) - - it('propagates tagName from the mobile element', () => { - const root = mobileEl('hierarchy', {}, [ - mobileEl('android.widget.EditText', { - clickable: 'true', - 'content-desc': '', - 'resource-id': 'com.example:id/email', - text: '' - }) - ]) - const result = jsonElementToSnapshotNodes(root, 'android') - const editText = result.find((n) => n.role === 'textbox') - expect(editText).toBeDefined() - expect(editText!.tagName).toBe('android.widget.EditText') - }) -}) diff --git a/packages/elements/tsconfig.json b/packages/elements/tsconfig.json index 3bcc9638..89949f1c 100644 --- a/packages/elements/tsconfig.json +++ b/packages/elements/tsconfig.json @@ -10,7 +10,7 @@ "allowImportingTsExtensions": false, "declaration": true, "skipLibCheck": true, - "types": ["node", "@wdio/globals/types"] + "types": ["node", "webdriverio"] }, "include": ["src/**/*"] } diff --git a/packages/service/src/index.ts b/packages/service/src/index.ts index 07ee2321..c55d9029 100644 --- a/packages/service/src/index.ts +++ b/packages/service/src/index.ts @@ -52,6 +52,7 @@ import { ScreencastLifecycle } from './screencast-lifecycle.js' import { attachBidiListeners } from './bidi-listeners.js' import { parse } from 'stack-trace' import { type ServiceOptions, TraceType } from './types.js' +import type { GetSnapshotOptions, GetSnapshotResult } from '@wdio/elements' import { CONTEXT_CHANGE_COMMANDS, INTERNAL_COMMANDS, @@ -294,17 +295,21 @@ export default class DevToolsHookService implements Services.ServiceInstance { * @wdio/elements' getSnapshot() directly — no trace-mode overhead, * no screenshot round-trip, no page-settling. * - * Returns { text, elements } — see @wdio/elements SnapshotResult. + * Returns { text, refs } — see @wdio/elements GetSnapshotResult. */ browser.addCommand( 'getSnapshot', - async (options?: { inViewportOnly?: boolean }) => { + async (options?: GetSnapshotOptions): Promise => { try { const { getSnapshot } = await import('@wdio/elements') - return await getSnapshot(browser, options ?? { inViewportOnly: true }) + // Viewport-only keeps the tree small enough for an agent loop to call repeatedly. + return await getSnapshot(browser, { + ...options, + viewport: options?.viewport ?? true + }) } catch (err) { log.warn(`getSnapshot failed: ${errorMessage(err)}`) - return { text: '[Snapshot unavailable]', elements: {} } + return { text: '[Snapshot unavailable]', refs: [] } } } ) diff --git a/packages/service/src/types.ts b/packages/service/src/types.ts index 1263df01..bc358e1a 100644 --- a/packages/service/src/types.ts +++ b/packages/service/src/types.ts @@ -4,7 +4,7 @@ // @wdio/devtools-service/types. New code should import directly from // @wdio/devtools-shared. -import type { SnapshotResult } from '@wdio/devtools-core/element-types' +import type { GetSnapshotOptions, GetSnapshotResult } from '@wdio/elements' export { TraceType, @@ -77,7 +77,7 @@ declare namespace WebdriverIO { getPuppeteer?: () => Promise // Runtime DOM snapshot for agent auto-healing. Added by // @wdio/devtools-service in the before hook. - getSnapshot(options?: { inViewportOnly?: boolean }): Promise + getSnapshot(options?: GetSnapshotOptions): Promise } } diff --git a/packages/shared/src/locator-dialect.ts b/packages/shared/src/locator-dialect.ts index 262057f5..f50d07ad 100644 --- a/packages/shared/src/locator-dialect.ts +++ b/packages/shared/src/locator-dialect.ts @@ -65,12 +65,6 @@ export function isXPathLocator(locator: string): boolean { return /^\(*\.{0,2}\//.test(locator) } -/** Tag name an XPath locator is scoped to, for a consumer that reports the tag - * it matched. Undefined for CSS and for a tag-less expression (`//*[…]`). */ -export function xpathLocatorTag(locator: string): string | undefined { - return /^\/\/([a-z][a-z0-9-]*)\[/.exec(locator)?.[1] -} - /** WebdriverIO's text-locator forms — `tag*=Contains`, `tag=Exact`, tag optional. */ const TEXT_LOCATOR_RE = /^([a-z][a-z0-9-]*)?(\*?)=(.+)$/i diff --git a/packages/shared/src/snapshot-format.ts b/packages/shared/src/snapshot-format.ts index feeb8651..6a5b87d6 100644 --- a/packages/shared/src/snapshot-format.ts +++ b/packages/shared/src/snapshot-format.ts @@ -62,8 +62,7 @@ export const INTERACTIVE_ROLES = new Set([ ]) /** The fields of a node this module's helpers read. Deliberately narrower than - * `AccessibilityNode` so core's mobile `SnapshotNode` satisfies it too — both - * snapshot pipelines share these helpers. */ + * `AccessibilityNode` so the web and mobile serializers' nodes both satisfy it. */ export interface SnapshotFormatNode { role: string name: string diff --git a/packages/shared/tests/locator-dialect.test.ts b/packages/shared/tests/locator-dialect.test.ts index 5ce40eee..4ae47698 100644 --- a/packages/shared/tests/locator-dialect.test.ts +++ b/packages/shared/tests/locator-dialect.test.ts @@ -8,8 +8,7 @@ import { describe, it, expect } from 'vitest' import { isXPathLocator, locatorDialect, - locatorsMatch, - xpathLocatorTag + locatorsMatch } from '../src/locator-dialect.js' import { TEST_RUNNER_IDS, isTestRunnerId } from '../src/types.js' @@ -133,19 +132,6 @@ describe('isXPathLocator', () => { }) }) -describe('xpathLocatorTag', () => { - it('reads the tag the capture scoped its text match to', () => { - expect(xpathLocatorTag('//a[contains(., "Logout")]')).toBe('a') - expect(xpathLocatorTag('//my-button[contains(., "Go")]')).toBe('my-button') - }) - - it('has no tag to report for CSS or a tag-less expression', () => { - expect(xpathLocatorTag('//*[@text="Logout"]')).toBeUndefined() - expect(xpathLocatorTag('#username')).toBeUndefined() - expect(xpathLocatorTag('a*=Logout')).toBeUndefined() - }) -}) - // The exporter matches a command's locator against the captured element records // to stamp an input point. Once the capture emitted portable XPath, a `===` match // stopped recognising a test that locates by WebdriverIO text syntax. diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1b820a83..38784614 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -415,6 +415,9 @@ importers: packages/elements: dependencies: + '@wdio/snapshot': + specifier: 10.0.2 + version: 10.0.2(yauzl@2.10.0) '@xmldom/xmldom': specifier: ^0.9.12 version: 0.9.12 @@ -1955,42 +1958,49 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] '@napi-rs/nice-linux-arm64-musl@1.1.1': resolution: {integrity: sha512-+2Rzdb3nTIYZ0YJF43qf2twhqOCkiSrHx2Pg6DJaCPYhhaxbLcdlV8hCRMHghQ+EtZQWGNcS2xF4KxBhSGeutg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [musl] '@napi-rs/nice-linux-ppc64-gnu@1.1.1': resolution: {integrity: sha512-4FS8oc0GeHpwvv4tKciKkw3Y4jKsL7FRhaOeiPei0X9T4Jd619wHNe4xCLmN2EMgZoeGg+Q7GY7BsvwKpL22Tg==} engines: {node: '>= 10'} cpu: [ppc64] os: [linux] + libc: [glibc] '@napi-rs/nice-linux-riscv64-gnu@1.1.1': resolution: {integrity: sha512-HU0nw9uD4FO/oGCCk409tCi5IzIZpH2agE6nN4fqpwVlCn5BOq0MS1dXGjXaG17JaAvrlpV5ZeyZwSon10XOXw==} engines: {node: '>= 10'} cpu: [riscv64] os: [linux] + libc: [glibc] '@napi-rs/nice-linux-s390x-gnu@1.1.1': resolution: {integrity: sha512-2YqKJWWl24EwrX0DzCQgPLKQBxYDdBxOHot1KWEq7aY2uYeX+Uvtv4I8xFVVygJDgf6/92h9N3Y43WPx8+PAgQ==} engines: {node: '>= 10'} cpu: [s390x] os: [linux] + libc: [glibc] '@napi-rs/nice-linux-x64-gnu@1.1.1': resolution: {integrity: sha512-/gaNz3R92t+dcrfCw/96pDopcmec7oCcAQ3l/M+Zxr82KT4DljD37CpgrnXV+pJC263JkW572pdbP3hP+KjcIg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] '@napi-rs/nice-linux-x64-musl@1.1.1': resolution: {integrity: sha512-xScCGnyj/oppsNPMnevsBe3pvNaoK7FGvMjT35riz9YdhB2WtTG47ZlbxtOLpjeO9SqqQ2J2igCmz6IJOD5JYw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] '@napi-rs/nice-openharmony-arm64@1.1.1': resolution: {integrity: sha512-6uJPRVwVCLDeoOaNyeiW0gp2kFIM4r7PL2MczdZQHkFi9gVlgm+Vn+V6nTWRcu856mJ2WjYJiumEajfSm7arPQ==} @@ -2935,51 +2945,61 @@ packages: resolution: {integrity: sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==} cpu: [arm64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-arm64-musl@1.12.2': resolution: {integrity: sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==} cpu: [arm64] os: [linux] + libc: [musl] '@unrs/resolver-binding-linux-loong64-gnu@1.12.2': resolution: {integrity: sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==} cpu: [loong64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-loong64-musl@1.12.2': resolution: {integrity: sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==} cpu: [loong64] os: [linux] + libc: [musl] '@unrs/resolver-binding-linux-ppc64-gnu@1.12.2': resolution: {integrity: sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==} cpu: [ppc64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-riscv64-gnu@1.12.2': resolution: {integrity: sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==} cpu: [riscv64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-riscv64-musl@1.12.2': resolution: {integrity: sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==} cpu: [riscv64] os: [linux] + libc: [musl] '@unrs/resolver-binding-linux-s390x-gnu@1.12.2': resolution: {integrity: sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==} cpu: [s390x] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-x64-gnu@1.12.2': resolution: {integrity: sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==} cpu: [x64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-x64-musl@1.12.2': resolution: {integrity: sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==} cpu: [x64] os: [linux] + libc: [musl] '@unrs/resolver-binding-openharmony-arm64@1.12.2': resolution: {integrity: sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==} From ffa7aa0f16eebcbe87cbc57880f7e2d47bd7fc3c Mon Sep 17 00:00:00 2001 From: Vince Graics Date: Mon, 12 Oct 2026 20:00:17 +0200 Subject: [PATCH 02/12] fix: ship the show-trace bin each adapter declares /packages/**/*.mjs is gitignored, so bin/show-trace.mjs was never committed and every published adapter declared a bin it did not ship. Also ignore the snapshot files the wdio example spec writes. Co-Authored-By: Claude Opus 5.5 --- .changeset/show-trace-bin.md | 7 +++++++ .gitignore | 5 +++++ packages/nightwatch-devtools/bin/show-trace.mjs | 4 ++++ packages/selenium-devtools/bin/show-trace.mjs | 4 ++++ packages/service/bin/show-trace.mjs | 4 ++++ 5 files changed, 24 insertions(+) create mode 100644 .changeset/show-trace-bin.md create mode 100755 packages/nightwatch-devtools/bin/show-trace.mjs create mode 100755 packages/selenium-devtools/bin/show-trace.mjs create mode 100755 packages/service/bin/show-trace.mjs diff --git a/.changeset/show-trace-bin.md b/.changeset/show-trace-bin.md new file mode 100644 index 00000000..07c14ef5 --- /dev/null +++ b/.changeset/show-trace-bin.md @@ -0,0 +1,7 @@ +--- +'@wdio/devtools-service': patch +'@wdio/selenium-devtools': patch +'@wdio/nightwatch-devtools': patch +--- + +Ship the `show-trace` bin the package has always declared. The file was never committed, so `npx show-trace ` failed in a project that installed only this adapter. diff --git a/.gitignore b/.gitignore index 1ca4ecec..45b959c0 100644 --- a/.gitignore +++ b/.gitignore @@ -62,6 +62,7 @@ coverage/ # pnpm state, cache, logs, and debug files /packages/**/*.mjs +!/packages/*/bin/*.mjs .claude @@ -72,3 +73,7 @@ packages/**/allure-results*/ packages/**/allure-report*/ examples/wdio/cucumber/wdio.allure.conf.ts +# Written on purpose by examples/wdio/mocha/specs/wdio.e2e.ts +examples/wdio/screenshot.jpg +examples/wdio/snapshot-full.txt +examples/wdio/snapshot-viewport.txt diff --git a/packages/nightwatch-devtools/bin/show-trace.mjs b/packages/nightwatch-devtools/bin/show-trace.mjs new file mode 100755 index 00000000..83cac9cb --- /dev/null +++ b/packages/nightwatch-devtools/bin/show-trace.mjs @@ -0,0 +1,4 @@ +#!/usr/bin/env node +import { runShowTraceCli } from '@wdio/devtools-backend/show-trace' + +void runShowTraceCli(process.argv.slice(2)) diff --git a/packages/selenium-devtools/bin/show-trace.mjs b/packages/selenium-devtools/bin/show-trace.mjs new file mode 100755 index 00000000..83cac9cb --- /dev/null +++ b/packages/selenium-devtools/bin/show-trace.mjs @@ -0,0 +1,4 @@ +#!/usr/bin/env node +import { runShowTraceCli } from '@wdio/devtools-backend/show-trace' + +void runShowTraceCli(process.argv.slice(2)) diff --git a/packages/service/bin/show-trace.mjs b/packages/service/bin/show-trace.mjs new file mode 100755 index 00000000..83cac9cb --- /dev/null +++ b/packages/service/bin/show-trace.mjs @@ -0,0 +1,4 @@ +#!/usr/bin/env node +import { runShowTraceCli } from '@wdio/devtools-backend/show-trace' + +void runShowTraceCli(process.argv.slice(2)) From 5d6a68f155ec6fe14e5f00ab247271f931c5cada Mon Sep 17 00:00:00 2001 From: Vince Graics Date: Mon, 19 Oct 2026 19:47:31 +0200 Subject: [PATCH 03/12] feat(service)!: require WebdriverIO 10 @wdio/devtools-service consumes @wdio/elements 2, which renders through @wdio/snapshot, and that package pins webdriverio exactly to a v10 release. A 9.x peer range could therefore never resolve, so the service's peers (webdriverio, @wdio/types, @wdio/reporter, @wdio/logger, @wdio/protocols, @wdio/allure-reporter) narrow to ^10.0.0. - changeset: mark the service major and spell out the break (getSnapshot returns refs, viewport replaces inViewportOnly, Node >=22.19.0) - CI: the wdio-peer smoke job drops the 9 leg; it only exercises the service and @wdio/elements - smoke.ts: GetSnapshotResult and the viewport option - CLAUDE.md, elements README: stop claiming getSnapshot shares the trace capture's scripts or keeps the portable XPath default Co-Authored-By: Claude Opus 5.5 --- .changeset/snapshot-matches-the-wdio-cli.md | 5 +++-- .github/scripts/wdio-peer-smoke/smoke.ts | 6 +++--- .github/workflows/ci.yml | 11 +++++------ CLAUDE.md | 2 +- packages/elements/README.md | 6 +----- packages/service/package.json | 12 ++++++------ 6 files changed, 19 insertions(+), 23 deletions(-) diff --git a/.changeset/snapshot-matches-the-wdio-cli.md b/.changeset/snapshot-matches-the-wdio-cli.md index 19c4c38b..1c452cfd 100644 --- a/.changeset/snapshot-matches-the-wdio-cli.md +++ b/.changeset/snapshot-matches-the-wdio-cli.md @@ -1,9 +1,10 @@ --- '@wdio/elements': major +'@wdio/devtools-service': major --- `getSnapshot` now produces the same text as `wdio session snapshot --selectors`, by delegating to `@wdio/snapshot` instead of keeping a second renderer that drifted from the CLI's. Agents that read the CLI's output and agents that call this function now see one format. -Breaking: it returns `{ text, refs }` rather than `{ text, elements }`, takes `{ viewport }` (default `false`, the full page) in place of `{ inViewportOnly }` (default `true`), no longer prefixes a `[Page: …]` header, and throws on failure instead of returning a placeholder. `serializeWebSnapshot`, `serializeMobileSnapshot` and their option and result types are removed. Requires `webdriverio` ^10. +Breaking: it returns `{ text, refs }` rather than `{ text, elements }`, takes `{ viewport }` (default `false`, the full page) in place of `{ inViewportOnly }` (default `true`), no longer prefixes a `[Page: …]` header, and throws on failure instead of returning a placeholder. `serializeWebSnapshot`, `serializeMobileSnapshot` and their option and result types are removed. Requires `webdriverio` ^10, and declares `engines.node` `>=22.19.0`. -`@wdio/devtools-service` now consumes this version, so its `browser.getSnapshot()` returns the same `{ text, refs }` (defaulting to `{ viewport: true }`). +Breaking for `@wdio/devtools-service`: it now requires WebdriverIO 10. It consumes `@wdio/elements` 2, which delegates to `@wdio/snapshot`, and that package pins `webdriverio` exactly to a v10 release, so the service's peer ranges for `webdriverio`, `@wdio/types`, `@wdio/reporter`, `@wdio/logger`, `@wdio/protocols` and `@wdio/allure-reporter` are narrowed to `^10.0.0`. Its `browser.getSnapshot()` returns the same `{ text, refs }` instead of `{ text, elements }`, and the `inViewportOnly` option is replaced by `viewport`. The service still defaults to the viewport (`viewport: true`) so repeated calls from an agent loop stay small, whereas calling `getSnapshot` from `@wdio/elements` directly defaults to the full page. diff --git a/.github/scripts/wdio-peer-smoke/smoke.ts b/.github/scripts/wdio-peer-smoke/smoke.ts index 50c7c8b3..e638ab6c 100644 --- a/.github/scripts/wdio-peer-smoke/smoke.ts +++ b/.github/scripts/wdio-peer-smoke/smoke.ts @@ -6,7 +6,7 @@ import DevToolsHookService, { setupForDevtools, type ServiceOptions } from '@wdio/devtools-service' -import { getElements, getSnapshot, type SnapshotResult } from '@wdio/elements' +import { getElements, getSnapshot, type GetSnapshotResult } from '@wdio/elements' import { xmlToJSON } from '@wdio/elements/locators' const serviceOptions: ServiceOptions = { @@ -35,8 +35,8 @@ export async function standalone(opts: Options.WebdriverIO): Promise { export async function elements( browser: WebdriverIO.Browser -): Promise { +): Promise { await getElements(browser, { inViewportOnly: false }) xmlToJSON('') - return getSnapshot(browser, { inViewportOnly: false }) + return getSnapshot(browser, { viewport: false }) } diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c70815a4..fcc1c635 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -93,18 +93,17 @@ jobs: - name: 🧩 Component Tests run: pnpm test:ui --maxInstances 2 - # The workspace develops against one WebdriverIO major, but the service and - # @wdio/elements declare peers on two. Each published major is installed from - # npm into a scratch project beside the packed tarballs, so a peer conflict, a - # broken entry point or a type mismatch with that major fails here first. 9 - # runs too so a red 10 can be told apart from a broken harness. + # The service and @wdio/elements are WebdriverIO-10-only (@wdio/snapshot pins + # webdriverio exactly). The published major is installed from npm into a + # scratch project beside the packed tarballs, so a peer conflict, a broken + # entry point or a type mismatch with that major fails here first. wdio-peer: name: wdio-peer (webdriverio ${{ matrix.wdio }}) runs-on: ubuntu-latest strategy: fail-fast: false matrix: - wdio: [9, 10] + wdio: [10] steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Use Node.js diff --git a/CLAUDE.md b/CLAUDE.md index e06da11a..c3dfe735 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -353,7 +353,7 @@ Documented divergences from the conventions above. They exist today as debt to b - Relatedly, cucumber's **per-step** `resetCommandTracking()` was wiping the native-assert buffer, so each scenario kept only its *last* step's assertions (measured 1 of 2). The buffer is per **test unit** — `resetTestTracking()` at `wrapBrowserOnce` now clears it. - **DOM `mutations` ARE captured**, and the old `ECONNRESET` / "collector not found" attribution is **wrong** — neither appears any more. The gap closed itself with document-start injection (`core/bidi-preload.ts`), confirmed on a *baseline* build so the credit belongs to that change and not to anything here. Measured per scenario with `traceGranularity:'test'` + `webSocketUrl: true`: 39 and 19 mutation entries, 2 DOM anchors each, **0 of 19 rows on the wrong document**. What remains is noise, not a gap: the screencast poller issues `/screenshot` every 200 ms into the session Nightwatch quits per scenario, logging 9–26 `WARN webdriverHttp: … socket hang up` per run. Fix is to stop the recorder before the per-scenario quit, or suppress the warn for a session in teardown. - **A tsup entry that another entry imports is not a leaf, so an `import.meta.url === process.argv[1]` self-start check inside it is dead code; a CLI has to be its own leaf entry.** tsup hoists a module body shared by two entries into `dist/chunk-*.js`, and there `import.meta.url` is the chunk's path, which can never equal `process.argv[1]`. `packages/backend/src/show-trace.ts` imports `start` from `index.ts`, which is what made index shared, so index's old "start if run directly" guard was dead in **every** build: `node dist/index.js` exited 0 without ever serving, while `dist/show-trace.js` self-started correctly for exactly the same reason inverted, being a leaf whose body stays in its own output file (it also compares realpaths, because the invoked path is the `node_modules/.bin` symlink). The live dashboard server is therefore its own leaf entry, `packages/backend/src/server.ts` (shebang, built to an executable `dist/server.js`, shipped as the `devtools-backend` bin, accepting `--port`, `--hostname`, `-h`/`--help`), and `index.ts` stays library-only for the three adapters' in-process `start`/`stop`. Same family as the `cucumberHooks.cts` entry above: the tsup entry list is part of the contract, and in both cases the symptom was silence rather than an error. -- **A captured text locator is generated in the recording runner's dialect; every other branch is portable CSS.** `shared/locator-dialect.ts` `locatorDialect(runner)` is the one fact table — WDIO runners (`mocha`/`jasmine`/`cucumber`) get `a*=Logout`, `nightwatch*`/`selenium-webdriver` and an **unidentified recorder** get `//a[contains(., "Logout")]`. The id reaches the page script as `CaptureActionSnapshotInput.runner` and the zip as an extension field on `context-options`, read back through `isTestRunnerId` onto `Metadata.runner`; absent in older and foreign zips, where the player shows no hint. Under WDIO a text carrying a `"` still emits XPath — WDIO compiles `tag*=` to XPath with `"` quoting and would build a broken expression — and WDIO resolves `//` itself. `locatorsMatch` decomposes **both** sides from either dialect, so `after.point` survives whichever way round the two grammars fall; the `concat()`-stitched literal is still left to exact comparison. `@wdio/elements`' standalone `getSnapshot` deliberately keeps the portable XPath default (its output is pasted into arbitrary tools), so a WDIO run's `browser.getSnapshot()` and its trace A11y tab disagree on that one branch. +- **A captured text locator is generated in the recording runner's dialect; every other branch is portable CSS.** `shared/locator-dialect.ts` `locatorDialect(runner)` is the one fact table — WDIO runners (`mocha`/`jasmine`/`cucumber`) get `a*=Logout`, `nightwatch*`/`selenium-webdriver` and an **unidentified recorder** get `//a[contains(., "Logout")]`. The id reaches the page script as `CaptureActionSnapshotInput.runner` and the zip as an extension field on `context-options`, read back through `isTestRunnerId` onto `Metadata.runner`; absent in older and foreign zips, where the player shows no hint. Under WDIO a text carrying a `"` still emits XPath — WDIO compiles `tag*=` to XPath with `"` quoting and would build a broken expression — and WDIO resolves `//` itself. `locatorsMatch` decomposes **both** sides from either dialect, so `after.point` survives whichever way round the two grammars fall; the `concat()`-stitched literal is still left to exact comparison. `@wdio/elements` 2.x `getSnapshot` is not on this table at all: it renders `@wdio/snapshot`'s `attachSelectors` choice so its text matches `wdio session snapshot --selectors`, which can be a WebdriverIO-v10-only strategy (`role/…`, `aria/…`), so a WDIO run's `browser.getSnapshot()` and its trace A11y tab can differ on that branch. - `Metadata.runner` (typed `TestRunnerId`) and `metadata.options.framework` (untyped `string`, read by the sidebar's `getFramework`) are two carriers of the same fact. All three adapters now set both; the next change to either should collapse `getFramework` onto `Metadata.runner`. - Selenium's `ctx.runner` (the *detected JS runner* — mocha/jest/cucumber, from `detectRunner()`) shadows the new `Metadata.runner` concept. Renamed to `detectedRunner` at the `driverMetadata` boundary only, where the two met inside one function and a swap would have been a silent dialect bug; the repo-wide rename in `plugin-internals.ts`/`session-lifecycle.ts` is still open. - **A node:assert's target comes from VALUE PROVENANCE, not from the assert call.** node:assert takes values, so `assert.equal(await el.getText(), …)` names no element. `core/read-value-locators.ts` owns the value→locator registry (`rememberReadValue`/`selectorForReadValue`/`resolveAssertTargetFromArgs`; most-recent-producer-wins, 200 keys, ≤256 chars, primitives only), reached through `patchNodeAssert`'s optional `resolveAssertTarget` hook, which walks the **raw** args (sanitizing destroys handle identity) onto the shared `CommandLog.selector`. It lives in core because two adapters feed it. **Selenium** feeds it from `driverPatcher`'s settle path and composes it with its handle WeakMap (`helpers/element-locators.ts`, adapter-local — it keys on `WebElement` identity, which no other framework has). **Nightwatch** feeds it from `browserProxy`'s capture callback via `assertTarget.commandTargetSelector`, an allowlist of element-read commands; every other command — every driver-level read (`title`, `getCurrentUrl`, `source`) included — records the **null sentinel**, without which a page-title assertion inherits the box of an element that reads the same text (measured on selenium's mocha example, `h1` vs `document.title` both "Example Domain"; reproduced and fixed for Nightwatch on a local `#hero`/`` collision page). Measured: Nightwatch node:assert rows with a locator **0/7 → 4/7** (the 3 without are a title read, the title/`#hero` collision, and a literal); Selenium unchanged at 2/4 mocha and 2/3 cucumber. Residuals: boolean reads are weakly discriminating, so a value collision with no intervening producer can mis-attribute; Nightwatch's `element()/.find()` API and its ignored commands (`execute`, `executeAsync`, `perform`) are never wrapped, so their values neither claim nor clear. **`after.point` is deliberately not extended to assert rows** — `resolveActionPoint` gates on `class:'Element'` + `POINTABLE_METHODS`, so WDIO's folded assertion rows get none either; a point draws a click marker an assertion never earned. diff --git a/packages/elements/README.md b/packages/elements/README.md index 967ff75b..9818a1f3 100644 --- a/packages/elements/README.md +++ b/packages/elements/README.md @@ -36,11 +36,7 @@ const visible = await getSnapshot(browser, { viewport: true }) Web sessions walk the DOM in the page; Android and iOS sessions parse the page source. Errors (`SnapshotError`) propagate instead of returning a placeholder. -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 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. +The DevTools **per-action trace capture** is a separate path with its own page scripts, so a locator in a trace can differ from the one `getSnapshot` renders. `getSnapshot` renders `@wdio/snapshot`'s output, the same as the CLI. Differences from the CLI, which keeps session state this function does not have: diff --git a/packages/service/package.json b/packages/service/package.json index 2e0c0bd2..887388a4 100644 --- a/packages/service/package.json +++ b/packages/service/package.json @@ -78,12 +78,12 @@ "webdriverio": "^10.0.2" }, "peerDependencies": { - "@wdio/allure-reporter": "^9.0.0 || ^10.0.0", - "@wdio/logger": "^9.18.0 || ^10.0.0", - "@wdio/protocols": "^9.30.1 || ^10.0.0", - "@wdio/reporter": "^9.19.1 || ^10.0.0", - "@wdio/types": "^9.19.1 || ^10.0.0", - "webdriverio": "^9.19.1 || ^10.0.0" + "@wdio/allure-reporter": "^10.0.0", + "@wdio/logger": "^10.0.0", + "@wdio/protocols": "^10.0.0", + "@wdio/reporter": "^10.0.0", + "@wdio/types": "^10.0.0", + "webdriverio": "^10.0.0" }, "peerDependenciesMeta": { "@wdio/allure-reporter": { From 2e9d92da03d53dd352558bf532a8725c5a4011d2 Mon Sep 17 00:00:00 2001 From: Vince Graics <wince17@gmail.com> Date: Mon, 19 Oct 2026 19:47:31 +0200 Subject: [PATCH 04/12] docs(core): fix a stale source-XML comment The page source is stamped on the root by action-snapshot.ts; the helper the comment named no longer exists. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --- packages/core/src/element-snapshot.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/element-snapshot.ts b/packages/core/src/element-snapshot.ts index 153ca01b..3ff5c790 100644 --- a/packages/core/src/element-snapshot.ts +++ b/packages/core/src/element-snapshot.ts @@ -636,7 +636,7 @@ export function serializeMobileSnapshot( const { platform, deviceName, viewport, sourceXML } = context const { inViewportOnly = true } = options - // Auto-detect source XML stashed by getMobileVisibleElementsWithTree + // Falls back to the page source action-snapshot.ts stamps on the root const effectiveXML = sourceXML || root.attributes._sourceXML const effectiveViewport = viewport ?? { width: 9999, height: 9999 } From 09187ac6b5e25b6ae1540acf8376b603481f71da Mon Sep 17 00:00:00 2001 From: Vince Graics <wince17@gmail.com> Date: Mon, 19 Oct 2026 19:49:05 +0200 Subject: [PATCH 05/12] fix(elements): accept any @wdio/snapshot 10 minor @wdio/snapshot pins webdriverio exactly, so an exact @wdio/snapshot pin nested a second webdriverio under every consumer whose ^10 resolved a newer minor. A caret range lets the consumer's install pick the @wdio/snapshot release that matches its webdriverio, leaving one copy; the peer smoke asserts exactly that. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --- .changeset/snapshot-matches-the-wdio-cli.md | 4 ++-- packages/elements/package.json | 2 +- pnpm-lock.yaml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.changeset/snapshot-matches-the-wdio-cli.md b/.changeset/snapshot-matches-the-wdio-cli.md index 1c452cfd..56b898bf 100644 --- a/.changeset/snapshot-matches-the-wdio-cli.md +++ b/.changeset/snapshot-matches-the-wdio-cli.md @@ -1,6 +1,6 @@ --- -'@wdio/elements': major -'@wdio/devtools-service': major +"@wdio/elements": major +"@wdio/devtools-service": major --- `getSnapshot` now produces the same text as `wdio session snapshot --selectors`, by delegating to `@wdio/snapshot` instead of keeping a second renderer that drifted from the CLI's. Agents that read the CLI's output and agents that call this function now see one format. diff --git a/packages/elements/package.json b/packages/elements/package.json index 2f82bc83..1ea1e5f5 100644 --- a/packages/elements/package.json +++ b/packages/elements/package.json @@ -39,7 +39,7 @@ "node": ">=22.19.0" }, "dependencies": { - "@wdio/snapshot": "10.0.2", + "@wdio/snapshot": "^10.0.2", "@xmldom/xmldom": "^0.9.12", "xpath": "^0.0.34" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 38784614..fb62ff0e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -416,7 +416,7 @@ importers: packages/elements: dependencies: '@wdio/snapshot': - specifier: 10.0.2 + specifier: ^10.0.2 version: 10.0.2(yauzl@2.10.0) '@xmldom/xmldom': specifier: ^0.9.12 From 7a3b693232fc90fe81eb06290fa85245b7612b5f Mon Sep 17 00:00:00 2001 From: Vince Graics <wince17@gmail.com> Date: Mon, 19 Oct 2026 19:54:15 +0200 Subject: [PATCH 06/12] docs(elements): drop references to the removed snapshot API The elements snapshot shim and its serializer surface were removed, so the docs no longer describe them as live. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --- ARCHITECTURE.md | 2 +- packages/elements/README.md | 4 ++-- packages/elements/ROADMAP.md | 4 +++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index c0f9fa98..89c51d0f 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -184,7 +184,7 @@ The execution environment is the browser, not Node, so this package cannot impor The WDIO-typed element-detection API (`@wdio/elements`): `getSnapshot`, `getElements`, the accessibility-tree and interactable-element readers. Unlike `shared` and `core` this package **is** published, so its exported names are public API. -Imports from: `core`. The script bodies, serializers, types, and locator generation all live in `core` (`element-scripts.ts`, `element-snapshot.ts`, `element-types.ts`, `locators/`) because they're framework-agnostic; this package holds only the wrappers that take a `WebdriverIO.Browser` — which `core` can't type against. The arrow never reverses: `core` does not import `elements`, and the adapters' per-action capture (`captureActionSnapshot`) calls core's script bodies directly rather than going through this package. `elements/snapshot.ts` and `elements/locators/index.ts` are re-export shims kept for API stability. +Imports from: `core`. The script bodies, serializers, types, and locator generation all live in `core` (`element-scripts.ts`, `element-snapshot.ts`, `element-types.ts`, `locators/`) because they're framework-agnostic; this package holds only the wrappers that take a `WebdriverIO.Browser` — which `core` can't type against. The arrow never reverses: `core` does not import `elements`, and the adapters' per-action capture (`captureActionSnapshot`) calls core's script bodies directly rather than going through this package. `elements/locators/index.ts` is a re-export shim kept for API stability. The DOM-walking scripts run in the page via `browser.execute`, so — like `script` — they avoid Node-only APIs. diff --git a/packages/elements/README.md b/packages/elements/README.md index 9818a1f3..de143690 100644 --- a/packages/elements/README.md +++ b/packages/elements/README.md @@ -34,7 +34,7 @@ const visible = await getSnapshot(browser, { viewport: true }) // refs: SnapshotRef[] — { id, candidates, … } for every ref in the text ``` -Web sessions walk the DOM in the page; Android and iOS sessions parse the page source. Errors (`SnapshotError`) propagate instead of returning a placeholder. +Web sessions walk the DOM in the page; native sessions (`platformName` of Android, iOS, Mac or Windows with no `browserName`) parse the page source. Errors (`SnapshotError`) propagate instead of returning a placeholder. The DevTools **per-action trace capture** is a separate path with its own page scripts, so a locator in a trace can differ from the one `getSnapshot` renders. `getSnapshot` renders `@wdio/snapshot`'s output, the same as the CLI. @@ -44,7 +44,7 @@ Differences from the CLI, which keeps session state this function does not have: - Iframes are not inlined over BiDi. A cut or cross-origin frame renders as the frame-hint note (`wdio session frame <ref>`). - Refs are numbered from `e1` on every call. -`@wdio/devtools-service`'s `browser.getSnapshot()` calls this function and returns its `{ text, refs }`. Unlike the default here, it passes `{ viewport: true }` when called without options, so it reads the visible viewport; pass `{ viewport: false }` for the full page. +`@wdio/devtools-service`'s `browser.getSnapshot()` calls this function and returns its `{ text, refs }`. Unlike the default here, it uses `viewport: true` when `viewport` is not set, so it reads the visible viewport; pass `{ viewport: false }` for the full page. It also returns the placeholder `{ text: '[Snapshot unavailable]', refs: [] }` (and logs a warning) instead of throwing, whereas the `getSnapshot` exported here throws `SnapshotError`. ### Browser diff --git a/packages/elements/ROADMAP.md b/packages/elements/ROADMAP.md index e1eeeff6..a5fc510c 100644 --- a/packages/elements/ROADMAP.md +++ b/packages/elements/ROADMAP.md @@ -19,7 +19,9 @@ net first. ### 1.2 Snapshot-output golden tests -`serializeMobileSnapshot` and `serializeWebSnapshot` need output-fidelity tests +`serializeMobileSnapshot` (`packages/core/src/element-snapshot.ts`) and +`serializeWebSnapshot` (`packages/trace/src/a11y-snapshot.ts`, re-exported by +core) 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. From 7cd00ab95c17b51a2d6b9aaa5061f915cdc5f84c Mon Sep 17 00:00:00 2001 From: Vince Graics <wince17@gmail.com> Date: Tue, 20 Oct 2026 07:30:16 +0200 Subject: [PATCH 07/12] docs(changeset): name the snapshot routing and getElements output changes The audit found two consumer-visible changes the changeset did not mention: mobile browser sessions move from Appium's page source to the page collector, and native getElements stops attaching _sourceXML. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --- .changeset/snapshot-matches-the-wdio-cli.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/snapshot-matches-the-wdio-cli.md b/.changeset/snapshot-matches-the-wdio-cli.md index 56b898bf..6077fdb2 100644 --- a/.changeset/snapshot-matches-the-wdio-cli.md +++ b/.changeset/snapshot-matches-the-wdio-cli.md @@ -5,6 +5,6 @@ `getSnapshot` now produces the same text as `wdio session snapshot --selectors`, by delegating to `@wdio/snapshot` instead of keeping a second renderer that drifted from the CLI's. Agents that read the CLI's output and agents that call this function now see one format. -Breaking: it returns `{ text, refs }` rather than `{ text, elements }`, takes `{ viewport }` (default `false`, the full page) in place of `{ inViewportOnly }` (default `true`), no longer prefixes a `[Page: …]` header, and throws on failure instead of returning a placeholder. `serializeWebSnapshot`, `serializeMobileSnapshot` and their option and result types are removed. Requires `webdriverio` ^10, and declares `engines.node` `>=22.19.0`. +Breaking: it returns `{ text, refs }` rather than `{ text, elements }`, takes `{ viewport }` (default `false`, the full page) in place of `{ inViewportOnly }` (default `true`), no longer prefixes a `[Page: …]` header, and throws on failure instead of returning a placeholder. `serializeWebSnapshot`, `serializeMobileSnapshot` and their option and result types are removed. Requires `webdriverio` ^10, and declares `engines.node` `>=22.19.0`. A mobile browser session (Chrome on Android, Safari on iOS) is now snapshotted from the page rather than from Appium's page source, and Mac and Windows native sessions take the native path. `getElements` on a native session no longer attaches the raw page source as `tree.attributes._sourceXML`. Breaking for `@wdio/devtools-service`: it now requires WebdriverIO 10. It consumes `@wdio/elements` 2, which delegates to `@wdio/snapshot`, and that package pins `webdriverio` exactly to a v10 release, so the service's peer ranges for `webdriverio`, `@wdio/types`, `@wdio/reporter`, `@wdio/logger`, `@wdio/protocols` and `@wdio/allure-reporter` are narrowed to `^10.0.0`. Its `browser.getSnapshot()` returns the same `{ text, refs }` instead of `{ text, elements }`, and the `inViewportOnly` option is replaced by `viewport`. The service still defaults to the viewport (`viewport: true`) so repeated calls from an agent loop stay small, whereas calling `getSnapshot` from `@wdio/elements` directly defaults to the full page. From 9fca30c56abbc8181488e0fdee255eccc1d0396f Mon Sep 17 00:00:00 2001 From: Vince Graics <wince17@gmail.com> Date: Tue, 20 Oct 2026 08:27:14 +0200 Subject: [PATCH 08/12] fix(elements): detect native sessions from vendor capabilities Device clouds state platformName (and browserName) only inside a vendor bag such as bstack:options. nativePlatformName read top-level keys only, so those native sessions fell through to the web collector and getSnapshot returned "[Snapshot unavailable]". The base got this from WDIO's isAndroid/isIOS, which read vendor options too. Reuse shared's one-level deepCapString (now exported) rather than a second reader, and cover it with unit tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --- packages/elements/package.json | 1 + packages/elements/src/get-snapshot.ts | 10 +++++--- packages/elements/tests/get-snapshot.test.ts | 24 ++++++++++++++++++++ packages/shared/src/device.ts | 2 +- packages/shared/tests/device.test.ts | 19 ++++++++++++++++ pnpm-lock.yaml | 3 +++ 6 files changed, 55 insertions(+), 4 deletions(-) diff --git a/packages/elements/package.json b/packages/elements/package.json index 1ea1e5f5..7ffdeb32 100644 --- a/packages/elements/package.json +++ b/packages/elements/package.json @@ -47,6 +47,7 @@ "@microsoft/api-extractor": "^7.59.3", "@types/node": "26.6.4", "@wdio/devtools-core": "workspace:^", + "@wdio/devtools-shared": "workspace:^", "@wdio/globals": "10.0.2", "tsup": "^8.5.1", "typescript": "6.0.3", diff --git a/packages/elements/src/get-snapshot.ts b/packages/elements/src/get-snapshot.ts index 23b22d5f..5f9bdb66 100644 --- a/packages/elements/src/get-snapshot.ts +++ b/packages/elements/src/get-snapshot.ts @@ -6,6 +6,7 @@ import { nativePlatform, parseNativeSource } from '@wdio/snapshot' +import { deepCapString } from '@wdio/devtools-shared/device' import type { NativePlatform, SnapshotNode, SnapshotRef } from '@wdio/snapshot' export interface GetSnapshotOptions { @@ -26,10 +27,13 @@ const NATIVE_PLATFORMS = new Set(['android', 'ios', 'mac', 'windows']) // @wdio/session's `classify`: a browserName makes a mobile session web. Desktop // browsers also report mac/windows here, which their browserName excludes too. function nativePlatformName(caps: Record<string, unknown>): string | undefined { - const platformName = String( - caps.platformName ?? caps['appium:platformName'] ?? '' + const platformName = ( + deepCapString(caps, 'platformName') ?? + deepCapString(caps, 'appium:platformName') ?? + '' ).toLowerCase() - return NATIVE_PLATFORMS.has(platformName) && !caps.browserName + return NATIVE_PLATFORMS.has(platformName) && + !deepCapString(caps, 'browserName') ? platformName : undefined } diff --git a/packages/elements/tests/get-snapshot.test.ts b/packages/elements/tests/get-snapshot.test.ts index 5ae8048a..7774567a 100644 --- a/packages/elements/tests/get-snapshot.test.ts +++ b/packages/elements/tests/get-snapshot.test.ts @@ -88,12 +88,36 @@ function nativeBrowser() { }) } +it('parses a vendor-bag-only native session with the resolved platform', async () => { + await getSnapshot( + browser({ + capabilities: { 'bstack:options': { platformName: 'iOS' } }, + getPageSource: vi.fn().mockResolvedValue(ANDROID_XML) + }) + ) + expect(vi.mocked(snap.collectWeb)).not.toHaveBeenCalled() + expect(vi.mocked(snap.parseNativeSource).mock.calls[0][1]).toBe('ios') +}) + describe('getSnapshot web/native routing (same as wdio session)', () => { it.each([ ['Android app', { platformName: 'Android' }, 'native'], ['iOS app via appium: prefix', { 'appium:platformName': 'iOS' }, 'native'], ['macOS app', { platformName: 'mac' }, 'native'], ['Windows app', { platformName: 'Windows' }, 'native'], + [ + 'Android app with platformName only in a vendor bag', + { + 'bstack:options': { platformName: 'Android' }, + 'appium:automationName': 'UiAutomator2' + }, + 'native' + ], + [ + 'Safari on iOS with both only in a vendor bag', + { 'bstack:options': { platformName: 'iOS', browserName: 'Safari' } }, + 'web' + ], [ 'Chrome on Android', { platformName: 'Android', browserName: 'chrome' }, diff --git a/packages/shared/src/device.ts b/packages/shared/src/device.ts index 006abb92..6b1cf800 100644 --- a/packages/shared/src/device.ts +++ b/packages/shared/src/device.ts @@ -56,7 +56,7 @@ function capString( * so a request-shaped bag's `firstMatch` array is deliberately not scanned: the * server merges `alwaysMatch` with the ONE entry it chose, and reading a * browser out of any entry would claim one the session never got. */ -function deepCapString( +export function deepCapString( caps: Record<string, unknown>, key: string ): string | undefined { diff --git a/packages/shared/tests/device.test.ts b/packages/shared/tests/device.test.ts index daf28c69..7842fe69 100644 --- a/packages/shared/tests/device.test.ts +++ b/packages/shared/tests/device.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it } from 'vitest' import { + deepCapString, deviceFromCapabilities, deviceLabel, isNativeAppSession, @@ -254,3 +255,21 @@ describe('isNativeAppSession', () => { expect(isNativeAppSession({})).toBe(false) }) }) + +describe('deepCapString', () => { + it('prefers a top-level key over a vendor bag', () => { + expect(deepCapString({ k: 'top', 'v:options': { k: 'nested' } }, 'k')).toBe( + 'top' + ) + }) + + it('reads one level of vendor options', () => { + expect(deepCapString({ 'v:options': { k: 'nested' } }, 'k')).toBe('nested') + }) + + it('does not descend further or into arrays', () => { + expect( + deepCapString({ a: { b: { k: 'deep' } }, c: [{ k: 'arr' }] }, 'k') + ).toBeUndefined() + }) +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fb62ff0e..e9fe32ae 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -434,6 +434,9 @@ importers: '@wdio/devtools-core': specifier: workspace:^ version: link:../core + '@wdio/devtools-shared': + specifier: workspace:^ + version: link:../shared '@wdio/globals': specifier: 10.0.2 version: 10.0.2(expect-webdriverio@8.0.0(@wdio/types@10.0.1)(webdriverio@10.0.2(yauzl@2.10.0)))(webdriverio@10.0.2(yauzl@2.10.0)) From 6a568d3035f29c0ea6df9afec82f9e1dac42e357 Mon Sep 17 00:00:00 2001 From: Vince Graics <wince17@gmail.com> Date: Tue, 20 Oct 2026 08:44:54 +0200 Subject: [PATCH 09/12] test(conformance): answer GET /timeouts the way a real browser does WebdriverIO 10.1.0's BiDi execute reads the session's script timeout before every call (scriptTimeoutOf, `getTimeouts().then(({ script }) => script, ...)`). The fake answered every unlisted endpoint with null, so that destructuring threw inside the fulfilment handler, which its own rejection handler does not catch: every browser.execute rejected before script.callFunction was sent. The service never drained the collector with its forced anchor, and the bidi:true scenario reported every document unanchored and every row replaying the wrong page. W3C requires GET /timeouts to return the timeouts object, and real Chrome does: the mocha login example traces identically on 10.0.2 and 10.1.0 (4 of 4 documents anchored, 13 actions). The gap was the fake's. The fixed-reply endpoints move into one table so the route stays under the function-size cap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --- packages/conformance/src/constants.ts | 7 +++++++ packages/conformance/src/fake-webdriver.ts | 23 ++++++++++------------ packages/conformance/tests/fakes.test.ts | 13 ++++++++++++ 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/packages/conformance/src/constants.ts b/packages/conformance/src/constants.ts index 4fef2ce7..7d2244f5 100644 --- a/packages/conformance/src/constants.ts +++ b/packages/conformance/src/constants.ts @@ -104,6 +104,13 @@ export const PAGES: Record<string, Record<string, string | null>> = { export const BROKEN_PATH = '/broken' +/** W3C's session defaults: a real browser always answers `GET /timeouts` with all three, and WebdriverIO 10.1 reads `script` from it before every BiDi `execute`. */ +export const DEFAULT_TIMEOUTS = { + script: 30_000, + pageLoad: 300_000, + implicit: 0 +} + export const BIDI_PATH = '/session/conformance/bidi' /** BiDi acknowledgements whose callers read a field back; commands the fake browser models are answered from its state. */ diff --git a/packages/conformance/src/fake-webdriver.ts b/packages/conformance/src/fake-webdriver.ts index 0bff064c..d30eda8f 100644 --- a/packages/conformance/src/fake-webdriver.ts +++ b/packages/conformance/src/fake-webdriver.ts @@ -14,6 +14,7 @@ import { BLANK_PNG, BROKEN_PATH, COLLECTOR_SIGNATURE, + DEFAULT_TIMEOUTS, ELEMENT_KEY, NAVIGATION_HALF_MS, PAGES, @@ -52,6 +53,14 @@ type Reply = { const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)) +const FIXED_REPLIES: Record<string, unknown> = { + '/timeouts': DEFAULT_TIMEOUTS, + '/screenshot': BLANK_PNG, + '/window/rect': { x: 0, y: 0, width: 1280, height: 720 }, + '/window': WINDOW_HANDLE, + '/window/handles': [WINDOW_HANDLE] +} + const anchorOf = (url: string, bornAt: number): TraceMutation => ({ type: 'childList', url, @@ -218,19 +227,7 @@ export class FakeWebDriver { ) } } - if (path === '/screenshot') { - return { value: BLANK_PNG } - } - if (path === '/window/rect') { - return { value: { x: 0, y: 0, width: 1280, height: 720 } } - } - if (path === '/window') { - return { value: WINDOW_HANDLE } - } - if (path === '/window/handles') { - return { value: [WINDOW_HANDLE] } - } - return { value: null } + return { value: FIXED_REPLIES[path] ?? null } } async #navigate(path: string): Promise<Reply> { diff --git a/packages/conformance/tests/fakes.test.ts b/packages/conformance/tests/fakes.test.ts index d31e3502..ee68feec 100644 --- a/packages/conformance/tests/fakes.test.ts +++ b/packages/conformance/tests/fakes.test.ts @@ -155,6 +155,19 @@ describe('the fake BiDi endpoint', () => { }) }) +describe('the fake WebDriver', () => { + // WebdriverIO 10.1 destructures this before every BiDi execute; a null answer made every script throw unsent. + it('answers GET /timeouts with the W3C timeouts object', async () => { + const driver = await start(true) + await session(driver) + expect(await call(driver, 'GET', '/session/conformance/timeouts')).toEqual({ + script: 30_000, + pageLoad: 300_000, + implicit: 0 + }) + }) +}) + describe('the fake collector', () => { // Regression: re-injection kept the document anchored, so a duplicate anchor could never be modelled. it('anchors again after a second injection, which one-anchor-per-document flags', async () => { From 1e88a9b15305357444bb7837b7aa1c3424358382 Mon Sep 17 00:00:00 2001 From: Vince Graics <wince17@gmail.com> Date: Tue, 20 Oct 2026 08:44:54 +0200 Subject: [PATCH 10/12] chore(deps): move the workspace to WebdriverIO 10.1.0 @wdio/snapshot pins webdriverio exactly, so adopting the release that carries webdriverio#16131 needs the workspace on 10.1.0. @wdio/protocols stays at 10.0.2; no 10.1.0 was published. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --- examples/wdio/package.json | 14 +- package.json | 6 +- packages/app/package.json | 8 +- packages/backend/package.json | 4 +- packages/conformance/package.json | 12 +- packages/elements/package.json | 4 +- packages/nightwatch-devtools/package.json | 4 +- packages/selenium-devtools/package.json | 4 +- packages/service/package.json | 12 +- pnpm-lock.yaml | 1179 +++++++++------------ 10 files changed, 548 insertions(+), 699 deletions(-) diff --git a/examples/wdio/package.json b/examples/wdio/package.json index 42f1becc..f3c53228 100644 --- a/examples/wdio/package.json +++ b/examples/wdio/package.json @@ -4,14 +4,14 @@ "private": true, "type": "module", "devDependencies": { - "@wdio/cli": "10.0.2", - "@wdio/cucumber-framework": "10.0.1", + "@wdio/cli": "10.1.0", + "@wdio/cucumber-framework": "10.1.0", "@wdio/devtools-service": "workspace:*", - "@wdio/globals": "10.0.2", - "@wdio/local-runner": "10.0.2", - "@wdio/mocha-framework": "10.0.1", - "@wdio/spec-reporter": "10.0.1", - "@wdio/types": "10.0.1", + "@wdio/globals": "10.1.0", + "@wdio/local-runner": "10.1.0", + "@wdio/mocha-framework": "10.1.0", + "@wdio/spec-reporter": "10.1.0", + "@wdio/types": "10.1.0", "expect-webdriverio": "^8.0.0", "ts-node": "^10.9.2", "tsconfig-paths": "^4.2.0", diff --git a/package.json b/package.json index 9c8942f0..3137699c 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "@typescript-eslint/parser": "^8.71.0", "@typescript-eslint/utils": "^8.71.0", "@vitest/coverage-v8": "^5.0.2", - "@wdio/allure-reporter": "^10.0.1", + "@wdio/allure-reporter": "^10.1.0", "autoprefixer": "^10.6.1", "eslint": "^10.11.0", "eslint-config-prettier": "^10.1.8", @@ -63,10 +63,10 @@ "unplugin-icons": "^24.0.0", "vite": "^8.3.2", "vitest": "^5.0.2", - "webdriverio": "^10.0.2", + "webdriverio": "^10.1.0", "ws": "^8.22.0" }, "dependencies": { - "@wdio/cli": "10.0.2" + "@wdio/cli": "10.1.0" } } diff --git a/packages/app/package.json b/packages/app/package.json index 951847dc..43043af7 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -26,12 +26,12 @@ "@iconify-json/mdi": "^1.2.3", "@lit/context": "^1.1.6", "@tailwindcss/postcss": "^4.3.3", - "@wdio/browser-runner": "^10.0.2", + "@wdio/browser-runner": "^10.1.0", "@wdio/devtools-shared": "workspace:^", - "@wdio/globals": "^10.0.2", - "@wdio/mocha-framework": "^10.0.1", + "@wdio/globals": "^10.1.0", + "@wdio/mocha-framework": "^10.1.0", "@wdio/protocols": "10.0.2", - "@wdio/reporter": "10.0.1", + "@wdio/reporter": "10.1.0", "autoprefixer": "^10.6.1", "codemirror": "^6.0.2", "expect-webdriverio": "^8.0.0", diff --git a/packages/backend/package.json b/packages/backend/package.json index 33a834ed..2f4d4a7f 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -44,7 +44,7 @@ "@fastify/websocket": "^11.3.1", "@wdio/devtools-app": "workspace:^", "@wdio/devtools-script": "workspace:^", - "@wdio/logger": "10.0.0", + "@wdio/logger": "10.1.0", "fastify": "^5.12.5", "fflate": "^0.8.2", "get-port": "^7.2.0", @@ -57,7 +57,7 @@ "devDependencies": { "@types/shell-quote": "^1.7.5", "@types/ws": "^8.18.2", - "@wdio/cli": "^10.0.2", + "@wdio/cli": "^10.1.0", "@wdio/devtools-shared": "workspace:^", "@wdio/devtools-trace": "workspace:^", "nodemon": "^3.1.14", diff --git a/packages/conformance/package.json b/packages/conformance/package.json index 6e442076..30db2216 100644 --- a/packages/conformance/package.json +++ b/packages/conformance/package.json @@ -18,19 +18,19 @@ "@types/node": "26.6.4", "@types/selenium-webdriver": "^4.35.6", "@types/ws": "^8.18.2", - "@wdio/cli": "10.0.2", + "@wdio/cli": "10.1.0", "@wdio/devtools-service": "workspace:^", "@wdio/devtools-shared": "workspace:^", - "@wdio/globals": "10.0.2", - "@wdio/local-runner": "10.0.2", - "@wdio/mocha-framework": "10.0.1", + "@wdio/globals": "10.1.0", + "@wdio/local-runner": "10.1.0", + "@wdio/mocha-framework": "10.1.0", "@wdio/nightwatch-devtools": "workspace:^", "@wdio/selenium-devtools": "workspace:^", - "@wdio/spec-reporter": "10.0.1", + "@wdio/spec-reporter": "10.1.0", "expect-webdriverio": "^8.0.0", "nightwatch": "^3.16.0", "selenium-webdriver": "^4.50.0", - "webdriverio": "^10.0.2", + "webdriverio": "^10.1.0", "ws": "^8.22.0" } } diff --git a/packages/elements/package.json b/packages/elements/package.json index 7ffdeb32..27f9d158 100644 --- a/packages/elements/package.json +++ b/packages/elements/package.json @@ -48,11 +48,11 @@ "@types/node": "26.6.4", "@wdio/devtools-core": "workspace:^", "@wdio/devtools-shared": "workspace:^", - "@wdio/globals": "10.0.2", + "@wdio/globals": "10.1.0", "tsup": "^8.5.1", "typescript": "6.0.3", "vitest": "^5.0.2", - "webdriverio": "^10.0.2" + "webdriverio": "^10.1.0" }, "peerDependencies": { "webdriverio": "^10.0.0" diff --git a/packages/nightwatch-devtools/package.json b/packages/nightwatch-devtools/package.json index d7d160db..f30be557 100644 --- a/packages/nightwatch-devtools/package.json +++ b/packages/nightwatch-devtools/package.json @@ -53,12 +53,12 @@ "dependencies": { "@wdio/devtools-backend": "workspace:*", "@wdio/devtools-script": "workspace:*", - "@wdio/logger": "^10.0.0", + "@wdio/logger": "^10.1.0", "@xmldom/xmldom": "^0.9.12", "fluent-ffmpeg": "^2.1.3", "import-meta-resolve": "^4.2.0", "stacktrace-parser": "^0.1.11", - "webdriverio": "^10.0.2", + "webdriverio": "^10.1.0", "ws": "^8.22.0", "xpath": "^0.0.34", "yazl": "^3.3.1" diff --git a/packages/selenium-devtools/package.json b/packages/selenium-devtools/package.json index e9feb001..df7fe42e 100644 --- a/packages/selenium-devtools/package.json +++ b/packages/selenium-devtools/package.json @@ -53,10 +53,10 @@ "dependencies": { "@wdio/devtools-backend": "workspace:*", "@wdio/devtools-script": "workspace:*", - "@wdio/logger": "^10.0.0", + "@wdio/logger": "^10.1.0", "@xmldom/xmldom": "^0.9.12", "stacktrace-parser": "^0.1.11", - "webdriverio": "^10.0.2", + "webdriverio": "^10.1.0", "ws": "^8.22.0", "xpath": "^0.0.34", "yazl": "^3.3.1" diff --git a/packages/service/package.json b/packages/service/package.json index 887388a4..8a3392d5 100644 --- a/packages/service/package.json +++ b/packages/service/package.json @@ -64,18 +64,18 @@ "@types/fluent-ffmpeg": "^2.1.28", "@types/stack-trace": "^0.0.33", "@types/ws": "^8.18.2", - "@wdio/allure-reporter": "^10.0.1", + "@wdio/allure-reporter": "^10.1.0", "@wdio/devtools-core": "workspace:^", "@wdio/devtools-shared": "workspace:^", - "@wdio/globals": "10.0.2", - "@wdio/logger": "10.0.0", + "@wdio/globals": "10.1.0", + "@wdio/logger": "10.1.0", "@wdio/protocols": "10.0.2", - "@wdio/reporter": "10.0.1", - "@wdio/types": "10.0.1", + "@wdio/reporter": "10.1.0", + "@wdio/types": "10.1.0", "typescript": "6.0.3", "vite": "^8.3.2", "vite-plugin-dts": "^5.1.1", - "webdriverio": "^10.0.2" + "webdriverio": "^10.1.0" }, "peerDependencies": { "@wdio/allure-reporter": "^10.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e9fe32ae..a4cd4e33 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,8 +14,8 @@ importers: .: dependencies: '@wdio/cli': - specifier: 10.0.2 - version: 10.0.2(@types/node@26.6.4)(expect-webdriverio@8.0.0(@wdio/types@10.0.1)(webdriverio@10.0.2(yauzl@2.10.0)))(yauzl@2.10.0) + specifier: 10.1.0 + version: 10.1.0(@types/node@26.6.4)(expect-webdriverio@8.0.0(@wdio/types@10.1.0)(webdriverio@10.1.0(yauzl@2.10.0)))(yauzl@2.10.0) devDependencies: '@changesets/cli': specifier: ^3.0.3 @@ -42,8 +42,8 @@ importers: specifier: ^5.0.2 version: 5.0.3(vitest@5.0.3) '@wdio/allure-reporter': - specifier: ^10.0.1 - version: 10.0.1 + specifier: ^10.1.0 + version: 10.1.0 autoprefixer: specifier: ^10.6.1 version: 10.6.1(postcss@8.5.28) @@ -102,8 +102,8 @@ importers: specifier: ^5.0.2 version: 5.0.3(@types/node@26.6.4)(@vitest/coverage-v8@5.0.3)(happy-dom@20.14.5)(jsdom@24.1.3)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1)) webdriverio: - specifier: ^10.0.2 - version: 10.0.2(yauzl@2.10.0) + specifier: ^10.1.0 + version: 10.1.0(yauzl@2.10.0) ws: specifier: ^8.22.0 version: 8.22.0 @@ -133,32 +133,32 @@ importers: examples/wdio: devDependencies: '@wdio/cli': - specifier: 10.0.2 - version: 10.0.2(@types/node@26.6.4)(expect-webdriverio@8.0.0(@wdio/types@10.0.1)(webdriverio@10.0.2(yauzl@2.10.0)))(yauzl@2.10.0) + specifier: 10.1.0 + version: 10.1.0(@types/node@26.6.4)(expect-webdriverio@8.0.0(@wdio/types@10.1.0)(webdriverio@10.1.0(yauzl@2.10.0)))(yauzl@2.10.0) '@wdio/cucumber-framework': - specifier: 10.0.1 - version: 10.0.1(yauzl@2.10.0) + specifier: 10.1.0 + version: 10.1.0(yauzl@2.10.0) '@wdio/devtools-service': specifier: workspace:* version: link:../../packages/service '@wdio/globals': - specifier: 10.0.2 - version: 10.0.2(expect-webdriverio@8.0.0(@wdio/types@10.0.1)(webdriverio@10.0.2(yauzl@2.10.0)))(webdriverio@10.0.2(yauzl@2.10.0)) + specifier: 10.1.0 + version: 10.1.0(expect-webdriverio@8.0.0(@wdio/types@10.1.0)(webdriverio@10.1.0(yauzl@2.10.0)))(webdriverio@10.1.0(yauzl@2.10.0)) '@wdio/local-runner': - specifier: 10.0.2 - version: 10.0.2(tsx@4.23.15)(webdriverio@10.0.2(yauzl@2.10.0))(yauzl@2.10.0) + specifier: 10.1.0 + version: 10.1.0(tsx@4.23.15)(webdriverio@10.1.0(yauzl@2.10.0))(yauzl@2.10.0) '@wdio/mocha-framework': - specifier: 10.0.1 - version: 10.0.1(yauzl@2.10.0) + specifier: 10.1.0 + version: 10.1.0(yauzl@2.10.0) '@wdio/spec-reporter': - specifier: 10.0.1 - version: 10.0.1 + specifier: 10.1.0 + version: 10.1.0 '@wdio/types': - specifier: 10.0.1 - version: 10.0.1 + specifier: 10.1.0 + version: 10.1.0 expect-webdriverio: specifier: ^8.0.0 - version: 8.0.0(@wdio/types@10.0.1)(webdriverio@10.0.2(yauzl@2.10.0)) + version: 8.0.0(@wdio/types@10.1.0)(webdriverio@10.1.0(yauzl@2.10.0)) ts-node: specifier: ^10.9.2 version: 10.9.2(@swc/core@1.16.13)(@swc/wasm@1.16.13)(@types/node@26.6.4)(typescript@6.0.3) @@ -196,23 +196,23 @@ importers: specifier: ^4.3.3 version: 4.3.3 '@wdio/browser-runner': - specifier: ^10.0.2 - version: 10.0.2(@types/node@26.6.4)(esbuild@0.28.2)(expect-webdriverio@8.0.0(@wdio/types@10.0.1)(webdriverio@10.0.2(yauzl@2.10.0)))(jiti@2.7.0)(magicast@0.5.5)(rollup@4.64.0)(tsx@4.23.15)(webdriver@10.0.2(yauzl@2.10.0))(webdriverio@10.0.2(yauzl@2.10.0))(yaml@2.9.1)(yauzl@2.10.0) + specifier: ^10.1.0 + version: 10.1.0(@types/node@26.6.4)(esbuild@0.28.2)(expect-webdriverio@8.0.0(@wdio/types@10.1.0)(webdriverio@10.1.0(yauzl@2.10.0)))(jiti@2.7.0)(magicast@0.5.5)(rollup@4.64.0)(tsx@4.23.15)(webdriver@10.1.0(yauzl@2.10.0))(webdriverio@10.1.0(yauzl@2.10.0))(yaml@2.9.1)(yauzl@2.10.0) '@wdio/devtools-shared': specifier: workspace:^ version: link:../shared '@wdio/globals': - specifier: ^10.0.2 - version: 10.0.2(expect-webdriverio@8.0.0(@wdio/types@10.0.1)(webdriverio@10.0.2(yauzl@2.10.0)))(webdriverio@10.0.2(yauzl@2.10.0)) + specifier: ^10.1.0 + version: 10.1.0(expect-webdriverio@8.0.0(@wdio/types@10.1.0)(webdriverio@10.1.0(yauzl@2.10.0)))(webdriverio@10.1.0(yauzl@2.10.0)) '@wdio/mocha-framework': - specifier: ^10.0.1 - version: 10.0.1(yauzl@2.10.0) + specifier: ^10.1.0 + version: 10.1.0(yauzl@2.10.0) '@wdio/protocols': specifier: 10.0.2 version: 10.0.2 '@wdio/reporter': - specifier: 10.0.1 - version: 10.0.1 + specifier: 10.1.0 + version: 10.1.0 autoprefixer: specifier: ^10.6.1 version: 10.6.1(postcss@8.5.28) @@ -221,7 +221,7 @@ importers: version: 6.0.2 expect-webdriverio: specifier: ^8.0.0 - version: 8.0.0(@wdio/types@10.0.1)(webdriverio@10.0.2(yauzl@2.10.0)) + version: 8.0.0(@wdio/types@10.1.0)(webdriverio@10.1.0(yauzl@2.10.0)) lit: specifier: ^3.3.3 version: 3.3.3 @@ -280,8 +280,8 @@ importers: specifier: workspace:^ version: link:../script '@wdio/logger': - specifier: 10.0.0 - version: 10.0.0 + specifier: 10.1.0 + version: 10.1.0 fastify: specifier: ^5.12.5 version: 5.12.5 @@ -314,8 +314,8 @@ importers: specifier: ^8.18.2 version: 8.18.2 '@wdio/cli': - specifier: ^10.0.2 - version: 10.0.2(@types/node@26.6.4)(expect-webdriverio@8.0.0(@wdio/types@10.0.1)(webdriverio@10.0.2(yauzl@2.10.0)))(yauzl@2.10.0) + specifier: ^10.1.0 + version: 10.1.0(@types/node@26.6.4)(expect-webdriverio@8.0.0(@wdio/types@10.1.0)(webdriverio@10.1.0(yauzl@2.10.0)))(yauzl@2.10.0) '@wdio/devtools-shared': specifier: workspace:^ version: link:../shared @@ -344,8 +344,8 @@ importers: specifier: ^8.18.2 version: 8.18.2 '@wdio/cli': - specifier: 10.0.2 - version: 10.0.2(@types/node@26.6.4)(expect-webdriverio@8.0.0(@wdio/types@10.0.1)(webdriverio@10.0.2(yauzl@2.10.0)))(yauzl@2.10.0) + specifier: 10.1.0 + version: 10.1.0(@types/node@26.6.4)(expect-webdriverio@8.0.0(@wdio/types@10.1.0)(webdriverio@10.1.0(yauzl@2.10.0)))(yauzl@2.10.0) '@wdio/devtools-service': specifier: workspace:^ version: link:../service @@ -353,14 +353,14 @@ importers: specifier: workspace:^ version: link:../shared '@wdio/globals': - specifier: 10.0.2 - version: 10.0.2(expect-webdriverio@8.0.0(@wdio/types@10.0.1)(webdriverio@10.0.2(yauzl@2.10.0)))(webdriverio@10.0.2(yauzl@2.10.0)) + specifier: 10.1.0 + version: 10.1.0(expect-webdriverio@8.0.0(@wdio/types@10.1.0)(webdriverio@10.1.0(yauzl@2.10.0)))(webdriverio@10.1.0(yauzl@2.10.0)) '@wdio/local-runner': - specifier: 10.0.2 - version: 10.0.2(tsx@4.23.15)(webdriverio@10.0.2(yauzl@2.10.0))(yauzl@2.10.0) + specifier: 10.1.0 + version: 10.1.0(tsx@4.23.15)(webdriverio@10.1.0(yauzl@2.10.0))(yauzl@2.10.0) '@wdio/mocha-framework': - specifier: 10.0.1 - version: 10.0.1(yauzl@2.10.0) + specifier: 10.1.0 + version: 10.1.0(yauzl@2.10.0) '@wdio/nightwatch-devtools': specifier: workspace:^ version: link:../nightwatch-devtools @@ -368,11 +368,11 @@ importers: specifier: workspace:^ version: link:../selenium-devtools '@wdio/spec-reporter': - specifier: 10.0.1 - version: 10.0.1 + specifier: 10.1.0 + version: 10.1.0 expect-webdriverio: specifier: ^8.0.0 - version: 8.0.0(@wdio/types@10.0.1)(webdriverio@10.0.2(yauzl@2.10.0)) + version: 8.0.0(@wdio/types@10.1.0)(webdriverio@10.1.0(yauzl@2.10.0)) nightwatch: specifier: ^3.16.0 version: 3.16.0(@cucumber/cucumber@13.2.1) @@ -380,8 +380,8 @@ importers: specifier: ^4.50.0 version: 4.50.0 webdriverio: - specifier: ^10.0.2 - version: 10.0.2(yauzl@2.10.0) + specifier: ^10.1.0 + version: 10.1.0(yauzl@2.10.0) ws: specifier: ^8.22.0 version: 8.22.0 @@ -417,7 +417,7 @@ importers: dependencies: '@wdio/snapshot': specifier: ^10.0.2 - version: 10.0.2(yauzl@2.10.0) + version: 10.1.0(yauzl@2.10.0) '@xmldom/xmldom': specifier: ^0.9.12 version: 0.9.12 @@ -438,8 +438,8 @@ importers: specifier: workspace:^ version: link:../shared '@wdio/globals': - specifier: 10.0.2 - version: 10.0.2(expect-webdriverio@8.0.0(@wdio/types@10.0.1)(webdriverio@10.0.2(yauzl@2.10.0)))(webdriverio@10.0.2(yauzl@2.10.0)) + specifier: 10.1.0 + version: 10.1.0(expect-webdriverio@8.0.0(@wdio/types@10.1.0)(webdriverio@10.1.0(yauzl@2.10.0)))(webdriverio@10.1.0(yauzl@2.10.0)) tsup: specifier: ^8.5.1 version: 8.5.1(@microsoft/api-extractor@7.59.3(@types/node@26.6.4))(@swc/core@1.16.13)(jiti@2.7.0)(postcss@8.5.28)(tsx@4.23.15)(typescript@6.0.3)(yaml@2.9.1) @@ -450,8 +450,8 @@ importers: specifier: ^5.0.2 version: 5.0.3(@types/node@26.6.4)(@vitest/coverage-v8@5.0.3)(happy-dom@20.14.5)(jsdom@24.1.3)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1)) webdriverio: - specifier: ^10.0.2 - version: 10.0.2(yauzl@2.10.0) + specifier: ^10.1.0 + version: 10.1.0(yauzl@2.10.0) packages/nightwatch-devtools: dependencies: @@ -462,8 +462,8 @@ importers: specifier: workspace:* version: link:../script '@wdio/logger': - specifier: ^10.0.0 - version: 10.0.0 + specifier: ^10.1.0 + version: 10.1.0 '@xmldom/xmldom': specifier: ^0.9.12 version: 0.9.12 @@ -477,8 +477,8 @@ importers: specifier: ^0.1.11 version: 0.1.11 webdriverio: - specifier: ^10.0.2 - version: 10.0.2(yauzl@2.10.0) + specifier: ^10.1.0 + version: 10.1.0(yauzl@2.10.0) ws: specifier: ^8.22.0 version: 8.22.0 @@ -538,8 +538,8 @@ importers: specifier: workspace:* version: link:../script '@wdio/logger': - specifier: ^10.0.0 - version: 10.0.0 + specifier: ^10.1.0 + version: 10.1.0 '@xmldom/xmldom': specifier: ^0.9.12 version: 0.9.12 @@ -547,8 +547,8 @@ importers: specifier: ^0.1.11 version: 0.1.11 webdriverio: - specifier: ^10.0.2 - version: 10.0.2(yauzl@2.10.0) + specifier: ^10.1.0 + version: 10.1.0(yauzl@2.10.0) ws: specifier: ^8.22.0 version: 8.22.0 @@ -667,8 +667,8 @@ importers: specifier: ^8.18.2 version: 8.18.2 '@wdio/allure-reporter': - specifier: ^10.0.1 - version: 10.0.1 + specifier: ^10.1.0 + version: 10.1.0 '@wdio/devtools-core': specifier: workspace:^ version: link:../core @@ -676,20 +676,20 @@ importers: specifier: workspace:^ version: link:../shared '@wdio/globals': - specifier: 10.0.2 - version: 10.0.2(expect-webdriverio@8.0.0(@wdio/types@10.0.1)(webdriverio@10.0.2(yauzl@2.10.0)))(webdriverio@10.0.2(yauzl@2.10.0)) + specifier: 10.1.0 + version: 10.1.0(expect-webdriverio@8.0.0(@wdio/types@10.1.0)(webdriverio@10.1.0(yauzl@2.10.0)))(webdriverio@10.1.0(yauzl@2.10.0)) '@wdio/logger': - specifier: 10.0.0 - version: 10.0.0 + specifier: 10.1.0 + version: 10.1.0 '@wdio/protocols': specifier: 10.0.2 version: 10.0.2 '@wdio/reporter': - specifier: 10.0.1 - version: 10.0.1 + specifier: 10.1.0 + version: 10.1.0 '@wdio/types': - specifier: 10.0.1 - version: 10.0.1 + specifier: 10.1.0 + version: 10.1.0 typescript: specifier: 6.0.3 version: 6.0.3 @@ -700,8 +700,8 @@ importers: specifier: ^5.1.1 version: 5.1.2(@microsoft/api-extractor@7.59.3(@types/node@26.6.4))(esbuild@0.28.2)(rolldown@1.2.12)(rollup@4.64.0)(typescript@6.0.3)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1)) webdriverio: - specifier: ^10.0.2 - version: 10.0.2(yauzl@2.10.0) + specifier: ^10.1.0 + version: 10.1.0(yauzl@2.10.0) packages/shared: {} @@ -1599,134 +1599,134 @@ packages: '@iconify/utils@3.1.7': resolution: {integrity: sha512-JZHlwdID+dy+lTgbYC8NEC4zeugqeYsc6jewvzb4c58kHauJn+X7rNwQjxz5p2qSjqaEeQoLkCIQ9v/H4PK0/w==} - '@inquirer/ansi@1.0.2': - resolution: {integrity: sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==} - engines: {node: '>=18'} + '@inquirer/ansi@2.0.9': + resolution: {integrity: sha512-0Ywt3S8tqM6TWUcsRlBFQ1x+X3/o8kktX0ZiKAnByYb9GFDknnOhse1PVtfiD2BXPI8ar+BWx9PV9R0lQIcygw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} - '@inquirer/checkbox@4.3.2': - resolution: {integrity: sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==} - engines: {node: '>=18'} + '@inquirer/checkbox@5.2.7': + resolution: {integrity: sha512-/+QmGE/9bvE/EobaZDnzYe+T+QWoJrnmxwBQNtoi2OVSUR9d1znYsNyEoUrOZvFSxDYttrhzRIpXpaLkN+iIsg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': 26.6.4 peerDependenciesMeta: '@types/node': optional: true - '@inquirer/confirm@5.1.21': - resolution: {integrity: sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==} - engines: {node: '>=18'} + '@inquirer/confirm@6.3.4': + resolution: {integrity: sha512-ZwnaZZDa1zxsyVrN32s447ydMnKcw7e8WWNT+R+Qsu0zAn8Rs6yi9d1B80Kob1Ul2cfUi7t8JD2t4/K7uEZpqA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': 26.6.4 peerDependenciesMeta: '@types/node': optional: true - '@inquirer/core@10.3.2': - resolution: {integrity: sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==} - engines: {node: '>=18'} + '@inquirer/core@12.0.5': + resolution: {integrity: sha512-c0ZSnO9oVk/IlEGoZK3u8b3IZQeu9I6jUkXJSQnQEVRXsjKvtAq3AGqFL2LgfJhlzMZne08pZHWRWasIcI3Rew==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': 26.6.4 peerDependenciesMeta: '@types/node': optional: true - '@inquirer/editor@4.2.23': - resolution: {integrity: sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==} - engines: {node: '>=18'} + '@inquirer/editor@5.3.5': + resolution: {integrity: sha512-6lU3/pPx5z3lHWejFW061MV4qZooBQYv/+EeGhNOPiP/i+hrYsHfxxBnnDGsAfg8YdLUd69YckoeOjtr3QuAUg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': 26.6.4 peerDependenciesMeta: '@types/node': optional: true - '@inquirer/expand@4.0.23': - resolution: {integrity: sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==} - engines: {node: '>=18'} + '@inquirer/expand@5.1.7': + resolution: {integrity: sha512-eiJ31YFCaB6fLoJCqLIn8S6eqUyME6vwr8KcP5h8cxmA+bV9RRVPO+kywoxb7G63HduDXV6sN/SM7o2R+ahEcQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': 26.6.4 peerDependenciesMeta: '@types/node': optional: true - '@inquirer/external-editor@1.0.3': - resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} - engines: {node: '>=18'} + '@inquirer/external-editor@3.0.6': + resolution: {integrity: sha512-rWmw6WG8JZ7LMn/srXEylYGhL6EXXJeROE7/4x4GREh6NgzqJBDbIgpNGsYoLPfobeb4VZtEYzPGnT5EFNYhiQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': 26.6.4 peerDependenciesMeta: '@types/node': optional: true - '@inquirer/figures@1.0.15': - resolution: {integrity: sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==} - engines: {node: '>=18'} + '@inquirer/figures@2.0.9': + resolution: {integrity: sha512-EAWgUTGQ/Umgga51dE3B2PUHbufuXarDfg86uVgoSgNHNNQnyFKcOrQLWVqYMghuSyHh8+2HUH0Js9cTC1WAdg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} - '@inquirer/input@4.3.1': - resolution: {integrity: sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==} - engines: {node: '>=18'} + '@inquirer/input@5.1.8': + resolution: {integrity: sha512-psOSTgkL8y/OtgJJYbqJ0oISr2QBE0U1Ro/qQdA/EKjeJiwiUTQgQmq8o4ESNzVOWthIeh3YLD7BCUR9dKnR5g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': 26.6.4 peerDependenciesMeta: '@types/node': optional: true - '@inquirer/number@3.0.23': - resolution: {integrity: sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==} - engines: {node: '>=18'} + '@inquirer/number@4.2.5': + resolution: {integrity: sha512-lEy7LWsdF5qHx5QjHZqabUqM9P7i5uTMCt8GRzqxmwXXKbapKJDA+ucVflVVPAyj4snD5DTAD4L+sct85XOlOQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': 26.6.4 peerDependenciesMeta: '@types/node': optional: true - '@inquirer/password@4.0.23': - resolution: {integrity: sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==} - engines: {node: '>=18'} + '@inquirer/password@5.2.4': + resolution: {integrity: sha512-T7ir3fflSSVCYEVqNyMBq+pIMsilQRtbZcD5gxn6Ewsin5TJWBxrGBdXveWtmeXVAeoInfhS+BhBUZaL4sFSDg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': 26.6.4 peerDependenciesMeta: '@types/node': optional: true - '@inquirer/prompts@7.10.1': - resolution: {integrity: sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==} - engines: {node: '>=18'} + '@inquirer/prompts@8.7.4': + resolution: {integrity: sha512-SAJt+QhY5rOJAIgZBw9TrHWfgrp6wsb9cqRCyqax+Hl6EiFfRXmD9EsIWkST8yq/dQ8H46Is2Sr/JG7Ls/LQ+Q==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': 26.6.4 peerDependenciesMeta: '@types/node': optional: true - '@inquirer/rawlist@4.1.11': - resolution: {integrity: sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==} - engines: {node: '>=18'} + '@inquirer/rawlist@5.3.7': + resolution: {integrity: sha512-XveEe8v3BHj9ec00n+9i8FHjWhXjS6+jCTizYvXCqKUKtgkrq9xid8S5j3mYGW96qfxG3mwNoud3m3v8GDk9Pw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': 26.6.4 peerDependenciesMeta: '@types/node': optional: true - '@inquirer/search@3.2.2': - resolution: {integrity: sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==} - engines: {node: '>=18'} + '@inquirer/search@4.3.5': + resolution: {integrity: sha512-kIUecMugEN2YTvTmff+uYz0nAE0f9RFNDVZknfrh264vAQM9P8tnsXiUp7z8XbGuNIBEtFMJhvc9QquawpC46A==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': 26.6.4 peerDependenciesMeta: '@types/node': optional: true - '@inquirer/select@4.4.2': - resolution: {integrity: sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==} - engines: {node: '>=18'} + '@inquirer/select@5.2.7': + resolution: {integrity: sha512-pRDWO4dTqSAvFQdO8yclKMtXNDm9K9HmvWB5UZmT3clw2OlOBNXveHzhyFmO/0d0yJMqlPcNITus1Jtpm6t8VQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': 26.6.4 peerDependenciesMeta: '@types/node': optional: true - '@inquirer/type@3.0.10': - resolution: {integrity: sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==} - engines: {node: '>=18'} + '@inquirer/type@4.2.0': + resolution: {integrity: sha512-2rnHX8RPo4glJYqnFc/v3nQeIhDcjyRRyaT7MGcfL/8xv/8K03SXYoA2tX06WVSKywWOE/ncbG3dtuzLjR5TCQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': 26.6.4 peerDependenciesMeta: @@ -2757,9 +2757,6 @@ packages: '@types/babel__generator@7.27.0': resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} - '@types/babel__generator@7.6.8': - resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} - '@types/babel__template@7.4.4': resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} @@ -2814,8 +2811,8 @@ packages: '@types/shell-quote@1.7.5': resolution: {integrity: sha512-+UE8GAGRPbJVQDdxi16dgadcBfQ+KG2vgZhV1+3A1XmHbmwcdwhCUwIdy+d3pAGrbvgRoVSjeI9vOWyq376Yzw==} - '@types/sinonjs__fake-timers@8.1.5': - resolution: {integrity: sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==} + '@types/sinonjs__fake-timers@15.0.1': + resolution: {integrity: sha512-Ko2tjWJq8oozHzHV+reuvS5KYIRAokHnGbDwGh/J64LntgpbuylF74ipEL24HCyRjf9FOlBiBHWBR1RlVKsI1w==} '@types/split2@4.2.3': resolution: {integrity: sha512-59OXIlfUsi2k++H6CHgUQKEb2HKRokUA39HY1i1dS8/AIcqVjtAAFdf8u+HxTWK/4FUHMJQlKSZ4I6irCBJ1Zw==} @@ -2832,9 +2829,6 @@ packages: '@types/whatwg-mimetype@3.0.2': resolution: {integrity: sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==} - '@types/which@2.0.2': - resolution: {integrity: sha512-113D3mDkZDjo+EeUEHCFy0qniNc1ZpecGiAU7WSo7YDoSzolZIQKpYFHrPpjkB2nuyahcKfrmLXeQlh7gqJYdw==} - '@types/ws@8.18.2': resolution: {integrity: sha512-67MQl+fpWKVTT1NYdnmo3U4sc/xPo/zQBncVnI74qmQa0z/b+1g6iYqNmGCPbxO+zz2aklb08a0oHfegiVd0/w==} @@ -3078,60 +3072,56 @@ packages: '@volar/typescript@2.4.28': resolution: {integrity: sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==} - '@wdio/allure-reporter@10.0.1': - resolution: {integrity: sha512-Unxlc1THjU8V3DDRqcFMWvFvZVdaeYUrNgQO4RlZvLF+ygio7mOkYjfz7SGmrrCsy7xye5Elqn4VCw/ic0pstQ==} + '@wdio/allure-reporter@10.1.0': + resolution: {integrity: sha512-+GRmFuEwVB3O5sNvbuA20qpteSE8Ub86G5FRu+yPoWRFgxu9aRhI0ju5FvgDw5r3FdqKYmzNRfRDM+jWpTxu1g==} engines: {node: '>=22.19.0'} - '@wdio/browser-runner@10.0.2': - resolution: {integrity: sha512-IJQZ725cBPQDFp3spmycFyOiBj76H0+7vB128us3+wpX2rjLG7UcFJZXfYLubquDa8ybMCZojzoIifHoiJH9ww==} + '@wdio/browser-runner@10.1.0': + resolution: {integrity: sha512-h6chh9SRc6gpJC/oJCooyxeTPtfOeln1+3qxXapQoEd8u0MrNp2oAHge4/b6o9pb2Jn0YqZatZFzctoiU5fPHg==} engines: {node: '>=22.19.0'} peerDependencies: - expect-webdriverio: ^8.0.0-0 || ^8.0.0-alpha.3 + expect-webdriverio: ^8.0.0 webdriver: ^10.0.0-0 || ^10.0.0 webdriverio: ^10.0.0-0 || ^10.0.0 - '@wdio/cli@10.0.2': - resolution: {integrity: sha512-t67EIII06oAtzCyy9RYNgmChzSnwY91Vw5NW8QwJg2p8ZT2cvDYrtytEYO/x3jZQK3uJ/mba2GlQQP4SHJK+Uw==} + '@wdio/cli@10.1.0': + resolution: {integrity: sha512-63cAdy/+lu2Z/2FdHA/8rRoV2NyiYg+pVRdi2pUADQZ20yzUQCjmu6clk+fWZ9sk3SeayUyMkm/8pvcO3hcoHA==} engines: {node: '>=22.19.0'} hasBin: true - '@wdio/config@10.0.1': - resolution: {integrity: sha512-n6elHE4ItKAtr4lwAaWIh/sAFuEFhyesLM27i5l9IWBJEK6VECdt0Pigj/4vUD2tliOoBtsWxphSW4oiNFXotg==} + '@wdio/config@10.1.0': + resolution: {integrity: sha512-V4G0uIhLTlSJRKLfr0ypu3SJLL90+LCuo9niBgqTmpNpJz4K3nwK2Mz//JuAKmz/OVXPb1HxJlrm2snfSOI2WQ==} engines: {node: '>=22.19.0'} - '@wdio/cucumber-framework@10.0.1': - resolution: {integrity: sha512-bcN2wlpUG/qtZ01TNPgrAnq8bWZKYp6C0qZglBz+PqBjzKQYGJ8fKt/bkHiuf6X93JqL3zqgzwG7mau5v5f/1w==} + '@wdio/cucumber-framework@10.1.0': + resolution: {integrity: sha512-HwtVd16xq/UoiirijGkZ8co/qw72MTgioaYKFGmuuvexCz5B6L9NcrK03TS358pJjVqYJYu3pxJ9GGLxvNBv+g==} engines: {node: '>=22.19.0 <23 || >=24 <25 || >=26'} - '@wdio/display-server@10.0.1': - resolution: {integrity: sha512-DMXpSCUpk/3KArcTFNlyEPI6O7EzeW9b3C4m54yZSlwaXFtix0P96c6ofiuvZ6bf/6bVd7s2GAxGk3yhmK8giw==} + '@wdio/display-server@10.1.0': + resolution: {integrity: sha512-RxtgqUPj58HIZypiZDwiWvp0e2u1eiojlSHdLkN5Z+iC5lexG0W8BHum4ujlbzhX3yclOUdBw7PR1IbpCd8cKg==} engines: {node: '>=22.19.0'} - '@wdio/dot-reporter@10.0.1': - resolution: {integrity: sha512-R0wuXuj4+f1yeXWnDyFqVPnJQzHrnepLH8ynS47wb87BVbtX9aVsbT9jctfplnfGHKySLm525w3/ajTj//anFg==} + '@wdio/dot-reporter@10.1.0': + resolution: {integrity: sha512-zE4A0GRTCoBJycdDnaZWPybf+v3Q8tCOW5Vva1YlHHA8LZ/nAVer8bI5Vo1hN2Hhcq7XIIrZbJK5vW/ZmTdQuw==} engines: {node: '>=22.19.0'} - '@wdio/globals@10.0.2': - resolution: {integrity: sha512-gQgKy2Yo99E9CIiHJgKDKrRPU3sYR92kZGStROArQvZqVcLsdPQZ3Ktf6L3cRyes1Vee/H82L/FG24vrB3C+mQ==} + '@wdio/globals@10.1.0': + resolution: {integrity: sha512-Y6FkkZz6jzMqIwI8R7BPj8bHuyRosxLZLGWRCHQFrU5mesjkW2NzYuPR7xwOPVmlaFJYUdzS1/EUYLr1BjpbSw==} engines: {node: '>=22.19.0'} peerDependencies: - expect-webdriverio: ^8.0.0-0 || ^8.0.0-alpha.3 + expect-webdriverio: ^8.0.0 webdriverio: ^10.0.0-0 || ^10.0.0 - '@wdio/local-runner@10.0.2': - resolution: {integrity: sha512-w3mamc9/CWttRYE+sLXYwHl7sDi2nxt7R8RpY41gT5sK2ol/UQ+96TaG4fO3KsotmLhNe15wfP/Z2umgeEx+Tg==} + '@wdio/local-runner@10.1.0': + resolution: {integrity: sha512-icWS+muUOAZzzW4SIThv36vpdyDiqZt2si/v1EK+h6g+D0lTMnTCN/Q+EmOk0iRwy8/d8JxmOcBty6Wdby8NxQ==} engines: {node: '>=22.19.0'} - '@wdio/logger@10.0.0': - resolution: {integrity: sha512-kUIw0ZzMs1mvXOaLEcjrM1nqNXY1reaJKReKNcjDSnfhnfUIb0Q5pAsxiMeil2uR0WsSUPzn0r3D9v//7vTOkA==} + '@wdio/logger@10.1.0': + resolution: {integrity: sha512-zZn9TMaxzTAHOGX5fLTs9E8Gw436l6xSFh+wZyXfRpgnzgeS5W/U0I6pBAjsvMn8rr5HAJC8Vy+WhiUxmbKsvQ==} engines: {node: '>=22.19.0'} - '@wdio/logger@9.29.1': - resolution: {integrity: sha512-0ZAEIo6PNyMIJPlOGkIgyOJUjcd0pC8/QHlVAAe1c91/IcjZ1X+k0yidXHaboJdN7dq1XPUacmhRdtua0U5EZg==} - engines: {node: '>=18.20.0'} - - '@wdio/mocha-framework@10.0.1': - resolution: {integrity: sha512-OZ++XMxtJnCmRZ2EA+ElzMxLXJNo27H9vvm3wWdFTFOFGuu5jWOlb6x090BVeX0c7jXYYjpAu0aXqix+dBItxQ==} + '@wdio/mocha-framework@10.1.0': + resolution: {integrity: sha512-KmdqVqMEc415ish7OnSaOYDr/wMBgxu5jqQ4s3aYwc5zsejeseBoUE5/6EmEBIOZk6nAtij3GkIQ6QeL6TRihw==} engines: {node: '>=22.19.0'} '@wdio/protocols@10.0.2': @@ -3142,26 +3132,26 @@ packages: resolution: {integrity: sha512-wAC9ksdWWv2E3Sj86eNKxB3IUt0gEyw9rgDGAWjk/GWgi6K3e+/n3erGNweI9JMJyC/+RNe0qHgVPQpRJMRVpg==} engines: {node: '>=22.19.0'} - '@wdio/reporter@10.0.1': - resolution: {integrity: sha512-A917SeniucyosspqazR9tGH6t9JaecU5SRP3VzRGyIo0JBEKV6walJU1VBauMH+7n043huzgiQvNJ414Vi8dmQ==} + '@wdio/reporter@10.1.0': + resolution: {integrity: sha512-VijtnGZHgwKFRzWwfGQSTF5WCxtwZLMstTQurKgXFUUh6rJi5ORkfL2515q5/7hbHm9ziMbAeBrfUdiDJCk1dQ==} engines: {node: '>=22.19.0'} - '@wdio/runner@10.0.2': - resolution: {integrity: sha512-+BnWawcWrqQh9RoKhqtnlJ3onRDpahu5BuJx1ESrWvR/+MDdlOx617Mk5Pvw69+vpDpMUdox9XqFMAt3H3hL7Q==} + '@wdio/runner@10.1.0': + resolution: {integrity: sha512-QMy/sgHuysjwLMu2J1qToLomXTIM63bxMlhuGyqaApShQvSc+9/qPoAZvLkwZy40Sn9IvMcaJChaBlF9vL0IJA==} engines: {node: '>=22.19.0'} peerDependencies: - expect-webdriverio: ^8.0.0-0 || ^8.0.0-alpha.3 + expect-webdriverio: ^8.0.0 webdriverio: ^10.0.0-0 || ^10.0.0 - '@wdio/session@10.0.2': - resolution: {integrity: sha512-Ttu2425dZ+r5whjScFRWVkAHAyhg/5nheN/bkTWPf2gluJb4/na2NM7bElOZ4IZFgwFgM5gFkE+9o5WwSVJfSQ==} + '@wdio/session@10.1.0': + resolution: {integrity: sha512-jn25P/1gQqehBEJkq/kUQsT+f5oyM54iNXxhxOjh7wX9BK15RcrgvEYjBhFAk5TrPj1emIkmM4seKqRB/YuKEQ==} engines: {node: '>=22.19.0'} hasBin: true peerDependencies: '@wdio/electron-service': '*' - '@wdio/visual-service': '*' + '@wdio/visual-service': ^10.0.0 || ^11.0.0 appium: ^3.0.0 - expect-webdriverio: '*' + expect-webdriverio: ^8.0.0 tsx: '*' peerDependenciesMeta: '@wdio/electron-service': @@ -3175,28 +3165,28 @@ packages: tsx: optional: true - '@wdio/snapshot@10.0.2': - resolution: {integrity: sha512-eN5DJEr4bQKGOMkA3x4FglMJxe9U91QUGXuChHjFAnzV9OcNQcfVNIsK3uIB3wJ0tnUlzqQ27Dbm8vP+2bA38w==} + '@wdio/snapshot@10.1.0': + resolution: {integrity: sha512-U4IitLvKGltDxtv0NoPPPBZRh3UKpsYms6mMdqGJhIC6riB9zG+faYxv/yk5J1CcPd25FMS0KkPfNpwTeE86rw==} engines: {node: '>=22.19.0'} - '@wdio/spec-reporter@10.0.1': - resolution: {integrity: sha512-zu4PQ7T95KQ/b3/coHQ3RLK5ffQDrpdXkxsfcTXU9Zd+l9K1GzTyJEgXCE7zY1l+INbrBqhM17aUmQvaBZH+JA==} + '@wdio/spec-reporter@10.1.0': + resolution: {integrity: sha512-3Ei+XqpJIsZ2tOwHZDtEw9e5T68X9jqnJ29WDPEUym8sB/XxRYAOUsxOV4EKtxy9LrDZ7yG1vNW/RKa5iNZAOg==} engines: {node: '>=22.19.0'} - '@wdio/types@10.0.1': - resolution: {integrity: sha512-HGP9lAYXCsLAXzugfT4khCh7tYByWOMIxZHCG+tUvgmpwNzi6lfIsxyIlWownXK1wYJUpk6tkDBX/iXwN+q0lg==} + '@wdio/types@10.1.0': + resolution: {integrity: sha512-MxGhIjP74w2xaIhqtzBfrtzDc41qyb9cMqvHgdIDC7MbaREJxpUd/paAg8qmouIph7hBp8MruXap/BPKlVXmVA==} engines: {node: '>=22.19.0'} - '@wdio/utils@10.0.1': - resolution: {integrity: sha512-aYvTtF+7IYsvkFdXdu32Q2vexEaXYK79R1wLTnAXrT2Q7fp0oCDL0UgBx8Jgo5Tn98ZLWx4JlUeZsDRAIkV4Zw==} + '@wdio/utils@10.1.0': + resolution: {integrity: sha512-BABJY+L4JidOD2bfQe3icuRZ0jQeUimTwUEe66I4BHVGWwaa5QBeVU+JZmdNQLjMA9q+Nk385aL96Mb1/PbKYw==} engines: {node: '>=22.19.0'} '@xmldom/xmldom@0.9.12': resolution: {integrity: sha512-5AXjrcMClTryPe9LgZrygpB1lj7s0S9E0+W+AHaVKAVyHanafK86iPSvG5xHVSp/jC+VH1UXu0TAEmY279xH7A==} engines: {node: '>=14.6'} - '@zip.js/zip.js@2.20.0': - resolution: {integrity: sha512-ws8m/tIfbt10L7E9PZj4h0vePKBodxpF13z9JzXdNw1e9ZMFdqnS7ppcK7vXPfN7vwSv2TuRTP7cO2OetdndvA==} + '@zip.js/zip.js@2.24.0': + resolution: {integrity: sha512-WlaVKzdqBK+RF3bdiXZWbdCnvGB6PdO4IHC1mn/2Yfl3bASgqUNegnU3+PNg61iecwoPfUi5BgLUPqBukXpyWw==} engines: {bun: '>=0.7.0', deno: '>=1.0.0', node: '>=18.0.0'} abort-controller@3.0.0: @@ -3229,6 +3219,10 @@ packages: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} + agent-base@9.0.0: + resolution: {integrity: sha512-TQf59BsZnytt8GdJKLPfUZ54g/iaUL2OWDSFCCvMOhsHduDQxO8xC4PNeyIkVcA5KwL2phPSv0douC0fgWzmnA==} + engines: {node: '>= 20'} + ajv-draft-04@1.0.0: resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} peerDependencies: @@ -3650,9 +3644,9 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - chalk@5.6.2: - resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + chalk@6.0.1: + resolution: {integrity: sha512-/Ce6KNm3vIbWdMlNna6RVIZ/ICQxnJxCicet5LBKK9ZffBkqzDw0xh9EiKSljdRtiIQ1S1z4YgcscUUGzNCWrA==} + engines: {node: '>=22'} change-case@5.4.4: resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==} @@ -3767,10 +3761,6 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} - commander@14.0.3: - resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} - engines: {node: '>=20'} - commander@15.0.0: resolution: {integrity: sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==} engines: {node: '>=22.12.0'} @@ -3853,8 +3843,8 @@ packages: create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - create-wdio@10.0.2: - resolution: {integrity: sha512-d3Z+a46mOhPVogOzqTatXoI5k+a2cYNpkvhLjbJaiteapxyGALBfM7XQO1po+NTIu1WTaVT0pM1eAoMkw14CNQ==} + create-wdio@10.1.0: + resolution: {integrity: sha512-tFlgNLoSDG1S2U9dpJqkv8tWHuT0cre5kNKGHEhjG6o9hyaTFa6aawO7U5aFxAk5Ts4rV63sNDiGMCoj2cifEw==} engines: {node: '>=22.19.0'} hasBin: true @@ -4071,6 +4061,11 @@ packages: resolution: {integrity: sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==} engines: {node: '>=12'} + dotenv@18.0.8: + resolution: {integrity: sha512-2KDeoFoK/zRtcnbunCDlLG5VWVMcyqeQc6iK65o231nfNAcuGrKoqUQfJFUtBI+QlShk5HKIHxwebBtUl3rEfA==} + engines: {node: '>=12'} + hasBin: true + dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} @@ -4084,20 +4079,23 @@ packages: easy-table@1.2.0: resolution: {integrity: sha512-OFzVOv03YpvtcWGe5AayU5G2hgybsg3iqA6drU8UaoZyB9jLGMTrz9+asnLp/E+6qPh88yEI1gvyZFZ41dmgww==} - edge-paths@3.0.5: - resolution: {integrity: sha512-sB7vSrDnFa4ezWQk9nZ/n0FdpdUuC6R1EOrlU3DL+bovcNFK28rqu2emmAUjujYEJTWIgQGqgVVWUZXMnc8iWg==} - engines: {node: '>=14.0.0'} - - edgedriver@7.0.0: - resolution: {integrity: sha512-4sJLhorcBSfzaqMN41q2VI9KVW9Qcpo7CHbUcBUDWeQ7uHiWnRWayV1KZOQr9jllSBOHD2aXzhUWrC2yCt+PRA==} - engines: {node: '>=20.0.0'} + edgedriver@8.0.0: + resolution: {integrity: sha512-h4twAKTFILvpjrxfeN1gslvnggztA6Cgb4iHQdLzEq3TivaHHlf6qEETpCgU7+iw9JIWjcveeckrDlbcY28a/A==} + engines: {node: '>=22.19.0'} hasBin: true + peerDependencies: + '@wdio/logger': ^9.29.1 || ^10.0.0 ejs@3.1.10: resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} engines: {node: '>=0.10.0'} hasBin: true + ejs@7.0.2: + resolution: {integrity: sha512-LcDSEP6Oclb9sTiSyG0MpG0RuKvLDRxCOOt+2sIk+kdk1rMkFU8SRnT0qaNt5zvU0LOV1YXXOvIoZL/sKwabvw==} + engines: {node: '>=0.12.18'} + hasBin: true + electron-to-chromium@1.5.443: resolution: {integrity: sha512-TDJG36L9A3CWWwZ97HKaE+Iz1sW80pNp6sAU0owzCIV3Zc9eVyRqZK8Vz6NKuMIU8WjP0pIkoqCa0VRf/yFD0Q==} @@ -4425,10 +4423,6 @@ packages: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - eslint-visitor-keys@4.2.1: - resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint-visitor-keys@5.0.1: resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} @@ -4443,10 +4437,6 @@ packages: jiti: optional: true - espree@10.4.0: - resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - espree@11.2.0: resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} @@ -4489,17 +4479,17 @@ packages: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} + execa@10.2.0: + resolution: {integrity: sha512-j1pFOVxaybMVCTNHSBuISL2KQcDS3KIE1wBmUp4n2X+1FgaQI2ZDJVy9TYxDyMCzYojRIMh9O8X/UoWZtxTJdw==} + engines: {node: '>=22'} + execa@5.1.1: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} - execa@9.6.1: - resolution: {integrity: sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==} - engines: {node: ^18.19.0 || >=20.5.0} - - exit-hook@4.0.0: - resolution: {integrity: sha512-Fqs7ChZm72y40wKjOFXBKg7nJZvQJmewP5/7LtePDdnah/+FH9Hp5sgMujSCMPXlxOAW2//1jrW9pnsY7o20vQ==} - engines: {node: '>=18'} + exit-hook@5.1.0: + resolution: {integrity: sha512-INjr2xyxHo7bhAqf5ong++GZPPnpcuBcaXUKt03yf7Fie9yWD7FapL4teOU0+awQazGs5ucBh7xWs/AD+6nhog==} + engines: {node: '>=20'} exit-x@0.2.2: resolution: {integrity: sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==} @@ -4627,10 +4617,6 @@ packages: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} - find-up@6.3.0: - resolution: {integrity: sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - fix-dts-default-cjs-exports@1.0.1: resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==} @@ -4689,10 +4675,12 @@ packages: functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} - geckodriver@7.0.0: - resolution: {integrity: sha512-RypjU7cNcyjWK3ruEriCkWMRa9nroCT3y2qwkPynGzKTK1wG0UCWubb4AfVDTXzG9BhzRTwg29DoeK7PCRnK1w==} - engines: {node: '>=20.0.0'} + geckodriver@8.0.0: + resolution: {integrity: sha512-2jGCA6deI5MAjODxmHljPqvaUccGbeeqMUHXlW4k3FNDp4YDpxSI+P5QG+w5KIsnCeuGEfTTNi2euKqHI1eD6Q==} + engines: {node: '>=22.19.0'} hasBin: true + peerDependencies: + '@wdio/logger': ^9.18.0 || ^10.0.0 generator-function@2.0.1: resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} @@ -4871,14 +4859,6 @@ packages: hosted-git-info@2.8.9: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} - hosted-git-info@7.0.2: - resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==} - engines: {node: ^16.14.0 || >=18.0.0} - - hosted-git-info@8.1.0: - resolution: {integrity: sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==} - engines: {node: ^18.17.0 || >=20.5.0} - hosted-git-info@9.0.3: resolution: {integrity: sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg==} engines: {node: ^20.17.0 || >=22.9.0} @@ -4915,6 +4895,10 @@ packages: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} + https-proxy-agent@9.1.0: + resolution: {integrity: sha512-ag87y7cJJ9/3+GxFr8Oy4O5faDsGRGnBGsJj/YjOSsSx/5eadKLYTMPlzuR6obgoCDDm0abAAZitXXQkMOPSpA==} + engines: {node: '>= 20'} + human-id@4.2.1: resolution: {integrity: sha512-zPGsiS+dWoTZtZ4AtpA9Y+BdSFSNWvnouNlWNoUFyAM6xHOHmdCvqO3k8AIbdamCOv4gUFUVNPf6rJFfc4UiJw==} hasBin: true @@ -5007,9 +4991,9 @@ packages: resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - inquirer@12.11.1: - resolution: {integrity: sha512-9VF7mrY+3OmsAfjH3yKz/pLbJ5z22E23hENKw3/LNSaA/sAt3v49bDRY+Ygct1xwuKT+U+cBfTzjCPySna69Qw==} - engines: {node: '>=18'} + inquirer@14.2.4: + resolution: {integrity: sha512-xPVejWqfkjG9OxSirMqu3pdzGIGsma68j2S+nEOuuwMbU3pRTHnYs9d8my+1M6OMlqr8XZPwCPonZ2DKMwQDLg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': 26.6.4 peerDependenciesMeta: @@ -5215,10 +5199,6 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - isexe@4.0.0: - resolution: {integrity: sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==} - engines: {node: '>=20'} - istanbul-lib-coverage@3.2.2: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} @@ -5418,10 +5398,6 @@ packages: json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - json-parse-even-better-errors@3.0.2: - resolution: {integrity: sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - json-schema-ref-resolver@3.0.0: resolution: {integrity: sha512-hOrZIVL5jyYFjzk7+y7n5JDzGlU8rfWDuYyHwGa2WA8/pcmMHezp2xsVwxrebD/Q9t8Nc5DboieySDpCp4WG4A==} @@ -5648,10 +5624,6 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - lines-and-columns@2.0.4: - resolution: {integrity: sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - lit-element@4.2.2: resolution: {integrity: sha512-aFKhNToWxoyhkNDmWZwEva2SlQia+jfG0fjIWV//YeTaWrVnOxD89dPKfigCUspXFmjzOEUQpOkejH5Ly6sG0w==} @@ -5688,10 +5660,6 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} - locate-path@7.2.0: - resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - lodash.clonedeep@4.5.0: resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} @@ -5898,9 +5866,9 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - mute-stream@2.0.0: - resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} - engines: {node: ^18.17.0 || >=20.5.0} + mute-stream@3.0.0: + resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==} + engines: {node: ^20.17.0 || >=22.9.0} mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} @@ -5959,17 +5927,13 @@ packages: engines: {node: '>=10'} hasBin: true + non-error@0.1.0: + resolution: {integrity: sha512-TMB1uHiGsHRGv1uYclfhivcnf0/PdFp2pNqRxXjncaAsjYMoisaQJI+SSZCqRq+VliwRTC8tsMQfmrWjDMhkPQ==} + engines: {node: '>=20'} + normalize-package-data@2.5.0: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} - normalize-package-data@6.0.2: - resolution: {integrity: sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==} - engines: {node: ^16.14.0 || >=18.0.0} - - normalize-package-data@7.0.1: - resolution: {integrity: sha512-linxNAT6M0ebEYZOx2tO6vBEFsVgnPpv+AVjk0wJHfaUIbq31Jm3T6vvZaarnOeWDh8ShnwXuaAyM7WT3RzErA==} - engines: {node: ^18.17.0 || >=20.5.0} - normalize-package-data@8.0.0: resolution: {integrity: sha512-RWk+PI433eESQ7ounYxIp67CYuVsS1uYSonX3kA6ps/3LWfjVQa/ptEg6Y3T6uAMq1mWpX9PQ+qx+QaHpsc7gQ==} engines: {node: ^20.17.0 || >=22.9.0} @@ -6075,10 +6039,6 @@ packages: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} - p-limit@4.0.0: - resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - p-locate@4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} engines: {node: '>=8'} @@ -6087,10 +6047,6 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} - p-locate@6.0.0: - resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - p-try@2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} @@ -6120,10 +6076,6 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} - parse-json@7.1.1: - resolution: {integrity: sha512-SgOTCX/EZXtZxBE5eJ97P4yGM5n37BwRU+YMsH4vNzFqJV/oWFXXCmwFlgWUM4PrakybVOueJJ6pwHqSVhTFDw==} - engines: {node: '>=16'} - parse-json@8.3.0: resolution: {integrity: sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==} engines: {node: '>=18'} @@ -6148,10 +6100,6 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} - path-exists@5.0.0: - resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} @@ -6350,6 +6298,15 @@ packages: property-expr@2.0.6: resolution: {integrity: sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==} + proxy-agent-negotiate@1.1.0: + resolution: {integrity: sha512-N8IBcM3UgCVzz2L2Lqv8DVntDnnC8/hiV4nEDUPkqq72TPUgYWjQc+bdZlBPZK9LzPAvOY//gAt0S0DApoOXWQ==} + engines: {node: '>= 20'} + peerDependencies: + kerberos: ^2.0.0 + peerDependenciesMeta: + kerberos: + optional: true + psl@1.15.0: resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} @@ -6405,10 +6362,6 @@ packages: resolution: {integrity: sha512-Q5hMVBYur/eQNWDdbF4/Wqqr9Bjvtrw2kjGxxBbKLbx8bVCL8gcArjTy8zDUuLGQicftpMuU0riQNcAsbtOVsw==} engines: {node: '>=20'} - read-pkg-up@10.1.0: - resolution: {integrity: sha512-aNtBq4jR8NawpKJQldrQcSW9y/d+KWH4v24HWkHljOZ7H0av+YTGANBzRh9A5pw7v/bLVsLVPpOhJ7gHNVy8lA==} - engines: {node: '>=16'} - read-pkg@10.1.0: resolution: {integrity: sha512-I8g2lArQiP78ll51UeMZojewtYgIRCKCWqZEgOO8c/uefTI+XDXvCSXu3+YNUaTNvZzobrL5+SqHjBrByRRTdg==} engines: {node: '>=20'} @@ -6417,10 +6370,6 @@ packages: resolution: {integrity: sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==} engines: {node: '>=4'} - read-pkg@8.1.0: - resolution: {integrity: sha512-PORM8AgzXeskHO/WEv312k9U03B8K9JSiWF/8N9sUuFjBa+9SF2u6K7VClzXwDXab51jCd8Nd36CNM+zR97ScQ==} - engines: {node: '>=16'} - readable-stream@2.3.8: resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} @@ -6563,12 +6512,9 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - rxjs@7.8.2: - resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} - - safaridriver@2.0.0: - resolution: {integrity: sha512-LL7Ufa9p1cltLmw0WGIbM6FFXibWMoM9NjJuS5QFiAAtEnEwkwMxaXy1BRKCBZKSZ+brs9eYd3LHHiJfIgjmZg==} - engines: {node: '>=18.0.0'} + safaridriver@3.0.0: + resolution: {integrity: sha512-NJSsR0tgn5j4fhg7vj4kD5mQ+t2ZRH3GX1jdUYraEjej9j8ZtBXEe4WZh2/89i7S9+GO3cZKWhUisWb7gyjRwA==} + engines: {node: '>=22.19.0'} safe-array-concat@1.1.4: resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==} @@ -6655,9 +6601,9 @@ packages: engines: {node: '>=10'} hasBin: true - serialize-error@12.0.0: - resolution: {integrity: sha512-ZYkZLAvKTKQXWuh5XpBw7CdbSzagarX39WyZ2H07CDLC5/KfsRGlIXV8d4+tfqX1M7916mRqR1QfNHSij+c9Pw==} - engines: {node: '>=18'} + serialize-error@13.0.2: + resolution: {integrity: sha512-G+YKwp9I6IvWMe5RlwqxdmTPIcq/G8HK/sU51gdV1XAJznYkRSTB0ph+zB9AfSxG0fx6xrqYA6i9HeIcHFTXow==} + engines: {node: '>=20'} serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} @@ -7009,6 +6955,10 @@ packages: resolution: {integrity: sha512-u9E6A+ZDYdp7a4WnarkXPZOx8Ilz46+kby6p1yZ8zsGTz9gYa6FIS7lj2oezzNKmtdyyJNNmmXDppga5GB7kSw==} engines: {node: '>=18'} + test-exclude@8.0.0: + resolution: {integrity: sha512-ZOffsNrXYggvU1mDGHk54I96r26P8SyMjO5slMKSc7+IWmtB/MQKnEC2fP51imB3/pT6YK5cT5E8f+Dd9KdyOQ==} + engines: {node: 20 || >=22} + text-decoder@1.2.7: resolution: {integrity: sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==} @@ -7164,10 +7114,6 @@ packages: resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} engines: {node: '>=12.20'} - type-fest@3.13.1: - resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==} - engines: {node: '>=14.16'} - type-fest@4.26.0: resolution: {integrity: sha512-OduNjVJsFbifKb57UqZ2EMP1i4u64Xwow3NYXUtBbD4vIwJdQd4+xl8YDou1dlm4DVrtwT/7Ky8z8WyCULVfxw==} engines: {node: '>=16'} @@ -7176,6 +7122,10 @@ packages: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} + type-fest@5.10.0: + resolution: {integrity: sha512-NoSdpq/WEiAg5sjmBkmV/hfxv6HJH4NqPNrqjtSO5CwRmpsDfaf4begxW34KdJykH/l1yHtwBWQkCRdoXO8mPA==} + engines: {node: '>=20'} + type-fest@5.8.0: resolution: {integrity: sha512-YGYEVz3Fm5iy/AybuA0oyNFq7H4CgQNfRp/qfe8nurE1kuCeNm3/vfm9X4Mtl+qLyaKJUh5xrFZwogr41SMjYA==} engines: {node: '>=20'} @@ -7413,8 +7363,8 @@ packages: vite: optional: true - vite-plugin-istanbul@7.2.1: - resolution: {integrity: sha512-DSPi4ulvYsjnP44sTI5oriNosbM0E6m3uoCxjdxboTtVzxSkFwcDy3/JnSYKebjr+ZToJwVLTms+2CM0rmbbzQ==} + vite-plugin-istanbul@9.0.1: + resolution: {integrity: sha512-zgcdcqa4r3urX+xqhMQG2uLR2s6IdklQW+acBEtLw6fvUWhuvXswYqxjHAZZ5HCfHEgRs7RH7qIZCklTLRsKLg==} peerDependencies: vite: ^8.0.7 @@ -7535,12 +7485,12 @@ packages: wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} - webdriver@10.0.2: - resolution: {integrity: sha512-q2eKiapaOyCH2QfVykBjrYznQiL4gjmGakTYVzQqq3Ycqpd2VPOMSkmbxGHFo8+CpO628QwvpxgKvAxA3hpDaA==} + webdriver@10.1.0: + resolution: {integrity: sha512-W3lNUfcjsD5T62U7HackcsfKefrBoUect9PkyObhuByjPFjR+JwmkWAjbFYHlATpZwFZN5d5h28VBanG4BUe4g==} engines: {node: '>=22.19.0'} - webdriverio@10.0.2: - resolution: {integrity: sha512-o+T+DXwsn2e0niAmz9PWUMDwE/8R06euZ4O85faB6fGG9X/uVOom8sloIH+SYc1tI0fjcI34sU9hptrQj9fDdQ==} + webdriverio@10.1.0: + resolution: {integrity: sha512-RDfW0GkHFM/ItdKEw8YvmLgcb5szUlJOKEVUoRgIeeQQ4+a7UMVavC6TVK438wNI32IHu0R1MoMrSW/6DRWYmQ==} engines: {node: '>=22.19.0'} peerDependencies: puppeteer-core: '>=24 <26' @@ -7584,6 +7534,11 @@ packages: resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} engines: {node: '>= 0.4'} + which-command@0.1.0: + resolution: {integrity: sha512-XZyoF5/5hZtXitIwzrU4NKK+Wtbb9aB9CezUEw2Q0wlYK8NUYQxC1rRXgNueYLtBAJwXIb+/tFVk4dozciNJMA==} + engines: {node: '>=22'} + hasBin: true + which-typed-array@1.1.21: resolution: {integrity: sha512-zbRA8cVm6io/d5W8uIe2hblzN76/Wm3v/yiythQvr+dpBWeqhPSWIDNj4zOyHi4zKbMK6DN34Xsr9jPHJERAEw==} engines: {node: '>= 0.4'} @@ -7597,11 +7552,6 @@ packages: engines: {node: '>= 8'} hasBin: true - which@6.0.1: - resolution: {integrity: sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==} - engines: {node: ^20.17.0 || >=22.9.0} - hasBin: true - why-is-node-running@3.2.1: resolution: {integrity: sha512-Tb2FUhB4vUsGQlfSquQLYkApkuPAFQXGFzxWKHHumVz2dK+X1RUm/HnID4+TfIGYJ1kTcwOaCk/buYCEJr6YjQ==} engines: {node: '>=20.11'} @@ -7621,10 +7571,6 @@ packages: workerpool@6.5.1: resolution: {integrity: sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==} - wrap-ansi@6.2.0: - resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} - engines: {node: '>=8'} - wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -7732,14 +7678,6 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - yocto-queue@1.2.2: - resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==} - engines: {node: '>=12.20'} - - yoctocolors-cjs@2.1.3: - resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==} - engines: {node: '>=18'} - yoctocolors@2.2.0: resolution: {integrity: sha512-xYqdZFUK/VYazNl/oCDYN+3WloWQwMfZxBoiNt6qNyk+xfOdi598muWE42rNZFp1kNOiqW936q5RhUdnpqElSg==} engines: {node: '>=18'} @@ -8637,128 +8575,122 @@ snapshots: '@iconify/types': 2.0.0 import-meta-resolve: 4.2.0 - '@inquirer/ansi@1.0.2': {} + '@inquirer/ansi@2.0.9': {} - '@inquirer/checkbox@4.3.2(@types/node@26.6.4)': + '@inquirer/checkbox@5.2.7(@types/node@26.6.4)': dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@26.6.4) - '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@26.6.4) - yoctocolors-cjs: 2.1.3 + '@inquirer/ansi': 2.0.9 + '@inquirer/core': 12.0.5(@types/node@26.6.4) + '@inquirer/figures': 2.0.9 + '@inquirer/type': 4.2.0(@types/node@26.6.4) optionalDependencies: '@types/node': 26.6.4 - '@inquirer/confirm@5.1.21(@types/node@26.6.4)': + '@inquirer/confirm@6.3.4(@types/node@26.6.4)': dependencies: - '@inquirer/core': 10.3.2(@types/node@26.6.4) - '@inquirer/type': 3.0.10(@types/node@26.6.4) + '@inquirer/core': 12.0.5(@types/node@26.6.4) + '@inquirer/type': 4.2.0(@types/node@26.6.4) optionalDependencies: '@types/node': 26.6.4 - '@inquirer/core@10.3.2(@types/node@26.6.4)': + '@inquirer/core@12.0.5(@types/node@26.6.4)': dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@26.6.4) + '@inquirer/ansi': 2.0.9 + '@inquirer/figures': 2.0.9 + '@inquirer/type': 4.2.0(@types/node@26.6.4) cli-width: 4.1.0 - mute-stream: 2.0.0 + fast-wrap-ansi: 0.2.2 + mute-stream: 3.0.0 signal-exit: 4.1.0 - wrap-ansi: 6.2.0 - yoctocolors-cjs: 2.1.3 optionalDependencies: '@types/node': 26.6.4 - '@inquirer/editor@4.2.23(@types/node@26.6.4)': + '@inquirer/editor@5.3.5(@types/node@26.6.4)': dependencies: - '@inquirer/core': 10.3.2(@types/node@26.6.4) - '@inquirer/external-editor': 1.0.3(@types/node@26.6.4) - '@inquirer/type': 3.0.10(@types/node@26.6.4) + '@inquirer/core': 12.0.5(@types/node@26.6.4) + '@inquirer/external-editor': 3.0.6(@types/node@26.6.4) + '@inquirer/type': 4.2.0(@types/node@26.6.4) optionalDependencies: '@types/node': 26.6.4 - '@inquirer/expand@4.0.23(@types/node@26.6.4)': + '@inquirer/expand@5.1.7(@types/node@26.6.4)': dependencies: - '@inquirer/core': 10.3.2(@types/node@26.6.4) - '@inquirer/type': 3.0.10(@types/node@26.6.4) - yoctocolors-cjs: 2.1.3 + '@inquirer/core': 12.0.5(@types/node@26.6.4) + '@inquirer/type': 4.2.0(@types/node@26.6.4) optionalDependencies: '@types/node': 26.6.4 - '@inquirer/external-editor@1.0.3(@types/node@26.6.4)': + '@inquirer/external-editor@3.0.6(@types/node@26.6.4)': dependencies: chardet: 2.2.0 iconv-lite: 0.7.3 optionalDependencies: '@types/node': 26.6.4 - '@inquirer/figures@1.0.15': {} + '@inquirer/figures@2.0.9': {} - '@inquirer/input@4.3.1(@types/node@26.6.4)': + '@inquirer/input@5.1.8(@types/node@26.6.4)': dependencies: - '@inquirer/core': 10.3.2(@types/node@26.6.4) - '@inquirer/type': 3.0.10(@types/node@26.6.4) + '@inquirer/core': 12.0.5(@types/node@26.6.4) + '@inquirer/type': 4.2.0(@types/node@26.6.4) optionalDependencies: '@types/node': 26.6.4 - '@inquirer/number@3.0.23(@types/node@26.6.4)': + '@inquirer/number@4.2.5(@types/node@26.6.4)': dependencies: - '@inquirer/core': 10.3.2(@types/node@26.6.4) - '@inquirer/type': 3.0.10(@types/node@26.6.4) + '@inquirer/core': 12.0.5(@types/node@26.6.4) + '@inquirer/type': 4.2.0(@types/node@26.6.4) optionalDependencies: '@types/node': 26.6.4 - '@inquirer/password@4.0.23(@types/node@26.6.4)': + '@inquirer/password@5.2.4(@types/node@26.6.4)': dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@26.6.4) - '@inquirer/type': 3.0.10(@types/node@26.6.4) + '@inquirer/ansi': 2.0.9 + '@inquirer/core': 12.0.5(@types/node@26.6.4) + '@inquirer/type': 4.2.0(@types/node@26.6.4) optionalDependencies: '@types/node': 26.6.4 - '@inquirer/prompts@7.10.1(@types/node@26.6.4)': - dependencies: - '@inquirer/checkbox': 4.3.2(@types/node@26.6.4) - '@inquirer/confirm': 5.1.21(@types/node@26.6.4) - '@inquirer/editor': 4.2.23(@types/node@26.6.4) - '@inquirer/expand': 4.0.23(@types/node@26.6.4) - '@inquirer/input': 4.3.1(@types/node@26.6.4) - '@inquirer/number': 3.0.23(@types/node@26.6.4) - '@inquirer/password': 4.0.23(@types/node@26.6.4) - '@inquirer/rawlist': 4.1.11(@types/node@26.6.4) - '@inquirer/search': 3.2.2(@types/node@26.6.4) - '@inquirer/select': 4.4.2(@types/node@26.6.4) + '@inquirer/prompts@8.7.4(@types/node@26.6.4)': + dependencies: + '@inquirer/checkbox': 5.2.7(@types/node@26.6.4) + '@inquirer/confirm': 6.3.4(@types/node@26.6.4) + '@inquirer/editor': 5.3.5(@types/node@26.6.4) + '@inquirer/expand': 5.1.7(@types/node@26.6.4) + '@inquirer/input': 5.1.8(@types/node@26.6.4) + '@inquirer/number': 4.2.5(@types/node@26.6.4) + '@inquirer/password': 5.2.4(@types/node@26.6.4) + '@inquirer/rawlist': 5.3.7(@types/node@26.6.4) + '@inquirer/search': 4.3.5(@types/node@26.6.4) + '@inquirer/select': 5.2.7(@types/node@26.6.4) optionalDependencies: '@types/node': 26.6.4 - '@inquirer/rawlist@4.1.11(@types/node@26.6.4)': + '@inquirer/rawlist@5.3.7(@types/node@26.6.4)': dependencies: - '@inquirer/core': 10.3.2(@types/node@26.6.4) - '@inquirer/type': 3.0.10(@types/node@26.6.4) - yoctocolors-cjs: 2.1.3 + '@inquirer/core': 12.0.5(@types/node@26.6.4) + '@inquirer/type': 4.2.0(@types/node@26.6.4) optionalDependencies: '@types/node': 26.6.4 - '@inquirer/search@3.2.2(@types/node@26.6.4)': + '@inquirer/search@4.3.5(@types/node@26.6.4)': dependencies: - '@inquirer/core': 10.3.2(@types/node@26.6.4) - '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@26.6.4) - yoctocolors-cjs: 2.1.3 + '@inquirer/core': 12.0.5(@types/node@26.6.4) + '@inquirer/figures': 2.0.9 + '@inquirer/type': 4.2.0(@types/node@26.6.4) optionalDependencies: '@types/node': 26.6.4 - '@inquirer/select@4.4.2(@types/node@26.6.4)': + '@inquirer/select@5.2.7(@types/node@26.6.4)': dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@26.6.4) - '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@26.6.4) - yoctocolors-cjs: 2.1.3 + '@inquirer/ansi': 2.0.9 + '@inquirer/core': 12.0.5(@types/node@26.6.4) + '@inquirer/figures': 2.0.9 + '@inquirer/type': 4.2.0(@types/node@26.6.4) optionalDependencies: '@types/node': 26.6.4 - '@inquirer/type@3.0.10(@types/node@26.6.4)': + '@inquirer/type@4.2.0(@types/node@26.6.4)': optionalDependencies: '@types/node': 26.6.4 @@ -9677,10 +9609,6 @@ snapshots: dependencies: '@babel/types': 7.29.8 - '@types/babel__generator@7.6.8': - dependencies: - '@babel/types': 7.29.8 - '@types/babel__template@7.4.4': dependencies: '@babel/parser': 7.29.9 @@ -9736,7 +9664,7 @@ snapshots: '@types/shell-quote@1.7.5': {} - '@types/sinonjs__fake-timers@8.1.5': {} + '@types/sinonjs__fake-timers@15.0.1': {} '@types/split2@4.2.3': dependencies: @@ -9750,8 +9678,6 @@ snapshots: '@types/whatwg-mimetype@3.0.2': {} - '@types/which@2.0.2': {} - '@types/ws@8.18.2': dependencies: '@types/node': 26.6.4 @@ -9987,11 +9913,11 @@ snapshots: path-browserify: 1.0.1 vscode-uri: 3.2.0 - '@wdio/allure-reporter@10.0.1': + '@wdio/allure-reporter@10.1.0': dependencies: '@types/node': 26.6.4 - '@wdio/reporter': 10.0.1 - '@wdio/types': 10.0.1 + '@wdio/reporter': 10.1.0 + '@wdio/types': 10.1.0 allure-js-commons: 3.13.0 csv-stringify: 6.9.0 html-entities: 2.6.0 @@ -9999,21 +9925,21 @@ snapshots: transitivePeerDependencies: - allure-playwright - '@wdio/browser-runner@10.0.2(@types/node@26.6.4)(esbuild@0.28.2)(expect-webdriverio@8.0.0(@wdio/types@10.0.1)(webdriverio@10.0.2(yauzl@2.10.0)))(jiti@2.7.0)(magicast@0.5.5)(rollup@4.64.0)(tsx@4.23.15)(webdriver@10.0.2(yauzl@2.10.0))(webdriverio@10.0.2(yauzl@2.10.0))(yaml@2.9.1)(yauzl@2.10.0)': + '@wdio/browser-runner@10.1.0(@types/node@26.6.4)(esbuild@0.28.2)(expect-webdriverio@8.0.0(@wdio/types@10.1.0)(webdriverio@10.1.0(yauzl@2.10.0)))(jiti@2.7.0)(magicast@0.5.5)(rollup@4.64.0)(tsx@4.23.15)(webdriver@10.1.0(yauzl@2.10.0))(webdriverio@10.1.0(yauzl@2.10.0))(yaml@2.9.1)(yauzl@2.10.0)': dependencies: '@originjs/vite-plugin-commonjs': 1.0.3 '@vitest/spy': 5.0.3 - '@wdio/globals': 10.0.2(expect-webdriverio@8.0.0(@wdio/types@10.0.1)(webdriverio@10.0.2(yauzl@2.10.0)))(webdriverio@10.0.2(yauzl@2.10.0)) - '@wdio/local-runner': 10.0.2(tsx@4.23.15)(webdriverio@10.0.2(yauzl@2.10.0))(yauzl@2.10.0) - '@wdio/logger': 10.0.0 - '@wdio/mocha-framework': 10.0.1(yauzl@2.10.0) + '@wdio/globals': 10.1.0(expect-webdriverio@8.0.0(@wdio/types@10.1.0)(webdriverio@10.1.0(yauzl@2.10.0)))(webdriverio@10.1.0(yauzl@2.10.0)) + '@wdio/local-runner': 10.1.0(tsx@4.23.15)(webdriverio@10.1.0(yauzl@2.10.0))(yauzl@2.10.0) + '@wdio/logger': 10.1.0 + '@wdio/mocha-framework': 10.1.0(yauzl@2.10.0) '@wdio/protocols': 10.0.2 - '@wdio/runner': 10.0.2(expect-webdriverio@8.0.0(@wdio/types@10.0.1)(webdriverio@10.0.2(yauzl@2.10.0)))(tsx@4.23.15)(webdriverio@10.0.2(yauzl@2.10.0))(yauzl@2.10.0) - '@wdio/types': 10.0.1 - '@wdio/utils': 10.0.1(yauzl@2.10.0) + '@wdio/runner': 10.1.0(expect-webdriverio@8.0.0(@wdio/types@10.1.0)(webdriverio@10.1.0(yauzl@2.10.0)))(tsx@4.23.15)(webdriverio@10.1.0(yauzl@2.10.0))(yauzl@2.10.0) + '@wdio/types': 10.1.0 + '@wdio/utils': 10.1.0(yauzl@2.10.0) deepmerge-ts: 8.0.2 expect: 30.5.2 - expect-webdriverio: 8.0.0(@wdio/types@10.0.1)(webdriverio@10.0.2(yauzl@2.10.0)) + expect-webdriverio: 8.0.0(@wdio/types@10.1.0)(webdriverio@10.1.0(yauzl@2.10.0)) get-port: 7.2.0 import-meta-resolve: 4.2.0 istanbul-lib-coverage: 3.2.2 @@ -10026,10 +9952,10 @@ snapshots: safe-stringify: 1.3.0 source-map-support: 0.5.21 vite: 8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1) - vite-plugin-istanbul: 7.2.1(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1)) + vite-plugin-istanbul: 9.0.1(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1)) vite-plugin-top-level-await: 1.6.0(rollup@4.64.0)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1)) - webdriver: 10.0.2(yauzl@2.10.0) - webdriverio: 10.0.2(yauzl@2.10.0) + webdriver: 10.1.0(yauzl@2.10.0) + webdriverio: 10.1.0(yauzl@2.10.0) optionalDependencies: '@nuxt/kit': 3.21.11(magicast@0.5.5) '@stencil/core': 4.45.1 @@ -10046,6 +9972,7 @@ snapshots: - bufferutil - esbuild - jiti + - kerberos - less - magicast - proxy-agent @@ -10063,28 +9990,28 @@ snapshots: - yaml - yauzl - '@wdio/cli@10.0.2(@types/node@26.6.4)(expect-webdriverio@8.0.0(@wdio/types@10.0.1)(webdriverio@10.0.2(yauzl@2.10.0)))(yauzl@2.10.0)': + '@wdio/cli@10.1.0(@types/node@26.6.4)(expect-webdriverio@8.0.0(@wdio/types@10.1.0)(webdriverio@10.1.0(yauzl@2.10.0)))(yauzl@2.10.0)': dependencies: '@vitest/snapshot': 5.0.3 - '@wdio/config': 10.0.1(yauzl@2.10.0) - '@wdio/globals': 10.0.2(expect-webdriverio@8.0.0(@wdio/types@10.0.1)(webdriverio@10.0.2(yauzl@2.10.0)))(webdriverio@10.0.2(yauzl@2.10.0)) - '@wdio/logger': 10.0.0 + '@wdio/config': 10.1.0(yauzl@2.10.0) + '@wdio/globals': 10.1.0(expect-webdriverio@8.0.0(@wdio/types@10.1.0)(webdriverio@10.1.0(yauzl@2.10.0)))(webdriverio@10.1.0(yauzl@2.10.0)) + '@wdio/logger': 10.1.0 '@wdio/protocols': 10.0.2 - '@wdio/session': 10.0.2(expect-webdriverio@8.0.0(@wdio/types@10.0.1)(webdriverio@10.0.2(yauzl@2.10.0)))(tsx@4.23.15)(yauzl@2.10.0) - '@wdio/types': 10.0.1 - '@wdio/utils': 10.0.1(yauzl@2.10.0) + '@wdio/session': 10.1.0(expect-webdriverio@8.0.0(@wdio/types@10.1.0)(webdriverio@10.1.0(yauzl@2.10.0)))(tsx@4.23.15)(yauzl@2.10.0) + '@wdio/types': 10.1.0 + '@wdio/utils': 10.1.0(yauzl@2.10.0) async-exit-hook: 2.0.1 - chalk: 5.6.2 - chokidar: 4.0.3 - create-wdio: 10.0.2(@types/node@26.6.4) - dotenv: 17.4.2 + chalk: 6.0.1 + chokidar: 5.0.0 + create-wdio: 10.1.0(@types/node@26.6.4) + dotenv: 18.0.8 import-meta-resolve: 4.2.0 lodash.flattendeep: 4.4.0 lodash.pickby: 4.6.0 lodash.union: 4.6.0 - read-pkg-up: 10.1.0 + read-package-up: 12.0.0 tsx: 4.23.15 - webdriverio: 10.0.2(yauzl@2.10.0) + webdriverio: 10.1.0(yauzl@2.10.0) yargs: 18.2.0 transitivePeerDependencies: - '@types/node' @@ -10095,6 +10022,7 @@ snapshots: - bare-buffer - bufferutil - expect-webdriverio + - kerberos - proxy-agent - puppeteer-core - react-native-b4a @@ -10102,13 +10030,13 @@ snapshots: - utf-8-validate - yauzl - '@wdio/config@10.0.1(yauzl@2.10.0)': + '@wdio/config@10.1.0(yauzl@2.10.0)': dependencies: - '@wdio/logger': 10.0.0 - '@wdio/types': 10.0.1 - '@wdio/utils': 10.0.1(yauzl@2.10.0) + '@wdio/logger': 10.1.0 + '@wdio/types': 10.1.0 + '@wdio/utils': 10.1.0(yauzl@2.10.0) deepmerge-ts: 8.0.2 - glob: 10.5.0 + glob: 13.0.6 import-meta-resolve: 4.2.0 jiti: 2.7.0 transitivePeerDependencies: @@ -10116,16 +10044,16 @@ snapshots: - supports-color - yauzl - '@wdio/cucumber-framework@10.0.1(yauzl@2.10.0)': + '@wdio/cucumber-framework@10.1.0(yauzl@2.10.0)': dependencies: '@cucumber/cucumber': 13.2.1 '@cucumber/gherkin': 42.0.1 '@cucumber/messages': 34.2.1 '@types/node': 26.6.4 - '@wdio/logger': 10.0.0 - '@wdio/types': 10.0.1 - '@wdio/utils': 10.0.1(yauzl@2.10.0) - glob: 10.5.0 + '@wdio/logger': 10.1.0 + '@wdio/types': 10.1.0 + '@wdio/utils': 10.1.0(yauzl@2.10.0) + glob: 13.0.6 import-meta-resolve: 4.2.0 is-glob: 4.0.3 transitivePeerDependencies: @@ -10133,33 +10061,33 @@ snapshots: - supports-color - yauzl - '@wdio/display-server@10.0.1': + '@wdio/display-server@10.1.0': dependencies: - '@wdio/logger': 10.0.0 - '@wdio/types': 10.0.1 + '@wdio/logger': 10.1.0 + '@wdio/types': 10.1.0 - '@wdio/dot-reporter@10.0.1': + '@wdio/dot-reporter@10.1.0': dependencies: - '@wdio/reporter': 10.0.1 - '@wdio/types': 10.0.1 - chalk: 5.6.2 + '@wdio/reporter': 10.1.0 + '@wdio/types': 10.1.0 + chalk: 6.0.1 - '@wdio/globals@10.0.2(expect-webdriverio@8.0.0(@wdio/types@10.0.1)(webdriverio@10.0.2(yauzl@2.10.0)))(webdriverio@10.0.2(yauzl@2.10.0))': + '@wdio/globals@10.1.0(expect-webdriverio@8.0.0(@wdio/types@10.1.0)(webdriverio@10.1.0(yauzl@2.10.0)))(webdriverio@10.1.0(yauzl@2.10.0))': dependencies: - expect-webdriverio: 8.0.0(@wdio/types@10.0.1)(webdriverio@10.0.2(yauzl@2.10.0)) - webdriverio: 10.0.2(yauzl@2.10.0) + expect-webdriverio: 8.0.0(@wdio/types@10.1.0)(webdriverio@10.1.0(yauzl@2.10.0)) + webdriverio: 10.1.0(yauzl@2.10.0) - '@wdio/local-runner@10.0.2(tsx@4.23.15)(webdriverio@10.0.2(yauzl@2.10.0))(yauzl@2.10.0)': + '@wdio/local-runner@10.1.0(tsx@4.23.15)(webdriverio@10.1.0(yauzl@2.10.0))(yauzl@2.10.0)': dependencies: '@types/node': 26.6.4 '@types/split2': 4.2.3 - '@wdio/display-server': 10.0.1 - '@wdio/logger': 10.0.0 + '@wdio/display-server': 10.1.0 + '@wdio/logger': 10.1.0 '@wdio/repl': 10.0.0 - '@wdio/runner': 10.0.2(expect-webdriverio@8.0.0(@wdio/types@10.0.1)(webdriverio@10.0.2(yauzl@2.10.0)))(tsx@4.23.15)(webdriverio@10.0.2(yauzl@2.10.0))(yauzl@2.10.0) - '@wdio/types': 10.0.1 - exit-hook: 4.0.0 - expect-webdriverio: 8.0.0(@wdio/types@10.0.1)(webdriverio@10.0.2(yauzl@2.10.0)) + '@wdio/runner': 10.1.0(expect-webdriverio@8.0.0(@wdio/types@10.1.0)(webdriverio@10.1.0(yauzl@2.10.0)))(tsx@4.23.15)(webdriverio@10.1.0(yauzl@2.10.0))(yauzl@2.10.0) + '@wdio/types': 10.1.0 + exit-hook: 5.1.0 + expect-webdriverio: 8.0.0(@wdio/types@10.1.0)(webdriverio@10.1.0(yauzl@2.10.0)) split2: 4.2.0 stream-buffers: 3.0.3 transitivePeerDependencies: @@ -10169,6 +10097,7 @@ snapshots: - bare-abort-controller - bare-buffer - bufferutil + - kerberos - proxy-agent - puppeteer-core - react-native-b4a @@ -10178,29 +10107,21 @@ snapshots: - webdriverio - yauzl - '@wdio/logger@10.0.0': - dependencies: - chalk: 5.6.2 - loglevel: 1.9.2 - loglevel-plugin-prefix: 0.8.4 - safe-regex2: 5.1.1 - strip-ansi: 7.2.0 - - '@wdio/logger@9.29.1': + '@wdio/logger@10.1.0': dependencies: - chalk: 5.6.2 + chalk: 6.0.1 loglevel: 1.9.2 loglevel-plugin-prefix: 0.8.4 safe-regex2: 5.1.1 strip-ansi: 7.2.0 - '@wdio/mocha-framework@10.0.1(yauzl@2.10.0)': + '@wdio/mocha-framework@10.1.0(yauzl@2.10.0)': dependencies: '@types/mocha': 10.0.10 '@types/node': 26.6.4 - '@wdio/logger': 10.0.0 - '@wdio/types': 10.0.1 - '@wdio/utils': 10.0.1(yauzl@2.10.0) + '@wdio/logger': 10.1.0 + '@wdio/types': 10.1.0 + '@wdio/utils': 10.1.0(yauzl@2.10.0) mocha: 12.0.3 transitivePeerDependencies: - proxy-agent @@ -10213,27 +10134,27 @@ snapshots: dependencies: '@types/node': 26.6.4 - '@wdio/reporter@10.0.1': + '@wdio/reporter@10.1.0': dependencies: '@types/node': 26.6.4 - '@wdio/types': 10.0.1 - diff: 8.0.4 + '@wdio/types': 10.1.0 + diff: 9.0.0 object-inspect: 1.13.4 - '@wdio/runner@10.0.2(expect-webdriverio@8.0.0(@wdio/types@10.0.1)(webdriverio@10.0.2(yauzl@2.10.0)))(tsx@4.23.15)(webdriverio@10.0.2(yauzl@2.10.0))(yauzl@2.10.0)': + '@wdio/runner@10.1.0(expect-webdriverio@8.0.0(@wdio/types@10.1.0)(webdriverio@10.1.0(yauzl@2.10.0)))(tsx@4.23.15)(webdriverio@10.1.0(yauzl@2.10.0))(yauzl@2.10.0)': dependencies: '@types/node': 26.6.4 - '@wdio/config': 10.0.1(yauzl@2.10.0) - '@wdio/dot-reporter': 10.0.1 - '@wdio/globals': 10.0.2(expect-webdriverio@8.0.0(@wdio/types@10.0.1)(webdriverio@10.0.2(yauzl@2.10.0)))(webdriverio@10.0.2(yauzl@2.10.0)) - '@wdio/logger': 10.0.0 - '@wdio/session': 10.0.2(expect-webdriverio@8.0.0(@wdio/types@10.0.1)(webdriverio@10.0.2(yauzl@2.10.0)))(tsx@4.23.15)(yauzl@2.10.0) - '@wdio/types': 10.0.1 - '@wdio/utils': 10.0.1(yauzl@2.10.0) + '@wdio/config': 10.1.0(yauzl@2.10.0) + '@wdio/dot-reporter': 10.1.0 + '@wdio/globals': 10.1.0(expect-webdriverio@8.0.0(@wdio/types@10.1.0)(webdriverio@10.1.0(yauzl@2.10.0)))(webdriverio@10.1.0(yauzl@2.10.0)) + '@wdio/logger': 10.1.0 + '@wdio/session': 10.1.0(expect-webdriverio@8.0.0(@wdio/types@10.1.0)(webdriverio@10.1.0(yauzl@2.10.0)))(tsx@4.23.15)(yauzl@2.10.0) + '@wdio/types': 10.1.0 + '@wdio/utils': 10.1.0(yauzl@2.10.0) deepmerge-ts: 8.0.2 - expect-webdriverio: 8.0.0(@wdio/types@10.0.1)(webdriverio@10.0.2(yauzl@2.10.0)) - webdriver: 10.0.2(yauzl@2.10.0) - webdriverio: 10.0.2(yauzl@2.10.0) + expect-webdriverio: 8.0.0(@wdio/types@10.1.0)(webdriverio@10.1.0(yauzl@2.10.0)) + webdriver: 10.1.0(yauzl@2.10.0) + webdriverio: 10.1.0(yauzl@2.10.0) transitivePeerDependencies: - '@wdio/electron-service' - '@wdio/visual-service' @@ -10241,6 +10162,7 @@ snapshots: - bare-abort-controller - bare-buffer - bufferutil + - kerberos - proxy-agent - puppeteer-core - react-native-b4a @@ -10249,29 +10171,30 @@ snapshots: - utf-8-validate - yauzl - '@wdio/session@10.0.2(expect-webdriverio@8.0.0(@wdio/types@10.0.1)(webdriverio@10.0.2(yauzl@2.10.0)))(tsx@4.23.15)(yauzl@2.10.0)': + '@wdio/session@10.1.0(expect-webdriverio@8.0.0(@wdio/types@10.1.0)(webdriverio@10.1.0(yauzl@2.10.0)))(tsx@4.23.15)(yauzl@2.10.0)': dependencies: '@types/node': 26.6.4 - '@wdio/config': 10.0.1(yauzl@2.10.0) - '@wdio/display-server': 10.0.1 - '@wdio/logger': 10.0.0 - '@wdio/snapshot': 10.0.2(yauzl@2.10.0) - '@wdio/types': 10.0.1 - '@wdio/utils': 10.0.1(yauzl@2.10.0) + '@wdio/config': 10.1.0(yauzl@2.10.0) + '@wdio/display-server': 10.1.0 + '@wdio/logger': 10.1.0 + '@wdio/snapshot': 10.1.0(yauzl@2.10.0) + '@wdio/types': 10.1.0 + '@wdio/utils': 10.1.0(yauzl@2.10.0) acorn: 8.18.0 get-port: 7.2.0 image-size: 2.0.4 picomatch: 4.0.7 semver: 7.8.5 - webdriverio: 10.0.2(yauzl@2.10.0) + webdriverio: 10.1.0(yauzl@2.10.0) yargs: 18.2.0 optionalDependencies: - expect-webdriverio: 8.0.0(@wdio/types@10.0.1)(webdriverio@10.0.2(yauzl@2.10.0)) + expect-webdriverio: 8.0.0(@wdio/types@10.1.0)(webdriverio@10.1.0(yauzl@2.10.0)) tsx: 4.23.15 transitivePeerDependencies: - bare-abort-controller - bare-buffer - bufferutil + - kerberos - proxy-agent - puppeteer-core - react-native-b4a @@ -10279,14 +10202,15 @@ snapshots: - utf-8-validate - yauzl - '@wdio/snapshot@10.0.2(yauzl@2.10.0)': + '@wdio/snapshot@10.1.0(yauzl@2.10.0)': dependencies: - '@wdio/utils': 10.0.1(yauzl@2.10.0) - webdriverio: 10.0.2(yauzl@2.10.0) + '@wdio/utils': 10.1.0(yauzl@2.10.0) + webdriverio: 10.1.0(yauzl@2.10.0) transitivePeerDependencies: - bare-abort-controller - bare-buffer - bufferutil + - kerberos - proxy-agent - puppeteer-core - react-native-b4a @@ -10294,35 +10218,35 @@ snapshots: - utf-8-validate - yauzl - '@wdio/spec-reporter@10.0.1': + '@wdio/spec-reporter@10.1.0': dependencies: - '@wdio/reporter': 10.0.1 - '@wdio/types': 10.0.1 - chalk: 5.6.2 + '@wdio/reporter': 10.1.0 + '@wdio/types': 10.1.0 + chalk: 6.0.1 easy-table: 1.2.0 pretty-ms: 9.3.1 - '@wdio/types@10.0.1': + '@wdio/types@10.1.0': dependencies: '@types/node': 26.6.4 - '@wdio/utils@10.0.1(yauzl@2.10.0)': + '@wdio/utils@10.1.0(yauzl@2.10.0)': dependencies: '@puppeteer/browsers': 3.2.4(yauzl@2.10.0) '@types/split2': 4.2.3 - '@wdio/logger': 10.0.0 - '@wdio/types': 10.0.1 + '@wdio/logger': 10.1.0 + '@wdio/types': 10.1.0 aria-query: 5.3.2 decamelize: 6.0.1 deepmerge-ts: 8.0.2 - edgedriver: 7.0.0 + edgedriver: 8.0.0(@wdio/logger@10.1.0) electron-to-chromium: 1.5.443 - geckodriver: 7.0.0 + geckodriver: 8.0.0(@wdio/logger@10.1.0) get-port: 7.2.0 import-meta-resolve: 4.2.0 locate-app: 2.5.0 mitt: 3.0.1 - safaridriver: 2.0.0 + safaridriver: 3.0.0 split2: 4.2.0 wait-port: 1.1.0 transitivePeerDependencies: @@ -10332,7 +10256,7 @@ snapshots: '@xmldom/xmldom@0.9.12': {} - '@zip.js/zip.js@2.20.0': {} + '@zip.js/zip.js@2.24.0': {} abort-controller@3.0.0: dependencies: @@ -10354,6 +10278,8 @@ snapshots: agent-base@7.1.4: {} + agent-base@9.0.0: {} + ajv-draft-04@1.0.0(ajv@8.20.0): optionalDependencies: ajv: 8.20.0 @@ -10847,7 +10773,7 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 - chalk@5.6.2: {} + chalk@6.0.1: {} change-case@5.4.4: {} @@ -10981,8 +10907,6 @@ snapshots: dependencies: delayed-stream: 1.0.0 - commander@14.0.3: {} - commander@15.0.0: {} commander@4.1.1: {} @@ -11052,19 +10976,19 @@ snapshots: create-require@1.1.1: {} - create-wdio@10.0.2(@types/node@26.6.4): + create-wdio@10.1.0(@types/node@26.6.4): dependencies: - chalk: 5.6.2 - commander: 14.0.3 + chalk: 6.0.1 + commander: 15.0.0 cross-spawn: 7.0.6 - ejs: 3.1.10 - execa: 9.6.1 + ejs: 7.0.2 + execa: 10.2.0 import-meta-resolve: 4.2.0 - inquirer: 12.11.1(@types/node@26.6.4) - normalize-package-data: 7.0.1 - read-pkg-up: 10.1.0 + inquirer: 14.2.4(@types/node@26.6.4) + normalize-package-data: 8.0.0 + read-package-up: 12.0.0 semver: 7.8.5 - type-fest: 4.41.0 + type-fest: 5.10.0 yargs: 18.2.0 transitivePeerDependencies: - '@types/node' @@ -11279,7 +11203,10 @@ snapshots: dotenv@16.3.1: {} - dotenv@17.4.2: {} + dotenv@17.4.2: + optional: true + + dotenv@18.0.8: {} dunder-proto@1.0.1: dependencies: @@ -11302,27 +11229,19 @@ snapshots: optionalDependencies: wcwidth: 1.0.1 - edge-paths@3.0.5: + edgedriver@8.0.0(@wdio/logger@10.1.0): dependencies: - '@types/which': 2.0.2 - which: 2.0.2 - - edgedriver@7.0.0: - dependencies: - '@wdio/logger': 9.29.1 - '@zip.js/zip.js': 2.20.0 + '@wdio/logger': 10.1.0 + '@zip.js/zip.js': 2.24.0 decamelize: 6.0.1 - edge-paths: 3.0.5 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 - which: 6.0.1 - transitivePeerDependencies: - - supports-color + undici: 8.11.2 ejs@3.1.10: dependencies: jake: 10.9.4 + ejs@7.0.2: {} + electron-to-chromium@1.5.443: {} emittery@0.13.1: {} @@ -11720,8 +11639,6 @@ snapshots: eslint-visitor-keys@3.4.3: {} - eslint-visitor-keys@4.2.1: {} - eslint-visitor-keys@5.0.1: {} eslint@10.12.0(jiti@2.7.0): @@ -11761,12 +11678,6 @@ snapshots: transitivePeerDependencies: - supports-color - espree@10.4.0: - dependencies: - acorn: 8.18.0 - acorn-jsx: 5.3.2(acorn@8.18.0) - eslint-visitor-keys: 4.2.1 - espree@11.2.0: dependencies: acorn: 8.18.0 @@ -11803,22 +11714,9 @@ snapshots: events@3.3.0: {} - execa@5.1.1: - dependencies: - cross-spawn: 7.0.6 - get-stream: 6.0.1 - human-signals: 2.1.0 - is-stream: 2.0.1 - merge-stream: 2.0.0 - npm-run-path: 4.0.1 - onetime: 5.1.2 - signal-exit: 3.0.7 - strip-final-newline: 2.0.0 - - execa@9.6.1: + execa@10.2.0: dependencies: '@sindresorhus/merge-streams': 4.0.0 - cross-spawn: 7.0.6 figures: 6.1.0 get-stream: 9.0.1 human-signals: 8.0.1 @@ -11828,21 +11726,34 @@ snapshots: pretty-ms: 9.3.1 signal-exit: 4.1.0 strip-final-newline: 4.0.0 + which-command: 0.1.0 yoctocolors: 2.2.0 - exit-hook@4.0.0: {} + execa@5.1.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + + exit-hook@5.1.0: {} exit-x@0.2.2: {} expect-type@1.4.0: {} - expect-webdriverio@8.0.0(@wdio/types@10.0.1)(webdriverio@10.0.2(yauzl@2.10.0)): + expect-webdriverio@8.0.0(@wdio/types@10.1.0)(webdriverio@10.1.0(yauzl@2.10.0)): dependencies: '@vitest/snapshot': 5.0.3 - '@wdio/types': 10.0.1 + '@wdio/types': 10.1.0 expect: 30.5.2 jest-matcher-utils: 30.5.2 - webdriverio: 10.0.2(yauzl@2.10.0) + webdriverio: 10.1.0(yauzl@2.10.0) expect@30.5.2: dependencies: @@ -11977,11 +11888,6 @@ snapshots: locate-path: 6.0.0 path-exists: 4.0.0 - find-up@6.3.0: - dependencies: - locate-path: 7.2.0 - path-exists: 5.0.0 - fix-dts-default-cjs-exports@1.0.1: dependencies: magic-string: 0.30.21 @@ -12048,16 +11954,13 @@ snapshots: functions-have-names@1.2.3: {} - geckodriver@7.0.0: + geckodriver@8.0.0(@wdio/logger@10.1.0): dependencies: - '@wdio/logger': 9.29.1 - '@zip.js/zip.js': 2.20.0 + '@wdio/logger': 10.1.0 + '@zip.js/zip.js': 2.24.0 decamelize: 6.0.1 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 modern-tar: 0.8.5 - transitivePeerDependencies: - - supports-color + undici: 8.11.2 generator-function@2.0.1: {} @@ -12243,14 +12146,6 @@ snapshots: hosted-git-info@2.8.9: {} - hosted-git-info@7.0.2: - dependencies: - lru-cache: 10.4.3 - - hosted-git-info@8.1.0: - dependencies: - lru-cache: 10.4.3 - hosted-git-info@9.0.3: dependencies: lru-cache: 11.5.3 @@ -12296,6 +12191,15 @@ snapshots: transitivePeerDependencies: - supports-color + https-proxy-agent@9.1.0: + dependencies: + agent-base: 9.0.0 + debug: 4.4.3 + proxy-agent-negotiate: 1.1.0 + transitivePeerDependencies: + - kerberos + - supports-color + human-id@4.2.1: {} human-signals@2.1.0: {} @@ -12360,15 +12264,14 @@ snapshots: ini@4.1.1: {} - inquirer@12.11.1(@types/node@26.6.4): + inquirer@14.2.4(@types/node@26.6.4): dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@26.6.4) - '@inquirer/prompts': 7.10.1(@types/node@26.6.4) - '@inquirer/type': 3.0.10(@types/node@26.6.4) - mute-stream: 2.0.0 + '@inquirer/ansi': 2.0.9 + '@inquirer/core': 12.0.5(@types/node@26.6.4) + '@inquirer/prompts': 8.7.4(@types/node@26.6.4) + '@inquirer/type': 4.2.0(@types/node@26.6.4) + mute-stream: 3.0.0 run-async: 4.0.6 - rxjs: 7.8.2 optionalDependencies: '@types/node': 26.6.4 @@ -12550,8 +12453,6 @@ snapshots: isexe@2.0.0: {} - isexe@4.0.0: {} - istanbul-lib-coverage@3.2.2: {} istanbul-lib-instrument@6.0.3: @@ -12964,8 +12865,6 @@ snapshots: json-parse-even-better-errors@2.3.1: {} - json-parse-even-better-errors@3.0.2: {} - json-schema-ref-resolver@3.0.0: dependencies: dequal: 2.0.3 @@ -13143,8 +13042,6 @@ snapshots: lines-and-columns@1.2.4: {} - lines-and-columns@2.0.4: {} - lit-element@4.2.2: dependencies: '@lit-labs/ssr-dom-shim': 1.6.0 @@ -13192,10 +13089,6 @@ snapshots: dependencies: p-locate: 5.0.0 - locate-path@7.2.0: - dependencies: - p-locate: 6.0.0 - lodash.clonedeep@4.5.0: {} lodash.defaults@4.2.0: {} @@ -13402,7 +13295,7 @@ snapshots: ms@2.1.3: {} - mute-stream@2.0.0: {} + mute-stream@3.0.0: {} mz@2.7.0: dependencies: @@ -13492,6 +13385,8 @@ snapshots: touch: 3.1.1 undefsafe: 2.0.5 + non-error@0.1.0: {} + normalize-package-data@2.5.0: dependencies: hosted-git-info: 2.8.9 @@ -13499,18 +13394,6 @@ snapshots: semver: 5.7.2 validate-npm-package-license: 3.0.4 - normalize-package-data@6.0.2: - dependencies: - hosted-git-info: 7.0.2 - semver: 7.8.5 - validate-npm-package-license: 3.0.4 - - normalize-package-data@7.0.1: - dependencies: - hosted-git-info: 8.1.0 - semver: 7.8.5 - validate-npm-package-license: 3.0.4 - normalize-package-data@8.0.0: dependencies: hosted-git-info: 9.0.3 @@ -13649,10 +13532,6 @@ snapshots: dependencies: yocto-queue: 0.1.0 - p-limit@4.0.0: - dependencies: - yocto-queue: 1.2.2 - p-locate@4.1.0: dependencies: p-limit: 2.3.0 @@ -13661,10 +13540,6 @@ snapshots: dependencies: p-limit: 3.1.0 - p-locate@6.0.0: - dependencies: - p-limit: 4.0.0 - p-try@2.2.0: {} package-json-from-dist@1.0.1: {} @@ -13693,14 +13568,6 @@ snapshots: json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 - parse-json@7.1.1: - dependencies: - '@babel/code-frame': 7.29.7 - error-ex: 1.3.4 - json-parse-even-better-errors: 3.0.2 - lines-and-columns: 2.0.4 - type-fest: 3.13.1 - parse-json@8.3.0: dependencies: '@babel/code-frame': 7.29.7 @@ -13726,8 +13593,6 @@ snapshots: path-exists@4.0.0: {} - path-exists@5.0.0: {} - path-is-absolute@1.0.1: {} path-key@2.0.1: {} @@ -13907,6 +13772,8 @@ snapshots: property-expr@2.0.6: {} + proxy-agent-negotiate@1.1.0: {} + psl@1.15.0: dependencies: punycode: 2.3.1 @@ -13955,12 +13822,6 @@ snapshots: read-pkg: 10.1.0 type-fest: 5.8.0 - read-pkg-up@10.1.0: - dependencies: - find-up: 6.3.0 - read-pkg: 8.1.0 - type-fest: 4.41.0 - read-pkg@10.1.0: dependencies: '@types/normalize-package-data': 2.4.4 @@ -13975,13 +13836,6 @@ snapshots: normalize-package-data: 2.5.0 path-type: 3.0.0 - read-pkg@8.1.0: - dependencies: - '@types/normalize-package-data': 2.4.4 - normalize-package-data: 6.0.2 - parse-json: 7.1.1 - type-fest: 4.41.0 - readable-stream@2.3.8: dependencies: core-util-is: 1.0.3 @@ -14174,11 +14028,7 @@ snapshots: dependencies: queue-microtask: 1.2.3 - rxjs@7.8.2: - dependencies: - tslib: 2.8.1 - - safaridriver@2.0.0: {} + safaridriver@3.0.0: {} safe-array-concat@1.1.4: dependencies: @@ -14262,9 +14112,10 @@ snapshots: semver@7.8.5: {} - serialize-error@12.0.0: + serialize-error@13.0.2: dependencies: - type-fest: 4.41.0 + non-error: 0.1.0 + type-fest: 5.8.0 serialize-javascript@6.0.2: dependencies: @@ -14671,6 +14522,12 @@ snapshots: glob: 10.5.0 minimatch: 10.2.6 + test-exclude@8.0.0: + dependencies: + '@istanbuljs/schema': 0.1.6 + glob: 13.0.6 + minimatch: 10.2.6 + text-decoder@1.2.7: dependencies: b4a: 1.9.0 @@ -14825,12 +14682,14 @@ snapshots: type-fest@2.19.0: {} - type-fest@3.13.1: {} - type-fest@4.26.0: {} type-fest@4.41.0: {} + type-fest@5.10.0: + dependencies: + tagged-tag: 1.0.0 + type-fest@5.8.0: dependencies: tagged-tag: 1.0.0 @@ -15083,16 +14942,16 @@ snapshots: - typescript - webpack - vite-plugin-istanbul@7.2.1(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1)): + vite-plugin-istanbul@9.0.1(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1)): dependencies: '@babel/generator': 7.29.8 '@istanbuljs/load-nyc-config': 1.1.0 - '@types/babel__generator': 7.6.8 - espree: 10.4.0 + '@types/babel__generator': 7.27.0 + espree: 11.2.0 istanbul-lib-instrument: 6.0.3 picocolors: 1.1.1 source-map: 0.7.6 - test-exclude: 7.0.2 + test-exclude: 8.0.0 vite: 8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1) transitivePeerDependencies: - supports-color @@ -15174,36 +15033,37 @@ snapshots: dependencies: defaults: 1.0.4 - webdriver@10.0.2(yauzl@2.10.0): + webdriver@10.1.0(yauzl@2.10.0): dependencies: '@types/node': 26.6.4 '@types/ws': 8.18.2 - '@wdio/config': 10.0.1(yauzl@2.10.0) - '@wdio/logger': 10.0.0 + '@wdio/config': 10.1.0(yauzl@2.10.0) + '@wdio/logger': 10.1.0 '@wdio/protocols': 10.0.2 - '@wdio/types': 10.0.1 - '@wdio/utils': 10.0.1(yauzl@2.10.0) + '@wdio/types': 10.1.0 + '@wdio/utils': 10.1.0(yauzl@2.10.0) deepmerge-ts: 8.0.2 - https-proxy-agent: 7.0.6 + https-proxy-agent: 9.1.0 undici: 8.11.2 ws: 8.22.0 transitivePeerDependencies: - bufferutil + - kerberos - proxy-agent - supports-color - utf-8-validate - yauzl - webdriverio@10.0.2(yauzl@2.10.0): + webdriverio@10.1.0(yauzl@2.10.0): dependencies: '@types/node': 26.6.4 - '@types/sinonjs__fake-timers': 8.1.5 - '@wdio/config': 10.0.1(yauzl@2.10.0) - '@wdio/logger': 10.0.0 + '@types/sinonjs__fake-timers': 15.0.1 + '@wdio/config': 10.1.0(yauzl@2.10.0) + '@wdio/logger': 10.1.0 '@wdio/protocols': 10.0.2 '@wdio/repl': 10.0.0 - '@wdio/types': 10.0.1 - '@wdio/utils': 10.0.1(yauzl@2.10.0) + '@wdio/types': 10.1.0 + '@wdio/utils': 10.1.0(yauzl@2.10.0) archiver: 8.0.0 aria-query: 5.3.2 cheerio: 1.2.0 @@ -15216,13 +15076,14 @@ snapshots: lodash.clonedeep: 4.5.0 query-selector-shadow-dom: 1.0.1 rgb2hex: 0.2.5 - serialize-error: 12.0.0 + serialize-error: 13.0.2 urlpattern-polyfill: 10.1.0 - webdriver: 10.0.2(yauzl@2.10.0) + webdriver: 10.1.0(yauzl@2.10.0) transitivePeerDependencies: - bare-abort-controller - bare-buffer - bufferutil + - kerberos - proxy-agent - react-native-b4a - supports-color @@ -15277,6 +15138,8 @@ snapshots: is-weakmap: 2.0.2 is-weakset: 2.0.4 + which-command@0.1.0: {} + which-typed-array@1.1.21: dependencies: available-typed-arrays: 1.0.7 @@ -15295,10 +15158,6 @@ snapshots: dependencies: isexe: 2.0.0 - which@6.0.1: - dependencies: - isexe: 4.0.0 - why-is-node-running@3.2.1: {} widest-line@3.1.0: @@ -15311,12 +15170,6 @@ snapshots: workerpool@6.5.1: {} - wrap-ansi@6.2.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 @@ -15420,10 +15273,6 @@ snapshots: yocto-queue@0.1.0: {} - yocto-queue@1.2.2: {} - - yoctocolors-cjs@2.1.3: {} - yoctocolors@2.2.0: {} yup@1.7.1: From 3002cba2d7b8cbc32a3c2435f76774003612cb6f Mon Sep 17 00:00:00 2001 From: Vince Graics <wince17@gmail.com> Date: Tue, 20 Oct 2026 08:54:29 +0200 Subject: [PATCH 11/12] test(conformance): answer only a GET of /timeouts with the defaults Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --- packages/conformance/src/fake-webdriver.ts | 4 +++- packages/conformance/tests/fakes.test.ts | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/conformance/src/fake-webdriver.ts b/packages/conformance/src/fake-webdriver.ts index d30eda8f..bf59569d 100644 --- a/packages/conformance/src/fake-webdriver.ts +++ b/packages/conformance/src/fake-webdriver.ts @@ -54,7 +54,6 @@ type Reply = { const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)) const FIXED_REPLIES: Record<string, unknown> = { - '/timeouts': DEFAULT_TIMEOUTS, '/screenshot': BLANK_PNG, '/window/rect': { x: 0, y: 0, width: 1280, height: 720 }, '/window': WINDOW_HANDLE, @@ -215,6 +214,9 @@ export class FakeWebDriver { if (method === 'POST' && (path === '/element' || path === '/elements')) { return this.#find(String(params.value), path === '/elements') } + if (method === 'GET' && path === '/timeouts') { + return { value: DEFAULT_TIMEOUTS } + } const element = path.match(/^\/element\/([^/]+)\/(value|click|clear|text)$/) if (element) { return this.#onElement(element[1], element[2], params) diff --git a/packages/conformance/tests/fakes.test.ts b/packages/conformance/tests/fakes.test.ts index ee68feec..adcc22f4 100644 --- a/packages/conformance/tests/fakes.test.ts +++ b/packages/conformance/tests/fakes.test.ts @@ -165,6 +165,11 @@ describe('the fake WebDriver', () => { pageLoad: 300_000, implicit: 0 }) + expect( + await call(driver, 'POST', '/session/conformance/timeouts', { + implicit: 5 + }) + ).toBeNull() }) }) From 12dd905d8c2aadef8abc7733ee3f322a40c3c361 Mon Sep 17 00:00:00 2001 From: Vince Graics <wince17@gmail.com> Date: Tue, 20 Oct 2026 08:54:29 +0200 Subject: [PATCH 12/12] chore(changeset): note the WebdriverIO 10.1.0 runtime lines Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --- .changeset/wdio-10-1.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/wdio-10-1.md diff --git a/.changeset/wdio-10-1.md b/.changeset/wdio-10-1.md new file mode 100644 index 00000000..e3314dc5 --- /dev/null +++ b/.changeset/wdio-10-1.md @@ -0,0 +1,7 @@ +--- +"@wdio/devtools-backend": patch +"@wdio/selenium-devtools": patch +"@wdio/nightwatch-devtools": patch +--- + +The `@wdio/logger` each package depends on, and the `webdriverio` the Selenium and Nightwatch adapters depend on, move to their 10.1.0 lines (`^10.1.0`), alongside the rest of the WebdriverIO toolchain the repository now builds and tests against. An install that pinned either to 10.0.x resolves 10.1.x from now on.