@wdio/nightwatch-devtools 1.4.0 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +167 -104
- package/dist/index.js.map +1 -1
- package/package.json +7 -3
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../../core/src/action-mapping.ts","../../core/src/element-scripts.ts","../../core/src/locators/xml-parsing.ts","../../core/src/locators/constants.ts","../../core/src/locators/locator-generation.ts","../../core/src/element-snapshot.ts","../../core/src/locators/element-filter.ts","../../core/src/locators/index.ts","../../core/src/with-timeout.ts","../../core/src/action-snapshot.ts","../../core/src/stack.ts","../../core/src/trace-exporter.ts","../../core/src/trace-har.ts","../../core/src/trace-zip-writer.ts","../../core/src/uid.ts","../../core/src/finalize-screencast.ts","../../core/src/video-encoder.ts","../../core/src/output-dir.ts","../../core/src/performance-capture.ts","../../core/src/retry-tracker.ts","../../shared/src/files.ts","../../shared/src/routes.ts","../../shared/src/runner.ts","../../shared/src/timing.ts","../../shared/src/types.ts","../../core/src/screencast.ts","../../core/src/script-loader.ts","../../core/src/session-capturer.ts","../../core/src/spec-trace-helpers.ts","../../core/src/suite-helpers.ts","../../core/src/test-discovery.ts","../../core/src/test-reporter.ts","../src/run-lifecycle.ts","../src/constants.ts","../src/helpers/utils.ts","../src/event-hub.ts","../src/cucumber-lifecycle.ts","../src/helpers/cucumberResult.ts","../src/helpers/cucumberScenarioBuilder.ts","../src/helpers/featureFileScan.ts","../src/test-lifecycle.ts","../src/helpers/specFileResolver.ts","../src/helpers/closePreviousTest.ts","../src/session-init.ts","../src/session.ts","../src/action-snapshot.ts","../src/helpers/perfLogs.ts","../src/reporter.ts","../src/helpers/testManager.ts","../src/helpers/suiteManager.ts","../src/helpers/browserProxy.ts","../src/helpers/serializeCommandResult.ts","../src/screencast.ts"],"sourcesContent":["/**\n * Nightwatch DevTools Plugin\n *\n * Integrates Nightwatch with WebdriverIO DevTools following the WDIO service pattern.\n * Captures commands, network requests, and console logs during test execution in real-time.\n */\n\nimport { fileURLToPath } from 'node:url'\nimport {\n errorMessage,\n recordSpecBoundary,\n resolveAdapterOutputDir,\n writeSpecTrace,\n writeTraceZip,\n type SpecRange\n} from '@wdio/devtools-core'\nimport { stop as stopBackend } from '@wdio/devtools-backend'\nimport { REUSE_ENV, SCREENCAST_DEFAULTS } from '@wdio/devtools-shared'\nimport logger from '@wdio/logger'\nimport {\n handleReuseMode,\n openDevtoolsBrowser,\n finalizeAllSuites,\n logRunSummary,\n waitForDevtoolsBrowserClose,\n runPluginBefore,\n type PluginBeforeCtx\n} from './run-lifecycle.js'\nimport type { PluginInternals } from './plugin-internals.js'\nimport type { SessionCapturer } from './session.js'\nimport type { TestReporter } from './reporter.js'\nimport type { ScreencastRecorder } from './screencast.js'\nimport type { TestManager } from './helpers/testManager.js'\nimport type { SuiteManager } from './helpers/suiteManager.js'\nimport type { BrowserProxy } from './helpers/browserProxy.js'\nimport type {\n DevToolsOptions,\n NightwatchBrowser,\n NightwatchCurrentTest,\n NightwatchEventHub,\n ScreencastOptions,\n SuiteStats,\n TestStats,\n TestMetadataMap\n} from './types.js'\nimport { registerEventHandlers as registerEventHandlersImpl } from './event-hub.js'\nimport {\n cucumberBefore as cucumberLifecycleBefore,\n cucumberAfter as cucumberLifecycleAfter,\n cucumberBeforeStep as cucumberLifecycleBeforeStep,\n cucumberAfterStep as cucumberLifecycleAfterStep,\n type CucumberPickle,\n type CucumberPickleStep,\n type CucumberResult\n} from './cucumber-lifecycle.js'\nimport {\n resolveSuiteMetadata,\n pickCurrentTestName,\n startNextTest,\n closePreviousRunningTest,\n wrapBrowserOnce,\n closeOutTestcases\n} from './test-lifecycle.js'\nimport {\n ensureSessionInitialized,\n finalizeCurrentScreencast\n} from './session-init.js'\nimport {\n getTestIcon,\n incrementCounters,\n buildPluginMetadataOptions\n} from './helpers/utils.js'\n\nconst log = logger('@wdio/nightwatch-devtools')\n\nclass NightwatchDevToolsPlugin {\n private options: Required<DevToolsOptions>\n private sessionCapturer!: SessionCapturer\n private testReporter!: TestReporter\n private testManager!: TestManager\n private suiteManager!: SuiteManager\n private browserProxy!: BrowserProxy\n private isScriptInjected = false\n #currentTest: unknown = null\n #currentScenarioSuite: SuiteStats | null = null\n #currentStep: unknown = null\n #lastSessionId: string | null = null\n #devtoolsBrowser?: WebdriverIO.Browser\n #userDataDir?: string\n #isCucumberRunner = false\n #passCount = 0\n #failCount = 0\n #skipCount = 0\n #configPath: string | undefined\n #srcFolders: string[] = []\n\n /** Index ranges into the session capturer's flat arrays, one per spec file. */\n #specRanges: SpecRange[] = []\n\n /** Set of spec files already flushed to disk. */\n #flushedSpecs = new Set<string>()\n\n #getRerunLabel() {\n return process.env[REUSE_ENV.RERUN_ENTRY_TYPE] === 'test'\n ? process.env[REUSE_ENV.RERUN_LABEL]?.trim()\n : undefined\n }\n\n #screencastOptions: ScreencastOptions\n #screencastRecorder?: ScreencastRecorder\n #screencastSessionId?: string\n #bidiEnabled = false\n #bidiAttachAttempted = false\n\n constructor(options: DevToolsOptions = {}) {\n const mode = options.mode ?? 'live'\n const ignore = mode === 'trace' && options.screencast?.enabled === true\n if (ignore) {\n log.warn('trace mode: ignoring screencast option (live-mode feature)')\n }\n const screencast = ignore ? {} : (options.screencast ?? {})\n this.options = {\n port: options.port ?? 3000,\n hostname: options.hostname ?? 'localhost',\n screencast,\n bidi: options.bidi ?? false,\n mode,\n traceFormat: options.traceFormat ?? 'zip',\n traceGranularity: options.traceGranularity ?? 'session'\n }\n this.#screencastOptions = { ...SCREENCAST_DEFAULTS, ...screencast }\n this.#bidiEnabled = options.bidi === true\n }\n\n // Single internals \"bag\" — structurally satisfies all 4 lifecycle ctx\n // interfaces. Lifecycle modules cast it to their narrow type at call time.\n #internals: PluginInternals | undefined\n // Declarative accessor map — splitting this purely to satisfy the\n // line-count rule hurts readability; the body is mechanical wiring.\n // eslint-disable-next-line max-lines-per-function\n #getInternals(): PluginInternals {\n if (this.#internals) {\n return this.#internals\n }\n const self = this\n this.#internals = {\n get options() {\n return self.options\n },\n get hostname() {\n return self.options.hostname\n },\n get port() {\n return self.options.port\n },\n get mode() {\n return self.options.mode\n },\n get screencastOptions() {\n return self.#screencastOptions\n },\n get bidiEnabled() {\n return self.#bidiEnabled\n },\n get sessionCapturer() {\n return self.sessionCapturer\n },\n set sessionCapturer(v) {\n self.sessionCapturer = v\n },\n get testReporter() {\n return self.testReporter\n },\n set testReporter(v) {\n self.testReporter = v\n },\n get testManager() {\n return self.testManager\n },\n set testManager(v) {\n self.testManager = v\n },\n get suiteManager() {\n return self.suiteManager\n },\n set suiteManager(v) {\n self.suiteManager = v\n },\n get browserProxy() {\n return self.browserProxy\n },\n set browserProxy(v) {\n self.browserProxy = v\n },\n get isScriptInjected() {\n return self.isScriptInjected\n },\n set isScriptInjected(v) {\n self.isScriptInjected = v\n },\n get devtoolsBrowser() {\n return self.#devtoolsBrowser\n },\n set devtoolsBrowser(v) {\n self.#devtoolsBrowser = v\n },\n get userDataDir() {\n return self.#userDataDir\n },\n set userDataDir(v) {\n self.#userDataDir = v\n },\n get passCount() {\n return self.#passCount\n },\n set passCount(v) {\n self.#passCount = v\n },\n get failCount() {\n return self.#failCount\n },\n set failCount(v) {\n self.#failCount = v\n },\n get skipCount() {\n return self.#skipCount\n },\n set skipCount(v) {\n self.#skipCount = v\n },\n get lastSessionId() {\n return self.#lastSessionId\n },\n set lastSessionId(v) {\n self.#lastSessionId = v\n },\n get bidiAttachAttempted() {\n return self.#bidiAttachAttempted\n },\n set bidiAttachAttempted(v) {\n self.#bidiAttachAttempted = v\n },\n get srcFolders() {\n return self.#srcFolders\n },\n set srcFolders(v) {\n self.#srcFolders = v\n },\n get screencastRecorder() {\n return self.#screencastRecorder\n },\n set screencastRecorder(v) {\n self.#screencastRecorder = v\n },\n get screencastSessionId() {\n return self.#screencastSessionId\n },\n set screencastSessionId(v) {\n self.#screencastSessionId = v\n },\n get configPath() {\n return self.#configPath\n },\n getCurrentTest: () => self.#currentTest,\n getCurrentScenarioSuite: () => self.#currentScenarioSuite,\n getCurrentStep: () => self.#currentStep,\n setCurrentTest: (t) => {\n self.#currentTest = t\n },\n setCurrentScenarioSuite: (s) => {\n self.#currentScenarioSuite = s\n },\n setCurrentStep: (s) => {\n self.#currentStep = s\n },\n clearExecutionData: () => {\n self.testReporter.clearExecutionData()\n self.suiteManager.clearExecutionData()\n },\n buildMetadataOptions: () => self.#buildMetadataOptions(),\n ensureSessionInitialized: (b) => self.#ensureSessionInitialized(b),\n wrapBrowserOnce: (b) => self.#wrapBrowserOnce(b),\n incrementCount: (s) => self.#incrementCount(s),\n testIcon: (s) => self.#testIcon(s),\n setCucumberRunner: (v) => {\n self.#isCucumberRunner = v\n },\n getRerunLabel: () => self.#getRerunLabel()\n }\n return this.#internals\n }\n\n #handleReuseMode(): void {\n handleReuseMode(this.#getInternals())\n }\n\n async #openDevtoolsBrowser(url: string): Promise<void> {\n await openDevtoolsBrowser(this.#getInternals(), url)\n }\n\n async before() {\n const internals = this.#getInternals() as unknown as PluginBeforeCtx\n internals.setConfigPath = (v) => {\n this.#configPath = v\n }\n internals.openDevtoolsBrowserAt = (url) => this.#openDevtoolsBrowser(url)\n internals.handleReuse = () => this.#handleReuseMode()\n internals.plugin = this\n await runPluginBefore(internals)\n }\n\n async #ensureSessionInitialized(browser: NightwatchBrowser) {\n await ensureSessionInitialized(this.#getInternals(), browser, () =>\n this.#finalizeCurrentScreencast()\n )\n }\n\n async #finalizeCurrentScreencast(): Promise<void> {\n await finalizeCurrentScreencast(this.#getInternals())\n }\n\n async cucumberBefore(browser: NightwatchBrowser, pickle: CucumberPickle) {\n await cucumberLifecycleBefore(this.#getInternals(), browser, pickle)\n }\n\n async cucumberAfter(\n browser: NightwatchBrowser,\n result: CucumberResult,\n pickle: CucumberPickle\n ) {\n await cucumberLifecycleAfter(this.#getInternals(), browser, result, pickle)\n }\n\n async cucumberBeforeStep(\n browser: NightwatchBrowser,\n pickleStep: CucumberPickleStep,\n pickle: CucumberPickle\n ) {\n await cucumberLifecycleBeforeStep(\n this.#getInternals(),\n browser,\n pickleStep,\n pickle\n )\n }\n\n async cucumberAfterStep(\n browser: NightwatchBrowser,\n result: CucumberResult,\n pickleStep: CucumberPickleStep,\n pickle: CucumberPickle\n ) {\n await cucumberLifecycleAfterStep(\n this.#getInternals(),\n browser,\n result,\n pickleStep,\n pickle\n )\n }\n\n #resolveSuiteMetadata(currentTest: NightwatchCurrentTest) {\n return resolveSuiteMetadata(this.#getInternals(), currentTest)\n }\n\n #pickCurrentTestName(\n currentTest: NightwatchCurrentTest,\n testNames: string[],\n processedTests: Set<string>\n ): string | undefined {\n return pickCurrentTestName(currentTest, testNames, processedTests)\n }\n\n async #startNextTest(\n currentSuite: SuiteStats,\n currentTestName: string,\n processedTests: Set<string>\n ): Promise<void> {\n await startNextTest(\n this.#getInternals(),\n currentSuite,\n currentTestName,\n processedTests\n )\n }\n\n async #closePreviousRunningTest(\n currentSuite: SuiteStats,\n testFile: string,\n currentTest: NightwatchCurrentTest\n ): Promise<void> {\n await closePreviousRunningTest(\n this.#getInternals(),\n currentSuite,\n testFile,\n currentTest\n )\n }\n\n #wrapBrowserOnce(browser: NightwatchBrowser): void {\n wrapBrowserOnce(this.#getInternals(), browser)\n }\n\n async beforeEach(browser: NightwatchBrowser) {\n if (this.#isCucumberRunner) {\n return\n }\n await this.#ensureSessionInitialized(browser)\n\n const currentTest = browser.currentTest as NightwatchCurrentTest | undefined\n if (!currentTest) {\n return\n }\n\n const { testFile, fullPath, suiteTitle, testNames, suiteLine, testLines } =\n this.#resolveSuiteMetadata(currentTest)\n const currentSuite = this.suiteManager.getOrCreateSuite(\n testFile,\n suiteTitle,\n fullPath,\n testNames,\n suiteLine,\n testLines\n )\n if (fullPath && fullPath.includes('/')) {\n this.sessionCapturer.captureSource(fullPath).catch(() => {})\n }\n\n // ── Per-spec boundary detection ──\n if (fullPath) {\n const prevRange = recordSpecBoundary(\n {\n specRanges: this.#specRanges,\n flushedSpecs: this.#flushedSpecs,\n capturer: this.sessionCapturer,\n actionSnapshots: this.sessionCapturer.actionSnapshots\n },\n fullPath,\n this.options.traceGranularity\n )\n if (prevRange) {\n void this.#flushSpecTrace(prevRange).catch((err) =>\n log.warn(\n `Failed to flush trace for spec \"${prevRange.specFile}\": ${errorMessage(err)}`\n )\n )\n }\n }\n\n await this.#closePreviousRunningTest(currentSuite, testFile, currentTest)\n\n const processedTests = this.testManager.getProcessedTests(testFile)\n const currentTestName = this.#pickCurrentTestName(\n currentTest,\n testNames,\n processedTests\n )\n if (currentTestName) {\n await this.#startNextTest(currentSuite, currentTestName, processedTests)\n }\n this.#wrapBrowserOnce(browser)\n }\n\n async afterEach(browser: NightwatchBrowser) {\n // Cucumber runner manages its own lifecycle via cucumberHooks.cjs\n if (this.#isCucumberRunner) {\n return\n }\n\n if (browser && this.sessionCapturer) {\n try {\n await this.#closeOutTestcases(browser)\n await this.sessionCapturer.captureTrace(browser)\n } catch (err) {\n log.error(`Failed to capture trace: ${errorMessage(err)}`)\n }\n }\n }\n\n async #closeOutTestcases(browser: NightwatchBrowser): Promise<void> {\n await closeOutTestcases(this.#getInternals(), browser)\n }\n\n async after(browser?: NightwatchBrowser) {\n await this.#finalizeCurrentScreencast()\n try {\n await this.#finalizeAllSuites(browser)\n this.#logRunSummary()\n if (this.options.mode === 'trace') {\n await this.#writeTraceZipIfNeeded()\n await this.sessionCapturer?.closeWebSocket()\n await stopBackend()\n return\n }\n if (!this.#devtoolsBrowser) {\n // Reuse mode: force one final suites broadcast so the UI reflects the\n // actual outcome before the process exits.\n this.testReporter?.updateSuites()\n log.info('♻ Rerun complete — flushing WebSocket...')\n await this.sessionCapturer?.closeWebSocket()\n return\n }\n log.info('💡 Please close the DevTools browser window to finish...')\n await this.#waitForDevtoolsBrowserClose()\n } catch (err) {\n log.error(`Failed to stop backend: ${errorMessage(err)}`)\n }\n }\n\n async #finalizeAllSuites(browser?: NightwatchBrowser): Promise<void> {\n await finalizeAllSuites(this.#getInternals(), browser)\n }\n\n #logRunSummary(): void {\n logRunSummary(this.#getInternals())\n }\n\n async #flushSpecTrace(\n range: SpecRange,\n nextRange?: SpecRange\n ): Promise<string | undefined> {\n if (this.#flushedSpecs.has(range.specFile)) {\n return undefined\n }\n this.#flushedSpecs.add(range.specFile)\n\n const sessionId = this.sessionCapturer.metadata?.sessionId\n if (!sessionId) {\n return undefined\n }\n\n // Collect test metadata from the suite tree. Nightwatch stores one suite\n // per spec file (flat data model) — a flat iteration is correct. If\n // child suites are ever introduced, this must switch to a recursive walk\n // matching the selenium adapter.\n const allMetadata: TestMetadataMap = new Map()\n if (this.suiteManager) {\n for (const suite of this.suiteManager.getAllSuites().values()) {\n for (const entry of suite.tests) {\n if (typeof entry === 'string') {\n continue\n }\n allMetadata.set(entry.uid, {\n title: entry.fullTitle,\n specFile: entry.file\n })\n }\n }\n }\n\n const tracePath = await writeSpecTrace({\n range,\n nextRange,\n capturer: this.sessionCapturer,\n actionSnapshots: this.sessionCapturer.actionSnapshots,\n sessionId,\n outputDir: resolveAdapterOutputDir({ configPath: this.#configPath }),\n format: this.options.traceFormat,\n testMetadata: allMetadata\n })\n log.info(`Trace for spec \"${range.specFile}\" saved to ${tracePath}`)\n return tracePath\n }\n\n async #writeTraceZipIfNeeded(): Promise<void> {\n if (this.options.mode !== 'trace' || !this.sessionCapturer) {\n return\n }\n const sessionId = this.sessionCapturer.metadata?.sessionId\n if (!sessionId) {\n return\n }\n try {\n if (this.sessionCapturer.snapshotCaptures.length) {\n await Promise.allSettled(this.sessionCapturer.snapshotCaptures)\n }\n\n if (this.options.traceGranularity === 'spec') {\n // Per-spec traces — flush any remaining ranges that weren't\n // flushed at spec boundaries (the last spec in the run).\n for (const range of this.#specRanges) {\n if (!this.#flushedSpecs.has(range.specFile)) {\n await this.#flushSpecTrace(range)\n }\n }\n return\n }\n\n // Session-level trace (default) — single artifact for the\n // entire worker session.\n const testMetadata: TestMetadataMap = new Map()\n if (this.suiteManager) {\n for (const suite of this.suiteManager.getAllSuites().values()) {\n for (const entry of suite.tests) {\n if (typeof entry === 'string') {\n continue\n }\n testMetadata.set(entry.uid, {\n title: entry.fullTitle,\n specFile: entry.file\n })\n }\n }\n }\n\n const snapshots = this.sessionCapturer.actionSnapshots\n const tracePath = await writeTraceZip(this.sessionCapturer, {\n outputDir: resolveAdapterOutputDir({\n configPath: this.#configPath\n }),\n sessionId,\n actionSnapshots: snapshots.length ? snapshots : undefined,\n format: this.options.traceFormat,\n testMetadata\n })\n log.info(`Trace saved to ${tracePath}`)\n } catch (err) {\n log.warn(`trace write failed: ${errorMessage(err)}`)\n }\n }\n\n async #waitForDevtoolsBrowserClose(): Promise<void> {\n await waitForDevtoolsBrowserClose(this.#getInternals())\n }\n\n #buildMetadataOptions() {\n return buildPluginMetadataOptions({\n isCucumberRunner: this.#isCucumberRunner,\n configPath: this.#configPath\n })\n }\n\n #incrementCount(state: TestStats['state']): void {\n const counters = {\n passCount: this.#passCount,\n failCount: this.#failCount,\n skipCount: this.#skipCount\n }\n incrementCounters(counters, state)\n this.#passCount = counters.passCount\n this.#failCount = counters.failCount\n this.#skipCount = counters.skipCount\n }\n\n #testIcon(state: TestStats['state']): string {\n return getTestIcon(state)\n }\n\n registerEventHandlers(eventHub: NightwatchEventHub): void {\n registerEventHandlersImpl(eventHub, {\n getSessionCapturer: () => this.sessionCapturer,\n buildMetadataOptions: () => this.#buildMetadataOptions(),\n setCucumberRunner: (v: boolean) => {\n this.#isCucumberRunner = v\n }\n })\n }\n}\n\nexport const cucumberHooksPath = fileURLToPath(\n new URL('./helpers/cucumberHooks.cjs', import.meta.url)\n)\n\nexport default function createNightwatchDevTools(options?: DevToolsOptions) {\n const plugin = new NightwatchDevToolsPlugin(options)\n\n return {\n // Set long timeout to allow user to review DevTools UI\n // The after() hook waits for the browser window to be closed\n asyncHookTimeout: 3600000,\n\n before: async function (this: unknown) {\n await plugin.before()\n },\n beforeEach: async function (this: unknown, browser: NightwatchBrowser) {\n await plugin.beforeEach(browser)\n },\n afterEach: async function (this: unknown, browser: NightwatchBrowser) {\n await plugin.afterEach(browser)\n },\n after: async function (this: unknown) {\n await plugin.after()\n },\n\n registerEventHandlers: function (eventHub: NightwatchEventHub) {\n plugin.registerEventHandlers(eventHub)\n }\n }\n}\n\nexport { NightwatchDevToolsPlugin }\n","// Allow-list mapping from runner-native command names to trace\n// vocabulary. Ported from Vince Graics' PR #209 (`@wdio/tracing-service`); the\n// existing devtools UI uses its own denylist (`INTERNAL_COMMANDS`) — this map\n// is for the trace.zip exporter to filter + rename in one step.\n\nexport interface TraceAction {\n class: string\n method: string\n}\n\nconst ACTION_MAP: Record<string, TraceAction> = {\n // WDIO browser-level\n url: { class: 'Page', method: 'navigate' },\n navigateTo: { class: 'Page', method: 'navigate' },\n back: { class: 'Page', method: 'goBack' },\n forward: { class: 'Page', method: 'goForward' },\n refresh: { class: 'Page', method: 'reload' },\n newWindow: { class: 'Page', method: 'goto' },\n // Selenium WebDriver navigation (driver.get, driver.navigate().to/back/forward/refresh)\n get: { class: 'Page', method: 'navigate' },\n to: { class: 'Page', method: 'navigate' },\n // WDIO element-level\n click: { class: 'Element', method: 'click' },\n doubleClick: { class: 'Element', method: 'dblclick' },\n setValue: { class: 'Element', method: 'fill' },\n selectByVisibleText: { class: 'Element', method: 'selectOption' },\n moveTo: { class: 'Element', method: 'hover' },\n scrollIntoView: { class: 'Element', method: 'scrollIntoViewIfNeeded' },\n dragAndDrop: { class: 'Element', method: 'dragTo' },\n // Selenium WebElement actions\n sendKeys: { class: 'Element', method: 'fill' },\n clear: { class: 'Element', method: 'clear' },\n submit: { class: 'Element', method: 'submit' },\n // Cross-runner\n keys: { class: 'Keyboard', method: 'press' },\n execute: { class: 'Page', method: 'evaluate' },\n executeAsync: { class: 'Page', method: 'evaluate' },\n switchToFrame: { class: 'Frame', method: 'goto' },\n touchAction: { class: 'Element', method: 'tap' }\n}\n\n// Excluded by design:\n// clearValue / addValue — WDIO fires these inside setValue (duplicate events).\n// executeScript — Selenium's `until` polling fires it ~50ms; also recurses\n// because @wdio/elements uses executeScript inside captureActionSnapshot.\n// WDIO's user-facing `execute`/`executeAsync` are still captured.\n\nexport function mapCommandToAction(command: string): TraceAction | null {\n return ACTION_MAP[command] ?? null\n}\n\nexport function formatActionTitle(\n action: TraceAction,\n args: unknown[],\n params?: Record<string, unknown>\n): string {\n const firstArg = args[0] ?? params?.selector\n if (firstArg === undefined) {\n return `${action.class}.${action.method}()`\n }\n const label =\n typeof firstArg === 'object' ? JSON.stringify(firstArg) : String(firstArg)\n return `${action.class}.${action.method}(\"${label}\")`\n}\n\n/**\n * Methods where the first positional argument should render as value= in the\n * transcript line (e.g. setValue, selectByVisibleText).\n */\nexport const FILL_METHODS = new Set(['fill', 'selectOption'])\n","/**\n * Browser-injectable script strings for element extraction.\n *\n * Each function returns a self-contained JavaScript string designed to run\n * inside a browser page via `browser.execute(script)`. The scripts have no\n * external dependencies and must be ES5-compatible.\n *\n * WDIO-dependent wrappers that call `browser.execute(script)` live in\n * `@wdio/elements` — these are just the script bodies.\n */\n\n/**\n * Accessibility tree walk — returns a flat array of AccessibilityNode.\n *\n * Walks the DOM from `document.body`, assigning semantic roles (button, link,\n * textbox, heading, img, statictext, …) based on tag name, ARIA attributes,\n * and visibility. Each node carries a unique CSS selector.\n */\nexport function accessibilityTreeScript(inViewportOnly: boolean): string {\n return `(function () {\n var INPUT_TYPE_ROLES = {\n text: 'textbox', search: 'searchbox', email: 'textbox', url: 'textbox',\n tel: 'textbox', password: 'textbox', number: 'spinbutton',\n checkbox: 'checkbox', radio: 'radio', range: 'slider',\n submit: 'button', reset: 'button', image: 'button', file: 'button', color: 'button'\n }\n\n var CONTAINER_ROLES = new Set([\n 'navigation', 'banner', 'contentinfo', 'complementary', 'main',\n 'form', 'region', 'group', 'list', 'listitem', 'table', 'row', 'rowgroup', 'generic'\n ])\n\n function getRole(el) {\n var explicit = el.getAttribute('role')\n if (explicit) { return explicit.split(' ')[0] }\n var tag = el.tagName.toLowerCase()\n switch (tag) {\n case 'button': return 'button'\n case 'a': return el.hasAttribute('href') ? 'link' : null\n case 'input': {\n var type = (el.getAttribute('type') || 'text').toLowerCase()\n if (type === 'hidden') { return null }\n return INPUT_TYPE_ROLES[type] || 'textbox'\n }\n case 'select': return 'combobox'\n case 'textarea': return 'textbox'\n case 'h1': case 'h2': case 'h3': case 'h4': case 'h5': case 'h6': return 'heading'\n case 'img': return 'img'\n case 'nav': return 'navigation'\n case 'main': return 'main'\n case 'header': return !el.closest('article,aside,main,nav,section') ? 'banner' : null\n case 'footer': return !el.closest('article,aside,main,nav,section') ? 'contentinfo' : null\n case 'aside': return 'complementary'\n case 'dialog': return 'dialog'\n case 'form': return 'form'\n case 'section': return el.hasAttribute('aria-label') || el.hasAttribute('aria-labelledby') ? 'region' : null\n case 'summary': return 'button'\n case 'details': return 'group'\n case 'progress': return 'progressbar'\n case 'meter': return 'meter'\n case 'ul': case 'ol': return 'list'\n case 'li': return 'listitem'\n case 'table': return 'table'\n }\n if (el.contentEditable === 'true') { return 'textbox' }\n if (el.hasAttribute('tabindex') && parseInt(el.getAttribute('tabindex') || '-1', 10) >= 0) { return 'generic' }\n if (getDirectText(el)) { return 'statictext' }\n return null\n }\n\n function getAccessibleName(el, role) {\n var ariaLabel = el.getAttribute('aria-label')\n if (ariaLabel) { return ariaLabel.trim() }\n var labelledBy = el.getAttribute('aria-labelledby')\n if (labelledBy) {\n var texts = labelledBy.split(/\\\\s+/).map(function(id) { return (document.getElementById(id)?.textContent || '').trim() }).filter(Boolean)\n if (texts.length > 0) { return texts.join(' ').slice(0, 200) }\n }\n var tag = el.tagName.toLowerCase()\n if (tag === 'img' || (tag === 'input' && el.getAttribute('type') === 'image')) {\n var alt = el.getAttribute('alt')\n if (alt !== null) { return alt.trim() }\n }\n if (['input', 'select', 'textarea'].indexOf(tag) !== -1) {\n var id = el.getAttribute('id')\n if (id) {\n var label = document.querySelector('label[for=\"' + CSS.escape(id) + '\"]')\n if (label) { return (label.textContent || '').trim() }\n }\n var parentLabel = el.closest('label')\n if (parentLabel) {\n var clone = parentLabel.cloneNode(true)\n clone.querySelectorAll('input,select,textarea').forEach(function(n) { n.remove() })\n var lt = (clone.textContent || '').trim()\n if (lt) { return lt }\n }\n }\n var ph = el.getAttribute('placeholder')\n if (ph) { return ph.trim() }\n var title = el.getAttribute('title')\n if (title) { return title.trim() }\n var childImg = el.querySelector('img')\n if (childImg) {\n var imgAlt = childImg.getAttribute('alt')\n if (imgAlt) { return imgAlt.trim() }\n }\n if (role && CONTAINER_ROLES.has(role)) { return '' }\n return ((el.textContent || '').trim().replace(/\\\\s+/g, ' ') || '').slice(0, 200)\n }\n\n function getSelector(element) {\n var tag = element.tagName.toLowerCase()\n var text = (element.textContent || '').trim().replace(/\\\\s+/g, ' ')\n if (text && text.length > 0 && text.length <= 120) {\n var sameTagElements = document.querySelectorAll(tag)\n var matchCount = 0\n sameTagElements.forEach(function(el) { if (el.textContent.includes(text)) { matchCount++ } })\n if (matchCount === 1) { return tag + '*=' + text }\n }\n var ariaLabel = element.getAttribute('aria-label')\n if (ariaLabel && ariaLabel.length <= 200) {\n var sel = '[aria-label=\"' + CSS.escape(ariaLabel) + '\"]'\n if (document.querySelectorAll(sel).length === 1) { return sel }\n }\n var testId = element.getAttribute('data-testid')\n if (testId) {\n var testSel = '[data-testid=\"' + CSS.escape(testId) + '\"]'\n if (document.querySelectorAll(testSel).length === 1) { return testSel }\n }\n if (element.id) { return '#' + CSS.escape(element.id) }\n var nameAttr = element.getAttribute('name')\n if (nameAttr) {\n var nameSel = tag + '[name=\"' + CSS.escape(nameAttr) + '\"]'\n if (document.querySelectorAll(nameSel).length === 1) { return nameSel }\n }\n if (element.className && typeof element.className === 'string') {\n var classes = element.className.trim().split(/\\\\s+/).filter(Boolean)\n for (var i = 0; i < classes.length; i++) {\n var clsSel = tag + '.' + CSS.escape(classes[i])\n if (document.querySelectorAll(clsSel).length === 1) { return clsSel }\n }\n if (classes.length >= 2) {\n var twoClsSel = tag + classes.slice(0, 2).map(function(c) { return '.' + CSS.escape(c) }).join('')\n if (document.querySelectorAll(twoClsSel).length === 1) { return twoClsSel }\n }\n }\n var current = element\n var path = []\n while (current && current !== document.documentElement) {\n var seg = current.tagName.toLowerCase()\n if (current.id) { path.unshift('#' + CSS.escape(current.id)); break }\n var parent = current.parentElement\n if (parent) {\n var siblings = Array.from(parent.children).filter(function(c) { return c.tagName === current.tagName })\n if (siblings.length > 1) { seg += ':nth-of-type(' + (siblings.indexOf(current) + 1) + ')' }\n }\n path.unshift(seg)\n current = current.parentElement\n if (path.length >= 4) { break }\n }\n return path.join(' > ')\n }\n\n function getDirectText(el) {\n var text = ''\n for (var i = 0; i < el.childNodes.length; i++) {\n if (el.childNodes[i].nodeType === 3) { text += el.childNodes[i].textContent }\n }\n return text.trim().replace(/\\\\s+/g, ' ')\n }\n\n function isVisible(el) {\n if (typeof el.checkVisibility === 'function') {\n return el.checkVisibility({ opacityProperty: true, visibilityProperty: true, contentVisibilityAuto: true })\n }\n var style = window.getComputedStyle(el)\n return style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0' && el.offsetWidth > 0 && el.offsetHeight > 0\n }\n\n function isInViewport(el) {\n var rect = el.getBoundingClientRect()\n return rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth)\n }\n\n function getLevel(el) {\n var m = el.tagName.toLowerCase().match(/^h([1-6])$/)\n if (m) { return parseInt(m[1], 10) }\n var ariaLevel = el.getAttribute('aria-level')\n if (ariaLevel) { return parseInt(ariaLevel, 10) }\n return undefined\n }\n\n function getState(el) {\n var inputEl = el\n var isCheckable = ['input', 'menuitemcheckbox', 'menuitemradio'].indexOf(el.tagName.toLowerCase()) !== -1 || ['checkbox', 'radio', 'switch'].indexOf(el.getAttribute('role') || '') !== -1\n return {\n disabled: el.getAttribute('aria-disabled') === 'true' || inputEl.disabled ? 'true' : '',\n checked: isCheckable && inputEl.checked ? 'true' : el.getAttribute('aria-checked') || '',\n expanded: el.getAttribute('aria-expanded') || '',\n selected: el.getAttribute('aria-selected') || '',\n pressed: el.getAttribute('aria-pressed') || '',\n required: inputEl.required || el.getAttribute('aria-required') === 'true' ? 'true' : '',\n readonly: inputEl.readOnly || el.getAttribute('aria-readonly') === 'true' ? 'true' : ''\n }\n }\n\n var result = []\n\n function walk(el, depth) {\n if (depth > 200) { return }\n if (!isVisible(el)) { return }\n var role = getRole(el)\n var inViewport = isInViewport(el)\n if (!role) {\n for (var i = 0; i < el.children.length; i++) { walk(el.children[i], depth + 1) }\n return\n }\n if (${inViewportOnly} && !inViewport) {\n for (var i = 0; i < el.children.length; i++) { walk(el.children[i], depth + 1) }\n return\n }\n var name = getAccessibleName(el, role)\n var selector = getSelector(el)\n var node = { role: role, name: name, selector: selector, depth: depth, level: getLevel(el) ?? '', isInViewport: inViewport }\n var state = getState(el)\n for (var k in state) { node[k] = state[k] }\n result.push(node)\n for (var i = 0; i < el.children.length; i++) { walk(el.children[i], depth + 1) }\n }\n\n for (var i = 0; i < document.body.children.length; i++) { walk(document.body.children[i], 0) }\n return result\n })()`\n}\n\n/**\n * Interactable element query — returns a flat array of BrowserElementInfo.\n *\n * Uses `querySelectorAll` with a broad interactable-selector list, then\n * filters by visibility and (optionally) viewport containment. Each element\n * gets a computed accessible name and a unique CSS selector.\n */\nexport function elementsScript(\n includeBounds: boolean,\n inViewportOnly: boolean\n): string {\n return `(function () {\n var interactableSelectors = [\n 'a[href]', 'button', 'input:not([type=\"hidden\"])', 'select', 'textarea',\n '[role=\"button\"]', '[role=\"link\"]', '[role=\"checkbox\"]', '[role=\"radio\"]',\n '[role=\"tab\"]', '[role=\"menuitem\"]', '[role=\"combobox\"]', '[role=\"option\"]',\n '[role=\"switch\"]', '[role=\"slider\"]', '[role=\"textbox\"]', '[role=\"searchbox\"]',\n '[role=\"spinbutton\"]', '[contenteditable=\"true\"]', '[tabindex]:not([tabindex=\"-1\"])'\n ].join(',')\n\n function isVisible(element) {\n if (typeof element.checkVisibility === 'function') {\n return element.checkVisibility({ opacityProperty: true, visibilityProperty: true, contentVisibilityAuto: true })\n }\n var style = window.getComputedStyle(element)\n return style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0' && element.offsetWidth > 0 && element.offsetHeight > 0\n }\n\n function getAccessibleName(el) {\n var ariaLabel = el.getAttribute('aria-label')\n if (ariaLabel) { return ariaLabel.trim() }\n var labelledBy = el.getAttribute('aria-labelledby')\n if (labelledBy) {\n var texts = labelledBy.split(/\\\\s+/).map(function(id) { return (document.getElementById(id)?.textContent || '').trim() }).filter(Boolean)\n if (texts.length > 0) { return texts.join(' ').slice(0, 200) }\n }\n var tag = el.tagName.toLowerCase()\n if (tag === 'img' || (tag === 'input' && el.getAttribute('type') === 'image')) {\n var alt = el.getAttribute('alt')\n if (alt !== null) { return alt.trim() }\n }\n if (['input', 'select', 'textarea'].indexOf(tag) !== -1) {\n var id = el.getAttribute('id')\n if (id) {\n var label = document.querySelector('label[for=\"' + CSS.escape(id) + '\"]')\n if (label) { return (label.textContent || '').trim() }\n }\n var parentLabel = el.closest('label')\n if (parentLabel) {\n var clone = parentLabel.cloneNode(true)\n clone.querySelectorAll('input,select,textarea').forEach(function(n) { n.remove() })\n var lt = (clone.textContent || '').trim()\n if (lt) { return lt }\n }\n }\n var ph = el.getAttribute('placeholder')\n if (ph) { return ph.trim() }\n var title = el.getAttribute('title')\n if (title) { return title.trim() }\n return ((el.textContent || '').trim().replace(/\\\\s+/g, ' ') || '').slice(0, 200)\n }\n\n function getSelector(element) {\n var tag = element.tagName.toLowerCase()\n var text = (element.textContent || '').trim().replace(/\\\\s+/g, ' ')\n if (text && text.length > 0 && text.length <= 120) {\n var sameTagElements = document.querySelectorAll(tag)\n var matchCount = 0\n sameTagElements.forEach(function(el) { if (el.textContent.includes(text)) { matchCount++ } })\n if (matchCount === 1) { return tag + '*=' + text }\n }\n var ariaLabel = element.getAttribute('aria-label')\n if (ariaLabel && ariaLabel.length <= 200) {\n var sel = '[aria-label=\"' + CSS.escape(ariaLabel) + '\"]'\n if (document.querySelectorAll(sel).length === 1) { return sel }\n }\n var testId = element.getAttribute('data-testid')\n if (testId) {\n var testSel = '[data-testid=\"' + CSS.escape(testId) + '\"]'\n if (document.querySelectorAll(testSel).length === 1) { return testSel }\n }\n if (element.id) { return '#' + CSS.escape(element.id) }\n var nameAttr = element.getAttribute('name')\n if (nameAttr) {\n var nameSel = tag + '[name=\"' + CSS.escape(nameAttr) + '\"]'\n if (document.querySelectorAll(nameSel).length === 1) { return nameSel }\n }\n if (element.className && typeof element.className === 'string') {\n var classes = element.className.trim().split(/\\\\s+/).filter(Boolean)\n for (var i = 0; i < classes.length; i++) {\n var clsSel = tag + '.' + CSS.escape(classes[i])\n if (document.querySelectorAll(clsSel).length === 1) { return clsSel }\n }\n if (classes.length >= 2) {\n var twoClsSel = tag + classes.slice(0, 2).map(function(c) { return '.' + CSS.escape(c) }).join('')\n if (document.querySelectorAll(twoClsSel).length === 1) { return twoClsSel }\n }\n }\n var current = element\n var path = []\n while (current && current !== document.documentElement) {\n var seg = current.tagName.toLowerCase()\n if (current.id) { path.unshift('#' + CSS.escape(current.id)); break }\n var parent = current.parentElement\n if (parent) {\n var siblings = Array.from(parent.children).filter(function(c) { return c.tagName === current.tagName })\n if (siblings.length > 1) { seg += ':nth-of-type(' + (siblings.indexOf(current) + 1) + ')' }\n }\n path.unshift(seg)\n current = current.parentElement\n if (path.length >= 4) { break }\n }\n return path.join(' > ')\n }\n\n var elements = []\n var seen = new Set()\n\n document.querySelectorAll(interactableSelectors).forEach(function(el) {\n if (seen.has(el)) { return }\n seen.add(el)\n var htmlEl = el\n if (!isVisible(htmlEl)) { return }\n var inputEl = htmlEl\n var rect = htmlEl.getBoundingClientRect()\n var isInVp = rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth)\n if (${inViewportOnly} && !isInVp) { return }\n var entry = {\n tagName: htmlEl.tagName.toLowerCase(),\n name: getAccessibleName(htmlEl),\n type: htmlEl.getAttribute('type') || '',\n value: inputEl.value || '',\n href: htmlEl.getAttribute('href') || '',\n selector: getSelector(htmlEl),\n isInViewport: isInVp\n }\n ${includeBounds ? 'entry.boundingBox = { x: rect.x + window.scrollX, y: rect.y + window.scrollY, width: rect.width, height: rect.height }' : ''}\n elements.push(entry)\n })\n return elements\n })()`\n}\n","/**\n * XML parsing utilities for mobile element source\n */\n\nimport { DOMParser } from '@xmldom/xmldom'\nimport type {\n Document as XMLDocument,\n Element as XMLElement,\n Node as XMLNode\n} from '@xmldom/xmldom'\nimport xpath from 'xpath'\nimport type {\n ElementAttributes,\n JSONElement,\n Bounds,\n UniquenessResult\n} from './types.js'\n\n/**\n * Get child nodes that are elements (not text nodes, comments, etc.)\n */\nfunction childNodesOf(node: XMLNode): XMLNode[] {\n const children: XMLNode[] = []\n if (node.childNodes) {\n for (let i = 0; i < node.childNodes.length; i++) {\n const child = node.childNodes.item(i)\n if (child?.nodeType === 1) {\n children.push(child)\n }\n }\n }\n return children\n}\n\n/**\n * Recursively translate DOM node to JSONElement\n */\nfunction translateRecursively(\n domNode: XMLNode,\n parentPath: string = '',\n index: number | null = null\n): JSONElement {\n const attributes: ElementAttributes = {}\n\n const element = domNode as XMLElement\n if (element.attributes) {\n for (let attrIdx = 0; attrIdx < element.attributes.length; attrIdx++) {\n const attr = element.attributes.item(attrIdx)\n if (attr) {\n attributes[attr.name] = attr.value.replace(/(\\n)/gm, '\\\\n')\n }\n }\n }\n\n const path =\n index === null ? '' : `${parentPath ? parentPath + '.' : ''}${index}`\n\n return {\n children: childNodesOf(domNode).map((childNode, childIndex) =>\n translateRecursively(childNode as XMLNode, path, childIndex)\n ),\n tagName: domNode.nodeName,\n attributes,\n path\n }\n}\n\n/**\n * Compare two nodes for equality by platform-specific attributes\n * (reference equality via === may fail when nodes come from different traversals)\n */\nfunction isSameElement(node1: XMLNode, node2: XMLNode): boolean {\n if (node1.nodeType !== 1 || node2.nodeType !== 1) {\n return false\n }\n const el1 = node1 as XMLElement\n const el2 = node2 as XMLElement\n\n if (el1.nodeName !== el2.nodeName) {\n return false\n }\n\n // For Android, compare by bounds (unique per element)\n const bounds1 = el1.getAttribute('bounds')\n const bounds2 = el2.getAttribute('bounds')\n if (bounds1 && bounds2) {\n return bounds1 === bounds2\n }\n\n // For iOS, compare by x, y, width, height\n const x1 = el1.getAttribute('x')\n const y1 = el1.getAttribute('y')\n const x2 = el2.getAttribute('x')\n const y2 = el2.getAttribute('y')\n if (x1 && y1 && x2 && y2) {\n return (\n x1 === x2 &&\n y1 === y2 &&\n el1.getAttribute('width') === el2.getAttribute('width') &&\n el1.getAttribute('height') === el2.getAttribute('height')\n )\n }\n\n return false\n}\n\n/**\n * Convert XML page source to JSON tree structure\n */\nexport function xmlToJSON(sourceXML: string): JSONElement | null {\n try {\n const parser = new DOMParser()\n const sourceDoc = parser.parseFromString(sourceXML, 'text/xml')\n\n // xmldom 0.9+ throws ParseError for fatal errors (caught below); this catches non-fatal cases\n const parseErrors = sourceDoc.getElementsByTagName('parsererror')\n if (parseErrors.length > 0) {\n console.error(\n '[xmlToJSON] XML parsing error:',\n parseErrors[0].textContent\n )\n return null\n }\n\n const children = childNodesOf(sourceDoc)\n const firstChild =\n children[0] ||\n (sourceDoc.documentElement\n ? childNodesOf(sourceDoc.documentElement)[0]\n : null)\n\n return firstChild\n ? translateRecursively(firstChild)\n : { children: [], tagName: '', attributes: {}, path: '' }\n } catch (e) {\n console.error('[xmlToJSON] Failed to parse XML:', e)\n return null\n }\n}\n\n/**\n * Parse XML source to DOM Document for XPath evaluation\n */\nexport function xmlToDOM(sourceXML: string): XMLDocument | null {\n try {\n const parser = new DOMParser()\n const doc = parser.parseFromString(sourceXML, 'text/xml')\n\n // xmldom 0.9+ throws ParseError for fatal errors (caught below); this catches non-fatal cases\n const parseErrors = doc.getElementsByTagName('parsererror')\n if (parseErrors.length > 0) {\n console.error('[xmlToDOM] XML parsing error:', parseErrors[0].textContent)\n return null\n }\n\n return doc\n } catch (e) {\n console.error('[xmlToDOM] Failed to parse XML:', e)\n return null\n }\n}\n\n/**\n * Execute XPath query on DOM document\n */\nexport function evaluateXPath(doc: XMLDocument, xpathExpr: string): XMLNode[] {\n try {\n // @xmldom/xmldom 0.9+ types don't satisfy global Node; xpath still works at runtime\n const nodes = xpath.select(xpathExpr, doc as unknown as Node)\n if (Array.isArray(nodes)) {\n return nodes as unknown as XMLNode[]\n }\n return []\n } catch (e) {\n console.error(`[evaluateXPath] Failed to evaluate \"${xpathExpr}\":`, e)\n return []\n }\n}\n\n/**\n * Check if an XPath selector is unique and get index if not\n */\nexport function checkXPathUniqueness(\n doc: XMLDocument,\n xpathExpr: string,\n targetNode?: XMLNode\n): UniquenessResult {\n try {\n const nodes = evaluateXPath(doc, xpathExpr)\n const totalMatches = nodes.length\n\n if (totalMatches === 0) {\n return { isUnique: false }\n }\n\n if (totalMatches === 1) {\n return { isUnique: true }\n }\n\n // Not unique - find index of target node if provided\n if (targetNode) {\n for (let i = 0; i < nodes.length; i++) {\n if (nodes[i] === targetNode || isSameElement(nodes[i], targetNode)) {\n return {\n isUnique: false,\n index: i + 1, // 1-based index for XPath\n totalMatches\n }\n }\n }\n }\n\n return { isUnique: false, totalMatches }\n } catch (e) {\n console.error(`[checkXPathUniqueness] Error checking \"${xpathExpr}\":`, e)\n return { isUnique: false }\n }\n}\n\n/**\n * Find DOM node by JSONElement path (e.g., \"0.2.1\")\n */\nexport function findDOMNodeByPath(\n doc: XMLDocument,\n path: string\n): XMLNode | null {\n if (!path) {\n return doc.documentElement\n }\n\n const indices = path.split('.').map(Number)\n let current: XMLNode | null = doc.documentElement\n\n for (const index of indices) {\n if (!current) {\n return null\n }\n\n const children: XMLNode[] = []\n if (current.childNodes) {\n for (let i = 0; i < current.childNodes.length; i++) {\n const child = current.childNodes.item(i)\n if (child?.nodeType === 1) {\n children.push(child)\n }\n }\n }\n\n current = children[index] || null\n }\n\n return current\n}\n\n/**\n * Parse Android bounds string \"[x1,y1][x2,y2]\" to coordinates\n */\nexport function parseAndroidBounds(bounds: string): Bounds {\n const match = bounds.match(/\\[(\\d+),(\\d+)\\]\\[(\\d+),(\\d+)\\]/)\n if (!match) {\n return { x: 0, y: 0, width: 0, height: 0 }\n }\n\n const x1 = parseInt(match[1], 10)\n const y1 = parseInt(match[2], 10)\n const x2 = parseInt(match[3], 10)\n const y2 = parseInt(match[4], 10)\n\n return {\n x: x1,\n y: y1,\n width: x2 - x1,\n height: y2 - y1\n }\n}\n\n/**\n * Parse iOS element bounds from individual x, y, width, height attributes\n */\nexport function parseIOSBounds(attributes: ElementAttributes): Bounds {\n return {\n x: parseInt(attributes.x || '0', 10),\n y: parseInt(attributes.y || '0', 10),\n width: parseInt(attributes.width || '0', 10),\n height: parseInt(attributes.height || '0', 10)\n }\n}\n\n/**\n * Flatten JSON element tree to array (depth-first)\n */\nexport function flattenElementTree(root: JSONElement): JSONElement[] {\n const result: JSONElement[] = []\n\n function traverse(element: JSONElement) {\n result.push(element)\n for (const child of element.children) {\n traverse(child)\n }\n }\n\n traverse(root)\n return result\n}\n\n/**\n * Count occurrences of an attribute value in the source XML\n */\nexport function countAttributeOccurrences(\n sourceXML: string,\n attribute: string,\n value: string\n): number {\n const escapedValue = value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n const pattern = new RegExp(`${attribute}=[\"']${escapedValue}[\"']`, 'g')\n const matches = sourceXML.match(pattern)\n return matches ? matches.length : 0\n}\n\n/**\n * Check if an attribute value is unique in the source (fast regex-based check)\n */\nexport function isAttributeUnique(\n sourceXML: string,\n attribute: string,\n value: string\n): boolean {\n return countAttributeOccurrences(sourceXML, attribute, value) === 1\n}\n","/**\n * Platform-specific element tag constants for mobile automation\n */\n\nexport const ANDROID_INTERACTABLE_TAGS = [\n // Input elements\n 'android.widget.EditText',\n 'android.widget.AutoCompleteTextView',\n 'android.widget.MultiAutoCompleteTextView',\n 'android.widget.SearchView',\n\n // Button-like elements\n 'android.widget.Button',\n 'android.widget.ImageButton',\n 'android.widget.ToggleButton',\n 'android.widget.CompoundButton',\n 'android.widget.RadioButton',\n 'android.widget.CheckBox',\n 'android.widget.Switch',\n 'android.widget.FloatingActionButton',\n 'com.google.android.material.button.MaterialButton',\n 'com.google.android.material.floatingactionbutton.FloatingActionButton',\n\n // Text elements (often tappable)\n 'android.widget.TextView',\n 'android.widget.CheckedTextView',\n\n // Image elements (often tappable)\n 'android.widget.ImageView',\n 'android.widget.QuickContactBadge',\n\n // Selection elements\n 'android.widget.Spinner',\n 'android.widget.SeekBar',\n 'android.widget.RatingBar',\n 'android.widget.ProgressBar',\n 'android.widget.DatePicker',\n 'android.widget.TimePicker',\n 'android.widget.NumberPicker',\n\n // List/grid items\n 'android.widget.AdapterView'\n]\n\nexport const ANDROID_LAYOUT_CONTAINERS = [\n // Core ViewGroup classes\n 'android.view.ViewGroup',\n 'android.view.View',\n 'android.widget.FrameLayout',\n 'android.widget.LinearLayout',\n 'android.widget.RelativeLayout',\n 'android.widget.GridLayout',\n 'android.widget.TableLayout',\n 'android.widget.TableRow',\n 'android.widget.AbsoluteLayout',\n\n // AndroidX layout classes\n 'androidx.constraintlayout.widget.ConstraintLayout',\n 'androidx.coordinatorlayout.widget.CoordinatorLayout',\n 'androidx.appcompat.widget.LinearLayoutCompat',\n 'androidx.cardview.widget.CardView',\n 'androidx.appcompat.widget.ContentFrameLayout',\n 'androidx.appcompat.widget.FitWindowsFrameLayout',\n\n // Scrolling containers\n 'android.widget.ScrollView',\n 'android.widget.HorizontalScrollView',\n 'android.widget.NestedScrollView',\n 'androidx.core.widget.NestedScrollView',\n 'androidx.recyclerview.widget.RecyclerView',\n 'android.widget.ListView',\n 'android.widget.GridView',\n 'android.widget.AbsListView',\n\n // App chrome / system elements\n 'android.widget.ActionBarContainer',\n 'android.widget.ActionBarOverlayLayout',\n 'android.view.ViewStub',\n 'androidx.appcompat.widget.ActionBarContainer',\n 'androidx.appcompat.widget.ActionBarContextView',\n 'androidx.appcompat.widget.ActionBarOverlayLayout',\n\n // Decor views\n 'com.android.internal.policy.DecorView',\n 'android.widget.DecorView'\n]\n\nexport const IOS_INTERACTABLE_TAGS = [\n // Input elements\n 'XCUIElementTypeTextField',\n 'XCUIElementTypeSecureTextField',\n 'XCUIElementTypeTextView',\n 'XCUIElementTypeSearchField',\n\n // Button-like elements\n 'XCUIElementTypeButton',\n 'XCUIElementTypeLink',\n\n // Text elements (often tappable)\n 'XCUIElementTypeStaticText',\n\n // Image elements\n 'XCUIElementTypeImage',\n 'XCUIElementTypeIcon',\n\n // Selection elements\n 'XCUIElementTypeSwitch',\n 'XCUIElementTypeSlider',\n 'XCUIElementTypeStepper',\n 'XCUIElementTypeSegmentedControl',\n 'XCUIElementTypePicker',\n 'XCUIElementTypePickerWheel',\n 'XCUIElementTypeDatePicker',\n 'XCUIElementTypePageIndicator',\n\n // Table/list items\n 'XCUIElementTypeCell',\n 'XCUIElementTypeMenuItem',\n 'XCUIElementTypeMenuBarItem',\n\n // Toggle elements\n 'XCUIElementTypeCheckBox',\n 'XCUIElementTypeRadioButton',\n 'XCUIElementTypeToggle',\n\n // Other interactive\n 'XCUIElementTypeKey',\n 'XCUIElementTypeKeyboard',\n 'XCUIElementTypeAlert',\n 'XCUIElementTypeSheet'\n]\n\nexport const IOS_LAYOUT_CONTAINERS = [\n // Generic containers\n 'XCUIElementTypeOther',\n 'XCUIElementTypeGroup',\n 'XCUIElementTypeLayoutItem',\n\n // Scroll containers\n 'XCUIElementTypeScrollView',\n 'XCUIElementTypeTable',\n 'XCUIElementTypeCollectionView',\n 'XCUIElementTypeScrollBar',\n\n // Navigation chrome\n 'XCUIElementTypeNavigationBar',\n 'XCUIElementTypeTabBar',\n 'XCUIElementTypeToolbar',\n 'XCUIElementTypeStatusBar',\n 'XCUIElementTypeMenuBar',\n\n // Windows and views\n 'XCUIElementTypeWindow',\n 'XCUIElementTypeSheet',\n 'XCUIElementTypeDrawer',\n 'XCUIElementTypeDialog',\n 'XCUIElementTypePopover',\n 'XCUIElementTypePopUpButton',\n\n // Outline elements\n 'XCUIElementTypeOutline',\n 'XCUIElementTypeOutlineRow',\n 'XCUIElementTypeBrowser',\n 'XCUIElementTypeSplitGroup',\n 'XCUIElementTypeSplitter',\n\n // Application root\n 'XCUIElementTypeApplication'\n]\n","/**\n * Locator strategy generation for mobile elements\n */\n\nimport type {\n JSONElement,\n LocatorStrategy,\n LocatorContext,\n UniquenessResult,\n XMLNode,\n XMLDocument\n} from './types.js'\nimport type { Element as XMLElement } from '@xmldom/xmldom'\nimport {\n checkXPathUniqueness,\n evaluateXPath,\n isAttributeUnique\n} from './xml-parsing.js'\n\n/**\n * Check if a string value is valid for use in a locator\n */\nfunction isValidValue(value: string | undefined): value is string {\n return (\n value !== undefined &&\n value !== null &&\n value !== 'null' &&\n value.trim() !== ''\n )\n}\n\n/**\n * Escape special characters in text for use in selectors\n */\nfunction escapeText(text: string): string {\n return text.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"').replace(/\\n/g, '\\\\n')\n}\n\n/**\n * Escape value for use in XPath expressions\n */\nfunction escapeXPathValue(value: string): string {\n if (!value.includes(\"'\")) {\n return `'${value}'`\n }\n if (!value.includes('\"')) {\n return `\"${value}\"`\n }\n const parts: string[] = []\n let current = ''\n for (const char of value) {\n if (char === \"'\") {\n if (current) {\n parts.push(`'${current}'`)\n }\n parts.push('\"\\'\"')\n current = ''\n } else {\n current += char\n }\n }\n if (current) {\n parts.push(`'${current}'`)\n }\n return `concat(${parts.join(',')})`\n}\n\n/**\n * Wrap non-unique XPath with index\n */\nfunction generateIndexedXPath(baseXPath: string, index: number): string {\n return `(${baseXPath})[${index}]`\n}\n\n/**\n * Add .instance(n) for UiAutomator (0-based)\n */\nfunction generateIndexedUiAutomator(\n baseSelector: string,\n index: number\n): string {\n return `${baseSelector}.instance(${index - 1})`\n}\n\n/**\n * Check uniqueness, falling back to regex if no DOM available\n */\nfunction checkUniqueness(\n ctx: LocatorContext,\n xpath: string,\n targetNode?: XMLNode\n): UniquenessResult {\n if (ctx.parsedDOM) {\n return checkXPathUniqueness(ctx.parsedDOM, xpath, targetNode)\n }\n\n // Bounded quantifiers ({1,200} for attr names, {1,2000} for values) prevent\n // polynomial-time backtracking on near-misses like `//[@<` repeated — CodeQL's\n // js/polynomial-redos query flagged the unbounded form. The bounds are well\n // above any realistic XPath attribute name/value length.\n const match = xpath.match(/\\/\\/\\*\\[@([^=]{1,200})=\"([^\"]{1,2000})\"\\]/)\n if (match) {\n const [, attr, value] = match\n return { isUnique: isAttributeUnique(ctx.sourceXML, attr, value) }\n }\n return { isUnique: false }\n}\n\n/**\n * Get sibling index (1-based) among same-tag siblings\n */\nfunction getSiblingIndex(element: XMLElement): number {\n const parent = element.parentNode\n if (!parent) {\n return 1\n }\n\n const tagName = element.nodeName\n let index = 0\n\n for (let i = 0; i < parent.childNodes.length; i++) {\n const child = parent.childNodes.item(i)\n if (child?.nodeType === 1 && child.nodeName === tagName) {\n index++\n if (child === element) {\n return index\n }\n }\n }\n\n return 1\n}\n\n/**\n * Count siblings with same tag name\n */\nfunction countSiblings(element: XMLElement): number {\n const parent = element.parentNode\n if (!parent) {\n return 1\n }\n\n const tagName = element.nodeName\n let count = 0\n\n for (let i = 0; i < parent.childNodes.length; i++) {\n const child = parent.childNodes.item(i)\n if (child?.nodeType === 1 && child.nodeName === tagName) {\n count++\n }\n }\n\n return count\n}\n\n/**\n * Find unique attribute for element in XPath format\n */\nfunction findUniqueAttribute(\n element: XMLElement,\n ctx: LocatorContext\n): string | null {\n const attrs = ctx.isAndroid\n ? ['resource-id', 'content-desc', 'text']\n : ['name', 'label', 'value']\n\n for (const attr of attrs) {\n const value = element.getAttribute(attr)\n if (value && value.trim()) {\n const xpath = `//*[@${attr}=${escapeXPathValue(value)}]`\n const result = ctx.parsedDOM\n ? checkXPathUniqueness(ctx.parsedDOM, xpath)\n : { isUnique: isAttributeUnique(ctx.sourceXML, attr, value) }\n\n if (result.isUnique) {\n return `@${attr}=${escapeXPathValue(value)}`\n }\n }\n }\n\n return null\n}\n\n/**\n * Build hierarchical XPath by traversing up the DOM tree\n */\nfunction buildHierarchicalXPath(\n ctx: LocatorContext,\n element: XMLElement,\n maxDepth: number = 3\n): string | null {\n if (!ctx.parsedDOM) {\n return null\n }\n\n const pathParts: string[] = []\n let current: XMLElement | null = element\n let depth = 0\n\n while (current && depth < maxDepth) {\n const tagName = current.nodeName\n const uniqueAttr = findUniqueAttribute(current, ctx)\n\n if (uniqueAttr) {\n pathParts.unshift(`//${tagName}[${uniqueAttr}]`)\n break\n } else {\n const siblingIndex = getSiblingIndex(current)\n const siblingCount = countSiblings(current)\n\n if (siblingCount > 1) {\n pathParts.unshift(`${tagName}[${siblingIndex}]`)\n } else {\n pathParts.unshift(tagName)\n }\n }\n\n const parent = current.parentNode as XMLElement | null\n current = parent && parent.nodeType === 1 ? parent : null\n depth++\n }\n\n if (pathParts.length === 0) {\n return null\n }\n\n let result = pathParts[0]\n for (let i = 1; i < pathParts.length; i++) {\n result += '/' + pathParts[i]\n }\n\n if (!result.startsWith('//')) {\n result = '//' + result\n }\n\n return result\n}\n\n/**\n * Add XPath locator with uniqueness checking and fallbacks\n */\nfunction addXPathLocator(\n results: [LocatorStrategy, string][],\n xpath: string,\n ctx: LocatorContext,\n targetNode?: XMLNode\n): void {\n const uniqueness = checkUniqueness(ctx, xpath, targetNode)\n if (uniqueness.isUnique) {\n results.push(['xpath', xpath])\n } else if (uniqueness.index) {\n results.push(['xpath', generateIndexedXPath(xpath, uniqueness.index)])\n } else {\n if (targetNode && ctx.parsedDOM) {\n // @xmldom/xmldom 0.9+ XMLNode doesn't satisfy global Node; safe at runtime\n const hierarchical = buildHierarchicalXPath(\n ctx,\n targetNode as unknown as XMLElement\n )\n if (hierarchical) {\n results.push(['xpath', hierarchical])\n }\n }\n results.push(['xpath', xpath])\n }\n}\n\n/**\n * Check if element is within UiAutomator scope\n */\nfunction isInUiAutomatorScope(\n element: JSONElement,\n doc: XMLDocument | null\n): boolean {\n if (!doc) {\n return true\n }\n\n const hierarchyNodes = evaluateXPath(doc, '/hierarchy/*')\n if (hierarchyNodes.length === 0) {\n return true\n }\n\n const lastIndex = hierarchyNodes.length\n const pathParts = element.path.split('.')\n if (pathParts.length === 0 || pathParts[0] === '') {\n return true\n }\n\n const firstIndex = parseInt(pathParts[0], 10)\n return firstIndex === lastIndex - 1\n}\n\n/**\n * Build Android UiAutomator selector with multiple attributes\n */\nfunction buildUiAutomatorSelector(element: JSONElement): string | null {\n const attrs = element.attributes\n const parts: string[] = []\n\n if (isValidValue(attrs['resource-id'])) {\n parts.push(`resourceId(\"${attrs['resource-id']}\")`)\n }\n if (isValidValue(attrs.text) && attrs.text!.length < 100) {\n parts.push(`text(\"${escapeText(attrs.text!)}\")`)\n }\n if (isValidValue(attrs['content-desc'])) {\n parts.push(`description(\"${attrs['content-desc']}\")`)\n }\n if (isValidValue(attrs.class)) {\n parts.push(`className(\"${attrs.class}\")`)\n }\n\n if (parts.length === 0) {\n return null\n }\n\n return `android=new UiSelector().${parts.join('.')}`\n}\n\n/**\n * Build iOS predicate string with multiple conditions\n */\nfunction buildPredicateString(element: JSONElement): string | null {\n const attrs = element.attributes\n const conditions: string[] = []\n\n if (isValidValue(attrs.name)) {\n conditions.push(`name == \"${escapeText(attrs.name!)}\"`)\n }\n if (isValidValue(attrs.label)) {\n conditions.push(`label == \"${escapeText(attrs.label!)}\"`)\n }\n if (isValidValue(attrs.value)) {\n conditions.push(`value == \"${escapeText(attrs.value!)}\"`)\n }\n if (attrs.visible === 'true') {\n conditions.push('visible == 1')\n }\n if (attrs.enabled === 'true') {\n conditions.push('enabled == 1')\n }\n\n if (conditions.length === 0) {\n return null\n }\n\n return `-ios predicate string:${conditions.join(' AND ')}`\n}\n\n/**\n * Build iOS class chain selector\n */\nfunction buildClassChain(element: JSONElement): string | null {\n const attrs = element.attributes\n const tagName = element.tagName\n\n if (!tagName.startsWith('XCUI')) {\n return null\n }\n\n let selector = `**/${tagName}`\n\n if (isValidValue(attrs.label)) {\n selector += `[\\`label == \"${escapeText(attrs.label!)}\"\\`]`\n } else if (isValidValue(attrs.name)) {\n selector += `[\\`name == \"${escapeText(attrs.name!)}\"\\`]`\n }\n\n return `-ios class chain:${selector}`\n}\n\n/**\n * Build XPath for element with unique identification\n */\nfunction buildXPath(\n element: JSONElement,\n _sourceXML: string,\n isAndroid: boolean\n): string | null {\n const attrs = element.attributes\n const tagName = element.tagName\n const conditions: string[] = []\n\n if (isAndroid) {\n if (isValidValue(attrs['resource-id'])) {\n conditions.push(`@resource-id=\"${attrs['resource-id']}\"`)\n }\n if (isValidValue(attrs['content-desc'])) {\n conditions.push(`@content-desc=\"${attrs['content-desc']}\"`)\n }\n if (isValidValue(attrs.text) && attrs.text!.length < 100) {\n conditions.push(`@text=\"${escapeText(attrs.text!)}\"`)\n }\n } else {\n if (isValidValue(attrs.name)) {\n conditions.push(`@name=\"${attrs.name}\"`)\n }\n if (isValidValue(attrs.label)) {\n conditions.push(`@label=\"${attrs.label}\"`)\n }\n if (isValidValue(attrs.value)) {\n conditions.push(`@value=\"${attrs.value}\"`)\n }\n }\n\n if (conditions.length === 0) {\n return `//${tagName}`\n }\n\n return `//${tagName}[${conditions.join(' and ')}]`\n}\n\n/**\n * Get simple locators based on single attributes\n */\nfunction getSimpleSuggestedLocators(\n element: JSONElement,\n ctx: LocatorContext,\n automationName: string,\n inUiAutomatorScope: boolean,\n targetNode?: XMLNode\n): [LocatorStrategy, string][] {\n const results: [LocatorStrategy, string][] = []\n const isAndroid = automationName.toLowerCase().includes('uiautomator')\n const attrs = element.attributes\n\n if (isAndroid) {\n // Resource ID\n const resourceId = attrs['resource-id']\n if (isValidValue(resourceId)) {\n const xpath = `//*[@resource-id=\"${resourceId}\"]`\n const uniqueness = checkUniqueness(ctx, xpath, targetNode)\n\n if (uniqueness.isUnique && inUiAutomatorScope) {\n results.push([\n 'id',\n `android=new UiSelector().resourceId(\"${resourceId}\")`\n ])\n } else if (uniqueness.index && inUiAutomatorScope) {\n const base = `android=new UiSelector().resourceId(\"${resourceId}\")`\n results.push(['id', generateIndexedUiAutomator(base, uniqueness.index)])\n }\n }\n\n // Content Description\n const contentDesc = attrs['content-desc']\n if (isValidValue(contentDesc)) {\n const xpath = `//*[@content-desc=\"${contentDesc}\"]`\n const uniqueness = checkUniqueness(ctx, xpath, targetNode)\n\n if (uniqueness.isUnique) {\n results.push(['accessibility-id', `~${contentDesc}`])\n }\n }\n\n // Text\n const text = attrs.text\n if (isValidValue(text) && text.length < 100) {\n const xpath = `//*[@text=\"${escapeText(text)}\"]`\n const uniqueness = checkUniqueness(ctx, xpath, targetNode)\n\n if (uniqueness.isUnique && inUiAutomatorScope) {\n results.push([\n 'text',\n `android=new UiSelector().text(\"${escapeText(text)}\")`\n ])\n } else if (uniqueness.index && inUiAutomatorScope) {\n const base = `android=new UiSelector().text(\"${escapeText(text)}\")`\n results.push([\n 'text',\n generateIndexedUiAutomator(base, uniqueness.index)\n ])\n }\n }\n } else {\n // iOS: Accessibility ID (name)\n const name = attrs.name\n if (isValidValue(name)) {\n const xpath = `//*[@name=\"${name}\"]`\n const uniqueness = checkUniqueness(ctx, xpath, targetNode)\n\n if (uniqueness.isUnique) {\n results.push(['accessibility-id', `~${name}`])\n }\n }\n\n // iOS: Label\n const label = attrs.label\n if (isValidValue(label) && label !== attrs.name) {\n const xpath = `//*[@label=\"${escapeText(label)}\"]`\n const uniqueness = checkUniqueness(ctx, xpath, targetNode)\n\n if (uniqueness.isUnique) {\n results.push([\n 'predicate-string',\n `-ios predicate string:label == \"${escapeText(label)}\"`\n ])\n }\n }\n\n // iOS: Value\n const value = attrs.value\n if (isValidValue(value)) {\n const xpath = `//*[@value=\"${escapeText(value)}\"]`\n const uniqueness = checkUniqueness(ctx, xpath, targetNode)\n\n if (uniqueness.isUnique) {\n results.push([\n 'predicate-string',\n `-ios predicate string:value == \"${escapeText(value)}\"`\n ])\n }\n }\n }\n\n return results\n}\n\n/**\n * Get complex locators (combinations, XPath, etc.)\n */\nfunction getComplexSuggestedLocators(\n element: JSONElement,\n ctx: LocatorContext,\n automationName: string,\n inUiAutomatorScope: boolean,\n targetNode?: XMLNode\n): [LocatorStrategy, string][] {\n const results: [LocatorStrategy, string][] = []\n const isAndroid = automationName.toLowerCase().includes('uiautomator')\n\n if (isAndroid) {\n if (inUiAutomatorScope) {\n const uiAutomator = buildUiAutomatorSelector(element)\n if (uiAutomator) {\n results.push(['uiautomator', uiAutomator])\n }\n }\n\n const xpath = buildXPath(element, ctx.sourceXML, true)\n if (xpath) {\n addXPathLocator(results, xpath, ctx, targetNode)\n }\n\n if (inUiAutomatorScope && isValidValue(element.attributes.class)) {\n results.push([\n 'class-name',\n `android=new UiSelector().className(\"${element.attributes.class}\")`\n ])\n }\n } else {\n const predicate = buildPredicateString(element)\n if (predicate) {\n results.push(['predicate-string', predicate])\n }\n\n const classChain = buildClassChain(element)\n if (classChain) {\n results.push(['class-chain', classChain])\n }\n\n const xpath = buildXPath(element, ctx.sourceXML, false)\n if (xpath) {\n addXPathLocator(results, xpath, ctx, targetNode)\n }\n\n const type = element.tagName\n if (type.startsWith('XCUIElementType')) {\n results.push(['class-name', `-ios class chain:**/${type}`])\n }\n }\n\n return results\n}\n\n/**\n * Get all suggested locators for an element\n */\nexport function getSuggestedLocators(\n element: JSONElement,\n sourceXML: string,\n automationName: string,\n ctx?: LocatorContext,\n targetNode?: XMLNode\n): [LocatorStrategy, string][] {\n const locatorCtx = ctx ?? {\n sourceXML,\n parsedDOM: null,\n isAndroid: automationName.toLowerCase().includes('uiautomator')\n }\n\n const inUiAutomatorScope = locatorCtx.isAndroid\n ? isInUiAutomatorScope(element, locatorCtx.parsedDOM)\n : true\n\n const simpleLocators = getSimpleSuggestedLocators(\n element,\n locatorCtx,\n automationName,\n inUiAutomatorScope,\n targetNode\n )\n const complexLocators = getComplexSuggestedLocators(\n element,\n locatorCtx,\n automationName,\n inUiAutomatorScope,\n targetNode\n )\n\n const seen = new Set<string>()\n const results: [LocatorStrategy, string][] = []\n\n for (const locator of [...simpleLocators, ...complexLocators]) {\n if (!seen.has(locator[1])) {\n seen.add(locator[1])\n results.push(locator)\n }\n }\n\n return results\n}\n\n/**\n * Get the best (first priority) locator for an element\n */\nexport function getBestLocator(\n element: JSONElement,\n sourceXML: string,\n automationName: string\n): string | null {\n const locators = getSuggestedLocators(element, sourceXML, automationName)\n return locators.length > 0 ? locators[0][1] : null\n}\n\n/**\n * Convert locator array to object format\n */\nexport function locatorsToObject(\n locators: [LocatorStrategy, string][]\n): Record<string, string> {\n const result: Record<string, string> = {}\n for (const [strategy, value] of locators) {\n if (!result[strategy]) {\n result[strategy] = value\n }\n }\n return result\n}\n","/**\n * AI-readable snapshot serializers\n *\n * Converts accessibility trees and mobile element trees into depth-indented\n * text files that LLMs can consume without any parsing.\n */\n\nimport type { AccessibilityNode } from './element-types.js'\nimport type { JSONElement } from './locators/types.js'\nimport { parseAndroidBounds, parseIOSBounds } from './locators/xml-parsing.js'\nimport {\n ANDROID_INTERACTABLE_TAGS,\n IOS_INTERACTABLE_TAGS\n} from './locators/constants.js'\nimport { getSuggestedLocators } from './locators/locator-generation.js'\n\n/**\n * Roles that can be interacted with — rendered with `→ selector`.\n * Structural roles (heading, img, form, nav, …) are intentionally excluded.\n */\nconst INTERACTIVE_ROLES = new Set([\n 'button',\n 'link',\n 'textbox',\n 'checkbox',\n 'radio',\n 'combobox',\n 'slider',\n 'searchbox',\n 'spinbutton',\n 'switch',\n 'tab',\n 'menuitem',\n 'option'\n])\n\n/**\n * Walk backwards from `index` to find the nearest ancestor or preceding\n * structural sibling with a non-empty name. Same-depth nodes are only\n * used when they are structural (img, heading, statictext, …) — never\n * another interactive element.\n */\nfunction inferPurpose(\n nodes: AccessibilityNode[],\n index: number\n): string | undefined {\n const myDepth = nodes[index].depth\n for (let i = index - 1; i >= 0; i--) {\n if (nodes[i].depth <= myDepth && nodes[i].name) {\n // Same-depth sibling: only structural elements count\n if (nodes[i].depth === myDepth && INTERACTIVE_ROLES.has(nodes[i].role)) {\n continue\n }\n return nodes[i].name\n }\n }\n return undefined\n}\n\nexport interface WebSnapshotOptions {\n /** Only include nodes whose bounding rect intersects the viewport (default true). */\n inViewportOnly?: boolean\n}\n\n/**\n * Serialize a web accessibility tree into a depth-indented text snapshot.\n *\n * @param nodes Flat ordered node list from getBrowserAccessibilityTree()\n * @param context Optional page context for the header line\n * @param options {@link WebSnapshotOptions}\n */\nexport function serializeWebSnapshot(\n nodes: AccessibilityNode[],\n context?: { url?: string; title?: string },\n options: WebSnapshotOptions = {}\n): string {\n const { inViewportOnly = true } = options\n\n let header = '[Page'\n if (context?.title) {\n header += `: ${context.title}`\n }\n if (context?.url) {\n header += ` — ${context.url}`\n }\n header += ']'\n\n const lines: string[] = [header]\n\n for (let i = 0; i < nodes.length; i++) {\n const node = nodes[i]\n\n // When viewport filtering is on, skip nodes that are known to be off-screen.\n // Nodes from a tree captured with inViewportOnly=false will have\n // isInViewport populated; nodes from a pre-filtered tree all have\n // isInViewport=true (or undefined for pre-existing data).\n if (inViewportOnly && node.isInViewport === false) {\n continue\n }\n\n const indent = ' '.repeat(node.depth + 1) // +1 indents everything under the header\n const isInteractive = INTERACTIVE_ROLES.has(node.role)\n\n // Skip statictext that merely echoes the parent link/button name.\n // Example: link \"Highlights\" → a*=Highlights doesn't need\n // statictext \"Highlights\" as a child because it adds no information.\n if (node.role === 'statictext' && node.name) {\n let echoedByParent = false\n for (let j = i - 1; j >= 0; j--) {\n if (nodes[j].depth < node.depth) {\n const parentRole = nodes[j].role\n const parentName = nodes[j].name\n if (\n INTERACTIVE_ROLES.has(parentRole) &&\n parentName &&\n parentName.includes(node.name)\n ) {\n echoedByParent = true\n }\n break // only check the immediate structural parent\n }\n }\n if (echoedByParent) {\n continue\n }\n }\n\n // Heading gets level suffix: heading[2]\n const roleLabel =\n node.role === 'heading' && node.level\n ? `heading[${node.level}]`\n : node.role\n\n if (isInteractive) {\n // No selector → agent can't act on this node; skip entirely\n if (!node.selector) {\n continue\n }\n const purpose = inferPurpose(nodes, i)\n if (node.name) {\n // Show parent context when available — disambiguates\n // duplicate selectors like six \"Add to Wishlist\" buttons.\n lines.push(\n purpose\n ? `${indent}${roleLabel} \"${node.name}\" ∈ \"${purpose}\" → ${node.selector}`\n : `${indent}${roleLabel} \"${node.name}\" → ${node.selector}`\n )\n } else if (purpose) {\n lines.push(`${indent}${roleLabel} ∈ \"${purpose}\" → ${node.selector}`)\n } else {\n lines.push(`${indent}${roleLabel} → ${node.selector}`)\n }\n } else {\n // Container / structural: show role + name when present, no selector\n lines.push(\n node.name\n ? `${indent}${roleLabel} \"${node.name}\"`\n : `${indent}${roleLabel}`\n )\n }\n }\n\n return lines.join('\\n')\n}\n\n// ---------------------------------------------------------------------------\n// Mobile snapshot helpers\n// ---------------------------------------------------------------------------\n\n/** Shorten fully-qualified Android/iOS class names to the last segment. */\nfunction simplifyTag(tagName: string): string {\n const dot = tagName.lastIndexOf('.')\n if (dot !== -1) {\n return tagName.slice(dot + 1)\n }\n return tagName.replace(/^XCUIElementType/, '')\n}\n\n// ---------------------------------------------------------------------------\n// Mobile role classification — maps raw Android/iOS class names to semantic\n// roles so the snapshot reads like the web version (button, textbox, img, …).\n// ---------------------------------------------------------------------------\n\nconst ANDROID_ROLE_MAP: Record<string, string> = {\n 'android.widget.Button': 'button',\n 'android.widget.ImageButton': 'button',\n 'android.widget.ToggleButton': 'button',\n 'android.widget.FloatingActionButton': 'button',\n 'com.google.android.material.button.MaterialButton': 'button',\n 'com.google.android.material.floatingactionbutton.FloatingActionButton':\n 'button',\n 'android.widget.EditText': 'textbox',\n 'android.widget.AutoCompleteTextView': 'textbox',\n 'android.widget.MultiAutoCompleteTextView': 'textbox',\n 'android.widget.SearchView': 'searchbox',\n 'android.widget.ImageView': 'img',\n 'android.widget.QuickContactBadge': 'img',\n 'android.widget.CheckBox': 'checkbox',\n 'android.widget.RadioButton': 'radio',\n 'android.widget.Switch': 'switch',\n 'android.widget.Spinner': 'combobox',\n 'android.widget.SeekBar': 'slider',\n 'android.widget.RatingBar': 'slider',\n 'android.widget.ProgressBar': 'progressbar',\n 'android.widget.TextView': 'statictext',\n 'android.widget.CheckedTextView': 'statictext',\n 'android.widget.RecyclerView': 'list',\n 'android.widget.ListView': 'list',\n 'android.widget.GridView': 'list',\n 'android.webkit.WebView': 'webview'\n}\n\nconst IOS_ROLE_MAP: Record<string, string> = {\n XCUIElementTypeButton: 'button',\n XCUIElementTypeLink: 'link',\n XCUIElementTypeTextField: 'textbox',\n XCUIElementTypeSecureTextField: 'textbox',\n XCUIElementTypeTextView: 'textbox',\n XCUIElementTypeSearchField: 'searchbox',\n XCUIElementTypeImage: 'img',\n XCUIElementTypeIcon: 'img',\n XCUIElementTypeSwitch: 'switch',\n XCUIElementTypeSlider: 'slider',\n XCUIElementTypeStepper: 'slider',\n XCUIElementTypeCheckBox: 'checkbox',\n XCUIElementTypeRadioButton: 'radio',\n XCUIElementTypePicker: 'combobox',\n XCUIElementTypePickerWheel: 'combobox',\n XCUIElementTypeDatePicker: 'combobox',\n XCUIElementTypeSegmentedControl: 'combobox',\n XCUIElementTypeStaticText: 'statictext',\n XCUIElementTypeCell: 'listitem',\n XCUIElementTypeTable: 'list',\n XCUIElementTypeCollectionView: 'list'\n}\n\nfunction classifyMobileRole(\n tagName: string,\n platform: 'android' | 'ios'\n): string {\n if (platform === 'android') {\n return ANDROID_ROLE_MAP[tagName] || simplifyTag(tagName)\n }\n return IOS_ROLE_MAP[tagName] || simplifyTag(tagName)\n}\n\n// ---------------------------------------------------------------------------\n// Locator generation\n// ---------------------------------------------------------------------------\n\nfunction getBestAndroidLocator(\n attrs: JSONElement['attributes']\n): string | undefined {\n // Pre-computed by the full locator pipeline (generateAllElementLocators).\n // Takes priority over the simplified fallback logic below.\n if (attrs._selector) {\n return attrs._selector\n }\n // ~ prefix = accessibility-id shorthand in WebdriverIO ($('~foo'))\n if (attrs['content-desc']) {\n return `~${attrs['content-desc']}`\n }\n if (attrs['resource-id']) {\n return `id:${attrs['resource-id']}`\n }\n if (attrs.text) {\n return `~${attrs.text}`\n }\n // Fallback: class-based locator (only useful with :nth-of-type or index)\n if (attrs.class) {\n return `class:${simplifyTag(attrs.class)}`\n }\n return undefined\n}\n\nfunction getBestIOSLocator(\n attrs: JSONElement['attributes']\n): string | undefined {\n // Pre-computed by the full locator pipeline.\n if (attrs._selector) {\n return attrs._selector\n }\n // ~ prefix = accessibility-id shorthand (maps to `name` on iOS)\n if (attrs.name) {\n return `~${attrs.name}`\n }\n if (attrs.label) {\n return `~${attrs.label}`\n }\n if (attrs.value) {\n return `~${attrs.value}`\n }\n // Fallback: class-based locator\n if (attrs.type) {\n return `class:${simplifyTag(attrs.type)}`\n }\n return undefined\n}\n\n// ---------------------------------------------------------------------------\n// Identity\n// ---------------------------------------------------------------------------\n\nfunction getMobileNodeIdentity(\n attrs: JSONElement['attributes'],\n platform: 'android' | 'ios'\n): string {\n if (platform === 'android') {\n const contentDesc = attrs['content-desc']\n if (contentDesc) {\n return contentDesc\n }\n if (attrs.text) {\n return attrs.text\n }\n // Fall back to the last segment of the resource-id (e.g. \"search_action_bar\")\n const rid = attrs['resource-id']\n if (rid) {\n const slash = rid.lastIndexOf('/')\n return slash !== -1 ? rid.slice(slash + 1) : rid\n }\n return ''\n }\n return attrs.name || attrs.label || attrs.value || attrs.text || ''\n}\n\n// ---------------------------------------------------------------------------\n// Interactivity\n// ---------------------------------------------------------------------------\n\nconst ANDROID_INTERACTABLE_SET = new Set(ANDROID_INTERACTABLE_TAGS)\nconst IOS_INTERACTABLE_SET = new Set(IOS_INTERACTABLE_TAGS)\n\n/** An element is *explicitly* interactive when it carries a click/focus/check\n * attribute — as opposed to being interactive only because its tag is in the\n * interactable-tag list. Explicit parents should carry the → selector, not\n * their tag-interactive children. */\nfunction isExplicitlyInteractive(\n attrs: JSONElement['attributes'],\n platform: 'android' | 'ios'\n): boolean {\n if (platform === 'android') {\n return (\n attrs.clickable === 'true' ||\n attrs.focusable === 'true' ||\n attrs.checkable === 'true' ||\n attrs['long-clickable'] === 'true'\n )\n }\n return attrs.accessible === 'true'\n}\n\nfunction isMobileInteractive(\n element: JSONElement,\n platform: 'android' | 'ios'\n): boolean {\n const attrs = element.attributes\n if (platform === 'android') {\n if (ANDROID_INTERACTABLE_SET.has(element.tagName)) {\n return true\n }\n return (\n attrs.clickable === 'true' ||\n attrs['long-clickable'] === 'true' ||\n attrs.focusable === 'true' ||\n attrs.checkable === 'true'\n )\n }\n if (IOS_INTERACTABLE_SET.has(element.tagName)) {\n return true\n }\n return attrs.accessible === 'true'\n}\n\n// ---------------------------------------------------------------------------\n// Viewport\n// ---------------------------------------------------------------------------\n\ninterface WalkMobileOptions {\n inViewportOnly: boolean\n viewport: { width: number; height: number }\n /** Raw page-source XML. When provided, the full locator pipeline is used. */\n sourceXML?: string\n /** 'uiautomator2' or 'xcuitest'. Required when sourceXML is set. */\n automationName?: string\n}\n\nfunction isMobileInViewport(\n element: JSONElement,\n platform: 'android' | 'ios',\n viewport: { width: number; height: number }\n): boolean {\n const bounds =\n platform === 'android'\n ? parseAndroidBounds(element.attributes.bounds || '')\n : parseIOSBounds(element.attributes)\n\n if (bounds.width === 0 && bounds.height === 0) {\n return true\n }\n\n return (\n bounds.x >= 0 &&\n bounds.y >= 0 &&\n bounds.width > 0 &&\n bounds.height > 0 &&\n bounds.x + bounds.width <= viewport.width &&\n bounds.y + bounds.height <= viewport.height\n )\n}\n\n// ---------------------------------------------------------------------------\n// Flat-node representation (mirrors AccessibilityNode so both pipelines share\n// inferPurpose, dedup, and rendering logic).\n// ---------------------------------------------------------------------------\n\ninterface MobileFlatNode {\n role: string\n name: string\n selector: string\n depth: number\n isInteractive: boolean\n /** True when the element has clickable/focusable/checkable — the intended tap target. */\n isExplicitInteractive: boolean\n isInViewport: boolean\n}\n\n/**\n * First pass: walk the JSONElement tree, apply viewport filtering and\n * collect every node into a flat array with semantic roles and selectors.\n */\nfunction collectMobileNodes(\n element: JSONElement,\n platform: 'android' | 'ios',\n depth: number,\n nodes: MobileFlatNode[],\n walkOpts: WalkMobileOptions\n): void {\n const attrs = element.attributes\n const role = classifyMobileRole(element.tagName, platform)\n const name = getMobileNodeIdentity(attrs, platform)\n const explicit = isExplicitlyInteractive(attrs, platform)\n const interactive = isMobileInteractive(element, platform)\n const inViewport = isMobileInViewport(element, platform, walkOpts.viewport)\n\n // Viewport filtering\n if (walkOpts.inViewportOnly) {\n if (interactive && !inViewport) {\n // Skip this node but still recurse (scroll children may be in view).\n for (const child of element.children || []) {\n collectMobileNodes(child, platform, depth + 1, nodes, walkOpts)\n }\n return\n }\n if (!interactive && !inViewport) {\n // Collapse off-screen container to a placeholder.\n nodes.push({\n role: 'generic',\n name: name ? `${role} \"${name}\"` : role,\n selector: '',\n depth,\n isInteractive: false,\n isExplicitInteractive: false,\n isInViewport: false\n })\n return\n }\n }\n\n // Generate a selector for every interactive element.\n // Use the full locator pipeline when source XML is available;\n // otherwise fall back to the simplified attribute-based heuristics.\n let locator = ''\n if (interactive) {\n if (walkOpts.sourceXML && walkOpts.automationName) {\n // Full pipeline: accessible-id, id, text, uiautomator, xpath, class-name\n const suggested = getSuggestedLocators(\n element,\n walkOpts.sourceXML,\n walkOpts.automationName,\n {\n sourceXML: walkOpts.sourceXML,\n parsedDOM: null,\n isAndroid: platform === 'android'\n }\n )\n if (suggested.length > 0) {\n locator = suggested[0][1] // first = best priority\n }\n }\n if (!locator) {\n // Simplified fallback\n locator =\n (platform === 'android'\n ? getBestAndroidLocator(attrs)\n : getBestIOSLocator(attrs)) ?? ''\n }\n }\n\n nodes.push({\n role,\n name,\n selector: locator,\n depth,\n isInteractive: interactive,\n isExplicitInteractive: explicit,\n isInViewport: inViewport\n })\n\n for (const child of element.children || []) {\n collectMobileNodes(child, platform, depth + 1, nodes, walkOpts)\n }\n}\n\n// ---------------------------------------------------------------------------\n// Context inference — shared with the web pipeline.\n// Same-depth structural siblings (img, statictext, heading, …) provide\n// context for following interactive nodes.\n// ---------------------------------------------------------------------------\n\nconst MOBILE_STRUCTURAL_ROLES = new Set([\n 'img',\n 'heading',\n 'list',\n 'listitem',\n 'webview',\n 'progressbar',\n 'slider',\n 'switch',\n 'generic'\n])\n\nfunction mobileInferPurpose(\n nodes: MobileFlatNode[],\n index: number\n): string | undefined {\n const myDepth = nodes[index].depth\n for (let i = index - 1; i >= 0; i--) {\n if (nodes[i].depth <= myDepth && nodes[i].name) {\n if (\n nodes[i].depth === myDepth &&\n !MOBILE_STRUCTURAL_ROLES.has(nodes[i].role)\n ) {\n continue\n }\n return nodes[i].name\n }\n }\n return undefined\n}\n\n// ---------------------------------------------------------------------------\n// When a tag-only-interactive child (e.g. a statictext TextView) sits\n// directly under an explicitly-interactive parent (e.g. a clickable\n// LinearLayout row), the *parent* should carry the → selector — the\n// child is just a label. Suppress the child's interactivity so the\n// parent renders as the actionable element.\n// ---------------------------------------------------------------------------\n\nfunction suppressTagOnlyChildren(nodes: MobileFlatNode[]): void {\n for (let i = 0; i < nodes.length; i++) {\n const node = nodes[i]\n if (!node.isInteractive || node.isExplicitInteractive) {\n continue\n }\n // Walk up through ALL ancestors looking for an explicitly-interactive\n // parent. The immediate depth-1 parent may just be a layout wrapper;\n // the real clickable row could be 2-3 levels up.\n for (let j = i - 1; j >= 0; j--) {\n if (nodes[j].depth < node.depth) {\n if (nodes[j].isExplicitInteractive) {\n node.isInteractive = false\n break // found — suppress and stop\n }\n // keep looking upward through the ancestor chain\n }\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Render pass: flat nodes into lines with ∈ context, dedup, noise filter,\n// and class-instance indexing.\n// ---------------------------------------------------------------------------\n\n/** Layout roles that carry no semantic meaning by themselves. */\nconst NOISY_ROLES = new Set([\n 'FrameLayout',\n 'LinearLayout',\n 'ViewGroup',\n 'RelativeLayout',\n 'View',\n 'CardView',\n 'ConstraintLayout',\n 'ScrollView'\n])\n\n/**\n * Pre-count selector occurrences so we can attach .instance(N) suffixes\n * to duplicate selectors.\n */\nfunction countSelectors(nodes: MobileFlatNode[]): Map<string, number> {\n const counts = new Map<string, number>()\n for (const node of nodes) {\n if (node.selector) {\n counts.set(node.selector, (counts.get(node.selector) ?? 0) + 1)\n }\n }\n return counts\n}\n\nfunction renderMobileNodes(nodes: MobileFlatNode[]): string[] {\n const lines: string[] = []\n const selectorCounts = countSelectors(nodes)\n const selectorIndex = new Map<string, number>()\n\n for (let i = 0; i < nodes.length; i++) {\n const node = nodes[i]\n const indent = ' '.repeat(node.depth + 1)\n\n // Collapse anonymous layout containers at depth ≥ 2.\n // Keep depth 0-1 structural chrome and any named container.\n if (\n NOISY_ROLES.has(node.role) &&\n !node.name &&\n node.depth > 1 &&\n !node.isInteractive\n ) {\n continue\n }\n\n // Off-screen containers rendered as collapsed placeholders\n if (node.isInViewport === false && !node.isInteractive) {\n lines.push(`${indent}⋯ ${node.name} (off-screen)`)\n continue\n }\n\n // Dedup: skip statictext whose text is echoed by the parent interactive element\n if (node.role === 'statictext' && node.name) {\n let echoedByParent = false\n for (let j = i - 1; j >= 0; j--) {\n if (nodes[j].depth < node.depth) {\n if (\n nodes[j].isInteractive &&\n nodes[j].name &&\n nodes[j].name.includes(node.name)\n ) {\n echoedByParent = true\n }\n break\n }\n }\n if (echoedByParent) {\n continue\n }\n }\n\n if (node.isInteractive && node.selector) {\n // Append .instance(N) when the same selector repeats\n let selector = node.selector\n const total = selectorCounts.get(selector) ?? 1\n if (total > 1) {\n const idx = selectorIndex.get(selector) ?? 0\n selectorIndex.set(selector, idx + 1)\n selector = `${selector}.instance(${idx})`\n }\n\n const purpose = mobileInferPurpose(nodes, i)\n if (node.name) {\n lines.push(\n purpose\n ? `${indent}${node.role} \"${node.name}\" ∈ \"${purpose}\" → ${selector}`\n : `${indent}${node.role} \"${node.name}\" → ${selector}`\n )\n } else if (purpose) {\n lines.push(`${indent}${node.role} ∈ \"${purpose}\" → ${selector}`)\n } else {\n lines.push(`${indent}${node.role} → ${selector}`)\n }\n } else {\n // Container / structural / non-locatable\n lines.push(\n node.name\n ? `${indent}${node.role} \"${node.name}\"`\n : `${indent}${node.role}`\n )\n }\n }\n\n return lines\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nexport interface MobileSnapshotOptions {\n /** Only include elements whose bounds intersect the viewport (default true). */\n inViewportOnly?: boolean\n /**\n * Raw XML page source string. When provided the full locator pipeline\n * (getSuggestedLocators) runs on every interactive node, producing the same\n * selectors that getElements() returns. Omit to use simplified heuristics.\n */\n sourceXML?: string\n}\n\n/**\n * Serialize a mobile element tree into a depth-indented text snapshot.\n *\n * @param root Root JSONElement from the page source XML parse\n * @param context Platform, optional device name, viewport, and source XML.\n * Include `sourceXML` to use the full locator pipeline.\n * @param options {@link MobileSnapshotOptions}\n */\nexport function serializeMobileSnapshot(\n root: JSONElement,\n context: {\n platform: 'android' | 'ios'\n deviceName?: string\n viewport?: { width: number; height: number }\n /** Raw page-source XML. When set, selectors match getElements() output. */\n sourceXML?: string\n },\n options: MobileSnapshotOptions = {}\n): string {\n const { platform, deviceName, viewport, sourceXML } = context\n const { inViewportOnly = true } = options\n\n // Auto-detect source XML stashed by getMobileVisibleElementsWithTree\n const effectiveXML = sourceXML || root.attributes._sourceXML\n\n const effectiveViewport = viewport ?? { width: 9999, height: 9999 }\n const automationName = platform === 'android' ? 'uiautomator2' : 'xcuitest'\n\n let header = `[${platform}`\n if (deviceName) {\n header += ` — ${deviceName}`\n }\n if (viewport) {\n header += ` (${viewport.width}×${viewport.height})`\n }\n header += ']'\n\n const nodes: MobileFlatNode[] = []\n collectMobileNodes(root, platform, 0, nodes, {\n inViewportOnly,\n viewport: effectiveViewport,\n sourceXML: effectiveXML,\n automationName: effectiveXML ? automationName : undefined\n })\n\n // Let explicitly-interactive parents carry the → selector\n suppressTagOnlyChildren(nodes)\n\n const lines = renderMobileNodes(nodes)\n return [header, ...lines].join('\\n')\n}\n","/**\n * Element filtering logic for mobile automation\n */\n\nimport type { JSONElement, FilterOptions } from './types.js'\nimport {\n ANDROID_INTERACTABLE_TAGS,\n IOS_INTERACTABLE_TAGS,\n ANDROID_LAYOUT_CONTAINERS,\n IOS_LAYOUT_CONTAINERS\n} from './constants.js'\n\n/**\n * Check if element tag matches any in the list (handles partial matches)\n */\nfunction matchesTagList(tagName: string, tagList: string[]): boolean {\n if (tagList.includes(tagName)) {\n return true\n }\n\n for (const tag of tagList) {\n if (tagName.endsWith(tag) || tagName.includes(tag)) {\n return true\n }\n }\n\n return false\n}\n\n/**\n * Check if element matches tag name filters\n */\nfunction matchesTagFilters(\n element: JSONElement,\n includeTagNames: string[],\n excludeTagNames: string[]\n): boolean {\n if (\n includeTagNames.length > 0 &&\n !matchesTagList(element.tagName, includeTagNames)\n ) {\n return false\n }\n\n if (matchesTagList(element.tagName, excludeTagNames)) {\n return false\n }\n\n return true\n}\n\n/**\n * Check if element matches attribute-based filters\n */\nfunction matchesAttributeFilters(\n element: JSONElement,\n requireAttributes: string[],\n minAttributeCount: number\n): boolean {\n if (requireAttributes.length > 0) {\n const hasRequiredAttr = requireAttributes.some(\n (attr) => element.attributes?.[attr]\n )\n if (!hasRequiredAttr) {\n return false\n }\n }\n\n if (element.attributes && minAttributeCount > 0) {\n const attrCount = Object.values(element.attributes).filter(\n (v) => v !== undefined && v !== null && v !== ''\n ).length\n if (attrCount < minAttributeCount) {\n return false\n }\n }\n\n return true\n}\n\n/**\n * Check if element is interactable based on platform\n */\nexport function isInteractableElement(\n element: JSONElement,\n _isNative: boolean,\n automationName: string\n): boolean {\n const isAndroid = automationName.toLowerCase().includes('uiautomator')\n const interactableTags = isAndroid\n ? ANDROID_INTERACTABLE_TAGS\n : IOS_INTERACTABLE_TAGS\n\n if (matchesTagList(element.tagName, interactableTags)) {\n return true\n }\n\n if (isAndroid) {\n if (\n element.attributes?.clickable === 'true' ||\n element.attributes?.focusable === 'true' ||\n element.attributes?.checkable === 'true' ||\n element.attributes?.['long-clickable'] === 'true'\n ) {\n return true\n }\n }\n\n if (!isAndroid) {\n if (element.attributes?.accessible === 'true') {\n return true\n }\n }\n\n return false\n}\n\n/**\n * Check if element is a layout container\n */\nexport function isLayoutContainer(\n element: JSONElement,\n platform: 'android' | 'ios'\n): boolean {\n const containerList =\n platform === 'android' ? ANDROID_LAYOUT_CONTAINERS : IOS_LAYOUT_CONTAINERS\n return matchesTagList(element.tagName, containerList)\n}\n\n/**\n * Check if element has meaningful content (text, accessibility info)\n */\nexport function hasMeaningfulContent(\n element: JSONElement,\n platform: 'android' | 'ios'\n): boolean {\n const attrs = element.attributes\n\n if (attrs.text && attrs.text.trim() !== '' && attrs.text !== 'null') {\n return true\n }\n\n if (platform === 'android') {\n if (\n attrs['content-desc'] &&\n attrs['content-desc'].trim() !== '' &&\n attrs['content-desc'] !== 'null'\n ) {\n return true\n }\n } else {\n if (attrs.label && attrs.label.trim() !== '' && attrs.label !== 'null') {\n return true\n }\n if (attrs.name && attrs.name.trim() !== '' && attrs.name !== 'null') {\n return true\n }\n }\n\n return false\n}\n\n/**\n * Determine if an element should be included based on all filter criteria\n */\nexport function shouldIncludeElement(\n element: JSONElement,\n filters: FilterOptions,\n isNative: boolean,\n automationName: string\n): boolean {\n const {\n includeTagNames = [],\n excludeTagNames = ['hierarchy'],\n requireAttributes = [],\n minAttributeCount = 0,\n fetchableOnly = false,\n clickableOnly = false,\n visibleOnly = true\n } = filters\n\n if (!matchesTagFilters(element, includeTagNames, excludeTagNames)) {\n if (element.attributes?.clickable !== 'true') {\n return false\n }\n }\n\n if (!matchesAttributeFilters(element, requireAttributes, minAttributeCount)) {\n return false\n }\n\n if (clickableOnly && element.attributes?.clickable !== 'true') {\n return false\n }\n\n if (visibleOnly) {\n const isAndroid = automationName.toLowerCase().includes('uiautomator')\n if (isAndroid && element.attributes?.displayed === 'false') {\n return false\n }\n if (!isAndroid && element.attributes?.visible === 'false') {\n return false\n }\n }\n\n if (\n fetchableOnly &&\n !isInteractableElement(element, isNative, automationName)\n ) {\n return false\n }\n\n return true\n}\n\n/**\n * Get default filter options for a platform\n */\nexport function getDefaultFilters(\n platform: 'android' | 'ios',\n includeContainers: boolean = false\n): FilterOptions {\n const layoutContainers =\n platform === 'android' ? ANDROID_LAYOUT_CONTAINERS : IOS_LAYOUT_CONTAINERS\n\n return {\n excludeTagNames: includeContainers\n ? ['hierarchy']\n : ['hierarchy', ...layoutContainers],\n fetchableOnly: !includeContainers,\n visibleOnly: true,\n clickableOnly: false\n }\n}\n","/**\n * Mobile element locator generation\n *\n * Main orchestrator module that coordinates XML parsing, element filtering,\n * and locator generation for mobile automation.\n */\n\n// Types\nexport type {\n ElementAttributes,\n JSONElement,\n Bounds,\n FilterOptions,\n UniquenessResult,\n LocatorStrategy,\n LocatorContext,\n ElementWithLocators,\n GenerateLocatorsOptions\n} from './types.js'\n\n// Constants\nexport {\n ANDROID_INTERACTABLE_TAGS,\n IOS_INTERACTABLE_TAGS,\n ANDROID_LAYOUT_CONTAINERS,\n IOS_LAYOUT_CONTAINERS\n} from './constants.js'\n\n// XML Parsing\nexport {\n xmlToJSON,\n xmlToDOM,\n evaluateXPath,\n checkXPathUniqueness,\n findDOMNodeByPath,\n parseAndroidBounds,\n parseIOSBounds,\n flattenElementTree,\n countAttributeOccurrences,\n isAttributeUnique\n} from './xml-parsing.js'\n\n// Element Filtering\nexport {\n isInteractableElement,\n isLayoutContainer,\n hasMeaningfulContent,\n shouldIncludeElement,\n getDefaultFilters\n} from './element-filter.js'\n\n// Locator Generation\nexport {\n getSuggestedLocators,\n getBestLocator,\n locatorsToObject\n} from './locator-generation.js'\n\nimport type {\n JSONElement,\n FilterOptions,\n LocatorStrategy,\n ElementWithLocators,\n GenerateLocatorsOptions,\n XMLDocument\n} from './types.js'\n\nimport {\n xmlToJSON,\n xmlToDOM,\n parseAndroidBounds,\n parseIOSBounds,\n findDOMNodeByPath\n} from './xml-parsing.js'\nimport {\n shouldIncludeElement,\n isLayoutContainer,\n hasMeaningfulContent\n} from './element-filter.js'\nimport { getSuggestedLocators, locatorsToObject } from './locator-generation.js'\n\ninterface ProcessingContext {\n sourceXML: string\n platform: 'android' | 'ios'\n automationName: string\n isNative: boolean\n viewportSize: { width: number; height: number }\n filters: FilterOptions\n inViewportOnly: boolean\n results: ElementWithLocators[]\n parsedDOM: XMLDocument | null\n}\n\n/**\n * Parse element bounds based on platform\n */\nfunction parseBounds(\n element: JSONElement,\n platform: 'android' | 'ios'\n): { x: number; y: number; width: number; height: number } {\n return platform === 'android'\n ? parseAndroidBounds(element.attributes.bounds || '')\n : parseIOSBounds(element.attributes)\n}\n\n/**\n * Check if bounds are within viewport\n */\nfunction isWithinViewport(\n bounds: { x: number; y: number; width: number; height: number },\n viewport: { width: number; height: number }\n): boolean {\n return (\n bounds.x >= 0 &&\n bounds.y >= 0 &&\n bounds.width > 0 &&\n bounds.height > 0 &&\n bounds.x + bounds.width <= viewport.width &&\n bounds.y + bounds.height <= viewport.height\n )\n}\n\n/**\n * Transform JSONElement to ElementWithLocators\n */\nfunction transformElement(\n element: JSONElement,\n locators: [LocatorStrategy, string][],\n ctx: ProcessingContext\n): ElementWithLocators {\n const attrs = element.attributes\n const bounds = parseBounds(element, ctx.platform)\n\n return {\n tagName: element.tagName,\n locators: locatorsToObject(locators),\n text: attrs.text || attrs.label || '',\n contentDesc: attrs['content-desc'] || '',\n resourceId: attrs['resource-id'] || '',\n accessibilityId: attrs.name || attrs['content-desc'] || '',\n label: attrs.label || '',\n value: attrs.value || '',\n className: attrs.class || element.tagName,\n clickable:\n attrs.clickable === 'true' ||\n attrs.accessible === 'true' ||\n attrs['long-clickable'] === 'true',\n enabled: attrs.enabled !== 'false',\n displayed:\n ctx.platform === 'android'\n ? attrs.displayed !== 'false'\n : attrs.visible !== 'false',\n bounds,\n isInViewport: isWithinViewport(bounds, ctx.viewportSize)\n }\n}\n\n/**\n * Check if element should be processed\n */\nfunction shouldProcess(element: JSONElement, ctx: ProcessingContext): boolean {\n if (\n shouldIncludeElement(element, ctx.filters, ctx.isNative, ctx.automationName)\n ) {\n return true\n }\n return (\n isLayoutContainer(element, ctx.platform) &&\n hasMeaningfulContent(element, ctx.platform)\n )\n}\n\n/**\n * Process a single element and add to results if valid\n */\nfunction processElement(element: JSONElement, ctx: ProcessingContext): void {\n if (!shouldProcess(element, ctx)) {\n return\n }\n\n // Skip off-screen elements early when viewport filtering is on —\n // avoids expensive locator generation for elements the caller doesn't want.\n if (ctx.inViewportOnly) {\n const b = parseBounds(element, ctx.platform)\n if (!isWithinViewport(b, ctx.viewportSize)) {\n return\n }\n }\n\n try {\n const targetNode = ctx.parsedDOM\n ? findDOMNodeByPath(ctx.parsedDOM, element.path)\n : undefined\n\n const locators = getSuggestedLocators(\n element,\n ctx.sourceXML,\n ctx.automationName,\n {\n sourceXML: ctx.sourceXML,\n parsedDOM: ctx.parsedDOM,\n isAndroid: ctx.platform === 'android'\n },\n targetNode || undefined\n )\n if (locators.length === 0) {\n return\n }\n\n // Stash the best locator on the tree node so serializeMobileSnapshot\n // can reuse the full locator pipeline instead of recomputing.\n element.attributes._selector = locators[0][1]\n\n const transformed = transformElement(element, locators, ctx)\n if (Object.keys(transformed.locators).length === 0) {\n return\n }\n\n ctx.results.push(transformed)\n } catch (error) {\n // Core is logger-free; console.error provides the required\n // \"enough detail to debug\" per the error-handling convention.\n // A single bad element never fails the entire page-source walk.\n console.error(`[processElement] Error at path ${element.path}:`, error)\n }\n}\n\n/**\n * Recursively traverse and process element tree\n */\nfunction traverseTree(\n element: JSONElement | null,\n ctx: ProcessingContext\n): void {\n if (!element) {\n return\n }\n\n processElement(element, ctx)\n\n for (const child of element.children || []) {\n traverseTree(child, ctx)\n }\n}\n\n/**\n * Generate locators for all elements from page source XML\n */\nexport function generateAllElementLocators(\n sourceXML: string,\n options: GenerateLocatorsOptions\n): ElementWithLocators[] {\n const sourceJSON = xmlToJSON(sourceXML)\n\n if (!sourceJSON) {\n // Core is logger-free; console.error is the only signal that XML\n // parsing failed — the caller receives an empty result silently otherwise.\n console.error(\n '[generateAllElementLocators] Failed to parse page source XML'\n )\n return []\n }\n\n const parsedDOM = xmlToDOM(sourceXML)\n\n const ctx: ProcessingContext = {\n sourceXML,\n platform: options.platform,\n automationName:\n options.platform === 'android' ? 'uiautomator2' : 'xcuitest',\n isNative: options.isNative ?? true,\n viewportSize: options.viewportSize ?? { width: 9999, height: 9999 },\n filters: options.filters ?? {},\n inViewportOnly: options.inViewportOnly ?? true,\n results: [],\n parsedDOM\n }\n\n traverseTree(sourceJSON, ctx)\n\n return ctx.results\n}\n","// Resolves `promise` if it settles before `ms`; otherwise resolves to the\n// supplied fallback value. Used by per-action snapshot capture to guard\n// against hung in-page scripts (heavy pages, infinite-render loops) without\n// stalling the user's test.\n\nexport function withTimeout<T>(\n promise: Promise<T>,\n ms: number,\n fallback: T\n): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | undefined\n const timeout = new Promise<T>((resolve) => {\n timer = setTimeout(() => resolve(fallback), ms)\n })\n // .finally on the race result clears the timer in BOTH the resolved AND\n // rejected branch — earlier code only cleared on fulfillment, leaving a\n // dangling timer per failed probe that delayed process exit by up to `ms`.\n return Promise.race([promise, timeout]).finally(() => {\n if (timer) {\n clearTimeout(timer)\n }\n })\n}\n\n/** Default ceiling for a single in-page snapshot probe. */\nexport const SNAPSHOT_PROBE_TIMEOUT_MS = 2500\n","// Adapter-agnostic per-action snapshot capture. Each adapter wires its own\n// `runScript`, `takeScreenshot`, etc. shim so the actual capture pipeline\n// (timeouts, fallbacks, snapshot serialization) lives in one place.\n\nimport { accessibilityTreeScript, elementsScript } from './element-scripts.js'\nimport {\n serializeWebSnapshot,\n serializeMobileSnapshot\n} from './element-snapshot.js'\nimport type { AccessibilityNode, BrowserElementInfo } from './element-types.js'\nimport { xmlToJSON } from './locators/xml-parsing.js'\nimport {\n generateAllElementLocators,\n getDefaultFilters\n} from './locators/index.js'\nimport { SNAPSHOT_PROBE_TIMEOUT_MS, withTimeout } from './with-timeout.js'\nimport type { ActionSnapshot } from '@wdio/devtools-shared'\n\nexport type ScriptRunner = (scriptSrc: string) => Promise<unknown>\n\nexport interface CaptureActionSnapshotInput {\n command: string\n /** Browser script runner — omit on native mobile where Appium can't execute JS. */\n runScript?: ScriptRunner\n takeScreenshot?: () => Promise<string | null | undefined>\n getUrl?: () => Promise<string | undefined>\n getTitle?: () => Promise<string | undefined>\n /** Page-source XML fetcher for native mobile — used instead of runScript. */\n getPageSource?: () => Promise<string | undefined>\n /** Platform identifier for mobile snapshot formatting ('android' | 'ios'). */\n platform?: 'android' | 'ios'\n}\n\nasync function runWith<T>(\n runScript: ScriptRunner | undefined,\n scriptSrc: string,\n fallback: T\n): Promise<T> {\n if (!runScript) {\n return fallback\n }\n\n return withTimeout(\n runScript(scriptSrc).then((r) => r as T),\n SNAPSHOT_PROBE_TIMEOUT_MS,\n fallback\n ).catch(() => fallback)\n}\n\nexport async function captureActionSnapshot(\n input: CaptureActionSnapshotInput\n): Promise<ActionSnapshot | null> {\n try {\n const timestamp = Date.now()\n const isNativeMobile = !input.runScript && !!input.getPageSource\n\n const [shot, url, title, pageSource, tree, elements] = await Promise.all([\n input.takeScreenshot?.().catch(() => null),\n input.getUrl?.().catch(() => undefined),\n input.getTitle?.().catch(() => undefined),\n isNativeMobile\n ? input.getPageSource?.().catch(() => undefined)\n : undefined,\n runWith<AccessibilityNode[]>(\n input.runScript,\n accessibilityTreeScript(true),\n []\n ),\n runWith<BrowserElementInfo[]>(\n input.runScript,\n elementsScript(false, true),\n []\n )\n ])\n\n let snapshotText: string\n let finalElements: unknown[] = elements\n\n if (isNativeMobile && pageSource) {\n const platform = input.platform ?? 'android'\n const jsonTree = xmlToJSON(pageSource)\n if (jsonTree) {\n jsonTree.attributes._sourceXML = pageSource\n snapshotText = serializeMobileSnapshot(jsonTree, {\n platform,\n sourceXML: pageSource\n })\n } else {\n snapshotText = `[${platform}]`\n }\n // Generate mobile element locators from the page source XML.\n try {\n const viewport = { width: 9999, height: 9999 }\n const filters = getDefaultFilters(platform, false)\n const locators = generateAllElementLocators(pageSource, {\n platform,\n viewportSize: viewport,\n filters,\n inViewportOnly: false\n })\n finalElements = locators\n } catch {\n // Non-fatal — snapshot text is the primary deliverable.\n }\n } else {\n snapshotText = serializeWebSnapshot(tree, { url, title })\n }\n\n return {\n timestamp,\n command: input.command,\n url,\n title,\n screenshot: shot ?? undefined,\n elements: finalElements,\n snapshotText\n }\n } catch {\n return null\n }\n}\n","import { parse as parseStackTrace } from 'stacktrace-parser'\n\n/**\n * Return true if a stack frame belongs to user code (not dependencies, Node\n * internals, build output, or a generic `index.js` entry point).\n */\nexport function isUserCodeFrame(frame: {\n file?: string | null\n}): frame is { file: string } {\n const { file } = frame\n return !!(\n file &&\n !file.includes('/node_modules/') &&\n !file.includes('<anonymous>') &&\n !file.includes('node:internal') &&\n !file.includes('/dist/') &&\n !file.endsWith('/index.js')\n )\n}\n\n/**\n * Strip the `file://` protocol, any trailing `:line:col` suffix, and\n * percent-decode the result. Node's ESM stack traces use file:// URLs which\n * URL-encode spaces — without decoding, `fs.readFile` hits ENOENT on any\n * path that contains one. Falls back to the literal path if decoding fails.\n */\nexport function normalizeFilePath(filePath: string): string {\n const stripped = filePath.replace(/^file:\\/\\//, '').split(':')[0]\n try {\n return decodeURIComponent(stripped)\n } catch {\n return stripped\n }\n}\n\n/**\n * Capture `{ filePath, callSource }` for the first user-code frame on the\n * current stack. `callSource` is `<file>:<line>` for the UI's source-location\n * displays; returns `'unknown:0'` (and `undefined` filePath) when no user\n * frame can be found.\n */\nexport function getCallSourceFromStack(): {\n filePath: string | undefined\n callSource: string\n} {\n const stack = new Error().stack\n if (!stack) {\n return { filePath: undefined, callSource: 'unknown:0' }\n }\n\n const frame = parseStackTrace(stack).find(isUserCodeFrame)\n if (!frame?.file) {\n return { filePath: undefined, callSource: 'unknown:0' }\n }\n\n const filePath = normalizeFilePath(frame.file)\n return { filePath, callSource: `${filePath}:${frame.lineNumber ?? 0}` }\n}\n","// Converts a captured TraceLog into a trace.zip Buffer.\n// Stays runner-agnostic so the three adapters can call this directly.\n\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport type {\n ActionSnapshot,\n CommandLog,\n ConsoleLog,\n Metadata,\n NetworkRequest,\n TestMetadataMap,\n TraceFormat,\n TraceLog,\n TraceMutation\n} from '@wdio/devtools-shared'\nimport {\n formatActionTitle,\n mapCommandToAction,\n FILL_METHODS,\n type TraceAction\n} from './action-mapping.js'\nimport { networkRequestToHar } from './trace-har.js'\nimport { buildTraceZip, type TraceZipResource } from './trace-zip-writer.js'\n\nconst TRACE_VERSION = 8\nconst LIBRARY_NAME = '@wdio/devtools-core'\nconst LIBRARY_VERSION = '1.0.0'\n\ninterface ContextOptionsEvent {\n version: number\n type: 'context-options'\n origin: 'library'\n libraryName: string\n libraryVersion: string\n browserName: string\n platform: string\n wallTime: number\n monotonicTime: number\n sdkLanguage: string\n title: string\n contextId: string\n options: { viewport: { width: number; height: number } }\n}\n\ninterface BeforeEvent {\n type: 'before'\n callId: string\n startTime: number\n class: string\n method: string\n pageId: string\n params: Record<string, unknown>\n title: string\n /** Playwright-compatible API name (e.g. 'page.goBack', 'element.click'). */\n apiName: string\n /** CallId of the Tracing.tracingGroup that wraps this action (if any). */\n parentId?: string\n}\n\ninterface AfterEvent {\n type: 'after'\n callId: string\n endTime: number\n error?: { message: string }\n /** CallId of the Tracing.tracingGroup that wraps this action (if any). */\n parentId?: string\n}\n\ninterface ScreencastFrameEvent {\n type: 'screencast-frame'\n pageId: string\n sha1: string\n elements?: string\n snapshot?: string\n width: number\n height: number\n timestamp: number\n}\n\ntype TraceEvent =\n | ContextOptionsEvent\n | BeforeEvent\n | AfterEvent\n | ScreencastFrameEvent\n\nfunction shortId(sessionId?: string): string {\n return (sessionId ?? Math.random().toString(36).slice(2, 10)).slice(0, 8)\n}\n\nfunction resolveContextNaming(caps: Record<string, unknown> | undefined): {\n browserName: string\n title: string\n} {\n const platformName =\n typeof caps?.platformName === 'string'\n ? caps.platformName.toLowerCase()\n : undefined\n const deviceName =\n typeof caps?.['appium:deviceName'] === 'string'\n ? (caps['appium:deviceName'] as string)\n : undefined\n if (platformName === 'android' || platformName === 'ios') {\n return {\n browserName: 'chromium',\n title: deviceName ? `${platformName} — ${deviceName}` : platformName\n }\n }\n const browserName =\n typeof caps?.browserName === 'string' ? caps.browserName : 'chromium'\n return { browserName, title: browserName }\n}\n\nfunction buildContextOptions(\n trace: TraceLog,\n contextId: string,\n wallTime: number\n): ContextOptionsEvent {\n const caps = trace.metadata.capabilities as\n | Record<string, unknown>\n | undefined\n const { browserName, title } = resolveContextNaming(caps)\n const viewport = trace.metadata.viewport ?? { width: 1280, height: 720 }\n return {\n version: TRACE_VERSION,\n type: 'context-options',\n origin: 'library',\n libraryName: LIBRARY_NAME,\n libraryVersion: LIBRARY_VERSION,\n browserName,\n platform:\n process.platform === 'darwin'\n ? 'darwin'\n : process.platform === 'win32'\n ? 'windows'\n : 'linux',\n wallTime,\n monotonicTime: 0,\n sdkLanguage: 'javascript',\n title,\n contextId,\n options: {\n viewport: { width: viewport.width, height: viewport.height }\n }\n }\n}\n\nexport function buildActionEvents(\n commands: CommandLog[],\n pageId: string,\n wallTime: number,\n testMetadata?: TestMetadataMap\n): TraceEvent[] {\n const events: TraceEvent[] = []\n let prevEndMs = 0\n let callCounter = 0\n let lastTestUid: string | undefined\n let groupCallId: string | undefined\n\n for (const cmd of commands) {\n const action = mapCommandToAction(cmd.command)\n if (!action) {\n continue\n }\n\n // ── Test boundary detection ──\n // When the testUid changes, close the previous Tracing.tracingGroup\n // and open a new one. Child actions inside the group reference it via\n // parentId so trace viewers render them as labelled spans.\n if (cmd.testUid && cmd.testUid !== lastTestUid) {\n // Close the previous group.\n if (lastTestUid && groupCallId) {\n callCounter++\n events.push({\n type: 'after',\n callId: groupCallId,\n endTime: prevEndMs\n } satisfies AfterEvent)\n }\n\n // Open a new group for this test.\n callCounter++\n groupCallId = `call@${callCounter}`\n const meta = testMetadata?.get(cmd.testUid)\n const groupName = meta?.title ?? cmd.testUid\n events.push({\n type: 'before',\n callId: groupCallId,\n startTime: Math.max(\n prevEndMs,\n (cmd.startTime ?? cmd.timestamp) - wallTime\n ),\n class: 'Tracing',\n method: 'tracingGroup',\n pageId,\n params: { name: groupName },\n title: groupName,\n apiName: 'tracing.tracingGroup'\n } satisfies BeforeEvent)\n lastTestUid = cmd.testUid\n }\n\n // ── Regular action (child of the current group) ──\n callCounter++\n const callId = `call@${callCounter}`\n // Use the command's actual invocation timestamp for the start, falling\n // back to the completion timestamp when startTime isn't recorded.\n const rawStartMs = (cmd.startTime ?? cmd.timestamp) - wallTime\n const rawEndMs = cmd.timestamp - wallTime\n // Floor at prevEndMs to prevent visual overlap with previous action.\n const startMs = Math.max(prevEndMs, rawStartMs)\n // +1ms minimum duration so the viewer never sees an `after` whose\n // matching `before` hasn't been parsed yet.\n const endMs = Math.max(startMs + 1, rawEndMs)\n const rawArgs = cmd.args as unknown[]\n let params: Record<string, unknown>\n const isValueMethod = FILL_METHODS.has(action.method)\n if (action.class === 'Element' && isValueMethod && rawArgs.length >= 2) {\n params = { selector: rawArgs[0], value: rawArgs[1] }\n } else if (\n action.class === 'Element' &&\n isValueMethod &&\n rawArgs.length === 1\n ) {\n params = { value: rawArgs[0] }\n } else if (\n action.class === 'Element' &&\n rawArgs.length === 1 &&\n typeof rawArgs[0] === 'string'\n ) {\n params = { selector: rawArgs[0] }\n } else if (rawArgs.length === 1 && typeof rawArgs[0] === 'string') {\n params = { url: rawArgs[0] }\n } else {\n params = Object.fromEntries(rawArgs.map((a, i) => [String(i), a]))\n }\n events.push({\n type: 'before',\n callId,\n startTime: startMs,\n class: action.class,\n method: action.method,\n pageId,\n params,\n title: formatActionTitle(action, cmd.args, params),\n apiName: `${action.class.toLowerCase()}.${action.method}`,\n parentId: groupCallId\n })\n const afterEvent: AfterEvent = {\n type: 'after',\n callId,\n endTime: endMs\n }\n if (cmd.error) {\n const err = cmd.error as { message?: string }\n afterEvent.error = { message: err.message ?? String(cmd.error) }\n }\n events.push(afterEvent)\n prevEndMs = endMs\n }\n\n // Close the final group after the last action.\n if (lastTestUid && groupCallId) {\n callCounter++\n events.push({\n type: 'after',\n callId: groupCallId,\n endTime: prevEndMs\n } satisfies AfterEvent)\n }\n\n return events\n}\n\nfunction buildNetworkNdjson(\n requests: NetworkRequest[],\n wallTime: number,\n pageId: string\n): Buffer {\n if (!requests.length) {\n return Buffer.alloc(0)\n }\n const lines = requests.map((r) => {\n const entry = networkRequestToHar(r) as unknown as Record<string, unknown>\n entry.snapshot = {\n ...(entry.snapshot as Record<string, unknown>),\n // Monotonic offset so the viewer positions bars on the timeline.\n _monotonicTime: Math.max(0, r.timestamp - wallTime),\n // Browsing context ID so the viewer associates requests with the page.\n _frameref: pageId\n }\n return JSON.stringify(entry)\n })\n return Buffer.from(lines.join('\\n'), 'utf8')\n}\n\nfunction buildSnapshotResources(\n snapshots: ActionSnapshot[],\n pageId: string\n): TraceZipResource[] {\n const out: TraceZipResource[] = []\n for (const snap of snapshots) {\n const base = `${pageId}-${snap.timestamp}`\n if (snap.screenshot) {\n out.push({\n resourceName: `${base}.jpeg`,\n data: Buffer.from(snap.screenshot, 'base64')\n })\n }\n if (snap.elements && snap.elements.length) {\n out.push({\n resourceName: `${base}-elements.json`,\n data: Buffer.from(JSON.stringify(snap.elements), 'utf8')\n })\n }\n if (snap.snapshotText) {\n out.push({\n resourceName: `${base}-snapshot.txt`,\n data: Buffer.from(snap.snapshotText, 'utf8')\n })\n }\n }\n return out\n}\n\nfunction buildScreencastFrames(\n snapshots: ActionSnapshot[],\n pageId: string,\n wallTime: number,\n viewport: { width: number; height: number }\n): ScreencastFrameEvent[] {\n return snapshots\n .filter((s) => s.screenshot)\n .map((s) => {\n const base = `${pageId}-${s.timestamp}`\n const frame: ScreencastFrameEvent = {\n type: 'screencast-frame',\n pageId,\n sha1: `${base}.jpeg`,\n width: viewport.width,\n height: viewport.height,\n timestamp: Math.max(0, s.timestamp - wallTime)\n }\n if (s.elements && s.elements.length) {\n frame.elements = `${base}-elements.json`\n }\n if (s.snapshotText) {\n frame.snapshot = `${base}-snapshot.txt`\n }\n return frame\n })\n}\n\n/**\n * Build a trace.zip buffer from the captured TraceLog.\n * Filters commands through ACTION_MAP and renames to trace vocabulary;\n * network entries become HAR resource-snapshots; per-action screenshots,\n * element JSON, and snapshot text are written under `resources/`.\n */\n/** Chronological sort key — see `compareEvents` for the tie-breaker rationale. */\nfunction eventTime(e: TraceEvent): number {\n switch (e.type) {\n case 'context-options':\n return -Infinity\n case 'before':\n return e.startTime\n case 'after':\n return e.endTime\n case 'screencast-frame':\n return e.timestamp\n }\n}\n\n/** At the same timestamp T: an action's `after` ends first, then the\n * snapshot captured at the action boundary, then the next action's `before`.\n * Matches the viewer's expectation that the screencast frame shows the\n * state between the previous action's completion and the next one's start. */\nfunction eventOrder(e: TraceEvent): number {\n switch (e.type) {\n case 'context-options':\n return 0\n case 'after':\n return 1\n case 'screencast-frame':\n return 2\n case 'before':\n return 3\n }\n}\n\nfunction compareEvents(a: TraceEvent, b: TraceEvent): number {\n const dt = eventTime(a) - eventTime(b)\n return dt !== 0 ? dt : eventOrder(a) - eventOrder(b)\n}\n\n/**\n * Generate a human/LLM-readable Markdown transcript from captured commands.\n */\nfunction generateTranscript(\n commands: CommandLog[],\n startWallTime: number,\n title?: string\n): string {\n const wallTimeISO = new Date(startWallTime).toISOString()\n const lines: string[] = [`# ${title ?? 'Session'} — ${wallTimeISO}`, '']\n\n const captured: { entry: CommandLog; action: TraceAction }[] = []\n for (const c of commands) {\n const action = mapCommandToAction(String(c.command))\n if (action) {\n captured.push({ entry: c, action })\n }\n }\n\n captured.forEach(({ entry, action }, idx) => {\n const label = formatActionTitle(action, entry.args as unknown[])\n\n const rawArgs = entry.args as unknown[]\n const parts: string[] = [`${idx + 1}. ${label}`]\n\n if (FILL_METHODS.has(action.method) && rawArgs) {\n const valueIdx = rawArgs.length >= 2 ? 1 : 0\n if (rawArgs[valueIdx] !== undefined) {\n parts.push(`value=\"${String(rawArgs[valueIdx])}\"`)\n }\n }\n\n if (entry.error) {\n const msg =\n typeof entry.error === 'object' && 'message' in entry.error\n ? (entry.error as { message: string }).message\n : String(entry.error)\n parts.push(`ERROR: ${msg}`)\n }\n\n lines.push(parts.join(' '))\n })\n\n return lines.join('\\n')\n}\n\ninterface TraceBundle {\n traceNdjson: string\n networkNdjson: Buffer\n transcriptMd: string\n resources: TraceZipResource[]\n}\n\nfunction buildTraceBundle(\n trace: TraceLog,\n opts: {\n sessionId?: string\n wallTimeOverride?: number\n testMetadata?: TestMetadataMap\n } = {}\n): TraceBundle {\n // wallTime anchors monotonic offsets at the first captured command so\n // subsequent actions render at positive deltas in the trace viewer.\n const firstCommandTs = trace.commands[0]?.timestamp\n const wallTime = opts.wallTimeOverride ?? firstCommandTs ?? Date.now()\n const idPrefix = shortId(opts.sessionId)\n const contextId = `context@${idPrefix}`\n const pageId = `page@${idPrefix}`\n const viewport = trace.metadata.viewport ?? { width: 1280, height: 720 }\n const snapshots = trace.actionSnapshots ?? []\n const ctxOptions = buildContextOptions(trace, contextId, wallTime)\n const events: TraceEvent[] = [ctxOptions]\n\n // Emit initial screencast-frame (timestamp=0) using the first snapshot's\n // resources so trace viewers show the page state before any interaction.\n const firstSnap = snapshots.find((s) => s.screenshot)\n if (firstSnap) {\n const base = `${pageId}-${firstSnap.timestamp}`\n const initFrame: ScreencastFrameEvent = {\n type: 'screencast-frame',\n pageId,\n sha1: `${base}.jpeg`,\n width: viewport.width,\n height: viewport.height,\n timestamp: 0\n }\n if (firstSnap.elements && firstSnap.elements.length) {\n initFrame.elements = `${base}-elements.json`\n }\n if (firstSnap.snapshotText) {\n initFrame.snapshot = `${base}-snapshot.txt`\n }\n events.push(initFrame)\n }\n\n events.push(\n // Skip the first snapshot in buildScreencastFrames — it was already emitted\n // as the initial t=0 frame above.\n ...buildScreencastFrames(\n firstSnap ? snapshots.filter((s) => s !== firstSnap) : snapshots,\n pageId,\n wallTime,\n viewport\n ),\n ...buildActionEvents(trace.commands, pageId, wallTime, opts.testMetadata)\n )\n events.sort(compareEvents)\n const ctxBName = ctxOptions.title\n return {\n traceNdjson: events.map((e) => JSON.stringify(e)).join('\\n') + '\\n',\n networkNdjson: buildNetworkNdjson(trace.networkRequests, wallTime, pageId),\n transcriptMd: generateTranscript(trace.commands, wallTime, ctxBName),\n resources: buildSnapshotResources(snapshots, pageId)\n }\n}\n\nexport async function exportTraceZip(\n trace: TraceLog,\n opts: {\n sessionId?: string\n wallTimeOverride?: number\n testMetadata?: TestMetadataMap\n } = {}\n): Promise<Buffer> {\n const bundle = buildTraceBundle(trace, opts)\n return buildTraceZip({\n traceNdjson: bundle.traceNdjson,\n networkNdjson: bundle.networkNdjson,\n resources: bundle.resources,\n transcriptMd: bundle.transcriptMd\n })\n}\n\nasync function exportTraceDirectory(\n trace: TraceLog,\n targetDir: string,\n opts: {\n sessionId?: string\n wallTimeOverride?: number\n testMetadata?: TestMetadataMap\n } = {}\n): Promise<void> {\n const bundle = buildTraceBundle(trace, opts)\n await fs.mkdir(path.join(targetDir, 'resources'), { recursive: true })\n await Promise.all([\n fs.writeFile(path.join(targetDir, 'trace.trace'), bundle.traceNdjson),\n fs.writeFile(\n path.join(targetDir, 'transcript.md'),\n bundle.transcriptMd,\n 'utf8'\n ),\n bundle.networkNdjson.length\n ? fs.writeFile(\n path.join(targetDir, 'trace.network'),\n bundle.networkNdjson\n )\n : Promise.resolve(),\n ...bundle.resources.map((r) =>\n fs.writeFile(path.join(targetDir, 'resources', r.resourceName), r.data)\n )\n ])\n}\n\n/** Minimum capturer surface needed to assemble a TraceLog. */\nexport interface TraceCapturer {\n mutations: TraceMutation[]\n traceLogs: string[]\n consoleLogs: ConsoleLog[]\n networkRequests: NetworkRequest[]\n commandsLog: CommandLog[]\n sources: Map<string, string>\n metadata?: Metadata\n startWallTime?: number\n}\n\nexport interface WriteTraceZipOptions {\n outputDir: string\n sessionId: string\n capabilities?: unknown\n /**\n * Per-action snapshots from a Phase-3-style hook. When omitted, snapshots\n * are synthesized from CommandLog entries that carry a screenshot so the\n * viewer still renders thumbnails for adapters without an action hook.\n */\n actionSnapshots?: ActionSnapshot[]\n /** Output layout — `zip` (default) writes a single archive, `directory`\n * unpacks the same files into `trace-<id>/`. */\n format?: TraceFormat\n /** Test metadata keyed by testUid for Tracing.tracingGroup events. */\n testMetadata?: TestMetadataMap\n}\n\n/**\n * Build a TraceLog from a SessionCapturer-shaped source and write the trace\n * artifact (zip file or directory). Returns the absolute path written.\n */\nexport async function writeTraceZip(\n capturer: TraceCapturer,\n opts: WriteTraceZipOptions\n): Promise<string> {\n const baseMetadata = capturer.metadata ?? ({} as Metadata)\n const actionSnapshots =\n opts.actionSnapshots ??\n synthesizeSnapshotsFromCommands(capturer.commandsLog)\n const traceLog: TraceLog = {\n mutations: capturer.mutations,\n logs: capturer.traceLogs,\n consoleLogs: capturer.consoleLogs,\n networkRequests: capturer.networkRequests,\n metadata: {\n ...baseMetadata,\n ...(opts.capabilities\n ? { capabilities: opts.capabilities as Metadata['capabilities'] }\n : {})\n },\n commands: capturer.commandsLog,\n sources: Object.fromEntries(capturer.sources),\n ...(actionSnapshots.length ? { actionSnapshots } : {})\n }\n await fs.mkdir(opts.outputDir, { recursive: true })\n const exportOpts = {\n sessionId: opts.sessionId,\n wallTimeOverride: capturer.startWallTime,\n testMetadata: opts.testMetadata\n }\n if (opts.format === 'ndjson-directory') {\n const dir = path.join(opts.outputDir, `trace-${opts.sessionId}`)\n await fs.mkdir(dir, { recursive: true })\n await exportTraceDirectory(traceLog, dir, exportOpts)\n return dir\n }\n const zip = await exportTraceZip(traceLog, exportOpts)\n const zipPath = path.join(opts.outputDir, `trace-${opts.sessionId}.zip`)\n await fs.writeFile(zipPath, zip)\n return zipPath\n}\n\nfunction synthesizeSnapshotsFromCommands(\n commands: CommandLog[]\n): ActionSnapshot[] {\n return commands\n .filter((c) => c.screenshot && mapCommandToAction(c.command))\n .map((c) => ({\n timestamp: c.timestamp,\n command: c.command,\n screenshot: c.screenshot\n }))\n}\n","// Convert the existing NetworkRequest shape into trace format\n// `resource-snapshot` NDJSON entries (HAR-flavoured) for trace.zip.\n\nimport type { NetworkRequest } from '@wdio/devtools-shared'\n\nexport interface ResourceSnapshotEntry {\n type: 'resource-snapshot'\n snapshot: {\n startedDateTime: string\n time: number\n request: {\n method: string\n url: string\n httpVersion: string\n cookies: unknown[]\n headers: { name: string; value: string }[]\n queryString: { name: string; value: string }[]\n headersSize: number\n bodySize: number\n }\n response: {\n status: number\n statusText: string\n httpVersion: string\n cookies: unknown[]\n headers: { name: string; value: string }[]\n content: { size: number; mimeType: string }\n redirectURL: string\n headersSize: number\n bodySize: number\n }\n cache: Record<string, never>\n timings: { send: number; wait: number; receive: number }\n }\n}\n\nfunction toHeaderArray(\n h: Record<string, string> | undefined\n): { name: string; value: string }[] {\n if (!h) {\n return []\n }\n return Object.entries(h).map(([name, value]) => ({ name, value }))\n}\n\nfunction toQueryString(url: string): { name: string; value: string }[] {\n try {\n const u = new URL(url)\n const out: { name: string; value: string }[] = []\n u.searchParams.forEach((value, name) => out.push({ name, value }))\n return out\n } catch {\n return []\n }\n}\n\nexport function networkRequestToHar(\n entry: NetworkRequest\n): ResourceSnapshotEntry {\n const startedDateTime = new Date(entry.timestamp).toISOString()\n const duration =\n entry.time ?? (entry.endTime ?? entry.startTime) - entry.startTime\n const status = entry.response?.status ?? entry.status ?? 0\n const mimeType = entry.response?.mimeType ?? ''\n const responseHeaders = entry.response?.headers ?? entry.responseHeaders\n return {\n type: 'resource-snapshot',\n snapshot: {\n startedDateTime,\n time: Math.max(0, duration),\n request: {\n method: entry.method,\n url: entry.url,\n httpVersion: 'HTTP/1.1',\n cookies: [],\n headers: toHeaderArray(entry.requestHeaders ?? entry.headers),\n queryString: toQueryString(entry.url),\n headersSize: -1,\n bodySize: entry.requestBody ? entry.requestBody.length : -1\n },\n response: {\n status,\n statusText: entry.statusText ?? '',\n httpVersion: 'HTTP/1.1',\n cookies: [],\n headers: toHeaderArray(responseHeaders),\n content: { size: entry.size ?? 0, mimeType },\n redirectURL: '',\n headersSize: -1,\n bodySize: entry.size ?? -1\n },\n cache: {},\n timings: {\n send: 0,\n wait: Math.max(0, duration),\n receive: 0\n }\n }\n }\n}\n","// Thin yazl wrapper that packages a trace into a single Buffer.\n// Ported from Vince Graics' PR #209.\n\nimport yazl from 'yazl'\n\nexport interface TraceZipResource {\n /** Path inside the zip, e.g. `resources/page@xxx-12345.jpeg`. */\n resourceName: string\n data: Buffer\n}\n\nexport interface TraceZipInputs {\n /** NDJSON action events (one JSON object per line). */\n traceNdjson: string\n /** NDJSON HAR resource-snapshot entries. Empty buffer when omitted. */\n networkNdjson: Buffer\n /** Human/LLM-readable Markdown transcript. */\n transcriptMd?: string\n /** Files written under `resources/` — typically screenshots + element snapshots. */\n resources: TraceZipResource[]\n}\n\nexport function buildTraceZip(inputs: TraceZipInputs): Promise<Buffer> {\n return new Promise((resolve, reject) => {\n const zipFile = new yazl.ZipFile()\n zipFile.addBuffer(Buffer.from(inputs.traceNdjson, 'utf8'), 'trace.trace')\n zipFile.addBuffer(inputs.networkNdjson, 'trace.network')\n if (inputs.transcriptMd) {\n zipFile.addBuffer(\n Buffer.from(inputs.transcriptMd, 'utf8'),\n 'transcript.md'\n )\n }\n for (const resource of inputs.resources) {\n zipFile.addBuffer(resource.data, `resources/${resource.resourceName}`)\n }\n zipFile.end()\n const chunks: Buffer[] = []\n zipFile.outputStream.on('data', (chunk: Buffer) => chunks.push(chunk))\n zipFile.outputStream.on('end', () => resolve(Buffer.concat(chunks)))\n zipFile.outputStream.on('error', reject)\n })\n}\n","// Stable UID generation for tests and suites. The hash function is a tiny\n// djb2-style char-code accumulator that produces compact base36 strings.\n// \"Stable\" means: the same input produces the same output across runs.\n\n/**\n * Hash arbitrary string parts into a stable, deterministic UID. Calling this\n * multiple times with the same inputs always returns the same value — no\n * counter, no hidden state. Use for entities that must map to the same UID\n * across retries (Cucumber scenarios, feature steps, etc.).\n */\nexport function deterministicUid(...parts: string[]): string {\n const hash = parts\n .join('::')\n .split('')\n .reduce((acc, char) => ((acc << 5) - acc + char.charCodeAt(0)) | 0, 0)\n return `stable-${Math.abs(hash).toString(36)}`\n}\n\n// Counter for disambiguating repeated (file, name) signatures within a single\n// test run. Cleared by resetSignatureCounters() between runs.\nconst signatureCounters = new Map<string, number>()\n\n/**\n * Generate a UID from a (file, name) pair, disambiguating repeated calls with\n * the same inputs via an in-run counter. Use for test/suite identity where\n * the same file::name combo may legitimately appear multiple times in one run\n * (e.g. parameterised tests). For entities that must produce the same UID on\n * every retry (Cucumber scenarios), use {@link deterministicUid} instead.\n */\nexport function generateStableUid(file: string, name: string): string {\n const signature = `${file}::${name}`\n const count = signatureCounters.get(signature) ?? 0\n signatureCounters.set(signature, count + 1)\n const input = count > 0 ? `${signature}::${count}` : signature\n return deterministicUid(input)\n}\n\n/** Reset the signature counter map. Call at the start of each test run. */\nexport function resetSignatureCounters(): void {\n signatureCounters.clear()\n}\n","import fs from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\nimport type { ScreencastInfo } from '@wdio/devtools-shared'\n\nimport type { ScreencastRecorderBase } from './screencast.js'\nimport { errorMessage } from './error.js'\nimport { encodeToVideo } from './video-encoder.js'\n\nexport interface FinalizeScreencastInput {\n recorder: ScreencastRecorderBase\n sessionId: string\n /** Filename without the .webm suffix (e.g. 'wdio-video', 'selenium-video'). */\n filenamePrefix: string\n /** Preferred output dir; falls back to cwd, then os.tmpdir() if unwritable. */\n outputDir?: string\n /** Skip encoding when the recorder collected fewer frames than this. */\n minFrames?: number\n captureFormat?: 'jpeg' | 'png'\n /** Forward the encoded-video metadata to the dashboard. */\n sendUpstream: (scope: string, data: ScreencastInfo) => void\n /** Optional hook for adapter-side logging on each lifecycle step. */\n onLog?: (level: 'info' | 'warn', message: string) => void\n}\n\n/**\n * Stop the recorder, encode its frames to a `.webm` (preferred dir → cwd →\n * tmpdir), and forward the metadata to the dashboard. All errors are caught\n * and reported via `onLog` — screencast is best-effort and must not abort the\n * run on stop/encode failure.\n *\n * Shared across all three adapters: each one provides only the recorder\n * subclass, the filename prefix, and a sendUpstream binding to its\n * SessionCapturer.\n */\nexport async function finalizeScreencast({\n recorder,\n sessionId,\n filenamePrefix,\n outputDir,\n minFrames = 1,\n captureFormat,\n sendUpstream,\n onLog\n}: FinalizeScreencastInput): Promise<void> {\n const log = (level: 'info' | 'warn', message: string) =>\n onLog?.(level, message)\n\n try {\n await recorder.stop()\n } catch (err) {\n log('warn', `Screencast stop failed: ${errorMessage(err)}`)\n return\n }\n\n const frames = recorder.frames\n if (frames.length < minFrames) {\n return\n }\n\n const fileName = `${filenamePrefix}-${sessionId}.webm`\n const candidate = outputDir || process.cwd()\n let videoPath = path.join(candidate, fileName)\n try {\n fs.accessSync(candidate, fs.constants.W_OK)\n } catch {\n videoPath = path.join(os.tmpdir(), fileName)\n }\n\n try {\n await encodeToVideo(frames, videoPath, { captureFormat })\n log('info', `📹 Screencast video: ${videoPath}`)\n sendUpstream('screencast', {\n sessionId,\n videoPath,\n videoFile: fileName,\n frameCount: frames.length,\n duration: recorder.duration,\n startTime: frames[0]?.timestamp\n })\n } catch (err) {\n log('warn', `Screencast encode failed: ${errorMessage(err)}`)\n }\n}\n","// VP8/WebM encoder for screencast frames. Loads fluent-ffmpeg lazily via\n// createRequire so the dep stays optional — adapters that ship screencast\n// support are expected to list fluent-ffmpeg in their own dependencies.\n\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport os from 'node:os'\nimport { createRequire } from 'node:module'\n\nimport type { ScreencastFrame, ScreencastOptions } from '@wdio/devtools-shared'\n\nconst require = createRequire(import.meta.url)\n\n/**\n * Encode an array of CDP screencast frames into a .webm file using ffmpeg\n * (via fluent-ffmpeg) and the VP8 codec (libvpx).\n *\n * Strategy:\n * 1. Write each frame as a JPEG (or PNG) file in a temp directory.\n * 2. Write an ffconcat manifest that assigns each frame its exact display\n * duration based on the inter-frame timestamp delta. Variable-frame-rate\n * output reflects real timing even across long command pauses.\n * 3. Run ffmpeg with the concat demuxer → libvpx (VP8) → .webm output.\n * Force CFR at 10fps — VFR WebMs don't write Cues reliably, so the\n * dashboard `<video>` can't read duration/seek without it.\n * 4. Clean up the temp directory regardless of success or failure.\n *\n * @throws If no frames are provided, fluent-ffmpeg is not installed, or\n * the ffmpeg binary is not found on PATH.\n */\n// fluent-ffmpeg is loaded lazily and its types aren't worth pinning — we\n// only call a tiny chained builder API on it. `unknown` at the boundary\n// + a single cast in the runner keeps the surface honest.\ntype FfmpegBuilder = {\n input(path: string): FfmpegBuilder\n inputOptions(opts: string[]): FfmpegBuilder\n videoCodec(name: string): FfmpegBuilder\n outputOptions(opts: string[]): FfmpegBuilder\n output(path: string): FfmpegBuilder\n on(event: 'end', cb: () => void): FfmpegBuilder\n on(event: 'error', cb: (err: Error) => void): FfmpegBuilder\n run(): void\n}\ntype FfmpegFactory = () => FfmpegBuilder\n\nfunction loadFfmpeg(): FfmpegFactory {\n try {\n return require('fluent-ffmpeg') as FfmpegFactory\n } catch {\n throw new Error(\n 'VideoEncoder: fluent-ffmpeg is required for screencast encoding. ' +\n 'Install it with: npm install fluent-ffmpeg'\n )\n }\n}\n\nasync function writeFramesAndManifest(\n frames: ScreencastFrame[],\n tmpDir: string,\n ext: string\n): Promise<string> {\n const manifestLines: string[] = ['ffconcat version 1.0']\n for (let i = 0; i < frames.length; i++) {\n const frameName = `frame-${String(i).padStart(6, '0')}.${ext}`\n const framePath = path.join(tmpDir, frameName)\n await fs.writeFile(framePath, Buffer.from(frames[i].data, 'base64'))\n const nextTs = frames[i + 1]?.timestamp ?? frames[i].timestamp + 100\n const durationSecs = Math.max((nextTs - frames[i].timestamp) / 1000, 0.01)\n manifestLines.push(`file '${framePath}'`)\n manifestLines.push(`duration ${durationSecs.toFixed(6)}`)\n }\n // The last frame needs to appear twice in the manifest — ffconcat ignores\n // the final `duration` directive without a trailing `file` line.\n const lastFramePath = path.join(\n tmpDir,\n `frame-${String(frames.length - 1).padStart(6, '0')}.${ext}`\n )\n manifestLines.push(`file '${lastFramePath}'`)\n const manifestPath = path.join(tmpDir, 'manifest.txt')\n await fs.writeFile(manifestPath, manifestLines.join('\\n'))\n return manifestPath\n}\n\nfunction classifyFfmpegError(err: Error): Error {\n const msg = err.message || ''\n if (\n msg.includes('Cannot find ffmpeg') ||\n msg.includes('ENOENT') ||\n msg.includes('spawn') ||\n msg.includes('not found')\n ) {\n return new Error(\n 'VideoEncoder: ffmpeg binary not found on PATH. ' +\n 'Install ffmpeg: https://ffmpeg.org/download.html'\n )\n }\n return new Error(`VideoEncoder: ffmpeg error — ${msg}`)\n}\n\nfunction runFfmpeg(\n ffmpeg: FfmpegFactory,\n manifestPath: string,\n outputPath: string\n): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n ffmpeg()\n .input(manifestPath)\n .inputOptions(['-f', 'concat', '-safe', '0'])\n .videoCodec('libvpx')\n .outputOptions([\n '-b:v',\n '1M',\n '-pix_fmt',\n 'yuv420p',\n '-vsync',\n 'cfr',\n '-r',\n '10',\n // Force a keyframe every frame. VP8 can only seek to keyframes, and\n // libvpx's default GOP places a single keyframe on near-static\n // screencast content — so scrubber/marker seeks snap back to the\n // start. At 10fps over short recordings, all-intra keeps seeking\n // frame-accurate at an acceptable size.\n '-g',\n '1',\n '-keyint_min',\n '1',\n '-auto-alt-ref',\n '0',\n '-disposition:v',\n 'default'\n ])\n .output(outputPath)\n .on('end', () => resolve())\n .on('error', (err: Error) => reject(classifyFfmpegError(err)))\n .run()\n })\n}\n\nexport async function encodeToVideo(\n frames: ScreencastFrame[],\n outputPath: string,\n options: Pick<ScreencastOptions, 'captureFormat'> = {}\n): Promise<void> {\n if (frames.length === 0) {\n throw new Error('VideoEncoder: no frames to encode')\n }\n const ffmpeg = loadFfmpeg()\n const ext = options.captureFormat === 'png' ? 'png' : 'jpg'\n const tmpDir = await fs.mkdtemp(\n path.join(os.tmpdir(), 'devtools-screencast-')\n )\n try {\n const manifestPath = await writeFramesAndManifest(frames, tmpDir, ext)\n await runFfmpeg(ffmpeg, manifestPath, outputPath)\n } finally {\n await fs.rm(tmpDir, { recursive: true, force: true }).catch(() => {\n /* tmp cleanup is best-effort */\n })\n }\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\n\nexport interface ResolveAdapterOutputDirInput {\n /**\n * Honored as-is if set — used by adapters that expose a user-facing\n * `outputDir` option (e.g. WDIO). Skips all other resolution steps.\n */\n userConfiguredDir?: string\n /**\n * Absolute path to the current test file. When known, the video / trace\n * lands in the same folder as the spec the user just ran. This is the\n * preferred location across adapters.\n */\n testFilePath?: string\n /**\n * Absolute path to the resolved framework config file (wdio.conf.ts,\n * nightwatch.conf.cjs, etc.). Used as a fallback when the test file\n * isn't known.\n */\n configPath?: string\n /** Last-resort fallback. Defaults to `process.cwd()`. */\n fallbackDir?: string\n}\n\nconst NODE_MODULES_SEGMENT = `${path.sep}node_modules${path.sep}`\n\nfunction isWritable(dir: string): boolean {\n try {\n fs.accessSync(dir, fs.constants.W_OK)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Resolve the directory where an adapter should write output files\n * (screencast .webm, trace JSON, etc.).\n *\n * Priority:\n * 1. `userConfiguredDir` — explicit opt-in, honored as-is.\n * 2. `dirname(testFilePath)` — same folder as the spec that just ran.\n * 3. `dirname(configPath)` — fallback to the framework config dir.\n * 4. `fallbackDir` (default `process.cwd()`).\n *\n * Any candidate inside a `node_modules/` segment is skipped — this can\n * happen in symlinked workspaces where the test file resolves through a\n * linked dependency. Each candidate must also be writable; non-writable\n * dirs fall through to the next.\n *\n * Shared by all three adapters (service / nightwatch-devtools /\n * selenium-devtools) so the output location stays consistent regardless\n * of where the user invoked the runner from. See CLAUDE.md §2.2.\n */\nexport function resolveAdapterOutputDir(\n input: ResolveAdapterOutputDirInput = {}\n): string {\n const fallback = input.fallbackDir ?? process.cwd()\n // userConfiguredDir bypasses the node_modules and writability filters\n // because the user opted into it explicitly — surprising overrides are\n // worse than failing loudly here.\n if (input.userConfiguredDir) {\n return input.userConfiguredDir\n }\n const candidates: string[] = []\n if (input.testFilePath) {\n candidates.push(path.dirname(input.testFilePath))\n }\n if (input.configPath) {\n candidates.push(path.dirname(input.configPath))\n }\n candidates.push(fallback)\n for (const dir of candidates) {\n if (!dir) {\n continue\n }\n if (dir.includes(NODE_MODULES_SEGMENT)) {\n continue\n }\n if (isWritable(dir)) {\n return dir\n }\n }\n return fallback\n}\n","import type {\n CommandLog,\n DocumentInfo,\n PerformanceData\n} from '@wdio/devtools-shared'\n\n/**\n * JS source that captures Performance API data, cookies, and document info\n * from the page under test. Passed as a string to the adapter's `execute`/\n * `executeScript` driver method so the browser-only types (PerformanceEntry,\n * Document) don't leak into the Node-side type-checker.\n *\n * Returns the bag shape consumed by {@link applyPerformanceData}.\n * Framework-agnostic — all three adapters can use it.\n */\nexport const CAPTURE_PERFORMANCE_SCRIPT = `\n (function() {\n const performance = window.performance;\n const navigation = performance.getEntriesByType?.('navigation')?.[0];\n const resources = performance.getEntriesByType?.('resource') || [];\n\n return {\n navigation: navigation ? {\n url: window.location.href,\n timing: {\n loadTime: navigation.loadEventEnd - navigation.fetchStart,\n domReady: navigation.domContentLoadedEventEnd - navigation.fetchStart,\n responseTime: navigation.responseEnd - navigation.requestStart,\n dnsLookup: navigation.domainLookupEnd - navigation.domainLookupStart,\n tcpConnection: navigation.connectEnd - navigation.connectStart,\n serverResponse: navigation.responseEnd - navigation.responseStart\n }\n } : undefined,\n resources: resources.map(function(resource) {\n return {\n url: resource.name,\n duration: resource.duration,\n size: resource.transferSize || 0,\n type: resource.initiatorType,\n startTime: resource.startTime,\n responseEnd: resource.responseEnd\n };\n }),\n cookies: (function() {\n try { return document.cookie; } catch (e) { return ''; }\n })(),\n documentInfo: {\n url: window.location.href,\n title: document.title,\n headers: {\n userAgent: navigator.userAgent,\n language: navigator.language,\n platform: navigator.platform\n },\n documentInfo: {\n readyState: document.readyState,\n referrer: document.referrer,\n characterSet: document.characterSet\n }\n }\n };\n })()\n`\n\n/** Untyped bag returned by {@link CAPTURE_PERFORMANCE_SCRIPT}. */\nexport interface CapturedPerformancePayload {\n navigation?: PerformanceData['navigation']\n resources?: PerformanceData['resources']\n cookies?: string\n documentInfo?: DocumentInfo\n}\n\n/**\n * Apply a captured performance payload onto a CommandLog entry in-place,\n * setting `performance`, `cookies`, `documentInfo`, and a synthesized `result`\n * matching nightwatch's existing dashboard shape. Returns `true` if anything\n * was applied — caller can branch on this to skip further work.\n */\nexport function applyPerformanceData(\n command: CommandLog,\n payload: CapturedPerformancePayload | undefined,\n navigatedUrl?: string\n): boolean {\n if (!payload || !payload.navigation) {\n return false\n }\n command.performance = {\n navigation: payload.navigation,\n resources: payload.resources\n }\n command.cookies = payload.cookies\n command.documentInfo = payload.documentInfo\n command.result = {\n url: navigatedUrl,\n loadTime: payload.navigation?.timing?.loadTime,\n resources: payload.resources,\n resourceCount: payload.resources?.length,\n cookies: payload.cookies,\n title: payload.documentInfo?.title\n }\n return true\n}\n","/**\n * Tiny state holder for command-retry detection. Both the selenium and\n * nightwatch adapters need exactly this same pattern: compute a stable\n * signature for the incoming command, compare it to the last one we\n * captured, and treat a match as \"the framework is retrying — replace the\n * previous entry instead of pushing a new one\".\n *\n * The signature is JSON-stringified `{command, args, src: callSource}`. Test\n * boundaries (new test, new scenario) call `reset()` to drop the last\n * signature so a deliberate re-run of the same call counts as a fresh\n * command, not a retry.\n */\nexport class RetryTracker {\n #lastSig: string | null = null\n #lastId: number | null = null\n\n /** Build the canonical signature used for retry-equality checks. */\n static signature(\n command: string,\n args: unknown,\n callSource?: string\n ): string {\n return JSON.stringify({ command, args, src: callSource ?? null })\n }\n\n /** True when the incoming signature matches the last captured one AND we\n * have an id to replace (otherwise there's nothing to replace yet). */\n isRetry(sig: string): boolean {\n return sig === this.#lastSig && this.#lastId !== null\n }\n\n /** The id of the last captured command, if any (for the replace-in-place\n * flow). */\n get lastId(): number | null {\n return this.#lastId\n }\n\n /** Record a fresh capture — sets both sig and id together. */\n recordCapture(sig: string, id: number | null): void {\n this.#lastSig = sig\n this.#lastId = id\n }\n\n /** Record only the id (used by adapters that compute the sig but defer the\n * id assignment to after an async capture call). */\n setLastId(id: number | null): void {\n this.#lastId = id\n }\n\n /** Stage the sig before an async capture so the next call already sees the\n * signature change (prevents stale-sig matches on rapid back-to-back\n * commands). Pair with {@link setLastId} once the capture resolves. */\n setLastSig(sig: string): void {\n this.#lastSig = sig\n }\n\n /** Reset at test/scenario boundaries so the next capture is \"fresh\". */\n reset(): void {\n this.#lastSig = null\n this.#lastId = null\n }\n}\n","// File-pattern regexes shared across packages — keep one canonical form\n// per concept so changes to \"what counts as a test file\" or \"what counts\n// as a Cucumber feature file\" propagate everywhere. See CLAUDE.md §2.1.\n\n/** Matches `*.test.ts`, `*.spec.ts`, `*.test.cjs`, etc. — the test-runner\n * convention used by every adapter to recognize spec files. */\nexport const SPEC_FILE_RE = /\\.(?:test|spec)\\.[cm]?[jt]sx?$/i\n\n/** Matches `*.feature` — Cucumber feature files. */\nexport const FEATURE_FILE_RE = /\\.feature$/i\n","/**\n * WebSocket upgrade paths on the backend's Fastify server. Each adapter opens\n * one socket at `worker`; one or more browser tabs subscribe at `client`.\n *\n * The HTTP API endpoints under `/api/baseline/*` live in `./baseline.ts`.\n */\nexport const WS_PATHS = {\n /** Adapter session upgrade endpoint. One socket per running adapter. */\n worker: '/worker',\n /** App/UI client upgrade endpoint. Multiple browser tabs may connect. */\n client: '/client'\n} as const\n\n/**\n * Control-frame scopes exchanged over the worker↔backend↔client WS channels.\n * `BASELINE_WS_SCOPE` (in `./baseline.ts`) covers the baseline-specific\n * scopes; this object covers the runtime control frames. Single source of\n * truth — typos previously caused silent breakage when one end of the wire\n * sent a string the other end didn't recognize.\n */\nexport const WS_SCOPE = {\n /** Backend → worker: a dashboard client has subscribed. Wakes up the\n * adapter's `await UI ready` gate before tests start. */\n clientConnected: 'clientConnected',\n /** Backend → worker: the last dashboard tab closed. Triggers the\n * interactive shutdown flow (close WS, exit, pkill the dashboard). */\n clientDisconnected: 'clientDisconnected',\n /** Worker → backend → clients (or app-local): wipe the visualization data\n * for a specific test/suite uid (or the whole tree). */\n clearExecutionData: 'clearExecutionData',\n /** Worker → backend: clear-by-test-uid request (drives clearExecutionData). */\n clearCommands: 'clearCommands',\n /** Backend → clients: signal that the active run was stopped by the UI. */\n testStopped: 'testStopped',\n /** Worker → backend → clients: swap an earlier captured command for an\n * updated entry (used when a retry coalesces commands). */\n replaceCommand: 'replaceCommand',\n /** Worker → backend: register the config file path so reruns can spawn\n * with the same config. */\n config: 'config'\n} as const\n\nexport type WsScope = (typeof WS_SCOPE)[keyof typeof WS_SCOPE]\n","/**\n * HTTP contracts for the runner endpoints. Imported by the backend route\n * handlers and the app's fetch callers — keeps the body shape in lockstep\n * across the wire instead of relying on `Record<string, unknown>`.\n */\n\nexport const TESTS_API = {\n run: '/api/tests/run',\n stop: '/api/tests/stop'\n} as const\n\n/**\n * Environment variables the backend's rerun spawner sets on the child\n * process so the adapter (service/nightwatch/selenium) can detect the\n * reuse-mode handshake and connect to the existing dashboard backend\n * instead of starting a new one. Single source of truth — typos in any\n * leg of the handshake silently break reruns, so all four packages\n * (backend writer + three adapter readers) reference this object.\n */\nexport const REUSE_ENV = {\n REUSE: 'DEVTOOLS_APP_REUSE',\n HOST: 'DEVTOOLS_APP_HOST',\n PORT: 'DEVTOOLS_APP_PORT',\n RERUN_LABEL: 'DEVTOOLS_RERUN_LABEL',\n RERUN_ENTRY_TYPE: 'DEVTOOLS_RERUN_ENTRY_TYPE'\n} as const\n\n/**\n * Environment variables the WDIO service writes during `onPrepare` (config\n * path it detected, initial --spec args) so the backend's rerun spawner can\n * relaunch with the same config. Also covers DEVTOOLS_RUNNER_CWD which the\n * backend reads to know which directory to spawn the child in. Bin-override\n * vars (DEVTOOLS_WDIO_BIN, DEVTOOLS_NIGHTWATCH_BIN) live here too — they're\n * test-rig overrides that backend's bin-resolver respects.\n */\nexport const RUNNER_ENV = {\n WDIO_CONFIG: 'DEVTOOLS_WDIO_CONFIG',\n NIGHTWATCH_CONFIG: 'DEVTOOLS_NIGHTWATCH_CONFIG',\n WDIO_INITIAL_SPECS: 'DEVTOOLS_WDIO_INITIAL_SPECS',\n RUNNER_CWD: 'DEVTOOLS_RUNNER_CWD',\n WDIO_BIN: 'DEVTOOLS_WDIO_BIN',\n NIGHTWATCH_BIN: 'DEVTOOLS_NIGHTWATCH_BIN'\n} as const\n\n/** POST /api/tests/run body. */\nexport interface RunnerRequestBody {\n uid: string\n entryType: 'suite' | 'test'\n specFile?: string\n fullTitle?: string\n label?: string\n callSource?: string\n runAll?: boolean\n framework?: string\n configFile?: string\n lineNumber?: number\n devtoolsHost?: string\n devtoolsPort?: number\n featureFile?: string\n featureLine?: number\n suiteType?: string\n rerunCommand?: string\n launchCommand?: string\n preserveBaseline?: boolean\n}\n\n/** 200 response from /api/tests/run and /api/tests/stop. */\nexport interface RunnerOkResponse {\n ok: true\n}\n\n/** 4xx response shape from runner endpoints. */\nexport interface RunnerErrorResponse {\n error: string\n}\n","/**\n * Cross-adapter timing + identifier defaults.\n *\n * The TIMING values are wall-clock milliseconds the adapter's plugin loop\n * waits for (UI render, test/suite boundaries, browser close, WS connection\n * setup). The DEFAULTS provide the canonical TestStats / SuiteStats field\n * defaults so every adapter produces the same shape on the wire.\n *\n * Adapters spread these into their local TIMING / DEFAULTS objects so they\n * can override per-framework values (Nightwatch's UI_CONNECTION_WAIT is\n * higher because its boot is slower; each adapter has its own SESSION_TITLE\n * / TEST_NAME placeholder).\n */\n\nexport const TIMING_BASE = {\n /** ms to let the dashboard render between rapid state updates. */\n UI_RENDER_DELAY: 150,\n /** ms to wait after a test starts before flushing initial state. */\n TEST_START_DELAY: 100,\n /** ms gap between consecutive suite-finalize broadcasts. */\n SUITE_COMPLETE_DELAY: 200,\n /** ms allowed for the browser-close handshake before forcing teardown. */\n BROWSER_CLOSE_WAIT: 2000,\n /** ms before first worker WS connection attempt — lets backend bind. */\n INITIAL_CONNECTION_WAIT: 500,\n /** ms between browser-window-alive polls. */\n BROWSER_POLL_INTERVAL: 1000\n} as const\n\nexport const DEFAULTS_BASE = {\n /** Synthetic capability ID — present on every CommandLog / TestStats. */\n CID: '0-0',\n /** TestStats.retries default — adapters that surface retry counts override. */\n RETRIES: 0,\n /** _duration placeholder for not-yet-finalized stats. */\n DURATION: 0\n} as const\n","// Canonical type definitions shared across @wdio/devtools-* packages.\n//\n// Adapters (service, nightwatch-devtools, selenium-devtools) produce events of\n// these shapes. The backend stores and forwards them. The app consumes them.\n// See ARCHITECTURE.md §2 and CLAUDE.md §2.1.\n\nexport type LogLevel = 'trace' | 'debug' | 'log' | 'info' | 'warn' | 'error'\n\n/** Where a captured ConsoleLog entry originated. */\nexport type LogSource = 'browser' | 'test' | 'terminal'\n\nexport enum TraceType {\n Standalone = 'standalone',\n Testrunner = 'testrunner'\n}\n\nexport type TestStatus = 'passed' | 'failed' | 'skipped' | 'pending' | 'running'\n\n/** `live` opens the DevTools UI window; `trace` skips it and lets a downstream exporter consume captured state. */\nexport type DevToolsMode = 'live' | 'trace'\n\n/** `zip` (default) writes a single `trace-<id>.zip`; `ndjson-directory` writes\n * the same `trace.trace` + `trace.network` + `resources/` layout unpacked\n * into `trace-<id>/`. Both are consumable by `playwright show-trace` — the\n * unpacked form skips the unzip step for agentic / scripted consumers. */\nexport type TraceFormat = 'zip' | 'ndjson-directory'\n\n/** `session` (default) writes one trace per worker session; `spec` writes one\n * trace per spec file, keyed on the spec's filename. Only applies in trace mode. */\nexport type TraceGranularity = 'session' | 'spec'\n\n/** Test metadata keyed by testUid — maps stable test IDs to human-readable\n * title + specFile for Tracing.tracingGroup events in trace output. */\nexport type TestMetadataMap = Map<string, { title: string; specFile: string }>\n\n/**\n * Enum-style accessor for the canonical TestStatus values. Adapter code uses\n * this for readable comparisons (`state === TEST_STATE.PASSED`). The app's\n * sidebar has a parallel `TestState` accessor with the same values; that's a\n * naming holdover (PascalCase enum-style) — both can coexist.\n */\nexport const TEST_STATE = {\n PENDING: 'pending',\n RUNNING: 'running',\n PASSED: 'passed',\n FAILED: 'failed',\n SKIPPED: 'skipped'\n} as const satisfies Record<string, TestStatus>\n\n/**\n * Identifier sent by each adapter on RunnerRequestBody.framework. Used by the\n * backend's runner to pick rerun CLI args. This is technically the *test\n * runner* identifier rather than the higher-level framework (wdio/nightwatch/\n * selenium) — wdio's runner can be mocha/jasmine/cucumber, nightwatch can be\n * vanilla or cucumber, selenium adapters report 'selenium-webdriver'.\n */\nexport type TestRunnerId =\n | 'mocha'\n | 'jasmine'\n | 'cucumber'\n | 'nightwatch'\n | 'nightwatch-cucumber'\n | 'selenium-webdriver'\n\n// ─── Inner event payloads ───────────────────────────────────────────────────\n\nexport interface PerformanceData {\n navigation?: {\n url: string\n timing: {\n loadTime?: number\n domReady?: number\n responseTime?: number\n dnsLookup?: number\n tcpConnection?: number\n serverResponse?: number\n }\n }\n resources?: Array<{\n url: string\n duration: number\n size: number\n type: string\n startTime: number\n responseEnd: number\n }>\n}\n\nexport interface DocumentInfo {\n url: string\n title: string\n headers: { userAgent: string; language: string; platform: string }\n documentInfo: { readyState: string; referrer: string; characterSet: string }\n}\n\nexport interface CommandLog {\n command: string\n args: unknown[]\n result?: unknown\n error?: Error | { name: string; message: string; stack?: string }\n timestamp: number\n /** Wall-clock ms when the command was invoked (before execution). */\n startTime?: number\n callSource?: string\n screenshot?: string\n testUid?: string\n performance?: PerformanceData\n cookies?: string\n documentInfo?: DocumentInfo\n id?: number\n}\n\n/**\n * Payload broadcast under the WS scope `'replaceCommand'`. Tells the UI to\n * swap an existing CommandLog in-place — used when an adapter reconciles a\n * preliminary entry with the actual final result (e.g. selenium's\n * driverPatcher emits a placeholder, then replaces it once the command\n * resolves).\n */\nexport interface ReplaceCommandWsPayload {\n oldTimestamp: number\n command: CommandLog\n}\n\nexport interface ConsoleLog {\n type: LogLevel\n args: unknown[]\n timestamp: number\n source?: LogSource\n}\n\nexport interface NetworkRequest {\n id: string\n url: string\n method: string\n headers?: Record<string, string>\n cookies?: unknown[]\n status?: number\n statusText?: string\n timestamp: number\n startTime: number\n endTime?: number\n time?: number\n type: string\n initiator?: string\n requestHeaders?: Record<string, string>\n responseHeaders?: Record<string, string>\n navigation?: string\n redirectChain?: unknown[]\n children?: NetworkRequest[]\n response?: {\n fromCache: boolean\n headers: Record<string, string>\n mimeType: string\n status: number\n }\n error?: string\n requestBody?: string\n responseBody?: string\n size?: number\n}\n\n// ─── Trace and metadata ─────────────────────────────────────────────────────\n\nexport interface Viewport {\n width: number\n height: number\n offsetLeft: number\n offsetTop: number\n scale: number\n}\n\nexport interface ScreencastInfo {\n sessionId?: string\n videoPath?: string\n videoFile?: string\n frameCount?: number\n duration?: number\n startTime?: number\n}\n\n/** Single captured screencast frame — base64 image + capture timestamp (ms). */\nexport interface ScreencastFrame {\n /** Base64-encoded image data — JPEG/PNG from CDP push mode or PNG from browser.takeScreenshot() in polling mode. */\n data: string\n /** Unix timestamp in milliseconds. */\n timestamp: number\n}\n\n/**\n * Screencast recorder configuration. Used by every adapter — the base recorder\n * in `@wdio/devtools-core` consumes this shape; per-adapter wrappers extend it\n * (e.g. WDIO's CDP fast-path opts).\n */\nexport interface ScreencastOptions {\n /** Enable screencast recording for this session (default: false). */\n enabled?: boolean\n /**\n * Image format for individual frames (default: 'jpeg').\n * - Chrome/Chromium (CDP mode): controls the format Chrome sends over CDP.\n * - Other browsers (polling mode): screenshots are always PNG; ignored.\n * Does NOT affect the output video container, which is always WebM.\n */\n captureFormat?: 'jpeg' | 'png'\n /** JPEG quality 0–100 (default: 70). CDP mode + 'jpeg' only. */\n quality?: number\n /** Max frame width in pixels Chrome sends over CDP (default: 1280). */\n maxWidth?: number\n /** Max frame height in pixels Chrome sends over CDP (default: 720). */\n maxHeight?: number\n /**\n * Screenshot polling interval in milliseconds for non-Chrome browsers\n * (default: 200 ms ≈ 5 fps). Lower = smoother, more WebDriver round-trips.\n */\n pollIntervalMs?: number\n}\n\n/** Defaults applied to ScreencastOptions when not specified by the user. */\nexport const SCREENCAST_DEFAULTS: Required<ScreencastOptions> = {\n enabled: false,\n captureFormat: 'jpeg',\n quality: 70,\n maxWidth: 1280,\n maxHeight: 720,\n pollIntervalMs: 200\n}\n\nexport interface Metadata {\n type: TraceType\n url?: string\n options?: unknown\n capabilities?: unknown\n viewport?: Viewport\n sessionId?: string\n testEnv?: string\n host?: string\n modulePath?: string\n desiredCapabilities?: Record<string, unknown>\n}\n\n/** Captured metadata keyed by browser `sessionId` — lets the UI keep each\n * session's metadata instead of overwriting on a new session. Record (not Map)\n * so it stays JSON-serializable across the WS boundary and in contexts. */\nexport type MetadataBySession = Record<string, Metadata>\n\n/**\n * Node-safe shape of a captured DOM mutation. The browser-side script\n * (packages/script) extends this with the real `MutationRecordType` union\n * via the global `TraceMutation` declaration there; this Node version uses\n * a plain string literal type so the shape can flow through shared without\n * dragging the DOM lib into shared's compilation.\n *\n * `addedNodes` / `removedNodes` are opaque payloads here — the browser side\n * stringifies / serializes them via SimplifiedVNode.\n */\nexport interface TraceMutation {\n type: 'attributes' | 'characterData' | 'childList'\n attributeName?: string\n attributeNamespace?: string\n attributeValue?: string\n newTextContent?: string\n oldValue?: string\n addedNodes: unknown[]\n target?: string\n removedNodes: string[]\n previousSibling?: string\n nextSibling?: string\n timestamp: number\n url?: string\n}\n\n/**\n * Captured at each user-facing action boundary in `trace` mode. Feeds the\n * downstream trace.zip exporter (Phase 4). `screenshot` is base64-encoded JPEG.\n */\nexport interface ActionSnapshot {\n timestamp: number\n command: string\n url?: string\n title?: string\n screenshot?: string\n elements?: unknown[]\n snapshotText?: string\n}\n\nexport interface TraceLog {\n mutations: TraceMutation[]\n logs: string[]\n consoleLogs: ConsoleLog[]\n networkRequests: NetworkRequest[]\n metadata: Metadata\n commands: CommandLog[]\n sources: Record<string, string>\n suites?: Record<string, unknown>[]\n screencast?: ScreencastInfo\n config?: { configFile?: string }\n /** Per-action snapshots captured in `mode: 'trace'` for the trace.zip exporter. */\n actionSnapshots?: ActionSnapshot[]\n}\n\n// ─── Preserve-and-rerun ─────────────────────────────────────────────────────\n\nexport interface TestError {\n message?: string\n name?: string\n stack?: string\n /** expect-webdriverio surfaces these directly on the error. */\n expected?: unknown\n actual?: unknown\n /** expect-webdriverio also bundles them under matcherResult. */\n matcherResult?: {\n expected?: unknown\n actual?: unknown\n message?: string\n }\n}\n\nexport interface PreservedStep {\n uid: string\n title?: string\n fullTitle?: string\n start?: number\n end?: number\n state?: TestStatus\n error?: TestError\n}\n\nexport interface PreservedAttempt {\n testUid: string\n scope: 'test' | 'suite'\n capturedAt: number\n window: { start: number; end: number }\n test: {\n title?: string\n fullTitle?: string\n file?: string\n callSource?: string\n start?: number\n end?: number\n duration?: number\n state?: TestStatus\n error?: TestError\n }\n steps?: PreservedStep[]\n commands: CommandLog[]\n consoleLogs: ConsoleLog[]\n networkRequests: NetworkRequest[]\n /** See note on TraceLog.mutations. */\n mutations: unknown[]\n sources: Record<string, string>\n}\n\n// ─── Test reporter stats (nightwatch + selenium adapters) ───────────────────\n\n/**\n * Serialized form of an `Error`, used after capture so the payload survives\n * `JSON.stringify` over the WS bridge. The capture-time shape (raw `Error`\n * instance) is also accepted for callers that haven't serialized yet.\n */\nexport type ReporterError =\n | Error\n | { name: string; message: string; stack?: string }\n\nexport interface TestStats {\n uid: string\n cid: string\n title: string\n fullTitle: string\n parent: string\n state: TestStatus\n start: Date\n end: Date | null\n type: 'test'\n file: string\n retries: number\n _duration: number\n error?: ReporterError\n hooks?: unknown[]\n callSource?: string\n}\n\nexport interface SuiteStats {\n uid: string\n cid: string\n title: string\n fullTitle: string\n type: 'suite'\n file: string\n start: Date\n state?: TestStatus\n end?: Date | null\n tests: (string | TestStats)[]\n suites: SuiteStats[]\n hooks: unknown[]\n _duration: number\n parent?: string\n callSource?: string\n /** Cucumber-only: the .feature file path. Distinct from `file` because the\n * root suite's `file` stays at cwd to keep its stable UID; rerun payloads\n * use this to drive feature-level filtering. */\n featureFile?: string\n}\n","import type { ScreencastFrame, ScreencastOptions } from '@wdio/devtools-shared'\nimport { SCREENCAST_DEFAULTS } from '@wdio/devtools-shared'\n\n/**\n * Shared screencast scaffolding consumed by every adapter (service, selenium,\n * nightwatch). Owns the frame buffer, public API (start/stop/setStartMarker,\n * frames/duration/isRecording getters) and the polling fallback. Subclasses\n * provide framework-specific driver access:\n *\n * - `takeScreenshot()` — required. Used by the polling path.\n * - `tryStartCdp() / tryStopCdp()` — optional CDP push-mode override.\n * Default returns false → falls through to polling.\n *\n * Adapters that have a stable CDP escape hatch (WDIO via getPuppeteer,\n * Selenium via createCDPConnection) override the CDP hooks. Nightwatch\n * inherits the polling-only default — works on every browser Nightwatch\n * supports without extra plumbing.\n */\nexport abstract class ScreencastRecorderBase<TDriver = unknown> {\n protected buffer: ScreencastFrame[] = []\n protected options: Required<ScreencastOptions>\n protected driver?: TDriver\n #pollTimer: ReturnType<typeof setInterval> | undefined\n #isRecording = false\n #cdpActive = false\n #startIndex = 0\n #startMarkerSet = false\n\n constructor(options: ScreencastOptions = {}) {\n this.options = { ...SCREENCAST_DEFAULTS, ...options }\n }\n\n /**\n * Start recording. Tries the CDP fast-path first (if the subclass overrode\n * `tryStartCdp`); falls back to screenshot polling otherwise. Safe to call\n * even if the browser doesn't support screenshots — failures are logged and\n * recording is simply skipped.\n */\n async start(driver: TDriver): Promise<void> {\n if (this.#isRecording) {\n return\n }\n this.driver = driver\n const cdpOk = await this.tryStartCdp()\n if (cdpOk) {\n this.#cdpActive = true\n this.#isRecording = true\n return\n }\n await this.#startPolling()\n }\n\n /**\n * Stop recording and release resources. Safe to call even if start() was\n * never called or failed.\n */\n async stop(): Promise<void> {\n if (!this.#isRecording) {\n return\n }\n if (this.#cdpActive) {\n await this.tryStopCdp()\n this.#cdpActive = false\n } else if (this.#pollTimer !== undefined) {\n this.#stopPolling()\n }\n this.#isRecording = false\n }\n\n /**\n * Mark the current frame position as the start of meaningful recording.\n * Frames captured before this call (blank browser, pre-navigation pauses)\n * are excluded from `frames`. Idempotent — only the first call takes effect.\n */\n setStartMarker(): void {\n if (!this.#startMarkerSet) {\n this.#startMarkerSet = true\n this.#startIndex = this.buffer.length\n }\n }\n\n /** Frames to encode — everything from the first meaningful action onwards. */\n get frames(): ScreencastFrame[] {\n return this.buffer.slice(this.#startIndex)\n }\n\n /** Duration in ms between first and last captured frame. Zero if <2 frames. */\n get duration(): number {\n const f = this.frames\n if (f.length < 2) {\n return 0\n }\n return f[f.length - 1].timestamp - f[0].timestamp\n }\n\n get isRecording(): boolean {\n return this.#isRecording\n }\n\n // ─── Subclass hooks ──────────────────────────────────────────────────────\n\n /**\n * Capture a single screenshot via the framework's driver API. Used by the\n * polling fallback. Return `null` to indicate a transient failure (loop\n * continues); throw to abort polling entirely.\n */\n protected abstract takeScreenshot(): Promise<string | null>\n\n /**\n * Try to start CDP push-mode recording. Return `true` on success. Default\n * returns `false` → caller falls back to polling. Subclasses that wire CDP\n * push themselves (WDIO via Puppeteer, Selenium via createCDPConnection)\n * override and push frames into `this.frames` directly when CDP fires.\n */\n protected async tryStartCdp(): Promise<boolean> {\n return false\n }\n\n /** Stop the CDP push-mode session started by `tryStartCdp`. */\n protected async tryStopCdp(): Promise<void> {\n // no-op\n }\n\n /**\n * Helper for CDP subclasses: push a frame onto the buffer with the right\n * timestamp normalization (CDP gives seconds-as-float; we store ms).\n */\n protected pushCdpFrame(data: string, timestampSeconds?: number): void {\n const timestamp =\n typeof timestampSeconds === 'number'\n ? Math.round(timestampSeconds * 1000)\n : Date.now()\n this.buffer.push({ data, timestamp })\n }\n\n /** Whether `setStartMarker` (or `markStartAtLatest`) has fired yet. */\n protected get hasStartMarker(): boolean {\n return this.#startMarkerSet\n }\n\n /**\n * Anchor the start marker to the most recently pushed frame. Used by\n * subclasses that detect the first content-bearing frame heuristically\n * (e.g. selenium's blank-frame-byte-size threshold) and want to skip the\n * preceding about:blank dead-air without waiting for an explicit caller.\n */\n protected markStartAtLatest(): void {\n if (!this.#startMarkerSet) {\n this.#startMarkerSet = true\n this.#startIndex = Math.max(0, this.buffer.length - 1)\n }\n }\n\n // ─── Polling implementation ─────────────────────────────────────────────\n\n /**\n * Hook fired when the polling loop starts. Default: no-op. Subclasses\n * (adapters with their own logger) override to surface visibility.\n */\n protected onPollingStarted(_intervalMs: number): void {\n // no-op\n }\n\n /** Hook fired when polling stops cleanly (driver still alive at the time). */\n protected onPollingStopped(_frameCount: number): void {\n // no-op\n }\n\n /** Hook fired when the polling fallback couldn't even take the first shot. */\n protected onUnavailable(_err: unknown): void {\n // no-op\n }\n\n // ─── Polling implementation ─────────────────────────────────────────────\n\n async #startPolling(): Promise<void> {\n try {\n const first = await this.takeScreenshot()\n if (first === null) {\n this.onUnavailable(new Error('first screenshot returned null'))\n return\n }\n this.buffer.push({ data: first, timestamp: Date.now() })\n\n const intervalMs = this.options.pollIntervalMs\n this.#pollTimer = setInterval(async () => {\n try {\n const data = await this.takeScreenshot()\n if (data !== null) {\n this.buffer.push({ data, timestamp: Date.now() })\n }\n } catch {\n // Session ended mid-interval — stop polling gracefully.\n this.#stopPolling()\n }\n }, intervalMs)\n\n this.#isRecording = true\n this.onPollingStarted(intervalMs)\n } catch (err) {\n this.onUnavailable(err)\n }\n }\n\n #stopPolling(): void {\n if (this.#pollTimer !== undefined) {\n clearInterval(this.#pollTimer)\n this.#pollTimer = undefined\n this.onPollingStopped(this.buffer.length)\n }\n }\n}\n","import fs from 'node:fs/promises'\nimport path from 'node:path'\nimport { createRequire } from 'node:module'\n\nconst require = createRequire(import.meta.url)\n\n/**\n * Load the `@wdio/devtools-script` browser preload, wrapped in an async IIFE\n * so its top-level `await` works inside a regular `<script>` element body.\n * Shared by selenium-devtools and nightwatch-devtools, which both inject the\n * script via `document.createElement('script')` rather than BiDi preload (the\n * WDIO service uses `browser.scriptAddPreloadScript`, which doesn't need the\n * wrap and stays in its own adapter).\n */\nexport async function loadInjectableScript(): Promise<string> {\n const scriptPath = require.resolve('@wdio/devtools-script')\n const scriptDir = path.dirname(scriptPath)\n const preloadScriptPath = path.join(scriptDir, 'script.js')\n const scriptContent = await fs.readFile(preloadScriptPath, 'utf-8')\n return `(async function() { ${scriptContent} })()`\n}\n\n/**\n * Poll a readiness check until it returns true, or the attempts run out.\n * Defaults to 5 × 200ms = up to 1 second total — chosen empirically to cover\n * the async IIFE init time across browsers we test against.\n */\nexport async function pollUntilReady(\n check: () => Promise<boolean>,\n opts: { attempts?: number; intervalMs?: number } = {}\n): Promise<boolean> {\n const attempts = opts.attempts ?? 5\n const intervalMs = opts.intervalMs ?? 200\n for (let i = 0; i < attempts; i++) {\n await new Promise((resolve) => setTimeout(resolve, intervalMs))\n if (await check()) {\n return true\n }\n }\n return false\n}\n","import fs from 'node:fs/promises'\nimport { WebSocket } from 'ws'\nimport type {\n CommandLog,\n ConsoleLog,\n LogLevel,\n LogSource,\n Metadata,\n NetworkRequest,\n TraceMutation\n} from '@wdio/devtools-shared'\nimport { WS_PATHS, WS_SCOPE } from '@wdio/devtools-shared'\nimport {\n CONSOLE_METHODS,\n LOG_SOURCES,\n SPINNER_RE,\n createConsoleLogEntry,\n detectLogLevel,\n isInternalStreamLine,\n stripAnsi\n} from './console.js'\n\n/**\n * Foundation class for adapter SessionCapturers. Owns the cross-framework\n * scaffolding (WS connection, console/stream patching, command id\n * bookkeeping). Framework-specific event handling stays in subclasses.\n *\n * Step 2 of {@link file://./../../../SESSIONCAPTURER_EXTRACTION_PLAN.md}.\n * **Not yet consumed by any adapter** — published so a future session can\n * migrate adapter SessionCapturer classes one at a time.\n */\n\nexport interface SessionCapturerOptions {\n hostname?: string\n port?: number\n /**\n * Set when this capturer reconnects mid-run (e.g. after `browser.end()` opens\n * a new session). Tells the backend to keep the accumulated run state instead\n * of resetting it — otherwise earlier tests' commands are wiped and Preserve\n * & Rerun on those tests finds nothing.\n */\n reconnect?: boolean\n}\n\ntype ConsoleMethod = (typeof CONSOLE_METHODS)[number]\n\nexport abstract class SessionCapturerBase {\n // ── State (mostly private; subclasses access shared ws via `this.ws`) ────\n /**\n * Exposed as `protected` so subclasses with framework-specific close/wait\n * semantics (e.g. nightwatch's `closeWebSocket` with timeout) can operate\n * on the socket directly. Default lifecycle is fully managed by the base.\n */\n protected ws: WebSocket | undefined\n #hasConnected = false\n #originalConsoleMethods: Record<ConsoleMethod, typeof console.log>\n #originalStdoutWrite = process.stdout.write.bind(process.stdout)\n #originalStderrWrite = process.stderr.write.bind(process.stderr)\n // Two flags (not one): prevents re-entrant capture when console.* writes to\n // stdout, OR when stream forwarding wants to log via console.\n #isCapturingConsole = false\n #isCapturingStream = false\n\n // Command bookkeeping — used by adapters that emit commands themselves\n // (nightwatch, selenium). The WDIO service adapter doesn't call sendCommand\n // (WDIO owns the command lifecycle), so this state is harmless overhead.\n // `protected` (not `#`) so subclasses can override the send/replace flow\n // while still sharing the counter and de-dup set with base helpers.\n protected commandCounter = 0\n protected sentCommandIds = new Set<number>()\n\n // Map of file path → source text. Populated by `captureSource` (also\n // accessed by adapter-specific source-discovery flows, e.g. service's\n // `ensureSourceLoaded` which parses `file://` locations first).\n sources = new Map<string, string>()\n\n // Captured trace payload — populated by `processTracePayload` (driven from\n // adapter-specific `captureTrace` flows) and by direct pushes from BiDi/CDP\n // listeners. Mutations stay `unknown[]` here because the canonical\n // `TraceMutation` shape is a browser-only DOM type (script package); cross-\n // package consumers treat the array as opaque.\n commandsLog: CommandLog[] = []\n consoleLogs: ConsoleLog[] = []\n networkRequests: NetworkRequest[] = []\n mutations: TraceMutation[] = []\n traceLogs: string[] = []\n metadata?: Metadata\n\n // ── Construction ────────────────────────────────────────────────────────\n constructor(opts: SessionCapturerOptions = {}) {\n const { hostname, port, reconnect } = opts\n if (hostname && port) {\n const query = reconnect ? '?reconnect=1' : ''\n this.ws = new WebSocket(\n `ws://${hostname}:${port}${WS_PATHS.worker}${query}`\n )\n this.ws.on('open', () => {\n this.#hasConnected = true\n this.onWsOpen()\n })\n this.ws.on('error', (err: unknown) => this.onWsError(err))\n this.ws.on('close', () => this.onWsClose())\n this.ws.on('message', (raw: Buffer | string) => {\n try {\n const parsed = JSON.parse(raw.toString())\n this.onWsMessage(parsed)\n } catch {\n // ignore non-JSON\n }\n })\n }\n\n this.#originalConsoleMethods = {\n log: console.log,\n info: console.info,\n warn: console.warn,\n error: console.error\n }\n }\n\n // ── Public API ──────────────────────────────────────────────────────────\n /**\n * Send a typed event to the dashboard. No-op if the WS isn't open. Catches\n * send-time exceptions so a transient socket error never aborts the host\n * runner. Subclasses that want diagnostics on drop or error override\n * {@link onUpstreamDrop}.\n */\n sendUpstream(event: string, data: unknown): void {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n this.onUpstreamDrop(event, 'closed')\n return\n }\n try {\n this.ws.send(JSON.stringify({ scope: event, data }))\n } catch (err) {\n this.onUpstreamDrop(event, 'send-error', err)\n }\n }\n\n /**\n * Hook fired when a {@link sendUpstream} call can't deliver. Default: silent\n * (matches the historical behavior of service/selenium). Nightwatch overrides\n * this to log a warning — useful when a runner drops mid-test and the user\n * needs to know why captured data is incomplete.\n */\n protected onUpstreamDrop(\n _event: string,\n _reason: 'closed' | 'send-error',\n _err?: unknown\n ): void {\n // no-op\n }\n\n /** True once the WS has opened at least once and is currently OPEN. */\n isConnected(): boolean {\n return Boolean(this.ws) && this.ws?.readyState === WebSocket.OPEN\n }\n\n /** Property-style alias for {@link isConnected} — used by tests that\n * read it as a getter while mutating `ws.readyState` directly. */\n get isReportingUpstream(): boolean {\n return this.isConnected()\n }\n\n /** Subclasses can read this to gate retry/reconnect logic. */\n protected hasEverConnected(): boolean {\n return this.#hasConnected\n }\n\n /**\n * Send a CommandLog over the WS. If the entry already has an `_id` (set by\n * the adapter's `captureCommand` during buffering), use it; otherwise\n * allocate a fresh one. The `_id` is the de-dup key and is stripped from\n * the broadcast payload — it's adapter-internal bookkeeping.\n * Returns the id, or 0 if the entry had no `_id` and none could be assigned.\n */\n sendCommand(command: CommandLog & { _id?: number }): number {\n if (command._id === undefined) {\n command._id = this.commandCounter++\n }\n const id = command._id\n if (this.sentCommandIds.has(id)) {\n return id\n }\n this.sentCommandIds.add(id)\n const toSend = { ...command }\n delete toSend._id\n this.sendUpstream('commands', [toSend])\n return id\n }\n\n /**\n * Emit a `replaceCommand` event swapping an earlier entry in-place. Strips\n * the adapter-internal `_id` field before sending — that's bookkeeping for\n * the local `sentCommandIds` set and shouldn't reach the UI.\n */\n sendReplaceCommand(\n oldTimestamp: number,\n command: CommandLog & { _id?: number }\n ): void {\n const toSend = { ...command }\n delete toSend._id\n this.sendUpstream(WS_SCOPE.replaceCommand, {\n oldTimestamp,\n command: toSend\n })\n }\n\n /**\n * Read a file from disk, store in `sources`, and broadcast to the UI via\n * `sendUpstream('sources', { [path]: text })`. Idempotent — a cached path is\n * a no-op. Read errors are logged via `onSourceReadError` (default: silent)\n * so a missing source never aborts capture.\n */\n async captureSource(filePath: string): Promise<void> {\n if (this.sources.has(filePath)) {\n return\n }\n try {\n const source = (await fs.readFile(filePath, 'utf-8')).toString()\n this.sources.set(filePath, source)\n this.sendUpstream('sources', { [filePath]: source })\n } catch (err) {\n this.onSourceReadError(filePath, err)\n }\n }\n\n /**\n * Hook fired when `captureSource` can't read a file. Default: silent.\n * Subclasses (nightwatch, selenium) override to log a warning.\n */\n protected onSourceReadError(_filePath: string, _err: unknown): void {\n // no-op — service silently swallows; subclasses can opt into a log line.\n }\n\n /**\n * Ingest the `{ mutations, traceLogs, consoleLogs, networkRequests, metadata }`\n * payload returned by the page-side `wdioTraceCollector.getTraceData()`.\n * Tags console logs with `source: 'browser'`, pushes each array into the\n * matching local field, and broadcasts via the appropriate WS scopes.\n *\n * `skipConsoleLogs` / `skipNetworkRequests` opt out when an out-of-band\n * channel (BiDi) is already delivering those streams — without the gate\n * the dashboard would see each entry twice.\n */\n protected processTracePayload(\n payload: {\n mutations?: unknown\n traceLogs?: unknown\n consoleLogs?: unknown\n networkRequests?: unknown\n metadata?: unknown\n },\n opts: { skipConsoleLogs?: boolean; skipNetworkRequests?: boolean } = {}\n ): void {\n const { mutations, traceLogs, consoleLogs, networkRequests, metadata } =\n payload\n\n if (metadata && typeof metadata === 'object') {\n // Page-side trace data is a JS bag; only fields that match Metadata\n // survive at runtime, but TS can't prove that. Cast to Partial<Metadata>\n // so the merge stays type-checked while accepting incomplete payloads.\n this.metadata = {\n ...this.metadata,\n ...(metadata as Partial<Metadata>)\n } as Metadata\n this.sendUpstream('metadata', this.metadata)\n }\n\n if (\n !opts.skipConsoleLogs &&\n Array.isArray(consoleLogs) &&\n consoleLogs.length > 0\n ) {\n const tagged = (consoleLogs as ConsoleLog[]).map((entry) => ({\n ...entry,\n source: LOG_SOURCES.BROWSER as LogSource\n }))\n this.consoleLogs.push(...tagged)\n this.sendUpstream('consoleLogs', tagged)\n }\n\n if (\n !opts.skipNetworkRequests &&\n Array.isArray(networkRequests) &&\n networkRequests.length > 0\n ) {\n const reqs = networkRequests as NetworkRequest[]\n this.networkRequests.push(...reqs)\n this.sendUpstream('networkRequests', reqs)\n }\n\n if (Array.isArray(mutations) && mutations.length > 0) {\n this.mutations.push(...mutations)\n this.sendUpstream('mutations', mutations)\n }\n\n if (Array.isArray(traceLogs) && traceLogs.length > 0) {\n const logs = traceLogs as string[]\n this.traceLogs.push(...logs)\n this.sendUpstream('logs', logs)\n }\n }\n\n /**\n * Resolve when the WS reaches OPEN state, or `false` on timeout / error.\n * Returns immediately if already open. Used by adapters that need a\n * synchronization barrier before injecting page-side scripts.\n */\n async waitForConnection(timeoutMs = 5000): Promise<boolean> {\n if (!this.ws) {\n return false\n }\n if (this.ws.readyState === WebSocket.OPEN) {\n return true\n }\n return new Promise((resolve) => {\n const timeout = setTimeout(() => resolve(false), timeoutMs)\n this.ws!.once('open', () => {\n clearTimeout(timeout)\n resolve(true)\n })\n this.ws!.once('error', () => {\n clearTimeout(timeout)\n resolve(false)\n })\n })\n }\n\n /**\n * Gracefully close the WS, waiting up to 2s for buffered messages to flush.\n * Call before process exit in reuse mode (or after dashboard close) so the\n * backend sees a clean close instead of an abrupt TCP reset.\n */\n async closeWebSocket(): Promise<void> {\n if (!this.ws || this.ws.readyState === WebSocket.CLOSED) {\n return\n }\n return new Promise<void>((resolve) => {\n const timeout = setTimeout(resolve, 2000)\n this.ws!.once('close', () => {\n clearTimeout(timeout)\n resolve()\n })\n this.ws!.close()\n })\n }\n\n /**\n * Restore console/streams. Does NOT close the WS — that's the subclass's\n * call (see `closeWebSocket` on nightwatch/selenium). Closing here would\n * break the wait-for-dashboard-close flow, since the worker WS is the\n * channel the backend uses to signal `clientDisconnected`.\n */\n cleanup(): void {\n this.restoreConsole()\n this.restoreStreams()\n }\n\n // ── Patching (call from subclass constructor) ───────────────────────────\n /** Patch `console.log/info/warn/error` to forward through `onLine`. */\n protected patchConsole(): void {\n CONSOLE_METHODS.forEach((method) => {\n const original = this.#originalConsoleMethods[method]\n console[method] = (...args: unknown[]) => {\n this.#isCapturingConsole = true\n const result = original.apply(console, args)\n this.#isCapturingConsole = false\n\n const serialized = args.map((a) =>\n typeof a === 'object' && a !== null ? safeStringify(a) : String(a)\n )\n const joined = stripAnsi(serialized.join(' ')).trim()\n if (!joined || this.isInternalStreamLine(joined)) {\n return result\n }\n // Pass the per-arg serialized array (`['payload', '{\"x\":1}']`) rather\n // than the joined string. The dashboard's `#formatArgs` joins on its\n // own; preserving the array form is lossless and lets future consumers\n // group/style individual args.\n this.onLine(method as LogLevel, serialized, LOG_SOURCES.TEST)\n return result\n }\n })\n }\n\n /**\n * Wrap `process.stdout.write` and `process.stderr.write` to forward chunks\n * through `onLine`. The base's `#isCapturingConsole` flag prevents\n * re-entrance when console patching itself writes to stdout.\n */\n protected patchStreams(): void {\n const captureChunk = (raw: string | Uint8Array) => {\n if (this.#isCapturingStream) {\n return\n }\n const text = typeof raw === 'string' ? raw : raw.toString()\n if (!text?.trim()) {\n return\n }\n this.#isCapturingStream = true\n try {\n for (const rawLine of text.split('\\n')) {\n // Strip CR-overwrites so progress bars don't show partial frames.\n const segments = rawLine.split('\\r').filter((s) => s.trim())\n const lastSegment = segments[segments.length - 1] ?? rawLine\n const clean = stripAnsi(lastSegment).trim()\n if (\n !clean ||\n this.isInternalStreamLine(clean) ||\n SPINNER_RE.test(clean)\n ) {\n continue\n }\n this.onLine(detectLogLevel(clean), [clean], LOG_SOURCES.TERMINAL)\n }\n } finally {\n this.#isCapturingStream = false\n }\n }\n\n // `stream.write` has Node's complex multi-overload signature that\n // doesn't unify with a generic `unknown[]` rest type — keep the\n // original ref typed by the stream itself.\n const wrap = (\n stream: NodeJS.WriteStream,\n original: NodeJS.WriteStream['write']\n ) => {\n const capturer = this\n // `stream.write` has Node's multi-overload signature that's hard to\n // satisfy with a single function expression — cast to the stream's\n // own `write` member type rather than `any`.\n stream.write = function (chunk: unknown, ...rest: unknown[]): boolean {\n // Cast original to a permissive shape — Node's multi-overload\n // signature for `stream.write` can't be unified with the `unknown`-\n // typed chunk we receive at the wrap boundary.\n const writeAny = original as unknown as (...a: unknown[]) => boolean\n const result = writeAny.call(stream, chunk, ...rest)\n if (chunk && !capturer.#isCapturingConsole) {\n captureChunk(chunk as string | Uint8Array)\n }\n return result\n } as typeof stream.write\n }\n\n wrap(process.stdout, this.#originalStdoutWrite)\n wrap(process.stderr, this.#originalStderrWrite)\n }\n\n protected restoreConsole(): void {\n CONSOLE_METHODS.forEach((method) => {\n console[method] = this.#originalConsoleMethods[method]\n })\n }\n\n protected restoreStreams(): void {\n // Restoring the pre-patch references — the typed write signature differs\n // slightly from the runtime instance type after `.bind()`, hence the cast\n // through the stream's own `write` member type.\n process.stdout.write = this\n .#originalStdoutWrite as typeof process.stdout.write\n process.stderr.write = this\n .#originalStderrWrite as typeof process.stderr.write\n }\n\n // ── Hooks (subclasses override) ─────────────────────────────────────────\n /**\n * Build a ConsoleLog entry from the captured line, push it into the local\n * `consoleLogs` array (so it ends up in any future trace export), and\n * broadcast it live via the `consoleLogs` WS scope.\n *\n * Args is passed as an array (matching the original console.* call shape:\n * `console.log('a', 'b')` → `args = ['a', 'b']`) so the multi-argument\n * structure is preserved for the UI.\n */\n protected onLine(type: LogLevel, args: string[], source: LogSource): void {\n const entry = createConsoleLogEntry(type, args, source)\n this.consoleLogs.push(entry)\n this.sendUpstream('consoleLogs', [entry])\n }\n\n /**\n * Default delegates to {@link isInternalStreamLine} from `./console.js`.\n * Subclasses can override to add framework-specific filters.\n */\n protected isInternalStreamLine(line: string): boolean {\n return isInternalStreamLine(line)\n }\n\n /** Hook: WS opened. Subclasses override to send a handshake, etc. */\n protected onWsOpen(): void {}\n\n /** Hook: WS errored before opening (likely no backend listening). */\n protected onWsError(_err: unknown): void {}\n\n /** Hook: WS closed (after open, or as a result of cleanup). */\n protected onWsClose(): void {}\n\n /**\n * Hook: WS message received from the backend. Currently used by selenium's\n * `awaitClientConnected` to know when a dashboard tab has subscribed.\n */\n protected onWsMessage(_msg: unknown): void {}\n}\n\nfunction safeStringify(value: unknown): string {\n try {\n return JSON.stringify(value)\n } catch {\n return String(value)\n }\n}\n","/**\n * Shared helpers for per-spec trace partitioning.\n *\n * The three adapters (service, nightwatch, selenium) all use the same\n * index-range tracking and spec-name sanitization logic. This module\n * is the single source of truth — adapters import from here.\n */\n\nimport type {\n ActionSnapshot,\n TestMetadataMap,\n TraceFormat,\n TraceGranularity\n} from '@wdio/devtools-shared'\nimport type { TraceCapturer } from './trace-exporter.js'\nimport { writeTraceZip } from './trace-exporter.js'\nimport { deterministicUid } from './uid.js'\n\n// ─── SpecRange ────────────────────────────────────────────────────────────────\n\n/** Index ranges into a SessionCapturer's flat arrays for a single spec file. */\nexport interface SpecRange {\n specFile: string\n commandStartIdx: number\n consoleStartIdx: number\n networkStartIdx: number\n mutationStartIdx: number\n traceLogStartIdx: number\n snapshotCount: number\n}\n\n// ─── Spec name sanitization ───────────────────────────────────────────────────\n\n/**\n * Sanitize a spec file path into a directory-safe identifier.\n * Strips directory, extension, and unsafe characters.\n * Falls back to `'unknown-spec'` when the result is empty.\n */\nexport function sanitizeSpecName(specFile: string): string {\n return (\n specFile\n .replace(/^.*[/\\\\]/, '')\n .replace(/\\.[^.]+$/, '')\n .replace(/[^a-zA-Z0-9_-]/g, '_')\n .replace(/^_+|_+$/g, '') || 'unknown-spec'\n )\n}\n\n// ─── Spec session ID ──────────────────────────────────────────────────────────\n\n/**\n * Build a collision-safe spec-level session ID from a spec file path and\n * the parent session ID. Includes an 8-char base36 hash of the full spec\n * path so two specs with the same basename in different directories don't\n * collide.\n */\nexport function buildSpecSessionId(\n specFile: string,\n sessionId: string\n): string {\n const base = sanitizeSpecName(specFile)\n const hash = deterministicUid(specFile).split('-').pop()!.slice(0, 8)\n return `${base}-${hash}-${sessionId.slice(0, 8)}`\n}\n\n// ─── TraceCapturer slice ─────────────────────────────────────────────────────\n\n/**\n * Build a TraceCapturer that contains only the data for one spec file,\n * sliced from the parent capturer's flat arrays using index ranges.\n * The returned `sources` is a shallow clone so mutations to the parent's\n * source map after this call don't affect the spec-level capturer.\n */\nexport function buildSpecCapturer(\n capturer: TraceCapturer,\n range: SpecRange,\n nextRange?: SpecRange\n): TraceCapturer {\n const end = nextRange\n ? {\n commands: nextRange.commandStartIdx,\n console: nextRange.consoleStartIdx,\n network: nextRange.networkStartIdx,\n mutations: nextRange.mutationStartIdx,\n traceLogs: nextRange.traceLogStartIdx\n }\n : undefined\n\n return {\n mutations: capturer.mutations.slice(range.mutationStartIdx, end?.mutations),\n traceLogs: capturer.traceLogs.slice(range.traceLogStartIdx, end?.traceLogs),\n consoleLogs: capturer.consoleLogs.slice(\n range.consoleStartIdx,\n end?.console\n ),\n networkRequests: capturer.networkRequests.slice(\n range.networkStartIdx,\n end?.network\n ),\n commandsLog: capturer.commandsLog.slice(\n range.commandStartIdx,\n end?.commands\n ),\n sources: new Map(capturer.sources),\n metadata: capturer.metadata,\n startWallTime: capturer.startWallTime\n }\n}\n\n// ─── Test metadata filtering ──────────────────────────────────────────────────\n\n/**\n * Filter a full `testUid → metadata` map to only include entries whose\n * `specFile` matches the given value. Used when flushing a per-spec trace\n * to attach only that spec's test titles as tracingGroup names.\n */\nexport function filterTestMetadataBySpec(\n allMetadata: TestMetadataMap,\n specFile: string\n): TestMetadataMap {\n const filtered: TestMetadataMap = new Map()\n for (const [uid, meta] of allMetadata) {\n if (meta.specFile === specFile) {\n filtered.set(uid, meta)\n }\n }\n return filtered\n}\n\n// ─── Spec boundary recording ──────────────────────────────────────────────────\n\n/**\n * Minimal context needed by `recordSpecBoundary` to detect spec-file\n * transitions and capture array index ranges.\n */\nexport interface SpecBoundaryContext {\n specRanges: SpecRange[]\n flushedSpecs: Set<string>\n capturer: {\n commandsLog: ArrayLike<unknown>\n consoleLogs: ArrayLike<unknown>\n networkRequests: ArrayLike<unknown>\n mutations: ArrayLike<unknown>\n traceLogs: ArrayLike<unknown>\n }\n actionSnapshots: ArrayLike<unknown>\n}\n\n/**\n * Record a spec-file boundary. When `traceGranularity` is `'spec'` and the\n * spec file has changed, this pushes a new `SpecRange` and returns the\n * previous range so the caller can flush its trace artifact.\n *\n * Returns `null` when no flush is needed (same spec, or granularity isn't\n * `'spec'`, or no capturer).\n */\nexport function recordSpecBoundary(\n ctx: SpecBoundaryContext,\n specFile: string,\n traceGranularity: TraceGranularity | undefined\n): SpecRange | null {\n if (traceGranularity !== 'spec') {\n return null\n }\n const lastRange = ctx.specRanges[ctx.specRanges.length - 1]\n if (lastRange && lastRange.specFile === specFile) {\n return null\n }\n\n const prevRange =\n lastRange && !ctx.flushedSpecs.has(lastRange.specFile) ? lastRange : null\n\n ctx.specRanges.push({\n specFile,\n commandStartIdx: ctx.capturer.commandsLog.length,\n consoleStartIdx: ctx.capturer.consoleLogs.length,\n networkStartIdx: ctx.capturer.networkRequests.length,\n mutationStartIdx: ctx.capturer.mutations.length,\n traceLogStartIdx: ctx.capturer.traceLogs.length,\n snapshotCount: ctx.actionSnapshots.length\n })\n\n return prevRange\n}\n\n// ─── Spec trace I/O ────────────────────────────────────────────────────────────\n\n/**\n * Pre-resolved inputs for `writeSpecTrace`. The caller gathers\n * adapter-specific state (session ID, output directory, test metadata …)\n * and passes it in; this function does the framework-agnostic slicing,\n * session-ID derivation, filtering, and disk I/O.\n */\nexport interface WriteSpecTraceInput {\n range: SpecRange\n nextRange?: SpecRange\n /** Shape-compatible with `buildSpecCapturer`'s first parameter. */\n capturer: Parameters<typeof buildSpecCapturer>[0]\n actionSnapshots: ActionSnapshot[]\n sessionId: string\n outputDir: string\n format?: TraceFormat\n /** Full test-metadata map (all specs); filtered to `range.specFile` internally. */\n testMetadata: TestMetadataMap\n capabilities?: unknown\n}\n\n/**\n * Write a standalone trace artifact (zip or ndjson-directory) for a single\n * spec file. This is the shared I/O path — all three adapters delegate to it\n * from their own `flushSpecTrace` wrappers.\n */\nexport async function writeSpecTrace(\n input: WriteSpecTraceInput\n): Promise<string> {\n const specCapturer = buildSpecCapturer(\n input.capturer,\n input.range,\n input.nextRange\n )\n\n const specSnapshots = input.actionSnapshots.slice(\n input.range.snapshotCount,\n input.nextRange?.snapshotCount ?? input.actionSnapshots.length\n )\n\n const specSessionId = buildSpecSessionId(\n input.range.specFile,\n input.sessionId\n )\n\n const testMetadata = filterTestMetadataBySpec(\n input.testMetadata,\n input.range.specFile\n )\n\n return writeTraceZip(specCapturer, {\n outputDir: input.outputDir,\n sessionId: specSessionId,\n capabilities: input.capabilities,\n actionSnapshots: specSnapshots.length > 0 ? specSnapshots : undefined,\n format: input.format,\n testMetadata\n })\n}\n","import type { SuiteStats, TestStats, TestStatus } from '@wdio/devtools-shared'\nimport { TEST_STATE } from '@wdio/devtools-shared'\n\n/**\n * Pure factories + state computations shared by the per-adapter SuiteManager\n * and TestManager classes. Pattern A from CLAUDE.md §the-four-patterns —\n * stateless helpers each adapter calls; no base class because storage\n * strategy differs (selenium uses a single root suite + optional Cucumber\n * sub-suites; nightwatch uses a Map keyed by test file).\n */\n\nexport interface SuiteStatsInit {\n uid: string\n title: string\n file: string\n cid?: string\n callSource?: string\n featureFile?: string\n parent?: string\n start?: Date\n state?: TestStatus\n}\n\nexport interface TestStatsInit {\n uid: string\n title: string\n file: string\n parent: string\n cid?: string\n fullTitle?: string\n callSource?: string\n state?: TestStatus\n start?: Date\n}\n\n/**\n * Build a SuiteStats with the standard defaults (empty children arrays,\n * RUNNING state, fresh start time). Adapters override fields as needed\n * before/after the call.\n */\nexport function createSuiteStats(init: SuiteStatsInit): SuiteStats {\n return {\n uid: init.uid,\n cid: init.cid ?? '0-0',\n title: init.title,\n fullTitle: init.title,\n file: init.file,\n type: 'suite',\n start: init.start ?? new Date(),\n state: init.state ?? TEST_STATE.RUNNING,\n end: null,\n tests: [],\n suites: [],\n hooks: [],\n _duration: 0,\n callSource: init.callSource,\n featureFile: init.featureFile,\n parent: init.parent\n }\n}\n\n/** Build a TestStats with the standard defaults. */\nexport function createTestStats(init: TestStatsInit): TestStats {\n return {\n uid: init.uid,\n cid: init.cid ?? '0-0',\n title: init.title,\n fullTitle: init.fullTitle ?? init.title,\n parent: init.parent,\n state: init.state ?? TEST_STATE.RUNNING,\n start: init.start ?? new Date(),\n end: null,\n type: 'test',\n file: init.file,\n retries: 0,\n _duration: 0,\n hooks: [],\n callSource: init.callSource\n }\n}\n\n/**\n * \"Permissive\" finalize: any failed child → FAILED, otherwise PASSED.\n * Matches selenium's policy — RUNNING tests don't fail the suite.\n */\nexport function computeSuiteFinalStatePermissive(\n suite: SuiteStats\n): TestStatus {\n const failedDirect = suite.tests.some(\n (t) => typeof t !== 'string' && t.state === TEST_STATE.FAILED\n )\n const failedNested = (suite.suites ?? []).some(\n (s) => s.state === TEST_STATE.FAILED\n )\n return failedDirect || failedNested ? TEST_STATE.FAILED : TEST_STATE.PASSED\n}\n\n/**\n * \"Strict\" finalize: any failed → FAILED; all PASSED/SKIPPED → PASSED;\n * empty suite → PASSED; orphaned RUNNING tests → FAILED. Matches nightwatch's\n * policy — incomplete runs are surfaced as failures.\n */\nexport function computeSuiteFinalStateStrict(suite: SuiteStats): TestStatus {\n const tests = suite.tests as TestStats[]\n const suites = suite.suites ?? []\n const hasFailures =\n tests.some((t) => t.state === TEST_STATE.FAILED) ||\n suites.some((s) => s.state === TEST_STATE.FAILED)\n if (hasFailures) {\n return TEST_STATE.FAILED\n }\n const allPassed =\n tests.every(\n (t) => t.state === TEST_STATE.PASSED || t.state === TEST_STATE.SKIPPED\n ) &&\n suites.every(\n (s) => s.state === TEST_STATE.PASSED || s.state === TEST_STATE.SKIPPED\n )\n const hasSkipped = tests.some((t) => t.state === TEST_STATE.SKIPPED)\n const hasItems = tests.length > 0 || suites.length > 0\n if (!hasItems || allPassed) {\n return TEST_STATE.PASSED\n }\n if (hasSkipped) {\n return TEST_STATE.PASSED\n }\n return TEST_STATE.FAILED\n}\n\n/**\n * In-progress state computation: if any child is FAILED → FAILED;\n * else if any RUNNING / unfinished → RUNNING; else → PASSED. Used to keep\n * a parent suite's state fresh while children are still executing\n * (Cucumber feature suite updates as each scenario completes).\n */\nexport function computeSuiteRunningState(suite: SuiteStats): TestStatus {\n const tests = suite.tests as TestStats[]\n const suites = suite.suites ?? []\n const hasFailures =\n tests.some((t) => t.state === TEST_STATE.FAILED) ||\n suites.some((s) => s.state === TEST_STATE.FAILED)\n if (hasFailures) {\n return TEST_STATE.FAILED\n }\n const hasRunning =\n tests.some((t) => t.state === TEST_STATE.RUNNING) ||\n suites.some((s) => s.state === TEST_STATE.RUNNING || !s.end)\n return hasRunning ? TEST_STATE.RUNNING : TEST_STATE.PASSED\n}\n\n/**\n * Stamp `end` + `_duration` on a suite. Pure mutation — kept here so the\n * arithmetic stays in one place; callers still own the state assignment.\n */\nexport function stampSuiteEnd(suite: SuiteStats, end: Date = new Date()): void {\n suite.end = end\n suite._duration = end.getTime() - (suite.start?.getTime() || end.getTime())\n}\n\n/** Stamp `end` + `_duration` on a test. */\nexport function stampTestEnd(test: TestStats, end: Date = new Date()): void {\n test.end = end\n test._duration = end.getTime() - (test.start?.getTime() ?? end.getTime())\n}\n","import fs from 'node:fs'\n\n/**\n * One test/suite definition discovered in a source file by line-regex scan.\n * Adapter-agnostic shape — both selenium and nightwatch consume this.\n */\nexport interface TestDefinition {\n kind: 'suite' | 'test'\n title: string\n line: number\n}\n\n/**\n * Regex-scan a JS/TS test file for `describe('...')` / `it('...')` style\n * definitions (and Mocha/Jest aliases — `suite`, `context`, `test`,\n * `specify`). Adapters call this from line-based helpers that resolve a\n * call-source `file:line` for the dashboard's TestLens / Actions tab.\n *\n * Set `includeNightwatchObjectStyle` to also match the object-export shape\n * Nightwatch's `--yes` scaffolder generates:\n *\n * 'My test name': () => { ... }\n * 'My test name': async function () { ... }\n *\n * Returns definitions in source order. Unreadable / unparseable files yield\n * an empty array — the dashboard already degrades to `file:0` in that case.\n */\nexport function findTestDefinitions(\n filePath: string,\n opts: { includeNightwatchObjectStyle?: boolean } = {}\n): TestDefinition[] {\n if (!fs.existsSync(filePath)) {\n return []\n }\n let source: string\n try {\n source = fs.readFileSync(filePath, 'utf-8')\n } catch {\n return []\n }\n\n const out: TestDefinition[] = []\n const lines = source.split('\\n')\n\n const suiteRe = /\\b(?:describe|suite|context)\\s*\\(\\s*['\"`]([^'\"`]+)['\"`]/\n const testRe = /\\b(?:it|test|specify)\\s*\\(\\s*['\"`]([^'\"`]+)['\"`]/\n // Nightwatch object-export: `'Title': () => { ... }` or `: function () {`\n const objRe =\n /^\\s*['\"`]([^'\"`]+)['\"`]\\s*:\\s*(?:async\\s+)?(?:\\([^)]*\\)\\s*=>|function\\s*\\()/\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]\n const lineNum = i + 1\n\n const suiteMatch = line.match(suiteRe)\n if (suiteMatch) {\n out.push({ kind: 'suite', title: suiteMatch[1], line: lineNum })\n continue\n }\n const testMatch = line.match(testRe)\n if (testMatch) {\n out.push({ kind: 'test', title: testMatch[1], line: lineNum })\n continue\n }\n if (opts.includeNightwatchObjectStyle) {\n const objMatch = line.match(objRe)\n if (objMatch) {\n out.push({ kind: 'test', title: objMatch[1], line: lineNum })\n }\n }\n }\n return out\n}\n\n/**\n * Convenience: find the line number where a specific test/suite is defined.\n * Returns null if the file or title isn't found. Used by adapter call-source\n * resolution from hooks where the user's stack frame isn't reachable.\n */\nexport function findTestLineInFile(\n filePath: string,\n title: string,\n kind: 'test' | 'suite' = 'test'\n): number | null {\n const defs = findTestDefinitions(filePath)\n return defs.find((d) => d.kind === kind && d.title === title)?.line ?? null\n}\n","import type { SuiteStats, TestStats } from '@wdio/devtools-shared'\nimport { resetSignatureCounters } from './uid.js'\n\n/**\n * Shape of the payload sent upstream — one record per suite, keyed by UID,\n * so the UI can merge it into its existing suite map without scanning.\n */\nexport type ReporterUpstreamPayload = Record<string, SuiteStats>[]\nexport type ReporterUpstream = (data: ReporterUpstreamPayload) => void\n\n/**\n * Foundation class for adapter TestReporters. Owns the cross-framework\n * scaffolding (suite collection, upstream batching). Framework-specific\n * lifecycle hooks (spec-file scanning, UID generation, skipped-test\n * synthesis) stay in subclasses.\n *\n * Service uses the WDIO reporter base instead — this class is for adapters\n * that own their own reporter lifecycle (nightwatch, selenium).\n */\nexport abstract class TestReporterBase {\n #report: ReporterUpstream\n protected allSuites: SuiteStats[] = []\n\n constructor(report: ReporterUpstream) {\n this.#report = report\n resetSignatureCounters()\n }\n\n /** Swap the upstream sink, e.g. after a WS reconnect. */\n updateUpstream(report: ReporterUpstream): void {\n this.#report = report\n }\n\n /** Manually trigger a flush of current state to the UI. */\n updateSuites(): void {\n this.sendUpstream()\n }\n\n /**\n * Reset collected state. Subclasses with extra state (test-name cache,\n * current-suite ref) override and call `super.clearExecutionData()` first.\n */\n clearExecutionData(): void {\n this.allSuites = []\n resetSignatureCounters()\n }\n\n /** Default: find by `uid`, replace in place. */\n onTestEnd(test: TestStats): void {\n for (const suite of this.allSuites) {\n const idx = suite.tests.findIndex(\n (t) => typeof t !== 'string' && t.uid === test.uid\n )\n if (idx !== -1) {\n suite.tests[idx] = test\n break\n }\n }\n this.sendUpstream()\n }\n\n /** Default: just flush. Subclasses with skipped-test synthesis override. */\n onSuiteEnd(_suite: SuiteStats): void {\n this.sendUpstream()\n }\n\n get report(): SuiteStats[] {\n return this.allSuites\n }\n\n /**\n * Flush current suite state to the upstream callback. Empty-payload guard\n * matches the existing adapter behavior — UI shouldn't receive an empty\n * array.\n */\n protected sendUpstream(): void {\n const payload: ReporterUpstreamPayload = []\n for (const suite of this.allSuites) {\n if (suite.uid) {\n payload.push({ [suite.uid]: suite })\n }\n }\n if (payload.length > 0) {\n this.#report(payload)\n }\n }\n}\n","/**\n * Run-level lifecycle helpers for the Nightwatch plugin — reuse-mode setup,\n * DevTools-browser spawning, end-of-run summary, and the post-run \"wait for\n * browser close\" loop.\n *\n * Extracted from `index.ts` to keep that file under the file-size cap.\n */\n\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport * as os from 'node:os'\nimport logger from '@wdio/logger'\nimport { remote } from 'webdriverio'\nimport { errorMessage } from '@wdio/devtools-core'\nimport { REUSE_ENV } from '@wdio/devtools-shared'\nimport { start, stop } from '@wdio/devtools-backend'\n\nimport type { SessionCapturer } from './session.js'\nimport type { TestReporter } from './reporter.js'\nimport type { SuiteManager } from './helpers/suiteManager.js'\nimport type { TestManager } from './helpers/testManager.js'\nimport type {\n DevToolsMode,\n NightwatchBrowser,\n NightwatchCurrentTest\n} from './types.js'\nimport { TIMING, PLUGIN_GLOBAL_KEY } from './constants.js'\nimport { findFreePort, resolveNightwatchConfig } from './helpers/utils.js'\n\nconst log = logger('@wdio/nightwatch-devtools:run-lifecycle')\n\nexport interface RunLifecycleCtx {\n options: { hostname: string; port: number; mode?: DevToolsMode }\n readonly testReporter: TestReporter | undefined\n readonly suiteManager: SuiteManager | undefined\n readonly testManager: TestManager\n readonly sessionCapturer: SessionCapturer | undefined\n devtoolsBrowser: WebdriverIO.Browser | undefined\n userDataDir: string | undefined\n passCount: number\n failCount: number\n skipCount: number\n clearExecutionData(): void\n}\n\nexport function handleReuseMode(ctx: RunLifecycleCtx): void {\n ctx.options.hostname = process.env[REUSE_ENV.HOST]!\n ctx.options.port = Number(process.env[REUSE_ENV.PORT])\n log.info(\n `♻ Reusing DevTools backend at ${ctx.options.hostname}:${ctx.options.port}`\n )\n // Clear execution data from the previous run when rerunning so test-name\n // caches and suites are fresh for the new run.\n if (ctx.testReporter) {\n ctx.clearExecutionData()\n ctx.passCount = 0\n ctx.failCount = 0\n ctx.skipCount = 0\n log.info('Cleared execution data for rerun')\n }\n}\n\nexport interface PluginBeforeCtx extends RunLifecycleCtx {\n setConfigPath(v: string | undefined): void\n openDevtoolsBrowserAt(url: string): Promise<void>\n handleReuse(): void\n // The plugin instance to assign to the global slot for cucumber hooks.\n plugin: unknown\n}\n\nexport async function runPluginBefore(ctx: PluginBeforeCtx): Promise<void> {\n // When relaunched by the DevTools UI rerun button the backend is already\n // running — skip startup and just connect the WebSocket worker.\n const isReuse =\n process.env[REUSE_ENV.REUSE] === '1' &&\n !!process.env[REUSE_ENV.HOST] &&\n !!process.env[REUSE_ENV.PORT]\n if (isReuse) {\n ctx.handleReuse()\n }\n const configPath = resolveNightwatchConfig()\n ctx.setConfigPath(configPath)\n if (configPath) {\n log.info(`✓ Config: ${configPath}`)\n } else {\n log.warn(\n 'Could not find nightwatch config — test rerun will be unavailable'\n )\n }\n if (isReuse) {\n ;(globalThis as Record<string, unknown>)[PLUGIN_GLOBAL_KEY] = ctx.plugin\n return\n }\n try {\n // Trace mode: skip backend port-bind and UI entirely — matches the WDIO\n // launcher gate. SessionCapturer construction in session-init also gates\n // its WS init off in trace mode.\n if (ctx.options.mode === 'trace') {\n log.info('Trace mode — skipping backend port-bind and UI window')\n } else {\n ctx.options.port = await findFreePort(\n ctx.options.port,\n ctx.options.hostname\n )\n log.info('🚀 Starting DevTools backend...')\n const { port } = await start(ctx.options)\n ctx.options.port = port\n const url = `http://${ctx.options.hostname}:${ctx.options.port}`\n log.info(`✓ Backend started on port ${ctx.options.port}`)\n log.info(` DevTools UI: ${url}`)\n await ctx.openDevtoolsBrowserAt(url)\n await new Promise((resolve) =>\n setTimeout(resolve, TIMING.UI_CONNECTION_WAIT)\n )\n }\n ;(globalThis as Record<string, unknown>)[PLUGIN_GLOBAL_KEY] = ctx.plugin\n } catch (err) {\n log.error(`Failed to start backend: ${errorMessage(err)}`)\n throw err\n }\n}\n\nexport async function openDevtoolsBrowser(\n ctx: RunLifecycleCtx,\n url: string\n): Promise<void> {\n try {\n // Unique user data directory per instance to prevent conflicts.\n ctx.userDataDir = path.join(\n os.tmpdir(),\n `nightwatch-devtools-${ctx.options.port}-${Date.now()}`\n )\n if (!fs.existsSync(ctx.userDataDir)) {\n fs.mkdirSync(ctx.userDataDir, { recursive: true })\n }\n ctx.devtoolsBrowser = await remote({\n logLevel: 'info',\n automationProtocol: 'devtools',\n capabilities: {\n browserName: 'chrome',\n 'goog:chromeOptions': {\n args: [\n '--window-size=1600,1200',\n `--user-data-dir=${ctx.userDataDir}`,\n '--no-first-run',\n '--no-default-browser-check'\n ]\n },\n // Dashboard uses the Puppeteer-based 'devtools' protocol; drop the\n // \"controlled by automated test software\" infobar by ignoring\n // Puppeteer's --enable-automation default. (Runtime-valid capability\n // not present in the type.)\n 'wdio:devtoolsOptions': {\n ignoreDefaultArgs: ['--enable-automation']\n }\n } as WebdriverIO.Capabilities\n })\n await ctx.devtoolsBrowser.url(url)\n } catch (err) {\n log.error(`Failed to open DevTools UI: ${errorMessage(err)}`)\n log.info(`Please manually open: ${url}`)\n }\n}\n\nexport async function finalizeAllSuites(\n ctx: RunLifecycleCtx,\n browser?: NightwatchBrowser\n): Promise<void> {\n const currentTest = browser?.currentTest as NightwatchCurrentTest | undefined\n const testcases = currentTest?.results?.testcases ?? {}\n for (const [, suite] of (\n ctx.suiteManager?.getAllSuites() ?? new Map()\n ).entries()) {\n ctx.testManager.finalizeSuiteTests(suite, testcases)\n await new Promise((resolve) =>\n setTimeout(resolve, TIMING.SUITE_COMPLETE_DELAY)\n )\n ctx.suiteManager?.finalizeSuite(suite)\n }\n await new Promise((resolve) =>\n setTimeout(resolve, TIMING.SUITE_COMPLETE_DELAY)\n )\n}\n\nexport function logRunSummary(ctx: RunLifecycleCtx): void {\n const summary = [\n ctx.passCount > 0 ? `${ctx.passCount} passed` : null,\n ctx.failCount > 0 ? `${ctx.failCount} failed` : null,\n ctx.skipCount > 0 ? `${ctx.skipCount} skipped` : null\n ]\n .filter(Boolean)\n .join(' ')\n log.info(`${ctx.failCount > 0 ? '❌' : '✅'} Tests complete! ${summary}`)\n log.info(` DevTools UI: http://${ctx.options.hostname}:${ctx.options.port}`)\n}\n\nexport async function waitForDevtoolsBrowserClose(\n ctx: RunLifecycleCtx\n): Promise<void> {\n if (!ctx.devtoolsBrowser) {\n return\n }\n ;(logger as { setLevel: (ns: string, lvl: string) => void }).setLevel(\n 'devtools',\n 'warn'\n )\n let exitBySignal = false\n const signalHandler = () => {\n exitBySignal = true\n log.info('\\n✓ Exiting... Browser window will remain open')\n process.exit(0)\n }\n process.once('SIGINT', signalHandler)\n process.once('SIGTERM', signalHandler)\n while (true) {\n try {\n await ctx.devtoolsBrowser.getTitle()\n await new Promise((res) => setTimeout(res, TIMING.BROWSER_POLL_INTERVAL))\n } catch {\n if (!exitBySignal) {\n log.info('Browser window closed, stopping DevTools app')\n break\n }\n }\n }\n if (exitBySignal) {\n return\n }\n process.removeListener('SIGINT', signalHandler)\n process.removeListener('SIGTERM', signalHandler)\n ;(logger as { setLevel: (ns: string, lvl: string) => void }).setLevel(\n 'devtools',\n 'info'\n )\n try {\n await ctx.devtoolsBrowser.deleteSession()\n } catch {\n /* session already closed */\n }\n await stop()\n process.exit(0)\n}\n","/**\n * Internal Nightwatch commands to exclude from capture\n */\nexport const INTERNAL_COMMANDS_TO_IGNORE = [\n 'isAppiumClient',\n 'isSafari',\n 'isChrome',\n 'isFirefox',\n 'isEdge',\n 'isMobile',\n 'isAndroid',\n 'isIOS',\n 'session',\n 'sessions',\n 'timeouts',\n 'timeoutsAsyncScript',\n 'timeoutsImplicitWait',\n 'getLog',\n 'getLogTypes',\n 'screenshot',\n 'availableContexts',\n 'currentContext',\n 'setChromeOptions',\n 'setDeviceName',\n 'perform',\n 'execute',\n 'executeAsync',\n 'executeScript',\n // Internal Nightwatch transport commands (used for log capture, not user actions)\n 'sessionLog',\n 'sessionLogTypes',\n 'isLogAvailable',\n 'end'\n] as const\n\n// Console capture constants are defined in @wdio/devtools-core; re-exported\n// here so existing imports from ./constants.js continue to work.\nexport {\n ANSI_REGEX,\n CONSOLE_METHODS,\n LOG_LEVEL_PATTERNS,\n LOG_SOURCES\n} from '@wdio/devtools-core'\n\nimport { DEFAULTS_BASE, TIMING_BASE } from '@wdio/devtools-shared'\n\nexport const DEFAULTS = {\n ...DEFAULTS_BASE,\n TEST_NAME: 'unknown',\n FILE_NAME: 'unknown'\n} as const\n\nexport const TIMING = {\n ...TIMING_BASE,\n /** Nightwatch boots slower than selenium — give the dashboard 10s. */\n UI_CONNECTION_WAIT: 10000\n} as const\n\nexport { TEST_STATE } from '@wdio/devtools-shared'\n\n/**\n * Generic pattern matching Nightwatch commands whose result is a boolean.\n */\nexport const BOOLEAN_COMMAND_PATTERN =\n /^waitFor|^is[A-Z]|^has[A-Z]|(Visible|Present|Enabled|Selected|NotVisible|NotPresent)$/\n\nexport const NAVIGATION_COMMANDS = ['url', 'navigate', 'navigateTo'] as const\n\nexport { SPINNER_RE } from '@wdio/devtools-core'\n\n/** Matches file names that follow the *.test.ts / *.spec.js naming\n * convention. Re-exported from @wdio/devtools-shared (single source of\n * truth — service uses the same pattern under the name SPEC_FILE_RE). */\nexport { SPEC_FILE_RE as TEST_FILE_PATTERN } from '@wdio/devtools-shared'\n\n/** Nightwatch config file names to search for, in priority order. */\nexport const CONFIG_FILENAMES = [\n 'nightwatch.conf.cjs',\n 'nightwatch.conf.js',\n 'nightwatch.conf.ts',\n 'nightwatch.conf.mjs',\n 'nightwatch.json'\n] as const\n\n/**\n * Global key used to share the plugin instance with Cucumber hooks.\n * Must match across index.ts and cucumberHooks.cts.\n */\nexport const PLUGIN_GLOBAL_KEY = '__nightwatchDevtoolsPlugin'\n","import * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport { parse as parseStackTrace } from 'stacktrace-parser'\nimport {\n findTestDefinitions,\n generateStableUid as generateStableUidByFileName,\n isUserCodeFrame,\n normalizeFilePath\n} from '@wdio/devtools-core'\nimport { TEST_FILE_PATTERN, CONFIG_FILENAMES } from '../constants.js'\nimport type {\n NightwatchTestCase,\n TestFileMetadata,\n StepLocation,\n TestStats\n} from '../types.js'\n\n// These three are pure re-exports — adapters use the core implementations\n// directly, no wrapper logic. Single-line re-exports keep the indirection\n// visible without introducing dummy variables.\nexport {\n deterministicUid,\n getCallSourceFromStack,\n resetSignatureCounters\n} from '@wdio/devtools-core'\n\nexport function determineTestState(\n testcase: NightwatchTestCase\n): 'passed' | 'failed' | 'skipped' {\n if (testcase.passed === 0 && testcase.failed === 0) {\n return 'skipped'\n }\n return testcase.passed > 0 && testcase.failed === 0 ? 'passed' : 'failed'\n}\n\nexport function getTestIcon(state: TestStats['state']): string {\n return state === 'passed' ? '✅' : state === 'skipped' ? '⏭' : '❌'\n}\n\nexport function incrementCounters(\n counters: { passCount: number; failCount: number; skipCount: number },\n state: TestStats['state']\n): void {\n if (state === 'passed') {\n counters.passCount++\n } else if (state === 'skipped') {\n counters.skipCount++\n } else {\n counters.failCount++\n }\n}\n\nexport function buildPluginMetadataOptions(input: {\n isCucumberRunner: boolean\n configPath: string | undefined\n}) {\n return {\n framework: input.isCucumberRunner ? 'nightwatch-cucumber' : 'nightwatch',\n configFile: input.configPath,\n baseDir: process.cwd(),\n runCapabilities: {\n canRunSuites: true,\n canRunTests: !input.isCucumberRunner,\n canRunAll: false\n }\n }\n}\n\n/**\n * Generate stable UID for test/suite.\n * Accepts either (item: SuiteStats | TestStats) or (file: string, name: string).\n * Hashing is delegated to @wdio/devtools-core; this wrapper preserves the\n * dual-signature convenience used by the Nightwatch suite/test managers.\n */\ntype StableUidSource = { file?: string; fullTitle?: string; title?: string }\nexport function generateStableUid(\n itemOrFile: string | StableUidSource,\n name?: string\n): string {\n let file: string, testName: string\n if (\n typeof itemOrFile === 'object' &&\n itemOrFile !== null &&\n name === undefined\n ) {\n file = itemOrFile.file || ''\n testName = String(itemOrFile.fullTitle || itemOrFile.title)\n } else {\n file = (itemOrFile as string) || ''\n testName = String(name || '')\n }\n return generateStableUidByFileName(file, testName)\n}\n\n/**\n * Find test file from stack trace.\n * Parses call stack to find the first frame that looks like a test file.\n */\nexport function findTestFileFromStack(): string | undefined {\n const stack = new Error().stack\n if (!stack) {\n return undefined\n }\n\n // Prefer a frame whose filename matches *.test.ts / *.spec.ts (strong\n // signal). Fall back to the first plain user-code frame so arbitrary\n // project layouts (e.g. Cucumber step files under `src/steps/`) still\n // surface their owning file instead of going undetected.\n const frames = parseStackTrace(stack)\n const preferred = frames.find(\n (f) => isUserCodeFrame(f) && TEST_FILE_PATTERN.test(f.file)\n )\n const frame = preferred ?? frames.find(isUserCodeFrame)\n if (!frame?.file) {\n return undefined\n }\n\n const filePath = normalizeFilePath(frame.file)\n return fs.existsSync(filePath) ? filePath : undefined\n}\n\n/**\n * Extract suite and test names — and their line numbers — from a test file.\n * Returns a TestFileMetadata object used to build `callSource` strings for\n * the TestLens eye-icon navigation.\n */\nexport function extractTestMetadata(filePath: string): TestFileMetadata {\n const defs = findTestDefinitions(filePath, {\n includeNightwatchObjectStyle: true\n })\n const firstSuite = defs.find((d) => d.kind === 'suite')\n const tests = defs.filter((d) => d.kind === 'test')\n return {\n suiteTitle: firstSuite?.title ?? null,\n suiteLine: firstSuite?.line ?? null,\n testNames: tests.map((t) => t.title),\n testLines: tests.map((t) => t.line)\n }\n}\n\n/**\n * Find test file by searching the workspace for a matching filename.\n * Used when the stack trace doesn't have the file yet (e.g. in beforeEach).\n */\nexport function findTestFileByName(\n filename: string,\n workspaceRoot?: string\n): string | undefined {\n if (!filename || !workspaceRoot) {\n return undefined\n }\n\n const baseFilename = filename.replace(/\\.[cm]?[jt]sx?$/, '')\n\n function searchDir(dir: string, depth = 0): string | undefined {\n if (depth > 5) {\n return undefined\n }\n try {\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const fullPath = path.join(dir, entry.name)\n if (entry.isDirectory()) {\n const found = searchDir(fullPath, depth + 1)\n if (found) {\n return found\n }\n } else if (\n TEST_FILE_PATTERN.test(entry.name) &&\n entry.name.replace(TEST_FILE_PATTERN, '') === baseFilename\n ) {\n return fullPath\n }\n }\n } catch {\n // Permission denied or other error — skip\n }\n return undefined\n }\n\n return searchDir(workspaceRoot)\n}\n\n// ---------------------------------------------------------------------------\n// Console / log helpers (used by SessionCapturer)\n// ---------------------------------------------------------------------------\n\n// Console helpers come from @wdio/devtools-core. `stripAnsiCodes` is the\n// local name kept for backwards compatibility with existing import sites.\nexport {\n stripAnsi as stripAnsiCodes,\n detectLogLevel,\n createConsoleLogEntry\n} from '@wdio/devtools-core'\n\nexport { chromeLogLevelToLogLevel } from '@wdio/devtools-core'\n\n/** Derive a human-readable request type from URL and MIME type. */\nexport { getRequestType } from '@wdio/devtools-core'\n\n// ---------------------------------------------------------------------------\n// Cucumber helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Parse a feature file for a given scenario in a single pass, returning:\n * - `featureLine` — 1-based line of the `Feature:` declaration\n * - `scenarioLine` — 1-based line of the matching `Scenario:` block\n * - `stepLines` — 1-based line numbers for each step (for TestLens navigation)\n * - `stepKeywords` — BDD keyword (Given/When/Then/And/But) for each step (for labels)\n */\nfunction collectStepsAfterScenario(\n lines: string[],\n scenarioIndex: number,\n stepCount: number\n): { stepLines: number[]; stepKeywords: string[] } {\n const stepRe = /^\\s*(Given|When|Then|And|But)\\s+/i\n const stepLines: number[] = []\n const stepKeywords: string[] = []\n for (\n let j = scenarioIndex + 1;\n j < lines.length && stepLines.length < stepCount;\n j++\n ) {\n if (/^\\s*(?:Scenario:|Feature:)/i.test(lines[j])) {\n break\n }\n const m = stepRe.exec(lines[j])\n if (m) {\n stepLines.push(j + 1)\n stepKeywords.push(m[1])\n }\n }\n return { stepLines, stepKeywords }\n}\n\nexport function parseCucumberScenario(\n featureContent: string,\n scenarioName: string,\n stepTexts: string[]\n): {\n featureLine: number\n scenarioLine: number\n stepLines: number[]\n stepKeywords: string[]\n} {\n const stepCount = stepTexts.length\n if (!featureContent) {\n return {\n featureLine: 1,\n scenarioLine: 1,\n stepLines: [],\n stepKeywords: Array<string>(stepCount).fill('')\n }\n }\n const lines = featureContent.split('\\n')\n let featureLine = 1\n let scenarioLine = 1\n let stepLines: number[] = []\n let stepKeywords: string[] = []\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]\n const lineNum = i + 1\n if (featureLine === 1 && /^\\s*Feature:/i.test(line)) {\n featureLine = lineNum\n continue\n }\n if (/^\\s*Scenario:/i.test(line) && line.includes(scenarioName)) {\n scenarioLine = lineNum\n const collected = collectStepsAfterScenario(lines, i, stepCount)\n stepLines = collected.stepLines\n stepKeywords = collected.stepKeywords\n break\n }\n }\n while (stepKeywords.length < stepCount) {\n stepKeywords.push('')\n }\n return { featureLine, scenarioLine, stepLines, stepKeywords }\n}\n\n/** Maps Cucumber parameter type placeholder names to their regex equivalents. */\nconst CUCUMBER_TYPE_PATTERNS: Record<string, string> = {\n int: '-?\\\\d+',\n float: '-?\\\\d+(?:\\\\.\\\\d+)?',\n string: '(?:\"[^\"]*\"|\\'[^\\']*\\')',\n word: '\\\\S+'\n}\n\n/**\n * Convert a Cucumber expression pattern to a regular expression and test it\n * against the given step text. Handles {int}, {float}, {string}, {word}, and\n * arbitrary {type} placeholders.\n */\nfunction matchesCucumberExpression(pattern: string, text: string): boolean {\n if (pattern === text) {\n return true\n }\n const regexSource = pattern\n .split(/(\\{[^}]*\\})/)\n .map((part, idx) => {\n if (idx % 2 === 1) {\n const type = part.slice(1, -1)\n return CUCUMBER_TYPE_PATTERNS[type] ?? '[\\\\s\\\\S]+'\n }\n return part.replace(/[|\\\\{}()[\\]^$+*?.]/g, '\\\\$&')\n })\n .join('')\n try {\n return new RegExp(`^${regexSource}$`).test(text)\n } catch {\n return false\n }\n}\n\n/**\n * Find the file and 1-based line number of the step definition that matches\n * the given step text. Searches the provided step definition files in order.\n * Handles both string/Cucumber-expression patterns and regex literals.\n */\nexport function findStepDefinitionLine(\n stepDefFiles: Array<{ filePath: string; content: string }>,\n stepText: string\n): StepLocation | null {\n const stepDefRe = /(?:Given|When|Then|And|But)\\s*\\(/i\n\n for (const { filePath, content } of stepDefFiles) {\n const lines = content.split('\\n')\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]\n if (!stepDefRe.test(line)) {\n continue\n }\n\n // String / Cucumber-expression: Given('pattern', ...) or Given(\"pattern\", ...)\n const literalMatch = line.match(\n /(?:Given|When|Then|And|But)\\s*\\(\\s*(['\"`])(.*?)\\1/i\n )\n if (literalMatch) {\n if (matchesCucumberExpression(literalMatch[2], stepText)) {\n return { filePath, line: i + 1 }\n }\n continue\n }\n\n // Regex literal: Given(/pattern/flags, ...)\n const regexMatch = line.match(\n /(?:Given|When|Then|And|But)\\s*\\(\\s*\\/(.+?)\\/([gimy]*)/i\n )\n if (regexMatch) {\n try {\n if (new RegExp(regexMatch[1], regexMatch[2]).test(stepText)) {\n return { filePath, line: i + 1 }\n }\n } catch {\n // invalid regex — skip\n }\n }\n }\n }\n return null\n}\n\nexport { isPortInUse, findFreePort } from '@wdio/devtools-core'\n\nexport function resolveNightwatchConfig(): string | undefined {\n // Prefer the config explicitly passed via -c / --config to avoid picking up\n // an unrelated config file that happens to sit higher in the directory tree.\n const configFlagIdx = process.argv.findIndex(\n (arg) => arg === '--config' || arg === '-c'\n )\n if (configFlagIdx !== -1 && process.argv[configFlagIdx + 1]) {\n const argvConfig = process.argv[configFlagIdx + 1]\n const resolved = path.isAbsolute(argvConfig)\n ? argvConfig\n : path.resolve(process.cwd(), argvConfig)\n if (fs.existsSync(resolved)) {\n return resolved\n }\n }\n\n // Fallback: walk up the directory tree\n let dir = process.cwd()\n const root = path.parse(dir).root\n while (dir && dir !== root) {\n for (const file of CONFIG_FILENAMES) {\n const candidate = path.join(dir, file)\n if (fs.existsSync(candidate)) {\n return candidate\n }\n }\n const parent = path.dirname(dir)\n if (parent === dir) {\n break\n }\n dir = parent\n }\n return undefined\n}\n","/**\n * Nightwatch eventHub integration.\n *\n * Extracted from the plugin class so registration stays out of the\n * file-size cap and the metadata-forwarding behavior is unit-testable\n * without standing up the whole plugin. The plugin's\n * `registerEventHandlers(eventHub)` delegates here.\n */\n\nimport logger from '@wdio/logger'\nimport { errorMessage } from '@wdio/devtools-core'\nimport { TraceType } from './types.js'\nimport type { SessionCapturer } from './session.js'\nimport type { NightwatchEventHub } from './types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:event-hub')\n\nexport interface EventHubBindings {\n /** Live session capturer — may be undefined until bringup completes. */\n getSessionCapturer(): SessionCapturer | undefined\n /** Builds the `options` field forwarded with the metadata payload. */\n buildMetadataOptions(): unknown\n /** Lets the plugin flip the cucumber-runner flag on detection. */\n setCucumberRunner(value: boolean): void\n}\n\nfunction makeSessionMetadataHandler(\n bindings: EventHubBindings\n): (data: unknown) => void {\n return (data: unknown) => {\n try {\n const md =\n ((data as { metadata?: Record<string, unknown> } | undefined)\n ?.metadata as Record<string, unknown> | undefined) ?? {}\n const capturer = bindings.getSessionCapturer()\n const sessionCapabilities = md.sessionCapabilities\n const sessionId = md.sessionId as string | undefined\n if (!capturer || (!sessionCapabilities && !sessionId)) {\n return\n }\n capturer.sendUpstream('metadata', {\n type: TraceType.Testrunner,\n capabilities: sessionCapabilities ?? {},\n sessionId,\n testEnv: md.testEnv as string | undefined,\n host: md.host as string | undefined,\n modulePath: md.modulePath as string | undefined,\n options: bindings.buildMetadataOptions()\n })\n } catch (err) {\n log.error(`Error in event handler: ${errorMessage(err)}`)\n }\n }\n}\n\nexport function registerEventHandlers(\n eventHub: NightwatchEventHub,\n bindings: EventHubBindings\n): void {\n bindings.setCucumberRunner(eventHub.runner === 'cucumber')\n if (eventHub.runner === 'cucumber') {\n log.info('✓ Cucumber runner detected via NightwatchEventHub')\n }\n log.info('✓ NightwatchEventHub registered — enriched metadata enabled')\n const handler = makeSessionMetadataHandler(bindings)\n eventHub.on('TestSuiteStarted', handler)\n eventHub.on('TestRunStarted', handler)\n}\n","/**\n * Cucumber lifecycle for the Nightwatch plugin.\n *\n * Extracted from the plugin class to keep `index.ts` under the file-size cap\n * and to isolate the cucumber-specific orchestration from the per-test\n * (Mocha/Jasmine-style) lifecycle.\n *\n * The plugin passes itself as a `CucumberLifecycleCtx` — a narrow interface\n * exposing only the fields and methods this module needs. The lifecycle\n * helpers mutate the plugin's \"current execution\" state via the accessors on\n * the ctx (they can't reach the plugin's private fields directly).\n */\n\nimport logger from '@wdio/logger'\nimport { errorMessage } from '@wdio/devtools-core'\nimport { WS_SCOPE } from '@wdio/devtools-shared'\n\nimport type { SessionCapturer } from './session.js'\nimport type { TestReporter } from './reporter.js'\nimport type { TestManager } from './helpers/testManager.js'\nimport type { SuiteManager } from './helpers/suiteManager.js'\nimport type { BrowserProxy } from './helpers/browserProxy.js'\nimport type { NightwatchBrowser, SuiteStats, TestStats } from './types.js'\nimport { TEST_STATE } from './constants.js'\nimport {\n closeOpenSteps,\n cucumberResultToTestState\n} from './helpers/cucumberResult.js'\nimport { buildCucumberScenarioSuite } from './helpers/cucumberScenarioBuilder.js'\nimport { scanFeatureFile } from './helpers/featureFileScan.js'\nimport { parseCucumberScenario } from './helpers/utils.js'\n\nconst log = logger('@wdio/nightwatch-devtools:cucumber')\n\n/** Minimal shapes for the Cucumber objects we touch. Cucumber's own types\n * vary across major versions; we pin only fields we read. */\nexport interface CucumberPickleStep {\n text?: string\n astNodeIds?: string[]\n location?: { line?: number }\n}\nexport interface CucumberPickle {\n uri?: string\n name?: string\n location?: { line?: number }\n astNodeIds?: string[]\n steps?: CucumberPickleStep[]\n}\nexport interface CucumberResult {\n status?: string\n}\n\nexport interface CucumberLifecycleCtx {\n readonly sessionCapturer: SessionCapturer\n readonly testReporter: TestReporter\n readonly testManager: TestManager\n readonly suiteManager: SuiteManager\n readonly browserProxy: BrowserProxy | undefined\n setCucumberRunner(v: boolean): void\n ensureSessionInitialized(browser: NightwatchBrowser): Promise<void>\n wrapBrowserOnce(browser: NightwatchBrowser): void\n incrementCount(state: TestStats['state']): void\n testIcon(state: TestStats['state']): string\n getCurrentScenarioSuite(): SuiteStats | null\n setCurrentScenarioSuite(s: SuiteStats | null): void\n setCurrentStep(s: unknown): void\n getCurrentStep(): unknown\n setCurrentTest(t: unknown): void\n}\n\ntype MutStep = {\n title?: string\n state?: string\n start?: Date | null\n end?: Date | null\n _duration?: number\n}\n\nfunction attachScenarioToFeature(\n ctx: CucumberLifecycleCtx,\n featureSuite: SuiteStats,\n scenarioSuite: SuiteStats\n): void {\n // If a suite with this uid already exists, this is a RETRY of the same\n // scenario — clear execution data so only the latest attempt shows.\n const existingIdx = featureSuite.suites.findIndex(\n (s: SuiteStats) => s.uid === scenarioSuite.uid\n )\n if (existingIdx !== -1) {\n featureSuite.suites[existingIdx] = scenarioSuite\n // Pass the specific scenario uid so only this scenario's execution data\n // is reset — a uid-less clearExecutionData would mark ALL suites as\n // running, destroying the previous terminal states of sibling scenarios.\n ctx.sessionCapturer.sendUpstream(WS_SCOPE.clearExecutionData, {\n uid: scenarioSuite.uid,\n entryType: 'suite'\n })\n } else {\n featureSuite.suites.push(scenarioSuite)\n }\n}\n\nfunction createFeatureSuite(\n ctx: CucumberLifecycleCtx,\n featureUri: string,\n featureName: string,\n featureContent: string,\n featureAbsPath: string,\n scenarioName: string,\n steps: Array<{ text: string }>\n): {\n featureSuite: SuiteStats\n scenarioLine: number\n stepLines: number[]\n stepKeywords: string[]\n} {\n const featureSuite = ctx.suiteManager.getOrCreateSuite(\n featureUri,\n featureName,\n featureUri,\n []\n )\n ctx.suiteManager.markSuiteAsRunning(featureSuite)\n const { featureLine, scenarioLine, stepLines, stepKeywords } =\n parseCucumberScenario(\n featureContent,\n scenarioName,\n steps.map((s) => s.text)\n )\n if (featureAbsPath && featureLine > 0) {\n featureSuite.callSource = `${featureAbsPath}:${featureLine}`\n }\n return { featureSuite, scenarioLine, stepLines, stepKeywords }\n}\n\nfunction normalizeSteps(\n pickleSteps: CucumberPickleStep[] | undefined\n): Array<{ text: string }> {\n return (pickleSteps ?? []).map((s) => ({ text: s.text ?? '' }))\n}\n\nexport async function initCucumberScenario(\n ctx: CucumberLifecycleCtx,\n browser: NightwatchBrowser,\n pickle: CucumberPickle\n): Promise<void> {\n await ctx.ensureSessionInitialized(browser)\n const featureUri: string = pickle.uri ?? 'unknown.feature'\n const scenarioName: string = pickle.name ?? 'Unknown Scenario'\n const steps = normalizeSteps(pickle.steps)\n const {\n featureName,\n featureContent,\n featureAbsPath,\n stepDefFiles,\n capturedPaths\n } = scanFeatureFile(featureUri)\n for (const p of capturedPaths) {\n ctx.sessionCapturer.captureSource(p).catch(() => {})\n }\n const { featureSuite, scenarioLine, stepLines, stepKeywords } =\n createFeatureSuite(\n ctx,\n featureUri,\n featureName,\n featureContent,\n featureAbsPath,\n scenarioName,\n steps\n )\n const scenarioSuite = buildCucumberScenarioSuite({\n featureUri,\n scenarioName,\n featureName,\n featureAbsPath,\n stepDefFiles,\n steps,\n stepLines,\n stepKeywords,\n scenarioLine,\n parentFeatureSuiteUid: featureSuite.uid\n })\n attachScenarioToFeature(ctx, featureSuite, scenarioSuite)\n ctx.setCurrentScenarioSuite(scenarioSuite)\n ctx.setCurrentStep(null)\n ctx.setCurrentTest(null)\n ctx.testReporter.updateSuites()\n ctx.wrapBrowserOnce(browser)\n log.info(`🥒 Scenario: ${scenarioName}`)\n}\n\nexport async function finalizeCucumberScenario(\n ctx: CucumberLifecycleCtx,\n browser: NightwatchBrowser,\n result: CucumberResult,\n pickle: CucumberPickle | undefined\n): Promise<void> {\n try {\n const scenarioState = cucumberResultToTestState(result)\n const scenario = ctx.getCurrentScenarioSuite()\n if (scenario) {\n const now = new Date()\n const duration =\n now.getTime() - (scenario.start?.getTime() ?? now.getTime())\n scenario.state = scenarioState\n scenario.end = now\n scenario._duration = duration\n closeOpenSteps(scenario, scenarioState, now)\n\n const featureUri: string = pickle?.uri ?? 'unknown.feature'\n ctx.testManager.markTestAsProcessed(featureUri, pickle?.name ?? '')\n\n const featureSuite = ctx.suiteManager.getSuite(featureUri)\n if (featureSuite) {\n // Finalize is not called until all scenarios are done — just update state.\n ctx.suiteManager.finalizeSuiteState(featureSuite)\n }\n\n ctx.incrementCount(scenarioState)\n const icon = ctx.testIcon(scenarioState)\n log.info(\n ` ${icon} ${pickle?.name ?? 'Unknown'} (${(duration / 1000).toFixed(2)}s)`\n )\n\n ctx.testReporter.updateSuites()\n ctx.setCurrentScenarioSuite(null)\n ctx.setCurrentStep(null)\n ctx.setCurrentTest(null)\n }\n await ctx.sessionCapturer.captureTrace(browser)\n } catch (err) {\n log.error(`Failed to finalize Cucumber scenario: ${errorMessage(err)}`)\n }\n}\n\nexport async function cucumberBeforeStep(\n ctx: CucumberLifecycleCtx,\n _browser: NightwatchBrowser,\n pickleStep: CucumberPickleStep,\n _pickle: CucumberPickle\n): Promise<void> {\n const scenario = ctx.getCurrentScenarioSuite()\n if (!scenario) {\n return\n }\n // Reset per-step dedup tracking so commands in step N are never\n // mistaken for retries of identically-signatured commands from step N-1.\n ctx.browserProxy?.resetCommandTracking()\n\n const stepText: string = pickleStep?.text ?? ''\n const step = (scenario.tests as Array<MutStep | string>).find(\n (t): t is MutStep =>\n typeof t !== 'string' &&\n (t.title?.endsWith(stepText) === true || t.title === stepText)\n )\n if (step) {\n step.state = TEST_STATE.RUNNING\n step.start = new Date()\n step.end = null\n ctx.setCurrentStep(step)\n ctx.testReporter.updateSuites()\n }\n}\n\nexport async function cucumberAfterStep(\n ctx: CucumberLifecycleCtx,\n _browser: NightwatchBrowser,\n result: CucumberResult,\n pickleStep: CucumberPickleStep,\n _pickle: CucumberPickle\n): Promise<void> {\n const step = ctx.getCurrentStep() as MutStep | null\n if (!step) {\n return\n }\n const status = String(result?.status ?? 'UNKNOWN').toUpperCase()\n const stepState: TestStats['state'] =\n status === 'PASSED'\n ? TEST_STATE.PASSED\n : status === 'SKIPPED'\n ? TEST_STATE.SKIPPED\n : TEST_STATE.FAILED\n step.state = stepState\n step.end = new Date()\n step._duration = Date.now() - (step.start?.getTime() ?? Date.now())\n ctx.setCurrentStep(null)\n ctx.testReporter.updateSuites()\n void pickleStep\n}\n\nexport async function cucumberBefore(\n ctx: CucumberLifecycleCtx,\n browser: NightwatchBrowser,\n pickle: CucumberPickle\n): Promise<void> {\n ctx.setCucumberRunner(true)\n await initCucumberScenario(ctx, browser, pickle)\n}\n\nexport async function cucumberAfter(\n ctx: CucumberLifecycleCtx,\n browser: NightwatchBrowser,\n result: CucumberResult,\n pickle: CucumberPickle\n): Promise<void> {\n await finalizeCucumberScenario(ctx, browser, result, pickle)\n}\n","import { TEST_STATE } from '../constants.js'\nimport type { SuiteStats, TestStats } from '../types.js'\n\n/**\n * Map a Cucumber Pickle step result's `status` field (PASSED/FAILED/SKIPPED/\n * UNKNOWN/PENDING/AMBIGUOUS/UNDEFINED) to the dashboard's TestStats state.\n * Everything non-passed and non-skipped is treated as FAILED for the UI —\n * the underlying error/message survives in the pickle for the Compare view.\n */\nexport function cucumberResultToTestState(result: unknown): TestStats['state'] {\n const status = String(\n (result as { status?: unknown })?.status ?? 'UNKNOWN'\n ).toUpperCase()\n if (status === 'PASSED') {\n return TEST_STATE.PASSED\n }\n if (status === 'SKIPPED') {\n return TEST_STATE.SKIPPED\n }\n return TEST_STATE.FAILED\n}\n\n/**\n * Cucumber's After hook fires with the scenario's final status, but any\n * still-running or pending steps (e.g. an early failure short-circuited\n * the rest) need to be closed too. Mark them PASSED only when the whole\n * scenario passed; FAILED otherwise. Pure in-place mutation — the suite's\n * `tests` array references are the same TestStats objects the dashboard\n * already received via earlier WS broadcasts.\n */\nexport function closeOpenSteps(\n suite: SuiteStats,\n scenarioState: TestStats['state'],\n now: Date = new Date()\n): void {\n for (const step of suite.tests) {\n if (typeof step === 'string') {\n continue\n }\n if (step.state === 'running' || step.state === 'pending') {\n step.state =\n scenarioState === TEST_STATE.PASSED\n ? TEST_STATE.PASSED\n : TEST_STATE.FAILED\n step.end = now\n }\n }\n}\n","import { DEFAULTS, TEST_STATE } from '../constants.js'\nimport type { SuiteStats } from '../types.js'\nimport { deterministicUid, findStepDefinitionLine } from './utils.js'\n\nexport interface CucumberScenarioBuildInput {\n /** Cucumber pickle URI — the .feature path. */\n featureUri: string\n scenarioName: string\n featureName: string\n /** Absolute file path used for callSource resolution (may be unresolved). */\n featureAbsPath?: string\n /** Step-definition files discovered for the feature (passed to find each step's source location). */\n stepDefFiles: Array<{ filePath: string; content: string }>\n /** Pickle steps as parsed by Cucumber. */\n steps: Array<{ text: string }>\n /** Per-step source line numbers from the .feature file (parsed once by the caller). */\n stepLines: number[]\n /** Per-step Gherkin keywords (Given/When/Then/And/But) parsed alongside `stepLines`. */\n stepKeywords: string[]\n /** Scenario header line number in the .feature file (0 if unresolvable). */\n scenarioLine: number\n /** Parent feature-suite uid — scenarios nest under this. */\n parentFeatureSuiteUid: string\n}\n\n/**\n * Build a fully-populated scenario sub-suite (with its `tests` array of step\n * TestStats entries) for one Cucumber scenario. Pure factory — no side effects;\n * the caller decides whether to push into the feature suite or replace an\n * existing entry (retry case).\n *\n * Extracted from `NightwatchDevToolsPlugin.#initCucumberScenario` — the\n * scenario+steps construction was ~70 lines of object-literal building that\n * doesn't touch plugin state.\n */\nfunction buildScenarioStepTest(\n input: CucumberScenarioBuildInput,\n scenarioUid: string,\n i: number\n): SuiteStats['tests'][number] {\n const {\n featureUri,\n scenarioName,\n featureAbsPath,\n stepDefFiles,\n steps,\n stepLines,\n stepKeywords\n } = input\n const step = steps[i]\n const keyword = stepKeywords[i] || ''\n const stepLabel = keyword ? `${keyword} ${step.text}` : step.text\n const stepDefLoc = findStepDefinitionLine(stepDefFiles, step.text)\n const callSource = stepDefLoc\n ? `${stepDefLoc.filePath}:${stepDefLoc.line}`\n : featureAbsPath && stepLines[i] > 0\n ? `${featureAbsPath}:${stepLines[i]}`\n : undefined\n return {\n uid: deterministicUid(featureUri, `step:${scenarioName}:${step.text}`),\n cid: DEFAULTS.CID,\n title: stepLabel,\n fullTitle: `${scenarioName} ${stepLabel}`,\n parent: scenarioUid,\n state: TEST_STATE.PENDING,\n start: new Date(),\n end: null,\n type: 'test' as const,\n file: featureUri,\n retries: 0,\n _duration: 0,\n hooks: [],\n callSource\n }\n}\n\nexport function buildCucumberScenarioSuite(\n input: CucumberScenarioBuildInput\n): SuiteStats {\n const {\n featureUri,\n scenarioName,\n featureName,\n featureAbsPath,\n steps,\n scenarioLine,\n parentFeatureSuiteUid\n } = input\n // deterministicUid (no counter) so the SAME scenario gets the SAME uid\n // across retries — that's what makes retry-coalescing work upstream.\n const scenarioUid = deterministicUid(featureUri, `scenario:${scenarioName}`)\n const scenarioSuite: SuiteStats = {\n uid: scenarioUid,\n cid: DEFAULTS.CID,\n title: scenarioName,\n fullTitle: `${featureName} ${scenarioName}`,\n parent: parentFeatureSuiteUid,\n type: 'suite' as const,\n file: featureUri,\n start: new Date(),\n state: TEST_STATE.RUNNING,\n end: null,\n tests: [],\n suites: [],\n hooks: [],\n _duration: 0,\n callSource:\n featureAbsPath && scenarioLine > 0\n ? `${featureAbsPath}:${scenarioLine}`\n : undefined\n }\n for (let i = 0; i < steps.length; i++) {\n scenarioSuite.tests.push(buildScenarioStepTest(input, scenarioUid, i))\n }\n return scenarioSuite\n}\n","import fs from 'fs'\nimport path from 'node:path'\n\nexport interface FeatureFileScan {\n /** Header `Feature:` value, or the filename basename if unreadable. */\n featureName: string\n /** Raw `.feature` file contents (empty when unreadable). */\n featureContent: string\n /** Absolute path to the `.feature` file (resolved from cwd + uri). */\n featureAbsPath: string\n /** Sibling step-definition files (under `step_definitions`/`steps`/`support`). */\n stepDefFiles: Array<{ filePath: string; content: string }>\n /** Paths the caller should feed to `sessionCapturer.captureSource` so the\n * dashboard's Source panel can render them. */\n capturedPaths: string[]\n}\n\n/**\n * Scan a Cucumber feature file and its sibling step-definitions. Pure I/O —\n * the caller invokes `sessionCapturer.captureSource` for each path in\n * `capturedPaths` so this helper stays free of the session capturer.\n */\nexport function scanFeatureFile(featureUri: string): FeatureFileScan {\n const featureAbsPath = path.resolve(process.cwd(), featureUri)\n const result: FeatureFileScan = {\n featureName: path.basename(featureUri, '.feature'),\n featureContent: '',\n featureAbsPath,\n stepDefFiles: [],\n capturedPaths: []\n }\n\n if (featureUri === 'unknown.feature' || !fs.existsSync(featureAbsPath)) {\n return result\n }\n\n result.featureContent = fs.readFileSync(featureAbsPath, 'utf-8')\n const match = result.featureContent.match(/^\\s*Feature:\\s*(.+)/m)\n if (match) {\n result.featureName = match[1].trim()\n }\n result.capturedPaths.push(featureAbsPath)\n\n const featureDir = path.dirname(featureAbsPath)\n const stepDirCandidates = ['step_definitions', 'steps', 'support']\n for (const candidate of stepDirCandidates) {\n const stepDir = path.join(featureDir, candidate)\n if (!fs.existsSync(stepDir) || !fs.statSync(stepDir).isDirectory()) {\n continue\n }\n for (const entry of fs.readdirSync(stepDir)) {\n if (!/\\.(js|ts|mjs|cjs)$/.test(entry)) {\n continue\n }\n const stepFilePath = path.join(stepDir, entry)\n result.capturedPaths.push(stepFilePath)\n try {\n result.stepDefFiles.push({\n filePath: stepFilePath,\n content: fs.readFileSync(stepFilePath, 'utf-8')\n })\n } catch {\n // skip unreadable files\n }\n }\n }\n\n return result\n}\n","/**\n * Test (Mocha/Jasmine-style) lifecycle helpers for the Nightwatch plugin.\n *\n * Extracted from the plugin class to keep `index.ts` under the file-size cap\n * and to keep the per-test orchestration distinct from the cucumber path.\n *\n * The plugin passes itself as a `TestLifecycleCtx` — a narrow interface that\n * exposes only the fields and methods these helpers need.\n */\n\nimport logger from '@wdio/logger'\nimport type { SessionCapturer } from './session.js'\nimport type { TestReporter } from './reporter.js'\nimport type { TestManager } from './helpers/testManager.js'\nimport type { SuiteManager } from './helpers/suiteManager.js'\nimport type { BrowserProxy } from './helpers/browserProxy.js'\nimport type {\n NightwatchBrowser,\n NightwatchCurrentTest,\n NightwatchTestCase,\n NightwatchTestResults,\n SuiteStats,\n TestStats\n} from './types.js'\nimport { DEFAULTS, TIMING, TEST_STATE } from './constants.js'\nimport { resolveSpecFilePath } from './helpers/specFileResolver.js'\nimport { closePreviousTest } from './helpers/closePreviousTest.js'\nimport { extractTestMetadata, determineTestState } from './helpers/utils.js'\n\nconst log = logger('@wdio/nightwatch-devtools:test-lifecycle')\n\nexport interface TestLifecycleCtx {\n readonly sessionCapturer: SessionCapturer\n readonly testReporter: TestReporter\n readonly testManager: TestManager\n readonly suiteManager: SuiteManager\n readonly browserProxy: BrowserProxy\n readonly srcFolders: string[]\n isScriptInjected: boolean\n getRerunLabel(): string | undefined\n incrementCount(state: TestStats['state']): void\n testIcon(state: TestStats['state']): string\n setCurrentTest(t: unknown): void\n}\n\ninterface SuiteMetadata {\n testFile: string\n fullPath: string | null\n suiteTitle: string\n testNames: string[]\n suiteLine: number | null\n testLines: number[]\n}\n\nexport function resolveSuiteMetadata(\n ctx: TestLifecycleCtx,\n currentTest: NightwatchCurrentTest\n): SuiteMetadata {\n const moduleName = currentTest.module ?? ''\n const testFile =\n moduleName.split('/').pop() || moduleName || DEFAULTS.FILE_NAME\n const fullPath = resolveSpecFilePath(\n testFile,\n moduleName,\n ctx.srcFolders,\n ctx.browserProxy.getCurrentTestFullPath() || undefined\n )\n if (!fullPath) {\n log.warn(\n `[beforeEach] Could not resolve file path for \"${testFile}\" — source view will be unavailable`\n )\n }\n let suiteTitle = testFile\n let testNames: string[] = []\n let suiteLine: number | null = null\n let testLines: number[] = []\n if (fullPath) {\n const parsed = extractTestMetadata(fullPath)\n if (parsed.suiteTitle) {\n suiteTitle = parsed.suiteTitle\n }\n testNames = parsed.testNames\n suiteLine = parsed.suiteLine\n testLines = parsed.testLines\n }\n const rerunLabel = ctx.getRerunLabel()\n if (rerunLabel) {\n const targetIndex = testNames.findIndex((name) => name === rerunLabel)\n if (targetIndex !== -1) {\n testNames = [testNames[targetIndex]]\n testLines = testLines[targetIndex] ? [testLines[targetIndex]] : []\n }\n }\n return { testFile, fullPath, suiteTitle, testNames, suiteLine, testLines }\n}\n\nexport function pickCurrentTestName(\n currentTest: NightwatchCurrentTest,\n testNames: string[],\n processedTests: Set<string>\n): string | undefined {\n const runtimeTestName =\n typeof currentTest?.name === 'string' ? currentTest.name.trim() : undefined\n const matchedRuntimeTestName = runtimeTestName\n ? testNames.find(\n (name) =>\n runtimeTestName === name || runtimeTestName.endsWith(` ${name}`)\n )\n : undefined\n return (\n matchedRuntimeTestName ||\n testNames.find((name) => !processedTests.has(name))\n )\n}\n\nexport async function startNextTest(\n ctx: TestLifecycleCtx,\n currentSuite: SuiteStats,\n currentTestName: string,\n processedTests: Set<string>\n): Promise<void> {\n if (processedTests.size === 0) {\n ctx.suiteManager.markSuiteAsRunning(currentSuite)\n }\n const test = ctx.testManager.findTestInSuite(currentSuite, currentTestName)\n if (test) {\n test.state = TEST_STATE.RUNNING as TestStats['state']\n test.start = new Date()\n test.end = null\n ctx.testReporter.onTestStart(test)\n ctx.setCurrentTest(test)\n log.info(` ▶ ${currentTestName}`)\n await new Promise((resolve) => setTimeout(resolve, TIMING.TEST_START_DELAY))\n } else {\n log.warn(\n `Test \"${currentTestName}\" not found in suite \"${currentSuite.title}\"`\n )\n ctx.setCurrentTest(null)\n }\n}\n\nexport async function closePreviousRunningTest(\n ctx: TestLifecycleCtx,\n currentSuite: SuiteStats,\n testFile: string,\n currentTest: NightwatchCurrentTest\n): Promise<void> {\n const runningTest = currentSuite.tests.find(\n (t): t is TestStats =>\n typeof t !== 'string' && t.state === TEST_STATE.RUNNING\n )\n if (!runningTest) {\n return\n }\n await closePreviousTest({\n runningTest,\n testFile,\n testcases: currentTest?.results?.testcases || {},\n testManager: ctx.testManager,\n incrementCount: (state) => ctx.incrementCount(state),\n testIcon: (state) => ctx.testIcon(state)\n })\n}\n\nexport function wrapBrowserOnce(\n ctx: TestLifecycleCtx,\n browser: NightwatchBrowser\n): void {\n if (!ctx.isScriptInjected) {\n ctx.browserProxy.wrapUrlMethod(browser)\n ctx.isScriptInjected = true\n }\n ctx.browserProxy.resetCommandTracking()\n ctx.browserProxy.wrapBrowserCommands(browser)\n}\n\nfunction closeUnreportedRunningTest(\n ctx: TestLifecycleCtx,\n currentSuite: SuiteStats,\n testFile: string,\n results: NightwatchTestResults,\n processedTests: Set<string>\n): void {\n const runningTest = currentSuite.tests.find(\n (t): t is TestStats =>\n typeof t !== 'string' && t.state === TEST_STATE.RUNNING\n )\n if (!runningTest || processedTests.has(runningTest.title)) {\n return\n }\n const failed = (results.errors ?? 0) > 0 || (results.failed ?? 0) > 0\n const testState: TestStats['state'] = failed\n ? TEST_STATE.FAILED\n : TEST_STATE.PASSED\n const endTime = new Date()\n const duration = endTime.getTime() - (runningTest.start?.getTime() || 0)\n ctx.testManager.updateTestState(runningTest, testState, endTime, duration)\n ctx.testManager.markTestAsProcessed(testFile, runningTest.title)\n ctx.incrementCount(testState)\n const icon = ctx.testIcon(testState)\n log.info(` ${icon} ${runningTest.title} (${(duration / 1000).toFixed(2)}s)`)\n}\n\nasync function closeReportedTestcases(\n ctx: TestLifecycleCtx,\n currentSuite: SuiteStats,\n testFile: string,\n testcases: Record<string, NightwatchTestCase>,\n processedTests: Set<string>\n): Promise<void> {\n const testcaseNames = Object.keys(testcases)\n const unprocessedTests = testcaseNames.filter(\n (name) => !processedTests.has(name)\n )\n for (const currentTestName of unprocessedTests) {\n const testcase = testcases[currentTestName]\n const testState = determineTestState(testcase)\n const test = ctx.testManager.findTestInSuite(currentSuite, currentTestName)\n if (test) {\n const dur = parseFloat(testcase.time || '0') * 1000\n ctx.testManager.updateTestState(test, testState, new Date(), dur)\n ctx.incrementCount(testState)\n const icon = ctx.testIcon(testState)\n log.info(` ${icon} ${currentTestName} (${(dur / 1000).toFixed(2)}s)`)\n }\n ctx.testManager.markTestAsProcessed(testFile, currentTestName)\n }\n if (processedTests.size === testcaseNames.length) {\n ctx.suiteManager.finalizeSuite(currentSuite)\n await new Promise((resolve) =>\n setTimeout(resolve, TIMING.SUITE_COMPLETE_DELAY)\n )\n }\n}\n\nexport async function closeOutTestcases(\n ctx: TestLifecycleCtx,\n browser: NightwatchBrowser\n): Promise<void> {\n const currentTest = (browser.currentTest ?? {}) as NightwatchCurrentTest\n const results: NightwatchTestResults = currentTest.results ?? {}\n const moduleName = currentTest.module ?? ''\n const testFile = moduleName.split('/').pop() || DEFAULTS.FILE_NAME\n const testcases = results.testcases ?? {}\n const currentSuite = ctx.suiteManager.getSuite(testFile)\n if (!currentSuite) {\n return\n }\n const processedTests = ctx.testManager.getProcessedTests(testFile)\n if (Object.keys(testcases).length === 0) {\n closeUnreportedRunningTest(\n ctx,\n currentSuite,\n testFile,\n results,\n processedTests\n )\n } else {\n await closeReportedTestcases(\n ctx,\n currentSuite,\n testFile,\n testcases,\n processedTests\n )\n }\n}\n","import fs from 'fs'\nimport path from 'node:path'\nimport { findTestFileFromStack } from './utils.js'\n\n/**\n * Resolve a Nightwatch test's `currentTest.module` to an absolute spec-file\n * path on disk. Priority:\n * 1. Walk the runtime stack for a user frame.\n * 2. A cached path from a previous command on the same browser (browserProxy).\n * 3. Cartesian search across the user's `src_folders` + cwd fallbacks.\n *\n * Used by `beforeEach` to find the file that `extractTestMetadata` should\n * parse for test names + suite/test line numbers. Returns `null` when the\n * file can't be located on disk (source view falls back to \"unavailable\").\n */\nexport function resolveSpecFilePath(\n testFile: string,\n modulePath: string | undefined,\n srcFolders: string[],\n cachedPath: string | undefined\n): string | null {\n let fullPath: string | null = findTestFileFromStack() || null\n if (!fullPath && cachedPath && cachedPath.includes(testFile)) {\n fullPath = cachedPath\n }\n if (fullPath) {\n return fullPath\n }\n if (!testFile) {\n return null\n }\n\n const workspaceRoot = process.cwd()\n // `currentTest.module` is relative to a src_folder, e.g. `basic/ecosia`.\n // We try each src_folder + cwd-level fallback. Use `path.resolve` (not\n // `path.join`) so absolute src_folders entries — like\n // `path.resolve(__dirname, 'tests')` from a nightwatch.conf.cjs living\n // outside the package — bypass `workspaceRoot` correctly.\n const normalized = (modulePath || '').replace(/\\\\/g, '/')\n const srcFolderPaths = srcFolders.flatMap((sf) =>\n normalized\n ? [\n path.resolve(workspaceRoot, sf, normalized + '.js'),\n path.resolve(workspaceRoot, sf, normalized + '.ts'),\n path.resolve(workspaceRoot, sf, normalized + '.cjs'),\n path.resolve(workspaceRoot, sf, normalized)\n ]\n : []\n )\n const possiblePaths = [\n ...srcFolderPaths,\n // Treat module path as relative to cwd (works when src_folders isn't nested)\n ...(normalized\n ? [\n path.resolve(workspaceRoot, normalized + '.js'),\n path.resolve(workspaceRoot, normalized + '.ts'),\n path.resolve(workspaceRoot, normalized + '.cjs'),\n path.resolve(workspaceRoot, normalized)\n ]\n : []),\n path.resolve(workspaceRoot, 'tests', testFile + '.js'),\n path.resolve(workspaceRoot, 'test', testFile + '.js'),\n path.resolve(workspaceRoot, testFile + '.js')\n ]\n\n for (const candidate of possiblePaths) {\n if (fs.existsSync(candidate)) {\n return candidate\n }\n }\n return null\n}\n","import logger from '@wdio/logger'\nimport { TIMING, TEST_STATE } from '../constants.js'\nimport type { NightwatchTestCase, TestStats } from '../types.js'\nimport { determineTestState } from './utils.js'\nimport type { TestManager } from './testManager.js'\n\nconst log = logger('@wdio/nightwatch-devtools:closePreviousTest')\n\nexport interface ClosePreviousTestInput {\n runningTest: TestStats\n testFile: string\n /** `currentTest?.results?.testcases || {}` — Nightwatch's per-suite testcase results. */\n testcases: Record<string, NightwatchTestCase>\n testManager: TestManager\n /** Plugin-side pass/fail/skip counter — called once with the resolved final state. */\n incrementCount: (state: TestStats['state']) => void\n /** Plugin-side icon resolver for the log line (✅/❌/⏭ etc.). */\n testIcon: (state: TestStats['state']) => string\n}\n\n/**\n * Close out the previously-running test when beforeEach fires for the next\n * one. Resolves the final state from Nightwatch's testcases bag if available\n * (preferred — has real timing), otherwise assumes PASSED (the runner only\n * advances to beforeEach if the prior test didn't throw). Returns nothing —\n * pure side-effect orchestration over the test, testManager, and the plugin's\n * counter / icon helpers.\n *\n * Extracted from `NightwatchDevToolsPlugin.beforeEach` — the ~37-line block\n * was the second-densest section of the 142-line method.\n */\nexport async function closePreviousTest(\n input: ClosePreviousTestInput\n): Promise<void> {\n const {\n runningTest,\n testFile,\n testcases,\n testManager,\n incrementCount,\n testIcon\n } = input\n\n if (testcases[runningTest.title]) {\n const testcase = testcases[runningTest.title]\n const testState = determineTestState(testcase)\n runningTest.state = testState\n runningTest.end = new Date()\n runningTest._duration = parseFloat(testcase.time || '0') * 1000\n testManager.updateTestState(runningTest, testState)\n testManager.markTestAsProcessed(testFile, runningTest.title)\n incrementCount(testState)\n log.info(\n ` ${testIcon(testState)} ${runningTest.title} (${(runningTest._duration / 1000).toFixed(2)}s)`\n )\n } else {\n const endTime = new Date()\n const duration = endTime.getTime() - (runningTest.start?.getTime() || 0)\n testManager.updateTestState(\n runningTest,\n TEST_STATE.PASSED as TestStats['state'],\n endTime,\n duration\n )\n testManager.markTestAsProcessed(testFile, runningTest.title)\n incrementCount(TEST_STATE.PASSED as TestStats['state'])\n log.info(` ✅ ${runningTest.title} (${(duration / 1000).toFixed(2)}s)`)\n }\n // Brief settle so the dashboard renders the terminal state before the next\n // test's \"running\" update arrives.\n await new Promise((resolve) => setTimeout(resolve, TIMING.UI_RENDER_DELAY))\n}\n","/**\n * Session-initialization lifecycle helpers for the Nightwatch plugin.\n *\n * Extracted from the plugin class to keep `index.ts` under the file-size\n * cap. The plugin passes itself as a `SessionInitCtx` — a narrow interface\n * exposing only the fields and methods these helpers need.\n *\n * Includes:\n * - Per-session bringup (capturer + reporter chain + metadata + BiDi + screencast)\n * - Session-change cleanup\n * - Screencast finalize-and-clear\n */\n\nimport logger from '@wdio/logger'\nimport {\n finalizeScreencast,\n resolveAdapterOutputDir\n} from '@wdio/devtools-core'\nimport { TraceType } from './types.js'\nimport { TIMING } from './constants.js'\nimport { SessionCapturer } from './session.js'\nimport { TestReporter } from './reporter.js'\nimport { TestManager } from './helpers/testManager.js'\nimport { SuiteManager } from './helpers/suiteManager.js'\nimport { BrowserProxy } from './helpers/browserProxy.js'\nimport { ScreencastRecorder } from './screencast.js'\nimport type {\n DevToolsMode,\n NightwatchBrowser,\n ScreencastOptions,\n SuiteStats\n} from './types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:session-init')\n\nexport interface SessionInitCtx {\n readonly hostname: string\n readonly port: number\n readonly screencastOptions: ScreencastOptions\n readonly bidiEnabled: boolean\n readonly mode: DevToolsMode\n\n sessionCapturer: SessionCapturer\n testReporter: TestReporter\n testManager: TestManager\n suiteManager: SuiteManager\n browserProxy: BrowserProxy\n isScriptInjected: boolean\n\n lastSessionId: string | null\n bidiAttachAttempted: boolean\n srcFolders: string[]\n screencastRecorder: ScreencastRecorder | undefined\n screencastSessionId: string | undefined\n configPath: string | undefined\n\n getCurrentTest(): unknown\n getCurrentScenarioSuite(): SuiteStats | null\n buildMetadataOptions(): unknown\n}\n\nasync function handleSessionChange(\n ctx: SessionInitCtx,\n finalizeCurrent: () => Promise<void>\n): Promise<void> {\n log.info('Browser session changed — reconnecting WebSocket only')\n ctx.isScriptInjected = false\n // Reset BiDi-attach state so the new session gets its own attach —\n // inspectors are bound to a specific driver instance and don't carry\n // across sessions. Without this, only the first session captures via\n // BiDi and the rest silently fall back to the perf-log path.\n ctx.bidiAttachAttempted = false\n // Finalize the previous session's screencast BEFORE we tear down its\n // capturer — encode + broadcast use the existing WS connection.\n await finalizeCurrent()\n ctx.sessionCapturer?.cleanup()\n // Intentional null-out — the next call to ensureSessionInitialized\n // reassigns. Cast through unknown so the strict field type passes.\n ctx.sessionCapturer = null as unknown as SessionCapturer\n}\n\nfunction initReporterChain(ctx: SessionInitCtx): void {\n // First-time setup: create reporter chain once for the entire run.\n // These must NOT be recreated on session change — doing so generates a\n // new feature suite with a fresh start timestamp, which DataManager sees\n // as a new run and wipes all accumulated commands.\n ctx.testReporter = new TestReporter((suitesData) => {\n if (ctx.sessionCapturer) {\n ctx.sessionCapturer.sendUpstream('suites', suitesData)\n }\n })\n ctx.testManager = new TestManager(ctx.testReporter)\n ctx.suiteManager = new SuiteManager(ctx.testReporter)\n ctx.browserProxy = new BrowserProxy(\n ctx.sessionCapturer,\n ctx.testManager,\n () => ctx.getCurrentTest() ?? ctx.getCurrentScenarioSuite()\n )\n}\n\nfunction rebindReporterToNewSession(ctx: SessionInitCtx): void {\n // Session change: update the reporter's upstream callback to use the new\n // WebSocket, update the proxy's capturer reference (avoids re-wrapping\n // already-wrapped browser methods which would double-capture commands),\n // then replay current suite state to the newly-connected UI.\n ctx.testReporter.updateUpstream((suitesData) => {\n if (ctx.sessionCapturer) {\n ctx.sessionCapturer.sendUpstream('suites', suitesData)\n }\n })\n ctx.browserProxy.updateSessionCapturer(ctx.sessionCapturer)\n ctx.testReporter.updateSuites()\n}\n\nfunction broadcastSessionMetadata(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): void {\n const capabilities = browser.capabilities || {}\n const desiredCapabilities = browser.desiredCapabilities || {}\n const sessionId = browser.sessionId\n const opts = browser.options || {}\n\n if (ctx.srcFolders.length === 0) {\n const sf = (opts as { src_folders?: string | string[] }).src_folders\n ctx.srcFolders = Array.isArray(sf) ? sf : sf ? [sf] : []\n }\n\n const metadata = {\n type: TraceType.Testrunner,\n capabilities,\n desiredCapabilities,\n sessionId,\n testEnv: opts.testEnv,\n host: opts.webdriver?.host,\n options: ctx.buildMetadataOptions(),\n url: ''\n }\n ctx.sessionCapturer.metadata = metadata\n ctx.sessionCapturer.sendUpstream('metadata', metadata)\n\n const browserName =\n capabilities.browserName || desiredCapabilities.browserName || 'unknown'\n const browserVersion =\n capabilities.browserVersion ||\n (capabilities as { version?: string }).version ||\n ''\n log.info(\n `✓ Browser: ${browserName}${browserVersion ? ' ' + browserVersion : ''} (session: ${sessionId})`\n )\n\n const loggingPrefs = ((capabilities as Record<string, unknown>)[\n 'goog:loggingPrefs'\n ] ||\n (desiredCapabilities as Record<string, unknown>)['goog:loggingPrefs'] ||\n {}) as { performance?: string }\n if (!loggingPrefs.performance && !ctx.bidiEnabled) {\n log.warn(\n \"⚠ Network tab will be empty — add 'goog:loggingPrefs': { performance: 'ALL' } to your capabilities (or enable bidi:true)\"\n )\n }\n}\n\n// BiDi: opt-in. Requires `webSocketUrl: true` capability + a BiDi-capable\n// chromedriver. We attempt once per session; on failure or unavailability\n// the perf-log fallback path continues to work.\nasync function tryAttachBidi(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): Promise<void> {\n if (!ctx.bidiEnabled || ctx.bidiAttachAttempted) {\n return\n }\n ctx.bidiAttachAttempted = true\n const driver = (browser as { driver?: unknown }).driver\n if (!driver) {\n log.warn('bidi:true set but browser.driver unavailable — skipping')\n return\n }\n const { attachBidiHandlers, buildBidiSinks } = await import('./bidi.js')\n const ok = await attachBidiHandlers(\n driver,\n buildBidiSinks(ctx.sessionCapturer)\n )\n if (ok) {\n ctx.sessionCapturer.bidiActive = true\n log.info('✓ BiDi attached — perf-log network capture disabled')\n }\n}\n\n// Screencast: start a fresh recorder per browser session — every\n// reloadSession / per-test browser produces its own .webm, matching\n// the WDIO service behavior. Polling mode only (Nightwatch has no\n// stable CDP escape hatch). Finalized when the next session change\n// fires or when after() runs.\nasync function tryStartScreencast(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser,\n sessionId: string | undefined\n): Promise<void> {\n if (!ctx.screencastOptions.enabled || ctx.screencastRecorder || !sessionId) {\n return\n }\n ctx.screencastRecorder = new ScreencastRecorder(\n ctx.sessionCapturer,\n ctx.screencastOptions\n )\n ctx.screencastSessionId = sessionId\n log.info(`🎬 Starting screencast for session ${sessionId}`)\n await ctx.screencastRecorder.start(browser)\n}\n\nexport async function ensureSessionInitialized(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser,\n finalizeCurrentScreencast: () => Promise<void>\n): Promise<void> {\n const currentSessionId = browser.sessionId\n const isSessionChange =\n currentSessionId &&\n ctx.lastSessionId &&\n currentSessionId !== ctx.lastSessionId\n if (isSessionChange) {\n await handleSessionChange(ctx, finalizeCurrentScreencast)\n }\n ctx.lastSessionId = currentSessionId ?? null\n if (ctx.sessionCapturer) {\n return\n }\n await new Promise((resolve) =>\n setTimeout(resolve, TIMING.INITIAL_CONNECTION_WAIT)\n )\n // Trace mode: empty opts skip SessionCapturerBase's WS init — no backend\n // to forward events to anyway.\n ctx.sessionCapturer = new SessionCapturer(\n ctx.mode === 'trace'\n ? {}\n : {\n port: ctx.port,\n hostname: ctx.hostname,\n // A session change reopens the WS mid-run — tell the backend to keep\n // the accumulated run state so earlier tests' commands survive.\n reconnect: Boolean(isSessionChange)\n },\n browser\n )\n ctx.sessionCapturer.traceMode = ctx.mode\n if (ctx.mode !== 'trace') {\n const connected = await ctx.sessionCapturer.waitForConnection(3000)\n if (!connected) {\n log.error('❌ Worker WebSocket failed to connect!')\n }\n }\n if (!ctx.testReporter) {\n initReporterChain(ctx)\n } else {\n rebindReporterToNewSession(ctx)\n }\n broadcastSessionMetadata(ctx, browser)\n await tryAttachBidi(ctx, browser)\n await tryStartScreencast(ctx, browser, browser.sessionId)\n}\n\nexport async function finalizeCurrentScreencast(\n ctx: SessionInitCtx\n): Promise<void> {\n if (!ctx.screencastRecorder || !ctx.screencastSessionId) {\n return\n }\n await finalizeScreencast({\n recorder: ctx.screencastRecorder,\n sessionId: ctx.screencastSessionId,\n filenamePrefix: 'nightwatch-video',\n outputDir: resolveAdapterOutputDir({\n testFilePath: ctx.browserProxy?.getCurrentTestFullPath?.() ?? undefined,\n configPath: ctx.configPath\n }),\n captureFormat: ctx.screencastOptions.captureFormat,\n sendUpstream: (scope, data) =>\n ctx.sessionCapturer?.sendUpstream(scope, data),\n onLog: (level, message) => log[level](message)\n })\n ctx.screencastRecorder = undefined\n ctx.screencastSessionId = undefined\n}\n","import http from 'node:http'\nimport logger from '@wdio/logger'\nimport {\n SessionCapturerBase,\n errorMessage,\n loadInjectableScript,\n mapChromeBrowserLogs,\n pollUntilReady,\n serializeError\n} from '@wdio/devtools-core'\nimport { mapCommandToAction } from '@wdio/devtools-core'\nimport { captureActionSnapshot } from './action-snapshot.js'\nimport { NAVIGATION_COMMANDS } from './constants.js'\nimport {\n parseNetworkFromPerfLogs,\n dedupeNetworkRequests,\n type NetworkEntry,\n type PerfLogEntry\n} from './helpers/perfLogs.js'\nimport {\n CAPTURE_PERFORMANCE_SCRIPT,\n type CapturedPerformancePayload,\n applyPerformanceData\n} from '@wdio/devtools-core'\nimport type {\n ActionSnapshot,\n CommandLog,\n DevToolsMode,\n NightwatchBrowser\n} from './types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:SessionCapturer')\n\n/**\n * WebDriver responses are sometimes wrapped as `{ value: T }` (the W3C\n * protocol shape) and sometimes flat. This helper unwraps the value field\n * if present, otherwise returns the input as-is.\n */\nfunction unwrapDriverValue<T = unknown>(result: unknown): T {\n if (result && typeof result === 'object' && 'value' in result) {\n return (result as { value: T }).value\n }\n return result as T\n}\n\ntype LooseRec = Record<string, unknown>\nconst getProp = (obj: unknown, key: string): unknown =>\n obj && typeof obj === 'object' ? (obj as LooseRec)[key] : undefined\nconst getPath = (obj: unknown, ...path: string[]): unknown =>\n path.reduce<unknown>((acc, k) => getProp(acc, k), obj)\nconst firstDefined = (obj: unknown, ...keys: string[]): unknown => {\n if (!obj || typeof obj !== 'object') {\n return undefined\n }\n const rec = obj as LooseRec\n for (const k of keys) {\n const v = rec[k]\n if (v !== undefined && v !== null) {\n return v\n }\n }\n return undefined\n}\n\n/**\n * Walks Nightwatch's internal config (transport / queue.transport /\n * nightwatchInstance — none of which are on the public NightwatchBrowser\n * type) to find the underlying WebDriver host+port for direct screenshot\n * fetches that bypass the command queue.\n */\nfunction resolveWebDriverAddress(browser: NightwatchBrowser): {\n driverHost: string\n driverPort: number\n} {\n const transportSettings =\n getPath(browser, 'transport', 'settings', 'webdriver') ||\n getPath(browser, 'queue', 'transport', 'settings', 'webdriver') ||\n getPath(\n browser,\n 'nightwatchInstance',\n 'transport',\n 'settings',\n 'webdriver'\n ) ||\n {}\n const opts = getProp(browser, 'options') ?? {}\n const nightwatchSettings =\n getPath(browser, 'nightwatchInstance', 'settings') ||\n getPath(browser, 'globals', 'nightwatchInstance', 'settings') ||\n {}\n const driverHost = String(\n firstDefined(transportSettings, 'host', 'server_address') ||\n firstDefined(getProp(opts, 'webdriver'), 'host') ||\n firstDefined(getProp(nightwatchSettings, 'webdriver'), 'host') ||\n 'localhost'\n )\n const driverPort = Number(\n firstDefined(transportSettings, 'port') ||\n firstDefined(getProp(opts, 'webdriver'), 'port') ||\n firstDefined(getProp(nightwatchSettings, 'webdriver'), 'port') ||\n 9515\n )\n return { driverHost, driverPort }\n}\n\nexport class SessionCapturer extends SessionCapturerBase {\n #browser: NightwatchBrowser | undefined\n\n // True once BiDi inspectors are attached — the per-command perf-log network\n // capture path skips when set, so we don't double-emit network requests.\n bidiActive = false\n\n // Populated by captureCommand when mode === 'trace' (set by the plugin).\n traceMode: DevToolsMode = 'live'\n readonly actionSnapshots: ActionSnapshot[] = []\n readonly snapshotCaptures: Promise<void>[] = []\n\n constructor(\n devtoolsOptions: {\n hostname?: string\n port?: number\n reconnect?: boolean\n } = {},\n browser?: NightwatchBrowser\n ) {\n super(devtoolsOptions)\n this.#browser = browser\n this.patchConsole()\n this.patchStreams()\n }\n\n protected override onWsOpen(): void {\n log.info('✓ Worker WebSocket connected to backend')\n }\n\n protected override onWsError(err: unknown): void {\n log.error(`Couldn't connect to devtools backend: ${errorMessage(err)}`)\n }\n\n protected override onWsClose(): void {\n log.info('Worker WebSocket disconnected')\n }\n\n async captureCommand(\n command: string,\n args: unknown[],\n result: unknown,\n error: Error | undefined,\n testUid?: string,\n callSource?: string,\n timestamp?: number\n ): Promise<boolean> {\n // Serialize error properly (Error objects don't JSON.stringify well)\n const serializedError = serializeError(error)\n\n const commandId = this.commandCounter++\n const commandLogEntry: CommandLog & { _id?: number } = {\n _id: commandId,\n command,\n args,\n result,\n error: serializedError,\n timestamp: timestamp || Date.now(),\n callSource,\n testUid\n }\n\n this.commandsLog.push(commandLogEntry)\n\n const isNavigationCommand = NAVIGATION_COMMANDS.some((cmd) =>\n command.toLowerCase().includes(cmd.toLowerCase())\n )\n if (isNavigationCommand && this.#browser && !error) {\n this.#capturePerformanceData(commandLogEntry, args).catch((err) => {\n log.warn(`Failed to capture performance data: ${errorMessage(err)}`)\n })\n }\n\n if (\n this.traceMode === 'trace' &&\n !error &&\n this.#browser &&\n mapCommandToAction(command)\n ) {\n const browser = this.#browser\n this.snapshotCaptures.push(\n captureActionSnapshot(browser, command, () =>\n this.takeScreenshotViaHttp(browser)\n ).then((snap) => {\n if (snap) {\n this.actionSnapshots.push(snap)\n }\n })\n )\n }\n\n return true\n }\n\n async #capturePerformanceData(\n commandLogEntry: CommandLog & { _id?: number },\n args: unknown[]\n ) {\n await new Promise((resolve) => setTimeout(resolve, 500))\n const raw = await this.#browser!.execute(CAPTURE_PERFORMANCE_SCRIPT)\n const payload = unwrapDriverValue<CapturedPerformancePayload | undefined>(\n raw\n )\n applyPerformanceData(\n commandLogEntry,\n payload,\n typeof args[0] === 'string' ? args[0] : undefined\n )\n }\n\n /**\n * Replace an already-captured command entry (used for retried commands so\n * only the final execution result is shown in the UI).\n * Removes the old entry from commandsLog, revokes its sent-status so the\n * replacement can be sent, and returns the new entry together with the\n * old entry's timestamp (so the UI can locate and replace it in-place).\n */\n replaceCommand(\n oldId: number,\n command: string,\n args: unknown[],\n result: unknown,\n error: Error | undefined,\n testUid?: string,\n callSource?: string,\n timestamp?: number\n ): { entry: CommandLog & { _id?: number }; oldTimestamp: number } {\n // Remove the superseded entry and capture its timestamp for the UI\n const idx = this.commandsLog.findIndex(\n (c) => (c as CommandLog & { _id?: number })._id === oldId\n )\n const oldTimestamp: number =\n idx !== -1 ? (this.commandsLog[idx]?.timestamp ?? 0) : 0\n if (idx !== -1) {\n this.commandsLog.splice(idx, 1)\n }\n // Allow the slot to be re-used by a new entry\n this.sentCommandIds.delete(oldId)\n\n const serializedError = serializeError(error)\n const commandId = this.commandCounter++\n const entry: CommandLog & { _id?: number } = {\n _id: commandId,\n command,\n args,\n result,\n error: serializedError,\n timestamp: timestamp || Date.now(),\n callSource,\n testUid\n }\n this.commandsLog.push(entry)\n return { entry, oldTimestamp }\n }\n\n /**\n * Take a screenshot by calling the WebDriver HTTP endpoint directly.\n * This completely bypasses Nightwatch's command queue so there is no risk\n * of the request being appended after `end()` / `quit()`.\n */\n // Nightwatch's internal config lives at non-public paths (transport,\n // queue.transport, nightwatchInstance.settings, globals.nightwatchInstance);\n // none are in the NightwatchBrowser type. Cast for dynamic access.\n #resolveDriverEndpoint(\n browser: NightwatchBrowser,\n sessionId: string\n ): string {\n const { driverHost, driverPort } = resolveWebDriverAddress(browser)\n return `http://${driverHost}:${driverPort}/session/${sessionId}/screenshot`\n }\n\n takeScreenshotViaHttp(browser: NightwatchBrowser): Promise<string | null> {\n const sessionId = (browser as unknown as { sessionId?: string }).sessionId\n if (!sessionId) {\n return Promise.resolve(null)\n }\n const endpoint = this.#resolveDriverEndpoint(browser, sessionId)\n return new Promise((resolve) => {\n const req = http.get(endpoint, (res) => {\n let body = ''\n res.on('data', (chunk: string | Buffer) => {\n body += chunk\n })\n res.on('end', () => {\n try {\n const value = JSON.parse(body).value || null\n if (!value) {\n log.warn(`[screenshot] Empty response from ${endpoint}`)\n }\n resolve(value)\n } catch {\n log.warn(`[screenshot] Failed to parse response from ${endpoint}`)\n resolve(null)\n }\n })\n })\n req.on('error', (err) => {\n log.warn(\n `[screenshot] HTTP request failed (${endpoint}): ${errorMessage(err)}`\n )\n resolve(null)\n })\n req.setTimeout(5000, () => {\n log.warn(`[screenshot] Request timed out (${endpoint})`)\n req.destroy()\n resolve(null)\n })\n })\n }\n\n protected override onSourceReadError(filePath: string, err: unknown): void {\n log.warn(`Failed to read source file ${filePath}: ${errorMessage(err)}`)\n }\n\n protected override onUpstreamDrop(\n event: string,\n reason: 'closed' | 'send-error',\n err?: unknown\n ): void {\n if (reason === 'send-error') {\n log.warn(`[upstream] Failed to send \"${event}\": ${errorMessage(err)}`)\n return\n }\n if (this.hasEverConnected()) {\n log.warn(`[upstream] WebSocket not open — dropping \"${event}\" event`)\n }\n }\n\n /**\n * Inject the WDIO devtools script into the browser page\n */\n async injectScript(browser: NightwatchBrowser) {\n try {\n const scriptContent = await loadInjectableScript()\n const injectionScript = `\n const script = document.createElement('script');\n script.textContent = arguments[0];\n document.head.appendChild(script);\n return true;\n `\n await browser.execute(injectionScript, [scriptContent])\n\n const hasCollector = await pollUntilReady(async () => {\n const checkResult = await browser.execute(\n 'return typeof window.wdioTraceCollector !== \"undefined\"'\n )\n return unwrapDriverValue<unknown>(checkResult) === true\n })\n\n if (hasCollector) {\n log.info('✓ Script injected and collector ready')\n } else {\n log.warn('Script injection may have failed — collector not found')\n }\n } catch (err) {\n log.error(`Failed to inject script: ${errorMessage(err)}`)\n throw err\n }\n }\n\n /**\n * Capture Chrome DevTools browser console logs via WebDriver log API.\n * Requires loggingPrefs: { browser: 'ALL' } in Chrome capabilities.\n */\n async captureBrowserLogs(browser: NightwatchBrowser) {\n try {\n const rawLogs = await (\n browser as unknown as Record<string, (type: string) => Promise<unknown>>\n ).getLog('browser')\n const logs =\n unwrapDriverValue<\n Array<{ level: string; message: string; timestamp: number }>\n >(rawLogs)\n\n if (!Array.isArray(logs) || logs.length === 0) {\n return\n }\n\n const entries = mapChromeBrowserLogs(logs)\n this.consoleLogs.push(...entries)\n this.sendUpstream('consoleLogs', entries)\n } catch {\n // Browser log capture not available (loggingPrefs not set or not supported)\n }\n }\n\n /**\n * Parse Chrome performance logs to extract network request entries.\n * Requires loggingPrefs: { performance: 'ALL' } in Chrome capabilities.\n */\n async captureNetworkFromPerformanceLogs(browser: NightwatchBrowser) {\n // BiDi network inspector is the source of truth when attached.\n if (this.bidiActive) {\n return\n }\n try {\n const rawLogs = await (\n browser as unknown as Record<string, (type: string) => Promise<unknown>>\n ).getLog('performance')\n const logs = unwrapDriverValue<PerfLogEntry[]>(rawLogs)\n\n if (!Array.isArray(logs) || logs.length === 0) {\n return\n }\n\n const networkEntries = parseNetworkFromPerfLogs(logs)\n if (networkEntries.length === 0) {\n return\n }\n\n const deduped = dedupeNetworkRequests(\n networkEntries,\n this.networkRequests as NetworkEntry[]\n )\n if (deduped.length > 0) {\n // NetworkEntry has `type?: string`; the shared NetworkRequest needs\n // `type: string` so default the field at this framework boundary.\n const normalized = deduped.map((d) => ({\n ...d,\n type: d.type ?? 'unknown'\n }))\n this.networkRequests.push(...normalized)\n this.sendUpstream('networkRequests', normalized)\n }\n } catch (err) {\n const msg = errorMessage(err) ?? ''\n // Silently skip when performance logging was not enabled in capabilities\n if (!msg.includes('log type') && !msg.includes('performance')) {\n log.warn(`Performance log capture failed: ${msg}`)\n }\n }\n }\n\n /**\n * Capture trace data from the browser (network requests, console logs, etc.)\n */\n async captureTrace(browser: NightwatchBrowser) {\n // Capture network requests from Chrome performance logs\n await this.captureNetworkFromPerformanceLogs(browser)\n\n // Also try the injected wdioTraceCollector script for XHR/fetch and mutations.\n // Atomic check+read — the inline `typeof === 'undefined' → null` guard is\n // the only safe form; a separate existence check would race page navigation\n // (the collector can disappear between the two round-trips).\n try {\n const result = await browser.execute(`\n if (typeof window.wdioTraceCollector === 'undefined') {\n return null;\n }\n return window.wdioTraceCollector.getTraceData();\n `)\n\n const traceData = unwrapDriverValue<Record<string, unknown> | null>(\n result\n )\n if (!traceData) {\n return\n }\n\n this.processTracePayload(traceData)\n const mutationCount = Array.isArray(\n (traceData as { mutations?: unknown }).mutations\n )\n ? (traceData as { mutations: unknown[] }).mutations.length\n : 0\n if (mutationCount > 0) {\n log.info(`[trace] Captured ${mutationCount} DOM mutation(s)`)\n }\n } catch (err) {\n log.error(\n `Failed to capture trace from injected script: ${errorMessage(err)}`\n )\n }\n }\n}\n","// Nightwatch adapter: wires NightwatchBrowser into core's captureActionSnapshot.\n\nimport { captureActionSnapshot as coreCapture } from '@wdio/devtools-core'\nimport type { ActionSnapshot } from '@wdio/devtools-shared'\nimport type { NightwatchBrowser } from './types.js'\n\ninterface BrowserWithUrl extends NightwatchBrowser {\n getCurrentUrl?: () => Promise<string>\n getTitle?: () => Promise<string>\n}\n\nexport function captureActionSnapshot(\n browser: NightwatchBrowser,\n command: string,\n takeScreenshot?: () => Promise<string | null | undefined>\n): Promise<ActionSnapshot | null> {\n const b = browser as BrowserWithUrl\n return coreCapture({\n command,\n runScript: (src) => browser.execute(`return (${src})`) as Promise<unknown>,\n takeScreenshot,\n getUrl: () =>\n b.getCurrentUrl\n ? b.getCurrentUrl().catch(() => undefined)\n : Promise.resolve(undefined),\n getTitle: () =>\n b.getTitle\n ? b.getTitle().catch(() => undefined)\n : Promise.resolve(undefined)\n })\n}\n","import { getRequestType } from './utils.js'\n\n/**\n * Pure parsers for Chrome's `performance` log (the format `browser.getLog('performance')`\n * returns). Separated from the SessionCapturer so they're testable and the\n * capture method stays focused on state + I/O.\n */\n\nexport interface PerfLogEntry {\n level: string\n message: string\n timestamp: number\n}\n\nexport interface NetworkEntry {\n id: string\n url: string\n method: string\n requestHeaders: Record<string, string>\n timestamp: number\n startTime: number\n status?: number\n statusText?: string\n responseHeaders?: Record<string, string>\n mimeType?: string\n type?: string\n size?: number\n endTime?: number\n time?: number\n error?: string\n}\n\n/**\n * Parse CDP `Network.*` events out of Chrome performance log entries into a\n * flat array of network entries. Builds up a per-requestId pending map as it\n * sees `requestWillBeSent` → `responseReceived` → `loadingFinished` events,\n * and emits the completed entry on the terminal event.\n */\ninterface CdpResponse {\n status?: number\n statusText?: string\n headers?: Record<string, unknown>\n mimeType?: string\n}\n\ninterface CdpRequest {\n url: string\n method: string\n headers?: Record<string, string>\n}\n\ninterface CdpEventParams {\n requestId: string\n request?: CdpRequest\n response?: CdpResponse\n timestamp?: number\n encodedDataLength?: number\n errorText?: string\n}\n\nfunction applyResponseReceived(p: NetworkEntry, response: CdpResponse): void {\n const responseHeaders: Record<string, string> = {}\n for (const [k, v] of Object.entries(response.headers || {})) {\n responseHeaders[k.toLowerCase()] = String(v)\n }\n p.status = response.status\n p.statusText = response.statusText\n p.responseHeaders = responseHeaders\n p.mimeType = response.mimeType\n p.type = getRequestType(p.url, response.mimeType)\n}\n\n/** CDP event time in ms. Uses the event's own monotonic `params.timestamp`\n * (what Chrome DevTools times requests with) rather than the perf-log wrapper\n * timestamp, which WebDriver stamps per batch — so request/response diffs in\n * the same batch would otherwise be 0. */\nfunction eventTimeMs(params: CdpEventParams, entry: PerfLogEntry): number {\n return typeof params.timestamp === 'number'\n ? Math.round(params.timestamp * 1000)\n : entry.timestamp\n}\n\nfunction startPendingRequest(\n params: CdpEventParams,\n entry: PerfLogEntry,\n pending: Map<string, NetworkEntry>\n): void {\n const { requestId, request: req, timestamp } = params\n if (!req || typeof timestamp !== 'number') {\n return\n }\n const startTime = eventTimeMs(params, entry)\n pending.set(requestId, {\n id: `${entry.timestamp}-${requestId}`,\n url: req.url,\n method: req.method,\n requestHeaders: req.headers ?? {},\n timestamp: startTime,\n startTime\n })\n}\n\nfunction handlePerfLogEvent(\n method: string,\n params: CdpEventParams,\n entry: PerfLogEntry,\n pending: Map<string, NetworkEntry>,\n completed: NetworkEntry[]\n): void {\n if (method === 'Network.requestWillBeSent') {\n startPendingRequest(params, entry, pending)\n return\n }\n if (method === 'Network.responseReceived') {\n const p = pending.get(params.requestId)\n if (p && params.response) {\n applyResponseReceived(p, params.response)\n }\n return\n }\n if (method === 'Network.loadingFinished') {\n const p = pending.get(params.requestId)\n if (p && p.status !== undefined) {\n p.size = params.encodedDataLength\n p.endTime = eventTimeMs(params, entry)\n p.time = Math.max(0, p.endTime - p.startTime)\n completed.push({ ...p })\n pending.delete(params.requestId)\n }\n return\n }\n if (method === 'Network.loadingFailed') {\n const p = pending.get(params.requestId)\n if (p) {\n p.error = params.errorText\n p.endTime = eventTimeMs(params, entry)\n p.time = Math.max(0, p.endTime - p.startTime)\n completed.push({ ...p })\n pending.delete(params.requestId)\n }\n }\n}\n\nexport function parseNetworkFromPerfLogs(logs: PerfLogEntry[]): NetworkEntry[] {\n const pending = new Map<string, NetworkEntry>()\n const completed: NetworkEntry[] = []\n for (const entry of logs) {\n let parsed: unknown\n try {\n parsed = JSON.parse(entry.message)\n } catch {\n continue\n }\n type ParsedShape = {\n message?: { method?: string; params?: CdpEventParams }\n }\n const message = (parsed as ParsedShape | undefined)?.message\n const method = message?.method\n const params = message?.params\n if (!method || !params) {\n continue\n }\n handlePerfLogEvent(method, params, entry, pending, completed)\n }\n return completed\n}\n\n/**\n * Dedupe incoming network entries against ones the session already holds.\n * Successful requests dedupe by (method, url, timestamp). Failed requests\n * collapse by (method, origin, pathname) — parallel autocomplete/prefetch\n * requests to the same path (e.g. `/search?q=W`, `/search?q=We`) otherwise\n * spam the network panel.\n */\nexport function dedupeNetworkRequests(\n incoming: NetworkEntry[],\n existing: NetworkEntry[]\n): NetworkEntry[] {\n const failedKey = (entry: NetworkEntry): string => {\n try {\n const u = new URL(entry.url)\n return `err:${entry.method}:${u.origin}${u.pathname}`\n } catch {\n return `err:${entry.method}:${entry.url}`\n }\n }\n\n const alreadySeen = new Set(\n existing.map((r) =>\n r.error !== undefined\n ? failedKey(r)\n : `ok:${r.method}:${r.url}:${r.timestamp}`\n )\n )\n\n const deduped: NetworkEntry[] = []\n const seenFailedInBatch = new Map<string, number>()\n\n for (const entry of incoming) {\n if (entry.error !== undefined) {\n const key = failedKey(entry)\n if (alreadySeen.has(key)) {\n continue\n }\n const existingIdx = seenFailedInBatch.get(key)\n if (existingIdx !== undefined) {\n deduped[existingIdx] = entry // replace with latest failure\n } else {\n seenFailedInBatch.set(key, deduped.length)\n deduped.push(entry)\n }\n } else {\n const key = `ok:${entry.method}:${entry.url}:${entry.timestamp}`\n if (!alreadySeen.has(key)) {\n deduped.push(entry)\n }\n }\n }\n\n return deduped\n}\n","import logger from '@wdio/logger'\nimport { TestReporterBase } from '@wdio/devtools-core'\nimport { REUSE_ENV } from '@wdio/devtools-shared'\nimport { DEFAULTS } from './constants.js'\nimport { extractTestMetadata, generateStableUid } from './helpers/utils.js'\nimport type { SuiteStats, TestStats } from './types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:Reporter')\n\nexport class TestReporter extends TestReporterBase {\n #currentSpecFile?: string\n #testNamesCache = new Map<string, string[]>()\n #currentSuite?: SuiteStats\n\n onSuiteStart(suiteStats: SuiteStats) {\n this.#currentSpecFile = suiteStats.file\n this.#currentSuite = suiteStats\n const rerunLabel =\n process.env[REUSE_ENV.RERUN_ENTRY_TYPE] === 'test'\n ? process.env[REUSE_ENV.RERUN_LABEL]?.trim()\n : undefined\n\n if (!suiteStats.uid) {\n suiteStats.uid = generateStableUid(suiteStats)\n }\n\n if (\n this.#currentSpecFile &&\n !this.#testNamesCache.has(this.#currentSpecFile)\n ) {\n const metadata = extractTestMetadata(this.#currentSpecFile)\n const testNames = rerunLabel\n ? metadata.testNames.filter((name) => name === rerunLabel)\n : metadata.testNames\n if (testNames.length > 0) {\n this.#testNamesCache.set(this.#currentSpecFile, testNames)\n log.info(\n `📝 Extracted ${testNames.length} test names from ${this.#currentSpecFile}`\n )\n }\n }\n\n this.allSuites.push(suiteStats)\n this.sendUpstream()\n }\n\n override clearExecutionData() {\n super.clearExecutionData()\n this.#testNamesCache.clear()\n this.#currentSuite = undefined\n this.#currentSpecFile = undefined\n }\n\n getCurrentSuite(): SuiteStats | undefined {\n return this.#currentSuite\n }\n\n /** Find by title within parent suite — Nightwatch retries reuse the title slot. */\n onTestStart(testStats: TestStats) {\n if (!testStats.uid || testStats.uid.includes('temp-')) {\n testStats.uid = generateStableUid(testStats)\n }\n\n for (const suite of this.allSuites) {\n const testIndex = suite.tests.findIndex((t) => {\n if (typeof t === 'string') {\n return false\n }\n return t.title === testStats.title && t.parent === suite.uid\n })\n if (testIndex !== -1) {\n suite.tests[testIndex] = testStats\n this.sendUpstream()\n return\n }\n }\n\n if (this.#currentSuite) {\n this.#currentSuite.tests.push(testStats)\n }\n this.sendUpstream()\n }\n\n /** Synthesize `skipped` entries for tests that never executed. */\n override onSuiteEnd(suiteStats: SuiteStats) {\n const cachedNames = this.#testNamesCache.get(suiteStats.file) || []\n const processedTestNames = new Set(\n suiteStats.tests\n .map((t) => (typeof t === 'string' ? t : t.title))\n .filter((title): title is string => Boolean(title))\n )\n\n cachedNames.forEach((testName) => {\n if (!processedTestNames.has(testName)) {\n const skippedTest: TestStats = {\n uid: generateStableUid({\n file: suiteStats.file,\n fullTitle: `${suiteStats.title} ${testName}`\n } as TestStats),\n cid: DEFAULTS.CID,\n title: testName,\n fullTitle: `${suiteStats.title} ${testName}`,\n parent: suiteStats.uid,\n state: 'skipped',\n start: new Date(),\n end: new Date(),\n type: 'test',\n file: suiteStats.file,\n retries: DEFAULTS.RETRIES,\n _duration: DEFAULTS.DURATION,\n hooks: []\n }\n suiteStats.tests.push(skippedTest)\n log.info(`Created skipped test \"${testName}\" (never executed)`)\n }\n })\n\n this.sendUpstream()\n }\n\n /** Replace a suite when its UID changes mid-run (after spec rescan). */\n updateSuite(suiteStats: SuiteStats) {\n const index = this.allSuites.findIndex((s) => s.file === suiteStats.file)\n if (index !== -1) {\n this.allSuites.splice(index, 1)\n }\n this.allSuites.push(suiteStats)\n this.#currentSuite = suiteStats\n this.sendUpstream()\n }\n}\n","import { stampTestEnd } from '@wdio/devtools-core'\nimport { TEST_STATE, DEFAULTS } from '../constants.js'\nimport {\n type TestStats,\n type SuiteStats,\n type NightwatchCurrentTest,\n type NightwatchTestCase\n} from '../types.js'\nimport { determineTestState } from './utils.js'\nimport type { TestReporter } from '../reporter.js'\n\nexport class TestManager {\n private processedTests = new Map<string, Set<string>>()\n private lastKnownTestName: string | null = null\n\n constructor(private testReporter: TestReporter) {}\n\n /**\n * Update test state and report to UI\n */\n updateTestState(\n test: TestStats,\n state: TestStats['state'],\n endTime?: Date,\n duration?: number\n ): void {\n test.state = state\n if (endTime) {\n test.end = endTime\n }\n if (duration !== undefined) {\n test._duration = duration\n }\n\n if (state === TEST_STATE.PASSED) {\n this.testReporter.onTestEnd(test)\n } else if (state === TEST_STATE.FAILED) {\n this.testReporter.onTestEnd(test)\n } else if (state === TEST_STATE.RUNNING) {\n this.testReporter.onTestStart(test)\n }\n\n if (state !== TEST_STATE.RUNNING) {\n this.testReporter.onTestEnd(test)\n }\n }\n\n /**\n * Find test in suite by title\n */\n findTestInSuite(suite: SuiteStats, testTitle: string): TestStats | undefined {\n return suite.tests.find(\n (t): t is TestStats => typeof t !== 'string' && t.title === testTitle\n )\n }\n\n /**\n * Mark test as processed to avoid duplicate reporting\n */\n markTestAsProcessed(testFile: string, testTitle: string): void {\n if (!this.processedTests.has(testFile)) {\n this.processedTests.set(testFile, new Set())\n }\n this.processedTests.get(testFile)!.add(testTitle)\n }\n\n /**\n * Check if test has been processed\n */\n isTestProcessed(testFile: string, testTitle: string): boolean {\n return this.processedTests.get(testFile)?.has(testTitle) ?? false\n }\n\n /**\n * Get processed tests for a file\n */\n getProcessedTests(testFile: string): Set<string> {\n return this.processedTests.get(testFile) || new Set()\n }\n\n /**\n * Detect test boundary and finalize previous test if needed\n * Returns the current test name\n */\n detectTestBoundary(currentNightwatchTest: NightwatchCurrentTest): string {\n const currentTestName = currentNightwatchTest?.name || DEFAULTS.TEST_NAME\n\n // If test name changed, finalize previous test\n if (\n this.lastKnownTestName &&\n currentTestName !== this.lastKnownTestName &&\n currentTestName !== DEFAULTS.TEST_NAME\n ) {\n const testFile =\n currentNightwatchTest.module?.split('/').pop() || DEFAULTS.FILE_NAME\n const currentSuite = this.testReporter.getCurrentSuite()\n\n if (currentSuite) {\n const prevTest = this.findTestInSuite(\n currentSuite,\n this.lastKnownTestName\n )\n if (prevTest && prevTest.state === TEST_STATE.RUNNING) {\n const prevTestCase =\n currentNightwatchTest.results?.testcases?.[this.lastKnownTestName]\n\n if (prevTestCase) {\n const testState = determineTestState(prevTestCase)\n this.updateTestState(\n prevTest,\n testState,\n new Date(),\n parseFloat(prevTestCase.time || '0') * 1000\n )\n this.markTestAsProcessed(testFile, this.lastKnownTestName)\n }\n }\n }\n }\n\n // Update last known test name\n if (currentTestName !== DEFAULTS.TEST_NAME) {\n this.lastKnownTestName = currentTestName\n }\n\n return currentTestName\n }\n\n /**\n * Start a pending test if this is its first command\n */\n startTestIfPending(currentTestName: string): void {\n if (\n currentTestName === DEFAULTS.TEST_NAME ||\n this.lastKnownTestName !== currentTestName\n ) {\n return\n }\n\n const currentSuite = this.testReporter.getCurrentSuite()\n if (!currentSuite) {\n return\n }\n\n const test = this.findTestInSuite(currentSuite, currentTestName)\n if (test && test.state === TEST_STATE.PENDING) {\n test.start = new Date()\n test.end = null\n this.updateTestState(test, TEST_STATE.RUNNING as TestStats['state'])\n }\n }\n\n /**\n * Finalize all incomplete tests in a suite\n */\n finalizeSuiteTests(\n suite: SuiteStats,\n testcases: Record<string, NightwatchTestCase>\n ): void {\n suite.tests.forEach((entry) => {\n if (typeof entry === 'string') {\n return\n }\n const test = entry\n if (test.state === TEST_STATE.RUNNING && test.start) {\n // Test was started but never finished - assume passed\n test.state = TEST_STATE.PASSED\n stampTestEnd(test)\n this.updateTestState(test, TEST_STATE.PASSED as TestStats['state'])\n } else if (test.state === TEST_STATE.PENDING) {\n const testcase = testcases[test.title]\n if (testcase) {\n const testState = determineTestState(testcase)\n test.start = test.start || new Date()\n this.updateTestState(\n test,\n testState,\n new Date(),\n parseFloat(testcase.time || '0') * 1000\n )\n } else {\n // Test never ran - mark as skipped\n this.updateTestState(\n test,\n TEST_STATE.SKIPPED as TestStats['state'],\n new Date(),\n 0\n )\n }\n }\n })\n }\n\n /**\n * Reset internal state for new test files\n */\n reset(): void {\n this.lastKnownTestName = null\n }\n}\n","/**\n * Suite Manager — Nightwatch flavor.\n * Maintains one suite per test file (Nightwatch runs each file independently).\n * Shares the suite factory + state-computation logic with selenium via\n * @wdio/devtools-core's suite-helpers; the storage strategy (Map by file)\n * is Nightwatch-specific and stays here.\n */\n\nimport {\n computeSuiteFinalStateStrict,\n computeSuiteRunningState,\n createSuiteStats,\n createTestStats,\n stampSuiteEnd\n} from '@wdio/devtools-core'\nimport { DEFAULTS, TEST_STATE } from '../constants.js'\nimport type { SuiteStats, TestStats } from '../types.js'\nimport type { TestReporter } from '../reporter.js'\nimport { generateStableUid } from './utils.js'\n\nexport class SuiteManager {\n private currentSuiteByFile = new Map<string, SuiteStats>()\n\n constructor(private testReporter: TestReporter) {}\n\n /**\n * Clear execution data when a rerun starts.\n * Resets all cached suites so they're recreated fresh during the new run.\n */\n clearExecutionData() {\n this.currentSuiteByFile.clear()\n }\n\n /**\n * Get or create suite for a test file\n */\n getOrCreateSuite(\n testFile: string,\n suiteTitle: string,\n fullPath: string | null,\n testNames: string[],\n suiteLine?: number | null,\n testLines?: number[]\n ): SuiteStats {\n if (!this.currentSuiteByFile.has(testFile)) {\n const file = fullPath || testFile\n const suiteStats = createSuiteStats({\n uid: generateStableUid(file, suiteTitle),\n cid: DEFAULTS.CID,\n title: suiteTitle,\n file,\n state: TEST_STATE.PENDING as TestStats['state'],\n callSource:\n suiteLine && fullPath ? `${fullPath}:${suiteLine}` : undefined\n })\n\n // Create test entries with pending state\n for (let idx = 0; idx < testNames.length; idx++) {\n const testName = testNames[idx]\n const testLine = testLines?.[idx]\n const fullTitle = `${suiteTitle} ${testName}`\n const testEntry = createTestStats({\n uid: generateStableUid(file, fullTitle),\n cid: DEFAULTS.CID,\n title: testName,\n fullTitle,\n file,\n parent: suiteStats.uid,\n state: TEST_STATE.PENDING as TestStats['state'],\n callSource:\n testLine && fullPath ? `${fullPath}:${testLine}` : undefined\n })\n suiteStats.tests.push(testEntry)\n }\n\n this.currentSuiteByFile.set(testFile, suiteStats)\n this.testReporter.onSuiteStart(suiteStats)\n }\n\n return this.currentSuiteByFile.get(testFile)!\n }\n\n /**\n * Get suite for a test file\n */\n getSuite(testFile: string): SuiteStats | undefined {\n return this.currentSuiteByFile.get(testFile)\n }\n\n /**\n * Mark suite as running.\n * Clears `end` so that the `finalizeSuite` guard (`if (suite.end) return`)\n * does not skip re-finalization during a rerun.\n */\n markSuiteAsRunning(suite: SuiteStats): void {\n suite.state = TEST_STATE.RUNNING\n suite.end = null\n this.testReporter.updateSuites()\n }\n\n /**\n * Update suite state from current children without marking it as ended.\n * Used during Cucumber runs to keep the feature-level suite state fresh.\n */\n finalizeSuiteState(suite: SuiteStats): void {\n suite.state = computeSuiteRunningState(suite)\n this.testReporter.updateSuites()\n }\n\n /**\n * Finalize suite with test results\n */\n finalizeSuite(suite: SuiteStats): void {\n if (suite.end) {\n return\n }\n stampSuiteEnd(suite)\n suite.state = computeSuiteFinalStateStrict(suite)\n this.testReporter.onSuiteEnd(suite)\n }\n\n /**\n * Get all suites\n */\n getAllSuites(): Map<string, SuiteStats> {\n return this.currentSuiteByFile\n }\n}\n","/**\n * Browser Proxy\n * Handles browser command interception and tracking\n */\n\nimport logger from '@wdio/logger'\nimport {\n INTERNAL_COMMANDS_TO_IGNORE,\n NAVIGATION_COMMANDS\n} from '../constants.js'\nimport { getCallSourceFromStack } from './utils.js'\nimport { serializeCommandResult } from './serializeCommandResult.js'\nimport { RetryTracker, toError } from '@wdio/devtools-core'\nimport type { SessionCapturer } from '../session.js'\nimport type { TestManager } from './testManager.js'\nimport type {\n CommandLog,\n NightwatchBrowser,\n NightwatchCurrentTest,\n CommandStackFrame\n} from '../types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:browserProxy')\n\nexport class BrowserProxy {\n /** Tracks which browser *instances* have already been proxied to avoid double-wrapping. */\n private proxiedBrowsers = new WeakSet<object>()\n private commandStack: CommandStackFrame[] = []\n private lastCommandSig: string | null = null\n private currentTestFullPath: string | null = null\n /**\n * Tracks the last captured command so that consecutive retries of the same\n * command (e.g. getText inside a waitFor loop) overwrite the previous entry\n * rather than appending, showing only the final execution result.\n */\n private retryTracker = new RetryTracker()\n\n constructor(\n private sessionCapturer: SessionCapturer,\n private testManager: TestManager,\n private getCurrentTest: () => { uid?: string } | null\n ) {}\n\n /**\n * Update the session capturer reference after a WebDriver session change.\n * Does NOT re-wrap browser methods — wrapping is permanent per browser object.\n */\n updateSessionCapturer(capturer: SessionCapturer): void {\n this.sessionCapturer = capturer\n }\n\n resetCommandTracking(): void {\n this.commandStack = []\n this.lastCommandSig = null\n this.retryTracker.reset()\n }\n\n getCurrentTestFullPath(): string | null {\n return this.currentTestFullPath\n }\n\n setCurrentTestFullPath(path: string | null): void {\n this.currentTestFullPath = path\n }\n\n /**\n * Wrap browser navigation methods (url / navigate / navigateTo) to inject\n * the DevTools script after every navigation.\n *\n * Uses `browser` from the closure (not `this` inside perform) so it works\n * for both standard Nightwatch (chainable API) and Cucumber async/await mode\n * where `this` inside a perform callback is not the browser.\n */\n wrapUrlMethod(browser: NightwatchBrowser): void {\n const sessionCapturer = this.sessionCapturer\n\n // Cast once for dynamic method access — Nightwatch's typed surface\n // doesn't enumerate every command, but they all live on the same object.\n // Return type is `unknown` because wrapNav has to handle both\n // Nightwatch's chainable API (returns a chainable with `.perform`) and\n // Cucumber async/await (returns a Promise) — we narrow at each branch.\n const b = browser as unknown as Record<\n string,\n (...args: unknown[]) => unknown\n >\n\n const wrapNav = (methodName: string) => {\n if (typeof b[methodName] !== 'function') {\n return\n }\n const original = b[methodName].bind(browser)\n\n b[methodName] = function (...args: unknown[]) {\n const result = original(...args)\n\n const injectAndCapture = () => {\n log.info(`[nav] ${methodName}(${args[0] ?? ''}) — injecting script`)\n return sessionCapturer\n .injectScript(browser)\n .then(() => sessionCapturer.captureTrace(browser))\n .catch((err: Error) =>\n log.error(`Failed to inject script: ${(err as Error).message}`)\n )\n }\n\n const chainable = result as\n | { perform?: (cb: (done: Function) => void) => void }\n | undefined\n if (chainable && typeof chainable.perform === 'function') {\n // Standard Nightwatch (chained API): queue inside perform so it\n // runs after navigation completes. Always pass `done` so the\n // command queue is unblocked even if injection fails.\n chainable.perform((done: Function) => {\n injectAndCapture().finally(() => done && done())\n })\n return result\n }\n // Cucumber async/await: result is a Promise (or thenable).\n // Return the AUGMENTED promise so that `await browser.url(...)` in\n // the step definition waits for injectAndCapture to finish before\n // Cucumber moves to the next step. Without this the injection races\n // with the next step's commands (e.g. setValue), causing stale-element\n // errors because the script-tag insertion mutates the DOM mid-form.\n return Promise.resolve(result)\n .then(injectAndCapture)\n .catch(() => {})\n }\n }\n\n wrapNav('url')\n wrapNav('navigate')\n wrapNav('navigateTo')\n\n log.info('✓ Script injection wrapped')\n }\n\n wrapBrowserCommands(browser: NightwatchBrowser): void {\n if (this.proxiedBrowsers.has(browser as object)) {\n return\n }\n\n // Single widening: Nightwatch's `browser` is a dynamic command bag —\n // every wrapped lookup below is property-name → function. Casting once\n // keeps the wrap loop readable.\n const browserAny = browser as unknown as Record<\n string,\n (...args: unknown[]) => unknown\n >\n const allMethods = new Set([\n ...Object.keys(browser),\n ...Object.getOwnPropertyNames(Object.getPrototypeOf(browser))\n ])\n const wrappedMethods: string[] = []\n\n allMethods.forEach((methodName) => {\n if (\n methodName === 'constructor' ||\n typeof browserAny[methodName] !== 'function'\n ) {\n return\n }\n\n if (\n (INTERNAL_COMMANDS_TO_IGNORE as readonly string[]).includes(\n methodName\n ) ||\n methodName.startsWith('__')\n ) {\n return\n }\n\n const originalMethod = browserAny[methodName].bind(browser)\n\n browserAny[methodName] = (...args: unknown[]) => {\n return this.handleCommandExecution(\n browser,\n browserAny,\n methodName,\n originalMethod,\n args\n )\n }\n\n wrappedMethods.push(methodName)\n })\n\n this.proxiedBrowsers.add(browser as object)\n log.info(`✓ Wrapped ${wrappedMethods.length} browser methods`)\n }\n\n private handleRetryReplacement(\n browser: NightwatchBrowser,\n methodName: string,\n logArgs: unknown[],\n serializedResult: unknown,\n effectiveUid: string,\n callSource: string | undefined,\n commandTimestamp: number\n ): void {\n // Same command fired again (internal retry) — replace the previous\n // entry so only the final result appears in the UI.\n const { entry, oldTimestamp } = this.sessionCapturer.replaceCommand(\n this.retryTracker.lastId!,\n methodName,\n logArgs,\n serializedResult,\n undefined,\n effectiveUid,\n callSource,\n commandTimestamp\n )\n this.retryTracker.setLastId(entry._id ?? null)\n this.sessionCapturer.sendReplaceCommand(oldTimestamp, entry)\n this.attachScreenshot(browser, entry, methodName, ' (retry)')\n }\n\n private captureFreshCommand(\n browser: NightwatchBrowser,\n methodName: string,\n logArgs: unknown[],\n serializedResult: unknown,\n effectiveUid: string,\n callSource: string | undefined,\n commandTimestamp: number,\n cmdSig: string\n ): void {\n // captureCommand() pushes the entry to commandsLog synchronously before\n // any async work (navigation perf capture), so we can grab the ID\n // immediately after the call — before any microtask fires. This avoids\n // the race where a Nightwatch retry callback executes before .then() sets\n // lastId, causing missed dedup. Stage the sig now, set the id after the\n // synchronous push lands.\n this.retryTracker.setLastSig(cmdSig)\n this.retryTracker.setLastId(null)\n this.sessionCapturer\n .captureCommand(\n methodName,\n logArgs,\n serializedResult,\n undefined,\n effectiveUid,\n callSource,\n commandTimestamp\n )\n .catch((err) =>\n log.error(`Failed to capture ${methodName}: ${(err as Error).message}`)\n )\n const lastCommand =\n this.sessionCapturer.commandsLog[\n this.sessionCapturer.commandsLog.length - 1\n ]\n if (lastCommand) {\n this.retryTracker.setLastId((lastCommand as { _id?: number })._id ?? null)\n this.sessionCapturer.sendCommand(lastCommand)\n log.info(`[command] ${methodName}`)\n this.attachScreenshot(browser, lastCommand, methodName)\n }\n this.maybeRepollMutations(browser, methodName)\n }\n\n // After DOM-mutating commands, re-poll mutations from the injected script\n // so the browser preview stays in sync. setTimeout runs OUTSIDE Nightwatch's\n // current callback stack (safer queue-wise).\n private maybeRepollMutations(\n browser: NightwatchBrowser,\n methodName: string\n ): void {\n const isDomMutating =\n (NAVIGATION_COMMANDS as readonly string[]).includes(methodName) ||\n [\n 'click',\n 'doubleClick',\n 'rightClick',\n 'setValue',\n 'clearValue',\n 'sendKeys',\n 'submitForm',\n 'back',\n 'forward',\n 'refresh'\n ].includes(methodName)\n if (!isDomMutating) {\n return\n }\n setTimeout(() => {\n this.sessionCapturer.captureTrace(browser).catch(() => {})\n }, 200)\n }\n\n private popCommandStackIfMatches(methodName: string, cmdSig: string): void {\n const stackFrame = this.commandStack[this.commandStack.length - 1]\n if (stackFrame?.command === methodName && stackFrame.signature === cmdSig) {\n this.commandStack.pop()\n }\n }\n\n // Result-capturing callback factory — called by Nightwatch's async queue\n // when the command completes. This is where we get the *actual* result.\n private makeCaptureCallback(\n browser: NightwatchBrowser,\n methodName: string,\n logArgs: unknown[],\n cmdSig: string,\n callSource: string | undefined,\n commandTimestamp: number,\n testUid: string | undefined,\n userCallback: Function | null\n ): (callbackResult: unknown) => unknown {\n return (callbackResult: unknown) => {\n this.popCommandStackIfMatches(methodName, cmdSig)\n const serializedResult = serializeCommandResult(\n callbackResult,\n methodName\n )\n const effectiveUid = this.getCurrentTest()?.uid ?? testUid\n if (effectiveUid) {\n if (this.retryTracker.isRetry(cmdSig)) {\n this.handleRetryReplacement(\n browser,\n methodName,\n logArgs,\n serializedResult,\n effectiveUid,\n callSource,\n commandTimestamp\n )\n } else {\n this.captureFreshCommand(\n browser,\n methodName,\n logArgs,\n serializedResult,\n effectiveUid,\n callSource,\n commandTimestamp,\n cmdSig\n )\n }\n }\n if (userCallback) {\n return userCallback(callbackResult)\n }\n }\n }\n\n private pushCommandStackIfNew(\n methodName: string,\n cmdSig: string,\n callSource: string | undefined\n ): void {\n if (this.lastCommandSig === cmdSig) {\n return\n }\n this.commandStack.push({\n command: methodName,\n callSource,\n signature: cmdSig\n })\n this.lastCommandSig = cmdSig\n }\n\n private handleCommandExecution(\n browser: NightwatchBrowser,\n browserAny: Record<string, unknown>,\n methodName: string,\n originalMethod: Function,\n args: unknown[]\n ): unknown {\n this.testManager.startTestIfPending(\n this.testManager.detectTestBoundary(\n browserAny.currentTest as NightwatchCurrentTest\n )\n )\n\n const callInfo = getCallSourceFromStack()\n if (callInfo.filePath && !this.currentTestFullPath) {\n this.currentTestFullPath = callInfo.filePath\n }\n\n const lastArg = args[args.length - 1]\n const hasUserCallback = typeof lastArg === 'function'\n const userCallback: Function | null = hasUserCallback ? lastArg : null\n const logArgs = hasUserCallback ? args.slice(0, -1) : args\n\n const cmdSig = JSON.stringify({\n command: methodName,\n args: logArgs,\n src: callInfo.callSource\n })\n this.pushCommandStackIfNew(methodName, cmdSig, callInfo.callSource)\n\n const callSource = callInfo.callSource\n const commandTimestamp = Date.now()\n const captureCallback = this.makeCaptureCallback(\n browser,\n methodName,\n logArgs,\n cmdSig,\n callSource,\n commandTimestamp,\n this.getCurrentTest()?.uid,\n userCallback\n )\n const modifiedArgs = [...logArgs, captureCallback]\n try {\n return originalMethod(...modifiedArgs)\n } catch (error) {\n this.popCommandStackIfMatches(methodName, cmdSig)\n this.captureCommandError(methodName, logArgs, error, callSource)\n throw error\n }\n }\n\n private captureCommandError(\n methodName: string,\n args: unknown[],\n error: unknown,\n callSource: string | undefined\n ): void {\n const currentTest = this.getCurrentTest()\n if (!currentTest) {\n return\n }\n\n const normalizedError = toError(error)\n log.error(`[command error] ${methodName}: ${normalizedError.message}`)\n\n this.sessionCapturer\n .captureCommand(\n methodName,\n args,\n undefined,\n normalizedError,\n currentTest.uid,\n callSource\n )\n .catch((err) =>\n log.error(`Failed to capture ${methodName}: ${(err as Error).message}`)\n )\n\n const lastCommand =\n this.sessionCapturer.commandsLog[\n this.sessionCapturer.commandsLog.length - 1\n ]\n if (lastCommand) {\n this.sessionCapturer.sendCommand(lastCommand)\n }\n }\n\n isProxied(browser: NightwatchBrowser): boolean {\n return this.proxiedBrowsers.has(browser as object)\n }\n\n /**\n * Fire-and-forget: pull a screenshot via the WebDriver HTTP endpoint and\n * attach it to an already-captured command entry. The `suffix` is appended\n * to the log line so retried-command screenshots show `(retry)`. Errors\n * are silently swallowed — screenshots are best-effort and shouldn't fail\n * the run.\n */\n private attachScreenshot(\n browser: NightwatchBrowser,\n entry: { timestamp?: number; screenshot?: string | null },\n methodName: string,\n suffix = ''\n ): void {\n const ts = entry.timestamp ?? 0\n this.sessionCapturer\n .takeScreenshotViaHttp(browser)\n .then((screenshot) => {\n if (screenshot) {\n entry.screenshot = screenshot\n this.sessionCapturer.sendReplaceCommand(ts, entry as CommandLog)\n log.info(`[screenshot] Attached to ${methodName}${suffix}`)\n }\n })\n .catch(() => {})\n }\n}\n","import { BOOLEAN_COMMAND_PATTERN } from '../constants.js'\n\n/**\n * Convert the raw value Nightwatch's async queue hands back to a\n * UI-friendly JSON-safe representation. Three special cases:\n *\n * - Nightwatch assertion objects `{ passed, actual, expected, message }`\n * collapse to `true` on pass, or the structured failure record on fail.\n * - Driver-result wrappers `{ value: <raw> }` unwrap to the inner value.\n * `null` on a boolean-semantic command (e.g. `waitForExist`) means\n * \"timed out / not found\" — coerce to `false` so the UI doesn't render\n * `null`.\n * - Plain objects are deep-cloned via JSON.parse/stringify so the UI can\n * safely serialize them; functions and circular references fall back to\n * `String(value)`.\n */\nexport function serializeCommandResult(\n callbackResult: unknown,\n methodName: string\n): unknown {\n if (callbackResult === null || callbackResult === undefined) {\n return undefined\n }\n\n const isBooleanCommand = BOOLEAN_COMMAND_PATTERN.test(methodName)\n\n // After the typeof + null guard above, the value is a non-null object —\n // safe to widen via `Record<string, unknown>` and probe for the discriminator\n // properties without per-access `as any`.\n if (typeof callbackResult === 'object') {\n const r = callbackResult as Record<string, unknown>\n if ('passed' in r) {\n return r.passed\n ? true\n : {\n passed: false,\n actual: r.actual,\n expected: r.expected,\n message: r.message\n }\n }\n if ('value' in r) {\n const raw = r.value\n return raw === null && isBooleanCommand ? false : raw\n }\n }\n\n if (typeof callbackResult !== 'function') {\n try {\n return JSON.parse(JSON.stringify(callbackResult))\n } catch {\n return String(callbackResult)\n }\n }\n\n return undefined\n}\n","import logger from '@wdio/logger'\nimport { ScreencastRecorderBase, errorMessage } from '@wdio/devtools-core'\nimport type { ScreencastOptions } from '@wdio/devtools-shared'\nimport type { SessionCapturer } from './session.js'\nimport type { NightwatchBrowser } from './types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:ScreencastRecorder')\n\n/**\n * Nightwatch screencast recorder. Polling-only — Nightwatch doesn't expose a\n * stable CDP escape hatch the way WDIO (getPuppeteer) and Selenium\n * (createCDPConnection) do.\n *\n * `browser.takeScreenshot()` goes through Nightwatch's command queue and is\n * unreliable for polling (the existing code has `takeScreenshotViaHttp` for\n * the same reason — see session.ts). The recorder delegates to that helper\n * instead so screenshots fire directly over the WebDriver HTTP transport.\n */\nexport class ScreencastRecorder extends ScreencastRecorderBase<NightwatchBrowser> {\n readonly #sessionCapturer: SessionCapturer\n\n constructor(sessionCapturer: SessionCapturer, options: ScreencastOptions) {\n super(options)\n this.#sessionCapturer = sessionCapturer\n }\n\n protected override onPollingStarted(intervalMs: number): void {\n log.info(\n `✓ Screencast recording started (polling mode, ${intervalMs} ms interval)`\n )\n }\n\n protected override onPollingStopped(frameCount: number): void {\n log.info(`✓ Screencast stopped — ${frameCount} frame(s) collected`)\n }\n\n protected override onUnavailable(err: unknown): void {\n log.warn(\n `Screencast unavailable (${errorMessage(err)}). Recording skipped.`\n )\n }\n\n protected override async takeScreenshot(): Promise<string | null> {\n const browser = this.driver\n if (!browser) {\n return null\n }\n return this.#sessionCapturer.takeScreenshotViaHttp(browser)\n }\n}\n"],"mappings":"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","names":["path","xpath","resolve","resolve","fs","os","path","fs","path","require","resolve","log","path","fs","os","fs","path","fs","path","createRequire","require","resolve","fs","fs","resolve","fs","logger","fs","path","os","fs","path","parseStackTrace","generateStableUid","parseStackTrace","resolve","logger","log","logger","logger","fs","path","log","logger","logger","fs","path","path","fs","logger","log","logger","resolve","log","logger","resolve","logger","logger","captureActionSnapshot","log","logger","path","captureActionSnapshot","resolve","logger","log","logger","generateStableUid","generateStableUid","logger","log","logger","path","logger","log","logger","log","logger","finalizeCurrentScreencast","resolve","log","logger"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../../shared/src/files.ts","../../shared/src/routes.ts","../../shared/src/runner.ts","../../shared/src/timing.ts","../../shared/src/trace-actions.ts","../../shared/src/types.ts","../../core/src/action-mapping.ts","../../core/src/element-scripts.ts","../../core/src/locators/xml-parsing.ts","../../core/src/locators/constants.ts","../../core/src/locators/locator-generation.ts","../../core/src/element-snapshot.ts","../../core/src/locators/element-filter.ts","../../core/src/locators/index.ts","../../core/src/with-timeout.ts","../../core/src/action-snapshot.ts","../../core/src/stack.ts","../../core/src/trace-exporter.ts","../../core/src/trace-har.ts","../../core/src/trace-zip-writer.ts","../../core/src/uid.ts","../../core/src/finalize-screencast.ts","../../core/src/video-encoder.ts","../../core/src/output-dir.ts","../../core/src/performance-capture.ts","../../core/src/retry-tracker.ts","../../core/src/screencast.ts","../../core/src/script-loader.ts","../../core/src/session-capturer.ts","../../core/src/spec-trace-helpers.ts","../../core/src/suite-helpers.ts","../../core/src/test-discovery.ts","../../core/src/test-reporter.ts","../src/run-lifecycle.ts","../src/constants.ts","../src/helpers/utils.ts","../src/event-hub.ts","../src/cucumber-lifecycle.ts","../src/helpers/cucumberResult.ts","../src/helpers/cucumberScenarioBuilder.ts","../src/helpers/featureFileScan.ts","../src/test-lifecycle.ts","../src/helpers/specFileResolver.ts","../src/helpers/closePreviousTest.ts","../src/session-init.ts","../src/session.ts","../src/action-snapshot.ts","../src/helpers/perfLogs.ts","../src/reporter.ts","../src/helpers/testManager.ts","../src/helpers/suiteManager.ts","../src/helpers/browserProxy.ts","../src/helpers/serializeCommandResult.ts","../src/screencast.ts"],"sourcesContent":["/**\n * Nightwatch DevTools Plugin\n *\n * Integrates Nightwatch with WebdriverIO DevTools following the WDIO service pattern.\n * Captures commands, network requests, and console logs during test execution in real-time.\n */\n\nimport { fileURLToPath } from 'node:url'\nimport {\n errorMessage,\n recordSpecBoundary,\n resolveAdapterOutputDir,\n writeSpecTrace,\n writeTraceZip,\n type SpecRange\n} from '@wdio/devtools-core'\nimport { stop as stopBackend } from '@wdio/devtools-backend'\nimport { REUSE_ENV, SCREENCAST_DEFAULTS } from '@wdio/devtools-shared'\nimport logger from '@wdio/logger'\nimport {\n handleReuseMode,\n openDevtoolsBrowser,\n finalizeAllSuites,\n logRunSummary,\n waitForDevtoolsBrowserClose,\n runPluginBefore,\n type PluginBeforeCtx\n} from './run-lifecycle.js'\nimport type { PluginInternals } from './plugin-internals.js'\nimport type { SessionCapturer } from './session.js'\nimport type { TestReporter } from './reporter.js'\nimport type { ScreencastRecorder } from './screencast.js'\nimport type { TestManager } from './helpers/testManager.js'\nimport type { SuiteManager } from './helpers/suiteManager.js'\nimport type { BrowserProxy } from './helpers/browserProxy.js'\nimport type {\n DevToolsOptions,\n NightwatchBrowser,\n NightwatchCurrentTest,\n NightwatchEventHub,\n ScreencastOptions,\n SuiteStats,\n TestStats,\n TestMetadataMap\n} from './types.js'\nimport { registerEventHandlers as registerEventHandlersImpl } from './event-hub.js'\nimport {\n cucumberBefore as cucumberLifecycleBefore,\n cucumberAfter as cucumberLifecycleAfter,\n cucumberBeforeStep as cucumberLifecycleBeforeStep,\n cucumberAfterStep as cucumberLifecycleAfterStep,\n type CucumberPickle,\n type CucumberPickleStep,\n type CucumberResult\n} from './cucumber-lifecycle.js'\nimport {\n resolveSuiteMetadata,\n pickCurrentTestName,\n startNextTest,\n closePreviousRunningTest,\n wrapBrowserOnce,\n closeOutTestcases\n} from './test-lifecycle.js'\nimport {\n ensureSessionInitialized,\n finalizeCurrentScreencast\n} from './session-init.js'\nimport {\n getTestIcon,\n incrementCounters,\n buildPluginMetadataOptions\n} from './helpers/utils.js'\n\nconst log = logger('@wdio/nightwatch-devtools')\n\nclass NightwatchDevToolsPlugin {\n private options: Required<DevToolsOptions>\n private sessionCapturer!: SessionCapturer\n private testReporter!: TestReporter\n private testManager!: TestManager\n private suiteManager!: SuiteManager\n private browserProxy!: BrowserProxy\n private isScriptInjected = false\n #currentTest: unknown = null\n #currentScenarioSuite: SuiteStats | null = null\n #currentStep: unknown = null\n #lastSessionId: string | null = null\n #devtoolsBrowser?: WebdriverIO.Browser\n #userDataDir?: string\n #isCucumberRunner = false\n #passCount = 0\n #failCount = 0\n #skipCount = 0\n #configPath: string | undefined\n #srcFolders: string[] = []\n\n /** Index ranges into the session capturer's flat arrays, one per spec file. */\n #specRanges: SpecRange[] = []\n\n /** Set of spec files already flushed to disk. */\n #flushedSpecs = new Set<string>()\n\n #getRerunLabel() {\n return process.env[REUSE_ENV.RERUN_ENTRY_TYPE] === 'test'\n ? process.env[REUSE_ENV.RERUN_LABEL]?.trim()\n : undefined\n }\n\n #screencastOptions: ScreencastOptions\n #screencastRecorder?: ScreencastRecorder\n #screencastSessionId?: string\n #bidiEnabled = false\n #bidiAttachAttempted = false\n\n constructor(options: DevToolsOptions = {}) {\n const mode = options.mode ?? 'live'\n const ignore = mode === 'trace' && options.screencast?.enabled === true\n if (ignore) {\n log.warn('trace mode: ignoring screencast option (live-mode feature)')\n }\n const screencast = ignore ? {} : (options.screencast ?? {})\n this.options = {\n port: options.port ?? 3000,\n hostname: options.hostname ?? 'localhost',\n screencast,\n bidi: options.bidi ?? false,\n mode,\n traceFormat: options.traceFormat ?? 'zip',\n traceGranularity: options.traceGranularity ?? 'session'\n }\n this.#screencastOptions = { ...SCREENCAST_DEFAULTS, ...screencast }\n this.#bidiEnabled = options.bidi === true\n }\n\n // Single internals \"bag\" — structurally satisfies all 4 lifecycle ctx\n // interfaces. Lifecycle modules cast it to their narrow type at call time.\n #internals: PluginInternals | undefined\n // Declarative accessor map — splitting this purely to satisfy the\n // line-count rule hurts readability; the body is mechanical wiring.\n // eslint-disable-next-line max-lines-per-function\n #getInternals(): PluginInternals {\n if (this.#internals) {\n return this.#internals\n }\n const self = this\n this.#internals = {\n get options() {\n return self.options\n },\n get hostname() {\n return self.options.hostname\n },\n get port() {\n return self.options.port\n },\n get mode() {\n return self.options.mode\n },\n get screencastOptions() {\n return self.#screencastOptions\n },\n get bidiEnabled() {\n return self.#bidiEnabled\n },\n get sessionCapturer() {\n return self.sessionCapturer\n },\n set sessionCapturer(v) {\n self.sessionCapturer = v\n },\n get testReporter() {\n return self.testReporter\n },\n set testReporter(v) {\n self.testReporter = v\n },\n get testManager() {\n return self.testManager\n },\n set testManager(v) {\n self.testManager = v\n },\n get suiteManager() {\n return self.suiteManager\n },\n set suiteManager(v) {\n self.suiteManager = v\n },\n get browserProxy() {\n return self.browserProxy\n },\n set browserProxy(v) {\n self.browserProxy = v\n },\n get isScriptInjected() {\n return self.isScriptInjected\n },\n set isScriptInjected(v) {\n self.isScriptInjected = v\n },\n get devtoolsBrowser() {\n return self.#devtoolsBrowser\n },\n set devtoolsBrowser(v) {\n self.#devtoolsBrowser = v\n },\n get userDataDir() {\n return self.#userDataDir\n },\n set userDataDir(v) {\n self.#userDataDir = v\n },\n get passCount() {\n return self.#passCount\n },\n set passCount(v) {\n self.#passCount = v\n },\n get failCount() {\n return self.#failCount\n },\n set failCount(v) {\n self.#failCount = v\n },\n get skipCount() {\n return self.#skipCount\n },\n set skipCount(v) {\n self.#skipCount = v\n },\n get lastSessionId() {\n return self.#lastSessionId\n },\n set lastSessionId(v) {\n self.#lastSessionId = v\n },\n get bidiAttachAttempted() {\n return self.#bidiAttachAttempted\n },\n set bidiAttachAttempted(v) {\n self.#bidiAttachAttempted = v\n },\n get srcFolders() {\n return self.#srcFolders\n },\n set srcFolders(v) {\n self.#srcFolders = v\n },\n get screencastRecorder() {\n return self.#screencastRecorder\n },\n set screencastRecorder(v) {\n self.#screencastRecorder = v\n },\n get screencastSessionId() {\n return self.#screencastSessionId\n },\n set screencastSessionId(v) {\n self.#screencastSessionId = v\n },\n get configPath() {\n return self.#configPath\n },\n getCurrentTest: () => self.#currentTest,\n getCurrentScenarioSuite: () => self.#currentScenarioSuite,\n getCurrentStep: () => self.#currentStep,\n setCurrentTest: (t) => {\n self.#currentTest = t\n },\n setCurrentScenarioSuite: (s) => {\n self.#currentScenarioSuite = s\n },\n setCurrentStep: (s) => {\n self.#currentStep = s\n },\n clearExecutionData: () => {\n self.testReporter.clearExecutionData()\n self.suiteManager.clearExecutionData()\n },\n buildMetadataOptions: () => self.#buildMetadataOptions(),\n ensureSessionInitialized: (b) => self.#ensureSessionInitialized(b),\n wrapBrowserOnce: (b) => self.#wrapBrowserOnce(b),\n incrementCount: (s) => self.#incrementCount(s),\n testIcon: (s) => self.#testIcon(s),\n setCucumberRunner: (v) => {\n self.#isCucumberRunner = v\n },\n getRerunLabel: () => self.#getRerunLabel()\n }\n return this.#internals\n }\n\n #handleReuseMode(): void {\n handleReuseMode(this.#getInternals())\n }\n\n async #openDevtoolsBrowser(url: string): Promise<void> {\n await openDevtoolsBrowser(this.#getInternals(), url)\n }\n\n async before() {\n const internals = this.#getInternals() as unknown as PluginBeforeCtx\n internals.setConfigPath = (v) => {\n this.#configPath = v\n }\n internals.openDevtoolsBrowserAt = (url) => this.#openDevtoolsBrowser(url)\n internals.handleReuse = () => this.#handleReuseMode()\n internals.plugin = this\n await runPluginBefore(internals)\n }\n\n async #ensureSessionInitialized(browser: NightwatchBrowser) {\n await ensureSessionInitialized(this.#getInternals(), browser, () =>\n this.#finalizeCurrentScreencast()\n )\n }\n\n async #finalizeCurrentScreencast(): Promise<void> {\n await finalizeCurrentScreencast(this.#getInternals())\n }\n\n async cucumberBefore(browser: NightwatchBrowser, pickle: CucumberPickle) {\n await cucumberLifecycleBefore(this.#getInternals(), browser, pickle)\n }\n\n async cucumberAfter(\n browser: NightwatchBrowser,\n result: CucumberResult,\n pickle: CucumberPickle\n ) {\n await cucumberLifecycleAfter(this.#getInternals(), browser, result, pickle)\n }\n\n async cucumberBeforeStep(\n browser: NightwatchBrowser,\n pickleStep: CucumberPickleStep,\n pickle: CucumberPickle\n ) {\n await cucumberLifecycleBeforeStep(\n this.#getInternals(),\n browser,\n pickleStep,\n pickle\n )\n }\n\n async cucumberAfterStep(\n browser: NightwatchBrowser,\n result: CucumberResult,\n pickleStep: CucumberPickleStep,\n pickle: CucumberPickle\n ) {\n await cucumberLifecycleAfterStep(\n this.#getInternals(),\n browser,\n result,\n pickleStep,\n pickle\n )\n }\n\n #resolveSuiteMetadata(currentTest: NightwatchCurrentTest) {\n return resolveSuiteMetadata(this.#getInternals(), currentTest)\n }\n\n #pickCurrentTestName(\n currentTest: NightwatchCurrentTest,\n testNames: string[],\n processedTests: Set<string>\n ): string | undefined {\n return pickCurrentTestName(currentTest, testNames, processedTests)\n }\n\n async #startNextTest(\n currentSuite: SuiteStats,\n currentTestName: string,\n processedTests: Set<string>\n ): Promise<void> {\n await startNextTest(\n this.#getInternals(),\n currentSuite,\n currentTestName,\n processedTests\n )\n }\n\n async #closePreviousRunningTest(\n currentSuite: SuiteStats,\n testFile: string,\n currentTest: NightwatchCurrentTest\n ): Promise<void> {\n await closePreviousRunningTest(\n this.#getInternals(),\n currentSuite,\n testFile,\n currentTest\n )\n }\n\n #wrapBrowserOnce(browser: NightwatchBrowser): void {\n wrapBrowserOnce(this.#getInternals(), browser)\n }\n\n async beforeEach(browser: NightwatchBrowser) {\n if (this.#isCucumberRunner) {\n return\n }\n await this.#ensureSessionInitialized(browser)\n\n const currentTest = browser.currentTest as NightwatchCurrentTest | undefined\n if (!currentTest) {\n return\n }\n\n const { testFile, fullPath, suiteTitle, testNames, suiteLine, testLines } =\n this.#resolveSuiteMetadata(currentTest)\n const currentSuite = this.suiteManager.getOrCreateSuite(\n testFile,\n suiteTitle,\n fullPath,\n testNames,\n suiteLine,\n testLines\n )\n if (fullPath && fullPath.includes('/')) {\n this.sessionCapturer.captureSource(fullPath).catch(() => {})\n }\n\n // ── Per-spec boundary detection ──\n if (fullPath) {\n const prevRange = recordSpecBoundary(\n {\n specRanges: this.#specRanges,\n flushedSpecs: this.#flushedSpecs,\n capturer: this.sessionCapturer,\n actionSnapshots: this.sessionCapturer.actionSnapshots\n },\n fullPath,\n this.options.traceGranularity\n )\n if (prevRange) {\n void this.#flushSpecTrace(prevRange).catch((err) =>\n log.warn(\n `Failed to flush trace for spec \"${prevRange.specFile}\": ${errorMessage(err)}`\n )\n )\n }\n }\n\n await this.#closePreviousRunningTest(currentSuite, testFile, currentTest)\n\n const processedTests = this.testManager.getProcessedTests(testFile)\n const currentTestName = this.#pickCurrentTestName(\n currentTest,\n testNames,\n processedTests\n )\n if (currentTestName) {\n await this.#startNextTest(currentSuite, currentTestName, processedTests)\n }\n this.#wrapBrowserOnce(browser)\n }\n\n async afterEach(browser: NightwatchBrowser) {\n // Cucumber runner manages its own lifecycle via cucumberHooks.cjs\n if (this.#isCucumberRunner) {\n return\n }\n\n if (browser && this.sessionCapturer) {\n try {\n await this.#closeOutTestcases(browser)\n await this.sessionCapturer.captureTrace(browser)\n } catch (err) {\n log.error(`Failed to capture trace: ${errorMessage(err)}`)\n }\n }\n }\n\n async #closeOutTestcases(browser: NightwatchBrowser): Promise<void> {\n await closeOutTestcases(this.#getInternals(), browser)\n }\n\n async after(browser?: NightwatchBrowser) {\n await this.#finalizeCurrentScreencast()\n try {\n await this.#finalizeAllSuites(browser)\n this.#logRunSummary()\n if (this.options.mode === 'trace') {\n await this.#writeTraceZipIfNeeded()\n await this.sessionCapturer?.closeWebSocket()\n await stopBackend()\n return\n }\n if (!this.#devtoolsBrowser) {\n // Reuse mode: force one final suites broadcast so the UI reflects the\n // actual outcome before the process exits.\n this.testReporter?.updateSuites()\n log.info('♻ Rerun complete — flushing WebSocket...')\n await this.sessionCapturer?.closeWebSocket()\n return\n }\n log.info('💡 Please close the DevTools browser window to finish...')\n await this.#waitForDevtoolsBrowserClose()\n } catch (err) {\n log.error(`Failed to stop backend: ${errorMessage(err)}`)\n }\n }\n\n async #finalizeAllSuites(browser?: NightwatchBrowser): Promise<void> {\n await finalizeAllSuites(this.#getInternals(), browser)\n }\n\n #logRunSummary(): void {\n logRunSummary(this.#getInternals())\n }\n\n async #flushSpecTrace(\n range: SpecRange,\n nextRange?: SpecRange\n ): Promise<string | undefined> {\n if (this.#flushedSpecs.has(range.specFile)) {\n return undefined\n }\n this.#flushedSpecs.add(range.specFile)\n\n const sessionId = this.sessionCapturer.metadata?.sessionId\n if (!sessionId) {\n return undefined\n }\n\n // Collect test metadata from the suite tree. Nightwatch stores one suite\n // per spec file (flat data model) — a flat iteration is correct. If\n // child suites are ever introduced, this must switch to a recursive walk\n // matching the selenium adapter.\n const allMetadata: TestMetadataMap = new Map()\n if (this.suiteManager) {\n for (const suite of this.suiteManager.getAllSuites().values()) {\n for (const entry of suite.tests) {\n if (typeof entry === 'string') {\n continue\n }\n allMetadata.set(entry.uid, {\n title: entry.fullTitle,\n specFile: entry.file\n })\n }\n }\n }\n\n const tracePath = await writeSpecTrace({\n range,\n nextRange,\n capturer: this.sessionCapturer,\n actionSnapshots: this.sessionCapturer.actionSnapshots,\n sessionId,\n outputDir: resolveAdapterOutputDir({ configPath: this.#configPath }),\n format: this.options.traceFormat,\n testMetadata: allMetadata\n })\n log.info(`Trace for spec \"${range.specFile}\" saved to ${tracePath}`)\n return tracePath\n }\n\n async #writeTraceZipIfNeeded(): Promise<void> {\n if (this.options.mode !== 'trace' || !this.sessionCapturer) {\n return\n }\n const sessionId = this.sessionCapturer.metadata?.sessionId\n if (!sessionId) {\n return\n }\n try {\n if (this.sessionCapturer.snapshotCaptures.length) {\n await Promise.allSettled(this.sessionCapturer.snapshotCaptures)\n }\n\n if (this.options.traceGranularity === 'spec') {\n // Per-spec traces — flush any remaining ranges that weren't\n // flushed at spec boundaries (the last spec in the run).\n for (const range of this.#specRanges) {\n if (!this.#flushedSpecs.has(range.specFile)) {\n await this.#flushSpecTrace(range)\n }\n }\n return\n }\n\n // Session-level trace (default) — single artifact for the\n // entire worker session.\n const testMetadata: TestMetadataMap = new Map()\n if (this.suiteManager) {\n for (const suite of this.suiteManager.getAllSuites().values()) {\n for (const entry of suite.tests) {\n if (typeof entry === 'string') {\n continue\n }\n testMetadata.set(entry.uid, {\n title: entry.fullTitle,\n specFile: entry.file\n })\n }\n }\n }\n\n const snapshots = this.sessionCapturer.actionSnapshots\n const tracePath = await writeTraceZip(this.sessionCapturer, {\n outputDir: resolveAdapterOutputDir({\n configPath: this.#configPath\n }),\n sessionId,\n actionSnapshots: snapshots.length ? snapshots : undefined,\n format: this.options.traceFormat,\n testMetadata\n })\n log.info(`Trace saved to ${tracePath}`)\n } catch (err) {\n log.warn(`trace write failed: ${errorMessage(err)}`)\n }\n }\n\n async #waitForDevtoolsBrowserClose(): Promise<void> {\n await waitForDevtoolsBrowserClose(this.#getInternals())\n }\n\n #buildMetadataOptions() {\n return buildPluginMetadataOptions({\n isCucumberRunner: this.#isCucumberRunner,\n configPath: this.#configPath\n })\n }\n\n #incrementCount(state: TestStats['state']): void {\n const counters = {\n passCount: this.#passCount,\n failCount: this.#failCount,\n skipCount: this.#skipCount\n }\n incrementCounters(counters, state)\n this.#passCount = counters.passCount\n this.#failCount = counters.failCount\n this.#skipCount = counters.skipCount\n }\n\n #testIcon(state: TestStats['state']): string {\n return getTestIcon(state)\n }\n\n registerEventHandlers(eventHub: NightwatchEventHub): void {\n registerEventHandlersImpl(eventHub, {\n getSessionCapturer: () => this.sessionCapturer,\n buildMetadataOptions: () => this.#buildMetadataOptions(),\n setCucumberRunner: (v: boolean) => {\n this.#isCucumberRunner = v\n }\n })\n }\n}\n\nexport const cucumberHooksPath = fileURLToPath(\n new URL('./helpers/cucumberHooks.cjs', import.meta.url)\n)\n\nexport default function createNightwatchDevTools(options?: DevToolsOptions) {\n const plugin = new NightwatchDevToolsPlugin(options)\n\n return {\n // Set long timeout to allow user to review DevTools UI\n // The after() hook waits for the browser window to be closed\n asyncHookTimeout: 3600000,\n\n before: async function (this: unknown) {\n await plugin.before()\n },\n beforeEach: async function (this: unknown, browser: NightwatchBrowser) {\n await plugin.beforeEach(browser)\n },\n afterEach: async function (this: unknown, browser: NightwatchBrowser) {\n await plugin.afterEach(browser)\n },\n after: async function (this: unknown) {\n await plugin.after()\n },\n\n registerEventHandlers: function (eventHub: NightwatchEventHub) {\n plugin.registerEventHandlers(eventHub)\n }\n }\n}\n\nexport { NightwatchDevToolsPlugin }\n","// File-pattern regexes shared across packages — keep one canonical form\n// per concept so changes to \"what counts as a test file\" or \"what counts\n// as a Cucumber feature file\" propagate everywhere. See CLAUDE.md §2.1.\n\n/** Matches `*.test.ts`, `*.spec.ts`, `*.test.cjs`, etc. — the test-runner\n * convention used by every adapter to recognize spec files. */\nexport const SPEC_FILE_RE = /\\.(?:test|spec)\\.[cm]?[jt]sx?$/i\n\n/** Matches `*.feature` — Cucumber feature files. */\nexport const FEATURE_FILE_RE = /\\.feature$/i\n","/**\n * WebSocket upgrade paths on the backend's Fastify server. Each adapter opens\n * one socket at `worker`; one or more browser tabs subscribe at `client`.\n *\n * The HTTP API endpoints under `/api/baseline/*` live in `./baseline.ts`.\n */\nexport const WS_PATHS = {\n /** Adapter session upgrade endpoint. One socket per running adapter. */\n worker: '/worker',\n /** App/UI client upgrade endpoint. Multiple browser tabs may connect. */\n client: '/client'\n} as const\n\n/**\n * Control-frame scopes exchanged over the worker↔backend↔client WS channels.\n * `BASELINE_WS_SCOPE` (in `./baseline.ts`) covers the baseline-specific\n * scopes; this object covers the runtime control frames. Single source of\n * truth — typos previously caused silent breakage when one end of the wire\n * sent a string the other end didn't recognize.\n */\nexport const WS_SCOPE = {\n /** Backend → worker: a dashboard client has subscribed. Wakes up the\n * adapter's `await UI ready` gate before tests start. */\n clientConnected: 'clientConnected',\n /** Backend → worker: the last dashboard tab closed. Triggers the\n * interactive shutdown flow (close WS, exit, pkill the dashboard). */\n clientDisconnected: 'clientDisconnected',\n /** Worker → backend → clients (or app-local): wipe the visualization data\n * for a specific test/suite uid (or the whole tree). */\n clearExecutionData: 'clearExecutionData',\n /** Worker → backend: clear-by-test-uid request (drives clearExecutionData). */\n clearCommands: 'clearCommands',\n /** Backend → clients: signal that the active run was stopped by the UI. */\n testStopped: 'testStopped',\n /** Worker → backend → clients: swap an earlier captured command for an\n * updated entry (used when a retry coalesces commands). */\n replaceCommand: 'replaceCommand',\n /** Worker → backend: register the config file path so reruns can spawn\n * with the same config. */\n config: 'config'\n} as const\n\nexport type WsScope = (typeof WS_SCOPE)[keyof typeof WS_SCOPE]\n","/**\n * HTTP contracts for the runner endpoints. Imported by the backend route\n * handlers and the app's fetch callers — keeps the body shape in lockstep\n * across the wire instead of relying on `Record<string, unknown>`.\n */\n\nexport const TESTS_API = {\n run: '/api/tests/run',\n stop: '/api/tests/stop'\n} as const\n\n/**\n * Environment variables the backend's rerun spawner sets on the child\n * process so the adapter (service/nightwatch/selenium) can detect the\n * reuse-mode handshake and connect to the existing dashboard backend\n * instead of starting a new one. Single source of truth — typos in any\n * leg of the handshake silently break reruns, so all four packages\n * (backend writer + three adapter readers) reference this object.\n */\nexport const REUSE_ENV = {\n REUSE: 'DEVTOOLS_APP_REUSE',\n HOST: 'DEVTOOLS_APP_HOST',\n PORT: 'DEVTOOLS_APP_PORT',\n RERUN_LABEL: 'DEVTOOLS_RERUN_LABEL',\n RERUN_ENTRY_TYPE: 'DEVTOOLS_RERUN_ENTRY_TYPE'\n} as const\n\n/**\n * Environment variables the WDIO service writes during `onPrepare` (config\n * path it detected, initial --spec args) so the backend's rerun spawner can\n * relaunch with the same config. Also covers DEVTOOLS_RUNNER_CWD which the\n * backend reads to know which directory to spawn the child in. Bin-override\n * vars (DEVTOOLS_WDIO_BIN, DEVTOOLS_NIGHTWATCH_BIN) live here too — they're\n * test-rig overrides that backend's bin-resolver respects.\n */\nexport const RUNNER_ENV = {\n WDIO_CONFIG: 'DEVTOOLS_WDIO_CONFIG',\n NIGHTWATCH_CONFIG: 'DEVTOOLS_NIGHTWATCH_CONFIG',\n WDIO_INITIAL_SPECS: 'DEVTOOLS_WDIO_INITIAL_SPECS',\n RUNNER_CWD: 'DEVTOOLS_RUNNER_CWD',\n WDIO_BIN: 'DEVTOOLS_WDIO_BIN',\n NIGHTWATCH_BIN: 'DEVTOOLS_NIGHTWATCH_BIN'\n} as const\n\n/** POST /api/tests/run body. */\nexport interface RunnerRequestBody {\n uid: string\n entryType: 'suite' | 'test'\n specFile?: string\n fullTitle?: string\n label?: string\n callSource?: string\n runAll?: boolean\n framework?: string\n configFile?: string\n lineNumber?: number\n devtoolsHost?: string\n devtoolsPort?: number\n featureFile?: string\n featureLine?: number\n suiteType?: string\n rerunCommand?: string\n launchCommand?: string\n preserveBaseline?: boolean\n}\n\n/** 200 response from /api/tests/run and /api/tests/stop. */\nexport interface RunnerOkResponse {\n ok: true\n}\n\n/** 4xx response shape from runner endpoints. */\nexport interface RunnerErrorResponse {\n error: string\n}\n","/**\n * Cross-adapter timing + identifier defaults.\n *\n * The TIMING values are wall-clock milliseconds the adapter's plugin loop\n * waits for (UI render, test/suite boundaries, browser close, WS connection\n * setup). The DEFAULTS provide the canonical TestStats / SuiteStats field\n * defaults so every adapter produces the same shape on the wire.\n *\n * Adapters spread these into their local TIMING / DEFAULTS objects so they\n * can override per-framework values (Nightwatch's UI_CONNECTION_WAIT is\n * higher because its boot is slower; each adapter has its own SESSION_TITLE\n * / TEST_NAME placeholder).\n */\n\nexport const TIMING_BASE = {\n /** ms to let the dashboard render between rapid state updates. */\n UI_RENDER_DELAY: 150,\n /** ms to wait after a test starts before flushing initial state. */\n TEST_START_DELAY: 100,\n /** ms gap between consecutive suite-finalize broadcasts. */\n SUITE_COMPLETE_DELAY: 200,\n /** ms allowed for the browser-close handshake before forcing teardown. */\n BROWSER_CLOSE_WAIT: 2000,\n /** ms before first worker WS connection attempt — lets backend bind. */\n INITIAL_CONNECTION_WAIT: 500,\n /** ms between browser-window-alive polls. */\n BROWSER_POLL_INTERVAL: 1000\n} as const\n\nexport const DEFAULTS_BASE = {\n /** Synthetic capability ID — present on every CommandLog / TestStats. */\n CID: '0-0',\n /** TestStats.retries default — adapters that surface retry counts override. */\n RETRIES: 0,\n /** _duration placeholder for not-yet-finalized stats. */\n DURATION: 0\n} as const\n","// The trace action vocabulary: maps runner-native command names to the trace\n// class/method pair. Single source of truth for both the exporter (core, forward\n// lookup) and the reader (backend, reverse lookup) so the two cannot drift.\n\nexport interface TraceAction {\n class: string\n method: string\n}\n\nexport const ACTION_MAP: Record<string, TraceAction> = {\n // WDIO browser-level\n url: { class: 'Page', method: 'navigate' },\n navigateTo: { class: 'Page', method: 'navigate' },\n back: { class: 'Page', method: 'goBack' },\n forward: { class: 'Page', method: 'goForward' },\n refresh: { class: 'Page', method: 'reload' },\n newWindow: { class: 'Page', method: 'goto' },\n // Selenium WebDriver navigation (driver.get, driver.navigate().to/back/forward/refresh)\n get: { class: 'Page', method: 'navigate' },\n to: { class: 'Page', method: 'navigate' },\n // WDIO element-level\n click: { class: 'Element', method: 'click' },\n doubleClick: { class: 'Element', method: 'dblclick' },\n setValue: { class: 'Element', method: 'fill' },\n selectByVisibleText: { class: 'Element', method: 'selectOption' },\n moveTo: { class: 'Element', method: 'hover' },\n scrollIntoView: { class: 'Element', method: 'scrollIntoViewIfNeeded' },\n dragAndDrop: { class: 'Element', method: 'dragTo' },\n // Selenium WebElement actions\n sendKeys: { class: 'Element', method: 'fill' },\n clear: { class: 'Element', method: 'clear' },\n submit: { class: 'Element', method: 'submit' },\n // Cross-runner\n keys: { class: 'Keyboard', method: 'press' },\n execute: { class: 'Page', method: 'evaluate' },\n executeAsync: { class: 'Page', method: 'evaluate' },\n switchToFrame: { class: 'Frame', method: 'goto' },\n touchAction: { class: 'Element', method: 'tap' }\n}\n","// Canonical type definitions shared across @wdio/devtools-* packages.\n//\n// Adapters (service, nightwatch-devtools, selenium-devtools) produce events of\n// these shapes. The backend stores and forwards them. The app consumes them.\n// See ARCHITECTURE.md §2 and CLAUDE.md §2.1.\n\nexport type LogLevel = 'trace' | 'debug' | 'log' | 'info' | 'warn' | 'error'\n\n/** Where a captured ConsoleLog entry originated. */\nexport type LogSource = 'browser' | 'test' | 'terminal'\n\nexport enum TraceType {\n Standalone = 'standalone',\n Testrunner = 'testrunner'\n}\n\nexport type TestStatus = 'passed' | 'failed' | 'skipped' | 'pending' | 'running'\n\n/** `live` opens the DevTools UI window; `trace` skips it and lets a downstream exporter consume captured state. */\nexport type DevToolsMode = 'live' | 'trace'\n\n/** `zip` (default) writes a single `trace-<id>.zip`; `ndjson-directory` writes\n * the same `trace.trace` + `trace.network` + `resources/` layout unpacked\n * into `trace-<id>/`. Both are consumable by `playwright show-trace` — the\n * unpacked form skips the unzip step for agentic / scripted consumers. */\nexport type TraceFormat = 'zip' | 'ndjson-directory'\n\n/** `session` (default) writes one trace per worker session; `spec` writes one\n * trace per spec file, keyed on the spec's filename. Only applies in trace mode. */\nexport type TraceGranularity = 'session' | 'spec'\n\n/** Test metadata keyed by testUid — maps stable test IDs to human-readable\n * title + specFile for Tracing.tracingGroup events in trace output. */\nexport type TestMetadataMap = Map<string, { title: string; specFile: string }>\n\n/**\n * Enum-style accessor for the canonical TestStatus values. Adapter code uses\n * this for readable comparisons (`state === TEST_STATE.PASSED`). The app's\n * sidebar has a parallel `TestState` accessor with the same values; that's a\n * naming holdover (PascalCase enum-style) — both can coexist.\n */\nexport const TEST_STATE = {\n PENDING: 'pending',\n RUNNING: 'running',\n PASSED: 'passed',\n FAILED: 'failed',\n SKIPPED: 'skipped'\n} as const satisfies Record<string, TestStatus>\n\n/**\n * Identifier sent by each adapter on RunnerRequestBody.framework. Used by the\n * backend's runner to pick rerun CLI args. This is technically the *test\n * runner* identifier rather than the higher-level framework (wdio/nightwatch/\n * selenium) — wdio's runner can be mocha/jasmine/cucumber, nightwatch can be\n * vanilla or cucumber, selenium adapters report 'selenium-webdriver'.\n */\nexport type TestRunnerId =\n | 'mocha'\n | 'jasmine'\n | 'cucumber'\n | 'nightwatch'\n | 'nightwatch-cucumber'\n | 'selenium-webdriver'\n\n// ─── Inner event payloads ───────────────────────────────────────────────────\n\nexport interface PerformanceData {\n navigation?: {\n url: string\n timing: {\n loadTime?: number\n domReady?: number\n responseTime?: number\n dnsLookup?: number\n tcpConnection?: number\n serverResponse?: number\n }\n }\n resources?: Array<{\n url: string\n duration: number\n size: number\n type: string\n startTime: number\n responseEnd: number\n }>\n}\n\nexport interface DocumentInfo {\n url: string\n title: string\n headers: { userAgent: string; language: string; platform: string }\n documentInfo: { readyState: string; referrer: string; characterSet: string }\n}\n\nexport interface CommandLog {\n command: string\n args: unknown[]\n /** Optional display label (e.g. trace-player's `Element.fill(\"x\")`). Falls\n * back to `command` when absent. */\n title?: string\n result?: unknown\n error?: Error | SerializedError\n timestamp: number\n /** Wall-clock ms when the command was invoked (before execution). */\n startTime?: number\n callSource?: string\n screenshot?: string\n testUid?: string\n performance?: PerformanceData\n cookies?: string\n documentInfo?: DocumentInfo\n id?: number\n}\n\n/**\n * Payload broadcast under the WS scope `'replaceCommand'`. Tells the UI to\n * swap an existing CommandLog in-place — used when an adapter reconciles a\n * preliminary entry with the actual final result (e.g. selenium's\n * driverPatcher emits a placeholder, then replaces it once the command\n * resolves).\n */\nexport interface ReplaceCommandWsPayload {\n oldTimestamp: number\n command: CommandLog\n}\n\nexport interface ConsoleLog {\n type: LogLevel\n args: unknown[]\n timestamp: number\n source?: LogSource\n}\n\nexport interface NetworkRequest {\n id: string\n url: string\n method: string\n headers?: Record<string, string>\n cookies?: unknown[]\n status?: number\n statusText?: string\n timestamp: number\n startTime: number\n endTime?: number\n time?: number\n type: string\n initiator?: string\n requestHeaders?: Record<string, string>\n responseHeaders?: Record<string, string>\n navigation?: string\n redirectChain?: unknown[]\n children?: NetworkRequest[]\n response?: {\n fromCache: boolean\n headers: Record<string, string>\n mimeType: string\n status: number\n }\n error?: string\n requestBody?: string\n responseBody?: string\n size?: number\n}\n\n// ─── Trace and metadata ─────────────────────────────────────────────────────\n\nexport interface Viewport {\n width: number\n height: number\n offsetLeft: number\n offsetTop: number\n scale: number\n}\n\nexport interface ScreencastInfo {\n sessionId?: string\n videoPath?: string\n videoFile?: string\n frameCount?: number\n duration?: number\n startTime?: number\n}\n\n/** Single captured screencast frame — base64 image + capture timestamp (ms). */\nexport interface ScreencastFrame {\n /** Base64-encoded image data — JPEG/PNG from CDP push mode or PNG from browser.takeScreenshot() in polling mode. */\n data: string\n /** Unix timestamp in milliseconds. */\n timestamp: number\n}\n\n/**\n * Screencast recorder configuration. Used by every adapter — the base recorder\n * in `@wdio/devtools-core` consumes this shape; per-adapter wrappers extend it\n * (e.g. WDIO's CDP fast-path opts).\n */\nexport interface ScreencastOptions {\n /** Enable screencast recording for this session (default: false). */\n enabled?: boolean\n /**\n * Image format for individual frames (default: 'jpeg').\n * - Chrome/Chromium (CDP mode): controls the format Chrome sends over CDP.\n * - Other browsers (polling mode): screenshots are always PNG; ignored.\n * Does NOT affect the output video container, which is always WebM.\n */\n captureFormat?: 'jpeg' | 'png'\n /** JPEG quality 0–100 (default: 70). CDP mode + 'jpeg' only. */\n quality?: number\n /** Max frame width in pixels Chrome sends over CDP (default: 1280). */\n maxWidth?: number\n /** Max frame height in pixels Chrome sends over CDP (default: 720). */\n maxHeight?: number\n /**\n * Screenshot polling interval in milliseconds for non-Chrome browsers\n * (default: 200 ms ≈ 5 fps). Lower = smoother, more WebDriver round-trips.\n */\n pollIntervalMs?: number\n}\n\n/** Defaults applied to ScreencastOptions when not specified by the user. */\nexport const SCREENCAST_DEFAULTS: Required<ScreencastOptions> = {\n enabled: false,\n captureFormat: 'jpeg',\n quality: 70,\n maxWidth: 1280,\n maxHeight: 720,\n pollIntervalMs: 200\n}\n\nexport interface Metadata {\n type: TraceType\n url?: string\n options?: unknown\n capabilities?: unknown\n viewport?: Viewport\n sessionId?: string\n testEnv?: string\n host?: string\n modulePath?: string\n desiredCapabilities?: Record<string, unknown>\n}\n\n/** Captured metadata keyed by browser `sessionId` — lets the UI keep each\n * session's metadata instead of overwriting on a new session. Record (not Map)\n * so it stays JSON-serializable across the WS boundary and in contexts. */\nexport type MetadataBySession = Record<string, Metadata>\n\n/**\n * Node-safe shape of a captured DOM mutation. The browser-side script\n * (packages/script) extends this with the real `MutationRecordType` union\n * via the global `TraceMutation` declaration there; this Node version uses\n * a plain string literal type so the shape can flow through shared without\n * dragging the DOM lib into shared's compilation.\n *\n * `addedNodes` / `removedNodes` are opaque payloads here — the browser side\n * stringifies / serializes them via SimplifiedVNode.\n */\nexport interface TraceMutation {\n type: 'attributes' | 'characterData' | 'childList'\n attributeName?: string\n attributeNamespace?: string\n attributeValue?: string\n newTextContent?: string\n oldValue?: string\n addedNodes: unknown[]\n target?: string\n removedNodes: string[]\n previousSibling?: string\n nextSibling?: string\n timestamp: number\n url?: string\n}\n\n/**\n * Captured at each user-facing action boundary in `trace` mode. Feeds the\n * downstream trace.zip exporter (Phase 4). `screenshot` is base64-encoded JPEG.\n */\nexport interface ActionSnapshot {\n timestamp: number\n command: string\n url?: string\n title?: string\n screenshot?: string\n elements?: unknown[]\n snapshotText?: string\n}\n\nexport interface TraceLog {\n mutations: TraceMutation[]\n logs: string[]\n consoleLogs: ConsoleLog[]\n networkRequests: NetworkRequest[]\n metadata: Metadata\n commands: CommandLog[]\n sources: Record<string, string>\n suites?: Record<string, unknown>[]\n screencast?: ScreencastInfo\n config?: { configFile?: string }\n /** Per-action snapshots captured in `mode: 'trace'` for the trace.zip exporter. */\n actionSnapshots?: ActionSnapshot[]\n}\n\n// ─── Preserve-and-rerun ─────────────────────────────────────────────────────\n\nexport interface TestError {\n message?: string\n name?: string\n stack?: string\n /** expect-webdriverio surfaces these directly on the error. */\n expected?: unknown\n actual?: unknown\n /** expect-webdriverio also bundles them under matcherResult. */\n matcherResult?: {\n expected?: unknown\n actual?: unknown\n message?: string\n }\n}\n\nexport interface PreservedStep {\n uid: string\n title?: string\n fullTitle?: string\n start?: number\n end?: number\n state?: TestStatus\n error?: TestError\n}\n\nexport interface PreservedAttempt {\n testUid: string\n scope: 'test' | 'suite'\n capturedAt: number\n window: { start: number; end: number }\n test: {\n title?: string\n fullTitle?: string\n file?: string\n callSource?: string\n start?: number\n end?: number\n duration?: number\n state?: TestStatus\n error?: TestError\n }\n steps?: PreservedStep[]\n commands: CommandLog[]\n consoleLogs: ConsoleLog[]\n networkRequests: NetworkRequest[]\n /** See note on TraceLog.mutations. */\n mutations: unknown[]\n sources: Record<string, string>\n}\n\n// ─── Test reporter stats (nightwatch + selenium adapters) ───────────────────\n\n/**\n * Serialized form of an `Error` — a plain object that survives\n * `JSON.stringify` over the WS bridge.\n */\nexport interface SerializedError {\n name: string\n message: string\n stack?: string\n}\n\n/**\n * An error payload as it flows through capture: the raw `Error` instance at\n * capture time, or its serialized form once it has crossed the WS bridge.\n */\nexport type ReporterError = Error | SerializedError\n\nexport interface TestStats {\n uid: string\n cid: string\n title: string\n fullTitle: string\n parent: string\n state: TestStatus\n start: Date\n end: Date | null\n type: 'test'\n file: string\n retries: number\n _duration: number\n error?: ReporterError\n hooks?: unknown[]\n callSource?: string\n}\n\nexport interface SuiteStats {\n uid: string\n cid: string\n title: string\n fullTitle: string\n type: 'suite'\n file: string\n start: Date\n state?: TestStatus\n end?: Date | null\n tests: (string | TestStats)[]\n suites: SuiteStats[]\n hooks: unknown[]\n _duration: number\n parent?: string\n callSource?: string\n /** Cucumber-only: the .feature file path. Distinct from `file` because the\n * root suite's `file` stays at cwd to keep its stable UID; rerun payloads\n * use this to drive feature-level filtering. */\n featureFile?: string\n}\n","// Helpers over the trace action vocabulary. ACTION_MAP itself lives in\n// @wdio/devtools-shared so the reader (backend) can derive its inverse from the\n// same source. Ported from Vince Graics' PR #209 (`@wdio/tracing-service`); the\n// existing devtools UI uses its own denylist (`INTERNAL_COMMANDS`) — this map\n// is for the trace.zip exporter to filter + rename in one step.\n\nimport { ACTION_MAP, type TraceAction } from '@wdio/devtools-shared'\n\nexport type { TraceAction }\n\n// Excluded by design:\n// clearValue / addValue — WDIO fires these inside setValue (duplicate events).\n// executeScript — Selenium's `until` polling fires it ~50ms; also recurses\n// because @wdio/elements uses executeScript inside captureActionSnapshot.\n// WDIO's user-facing `execute`/`executeAsync` are still captured.\n\nexport function mapCommandToAction(command: string): TraceAction | null {\n return ACTION_MAP[command] ?? null\n}\n\nexport function formatActionTitle(\n action: TraceAction,\n args: unknown[],\n params?: Record<string, unknown>\n): string {\n const firstArg = args[0] ?? params?.selector\n if (firstArg === undefined) {\n return `${action.class}.${action.method}()`\n }\n const label =\n typeof firstArg === 'object' ? JSON.stringify(firstArg) : String(firstArg)\n return `${action.class}.${action.method}(\"${label}\")`\n}\n\n/**\n * Methods where the first positional argument should render as value= in the\n * transcript line (e.g. setValue, selectByVisibleText).\n */\nexport const FILL_METHODS = new Set(['fill', 'selectOption'])\n","/**\n * Browser-injectable script strings for element extraction.\n *\n * Each function returns a self-contained JavaScript string designed to run\n * inside a browser page via `browser.execute(script)`. The scripts have no\n * external dependencies and must be ES5-compatible.\n *\n * WDIO-dependent wrappers that call `browser.execute(script)` live in\n * `@wdio/elements` — these are just the script bodies.\n */\n\n/**\n * Accessibility tree walk — returns a flat array of AccessibilityNode.\n *\n * Walks the DOM from `document.body`, assigning semantic roles (button, link,\n * textbox, heading, img, statictext, …) based on tag name, ARIA attributes,\n * and visibility. Each node carries a unique CSS selector.\n */\nexport function accessibilityTreeScript(inViewportOnly: boolean): string {\n return `(function () {\n var INPUT_TYPE_ROLES = {\n text: 'textbox', search: 'searchbox', email: 'textbox', url: 'textbox',\n tel: 'textbox', password: 'textbox', number: 'spinbutton',\n checkbox: 'checkbox', radio: 'radio', range: 'slider',\n submit: 'button', reset: 'button', image: 'button', file: 'button', color: 'button'\n }\n\n var CONTAINER_ROLES = new Set([\n 'navigation', 'banner', 'contentinfo', 'complementary', 'main',\n 'form', 'region', 'group', 'list', 'listitem', 'table', 'row', 'rowgroup', 'generic'\n ])\n\n function getRole(el) {\n var explicit = el.getAttribute('role')\n if (explicit) { return explicit.split(' ')[0] }\n var tag = el.tagName.toLowerCase()\n switch (tag) {\n case 'button': return 'button'\n case 'a': return el.hasAttribute('href') ? 'link' : null\n case 'input': {\n var type = (el.getAttribute('type') || 'text').toLowerCase()\n if (type === 'hidden') { return null }\n return INPUT_TYPE_ROLES[type] || 'textbox'\n }\n case 'select': return 'combobox'\n case 'textarea': return 'textbox'\n case 'h1': case 'h2': case 'h3': case 'h4': case 'h5': case 'h6': return 'heading'\n case 'img': return 'img'\n case 'nav': return 'navigation'\n case 'main': return 'main'\n case 'header': return !el.closest('article,aside,main,nav,section') ? 'banner' : null\n case 'footer': return !el.closest('article,aside,main,nav,section') ? 'contentinfo' : null\n case 'aside': return 'complementary'\n case 'dialog': return 'dialog'\n case 'form': return 'form'\n case 'section': return el.hasAttribute('aria-label') || el.hasAttribute('aria-labelledby') ? 'region' : null\n case 'summary': return 'button'\n case 'details': return 'group'\n case 'progress': return 'progressbar'\n case 'meter': return 'meter'\n case 'ul': case 'ol': return 'list'\n case 'li': return 'listitem'\n case 'table': return 'table'\n }\n if (el.contentEditable === 'true') { return 'textbox' }\n if (el.hasAttribute('tabindex') && parseInt(el.getAttribute('tabindex') || '-1', 10) >= 0) { return 'generic' }\n if (getDirectText(el)) { return 'statictext' }\n return null\n }\n\n function getAccessibleName(el, role) {\n var ariaLabel = el.getAttribute('aria-label')\n if (ariaLabel) { return ariaLabel.trim() }\n var labelledBy = el.getAttribute('aria-labelledby')\n if (labelledBy) {\n var texts = labelledBy.split(/\\\\s+/).map(function(id) { return (document.getElementById(id)?.textContent || '').trim() }).filter(Boolean)\n if (texts.length > 0) { return texts.join(' ').slice(0, 200) }\n }\n var tag = el.tagName.toLowerCase()\n if (tag === 'img' || (tag === 'input' && el.getAttribute('type') === 'image')) {\n var alt = el.getAttribute('alt')\n if (alt !== null) { return alt.trim() }\n }\n if (['input', 'select', 'textarea'].indexOf(tag) !== -1) {\n var id = el.getAttribute('id')\n if (id) {\n var label = document.querySelector('label[for=\"' + CSS.escape(id) + '\"]')\n if (label) { return (label.textContent || '').trim() }\n }\n var parentLabel = el.closest('label')\n if (parentLabel) {\n var clone = parentLabel.cloneNode(true)\n clone.querySelectorAll('input,select,textarea').forEach(function(n) { n.remove() })\n var lt = (clone.textContent || '').trim()\n if (lt) { return lt }\n }\n }\n var ph = el.getAttribute('placeholder')\n if (ph) { return ph.trim() }\n var title = el.getAttribute('title')\n if (title) { return title.trim() }\n var childImg = el.querySelector('img')\n if (childImg) {\n var imgAlt = childImg.getAttribute('alt')\n if (imgAlt) { return imgAlt.trim() }\n }\n if (role && CONTAINER_ROLES.has(role)) { return '' }\n return ((el.textContent || '').trim().replace(/\\\\s+/g, ' ') || '').slice(0, 200)\n }\n\n function getSelector(element) {\n var tag = element.tagName.toLowerCase()\n var text = (element.textContent || '').trim().replace(/\\\\s+/g, ' ')\n if (text && text.length > 0 && text.length <= 120) {\n var sameTagElements = document.querySelectorAll(tag)\n var matchCount = 0\n sameTagElements.forEach(function(el) { if (el.textContent.includes(text)) { matchCount++ } })\n if (matchCount === 1) { return tag + '*=' + text }\n }\n var ariaLabel = element.getAttribute('aria-label')\n if (ariaLabel && ariaLabel.length <= 200) {\n var sel = '[aria-label=\"' + CSS.escape(ariaLabel) + '\"]'\n if (document.querySelectorAll(sel).length === 1) { return sel }\n }\n var testId = element.getAttribute('data-testid')\n if (testId) {\n var testSel = '[data-testid=\"' + CSS.escape(testId) + '\"]'\n if (document.querySelectorAll(testSel).length === 1) { return testSel }\n }\n if (element.id) { return '#' + CSS.escape(element.id) }\n var nameAttr = element.getAttribute('name')\n if (nameAttr) {\n var nameSel = tag + '[name=\"' + CSS.escape(nameAttr) + '\"]'\n if (document.querySelectorAll(nameSel).length === 1) { return nameSel }\n }\n if (element.className && typeof element.className === 'string') {\n var classes = element.className.trim().split(/\\\\s+/).filter(Boolean)\n for (var i = 0; i < classes.length; i++) {\n var clsSel = tag + '.' + CSS.escape(classes[i])\n if (document.querySelectorAll(clsSel).length === 1) { return clsSel }\n }\n if (classes.length >= 2) {\n var twoClsSel = tag + classes.slice(0, 2).map(function(c) { return '.' + CSS.escape(c) }).join('')\n if (document.querySelectorAll(twoClsSel).length === 1) { return twoClsSel }\n }\n }\n var current = element\n var path = []\n while (current && current !== document.documentElement) {\n var seg = current.tagName.toLowerCase()\n if (current.id) { path.unshift('#' + CSS.escape(current.id)); break }\n var parent = current.parentElement\n if (parent) {\n var siblings = Array.from(parent.children).filter(function(c) { return c.tagName === current.tagName })\n if (siblings.length > 1) { seg += ':nth-of-type(' + (siblings.indexOf(current) + 1) + ')' }\n }\n path.unshift(seg)\n current = current.parentElement\n if (path.length >= 4) { break }\n }\n return path.join(' > ')\n }\n\n function getDirectText(el) {\n var text = ''\n for (var i = 0; i < el.childNodes.length; i++) {\n if (el.childNodes[i].nodeType === 3) { text += el.childNodes[i].textContent }\n }\n return text.trim().replace(/\\\\s+/g, ' ')\n }\n\n function isVisible(el) {\n if (typeof el.checkVisibility === 'function') {\n return el.checkVisibility({ opacityProperty: true, visibilityProperty: true, contentVisibilityAuto: true })\n }\n var style = window.getComputedStyle(el)\n return style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0' && el.offsetWidth > 0 && el.offsetHeight > 0\n }\n\n function isInViewport(el) {\n var rect = el.getBoundingClientRect()\n return rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth)\n }\n\n function getLevel(el) {\n var m = el.tagName.toLowerCase().match(/^h([1-6])$/)\n if (m) { return parseInt(m[1], 10) }\n var ariaLevel = el.getAttribute('aria-level')\n if (ariaLevel) { return parseInt(ariaLevel, 10) }\n return undefined\n }\n\n function getState(el) {\n var inputEl = el\n var isCheckable = ['input', 'menuitemcheckbox', 'menuitemradio'].indexOf(el.tagName.toLowerCase()) !== -1 || ['checkbox', 'radio', 'switch'].indexOf(el.getAttribute('role') || '') !== -1\n return {\n disabled: el.getAttribute('aria-disabled') === 'true' || inputEl.disabled ? 'true' : '',\n checked: isCheckable && inputEl.checked ? 'true' : el.getAttribute('aria-checked') || '',\n expanded: el.getAttribute('aria-expanded') || '',\n selected: el.getAttribute('aria-selected') || '',\n pressed: el.getAttribute('aria-pressed') || '',\n required: inputEl.required || el.getAttribute('aria-required') === 'true' ? 'true' : '',\n readonly: inputEl.readOnly || el.getAttribute('aria-readonly') === 'true' ? 'true' : ''\n }\n }\n\n var result = []\n\n function walk(el, depth) {\n if (depth > 200) { return }\n if (!isVisible(el)) { return }\n var role = getRole(el)\n var inViewport = isInViewport(el)\n if (!role) {\n for (var i = 0; i < el.children.length; i++) { walk(el.children[i], depth + 1) }\n return\n }\n if (${inViewportOnly} && !inViewport) {\n for (var i = 0; i < el.children.length; i++) { walk(el.children[i], depth + 1) }\n return\n }\n var name = getAccessibleName(el, role)\n var selector = getSelector(el)\n var node = { role: role, name: name, selector: selector, depth: depth, level: getLevel(el) ?? '', isInViewport: inViewport }\n var state = getState(el)\n for (var k in state) { node[k] = state[k] }\n result.push(node)\n for (var i = 0; i < el.children.length; i++) { walk(el.children[i], depth + 1) }\n }\n\n for (var i = 0; i < document.body.children.length; i++) { walk(document.body.children[i], 0) }\n return result\n })()`\n}\n\n/**\n * Interactable element query — returns a flat array of BrowserElementInfo.\n *\n * Uses `querySelectorAll` with a broad interactable-selector list, then\n * filters by visibility and (optionally) viewport containment. Each element\n * gets a computed accessible name and a unique CSS selector.\n */\nexport function elementsScript(\n includeBounds: boolean,\n inViewportOnly: boolean\n): string {\n return `(function () {\n var interactableSelectors = [\n 'a[href]', 'button', 'input:not([type=\"hidden\"])', 'select', 'textarea',\n '[role=\"button\"]', '[role=\"link\"]', '[role=\"checkbox\"]', '[role=\"radio\"]',\n '[role=\"tab\"]', '[role=\"menuitem\"]', '[role=\"combobox\"]', '[role=\"option\"]',\n '[role=\"switch\"]', '[role=\"slider\"]', '[role=\"textbox\"]', '[role=\"searchbox\"]',\n '[role=\"spinbutton\"]', '[contenteditable=\"true\"]', '[tabindex]:not([tabindex=\"-1\"])'\n ].join(',')\n\n function isVisible(element) {\n if (typeof element.checkVisibility === 'function') {\n return element.checkVisibility({ opacityProperty: true, visibilityProperty: true, contentVisibilityAuto: true })\n }\n var style = window.getComputedStyle(element)\n return style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0' && element.offsetWidth > 0 && element.offsetHeight > 0\n }\n\n function getAccessibleName(el) {\n var ariaLabel = el.getAttribute('aria-label')\n if (ariaLabel) { return ariaLabel.trim() }\n var labelledBy = el.getAttribute('aria-labelledby')\n if (labelledBy) {\n var texts = labelledBy.split(/\\\\s+/).map(function(id) { return (document.getElementById(id)?.textContent || '').trim() }).filter(Boolean)\n if (texts.length > 0) { return texts.join(' ').slice(0, 200) }\n }\n var tag = el.tagName.toLowerCase()\n if (tag === 'img' || (tag === 'input' && el.getAttribute('type') === 'image')) {\n var alt = el.getAttribute('alt')\n if (alt !== null) { return alt.trim() }\n }\n if (['input', 'select', 'textarea'].indexOf(tag) !== -1) {\n var id = el.getAttribute('id')\n if (id) {\n var label = document.querySelector('label[for=\"' + CSS.escape(id) + '\"]')\n if (label) { return (label.textContent || '').trim() }\n }\n var parentLabel = el.closest('label')\n if (parentLabel) {\n var clone = parentLabel.cloneNode(true)\n clone.querySelectorAll('input,select,textarea').forEach(function(n) { n.remove() })\n var lt = (clone.textContent || '').trim()\n if (lt) { return lt }\n }\n }\n var ph = el.getAttribute('placeholder')\n if (ph) { return ph.trim() }\n var title = el.getAttribute('title')\n if (title) { return title.trim() }\n return ((el.textContent || '').trim().replace(/\\\\s+/g, ' ') || '').slice(0, 200)\n }\n\n function getSelector(element) {\n var tag = element.tagName.toLowerCase()\n var text = (element.textContent || '').trim().replace(/\\\\s+/g, ' ')\n if (text && text.length > 0 && text.length <= 120) {\n var sameTagElements = document.querySelectorAll(tag)\n var matchCount = 0\n sameTagElements.forEach(function(el) { if (el.textContent.includes(text)) { matchCount++ } })\n if (matchCount === 1) { return tag + '*=' + text }\n }\n var ariaLabel = element.getAttribute('aria-label')\n if (ariaLabel && ariaLabel.length <= 200) {\n var sel = '[aria-label=\"' + CSS.escape(ariaLabel) + '\"]'\n if (document.querySelectorAll(sel).length === 1) { return sel }\n }\n var testId = element.getAttribute('data-testid')\n if (testId) {\n var testSel = '[data-testid=\"' + CSS.escape(testId) + '\"]'\n if (document.querySelectorAll(testSel).length === 1) { return testSel }\n }\n if (element.id) { return '#' + CSS.escape(element.id) }\n var nameAttr = element.getAttribute('name')\n if (nameAttr) {\n var nameSel = tag + '[name=\"' + CSS.escape(nameAttr) + '\"]'\n if (document.querySelectorAll(nameSel).length === 1) { return nameSel }\n }\n if (element.className && typeof element.className === 'string') {\n var classes = element.className.trim().split(/\\\\s+/).filter(Boolean)\n for (var i = 0; i < classes.length; i++) {\n var clsSel = tag + '.' + CSS.escape(classes[i])\n if (document.querySelectorAll(clsSel).length === 1) { return clsSel }\n }\n if (classes.length >= 2) {\n var twoClsSel = tag + classes.slice(0, 2).map(function(c) { return '.' + CSS.escape(c) }).join('')\n if (document.querySelectorAll(twoClsSel).length === 1) { return twoClsSel }\n }\n }\n var current = element\n var path = []\n while (current && current !== document.documentElement) {\n var seg = current.tagName.toLowerCase()\n if (current.id) { path.unshift('#' + CSS.escape(current.id)); break }\n var parent = current.parentElement\n if (parent) {\n var siblings = Array.from(parent.children).filter(function(c) { return c.tagName === current.tagName })\n if (siblings.length > 1) { seg += ':nth-of-type(' + (siblings.indexOf(current) + 1) + ')' }\n }\n path.unshift(seg)\n current = current.parentElement\n if (path.length >= 4) { break }\n }\n return path.join(' > ')\n }\n\n var elements = []\n var seen = new Set()\n\n document.querySelectorAll(interactableSelectors).forEach(function(el) {\n if (seen.has(el)) { return }\n seen.add(el)\n var htmlEl = el\n if (!isVisible(htmlEl)) { return }\n var inputEl = htmlEl\n var rect = htmlEl.getBoundingClientRect()\n var isInVp = rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth)\n if (${inViewportOnly} && !isInVp) { return }\n var entry = {\n tagName: htmlEl.tagName.toLowerCase(),\n name: getAccessibleName(htmlEl),\n type: htmlEl.getAttribute('type') || '',\n value: inputEl.value || '',\n href: htmlEl.getAttribute('href') || '',\n selector: getSelector(htmlEl),\n isInViewport: isInVp\n }\n ${includeBounds ? 'entry.boundingBox = { x: rect.x + window.scrollX, y: rect.y + window.scrollY, width: rect.width, height: rect.height }' : ''}\n elements.push(entry)\n })\n return elements\n })()`\n}\n","/**\n * XML parsing utilities for mobile element source\n */\n\nimport { DOMParser } from '@xmldom/xmldom'\nimport type {\n Document as XMLDocument,\n Element as XMLElement,\n Node as XMLNode\n} from '@xmldom/xmldom'\nimport xpath from 'xpath'\nimport type {\n ElementAttributes,\n JSONElement,\n Bounds,\n UniquenessResult\n} from './types.js'\n\n/**\n * Get child nodes that are elements (not text nodes, comments, etc.)\n */\nfunction childNodesOf(node: XMLNode): XMLNode[] {\n const children: XMLNode[] = []\n if (node.childNodes) {\n for (let i = 0; i < node.childNodes.length; i++) {\n const child = node.childNodes.item(i)\n if (child?.nodeType === 1) {\n children.push(child)\n }\n }\n }\n return children\n}\n\n/**\n * Recursively translate DOM node to JSONElement\n */\nfunction translateRecursively(\n domNode: XMLNode,\n parentPath: string = '',\n index: number | null = null\n): JSONElement {\n const attributes: ElementAttributes = {}\n\n const element = domNode as XMLElement\n if (element.attributes) {\n for (let attrIdx = 0; attrIdx < element.attributes.length; attrIdx++) {\n const attr = element.attributes.item(attrIdx)\n if (attr) {\n attributes[attr.name] = attr.value.replace(/(\\n)/gm, '\\\\n')\n }\n }\n }\n\n const path =\n index === null ? '' : `${parentPath ? parentPath + '.' : ''}${index}`\n\n return {\n children: childNodesOf(domNode).map((childNode, childIndex) =>\n translateRecursively(childNode as XMLNode, path, childIndex)\n ),\n tagName: domNode.nodeName,\n attributes,\n path\n }\n}\n\n/**\n * Compare two nodes for equality by platform-specific attributes\n * (reference equality via === may fail when nodes come from different traversals)\n */\nfunction isSameElement(node1: XMLNode, node2: XMLNode): boolean {\n if (node1.nodeType !== 1 || node2.nodeType !== 1) {\n return false\n }\n const el1 = node1 as XMLElement\n const el2 = node2 as XMLElement\n\n if (el1.nodeName !== el2.nodeName) {\n return false\n }\n\n // For Android, compare by bounds (unique per element)\n const bounds1 = el1.getAttribute('bounds')\n const bounds2 = el2.getAttribute('bounds')\n if (bounds1 && bounds2) {\n return bounds1 === bounds2\n }\n\n // For iOS, compare by x, y, width, height\n const x1 = el1.getAttribute('x')\n const y1 = el1.getAttribute('y')\n const x2 = el2.getAttribute('x')\n const y2 = el2.getAttribute('y')\n if (x1 && y1 && x2 && y2) {\n return (\n x1 === x2 &&\n y1 === y2 &&\n el1.getAttribute('width') === el2.getAttribute('width') &&\n el1.getAttribute('height') === el2.getAttribute('height')\n )\n }\n\n return false\n}\n\n/**\n * Sanitize page source XML that may contain HTML artifacts (WebView content).\n *\n * Handles two classes of invalid XML that xmldom's strict parser rejects:\n * 1. Bare ampersands — `&` not part of a valid XML entity (e.g. ` `,\n * `©`, bare `&` in URLs) → replaced with `&`.\n * 2. HTML void elements without explicit self-closure (`<link>`, `<meta>`,\n * `<br>`, `<img>`, `<input>`, `<hr>`, `<source>`, `<area>`, `<base>`,\n * `<col>`, `<embed>`, `<track>`, `<wbr>`) → closed with `/>`.\n */\nfunction sanitizePageSource(sourceXML: string): string {\n // Pass 1: escape bare ampersands — & not followed by a known XML entity.\n let sanitized = sourceXML.replace(\n /&(?!(?:amp|lt|gt|quot|apos|#\\d+|#x[0-9a-fA-F]+);)/g,\n '&'\n )\n\n // Pass 2: self-close known HTML void elements.\n const VOID_ELEMENTS = [\n 'link',\n 'meta',\n 'br',\n 'hr',\n 'img',\n 'input',\n 'source',\n 'area',\n 'base',\n 'col',\n 'embed',\n 'track',\n 'wbr'\n ]\n\n for (const tag of VOID_ELEMENTS) {\n // <tagname ...> → <tagname .../> (not already self-closed)\n sanitized = sanitized.replace(\n // eslint-disable-next-line security/detect-non-literal-regexp -- tag from hardcoded list\n new RegExp(`<${tag}(\\\\s[^>]*?[^/])>`, 'gi'),\n `<${tag}$1/>`\n )\n // <tagname> → <tagname/> (no attributes)\n sanitized = sanitized.replace(\n // eslint-disable-next-line security/detect-non-literal-regexp -- tag from hardcoded list\n new RegExp(`<${tag}>`, 'gi'),\n `<${tag}/>`\n )\n }\n\n return sanitized\n}\n\n/**\n * Convert XML page source to JSON tree structure\n */\nexport function xmlToJSON(sourceXML: string): JSONElement | null {\n try {\n const sanitized = sanitizePageSource(sourceXML)\n const parser = new DOMParser()\n const sourceDoc = parser.parseFromString(sanitized, 'text/xml')\n\n // xmldom 0.9+ throws ParseError for fatal errors (caught below); this catches non-fatal cases\n const parseErrors = sourceDoc.getElementsByTagName('parsererror')\n if (parseErrors.length > 0) {\n console.error(\n '[xmlToJSON] XML parsing error:',\n parseErrors[0].textContent\n )\n return null\n }\n\n const children = childNodesOf(sourceDoc)\n const firstChild =\n children[0] ||\n (sourceDoc.documentElement\n ? childNodesOf(sourceDoc.documentElement)[0]\n : null)\n\n return firstChild\n ? translateRecursively(firstChild)\n : { children: [], tagName: '', attributes: {}, path: '' }\n } catch (e) {\n console.error('[xmlToJSON] Failed to parse XML:', e)\n return null\n }\n}\n\n/**\n * Parse XML source to DOM Document for XPath evaluation\n */\nexport function xmlToDOM(sourceXML: string): XMLDocument | null {\n try {\n const sanitized = sanitizePageSource(sourceXML)\n const parser = new DOMParser()\n const doc = parser.parseFromString(sanitized, 'text/xml')\n\n // xmldom 0.9+ throws ParseError for fatal errors (caught below); this catches non-fatal cases\n const parseErrors = doc.getElementsByTagName('parsererror')\n if (parseErrors.length > 0) {\n console.error('[xmlToDOM] XML parsing error:', parseErrors[0].textContent)\n return null\n }\n\n return doc\n } catch (e) {\n console.error('[xmlToDOM] Failed to parse XML:', e)\n return null\n }\n}\n\n/**\n * Execute XPath query on DOM document\n */\nexport function evaluateXPath(doc: XMLDocument, xpathExpr: string): XMLNode[] {\n try {\n // @xmldom/xmldom 0.9+ types don't satisfy global Node; xpath still works at runtime\n const nodes = xpath.select(xpathExpr, doc as unknown as Node)\n if (Array.isArray(nodes)) {\n return nodes as unknown as XMLNode[]\n }\n return []\n } catch (e) {\n console.error(`[evaluateXPath] Failed to evaluate \"${xpathExpr}\":`, e)\n return []\n }\n}\n\n/**\n * Check if an XPath selector is unique and get index if not\n */\nexport function checkXPathUniqueness(\n doc: XMLDocument,\n xpathExpr: string,\n targetNode?: XMLNode\n): UniquenessResult {\n try {\n const nodes = evaluateXPath(doc, xpathExpr)\n const totalMatches = nodes.length\n\n if (totalMatches === 0) {\n return { isUnique: false }\n }\n\n if (totalMatches === 1) {\n return { isUnique: true }\n }\n\n // Not unique - find index of target node if provided\n if (targetNode) {\n for (let i = 0; i < nodes.length; i++) {\n if (nodes[i] === targetNode || isSameElement(nodes[i], targetNode)) {\n return {\n isUnique: false,\n index: i + 1, // 1-based index for XPath\n totalMatches\n }\n }\n }\n }\n\n return { isUnique: false, totalMatches }\n } catch (e) {\n console.error(`[checkXPathUniqueness] Error checking \"${xpathExpr}\":`, e)\n return { isUnique: false }\n }\n}\n\n/**\n * Find DOM node by JSONElement path (e.g., \"0.2.1\")\n */\nexport function findDOMNodeByPath(\n doc: XMLDocument,\n path: string\n): XMLNode | null {\n if (!path) {\n return doc.documentElement\n }\n\n const indices = path.split('.').map(Number)\n let current: XMLNode | null = doc.documentElement\n\n for (const index of indices) {\n if (!current) {\n return null\n }\n\n const children: XMLNode[] = []\n if (current.childNodes) {\n for (let i = 0; i < current.childNodes.length; i++) {\n const child = current.childNodes.item(i)\n if (child?.nodeType === 1) {\n children.push(child)\n }\n }\n }\n\n current = children[index] || null\n }\n\n return current\n}\n\n/**\n * Parse Android bounds string \"[x1,y1][x2,y2]\" to coordinates\n */\nexport function parseAndroidBounds(bounds: string): Bounds {\n const match = bounds.match(/\\[(\\d+),(\\d+)\\]\\[(\\d+),(\\d+)\\]/)\n if (!match) {\n return { x: 0, y: 0, width: 0, height: 0 }\n }\n\n const x1 = parseInt(match[1], 10)\n const y1 = parseInt(match[2], 10)\n const x2 = parseInt(match[3], 10)\n const y2 = parseInt(match[4], 10)\n\n return {\n x: x1,\n y: y1,\n width: x2 - x1,\n height: y2 - y1\n }\n}\n\n/**\n * Parse iOS element bounds from individual x, y, width, height attributes\n */\nexport function parseIOSBounds(attributes: ElementAttributes): Bounds {\n return {\n x: parseInt(attributes.x || '0', 10),\n y: parseInt(attributes.y || '0', 10),\n width: parseInt(attributes.width || '0', 10),\n height: parseInt(attributes.height || '0', 10)\n }\n}\n\n/**\n * Flatten JSON element tree to array (depth-first)\n */\nexport function flattenElementTree(root: JSONElement): JSONElement[] {\n const result: JSONElement[] = []\n\n function traverse(element: JSONElement) {\n result.push(element)\n for (const child of element.children) {\n traverse(child)\n }\n }\n\n traverse(root)\n return result\n}\n\n/**\n * Count occurrences of an attribute value in the source XML\n */\nexport function countAttributeOccurrences(\n sourceXML: string,\n attribute: string,\n value: string\n): number {\n const escapedValue = value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n const pattern = new RegExp(`${attribute}=[\"']${escapedValue}[\"']`, 'g')\n const matches = sourceXML.match(pattern)\n return matches ? matches.length : 0\n}\n\n/**\n * Check if an attribute value is unique in the source (fast regex-based check)\n */\nexport function isAttributeUnique(\n sourceXML: string,\n attribute: string,\n value: string\n): boolean {\n return countAttributeOccurrences(sourceXML, attribute, value) === 1\n}\n","/**\n * Platform-specific element tag constants for mobile automation\n */\n\nexport const ANDROID_INTERACTABLE_TAGS = [\n // Input elements\n 'android.widget.EditText',\n 'android.widget.AutoCompleteTextView',\n 'android.widget.MultiAutoCompleteTextView',\n 'android.widget.SearchView',\n\n // Button-like elements\n 'android.widget.Button',\n 'android.widget.ImageButton',\n 'android.widget.ToggleButton',\n 'android.widget.CompoundButton',\n 'android.widget.RadioButton',\n 'android.widget.CheckBox',\n 'android.widget.Switch',\n 'android.widget.FloatingActionButton',\n 'com.google.android.material.button.MaterialButton',\n 'com.google.android.material.floatingactionbutton.FloatingActionButton',\n\n // Text elements (often tappable)\n 'android.widget.TextView',\n 'android.widget.CheckedTextView',\n\n // Image elements (often tappable)\n 'android.widget.ImageView',\n 'android.widget.QuickContactBadge',\n\n // Selection elements\n 'android.widget.Spinner',\n 'android.widget.SeekBar',\n 'android.widget.RatingBar',\n 'android.widget.ProgressBar',\n 'android.widget.DatePicker',\n 'android.widget.TimePicker',\n 'android.widget.NumberPicker',\n\n // List/grid items\n 'android.widget.AdapterView'\n]\n\nexport const ANDROID_LAYOUT_CONTAINERS = [\n // Core ViewGroup classes\n 'android.view.ViewGroup',\n 'android.view.View',\n 'android.widget.FrameLayout',\n 'android.widget.LinearLayout',\n 'android.widget.RelativeLayout',\n 'android.widget.GridLayout',\n 'android.widget.TableLayout',\n 'android.widget.TableRow',\n 'android.widget.AbsoluteLayout',\n\n // AndroidX layout classes\n 'androidx.constraintlayout.widget.ConstraintLayout',\n 'androidx.coordinatorlayout.widget.CoordinatorLayout',\n 'androidx.appcompat.widget.LinearLayoutCompat',\n 'androidx.cardview.widget.CardView',\n 'androidx.appcompat.widget.ContentFrameLayout',\n 'androidx.appcompat.widget.FitWindowsFrameLayout',\n\n // Scrolling containers\n 'android.widget.ScrollView',\n 'android.widget.HorizontalScrollView',\n 'android.widget.NestedScrollView',\n 'androidx.core.widget.NestedScrollView',\n 'androidx.recyclerview.widget.RecyclerView',\n 'android.widget.ListView',\n 'android.widget.GridView',\n 'android.widget.AbsListView',\n\n // App chrome / system elements\n 'android.widget.ActionBarContainer',\n 'android.widget.ActionBarOverlayLayout',\n 'android.view.ViewStub',\n 'androidx.appcompat.widget.ActionBarContainer',\n 'androidx.appcompat.widget.ActionBarContextView',\n 'androidx.appcompat.widget.ActionBarOverlayLayout',\n\n // Decor views\n 'com.android.internal.policy.DecorView',\n 'android.widget.DecorView'\n]\n\nexport const IOS_INTERACTABLE_TAGS = [\n // Input elements\n 'XCUIElementTypeTextField',\n 'XCUIElementTypeSecureTextField',\n 'XCUIElementTypeTextView',\n 'XCUIElementTypeSearchField',\n\n // Button-like elements\n 'XCUIElementTypeButton',\n 'XCUIElementTypeLink',\n\n // Text elements (often tappable)\n 'XCUIElementTypeStaticText',\n\n // Image elements\n 'XCUIElementTypeImage',\n 'XCUIElementTypeIcon',\n\n // Selection elements\n 'XCUIElementTypeSwitch',\n 'XCUIElementTypeSlider',\n 'XCUIElementTypeStepper',\n 'XCUIElementTypeSegmentedControl',\n 'XCUIElementTypePicker',\n 'XCUIElementTypePickerWheel',\n 'XCUIElementTypeDatePicker',\n 'XCUIElementTypePageIndicator',\n\n // Table/list items\n 'XCUIElementTypeCell',\n 'XCUIElementTypeMenuItem',\n 'XCUIElementTypeMenuBarItem',\n\n // Toggle elements\n 'XCUIElementTypeCheckBox',\n 'XCUIElementTypeRadioButton',\n 'XCUIElementTypeToggle',\n\n // Other interactive\n 'XCUIElementTypeKey',\n 'XCUIElementTypeKeyboard',\n 'XCUIElementTypeAlert',\n 'XCUIElementTypeSheet'\n]\n\nexport const IOS_LAYOUT_CONTAINERS = [\n // Generic containers\n 'XCUIElementTypeOther',\n 'XCUIElementTypeGroup',\n 'XCUIElementTypeLayoutItem',\n\n // Scroll containers\n 'XCUIElementTypeScrollView',\n 'XCUIElementTypeTable',\n 'XCUIElementTypeCollectionView',\n 'XCUIElementTypeScrollBar',\n\n // Navigation chrome\n 'XCUIElementTypeNavigationBar',\n 'XCUIElementTypeTabBar',\n 'XCUIElementTypeToolbar',\n 'XCUIElementTypeStatusBar',\n 'XCUIElementTypeMenuBar',\n\n // Windows and views\n 'XCUIElementTypeWindow',\n 'XCUIElementTypeSheet',\n 'XCUIElementTypeDrawer',\n 'XCUIElementTypeDialog',\n 'XCUIElementTypePopover',\n 'XCUIElementTypePopUpButton',\n\n // Outline elements\n 'XCUIElementTypeOutline',\n 'XCUIElementTypeOutlineRow',\n 'XCUIElementTypeBrowser',\n 'XCUIElementTypeSplitGroup',\n 'XCUIElementTypeSplitter',\n\n // Application root\n 'XCUIElementTypeApplication'\n]\n","/**\n * Locator strategy generation for mobile elements\n */\n\nimport type {\n JSONElement,\n LocatorStrategy,\n LocatorContext,\n UniquenessResult,\n XMLNode,\n XMLDocument\n} from './types.js'\nimport type { Element as XMLElement } from '@xmldom/xmldom'\nimport {\n checkXPathUniqueness,\n evaluateXPath,\n isAttributeUnique\n} from './xml-parsing.js'\n\n/**\n * Check if a string value is valid for use in a locator\n */\nfunction isValidValue(value: string | undefined): value is string {\n return (\n value !== undefined &&\n value !== null &&\n value !== 'null' &&\n value.trim() !== ''\n )\n}\n\n/**\n * Escape special characters in text for use in selectors\n */\nexport function escapeText(text: string): string {\n return text\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\"/g, '\\\\\"')\n .replace(/\\n/g, '\\\\n')\n .replace(/\\r/g, '\\\\r')\n .replace(/\\t/g, '\\\\t')\n}\n\n/**\n * Escape value for use in XPath expressions\n */\nfunction escapeXPathValue(value: string): string {\n if (!value.includes(\"'\")) {\n return `'${value}'`\n }\n if (!value.includes('\"')) {\n return `\"${value}\"`\n }\n const parts: string[] = []\n let current = ''\n for (const char of value) {\n if (char === \"'\") {\n if (current) {\n parts.push(`'${current}'`)\n }\n parts.push('\"\\'\"')\n current = ''\n } else {\n current += char\n }\n }\n if (current) {\n parts.push(`'${current}'`)\n }\n return `concat(${parts.join(',')})`\n}\n\n/**\n * Wrap non-unique XPath with index\n */\nfunction generateIndexedXPath(baseXPath: string, index: number): string {\n return `(${baseXPath})[${index}]`\n}\n\n/**\n * Add .instance(n) for UiAutomator (0-based)\n */\nfunction generateIndexedUiAutomator(\n baseSelector: string,\n index: number\n): string {\n return `${baseSelector}.instance(${index - 1})`\n}\n\n/**\n * Check uniqueness, falling back to regex if no DOM available\n */\nfunction checkUniqueness(\n ctx: LocatorContext,\n xpath: string,\n targetNode?: XMLNode\n): UniquenessResult {\n if (ctx.parsedDOM) {\n return checkXPathUniqueness(ctx.parsedDOM, xpath, targetNode)\n }\n\n // Bounded quantifiers ({1,200} for attr names, {1,2000} for values) prevent\n // polynomial-time backtracking on near-misses like `//[@<` repeated — CodeQL's\n // js/polynomial-redos query flagged the unbounded form. The bounds are well\n // above any realistic XPath attribute name/value length.\n const match = xpath.match(/\\/\\/\\*\\[@([^=]{1,200})=\"([^\"]{1,2000})\"\\]/)\n if (match) {\n const [, attr, value] = match\n return { isUnique: isAttributeUnique(ctx.sourceXML, attr, value) }\n }\n return { isUnique: false }\n}\n\n/**\n * Get sibling index (1-based) among same-tag siblings\n */\nfunction getSiblingIndex(element: XMLElement): number {\n const parent = element.parentNode\n if (!parent) {\n return 1\n }\n\n const tagName = element.nodeName\n let index = 0\n\n for (let i = 0; i < parent.childNodes.length; i++) {\n const child = parent.childNodes.item(i)\n if (child?.nodeType === 1 && child.nodeName === tagName) {\n index++\n if (child === element) {\n return index\n }\n }\n }\n\n return 1\n}\n\n/**\n * Count siblings with same tag name\n */\nfunction countSiblings(element: XMLElement): number {\n const parent = element.parentNode\n if (!parent) {\n return 1\n }\n\n const tagName = element.nodeName\n let count = 0\n\n for (let i = 0; i < parent.childNodes.length; i++) {\n const child = parent.childNodes.item(i)\n if (child?.nodeType === 1 && child.nodeName === tagName) {\n count++\n }\n }\n\n return count\n}\n\n/**\n * Find unique attribute for element in XPath format\n */\nfunction findUniqueAttribute(\n element: XMLElement,\n ctx: LocatorContext\n): string | null {\n const attrs = ctx.isAndroid\n ? ['resource-id', 'content-desc', 'text']\n : ['name', 'label', 'value']\n\n for (const attr of attrs) {\n const value = element.getAttribute(attr)\n if (value && value.trim()) {\n const xpath = `//*[@${attr}=${escapeXPathValue(value)}]`\n const result = ctx.parsedDOM\n ? checkXPathUniqueness(ctx.parsedDOM, xpath)\n : { isUnique: isAttributeUnique(ctx.sourceXML, attr, value) }\n\n if (result.isUnique) {\n return `@${attr}=${escapeXPathValue(value)}`\n }\n }\n }\n\n return null\n}\n\n/**\n * Build hierarchical XPath by traversing up the DOM tree\n */\nfunction buildHierarchicalXPath(\n ctx: LocatorContext,\n element: XMLElement,\n maxDepth: number = 3\n): string | null {\n if (!ctx.parsedDOM) {\n return null\n }\n\n const pathParts: string[] = []\n let current: XMLElement | null = element\n let depth = 0\n\n while (current && depth < maxDepth) {\n const tagName = current.nodeName\n const uniqueAttr = findUniqueAttribute(current, ctx)\n\n if (uniqueAttr) {\n pathParts.unshift(`//${tagName}[${uniqueAttr}]`)\n break\n } else {\n const siblingIndex = getSiblingIndex(current)\n const siblingCount = countSiblings(current)\n\n if (siblingCount > 1) {\n pathParts.unshift(`${tagName}[${siblingIndex}]`)\n } else {\n pathParts.unshift(tagName)\n }\n }\n\n const parent = current.parentNode as XMLElement | null\n current = parent && parent.nodeType === 1 ? parent : null\n depth++\n }\n\n if (pathParts.length === 0) {\n return null\n }\n\n let result = pathParts[0]\n for (let i = 1; i < pathParts.length; i++) {\n result += '/' + pathParts[i]\n }\n\n if (!result.startsWith('//')) {\n result = '//' + result\n }\n\n return result\n}\n\n/**\n * Add XPath locator with uniqueness checking and fallbacks\n */\nfunction addXPathLocator(\n results: [LocatorStrategy, string][],\n xpath: string,\n ctx: LocatorContext,\n targetNode?: XMLNode\n): void {\n const uniqueness = checkUniqueness(ctx, xpath, targetNode)\n if (uniqueness.isUnique) {\n results.push(['xpath', xpath])\n } else if (uniqueness.index) {\n results.push(['xpath', generateIndexedXPath(xpath, uniqueness.index)])\n } else {\n if (targetNode && ctx.parsedDOM) {\n // @xmldom/xmldom 0.9+ XMLNode doesn't satisfy global Node; safe at runtime\n const hierarchical = buildHierarchicalXPath(\n ctx,\n targetNode as unknown as XMLElement\n )\n if (hierarchical) {\n results.push(['xpath', hierarchical])\n }\n }\n results.push(['xpath', xpath])\n }\n}\n\n/**\n * Check if element is within UiAutomator scope\n */\nfunction isInUiAutomatorScope(\n element: JSONElement,\n doc: XMLDocument | null\n): boolean {\n if (!doc) {\n return true\n }\n\n const hierarchyNodes = evaluateXPath(doc, '/hierarchy/*')\n if (hierarchyNodes.length === 0) {\n return true\n }\n\n const lastIndex = hierarchyNodes.length\n const pathParts = element.path.split('.')\n if (pathParts.length === 0 || pathParts[0] === '') {\n return true\n }\n\n const firstIndex = parseInt(pathParts[0], 10)\n return firstIndex === lastIndex - 1\n}\n\n/**\n * Build Android UiAutomator selector with multiple attributes\n */\nfunction buildUiAutomatorSelector(element: JSONElement): string | null {\n const attrs = element.attributes\n const parts: string[] = []\n\n if (isValidValue(attrs['resource-id'])) {\n parts.push(`resourceId(\"${attrs['resource-id']}\")`)\n }\n if (isValidValue(attrs.text) && attrs.text!.length < 100) {\n parts.push(`text(\"${escapeText(attrs.text!)}\")`)\n }\n if (isValidValue(attrs['content-desc'])) {\n parts.push(`description(\"${attrs['content-desc']}\")`)\n }\n if (isValidValue(attrs.class)) {\n parts.push(`className(\"${attrs.class}\")`)\n }\n\n if (parts.length === 0) {\n return null\n }\n\n return `android=new UiSelector().${parts.join('.')}`\n}\n\n/**\n * Build iOS predicate string with multiple conditions\n */\nfunction buildPredicateString(element: JSONElement): string | null {\n const attrs = element.attributes\n const conditions: string[] = []\n\n if (isValidValue(attrs.name)) {\n conditions.push(`name == \"${escapeText(attrs.name!)}\"`)\n }\n if (isValidValue(attrs.label)) {\n conditions.push(`label == \"${escapeText(attrs.label!)}\"`)\n }\n if (isValidValue(attrs.value)) {\n conditions.push(`value == \"${escapeText(attrs.value!)}\"`)\n }\n if (attrs.visible === 'true') {\n conditions.push('visible == 1')\n }\n if (attrs.enabled === 'true') {\n conditions.push('enabled == 1')\n }\n\n if (conditions.length === 0) {\n return null\n }\n\n return `-ios predicate string:${conditions.join(' AND ')}`\n}\n\n/**\n * Build iOS class chain selector\n */\nfunction buildClassChain(element: JSONElement): string | null {\n const attrs = element.attributes\n const tagName = element.tagName\n\n if (!tagName.startsWith('XCUI')) {\n return null\n }\n\n let selector = `**/${tagName}`\n\n if (isValidValue(attrs.label)) {\n selector += `[\\`label == \"${escapeText(attrs.label!)}\"\\`]`\n } else if (isValidValue(attrs.name)) {\n selector += `[\\`name == \"${escapeText(attrs.name!)}\"\\`]`\n }\n\n return `-ios class chain:${selector}`\n}\n\n/**\n * Build XPath for element with unique identification\n */\nfunction buildXPath(\n element: JSONElement,\n _sourceXML: string,\n isAndroid: boolean\n): string | null {\n const attrs = element.attributes\n const tagName = element.tagName\n const conditions: string[] = []\n\n if (isAndroid) {\n if (isValidValue(attrs['resource-id'])) {\n conditions.push(`@resource-id=\"${attrs['resource-id']}\"`)\n }\n if (isValidValue(attrs['content-desc'])) {\n conditions.push(`@content-desc=\"${attrs['content-desc']}\"`)\n }\n if (isValidValue(attrs.text) && attrs.text!.length < 100) {\n conditions.push(`@text=\"${escapeText(attrs.text!)}\"`)\n }\n } else {\n if (isValidValue(attrs.name)) {\n conditions.push(`@name=\"${attrs.name}\"`)\n }\n if (isValidValue(attrs.label)) {\n conditions.push(`@label=\"${attrs.label}\"`)\n }\n if (isValidValue(attrs.value)) {\n conditions.push(`@value=\"${attrs.value}\"`)\n }\n }\n\n if (conditions.length === 0) {\n return `//${tagName}`\n }\n\n return `//${tagName}[${conditions.join(' and ')}]`\n}\n\n/**\n * Get simple locators based on single attributes\n */\nfunction getSimpleSuggestedLocators(\n element: JSONElement,\n ctx: LocatorContext,\n automationName: string,\n inUiAutomatorScope: boolean,\n targetNode?: XMLNode\n): [LocatorStrategy, string][] {\n const results: [LocatorStrategy, string][] = []\n const isAndroid = automationName.toLowerCase().includes('uiautomator')\n const attrs = element.attributes\n\n if (isAndroid) {\n // Resource ID\n const resourceId = attrs['resource-id']\n if (isValidValue(resourceId)) {\n const xpath = `//*[@resource-id=\"${resourceId}\"]`\n const uniqueness = checkUniqueness(ctx, xpath, targetNode)\n\n if (uniqueness.isUnique && inUiAutomatorScope) {\n results.push([\n 'id',\n `android=new UiSelector().resourceId(\"${resourceId}\")`\n ])\n } else if (uniqueness.index && inUiAutomatorScope) {\n const base = `android=new UiSelector().resourceId(\"${resourceId}\")`\n results.push(['id', generateIndexedUiAutomator(base, uniqueness.index)])\n }\n }\n\n // Content Description\n const contentDesc = attrs['content-desc']\n if (isValidValue(contentDesc)) {\n const xpath = `//*[@content-desc=\"${contentDesc}\"]`\n const uniqueness = checkUniqueness(ctx, xpath, targetNode)\n\n if (uniqueness.isUnique) {\n results.push(['accessibility-id', `~${contentDesc}`])\n }\n }\n\n // Text\n const text = attrs.text\n if (isValidValue(text) && text.length < 100) {\n const xpath = `//*[@text=\"${escapeText(text)}\"]`\n const uniqueness = checkUniqueness(ctx, xpath, targetNode)\n\n if (uniqueness.isUnique && inUiAutomatorScope) {\n results.push([\n 'text',\n `android=new UiSelector().text(\"${escapeText(text)}\")`\n ])\n } else if (uniqueness.index && inUiAutomatorScope) {\n const base = `android=new UiSelector().text(\"${escapeText(text)}\")`\n results.push([\n 'text',\n generateIndexedUiAutomator(base, uniqueness.index)\n ])\n }\n }\n } else {\n // iOS: Accessibility ID (name)\n const name = attrs.name\n if (isValidValue(name)) {\n const xpath = `//*[@name=\"${name}\"]`\n const uniqueness = checkUniqueness(ctx, xpath, targetNode)\n\n if (uniqueness.isUnique) {\n results.push(['accessibility-id', `~${name}`])\n }\n }\n\n // iOS: Label\n const label = attrs.label\n if (isValidValue(label) && label !== attrs.name) {\n const xpath = `//*[@label=\"${escapeText(label)}\"]`\n const uniqueness = checkUniqueness(ctx, xpath, targetNode)\n\n if (uniqueness.isUnique) {\n results.push([\n 'predicate-string',\n `-ios predicate string:label == \"${escapeText(label)}\"`\n ])\n }\n }\n\n // iOS: Value\n const value = attrs.value\n if (isValidValue(value)) {\n const xpath = `//*[@value=\"${escapeText(value)}\"]`\n const uniqueness = checkUniqueness(ctx, xpath, targetNode)\n\n if (uniqueness.isUnique) {\n results.push([\n 'predicate-string',\n `-ios predicate string:value == \"${escapeText(value)}\"`\n ])\n }\n }\n }\n\n return results\n}\n\n/**\n * Get complex locators (combinations, XPath, etc.)\n */\nfunction getComplexSuggestedLocators(\n element: JSONElement,\n ctx: LocatorContext,\n automationName: string,\n inUiAutomatorScope: boolean,\n targetNode?: XMLNode\n): [LocatorStrategy, string][] {\n const results: [LocatorStrategy, string][] = []\n const isAndroid = automationName.toLowerCase().includes('uiautomator')\n\n if (isAndroid) {\n if (inUiAutomatorScope) {\n const uiAutomator = buildUiAutomatorSelector(element)\n if (uiAutomator) {\n results.push(['uiautomator', uiAutomator])\n }\n }\n\n const xpath = buildXPath(element, ctx.sourceXML, true)\n if (xpath) {\n addXPathLocator(results, xpath, ctx, targetNode)\n }\n\n if (inUiAutomatorScope && isValidValue(element.attributes.class)) {\n results.push([\n 'class-name',\n `android=new UiSelector().className(\"${element.attributes.class}\")`\n ])\n }\n } else {\n const predicate = buildPredicateString(element)\n if (predicate) {\n results.push(['predicate-string', predicate])\n }\n\n const classChain = buildClassChain(element)\n if (classChain) {\n results.push(['class-chain', classChain])\n }\n\n const xpath = buildXPath(element, ctx.sourceXML, false)\n if (xpath) {\n addXPathLocator(results, xpath, ctx, targetNode)\n }\n\n const type = element.tagName\n if (type.startsWith('XCUIElementType')) {\n results.push(['class-name', `-ios class chain:**/${type}`])\n }\n }\n\n return results\n}\n\n/**\n * Get all suggested locators for an element\n */\nexport function getSuggestedLocators(\n element: JSONElement,\n sourceXML: string,\n automationName: string,\n ctx?: LocatorContext,\n targetNode?: XMLNode\n): [LocatorStrategy, string][] {\n const locatorCtx = ctx ?? {\n sourceXML,\n parsedDOM: null,\n isAndroid: automationName.toLowerCase().includes('uiautomator')\n }\n\n const inUiAutomatorScope = locatorCtx.isAndroid\n ? isInUiAutomatorScope(element, locatorCtx.parsedDOM)\n : true\n\n const simpleLocators = getSimpleSuggestedLocators(\n element,\n locatorCtx,\n automationName,\n inUiAutomatorScope,\n targetNode\n )\n const complexLocators = getComplexSuggestedLocators(\n element,\n locatorCtx,\n automationName,\n inUiAutomatorScope,\n targetNode\n )\n\n const seen = new Set<string>()\n const results: [LocatorStrategy, string][] = []\n\n for (const locator of [...simpleLocators, ...complexLocators]) {\n if (!seen.has(locator[1])) {\n seen.add(locator[1])\n results.push(locator)\n }\n }\n\n return results\n}\n\n/**\n * Get the best (first priority) locator for an element\n */\nexport function getBestLocator(\n element: JSONElement,\n sourceXML: string,\n automationName: string\n): string | null {\n const locators = getSuggestedLocators(element, sourceXML, automationName)\n return locators.length > 0 ? locators[0][1] : null\n}\n\n/**\n * Convert locator array to object format\n */\nexport function locatorsToObject(\n locators: [LocatorStrategy, string][]\n): Record<string, string> {\n const result: Record<string, string> = {}\n for (const [strategy, value] of locators) {\n if (!result[strategy]) {\n result[strategy] = value\n }\n }\n return result\n}\n","/**\n * AI-readable snapshot serializers\n *\n * Converts accessibility trees and mobile element trees into depth-indented\n * text files that LLMs can consume without any parsing.\n */\n\nimport type {\n AccessibilityNode,\n SnapshotNode,\n SnapshotElement,\n SnapshotResult\n} from './element-types.js'\nimport type { JSONElement } from './locators/types.js'\nimport { parseAndroidBounds, parseIOSBounds } from './locators/xml-parsing.js'\nimport {\n ANDROID_INTERACTABLE_TAGS,\n IOS_INTERACTABLE_TAGS\n} from './locators/constants.js'\nimport { getSuggestedLocators } from './locators/locator-generation.js'\n\n/**\n * Roles that can be interacted with — rendered with `→ selector`.\n * Structural roles (heading, img, form, nav, …) are intentionally excluded.\n */\nconst INTERACTIVE_ROLES = new Set([\n 'button',\n 'link',\n 'textbox',\n 'checkbox',\n 'radio',\n 'combobox',\n 'slider',\n 'searchbox',\n 'spinbutton',\n 'switch',\n 'tab',\n 'menuitem',\n 'option'\n])\n\n/**\n * Walk backwards from `index` to find the nearest ancestor or preceding\n * structural sibling with a non-empty name. Same-depth nodes are only\n * used when they are structural (img, heading, statictext, …) — never\n * another interactive element.\n */\nfunction inferPurpose(\n nodes: AccessibilityNode[],\n index: number\n): string | undefined {\n const myDepth = nodes[index].depth\n for (let i = index - 1; i >= 0; i--) {\n if (nodes[i].depth <= myDepth && nodes[i].name) {\n // Same-depth sibling: only structural elements count\n if (nodes[i].depth === myDepth && INTERACTIVE_ROLES.has(nodes[i].role)) {\n continue\n }\n return nodes[i].name\n }\n }\n return undefined\n}\n\nexport interface WebSnapshotOptions {\n /** Only include nodes whose bounding rect intersects the viewport (default true). */\n inViewportOnly?: boolean\n}\n\n/**\n * Serialize a web accessibility tree into a depth-indented text snapshot.\n *\n * @param nodes Flat ordered node list from getBrowserAccessibilityTree()\n * @param context Optional page context for the header line\n * @param options {@link WebSnapshotOptions}\n */\nexport function serializeWebSnapshot(\n nodes: AccessibilityNode[],\n context?: { url?: string; title?: string },\n options: WebSnapshotOptions = {}\n): string {\n const { inViewportOnly = true } = options\n\n let header = '[Page'\n if (context?.title) {\n header += `: ${context.title}`\n }\n if (context?.url) {\n header += ` — ${context.url}`\n }\n header += ']'\n\n const lines: string[] = [header]\n\n for (let i = 0; i < nodes.length; i++) {\n const node = nodes[i]\n\n // When viewport filtering is on, skip nodes that are known to be off-screen.\n // Nodes from a tree captured with inViewportOnly=false will have\n // isInViewport populated; nodes from a pre-filtered tree all have\n // isInViewport=true (or undefined for pre-existing data).\n if (inViewportOnly && node.isInViewport === false) {\n continue\n }\n\n const indent = ' '.repeat(node.depth + 1) // +1 indents everything under the header\n const isInteractive = INTERACTIVE_ROLES.has(node.role)\n\n if (isStatictextEchoedByParent(nodes, i)) {\n continue\n }\n\n // Heading gets level suffix: heading[2]\n const roleLabel =\n node.role === 'heading' && node.level\n ? `heading[${node.level}]`\n : node.role\n\n if (isInteractive) {\n // No selector → agent can't act on this node; skip entirely\n if (!node.selector) {\n continue\n }\n const purpose = inferPurpose(nodes, i)\n if (node.name) {\n // Show parent context when available — disambiguates\n // duplicate selectors like six \"Add to Wishlist\" buttons.\n lines.push(\n purpose\n ? `${indent}${roleLabel} \"${node.name}\" ∈ \"${purpose}\" → ${node.selector}`\n : `${indent}${roleLabel} \"${node.name}\" → ${node.selector}`\n )\n } else if (purpose) {\n lines.push(`${indent}${roleLabel} ∈ \"${purpose}\" → ${node.selector}`)\n } else {\n lines.push(`${indent}${roleLabel} → ${node.selector}`)\n }\n } else {\n // Container / structural: show role + name when present, no selector\n lines.push(\n node.name\n ? `${indent}${roleLabel} \"${node.name}\"`\n : `${indent}${roleLabel}`\n )\n }\n }\n\n return lines.join('\\n')\n}\n\n// ---------------------------------------------------------------------------\n// Mobile snapshot helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Returns true when `nodes[index]` is a statictext whose accessible name\n * is already echoed by its immediate interactive parent — such a node\n * adds no information and should be suppressed from the output.\n */\nfunction isStatictextEchoedByParent(\n nodes: AccessibilityNode[],\n index: number\n): boolean {\n const node = nodes[index]!\n if (node.role !== 'statictext' || !node.name) {\n return false\n }\n for (let j = index - 1; j >= 0; j--) {\n if (nodes[j]!.depth < node.depth) {\n const parent = nodes[j]!\n if (\n INTERACTIVE_ROLES.has(parent.role) &&\n parent.name &&\n parent.name.includes(node.name)\n ) {\n return true\n }\n break\n }\n }\n return false\n}\n\n/** Shorten fully-qualified Android/iOS class names to the last segment. */\nfunction simplifyTag(tagName: string): string {\n const dot = tagName.lastIndexOf('.')\n if (dot !== -1) {\n return tagName.slice(dot + 1)\n }\n return tagName.replace(/^XCUIElementType/, '')\n}\n\n// ---------------------------------------------------------------------------\n// Mobile role classification — maps raw Android/iOS class names to semantic\n// roles so the snapshot reads like the web version (button, textbox, img, …).\n// ---------------------------------------------------------------------------\n\nconst ANDROID_ROLE_MAP: Record<string, string> = {\n 'android.widget.Button': 'button',\n 'android.widget.ImageButton': 'button',\n 'android.widget.ToggleButton': 'button',\n 'android.widget.FloatingActionButton': 'button',\n 'com.google.android.material.button.MaterialButton': 'button',\n 'com.google.android.material.floatingactionbutton.FloatingActionButton':\n 'button',\n 'android.widget.EditText': 'textbox',\n 'android.widget.AutoCompleteTextView': 'textbox',\n 'android.widget.MultiAutoCompleteTextView': 'textbox',\n 'android.widget.SearchView': 'searchbox',\n 'android.widget.ImageView': 'img',\n 'android.widget.QuickContactBadge': 'img',\n 'android.widget.CheckBox': 'checkbox',\n 'android.widget.RadioButton': 'radio',\n 'android.widget.Switch': 'switch',\n 'android.widget.Spinner': 'combobox',\n 'android.widget.SeekBar': 'slider',\n 'android.widget.RatingBar': 'slider',\n 'android.widget.ProgressBar': 'progressbar',\n 'android.widget.TextView': 'statictext',\n 'android.widget.CheckedTextView': 'statictext',\n 'android.widget.RecyclerView': 'list',\n 'android.widget.ListView': 'list',\n 'android.widget.GridView': 'list',\n 'android.webkit.WebView': 'webview'\n}\n\nconst IOS_ROLE_MAP: Record<string, string> = {\n XCUIElementTypeButton: 'button',\n XCUIElementTypeLink: 'link',\n XCUIElementTypeTextField: 'textbox',\n XCUIElementTypeSecureTextField: 'textbox',\n XCUIElementTypeTextView: 'textbox',\n XCUIElementTypeSearchField: 'searchbox',\n XCUIElementTypeImage: 'img',\n XCUIElementTypeIcon: 'img',\n XCUIElementTypeSwitch: 'switch',\n XCUIElementTypeSlider: 'slider',\n XCUIElementTypeStepper: 'slider',\n XCUIElementTypeCheckBox: 'checkbox',\n XCUIElementTypeRadioButton: 'radio',\n XCUIElementTypePicker: 'combobox',\n XCUIElementTypePickerWheel: 'combobox',\n XCUIElementTypeDatePicker: 'combobox',\n XCUIElementTypeSegmentedControl: 'combobox',\n XCUIElementTypeStaticText: 'statictext',\n XCUIElementTypeCell: 'listitem',\n XCUIElementTypeTable: 'list',\n XCUIElementTypeCollectionView: 'list'\n}\n\nfunction classifyMobileRole(\n tagName: string,\n platform: 'android' | 'ios'\n): string {\n if (platform === 'android') {\n return ANDROID_ROLE_MAP[tagName] || simplifyTag(tagName)\n }\n return IOS_ROLE_MAP[tagName] || simplifyTag(tagName)\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Clickable container whose label lives on a child TextView. */\nfunction getFirstChildText(element: JSONElement): string | undefined {\n // Breadth-first: direct children checked before grandchildren.\n // This prefers a direct sibling label over a deeply nested one.\n const queue: JSONElement[] = [...(element.children || [])]\n while (queue.length > 0) {\n const el = queue.shift()!\n const text = el.attributes?.text?.trim()\n if (text) {\n return text\n }\n if (el.children) {\n queue.push(...el.children)\n }\n }\n return undefined\n}\n\n// ---------------------------------------------------------------------------\n// Locator generation\n// ---------------------------------------------------------------------------\n\nfunction getBestAndroidLocator(\n attrs: JSONElement['attributes']\n): string | undefined {\n // Pre-computed by the full locator pipeline (generateAllElementLocators).\n // Takes priority over the simplified fallback logic below.\n if (attrs._selector) {\n return attrs._selector\n }\n // ~ prefix = accessibility-id shorthand in WebdriverIO ($('~foo'))\n if (attrs['content-desc']) {\n return `~${attrs['content-desc']}`\n }\n if (attrs['resource-id']) {\n return `id:${attrs['resource-id']}`\n }\n if (attrs.text) {\n return `~${attrs.text}`\n }\n // Fallback: class-based locator (only useful with :nth-of-type or index)\n if (attrs.class) {\n return `class:${simplifyTag(attrs.class)}`\n }\n return undefined\n}\n\nfunction getBestIOSLocator(\n attrs: JSONElement['attributes']\n): string | undefined {\n // Pre-computed by the full locator pipeline.\n if (attrs._selector) {\n return attrs._selector\n }\n // ~ prefix = accessibility-id shorthand (maps to `name` on iOS)\n if (attrs.name) {\n return `~${attrs.name}`\n }\n if (attrs.label) {\n return `~${attrs.label}`\n }\n if (attrs.value) {\n return `~${attrs.value}`\n }\n // Fallback: class-based locator\n if (attrs.type) {\n return `class:${simplifyTag(attrs.type)}`\n }\n return undefined\n}\n\n// ---------------------------------------------------------------------------\n// Identity\n// ---------------------------------------------------------------------------\n\nfunction getMobileNodeIdentity(\n attrs: JSONElement['attributes'],\n platform: 'android' | 'ios'\n): string {\n if (platform === 'android') {\n const contentDesc = attrs['content-desc']\n if (contentDesc) {\n return contentDesc\n }\n if (attrs.text) {\n return attrs.text\n }\n // Fall back to the last segment of the resource-id (e.g. \"search_action_bar\")\n const rid = attrs['resource-id']\n if (rid) {\n const slash = rid.lastIndexOf('/')\n return slash !== -1 ? rid.slice(slash + 1) : rid\n }\n return ''\n }\n return attrs.name || attrs.label || attrs.value || attrs.text || ''\n}\n\n// ---------------------------------------------------------------------------\n// Interactivity\n// ---------------------------------------------------------------------------\n\nconst ANDROID_INTERACTABLE_SET = new Set(ANDROID_INTERACTABLE_TAGS)\nconst IOS_INTERACTABLE_SET = new Set(IOS_INTERACTABLE_TAGS)\n\n/** An element is *explicitly* interactive when it carries a click/focus/check\n * attribute — as opposed to being interactive only because its tag is in the\n * interactable-tag list. Explicit parents should carry the → selector, not\n * their tag-interactive children. */\nfunction isExplicitlyInteractive(\n attrs: JSONElement['attributes'],\n platform: 'android' | 'ios'\n): boolean {\n if (platform === 'android') {\n return (\n attrs.clickable === 'true' ||\n attrs.focusable === 'true' ||\n attrs.checkable === 'true' ||\n attrs['long-clickable'] === 'true'\n )\n }\n return attrs.accessible === 'true'\n}\n\nfunction isMobileInteractive(\n element: JSONElement,\n platform: 'android' | 'ios'\n): boolean {\n const attrs = element.attributes\n if (platform === 'android') {\n if (ANDROID_INTERACTABLE_SET.has(element.tagName)) {\n return true\n }\n return (\n attrs.clickable === 'true' ||\n attrs['long-clickable'] === 'true' ||\n attrs.focusable === 'true' ||\n attrs.checkable === 'true'\n )\n }\n if (IOS_INTERACTABLE_SET.has(element.tagName)) {\n return true\n }\n return attrs.accessible === 'true'\n}\n\n// ---------------------------------------------------------------------------\n// Viewport\n// ---------------------------------------------------------------------------\n\ninterface WalkMobileOptions {\n inViewportOnly: boolean\n viewport: { width: number; height: number }\n /** Raw page-source XML. When provided, the full locator pipeline is used. */\n sourceXML?: string\n /** 'uiautomator2' or 'xcuitest'. Required when sourceXML is set. */\n automationName?: string\n}\n\nfunction isMobileInViewport(\n element: JSONElement,\n platform: 'android' | 'ios',\n viewport: { width: number; height: number }\n): boolean {\n const bounds =\n platform === 'android'\n ? parseAndroidBounds(element.attributes.bounds || '')\n : parseIOSBounds(element.attributes)\n\n if (bounds.width === 0 && bounds.height === 0) {\n return true\n }\n\n return (\n bounds.x >= 0 &&\n bounds.y >= 0 &&\n bounds.width > 0 &&\n bounds.height > 0 &&\n bounds.x + bounds.width <= viewport.width &&\n bounds.y + bounds.height <= viewport.height\n )\n}\n\n// ---------------------------------------------------------------------------\n// Flat-node representation (mirrors AccessibilityNode so both pipelines share\n// inferPurpose, dedup, and rendering logic).\n// ---------------------------------------------------------------------------\n\ninterface MobileFlatNode {\n role: string\n name: string\n selector: string\n depth: number\n isInteractive: boolean\n /** True when the element has clickable/focusable/checkable — the intended tap target. */\n isExplicitInteractive: boolean\n isInViewport: boolean\n tagName: string\n}\n\n/**\n * First pass: walk the JSONElement tree, apply viewport filtering and\n * collect every node into a flat array with semantic roles and selectors.\n */\nfunction collectMobileNodes(\n element: JSONElement,\n platform: 'android' | 'ios',\n depth: number,\n nodes: MobileFlatNode[],\n walkOpts: WalkMobileOptions\n): void {\n const attrs = element.attributes\n const role = classifyMobileRole(element.tagName, platform)\n let name = getMobileNodeIdentity(attrs, platform)\n const explicit = isExplicitlyInteractive(attrs, platform)\n const interactive = isMobileInteractive(element, platform)\n const inViewport = isMobileInViewport(element, platform, walkOpts.viewport)\n\n // Viewport filtering\n if (walkOpts.inViewportOnly) {\n if (interactive && !inViewport) {\n // Skip this node but still recurse (scroll children may be in view).\n for (const child of element.children || []) {\n collectMobileNodes(child, platform, depth + 1, nodes, walkOpts)\n }\n return\n }\n if (!interactive && !inViewport) {\n // Collapse off-screen container to a placeholder.\n nodes.push({\n role: 'generic',\n name: name ? `${role} \"${name}\"` : role,\n selector: '',\n depth,\n isInteractive: false,\n isExplicitInteractive: false,\n isInViewport: false,\n tagName: element.tagName\n })\n return\n }\n }\n\n // Generate a selector for every interactive element.\n // Use the full locator pipeline when source XML is available;\n // otherwise fall back to the simplified attribute-based heuristics.\n let locator = ''\n if (interactive) {\n if (walkOpts.sourceXML && walkOpts.automationName) {\n // Full pipeline: accessible-id, id, text, uiautomator, xpath, class-name\n const suggested = getSuggestedLocators(\n element,\n walkOpts.sourceXML,\n walkOpts.automationName,\n {\n sourceXML: walkOpts.sourceXML,\n parsedDOM: null,\n isAndroid: platform === 'android'\n }\n )\n if (suggested.length > 0) {\n locator = suggested[0][1] // first = best priority\n }\n }\n if (!locator) {\n // Simplified fallback\n locator =\n (platform === 'android'\n ? getBestAndroidLocator(attrs)\n : getBestIOSLocator(attrs)) ?? ''\n }\n\n // When the only locator is class-based and the element has no name,\n // pull a label from a child — common in Android native apps where a\n // clickable container row's label lives on a child TextView. We\n // enrich the name rather than replacing the selector so the locator\n // still targets the correct (parent) element.\n if (\n !name &&\n platform === 'android' &&\n (locator.startsWith('class:') ||\n locator.startsWith('android=new UiSelector().className(\"'))\n ) {\n const childText = getFirstChildText(element)\n if (childText && childText.length < 100) {\n name = childText\n }\n }\n }\n\n nodes.push({\n role,\n name,\n selector: locator,\n depth,\n isInteractive: interactive,\n isExplicitInteractive: explicit,\n isInViewport: inViewport,\n tagName: element.tagName\n })\n\n for (const child of element.children || []) {\n collectMobileNodes(child, platform, depth + 1, nodes, walkOpts)\n }\n}\n\n// ---------------------------------------------------------------------------\n// Context inference — shared with the web pipeline.\n// Same-depth structural siblings (img, statictext, heading, …) provide\n// context for following interactive nodes.\n// ---------------------------------------------------------------------------\n\nconst MOBILE_STRUCTURAL_ROLES = new Set([\n 'img',\n 'heading',\n 'list',\n 'listitem',\n 'webview',\n 'progressbar',\n 'slider',\n 'switch',\n 'generic'\n])\n\nfunction mobileInferPurpose(\n nodes: MobileFlatNode[],\n index: number\n): string | undefined {\n const myDepth = nodes[index].depth\n for (let i = index - 1; i >= 0; i--) {\n if (nodes[i].depth <= myDepth && nodes[i].name) {\n if (\n nodes[i].depth === myDepth &&\n !MOBILE_STRUCTURAL_ROLES.has(nodes[i].role)\n ) {\n continue\n }\n return nodes[i].name\n }\n }\n return undefined\n}\n\n// ---------------------------------------------------------------------------\n// When a tag-only-interactive child (e.g. a statictext TextView) sits\n// directly under an explicitly-interactive parent (e.g. a clickable\n// LinearLayout row), the *parent* should carry the → selector — the\n// child is just a label. Suppress the child's interactivity so the\n// parent renders as the actionable element.\n// ---------------------------------------------------------------------------\n\nfunction suppressTagOnlyChildren(nodes: MobileFlatNode[]): void {\n for (let i = 0; i < nodes.length; i++) {\n const node = nodes[i]\n if (!node.isInteractive || node.isExplicitInteractive) {\n continue\n }\n // Walk up through ALL ancestors looking for an explicitly-interactive\n // parent. The immediate depth-1 parent may just be a layout wrapper;\n // the real clickable row could be 2-3 levels up.\n for (let j = i - 1; j >= 0; j--) {\n if (nodes[j].depth < node.depth) {\n if (nodes[j].isExplicitInteractive) {\n node.isInteractive = false\n break // found — suppress and stop\n }\n // keep looking upward through the ancestor chain\n }\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Render pass: flat nodes into lines with ∈ context, dedup, noise filter,\n// and class-instance indexing.\n// ---------------------------------------------------------------------------\n\n/** Layout roles that carry no semantic meaning by themselves. */\nconst NOISY_ROLES = new Set([\n 'FrameLayout',\n 'LinearLayout',\n 'ViewGroup',\n 'RelativeLayout',\n 'View',\n 'CardView',\n 'ConstraintLayout',\n 'ScrollView'\n])\n\n/**\n * Pre-count selector occurrences so we can attach .instance(N) suffixes\n * to duplicate selectors.\n */\nfunction countSelectors(nodes: MobileFlatNode[]): Map<string, number> {\n const counts = new Map<string, number>()\n for (const node of nodes) {\n if (node.selector) {\n counts.set(node.selector, (counts.get(node.selector) ?? 0) + 1)\n }\n }\n return counts\n}\n\nfunction renderMobileNodes(nodes: MobileFlatNode[]): string[] {\n const lines: string[] = []\n const selectorCounts = countSelectors(nodes)\n const selectorIndex = new Map<string, number>()\n\n for (let i = 0; i < nodes.length; i++) {\n const node = nodes[i]\n const indent = ' '.repeat(node.depth + 1)\n\n // Collapse anonymous layout containers at depth ≥ 2.\n // Keep depth 0-1 structural chrome and any named container.\n if (\n NOISY_ROLES.has(node.role) &&\n !node.name &&\n node.depth > 1 &&\n !node.isInteractive\n ) {\n continue\n }\n\n // Off-screen containers rendered as collapsed placeholders\n if (node.isInViewport === false && !node.isInteractive) {\n lines.push(`${indent}⋯ ${node.name} (off-screen)`)\n continue\n }\n\n // Dedup: skip statictext whose text is echoed by the parent interactive element\n if (node.role === 'statictext' && node.name) {\n let echoedByParent = false\n for (let j = i - 1; j >= 0; j--) {\n if (nodes[j].depth < node.depth) {\n if (\n nodes[j].isInteractive &&\n nodes[j].name &&\n nodes[j].name.includes(node.name)\n ) {\n echoedByParent = true\n }\n break\n }\n }\n if (echoedByParent) {\n continue\n }\n }\n\n if (node.isInteractive && node.selector) {\n // Append .instance(N) when the same selector repeats\n let selector = node.selector\n const total = selectorCounts.get(selector) ?? 1\n if (total > 1) {\n const idx = selectorIndex.get(selector) ?? 0\n selectorIndex.set(selector, idx + 1)\n selector = `${selector}.instance(${idx})`\n }\n\n const purpose = mobileInferPurpose(nodes, i)\n if (node.name) {\n lines.push(\n purpose\n ? `${indent}${node.role} \"${node.name}\" ∈ \"${purpose}\" → ${selector}`\n : `${indent}${node.role} \"${node.name}\" → ${selector}`\n )\n } else if (purpose) {\n lines.push(`${indent}${node.role} ∈ \"${purpose}\" → ${selector}`)\n } else {\n lines.push(`${indent}${node.role} → ${selector}`)\n }\n } else {\n // Container / structural / non-locatable\n lines.push(\n node.name\n ? `${indent}${node.role} \"${node.name}\"`\n : `${indent}${node.role}`\n )\n }\n }\n\n return lines\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nexport interface MobileSnapshotOptions {\n /** Only include elements whose bounds intersect the viewport (default true). */\n inViewportOnly?: boolean\n /**\n * Raw XML page source string. When provided the full locator pipeline\n * (getSuggestedLocators) runs on every interactive node, producing the same\n * selectors that getElements() returns. Omit to use simplified heuristics.\n */\n sourceXML?: string\n}\n\n/**\n * Serialize a mobile element tree into a depth-indented text snapshot.\n *\n * @param root Root JSONElement from the page source XML parse\n * @param context Platform, optional device name, viewport, and source XML.\n * Include `sourceXML` to use the full locator pipeline.\n * @param options {@link MobileSnapshotOptions}\n */\nexport function serializeMobileSnapshot(\n root: JSONElement,\n context: {\n platform: 'android' | 'ios'\n deviceName?: string\n viewport?: { width: number; height: number }\n /** Raw page-source XML. When set, selectors match getElements() output. */\n sourceXML?: string\n },\n options: MobileSnapshotOptions = {}\n): string {\n const { platform, deviceName, viewport, sourceXML } = context\n const { inViewportOnly = true } = options\n\n // Auto-detect source XML stashed by getMobileVisibleElementsWithTree\n const effectiveXML = sourceXML || root.attributes._sourceXML\n\n const effectiveViewport = viewport ?? { width: 9999, height: 9999 }\n const automationName = platform === 'android' ? 'uiautomator2' : 'xcuitest'\n\n let header = `[${platform}`\n if (deviceName) {\n header += ` — ${deviceName}`\n }\n if (viewport) {\n header += ` (${viewport.width}×${viewport.height})`\n }\n header += ']'\n\n const nodes: MobileFlatNode[] = []\n collectMobileNodes(root, platform, 0, nodes, {\n inViewportOnly,\n viewport: effectiveViewport,\n sourceXML: effectiveXML,\n automationName: effectiveXML ? automationName : undefined\n })\n\n // Let explicitly-interactive parents carry the → selector\n suppressTagOnlyChildren(nodes)\n\n const lines = renderMobileNodes(nodes)\n return [header, ...lines].join('\\n')\n}\n\n// ---------------------------------------------------------------------------\n// Unified snapshot formatter — web + mobile share the same render pass.\n// ---------------------------------------------------------------------------\n\n/** Derive a tag name from a CSS selector prefix (e.g. \"button*=Submit\" → \"button\"). */\nfunction extractTagFromSelector(selector: string, fallback: string): string {\n // Matches tag name followed by a CSS selector combinator or operator.\n // Supports hyphenated custom elements (my-component) and pseudo-classes (:nth-of-type).\n const match = selector.match(\n /^([a-z][a-z0-9]*(?:-[a-z][a-z0-9]*)*)[*.#\\[:=(^$~]/\n )\n if (match) {\n return match[1]\n }\n const spaceMatch = selector.match(/^([a-z][a-z0-9]*(?:-[a-z][a-z0-9]*)*)\\s/)\n if (spaceMatch) {\n return spaceMatch[1]\n }\n return fallback\n}\n\n/** Walk backwards to find the nearest structural container name for ∈ context. */\nfunction findContextName(\n nodes: SnapshotNode[],\n index: number\n): string | undefined {\n const myDepth = nodes[index].depth\n for (let i = index - 1; i >= 0; i--) {\n if (nodes[i].depth <= myDepth && nodes[i].name) {\n if (nodes[i].depth === myDepth && nodes[i].isInteractive) {\n continue\n }\n // Suppressed tag-interactive nodes (isInteractive=false but role is a\n // mobile-interactable label like 'statictext') shouldn't provide context\n // at same depth — their name is label text, not a container identity.\n // Mirrors mobileInferPurpose which skips same-depth nodes not in\n // MOBILE_STRUCTURAL_ROLES (which excludes 'statictext').\n if (nodes[i].depth === myDepth && nodes[i].role === 'statictext') {\n continue\n }\n return nodes[i].name\n }\n }\n return undefined\n}\n\n/**\n * Core formatter — converts a flat SnapshotNode[] into a text tree with\n * eN virtual IDs and an elements map for selector resolution.\n *\n * Platform-agnostic: both web and mobile pipelines feed into this function.\n */\nexport function buildSnapshot(\n header: string,\n nodes: SnapshotNode[]\n): SnapshotResult {\n const selectorCounts = new Map<string, number>()\n for (const node of nodes) {\n if (node.isInteractive && node.selector) {\n selectorCounts.set(\n node.selector,\n (selectorCounts.get(node.selector) ?? 0) + 1\n )\n }\n }\n\n const lines: string[] = [header]\n const elements: Record<string, SnapshotElement> = {}\n const selectorIndex = new Map<string, number>()\n let counter = 1\n\n for (let i = 0; i < nodes.length; i++) {\n const node = nodes[i]\n const indent = ' '.repeat(node.depth + 1)\n\n if (node.isInteractive && node.selector) {\n let selector = node.selector\n const total = selectorCounts.get(selector) ?? 1\n if (total > 1) {\n const idx = selectorIndex.get(selector) ?? 0\n selectorIndex.set(selector, idx + 1)\n selector = `${selector}.instance(${idx})`\n }\n\n const eId = `e${counter++}`\n\n const roleLabel =\n node.role === 'heading' && node.level\n ? `heading[${node.level}]`\n : node.role\n\n const context = findContextName(nodes, i)\n if (node.name && context) {\n lines.push(\n `${indent}${eId} ${roleLabel} \"${node.name}\" ∈ \"${context}\" → ${selector}`\n )\n } else if (node.name) {\n lines.push(\n `${indent}${eId} ${roleLabel} \"${node.name}\" → ${selector}`\n )\n } else if (context) {\n lines.push(\n `${indent}${eId} ${roleLabel} ∈ \"${context}\" → ${selector}`\n )\n } else {\n lines.push(`${indent}${eId} ${roleLabel} → ${selector}`)\n }\n\n elements[eId] = {\n selector: node.selector,\n ...(selector !== node.selector ? { qualifiedSelector: selector } : {}),\n tagName: node.tagName,\n role: node.role,\n text: node.name\n }\n } else {\n const roleLabel =\n node.role === 'heading' && node.level\n ? `heading[${node.level}]`\n : node.role\n lines.push(\n node.name\n ? `${indent}${roleLabel} \"${node.name}\"`\n : `${indent}${roleLabel}`\n )\n }\n }\n\n return { text: lines.join('\\n'), elements }\n}\n\n// ---------------------------------------------------------------------------\n// Web adapter — AccessibilityNode[] → SnapshotNode[]\n// ---------------------------------------------------------------------------\n\nexport function accessibilityNodesToSnapshotNodes(\n nodes: AccessibilityNode[],\n options?: { inViewportOnly?: boolean }\n): SnapshotNode[] {\n const { inViewportOnly = true } = options ?? {}\n\n const result: SnapshotNode[] = []\n\n for (let i = 0; i < nodes.length; i++) {\n const node = nodes[i]\n\n if (inViewportOnly && node.isInViewport === false) {\n continue\n }\n\n const isInteractive = INTERACTIVE_ROLES.has(node.role)\n\n if (isStatictextEchoedByParent(nodes, i)) {\n continue\n }\n\n // Interactive nodes without a selector can't be acted on — skip them\n // so they don't leak as non-actionable entries in the text tree.\n // Matches the guard in serializeWebSnapshot (line 141).\n if (isInteractive && !node.selector) {\n continue\n }\n\n const tagName =\n isInteractive && node.selector\n ? extractTagFromSelector(node.selector, node.role)\n : node.role\n\n result.push({\n role: node.role,\n name: node.name,\n selector: node.selector,\n depth: node.depth,\n isInteractive,\n tagName,\n level: node.level || undefined\n })\n }\n\n return result\n}\n\n// ---------------------------------------------------------------------------\n// Mobile adapter — JSONElement tree → SnapshotNode[]\n// ---------------------------------------------------------------------------\n\nexport function jsonElementToSnapshotNodes(\n root: JSONElement,\n platform: 'android' | 'ios',\n options?: {\n inViewportOnly?: boolean\n viewport?: { width: number; height: number }\n sourceXML?: string\n }\n): SnapshotNode[] {\n const { inViewportOnly = true } = options ?? {}\n const effectiveViewport = options?.viewport ?? { width: 9999, height: 9999 }\n const automationName = platform === 'android' ? 'uiautomator2' : 'xcuitest'\n const effectiveXML = options?.sourceXML || root.attributes._sourceXML\n\n const mobileNodes: MobileFlatNode[] = []\n collectMobileNodes(root, platform, 0, mobileNodes, {\n inViewportOnly,\n viewport: effectiveViewport,\n sourceXML: effectiveXML,\n automationName: effectiveXML ? automationName : undefined\n })\n\n suppressTagOnlyChildren(mobileNodes)\n\n return mobileNodes.map((m) => ({\n role: m.role,\n name: m.name,\n selector: m.selector,\n depth: m.depth,\n isInteractive: m.isInteractive,\n tagName: m.tagName,\n level: undefined\n }))\n}\n","/**\n * Element filtering logic for mobile automation\n */\n\nimport type { JSONElement, FilterOptions } from './types.js'\nimport {\n ANDROID_INTERACTABLE_TAGS,\n IOS_INTERACTABLE_TAGS,\n ANDROID_LAYOUT_CONTAINERS,\n IOS_LAYOUT_CONTAINERS\n} from './constants.js'\n\n/**\n * Check if element tag matches any in the list (handles partial matches)\n */\nfunction matchesTagList(tagName: string, tagList: string[]): boolean {\n if (tagList.includes(tagName)) {\n return true\n }\n\n for (const tag of tagList) {\n if (tagName.endsWith(tag) || tagName.includes(tag)) {\n return true\n }\n }\n\n return false\n}\n\n/**\n * Check if element matches tag name filters\n */\nfunction matchesTagFilters(\n element: JSONElement,\n includeTagNames: string[],\n excludeTagNames: string[]\n): boolean {\n if (\n includeTagNames.length > 0 &&\n !matchesTagList(element.tagName, includeTagNames)\n ) {\n return false\n }\n\n if (matchesTagList(element.tagName, excludeTagNames)) {\n return false\n }\n\n return true\n}\n\n/**\n * Check if element matches attribute-based filters\n */\nfunction matchesAttributeFilters(\n element: JSONElement,\n requireAttributes: string[],\n minAttributeCount: number\n): boolean {\n if (requireAttributes.length > 0) {\n const hasRequiredAttr = requireAttributes.some(\n (attr) => element.attributes?.[attr]\n )\n if (!hasRequiredAttr) {\n return false\n }\n }\n\n if (element.attributes && minAttributeCount > 0) {\n const attrCount = Object.values(element.attributes).filter(\n (v) => v !== undefined && v !== null && v !== ''\n ).length\n if (attrCount < minAttributeCount) {\n return false\n }\n }\n\n return true\n}\n\n/**\n * Check if element is interactable based on platform\n */\nexport function isInteractableElement(\n element: JSONElement,\n _isNative: boolean,\n automationName: string\n): boolean {\n const isAndroid = automationName.toLowerCase().includes('uiautomator')\n const interactableTags = isAndroid\n ? ANDROID_INTERACTABLE_TAGS\n : IOS_INTERACTABLE_TAGS\n\n if (matchesTagList(element.tagName, interactableTags)) {\n return true\n }\n\n if (isAndroid) {\n if (\n element.attributes?.clickable === 'true' ||\n element.attributes?.focusable === 'true' ||\n element.attributes?.checkable === 'true' ||\n element.attributes?.['long-clickable'] === 'true'\n ) {\n return true\n }\n }\n\n if (!isAndroid) {\n if (element.attributes?.accessible === 'true') {\n return true\n }\n }\n\n return false\n}\n\n/**\n * Check if element is a layout container\n */\nexport function isLayoutContainer(\n element: JSONElement,\n platform: 'android' | 'ios'\n): boolean {\n const containerList =\n platform === 'android' ? ANDROID_LAYOUT_CONTAINERS : IOS_LAYOUT_CONTAINERS\n return matchesTagList(element.tagName, containerList)\n}\n\n/**\n * Check if element has meaningful content (text, accessibility info)\n */\nexport function hasMeaningfulContent(\n element: JSONElement,\n platform: 'android' | 'ios'\n): boolean {\n const attrs = element.attributes\n\n if (attrs.text && attrs.text.trim() !== '' && attrs.text !== 'null') {\n return true\n }\n\n if (platform === 'android') {\n if (\n attrs['content-desc'] &&\n attrs['content-desc'].trim() !== '' &&\n attrs['content-desc'] !== 'null'\n ) {\n return true\n }\n } else {\n if (attrs.label && attrs.label.trim() !== '' && attrs.label !== 'null') {\n return true\n }\n if (attrs.name && attrs.name.trim() !== '' && attrs.name !== 'null') {\n return true\n }\n }\n\n return false\n}\n\n/**\n * Determine if an element should be included based on all filter criteria\n */\nexport function shouldIncludeElement(\n element: JSONElement,\n filters: FilterOptions,\n isNative: boolean,\n automationName: string\n): boolean {\n const {\n includeTagNames = [],\n excludeTagNames = ['hierarchy'],\n requireAttributes = [],\n minAttributeCount = 0,\n fetchableOnly = false,\n clickableOnly = false,\n visibleOnly = true\n } = filters\n\n if (!matchesTagFilters(element, includeTagNames, excludeTagNames)) {\n if (element.attributes?.clickable !== 'true') {\n return false\n }\n }\n\n if (!matchesAttributeFilters(element, requireAttributes, minAttributeCount)) {\n return false\n }\n\n if (clickableOnly && element.attributes?.clickable !== 'true') {\n return false\n }\n\n if (visibleOnly) {\n const isAndroid = automationName.toLowerCase().includes('uiautomator')\n if (isAndroid && element.attributes?.displayed === 'false') {\n return false\n }\n if (!isAndroid && element.attributes?.visible === 'false') {\n return false\n }\n }\n\n if (\n fetchableOnly &&\n !isInteractableElement(element, isNative, automationName)\n ) {\n return false\n }\n\n return true\n}\n\n/**\n * Get default filter options for a platform\n */\nexport function getDefaultFilters(\n platform: 'android' | 'ios',\n includeContainers: boolean = false\n): FilterOptions {\n const layoutContainers =\n platform === 'android' ? ANDROID_LAYOUT_CONTAINERS : IOS_LAYOUT_CONTAINERS\n\n return {\n excludeTagNames: includeContainers\n ? ['hierarchy']\n : ['hierarchy', ...layoutContainers],\n fetchableOnly: !includeContainers,\n visibleOnly: true,\n clickableOnly: false\n }\n}\n","/**\n * Mobile element locator generation\n *\n * Main orchestrator module that coordinates XML parsing, element filtering,\n * and locator generation for mobile automation.\n */\n\n// Types\nexport type {\n ElementAttributes,\n JSONElement,\n Bounds,\n FilterOptions,\n UniquenessResult,\n LocatorStrategy,\n LocatorContext,\n ElementWithLocators,\n GenerateLocatorsOptions\n} from './types.js'\n\n// Constants\nexport {\n ANDROID_INTERACTABLE_TAGS,\n IOS_INTERACTABLE_TAGS,\n ANDROID_LAYOUT_CONTAINERS,\n IOS_LAYOUT_CONTAINERS\n} from './constants.js'\n\n// XML Parsing\nexport {\n xmlToJSON,\n xmlToDOM,\n evaluateXPath,\n checkXPathUniqueness,\n findDOMNodeByPath,\n parseAndroidBounds,\n parseIOSBounds,\n flattenElementTree,\n countAttributeOccurrences,\n isAttributeUnique\n} from './xml-parsing.js'\n\n// Element Filtering\nexport {\n isInteractableElement,\n isLayoutContainer,\n hasMeaningfulContent,\n shouldIncludeElement,\n getDefaultFilters\n} from './element-filter.js'\n\n// Locator Generation\nexport {\n getSuggestedLocators,\n getBestLocator,\n locatorsToObject\n} from './locator-generation.js'\n\nimport type {\n JSONElement,\n FilterOptions,\n LocatorStrategy,\n ElementWithLocators,\n GenerateLocatorsOptions,\n XMLDocument\n} from './types.js'\n\nimport {\n xmlToJSON,\n xmlToDOM,\n parseAndroidBounds,\n parseIOSBounds,\n findDOMNodeByPath\n} from './xml-parsing.js'\nimport {\n shouldIncludeElement,\n isLayoutContainer,\n hasMeaningfulContent\n} from './element-filter.js'\nimport { getSuggestedLocators, locatorsToObject } from './locator-generation.js'\n\ninterface ProcessingContext {\n sourceXML: string\n platform: 'android' | 'ios'\n automationName: string\n isNative: boolean\n viewportSize: { width: number; height: number }\n filters: FilterOptions\n inViewportOnly: boolean\n results: ElementWithLocators[]\n parsedDOM: XMLDocument | null\n}\n\n/**\n * Parse element bounds based on platform\n */\nfunction parseBounds(\n element: JSONElement,\n platform: 'android' | 'ios'\n): { x: number; y: number; width: number; height: number } {\n return platform === 'android'\n ? parseAndroidBounds(element.attributes.bounds || '')\n : parseIOSBounds(element.attributes)\n}\n\n/**\n * Check if bounds are within viewport\n */\nfunction isWithinViewport(\n bounds: { x: number; y: number; width: number; height: number },\n viewport: { width: number; height: number }\n): boolean {\n return (\n bounds.x >= 0 &&\n bounds.y >= 0 &&\n bounds.width > 0 &&\n bounds.height > 0 &&\n bounds.x + bounds.width <= viewport.width &&\n bounds.y + bounds.height <= viewport.height\n )\n}\n\n/**\n * Transform JSONElement to ElementWithLocators\n */\nfunction transformElement(\n element: JSONElement,\n locators: [LocatorStrategy, string][],\n ctx: ProcessingContext\n): ElementWithLocators {\n const attrs = element.attributes\n const bounds = parseBounds(element, ctx.platform)\n\n return {\n tagName: element.tagName,\n locators: locatorsToObject(locators),\n text: attrs.text || attrs.label || '',\n contentDesc: attrs['content-desc'] || '',\n resourceId: attrs['resource-id'] || '',\n accessibilityId: attrs.name || attrs['content-desc'] || '',\n label: attrs.label || '',\n value: attrs.value || '',\n className: attrs.class || element.tagName,\n clickable:\n attrs.clickable === 'true' ||\n attrs.accessible === 'true' ||\n attrs['long-clickable'] === 'true',\n enabled: attrs.enabled !== 'false',\n displayed:\n ctx.platform === 'android'\n ? attrs.displayed !== 'false'\n : attrs.visible !== 'false',\n bounds,\n isInViewport: isWithinViewport(bounds, ctx.viewportSize)\n }\n}\n\n/**\n * Check if element should be processed\n */\nfunction shouldProcess(element: JSONElement, ctx: ProcessingContext): boolean {\n if (\n shouldIncludeElement(element, ctx.filters, ctx.isNative, ctx.automationName)\n ) {\n return true\n }\n return (\n isLayoutContainer(element, ctx.platform) &&\n hasMeaningfulContent(element, ctx.platform)\n )\n}\n\n/**\n * Process a single element and add to results if valid\n */\nfunction processElement(element: JSONElement, ctx: ProcessingContext): void {\n if (!shouldProcess(element, ctx)) {\n return\n }\n\n // Skip off-screen elements early when viewport filtering is on —\n // avoids expensive locator generation for elements the caller doesn't want.\n if (ctx.inViewportOnly) {\n const b = parseBounds(element, ctx.platform)\n if (!isWithinViewport(b, ctx.viewportSize)) {\n return\n }\n }\n\n try {\n const targetNode = ctx.parsedDOM\n ? findDOMNodeByPath(ctx.parsedDOM, element.path)\n : undefined\n\n const locators = getSuggestedLocators(\n element,\n ctx.sourceXML,\n ctx.automationName,\n {\n sourceXML: ctx.sourceXML,\n parsedDOM: ctx.parsedDOM,\n isAndroid: ctx.platform === 'android'\n },\n targetNode || undefined\n )\n if (locators.length === 0) {\n return\n }\n\n // Stash the best locator on the tree node so serializeMobileSnapshot\n // can reuse the full locator pipeline instead of recomputing.\n element.attributes._selector = locators[0][1]\n\n const transformed = transformElement(element, locators, ctx)\n if (Object.keys(transformed.locators).length === 0) {\n return\n }\n\n ctx.results.push(transformed)\n } catch (error) {\n // Core is logger-free; console.error provides the required\n // \"enough detail to debug\" per the error-handling convention.\n // A single bad element never fails the entire page-source walk.\n console.error(`[processElement] Error at path ${element.path}:`, error)\n }\n}\n\n/**\n * Recursively traverse and process element tree\n */\nfunction traverseTree(\n element: JSONElement | null,\n ctx: ProcessingContext\n): void {\n if (!element) {\n return\n }\n\n processElement(element, ctx)\n\n for (const child of element.children || []) {\n traverseTree(child, ctx)\n }\n}\n\n/**\n * Generate locators for all elements from page source XML\n */\nexport function generateAllElementLocators(\n sourceXML: string,\n options: GenerateLocatorsOptions\n): ElementWithLocators[] {\n const sourceJSON = xmlToJSON(sourceXML)\n\n if (!sourceJSON) {\n // Core is logger-free; console.error is the only signal that XML\n // parsing failed — the caller receives an empty result silently otherwise.\n console.error(\n '[generateAllElementLocators] Failed to parse page source XML'\n )\n return []\n }\n\n const parsedDOM = xmlToDOM(sourceXML)\n\n const ctx: ProcessingContext = {\n sourceXML,\n platform: options.platform,\n automationName:\n options.platform === 'android' ? 'uiautomator2' : 'xcuitest',\n isNative: options.isNative ?? true,\n viewportSize: options.viewportSize ?? { width: 9999, height: 9999 },\n filters: options.filters ?? {},\n inViewportOnly: options.inViewportOnly ?? true,\n results: [],\n parsedDOM\n }\n\n traverseTree(sourceJSON, ctx)\n\n return ctx.results\n}\n","// Resolves `promise` if it settles before `ms`; otherwise resolves to the\n// supplied fallback value. Used by per-action snapshot capture to guard\n// against hung in-page scripts (heavy pages, infinite-render loops) without\n// stalling the user's test.\n\nexport function withTimeout<T>(\n promise: Promise<T>,\n ms: number,\n fallback: T\n): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | undefined\n const timeout = new Promise<T>((resolve) => {\n timer = setTimeout(() => resolve(fallback), ms)\n })\n // .finally on the race result clears the timer in BOTH the resolved AND\n // rejected branch — earlier code only cleared on fulfillment, leaving a\n // dangling timer per failed probe that delayed process exit by up to `ms`.\n return Promise.race([promise, timeout]).finally(() => {\n if (timer) {\n clearTimeout(timer)\n }\n })\n}\n\n/** Default ceiling for a single in-page snapshot probe. */\nexport const SNAPSHOT_PROBE_TIMEOUT_MS = 2500\n","// Adapter-agnostic per-action snapshot capture. Each adapter wires its own\n// `runScript`, `takeScreenshot`, etc. shim so the actual capture pipeline\n// (timeouts, fallbacks, snapshot serialization) lives in one place.\n\nimport { accessibilityTreeScript, elementsScript } from './element-scripts.js'\nimport {\n serializeWebSnapshot,\n serializeMobileSnapshot\n} from './element-snapshot.js'\nimport type { AccessibilityNode, BrowserElementInfo } from './element-types.js'\nimport { xmlToJSON } from './locators/xml-parsing.js'\nimport {\n generateAllElementLocators,\n getDefaultFilters\n} from './locators/index.js'\nimport { SNAPSHOT_PROBE_TIMEOUT_MS, withTimeout } from './with-timeout.js'\nimport type { ActionSnapshot } from '@wdio/devtools-shared'\n\nexport type ScriptRunner = (scriptSrc: string) => Promise<unknown>\n\nexport interface CaptureActionSnapshotInput {\n command: string\n /** Browser script runner — omit on native mobile where Appium can't execute JS. */\n runScript?: ScriptRunner\n takeScreenshot?: () => Promise<string | null | undefined>\n getUrl?: () => Promise<string | undefined>\n getTitle?: () => Promise<string | undefined>\n /** Page-source XML fetcher for native mobile — used instead of runScript. */\n getPageSource?: () => Promise<string | undefined>\n /** Platform identifier for mobile snapshot formatting ('android' | 'ios'). */\n platform?: 'android' | 'ios'\n}\n\nasync function runWith<T>(\n runScript: ScriptRunner | undefined,\n scriptSrc: string,\n fallback: T\n): Promise<T> {\n if (!runScript) {\n return fallback\n }\n\n return withTimeout(\n runScript(scriptSrc).then((r) => r as T),\n SNAPSHOT_PROBE_TIMEOUT_MS,\n fallback\n ).catch(() => fallback)\n}\n\nexport async function captureActionSnapshot(\n input: CaptureActionSnapshotInput\n): Promise<ActionSnapshot | null> {\n try {\n const timestamp = Date.now()\n const isNativeMobile = !input.runScript && !!input.getPageSource\n\n const [shot, url, title, pageSource, tree, elements] = await Promise.all([\n input.takeScreenshot?.().catch(() => null),\n input.getUrl?.().catch(() => undefined),\n input.getTitle?.().catch(() => undefined),\n isNativeMobile\n ? input.getPageSource?.().catch(() => undefined)\n : undefined,\n runWith<AccessibilityNode[]>(\n input.runScript,\n accessibilityTreeScript(true),\n []\n ),\n runWith<BrowserElementInfo[]>(\n input.runScript,\n elementsScript(false, true),\n []\n )\n ])\n\n let snapshotText: string\n let finalElements: unknown[] = elements\n\n if (isNativeMobile && pageSource) {\n const platform = input.platform ?? 'android'\n const jsonTree = xmlToJSON(pageSource)\n if (jsonTree) {\n jsonTree.attributes._sourceXML = pageSource\n snapshotText = serializeMobileSnapshot(jsonTree, {\n platform,\n sourceXML: pageSource\n })\n } else {\n snapshotText = `[${platform}]`\n }\n // Generate mobile element locators from the page source XML.\n try {\n const viewport = { width: 9999, height: 9999 }\n const filters = getDefaultFilters(platform, false)\n const locators = generateAllElementLocators(pageSource, {\n platform,\n viewportSize: viewport,\n filters,\n inViewportOnly: false\n })\n finalElements = locators\n } catch {\n // Non-fatal — snapshot text is the primary deliverable.\n }\n } else {\n snapshotText = serializeWebSnapshot(tree, { url, title })\n }\n\n return {\n timestamp,\n command: input.command,\n url,\n title,\n screenshot: shot ?? undefined,\n elements: finalElements,\n snapshotText\n }\n } catch {\n return null\n }\n}\n","import { parse as parseStackTrace } from 'stacktrace-parser'\n\n/**\n * Return true if a stack frame belongs to user code (not dependencies, Node\n * internals, build output, or a generic `index.js` entry point).\n */\nexport function isUserCodeFrame(frame: {\n file?: string | null\n}): frame is { file: string } {\n const { file } = frame\n return !!(\n file &&\n !file.includes('/node_modules/') &&\n !file.includes('<anonymous>') &&\n !file.includes('node:internal') &&\n !file.includes('/dist/') &&\n !file.endsWith('/index.js')\n )\n}\n\n/**\n * Strip the `file://` protocol, any trailing `:line:col` suffix, and\n * percent-decode the result. Node's ESM stack traces use file:// URLs which\n * URL-encode spaces — without decoding, `fs.readFile` hits ENOENT on any\n * path that contains one. Falls back to the literal path if decoding fails.\n */\nexport function normalizeFilePath(filePath: string): string {\n const stripped = filePath.replace(/^file:\\/\\//, '').split(':')[0]\n try {\n return decodeURIComponent(stripped)\n } catch {\n return stripped\n }\n}\n\n/**\n * Capture `{ filePath, callSource }` for the first user-code frame on the\n * current stack. `callSource` is `<file>:<line>` for the UI's source-location\n * displays; returns `'unknown:0'` (and `undefined` filePath) when no user\n * frame can be found.\n */\nexport function getCallSourceFromStack(): {\n filePath: string | undefined\n callSource: string\n} {\n const stack = new Error().stack\n if (!stack) {\n return { filePath: undefined, callSource: 'unknown:0' }\n }\n\n const frame = parseStackTrace(stack).find(isUserCodeFrame)\n if (!frame?.file) {\n return { filePath: undefined, callSource: 'unknown:0' }\n }\n\n const filePath = normalizeFilePath(frame.file)\n return { filePath, callSource: `${filePath}:${frame.lineNumber ?? 0}` }\n}\n","// Converts a captured TraceLog into a trace.zip Buffer.\n// Stays runner-agnostic so the three adapters can call this directly.\n\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport type {\n ActionSnapshot,\n CommandLog,\n ConsoleLog,\n Metadata,\n NetworkRequest,\n TestMetadataMap,\n TraceFormat,\n TraceLog,\n TraceMutation\n} from '@wdio/devtools-shared'\nimport {\n formatActionTitle,\n mapCommandToAction,\n FILL_METHODS,\n type TraceAction\n} from './action-mapping.js'\nimport { networkRequestToHar } from './trace-har.js'\nimport { buildTraceZip, type TraceZipResource } from './trace-zip-writer.js'\n\nconst TRACE_VERSION = 8\nconst LIBRARY_NAME = '@wdio/devtools-core'\nconst LIBRARY_VERSION = '1.0.0'\n\ninterface ContextOptionsEvent {\n version: number\n type: 'context-options'\n origin: 'library'\n libraryName: string\n libraryVersion: string\n browserName: string\n platform: string\n wallTime: number\n monotonicTime: number\n sdkLanguage: string\n title: string\n contextId: string\n options: { viewport: { width: number; height: number } }\n}\n\ninterface BeforeEvent {\n type: 'before'\n callId: string\n startTime: number\n class: string\n method: string\n pageId: string\n params: Record<string, unknown>\n title: string\n /** Playwright-compatible API name (e.g. 'page.goBack', 'element.click'). */\n apiName: string\n /** CallId of the Tracing.tracingGroup that wraps this action (if any). */\n parentId?: string\n}\n\ninterface AfterEvent {\n type: 'after'\n callId: string\n endTime: number\n error?: { message: string }\n /** CallId of the Tracing.tracingGroup that wraps this action (if any). */\n parentId?: string\n}\n\ninterface ScreencastFrameEvent {\n type: 'screencast-frame'\n pageId: string\n sha1: string\n elements?: string\n snapshot?: string\n width: number\n height: number\n timestamp: number\n}\n\ntype TraceEvent =\n | ContextOptionsEvent\n | BeforeEvent\n | AfterEvent\n | ScreencastFrameEvent\n\nfunction shortId(sessionId?: string): string {\n return (sessionId ?? Math.random().toString(36).slice(2, 10)).slice(0, 8)\n}\n\nfunction resolveContextNaming(caps: Record<string, unknown> | undefined): {\n browserName: string\n title: string\n} {\n const platformName =\n typeof caps?.platformName === 'string'\n ? caps.platformName.toLowerCase()\n : undefined\n const deviceName =\n typeof caps?.['appium:deviceName'] === 'string'\n ? (caps['appium:deviceName'] as string)\n : undefined\n if (platformName === 'android' || platformName === 'ios') {\n return {\n browserName: 'chromium',\n title: deviceName ? `${platformName} — ${deviceName}` : platformName\n }\n }\n const browserName =\n typeof caps?.browserName === 'string' ? caps.browserName : 'chromium'\n return { browserName, title: browserName }\n}\n\nfunction buildContextOptions(\n trace: TraceLog,\n contextId: string,\n wallTime: number\n): ContextOptionsEvent {\n const caps = trace.metadata.capabilities as\n | Record<string, unknown>\n | undefined\n const { browserName, title } = resolveContextNaming(caps)\n const viewport = trace.metadata.viewport ?? { width: 1280, height: 720 }\n return {\n version: TRACE_VERSION,\n type: 'context-options',\n origin: 'library',\n libraryName: LIBRARY_NAME,\n libraryVersion: LIBRARY_VERSION,\n browserName,\n platform:\n process.platform === 'darwin'\n ? 'darwin'\n : process.platform === 'win32'\n ? 'windows'\n : 'linux',\n wallTime,\n monotonicTime: 0,\n sdkLanguage: 'javascript',\n title,\n contextId,\n options: {\n viewport: { width: viewport.width, height: viewport.height }\n }\n }\n}\n\nexport function buildActionEvents(\n commands: CommandLog[],\n pageId: string,\n wallTime: number,\n testMetadata?: TestMetadataMap\n): TraceEvent[] {\n const events: TraceEvent[] = []\n let prevEndMs = 0\n let callCounter = 0\n let lastTestUid: string | undefined\n let groupCallId: string | undefined\n\n for (const cmd of commands) {\n const action = mapCommandToAction(cmd.command)\n if (!action) {\n continue\n }\n\n // ── Test boundary detection ──\n // When the testUid changes, close the previous Tracing.tracingGroup\n // and open a new one. Child actions inside the group reference it via\n // parentId so trace viewers render them as labelled spans.\n if (cmd.testUid && cmd.testUid !== lastTestUid) {\n // Close the previous group.\n if (lastTestUid && groupCallId) {\n callCounter++\n events.push({\n type: 'after',\n callId: groupCallId,\n endTime: prevEndMs\n } satisfies AfterEvent)\n }\n\n // Open a new group for this test.\n callCounter++\n groupCallId = `call@${callCounter}`\n const meta = testMetadata?.get(cmd.testUid)\n const groupName = meta?.title ?? cmd.testUid\n events.push({\n type: 'before',\n callId: groupCallId,\n startTime: Math.max(\n prevEndMs,\n (cmd.startTime ?? cmd.timestamp) - wallTime\n ),\n class: 'Tracing',\n method: 'tracingGroup',\n pageId,\n params: { name: groupName },\n title: groupName,\n apiName: 'tracing.tracingGroup'\n } satisfies BeforeEvent)\n lastTestUid = cmd.testUid\n }\n\n // ── Regular action (child of the current group) ──\n callCounter++\n const callId = `call@${callCounter}`\n // Use the command's actual invocation timestamp for the start, falling\n // back to the completion timestamp when startTime isn't recorded.\n const rawStartMs = (cmd.startTime ?? cmd.timestamp) - wallTime\n const rawEndMs = cmd.timestamp - wallTime\n // Floor at prevEndMs to prevent visual overlap with previous action.\n const startMs = Math.max(prevEndMs, rawStartMs)\n // +1ms minimum duration so the viewer never sees an `after` whose\n // matching `before` hasn't been parsed yet.\n const endMs = Math.max(startMs + 1, rawEndMs)\n const rawArgs = cmd.args as unknown[]\n let params: Record<string, unknown>\n const isValueMethod = FILL_METHODS.has(action.method)\n if (action.class === 'Element' && isValueMethod && rawArgs.length >= 2) {\n params = { selector: rawArgs[0], value: rawArgs[1] }\n } else if (\n action.class === 'Element' &&\n isValueMethod &&\n rawArgs.length === 1\n ) {\n params = { value: rawArgs[0] }\n } else if (\n action.class === 'Element' &&\n rawArgs.length === 1 &&\n typeof rawArgs[0] === 'string'\n ) {\n params = { selector: rawArgs[0] }\n } else if (rawArgs.length === 1 && typeof rawArgs[0] === 'string') {\n params = { url: rawArgs[0] }\n } else {\n params = Object.fromEntries(rawArgs.map((a, i) => [String(i), a]))\n }\n events.push({\n type: 'before',\n callId,\n startTime: startMs,\n class: action.class,\n method: action.method,\n pageId,\n params,\n title: formatActionTitle(action, cmd.args, params),\n apiName: `${action.class.toLowerCase()}.${action.method}`,\n parentId: groupCallId\n })\n const afterEvent: AfterEvent = {\n type: 'after',\n callId,\n endTime: endMs\n }\n if (cmd.error) {\n const err = cmd.error as { message?: string }\n afterEvent.error = { message: err.message ?? String(cmd.error) }\n }\n events.push(afterEvent)\n prevEndMs = endMs\n }\n\n // Close the final group after the last action.\n if (lastTestUid && groupCallId) {\n callCounter++\n events.push({\n type: 'after',\n callId: groupCallId,\n endTime: prevEndMs\n } satisfies AfterEvent)\n }\n\n return events\n}\n\nfunction buildNetworkNdjson(\n requests: NetworkRequest[],\n wallTime: number,\n pageId: string\n): Buffer {\n if (!requests.length) {\n return Buffer.alloc(0)\n }\n const lines = requests.map((r) => {\n const entry = networkRequestToHar(r) as unknown as Record<string, unknown>\n entry.snapshot = {\n ...(entry.snapshot as Record<string, unknown>),\n // Monotonic offset so the viewer positions bars on the timeline.\n _monotonicTime: Math.max(0, r.timestamp - wallTime),\n // Browsing context ID so the viewer associates requests with the page.\n _frameref: pageId\n }\n return JSON.stringify(entry)\n })\n return Buffer.from(lines.join('\\n'), 'utf8')\n}\n\nfunction buildSnapshotResources(\n snapshots: ActionSnapshot[],\n pageId: string\n): TraceZipResource[] {\n const out: TraceZipResource[] = []\n for (const snap of snapshots) {\n const base = `${pageId}-${snap.timestamp}`\n if (snap.screenshot) {\n out.push({\n resourceName: `${base}.jpeg`,\n data: Buffer.from(snap.screenshot, 'base64')\n })\n }\n if (snap.elements && snap.elements.length) {\n out.push({\n resourceName: `${base}-elements.json`,\n data: Buffer.from(JSON.stringify(snap.elements), 'utf8')\n })\n }\n if (snap.snapshotText) {\n out.push({\n resourceName: `${base}-snapshot.txt`,\n data: Buffer.from(snap.snapshotText, 'utf8')\n })\n }\n }\n return out\n}\n\nfunction buildScreencastFrames(\n snapshots: ActionSnapshot[],\n pageId: string,\n wallTime: number,\n viewport: { width: number; height: number }\n): ScreencastFrameEvent[] {\n return snapshots\n .filter((s) => s.screenshot)\n .map((s) => {\n const base = `${pageId}-${s.timestamp}`\n const frame: ScreencastFrameEvent = {\n type: 'screencast-frame',\n pageId,\n sha1: `${base}.jpeg`,\n width: viewport.width,\n height: viewport.height,\n timestamp: Math.max(0, s.timestamp - wallTime)\n }\n if (s.elements && s.elements.length) {\n frame.elements = `${base}-elements.json`\n }\n if (s.snapshotText) {\n frame.snapshot = `${base}-snapshot.txt`\n }\n return frame\n })\n}\n\n/**\n * Build a trace.zip buffer from the captured TraceLog.\n * Filters commands through ACTION_MAP and renames to trace vocabulary;\n * network entries become HAR resource-snapshots; per-action screenshots,\n * element JSON, and snapshot text are written under `resources/`.\n */\n/** Chronological sort key — see `compareEvents` for the tie-breaker rationale. */\nfunction eventTime(e: TraceEvent): number {\n switch (e.type) {\n case 'context-options':\n return -Infinity\n case 'before':\n return e.startTime\n case 'after':\n return e.endTime\n case 'screencast-frame':\n return e.timestamp\n }\n}\n\n/** At the same timestamp T: an action's `after` ends first, then the\n * snapshot captured at the action boundary, then the next action's `before`.\n * Matches the viewer's expectation that the screencast frame shows the\n * state between the previous action's completion and the next one's start. */\nfunction eventOrder(e: TraceEvent): number {\n switch (e.type) {\n case 'context-options':\n return 0\n case 'after':\n return 1\n case 'screencast-frame':\n return 2\n case 'before':\n return 3\n }\n}\n\nfunction compareEvents(a: TraceEvent, b: TraceEvent): number {\n const dt = eventTime(a) - eventTime(b)\n return dt !== 0 ? dt : eventOrder(a) - eventOrder(b)\n}\n\n/**\n * Generate a human/LLM-readable Markdown transcript from captured commands.\n */\nfunction generateTranscript(\n commands: CommandLog[],\n startWallTime: number,\n title?: string\n): string {\n const wallTimeISO = new Date(startWallTime).toISOString()\n const lines: string[] = [`# ${title ?? 'Session'} — ${wallTimeISO}`, '']\n\n const captured: { entry: CommandLog; action: TraceAction }[] = []\n for (const c of commands) {\n const action = mapCommandToAction(String(c.command))\n if (action) {\n captured.push({ entry: c, action })\n }\n }\n\n captured.forEach(({ entry, action }, idx) => {\n const label = formatActionTitle(action, entry.args as unknown[])\n\n const rawArgs = entry.args as unknown[]\n const parts: string[] = [`${idx + 1}. ${label}`]\n\n if (FILL_METHODS.has(action.method) && rawArgs) {\n const valueIdx = rawArgs.length >= 2 ? 1 : 0\n if (rawArgs[valueIdx] !== undefined) {\n parts.push(`value=\"${String(rawArgs[valueIdx])}\"`)\n }\n }\n\n if (entry.error) {\n const msg =\n typeof entry.error === 'object' && 'message' in entry.error\n ? (entry.error as { message: string }).message\n : String(entry.error)\n parts.push(`ERROR: ${msg}`)\n }\n\n lines.push(parts.join(' '))\n })\n\n return lines.join('\\n')\n}\n\ninterface TraceBundle {\n traceNdjson: string\n networkNdjson: Buffer\n transcriptMd: string\n resources: TraceZipResource[]\n}\n\nfunction buildTraceBundle(\n trace: TraceLog,\n opts: {\n sessionId?: string\n wallTimeOverride?: number\n testMetadata?: TestMetadataMap\n } = {}\n): TraceBundle {\n // wallTime anchors monotonic offsets at the first captured command so\n // subsequent actions render at positive deltas in the trace viewer.\n const firstCommandTs = trace.commands[0]?.timestamp\n const wallTime = opts.wallTimeOverride ?? firstCommandTs ?? Date.now()\n const idPrefix = shortId(opts.sessionId)\n const contextId = `context@${idPrefix}`\n const pageId = `page@${idPrefix}`\n const viewport = trace.metadata.viewport ?? { width: 1280, height: 720 }\n const snapshots = trace.actionSnapshots ?? []\n const ctxOptions = buildContextOptions(trace, contextId, wallTime)\n const events: TraceEvent[] = [ctxOptions]\n\n // Emit initial screencast-frame (timestamp=0) using the first snapshot's\n // resources so trace viewers show the page state before any interaction.\n const firstSnap = snapshots.find((s) => s.screenshot)\n if (firstSnap) {\n const base = `${pageId}-${firstSnap.timestamp}`\n const initFrame: ScreencastFrameEvent = {\n type: 'screencast-frame',\n pageId,\n sha1: `${base}.jpeg`,\n width: viewport.width,\n height: viewport.height,\n timestamp: 0\n }\n if (firstSnap.elements && firstSnap.elements.length) {\n initFrame.elements = `${base}-elements.json`\n }\n if (firstSnap.snapshotText) {\n initFrame.snapshot = `${base}-snapshot.txt`\n }\n events.push(initFrame)\n }\n\n events.push(\n // Skip the first snapshot in buildScreencastFrames — it was already emitted\n // as the initial t=0 frame above.\n ...buildScreencastFrames(\n firstSnap ? snapshots.filter((s) => s !== firstSnap) : snapshots,\n pageId,\n wallTime,\n viewport\n ),\n ...buildActionEvents(trace.commands, pageId, wallTime, opts.testMetadata)\n )\n events.sort(compareEvents)\n const ctxBName = ctxOptions.title\n return {\n traceNdjson: events.map((e) => JSON.stringify(e)).join('\\n') + '\\n',\n networkNdjson: buildNetworkNdjson(trace.networkRequests, wallTime, pageId),\n transcriptMd: generateTranscript(trace.commands, wallTime, ctxBName),\n resources: buildSnapshotResources(snapshots, pageId)\n }\n}\n\nexport async function exportTraceZip(\n trace: TraceLog,\n opts: {\n sessionId?: string\n wallTimeOverride?: number\n testMetadata?: TestMetadataMap\n } = {}\n): Promise<Buffer> {\n const bundle = buildTraceBundle(trace, opts)\n return buildTraceZip({\n traceNdjson: bundle.traceNdjson,\n networkNdjson: bundle.networkNdjson,\n resources: bundle.resources,\n transcriptMd: bundle.transcriptMd\n })\n}\n\nasync function exportTraceDirectory(\n trace: TraceLog,\n targetDir: string,\n opts: {\n sessionId?: string\n wallTimeOverride?: number\n testMetadata?: TestMetadataMap\n } = {}\n): Promise<void> {\n const bundle = buildTraceBundle(trace, opts)\n await fs.mkdir(path.join(targetDir, 'resources'), { recursive: true })\n await Promise.all([\n fs.writeFile(path.join(targetDir, 'trace.trace'), bundle.traceNdjson),\n fs.writeFile(\n path.join(targetDir, 'transcript.md'),\n bundle.transcriptMd,\n 'utf8'\n ),\n bundle.networkNdjson.length\n ? fs.writeFile(\n path.join(targetDir, 'trace.network'),\n bundle.networkNdjson\n )\n : Promise.resolve(),\n ...bundle.resources.map((r) =>\n fs.writeFile(path.join(targetDir, 'resources', r.resourceName), r.data)\n )\n ])\n}\n\n/** Minimum capturer surface needed to assemble a TraceLog. */\nexport interface TraceCapturer {\n mutations: TraceMutation[]\n traceLogs: string[]\n consoleLogs: ConsoleLog[]\n networkRequests: NetworkRequest[]\n commandsLog: CommandLog[]\n sources: Map<string, string>\n metadata?: Metadata\n startWallTime?: number\n}\n\nexport interface WriteTraceZipOptions {\n outputDir: string\n sessionId: string\n capabilities?: unknown\n /**\n * Per-action snapshots from a Phase-3-style hook. When omitted, snapshots\n * are synthesized from CommandLog entries that carry a screenshot so the\n * viewer still renders thumbnails for adapters without an action hook.\n */\n actionSnapshots?: ActionSnapshot[]\n /** Output layout — `zip` (default) writes a single archive, `directory`\n * unpacks the same files into `trace-<id>/`. */\n format?: TraceFormat\n /** Test metadata keyed by testUid for Tracing.tracingGroup events. */\n testMetadata?: TestMetadataMap\n}\n\n/**\n * Build a TraceLog from a SessionCapturer-shaped source and write the trace\n * artifact (zip file or directory). Returns the absolute path written.\n */\nexport async function writeTraceZip(\n capturer: TraceCapturer,\n opts: WriteTraceZipOptions\n): Promise<string> {\n const baseMetadata = capturer.metadata ?? ({} as Metadata)\n const actionSnapshots =\n opts.actionSnapshots ??\n synthesizeSnapshotsFromCommands(capturer.commandsLog)\n const traceLog: TraceLog = {\n mutations: capturer.mutations,\n logs: capturer.traceLogs,\n consoleLogs: capturer.consoleLogs,\n networkRequests: capturer.networkRequests,\n metadata: {\n ...baseMetadata,\n ...(opts.capabilities\n ? { capabilities: opts.capabilities as Metadata['capabilities'] }\n : {})\n },\n commands: capturer.commandsLog,\n sources: Object.fromEntries(capturer.sources),\n ...(actionSnapshots.length ? { actionSnapshots } : {})\n }\n await fs.mkdir(opts.outputDir, { recursive: true })\n const exportOpts = {\n sessionId: opts.sessionId,\n wallTimeOverride: capturer.startWallTime,\n testMetadata: opts.testMetadata\n }\n if (opts.format === 'ndjson-directory') {\n const dir = path.join(opts.outputDir, `trace-${opts.sessionId}`)\n await fs.mkdir(dir, { recursive: true })\n await exportTraceDirectory(traceLog, dir, exportOpts)\n return dir\n }\n const zip = await exportTraceZip(traceLog, exportOpts)\n const zipPath = path.join(opts.outputDir, `trace-${opts.sessionId}.zip`)\n await fs.writeFile(zipPath, zip)\n return zipPath\n}\n\nfunction synthesizeSnapshotsFromCommands(\n commands: CommandLog[]\n): ActionSnapshot[] {\n return commands\n .filter((c) => c.screenshot && mapCommandToAction(c.command))\n .map((c) => ({\n timestamp: c.timestamp,\n command: c.command,\n screenshot: c.screenshot\n }))\n}\n","// Convert the existing NetworkRequest shape into trace format\n// `resource-snapshot` NDJSON entries (HAR-flavoured) for trace.zip.\n\nimport type { NetworkRequest } from '@wdio/devtools-shared'\n\nexport interface ResourceSnapshotEntry {\n type: 'resource-snapshot'\n snapshot: {\n startedDateTime: string\n time: number\n request: {\n method: string\n url: string\n httpVersion: string\n cookies: unknown[]\n headers: { name: string; value: string }[]\n queryString: { name: string; value: string }[]\n headersSize: number\n bodySize: number\n }\n response: {\n status: number\n statusText: string\n httpVersion: string\n cookies: unknown[]\n headers: { name: string; value: string }[]\n content: { size: number; mimeType: string }\n redirectURL: string\n headersSize: number\n bodySize: number\n }\n cache: Record<string, never>\n timings: { send: number; wait: number; receive: number }\n }\n}\n\nfunction toHeaderArray(\n h: Record<string, string> | undefined\n): { name: string; value: string }[] {\n if (!h) {\n return []\n }\n return Object.entries(h).map(([name, value]) => ({ name, value }))\n}\n\nfunction toQueryString(url: string): { name: string; value: string }[] {\n try {\n const u = new URL(url)\n const out: { name: string; value: string }[] = []\n u.searchParams.forEach((value, name) => out.push({ name, value }))\n return out\n } catch {\n return []\n }\n}\n\nexport function networkRequestToHar(\n entry: NetworkRequest\n): ResourceSnapshotEntry {\n const startedDateTime = new Date(entry.timestamp).toISOString()\n const duration =\n entry.time ?? (entry.endTime ?? entry.startTime) - entry.startTime\n const status = entry.response?.status ?? entry.status ?? 0\n const mimeType = entry.response?.mimeType ?? ''\n const responseHeaders = entry.response?.headers ?? entry.responseHeaders\n return {\n type: 'resource-snapshot',\n snapshot: {\n startedDateTime,\n time: Math.max(0, duration),\n request: {\n method: entry.method,\n url: entry.url,\n httpVersion: 'HTTP/1.1',\n cookies: [],\n headers: toHeaderArray(entry.requestHeaders ?? entry.headers),\n queryString: toQueryString(entry.url),\n headersSize: -1,\n bodySize: entry.requestBody ? entry.requestBody.length : -1\n },\n response: {\n status,\n statusText: entry.statusText ?? '',\n httpVersion: 'HTTP/1.1',\n cookies: [],\n headers: toHeaderArray(responseHeaders),\n content: { size: entry.size ?? 0, mimeType },\n redirectURL: '',\n headersSize: -1,\n bodySize: entry.size ?? -1\n },\n cache: {},\n timings: {\n send: 0,\n wait: Math.max(0, duration),\n receive: 0\n }\n }\n }\n}\n","// Thin yazl wrapper that packages a trace into a single Buffer.\n// Ported from Vince Graics' PR #209.\n\nimport yazl from 'yazl'\n\nexport interface TraceZipResource {\n /** Path inside the zip, e.g. `resources/page@xxx-12345.jpeg`. */\n resourceName: string\n data: Buffer\n}\n\nexport interface TraceZipInputs {\n /** NDJSON action events (one JSON object per line). */\n traceNdjson: string\n /** NDJSON HAR resource-snapshot entries. Empty buffer when omitted. */\n networkNdjson: Buffer\n /** Human/LLM-readable Markdown transcript. */\n transcriptMd?: string\n /** Files written under `resources/` — typically screenshots + element snapshots. */\n resources: TraceZipResource[]\n}\n\nexport function buildTraceZip(inputs: TraceZipInputs): Promise<Buffer> {\n return new Promise((resolve, reject) => {\n const zipFile = new yazl.ZipFile()\n zipFile.addBuffer(Buffer.from(inputs.traceNdjson, 'utf8'), 'trace.trace')\n zipFile.addBuffer(inputs.networkNdjson, 'trace.network')\n if (inputs.transcriptMd) {\n zipFile.addBuffer(\n Buffer.from(inputs.transcriptMd, 'utf8'),\n 'transcript.md'\n )\n }\n for (const resource of inputs.resources) {\n zipFile.addBuffer(resource.data, `resources/${resource.resourceName}`)\n }\n zipFile.end()\n const chunks: Buffer[] = []\n zipFile.outputStream.on('data', (chunk: Buffer) => chunks.push(chunk))\n zipFile.outputStream.on('end', () => resolve(Buffer.concat(chunks)))\n zipFile.outputStream.on('error', reject)\n })\n}\n","// Stable UID generation for tests and suites. The hash function is a tiny\n// djb2-style char-code accumulator that produces compact base36 strings.\n// \"Stable\" means: the same input produces the same output across runs.\n\n/**\n * Hash arbitrary string parts into a stable, deterministic UID. Calling this\n * multiple times with the same inputs always returns the same value — no\n * counter, no hidden state. Use for entities that must map to the same UID\n * across retries (Cucumber scenarios, feature steps, etc.).\n */\nexport function deterministicUid(...parts: string[]): string {\n const hash = parts\n .join('::')\n .split('')\n .reduce((acc, char) => ((acc << 5) - acc + char.charCodeAt(0)) | 0, 0)\n return `stable-${Math.abs(hash).toString(36)}`\n}\n\n// Counter for disambiguating repeated (file, name) signatures within a single\n// test run. Cleared by resetSignatureCounters() between runs.\nconst signatureCounters = new Map<string, number>()\n\n/**\n * Generate a UID from a (file, name) pair, disambiguating repeated calls with\n * the same inputs via an in-run counter. Use for test/suite identity where\n * the same file::name combo may legitimately appear multiple times in one run\n * (e.g. parameterised tests). For entities that must produce the same UID on\n * every retry (Cucumber scenarios), use {@link deterministicUid} instead.\n */\nexport function generateStableUid(file: string, name: string): string {\n const signature = `${file}::${name}`\n const count = signatureCounters.get(signature) ?? 0\n signatureCounters.set(signature, count + 1)\n const input = count > 0 ? `${signature}::${count}` : signature\n return deterministicUid(input)\n}\n\n/** Reset the signature counter map. Call at the start of each test run. */\nexport function resetSignatureCounters(): void {\n signatureCounters.clear()\n}\n","import fs from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\nimport type { ScreencastInfo } from '@wdio/devtools-shared'\n\nimport type { ScreencastRecorderBase } from './screencast.js'\nimport { errorMessage } from './error.js'\nimport { encodeToVideo } from './video-encoder.js'\n\nexport interface FinalizeScreencastInput {\n recorder: ScreencastRecorderBase\n sessionId: string\n /** Filename without the .webm suffix (e.g. 'wdio-video', 'selenium-video'). */\n filenamePrefix: string\n /** Preferred output dir; falls back to cwd, then os.tmpdir() if unwritable. */\n outputDir?: string\n /** Skip encoding when the recorder collected fewer frames than this. */\n minFrames?: number\n captureFormat?: 'jpeg' | 'png'\n /** Forward the encoded-video metadata to the dashboard. */\n sendUpstream: (scope: string, data: ScreencastInfo) => void\n /** Optional hook for adapter-side logging on each lifecycle step. */\n onLog?: (level: 'info' | 'warn', message: string) => void\n}\n\n/**\n * Stop the recorder, encode its frames to a `.webm` (preferred dir → cwd →\n * tmpdir), and forward the metadata to the dashboard. All errors are caught\n * and reported via `onLog` — screencast is best-effort and must not abort the\n * run on stop/encode failure.\n *\n * Shared across all three adapters: each one provides only the recorder\n * subclass, the filename prefix, and a sendUpstream binding to its\n * SessionCapturer.\n */\nexport async function finalizeScreencast({\n recorder,\n sessionId,\n filenamePrefix,\n outputDir,\n minFrames = 1,\n captureFormat,\n sendUpstream,\n onLog\n}: FinalizeScreencastInput): Promise<void> {\n const log = (level: 'info' | 'warn', message: string) =>\n onLog?.(level, message)\n\n try {\n await recorder.stop()\n } catch (err) {\n log('warn', `Screencast stop failed: ${errorMessage(err)}`)\n return\n }\n\n const frames = recorder.frames\n if (frames.length < minFrames) {\n return\n }\n\n const fileName = `${filenamePrefix}-${sessionId}.webm`\n const candidate = outputDir || process.cwd()\n let videoPath = path.join(candidate, fileName)\n try {\n fs.accessSync(candidate, fs.constants.W_OK)\n } catch {\n videoPath = path.join(os.tmpdir(), fileName)\n }\n\n try {\n await encodeToVideo(frames, videoPath, { captureFormat })\n log('info', `📹 Screencast video: ${videoPath}`)\n sendUpstream('screencast', {\n sessionId,\n videoPath,\n videoFile: fileName,\n frameCount: frames.length,\n duration: recorder.duration,\n startTime: frames[0]?.timestamp\n })\n } catch (err) {\n log('warn', `Screencast encode failed: ${errorMessage(err)}`)\n }\n}\n","// VP8/WebM encoder for screencast frames. Loads fluent-ffmpeg lazily via\n// createRequire so the dep stays optional — adapters that ship screencast\n// support are expected to list fluent-ffmpeg in their own dependencies.\n\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport os from 'node:os'\nimport { createRequire } from 'node:module'\n\nimport type { ScreencastFrame, ScreencastOptions } from '@wdio/devtools-shared'\n\nconst require = createRequire(import.meta.url)\n\n/**\n * Encode an array of CDP screencast frames into a .webm file using ffmpeg\n * (via fluent-ffmpeg) and the VP8 codec (libvpx).\n *\n * Strategy:\n * 1. Write each frame as a JPEG (or PNG) file in a temp directory.\n * 2. Write an ffconcat manifest that assigns each frame its exact display\n * duration based on the inter-frame timestamp delta. Variable-frame-rate\n * output reflects real timing even across long command pauses.\n * 3. Run ffmpeg with the concat demuxer → libvpx (VP8) → .webm output.\n * Force CFR at 10fps — VFR WebMs don't write Cues reliably, so the\n * dashboard `<video>` can't read duration/seek without it.\n * 4. Clean up the temp directory regardless of success or failure.\n *\n * @throws If no frames are provided, fluent-ffmpeg is not installed, or\n * the ffmpeg binary is not found on PATH.\n */\n// fluent-ffmpeg is loaded lazily and its types aren't worth pinning — we\n// only call a tiny chained builder API on it. `unknown` at the boundary\n// + a single cast in the runner keeps the surface honest.\ntype FfmpegBuilder = {\n input(path: string): FfmpegBuilder\n inputOptions(opts: string[]): FfmpegBuilder\n videoCodec(name: string): FfmpegBuilder\n outputOptions(opts: string[]): FfmpegBuilder\n output(path: string): FfmpegBuilder\n on(event: 'end', cb: () => void): FfmpegBuilder\n on(event: 'error', cb: (err: Error) => void): FfmpegBuilder\n run(): void\n}\ntype FfmpegFactory = () => FfmpegBuilder\n\nfunction loadFfmpeg(): FfmpegFactory {\n try {\n return require('fluent-ffmpeg') as FfmpegFactory\n } catch {\n throw new Error(\n 'VideoEncoder: fluent-ffmpeg is required for screencast encoding. ' +\n 'Install it with: npm install fluent-ffmpeg'\n )\n }\n}\n\nasync function writeFramesAndManifest(\n frames: ScreencastFrame[],\n tmpDir: string,\n ext: string\n): Promise<string> {\n const manifestLines: string[] = ['ffconcat version 1.0']\n for (let i = 0; i < frames.length; i++) {\n const frameName = `frame-${String(i).padStart(6, '0')}.${ext}`\n const framePath = path.join(tmpDir, frameName)\n await fs.writeFile(framePath, Buffer.from(frames[i].data, 'base64'))\n const nextTs = frames[i + 1]?.timestamp ?? frames[i].timestamp + 100\n const durationSecs = Math.max((nextTs - frames[i].timestamp) / 1000, 0.01)\n manifestLines.push(`file '${framePath}'`)\n manifestLines.push(`duration ${durationSecs.toFixed(6)}`)\n }\n // The last frame needs to appear twice in the manifest — ffconcat ignores\n // the final `duration` directive without a trailing `file` line.\n const lastFramePath = path.join(\n tmpDir,\n `frame-${String(frames.length - 1).padStart(6, '0')}.${ext}`\n )\n manifestLines.push(`file '${lastFramePath}'`)\n const manifestPath = path.join(tmpDir, 'manifest.txt')\n await fs.writeFile(manifestPath, manifestLines.join('\\n'))\n return manifestPath\n}\n\nfunction classifyFfmpegError(err: Error): Error {\n const msg = err.message || ''\n if (\n msg.includes('Cannot find ffmpeg') ||\n msg.includes('ENOENT') ||\n msg.includes('spawn') ||\n msg.includes('not found')\n ) {\n return new Error(\n 'VideoEncoder: ffmpeg binary not found on PATH. ' +\n 'Install ffmpeg: https://ffmpeg.org/download.html'\n )\n }\n return new Error(`VideoEncoder: ffmpeg error — ${msg}`)\n}\n\nfunction runFfmpeg(\n ffmpeg: FfmpegFactory,\n manifestPath: string,\n outputPath: string\n): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n ffmpeg()\n .input(manifestPath)\n .inputOptions(['-f', 'concat', '-safe', '0'])\n .videoCodec('libvpx')\n .outputOptions([\n '-b:v',\n '1M',\n '-pix_fmt',\n 'yuv420p',\n '-vsync',\n 'cfr',\n '-r',\n '10',\n // Force a keyframe every frame. VP8 can only seek to keyframes, and\n // libvpx's default GOP places a single keyframe on near-static\n // screencast content — so scrubber/marker seeks snap back to the\n // start. At 10fps over short recordings, all-intra keeps seeking\n // frame-accurate at an acceptable size.\n '-g',\n '1',\n '-keyint_min',\n '1',\n '-auto-alt-ref',\n '0',\n '-disposition:v',\n 'default'\n ])\n .output(outputPath)\n .on('end', () => resolve())\n .on('error', (err: Error) => reject(classifyFfmpegError(err)))\n .run()\n })\n}\n\nexport async function encodeToVideo(\n frames: ScreencastFrame[],\n outputPath: string,\n options: Pick<ScreencastOptions, 'captureFormat'> = {}\n): Promise<void> {\n if (frames.length === 0) {\n throw new Error('VideoEncoder: no frames to encode')\n }\n const ffmpeg = loadFfmpeg()\n const ext = options.captureFormat === 'png' ? 'png' : 'jpg'\n const tmpDir = await fs.mkdtemp(\n path.join(os.tmpdir(), 'devtools-screencast-')\n )\n try {\n const manifestPath = await writeFramesAndManifest(frames, tmpDir, ext)\n await runFfmpeg(ffmpeg, manifestPath, outputPath)\n } finally {\n await fs.rm(tmpDir, { recursive: true, force: true }).catch(() => {\n /* tmp cleanup is best-effort */\n })\n }\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\n\nexport interface ResolveAdapterOutputDirInput {\n /**\n * Honored as-is if set — used by adapters that expose a user-facing\n * `outputDir` option (e.g. WDIO). Skips all other resolution steps.\n */\n userConfiguredDir?: string\n /**\n * Absolute path to the current test file. When known, the video / trace\n * lands in the same folder as the spec the user just ran. This is the\n * preferred location across adapters.\n */\n testFilePath?: string\n /**\n * Absolute path to the resolved framework config file (wdio.conf.ts,\n * nightwatch.conf.cjs, etc.). Used as a fallback when the test file\n * isn't known.\n */\n configPath?: string\n /** Last-resort fallback. Defaults to `process.cwd()`. */\n fallbackDir?: string\n}\n\nconst NODE_MODULES_SEGMENT = `${path.sep}node_modules${path.sep}`\n\nfunction isWritable(dir: string): boolean {\n try {\n fs.accessSync(dir, fs.constants.W_OK)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Resolve the directory where an adapter should write output files\n * (screencast .webm, trace JSON, etc.).\n *\n * Priority:\n * 1. `userConfiguredDir` — explicit opt-in, honored as-is.\n * 2. `dirname(testFilePath)` — same folder as the spec that just ran.\n * 3. `dirname(configPath)` — fallback to the framework config dir.\n * 4. `fallbackDir` (default `process.cwd()`).\n *\n * Any candidate inside a `node_modules/` segment is skipped — this can\n * happen in symlinked workspaces where the test file resolves through a\n * linked dependency. Each candidate must also be writable; non-writable\n * dirs fall through to the next.\n *\n * Shared by all three adapters (service / nightwatch-devtools /\n * selenium-devtools) so the output location stays consistent regardless\n * of where the user invoked the runner from. See CLAUDE.md §2.2.\n */\nexport function resolveAdapterOutputDir(\n input: ResolveAdapterOutputDirInput = {}\n): string {\n const fallback = input.fallbackDir ?? process.cwd()\n // userConfiguredDir bypasses the node_modules and writability filters\n // because the user opted into it explicitly — surprising overrides are\n // worse than failing loudly here.\n if (input.userConfiguredDir) {\n return input.userConfiguredDir\n }\n const candidates: string[] = []\n if (input.testFilePath) {\n candidates.push(path.dirname(input.testFilePath))\n }\n if (input.configPath) {\n candidates.push(path.dirname(input.configPath))\n }\n candidates.push(fallback)\n for (const dir of candidates) {\n if (!dir) {\n continue\n }\n if (dir.includes(NODE_MODULES_SEGMENT)) {\n continue\n }\n if (isWritable(dir)) {\n return dir\n }\n }\n return fallback\n}\n","import type {\n CommandLog,\n DocumentInfo,\n PerformanceData\n} from '@wdio/devtools-shared'\n\n/**\n * JS source that captures Performance API data, cookies, and document info\n * from the page under test. Passed as a string to the adapter's `execute`/\n * `executeScript` driver method so the browser-only types (PerformanceEntry,\n * Document) don't leak into the Node-side type-checker.\n *\n * Returns the bag shape consumed by {@link applyPerformanceData}.\n * Framework-agnostic — all three adapters can use it.\n */\nexport const CAPTURE_PERFORMANCE_SCRIPT = `\n (function() {\n const performance = window.performance;\n const navigation = performance.getEntriesByType?.('navigation')?.[0];\n const resources = performance.getEntriesByType?.('resource') || [];\n\n return {\n navigation: navigation ? {\n url: window.location.href,\n timing: {\n loadTime: navigation.loadEventEnd - navigation.fetchStart,\n domReady: navigation.domContentLoadedEventEnd - navigation.fetchStart,\n responseTime: navigation.responseEnd - navigation.requestStart,\n dnsLookup: navigation.domainLookupEnd - navigation.domainLookupStart,\n tcpConnection: navigation.connectEnd - navigation.connectStart,\n serverResponse: navigation.responseEnd - navigation.responseStart\n }\n } : undefined,\n resources: resources.map(function(resource) {\n return {\n url: resource.name,\n duration: resource.duration,\n size: resource.transferSize || 0,\n type: resource.initiatorType,\n startTime: resource.startTime,\n responseEnd: resource.responseEnd\n };\n }),\n cookies: (function() {\n try { return document.cookie; } catch (e) { return ''; }\n })(),\n documentInfo: {\n url: window.location.href,\n title: document.title,\n headers: {\n userAgent: navigator.userAgent,\n language: navigator.language,\n platform: navigator.platform\n },\n documentInfo: {\n readyState: document.readyState,\n referrer: document.referrer,\n characterSet: document.characterSet\n }\n }\n };\n })()\n`\n\n/** Untyped bag returned by {@link CAPTURE_PERFORMANCE_SCRIPT}. */\nexport interface CapturedPerformancePayload {\n navigation?: PerformanceData['navigation']\n resources?: PerformanceData['resources']\n cookies?: string\n documentInfo?: DocumentInfo\n}\n\n/**\n * Apply a captured performance payload onto a CommandLog entry in-place,\n * setting `performance`, `cookies`, `documentInfo`, and a synthesized `result`\n * matching nightwatch's existing dashboard shape. Returns `true` if anything\n * was applied — caller can branch on this to skip further work.\n */\nexport function applyPerformanceData(\n command: CommandLog,\n payload: CapturedPerformancePayload | undefined,\n navigatedUrl?: string\n): boolean {\n if (!payload || !payload.navigation) {\n return false\n }\n command.performance = {\n navigation: payload.navigation,\n resources: payload.resources\n }\n command.cookies = payload.cookies\n command.documentInfo = payload.documentInfo\n command.result = {\n url: navigatedUrl,\n loadTime: payload.navigation?.timing?.loadTime,\n resources: payload.resources,\n resourceCount: payload.resources?.length,\n cookies: payload.cookies,\n title: payload.documentInfo?.title\n }\n return true\n}\n","/**\n * Tiny state holder for command-retry detection. Both the selenium and\n * nightwatch adapters need exactly this same pattern: compute a stable\n * signature for the incoming command, compare it to the last one we\n * captured, and treat a match as \"the framework is retrying — replace the\n * previous entry instead of pushing a new one\".\n *\n * The signature is JSON-stringified `{command, args, src: callSource}`. Test\n * boundaries (new test, new scenario) call `reset()` to drop the last\n * signature so a deliberate re-run of the same call counts as a fresh\n * command, not a retry.\n */\nexport class RetryTracker {\n #lastSig: string | null = null\n #lastId: number | null = null\n\n /** Build the canonical signature used for retry-equality checks. */\n static signature(\n command: string,\n args: unknown,\n callSource?: string\n ): string {\n return JSON.stringify({ command, args, src: callSource ?? null })\n }\n\n /** True when the incoming signature matches the last captured one AND we\n * have an id to replace (otherwise there's nothing to replace yet). */\n isRetry(sig: string): boolean {\n return sig === this.#lastSig && this.#lastId !== null\n }\n\n /** The id of the last captured command, if any (for the replace-in-place\n * flow). */\n get lastId(): number | null {\n return this.#lastId\n }\n\n /** Record a fresh capture — sets both sig and id together. */\n recordCapture(sig: string, id: number | null): void {\n this.#lastSig = sig\n this.#lastId = id\n }\n\n /** Record only the id (used by adapters that compute the sig but defer the\n * id assignment to after an async capture call). */\n setLastId(id: number | null): void {\n this.#lastId = id\n }\n\n /** Stage the sig before an async capture so the next call already sees the\n * signature change (prevents stale-sig matches on rapid back-to-back\n * commands). Pair with {@link setLastId} once the capture resolves. */\n setLastSig(sig: string): void {\n this.#lastSig = sig\n }\n\n /** Reset at test/scenario boundaries so the next capture is \"fresh\". */\n reset(): void {\n this.#lastSig = null\n this.#lastId = null\n }\n}\n","import type { ScreencastFrame, ScreencastOptions } from '@wdio/devtools-shared'\nimport { SCREENCAST_DEFAULTS } from '@wdio/devtools-shared'\n\n/**\n * Shared screencast scaffolding consumed by every adapter (service, selenium,\n * nightwatch). Owns the frame buffer, public API (start/stop/setStartMarker,\n * frames/duration/isRecording getters) and the polling fallback. Subclasses\n * provide framework-specific driver access:\n *\n * - `takeScreenshot()` — required. Used by the polling path.\n * - `tryStartCdp() / tryStopCdp()` — optional CDP push-mode override.\n * Default returns false → falls through to polling.\n *\n * Adapters that have a stable CDP escape hatch (WDIO via getPuppeteer,\n * Selenium via createCDPConnection) override the CDP hooks. Nightwatch\n * inherits the polling-only default — works on every browser Nightwatch\n * supports without extra plumbing.\n */\nexport abstract class ScreencastRecorderBase<TDriver = unknown> {\n protected buffer: ScreencastFrame[] = []\n protected options: Required<ScreencastOptions>\n protected driver?: TDriver\n #pollTimer: ReturnType<typeof setInterval> | undefined\n #isRecording = false\n #cdpActive = false\n #startIndex = 0\n #startMarkerSet = false\n\n constructor(options: ScreencastOptions = {}) {\n this.options = { ...SCREENCAST_DEFAULTS, ...options }\n }\n\n /**\n * Start recording. Tries the CDP fast-path first (if the subclass overrode\n * `tryStartCdp`); falls back to screenshot polling otherwise. Safe to call\n * even if the browser doesn't support screenshots — failures are logged and\n * recording is simply skipped.\n */\n async start(driver: TDriver): Promise<void> {\n if (this.#isRecording) {\n return\n }\n this.driver = driver\n const cdpOk = await this.tryStartCdp()\n if (cdpOk) {\n this.#cdpActive = true\n this.#isRecording = true\n return\n }\n await this.#startPolling()\n }\n\n /**\n * Stop recording and release resources. Safe to call even if start() was\n * never called or failed.\n */\n async stop(): Promise<void> {\n if (!this.#isRecording) {\n return\n }\n if (this.#cdpActive) {\n await this.tryStopCdp()\n this.#cdpActive = false\n } else if (this.#pollTimer !== undefined) {\n this.#stopPolling()\n }\n this.#isRecording = false\n }\n\n /**\n * Mark the current frame position as the start of meaningful recording.\n * Frames captured before this call (blank browser, pre-navigation pauses)\n * are excluded from `frames`. Idempotent — only the first call takes effect.\n */\n setStartMarker(): void {\n if (!this.#startMarkerSet) {\n this.#startMarkerSet = true\n this.#startIndex = this.buffer.length\n }\n }\n\n /** Frames to encode — everything from the first meaningful action onwards. */\n get frames(): ScreencastFrame[] {\n return this.buffer.slice(this.#startIndex)\n }\n\n /** Duration in ms between first and last captured frame. Zero if <2 frames. */\n get duration(): number {\n const f = this.frames\n if (f.length < 2) {\n return 0\n }\n return f[f.length - 1].timestamp - f[0].timestamp\n }\n\n get isRecording(): boolean {\n return this.#isRecording\n }\n\n // ─── Subclass hooks ──────────────────────────────────────────────────────\n\n /**\n * Capture a single screenshot via the framework's driver API. Used by the\n * polling fallback. Return `null` to indicate a transient failure (loop\n * continues); throw to abort polling entirely.\n */\n protected abstract takeScreenshot(): Promise<string | null>\n\n /**\n * Try to start CDP push-mode recording. Return `true` on success. Default\n * returns `false` → caller falls back to polling. Subclasses that wire CDP\n * push themselves (WDIO via Puppeteer, Selenium via createCDPConnection)\n * override and push frames into `this.frames` directly when CDP fires.\n */\n protected async tryStartCdp(): Promise<boolean> {\n return false\n }\n\n /** Stop the CDP push-mode session started by `tryStartCdp`. */\n protected async tryStopCdp(): Promise<void> {\n // no-op\n }\n\n /**\n * Helper for CDP subclasses: push a frame onto the buffer with the right\n * timestamp normalization (CDP gives seconds-as-float; we store ms).\n */\n protected pushCdpFrame(data: string, timestampSeconds?: number): void {\n const timestamp =\n typeof timestampSeconds === 'number'\n ? Math.round(timestampSeconds * 1000)\n : Date.now()\n this.buffer.push({ data, timestamp })\n }\n\n /** Whether `setStartMarker` (or `markStartAtLatest`) has fired yet. */\n protected get hasStartMarker(): boolean {\n return this.#startMarkerSet\n }\n\n /**\n * Anchor the start marker to the most recently pushed frame. Used by\n * subclasses that detect the first content-bearing frame heuristically\n * (e.g. selenium's blank-frame-byte-size threshold) and want to skip the\n * preceding about:blank dead-air without waiting for an explicit caller.\n */\n protected markStartAtLatest(): void {\n if (!this.#startMarkerSet) {\n this.#startMarkerSet = true\n this.#startIndex = Math.max(0, this.buffer.length - 1)\n }\n }\n\n // ─── Polling implementation ─────────────────────────────────────────────\n\n /**\n * Hook fired when the polling loop starts. Default: no-op. Subclasses\n * (adapters with their own logger) override to surface visibility.\n */\n protected onPollingStarted(_intervalMs: number): void {\n // no-op\n }\n\n /** Hook fired when polling stops cleanly (driver still alive at the time). */\n protected onPollingStopped(_frameCount: number): void {\n // no-op\n }\n\n /** Hook fired when the polling fallback couldn't even take the first shot. */\n protected onUnavailable(_err: unknown): void {\n // no-op\n }\n\n // ─── Polling implementation ─────────────────────────────────────────────\n\n async #startPolling(): Promise<void> {\n try {\n const first = await this.takeScreenshot()\n if (first === null) {\n this.onUnavailable(new Error('first screenshot returned null'))\n return\n }\n this.buffer.push({ data: first, timestamp: Date.now() })\n\n const intervalMs = this.options.pollIntervalMs\n this.#pollTimer = setInterval(async () => {\n try {\n const data = await this.takeScreenshot()\n if (data !== null) {\n this.buffer.push({ data, timestamp: Date.now() })\n }\n } catch {\n // Session ended mid-interval — stop polling gracefully.\n this.#stopPolling()\n }\n }, intervalMs)\n\n this.#isRecording = true\n this.onPollingStarted(intervalMs)\n } catch (err) {\n this.onUnavailable(err)\n }\n }\n\n #stopPolling(): void {\n if (this.#pollTimer !== undefined) {\n clearInterval(this.#pollTimer)\n this.#pollTimer = undefined\n this.onPollingStopped(this.buffer.length)\n }\n }\n}\n","import fs from 'node:fs/promises'\nimport path from 'node:path'\nimport { createRequire } from 'node:module'\n\nconst require = createRequire(import.meta.url)\n\n/**\n * Load the `@wdio/devtools-script` browser preload, wrapped in an async IIFE\n * so its top-level `await` works inside a regular `<script>` element body.\n * Shared by selenium-devtools and nightwatch-devtools, which both inject the\n * script via `document.createElement('script')` rather than BiDi preload (the\n * WDIO service uses `browser.scriptAddPreloadScript`, which doesn't need the\n * wrap and stays in its own adapter).\n */\nexport async function loadInjectableScript(): Promise<string> {\n const scriptPath = require.resolve('@wdio/devtools-script')\n const scriptDir = path.dirname(scriptPath)\n const preloadScriptPath = path.join(scriptDir, 'script.js')\n const scriptContent = await fs.readFile(preloadScriptPath, 'utf-8')\n return `(async function() { ${scriptContent} })()`\n}\n\n/**\n * Poll a readiness check until it returns true, or the attempts run out.\n * Defaults to 5 × 200ms = up to 1 second total — chosen empirically to cover\n * the async IIFE init time across browsers we test against.\n */\nexport async function pollUntilReady(\n check: () => Promise<boolean>,\n opts: { attempts?: number; intervalMs?: number } = {}\n): Promise<boolean> {\n const attempts = opts.attempts ?? 5\n const intervalMs = opts.intervalMs ?? 200\n for (let i = 0; i < attempts; i++) {\n await new Promise((resolve) => setTimeout(resolve, intervalMs))\n if (await check()) {\n return true\n }\n }\n return false\n}\n","import fs from 'node:fs/promises'\nimport { WebSocket } from 'ws'\nimport type {\n CommandLog,\n ConsoleLog,\n LogLevel,\n LogSource,\n Metadata,\n NetworkRequest,\n TraceMutation\n} from '@wdio/devtools-shared'\nimport { WS_PATHS, WS_SCOPE } from '@wdio/devtools-shared'\nimport {\n CONSOLE_METHODS,\n LOG_SOURCES,\n SPINNER_RE,\n createConsoleLogEntry,\n detectLogLevel,\n isInternalStreamLine,\n stripAnsi\n} from './console.js'\n\n/**\n * Foundation class for adapter SessionCapturers. Owns the cross-framework\n * scaffolding (WS connection, console/stream patching, command id\n * bookkeeping). Framework-specific event handling stays in subclasses.\n *\n * Step 2 of {@link file://./../../../SESSIONCAPTURER_EXTRACTION_PLAN.md}.\n * **Not yet consumed by any adapter** — published so a future session can\n * migrate adapter SessionCapturer classes one at a time.\n */\n\nexport interface SessionCapturerOptions {\n hostname?: string\n port?: number\n /**\n * Set when this capturer reconnects mid-run (e.g. after `browser.end()` opens\n * a new session). Tells the backend to keep the accumulated run state instead\n * of resetting it — otherwise earlier tests' commands are wiped and Preserve\n * & Rerun on those tests finds nothing.\n */\n reconnect?: boolean\n}\n\ntype ConsoleMethod = (typeof CONSOLE_METHODS)[number]\n\nexport abstract class SessionCapturerBase {\n // ── State (mostly private; subclasses access shared ws via `this.ws`) ────\n /**\n * Exposed as `protected` so subclasses with framework-specific close/wait\n * semantics (e.g. nightwatch's `closeWebSocket` with timeout) can operate\n * on the socket directly. Default lifecycle is fully managed by the base.\n */\n protected ws: WebSocket | undefined\n #hasConnected = false\n #originalConsoleMethods: Record<ConsoleMethod, typeof console.log>\n #originalStdoutWrite = process.stdout.write.bind(process.stdout)\n #originalStderrWrite = process.stderr.write.bind(process.stderr)\n // Two flags (not one): prevents re-entrant capture when console.* writes to\n // stdout, OR when stream forwarding wants to log via console.\n #isCapturingConsole = false\n #isCapturingStream = false\n\n // Command bookkeeping — used by adapters that emit commands themselves\n // (nightwatch, selenium). The WDIO service adapter doesn't call sendCommand\n // (WDIO owns the command lifecycle), so this state is harmless overhead.\n // `protected` (not `#`) so subclasses can override the send/replace flow\n // while still sharing the counter and de-dup set with base helpers.\n protected commandCounter = 0\n protected sentCommandIds = new Set<number>()\n\n // Map of file path → source text. Populated by `captureSource` (also\n // accessed by adapter-specific source-discovery flows, e.g. service's\n // `ensureSourceLoaded` which parses `file://` locations first).\n sources = new Map<string, string>()\n\n // Captured trace payload — populated by `processTracePayload` (driven from\n // adapter-specific `captureTrace` flows) and by direct pushes from BiDi/CDP\n // listeners. Mutations stay `unknown[]` here because the canonical\n // `TraceMutation` shape is a browser-only DOM type (script package); cross-\n // package consumers treat the array as opaque.\n commandsLog: CommandLog[] = []\n consoleLogs: ConsoleLog[] = []\n networkRequests: NetworkRequest[] = []\n mutations: TraceMutation[] = []\n traceLogs: string[] = []\n metadata?: Metadata\n\n // ── Construction ────────────────────────────────────────────────────────\n constructor(opts: SessionCapturerOptions = {}) {\n const { hostname, port, reconnect } = opts\n if (hostname && port) {\n const query = reconnect ? '?reconnect=1' : ''\n this.ws = new WebSocket(\n `ws://${hostname}:${port}${WS_PATHS.worker}${query}`\n )\n this.ws.on('open', () => {\n this.#hasConnected = true\n this.onWsOpen()\n })\n this.ws.on('error', (err: unknown) => this.onWsError(err))\n this.ws.on('close', () => this.onWsClose())\n this.ws.on('message', (raw: Buffer | string) => {\n try {\n const parsed = JSON.parse(raw.toString())\n this.onWsMessage(parsed)\n } catch {\n // ignore non-JSON\n }\n })\n }\n\n this.#originalConsoleMethods = {\n log: console.log,\n info: console.info,\n warn: console.warn,\n error: console.error\n }\n }\n\n // ── Public API ──────────────────────────────────────────────────────────\n /**\n * Send a typed event to the dashboard. No-op if the WS isn't open. Catches\n * send-time exceptions so a transient socket error never aborts the host\n * runner. Subclasses that want diagnostics on drop or error override\n * {@link onUpstreamDrop}.\n */\n sendUpstream(event: string, data: unknown): void {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n this.onUpstreamDrop(event, 'closed')\n return\n }\n try {\n this.ws.send(JSON.stringify({ scope: event, data }))\n } catch (err) {\n this.onUpstreamDrop(event, 'send-error', err)\n }\n }\n\n /**\n * Hook fired when a {@link sendUpstream} call can't deliver. Default: silent\n * (matches the historical behavior of service/selenium). Nightwatch overrides\n * this to log a warning — useful when a runner drops mid-test and the user\n * needs to know why captured data is incomplete.\n */\n protected onUpstreamDrop(\n _event: string,\n _reason: 'closed' | 'send-error',\n _err?: unknown\n ): void {\n // no-op\n }\n\n /** True once the WS has opened at least once and is currently OPEN. */\n isConnected(): boolean {\n return Boolean(this.ws) && this.ws?.readyState === WebSocket.OPEN\n }\n\n /** Property-style alias for {@link isConnected} — used by tests that\n * read it as a getter while mutating `ws.readyState` directly. */\n get isReportingUpstream(): boolean {\n return this.isConnected()\n }\n\n /** Subclasses can read this to gate retry/reconnect logic. */\n protected hasEverConnected(): boolean {\n return this.#hasConnected\n }\n\n /**\n * Send a CommandLog over the WS. If the entry already has an `_id` (set by\n * the adapter's `captureCommand` during buffering), use it; otherwise\n * allocate a fresh one. The `_id` is the de-dup key and is stripped from\n * the broadcast payload — it's adapter-internal bookkeeping.\n * Returns the id, or 0 if the entry had no `_id` and none could be assigned.\n */\n sendCommand(command: CommandLog & { _id?: number }): number {\n if (command._id === undefined) {\n command._id = this.commandCounter++\n }\n const id = command._id\n if (this.sentCommandIds.has(id)) {\n return id\n }\n this.sentCommandIds.add(id)\n const toSend = { ...command }\n delete toSend._id\n this.sendUpstream('commands', [toSend])\n return id\n }\n\n /**\n * Emit a `replaceCommand` event swapping an earlier entry in-place. Strips\n * the adapter-internal `_id` field before sending — that's bookkeeping for\n * the local `sentCommandIds` set and shouldn't reach the UI.\n */\n sendReplaceCommand(\n oldTimestamp: number,\n command: CommandLog & { _id?: number }\n ): void {\n const toSend = { ...command }\n delete toSend._id\n this.sendUpstream(WS_SCOPE.replaceCommand, {\n oldTimestamp,\n command: toSend\n })\n }\n\n /**\n * Read a file from disk, store in `sources`, and broadcast to the UI via\n * `sendUpstream('sources', { [path]: text })`. Idempotent — a cached path is\n * a no-op. Read errors are logged via `onSourceReadError` (default: silent)\n * so a missing source never aborts capture.\n */\n async captureSource(filePath: string): Promise<void> {\n if (this.sources.has(filePath)) {\n return\n }\n try {\n const source = (await fs.readFile(filePath, 'utf-8')).toString()\n this.sources.set(filePath, source)\n this.sendUpstream('sources', { [filePath]: source })\n } catch (err) {\n this.onSourceReadError(filePath, err)\n }\n }\n\n /**\n * Hook fired when `captureSource` can't read a file. Default: silent.\n * Subclasses (nightwatch, selenium) override to log a warning.\n */\n protected onSourceReadError(_filePath: string, _err: unknown): void {\n // no-op — service silently swallows; subclasses can opt into a log line.\n }\n\n /**\n * Ingest the `{ mutations, traceLogs, consoleLogs, networkRequests, metadata }`\n * payload returned by the page-side `wdioTraceCollector.getTraceData()`.\n * Tags console logs with `source: 'browser'`, pushes each array into the\n * matching local field, and broadcasts via the appropriate WS scopes.\n *\n * `skipConsoleLogs` / `skipNetworkRequests` opt out when an out-of-band\n * channel (BiDi) is already delivering those streams — without the gate\n * the dashboard would see each entry twice.\n */\n protected processTracePayload(\n payload: {\n mutations?: unknown\n traceLogs?: unknown\n consoleLogs?: unknown\n networkRequests?: unknown\n metadata?: unknown\n },\n opts: { skipConsoleLogs?: boolean; skipNetworkRequests?: boolean } = {}\n ): void {\n const { mutations, traceLogs, consoleLogs, networkRequests, metadata } =\n payload\n\n if (metadata && typeof metadata === 'object') {\n // Page-side trace data is a JS bag; only fields that match Metadata\n // survive at runtime, but TS can't prove that. Cast to Partial<Metadata>\n // so the merge stays type-checked while accepting incomplete payloads.\n this.metadata = {\n ...this.metadata,\n ...(metadata as Partial<Metadata>)\n } as Metadata\n this.sendUpstream('metadata', this.metadata)\n }\n\n if (\n !opts.skipConsoleLogs &&\n Array.isArray(consoleLogs) &&\n consoleLogs.length > 0\n ) {\n const tagged = (consoleLogs as ConsoleLog[]).map((entry) => ({\n ...entry,\n source: LOG_SOURCES.BROWSER as LogSource\n }))\n this.consoleLogs.push(...tagged)\n this.sendUpstream('consoleLogs', tagged)\n }\n\n if (\n !opts.skipNetworkRequests &&\n Array.isArray(networkRequests) &&\n networkRequests.length > 0\n ) {\n const reqs = networkRequests as NetworkRequest[]\n this.networkRequests.push(...reqs)\n this.sendUpstream('networkRequests', reqs)\n }\n\n if (Array.isArray(mutations) && mutations.length > 0) {\n this.mutations.push(...mutations)\n this.sendUpstream('mutations', mutations)\n }\n\n if (Array.isArray(traceLogs) && traceLogs.length > 0) {\n const logs = traceLogs as string[]\n this.traceLogs.push(...logs)\n this.sendUpstream('logs', logs)\n }\n }\n\n /**\n * Resolve when the WS reaches OPEN state, or `false` on timeout / error.\n * Returns immediately if already open. Used by adapters that need a\n * synchronization barrier before injecting page-side scripts.\n */\n async waitForConnection(timeoutMs = 5000): Promise<boolean> {\n if (!this.ws) {\n return false\n }\n if (this.ws.readyState === WebSocket.OPEN) {\n return true\n }\n return new Promise((resolve) => {\n const timeout = setTimeout(() => resolve(false), timeoutMs)\n this.ws!.once('open', () => {\n clearTimeout(timeout)\n resolve(true)\n })\n this.ws!.once('error', () => {\n clearTimeout(timeout)\n resolve(false)\n })\n })\n }\n\n /**\n * Gracefully close the WS, waiting up to 2s for buffered messages to flush.\n * Call before process exit in reuse mode (or after dashboard close) so the\n * backend sees a clean close instead of an abrupt TCP reset.\n */\n async closeWebSocket(): Promise<void> {\n if (!this.ws || this.ws.readyState === WebSocket.CLOSED) {\n return\n }\n return new Promise<void>((resolve) => {\n const timeout = setTimeout(resolve, 2000)\n this.ws!.once('close', () => {\n clearTimeout(timeout)\n resolve()\n })\n this.ws!.close()\n })\n }\n\n /**\n * Restore console/streams. Does NOT close the WS — that's the subclass's\n * call (see `closeWebSocket` on nightwatch/selenium). Closing here would\n * break the wait-for-dashboard-close flow, since the worker WS is the\n * channel the backend uses to signal `clientDisconnected`.\n */\n cleanup(): void {\n this.restoreConsole()\n this.restoreStreams()\n }\n\n // ── Patching (call from subclass constructor) ───────────────────────────\n /** Patch `console.log/info/warn/error` to forward through `onLine`. */\n protected patchConsole(): void {\n CONSOLE_METHODS.forEach((method) => {\n const original = this.#originalConsoleMethods[method]\n console[method] = (...args: unknown[]) => {\n this.#isCapturingConsole = true\n const result = original.apply(console, args)\n this.#isCapturingConsole = false\n\n const serialized = args.map((a) =>\n typeof a === 'object' && a !== null ? safeStringify(a) : String(a)\n )\n const joined = stripAnsi(serialized.join(' ')).trim()\n if (!joined || this.isInternalStreamLine(joined)) {\n return result\n }\n // Pass the per-arg serialized array (`['payload', '{\"x\":1}']`) rather\n // than the joined string. The dashboard's `#formatArgs` joins on its\n // own; preserving the array form is lossless and lets future consumers\n // group/style individual args.\n this.onLine(method as LogLevel, serialized, LOG_SOURCES.TEST)\n return result\n }\n })\n }\n\n /**\n * Wrap `process.stdout.write` and `process.stderr.write` to forward chunks\n * through `onLine`. The base's `#isCapturingConsole` flag prevents\n * re-entrance when console patching itself writes to stdout.\n */\n protected patchStreams(): void {\n const captureChunk = (raw: string | Uint8Array) => {\n if (this.#isCapturingStream) {\n return\n }\n const text = typeof raw === 'string' ? raw : raw.toString()\n if (!text?.trim()) {\n return\n }\n this.#isCapturingStream = true\n try {\n for (const rawLine of text.split('\\n')) {\n // Strip CR-overwrites so progress bars don't show partial frames.\n const segments = rawLine.split('\\r').filter((s) => s.trim())\n const lastSegment = segments[segments.length - 1] ?? rawLine\n const clean = stripAnsi(lastSegment).trim()\n if (\n !clean ||\n this.isInternalStreamLine(clean) ||\n SPINNER_RE.test(clean)\n ) {\n continue\n }\n this.onLine(detectLogLevel(clean), [clean], LOG_SOURCES.TERMINAL)\n }\n } finally {\n this.#isCapturingStream = false\n }\n }\n\n // `stream.write` has Node's complex multi-overload signature that\n // doesn't unify with a generic `unknown[]` rest type — keep the\n // original ref typed by the stream itself.\n const wrap = (\n stream: NodeJS.WriteStream,\n original: NodeJS.WriteStream['write']\n ) => {\n const capturer = this\n // `stream.write` has Node's multi-overload signature that's hard to\n // satisfy with a single function expression — cast to the stream's\n // own `write` member type rather than `any`.\n stream.write = function (chunk: unknown, ...rest: unknown[]): boolean {\n // Cast original to a permissive shape — Node's multi-overload\n // signature for `stream.write` can't be unified with the `unknown`-\n // typed chunk we receive at the wrap boundary.\n const writeAny = original as unknown as (...a: unknown[]) => boolean\n const result = writeAny.call(stream, chunk, ...rest)\n if (chunk && !capturer.#isCapturingConsole) {\n captureChunk(chunk as string | Uint8Array)\n }\n return result\n } as typeof stream.write\n }\n\n wrap(process.stdout, this.#originalStdoutWrite)\n wrap(process.stderr, this.#originalStderrWrite)\n }\n\n protected restoreConsole(): void {\n CONSOLE_METHODS.forEach((method) => {\n console[method] = this.#originalConsoleMethods[method]\n })\n }\n\n protected restoreStreams(): void {\n // Restoring the pre-patch references — the typed write signature differs\n // slightly from the runtime instance type after `.bind()`, hence the cast\n // through the stream's own `write` member type.\n process.stdout.write = this\n .#originalStdoutWrite as typeof process.stdout.write\n process.stderr.write = this\n .#originalStderrWrite as typeof process.stderr.write\n }\n\n // ── Hooks (subclasses override) ─────────────────────────────────────────\n /**\n * Build a ConsoleLog entry from the captured line, push it into the local\n * `consoleLogs` array (so it ends up in any future trace export), and\n * broadcast it live via the `consoleLogs` WS scope.\n *\n * Args is passed as an array (matching the original console.* call shape:\n * `console.log('a', 'b')` → `args = ['a', 'b']`) so the multi-argument\n * structure is preserved for the UI.\n */\n protected onLine(type: LogLevel, args: string[], source: LogSource): void {\n const entry = createConsoleLogEntry(type, args, source)\n this.consoleLogs.push(entry)\n this.sendUpstream('consoleLogs', [entry])\n }\n\n /**\n * Default delegates to {@link isInternalStreamLine} from `./console.js`.\n * Subclasses can override to add framework-specific filters.\n */\n protected isInternalStreamLine(line: string): boolean {\n return isInternalStreamLine(line)\n }\n\n /** Hook: WS opened. Subclasses override to send a handshake, etc. */\n protected onWsOpen(): void {}\n\n /** Hook: WS errored before opening (likely no backend listening). */\n protected onWsError(_err: unknown): void {}\n\n /** Hook: WS closed (after open, or as a result of cleanup). */\n protected onWsClose(): void {}\n\n /**\n * Hook: WS message received from the backend. Currently used by selenium's\n * `awaitClientConnected` to know when a dashboard tab has subscribed.\n */\n protected onWsMessage(_msg: unknown): void {}\n}\n\nfunction safeStringify(value: unknown): string {\n try {\n return JSON.stringify(value)\n } catch {\n return String(value)\n }\n}\n","/**\n * Shared helpers for per-spec trace partitioning.\n *\n * The three adapters (service, nightwatch, selenium) all use the same\n * index-range tracking and spec-name sanitization logic. This module\n * is the single source of truth — adapters import from here.\n */\n\nimport type {\n ActionSnapshot,\n TestMetadataMap,\n TraceFormat,\n TraceGranularity\n} from '@wdio/devtools-shared'\nimport type { TraceCapturer } from './trace-exporter.js'\nimport { writeTraceZip } from './trace-exporter.js'\nimport { deterministicUid } from './uid.js'\n\n// ─── SpecRange ────────────────────────────────────────────────────────────────\n\n/** Index ranges into a SessionCapturer's flat arrays for a single spec file. */\nexport interface SpecRange {\n specFile: string\n commandStartIdx: number\n consoleStartIdx: number\n networkStartIdx: number\n mutationStartIdx: number\n traceLogStartIdx: number\n snapshotCount: number\n}\n\n// ─── Spec name sanitization ───────────────────────────────────────────────────\n\n/**\n * Sanitize a spec file path into a directory-safe identifier.\n * Strips directory, extension, and unsafe characters.\n * Falls back to `'unknown-spec'` when the result is empty.\n */\nexport function sanitizeSpecName(specFile: string): string {\n return (\n specFile\n .replace(/^.*[/\\\\]/, '')\n .replace(/\\.[^.]+$/, '')\n .replace(/[^a-zA-Z0-9_-]/g, '_')\n .replace(/^_+|_+$/g, '') || 'unknown-spec'\n )\n}\n\n// ─── Spec session ID ──────────────────────────────────────────────────────────\n\n/**\n * Build a collision-safe spec-level session ID from a spec file path and\n * the parent session ID. Includes an 8-char base36 hash of the full spec\n * path so two specs with the same basename in different directories don't\n * collide.\n */\nexport function buildSpecSessionId(\n specFile: string,\n sessionId: string\n): string {\n const base = sanitizeSpecName(specFile)\n const hash = deterministicUid(specFile).split('-').pop()!.slice(0, 8)\n return `${base}-${hash}-${sessionId.slice(0, 8)}`\n}\n\n// ─── TraceCapturer slice ─────────────────────────────────────────────────────\n\n/**\n * Build a TraceCapturer that contains only the data for one spec file,\n * sliced from the parent capturer's flat arrays using index ranges.\n * The returned `sources` is a shallow clone so mutations to the parent's\n * source map after this call don't affect the spec-level capturer.\n */\nexport function buildSpecCapturer(\n capturer: TraceCapturer,\n range: SpecRange,\n nextRange?: SpecRange\n): TraceCapturer {\n const end = nextRange\n ? {\n commands: nextRange.commandStartIdx,\n console: nextRange.consoleStartIdx,\n network: nextRange.networkStartIdx,\n mutations: nextRange.mutationStartIdx,\n traceLogs: nextRange.traceLogStartIdx\n }\n : undefined\n\n return {\n mutations: capturer.mutations.slice(range.mutationStartIdx, end?.mutations),\n traceLogs: capturer.traceLogs.slice(range.traceLogStartIdx, end?.traceLogs),\n consoleLogs: capturer.consoleLogs.slice(\n range.consoleStartIdx,\n end?.console\n ),\n networkRequests: capturer.networkRequests.slice(\n range.networkStartIdx,\n end?.network\n ),\n commandsLog: capturer.commandsLog.slice(\n range.commandStartIdx,\n end?.commands\n ),\n sources: new Map(capturer.sources),\n metadata: capturer.metadata,\n startWallTime: capturer.startWallTime\n }\n}\n\n// ─── Test metadata filtering ──────────────────────────────────────────────────\n\n/**\n * Filter a full `testUid → metadata` map to only include entries whose\n * `specFile` matches the given value. Used when flushing a per-spec trace\n * to attach only that spec's test titles as tracingGroup names.\n */\nexport function filterTestMetadataBySpec(\n allMetadata: TestMetadataMap,\n specFile: string\n): TestMetadataMap {\n const filtered: TestMetadataMap = new Map()\n for (const [uid, meta] of allMetadata) {\n if (meta.specFile === specFile) {\n filtered.set(uid, meta)\n }\n }\n return filtered\n}\n\n// ─── Spec boundary recording ──────────────────────────────────────────────────\n\n/**\n * Minimal context needed by `recordSpecBoundary` to detect spec-file\n * transitions and capture array index ranges.\n */\nexport interface SpecBoundaryContext {\n specRanges: SpecRange[]\n flushedSpecs: Set<string>\n capturer: {\n commandsLog: ArrayLike<unknown>\n consoleLogs: ArrayLike<unknown>\n networkRequests: ArrayLike<unknown>\n mutations: ArrayLike<unknown>\n traceLogs: ArrayLike<unknown>\n }\n actionSnapshots: ArrayLike<unknown>\n}\n\n/**\n * Record a spec-file boundary. When `traceGranularity` is `'spec'` and the\n * spec file has changed, this pushes a new `SpecRange` and returns the\n * previous range so the caller can flush its trace artifact.\n *\n * Returns `null` when no flush is needed (same spec, or granularity isn't\n * `'spec'`, or no capturer).\n */\nexport function recordSpecBoundary(\n ctx: SpecBoundaryContext,\n specFile: string,\n traceGranularity: TraceGranularity | undefined\n): SpecRange | null {\n if (traceGranularity !== 'spec') {\n return null\n }\n const lastRange = ctx.specRanges[ctx.specRanges.length - 1]\n if (lastRange && lastRange.specFile === specFile) {\n return null\n }\n\n const prevRange =\n lastRange && !ctx.flushedSpecs.has(lastRange.specFile) ? lastRange : null\n\n ctx.specRanges.push({\n specFile,\n commandStartIdx: ctx.capturer.commandsLog.length,\n consoleStartIdx: ctx.capturer.consoleLogs.length,\n networkStartIdx: ctx.capturer.networkRequests.length,\n mutationStartIdx: ctx.capturer.mutations.length,\n traceLogStartIdx: ctx.capturer.traceLogs.length,\n snapshotCount: ctx.actionSnapshots.length\n })\n\n return prevRange\n}\n\n// ─── Spec trace I/O ────────────────────────────────────────────────────────────\n\n/**\n * Pre-resolved inputs for `writeSpecTrace`. The caller gathers\n * adapter-specific state (session ID, output directory, test metadata …)\n * and passes it in; this function does the framework-agnostic slicing,\n * session-ID derivation, filtering, and disk I/O.\n */\nexport interface WriteSpecTraceInput {\n range: SpecRange\n nextRange?: SpecRange\n /** Shape-compatible with `buildSpecCapturer`'s first parameter. */\n capturer: Parameters<typeof buildSpecCapturer>[0]\n actionSnapshots: ActionSnapshot[]\n sessionId: string\n outputDir: string\n format?: TraceFormat\n /** Full test-metadata map (all specs); filtered to `range.specFile` internally. */\n testMetadata: TestMetadataMap\n capabilities?: unknown\n}\n\n/**\n * Write a standalone trace artifact (zip or ndjson-directory) for a single\n * spec file. This is the shared I/O path — all three adapters delegate to it\n * from their own `flushSpecTrace` wrappers.\n */\nexport async function writeSpecTrace(\n input: WriteSpecTraceInput\n): Promise<string> {\n const specCapturer = buildSpecCapturer(\n input.capturer,\n input.range,\n input.nextRange\n )\n\n const specSnapshots = input.actionSnapshots.slice(\n input.range.snapshotCount,\n input.nextRange?.snapshotCount ?? input.actionSnapshots.length\n )\n\n const specSessionId = buildSpecSessionId(\n input.range.specFile,\n input.sessionId\n )\n\n const testMetadata = filterTestMetadataBySpec(\n input.testMetadata,\n input.range.specFile\n )\n\n return writeTraceZip(specCapturer, {\n outputDir: input.outputDir,\n sessionId: specSessionId,\n capabilities: input.capabilities,\n actionSnapshots: specSnapshots.length > 0 ? specSnapshots : undefined,\n format: input.format,\n testMetadata\n })\n}\n","import type { SuiteStats, TestStats, TestStatus } from '@wdio/devtools-shared'\nimport { TEST_STATE } from '@wdio/devtools-shared'\n\n/**\n * Pure factories + state computations shared by the per-adapter SuiteManager\n * and TestManager classes. Pattern A from CLAUDE.md §the-four-patterns —\n * stateless helpers each adapter calls; no base class because storage\n * strategy differs (selenium uses a single root suite + optional Cucumber\n * sub-suites; nightwatch uses a Map keyed by test file).\n */\n\nexport interface SuiteStatsInit {\n uid: string\n title: string\n file: string\n cid?: string\n callSource?: string\n featureFile?: string\n parent?: string\n start?: Date\n state?: TestStatus\n}\n\nexport interface TestStatsInit {\n uid: string\n title: string\n file: string\n parent: string\n cid?: string\n fullTitle?: string\n callSource?: string\n state?: TestStatus\n start?: Date\n}\n\n/**\n * Build a SuiteStats with the standard defaults (empty children arrays,\n * RUNNING state, fresh start time). Adapters override fields as needed\n * before/after the call.\n */\nexport function createSuiteStats(init: SuiteStatsInit): SuiteStats {\n return {\n uid: init.uid,\n cid: init.cid ?? '0-0',\n title: init.title,\n fullTitle: init.title,\n file: init.file,\n type: 'suite',\n start: init.start ?? new Date(),\n state: init.state ?? TEST_STATE.RUNNING,\n end: null,\n tests: [],\n suites: [],\n hooks: [],\n _duration: 0,\n callSource: init.callSource,\n featureFile: init.featureFile,\n parent: init.parent\n }\n}\n\n/** Build a TestStats with the standard defaults. */\nexport function createTestStats(init: TestStatsInit): TestStats {\n return {\n uid: init.uid,\n cid: init.cid ?? '0-0',\n title: init.title,\n fullTitle: init.fullTitle ?? init.title,\n parent: init.parent,\n state: init.state ?? TEST_STATE.RUNNING,\n start: init.start ?? new Date(),\n end: null,\n type: 'test',\n file: init.file,\n retries: 0,\n _duration: 0,\n hooks: [],\n callSource: init.callSource\n }\n}\n\n/**\n * \"Permissive\" finalize: any failed child → FAILED, otherwise PASSED.\n * Matches selenium's policy — RUNNING tests don't fail the suite.\n */\nexport function computeSuiteFinalStatePermissive(\n suite: SuiteStats\n): TestStatus {\n const failedDirect = suite.tests.some(\n (t) => typeof t !== 'string' && t.state === TEST_STATE.FAILED\n )\n const failedNested = (suite.suites ?? []).some(\n (s) => s.state === TEST_STATE.FAILED\n )\n return failedDirect || failedNested ? TEST_STATE.FAILED : TEST_STATE.PASSED\n}\n\n/**\n * \"Strict\" finalize: any failed → FAILED; all PASSED/SKIPPED → PASSED;\n * empty suite → PASSED; orphaned RUNNING tests → FAILED. Matches nightwatch's\n * policy — incomplete runs are surfaced as failures.\n */\nexport function computeSuiteFinalStateStrict(suite: SuiteStats): TestStatus {\n const tests = suite.tests as TestStats[]\n const suites = suite.suites ?? []\n const hasFailures =\n tests.some((t) => t.state === TEST_STATE.FAILED) ||\n suites.some((s) => s.state === TEST_STATE.FAILED)\n if (hasFailures) {\n return TEST_STATE.FAILED\n }\n const allPassed =\n tests.every(\n (t) => t.state === TEST_STATE.PASSED || t.state === TEST_STATE.SKIPPED\n ) &&\n suites.every(\n (s) => s.state === TEST_STATE.PASSED || s.state === TEST_STATE.SKIPPED\n )\n const hasSkipped = tests.some((t) => t.state === TEST_STATE.SKIPPED)\n const hasItems = tests.length > 0 || suites.length > 0\n if (!hasItems || allPassed) {\n return TEST_STATE.PASSED\n }\n if (hasSkipped) {\n return TEST_STATE.PASSED\n }\n return TEST_STATE.FAILED\n}\n\n/**\n * In-progress state computation: if any child is FAILED → FAILED;\n * else if any RUNNING / unfinished → RUNNING; else → PASSED. Used to keep\n * a parent suite's state fresh while children are still executing\n * (Cucumber feature suite updates as each scenario completes).\n */\nexport function computeSuiteRunningState(suite: SuiteStats): TestStatus {\n const tests = suite.tests as TestStats[]\n const suites = suite.suites ?? []\n const hasFailures =\n tests.some((t) => t.state === TEST_STATE.FAILED) ||\n suites.some((s) => s.state === TEST_STATE.FAILED)\n if (hasFailures) {\n return TEST_STATE.FAILED\n }\n const hasRunning =\n tests.some((t) => t.state === TEST_STATE.RUNNING) ||\n suites.some((s) => s.state === TEST_STATE.RUNNING || !s.end)\n return hasRunning ? TEST_STATE.RUNNING : TEST_STATE.PASSED\n}\n\n/**\n * Stamp `end` + `_duration` on a suite. Pure mutation — kept here so the\n * arithmetic stays in one place; callers still own the state assignment.\n */\nexport function stampSuiteEnd(suite: SuiteStats, end: Date = new Date()): void {\n suite.end = end\n suite._duration = end.getTime() - (suite.start?.getTime() || end.getTime())\n}\n\n/** Stamp `end` + `_duration` on a test. */\nexport function stampTestEnd(test: TestStats, end: Date = new Date()): void {\n test.end = end\n test._duration = end.getTime() - (test.start?.getTime() ?? end.getTime())\n}\n","import fs from 'node:fs'\n\n/**\n * One test/suite definition discovered in a source file by line-regex scan.\n * Adapter-agnostic shape — both selenium and nightwatch consume this.\n */\nexport interface TestDefinition {\n kind: 'suite' | 'test'\n title: string\n line: number\n}\n\n/**\n * Regex-scan a JS/TS test file for `describe('...')` / `it('...')` style\n * definitions (and Mocha/Jest aliases — `suite`, `context`, `test`,\n * `specify`). Adapters call this from line-based helpers that resolve a\n * call-source `file:line` for the dashboard's TestLens / Actions tab.\n *\n * Set `includeNightwatchObjectStyle` to also match the object-export shape\n * Nightwatch's `--yes` scaffolder generates:\n *\n * 'My test name': () => { ... }\n * 'My test name': async function () { ... }\n *\n * Returns definitions in source order. Unreadable / unparseable files yield\n * an empty array — the dashboard already degrades to `file:0` in that case.\n */\nexport function findTestDefinitions(\n filePath: string,\n opts: { includeNightwatchObjectStyle?: boolean } = {}\n): TestDefinition[] {\n if (!fs.existsSync(filePath)) {\n return []\n }\n let source: string\n try {\n source = fs.readFileSync(filePath, 'utf-8')\n } catch {\n return []\n }\n\n const out: TestDefinition[] = []\n const lines = source.split('\\n')\n\n const suiteRe = /\\b(?:describe|suite|context)\\s*\\(\\s*['\"`]([^'\"`]+)['\"`]/\n const testRe = /\\b(?:it|test|specify)\\s*\\(\\s*['\"`]([^'\"`]+)['\"`]/\n // Nightwatch object-export: `'Title': () => { ... }` or `: function () {`\n const objRe =\n /^\\s*['\"`]([^'\"`]+)['\"`]\\s*:\\s*(?:async\\s+)?(?:\\([^)]*\\)\\s*=>|function\\s*\\()/\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]\n const lineNum = i + 1\n\n const suiteMatch = line.match(suiteRe)\n if (suiteMatch) {\n out.push({ kind: 'suite', title: suiteMatch[1], line: lineNum })\n continue\n }\n const testMatch = line.match(testRe)\n if (testMatch) {\n out.push({ kind: 'test', title: testMatch[1], line: lineNum })\n continue\n }\n if (opts.includeNightwatchObjectStyle) {\n const objMatch = line.match(objRe)\n if (objMatch) {\n out.push({ kind: 'test', title: objMatch[1], line: lineNum })\n }\n }\n }\n return out\n}\n\n/**\n * Convenience: find the line number where a specific test/suite is defined.\n * Returns null if the file or title isn't found. Used by adapter call-source\n * resolution from hooks where the user's stack frame isn't reachable.\n */\nexport function findTestLineInFile(\n filePath: string,\n title: string,\n kind: 'test' | 'suite' = 'test'\n): number | null {\n const defs = findTestDefinitions(filePath)\n return defs.find((d) => d.kind === kind && d.title === title)?.line ?? null\n}\n","import type { SuiteStats, TestStats } from '@wdio/devtools-shared'\nimport { resetSignatureCounters } from './uid.js'\n\n/**\n * Shape of the payload sent upstream — one record per suite, keyed by UID,\n * so the UI can merge it into its existing suite map without scanning.\n */\nexport type ReporterUpstreamPayload = Record<string, SuiteStats>[]\nexport type ReporterUpstream = (data: ReporterUpstreamPayload) => void\n\n/**\n * Foundation class for adapter TestReporters. Owns the cross-framework\n * scaffolding (suite collection, upstream batching). Framework-specific\n * lifecycle hooks (spec-file scanning, UID generation, skipped-test\n * synthesis) stay in subclasses.\n *\n * Service uses the WDIO reporter base instead — this class is for adapters\n * that own their own reporter lifecycle (nightwatch, selenium).\n */\nexport abstract class TestReporterBase {\n #report: ReporterUpstream\n protected allSuites: SuiteStats[] = []\n\n constructor(report: ReporterUpstream) {\n this.#report = report\n resetSignatureCounters()\n }\n\n /** Swap the upstream sink, e.g. after a WS reconnect. */\n updateUpstream(report: ReporterUpstream): void {\n this.#report = report\n }\n\n /** Manually trigger a flush of current state to the UI. */\n updateSuites(): void {\n this.sendUpstream()\n }\n\n /**\n * Reset collected state. Subclasses with extra state (test-name cache,\n * current-suite ref) override and call `super.clearExecutionData()` first.\n */\n clearExecutionData(): void {\n this.allSuites = []\n resetSignatureCounters()\n }\n\n /** Default: find by `uid`, replace in place. */\n onTestEnd(test: TestStats): void {\n for (const suite of this.allSuites) {\n const idx = suite.tests.findIndex(\n (t) => typeof t !== 'string' && t.uid === test.uid\n )\n if (idx !== -1) {\n suite.tests[idx] = test\n break\n }\n }\n this.sendUpstream()\n }\n\n /** Default: just flush. Subclasses with skipped-test synthesis override. */\n onSuiteEnd(_suite: SuiteStats): void {\n this.sendUpstream()\n }\n\n get report(): SuiteStats[] {\n return this.allSuites\n }\n\n /**\n * Flush current suite state to the upstream callback. Empty-payload guard\n * matches the existing adapter behavior — UI shouldn't receive an empty\n * array.\n */\n protected sendUpstream(): void {\n const payload: ReporterUpstreamPayload = []\n for (const suite of this.allSuites) {\n if (suite.uid) {\n payload.push({ [suite.uid]: suite })\n }\n }\n if (payload.length > 0) {\n this.#report(payload)\n }\n }\n}\n","/**\n * Run-level lifecycle helpers for the Nightwatch plugin — reuse-mode setup,\n * DevTools-browser spawning, end-of-run summary, and the post-run \"wait for\n * browser close\" loop.\n *\n * Extracted from `index.ts` to keep that file under the file-size cap.\n */\n\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport * as os from 'node:os'\nimport logger from '@wdio/logger'\nimport { remote } from 'webdriverio'\nimport { errorMessage } from '@wdio/devtools-core'\nimport { REUSE_ENV } from '@wdio/devtools-shared'\nimport { start, stop } from '@wdio/devtools-backend'\n\nimport type { SessionCapturer } from './session.js'\nimport type { TestReporter } from './reporter.js'\nimport type { SuiteManager } from './helpers/suiteManager.js'\nimport type { TestManager } from './helpers/testManager.js'\nimport type {\n DevToolsMode,\n NightwatchBrowser,\n NightwatchCurrentTest\n} from './types.js'\nimport { TIMING, PLUGIN_GLOBAL_KEY } from './constants.js'\nimport { findFreePort, resolveNightwatchConfig } from './helpers/utils.js'\n\nconst log = logger('@wdio/nightwatch-devtools:run-lifecycle')\n\nexport interface RunLifecycleCtx {\n options: { hostname: string; port: number; mode?: DevToolsMode }\n readonly testReporter: TestReporter | undefined\n readonly suiteManager: SuiteManager | undefined\n readonly testManager: TestManager\n readonly sessionCapturer: SessionCapturer | undefined\n devtoolsBrowser: WebdriverIO.Browser | undefined\n userDataDir: string | undefined\n passCount: number\n failCount: number\n skipCount: number\n clearExecutionData(): void\n}\n\nexport function handleReuseMode(ctx: RunLifecycleCtx): void {\n ctx.options.hostname = process.env[REUSE_ENV.HOST]!\n ctx.options.port = Number(process.env[REUSE_ENV.PORT])\n log.info(\n `♻ Reusing DevTools backend at ${ctx.options.hostname}:${ctx.options.port}`\n )\n // Clear execution data from the previous run when rerunning so test-name\n // caches and suites are fresh for the new run.\n if (ctx.testReporter) {\n ctx.clearExecutionData()\n ctx.passCount = 0\n ctx.failCount = 0\n ctx.skipCount = 0\n log.info('Cleared execution data for rerun')\n }\n}\n\nexport interface PluginBeforeCtx extends RunLifecycleCtx {\n setConfigPath(v: string | undefined): void\n openDevtoolsBrowserAt(url: string): Promise<void>\n handleReuse(): void\n // The plugin instance to assign to the global slot for cucumber hooks.\n plugin: unknown\n}\n\nexport async function runPluginBefore(ctx: PluginBeforeCtx): Promise<void> {\n // When relaunched by the DevTools UI rerun button the backend is already\n // running — skip startup and just connect the WebSocket worker.\n const isReuse =\n process.env[REUSE_ENV.REUSE] === '1' &&\n !!process.env[REUSE_ENV.HOST] &&\n !!process.env[REUSE_ENV.PORT]\n if (isReuse) {\n ctx.handleReuse()\n }\n const configPath = resolveNightwatchConfig()\n ctx.setConfigPath(configPath)\n if (configPath) {\n log.info(`✓ Config: ${configPath}`)\n } else {\n log.warn(\n 'Could not find nightwatch config — test rerun will be unavailable'\n )\n }\n if (isReuse) {\n ;(globalThis as Record<string, unknown>)[PLUGIN_GLOBAL_KEY] = ctx.plugin\n return\n }\n try {\n // Trace mode: skip backend port-bind and UI entirely — matches the WDIO\n // launcher gate. SessionCapturer construction in session-init also gates\n // its WS init off in trace mode.\n if (ctx.options.mode === 'trace') {\n log.info('Trace mode — skipping backend port-bind and UI window')\n } else {\n ctx.options.port = await findFreePort(\n ctx.options.port,\n ctx.options.hostname\n )\n log.info('🚀 Starting DevTools backend...')\n const { port } = await start(ctx.options)\n ctx.options.port = port\n const url = `http://${ctx.options.hostname}:${ctx.options.port}`\n log.info(`✓ Backend started on port ${ctx.options.port}`)\n log.info(` DevTools UI: ${url}`)\n await ctx.openDevtoolsBrowserAt(url)\n await new Promise((resolve) =>\n setTimeout(resolve, TIMING.UI_CONNECTION_WAIT)\n )\n }\n ;(globalThis as Record<string, unknown>)[PLUGIN_GLOBAL_KEY] = ctx.plugin\n } catch (err) {\n log.error(`Failed to start backend: ${errorMessage(err)}`)\n throw err\n }\n}\n\nexport async function openDevtoolsBrowser(\n ctx: RunLifecycleCtx,\n url: string\n): Promise<void> {\n try {\n // Unique user data directory per instance to prevent conflicts.\n ctx.userDataDir = path.join(\n os.tmpdir(),\n `nightwatch-devtools-${ctx.options.port}-${Date.now()}`\n )\n if (!fs.existsSync(ctx.userDataDir)) {\n fs.mkdirSync(ctx.userDataDir, { recursive: true })\n }\n ctx.devtoolsBrowser = await remote({\n logLevel: 'info',\n automationProtocol: 'devtools',\n capabilities: {\n browserName: 'chrome',\n 'goog:chromeOptions': {\n args: [\n '--window-size=1600,1200',\n `--user-data-dir=${ctx.userDataDir}`,\n '--no-first-run',\n '--no-default-browser-check'\n ]\n },\n // Dashboard uses the Puppeteer-based 'devtools' protocol; drop the\n // \"controlled by automated test software\" infobar by ignoring\n // Puppeteer's --enable-automation default. (Runtime-valid capability\n // not present in the type.)\n 'wdio:devtoolsOptions': {\n ignoreDefaultArgs: ['--enable-automation']\n }\n } as WebdriverIO.Capabilities\n })\n await ctx.devtoolsBrowser.url(url)\n } catch (err) {\n log.error(`Failed to open DevTools UI: ${errorMessage(err)}`)\n log.info(`Please manually open: ${url}`)\n }\n}\n\nexport async function finalizeAllSuites(\n ctx: RunLifecycleCtx,\n browser?: NightwatchBrowser\n): Promise<void> {\n const currentTest = browser?.currentTest as NightwatchCurrentTest | undefined\n const testcases = currentTest?.results?.testcases ?? {}\n for (const [, suite] of (\n ctx.suiteManager?.getAllSuites() ?? new Map()\n ).entries()) {\n ctx.testManager.finalizeSuiteTests(suite, testcases)\n await new Promise((resolve) =>\n setTimeout(resolve, TIMING.SUITE_COMPLETE_DELAY)\n )\n ctx.suiteManager?.finalizeSuite(suite)\n }\n await new Promise((resolve) =>\n setTimeout(resolve, TIMING.SUITE_COMPLETE_DELAY)\n )\n}\n\nexport function logRunSummary(ctx: RunLifecycleCtx): void {\n const summary = [\n ctx.passCount > 0 ? `${ctx.passCount} passed` : null,\n ctx.failCount > 0 ? `${ctx.failCount} failed` : null,\n ctx.skipCount > 0 ? `${ctx.skipCount} skipped` : null\n ]\n .filter(Boolean)\n .join(' ')\n log.info(`${ctx.failCount > 0 ? '❌' : '✅'} Tests complete! ${summary}`)\n log.info(` DevTools UI: http://${ctx.options.hostname}:${ctx.options.port}`)\n}\n\nexport async function waitForDevtoolsBrowserClose(\n ctx: RunLifecycleCtx\n): Promise<void> {\n if (!ctx.devtoolsBrowser) {\n return\n }\n ;(logger as { setLevel: (ns: string, lvl: string) => void }).setLevel(\n 'devtools',\n 'warn'\n )\n let exitBySignal = false\n const signalHandler = () => {\n exitBySignal = true\n log.info('\\n✓ Exiting... Browser window will remain open')\n process.exit(0)\n }\n process.once('SIGINT', signalHandler)\n process.once('SIGTERM', signalHandler)\n while (true) {\n try {\n await ctx.devtoolsBrowser.getTitle()\n await new Promise((res) => setTimeout(res, TIMING.BROWSER_POLL_INTERVAL))\n } catch {\n if (!exitBySignal) {\n log.info('Browser window closed, stopping DevTools app')\n break\n }\n }\n }\n if (exitBySignal) {\n return\n }\n process.removeListener('SIGINT', signalHandler)\n process.removeListener('SIGTERM', signalHandler)\n ;(logger as { setLevel: (ns: string, lvl: string) => void }).setLevel(\n 'devtools',\n 'info'\n )\n try {\n await ctx.devtoolsBrowser.deleteSession()\n } catch {\n /* session already closed */\n }\n await stop()\n process.exit(0)\n}\n","/**\n * Internal Nightwatch commands to exclude from capture\n */\nexport const INTERNAL_COMMANDS_TO_IGNORE = [\n 'isAppiumClient',\n 'isSafari',\n 'isChrome',\n 'isFirefox',\n 'isEdge',\n 'isMobile',\n 'isAndroid',\n 'isIOS',\n 'session',\n 'sessions',\n 'timeouts',\n 'timeoutsAsyncScript',\n 'timeoutsImplicitWait',\n 'getLog',\n 'getLogTypes',\n 'screenshot',\n 'availableContexts',\n 'currentContext',\n 'setChromeOptions',\n 'setDeviceName',\n 'perform',\n 'execute',\n 'executeAsync',\n 'executeScript',\n // Internal Nightwatch transport commands (used for log capture, not user actions)\n 'sessionLog',\n 'sessionLogTypes',\n 'isLogAvailable',\n 'end'\n] as const\n\n// Console capture constants are defined in @wdio/devtools-core; re-exported\n// here so existing imports from ./constants.js continue to work.\nexport {\n ANSI_REGEX,\n CONSOLE_METHODS,\n LOG_LEVEL_PATTERNS,\n LOG_SOURCES\n} from '@wdio/devtools-core'\n\nimport { DEFAULTS_BASE, TIMING_BASE } from '@wdio/devtools-shared'\n\nexport const DEFAULTS = {\n ...DEFAULTS_BASE,\n TEST_NAME: 'unknown',\n FILE_NAME: 'unknown'\n} as const\n\nexport const TIMING = {\n ...TIMING_BASE,\n /** Nightwatch boots slower than selenium — give the dashboard 10s. */\n UI_CONNECTION_WAIT: 10000\n} as const\n\nexport { TEST_STATE } from '@wdio/devtools-shared'\n\n/**\n * Generic pattern matching Nightwatch commands whose result is a boolean.\n */\nexport const BOOLEAN_COMMAND_PATTERN =\n /^waitFor|^is[A-Z]|^has[A-Z]|(Visible|Present|Enabled|Selected|NotVisible|NotPresent)$/\n\nexport const NAVIGATION_COMMANDS = ['url', 'navigate', 'navigateTo'] as const\n\nexport { SPINNER_RE } from '@wdio/devtools-core'\n\n/** Matches file names that follow the *.test.ts / *.spec.js naming\n * convention. Re-exported from @wdio/devtools-shared (single source of\n * truth — service uses the same pattern under the name SPEC_FILE_RE). */\nexport { SPEC_FILE_RE as TEST_FILE_PATTERN } from '@wdio/devtools-shared'\n\n/** Nightwatch config file names to search for, in priority order. */\nexport const CONFIG_FILENAMES = [\n 'nightwatch.conf.cjs',\n 'nightwatch.conf.js',\n 'nightwatch.conf.ts',\n 'nightwatch.conf.mjs',\n 'nightwatch.json'\n] as const\n\n/**\n * Global key used to share the plugin instance with Cucumber hooks.\n * Must match across index.ts and cucumberHooks.cts.\n */\nexport const PLUGIN_GLOBAL_KEY = '__nightwatchDevtoolsPlugin'\n","import * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport { parse as parseStackTrace } from 'stacktrace-parser'\nimport {\n findTestDefinitions,\n generateStableUid as generateStableUidByFileName,\n isUserCodeFrame,\n normalizeFilePath\n} from '@wdio/devtools-core'\nimport { TEST_FILE_PATTERN, CONFIG_FILENAMES } from '../constants.js'\nimport type {\n NightwatchTestCase,\n TestFileMetadata,\n StepLocation,\n TestStats\n} from '../types.js'\n\n// These three are pure re-exports — adapters use the core implementations\n// directly, no wrapper logic. Single-line re-exports keep the indirection\n// visible without introducing dummy variables.\nexport {\n deterministicUid,\n getCallSourceFromStack,\n resetSignatureCounters\n} from '@wdio/devtools-core'\n\nexport function determineTestState(\n testcase: NightwatchTestCase\n): 'passed' | 'failed' | 'skipped' {\n if (testcase.passed === 0 && testcase.failed === 0) {\n return 'skipped'\n }\n return testcase.passed > 0 && testcase.failed === 0 ? 'passed' : 'failed'\n}\n\nexport function getTestIcon(state: TestStats['state']): string {\n return state === 'passed' ? '✅' : state === 'skipped' ? '⏭' : '❌'\n}\n\nexport function incrementCounters(\n counters: { passCount: number; failCount: number; skipCount: number },\n state: TestStats['state']\n): void {\n if (state === 'passed') {\n counters.passCount++\n } else if (state === 'skipped') {\n counters.skipCount++\n } else {\n counters.failCount++\n }\n}\n\nexport function buildPluginMetadataOptions(input: {\n isCucumberRunner: boolean\n configPath: string | undefined\n}) {\n return {\n framework: input.isCucumberRunner ? 'nightwatch-cucumber' : 'nightwatch',\n configFile: input.configPath,\n baseDir: process.cwd(),\n runCapabilities: {\n canRunSuites: true,\n canRunTests: !input.isCucumberRunner,\n canRunAll: false\n }\n }\n}\n\n/**\n * Generate stable UID for test/suite.\n * Accepts either (item: SuiteStats | TestStats) or (file: string, name: string).\n * Hashing is delegated to @wdio/devtools-core; this wrapper preserves the\n * dual-signature convenience used by the Nightwatch suite/test managers.\n */\ntype StableUidSource = { file?: string; fullTitle?: string; title?: string }\nexport function generateStableUid(\n itemOrFile: string | StableUidSource,\n name?: string\n): string {\n let file: string, testName: string\n if (\n typeof itemOrFile === 'object' &&\n itemOrFile !== null &&\n name === undefined\n ) {\n file = itemOrFile.file || ''\n testName = String(itemOrFile.fullTitle || itemOrFile.title)\n } else {\n file = (itemOrFile as string) || ''\n testName = String(name || '')\n }\n return generateStableUidByFileName(file, testName)\n}\n\n/**\n * Find test file from stack trace.\n * Parses call stack to find the first frame that looks like a test file.\n */\nexport function findTestFileFromStack(): string | undefined {\n const stack = new Error().stack\n if (!stack) {\n return undefined\n }\n\n // Prefer a frame whose filename matches *.test.ts / *.spec.ts (strong\n // signal). Fall back to the first plain user-code frame so arbitrary\n // project layouts (e.g. Cucumber step files under `src/steps/`) still\n // surface their owning file instead of going undetected.\n const frames = parseStackTrace(stack)\n const preferred = frames.find(\n (f) => isUserCodeFrame(f) && TEST_FILE_PATTERN.test(f.file)\n )\n const frame = preferred ?? frames.find(isUserCodeFrame)\n if (!frame?.file) {\n return undefined\n }\n\n const filePath = normalizeFilePath(frame.file)\n return fs.existsSync(filePath) ? filePath : undefined\n}\n\n/**\n * Extract suite and test names — and their line numbers — from a test file.\n * Returns a TestFileMetadata object used to build `callSource` strings for\n * the TestLens eye-icon navigation.\n */\nexport function extractTestMetadata(filePath: string): TestFileMetadata {\n const defs = findTestDefinitions(filePath, {\n includeNightwatchObjectStyle: true\n })\n const firstSuite = defs.find((d) => d.kind === 'suite')\n const tests = defs.filter((d) => d.kind === 'test')\n return {\n suiteTitle: firstSuite?.title ?? null,\n suiteLine: firstSuite?.line ?? null,\n testNames: tests.map((t) => t.title),\n testLines: tests.map((t) => t.line)\n }\n}\n\n/**\n * Find test file by searching the workspace for a matching filename.\n * Used when the stack trace doesn't have the file yet (e.g. in beforeEach).\n */\nexport function findTestFileByName(\n filename: string,\n workspaceRoot?: string\n): string | undefined {\n if (!filename || !workspaceRoot) {\n return undefined\n }\n\n const baseFilename = filename.replace(/\\.[cm]?[jt]sx?$/, '')\n\n function searchDir(dir: string, depth = 0): string | undefined {\n if (depth > 5) {\n return undefined\n }\n try {\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const fullPath = path.join(dir, entry.name)\n if (entry.isDirectory()) {\n const found = searchDir(fullPath, depth + 1)\n if (found) {\n return found\n }\n } else if (\n TEST_FILE_PATTERN.test(entry.name) &&\n entry.name.replace(TEST_FILE_PATTERN, '') === baseFilename\n ) {\n return fullPath\n }\n }\n } catch {\n // Permission denied or other error — skip\n }\n return undefined\n }\n\n return searchDir(workspaceRoot)\n}\n\n// ---------------------------------------------------------------------------\n// Console / log helpers (used by SessionCapturer)\n// ---------------------------------------------------------------------------\n\n// Console helpers come from @wdio/devtools-core. `stripAnsiCodes` is the\n// local name kept for backwards compatibility with existing import sites.\nexport {\n stripAnsi as stripAnsiCodes,\n detectLogLevel,\n createConsoleLogEntry\n} from '@wdio/devtools-core'\n\nexport { chromeLogLevelToLogLevel } from '@wdio/devtools-core'\n\n/** Derive a human-readable request type from URL and MIME type. */\nexport { getRequestType } from '@wdio/devtools-core'\n\n// ---------------------------------------------------------------------------\n// Cucumber helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Parse a feature file for a given scenario in a single pass, returning:\n * - `featureLine` — 1-based line of the `Feature:` declaration\n * - `scenarioLine` — 1-based line of the matching `Scenario:` block\n * - `stepLines` — 1-based line numbers for each step (for TestLens navigation)\n * - `stepKeywords` — BDD keyword (Given/When/Then/And/But) for each step (for labels)\n */\nfunction collectStepsAfterScenario(\n lines: string[],\n scenarioIndex: number,\n stepCount: number\n): { stepLines: number[]; stepKeywords: string[] } {\n const stepRe = /^\\s*(Given|When|Then|And|But)\\s+/i\n const stepLines: number[] = []\n const stepKeywords: string[] = []\n for (\n let j = scenarioIndex + 1;\n j < lines.length && stepLines.length < stepCount;\n j++\n ) {\n if (/^\\s*(?:Scenario:|Feature:)/i.test(lines[j])) {\n break\n }\n const m = stepRe.exec(lines[j])\n if (m) {\n stepLines.push(j + 1)\n stepKeywords.push(m[1])\n }\n }\n return { stepLines, stepKeywords }\n}\n\nexport function parseCucumberScenario(\n featureContent: string,\n scenarioName: string,\n stepTexts: string[]\n): {\n featureLine: number\n scenarioLine: number\n stepLines: number[]\n stepKeywords: string[]\n} {\n const stepCount = stepTexts.length\n if (!featureContent) {\n return {\n featureLine: 1,\n scenarioLine: 1,\n stepLines: [],\n stepKeywords: Array<string>(stepCount).fill('')\n }\n }\n const lines = featureContent.split('\\n')\n let featureLine = 1\n let scenarioLine = 1\n let stepLines: number[] = []\n let stepKeywords: string[] = []\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]\n const lineNum = i + 1\n if (featureLine === 1 && /^\\s*Feature:/i.test(line)) {\n featureLine = lineNum\n continue\n }\n if (/^\\s*Scenario:/i.test(line) && line.includes(scenarioName)) {\n scenarioLine = lineNum\n const collected = collectStepsAfterScenario(lines, i, stepCount)\n stepLines = collected.stepLines\n stepKeywords = collected.stepKeywords\n break\n }\n }\n while (stepKeywords.length < stepCount) {\n stepKeywords.push('')\n }\n return { featureLine, scenarioLine, stepLines, stepKeywords }\n}\n\n/** Maps Cucumber parameter type placeholder names to their regex equivalents. */\nconst CUCUMBER_TYPE_PATTERNS: Record<string, string> = {\n int: '-?\\\\d+',\n float: '-?\\\\d+(?:\\\\.\\\\d+)?',\n string: '(?:\"[^\"]*\"|\\'[^\\']*\\')',\n word: '\\\\S+'\n}\n\n/**\n * Convert a Cucumber expression pattern to a regular expression and test it\n * against the given step text. Handles {int}, {float}, {string}, {word}, and\n * arbitrary {type} placeholders.\n */\nfunction matchesCucumberExpression(pattern: string, text: string): boolean {\n if (pattern === text) {\n return true\n }\n const regexSource = pattern\n .split(/(\\{[^}]*\\})/)\n .map((part, idx) => {\n if (idx % 2 === 1) {\n const type = part.slice(1, -1)\n return CUCUMBER_TYPE_PATTERNS[type] ?? '[\\\\s\\\\S]+'\n }\n return part.replace(/[|\\\\{}()[\\]^$+*?.]/g, '\\\\$&')\n })\n .join('')\n try {\n return new RegExp(`^${regexSource}$`).test(text)\n } catch {\n return false\n }\n}\n\n/**\n * Find the file and 1-based line number of the step definition that matches\n * the given step text. Searches the provided step definition files in order.\n * Handles both string/Cucumber-expression patterns and regex literals.\n */\nexport function findStepDefinitionLine(\n stepDefFiles: Array<{ filePath: string; content: string }>,\n stepText: string\n): StepLocation | null {\n const stepDefRe = /(?:Given|When|Then|And|But)\\s*\\(/i\n\n for (const { filePath, content } of stepDefFiles) {\n const lines = content.split('\\n')\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]\n if (!stepDefRe.test(line)) {\n continue\n }\n\n // String / Cucumber-expression: Given('pattern', ...) or Given(\"pattern\", ...)\n const literalMatch = line.match(\n /(?:Given|When|Then|And|But)\\s*\\(\\s*(['\"`])(.*?)\\1/i\n )\n if (literalMatch) {\n if (matchesCucumberExpression(literalMatch[2], stepText)) {\n return { filePath, line: i + 1 }\n }\n continue\n }\n\n // Regex literal: Given(/pattern/flags, ...)\n const regexMatch = line.match(\n /(?:Given|When|Then|And|But)\\s*\\(\\s*\\/(.+?)\\/([gimy]*)/i\n )\n if (regexMatch) {\n try {\n if (new RegExp(regexMatch[1], regexMatch[2]).test(stepText)) {\n return { filePath, line: i + 1 }\n }\n } catch {\n // invalid regex — skip\n }\n }\n }\n }\n return null\n}\n\nexport { isPortInUse, findFreePort } from '@wdio/devtools-core'\n\nexport function resolveNightwatchConfig(): string | undefined {\n // Prefer the config explicitly passed via -c / --config to avoid picking up\n // an unrelated config file that happens to sit higher in the directory tree.\n const configFlagIdx = process.argv.findIndex(\n (arg) => arg === '--config' || arg === '-c'\n )\n if (configFlagIdx !== -1 && process.argv[configFlagIdx + 1]) {\n const argvConfig = process.argv[configFlagIdx + 1]\n const resolved = path.isAbsolute(argvConfig)\n ? argvConfig\n : path.resolve(process.cwd(), argvConfig)\n if (fs.existsSync(resolved)) {\n return resolved\n }\n }\n\n // Fallback: walk up the directory tree\n let dir = process.cwd()\n const root = path.parse(dir).root\n while (dir && dir !== root) {\n for (const file of CONFIG_FILENAMES) {\n const candidate = path.join(dir, file)\n if (fs.existsSync(candidate)) {\n return candidate\n }\n }\n const parent = path.dirname(dir)\n if (parent === dir) {\n break\n }\n dir = parent\n }\n return undefined\n}\n","/**\n * Nightwatch eventHub integration.\n *\n * Extracted from the plugin class so registration stays out of the\n * file-size cap and the metadata-forwarding behavior is unit-testable\n * without standing up the whole plugin. The plugin's\n * `registerEventHandlers(eventHub)` delegates here.\n */\n\nimport logger from '@wdio/logger'\nimport { errorMessage } from '@wdio/devtools-core'\nimport { TraceType } from './types.js'\nimport type { SessionCapturer } from './session.js'\nimport type { NightwatchEventHub } from './types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:event-hub')\n\nexport interface EventHubBindings {\n /** Live session capturer — may be undefined until bringup completes. */\n getSessionCapturer(): SessionCapturer | undefined\n /** Builds the `options` field forwarded with the metadata payload. */\n buildMetadataOptions(): unknown\n /** Lets the plugin flip the cucumber-runner flag on detection. */\n setCucumberRunner(value: boolean): void\n}\n\nfunction makeSessionMetadataHandler(\n bindings: EventHubBindings\n): (data: unknown) => void {\n return (data: unknown) => {\n try {\n const md =\n ((data as { metadata?: Record<string, unknown> } | undefined)\n ?.metadata as Record<string, unknown> | undefined) ?? {}\n const capturer = bindings.getSessionCapturer()\n const sessionCapabilities = md.sessionCapabilities\n const sessionId = md.sessionId as string | undefined\n if (!capturer || (!sessionCapabilities && !sessionId)) {\n return\n }\n capturer.sendUpstream('metadata', {\n type: TraceType.Testrunner,\n capabilities: sessionCapabilities ?? {},\n sessionId,\n testEnv: md.testEnv as string | undefined,\n host: md.host as string | undefined,\n modulePath: md.modulePath as string | undefined,\n options: bindings.buildMetadataOptions()\n })\n } catch (err) {\n log.error(`Error in event handler: ${errorMessage(err)}`)\n }\n }\n}\n\nexport function registerEventHandlers(\n eventHub: NightwatchEventHub,\n bindings: EventHubBindings\n): void {\n bindings.setCucumberRunner(eventHub.runner === 'cucumber')\n if (eventHub.runner === 'cucumber') {\n log.info('✓ Cucumber runner detected via NightwatchEventHub')\n }\n log.info('✓ NightwatchEventHub registered — enriched metadata enabled')\n const handler = makeSessionMetadataHandler(bindings)\n eventHub.on('TestSuiteStarted', handler)\n eventHub.on('TestRunStarted', handler)\n}\n","/**\n * Cucumber lifecycle for the Nightwatch plugin.\n *\n * Extracted from the plugin class to keep `index.ts` under the file-size cap\n * and to isolate the cucumber-specific orchestration from the per-test\n * (Mocha/Jasmine-style) lifecycle.\n *\n * The plugin passes itself as a `CucumberLifecycleCtx` — a narrow interface\n * exposing only the fields and methods this module needs. The lifecycle\n * helpers mutate the plugin's \"current execution\" state via the accessors on\n * the ctx (they can't reach the plugin's private fields directly).\n */\n\nimport logger from '@wdio/logger'\nimport { errorMessage } from '@wdio/devtools-core'\nimport { WS_SCOPE } from '@wdio/devtools-shared'\n\nimport type { SessionCapturer } from './session.js'\nimport type { TestReporter } from './reporter.js'\nimport type { TestManager } from './helpers/testManager.js'\nimport type { SuiteManager } from './helpers/suiteManager.js'\nimport type { BrowserProxy } from './helpers/browserProxy.js'\nimport type { NightwatchBrowser, SuiteStats, TestStats } from './types.js'\nimport { TEST_STATE } from './constants.js'\nimport {\n closeOpenSteps,\n cucumberResultToTestState\n} from './helpers/cucumberResult.js'\nimport { buildCucumberScenarioSuite } from './helpers/cucumberScenarioBuilder.js'\nimport { scanFeatureFile } from './helpers/featureFileScan.js'\nimport { parseCucumberScenario } from './helpers/utils.js'\n\nconst log = logger('@wdio/nightwatch-devtools:cucumber')\n\n/** Minimal shapes for the Cucumber objects we touch. Cucumber's own types\n * vary across major versions; we pin only fields we read. */\nexport interface CucumberPickleStep {\n text?: string\n astNodeIds?: string[]\n location?: { line?: number }\n}\nexport interface CucumberPickle {\n uri?: string\n name?: string\n location?: { line?: number }\n astNodeIds?: string[]\n steps?: CucumberPickleStep[]\n}\nexport interface CucumberResult {\n status?: string\n}\n\nexport interface CucumberLifecycleCtx {\n readonly sessionCapturer: SessionCapturer\n readonly testReporter: TestReporter\n readonly testManager: TestManager\n readonly suiteManager: SuiteManager\n readonly browserProxy: BrowserProxy | undefined\n setCucumberRunner(v: boolean): void\n ensureSessionInitialized(browser: NightwatchBrowser): Promise<void>\n wrapBrowserOnce(browser: NightwatchBrowser): void\n incrementCount(state: TestStats['state']): void\n testIcon(state: TestStats['state']): string\n getCurrentScenarioSuite(): SuiteStats | null\n setCurrentScenarioSuite(s: SuiteStats | null): void\n setCurrentStep(s: unknown): void\n getCurrentStep(): unknown\n setCurrentTest(t: unknown): void\n}\n\ntype MutStep = {\n title?: string\n state?: string\n start?: Date | null\n end?: Date | null\n _duration?: number\n}\n\nfunction attachScenarioToFeature(\n ctx: CucumberLifecycleCtx,\n featureSuite: SuiteStats,\n scenarioSuite: SuiteStats\n): void {\n // If a suite with this uid already exists, this is a RETRY of the same\n // scenario — clear execution data so only the latest attempt shows.\n const existingIdx = featureSuite.suites.findIndex(\n (s: SuiteStats) => s.uid === scenarioSuite.uid\n )\n if (existingIdx !== -1) {\n featureSuite.suites[existingIdx] = scenarioSuite\n // Pass the specific scenario uid so only this scenario's execution data\n // is reset — a uid-less clearExecutionData would mark ALL suites as\n // running, destroying the previous terminal states of sibling scenarios.\n ctx.sessionCapturer.sendUpstream(WS_SCOPE.clearExecutionData, {\n uid: scenarioSuite.uid,\n entryType: 'suite'\n })\n } else {\n featureSuite.suites.push(scenarioSuite)\n }\n}\n\nfunction createFeatureSuite(\n ctx: CucumberLifecycleCtx,\n featureUri: string,\n featureName: string,\n featureContent: string,\n featureAbsPath: string,\n scenarioName: string,\n steps: Array<{ text: string }>\n): {\n featureSuite: SuiteStats\n scenarioLine: number\n stepLines: number[]\n stepKeywords: string[]\n} {\n const featureSuite = ctx.suiteManager.getOrCreateSuite(\n featureUri,\n featureName,\n featureUri,\n []\n )\n ctx.suiteManager.markSuiteAsRunning(featureSuite)\n const { featureLine, scenarioLine, stepLines, stepKeywords } =\n parseCucumberScenario(\n featureContent,\n scenarioName,\n steps.map((s) => s.text)\n )\n if (featureAbsPath && featureLine > 0) {\n featureSuite.callSource = `${featureAbsPath}:${featureLine}`\n }\n return { featureSuite, scenarioLine, stepLines, stepKeywords }\n}\n\nfunction normalizeSteps(\n pickleSteps: CucumberPickleStep[] | undefined\n): Array<{ text: string }> {\n return (pickleSteps ?? []).map((s) => ({ text: s.text ?? '' }))\n}\n\nexport async function initCucumberScenario(\n ctx: CucumberLifecycleCtx,\n browser: NightwatchBrowser,\n pickle: CucumberPickle\n): Promise<void> {\n await ctx.ensureSessionInitialized(browser)\n const featureUri: string = pickle.uri ?? 'unknown.feature'\n const scenarioName: string = pickle.name ?? 'Unknown Scenario'\n const steps = normalizeSteps(pickle.steps)\n const {\n featureName,\n featureContent,\n featureAbsPath,\n stepDefFiles,\n capturedPaths\n } = scanFeatureFile(featureUri)\n for (const p of capturedPaths) {\n ctx.sessionCapturer.captureSource(p).catch(() => {})\n }\n const { featureSuite, scenarioLine, stepLines, stepKeywords } =\n createFeatureSuite(\n ctx,\n featureUri,\n featureName,\n featureContent,\n featureAbsPath,\n scenarioName,\n steps\n )\n const scenarioSuite = buildCucumberScenarioSuite({\n featureUri,\n scenarioName,\n featureName,\n featureAbsPath,\n stepDefFiles,\n steps,\n stepLines,\n stepKeywords,\n scenarioLine,\n parentFeatureSuiteUid: featureSuite.uid\n })\n attachScenarioToFeature(ctx, featureSuite, scenarioSuite)\n ctx.setCurrentScenarioSuite(scenarioSuite)\n ctx.setCurrentStep(null)\n ctx.setCurrentTest(null)\n ctx.testReporter.updateSuites()\n ctx.wrapBrowserOnce(browser)\n log.info(`🥒 Scenario: ${scenarioName}`)\n}\n\nexport async function finalizeCucumberScenario(\n ctx: CucumberLifecycleCtx,\n browser: NightwatchBrowser,\n result: CucumberResult,\n pickle: CucumberPickle | undefined\n): Promise<void> {\n try {\n const scenarioState = cucumberResultToTestState(result)\n const scenario = ctx.getCurrentScenarioSuite()\n if (scenario) {\n const now = new Date()\n const duration =\n now.getTime() - (scenario.start?.getTime() ?? now.getTime())\n scenario.state = scenarioState\n scenario.end = now\n scenario._duration = duration\n closeOpenSteps(scenario, scenarioState, now)\n\n const featureUri: string = pickle?.uri ?? 'unknown.feature'\n ctx.testManager.markTestAsProcessed(featureUri, pickle?.name ?? '')\n\n const featureSuite = ctx.suiteManager.getSuite(featureUri)\n if (featureSuite) {\n // Finalize is not called until all scenarios are done — just update state.\n ctx.suiteManager.finalizeSuiteState(featureSuite)\n }\n\n ctx.incrementCount(scenarioState)\n const icon = ctx.testIcon(scenarioState)\n log.info(\n ` ${icon} ${pickle?.name ?? 'Unknown'} (${(duration / 1000).toFixed(2)}s)`\n )\n\n ctx.testReporter.updateSuites()\n ctx.setCurrentScenarioSuite(null)\n ctx.setCurrentStep(null)\n ctx.setCurrentTest(null)\n }\n await ctx.sessionCapturer.captureTrace(browser)\n } catch (err) {\n log.error(`Failed to finalize Cucumber scenario: ${errorMessage(err)}`)\n }\n}\n\nexport async function cucumberBeforeStep(\n ctx: CucumberLifecycleCtx,\n _browser: NightwatchBrowser,\n pickleStep: CucumberPickleStep,\n _pickle: CucumberPickle\n): Promise<void> {\n const scenario = ctx.getCurrentScenarioSuite()\n if (!scenario) {\n return\n }\n // Reset per-step dedup tracking so commands in step N are never\n // mistaken for retries of identically-signatured commands from step N-1.\n ctx.browserProxy?.resetCommandTracking()\n\n const stepText: string = pickleStep?.text ?? ''\n const step = (scenario.tests as Array<MutStep | string>).find(\n (t): t is MutStep =>\n typeof t !== 'string' &&\n (t.title?.endsWith(stepText) === true || t.title === stepText)\n )\n if (step) {\n step.state = TEST_STATE.RUNNING\n step.start = new Date()\n step.end = null\n ctx.setCurrentStep(step)\n ctx.testReporter.updateSuites()\n }\n}\n\nexport async function cucumberAfterStep(\n ctx: CucumberLifecycleCtx,\n _browser: NightwatchBrowser,\n result: CucumberResult,\n pickleStep: CucumberPickleStep,\n _pickle: CucumberPickle\n): Promise<void> {\n const step = ctx.getCurrentStep() as MutStep | null\n if (!step) {\n return\n }\n const status = String(result?.status ?? 'UNKNOWN').toUpperCase()\n const stepState: TestStats['state'] =\n status === 'PASSED'\n ? TEST_STATE.PASSED\n : status === 'SKIPPED'\n ? TEST_STATE.SKIPPED\n : TEST_STATE.FAILED\n step.state = stepState\n step.end = new Date()\n step._duration = Date.now() - (step.start?.getTime() ?? Date.now())\n ctx.setCurrentStep(null)\n ctx.testReporter.updateSuites()\n void pickleStep\n}\n\nexport async function cucumberBefore(\n ctx: CucumberLifecycleCtx,\n browser: NightwatchBrowser,\n pickle: CucumberPickle\n): Promise<void> {\n ctx.setCucumberRunner(true)\n await initCucumberScenario(ctx, browser, pickle)\n}\n\nexport async function cucumberAfter(\n ctx: CucumberLifecycleCtx,\n browser: NightwatchBrowser,\n result: CucumberResult,\n pickle: CucumberPickle\n): Promise<void> {\n await finalizeCucumberScenario(ctx, browser, result, pickle)\n}\n","import { TEST_STATE } from '../constants.js'\nimport type { SuiteStats, TestStats } from '../types.js'\n\n/**\n * Map a Cucumber Pickle step result's `status` field (PASSED/FAILED/SKIPPED/\n * UNKNOWN/PENDING/AMBIGUOUS/UNDEFINED) to the dashboard's TestStats state.\n * Everything non-passed and non-skipped is treated as FAILED for the UI —\n * the underlying error/message survives in the pickle for the Compare view.\n */\nexport function cucumberResultToTestState(result: unknown): TestStats['state'] {\n const status = String(\n (result as { status?: unknown })?.status ?? 'UNKNOWN'\n ).toUpperCase()\n if (status === 'PASSED') {\n return TEST_STATE.PASSED\n }\n if (status === 'SKIPPED') {\n return TEST_STATE.SKIPPED\n }\n return TEST_STATE.FAILED\n}\n\n/**\n * Cucumber's After hook fires with the scenario's final status, but any\n * still-running or pending steps (e.g. an early failure short-circuited\n * the rest) need to be closed too. Mark them PASSED only when the whole\n * scenario passed; FAILED otherwise. Pure in-place mutation — the suite's\n * `tests` array references are the same TestStats objects the dashboard\n * already received via earlier WS broadcasts.\n */\nexport function closeOpenSteps(\n suite: SuiteStats,\n scenarioState: TestStats['state'],\n now: Date = new Date()\n): void {\n for (const step of suite.tests) {\n if (typeof step === 'string') {\n continue\n }\n if (step.state === 'running' || step.state === 'pending') {\n step.state =\n scenarioState === TEST_STATE.PASSED\n ? TEST_STATE.PASSED\n : TEST_STATE.FAILED\n step.end = now\n }\n }\n}\n","import { DEFAULTS, TEST_STATE } from '../constants.js'\nimport type { SuiteStats } from '../types.js'\nimport { deterministicUid, findStepDefinitionLine } from './utils.js'\n\nexport interface CucumberScenarioBuildInput {\n /** Cucumber pickle URI — the .feature path. */\n featureUri: string\n scenarioName: string\n featureName: string\n /** Absolute file path used for callSource resolution (may be unresolved). */\n featureAbsPath?: string\n /** Step-definition files discovered for the feature (passed to find each step's source location). */\n stepDefFiles: Array<{ filePath: string; content: string }>\n /** Pickle steps as parsed by Cucumber. */\n steps: Array<{ text: string }>\n /** Per-step source line numbers from the .feature file (parsed once by the caller). */\n stepLines: number[]\n /** Per-step Gherkin keywords (Given/When/Then/And/But) parsed alongside `stepLines`. */\n stepKeywords: string[]\n /** Scenario header line number in the .feature file (0 if unresolvable). */\n scenarioLine: number\n /** Parent feature-suite uid — scenarios nest under this. */\n parentFeatureSuiteUid: string\n}\n\n/**\n * Build a fully-populated scenario sub-suite (with its `tests` array of step\n * TestStats entries) for one Cucumber scenario. Pure factory — no side effects;\n * the caller decides whether to push into the feature suite or replace an\n * existing entry (retry case).\n *\n * Extracted from `NightwatchDevToolsPlugin.#initCucumberScenario` — the\n * scenario+steps construction was ~70 lines of object-literal building that\n * doesn't touch plugin state.\n */\nfunction buildScenarioStepTest(\n input: CucumberScenarioBuildInput,\n scenarioUid: string,\n i: number\n): SuiteStats['tests'][number] {\n const {\n featureUri,\n scenarioName,\n featureAbsPath,\n stepDefFiles,\n steps,\n stepLines,\n stepKeywords\n } = input\n const step = steps[i]\n const keyword = stepKeywords[i] || ''\n const stepLabel = keyword ? `${keyword} ${step.text}` : step.text\n const stepDefLoc = findStepDefinitionLine(stepDefFiles, step.text)\n const callSource = stepDefLoc\n ? `${stepDefLoc.filePath}:${stepDefLoc.line}`\n : featureAbsPath && stepLines[i] > 0\n ? `${featureAbsPath}:${stepLines[i]}`\n : undefined\n return {\n uid: deterministicUid(featureUri, `step:${scenarioName}:${step.text}`),\n cid: DEFAULTS.CID,\n title: stepLabel,\n fullTitle: `${scenarioName} ${stepLabel}`,\n parent: scenarioUid,\n state: TEST_STATE.PENDING,\n start: new Date(),\n end: null,\n type: 'test' as const,\n file: featureUri,\n retries: 0,\n _duration: 0,\n hooks: [],\n callSource\n }\n}\n\nexport function buildCucumberScenarioSuite(\n input: CucumberScenarioBuildInput\n): SuiteStats {\n const {\n featureUri,\n scenarioName,\n featureName,\n featureAbsPath,\n steps,\n scenarioLine,\n parentFeatureSuiteUid\n } = input\n // deterministicUid (no counter) so the SAME scenario gets the SAME uid\n // across retries — that's what makes retry-coalescing work upstream.\n const scenarioUid = deterministicUid(featureUri, `scenario:${scenarioName}`)\n const scenarioSuite: SuiteStats = {\n uid: scenarioUid,\n cid: DEFAULTS.CID,\n title: scenarioName,\n fullTitle: `${featureName} ${scenarioName}`,\n parent: parentFeatureSuiteUid,\n type: 'suite' as const,\n file: featureUri,\n start: new Date(),\n state: TEST_STATE.RUNNING,\n end: null,\n tests: [],\n suites: [],\n hooks: [],\n _duration: 0,\n callSource:\n featureAbsPath && scenarioLine > 0\n ? `${featureAbsPath}:${scenarioLine}`\n : undefined\n }\n for (let i = 0; i < steps.length; i++) {\n scenarioSuite.tests.push(buildScenarioStepTest(input, scenarioUid, i))\n }\n return scenarioSuite\n}\n","import fs from 'fs'\nimport path from 'node:path'\n\nexport interface FeatureFileScan {\n /** Header `Feature:` value, or the filename basename if unreadable. */\n featureName: string\n /** Raw `.feature` file contents (empty when unreadable). */\n featureContent: string\n /** Absolute path to the `.feature` file (resolved from cwd + uri). */\n featureAbsPath: string\n /** Sibling step-definition files (under `step_definitions`/`steps`/`support`). */\n stepDefFiles: Array<{ filePath: string; content: string }>\n /** Paths the caller should feed to `sessionCapturer.captureSource` so the\n * dashboard's Source panel can render them. */\n capturedPaths: string[]\n}\n\n/**\n * Scan a Cucumber feature file and its sibling step-definitions. Pure I/O —\n * the caller invokes `sessionCapturer.captureSource` for each path in\n * `capturedPaths` so this helper stays free of the session capturer.\n */\nexport function scanFeatureFile(featureUri: string): FeatureFileScan {\n const featureAbsPath = path.resolve(process.cwd(), featureUri)\n const result: FeatureFileScan = {\n featureName: path.basename(featureUri, '.feature'),\n featureContent: '',\n featureAbsPath,\n stepDefFiles: [],\n capturedPaths: []\n }\n\n if (featureUri === 'unknown.feature' || !fs.existsSync(featureAbsPath)) {\n return result\n }\n\n result.featureContent = fs.readFileSync(featureAbsPath, 'utf-8')\n const match = result.featureContent.match(/^\\s*Feature:\\s*(.+)/m)\n if (match) {\n result.featureName = match[1].trim()\n }\n result.capturedPaths.push(featureAbsPath)\n\n const featureDir = path.dirname(featureAbsPath)\n const stepDirCandidates = ['step_definitions', 'steps', 'support']\n for (const candidate of stepDirCandidates) {\n const stepDir = path.join(featureDir, candidate)\n if (!fs.existsSync(stepDir) || !fs.statSync(stepDir).isDirectory()) {\n continue\n }\n for (const entry of fs.readdirSync(stepDir)) {\n if (!/\\.(js|ts|mjs|cjs)$/.test(entry)) {\n continue\n }\n const stepFilePath = path.join(stepDir, entry)\n result.capturedPaths.push(stepFilePath)\n try {\n result.stepDefFiles.push({\n filePath: stepFilePath,\n content: fs.readFileSync(stepFilePath, 'utf-8')\n })\n } catch {\n // skip unreadable files\n }\n }\n }\n\n return result\n}\n","/**\n * Test (Mocha/Jasmine-style) lifecycle helpers for the Nightwatch plugin.\n *\n * Extracted from the plugin class to keep `index.ts` under the file-size cap\n * and to keep the per-test orchestration distinct from the cucumber path.\n *\n * The plugin passes itself as a `TestLifecycleCtx` — a narrow interface that\n * exposes only the fields and methods these helpers need.\n */\n\nimport logger from '@wdio/logger'\nimport type { SessionCapturer } from './session.js'\nimport type { TestReporter } from './reporter.js'\nimport type { TestManager } from './helpers/testManager.js'\nimport type { SuiteManager } from './helpers/suiteManager.js'\nimport type { BrowserProxy } from './helpers/browserProxy.js'\nimport type {\n NightwatchBrowser,\n NightwatchCurrentTest,\n NightwatchTestCase,\n NightwatchTestResults,\n SuiteStats,\n TestStats\n} from './types.js'\nimport { DEFAULTS, TIMING, TEST_STATE } from './constants.js'\nimport { resolveSpecFilePath } from './helpers/specFileResolver.js'\nimport { closePreviousTest } from './helpers/closePreviousTest.js'\nimport { extractTestMetadata, determineTestState } from './helpers/utils.js'\n\nconst log = logger('@wdio/nightwatch-devtools:test-lifecycle')\n\nexport interface TestLifecycleCtx {\n readonly sessionCapturer: SessionCapturer\n readonly testReporter: TestReporter\n readonly testManager: TestManager\n readonly suiteManager: SuiteManager\n readonly browserProxy: BrowserProxy\n readonly srcFolders: string[]\n isScriptInjected: boolean\n getRerunLabel(): string | undefined\n incrementCount(state: TestStats['state']): void\n testIcon(state: TestStats['state']): string\n setCurrentTest(t: unknown): void\n}\n\ninterface SuiteMetadata {\n testFile: string\n fullPath: string | null\n suiteTitle: string\n testNames: string[]\n suiteLine: number | null\n testLines: number[]\n}\n\nexport function resolveSuiteMetadata(\n ctx: TestLifecycleCtx,\n currentTest: NightwatchCurrentTest\n): SuiteMetadata {\n const moduleName = currentTest.module ?? ''\n const testFile =\n moduleName.split('/').pop() || moduleName || DEFAULTS.FILE_NAME\n const fullPath = resolveSpecFilePath(\n testFile,\n moduleName,\n ctx.srcFolders,\n ctx.browserProxy.getCurrentTestFullPath() || undefined\n )\n if (!fullPath) {\n log.warn(\n `[beforeEach] Could not resolve file path for \"${testFile}\" — source view will be unavailable`\n )\n }\n let suiteTitle = testFile\n let testNames: string[] = []\n let suiteLine: number | null = null\n let testLines: number[] = []\n if (fullPath) {\n const parsed = extractTestMetadata(fullPath)\n if (parsed.suiteTitle) {\n suiteTitle = parsed.suiteTitle\n }\n testNames = parsed.testNames\n suiteLine = parsed.suiteLine\n testLines = parsed.testLines\n }\n const rerunLabel = ctx.getRerunLabel()\n if (rerunLabel) {\n const targetIndex = testNames.findIndex((name) => name === rerunLabel)\n if (targetIndex !== -1) {\n testNames = [testNames[targetIndex]]\n testLines = testLines[targetIndex] ? [testLines[targetIndex]] : []\n }\n }\n return { testFile, fullPath, suiteTitle, testNames, suiteLine, testLines }\n}\n\nexport function pickCurrentTestName(\n currentTest: NightwatchCurrentTest,\n testNames: string[],\n processedTests: Set<string>\n): string | undefined {\n const runtimeTestName =\n typeof currentTest?.name === 'string' ? currentTest.name.trim() : undefined\n const matchedRuntimeTestName = runtimeTestName\n ? testNames.find(\n (name) =>\n runtimeTestName === name || runtimeTestName.endsWith(` ${name}`)\n )\n : undefined\n return (\n matchedRuntimeTestName ||\n testNames.find((name) => !processedTests.has(name))\n )\n}\n\nexport async function startNextTest(\n ctx: TestLifecycleCtx,\n currentSuite: SuiteStats,\n currentTestName: string,\n processedTests: Set<string>\n): Promise<void> {\n if (processedTests.size === 0) {\n ctx.suiteManager.markSuiteAsRunning(currentSuite)\n }\n const test = ctx.testManager.findTestInSuite(currentSuite, currentTestName)\n if (test) {\n test.state = TEST_STATE.RUNNING as TestStats['state']\n test.start = new Date()\n test.end = null\n ctx.testReporter.onTestStart(test)\n ctx.setCurrentTest(test)\n log.info(` ▶ ${currentTestName}`)\n await new Promise((resolve) => setTimeout(resolve, TIMING.TEST_START_DELAY))\n } else {\n log.warn(\n `Test \"${currentTestName}\" not found in suite \"${currentSuite.title}\"`\n )\n ctx.setCurrentTest(null)\n }\n}\n\nexport async function closePreviousRunningTest(\n ctx: TestLifecycleCtx,\n currentSuite: SuiteStats,\n testFile: string,\n currentTest: NightwatchCurrentTest\n): Promise<void> {\n const runningTest = currentSuite.tests.find(\n (t): t is TestStats =>\n typeof t !== 'string' && t.state === TEST_STATE.RUNNING\n )\n if (!runningTest) {\n return\n }\n await closePreviousTest({\n runningTest,\n testFile,\n testcases: currentTest?.results?.testcases || {},\n testManager: ctx.testManager,\n incrementCount: (state) => ctx.incrementCount(state),\n testIcon: (state) => ctx.testIcon(state)\n })\n}\n\nexport function wrapBrowserOnce(\n ctx: TestLifecycleCtx,\n browser: NightwatchBrowser\n): void {\n if (!ctx.isScriptInjected) {\n ctx.browserProxy.wrapUrlMethod(browser)\n ctx.isScriptInjected = true\n }\n ctx.browserProxy.resetCommandTracking()\n ctx.browserProxy.wrapBrowserCommands(browser)\n}\n\nfunction closeUnreportedRunningTest(\n ctx: TestLifecycleCtx,\n currentSuite: SuiteStats,\n testFile: string,\n results: NightwatchTestResults,\n processedTests: Set<string>\n): void {\n const runningTest = currentSuite.tests.find(\n (t): t is TestStats =>\n typeof t !== 'string' && t.state === TEST_STATE.RUNNING\n )\n if (!runningTest || processedTests.has(runningTest.title)) {\n return\n }\n const failed = (results.errors ?? 0) > 0 || (results.failed ?? 0) > 0\n const testState: TestStats['state'] = failed\n ? TEST_STATE.FAILED\n : TEST_STATE.PASSED\n const endTime = new Date()\n const duration = endTime.getTime() - (runningTest.start?.getTime() || 0)\n ctx.testManager.updateTestState(runningTest, testState, endTime, duration)\n ctx.testManager.markTestAsProcessed(testFile, runningTest.title)\n ctx.incrementCount(testState)\n const icon = ctx.testIcon(testState)\n log.info(` ${icon} ${runningTest.title} (${(duration / 1000).toFixed(2)}s)`)\n}\n\nasync function closeReportedTestcases(\n ctx: TestLifecycleCtx,\n currentSuite: SuiteStats,\n testFile: string,\n testcases: Record<string, NightwatchTestCase>,\n processedTests: Set<string>\n): Promise<void> {\n const testcaseNames = Object.keys(testcases)\n const unprocessedTests = testcaseNames.filter(\n (name) => !processedTests.has(name)\n )\n for (const currentTestName of unprocessedTests) {\n const testcase = testcases[currentTestName]\n const testState = determineTestState(testcase)\n const test = ctx.testManager.findTestInSuite(currentSuite, currentTestName)\n if (test) {\n const dur = parseFloat(testcase.time || '0') * 1000\n ctx.testManager.updateTestState(test, testState, new Date(), dur)\n ctx.incrementCount(testState)\n const icon = ctx.testIcon(testState)\n log.info(` ${icon} ${currentTestName} (${(dur / 1000).toFixed(2)}s)`)\n }\n ctx.testManager.markTestAsProcessed(testFile, currentTestName)\n }\n if (processedTests.size === testcaseNames.length) {\n ctx.suiteManager.finalizeSuite(currentSuite)\n await new Promise((resolve) =>\n setTimeout(resolve, TIMING.SUITE_COMPLETE_DELAY)\n )\n }\n}\n\nexport async function closeOutTestcases(\n ctx: TestLifecycleCtx,\n browser: NightwatchBrowser\n): Promise<void> {\n const currentTest = (browser.currentTest ?? {}) as NightwatchCurrentTest\n const results: NightwatchTestResults = currentTest.results ?? {}\n const moduleName = currentTest.module ?? ''\n const testFile = moduleName.split('/').pop() || DEFAULTS.FILE_NAME\n const testcases = results.testcases ?? {}\n const currentSuite = ctx.suiteManager.getSuite(testFile)\n if (!currentSuite) {\n return\n }\n const processedTests = ctx.testManager.getProcessedTests(testFile)\n if (Object.keys(testcases).length === 0) {\n closeUnreportedRunningTest(\n ctx,\n currentSuite,\n testFile,\n results,\n processedTests\n )\n } else {\n await closeReportedTestcases(\n ctx,\n currentSuite,\n testFile,\n testcases,\n processedTests\n )\n }\n}\n","import fs from 'fs'\nimport path from 'node:path'\nimport { findTestFileFromStack } from './utils.js'\n\n/**\n * Resolve a Nightwatch test's `currentTest.module` to an absolute spec-file\n * path on disk. Priority:\n * 1. Walk the runtime stack for a user frame.\n * 2. A cached path from a previous command on the same browser (browserProxy).\n * 3. Cartesian search across the user's `src_folders` + cwd fallbacks.\n *\n * Used by `beforeEach` to find the file that `extractTestMetadata` should\n * parse for test names + suite/test line numbers. Returns `null` when the\n * file can't be located on disk (source view falls back to \"unavailable\").\n */\nexport function resolveSpecFilePath(\n testFile: string,\n modulePath: string | undefined,\n srcFolders: string[],\n cachedPath: string | undefined\n): string | null {\n let fullPath: string | null = findTestFileFromStack() || null\n if (!fullPath && cachedPath && cachedPath.includes(testFile)) {\n fullPath = cachedPath\n }\n if (fullPath) {\n return fullPath\n }\n if (!testFile) {\n return null\n }\n\n const workspaceRoot = process.cwd()\n // `currentTest.module` is relative to a src_folder, e.g. `basic/ecosia`.\n // We try each src_folder + cwd-level fallback. Use `path.resolve` (not\n // `path.join`) so absolute src_folders entries — like\n // `path.resolve(__dirname, 'tests')` from a nightwatch.conf.cjs living\n // outside the package — bypass `workspaceRoot` correctly.\n const normalized = (modulePath || '').replace(/\\\\/g, '/')\n const srcFolderPaths = srcFolders.flatMap((sf) =>\n normalized\n ? [\n path.resolve(workspaceRoot, sf, normalized + '.js'),\n path.resolve(workspaceRoot, sf, normalized + '.ts'),\n path.resolve(workspaceRoot, sf, normalized + '.cjs'),\n path.resolve(workspaceRoot, sf, normalized)\n ]\n : []\n )\n const possiblePaths = [\n ...srcFolderPaths,\n // Treat module path as relative to cwd (works when src_folders isn't nested)\n ...(normalized\n ? [\n path.resolve(workspaceRoot, normalized + '.js'),\n path.resolve(workspaceRoot, normalized + '.ts'),\n path.resolve(workspaceRoot, normalized + '.cjs'),\n path.resolve(workspaceRoot, normalized)\n ]\n : []),\n path.resolve(workspaceRoot, 'tests', testFile + '.js'),\n path.resolve(workspaceRoot, 'test', testFile + '.js'),\n path.resolve(workspaceRoot, testFile + '.js')\n ]\n\n for (const candidate of possiblePaths) {\n if (fs.existsSync(candidate)) {\n return candidate\n }\n }\n return null\n}\n","import logger from '@wdio/logger'\nimport { TIMING, TEST_STATE } from '../constants.js'\nimport type { NightwatchTestCase, TestStats } from '../types.js'\nimport { determineTestState } from './utils.js'\nimport type { TestManager } from './testManager.js'\n\nconst log = logger('@wdio/nightwatch-devtools:closePreviousTest')\n\nexport interface ClosePreviousTestInput {\n runningTest: TestStats\n testFile: string\n /** `currentTest?.results?.testcases || {}` — Nightwatch's per-suite testcase results. */\n testcases: Record<string, NightwatchTestCase>\n testManager: TestManager\n /** Plugin-side pass/fail/skip counter — called once with the resolved final state. */\n incrementCount: (state: TestStats['state']) => void\n /** Plugin-side icon resolver for the log line (✅/❌/⏭ etc.). */\n testIcon: (state: TestStats['state']) => string\n}\n\n/**\n * Close out the previously-running test when beforeEach fires for the next\n * one. Resolves the final state from Nightwatch's testcases bag if available\n * (preferred — has real timing), otherwise assumes PASSED (the runner only\n * advances to beforeEach if the prior test didn't throw). Returns nothing —\n * pure side-effect orchestration over the test, testManager, and the plugin's\n * counter / icon helpers.\n *\n * Extracted from `NightwatchDevToolsPlugin.beforeEach` — the ~37-line block\n * was the second-densest section of the 142-line method.\n */\nexport async function closePreviousTest(\n input: ClosePreviousTestInput\n): Promise<void> {\n const {\n runningTest,\n testFile,\n testcases,\n testManager,\n incrementCount,\n testIcon\n } = input\n\n if (testcases[runningTest.title]) {\n const testcase = testcases[runningTest.title]\n const testState = determineTestState(testcase)\n runningTest.state = testState\n runningTest.end = new Date()\n runningTest._duration = parseFloat(testcase.time || '0') * 1000\n testManager.updateTestState(runningTest, testState)\n testManager.markTestAsProcessed(testFile, runningTest.title)\n incrementCount(testState)\n log.info(\n ` ${testIcon(testState)} ${runningTest.title} (${(runningTest._duration / 1000).toFixed(2)}s)`\n )\n } else {\n const endTime = new Date()\n const duration = endTime.getTime() - (runningTest.start?.getTime() || 0)\n testManager.updateTestState(\n runningTest,\n TEST_STATE.PASSED as TestStats['state'],\n endTime,\n duration\n )\n testManager.markTestAsProcessed(testFile, runningTest.title)\n incrementCount(TEST_STATE.PASSED as TestStats['state'])\n log.info(` ✅ ${runningTest.title} (${(duration / 1000).toFixed(2)}s)`)\n }\n // Brief settle so the dashboard renders the terminal state before the next\n // test's \"running\" update arrives.\n await new Promise((resolve) => setTimeout(resolve, TIMING.UI_RENDER_DELAY))\n}\n","/**\n * Session-initialization lifecycle helpers for the Nightwatch plugin.\n *\n * Extracted from the plugin class to keep `index.ts` under the file-size\n * cap. The plugin passes itself as a `SessionInitCtx` — a narrow interface\n * exposing only the fields and methods these helpers need.\n *\n * Includes:\n * - Per-session bringup (capturer + reporter chain + metadata + BiDi + screencast)\n * - Session-change cleanup\n * - Screencast finalize-and-clear\n */\n\nimport logger from '@wdio/logger'\nimport {\n finalizeScreencast,\n resolveAdapterOutputDir\n} from '@wdio/devtools-core'\nimport { TraceType } from './types.js'\nimport { TIMING } from './constants.js'\nimport { SessionCapturer } from './session.js'\nimport { TestReporter } from './reporter.js'\nimport { TestManager } from './helpers/testManager.js'\nimport { SuiteManager } from './helpers/suiteManager.js'\nimport { BrowserProxy } from './helpers/browserProxy.js'\nimport { ScreencastRecorder } from './screencast.js'\nimport type {\n DevToolsMode,\n NightwatchBrowser,\n ScreencastOptions,\n SuiteStats\n} from './types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:session-init')\n\nexport interface SessionInitCtx {\n readonly hostname: string\n readonly port: number\n readonly screencastOptions: ScreencastOptions\n readonly bidiEnabled: boolean\n readonly mode: DevToolsMode\n\n sessionCapturer: SessionCapturer\n testReporter: TestReporter\n testManager: TestManager\n suiteManager: SuiteManager\n browserProxy: BrowserProxy\n isScriptInjected: boolean\n\n lastSessionId: string | null\n bidiAttachAttempted: boolean\n srcFolders: string[]\n screencastRecorder: ScreencastRecorder | undefined\n screencastSessionId: string | undefined\n configPath: string | undefined\n\n getCurrentTest(): unknown\n getCurrentScenarioSuite(): SuiteStats | null\n buildMetadataOptions(): unknown\n}\n\nasync function handleSessionChange(\n ctx: SessionInitCtx,\n finalizeCurrent: () => Promise<void>\n): Promise<void> {\n log.info('Browser session changed — reconnecting WebSocket only')\n ctx.isScriptInjected = false\n // Reset BiDi-attach state so the new session gets its own attach —\n // inspectors are bound to a specific driver instance and don't carry\n // across sessions. Without this, only the first session captures via\n // BiDi and the rest silently fall back to the perf-log path.\n ctx.bidiAttachAttempted = false\n // Finalize the previous session's screencast BEFORE we tear down its\n // capturer — encode + broadcast use the existing WS connection.\n await finalizeCurrent()\n ctx.sessionCapturer?.cleanup()\n // Intentional null-out — the next call to ensureSessionInitialized\n // reassigns. Cast through unknown so the strict field type passes.\n ctx.sessionCapturer = null as unknown as SessionCapturer\n}\n\nfunction initReporterChain(ctx: SessionInitCtx): void {\n // First-time setup: create reporter chain once for the entire run.\n // These must NOT be recreated on session change — doing so generates a\n // new feature suite with a fresh start timestamp, which DataManager sees\n // as a new run and wipes all accumulated commands.\n ctx.testReporter = new TestReporter((suitesData) => {\n if (ctx.sessionCapturer) {\n ctx.sessionCapturer.sendUpstream('suites', suitesData)\n }\n })\n ctx.testManager = new TestManager(ctx.testReporter)\n ctx.suiteManager = new SuiteManager(ctx.testReporter)\n ctx.browserProxy = new BrowserProxy(\n ctx.sessionCapturer,\n ctx.testManager,\n () => ctx.getCurrentTest() ?? ctx.getCurrentScenarioSuite()\n )\n}\n\nfunction rebindReporterToNewSession(ctx: SessionInitCtx): void {\n // Session change: update the reporter's upstream callback to use the new\n // WebSocket, update the proxy's capturer reference (avoids re-wrapping\n // already-wrapped browser methods which would double-capture commands),\n // then replay current suite state to the newly-connected UI.\n ctx.testReporter.updateUpstream((suitesData) => {\n if (ctx.sessionCapturer) {\n ctx.sessionCapturer.sendUpstream('suites', suitesData)\n }\n })\n ctx.browserProxy.updateSessionCapturer(ctx.sessionCapturer)\n ctx.testReporter.updateSuites()\n}\n\nfunction broadcastSessionMetadata(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): void {\n const capabilities = browser.capabilities || {}\n const desiredCapabilities = browser.desiredCapabilities || {}\n const sessionId = browser.sessionId\n const opts = browser.options || {}\n\n if (ctx.srcFolders.length === 0) {\n const sf = (opts as { src_folders?: string | string[] }).src_folders\n ctx.srcFolders = Array.isArray(sf) ? sf : sf ? [sf] : []\n }\n\n const metadata = {\n type: TraceType.Testrunner,\n capabilities,\n desiredCapabilities,\n sessionId,\n testEnv: opts.testEnv,\n host: opts.webdriver?.host,\n options: ctx.buildMetadataOptions(),\n url: ''\n }\n ctx.sessionCapturer.metadata = metadata\n ctx.sessionCapturer.sendUpstream('metadata', metadata)\n\n const browserName =\n capabilities.browserName || desiredCapabilities.browserName || 'unknown'\n const browserVersion =\n capabilities.browserVersion ||\n (capabilities as { version?: string }).version ||\n ''\n log.info(\n `✓ Browser: ${browserName}${browserVersion ? ' ' + browserVersion : ''} (session: ${sessionId})`\n )\n\n const loggingPrefs = ((capabilities as Record<string, unknown>)[\n 'goog:loggingPrefs'\n ] ||\n (desiredCapabilities as Record<string, unknown>)['goog:loggingPrefs'] ||\n {}) as { performance?: string }\n if (!loggingPrefs.performance && !ctx.bidiEnabled) {\n log.warn(\n \"⚠ Network tab will be empty — add 'goog:loggingPrefs': { performance: 'ALL' } to your capabilities (or enable bidi:true)\"\n )\n }\n}\n\n// BiDi: opt-in. Requires `webSocketUrl: true` capability + a BiDi-capable\n// chromedriver. We attempt once per session; on failure or unavailability\n// the perf-log fallback path continues to work.\nasync function tryAttachBidi(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): Promise<void> {\n if (!ctx.bidiEnabled || ctx.bidiAttachAttempted) {\n return\n }\n ctx.bidiAttachAttempted = true\n const driver = (browser as { driver?: unknown }).driver\n if (!driver) {\n log.warn('bidi:true set but browser.driver unavailable — skipping')\n return\n }\n const { attachBidiHandlers, buildBidiSinks } = await import('./bidi.js')\n const ok = await attachBidiHandlers(\n driver,\n buildBidiSinks(ctx.sessionCapturer)\n )\n if (ok) {\n ctx.sessionCapturer.bidiActive = true\n log.info('✓ BiDi attached — perf-log network capture disabled')\n }\n}\n\n// Screencast: start a fresh recorder per browser session — every\n// reloadSession / per-test browser produces its own .webm, matching\n// the WDIO service behavior. Polling mode only (Nightwatch has no\n// stable CDP escape hatch). Finalized when the next session change\n// fires or when after() runs.\nasync function tryStartScreencast(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser,\n sessionId: string | undefined\n): Promise<void> {\n if (!ctx.screencastOptions.enabled || ctx.screencastRecorder || !sessionId) {\n return\n }\n ctx.screencastRecorder = new ScreencastRecorder(\n ctx.sessionCapturer,\n ctx.screencastOptions\n )\n ctx.screencastSessionId = sessionId\n log.info(`🎬 Starting screencast for session ${sessionId}`)\n await ctx.screencastRecorder.start(browser)\n}\n\nexport async function ensureSessionInitialized(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser,\n finalizeCurrentScreencast: () => Promise<void>\n): Promise<void> {\n const currentSessionId = browser.sessionId\n const isSessionChange =\n currentSessionId &&\n ctx.lastSessionId &&\n currentSessionId !== ctx.lastSessionId\n if (isSessionChange) {\n await handleSessionChange(ctx, finalizeCurrentScreencast)\n }\n ctx.lastSessionId = currentSessionId ?? null\n if (ctx.sessionCapturer) {\n return\n }\n await new Promise((resolve) =>\n setTimeout(resolve, TIMING.INITIAL_CONNECTION_WAIT)\n )\n // Trace mode: empty opts skip SessionCapturerBase's WS init — no backend\n // to forward events to anyway.\n ctx.sessionCapturer = new SessionCapturer(\n ctx.mode === 'trace'\n ? {}\n : {\n port: ctx.port,\n hostname: ctx.hostname,\n // A session change reopens the WS mid-run — tell the backend to keep\n // the accumulated run state so earlier tests' commands survive.\n reconnect: Boolean(isSessionChange)\n },\n browser\n )\n ctx.sessionCapturer.traceMode = ctx.mode\n if (ctx.mode !== 'trace') {\n const connected = await ctx.sessionCapturer.waitForConnection(3000)\n if (!connected) {\n log.error('❌ Worker WebSocket failed to connect!')\n }\n }\n if (!ctx.testReporter) {\n initReporterChain(ctx)\n } else {\n rebindReporterToNewSession(ctx)\n }\n broadcastSessionMetadata(ctx, browser)\n await tryAttachBidi(ctx, browser)\n await tryStartScreencast(ctx, browser, browser.sessionId)\n}\n\nexport async function finalizeCurrentScreencast(\n ctx: SessionInitCtx\n): Promise<void> {\n if (!ctx.screencastRecorder || !ctx.screencastSessionId) {\n return\n }\n await finalizeScreencast({\n recorder: ctx.screencastRecorder,\n sessionId: ctx.screencastSessionId,\n filenamePrefix: 'nightwatch-video',\n outputDir: resolveAdapterOutputDir({\n testFilePath: ctx.browserProxy?.getCurrentTestFullPath?.() ?? undefined,\n configPath: ctx.configPath\n }),\n captureFormat: ctx.screencastOptions.captureFormat,\n sendUpstream: (scope, data) =>\n ctx.sessionCapturer?.sendUpstream(scope, data),\n onLog: (level, message) => log[level](message)\n })\n ctx.screencastRecorder = undefined\n ctx.screencastSessionId = undefined\n}\n","import http from 'node:http'\nimport logger from '@wdio/logger'\nimport {\n SessionCapturerBase,\n errorMessage,\n loadInjectableScript,\n mapChromeBrowserLogs,\n pollUntilReady,\n serializeError\n} from '@wdio/devtools-core'\nimport { mapCommandToAction } from '@wdio/devtools-core'\nimport { captureActionSnapshot } from './action-snapshot.js'\nimport { NAVIGATION_COMMANDS } from './constants.js'\nimport {\n parseNetworkFromPerfLogs,\n dedupeNetworkRequests,\n type NetworkEntry,\n type PerfLogEntry\n} from './helpers/perfLogs.js'\nimport {\n CAPTURE_PERFORMANCE_SCRIPT,\n type CapturedPerformancePayload,\n applyPerformanceData\n} from '@wdio/devtools-core'\nimport type {\n ActionSnapshot,\n CommandLog,\n DevToolsMode,\n NightwatchBrowser\n} from './types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:SessionCapturer')\n\n/**\n * WebDriver responses are sometimes wrapped as `{ value: T }` (the W3C\n * protocol shape) and sometimes flat. This helper unwraps the value field\n * if present, otherwise returns the input as-is.\n */\nfunction unwrapDriverValue<T = unknown>(result: unknown): T {\n if (result && typeof result === 'object' && 'value' in result) {\n return (result as { value: T }).value\n }\n return result as T\n}\n\ntype LooseRec = Record<string, unknown>\nconst getProp = (obj: unknown, key: string): unknown =>\n obj && typeof obj === 'object' ? (obj as LooseRec)[key] : undefined\nconst getPath = (obj: unknown, ...path: string[]): unknown =>\n path.reduce<unknown>((acc, k) => getProp(acc, k), obj)\nconst firstDefined = (obj: unknown, ...keys: string[]): unknown => {\n if (!obj || typeof obj !== 'object') {\n return undefined\n }\n const rec = obj as LooseRec\n for (const k of keys) {\n const v = rec[k]\n if (v !== undefined && v !== null) {\n return v\n }\n }\n return undefined\n}\n\n/**\n * Walks Nightwatch's internal config (transport / queue.transport /\n * nightwatchInstance — none of which are on the public NightwatchBrowser\n * type) to find the underlying WebDriver host+port for direct screenshot\n * fetches that bypass the command queue.\n */\nfunction resolveWebDriverAddress(browser: NightwatchBrowser): {\n driverHost: string\n driverPort: number\n} {\n const transportSettings =\n getPath(browser, 'transport', 'settings', 'webdriver') ||\n getPath(browser, 'queue', 'transport', 'settings', 'webdriver') ||\n getPath(\n browser,\n 'nightwatchInstance',\n 'transport',\n 'settings',\n 'webdriver'\n ) ||\n {}\n const opts = getProp(browser, 'options') ?? {}\n const nightwatchSettings =\n getPath(browser, 'nightwatchInstance', 'settings') ||\n getPath(browser, 'globals', 'nightwatchInstance', 'settings') ||\n {}\n const driverHost = String(\n firstDefined(transportSettings, 'host', 'server_address') ||\n firstDefined(getProp(opts, 'webdriver'), 'host') ||\n firstDefined(getProp(nightwatchSettings, 'webdriver'), 'host') ||\n 'localhost'\n )\n const driverPort = Number(\n firstDefined(transportSettings, 'port') ||\n firstDefined(getProp(opts, 'webdriver'), 'port') ||\n firstDefined(getProp(nightwatchSettings, 'webdriver'), 'port') ||\n 9515\n )\n return { driverHost, driverPort }\n}\n\nexport class SessionCapturer extends SessionCapturerBase {\n #browser: NightwatchBrowser | undefined\n\n // True once BiDi inspectors are attached — the per-command perf-log network\n // capture path skips when set, so we don't double-emit network requests.\n bidiActive = false\n\n // Populated by captureCommand when mode === 'trace' (set by the plugin).\n traceMode: DevToolsMode = 'live'\n readonly actionSnapshots: ActionSnapshot[] = []\n readonly snapshotCaptures: Promise<void>[] = []\n\n constructor(\n devtoolsOptions: {\n hostname?: string\n port?: number\n reconnect?: boolean\n } = {},\n browser?: NightwatchBrowser\n ) {\n super(devtoolsOptions)\n this.#browser = browser\n this.patchConsole()\n this.patchStreams()\n }\n\n protected override onWsOpen(): void {\n log.info('✓ Worker WebSocket connected to backend')\n }\n\n protected override onWsError(err: unknown): void {\n log.error(`Couldn't connect to devtools backend: ${errorMessage(err)}`)\n }\n\n protected override onWsClose(): void {\n log.info('Worker WebSocket disconnected')\n }\n\n async captureCommand(\n command: string,\n args: unknown[],\n result: unknown,\n error: Error | undefined,\n testUid?: string,\n callSource?: string,\n timestamp?: number\n ): Promise<boolean> {\n // Serialize error properly (Error objects don't JSON.stringify well)\n const serializedError = serializeError(error)\n\n const commandId = this.commandCounter++\n const commandLogEntry: CommandLog & { _id?: number } = {\n _id: commandId,\n command,\n args,\n result,\n error: serializedError,\n timestamp: timestamp || Date.now(),\n callSource,\n testUid\n }\n\n this.commandsLog.push(commandLogEntry)\n\n const isNavigationCommand = NAVIGATION_COMMANDS.some((cmd) =>\n command.toLowerCase().includes(cmd.toLowerCase())\n )\n if (isNavigationCommand && this.#browser && !error) {\n this.#capturePerformanceData(commandLogEntry, args).catch((err) => {\n log.warn(`Failed to capture performance data: ${errorMessage(err)}`)\n })\n }\n\n if (\n this.traceMode === 'trace' &&\n !error &&\n this.#browser &&\n mapCommandToAction(command)\n ) {\n const browser = this.#browser\n this.snapshotCaptures.push(\n captureActionSnapshot(browser, command, () =>\n this.takeScreenshotViaHttp(browser)\n ).then((snap) => {\n if (snap) {\n this.actionSnapshots.push(snap)\n }\n })\n )\n }\n\n return true\n }\n\n async #capturePerformanceData(\n commandLogEntry: CommandLog & { _id?: number },\n args: unknown[]\n ) {\n await new Promise((resolve) => setTimeout(resolve, 500))\n const raw = await this.#browser!.execute(CAPTURE_PERFORMANCE_SCRIPT)\n const payload = unwrapDriverValue<CapturedPerformancePayload | undefined>(\n raw\n )\n applyPerformanceData(\n commandLogEntry,\n payload,\n typeof args[0] === 'string' ? args[0] : undefined\n )\n }\n\n /**\n * Replace an already-captured command entry (used for retried commands so\n * only the final execution result is shown in the UI).\n * Removes the old entry from commandsLog, revokes its sent-status so the\n * replacement can be sent, and returns the new entry together with the\n * old entry's timestamp (so the UI can locate and replace it in-place).\n */\n replaceCommand(\n oldId: number,\n command: string,\n args: unknown[],\n result: unknown,\n error: Error | undefined,\n testUid?: string,\n callSource?: string,\n timestamp?: number\n ): { entry: CommandLog & { _id?: number }; oldTimestamp: number } {\n // Remove the superseded entry and capture its timestamp for the UI\n const idx = this.commandsLog.findIndex(\n (c) => (c as CommandLog & { _id?: number })._id === oldId\n )\n const oldTimestamp: number =\n idx !== -1 ? (this.commandsLog[idx]?.timestamp ?? 0) : 0\n if (idx !== -1) {\n this.commandsLog.splice(idx, 1)\n }\n // Allow the slot to be re-used by a new entry\n this.sentCommandIds.delete(oldId)\n\n const serializedError = serializeError(error)\n const commandId = this.commandCounter++\n const entry: CommandLog & { _id?: number } = {\n _id: commandId,\n command,\n args,\n result,\n error: serializedError,\n timestamp: timestamp || Date.now(),\n callSource,\n testUid\n }\n this.commandsLog.push(entry)\n return { entry, oldTimestamp }\n }\n\n /**\n * Take a screenshot by calling the WebDriver HTTP endpoint directly.\n * This completely bypasses Nightwatch's command queue so there is no risk\n * of the request being appended after `end()` / `quit()`.\n */\n // Nightwatch's internal config lives at non-public paths (transport,\n // queue.transport, nightwatchInstance.settings, globals.nightwatchInstance);\n // none are in the NightwatchBrowser type. Cast for dynamic access.\n #resolveDriverEndpoint(\n browser: NightwatchBrowser,\n sessionId: string\n ): string {\n const { driverHost, driverPort } = resolveWebDriverAddress(browser)\n return `http://${driverHost}:${driverPort}/session/${sessionId}/screenshot`\n }\n\n takeScreenshotViaHttp(browser: NightwatchBrowser): Promise<string | null> {\n const sessionId = (browser as unknown as { sessionId?: string }).sessionId\n if (!sessionId) {\n return Promise.resolve(null)\n }\n const endpoint = this.#resolveDriverEndpoint(browser, sessionId)\n return new Promise((resolve) => {\n const req = http.get(endpoint, (res) => {\n let body = ''\n res.on('data', (chunk: string | Buffer) => {\n body += chunk\n })\n res.on('end', () => {\n try {\n const value = JSON.parse(body).value || null\n if (!value) {\n log.warn(`[screenshot] Empty response from ${endpoint}`)\n }\n resolve(value)\n } catch {\n log.warn(`[screenshot] Failed to parse response from ${endpoint}`)\n resolve(null)\n }\n })\n })\n req.on('error', (err) => {\n log.warn(\n `[screenshot] HTTP request failed (${endpoint}): ${errorMessage(err)}`\n )\n resolve(null)\n })\n req.setTimeout(5000, () => {\n log.warn(`[screenshot] Request timed out (${endpoint})`)\n req.destroy()\n resolve(null)\n })\n })\n }\n\n protected override onSourceReadError(filePath: string, err: unknown): void {\n log.warn(`Failed to read source file ${filePath}: ${errorMessage(err)}`)\n }\n\n protected override onUpstreamDrop(\n event: string,\n reason: 'closed' | 'send-error',\n err?: unknown\n ): void {\n if (reason === 'send-error') {\n log.warn(`[upstream] Failed to send \"${event}\": ${errorMessage(err)}`)\n return\n }\n if (this.hasEverConnected()) {\n log.warn(`[upstream] WebSocket not open — dropping \"${event}\" event`)\n }\n }\n\n /**\n * Inject the WDIO devtools script into the browser page\n */\n async injectScript(browser: NightwatchBrowser) {\n try {\n const scriptContent = await loadInjectableScript()\n const injectionScript = `\n const script = document.createElement('script');\n script.textContent = arguments[0];\n document.head.appendChild(script);\n return true;\n `\n await browser.execute(injectionScript, [scriptContent])\n\n const hasCollector = await pollUntilReady(async () => {\n const checkResult = await browser.execute(\n 'return typeof window.wdioTraceCollector !== \"undefined\"'\n )\n return unwrapDriverValue<unknown>(checkResult) === true\n })\n\n if (hasCollector) {\n log.info('✓ Script injected and collector ready')\n } else {\n log.warn('Script injection may have failed — collector not found')\n }\n } catch (err) {\n log.error(`Failed to inject script: ${errorMessage(err)}`)\n throw err\n }\n }\n\n /**\n * Capture Chrome DevTools browser console logs via WebDriver log API.\n * Requires loggingPrefs: { browser: 'ALL' } in Chrome capabilities.\n */\n async captureBrowserLogs(browser: NightwatchBrowser) {\n try {\n const rawLogs = await (\n browser as unknown as Record<string, (type: string) => Promise<unknown>>\n ).getLog('browser')\n const logs =\n unwrapDriverValue<\n Array<{ level: string; message: string; timestamp: number }>\n >(rawLogs)\n\n if (!Array.isArray(logs) || logs.length === 0) {\n return\n }\n\n const entries = mapChromeBrowserLogs(logs)\n this.consoleLogs.push(...entries)\n this.sendUpstream('consoleLogs', entries)\n } catch {\n // Browser log capture not available (loggingPrefs not set or not supported)\n }\n }\n\n /**\n * Parse Chrome performance logs to extract network request entries.\n * Requires loggingPrefs: { performance: 'ALL' } in Chrome capabilities.\n */\n async captureNetworkFromPerformanceLogs(browser: NightwatchBrowser) {\n // BiDi network inspector is the source of truth when attached.\n if (this.bidiActive) {\n return\n }\n try {\n const rawLogs = await (\n browser as unknown as Record<string, (type: string) => Promise<unknown>>\n ).getLog('performance')\n const logs = unwrapDriverValue<PerfLogEntry[]>(rawLogs)\n\n if (!Array.isArray(logs) || logs.length === 0) {\n return\n }\n\n const networkEntries = parseNetworkFromPerfLogs(logs)\n if (networkEntries.length === 0) {\n return\n }\n\n const deduped = dedupeNetworkRequests(\n networkEntries,\n this.networkRequests as NetworkEntry[]\n )\n if (deduped.length > 0) {\n // NetworkEntry has `type?: string`; the shared NetworkRequest needs\n // `type: string` so default the field at this framework boundary.\n const normalized = deduped.map((d) => ({\n ...d,\n type: d.type ?? 'unknown'\n }))\n this.networkRequests.push(...normalized)\n this.sendUpstream('networkRequests', normalized)\n }\n } catch (err) {\n const msg = errorMessage(err) ?? ''\n // Silently skip when performance logging was not enabled in capabilities\n if (!msg.includes('log type') && !msg.includes('performance')) {\n log.warn(`Performance log capture failed: ${msg}`)\n }\n }\n }\n\n /**\n * Capture trace data from the browser (network requests, console logs, etc.)\n */\n async captureTrace(browser: NightwatchBrowser) {\n // Capture network requests from Chrome performance logs\n await this.captureNetworkFromPerformanceLogs(browser)\n\n // Also try the injected wdioTraceCollector script for XHR/fetch and mutations.\n // Atomic check+read — the inline `typeof === 'undefined' → null` guard is\n // the only safe form; a separate existence check would race page navigation\n // (the collector can disappear between the two round-trips).\n try {\n const result = await browser.execute(`\n if (typeof window.wdioTraceCollector === 'undefined') {\n return null;\n }\n return window.wdioTraceCollector.getTraceData();\n `)\n\n const traceData = unwrapDriverValue<Record<string, unknown> | null>(\n result\n )\n if (!traceData) {\n return\n }\n\n this.processTracePayload(traceData)\n const mutationCount = Array.isArray(\n (traceData as { mutations?: unknown }).mutations\n )\n ? (traceData as { mutations: unknown[] }).mutations.length\n : 0\n if (mutationCount > 0) {\n log.info(`[trace] Captured ${mutationCount} DOM mutation(s)`)\n }\n } catch (err) {\n log.error(\n `Failed to capture trace from injected script: ${errorMessage(err)}`\n )\n }\n }\n}\n","// Nightwatch adapter: wires NightwatchBrowser into core's captureActionSnapshot.\n\nimport { captureActionSnapshot as coreCapture } from '@wdio/devtools-core'\nimport type { ActionSnapshot } from '@wdio/devtools-shared'\nimport type { NightwatchBrowser } from './types.js'\n\ninterface BrowserWithUrl extends NightwatchBrowser {\n getCurrentUrl?: () => Promise<string>\n getTitle?: () => Promise<string>\n}\n\nexport function captureActionSnapshot(\n browser: NightwatchBrowser,\n command: string,\n takeScreenshot?: () => Promise<string | null | undefined>\n): Promise<ActionSnapshot | null> {\n const b = browser as BrowserWithUrl\n return coreCapture({\n command,\n runScript: (src) => browser.execute(`return (${src})`) as Promise<unknown>,\n takeScreenshot,\n getUrl: () =>\n b.getCurrentUrl\n ? b.getCurrentUrl().catch(() => undefined)\n : Promise.resolve(undefined),\n getTitle: () =>\n b.getTitle\n ? b.getTitle().catch(() => undefined)\n : Promise.resolve(undefined)\n })\n}\n","import { getRequestType } from './utils.js'\n\n/**\n * Pure parsers for Chrome's `performance` log (the format `browser.getLog('performance')`\n * returns). Separated from the SessionCapturer so they're testable and the\n * capture method stays focused on state + I/O.\n */\n\nexport interface PerfLogEntry {\n level: string\n message: string\n timestamp: number\n}\n\nexport interface NetworkEntry {\n id: string\n url: string\n method: string\n requestHeaders: Record<string, string>\n timestamp: number\n startTime: number\n status?: number\n statusText?: string\n responseHeaders?: Record<string, string>\n mimeType?: string\n type?: string\n size?: number\n endTime?: number\n time?: number\n error?: string\n}\n\n/**\n * Parse CDP `Network.*` events out of Chrome performance log entries into a\n * flat array of network entries. Builds up a per-requestId pending map as it\n * sees `requestWillBeSent` → `responseReceived` → `loadingFinished` events,\n * and emits the completed entry on the terminal event.\n */\ninterface CdpResponse {\n status?: number\n statusText?: string\n headers?: Record<string, unknown>\n mimeType?: string\n}\n\ninterface CdpRequest {\n url: string\n method: string\n headers?: Record<string, string>\n}\n\ninterface CdpEventParams {\n requestId: string\n request?: CdpRequest\n response?: CdpResponse\n timestamp?: number\n encodedDataLength?: number\n errorText?: string\n}\n\nfunction applyResponseReceived(p: NetworkEntry, response: CdpResponse): void {\n const responseHeaders: Record<string, string> = {}\n for (const [k, v] of Object.entries(response.headers || {})) {\n responseHeaders[k.toLowerCase()] = String(v)\n }\n p.status = response.status\n p.statusText = response.statusText\n p.responseHeaders = responseHeaders\n p.mimeType = response.mimeType\n p.type = getRequestType(p.url, response.mimeType)\n}\n\n/** CDP event time in ms. Uses the event's own monotonic `params.timestamp`\n * (what Chrome DevTools times requests with) rather than the perf-log wrapper\n * timestamp, which WebDriver stamps per batch — so request/response diffs in\n * the same batch would otherwise be 0. */\nfunction eventTimeMs(params: CdpEventParams, entry: PerfLogEntry): number {\n return typeof params.timestamp === 'number'\n ? Math.round(params.timestamp * 1000)\n : entry.timestamp\n}\n\nfunction startPendingRequest(\n params: CdpEventParams,\n entry: PerfLogEntry,\n pending: Map<string, NetworkEntry>\n): void {\n const { requestId, request: req, timestamp } = params\n if (!req || typeof timestamp !== 'number') {\n return\n }\n const startTime = eventTimeMs(params, entry)\n pending.set(requestId, {\n id: `${entry.timestamp}-${requestId}`,\n url: req.url,\n method: req.method,\n requestHeaders: req.headers ?? {},\n timestamp: startTime,\n startTime\n })\n}\n\nfunction handlePerfLogEvent(\n method: string,\n params: CdpEventParams,\n entry: PerfLogEntry,\n pending: Map<string, NetworkEntry>,\n completed: NetworkEntry[]\n): void {\n if (method === 'Network.requestWillBeSent') {\n startPendingRequest(params, entry, pending)\n return\n }\n if (method === 'Network.responseReceived') {\n const p = pending.get(params.requestId)\n if (p && params.response) {\n applyResponseReceived(p, params.response)\n }\n return\n }\n if (method === 'Network.loadingFinished') {\n const p = pending.get(params.requestId)\n if (p && p.status !== undefined) {\n p.size = params.encodedDataLength\n p.endTime = eventTimeMs(params, entry)\n p.time = Math.max(0, p.endTime - p.startTime)\n completed.push({ ...p })\n pending.delete(params.requestId)\n }\n return\n }\n if (method === 'Network.loadingFailed') {\n const p = pending.get(params.requestId)\n if (p) {\n p.error = params.errorText\n p.endTime = eventTimeMs(params, entry)\n p.time = Math.max(0, p.endTime - p.startTime)\n completed.push({ ...p })\n pending.delete(params.requestId)\n }\n }\n}\n\nexport function parseNetworkFromPerfLogs(logs: PerfLogEntry[]): NetworkEntry[] {\n const pending = new Map<string, NetworkEntry>()\n const completed: NetworkEntry[] = []\n for (const entry of logs) {\n let parsed: unknown\n try {\n parsed = JSON.parse(entry.message)\n } catch {\n continue\n }\n type ParsedShape = {\n message?: { method?: string; params?: CdpEventParams }\n }\n const message = (parsed as ParsedShape | undefined)?.message\n const method = message?.method\n const params = message?.params\n if (!method || !params) {\n continue\n }\n handlePerfLogEvent(method, params, entry, pending, completed)\n }\n return completed\n}\n\n/**\n * Dedupe incoming network entries against ones the session already holds.\n * Successful requests dedupe by (method, url, timestamp). Failed requests\n * collapse by (method, origin, pathname) — parallel autocomplete/prefetch\n * requests to the same path (e.g. `/search?q=W`, `/search?q=We`) otherwise\n * spam the network panel.\n */\nexport function dedupeNetworkRequests(\n incoming: NetworkEntry[],\n existing: NetworkEntry[]\n): NetworkEntry[] {\n const failedKey = (entry: NetworkEntry): string => {\n try {\n const u = new URL(entry.url)\n return `err:${entry.method}:${u.origin}${u.pathname}`\n } catch {\n return `err:${entry.method}:${entry.url}`\n }\n }\n\n const alreadySeen = new Set(\n existing.map((r) =>\n r.error !== undefined\n ? failedKey(r)\n : `ok:${r.method}:${r.url}:${r.timestamp}`\n )\n )\n\n const deduped: NetworkEntry[] = []\n const seenFailedInBatch = new Map<string, number>()\n\n for (const entry of incoming) {\n if (entry.error !== undefined) {\n const key = failedKey(entry)\n if (alreadySeen.has(key)) {\n continue\n }\n const existingIdx = seenFailedInBatch.get(key)\n if (existingIdx !== undefined) {\n deduped[existingIdx] = entry // replace with latest failure\n } else {\n seenFailedInBatch.set(key, deduped.length)\n deduped.push(entry)\n }\n } else {\n const key = `ok:${entry.method}:${entry.url}:${entry.timestamp}`\n if (!alreadySeen.has(key)) {\n deduped.push(entry)\n }\n }\n }\n\n return deduped\n}\n","import logger from '@wdio/logger'\nimport { TestReporterBase } from '@wdio/devtools-core'\nimport { REUSE_ENV } from '@wdio/devtools-shared'\nimport { DEFAULTS } from './constants.js'\nimport { extractTestMetadata, generateStableUid } from './helpers/utils.js'\nimport type { SuiteStats, TestStats } from './types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:Reporter')\n\nexport class TestReporter extends TestReporterBase {\n #currentSpecFile?: string\n #testNamesCache = new Map<string, string[]>()\n #currentSuite?: SuiteStats\n\n onSuiteStart(suiteStats: SuiteStats) {\n this.#currentSpecFile = suiteStats.file\n this.#currentSuite = suiteStats\n const rerunLabel =\n process.env[REUSE_ENV.RERUN_ENTRY_TYPE] === 'test'\n ? process.env[REUSE_ENV.RERUN_LABEL]?.trim()\n : undefined\n\n if (!suiteStats.uid) {\n suiteStats.uid = generateStableUid(suiteStats)\n }\n\n if (\n this.#currentSpecFile &&\n !this.#testNamesCache.has(this.#currentSpecFile)\n ) {\n const metadata = extractTestMetadata(this.#currentSpecFile)\n const testNames = rerunLabel\n ? metadata.testNames.filter((name) => name === rerunLabel)\n : metadata.testNames\n if (testNames.length > 0) {\n this.#testNamesCache.set(this.#currentSpecFile, testNames)\n log.info(\n `📝 Extracted ${testNames.length} test names from ${this.#currentSpecFile}`\n )\n }\n }\n\n this.allSuites.push(suiteStats)\n this.sendUpstream()\n }\n\n override clearExecutionData() {\n super.clearExecutionData()\n this.#testNamesCache.clear()\n this.#currentSuite = undefined\n this.#currentSpecFile = undefined\n }\n\n getCurrentSuite(): SuiteStats | undefined {\n return this.#currentSuite\n }\n\n /** Find by title within parent suite — Nightwatch retries reuse the title slot. */\n onTestStart(testStats: TestStats) {\n if (!testStats.uid || testStats.uid.includes('temp-')) {\n testStats.uid = generateStableUid(testStats)\n }\n\n for (const suite of this.allSuites) {\n const testIndex = suite.tests.findIndex((t) => {\n if (typeof t === 'string') {\n return false\n }\n return t.title === testStats.title && t.parent === suite.uid\n })\n if (testIndex !== -1) {\n suite.tests[testIndex] = testStats\n this.sendUpstream()\n return\n }\n }\n\n if (this.#currentSuite) {\n this.#currentSuite.tests.push(testStats)\n }\n this.sendUpstream()\n }\n\n /** Synthesize `skipped` entries for tests that never executed. */\n override onSuiteEnd(suiteStats: SuiteStats) {\n const cachedNames = this.#testNamesCache.get(suiteStats.file) || []\n const processedTestNames = new Set(\n suiteStats.tests\n .map((t) => (typeof t === 'string' ? t : t.title))\n .filter((title): title is string => Boolean(title))\n )\n\n cachedNames.forEach((testName) => {\n if (!processedTestNames.has(testName)) {\n const skippedTest: TestStats = {\n uid: generateStableUid({\n file: suiteStats.file,\n fullTitle: `${suiteStats.title} ${testName}`\n } as TestStats),\n cid: DEFAULTS.CID,\n title: testName,\n fullTitle: `${suiteStats.title} ${testName}`,\n parent: suiteStats.uid,\n state: 'skipped',\n start: new Date(),\n end: new Date(),\n type: 'test',\n file: suiteStats.file,\n retries: DEFAULTS.RETRIES,\n _duration: DEFAULTS.DURATION,\n hooks: []\n }\n suiteStats.tests.push(skippedTest)\n log.info(`Created skipped test \"${testName}\" (never executed)`)\n }\n })\n\n this.sendUpstream()\n }\n\n /** Replace a suite when its UID changes mid-run (after spec rescan). */\n updateSuite(suiteStats: SuiteStats) {\n const index = this.allSuites.findIndex((s) => s.file === suiteStats.file)\n if (index !== -1) {\n this.allSuites.splice(index, 1)\n }\n this.allSuites.push(suiteStats)\n this.#currentSuite = suiteStats\n this.sendUpstream()\n }\n}\n","import { stampTestEnd } from '@wdio/devtools-core'\nimport { TEST_STATE, DEFAULTS } from '../constants.js'\nimport {\n type TestStats,\n type SuiteStats,\n type NightwatchCurrentTest,\n type NightwatchTestCase\n} from '../types.js'\nimport { determineTestState } from './utils.js'\nimport type { TestReporter } from '../reporter.js'\n\nexport class TestManager {\n private processedTests = new Map<string, Set<string>>()\n private lastKnownTestName: string | null = null\n\n constructor(private testReporter: TestReporter) {}\n\n /**\n * Update test state and report to UI\n */\n updateTestState(\n test: TestStats,\n state: TestStats['state'],\n endTime?: Date,\n duration?: number\n ): void {\n test.state = state\n if (endTime) {\n test.end = endTime\n }\n if (duration !== undefined) {\n test._duration = duration\n }\n\n if (state === TEST_STATE.PASSED) {\n this.testReporter.onTestEnd(test)\n } else if (state === TEST_STATE.FAILED) {\n this.testReporter.onTestEnd(test)\n } else if (state === TEST_STATE.RUNNING) {\n this.testReporter.onTestStart(test)\n }\n\n if (state !== TEST_STATE.RUNNING) {\n this.testReporter.onTestEnd(test)\n }\n }\n\n /**\n * Find test in suite by title\n */\n findTestInSuite(suite: SuiteStats, testTitle: string): TestStats | undefined {\n return suite.tests.find(\n (t): t is TestStats => typeof t !== 'string' && t.title === testTitle\n )\n }\n\n /**\n * Mark test as processed to avoid duplicate reporting\n */\n markTestAsProcessed(testFile: string, testTitle: string): void {\n if (!this.processedTests.has(testFile)) {\n this.processedTests.set(testFile, new Set())\n }\n this.processedTests.get(testFile)!.add(testTitle)\n }\n\n /**\n * Check if test has been processed\n */\n isTestProcessed(testFile: string, testTitle: string): boolean {\n return this.processedTests.get(testFile)?.has(testTitle) ?? false\n }\n\n /**\n * Get processed tests for a file\n */\n getProcessedTests(testFile: string): Set<string> {\n return this.processedTests.get(testFile) || new Set()\n }\n\n /**\n * Detect test boundary and finalize previous test if needed\n * Returns the current test name\n */\n detectTestBoundary(currentNightwatchTest: NightwatchCurrentTest): string {\n const currentTestName = currentNightwatchTest?.name || DEFAULTS.TEST_NAME\n\n // If test name changed, finalize previous test\n if (\n this.lastKnownTestName &&\n currentTestName !== this.lastKnownTestName &&\n currentTestName !== DEFAULTS.TEST_NAME\n ) {\n const testFile =\n currentNightwatchTest.module?.split('/').pop() || DEFAULTS.FILE_NAME\n const currentSuite = this.testReporter.getCurrentSuite()\n\n if (currentSuite) {\n const prevTest = this.findTestInSuite(\n currentSuite,\n this.lastKnownTestName\n )\n if (prevTest && prevTest.state === TEST_STATE.RUNNING) {\n const prevTestCase =\n currentNightwatchTest.results?.testcases?.[this.lastKnownTestName]\n\n if (prevTestCase) {\n const testState = determineTestState(prevTestCase)\n this.updateTestState(\n prevTest,\n testState,\n new Date(),\n parseFloat(prevTestCase.time || '0') * 1000\n )\n this.markTestAsProcessed(testFile, this.lastKnownTestName)\n }\n }\n }\n }\n\n // Update last known test name\n if (currentTestName !== DEFAULTS.TEST_NAME) {\n this.lastKnownTestName = currentTestName\n }\n\n return currentTestName\n }\n\n /**\n * Start a pending test if this is its first command\n */\n startTestIfPending(currentTestName: string): void {\n if (\n currentTestName === DEFAULTS.TEST_NAME ||\n this.lastKnownTestName !== currentTestName\n ) {\n return\n }\n\n const currentSuite = this.testReporter.getCurrentSuite()\n if (!currentSuite) {\n return\n }\n\n const test = this.findTestInSuite(currentSuite, currentTestName)\n if (test && test.state === TEST_STATE.PENDING) {\n test.start = new Date()\n test.end = null\n this.updateTestState(test, TEST_STATE.RUNNING as TestStats['state'])\n }\n }\n\n /**\n * Finalize all incomplete tests in a suite\n */\n finalizeSuiteTests(\n suite: SuiteStats,\n testcases: Record<string, NightwatchTestCase>\n ): void {\n suite.tests.forEach((entry) => {\n if (typeof entry === 'string') {\n return\n }\n const test = entry\n if (test.state === TEST_STATE.RUNNING && test.start) {\n // Test was started but never finished - assume passed\n test.state = TEST_STATE.PASSED\n stampTestEnd(test)\n this.updateTestState(test, TEST_STATE.PASSED as TestStats['state'])\n } else if (test.state === TEST_STATE.PENDING) {\n const testcase = testcases[test.title]\n if (testcase) {\n const testState = determineTestState(testcase)\n test.start = test.start || new Date()\n this.updateTestState(\n test,\n testState,\n new Date(),\n parseFloat(testcase.time || '0') * 1000\n )\n } else {\n // Test never ran - mark as skipped\n this.updateTestState(\n test,\n TEST_STATE.SKIPPED as TestStats['state'],\n new Date(),\n 0\n )\n }\n }\n })\n }\n\n /**\n * Reset internal state for new test files\n */\n reset(): void {\n this.lastKnownTestName = null\n }\n}\n","/**\n * Suite Manager — Nightwatch flavor.\n * Maintains one suite per test file (Nightwatch runs each file independently).\n * Shares the suite factory + state-computation logic with selenium via\n * @wdio/devtools-core's suite-helpers; the storage strategy (Map by file)\n * is Nightwatch-specific and stays here.\n */\n\nimport {\n computeSuiteFinalStateStrict,\n computeSuiteRunningState,\n createSuiteStats,\n createTestStats,\n stampSuiteEnd\n} from '@wdio/devtools-core'\nimport { DEFAULTS, TEST_STATE } from '../constants.js'\nimport type { SuiteStats, TestStats } from '../types.js'\nimport type { TestReporter } from '../reporter.js'\nimport { generateStableUid } from './utils.js'\n\nexport class SuiteManager {\n private currentSuiteByFile = new Map<string, SuiteStats>()\n\n constructor(private testReporter: TestReporter) {}\n\n /**\n * Clear execution data when a rerun starts.\n * Resets all cached suites so they're recreated fresh during the new run.\n */\n clearExecutionData() {\n this.currentSuiteByFile.clear()\n }\n\n /**\n * Get or create suite for a test file\n */\n getOrCreateSuite(\n testFile: string,\n suiteTitle: string,\n fullPath: string | null,\n testNames: string[],\n suiteLine?: number | null,\n testLines?: number[]\n ): SuiteStats {\n if (!this.currentSuiteByFile.has(testFile)) {\n const file = fullPath || testFile\n const suiteStats = createSuiteStats({\n uid: generateStableUid(file, suiteTitle),\n cid: DEFAULTS.CID,\n title: suiteTitle,\n file,\n state: TEST_STATE.PENDING as TestStats['state'],\n callSource:\n suiteLine && fullPath ? `${fullPath}:${suiteLine}` : undefined\n })\n\n // Create test entries with pending state\n for (let idx = 0; idx < testNames.length; idx++) {\n const testName = testNames[idx]\n const testLine = testLines?.[idx]\n const fullTitle = `${suiteTitle} ${testName}`\n const testEntry = createTestStats({\n uid: generateStableUid(file, fullTitle),\n cid: DEFAULTS.CID,\n title: testName,\n fullTitle,\n file,\n parent: suiteStats.uid,\n state: TEST_STATE.PENDING as TestStats['state'],\n callSource:\n testLine && fullPath ? `${fullPath}:${testLine}` : undefined\n })\n suiteStats.tests.push(testEntry)\n }\n\n this.currentSuiteByFile.set(testFile, suiteStats)\n this.testReporter.onSuiteStart(suiteStats)\n }\n\n return this.currentSuiteByFile.get(testFile)!\n }\n\n /**\n * Get suite for a test file\n */\n getSuite(testFile: string): SuiteStats | undefined {\n return this.currentSuiteByFile.get(testFile)\n }\n\n /**\n * Mark suite as running.\n * Clears `end` so that the `finalizeSuite` guard (`if (suite.end) return`)\n * does not skip re-finalization during a rerun.\n */\n markSuiteAsRunning(suite: SuiteStats): void {\n suite.state = TEST_STATE.RUNNING\n suite.end = null\n this.testReporter.updateSuites()\n }\n\n /**\n * Update suite state from current children without marking it as ended.\n * Used during Cucumber runs to keep the feature-level suite state fresh.\n */\n finalizeSuiteState(suite: SuiteStats): void {\n suite.state = computeSuiteRunningState(suite)\n this.testReporter.updateSuites()\n }\n\n /**\n * Finalize suite with test results\n */\n finalizeSuite(suite: SuiteStats): void {\n if (suite.end) {\n return\n }\n stampSuiteEnd(suite)\n suite.state = computeSuiteFinalStateStrict(suite)\n this.testReporter.onSuiteEnd(suite)\n }\n\n /**\n * Get all suites\n */\n getAllSuites(): Map<string, SuiteStats> {\n return this.currentSuiteByFile\n }\n}\n","/**\n * Browser Proxy\n * Handles browser command interception and tracking\n */\n\nimport logger from '@wdio/logger'\nimport {\n INTERNAL_COMMANDS_TO_IGNORE,\n NAVIGATION_COMMANDS\n} from '../constants.js'\nimport { getCallSourceFromStack } from './utils.js'\nimport { serializeCommandResult } from './serializeCommandResult.js'\nimport { RetryTracker, toError } from '@wdio/devtools-core'\nimport type { SessionCapturer } from '../session.js'\nimport type { TestManager } from './testManager.js'\nimport type {\n CommandLog,\n NightwatchBrowser,\n NightwatchCurrentTest,\n CommandStackFrame\n} from '../types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:browserProxy')\n\n/** Nightwatch's chainable command result — exposes `.perform` to queue work\n * after the command completes. Cucumber async/await mode returns a Promise\n * instead, so callers narrow on `.perform` before using it. */\ntype NightwatchChainable = {\n perform?: (cb: (done: Function) => void) => void\n}\n\nexport class BrowserProxy {\n /** Tracks which browser *instances* have already been proxied to avoid double-wrapping. */\n private proxiedBrowsers = new WeakSet<object>()\n private commandStack: CommandStackFrame[] = []\n private lastCommandSig: string | null = null\n private currentTestFullPath: string | null = null\n /**\n * Tracks the last captured command so that consecutive retries of the same\n * command (e.g. getText inside a waitFor loop) overwrite the previous entry\n * rather than appending, showing only the final execution result.\n */\n private retryTracker = new RetryTracker()\n\n constructor(\n private sessionCapturer: SessionCapturer,\n private testManager: TestManager,\n private getCurrentTest: () => { uid?: string } | null\n ) {}\n\n /**\n * Update the session capturer reference after a WebDriver session change.\n * Does NOT re-wrap browser methods — wrapping is permanent per browser object.\n */\n updateSessionCapturer(capturer: SessionCapturer): void {\n this.sessionCapturer = capturer\n }\n\n resetCommandTracking(): void {\n this.commandStack = []\n this.lastCommandSig = null\n this.retryTracker.reset()\n }\n\n getCurrentTestFullPath(): string | null {\n return this.currentTestFullPath\n }\n\n setCurrentTestFullPath(path: string | null): void {\n this.currentTestFullPath = path\n }\n\n /**\n * Wrap browser navigation methods (url / navigate / navigateTo) to inject\n * the DevTools script after every navigation.\n *\n * Uses `browser` from the closure (not `this` inside perform) so it works\n * for both standard Nightwatch (chainable API) and Cucumber async/await mode\n * where `this` inside a perform callback is not the browser.\n */\n wrapUrlMethod(browser: NightwatchBrowser): void {\n const sessionCapturer = this.sessionCapturer\n\n // Cast once for dynamic method access — Nightwatch's typed surface\n // doesn't enumerate every command, but they all live on the same object.\n // Return type is `unknown` because wrapNav has to handle both\n // Nightwatch's chainable API (returns a chainable with `.perform`) and\n // Cucumber async/await (returns a Promise) — we narrow at each branch.\n const b = browser as unknown as Record<\n string,\n (...args: unknown[]) => unknown\n >\n\n const wrapNav = (methodName: string) => {\n if (typeof b[methodName] !== 'function') {\n return\n }\n const original = b[methodName].bind(browser)\n\n b[methodName] = function (...args: unknown[]) {\n const result = original(...args)\n\n const injectAndCapture = () => {\n log.info(`[nav] ${methodName}(${args[0] ?? ''}) — injecting script`)\n return sessionCapturer\n .injectScript(browser)\n .then(() => sessionCapturer.captureTrace(browser))\n .catch((err: Error) =>\n log.error(`Failed to inject script: ${(err as Error).message}`)\n )\n }\n\n const chainable = result as NightwatchChainable | undefined\n if (chainable && typeof chainable.perform === 'function') {\n // Standard Nightwatch (chained API): queue inside perform so it\n // runs after navigation completes. Always pass `done` so the\n // command queue is unblocked even if injection fails.\n chainable.perform((done: Function) => {\n injectAndCapture().finally(() => done && done())\n })\n return result\n }\n // Cucumber async/await: result is a Promise (or thenable).\n // Return the AUGMENTED promise so that `await browser.url(...)` in\n // the step definition waits for injectAndCapture to finish before\n // Cucumber moves to the next step. Without this the injection races\n // with the next step's commands (e.g. setValue), causing stale-element\n // errors because the script-tag insertion mutates the DOM mid-form.\n return Promise.resolve(result)\n .then(injectAndCapture)\n .catch(() => {})\n }\n }\n\n wrapNav('url')\n wrapNav('navigate')\n wrapNav('navigateTo')\n\n log.info('✓ Script injection wrapped')\n }\n\n wrapBrowserCommands(browser: NightwatchBrowser): void {\n if (this.proxiedBrowsers.has(browser as object)) {\n return\n }\n\n // Single widening: Nightwatch's `browser` is a dynamic command bag —\n // every wrapped lookup below is property-name → function. Casting once\n // keeps the wrap loop readable.\n const browserAny = browser as unknown as Record<\n string,\n (...args: unknown[]) => unknown\n >\n const allMethods = new Set([\n ...Object.keys(browser),\n ...Object.getOwnPropertyNames(Object.getPrototypeOf(browser))\n ])\n const wrappedMethods: string[] = []\n\n allMethods.forEach((methodName) => {\n if (\n methodName === 'constructor' ||\n typeof browserAny[methodName] !== 'function'\n ) {\n return\n }\n\n if (\n (INTERNAL_COMMANDS_TO_IGNORE as readonly string[]).includes(\n methodName\n ) ||\n methodName.startsWith('__')\n ) {\n return\n }\n\n const originalMethod = browserAny[methodName].bind(browser)\n\n browserAny[methodName] = (...args: unknown[]) => {\n return this.handleCommandExecution(\n browser,\n browserAny,\n methodName,\n originalMethod,\n args\n )\n }\n\n wrappedMethods.push(methodName)\n })\n\n this.proxiedBrowsers.add(browser as object)\n log.info(`✓ Wrapped ${wrappedMethods.length} browser methods`)\n }\n\n private handleRetryReplacement(\n browser: NightwatchBrowser,\n methodName: string,\n logArgs: unknown[],\n serializedResult: unknown,\n effectiveUid: string,\n callSource: string | undefined,\n commandTimestamp: number\n ): void {\n // Same command fired again (internal retry) — replace the previous\n // entry so only the final result appears in the UI.\n const { entry, oldTimestamp } = this.sessionCapturer.replaceCommand(\n this.retryTracker.lastId!,\n methodName,\n logArgs,\n serializedResult,\n undefined,\n effectiveUid,\n callSource,\n commandTimestamp\n )\n this.retryTracker.setLastId(entry._id ?? null)\n this.sessionCapturer.sendReplaceCommand(oldTimestamp, entry)\n this.attachScreenshot(browser, entry, methodName, ' (retry)')\n }\n\n private captureFreshCommand(\n browser: NightwatchBrowser,\n methodName: string,\n logArgs: unknown[],\n serializedResult: unknown,\n effectiveUid: string,\n callSource: string | undefined,\n commandTimestamp: number,\n cmdSig: string\n ): void {\n // captureCommand() pushes the entry to commandsLog synchronously before\n // any async work (navigation perf capture), so we can grab the ID\n // immediately after the call — before any microtask fires. This avoids\n // the race where a Nightwatch retry callback executes before .then() sets\n // lastId, causing missed dedup. Stage the sig now, set the id after the\n // synchronous push lands.\n this.retryTracker.setLastSig(cmdSig)\n this.retryTracker.setLastId(null)\n this.sessionCapturer\n .captureCommand(\n methodName,\n logArgs,\n serializedResult,\n undefined,\n effectiveUid,\n callSource,\n commandTimestamp\n )\n .catch((err) =>\n log.error(`Failed to capture ${methodName}: ${(err as Error).message}`)\n )\n const lastCommand =\n this.sessionCapturer.commandsLog[\n this.sessionCapturer.commandsLog.length - 1\n ]\n if (lastCommand) {\n this.retryTracker.setLastId((lastCommand as { _id?: number })._id ?? null)\n this.sessionCapturer.sendCommand(lastCommand)\n log.info(`[command] ${methodName}`)\n this.attachScreenshot(browser, lastCommand, methodName)\n }\n this.maybeRepollMutations(browser, methodName)\n }\n\n // After DOM-mutating commands, re-poll mutations from the injected script\n // so the browser preview stays in sync. setTimeout runs OUTSIDE Nightwatch's\n // current callback stack (safer queue-wise).\n private maybeRepollMutations(\n browser: NightwatchBrowser,\n methodName: string\n ): void {\n const isDomMutating =\n (NAVIGATION_COMMANDS as readonly string[]).includes(methodName) ||\n [\n 'click',\n 'doubleClick',\n 'rightClick',\n 'setValue',\n 'clearValue',\n 'sendKeys',\n 'submitForm',\n 'back',\n 'forward',\n 'refresh'\n ].includes(methodName)\n if (!isDomMutating) {\n return\n }\n setTimeout(() => {\n this.sessionCapturer.captureTrace(browser).catch(() => {})\n }, 200)\n }\n\n private popCommandStackIfMatches(methodName: string, cmdSig: string): void {\n const stackFrame = this.commandStack[this.commandStack.length - 1]\n if (stackFrame?.command === methodName && stackFrame.signature === cmdSig) {\n this.commandStack.pop()\n }\n }\n\n // Result-capturing callback factory — called by Nightwatch's async queue\n // when the command completes. This is where we get the *actual* result.\n private makeCaptureCallback(\n browser: NightwatchBrowser,\n methodName: string,\n logArgs: unknown[],\n cmdSig: string,\n callSource: string | undefined,\n commandTimestamp: number,\n testUid: string | undefined,\n userCallback: Function | null\n ): (callbackResult: unknown) => unknown {\n return (callbackResult: unknown) => {\n this.popCommandStackIfMatches(methodName, cmdSig)\n const serializedResult = serializeCommandResult(\n callbackResult,\n methodName\n )\n const effectiveUid = this.getCurrentTest()?.uid ?? testUid\n if (effectiveUid) {\n if (this.retryTracker.isRetry(cmdSig)) {\n this.handleRetryReplacement(\n browser,\n methodName,\n logArgs,\n serializedResult,\n effectiveUid,\n callSource,\n commandTimestamp\n )\n } else {\n this.captureFreshCommand(\n browser,\n methodName,\n logArgs,\n serializedResult,\n effectiveUid,\n callSource,\n commandTimestamp,\n cmdSig\n )\n }\n }\n if (userCallback) {\n return userCallback(callbackResult)\n }\n }\n }\n\n private pushCommandStackIfNew(\n methodName: string,\n cmdSig: string,\n callSource: string | undefined\n ): void {\n if (this.lastCommandSig === cmdSig) {\n return\n }\n this.commandStack.push({\n command: methodName,\n callSource,\n signature: cmdSig\n })\n this.lastCommandSig = cmdSig\n }\n\n private handleCommandExecution(\n browser: NightwatchBrowser,\n browserAny: Record<string, unknown>,\n methodName: string,\n originalMethod: Function,\n args: unknown[]\n ): unknown {\n this.testManager.startTestIfPending(\n this.testManager.detectTestBoundary(\n browserAny.currentTest as NightwatchCurrentTest\n )\n )\n\n const callInfo = getCallSourceFromStack()\n if (callInfo.filePath && !this.currentTestFullPath) {\n this.currentTestFullPath = callInfo.filePath\n }\n\n const lastArg = args[args.length - 1]\n const hasUserCallback = typeof lastArg === 'function'\n const userCallback: Function | null = hasUserCallback ? lastArg : null\n const logArgs = hasUserCallback ? args.slice(0, -1) : args\n\n const cmdSig = JSON.stringify({\n command: methodName,\n args: logArgs,\n src: callInfo.callSource\n })\n this.pushCommandStackIfNew(methodName, cmdSig, callInfo.callSource)\n\n const callSource = callInfo.callSource\n const commandTimestamp = Date.now()\n const captureCallback = this.makeCaptureCallback(\n browser,\n methodName,\n logArgs,\n cmdSig,\n callSource,\n commandTimestamp,\n this.getCurrentTest()?.uid,\n userCallback\n )\n const modifiedArgs = [...logArgs, captureCallback]\n try {\n return originalMethod(...modifiedArgs)\n } catch (error) {\n this.popCommandStackIfMatches(methodName, cmdSig)\n this.captureCommandError(methodName, logArgs, error, callSource)\n throw error\n }\n }\n\n private captureCommandError(\n methodName: string,\n args: unknown[],\n error: unknown,\n callSource: string | undefined\n ): void {\n const currentTest = this.getCurrentTest()\n if (!currentTest) {\n return\n }\n\n const normalizedError = toError(error)\n log.error(`[command error] ${methodName}: ${normalizedError.message}`)\n\n this.sessionCapturer\n .captureCommand(\n methodName,\n args,\n undefined,\n normalizedError,\n currentTest.uid,\n callSource\n )\n .catch((err) =>\n log.error(`Failed to capture ${methodName}: ${(err as Error).message}`)\n )\n\n const lastCommand =\n this.sessionCapturer.commandsLog[\n this.sessionCapturer.commandsLog.length - 1\n ]\n if (lastCommand) {\n this.sessionCapturer.sendCommand(lastCommand)\n }\n }\n\n isProxied(browser: NightwatchBrowser): boolean {\n return this.proxiedBrowsers.has(browser as object)\n }\n\n /**\n * Fire-and-forget: pull a screenshot via the WebDriver HTTP endpoint and\n * attach it to an already-captured command entry. The `suffix` is appended\n * to the log line so retried-command screenshots show `(retry)`. Errors\n * are silently swallowed — screenshots are best-effort and shouldn't fail\n * the run.\n */\n private attachScreenshot(\n browser: NightwatchBrowser,\n entry: { timestamp?: number; screenshot?: string | null },\n methodName: string,\n suffix = ''\n ): void {\n const ts = entry.timestamp ?? 0\n this.sessionCapturer\n .takeScreenshotViaHttp(browser)\n .then((screenshot) => {\n if (screenshot) {\n entry.screenshot = screenshot\n this.sessionCapturer.sendReplaceCommand(ts, entry as CommandLog)\n log.info(`[screenshot] Attached to ${methodName}${suffix}`)\n }\n })\n .catch(() => {})\n }\n}\n","import { BOOLEAN_COMMAND_PATTERN } from '../constants.js'\n\n/**\n * Convert the raw value Nightwatch's async queue hands back to a\n * UI-friendly JSON-safe representation. Three special cases:\n *\n * - Nightwatch assertion objects `{ passed, actual, expected, message }`\n * collapse to `true` on pass, or the structured failure record on fail.\n * - Driver-result wrappers `{ value: <raw> }` unwrap to the inner value.\n * `null` on a boolean-semantic command (e.g. `waitForExist`) means\n * \"timed out / not found\" — coerce to `false` so the UI doesn't render\n * `null`.\n * - Plain objects are deep-cloned via JSON.parse/stringify so the UI can\n * safely serialize them; functions and circular references fall back to\n * `String(value)`.\n */\nexport function serializeCommandResult(\n callbackResult: unknown,\n methodName: string\n): unknown {\n if (callbackResult === null || callbackResult === undefined) {\n return undefined\n }\n\n const isBooleanCommand = BOOLEAN_COMMAND_PATTERN.test(methodName)\n\n // After the typeof + null guard above, the value is a non-null object —\n // safe to widen via `Record<string, unknown>` and probe for the discriminator\n // properties without per-access `as any`.\n if (typeof callbackResult === 'object') {\n const r = callbackResult as Record<string, unknown>\n if ('passed' in r) {\n return r.passed\n ? true\n : {\n passed: false,\n actual: r.actual,\n expected: r.expected,\n message: r.message\n }\n }\n if ('value' in r) {\n const raw = r.value\n return raw === null && isBooleanCommand ? false : raw\n }\n }\n\n if (typeof callbackResult !== 'function') {\n try {\n return JSON.parse(JSON.stringify(callbackResult))\n } catch {\n return String(callbackResult)\n }\n }\n\n return undefined\n}\n","import logger from '@wdio/logger'\nimport { ScreencastRecorderBase, errorMessage } from '@wdio/devtools-core'\nimport type { ScreencastOptions } from '@wdio/devtools-shared'\nimport type { SessionCapturer } from './session.js'\nimport type { NightwatchBrowser } from './types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:ScreencastRecorder')\n\n/**\n * Nightwatch screencast recorder. Polling-only — Nightwatch doesn't expose a\n * stable CDP escape hatch the way WDIO (getPuppeteer) and Selenium\n * (createCDPConnection) do.\n *\n * `browser.takeScreenshot()` goes through Nightwatch's command queue and is\n * unreliable for polling (the existing code has `takeScreenshotViaHttp` for\n * the same reason — see session.ts). The recorder delegates to that helper\n * instead so screenshots fire directly over the WebDriver HTTP transport.\n */\nexport class ScreencastRecorder extends ScreencastRecorderBase<NightwatchBrowser> {\n readonly #sessionCapturer: SessionCapturer\n\n constructor(sessionCapturer: SessionCapturer, options: ScreencastOptions) {\n super(options)\n this.#sessionCapturer = sessionCapturer\n }\n\n protected override onPollingStarted(intervalMs: number): void {\n log.info(\n `✓ Screencast recording started (polling mode, ${intervalMs} ms interval)`\n )\n }\n\n protected override onPollingStopped(frameCount: number): void {\n log.info(`✓ Screencast stopped — ${frameCount} frame(s) collected`)\n }\n\n protected override onUnavailable(err: unknown): void {\n log.warn(\n `Screencast unavailable (${errorMessage(err)}). Recording skipped.`\n )\n }\n\n protected override async takeScreenshot(): Promise<string | null> {\n const browser = this.driver\n if (!browser) {\n return null\n }\n return this.#sessionCapturer.takeScreenshotViaHttp(browser)\n }\n}\n"],"mappings":"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","names":["path","xpath","resolve","resolve","fs","os","path","fs","path","require","resolve","log","path","fs","os","fs","path","fs","path","createRequire","require","resolve","fs","fs","resolve","fs","logger","fs","path","os","fs","path","parseStackTrace","generateStableUid","parseStackTrace","resolve","logger","log","logger","logger","fs","path","log","logger","logger","fs","path","path","fs","logger","log","logger","resolve","log","logger","resolve","logger","logger","captureActionSnapshot","log","logger","path","captureActionSnapshot","resolve","logger","log","logger","generateStableUid","generateStableUid","logger","log","logger","path","logger","log","logger","log","logger","finalizeCurrentScreencast","resolve","log","logger"]}
|