@wdio/nightwatch-devtools 1.7.0 → 1.7.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.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../../shared/src/files.ts","../../shared/src/locator-dialect.ts","../../shared/src/routes.ts","../../shared/src/runner.ts","../../shared/src/snapshot-format.ts","../../shared/src/timing.ts","../../shared/src/trace-actions.ts","../../shared/src/trace-file.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/artifact-naming.ts","../../core/src/artifacts-manifest.ts","../../core/src/allure-artifacts.ts","../../core/src/screenshot-artifact.ts","../../core/src/video-slice.ts","../../core/src/video-encoder.ts","../../core/src/trace-retention.ts","../../core/src/attempt-tracker.ts","../../core/src/assert-patcher.ts","../../core/src/stack.ts","../../core/src/sha1.ts","../../core/src/trace-sources.ts","../../core/src/trace-hierarchy.ts","../../core/src/trace-action-events.ts","../../core/src/trace-console.ts","../../core/src/trace-exporter.ts","../../core/src/trace-snapshots.ts","../../core/src/screencast-trace.ts","../../core/src/trace-frame-snapshots.ts","../../core/src/trace-har.ts","../../core/src/trace-zip-writer.ts","../../core/src/trace-mutations.ts","../../core/src/trace-transcript.ts","../../core/src/spec-trace-helpers.ts","../../core/src/uid.ts","../../core/src/trace-finalizer.ts","../../core/src/script-loader.ts","../../core/src/bidi-preload.ts","../../core/src/terminal-throttle.ts","../../core/src/finalize-screencast.ts","../../core/src/input-dispatch.ts","../../core/src/output-dir.ts","../../core/src/performance-capture.ts","../../core/src/read-value-locators.ts","../../core/src/retry-tracker.ts","../../core/src/run-id.ts","../../core/src/screencast.ts","../../core/src/session-capturer.ts","../../core/src/suite-helpers.ts","../../core/src/test-discovery.ts","../../core/src/test-reporter.ts","../src/trace-context.ts","../src/test-artifacts.ts","../src/helpers/assertCapture.ts","../src/run-lifecycle.ts","../src/plugin-global-key.ts","../src/constants.ts","../src/helpers/utils.ts","../src/session-init.ts","../src/session.ts","../src/helpers/webdriverHttp.ts","../src/action-snapshot.ts","../src/helpers/perfLogs.ts","../src/reporter.ts","../src/helpers/runningTest.ts","../src/helpers/testManager.ts","../src/helpers/suiteManager.ts","../src/helpers/browserProxy.ts","../src/helpers/callbackError.ts","../src/helpers/assertTarget.ts","../src/helpers/serializeCommandResult.ts","../src/helpers/nativeAssertions.ts","../src/helpers/nativeAssertRecorder.ts","../src/screencast-session.ts","../src/screencast.ts","../src/plugin-screencast.ts","../src/event-hub.ts","../src/cucumber-lifecycle.ts","../src/helpers/cucumberResult.ts","../src/helpers/cucumberScenarioBuilder.ts","../src/helpers/featureFileScan.ts","../src/trace-slices.ts","../src/test-lifecycle.ts","../src/helpers/specFileResolver.ts","../src/helpers/closePreviousTest.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 finalizeTraceExport,\n flushRangeLogged,\n resolveAdapterOutputDir,\n TestAttemptTracker,\n tracePolicyModeWarning,\n type SpecRange,\n type TraceArtifact,\n type TraceExportContext\n} from '@wdio/devtools-core'\nimport { buildTraceContext } from './trace-context.js'\nimport { emitTestArtifacts } from './test-artifacts.js'\nimport { wireAssertCapture } from './helpers/assertCapture.js'\nimport { stop as stopBackend } from '@wdio/devtools-backend'\nimport {\n REUSE_ENV,\n type CucumberPickle,\n type CucumberPickleStep\n} 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 {\n PluginScreencast,\n resolveScreencastOptions\n} from './plugin-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 SuiteStats,\n TestStats\n} from './types.js'\nimport { registerEventHandlers as registerEventHandlersImpl } from './event-hub.js'\nimport {\n cucumberBefore as cucumberLifecycleBefore,\n cucumberAfter as cucumberLifecycleAfter,\n captureCucumberScenarioBeforeQuit as cucumberLifecycleBeforeQuit,\n cucumberBeforeStep as cucumberLifecycleBeforeStep,\n cucumberAfterStep as cucumberLifecycleAfterStep,\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 { ensureSessionInitialized } from './session-init.js'\nimport { captureNativeAssertions } from './helpers/nativeAssertions.js'\nimport { flushTestSlice, recordSpecSliceBoundary } from './trace-slices.js'\nimport {\n getTestIcon,\n incrementCounters,\n buildPluginMetadataOptions,\n nightwatchRunnerId\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 /** Every trace/video artifact seen this run (retained or not), for the\n * end-of-run artifacts manifest. Populated via the context's onArtifact. */\n #artifacts: TraceArtifact[] = []\n\n /** In-flight fire-and-forget eager/boundary slice flushes, awaited at finalize\n * so the last slice's write + its manifest entry land before teardown. */\n #traceFlushes: Promise<unknown>[] = []\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 #screencast: PluginScreencast\n #bidiEnabled = false\n #armedSessionId: string | undefined\n\n /** Wall-clock ms at the current test/scenario start — the lower bound of that\n * test's action-snapshot and video-frame window (per-test artifact slicing). */\n #currentTestStartWallTime = 0\n\n // Nightwatch `--retries` and cross-worker reruns may reset this in-process\n // tracker; only retries that re-enter this process's start hook are counted.\n #attemptTracker = new TestAttemptTracker()\n\n constructor(options: DevToolsOptions = {}) {\n const mode = options.mode ?? 'live'\n // Resolve the defaults ONCE and gate on the resolved values: reading the raw\n // `options.filmstrip === true` left the default-on filmstrip inert unless a\n // config set it explicitly, so the recorder never started while\n // `this.options.filmstrip` claimed it was on.\n const filmstrip = options.filmstrip ?? true\n const video = options.video ?? 'off'\n const screencast = resolveScreencastOptions({\n mode,\n filmstrip,\n video,\n screencast: options.screencast\n })\n if (screencast.warning) {\n log.warn(screencast.warning)\n }\n this.options = {\n port: options.port ?? 3000,\n hostname: options.hostname ?? 'localhost',\n screencast: screencast.options,\n bidi: options.bidi ?? false,\n captureAssertions: options.captureAssertions ?? true,\n mode,\n traceFormat: options.traceFormat ?? 'zip',\n traceGranularity: options.traceGranularity ?? 'session',\n tracePolicy: options.tracePolicy ?? 'on',\n filmstrip,\n screenshot: options.screenshot ?? 'off',\n video,\n // No live Allure signal in Nightwatch (produce-only), so no auto-detect:\n // off by default, opt-in via the option.\n emitArtifactsManifest: options.emitArtifactsManifest ?? false\n }\n const policyWarning = tracePolicyModeWarning(options.tracePolicy, mode)\n if (policyWarning) {\n log.warn(policyWarning)\n }\n this.#screencast = new PluginScreencast(\n screencast.options,\n filmstrip || video !== 'off'\n )\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.#screencast.options\n },\n get bidiEnabled() {\n return self.#bidiEnabled\n },\n get captureAssertions() {\n return self.options.captureAssertions\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 armedSessionId() {\n return self.#armedSessionId\n },\n set armedSessionId(v) {\n self.#armedSessionId = 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.#screencast.recorder\n },\n set screencastRecorder(v) {\n self.#screencast.recorder = v\n },\n get screencastSessionId() {\n return self.#screencast.sessionId\n },\n set screencastSessionId(v) {\n self.#screencast.sessionId = v\n },\n get screencastRotation() {\n return self.#screencast.rotation\n },\n set screencastRotation(v) {\n self.#screencast.rotation = v\n },\n get configPath() {\n return self.#configPath\n },\n get runner() {\n return nightwatchRunnerId(self.#isCucumberRunner)\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 self.#attemptTracker.reset()\n },\n recordAttempt: (uid, specFile) =>\n self.#attemptTracker.recordStart(uid, specFile),\n recordOutcome: (uid, state) =>\n self.#attemptTracker.recordOutcome(uid, state),\n attemptFor: (uid) => self.#attemptTracker.attemptFor(uid),\n buildMetadataOptions: () => self.#buildMetadataOptions(),\n ensureSessionInitialized: (b) => self.#ensureSessionInitialized(b),\n finalizeCurrentScreencast: () => self.#finalizeCurrentScreencast(),\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 get traceMode() {\n return self.options.mode === 'trace'\n },\n get traceGranularity() {\n return self.options.traceGranularity\n },\n get specRanges() {\n return self.#specRanges\n },\n get flushedSpecs() {\n return self.#flushedSpecs\n },\n flushTraceRange: (range) => self.#flushSpecTrace(range),\n emitTestArtifacts: (uid, failed) => self.#emitTestArtifacts(uid, failed)\n }\n return this.#internals\n }\n\n /** Boundary cast: currentTest is Nightwatch's loose bag; only uid is read. */\n #currentTestUid(): string | undefined {\n return (this.#currentTest as { uid?: string } | null)?.uid\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 if (this.options.captureAssertions) {\n wireAssertCapture(\n () => this.sessionCapturer,\n // Rows are tagged per-`it` (BrowserProxy.rowTestUid); `#currentTestUid`\n // — the lifecycle test, which the BDD interface pins to the module's\n // first `it` — is only the fallback, and stays the artifact/slice key.\n () => this.browserProxy?.rowTestUid() ?? this.#currentTestUid()\n )\n }\n }\n\n async #ensureSessionInitialized(browser: NightwatchBrowser) {\n await ensureSessionInitialized(this.#getInternals(), browser)\n }\n\n async #finalizeCurrentScreencast(): Promise<void> {\n await this.#screencast.finalize(this.#getInternals())\n }\n\n async cucumberBefore(browser: NightwatchBrowser, pickle: CucumberPickle) {\n this.#currentTestStartWallTime = Date.now()\n await cucumberLifecycleBefore(this.#getInternals(), browser, pickle)\n }\n\n async cucumberBeforeQuit(browser: NightwatchBrowser, result: CucumberResult) {\n await cucumberLifecycleBeforeQuit(this.#getInternals(), browser, result)\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 specFile: string | null\n ): Promise<void> {\n await startNextTest(\n this.#getInternals(),\n currentSuite,\n currentTestName,\n processedTests,\n specFile\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 this.#currentTestStartWallTime = Date.now()\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 if (fullPath) {\n recordSpecSliceBoundary(this.#getInternals(), fullPath)\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(\n currentSuite,\n currentTestName,\n processedTests,\n fullPath\n )\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 if (this.options.captureAssertions) {\n await captureNativeAssertions(\n this.sessionCapturer,\n browser,\n browser.currentTest as NightwatchCurrentTest | undefined,\n this.#currentTestUid(),\n this.browserProxy.drainNativeAssertCalls()\n )\n }\n // Anchored: a test ending on a navigation leaves the destination's\n // async initial anchor unrun, so its DOM would be absent from the slice.\n await this.sessionCapturer.captureTrace(browser, true)\n // Flush this test's slice before the next test overwrites its outcome.\n flushTestSlice(this.#getInternals())\n const results = (browser.currentTest as NightwatchCurrentTest)?.results\n const failed =\n !!results && ((results.errors ?? 0) > 0 || (results.failed ?? 0) > 0)\n await this.#emitTestArtifacts(this.#currentTestUid(), failed)\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 /** Directory for produced artifacts — next to the test file, else the config,\n * else cwd (mirrors #traceContext's resolution). */\n get #outputDir(): string {\n return resolveAdapterOutputDir({\n testFilePath: this.browserProxy?.getCurrentTestFullPath?.() ?? undefined,\n configPath: this.#configPath\n })\n }\n\n /** Produce (never attach — no live Allure API) this test's per-test screenshot\n * and video slice. No-op outside trace mode + `test` granularity (core-gated).\n * Shared by the per-test `afterEach` and the cucumber per-scenario finalize. */\n async #emitTestArtifacts(\n uid: string | undefined,\n failed: boolean\n ): Promise<void> {\n const attempt = uid ? this.#attemptTracker.attemptFor(uid) : undefined\n await emitTestArtifacts({\n mode: this.options.mode,\n granularity: this.options.traceGranularity,\n screenshotPolicy: this.options.screenshot,\n videoPolicy: this.options.video,\n failed,\n actionSnapshots: this.sessionCapturer.actionSnapshots,\n frames: this.#screencast.frames,\n startWallTime: this.#currentTestStartWallTime,\n outcomes: uid ? this.#attemptTracker.forTest(uid, attempt) : [],\n uid,\n attempt,\n sessionId: this.sessionCapturer?.metadata?.sessionId,\n outputDir: this.#outputDir,\n captureFormat: this.#screencast.options.captureFormat,\n onArtifact: (a) => this.#artifacts.push(a),\n onLog: (level, msg) => log[level](msg)\n })\n }\n\n async after(browser?: NightwatchBrowser) {\n // Drain first: a rotation still in flight would otherwise start its new\n // recorder after this finalize and strand the poll interval.\n await this.#screencast.rotation\n await this.#finalizeCurrentScreencast()\n try {\n await this.#finalizeAllSuites(browser)\n this.#logRunSummary()\n if (this.options.mode === 'trace') {\n await this.#writeTraceIfNeeded()\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 /** Thin wrapper so boundary flushes and the final flush share one path.\n * flushRangeLogged logs+swallows a failed flush (shared spec/test string) so\n * the fire-and-forget boundary callers don't each re-implement the guard. */\n #flushSpecTrace(range: SpecRange): Promise<TraceArtifact | undefined> {\n const sessionId = this.sessionCapturer.metadata?.sessionId\n if (!sessionId) {\n return Promise.resolve(undefined)\n }\n // Track the promise (before it's added to awaitPending) so finalize awaits\n // this fire-and-forget flush — its context snapshot already excludes it.\n const flush = flushRangeLogged(this.#traceContext(sessionId), range)\n this.#traceFlushes.push(flush)\n return flush\n }\n\n /** Assemble the framework-agnostic trace-export context from plugin state.\n * Output dir ignores the spec range — nightwatch writes next to config. */\n #traceContext(sessionId: string): TraceExportContext {\n return buildTraceContext(\n {\n mode: this.options.mode,\n policy: this.options.tracePolicy,\n granularity: this.options.traceGranularity,\n format: this.options.traceFormat,\n capturer: this.sessionCapturer,\n suites: this.suiteManager.getAllSuites().values(),\n outcomes: this.#attemptTracker,\n ranges: this.#specRanges,\n flushed: this.#flushedSpecs,\n artifacts: this.#artifacts,\n traceFlushes: this.#traceFlushes,\n emitArtifactsManifest: this.options.emitArtifactsManifest,\n screencastFrames: this.options.filmstrip\n ? this.#screencast.frames\n : undefined,\n configPath: this.#configPath,\n testFilePath:\n this.browserProxy?.getCurrentTestFullPath?.() ?? undefined,\n log: (level, msg) => log[level](msg)\n },\n sessionId\n )\n }\n\n async #writeTraceIfNeeded(): Promise<void> {\n const sessionId = this.sessionCapturer?.metadata?.sessionId\n if (this.options.mode !== 'trace' || !this.sessionCapturer || !sessionId) {\n return\n }\n await finalizeTraceExport(this.#traceContext(sessionId))\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","// Which locator each runner is best handed: the grammar its text form is\n// written in, where that text form sits in the capture's branch order, and what\n// it needs told when the grammar is XPath. The one fact table behind both sides\n// — the page capture reads it to decide what to emit, the player reads it to\n// caption what it hands the user.\n//\n// WebdriverIO has its own `tag*=text` form and auto-detects a leading `//`;\n// Selenium and Nightwatch have no text form, and neither auto-detects XPath —\n// Selenium takes it via `By.xpath`, Nightwatch via its `'xpath'` strategy.\n\nimport type { TestRunnerId } from './types.js'\n\n/** Text-locator grammar a runner resolves natively. Only the capture's text\n * branch varies by it; every other branch is portable CSS. */\nexport type TextLocatorDialect = 'webdriverio' | 'xpath'\n\n/** Where the text branch sits among the capture's branches. `first` prefers the\n * readable text form; `fallback` reaches it only when no unique CSS locator\n * exists, still ahead of the meaningless positional path. */\nexport type TextBranchOrder = 'first' | 'fallback'\n\nexport interface LocatorDialect {\n text: TextLocatorDialect\n textBranch: TextBranchOrder\n /** The call that makes the runner treat a captured XPath locator as XPath.\n * Undefined where naming one would be wrong (WebdriverIO needs none) or a\n * guess (an unidentified recorder). */\n xpathHint?: string\n}\n\n/** An unidentified recorder gets XPath text first: it is the portable form and\n * the readable one, so it is the safe answer rather than a guess. */\nexport function locatorDialect(\n runner: TestRunnerId | undefined\n): LocatorDialect {\n switch (runner) {\n case 'mocha':\n case 'jasmine':\n case 'cucumber':\n return { text: 'webdriverio', textBranch: 'first' }\n // Every Selenium locator is wrapped in a `By.…`, so `By.xpath` asks no more\n // of the user than `By.css` — the text form stays first.\n case 'selenium-webdriver':\n return { text: 'xpath', textBranch: 'first', xpathHint: 'By.xpath()' }\n // The only runner where a bare locator string is read under a default CSS\n // strategy, so it is the only one where XPath costs the user something.\n case 'nightwatch':\n case 'nightwatch-cucumber':\n return {\n text: 'xpath',\n textBranch: 'fallback',\n xpathHint: \"useXpath() or locateStrategy: 'xpath'\"\n }\n case undefined:\n return { text: 'xpath', textBranch: 'first' }\n }\n}\n\n/** A captured locator is portable CSS, or XPath where the element's own text\n * identifies it */\nexport function isXPathLocator(locator: string): boolean {\n // No CSS selector can open with `/` or `(`, so this never claims one that\n // querySelector could have parsed. Covers `//a[…]`, `/html/…`, `./a`, `.//a`\n // and the indexed `(//a[…])[2]`.\n return /^\\(*\\.{0,2}\\//.test(locator)\n}\n\n/** Tag name an XPath locator is scoped to, for a consumer that reports the tag\n * it matched. Undefined for CSS and for a tag-less expression (`//*[…]`). */\nexport function xpathLocatorTag(locator: string): string | undefined {\n return /^\\/\\/([a-z][a-z0-9-]*)\\[/.exec(locator)?.[1]\n}\n\n/** WebdriverIO's text-locator forms — `tag*=Contains`, `tag=Exact`, tag optional. */\nconst TEXT_LOCATOR_RE = /^([a-z][a-z0-9-]*)?(\\*?)=(.+)$/i\n\n/** A text locator in either dialect, decomposed. `tag` is `*` when the form\n * names none; `partial` marks a contains-match rather than an exact one.\n * Undefined for every non-text locator, including the `concat()`-stitched\n * literal that text carrying both quote kinds produces — that one is left to\n * exact comparison rather than parsed back. */\nfunction textLocatorParts(\n locator: string\n): { tag: string; text: string; partial: boolean } | undefined {\n const xpath =\n /^\\/\\/([a-z][a-z0-9-]*|\\*)\\[contains\\(\\.,\\s*([\"'])(.*)\\2\\)\\]$/i.exec(\n locator\n )\n if (xpath) {\n return { tag: xpath[1]!.toLowerCase(), text: xpath[3]!, partial: true }\n }\n const wdio = TEXT_LOCATOR_RE.exec(locator)\n if (!wdio) {\n return undefined\n }\n return {\n tag: wdio[1]?.toLowerCase() ?? '*',\n text: wdio[3]!,\n partial: wdio[2] === '*'\n }\n}\n\n/** Whether a locator the page capture generated and one a test wrote denote the\n * same element. Both sides are decomposed from either dialect, because which of\n * the two the capture emits depends on the runner while what a test writes does\n * not — a `===` drops the input point whenever they differ. */\nexport function locatorsMatch(captured: string, written: string): boolean {\n if (captured === written) {\n return true\n }\n const target = textLocatorParts(captured)\n const test = textLocatorParts(written)\n if (!target || !test) {\n return false\n }\n if (target.tag !== '*' && test.tag !== '*' && target.tag !== test.tag) {\n return false\n }\n // The capture stores the element's whole trimmed text, so a contains-match has\n // to be a substring of it while an exact one names it in full.\n return test.partial\n ? target.text.includes(test.text)\n : target.text === test.text\n}\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 * Query params on the worker upgrade. Both answer the same question for the\n * backend — does this socket continue the current run, or start a new one? —\n * which decides whether accumulated run state is kept or wiped. A run opens\n * one worker socket per spec file, so treating every connect as a new run\n * drops earlier specs' data and Preserve & Rerun then has nothing to diff.\n */\nexport const WORKER_WS_QUERY = {\n /** `1` when the adapter reopened this socket mid-run (e.g. after a session\n * change from `browser.end()` or `reloadSession()`). */\n reconnect: 'reconnect',\n /** Identity of the run this worker belongs to; shared by every worker the\n * same run forks. */\n runId: 'runId'\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. RUN_ID is stamped\n * before any worker forks so every worker of one run inherits it and reports\n * the same run identity (see `WORKER_WS_QUERY.runId`).\n */\nexport const RUNNER_ENV = {\n RUN_ID: 'DEVTOOLS_RUN_ID',\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","// Single source of truth for the accessibility-snapshot text format (the\n// `-snapshot.txt` trace resource). The producer (core's element-snapshot\n// serializers) and the consumer (the app's A11y-tree parser) both reference\n// these, so the written and parsed grammar can't drift. Keep one canonical form\n// per concept — see CLAUDE.md § \"One source of truth per concept\".\n\n/** Indent step per tree depth (the header sits at one unit; nodes at depth+1). */\nexport const SNAPSHOT_INDENT_UNIT = ' '\n\n/** Prefix of the web page-header line (`[Page: <title> — <url>]`). */\nexport const SNAPSHOT_PAGE_HEADER = '[Page'\n\n/** Native-mobile platforms the mobile serializer writes a header for. */\nexport const SNAPSHOT_NATIVE_PLATFORMS = ['android', 'ios'] as const\n\nexport type SnapshotNativePlatform = (typeof SNAPSHOT_NATIVE_PLATFORMS)[number]\n\n/** Prefix of the native-mobile header line — bare `[android]` when the capture\n * knew neither device nor viewport (what the per-action capture passes), else\n * `[android — <device> (<width>×<height>)]`. */\nexport function snapshotNativeHeader(platform: SnapshotNativePlatform): string {\n return `[${platform}`\n}\n\n/** Whether a line is a snapshot's header rather than a tree node. Every\n * serializer opens with exactly one — `[Page …]` for web, `[<platform> …]` for\n * native — so both forms live in this one predicate and a consumer that only\n * knew the web form cannot render a native header as a node. */\nexport function isSnapshotHeaderLine(line: string): boolean {\n return (\n line.startsWith(SNAPSHOT_PAGE_HEADER) ||\n SNAPSHOT_NATIVE_PLATFORMS.some((platform) =>\n line.startsWith(snapshotNativeHeader(platform))\n )\n )\n}\n\n/** Separator between a node and its captured locator (rendered space-padded). */\nexport const SNAPSHOT_LOCATOR_DELIM = '→'\n\n/** Marks an inferred purpose before the locator (`<role> ∈ \"<purpose>\"`). */\nexport const SNAPSHOT_PURPOSE_TOKEN = '∈'\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\n/** Trace action class assertion commands map to. */\nexport const ASSERT_ACTION_CLASS = 'Assert'\n\n/** node:assert methods the core assert patcher wraps; the reader derives its\n * `Assert.<m>` → `assert.<m>` reverse entries from the same list. */\nexport const TRACKED_ASSERT_METHODS = [\n 'equal',\n 'strictEqual',\n 'deepEqual',\n 'deepStrictEqual',\n 'notEqual',\n 'notStrictEqual',\n 'notDeepEqual',\n 'notDeepStrictEqual',\n 'ok',\n 'fail',\n 'throws',\n 'doesNotThrow',\n 'rejects',\n 'doesNotReject',\n 'match',\n 'doesNotMatch'\n] as const\n\n// assert.<m> (node:assert), verify.<m> (nightwatch soft variants), and\n// expect.<m> (synthesized failing-matcher entries) all render as Assert.\n// Only FAILING expect-webdriverio matchers reach the command log today (via the\n// reporter); recording passing matchers needs a per-adapter capture change.\nconst ASSERT_COMMAND_RE = /^(?:assert|verify|expect)\\.(\\w+)$/\n\nexport function mapAssertCommand(command: string): TraceAction | null {\n const match = ASSERT_COMMAND_RE.exec(command)\n return match ? { class: ASSERT_ACTION_CLASS, method: match[1] } : null\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 // WDIO element reads — surfaced so query steps appear in the timeline the way\n // locator queries do in standard trace viewers. Adapters already capture\n // these; only the export allow-list kept them out.\n getText: { class: 'Element', method: 'getText' },\n getValue: { class: 'Element', method: 'getValue' },\n getAttribute: { class: 'Element', method: 'getAttribute' },\n getProperty: { class: 'Element', method: 'getProperty' },\n getCSSProperty: { class: 'Element', method: 'getCSSProperty' },\n getTagName: { class: 'Element', method: 'getTagName' },\n getLocation: { class: 'Element', method: 'getLocation' },\n getSize: { class: 'Element', method: 'getSize' },\n isDisplayed: { class: 'Element', method: 'isDisplayed' },\n isExisting: { class: 'Element', method: 'isExisting' },\n isEnabled: { class: 'Element', method: 'isEnabled' },\n isSelected: { class: 'Element', method: 'isSelected' },\n isClickable: { class: 'Element', method: 'isClickable' },\n isFocused: { class: 'Element', method: 'isFocused' },\n // Explicit user-facing waits (not the internal polling loops behind them).\n waitForDisplayed: { class: 'Element', method: 'waitForDisplayed' },\n waitForExist: { class: 'Element', method: 'waitForExist' },\n waitForEnabled: { class: 'Element', method: 'waitForEnabled' },\n waitForClickable: { class: 'Element', method: 'waitForClickable' },\n waitUntil: { class: 'Browser', method: 'waitForFunction' },\n // Nightwatch's wait family, normalized onto the WDIO names above. These are\n // written by the user and are the commands a Nightwatch test most often fails\n // on, so leaving them out of the allow-list meant the failing step produced no\n // row at all — and with no row, its error had nowhere to attach and neither the\n // action list nor the Errors tab showed the failure.\n waitForElementVisible: { class: 'Element', method: 'waitForDisplayed' },\n waitForElementNotVisible: { class: 'Element', method: 'waitForHidden' },\n waitForElementPresent: { class: 'Element', method: 'waitForExist' },\n waitForElementNotPresent: { class: 'Element', method: 'waitForNotExist' },\n // WDIO page/browser reads\n getTitle: { class: 'Page', method: 'getTitle' },\n getUrl: { class: 'Page', method: 'getUrl' },\n getPageSource: { class: 'Page', method: 'getPageSource' },\n // Selenium read aliases — normalized onto the WDIO names above so both runners\n // read identically. getText/getAttribute/getTagName/isDisplayed/isEnabled/\n // isSelected share the command name across runners and need no alias.\n getCssValue: { class: 'Element', method: 'getCSSProperty' },\n getRect: { class: 'Element', method: 'getRect' },\n getCurrentUrl: { class: 'Page', method: 'getUrl' }\n}\n\n/** Trace methods (ACTION_MAP values) that act at a point on the page — the\n * exporter emits a hit `point` for these so the player can mark where the\n * action landed. `fill` is included (typing focuses the element first). */\nexport const POINTABLE_METHODS: ReadonlySet<string> = new Set([\n 'click',\n 'dblclick',\n 'hover',\n 'tap',\n 'dragTo',\n 'scrollIntoViewIfNeeded',\n 'selectOption',\n 'fill'\n])\n\n/** Runner command names (native or trace method) that type into an element —\n * drives the timeline's keyboard glyph vs the pointer glyph. */\nconst KEYBOARD_COMMANDS: ReadonlySet<string> = new Set([\n 'setValue',\n 'sendKeys',\n 'addValue',\n 'clearValue',\n 'clear',\n 'keys',\n 'fill',\n 'press'\n])\n\nexport function isKeyboardCommand(command: string): boolean {\n return KEYBOARD_COMMANDS.has(command)\n}\n\n/** Runner-native pointer commands the trace vocabulary deliberately doesn't\n * name — Nightwatch's low-level mouse API has no meaningful trace method, so\n * adding it to ACTION_MAP would put noise in the timeline. Listed here because\n * they still dispatch, and a command that dispatches unrecognized keeps the\n * screenshot poll running straight through it. */\nconst UNMAPPED_POINTER_COMMANDS: ReadonlySet<string> = new Set([\n 'moveToElement',\n 'mouseButtonClick',\n 'mouseButtonDown',\n 'mouseButtonUp',\n 'rightClick',\n 'submitForm'\n])\n\n/**\n * Whether a runner command makes the driver dispatch trusted input into the\n * page. Derived from the vocabulary above rather than declared as a second list:\n * a command qualifies when it types (`isKeyboardCommand`) or when its trace\n * method acts at a point (`POINTABLE_METHODS`). A command wrongly classified as\n * non-dispatching is the costly direction — it leaves the screencast poll racing\n * the dispatch — so the unmapped pointer commands above are named explicitly.\n */\nexport function isInputDispatchingCommand(command: string): boolean {\n if (isKeyboardCommand(command) || UNMAPPED_POINTER_COMMANDS.has(command)) {\n return true\n }\n const action = ACTION_MAP[command]\n return action !== undefined && POINTABLE_METHODS.has(action.method)\n}\n","// The trace.zip file layout — the one definition of how a trace is packaged.\n//\n// `core` writes these entries and `backend` reads them, and the two packages\n// cannot import each other (backend depends on shared only). Before this module\n// both sides carried their own copies of these strings, so a rename in the\n// writer left every reader test green and the mismatch only surfaced when\n// someone opened a real trace. Import from here on both sides instead.\n//\n// The *event interfaces* deliberately stay separate: `core`'s are strict (the\n// writer always emits `pageId`, `apiName`, `params`, `title`) while the reader's\n// are permissive, because it also parses traces this repo did not write — see\n// `backend/src/trace-reader-types.ts`. Strict producer, lenient consumer; only\n// the names below have to agree.\n\n/** Entry names inside a trace.zip, and the directory prefix for blobs. Also the\n * filenames used by the unpacked `ndjson-directory` format. */\nexport const TRACE_ZIP_ENTRIES = {\n /** NDJSON action events. */\n trace: 'trace.trace',\n /** NDJSON HAR resource snapshots. Omitted when nothing was captured. */\n network: 'trace.network',\n /** NDJSON DOM mutation stream. Omitted when nothing was captured. */\n mutations: 'trace.mutations',\n /** Human/LLM-readable Markdown run transcript. */\n transcript: 'transcript.md',\n /** Screenshots, element snapshots and network bodies live under here. */\n resourcesDir: 'resources'\n} as const\n\n/** Stream suffixes the reader matches on. A zip may carry several streams (one\n * per page/session), so entries are matched by suffix rather than exact name;\n * `.stacks` is a sidecar the writer does not emit but foreign traces do. */\nexport const TRACE_STREAM_SUFFIXES = {\n trace: '.trace',\n network: '.network',\n mutations: '.mutations',\n stacks: '.stacks'\n} as const\n\n/** `type` discriminants of the NDJSON events in a `.trace` stream. */\nexport const TRACE_EVENT_TYPES = {\n before: 'before',\n after: 'after',\n frameSnapshot: 'frame-snapshot',\n screencastFrame: 'screencast-frame',\n contextOptions: 'context-options',\n console: 'console',\n stdout: 'stdout',\n stderr: 'stderr'\n} as const\n\nexport type TraceEventType =\n (typeof TRACE_EVENT_TYPES)[keyof typeof TRACE_EVENT_TYPES]\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 const LOG_LEVELS = [\n 'trace',\n 'debug',\n 'log',\n 'info',\n 'warn',\n 'error'\n] as const\nexport type LogLevel = (typeof LOG_LEVELS)[number]\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 open in any standard trace viewer — the unpacked\n * 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; `test` writes one trace\n * per test. Only applies in trace mode. */\nexport type TraceGranularity = 'session' | 'spec' | 'test'\n\n/** Retention policy for written traces. Only applies in trace mode; `on` is\n * the current always-write behavior (there is no `off` — that's simply not\n * using trace mode). */\nexport type TraceRetentionPolicy =\n | 'on'\n | 'retain-on-failure'\n | 'retain-on-first-failure'\n | 'on-first-retry'\n | 'on-all-retries'\n | 'retain-on-failure-and-retries'\n\n/** Per-test screenshot capture policy, mirroring Playwright's `screenshot`\n * option. `only-on-failure` shoots after a failing test; `on` after every\n * test; `off` (default) never. Only applies in trace mode. */\nexport type TraceScreenshotPolicy = 'off' | 'on' | 'only-on-failure'\n\n/** Per-test video capture policy. `off` (default) records nothing; any other\n * value records the screencast and keeps each test's video slice per the same\n * retention semantics as `tracePolicy`. Only applies in trace mode at\n * `traceGranularity: 'test'` (the per-test scope videos attach to). */\nexport type TraceVideoPolicy = 'off' | TraceRetentionPolicy\n\n/** One node in a test's ancestor chain, outermost first. */\nexport interface TestAncestor {\n uid: string\n title: string\n kind: 'feature' | 'scenario' | 'suite' | 'test' | 'step' | 'hook'\n}\n\n/** Per-test metadata for Tracing.tracingGroup events in trace output. */\nexport interface TestMetadataEntry {\n title: string\n specFile: string\n state?: TestStatus\n attempt?: number\n ancestry?: TestAncestor[]\n}\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, TestMetadataEntry>\n\n/**\n * Normalized assertion result an adapter may attach to `CommandLog.result` for\n * an assertion command. The trace exporter's assert-param builder prefers this\n * over the positional `[actual, expected]` arg convention — correct for\n * frameworks whose asserts pass only an expected value (a matcher like\n * `titleContains('x')`), where args[0] is the expected, not the actual.\n * Cross-package contract: adapters produce it, core's exporter consumes it.\n */\nexport interface CollapsedAssertResult {\n passed: boolean\n actual?: unknown\n expected?: unknown\n message?: string\n}\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 const TEST_RUNNER_IDS = [\n 'mocha',\n 'jasmine',\n 'cucumber',\n 'nightwatch',\n 'nightwatch-cucumber',\n 'selenium-webdriver'\n] as const\nexport type TestRunnerId = (typeof TEST_RUNNER_IDS)[number]\n\n/**\n * Narrow an untrusted value onto {@link TestRunnerId}. Needed wherever the id\n * arrives from outside this build — a framework's own config value, or a trace\n * zip recorded by another version — since the static type can't vouch for it.\n */\nexport function isTestRunnerId(value: unknown): value is TestRunnerId {\n return (\n typeof value === 'string' &&\n (TEST_RUNNER_IDS as readonly string[]).includes(value)\n )\n}\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 /** Monotonic issue order, stamped when the test issued the command. Breaks\n * ties the millisecond clock can't: a deferred row (a Nightwatch native\n * assert, finalized in a batch at test-end) is appended long after the driver\n * row it was issued before, so an equal `startTime` otherwise resolved in\n * insertion order and put the assert after the command that followed it. */\n sequence?: number\n callSource?: string\n screenshot?: string\n testUid?: string\n performance?: PerformanceData\n cookies?: string\n documentInfo?: DocumentInfo\n id?: number\n /** Cucumber step this command ran under — nests below testUid in the trace's\n * group tree (Feature → Scenario → Step). Set by the adapter step hooks. */\n stepUid?: string\n /** Depth-indented accessibility-tree text for the page state at this command\n * (from the per-action `-snapshot.txt` resource). Reconstructed by the trace\n * reader; drives the player's A11y tab. */\n snapshotText?: string\n /** Page-coordinate hit point for pointer actions — the centre of the matched\n * element at capture time. Synthesized in the exporter; drives the snapshot\n * click marker and the timeline pointer glyph. */\n point?: { x: number; y: number }\n /** Locator of the element this command acted on. Survives assertion folding\n * (where `args` becomes the expected value), so the player's element overlay\n * can box an assertion target like `#flash`. Carried through the trace as the\n * `locator` action param — kept distinct from the `selector` param so it\n * never feeds `paramsToArgs`. */\n selector?: string\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\n/**\n * Every word a captured `NetworkRequest.type` can carry. The capture side\n * classifies once — `core`'s `getRequestType` for driver-observed traffic, the\n * page collector for its patched `fetch`/`XMLHttpRequest` — and the app only\n * translates these words into display buckets. Adding a category here is the\n * compile-time trigger that forces every consumer's mapping to handle it.\n * `other` is the residual for traffic no producer could classify (a failed\n * request that never got a content-type, a foreign trace's unknown mime).\n */\nexport const REQUEST_TYPES = [\n 'document',\n 'stylesheet',\n 'script',\n 'image',\n 'font',\n 'fetch',\n 'xhr',\n 'other'\n] as const\nexport type RequestType = (typeof REQUEST_TYPES)[number]\n\n/**\n * Narrow an untrusted value onto {@link RequestType}. Needed wherever a type\n * arrives from outside this build — a trace file written by an older version,\n * or an adapter payload off the wire — since the static type can't vouch for it.\n */\nexport function isRequestType(value: unknown): value is RequestType {\n return (\n typeof value === 'string' &&\n (REQUEST_TYPES as readonly string[]).includes(value)\n )\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: RequestType\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 /** Cap on frames held in memory (default: 2000 ≈ several minutes at ~5 fps); the buffer is decimated in place past this. */\n maxBufferFrames?: 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 maxBufferFrames: 2000\n}\n\n/**\n * Options every framework adapter accepts. Each adapter's own options interface\n * extends this and adds only its framework-specific fields (e.g. WDIO's\n * devtoolsCapabilities, Selenium's openUi, Nightwatch's bidi).\n */\nexport interface BaseDevToolsOptions {\n /** Port to launch the application on (default: random). */\n port?: number\n /** Hostname to launch the application on. @default localhost */\n hostname?: string\n /** Screencast recording options. When enabled, a continuous video of the\n * browser session is recorded and saved as a .webm file. */\n screencast?: ScreencastOptions\n /** Capture node:assert assertions (and framework `expect` matchers where\n * supported) as first-class commands. Default true. */\n captureAssertions?: boolean\n /** `live` (default) launches the DevTools UI; `trace` skips it. */\n mode?: DevToolsMode\n /** Trace output layout — `zip` (default) writes a single archive,\n * `ndjson-directory` unpacks into `trace-<id>/`. Only applies in trace mode. */\n traceFormat?: TraceFormat\n /** Trace output granularity — `session` (default) writes one trace per\n * worker session; `spec` writes one per spec file. Only applies in trace mode. */\n traceGranularity?: TraceGranularity\n /** Trace retention policy — gates which traces are kept (e.g.\n * `retain-on-failure`). Default `on` (keep all). Only applies in trace mode. */\n tracePolicy?: TraceRetentionPolicy\n /** Record a dense, continuous screencast filmstrip into the trace for\n * scrubbable playback in the trace player — not just one frame per action.\n * The dense frames are added alongside the per-action frames (which carry the\n * DOM snapshots). Runs the screencast recorder (CDP push on Chrome, polling\n * elsewhere). Default true. Set `false` to record only one frame per action\n * (smaller trace, no continuous recorder). Only applies in trace mode. */\n filmstrip?: boolean\n /** Write the `devtools-artifacts-<sessionId>.json` manifest next to the trace\n * — the generic index reporters/CI consume to discover produced artifacts.\n * Off by default; auto-enabled when an Allure reporter is detected (WDIO:\n * `@wdio/allure-reporter` in config; Selenium: an active `allure-js-commons`\n * runtime). Nightwatch has no live Allure signal, so it stays opt-in there.\n * Only applies in trace mode. */\n emitArtifactsManifest?: boolean\n}\n\n/** Minimal Cucumber pickle-step shape — only the fields the adapters read.\n * Cucumber's own types vary across versions, so we pin just these. */\nexport interface CucumberPickleStep {\n text?: string\n astNodeIds?: string[]\n location?: { line?: number }\n}\n\n/** Minimal Cucumber pickle shape — only the fields the adapters read. `steps`\n * is present only where the adapter walks step boundaries (Nightwatch). */\nexport interface CucumberPickle {\n name?: string\n uri?: string\n location?: { line?: number }\n astNodeIds?: string[]\n steps?: CucumberPickleStep[]\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 /** Runner that recorded this stream. Drives the locator dialect the page\n * capture emits and the player's locator hint; undefined for a trace zip\n * recorded before the field existed, or by a foreign tool. */\n runner?: TestRunnerId\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 * Trailing sentinel line in a `trace.mutations` NDJSON stream, marking that\n * `dropped` late mutations were discarded under the size cap. A distinct shape\n * (not a `TraceMutation.type`) so it never collides with a real mutation. This\n * is the writer↔reader contract shared by core's exporter and the backend\n * reader, so both agree on the key.\n */\nexport interface MutationsTruncationMarker {\n __truncated__: true\n dropped: number\n}\n\n/** True when an NDJSON entry is the truncation sentinel, not a mutation. */\nexport function isMutationsTruncationMarker(\n entry: unknown\n): entry is MutationsTruncationMarker {\n return (\n typeof entry === 'object' &&\n entry !== null &&\n (entry as { __truncated__?: unknown }).__truncated__ === true\n )\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 /** Dense screencast frames (continuous recorder buffer) for the trace-mode\n * filmstrip. Present only when `filmstrip` is enabled; thinned + content-\n * addressed at export time. */\n screencastFrames?: ScreencastFrame[]\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 {\n ACTION_MAP,\n ASSERT_ACTION_CLASS,\n mapAssertCommand,\n type TraceAction\n} from '@wdio/devtools-shared'\n\nexport type { TraceAction }\nexport { ASSERT_ACTION_CLASS, mapAssertCommand }\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// $ / $$ / findElement(s) / getElement(s) — locator resolution fires on every\n// element access; high-frequency internal machinery, not a timeline step.\n// Passing expect-webdriverio matchers — never reach the command log (only\n// failures do, via the reporter); surfacing them is a per-adapter change.\n\nexport function mapCommandToAction(command: string): TraceAction | null {\n return ACTION_MAP[command] ?? mapAssertCommand(command)\n}\n\nconst ASSERT_TITLE_VALUE_MAX = 40\n\nfunction formatAssertValue(value: unknown): string {\n let text: string\n if (typeof value === 'object' && value !== null) {\n try {\n text = JSON.stringify(value)\n } catch {\n text = String(value)\n }\n } else {\n text = typeof value === 'string' ? JSON.stringify(value) : String(value)\n }\n return text.length > ASSERT_TITLE_VALUE_MAX\n ? `${text.slice(0, ASSERT_TITLE_VALUE_MAX - 1)}…`\n : text\n}\n\n// The label mirrors the CALL the user wrote — its positional args\n// (`assert.equal(a, b)`, `titleContains('x')`). The normalized actual/expected\n// params drive the result diff, NOT the label: a single-arg assert like\n// titleContains passes only the expected, so labelling it with the derived\n// actual would misrepresent the call. Fall back to actual/expected only when\n// no args survived (reader round-trips that dropped them).\nfunction formatAssertTitle(\n action: TraceAction,\n args: unknown[],\n params?: Record<string, unknown>,\n command?: string\n): string {\n const values =\n args.length > 0 ? args.slice(0, 2) : [params?.actual, params?.expected]\n const label = values\n .filter((value) => value !== undefined)\n .map(formatAssertValue)\n .join(', ')\n return `${command ?? `assert.${action.method}`}(${label})`\n}\n\nexport function formatActionTitle(\n action: TraceAction,\n args: unknown[],\n params?: Record<string, unknown>,\n command?: string\n): string {\n if (action.class === ASSERT_ACTION_CLASS) {\n return formatAssertTitle(action, args, params, command)\n }\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\nimport { locatorDialect } from '@wdio/devtools-shared'\nimport type {\n LocatorDialect,\n TestRunnerId,\n TextLocatorDialect\n} from '@wdio/devtools-shared'\n\n/** Shared by both injected scripts below — the same visibility gate decides\n * which elements each one reports. */\nconst IS_VISIBLE_SCRIPT = `\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\n/** XPath 1.0 has no string escape, so a value carrying both quote kinds is\n * stitched from single-kind literals; a literal double quote can only enter the\n * expression as its own single-quoted token. */\nconst XPATH_TEXT_LITERAL_SCRIPT = `\n function xpathTextLiteral(value) {\n if (value.indexOf('\"') === -1) { return '\"' + value + '\"' }\n if (value.indexOf(\"'\") === -1) { return \"'\" + value + \"'\" }\n var quoteToken = \"'\" + '\"' + \"'\"\n var parts = value.split('\"')\n var pieces = []\n for (var p = 0; p < parts.length; p++) {\n if (parts[p]) { pieces.push('\"' + parts[p] + '\"') }\n if (p < parts.length - 1) { pieces.push(quoteToken) }\n }\n // concat() takes at least two arguments — a value that is nothing but\n // double quotes yields one piece and needs no concat.\n return pieces.length > 1 ? 'concat(' + pieces.join(', ') + ')' : pieces[0]\n }\n`\n\n/** The meaning-bearing CSS branches: portable across all runners, so they are\n * dialect-independent. Null when none of them identifies the element uniquely,\n * which is what lets a caller order them against the text branch. */\nconst SEMANTIC_CSS_SELECTOR_SCRIPT = `\n function semanticCssSelector(element, tag) {\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) {\n var idSel = '#' + CSS.escape(element.id)\n if (document.querySelectorAll(idSel).length === 1) { return idSel }\n }\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 var typeAttr = element.getAttribute('type')\n if (typeAttr) {\n var typeSel = tag + '[type=\"' + CSS.escape(typeAttr) + '\"]'\n if (document.querySelectorAll(typeSel).length === 1) { return typeSel }\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 return null\n }\n`\n\n/** Last resort: a positional `:nth-of-type` path, which always resolves but\n * carries no meaning — hence every other branch getting first refusal. */\nconst POSITIONAL_SELECTOR_SCRIPT = `\n function positionalSelector(element) {\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\n/** The text branch's return expression. WebdriverIO's `tag*=text` compiles\n * internally to XPath with `\"` quoting, so a text carrying a double quote would\n * yield a broken expression — those keep the XPath form, which it also resolves. */\nfunction textLocatorExpression(dialect: TextLocatorDialect): string {\n const xpath = \"'//' + tag + '[contains(., ' + xpathTextLiteral(text) + ')]'\"\n return dialect === 'webdriverio'\n ? `text.indexOf('\"') === -1 ? tag + '*=' + text : ${xpath}`\n : xpath\n}\n\n/** Identify the element by its own text, in `dialect`'s grammar. Null when the\n * text neither exists nor singles it out, so it composes with the CSS branches\n * in either order. */\nfunction textSelectorScript(dialect: TextLocatorDialect): string {\n return `\n function textSelector(element, tag) {\n var text = (element.textContent || '').trim().replace(/\\\\s+/g, ' ')\n if (!text || text.length > 120) { return null }\n var sameTagElements = document.querySelectorAll(tag)\n var matchCount = 0\n sameTagElements.forEach(function(el) { if (el.textContent.includes(text)) { matchCount++ } })\n // The DOM predicate this counts is exactly XPath's\n // \\`//tag[contains(., text)]\\`, so a single match here is a unique match\n // there — the emitted expression carries the uniqueness just checked.\n if (matchCount !== 1) { return null }\n return ${textLocatorExpression(dialect)}\n }\n`\n}\n\n/** Shared by both injected scripts below, so one grammar produces the locator in\n * `-snapshot.txt` and `-elements.json`. The dialect decides both what the text\n * branch emits and where it sits; the positional path stays last either way. */\nfunction getSelectorScript(dialect: LocatorDialect): string {\n const preferred =\n dialect.textBranch === 'first'\n ? ['textSelector(element, tag)', 'semanticCssSelector(element, tag)']\n : ['semanticCssSelector(element, tag)', 'textSelector(element, tag)']\n return `\n ${XPATH_TEXT_LITERAL_SCRIPT}\n ${SEMANTIC_CSS_SELECTOR_SCRIPT}\n ${POSITIONAL_SELECTOR_SCRIPT}\n ${textSelectorScript(dialect.text)}\n\n function getSelector(element) {\n var tag = element.tagName.toLowerCase()\n return ${preferred[0]} || ${preferred[1]} || positionalSelector(element)\n }\n`\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 locator, in `runner`'s own text\n * dialect — omit it for the portable XPath form every runner resolves.\n */\nexport function accessibilityTreeScript(\n inViewportOnly: boolean,\n runner?: TestRunnerId\n): 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 ${getSelectorScript(locatorDialect(runner))}\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 ${IS_VISIBLE_SCRIPT}\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 locator, in `runner`'s own text\n * dialect — omit it for the portable XPath form every runner resolves.\n */\nexport function elementsScript(\n includeBounds: boolean,\n inViewportOnly: boolean,\n runner?: TestRunnerId\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 ${IS_VISIBLE_SCRIPT}\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 ${getSelectorScript(locatorDialect(runner))}\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. `&nbsp;`,\n * `&copy;`, bare `&` in URLs) → replaced with `&amp;`.\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 '&amp;'\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'\n\nimport {\n SNAPSHOT_INDENT_UNIT,\n SNAPSHOT_PAGE_HEADER,\n SNAPSHOT_LOCATOR_DELIM,\n SNAPSHOT_PURPOSE_TOKEN,\n xpathLocatorTag\n} from '@wdio/devtools-shared'\n\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 = SNAPSHOT_PAGE_HEADER\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 = SNAPSHOT_INDENT_UNIT.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}\" ${SNAPSHOT_PURPOSE_TOKEN} \"${purpose}\" ${SNAPSHOT_LOCATOR_DELIM} ${node.selector}`\n : `${indent}${roleLabel} \"${node.name}\" ${SNAPSHOT_LOCATOR_DELIM} ${node.selector}`\n )\n } else if (purpose) {\n lines.push(\n `${indent}${roleLabel} ${SNAPSHOT_PURPOSE_TOKEN} \"${purpose}\" ${SNAPSHOT_LOCATOR_DELIM} ${node.selector}`\n )\n } else {\n lines.push(\n `${indent}${roleLabel} ${SNAPSHOT_LOCATOR_DELIM} ${node.selector}`\n )\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 = SNAPSHOT_INDENT_UNIT.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}\" ${SNAPSHOT_PURPOSE_TOKEN} \"${purpose}\" ${SNAPSHOT_LOCATOR_DELIM} ${selector}`\n : `${indent}${node.role} \"${node.name}\" ${SNAPSHOT_LOCATOR_DELIM} ${selector}`\n )\n } else if (purpose) {\n lines.push(\n `${indent}${node.role} ${SNAPSHOT_PURPOSE_TOKEN} \"${purpose}\" ${SNAPSHOT_LOCATOR_DELIM} ${selector}`\n )\n } else {\n lines.push(\n `${indent}${node.role} ${SNAPSHOT_LOCATOR_DELIM} ${selector}`\n )\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 locator prefix (e.g. `//button[contains(., \"Submit\")]`\n * or \"button*=Submit\" → \"button\"). */\nfunction extractTagFromSelector(selector: string, fallback: string): string {\n const xpathTag = xpathLocatorTag(selector)\n if (xpathTag) {\n return xpathTag\n }\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\n/** Ceiling for a driver round-trip probe (screenshot, url, title). Larger than\n * the in-page ceiling because encoding a full-page screenshot legitimately\n * takes longer than reading the DOM, but still inside the adapters' 5s\n * settle window so a hung probe can't strand the whole capture. */\nexport const SNAPSHOT_DRIVER_PROBE_TIMEOUT_MS = 4000\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 {\n SNAPSHOT_DRIVER_PROBE_TIMEOUT_MS,\n SNAPSHOT_PROBE_TIMEOUT_MS,\n withTimeout\n} from './with-timeout.js'\nimport type { ActionSnapshot, TestRunnerId } from '@wdio/devtools-shared'\n\nexport type ScriptRunner = (scriptSrc: string) => Promise<unknown>\n\nexport interface CaptureActionSnapshotInput {\n command: string\n /** The logged command's timestamp. Export claims snapshots by exact equality\n * (`claimAfter`/`elementsAt`) and slices them by command time, so a snapshot\n * stamped with its own capture time binds to no action at all. */\n timestamp?: number\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 | null | undefined>\n getTitle?: () => Promise<string | null | 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 /** Runner this capture belongs to — decides which text-locator dialect the\n * injected scripts emit. Omitted → the portable XPath form. */\n runner?: TestRunnerId\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 // A driver can answer `null` rather than reject (no-such-session, a script\n // error swallowed by the transport). Passing that through hands the\n // serializers a non-array where they expect one, which throws and loses the\n // WHOLE snapshot — screenshot, url and all — not just the probe.\n runScript(scriptSrc).then((r) =>\n r === null || r === undefined ? fallback : (r as T)\n ),\n SNAPSHOT_PROBE_TIMEOUT_MS,\n fallback\n ).catch(() => fallback)\n}\n\n/**\n * Run one driver-side probe under a timeout, resolving to `undefined` on a\n * timeout, a rejection, or a synchronous throw.\n *\n * Every probe is guarded, not just the in-page scripts: a framework whose\n * url/title/screenshot readers go through its own command queue blocks\n * indefinitely when called from inside a command hook, and an unguarded probe\n * in this `Promise.all` stranded the entire capture — observed as 10 of 14\n * Nightwatch captures never settling, so those actions reached the trace with\n * no DOM, no a11y tree and no element rects at all.\n */\nfunction probe<T>(\n read: (() => Promise<T | null | undefined>) | undefined\n): Promise<T | undefined> {\n if (!read) {\n return Promise.resolve(undefined)\n }\n // `Promise.resolve().then(read)` so a probe that throws synchronously becomes\n // a rejection this catch can absorb, rather than escaping the Promise.all.\n return withTimeout(\n Promise.resolve()\n .then(read)\n .then((value) => value ?? undefined),\n SNAPSHOT_DRIVER_PROBE_TIMEOUT_MS,\n undefined\n ).catch(() => undefined)\n}\n\n/** Native-mobile snapshot text + locators, derived from the page-source XML. */\nfunction fromPageSource(\n pageSource: string,\n platform: 'android' | 'ios'\n): { snapshotText: string; elements: unknown[] } {\n const jsonTree = xmlToJSON(pageSource)\n let snapshotText = `[${platform}]`\n if (jsonTree) {\n jsonTree.attributes._sourceXML = pageSource\n snapshotText = serializeMobileSnapshot(jsonTree, {\n platform,\n sourceXML: pageSource\n })\n }\n try {\n const filters = getDefaultFilters(platform, false)\n return {\n snapshotText,\n elements: generateAllElementLocators(pageSource, {\n platform,\n viewportSize: { width: 9999, height: 9999 },\n filters,\n inViewportOnly: false\n })\n }\n } catch {\n // Non-fatal — snapshot text is the primary deliverable.\n return { snapshotText, elements: [] }\n }\n}\n\nexport async function captureActionSnapshot(\n input: CaptureActionSnapshotInput\n): Promise<ActionSnapshot | null> {\n try {\n const timestamp = input.timestamp ?? Date.now()\n const isNativeMobile = !input.runScript && !!input.getPageSource\n\n // Probe order is load-bearing, not cosmetic. A driver serialises requests\n // per session, so `Promise.all` starting them together still has them served\n // in the order issued — and the screenshot is by far the slowest. Issued\n // first, it delayed the cheap reads behind it by hundreds of ms, long enough\n // for the next command to navigate: a fill on the login page reported the\n // page its submit had already reached. Cheapest-and-most-order-sensitive\n // first, screenshot last; the screenshot is served no later than before.\n const [url, title, pageSource, tree, elements, shot] = await Promise.all([\n probe(input.getUrl),\n probe(input.getTitle),\n isNativeMobile ? probe(input.getPageSource) : undefined,\n runWith<AccessibilityNode[]>(\n input.runScript,\n accessibilityTreeScript(true, input.runner),\n []\n ),\n runWith<BrowserElementInfo[]>(\n input.runScript,\n // includeBounds: the per-action element rects drive A8 input points.\n elementsScript(true, true, input.runner),\n []\n ),\n probe(input.takeScreenshot)\n ])\n\n const mobile =\n isNativeMobile && pageSource\n ? fromPageSource(pageSource, input.platform ?? 'android')\n : undefined\n\n return {\n timestamp,\n command: input.command,\n url,\n title,\n screenshot: shot ?? undefined,\n elements: mobile?.elements ?? elements,\n snapshotText:\n mobile?.snapshotText ?? serializeWebSnapshot(tree, { url, title })\n }\n } catch {\n return null\n }\n}\n","/**\n * Filename helpers for per-test artifacts (screenshots, videos). Shared so the\n * screenshot and video writers slug a test uid identically.\n */\n\n/** Strip leading and trailing occurrences of `char` via a linear scan. A regex\n * like `/^c+|c+$/` is O(n²) on long runs of `c` (the anchored `+` is retried\n * at each position) — flagged by CodeQL's js/polynomial-redos — so this avoids\n * regex entirely. `char` must be a single character. */\nexport function trimChar(value: string, char: string): string {\n let start = 0\n let end = value.length\n while (start < end && value[start] === char) {\n start++\n }\n while (end > start && value[end - 1] === char) {\n end--\n }\n return value.slice(start, end)\n}\n\n/** File-safe slug of a value: keep alphanumerics, `_` and `-`; collapse the\n * rest to a single `-`; trim leading/trailing dashes. */\nexport function fileSlug(value: string): string {\n return trimChar(value.replace(/[^a-zA-Z0-9_-]+/g, '-'), '-')\n}\n","/**\n * The generic artifacts manifest: a single JSON side-file\n * (`devtools-artifacts-<sessionId>.json`) written next to the trace/video\n * artifacts at end-of-run. It enumerates every artifact a trace-mode session\n * produced — including the ones a retention policy decided against\n * (`retained: false`) — alongside each test's final state and attempt. Any\n * ecosystem reporter (Allure, a CI collector, a custom dashboard) can read this\n * one file to discover what to attach and how each test fared, instead of\n * re-deriving it from framework internals. The WDIO Allure glue consumes it\n * directly; Selenium/Nightwatch document reader recipes against it.\n */\n\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport type {\n TestMetadataEntry,\n TestMetadataMap,\n TraceFormat\n} from '@wdio/devtools-shared'\nimport type { TraceArtifact } from './trace-finalizer.js'\n\n/** One test's outcome as recorded at export time. Field types track\n * TestMetadataEntry in shared; adds the keying uid, drops trace-only ancestry. */\nexport interface ArtifactManifestTest extends Pick<\n TestMetadataEntry,\n 'title' | 'specFile' | 'state' | 'attempt'\n> {\n uid: string\n}\n\nexport interface ArtifactsManifest {\n sessionId: string\n format: TraceFormat\n artifacts: TraceArtifact[]\n tests: ArtifactManifestTest[]\n}\n\n/** Deterministic manifest filename for a session (used by writers and readers). */\nexport function artifactsManifestFilename(sessionId: string): string {\n return `devtools-artifacts-${sessionId}.json`\n}\n\n/** Assemble the manifest from the artifacts collected across the run and the\n * test-metadata map. Pure — the write is separate so it stays unit-testable. */\nexport function buildArtifactsManifest(input: {\n sessionId: string\n format: TraceFormat\n artifacts: readonly TraceArtifact[]\n testMetadata: TestMetadataMap\n}): ArtifactsManifest {\n const tests: ArtifactManifestTest[] = []\n for (const [uid, meta] of input.testMetadata) {\n tests.push({\n uid,\n title: meta.title,\n specFile: meta.specFile,\n state: meta.state,\n attempt: meta.attempt\n })\n }\n return {\n sessionId: input.sessionId,\n format: input.format,\n artifacts: [...input.artifacts],\n tests\n }\n}\n\n/** Write the manifest to `outputDir`, returning its absolute path. */\nexport async function writeArtifactsManifest(\n outputDir: string,\n manifest: ArtifactsManifest\n): Promise<string> {\n await fs.mkdir(outputDir, { recursive: true })\n const filePath = path.join(\n outputDir,\n artifactsManifestFilename(manifest.sessionId)\n )\n await fs.writeFile(filePath, JSON.stringify(manifest, null, 2), 'utf8')\n return filePath\n}\n","// Framework-agnostic capture-and-attach of per-test trace artifacts to Allure.\n// The *produce* half (screenshot/video write, retention decision) already lives\n// in core; this module adds the orchestration + the attach, behind a pluggable\n// `AllureAttachSink` so each adapter supplies only its reporter binding:\n// - WDIO service → @wdio/allure-reporter's addAttachment\n// - Selenium → allure-js-commons' attachment() (runtime-agnostic)\n// - Nightwatch → no sink (produce-only; its Allure path is post-hoc, with no\n// live attach API), so artifacts land in the manifest only.\n// A missing/undefined sink means \"produce but don't attach\" — never an error.\n\nimport fs from 'node:fs/promises'\nimport { basename } from 'node:path'\nimport type {\n ActionSnapshot,\n DevToolsMode,\n ScreencastFrame,\n TraceGranularity,\n TraceScreenshotPolicy,\n TraceVideoPolicy\n} from '@wdio/devtools-shared'\nimport {\n shouldCaptureScreenshot,\n writeScreenshotArtifact\n} from './screenshot-artifact.js'\nimport { encodePerTestVideo, sliceFramesFrom } from './video-slice.js'\nimport { shouldRetainTrace, type TestOutcome } from './trace-retention.js'\nimport type { TraceArtifact } from './trace-finalizer.js'\n\n/** Adapter-supplied binding to the active Allure reporter. Attaches one file to\n * the currently-executing test. Undefined = attach unsupported (produce-only). */\nexport type AllureAttachSink = (\n name: string,\n content: Buffer,\n contentType: string\n) => void | Promise<void>\n\ntype LogFn = (level: 'info' | 'warn', message: string) => void\n\n// Trace stays a plain zip download (the viewer is the user's choice, not a\n// third-party viewer Allure would open for a trace-specific content type);\n// video/image render inline.\nconst CONTENT_TYPE_BY_KIND: Record<TraceArtifact['kind'], string> = {\n trace: 'application/zip',\n video: 'video/webm',\n screenshot: 'image/png'\n}\n\n/**\n * Read a retained artifact and hand it to the sink for attachment to the current\n * Allure test. No-op when the artifact wasn't retained, has no path, the sink is\n * absent, or the path is a directory (the ndjson-directory trace format). Never\n * throws — a missing/unreadable artifact must not reject the caller's hook.\n */\nexport async function attachTraceArtifact(\n artifact: TraceArtifact,\n sink: AllureAttachSink | undefined,\n onLog?: LogFn\n): Promise<void> {\n if (!sink || !artifact.retained || !artifact.path) {\n return\n }\n try {\n const stat = await fs.stat(artifact.path)\n if (!stat.isFile()) {\n return\n }\n const content = await fs.readFile(artifact.path)\n await sink(\n basename(artifact.path),\n content,\n CONTENT_TYPE_BY_KIND[artifact.kind]\n )\n } catch (err) {\n onLog?.(\n 'warn',\n `Allure attach skipped for ${artifact.path}: ${String(err)}`\n )\n }\n}\n\n/**\n * The base64 of the last rendered action snapshot for the current test, skipping\n * the end-of-scenario `__final__` frame (captured post-teardown, often blank when\n * a reloadSession runs before the after-hook). Scoped to `>= startWallTime` so a\n * test that captured nothing doesn't borrow the previous test's frame. Reused as\n * the per-test screenshot — reload-immune and one fewer WebDriver command than a\n * fresh end-of-test capture.\n */\nexport function lastRenderedScreenshot(\n snapshots: readonly ActionSnapshot[],\n startWallTime: number\n): string | undefined {\n for (let i = snapshots.length - 1; i >= 0; i--) {\n const snap = snapshots[i]!\n if (snap.timestamp < startWallTime) {\n return undefined\n }\n if (snap.command !== '__final__' && snap.screenshot) {\n return snap.screenshot\n }\n }\n return undefined\n}\n\n/**\n * Write the per-test screenshot (per policy) from an already-captured base64\n * frame, record it in the manifest, and attach it via the sink. No-op outside\n * `test`-granularity trace mode, without a uid/session/frame, or when the policy\n * declines. Gated to `test` granularity so the whole per-test inline story\n * (trace + screenshot + video) is one rule.\n */\nexport async function captureAndAttachScreenshot(input: {\n mode: DevToolsMode | undefined\n granularity: TraceGranularity | undefined\n policy: TraceScreenshotPolicy | undefined\n failed: boolean\n /** Base64 of the last rendered action snapshot (reload-immune). */\n screenshotBase64: string | undefined\n sessionId: string | undefined\n outputDir: string\n testUid: string | undefined\n attach: AllureAttachSink | undefined\n onArtifact: (artifact: TraceArtifact) => void\n onLog?: LogFn\n}): Promise<void> {\n const { mode, granularity, policy, failed, screenshotBase64, sessionId } =\n input\n if (\n mode !== 'trace' ||\n granularity !== 'test' ||\n !screenshotBase64 ||\n !sessionId ||\n !input.testUid ||\n !shouldCaptureScreenshot(policy, failed)\n ) {\n return\n }\n const artifact = await writeScreenshotArtifact({\n outputDir: input.outputDir,\n testUid: input.testUid,\n sessionId,\n base64: screenshotBase64\n })\n input.onArtifact(artifact)\n await attachTraceArtifact(artifact, input.attach, input.onLog)\n}\n\n/**\n * Slice the continuous screencast to this test's window, encode a `.webm`,\n * record it in the manifest, and attach it via the sink — when trace mode +\n * `test` granularity + a non-`off` `video` policy retains this test's outcome.\n * Empty outcomes (test never started) skip rather than fail-open onto the\n * previous test's frames.\n */\nexport async function captureAndAttachVideo(input: {\n mode: DevToolsMode | undefined\n granularity: TraceGranularity | undefined\n policy: TraceVideoPolicy | undefined\n frames: readonly ScreencastFrame[] | undefined\n startWallTime: number\n outcomes: TestOutcome[]\n attempt: number | undefined\n outputDir: string\n testUid: string | undefined\n sessionId: string | undefined\n captureFormat?: 'jpeg' | 'png'\n attach: AllureAttachSink | undefined\n onArtifact: (artifact: TraceArtifact) => void\n onLog?: LogFn\n}): Promise<void> {\n const { mode, granularity, policy, frames, testUid, sessionId } = input\n if (\n mode !== 'trace' ||\n granularity !== 'test' ||\n !policy ||\n policy === 'off' ||\n !frames ||\n !testUid ||\n !sessionId ||\n input.outcomes.length === 0\n ) {\n return\n }\n if (\n !shouldRetainTrace(policy, {\n outcomes: input.outcomes,\n attemptInfoAvailable: true\n }).retain\n ) {\n return\n }\n const artifact = await encodePerTestVideo({\n frames: sliceFramesFrom(frames, input.startWallTime),\n outputDir: input.outputDir,\n testUid,\n sessionId,\n attempt: input.attempt,\n captureFormat: input.captureFormat,\n onLog: input.onLog\n })\n if (!artifact) {\n return\n }\n input.onArtifact(artifact)\n await attachTraceArtifact(artifact, input.attach, input.onLog)\n}\n","/**\n * Per-test screenshot artifact: the policy decision and the file write, kept\n * framework-agnostic so every adapter feeds it the same way (the adapter only\n * supplies the base64 capture from its own driver). Mirrors Playwright's\n * `screenshot` option — `on` after every test, `only-on-failure` after a\n * failing one — and produces a TraceArtifact the manifest and the Allure glue\n * consume like any other artifact.\n */\n\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport type { TraceScreenshotPolicy } from '@wdio/devtools-shared'\nimport { fileSlug } from './artifact-naming.js'\nimport type { TraceArtifact } from './trace-finalizer.js'\n\n/** Whether a per-test screenshot should be captured for this outcome. */\nexport function shouldCaptureScreenshot(\n policy: TraceScreenshotPolicy | undefined,\n failed: boolean\n): boolean {\n if (policy === 'on') {\n return true\n }\n if (policy === 'only-on-failure') {\n return failed\n }\n return false\n}\n\n/**\n * Write a base64 PNG (from the driver's screenshot command) next to the trace\n * output and return the artifact describing it. Retained is always true — a\n * screenshot is only ever written when the policy already decided to capture.\n */\nexport async function writeScreenshotArtifact(input: {\n outputDir: string\n testUid: string\n sessionId: string\n base64: string\n}): Promise<TraceArtifact> {\n await fs.mkdir(input.outputDir, { recursive: true })\n const filename = `screenshot-${fileSlug(input.testUid)}-${input.sessionId.slice(0, 8)}.png`\n const filePath = path.join(input.outputDir, filename)\n await fs.writeFile(filePath, Buffer.from(input.base64, 'base64'))\n return {\n kind: 'screenshot',\n path: filePath,\n scope: 'test',\n key: input.testUid,\n testUids: [input.testUid],\n retained: true\n }\n}\n","/**\n * Per-test video: slice the continuous screencast frame buffer to one test's\n * wall-clock window and encode that slice to a `.webm`. Framework-agnostic —\n * the adapter supplies the recorder's frames and the test's start time; the\n * recorder itself (CDP push on Chrome, screenshot polling elsewhere) lives in\n * each adapter. Best-effort: too few frames or a missing ffmpeg yields no\n * artifact rather than an error, so video never aborts a run.\n */\n\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport type { ScreencastFrame } from '@wdio/devtools-shared'\nimport { fileSlug } from './artifact-naming.js'\nimport { encodeToVideo } from './video-encoder.js'\nimport { errorMessage } from './error.js'\nimport type { TraceArtifact } from './trace-finalizer.js'\n\n/** Frames captured at or after `startWallTime` (ms) — one test's window, since\n * the buffer runs continuously and the next test's frames arrive later. */\nexport function sliceFramesFrom(\n frames: readonly ScreencastFrame[],\n startWallTime: number\n): ScreencastFrame[] {\n return frames.filter((f) => f.timestamp >= startWallTime)\n}\n\n/**\n * Encode a test's frame slice to `<outputDir>/video-<uid>-<session>.webm` and\n * return the artifact. Returns undefined when there are too few frames to make\n * a video or when encoding fails (e.g. fluent-ffmpeg absent) — video is\n * best-effort and must never abort the run.\n */\nexport async function encodePerTestVideo(input: {\n frames: readonly ScreencastFrame[]\n outputDir: string\n testUid: string\n sessionId: string\n /** 0-based attempt; a `-retry<n>` suffix for n>0 keeps retries from\n * overwriting each other's video (mirrors the trace slice's retry keys). */\n attempt?: number\n captureFormat?: 'jpeg' | 'png'\n minFrames?: number\n onLog?: (level: 'info' | 'warn', message: string) => void\n}): Promise<TraceArtifact | undefined> {\n const minFrames = input.minFrames ?? 2\n if (input.frames.length < minFrames) {\n return undefined\n }\n await fs.mkdir(input.outputDir, { recursive: true })\n const retrySuffix = input.attempt ? `-retry${input.attempt}` : ''\n const filename = `video-${fileSlug(input.testUid)}-${input.sessionId.slice(0, 8)}${retrySuffix}.webm`\n const filePath = path.join(input.outputDir, filename)\n try {\n await encodeToVideo([...input.frames], filePath, {\n captureFormat: input.captureFormat\n })\n } catch (err) {\n input.onLog?.('warn', `Per-test video encode failed: ${errorMessage(err)}`)\n return undefined\n }\n return {\n kind: 'video',\n path: filePath,\n scope: 'test',\n key: input.testUid,\n testUids: [input.testUid],\n retained: true\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 type {\n DevToolsMode,\n TestStatus,\n TraceRetentionPolicy\n} from '@wdio/devtools-shared'\n\n/**\n * Pure retention policy evaluation for trace mode. Adapters collect the\n * observed test outcomes for a trace scope (session/spec/test) and ask\n * whether the written trace should be kept.\n */\n\n/** Every policy the evaluator recognizes. An unknown string (from a JS config\n * that slipped past the type) is treated as `on` — fail open, never silently\n * drop a trace the user might need. */\nconst KNOWN_POLICIES = new Set<TraceRetentionPolicy>([\n 'on',\n 'retain-on-failure',\n 'retain-on-first-failure',\n 'on-first-retry',\n 'on-all-retries',\n 'retain-on-failure-and-retries'\n])\n\nexport interface TestOutcome {\n /** Retry-stable test identity. Outcomes sharing a uid are one test's attempts\n * (attempt 0, 1, …); an outcome with no uid is its own single-attempt group,\n * so a flat/uid-less feed evaluates exactly as one-outcome-per-test. */\n uid?: string\n state?: TestStatus\n attempt?: number\n}\n\nexport interface RetentionInput {\n outcomes: Iterable<TestOutcome>\n /** Whether the adapter can distinguish retries (per-outcome `attempt`). */\n attemptInfoAvailable: boolean\n}\n\nexport interface RetentionDecision {\n retain: boolean\n /** Set when a retry-aware policy fell back to `retain-on-failure` because attempt info was unavailable. */\n degradedToFailure?: boolean\n /** Set when no outcomes were observed (standalone scripts) — retained rather than risk losing a needed trace. */\n failOpen?: boolean\n}\n\nexport function shouldRetainTrace(\n policy: TraceRetentionPolicy | undefined,\n input: RetentionInput\n): RetentionDecision {\n if (policy === undefined || policy === 'on' || !KNOWN_POLICIES.has(policy)) {\n return { retain: true }\n }\n const outcomes = Array.from(input.outcomes)\n if (outcomes.length === 0) {\n return { retain: true, failOpen: true }\n }\n const anyFailed = outcomes.some((o) => o.state === 'failed')\n if (!input.attemptInfoAvailable && policy !== 'retain-on-failure') {\n return { retain: anyFailed, degradedToFailure: true }\n }\n // Group a test's attempts so failure policies key on the RIGHT attempt: the\n // *final* attempt (retain-on-failure — a fail-then-pass ends passed) vs the\n // *first* attempt (retain-on-first-failure). A uid-less feed makes every\n // outcome its own group, so this reduces to the flat one-outcome-per-test\n // logic and is byte-identical for callers that don't supply per-attempt uids.\n const groups = groupByTest(outcomes)\n switch (policy) {\n case 'retain-on-failure':\n return { retain: groups.some((g) => finalAttempt(g).state === 'failed') }\n case 'retain-on-first-failure':\n return {\n retain: groups.some((g) => firstAttempt(g)?.state === 'failed')\n }\n case 'on-first-retry':\n return { retain: groups.some((g) => g.some((o) => o.attempt === 1)) }\n case 'on-all-retries':\n return { retain: groups.some((g) => g.some((o) => attemptOf(o) >= 1)) }\n case 'retain-on-failure-and-retries':\n return {\n retain: groups.some(\n (g) =>\n finalAttempt(g).state === 'failed' ||\n g.some((o) => attemptOf(o) >= 1)\n )\n }\n }\n}\n\nconst attemptOf = (o: TestOutcome): number => o.attempt ?? 0\n\n/** Group outcomes by `uid`; a uid-less outcome becomes its own singleton group\n * (preserving flat, one-outcome-per-test evaluation for callers without uids). */\nfunction groupByTest(outcomes: TestOutcome[]): TestOutcome[][] {\n const byUid = new Map<string, TestOutcome[]>()\n const groups: TestOutcome[][] = []\n for (const outcome of outcomes) {\n if (outcome.uid === undefined) {\n groups.push([outcome])\n continue\n }\n const existing = byUid.get(outcome.uid)\n if (existing) {\n existing.push(outcome)\n } else {\n const group = [outcome]\n byUid.set(outcome.uid, group)\n groups.push(group)\n }\n }\n return groups\n}\n\n/** The highest-numbered attempt's outcome — the test's final result. */\nfunction finalAttempt(group: TestOutcome[]): TestOutcome {\n return group.reduce((best, o) => (attemptOf(o) >= attemptOf(best) ? o : best))\n}\n\n/** The attempt-0 outcome, if the group recorded one. */\nfunction firstAttempt(group: TestOutcome[]): TestOutcome | undefined {\n return group.find((o) => attemptOf(o) === 0)\n}\n\n/**\n * Warning text when a retention policy is configured outside trace mode, where\n * it has no effect (the finalizer no-ops in live mode). Returns undefined when\n * there is nothing to warn about, so each adapter can `if (msg) log.warn(msg)`.\n */\nexport function tracePolicyModeWarning(\n policy: TraceRetentionPolicy | undefined,\n mode: DevToolsMode | undefined\n): string | undefined {\n if (policy === undefined || mode === 'trace') {\n return undefined\n }\n return 'tracePolicy only applies in trace mode; ignoring it because mode is not \"trace\".'\n}\n","import type { TestStatus } from '@wdio/devtools-shared'\nimport type { TestOutcome } from './trace-retention.js'\n\n/**\n * Framework-agnostic per-test attempt ledger. Every supported runner re-enters\n * its per-test start hook when a test is retried, so recording the same uid\n * again appends a new attempt slot (0-based: first run is attempt 0, first retry\n * is attempt 1). Once the outcome is known the adapter stamps it onto the slot.\n * This is the primary, runner-independent retry signal feeding the retry-aware\n * trace policies — the finalizer reads the scoped views to evaluate retention\n * per test/spec/session with real per-attempt outcomes (see trace-retention.ts).\n */\nexport interface RetryOutcomeView {\n /** Every attempt of every test — for session-scope retention. */\n all(): TestOutcome[]\n /** Every attempt of the tests recorded against `specFile` — for spec scope. */\n forSpec(specFile: string): TestOutcome[]\n /** A single test's attempts; pass `attempt` to scope to one attempt's slice. */\n forTest(uid: string, attempt?: number): TestOutcome[]\n}\n\nexport class TestAttemptTracker implements RetryOutcomeView {\n #ledger = new Map<string, { specFile?: string; attempts: TestOutcome[] }>()\n #sawRetry = false\n\n /** Record a starting test; returns its attempt number (0 first, +1 per rerun).\n * `specFile` (optional) enables spec-scoped retention lookups. */\n recordStart(uid: string, specFile?: string): number {\n const entry = this.#ledger.get(uid)\n if (entry && entry.attempts.length > 0) {\n this.#sawRetry = true\n // A retry only follows a failure. Some runners (e.g. Mocha through a\n // --require plugin) can't observe the failed attempt's state from their\n // outcome hook and report it as passed — the retry starting IS the\n // reliable failure signal, so stamp the prior attempt failed here.\n entry.attempts[entry.attempts.length - 1].state = 'failed'\n }\n const attempt = entry ? entry.attempts.length : 0\n const slot: TestOutcome = { uid, attempt }\n if (entry) {\n entry.attempts.push(slot)\n if (specFile !== undefined) {\n entry.specFile = specFile\n }\n } else {\n this.#ledger.set(uid, { specFile, attempts: [slot] })\n }\n return attempt\n }\n\n /** Stamp the resolved state onto uid's most recent attempt slot once the\n * outcome is known. `attempt` overrides the slot's number when the adapter\n * resolved a more authoritative value (e.g. WDIO's result.retries). */\n recordOutcome(\n uid: string,\n state: TestStatus | undefined,\n attempt?: number\n ): void {\n const attempts = this.#ledger.get(uid)?.attempts\n const slot = attempts?.[attempts.length - 1]\n if (!slot) {\n return\n }\n slot.state = state\n if (attempt !== undefined) {\n slot.attempt = attempt\n }\n }\n\n /** Latest attempt recorded for `uid`, or undefined if it never started. */\n attemptFor(uid: string): number | undefined {\n const entry = this.#ledger.get(uid)\n return entry ? entry.attempts.length - 1 : undefined\n }\n\n /** True once any test has started more than once (a retry occurred). */\n get sawRetry(): boolean {\n return this.#sawRetry\n }\n\n all(): TestOutcome[] {\n const out: TestOutcome[] = []\n for (const entry of this.#ledger.values()) {\n out.push(...entry.attempts)\n }\n return out\n }\n\n forSpec(specFile: string): TestOutcome[] {\n const out: TestOutcome[] = []\n for (const entry of this.#ledger.values()) {\n if (entry.specFile === specFile) {\n out.push(...entry.attempts)\n }\n }\n return out\n }\n\n forTest(uid: string, attempt?: number): TestOutcome[] {\n const attempts = this.#ledger.get(uid)?.attempts ?? []\n return attempt === undefined\n ? attempts\n : attempts.filter((a) => a.attempt === attempt)\n }\n\n /** Clear all state — used at session boundaries and reuse-mode reconnects. */\n reset(): void {\n this.#ledger.clear()\n this.#sawRetry = false\n }\n}\n","import { createRequire } from 'node:module'\nimport {\n TRACKED_ASSERT_METHODS,\n type CollapsedAssertResult,\n type CommandLog\n} from '@wdio/devtools-shared'\nimport { getCallSourceFromStack, isAssertFromUserCode } from './stack.js'\nimport { toError } from './error.js'\nimport { stripAnsi } from './console.js'\n\nexport { TRACKED_ASSERT_METHODS }\n\nconst require = createRequire(import.meta.url)\n\n/** Per-process guard so a second `patchNodeAssert()` call is a no-op. */\nexport const ASSERT_PATCHED_SYMBOL = Symbol.for(\n '@wdio/devtools-core/assert-patched'\n)\n\n/**\n * Minimum shape `patchNodeAssert` emits. Adapters that need extra bookkeeping\n * (selenium adds `fromElement` and `rawResult`) wrap the callback to extend\n * the object before forwarding to their own `onCommand` sink.\n */\nexport interface CapturedAssert {\n command: string\n args: unknown[]\n /** `'passed'` for a passing assert; a `CollapsedAssertResult` for a failing\n * node:assert (its clean `actual`/`expected` props), so the trace shows\n * labelled rows rather than node's ANSI-stripped char-diff. */\n result: 'passed' | CollapsedAssertResult | undefined\n error: Error | undefined\n callSource: string | undefined\n timestamp: number\n /** Locator of the element the assertion was about, when the adapter can name\n * one from the raw args (see `patchNodeAssert`'s `resolveAssertTarget`).\n * Lands on `CommandLog.selector` so the player's overlay boxes the target the\n * way it does for a folded matcher row. */\n selector?: string\n}\n\n/**\n * JSON-safe stringify of an assert argument. Non-serialisable inputs degrade\n * gracefully: functions → '[Function]', RegExp → `/.../i`, cyclic objects →\n * `String(value)`. Exported so adapters can mirror the shape if they wrap.\n */\nexport function safeSerializeAssertArg(value: unknown): unknown {\n if (value === null || value === undefined) {\n return value\n }\n if (value instanceof RegExp) {\n return value.toString()\n }\n if (typeof value === 'function') {\n return '[Function]'\n }\n if (typeof value === 'object') {\n try {\n return JSON.parse(JSON.stringify(value))\n } catch {\n return String(value)\n }\n }\n return value\n}\n\ninterface NodeAssertionError {\n actual?: unknown\n expected?: unknown\n generatedMessage?: boolean\n}\n\n/** Human-readable rendering of an assert value for the message body. */\nfunction displayAssertValue(value: unknown): string {\n if (typeof value === 'string') {\n return `'${value}'`\n }\n try {\n return JSON.stringify(value) ?? String(value)\n } catch {\n return String(value)\n }\n}\n\n/**\n * Turn a thrown assert error into the captured shape. node:assert's\n * AssertionError carries clean `actual`/`expected` props plus, when it\n * auto-generated the message, a per-character COLORED diff whose body becomes\n * unreadable noise once ANSI is stripped (`'ExampleThis DIs Nomt...'`). Pull the\n * clean values into a CollapsedAssertResult AND rebuild an auto-generated\n * message (and its echo in the stack) as a value-bearing `Expected:/Received:`\n * block, so every consumer — the trace's Errors tab, the runner console, and\n * allure-mocha's error box (which strips the diff's ANSI to mush) — shows\n * readable values. A user-supplied message and errors without `actual`/\n * `expected` (e.g. `assert.ok`, a thrown non-assert error) pass through.\n */\nfunction describeAssertFailure(err: unknown): {\n result: CollapsedAssertResult | undefined\n error: Error\n} {\n const error = toError(err)\n const e = (typeof err === 'object' && err ? err : {}) as NodeAssertionError\n if (!('actual' in e) && !('expected' in e)) {\n return { result: undefined, error }\n }\n const rawMessage = stripAnsi(error.message)\n const cleanMessage = e.generatedMessage\n ? `${rawMessage.split('\\n')[0] ?? 'Assertion failed'}\\n\\n` +\n `Expected: ${displayAssertValue(e.expected)}\\n` +\n `Received: ${displayAssertValue(e.actual)}`\n : rawMessage\n error.message = cleanMessage\n if (error.stack) {\n error.stack = stripAnsi(error.stack).replace(rawMessage, cleanMessage)\n }\n return {\n result: {\n passed: false,\n actual: safeSerializeAssertArg(e.actual),\n expected: safeSerializeAssertArg(e.expected)\n },\n error\n }\n}\n\nfunction makeAssertEmitters(\n methodName: string,\n args: unknown[],\n onCommand: (cmd: CapturedAssert) => void,\n callerStack: string | undefined,\n resolveAssertTarget?: (args: unknown[]) => string | undefined\n): { passed: () => void; failed: (err: unknown) => void } {\n const callInfo = getCallSourceFromStack(callerStack)\n // Drop asserts the user's test didn't fire directly: either no user-code frame\n // on the stack at all, or the assert's immediate caller is a dependency (a\n // framework/library assert firing during a user operation) — both are noise.\n // callerStack is captured in patchedAssert so the frame offsets line up.\n if (callInfo.filePath === undefined || !isAssertFromUserCode(callerStack)) {\n return { passed: () => {}, failed: () => {} }\n }\n const startedAt = Date.now()\n // Resolved from the RAW args: sanitizing loses the object identity an element\n // handle is recognised by. After the user-code gate, so a dropped assert\n // doesn't pay for it.\n const selector = resolveAssertTarget?.(args)\n const sanitizedArgs = args.map(safeSerializeAssertArg)\n const emit = (result: CapturedAssert['result'], error: Error | undefined) => {\n const cmd: CapturedAssert = {\n command: `assert.${methodName}`,\n args: sanitizedArgs,\n result,\n error,\n callSource: callInfo.callSource,\n timestamp: startedAt\n }\n if (selector) {\n cmd.selector = selector\n }\n onCommand(cmd)\n }\n return {\n passed: () => emit('passed', undefined),\n failed: (err: unknown) => {\n const { result, error } = describeAssertFailure(err)\n emit(result, error)\n }\n }\n}\n\nfunction makePatchedAssertMethod(\n methodName: string,\n assertObj: Record<string | symbol, unknown>,\n original: (...a: unknown[]) => unknown,\n onCommand: (cmd: CapturedAssert) => void,\n resolveAssertTarget?: (args: unknown[]) => string | undefined\n): (...args: unknown[]) => unknown {\n return function patchedAssert(this: unknown, ...args: unknown[]) {\n // Captured HERE so frames[0] is this wrapper and frames[1] is the assert's\n // caller — a fixed offset isAssertFromUserCode reads (minification-robust).\n const callerStack = new Error().stack\n const { passed, failed } = makeAssertEmitters(\n methodName,\n args,\n onCommand,\n callerStack,\n resolveAssertTarget\n )\n let result: unknown\n // Node's internalMatch dispatches on `fn === assert.match` (Node ≤20), so\n // a wrapper installed on that property silently inverts `match` into\n // `doesNotMatch`. Restore the original binding for the call so identity\n // checks inside node:assert see the real method.\n assertObj[methodName] = original\n try {\n result = original.apply(this, args)\n } catch (err) {\n failed(err)\n throw err\n } finally {\n assertObj[methodName] = patchedAssert\n }\n // Async assert methods (rejects/doesNotReject) return a Promise.\n const maybe = result as { then?: unknown } | null | undefined\n if (maybe && typeof maybe.then === 'function') {\n return (result as Promise<unknown>).then(\n (v) => {\n passed()\n return v\n },\n (err) => {\n failed(err)\n throw err\n }\n )\n }\n passed()\n return result\n }\n}\n\n/**\n * Convert a `CapturedAssert` into the shared `CommandLog` shape adapters push\n * into their session capturer. Asserts are effectively instantaneous, so the\n * capture timestamp doubles as `startTime`.\n */\nexport function capturedAssertToCommandLog(\n cmd: CapturedAssert,\n testUid?: string\n): CommandLog {\n const entry: CommandLog = {\n command: cmd.command,\n args: cmd.args,\n result: cmd.result,\n timestamp: cmd.timestamp,\n startTime: cmd.timestamp\n }\n if (cmd.error) {\n entry.error = {\n name: cmd.error.name,\n message: cmd.error.message,\n stack: cmd.error.stack\n }\n }\n if (cmd.callSource) {\n entry.callSource = cmd.callSource\n }\n if (cmd.selector) {\n entry.selector = cmd.selector\n }\n if (testUid) {\n entry.testUid = testUid\n }\n return entry\n}\n\n/**\n * Params any adapter's matcher tap produces. `prefix` selects the command\n * namespace (`expect` / `assert` / `verify`), all of which map to an `Assert`\n * action via the shared action map. Adapters do the thin framework-specific\n * extraction (matcher name, args, pass flag, message); this conversion is the\n * single shared path — the generic counterpart to `capturedAssertToCommandLog`\n * for libraries that aren't node:assert (expect-webdriverio, chai, Nightwatch).\n */\nexport interface MatcherAssertion {\n prefix?: string\n method: string\n args?: unknown[]\n passed: boolean\n message?: string | (() => string)\n callSource?: string\n /** Explicit display label for the action row. Falls back to `command` when\n * absent — set it when the framework carries a richer human message than\n * `prefix.method` (e.g. Nightwatch's \"Testing if the page title contains …\"). */\n title?: string\n}\n\nexport function matcherAssertionToCommandLog(\n input: MatcherAssertion,\n testUid?: string\n): CommandLog {\n const command = `${input.prefix ?? 'expect'}.${input.method}`\n const message =\n typeof input.message === 'function' ? input.message() : input.message\n const entry = capturedAssertToCommandLog(\n {\n command,\n args: (input.args ?? []).map(safeSerializeAssertArg),\n result: input.passed ? 'passed' : undefined,\n error: input.passed\n ? undefined\n : new Error(stripAnsi(message ?? `${command} failed`)),\n callSource: input.callSource,\n timestamp: Date.now()\n },\n testUid\n )\n if (input.title) {\n entry.title = input.title\n }\n return entry\n}\n\n/** Wrap every tracked method on one assert namespace object in place; returns\n * how many were patched. Used for both `assert.*` and `assert.strict.*`. */\nfunction patchAssertNamespace(\n nsObj: Record<string | symbol, unknown>,\n onCommand: (cmd: CapturedAssert) => void,\n resolveAssertTarget?: (args: unknown[]) => string | undefined\n): number {\n let count = 0\n for (const methodName of TRACKED_ASSERT_METHODS) {\n const original = nsObj[methodName]\n if (typeof original !== 'function') {\n continue\n }\n nsObj[methodName] = makePatchedAssertMethod(\n methodName,\n nsObj,\n original as (...a: unknown[]) => unknown,\n onCommand,\n resolveAssertTarget\n )\n count++\n }\n return count\n}\n\n/**\n * Patch `node:assert` so each tracked method emits a `CapturedAssert` to the\n * supplied hook. Idempotent across calls (guarded by `ASSERT_PATCHED_SYMBOL`).\n * Returns `true` on success, `false` when node:assert can't be resolved\n * (rare — browser-only Node-incompatible runtimes).\n *\n * Wraps both the function-form (`assert(...)`) and the namespace methods\n * (`assert.equal(...)`). User code that imported the methods BEFORE this\n * patcher loaded keeps stale references — adapters should import node:assert\n * from their main entry before user test files load.\n *\n * @param onCommand Callback invoked once per assert call (sync OR async).\n * Receives the captured shape; do NOT throw — the wrapper\n * re-throws the original assert error after the callback.\n * @param onLog Optional logger for lifecycle events. Default: silent.\n * @param resolveAssertTarget\n * Optional adapter hook naming the element an assert was about,\n * from its raw args. node:assert takes values, not elements, so\n * only the adapter that captured the reads can make the link;\n * the result rides along as `CapturedAssert.selector`.\n */\nexport function patchNodeAssert(\n onCommand: (cmd: CapturedAssert) => void,\n onLog?: (level: 'info' | 'warn', message: string) => void,\n resolveAssertTarget?: (args: unknown[]) => string | undefined\n): boolean {\n const log = (level: 'info' | 'warn', message: string) =>\n onLog?.(level, message)\n\n let assertModule: unknown\n try {\n assertModule = require('node:assert')\n } catch {\n log('warn', 'node:assert not available — skipping assertion capture')\n return false\n }\n\n // Node's `assert` is a function with methods on it — cast once for the\n // symbol + dynamic method access we do here.\n const assertObj = assertModule as Record<string | symbol, unknown>\n if (assertObj[ASSERT_PATCHED_SYMBOL]) {\n return true\n }\n assertObj[ASSERT_PATCHED_SYMBOL] = true\n\n let patched = patchAssertNamespace(assertObj, onCommand, resolveAssertTarget)\n // `import { strict as assert }` and `assert.strict.*` reference a separate\n // object with its own method identities; patch it too so strict-mode\n // assertions are captured the same as the default ones. `assert.strict` is a\n // callable (strict-mode `assert()`) with the methods hung off it, so it's a\n // function, not a plain object.\n const strict = assertObj.strict\n if (strict && (typeof strict === 'object' || typeof strict === 'function')) {\n patched += patchAssertNamespace(\n strict as Record<string | symbol, unknown>,\n onCommand,\n resolveAssertTarget\n )\n }\n\n log('info', `Patched ${patched} node:assert method(s)`)\n return true\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 * True when a tracked assert was called DIRECTLY by user code. `stack` MUST be\n * captured inside the `patchedAssert` wrapper (`new Error().stack` on its first\n * line), so frames[0] is the wrapper and frames[1] is whoever called the\n * assert. A non-user immediate caller means a framework/dependency assert fired\n * *during* a user operation (which `getCallSourceFromStack` would otherwise\n * mis-attribute to the far-off user frame and surface as a noisy row) — drop it.\n * Uses the fixed frame offset rather than the wrapper's name, so it survives a\n * bundler minifying/renaming the wrapper. No stack → keep (never lose a real\n * assert to an absent stack).\n */\nexport function isAssertFromUserCode(stack: string | undefined): boolean {\n if (!stack) {\n return true\n }\n const caller = parseStackTrace(stack)[1]\n return !!caller && isUserCodeFrame(caller)\n}\n\n/**\n * Capture `{ filePath, callSource }` for the first user-code frame on the\n * stack. `callSource` is `<file>:<line>` for the UI's source-location displays;\n * returns `'unknown:0'` (and `undefined` filePath) when no user frame can be\n * found. `stack` defaults to the live stack; callers with a pre-captured stack\n * (e.g. the assert wrapper) pass it so the frame offsets line up.\n */\nexport function getCallSourceFromStack(\n stack: string | undefined = new Error().stack\n): {\n filePath: string | undefined\n callSource: string\n} {\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","import { createHash } from 'node:crypto'\n\n/** Hex SHA-1 used for content-addressed trace resources. */\nexport function sha1Hex(data: Buffer | string): string {\n return createHash('sha1').update(data).digest('hex')\n}\n","// Source files and per-action call stacks for the trace: sources are written\n// as path-addressed `src@<sha1(path)>.txt` resources, and each action's\n// captured `<file>:<line>[:<column>]` callSource becomes an inline stack frame.\n\nimport { sha1Hex } from './sha1.js'\nimport type { TraceZipResource } from './trace-zip-writer.js'\n\nexport interface StackFrame {\n file: string\n line: number\n column: number\n}\n\n// Test files are small; anything bigger is generated/bundled and not worth shipping.\nconst MAX_SOURCE_BYTES = 2 * 1024 * 1024\n\nexport function sourceResourceName(filePath: string): string {\n return `src@${sha1Hex(filePath)}.txt`\n}\n\n// Splits one trailing `:<digits>` segment; `sep <= 0` keeps Windows drive\n// letters (`C:...`) and colon-less paths intact.\nfunction splitNumericSuffix(value: string): { rest: string; num?: number } {\n const sep = value.lastIndexOf(':')\n if (sep <= 0) {\n return { rest: value }\n }\n const digits = value.slice(sep + 1)\n if (!/^\\d+$/.test(digits)) {\n return { rest: value }\n }\n return { rest: value.slice(0, sep), num: Number(digits) }\n}\n\n/** Inline stack from a captured `<file>:<line>[:<column>]` callSource. */\nexport function callSourceToStack(\n callSource?: string\n): StackFrame[] | undefined {\n if (!callSource || callSource === 'unknown:0') {\n return undefined\n }\n const last = splitNumericSuffix(callSource)\n if (last.num === undefined) {\n return [{ file: callSource, line: 0, column: 0 }]\n }\n const prev = splitNumericSuffix(last.rest)\n if (prev.num === undefined) {\n return [{ file: last.rest, line: last.num, column: 0 }]\n }\n return [{ file: prev.rest, line: prev.num, column: last.num }]\n}\n\nexport function buildSourceResources(\n sources: Record<string, string>\n): TraceZipResource[] {\n const out: TraceZipResource[] = []\n for (const [filePath, text] of Object.entries(sources)) {\n const data = Buffer.from(text, 'utf8')\n if (data.byteLength > MAX_SOURCE_BYTES) {\n continue\n }\n out.push({ resourceName: sourceResourceName(filePath), data })\n }\n return out\n}\n","// The ordered open-group path for a command — outermost first — that the action\n// exporter's group stack turns into balanced nested Tracing.tracingGroup\n// markers. The path is the test's ancestry (feature/scenario/suite, from the\n// suite-metadata walk) + the test itself + its step, when a stepUid is set.\n\nimport type { CommandLog, TestMetadataMap } from '@wdio/devtools-shared'\n\nexport interface GroupNode {\n uid: string\n title: string\n}\n\n/** A node's own name, with an enclosing group's title stripped off the front.\n * Some runners report a test's FULL title (`<suite> <test>`), so nesting it\n * under its own suite renders the suite name twice. Display only — the entry's\n * stored title still keys artifacts and slices. Falls back to the full title\n * when stripping would leave nothing, so a test named after its suite keeps a\n * label. */\nfunction ownTitle(title: string, parent: GroupNode | undefined): string {\n if (!parent || !title.startsWith(parent.title)) {\n return title\n }\n return title.slice(parent.title.length).trim() || title\n}\n\nexport function buildGroupPath(\n cmd: CommandLog,\n testMetadata?: TestMetadataMap\n): GroupNode[] {\n const path: GroupNode[] = []\n if (cmd.testUid) {\n const entry = testMetadata?.get(cmd.testUid)\n for (const ancestor of entry?.ancestry ?? []) {\n path.push({\n uid: ancestor.uid,\n title: ownTitle(ancestor.title, path[path.length - 1])\n })\n }\n path.push({\n uid: cmd.testUid,\n title: ownTitle(entry?.title ?? cmd.testUid, path[path.length - 1])\n })\n }\n if (cmd.stepUid) {\n const stepEntry = testMetadata?.get(cmd.stepUid)\n path.push({\n uid: cmd.stepUid,\n title: ownTitle(stepEntry?.title ?? cmd.stepUid, path[path.length - 1])\n })\n }\n return path\n}\n","// Builds the before/after action events of the exported trace stream,\n// including tracingGroup test boundaries and frame-snapshot ref stamping.\n\nimport type {\n CollapsedAssertResult,\n CommandLog,\n TestMetadataMap\n} from '@wdio/devtools-shared'\nimport { locatorsMatch, POINTABLE_METHODS } from '@wdio/devtools-shared'\nimport {\n ASSERT_ACTION_CLASS,\n formatActionTitle,\n mapCommandToAction,\n FILL_METHODS,\n type TraceAction\n} from './action-mapping.js'\nimport { callSourceToStack, type StackFrame } from './trace-sources.js'\nimport type { FrameSnapshotIndex } from './trace-frame-snapshots.js'\nimport { buildGroupPath } from './trace-hierarchy.js'\n\nexport interface 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 /** Trace-viewer 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 /** User-code frame the command was issued from, when captured. */\n stack?: StackFrame[]\n /** Frame-snapshot name rendered as the action's before state. */\n beforeSnapshot?: string\n}\n\nexport interface AfterEvent {\n type: 'after'\n callId: string\n endTime: number\n error?: { message: string }\n /** Command return value (e.g. the text getText resolved to). */\n result?: unknown\n /** CallId of the Tracing.tracingGroup that wraps this action (if any). */\n parentId?: string\n /** Frame-snapshot name rendered as the action's after state. */\n afterSnapshot?: string\n /** Page-coordinate hit point (centre of the matched element) for pointer\n * actions — drives the player's click marker + timeline pointer glyph. */\n point?: { x: number; y: number }\n}\n\n// Serialized command results over this size are dropped from the trace — a\n// huge execute() return shouldn't bloat every action line.\nconst MAX_RESULT_BYTES = 64 * 1024\n\n/** JSON-safe command result within the size cap; undefined when absent,\n * oversized, or not serializable. */\nfunction serializableResult(result: unknown): unknown {\n if (result === undefined) {\n return undefined\n }\n try {\n const json = JSON.stringify(result)\n if (json === undefined || json.length > MAX_RESULT_BYTES) {\n return undefined\n }\n return JSON.parse(json)\n } catch {\n return undefined\n }\n}\n\nexport type ActionEvent = BeforeEvent | AfterEvent\n\ninterface ActionStream {\n events: ActionEvent[]\n prevEndMs: number\n callCounter: number\n /** Currently-open tracingGroup path, outermost first (feature→scenario→step).\n * Actions nest under the innermost entry. */\n openGroups: { uid: string; callId: string }[]\n}\n\n// An adapter may attach a normalized CollapsedAssertResult (see shared) to an\n// assertion command — prefer its actual/expected over the positional args,\n// which are only correct for node:assert-style `[actual, expected]` calls.\nfunction collapsedAssertResult(\n result: unknown\n): CollapsedAssertResult | undefined {\n if (typeof result === 'object' && result !== null && 'passed' in result) {\n return result as CollapsedAssertResult\n }\n return undefined\n}\n\n// Assert params: node:assert positional order (actual, expected, message?),\n// plus a numeric echo of the raw args so the reader's paramsToArgs inverse\n// reconstructs the original arg list without assert-specific knowledge.\nfunction buildAssertParams(cmd: CommandLog): Record<string, unknown> {\n const params: Record<string, unknown> = Object.fromEntries(\n cmd.args.map((arg, index) => [String(index), arg])\n )\n const [actual, expected, message] = cmd.args\n const collapsed = collapsedAssertResult(cmd.result)\n const semantic = {\n actual: collapsed?.actual ?? actual,\n expected: collapsed?.expected ?? expected,\n message: collapsed?.message ?? message\n }\n for (const [key, value] of Object.entries(semantic)) {\n if (value !== undefined) {\n params[key] = value\n }\n }\n return params\n}\n\n// Semantic params from positional args (selector/value/url), falling back to\n// index keys; the reader's paramsToArgs is the inverse.\nfunction buildActionParams(\n action: TraceAction,\n rawArgs: unknown[]\n): Record<string, unknown> {\n const isValueMethod = FILL_METHODS.has(action.method)\n if (action.class === 'Element' && isValueMethod && rawArgs.length >= 2) {\n return { selector: rawArgs[0], value: rawArgs[1] }\n }\n if (action.class === 'Element' && isValueMethod && rawArgs.length === 1) {\n return { value: rawArgs[0] }\n }\n if (\n action.class === 'Element' &&\n rawArgs.length === 1 &&\n typeof rawArgs[0] === 'string'\n ) {\n return { selector: rawArgs[0] }\n }\n if (rawArgs.length === 1 && typeof rawArgs[0] === 'string') {\n return { url: rawArgs[0] }\n }\n return Object.fromEntries(rawArgs.map((a, i) => [String(i), a]))\n}\n\nfunction closeGroupsFrom(stream: ActionStream, from: number): void {\n // Innermost-first so nested after events stay balanced.\n for (let i = stream.openGroups.length - 1; i >= from; i--) {\n stream.events.push({\n type: 'after',\n callId: stream.openGroups[i].callId,\n endTime: stream.prevEndMs\n })\n }\n stream.openGroups.length = Math.max(0, from)\n}\n\n// Diff the command's desired group path (feature→scenario→step, or just the\n// test) against the open stack: close the diverged tail, then open the new tail\n// with parentId chaining so the reader nests them. Actions then reference the\n// innermost open group. A path of length ≤1 with no ancestry/step reproduces\n// the previous single-group-per-test output.\nfunction syncGroups(\n stream: ActionStream,\n cmd: CommandLog,\n pageId: string,\n wallTime: number,\n testMetadata?: TestMetadataMap\n): void {\n const desired = buildGroupPath(cmd, testMetadata)\n let common = 0\n while (\n common < stream.openGroups.length &&\n common < desired.length &&\n stream.openGroups[common].uid === desired[common].uid\n ) {\n common++\n }\n closeGroupsFrom(stream, common)\n for (let i = common; i < desired.length; i++) {\n stream.callCounter++\n const callId = `call@${stream.callCounter}`\n const parentId = stream.openGroups[i - 1]?.callId\n const groupBefore: BeforeEvent = {\n type: 'before',\n callId,\n startTime: Math.max(\n stream.prevEndMs,\n (cmd.startTime ?? cmd.timestamp) - wallTime\n ),\n class: 'Tracing',\n method: 'tracingGroup',\n pageId,\n params: { name: desired[i].title },\n title: desired[i].title,\n apiName: 'tracing.tracingGroup'\n }\n if (parentId) {\n groupBefore.parentId = parentId\n }\n stream.events.push(groupBefore)\n stream.openGroups.push({ uid: desired[i].uid, callId })\n }\n}\n\nfunction buildParamsAndTitle(\n action: TraceAction,\n cmd: CommandLog\n): { params: Record<string, unknown>; title: string } {\n const isAssert = action.class === ASSERT_ACTION_CLASS\n const params = isAssert\n ? buildAssertParams(cmd)\n : buildActionParams(action, cmd.args)\n // The element this command targeted, under a key distinct from `selector` so\n // it never feeds `paramsToArgs` — lets the reader restore CommandLog.selector\n // for the player's overlay even when args carry an assertion's expected value.\n if (cmd.selector && params.locator === undefined) {\n params.locator = cmd.selector\n }\n return {\n params,\n title: formatActionTitle(\n action,\n cmd.args,\n params,\n isAssert ? cmd.command : undefined\n )\n }\n}\n\nfunction actionError(\n cmd: CommandLog,\n action: TraceAction\n): { message: string } | undefined {\n if (cmd.error) {\n const err = cmd.error as { message?: string }\n return { message: err.message ?? String(cmd.error) }\n }\n const isAssert = action.class === ASSERT_ACTION_CLASS\n // A command whose own result reports `passed: false` failed, whatever class it\n // maps to — not just the assert rows this used to be gated on.\n const collapsed = collapsedAssertResult(cmd.result)\n if (collapsed && collapsed.passed === false) {\n return {\n message: String(\n collapsed.message ??\n (isAssert ? 'Assertion failed' : `${cmd.command} failed`)\n )\n }\n }\n // A wait that resolved `false` timed out. Nightwatch reports its\n // waitForElement* timeouts through its own assertion channel rather than the\n // command callback, which hands back `{value: null}` — so the row's only\n // evidence of the failure is that boolean, and without this the step a test\n // most often fails on rendered green with nothing in the Errors tab. Scoped to\n // waits because `false` is a legitimate value for a boolean read like\n // `isDisplayed`, and WDIO's waits throw rather than return false.\n if (cmd.result === false && action.method.startsWith('waitFor')) {\n const target = cmd.args?.find((a) => typeof a === 'string')\n return {\n message: target\n ? `${cmd.command} timed out waiting for ${String(target)}`\n : `${cmd.command} timed out`\n }\n }\n return undefined\n}\n\n/** Centre of the element a pointer action matched, from the captured element\n * rects at the command's completion. Undefined for non-pointer actions, a\n * non-string selector, or a selector absent from the captured elements (e.g. a\n * WDIO text/xpath locator `getSelector` didn't reproduce). A runner that acts\n * through a resolved element handle leaves nothing selector-shaped in `args`\n * (Selenium: the handle is the receiver, the args hold only the typed value), so\n * the row's recorded locator is read first. */\nfunction resolveActionPoint(\n action: TraceAction,\n cmd: CommandLog,\n snapshotIndex?: FrameSnapshotIndex\n): { x: number; y: number } | undefined {\n if (action.class !== 'Element' || !POINTABLE_METHODS.has(action.method)) {\n return undefined\n }\n const selector = cmd.selector ?? cmd.args?.[0]\n if (typeof selector !== 'string') {\n return undefined\n }\n const elements = snapshotIndex?.elementsAt(cmd.timestamp)\n for (const el of elements ?? []) {\n // Narrow the unknown element record at the boundary (element-scripts shape).\n const e = el as {\n selector?: string\n boundingBox?: { x: number; y: number; width: number; height: number }\n }\n if (e.selector && locatorsMatch(e.selector, selector) && e.boundingBox) {\n const bb = e.boundingBox\n return { x: bb.x + bb.width / 2, y: bb.y + bb.height / 2 }\n }\n }\n return undefined\n}\n\nfunction buildAfterEvent(\n cmd: CommandLog,\n action: TraceAction,\n callId: string,\n endMs: number,\n snapshotIndex?: FrameSnapshotIndex\n): AfterEvent {\n const afterEvent: AfterEvent = { type: 'after', callId, endTime: endMs }\n const error = actionError(cmd, action)\n if (error) {\n afterEvent.error = error\n }\n const result = serializableResult(cmd.result)\n if (result !== undefined) {\n afterEvent.result = result\n }\n const afterName = snapshotIndex?.claimAfter(cmd.timestamp, callId)\n if (afterName) {\n afterEvent.afterSnapshot = afterName\n }\n const point = resolveActionPoint(action, cmd, snapshotIndex)\n if (point) {\n afterEvent.point = point\n }\n return afterEvent\n}\n\nfunction pushActionPair(\n stream: ActionStream,\n cmd: CommandLog,\n action: TraceAction,\n pageId: string,\n wallTime: number,\n snapshotIndex?: FrameSnapshotIndex\n): void {\n stream.callCounter++\n const callId = `call@${stream.callCounter}`\n // Command invocation timestamp, falling back to completion when absent.\n const rawStartMs = (cmd.startTime ?? cmd.timestamp) - wallTime\n const rawEndMs = cmd.timestamp - wallTime\n // Floor at prevEndMs to prevent visual overlap with the previous action.\n const startMs = Math.max(stream.prevEndMs, rawStartMs)\n // +1ms minimum duration so an `after` never precedes its parsed `before`.\n const endMs = Math.max(startMs + 1, rawEndMs)\n const { params, title } = buildParamsAndTitle(action, cmd)\n const beforeEvent: BeforeEvent = {\n type: 'before',\n callId,\n startTime: startMs,\n class: action.class,\n method: action.method,\n pageId,\n params,\n title,\n apiName: `${action.class.toLowerCase()}.${action.method}`,\n parentId: stream.openGroups[stream.openGroups.length - 1]?.callId\n }\n const stack = callSourceToStack(cmd.callSource)\n if (stack) {\n beforeEvent.stack = stack\n }\n const beforeName = snapshotIndex?.beforeName()\n if (beforeName) {\n beforeEvent.beforeSnapshot = beforeName\n }\n stream.events.push(beforeEvent)\n stream.events.push(buildAfterEvent(cmd, action, callId, endMs, snapshotIndex))\n stream.prevEndMs = endMs\n}\n\nexport function buildActionEvents(\n commands: CommandLog[],\n pageId: string,\n wallTime: number,\n testMetadata?: TestMetadataMap,\n snapshotIndex?: FrameSnapshotIndex\n): ActionEvent[] {\n const stream: ActionStream = {\n events: [],\n prevEndMs: 0,\n callCounter: 0,\n openGroups: []\n }\n // Process in chronological order, not insertion order. Deferred rows (e.g.\n // Nightwatch native asserts, finalized in one batch at test-end) are appended\n // late but carry their real call-time `startTime`; since pushActionPair floors\n // each start at the running prevEndMs, out-of-order input would clamp those\n // late rows to the end of the timeline (clustering them after the last real\n // command). A stable sort by start time restores true positions. `sequence`\n // then breaks ties the millisecond clock can't: a deferred row issued\n // microseconds before the command that follows it reads as the same\n // `startTime`, and insertion order put it after — which is how an assert that\n // ran on the secure page landed below the logout click it preceded. Each\n // command owns its own before/after pair, so pairing is unaffected.\n const ordered = [...commands].sort(\n (a, b) =>\n (a.startTime ?? a.timestamp) - (b.startTime ?? b.timestamp) ||\n (a.sequence ?? 0) - (b.sequence ?? 0)\n )\n for (const cmd of ordered) {\n const action = mapCommandToAction(cmd.command)\n if (!action) {\n continue\n }\n syncGroups(stream, cmd, pageId, wallTime, testMetadata)\n pushActionPair(stream, cmd, action, pageId, wallTime, snapshotIndex)\n }\n closeGroupsFrom(stream, 0)\n return stream.events\n}\n","// Maps captured ConsoleLog entries into trace-event vocabulary: browser\n// console entries become `console` events; test/terminal output becomes\n// `stdout`/`stderr` events (which carry no location semantics — matching\n// what we capture for Node-side lines).\n\nimport type { ConsoleLog, LogSource } from '@wdio/devtools-shared'\n\nexport interface ConsoleEvent {\n type: 'console'\n time: number\n pageId?: string\n messageType: string\n text: string\n args?: { preview: string; value: unknown }[]\n location: { url: string; lineNumber: number; columnNumber: number }\n}\n\nexport interface StdioEvent {\n type: 'stdout' | 'stderr'\n timestamp: number\n text?: string\n /** Extension field: test-vs-terminal origin; foreign viewers ignore it. */\n source?: Extract<LogSource, 'test' | 'terminal'>\n}\n\n// Caps pathological runs (console.log in a loop) so the trace stays openable.\nconst MAX_CONSOLE_EVENTS = 10_000\n\n/** Trace vocabulary uses 'warning'; 'trace' maps to the nearest severity, 'debug'. */\nfunction toTraceLevel(level: ConsoleLog['type']): string {\n if (level === 'warn') {\n return 'warning'\n }\n if (level === 'trace') {\n return 'debug'\n }\n return level\n}\n\nfunction previewArg(arg: unknown): string {\n if (typeof arg === 'string') {\n return arg\n }\n try {\n return JSON.stringify(arg) ?? String(arg)\n } catch {\n return String(arg)\n }\n}\n\nexport function buildConsoleEvents(\n logs: ConsoleLog[],\n pageId: string,\n wallTime: number\n): (ConsoleEvent | StdioEvent)[] {\n const capped = logs.slice(0, MAX_CONSOLE_EVENTS)\n const events: (ConsoleEvent | StdioEvent)[] = capped.map((log) => {\n const time = Math.max(0, log.timestamp - wallTime)\n const text = log.args.map(previewArg).join(' ')\n // Untagged entries predate source tagging; they came from the page.\n if (log.source === 'browser' || log.source === undefined) {\n return {\n type: 'console',\n time,\n pageId,\n messageType: toTraceLevel(log.type),\n text,\n args: log.args.map((arg) => ({ preview: previewArg(arg), value: arg })),\n // Location isn't captured at the patch site; the required field ships zeroed.\n location: { url: '', lineNumber: 0, columnNumber: 0 }\n } satisfies ConsoleEvent\n }\n return {\n type: log.type === 'error' || log.type === 'warn' ? 'stderr' : 'stdout',\n timestamp: time,\n text,\n source: log.source\n } satisfies StdioEvent\n })\n if (logs.length > MAX_CONSOLE_EVENTS) {\n const last = capped[capped.length - 1]\n events.push({\n type: 'stderr',\n timestamp: last ? Math.max(0, last.timestamp - wallTime) : 0,\n text: `[devtools] console truncated: dropped ${logs.length - MAX_CONSOLE_EVENTS} entries`\n })\n }\n return events\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 { TRACE_ZIP_ENTRIES } from '@wdio/devtools-shared'\nimport type {\n ActionSnapshot,\n CommandLog,\n ConsoleLog,\n Metadata,\n NetworkRequest,\n ScreencastFrame,\n TestMetadataMap,\n TestRunnerId,\n TraceFormat,\n TraceLog,\n TraceMutation\n} from '@wdio/devtools-shared'\nimport { mapCommandToAction } from './action-mapping.js'\nimport {\n buildConsoleEvents,\n type ConsoleEvent,\n type StdioEvent\n} from './trace-console.js'\nimport {\n buildFilmstripEvents,\n buildSnapshotResources,\n type ScreencastFrameEvent\n} from './trace-snapshots.js'\nimport { buildDenseScreencast } from './screencast-trace.js'\nimport {\n buildImageFrameSnapshots,\n FrameSnapshotIndex,\n type FrameSnapshotEvent\n} from './trace-frame-snapshots.js'\nimport { buildActionEvents, type ActionEvent } from './trace-action-events.js'\nimport { buildSourceResources } from './trace-sources.js'\nimport { networkRequestToHar } from './trace-har.js'\nimport { buildTraceZip, type TraceZipResource } from './trace-zip-writer.js'\nimport { buildMutationsNdjson } from './trace-mutations.js'\nimport { generateTranscript } from './trace-transcript.js'\nimport { sha1Hex } from './sha1.js'\n\n// Transcript building moved to its own module; re-exported here because this is\n// the name the package barrel and downstream adapters already import.\nexport { generateTranscript }\n\nconst TRACE_VERSION = 8\nconst LIBRARY_NAME = '@wdio/devtools-core'\nconst LIBRARY_VERSION = '1.0.0'\n\n/** Response bodies above this size are not embedded in the trace. */\nconst MAX_BODY_RESOURCE_BYTES = 1024 * 1024\n/** Per-trace ceiling on total embedded response-body bytes. */\nconst MAX_TOTAL_BODY_RESOURCE_BYTES = 20 * 1024 * 1024\n\nexport interface NetworkBodyCaps {\n maxBodyBytes: number\n maxTotalBytes: number\n}\n\nexport interface NetworkBodyResources {\n resources: TraceZipResource[]\n sha1ByRequestId: Map<string, string>\n}\n\n/** Content-addressed `resources/<sha1>` entries for captured response bodies. */\nexport function buildNetworkBodyResources(\n requests: NetworkRequest[],\n caps: NetworkBodyCaps = {\n maxBodyBytes: MAX_BODY_RESOURCE_BYTES,\n maxTotalBytes: MAX_TOTAL_BODY_RESOURCE_BYTES\n }\n): NetworkBodyResources {\n const resources: TraceZipResource[] = []\n const sha1ByRequestId = new Map<string, string>()\n const stored = new Set<string>()\n let totalBytes = 0\n // Cap skips are silent by design; a warn hook would slot into these branches.\n for (const request of requests) {\n if (request.responseBody === undefined) {\n continue\n }\n const data = Buffer.from(request.responseBody, 'utf8')\n if (data.byteLength > caps.maxBodyBytes) {\n continue\n }\n const sha1 = sha1Hex(data)\n if (stored.has(sha1)) {\n sha1ByRequestId.set(request.id, sha1)\n continue\n }\n if (totalBytes + data.byteLength > caps.maxTotalBytes) {\n continue\n }\n stored.add(sha1)\n totalBytes += data.byteLength\n resources.push({ resourceName: sha1, data })\n sha1ByRequestId.set(request.id, sha1)\n }\n return { resources, sha1ByRequestId }\n}\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 /** Extension field: the runner that recorded this zip, so the player can tell\n * the user how to resolve a captured locator in their own framework. Absent\n * when the capture didn't identify itself, and in foreign zips. */\n runner?: TestRunnerId\n}\n\ntype TraceEvent =\n | ContextOptionsEvent\n | ActionEvent\n | ScreencastFrameEvent\n | ConsoleEvent\n | StdioEvent\n | FrameSnapshotEvent\n\nfunction shortId(sessionId?: string): string {\n return (sessionId ?? Math.random().toString(36).slice(2, 10)).slice(0, 8)\n}\n\nfunction allocateTraceIds(sessionId?: string): {\n contextId: string\n pageId: string\n} {\n const idPrefix = shortId(sessionId)\n return { contextId: `context@${idPrefix}`, pageId: `page@${idPrefix}` }\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 ...(trace.metadata.runner ? { runner: trace.metadata.runner } : {})\n }\n}\n\nfunction buildNetworkNdjson(\n requests: NetworkRequest[],\n wallTime: number,\n pageId: string,\n sha1ByRequestId: Map<string, string>\n): Buffer {\n if (!requests.length) {\n return Buffer.alloc(0)\n }\n const lines = requests.map((r) => {\n const entry = networkRequestToHar(r, {\n bodySha1: sha1ByRequestId.get(r.id)\n }) 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\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 case 'frame-snapshot':\n return e.snapshot.timestamp\n case 'console':\n return e.time\n case 'stdout':\n case 'stderr':\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 console output observed\n * at the boundary, then the next action's `before`. Matches the viewer's\n * expectation that the screencast frame shows the state between the\n * 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 case 'frame-snapshot':\n return 2\n case 'console':\n case 'stdout':\n case 'stderr':\n return 3\n case 'before':\n return 4\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\ninterface TraceBundle {\n traceNdjson: string\n networkNdjson: Buffer\n transcriptMd: string\n mutationsNdjson: Buffer\n resources: TraceZipResource[]\n}\n\nfunction buildEventStream(\n trace: TraceLog,\n ctxOptions: ContextOptionsEvent,\n pageId: string,\n wallTime: number,\n testMetadata?: TestMetadataMap,\n denseFrameEvents: ScreencastFrameEvent[] = []\n): TraceEvent[] {\n const viewport = trace.metadata.viewport ?? { width: 1280, height: 720 }\n const snapshots = trace.actionSnapshots ?? []\n const snapshotIndex = new FrameSnapshotIndex(snapshots)\n // Dense frames supersede the sparse per-action filmstrip; per-action DOM is\n // carried by the frame-snapshot events, so sparse frames would only duplicate.\n const filmstrip =\n denseFrameEvents.length > 0\n ? denseFrameEvents\n : buildFilmstripEvents(snapshots, pageId, wallTime, viewport)\n const events: TraceEvent[] = [\n ctxOptions,\n ...filmstrip,\n ...buildActionEvents(\n trace.commands,\n pageId,\n wallTime,\n testMetadata,\n snapshotIndex\n ),\n ...buildImageFrameSnapshots(\n snapshotIndex.refs(),\n pageId,\n wallTime,\n viewport\n ),\n ...buildConsoleEvents(trace.consoleLogs, pageId, wallTime)\n ]\n events.sort(compareEvents)\n return events\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 { contextId, pageId } = allocateTraceIds(opts.sessionId)\n const ctxOptions = buildContextOptions(trace, contextId, wallTime)\n const dense = buildDenseScreencast(\n trace.screencastFrames ?? [],\n pageId,\n wallTime,\n trace.metadata.viewport ?? { width: 1280, height: 720 }\n )\n const events = buildEventStream(\n trace,\n ctxOptions,\n pageId,\n wallTime,\n opts.testMetadata,\n dense.events\n )\n const networkBodies = buildNetworkBodyResources(trace.networkRequests)\n return {\n traceNdjson: events.map((e) => JSON.stringify(e)).join('\\n') + '\\n',\n networkNdjson: buildNetworkNdjson(\n trace.networkRequests,\n wallTime,\n pageId,\n networkBodies.sha1ByRequestId\n ),\n transcriptMd: generateTranscript(\n trace.commands,\n wallTime,\n ctxOptions.title\n ),\n mutationsNdjson: buildMutationsNdjson(trace.mutations ?? []).ndjson,\n resources: [\n ...buildSnapshotResources(trace.actionSnapshots ?? [], pageId),\n ...dense.resources,\n ...buildSourceResources(trace.sources),\n ...networkBodies.resources\n ]\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 mutationsNdjson: bundle.mutationsNdjson\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 const entry = (name: string) => path.join(targetDir, name)\n const {\n trace: T,\n network,\n mutations,\n transcript,\n resourcesDir\n } = TRACE_ZIP_ENTRIES\n await fs.mkdir(entry(resourcesDir), { recursive: true })\n await Promise.all([\n fs.writeFile(entry(T), bundle.traceNdjson),\n fs.writeFile(entry(transcript), bundle.transcriptMd, 'utf8'),\n bundle.networkNdjson.length\n ? fs.writeFile(entry(network), bundle.networkNdjson)\n : Promise.resolve(),\n bundle.mutationsNdjson.length\n ? fs.writeFile(entry(mutations), bundle.mutationsNdjson)\n : Promise.resolve(),\n ...bundle.resources.map((r) =>\n fs.writeFile(\n path.join(targetDir, TRACE_ZIP_ENTRIES.resourcesDir, r.resourceName),\n r.data\n )\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 /** Dense screencast frames for the filmstrip. Thinned + content-addressed at\n * export time; adapters pass the slice's windowed frames (or all, session\n * scope). Omitted → no dense filmstrip (byte-stable with today's output). */\n screencastFrames?: readonly ScreencastFrame[]\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 /** Base name for the artifact (zip file stem / directory name). Defaults to\n * `trace-<sessionId>`; per-test slices pass `'trace'` inside a named folder. */\n fileStem?: string\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 ...(opts.screencastFrames?.length\n ? { screencastFrames: [...opts.screencastFrames] }\n : {})\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 const stem = opts.fileStem ?? `trace-${opts.sessionId}`\n if (opts.format === 'ndjson-directory') {\n const dir = path.join(opts.outputDir, stem)\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, `${stem}.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","// Per-action snapshot resources and the screencast-frame filmstrip derived\n// from them. Split from trace-exporter.ts; the exporter composes these into\n// the trace event stream.\n\nimport type { ActionSnapshot } from '@wdio/devtools-shared'\nimport type { TraceZipResource } from './trace-zip-writer.js'\n\nexport interface 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\n// The one merge rule for same-timestamp snapshots: richest wins per field,\n// first-seen wins for identity (command/url/title, which no export path reads).\n// Merging per field matters because the real screenshot and the real elements\n// can land on different captures of the same action.\nfunction mergeSnapshotInto(\n existing: ActionSnapshot,\n snap: ActionSnapshot\n): void {\n if ((snap.screenshot?.length ?? 0) > (existing.screenshot?.length ?? 0)) {\n existing.screenshot = snap.screenshot\n }\n if (!existing.elements?.length && snap.elements?.length) {\n existing.elements = snap.elements\n }\n if (!existing.snapshotText && snap.snapshotText) {\n existing.snapshotText = snap.snapshotText\n }\n}\n\n// Two snapshots at the same timestamp (a real post-action capture and a blank\n// end-of-scenario one) map to the same `${pageId}-${ts}` resource name, so a\n// last-wins write lets a blank frame clobber the real one. Collapse to one per\n// timestamp so the resource writer never sees a collision.\nfunction collapseByTimestamp(snapshots: ActionSnapshot[]): ActionSnapshot[] {\n const byTs = new Map<number, ActionSnapshot>()\n const order: number[] = []\n for (const snap of snapshots) {\n const existing = byTs.get(snap.timestamp)\n if (!existing) {\n byTs.set(snap.timestamp, { ...snap })\n order.push(snap.timestamp)\n continue\n }\n mergeSnapshotInto(existing, snap)\n }\n return order.map((ts) => byTs.get(ts)!)\n}\n\n/** Insert a snapshot, or merge it into the one already holding its timestamp.\n * Applies the collapse rule at capture time so the accumulator holds one record\n * per timestamp: a mid-run slice flush reads this array live, before export\n * would have collapsed it, and would otherwise emit a blank duplicate. */\nexport function upsertRichestSnapshot(\n snapshots: ActionSnapshot[],\n snap: ActionSnapshot\n): void {\n const idx = snapshots.findIndex((s) => s.timestamp === snap.timestamp)\n if (idx === -1) {\n snapshots.push(snap)\n return\n }\n mergeSnapshotInto(snapshots[idx]!, snap)\n}\n\nexport function buildSnapshotResources(\n rawSnapshots: ActionSnapshot[],\n pageId: string\n): TraceZipResource[] {\n const snapshots = collapseByTimestamp(rawSnapshots)\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 frameForSnapshot(\n snap: ActionSnapshot,\n pageId: string,\n timestamp: number,\n viewport: { width: number; height: number }\n): ScreencastFrameEvent {\n const base = `${pageId}-${snap.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\n }\n if (snap.elements && snap.elements.length) {\n frame.elements = `${base}-elements.json`\n }\n if (snap.snapshotText) {\n frame.snapshot = `${base}-snapshot.txt`\n }\n return frame\n}\n\n/** Filmstrip events; the first snapshot is re-anchored to t=0 for pre-interaction state. */\nexport function buildFilmstripEvents(\n rawSnapshots: ActionSnapshot[],\n pageId: string,\n wallTime: number,\n viewport: { width: number; height: number }\n): ScreencastFrameEvent[] {\n const snapshots = collapseByTimestamp(rawSnapshots)\n const firstSnap = snapshots.find((s) => s.screenshot)\n const events: ScreencastFrameEvent[] = []\n if (firstSnap) {\n events.push(frameForSnapshot(firstSnap, pageId, 0, viewport))\n }\n for (const snap of snapshots) {\n if (snap === firstSnap || !snap.screenshot) {\n continue\n }\n events.push(\n frameForSnapshot(\n snap,\n pageId,\n Math.max(0, snap.timestamp - wallTime),\n viewport\n )\n )\n }\n return events\n}\n","/**\n * Dense screencast filmstrip for trace mode. Turns the recorder's continuous\n * frame buffer into `screencast-frame` events plus content-addressed image\n * resources, so the trace player scrubs smooth playback instead of one frame\n * per action.\n *\n * Two independent bounds keep the cost in check: thinning caps the number of\n * emitted events (min inter-frame gap + drop consecutive byte-identical frames\n * + even downsample to a hard cap), and content-addressing dedupes the image\n * bytes (identical frames — a static wait polled repeatedly — collapse to one\n * resource). Framework-agnostic: adapters feed `recorder.frames`; the recorder\n * itself (CDP push on Chrome, screenshot polling elsewhere) lives per-adapter.\n */\n\nimport { createHash } from 'node:crypto'\nimport type { ScreencastFrame } from '@wdio/devtools-shared'\nimport type { TraceZipResource } from './trace-zip-writer.js'\nimport type { ScreencastFrameEvent } from './trace-snapshots.js'\n\nexport interface DenseScreencastOptions {\n /** Hard cap on emitted frame events (default 600). */\n maxFrames?: number\n /** Minimum ms between kept frames (default 100 ≈ 10 fps). */\n minFrameIntervalMs?: number\n}\n\nconst DEFAULT_MAX_FRAMES = 600\nconst DEFAULT_MIN_INTERVAL_MS = 100\n\n/**\n * A run's whole filmstrip: the frames drained from sessions that have already\n * ended plus the one still recording. Both sources are always needed — a\n * mid-run per-spec/per-test flush fires before the live recorder is drained, so\n * its frames exist only on the recorder, while at the final write the recorder\n * is already gone. Reading either alone silently truncates the filmstrip.\n */\nexport function accumulatedScreencastFrames(\n drained: readonly ScreencastFrame[],\n recording: { readonly frames: readonly ScreencastFrame[] } | undefined\n): ScreencastFrame[] {\n return [...drained, ...(recording?.frames ?? [])]\n}\n\n/**\n * Thin a continuous frame buffer down to a bounded event set. Keeps a frame\n * only when it is at least `minFrameIntervalMs` after the last kept frame and\n * its bytes differ from the last kept frame (a static run collapses to its\n * first frame — the player holds it until the next change, which is the big win\n * for polling mode where every poll re-shoots an unchanged page). A final even\n * downsample enforces the hard cap while always keeping the first and last\n * frames so the timeline still spans the full run.\n */\nexport function thinScreencastFrames(\n frames: readonly ScreencastFrame[],\n options: DenseScreencastOptions = {}\n): ScreencastFrame[] {\n const minInterval = options.minFrameIntervalMs ?? DEFAULT_MIN_INTERVAL_MS\n const maxFrames = options.maxFrames ?? DEFAULT_MAX_FRAMES\n\n const kept: ScreencastFrame[] = []\n let lastKeptTs = -Infinity\n let lastData: string | undefined\n for (const frame of frames) {\n if (frame.timestamp - lastKeptTs < minInterval) {\n continue\n }\n if (frame.data === lastData) {\n continue\n }\n kept.push(frame)\n lastKeptTs = frame.timestamp\n lastData = frame.data\n }\n\n if (kept.length <= maxFrames) {\n return kept\n }\n if (maxFrames <= 1) {\n return kept.length ? [kept[0]!] : []\n }\n const out: ScreencastFrame[] = []\n const step = (kept.length - 1) / (maxFrames - 1)\n for (let i = 0; i < maxFrames; i++) {\n out.push(kept[Math.round(i * step)]!)\n }\n return out\n}\n\n/**\n * Build the dense filmstrip: `screencast-frame` events (offsets rebased against\n * `wallTime`, matching the sparse filmstrip) and their image resources named by\n * content hash so byte-identical frames share a single resource. Adapters that\n * don't record frames pass an empty array and get an empty result — byte-stable\n * with today's output.\n */\nexport function buildDenseScreencast(\n frames: readonly ScreencastFrame[],\n pageId: string,\n wallTime: number,\n viewport: { width: number; height: number },\n options: DenseScreencastOptions = {}\n): { events: ScreencastFrameEvent[]; resources: TraceZipResource[] } {\n const events: ScreencastFrameEvent[] = []\n const resources: TraceZipResource[] = []\n const seen = new Set<string>()\n for (const frame of thinScreencastFrames(frames, options)) {\n // One malformed frame must not cost the run its whole trace: a screenshot\n // probe that answered a driver error object instead of base64 reached here\n // and `Buffer.from` threw inside the export, losing every other event too.\n if (typeof frame.data !== 'string' || !frame.data) {\n continue\n }\n const data = Buffer.from(frame.data, 'base64')\n const sha1 = createHash('sha1').update(data).digest('hex')\n const resourceName = `${sha1}.jpeg`\n if (!seen.has(sha1)) {\n seen.add(sha1)\n resources.push({ resourceName, data })\n }\n events.push({\n type: 'screencast-frame',\n pageId,\n sha1: resourceName,\n width: viewport.width,\n height: viewport.height,\n timestamp: Math.max(0, frame.timestamp - wallTime)\n })\n }\n return { events, resources }\n}\n","// Image-backed frame-snapshot synthesis: each action screenshot becomes a\n// minimal DOM document so standard trace viewers render the action pane.\n// Compatibility shim until real DOM snapshots are captured.\n\nimport type { ActionSnapshot } from '@wdio/devtools-shared'\n\nconst SNAPSHOT_DOCTYPE = 'html'\nconst FALLBACK_FRAME_URL = 'about:blank'\nconst BODY_STYLE = 'margin:0'\nconst IMAGE_STYLE = 'display:block;width:100vw;height:100vh;object-fit:contain'\n\n/** Serialized DOM node: text, or a [TAG, attributes, ...children] tuple. */\nexport type FrameSnapshotNode =\n | string\n | [string, Record<string, string>, ...FrameSnapshotNode[]]\n\nexport interface FrameSnapshotResourceOverride {\n url: string\n sha1?: string\n ref?: number\n}\n\nexport interface FrameSnapshot {\n callId: string\n snapshotName: string\n pageId: string\n frameId: string\n frameUrl: string\n doctype?: string\n html: FrameSnapshotNode\n viewport: { width: number; height: number }\n timestamp: number\n wallTime: number\n collectionTime: number\n resourceOverrides: FrameSnapshotResourceOverride[]\n isMainFrame: boolean\n}\n\nexport interface FrameSnapshotEvent {\n type: 'frame-snapshot'\n snapshot: FrameSnapshot\n}\n\nexport interface FrameSnapshotRef {\n callId: string\n snapshotName: string\n snapshot: ActionSnapshot\n}\n\n/** Correlates captured screenshots to callIds as the exporter assigns them. */\nexport class FrameSnapshotIndex {\n #byTimestamp = new Map<number, ActionSnapshot>()\n // Element rects by timestamp, indexed independently of the screenshot gate so\n // A8 input points resolve even when the frame carries no screenshot.\n #elementsByTimestamp = new Map<number, unknown[]>()\n #refs: FrameSnapshotRef[] = []\n #lastName?: string\n\n constructor(snapshots: ActionSnapshot[]) {\n for (const snap of snapshots) {\n if (snap.elements) {\n const currentEls = this.#elementsByTimestamp.get(snap.timestamp)\n if (!currentEls || snap.elements.length > currentEls.length) {\n this.#elementsByTimestamp.set(snap.timestamp, snap.elements)\n }\n }\n if (!snap.screenshot) {\n continue\n }\n const current = this.#byTimestamp.get(snap.timestamp)\n // Same-timestamp duplicates keep the richest capture (dedupe parity).\n if (\n !current ||\n snap.screenshot.length > (current.screenshot?.length ?? 0)\n ) {\n this.#byTimestamp.set(snap.timestamp, snap)\n }\n }\n }\n\n /** Captured element rects at a command's completion timestamp, if any. */\n elementsAt(timestamp: number): unknown[] | undefined {\n return this.#elementsByTimestamp.get(timestamp)\n }\n\n /** Snapshot name representing the page state before the next action. */\n beforeName(): string | undefined {\n return this.#lastName\n }\n\n /**\n * The capture describing the page when this command completed: the one at its\n * exact timestamp, else the most recent one before it.\n *\n * Non-consuming, and falls back rather than returning nothing, because several\n * actions legitimately share one page state. Nightwatch emits its native\n * assertion rows in a batch whose execution windows collapse onto the same\n * instant, so handing the capture to whichever claimed first left the rest of\n * the batch with no DOM, no a11y tree and no screenshot in the action pane.\n * And an assertion reads the page rather than changing it, so it takes no\n * capture of its own and correctly inherits the preceding action's.\n */\n claimAfter(timestamp: number, callId: string): string | undefined {\n const snap =\n this.#byTimestamp.get(timestamp) ?? this.#latestAtOrBefore(timestamp)\n if (!snap) {\n return undefined\n }\n const snapshotName = `after@${callId}`\n this.#refs.push({ callId, snapshotName, snapshot: snap })\n this.#lastName = snapshotName\n return snapshotName\n }\n\n #latestAtOrBefore(timestamp: number): ActionSnapshot | undefined {\n let best: ActionSnapshot | undefined\n for (const [ts, snap] of this.#byTimestamp) {\n if (ts <= timestamp && (!best || ts > best.timestamp)) {\n best = snap\n }\n }\n return best\n }\n\n refs(): FrameSnapshotRef[] {\n return this.#refs\n }\n}\n\nfunction frameIdForPage(pageId: string): string {\n const suffix = pageId.startsWith('page@')\n ? pageId.slice('page@'.length)\n : pageId\n return `frame@${suffix}`\n}\n\n// Captures come from WebDriver screenshots (PNG) or CDP screencasts (JPEG),\n// so the mime is sniffed from the base64 magic rather than assumed.\nfunction imageMimeType(base64: string): string {\n return base64.startsWith('iVBOR') ? 'image/png' : 'image/jpeg'\n}\n\nfunction imageDocument(snap: ActionSnapshot): FrameSnapshotNode {\n const screenshot = snap.screenshot ?? ''\n return [\n 'HTML',\n {},\n ['HEAD', {}, ['BASE', { href: snap.url ?? FALLBACK_FRAME_URL }]],\n [\n 'BODY',\n { style: BODY_STYLE },\n [\n 'IMG',\n {\n src: `data:${imageMimeType(screenshot)};base64,${screenshot}`,\n style: IMAGE_STYLE\n }\n ]\n ]\n ]\n}\n\n/** One frame-snapshot event per claimed screenshot, viewer-shape exact. */\nexport function buildImageFrameSnapshots(\n refs: FrameSnapshotRef[],\n pageId: string,\n wallTime: number,\n viewport: { width: number; height: number }\n): FrameSnapshotEvent[] {\n const frameId = frameIdForPage(pageId)\n return refs.map((ref) => ({\n type: 'frame-snapshot' as const,\n snapshot: {\n callId: ref.callId,\n snapshotName: ref.snapshotName,\n pageId,\n frameId,\n frameUrl: ref.snapshot.url ?? FALLBACK_FRAME_URL,\n doctype: SNAPSHOT_DOCTYPE,\n html: imageDocument(ref.snapshot),\n viewport: { width: viewport.width, height: viewport.height },\n timestamp: Math.max(0, ref.snapshot.timestamp - wallTime),\n wallTime: ref.snapshot.timestamp,\n collectionTime: 0,\n resourceOverrides: [],\n isMainFrame: true\n }\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\n/** Bodies below this inline as plain `text`; larger ones rely on `_sha1` only. */\nconst INLINE_BODY_TEXT_MAX_BYTES = 8 * 1024\n\nexport interface HarResponseContent {\n size: number\n mimeType: string\n /** Inline body fallback for small textual bodies. */\n text?: string\n /** Content-addressed body resource name under `resources/`. */\n _sha1?: string\n}\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: HarResponseContent\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\nfunction toHarContent(\n entry: NetworkRequest,\n mimeType: string,\n bodySha1: string | undefined\n): HarResponseContent {\n const content: HarResponseContent = { size: entry.size ?? 0, mimeType }\n const body = entry.responseBody\n if (body === undefined) {\n return content\n }\n if (Buffer.byteLength(body, 'utf8') < INLINE_BODY_TEXT_MAX_BYTES) {\n content.text = body\n }\n if (bodySha1) {\n content._sha1 = bodySha1\n }\n return content\n}\n\nexport function networkRequestToHar(\n entry: NetworkRequest,\n opts: { bodySha1?: string } = {}\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: toHarContent(entry, mimeType, opts.bodySha1),\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 { TRACE_ZIP_ENTRIES } from '@wdio/devtools-shared'\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 /** NDJSON DOM mutation stream (one mutation per line). Omitted/empty → no entry. */\n mutationsNdjson?: Buffer\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(\n Buffer.from(inputs.traceNdjson, 'utf8'),\n TRACE_ZIP_ENTRIES.trace\n )\n zipFile.addBuffer(inputs.networkNdjson, TRACE_ZIP_ENTRIES.network)\n if (inputs.transcriptMd) {\n zipFile.addBuffer(\n Buffer.from(inputs.transcriptMd, 'utf8'),\n TRACE_ZIP_ENTRIES.transcript\n )\n }\n if (inputs.mutationsNdjson?.length) {\n zipFile.addBuffer(inputs.mutationsNdjson, TRACE_ZIP_ENTRIES.mutations)\n }\n for (const resource of inputs.resources) {\n zipFile.addBuffer(\n resource.data,\n `${TRACE_ZIP_ENTRIES.resourcesDir}/${resource.resourceName}`\n )\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","// Serialize the captured DOM mutation stream into the trace zip's\n// `trace.mutations` NDJSON entry so the offline player can replay DOM\n// time-travel. Standard trace viewers ignore the unknown entry, so it's\n// compat-safe. Keep-earliest under the byte cap — the initial full-DOM\n// childList and early diffs survive so replay-from-start stays intact; only\n// late mutations drop, and a trailing sentinel records how many.\n\nimport type {\n MutationsTruncationMarker,\n TraceMutation\n} from '@wdio/devtools-shared'\n\n/** Ceiling on the serialized `trace.mutations` payload — keeps archives bounded\n * on mutation-heavy SPAs. Late mutations drop first (replay-from-start holds). */\nexport const MAX_MUTATIONS_NDJSON_BYTES = 50 * 1024 * 1024\n\n/** A full-document anchor rather than an observed diff. `url` is the reliable\n * discriminator: only the collector's `captureCurrentDom` sets it, and neither\n * the MutationObserver serializer nor the synthetic field-state records do. */\nfunction isDomAnchor(mutation: TraceMutation): boolean {\n return mutation.type === 'childList' && mutation.url !== undefined\n}\n\n/**\n * Re-stamp the full-document anchors in a freshly drained batch onto the action\n * that produced the document.\n *\n * An anchor carries the document's own birth time, which lands a few ms *after*\n * the command that navigated there — so the navigating action's row, which ends\n * the moment the command was logged, still resolves to the PREVIOUS page's DOM.\n *\n * `resolve` maps an anchor's own timestamp to the action it belongs to. Deriving\n * it from the anchor rather than from whichever drain collected it is what makes\n * this race-free: several drains compete for a fresh collector's buffer (the\n * first `getTraceData` empties it) and the winner is routinely a later command's\n * post-DOM drain, so trusting the collecting drain mis-credited the anchor.\n *\n * `floor` is the newest timestamp already in the accumulated stream. An anchor is\n * never pulled ahead of mutations belonging to the document it replaces — replay\n * would then apply those stale refs to the new tree — and never pushed later than\n * the page stamped it, so an overshooting attribution is simply ignored.\n */\nexport function reattributeDomAnchors(\n batch: TraceMutation[],\n resolve: (anchorOwnTime: number) => number | undefined,\n floor = 0\n): void {\n let lowerBound = floor\n for (const mutation of batch) {\n if (isDomAnchor(mutation)) {\n const attributed = resolve(mutation.timestamp)\n if (attributed !== undefined) {\n const target = Math.max(attributed, lowerBound)\n if (target < mutation.timestamp) {\n mutation.timestamp = target\n }\n }\n }\n lowerBound = Math.max(lowerBound, mutation.timestamp)\n }\n}\n\nexport interface MutationsNdjsonResult {\n /** NDJSON payload (one mutation per line, optional trailing marker). Empty\n * buffer when there are no mutations. */\n ndjson: Buffer\n truncated: boolean\n /** Count actually written (excludes the dropped tail and the marker line). */\n written: number\n}\n\n/**\n * Serialize mutations to NDJSON under `cap` bytes, keeping the earliest. The\n * first mutation is always emitted (even if it alone exceeds the cap) so the\n * initial full-DOM snapshot is never lost; when any are dropped a\n * `MutationsTruncationMarker` line is appended.\n */\nexport function buildMutationsNdjson(\n mutations: readonly TraceMutation[],\n cap: number = MAX_MUTATIONS_NDJSON_BYTES\n): MutationsNdjsonResult {\n if (!mutations.length) {\n return { ndjson: Buffer.alloc(0), truncated: false, written: 0 }\n }\n const lines: string[] = []\n let bytes = 0\n for (const mutation of mutations) {\n const line = JSON.stringify(mutation)\n // +1 for the '\\n' that will join this line to the previous one.\n const add = Buffer.byteLength(line, 'utf8') + (lines.length ? 1 : 0)\n if (lines.length > 0 && bytes + add > cap) {\n break\n }\n lines.push(line)\n bytes += add\n }\n const written = lines.length\n const dropped = mutations.length - written\n if (dropped > 0) {\n const marker: MutationsTruncationMarker = { __truncated__: true, dropped }\n lines.push(JSON.stringify(marker))\n }\n return {\n ndjson: Buffer.from(lines.join('\\n'), 'utf8'),\n truncated: dropped > 0,\n written\n }\n}\n","// Builds the trace's `transcript.md` — a Markdown step list read by humans and\n// fed to an LLM. Runner-agnostic; the exporter writes whatever this returns.\n\nimport type { CommandLog } from '@wdio/devtools-shared'\nimport {\n formatActionTitle,\n mapCommandToAction,\n FILL_METHODS,\n type TraceAction\n} from './action-mapping.js'\nimport { stripAnsi } from './console.js'\n\n/** Render `text` as one numbered markdown list item, indenting every line after\n * the first to the marker's content column (`'1. '` → 3, `'10. '` → 4 — an\n * ordered list's continuation indent tracks the marker width, unlike a\n * bullet's fixed two). A step interpolates captured strings that may hold\n * newlines — a framework error routinely does (expect-webdriverio puts\n * `Expected:` and `Received:` on their own lines) and a typed value can too —\n * and an unindented tail leaves the list, reading as top-level prose\n * unattributed to the step that produced it. */\nfunction asNumberedItem(marker: string, text: string): string {\n const indent = ' '.repeat(marker.length)\n const [head, ...tail] = text.split('\\n')\n return [\n `${marker}${head}`,\n // A whitespace-only line stays empty: indenting it would only add trailing\n // whitespace, and a blank line inside an indented item is still inside it.\n ...tail.map((line) => (line.trim() ? `${indent}${line}` : ''))\n ].join('\\n')\n}\n\nfunction errorMessage(error: NonNullable<CommandLog['error']>): string {\n const raw =\n typeof error === 'object' && error !== null && 'message' in error\n ? String((error as { message: unknown }).message)\n : String(error)\n // Runner errors carry terminal colour (node's AssertionError diff is\n // colour-coded), which is noise in a document read by a model.\n return stripAnsi(raw).trim()\n}\n\n/** Commands that map to a trace action, in invocation order. */\nfunction capturedSteps(\n commands: CommandLog[]\n): { entry: CommandLog; action: TraceAction }[] {\n // Sort by invocation time so batched commands land at their real timeline\n // positions — Nightwatch buffers native asserts and emits them at test-end,\n // so raw order clusters all asserts after the navigations. The Actions tree\n // stays correct because buildActionEvents applies the same sort; mirror it\n // here so the transcript matches execution order. Stable + a no-op for\n // already-ordered WDIO/Selenium command logs.\n const ordered = [...commands].sort(\n (a, b) =>\n (a.startTime ?? a.timestamp) - (b.startTime ?? b.timestamp) ||\n (a.sequence ?? 0) - (b.sequence ?? 0)\n )\n const captured: { entry: CommandLog; action: TraceAction }[] = []\n for (const c of ordered) {\n const action = mapCommandToAction(String(c.command))\n if (action) {\n captured.push({ entry: c, action })\n }\n }\n return captured\n}\n\n/**\n * Generate a human/LLM-readable Markdown transcript from captured commands.\n */\nexport function 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 capturedSteps(commands).forEach(({ entry, action }, idx) => {\n const rawArgs = entry.args as unknown[]\n const parts: string[] = [stripAnsi(formatActionTitle(action, rawArgs))]\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=\"${stripAnsi(String(rawArgs[valueIdx]))}\"`)\n }\n }\n\n if (entry.error) {\n parts.push(`ERROR: ${errorMessage(entry.error)}`)\n }\n\n lines.push(asNumberedItem(`${idx + 1}. `, parts.join(' ')))\n })\n\n return lines.join('\\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 path from 'node:path'\nimport type {\n ActionSnapshot,\n CommandLog,\n ScreencastFrame,\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, isStepUidOf } from './uid.js'\nimport { trimChar } from './artifact-naming.js'\n\n// ─── SpecRange ────────────────────────────────────────────────────────────────\n\n/** Index ranges into a SessionCapturer's flat arrays for one trace slice\n * (a spec file, or a single test under `test` granularity). */\nexport interface SpecRange {\n specFile: string\n /** Dedupe/identity key: spec path for spec slices; testUid for test slices,\n * or `${testUid}-retry${n}` so each retried attempt is its own slice. */\n key: string\n /** Present only for test-granularity slices; the base (non-retry) testUid. */\n testUid?: string\n commandStartIdx: number\n consoleStartIdx: number\n networkStartIdx: number\n mutationStartIdx: number\n traceLogStartIdx: number\n}\n\n/**\n * The slice range to eager-flush for a just-ended test. When the caller knows\n * the test's uid (e.g. WDIO's `afterTest`), reverse-scan for it — retries push\n * a new range under the same uid, and the next test's boundary may already be\n * recorded, so the last range isn't reliably this test's. When the uid isn't\n * independently known (Nightwatch/Selenium discover it from the range itself),\n * the last recorded range is the just-ended test's. Undefined when there is no\n * range to flush. One helper for what the three adapters each open-coded.\n */\nexport function findFlushableRange(\n ranges: readonly SpecRange[],\n testUid?: string\n): SpecRange | undefined {\n if (testUid !== undefined) {\n for (let i = ranges.length - 1; i >= 0; i--) {\n if (ranges[i]!.testUid === testUid) {\n return ranges[i]\n }\n }\n return undefined\n }\n return ranges[ranges.length - 1]\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 const cleaned = trimChar(\n specFile\n .replace(/^.*[/\\\\]/, '')\n .replace(/\\.[^.]+$/, '')\n .replace(/[^a-zA-Z0-9_-]/g, '_'),\n '_'\n )\n return cleaned || 'unknown-spec'\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// ─── Test slice session ID ──────────────────────────────────────────────────\n\n/**\n * Build a collision-safe test-level session ID from the slice's spec file, its\n * identity `key` (testUid, or `${testUid}-retry${n}` for retries), and the\n * parent session ID. Hashing the key keeps retries and sibling tests in the\n * same spec from colliding on filename, while the spec basename keeps the name\n * human-readable.\n */\nexport function buildTestSliceSessionId(\n specFile: string,\n key: string,\n sessionId: string\n): string {\n const base = sanitizeSpecName(specFile)\n const hash = deterministicUid(key).split('-').pop()!.slice(0, 8)\n return `${base}-${hash}-${sessionId.slice(0, 8)}`\n}\n\n// ─── Test slice output folder ────────────────────────────────────────────────\n\n/** Max slug length so a title doesn't blow past filesystem path limits. */\nconst MAX_SLUG_LENGTH = 60\n\n/** Lowercase, collapse runs of non-alphanumerics to `-`, trim edge dashes,\n * and cap length (trimming a dash the cut may leave behind). */\nfunction slugify(text: string): string {\n const collapsed = trimChar(\n text.toLowerCase().replace(/[^a-z0-9]+/g, '-'),\n '-'\n )\n // Trim again after the length cap in case it sliced mid-dash-run.\n return trimChar(collapsed.slice(0, MAX_SLUG_LENGTH), '-')\n}\n\n/**\n * Build the per-test output folder name: `<spec>-<title>-<browser>[-retryN]`.\n * The spec basename is sanitized via {@link sanitizeSpecName}; title and browser\n * are slugified. An empty title falls back to a short hash of `key`, and a\n * `${uid}-retry${n}` key appends a `-retry<N>` suffix so retries don't collide.\n */\nexport function buildTestSliceFolder(\n specFile: string,\n testTitle: string | undefined,\n browser: string | undefined,\n key: string\n): string {\n const specBase = sanitizeSpecName(path.basename(specFile))\n // A short hash of the slice key disambiguates two tests that share a title\n // (Scenario Outline examples with a placeholder-free name, or duplicate `it`\n // titles) — without it their folders collide and one trace overwrites the\n // other. Mirrors buildTestSliceSessionId, which hashes the key for the id.\n const keyHash = deterministicUid(key).split('-').pop()!.slice(0, 8)\n const titleSlug = slugify(testTitle ?? '') || keyHash\n const browserSlug = slugify(browser ?? '') || 'browser'\n const retryMatch = key.match(/-retry(\\d+)$/)\n const retrySuffix = retryMatch ? `-retry${retryMatch[1]}` : ''\n return `${specBase}-${titleSlug}-${browserSlug}-${keyHash}${retrySuffix}`\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/**\n * Filter a full `testUid → metadata` map down to a single test's entry and its\n * step entries. The per-test analog of {@link filterTestMetadataBySpec}. The\n * steps have to come along: `buildGroupPath` names each step group from this\n * map and falls back to the raw uid when the entry is missing, so dropping them\n * renders a scenario's steps as `stable-…:step:1` instead of their Gherkin text.\n */\nexport function filterTestMetadataByUid(\n allMetadata: TestMetadataMap,\n testUid: string\n): TestMetadataMap {\n const filtered: TestMetadataMap = new Map()\n const entry = allMetadata.get(testUid)\n if (entry) {\n filtered.set(testUid, entry)\n }\n for (const [uid, meta] of allMetadata) {\n if (isStepUidOf(uid, testUid)) {\n filtered.set(uid, meta)\n }\n }\n return filtered\n}\n\n// ─── Spec boundary recording ──────────────────────────────────────────────────\n\n/**\n * Minimal context needed by `recordSliceBoundary` to detect spec-file / test\n * transitions and capture array index ranges. `flushedSpecs` holds already-\n * flushed slice keys (spec paths or test keys), shared with the finalizer.\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}\n\n/** Push a new slice range and return the previous (unflushed) range to flush.\n * `suppressSameKey` skips recording when the incoming key matches the last\n * range's — used for spec granularity so consecutive tests in one file share\n * a slice; test granularity records every attempt (retries included). */\nfunction pushSliceRange(\n ctx: SpecBoundaryContext,\n specFile: string,\n key: string,\n testUid: string | undefined,\n suppressSameKey: boolean\n): SpecRange | null {\n const lastRange = ctx.specRanges[ctx.specRanges.length - 1]\n if (suppressSameKey && lastRange && lastRange.key === key) {\n return null\n }\n const prevRange =\n lastRange && !ctx.flushedSpecs.has(lastRange.key) ? lastRange : null\n\n ctx.specRanges.push({\n specFile,\n key,\n testUid,\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 })\n\n return prevRange\n}\n\n/**\n * Record a trace-slice boundary. For `spec` granularity, a new slice starts\n * when the spec file changes (existing behavior). For `test` granularity, a\n * new slice starts on every recorded test — including retries: a repeated\n * `testUid` is keyed `${testUid}-retry${n}` so each attempt is its own slice.\n * Returns the previous, not-yet-flushed range so the caller can flush it, or\n * `null` when nothing needs flushing (same spec, missing testUid, or a\n * non-sliced granularity).\n */\nexport function recordSliceBoundary(\n ctx: SpecBoundaryContext,\n granularity: TraceGranularity | undefined,\n specFile: string,\n testUid?: string\n): SpecRange | null {\n if (granularity === 'spec') {\n return pushSliceRange(ctx, specFile, specFile, undefined, true)\n }\n if (granularity === 'test' && testUid !== undefined) {\n const priorAttempts = ctx.specRanges.filter(\n (r) => r.testUid === testUid\n ).length\n const key =\n priorAttempts === 0 ? testUid : `${testUid}-retry${priorAttempts}`\n return pushSliceRange(ctx, specFile, key, testUid, false)\n }\n return null\n}\n\n/**\n * Record a spec-file boundary. Thin back-compat wrapper over\n * {@link recordSliceBoundary}; behavior is unchanged for `spec` granularity\n * and returns `null` for every other granularity.\n */\nexport function recordSpecBoundary(\n ctx: SpecBoundaryContext,\n specFile: string,\n traceGranularity: TraceGranularity | undefined\n): SpecRange | null {\n return recordSliceBoundary(ctx, traceGranularity, specFile)\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 /** Full session frame buffer; windowed to this slice's wall-clock span. */\n screencastFrames?: readonly ScreencastFrame[]\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/** Timestamped items inside a slice's wall-clock window `[start, end)`. An\n * undefined `start` (a slice with nothing to anchor on) yields nothing; an\n * undefined `end` (the last slice) is open-ended, so a trailing assertion-wait\n * stays with its test. Used for both action snapshots and screencast frames so\n * they partition on the same reliable boundary as the sliced commands. */\nfunction sliceByWindow<T extends { timestamp: number }>(\n items: readonly T[],\n start: number | undefined,\n end: number | undefined\n): T[] {\n if (start === undefined) {\n return []\n }\n return items.filter(\n (i) => i.timestamp >= start && (end === undefined || i.timestamp < end)\n )\n}\n\n/** Slice the parent capturer/snapshots for one range and write the artifact\n * under `sliceSessionId` with the pre-filtered `testMetadata`. Shared by the\n * spec and test write paths so both slice identically. `overrides` lets the\n * test path redirect into a named folder with a fixed `trace` file stem. */\nasync function writeSliceTrace(\n input: WriteSpecTraceInput,\n sliceSessionId: string,\n testMetadata: TestMetadataMap,\n overrides: { outputDir?: string; fileStem?: string } = {}\n): Promise<string> {\n const sliceCapturer = buildSpecCapturer(\n input.capturer,\n input.range,\n input.nextRange\n )\n\n // Anchor the slice on its own commands, which buildSpecCapturer index-slices\n // reliably — parent-array index lookups desync under reloadSession and would\n // sweep an earlier test's snapshots/frames in. Snapshots and frames are then\n // windowed by that wall-clock span, and the slice is rebased to its own start\n // (start === wallTime) so a per-test trace begins at 0, not the session\n // offset. `windowEnd` is the next slice's start; open for the last. Anchor on\n // the command's `startTime` (invocation), not `timestamp` (completion), so the\n // frames captured *during* the first action — e.g. the page load of an opening\n // `url()` — fall inside this slice rather than being dropped or bleeding into\n // the previous one.\n const commandStart = (c: CommandLog | undefined): number | undefined =>\n c ? (c.startTime ?? c.timestamp) : undefined\n // A command-less slice (assertion-only test) has no command to anchor on; fall\n // back to the earliest of its own index-sliced console/network timestamps so\n // its snapshots/frames still window+rebase rather than drop to the session\n // offset. Empty of both keeps today's undefined → no-window behavior.\n // Both are epoch ms (Date.now), matching the command anchor and the frame/\n // snapshot clock — NOT NetworkRequest.startTime, which is performance.now\n // (relative ms) and would anchor the window ~54,000 years off.\n const fallbackStart = (): number | undefined => {\n const times = [\n ...sliceCapturer.consoleLogs.map((c) => c.timestamp),\n ...sliceCapturer.networkRequests.map((n) => n.timestamp)\n ]\n return times.length ? Math.min(...times) : undefined\n }\n const windowStart =\n commandStart(sliceCapturer.commandsLog[0]) ?? fallbackStart()\n const windowEnd = input.nextRange\n ? commandStart(input.capturer.commandsLog[input.nextRange.commandStartIdx])\n : undefined\n\n const sliceSnapshots = sliceByWindow(\n input.actionSnapshots,\n windowStart,\n windowEnd\n )\n const sliceFrames = input.screencastFrames\n ? sliceByWindow(input.screencastFrames, windowStart, windowEnd)\n : undefined\n\n if (windowStart !== undefined) {\n sliceCapturer.startWallTime = windowStart\n }\n\n return writeTraceZip(sliceCapturer, {\n outputDir: overrides.outputDir ?? input.outputDir,\n sessionId: sliceSessionId,\n fileStem: overrides.fileStem,\n capabilities: input.capabilities,\n actionSnapshots: sliceSnapshots.length > 0 ? sliceSnapshots : undefined,\n screencastFrames: sliceFrames?.length ? sliceFrames : undefined,\n format: input.format,\n testMetadata\n })\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 return writeSliceTrace(\n input,\n buildSpecSessionId(input.range.specFile, input.sessionId),\n filterTestMetadataBySpec(input.testMetadata, input.range.specFile)\n )\n}\n\n/**\n * Write a standalone trace artifact for a single test slice into its own\n * folder: `<outputDir>/<specBasename>-<titleSlug>-<browserSlug>[-retryN]/trace.zip`.\n * Reuses {@link WriteSpecTraceInput}; the folder is the slice's external\n * identity (title/browser/retry), while {@link buildTestSliceSessionId} names\n * the sessionId embedded inside the archive.\n */\nexport async function writeTestSliceTrace(\n input: WriteSpecTraceInput\n): Promise<string> {\n const testUid = input.range.testUid ?? input.range.key\n const title = input.testMetadata.get(testUid)?.title\n // capabilities is framework-typed unknown; read only browserName here.\n const browserName = (\n input.capabilities as { browserName?: string } | undefined\n )?.browserName\n const folder = buildTestSliceFolder(\n input.range.specFile,\n title,\n browserName,\n input.range.key\n )\n return writeSliceTrace(\n input,\n buildTestSliceSessionId(\n input.range.specFile,\n input.range.key,\n input.sessionId\n ),\n filterTestMetadataByUid(input.testMetadata, testUid),\n { outputDir: path.join(input.outputDir, folder), fileStem: 'trace' }\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\nconst STEP_UID_SEPARATOR = ':step:'\n\n/**\n * Key for one step (a Cucumber `Given`/`When`/`Then`) inside a test, derived\n * from the test's own uid plus a per-test index — repeated step text can't\n * collide. Derived rather than hashed so a step's owning test is recoverable\n * from the key alone, which is what {@link isStepUidOf} relies on.\n */\nexport function stepMetadataUid(testUid: string, index: number): string {\n return `${testUid}${STEP_UID_SEPARATOR}${index}`\n}\n\n/** True when `uid` is a step of `testUid`. Anchored on the full test uid plus\n * the separator, so a sibling test whose uid merely starts with the same\n * characters doesn't match. */\nexport function isStepUidOf(uid: string, testUid: string): boolean {\n return uid.startsWith(`${testUid}${STEP_UID_SEPARATOR}`)\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","/**\n * Framework-agnostic orchestration of the trace-mode export at end-of-run:\n * session vs per-spec fan-out, the spec-granularity-without-boundaries\n * fallback, retention gating, and per-write error isolation. The three\n * adapters assemble a TraceExportContext from their own state and call\n * finalizeTraceExport — all sequencing lives here.\n */\n\nimport type {\n ActionSnapshot,\n DevToolsMode,\n ScreencastFrame,\n TestMetadataMap,\n TraceFormat,\n TraceGranularity,\n TraceRetentionPolicy\n} from '@wdio/devtools-shared'\nimport { errorMessage } from './error.js'\nimport {\n buildArtifactsManifest,\n writeArtifactsManifest\n} from './artifacts-manifest.js'\nimport {\n filterTestMetadataBySpec,\n filterTestMetadataByUid,\n writeSpecTrace,\n writeTestSliceTrace,\n type SpecRange\n} from './spec-trace-helpers.js'\nimport { shouldRetainTrace, type TestOutcome } from './trace-retention.js'\nimport type { RetryOutcomeView } from './attempt-tracker.js'\nimport { writeTraceZip, type TraceCapturer } from './trace-exporter.js'\n\n/** One artifact produced (or, when `retained` is false, decided-against) by a\n * trace-mode run — a trace slice, a screencast video, or a per-test\n * screenshot. */\nexport interface TraceArtifact {\n kind: 'trace' | 'video' | 'screenshot'\n path: string\n scope: 'session' | 'spec' | 'test'\n /** specFile for spec scope, testUid for test scope. */\n key?: string\n testUids: string[]\n /** false = decided-not-to-write (reported, not written). Always true today. */\n retained: boolean\n}\n\nexport interface TraceExportContext {\n mode?: DevToolsMode\n /** undefined → treated as `on` (always retain) by the retention evaluator. */\n policy?: TraceRetentionPolicy\n /** True when the adapter fed real per-test attempt numbers (B4); retry-aware\n * policies degrade to retain-on-failure when this is false. */\n attemptInfoAvailable?: boolean\n /** Per-attempt outcome ledger. When present, retention is evaluated against\n * real per-attempt outcomes (scoped per session/spec/test) instead of the\n * collapsed final-attempt state in `testMetadata` — this is what lets\n * retain-on-first-failure see a failed-then-passed first attempt and stops\n * retain-on-failure over-retaining it. Absent → falls back to `testMetadata`\n * (unchanged behavior). */\n outcomes?: RetryOutcomeView\n granularity?: TraceGranularity\n format?: TraceFormat\n capturer: TraceCapturer\n actionSnapshots?: ActionSnapshot[]\n /** Continuous screencast frame buffer for the dense filmstrip (filmstrip\n * option on). Session write uses all; slice writes window per test. */\n screencastFrames?: readonly ScreencastFrame[]\n sessionId: string\n capabilities?: unknown\n testMetadata: TestMetadataMap\n /** Recorded spec boundaries; empty for session granularity. */\n ranges: SpecRange[]\n /** Spec-file dedupe set shared with the adapter's boundary flushes. */\n flushed: Set<string>\n /** Adapters keep their differing dir logic; range is set for spec writes. */\n resolveOutputDir: (range?: SpecRange) => string\n /** Pending snapshot captures to settle before writing (selenium/nightwatch). */\n awaitPending?: Promise<unknown>[]\n log?: (level: 'info' | 'warn', msg: string) => void\n onArtifact?: (a: TraceArtifact) => void\n /** When true, finalize writes a `devtools-artifacts-<sessionId>.json` manifest\n * enumerating every artifact (retained or not) plus per-test states, for\n * ecosystem reporters (Allure, CI collectors) to consume. */\n emitManifest?: boolean\n /** Every artifact seen via `onArtifact` across the run (including eager\n * mid-run test-slice flushes, which the end-of-run fan-out dedupes away).\n * Pass the same list the adapter accumulates so the manifest is complete;\n * finalize falls back to its own fan-out results when this is omitted. */\n collectedArtifacts?: readonly TraceArtifact[]\n}\n\nconst SPEC_WITHOUT_BOUNDARIES_WARNING =\n 'traceGranularity is \"spec\" but no spec boundaries were detected ' +\n '(the runner may not expose per-test hooks). Falling back to ' +\n 'session-level trace.'\n\nconst TEST_WITHOUT_BOUNDARIES_WARNING =\n 'traceGranularity is \"test\" but no test boundaries were detected ' +\n '(the runner may not expose per-test hooks). Falling back to ' +\n 'session-level trace.'\n\n/** Above this many slices, warn to pair granularity with a retention policy. */\nconst SLICE_COUNT_WARN_THRESHOLD = 200\n\n/** Cap on how long finalize waits for still-in-flight snapshot captures. A\n * fire-and-forget capture against a tearing-down session can hang forever\n * (never resolves nor rejects); blocking the export on it would deadlock the\n * whole run (finalize never returns → the runner never tears the session down\n * → the capture stays stuck). Past this bound we write whatever snapshots\n * resolved in time — the same \"flush from what exists\" rule slices use. */\nconst PENDING_SETTLE_TIMEOUT_MS = 5000\n\n/**\n * `Promise.allSettled` bounded by a timeout. Resolves when every pending\n * capture settles OR the cap elapses, whichever comes first — a stuck capture\n * can never hang the export. Returns whether it timed out so the caller can\n * warn. The timer is unref'd so it can't itself keep the process alive.\n */\nasync function settlePending(\n pending: Promise<unknown>[],\n timeoutMs: number\n): Promise<boolean> {\n let timer: ReturnType<typeof setTimeout> | undefined\n const timedOut = new Promise<true>((resolve) => {\n timer = setTimeout(() => resolve(true), timeoutMs)\n timer.unref?.()\n })\n const settled = Promise.allSettled(pending).then(() => false)\n const result = await Promise.race([settled, timedOut])\n if (timer) {\n clearTimeout(timer)\n }\n return result\n}\n\n/** Settle in-flight snapshot captures under the timeout cap, warning if the\n * bound elapses. Never throws; never hangs. */\nasync function awaitPendingCaptures(ctx: TraceExportContext): Promise<void> {\n if (!ctx.awaitPending?.length) {\n return\n }\n const timedOut = await settlePending(\n ctx.awaitPending,\n PENDING_SETTLE_TIMEOUT_MS\n )\n if (timedOut) {\n ctx.log?.(\n 'warn',\n `One or more of ${ctx.awaitPending.length} snapshot capture(s) did not ` +\n `settle within ${PENDING_SETTLE_TIMEOUT_MS}ms; writing trace with the ` +\n 'snapshots captured so far.'\n )\n }\n}\n\nfunction sliceCountWarning(count: number): string {\n return (\n `traceGranularity produced ${count} trace slices. Consider pairing it ` +\n 'with a retention policy (e.g. tracePolicy: \"retain-on-failure\") to ' +\n 'avoid writing hundreds of trace archives.'\n )\n}\n\n/** Project a metadata slice onto the retention evaluator's outcome shape. */\nfunction toOutcomes(metadata: TestMetadataMap): TestOutcome[] {\n return Array.from(metadata.values(), (m) => ({\n state: m.state,\n attempt: m.attempt\n }))\n}\n\n/**\n * Evaluate the retention policy for one trace slice. Adapters that feed real\n * per-test attempt numbers also set `attemptInfoAvailable`; when they don't,\n * retry-aware policies degrade to retain-on-failure (see trace-retention.ts).\n */\nfunction shouldRetain(\n ctx: TraceExportContext,\n metadata: TestMetadataMap,\n ledgerOutcomes?: TestOutcome[]\n): boolean {\n // An empty scoped ledger view (adapter fed outcomes but not for this scope)\n // falls back to metadata rather than fail-opening the evaluator into retaining\n // everything — only a genuinely empty metadata slice fails open.\n return shouldRetainTrace(ctx.policy, {\n outcomes: ledgerOutcomes?.length ? ledgerOutcomes : toOutcomes(metadata),\n attemptInfoAvailable: ctx.attemptInfoAvailable ?? false\n }).retain\n}\n\n/** Attempt number encoded in a test-slice key (`<uid>-retry<n>`); 0 when the\n * key is the base uid (the first attempt / no retry suffix). */\nfunction attemptFromKey(key: string): number {\n const match = /-retry(\\d+)$/.exec(key)\n return match ? Number(match[1]) : 0\n}\n\n/** Scope the per-attempt ledger to a slice: a test slice sees only its own\n * attempt (so a passing retry isn't retained on first-failure); a spec slice\n * sees every attempt of its tests. */\nfunction sliceOutcomeView(\n ctx: TraceExportContext,\n range: SpecRange,\n isTestSlice: boolean\n): TestOutcome[] | undefined {\n if (!ctx.outcomes) {\n return undefined\n }\n return isTestSlice\n ? ctx.outcomes.forTest(range.testUid!, attemptFromKey(range.key))\n : ctx.outcomes.forSpec(range.specFile)\n}\n\n/**\n * Policy-aware single-range flush: dedupes via `ctx.flushed` on the slice\n * `key`, applies the retention decision, and delegates the byte-level\n * slicing/naming to `writeSpecTrace` (spec slices) or `writeTestSliceTrace`\n * (test slices, distinguished by `range.testUid`). Returns the artifact, or\n * undefined when the range was already flushed.\n */\nexport async function flushRangeTrace(\n ctx: TraceExportContext,\n range: SpecRange,\n nextRange?: SpecRange\n): Promise<TraceArtifact | undefined> {\n // Eager mid-run flushes reach here directly, bypassing finalizeTraceExport's\n // gate, so enforce trace-mode here too — a live run must not write slices.\n if (ctx.mode !== 'trace') {\n return undefined\n }\n if (ctx.flushed.has(range.key)) {\n return undefined\n }\n ctx.flushed.add(range.key)\n\n const isTestSlice = range.testUid !== undefined\n const sliceMetadata = isTestSlice\n ? filterTestMetadataByUid(ctx.testMetadata, range.testUid!)\n : filterTestMetadataBySpec(ctx.testMetadata, range.specFile)\n const outcomes = sliceOutcomeView(ctx, range, isTestSlice)\n const artifact: TraceArtifact = {\n kind: 'trace',\n path: '',\n scope: isTestSlice ? 'test' : 'spec',\n key: range.key,\n testUids: Array.from(sliceMetadata.keys()),\n retained: shouldRetain(ctx, sliceMetadata, outcomes)\n }\n if (!artifact.retained) {\n ctx.onArtifact?.(artifact)\n return artifact\n }\n\n const writeSlice = isTestSlice ? writeTestSliceTrace : writeSpecTrace\n artifact.path = await writeSlice({\n range,\n nextRange,\n capturer: ctx.capturer,\n actionSnapshots: ctx.actionSnapshots ?? [],\n screencastFrames: ctx.screencastFrames,\n sessionId: ctx.sessionId,\n outputDir: ctx.resolveOutputDir(range),\n format: ctx.format,\n testMetadata: ctx.testMetadata,\n capabilities: ctx.capabilities\n })\n ctx.log?.(\n 'info',\n `Trace for ${isTestSlice ? 'test' : 'spec'} \"${range.key}\" saved to ${artifact.path}`\n )\n ctx.onArtifact?.(artifact)\n return artifact\n}\n\n/**\n * Flush one slice via {@link flushRangeTrace}, logging the shared spec/test\n * error string on failure so a failed boundary flush can't abort the next test.\n * All three adapters wrapped this identically; the label + identity are derived\n * from `range.testUid` (test slice → `test \"<key>\"`, else `spec \"<specFile>\"`),\n * so each call site keeps its exact message. Errors are logged and swallowed\n * (resolves `undefined`), so callers `await` it when the write must land before\n * a retry overwrites metadata, or fire-and-forget (`void`, or tracked in an\n * in-flight list) otherwise. Callers keep their own find-current-range strategy\n * and pass the resolved range in.\n */\nexport async function flushRangeLogged(\n ctx: TraceExportContext,\n range: SpecRange\n): Promise<TraceArtifact | undefined> {\n try {\n return await flushRangeTrace(ctx, range)\n } catch (err) {\n const label =\n range.testUid !== undefined\n ? `test \"${range.key}\"`\n : `spec \"${range.specFile}\"`\n ctx.log?.(\n 'warn',\n `Failed to flush trace for ${label}: ${errorMessage(err)}`\n )\n return undefined\n }\n}\n\nasync function writeSessionTrace(\n ctx: TraceExportContext\n): Promise<TraceArtifact | undefined> {\n const snapshots = ctx.actionSnapshots ?? []\n const artifact: TraceArtifact = {\n kind: 'trace',\n path: '',\n scope: 'session',\n testUids: Array.from(ctx.testMetadata.keys()),\n retained: shouldRetain(ctx, ctx.testMetadata, ctx.outcomes?.all())\n }\n if (!artifact.retained) {\n ctx.onArtifact?.(artifact)\n return artifact\n }\n\n artifact.path = await writeTraceZip(ctx.capturer, {\n outputDir: ctx.resolveOutputDir(),\n sessionId: ctx.sessionId,\n capabilities: ctx.capabilities,\n actionSnapshots: snapshots.length ? snapshots : undefined,\n screencastFrames: ctx.screencastFrames,\n format: ctx.format,\n testMetadata: ctx.testMetadata\n })\n ctx.log?.('info', `Trace saved to ${artifact.path}`)\n ctx.onArtifact?.(artifact)\n return artifact\n}\n\n/** Run one write, logging and swallowing its error so siblings still write. */\nasync function safely(\n ctx: TraceExportContext,\n write: () => Promise<TraceArtifact | undefined>\n): Promise<TraceArtifact | undefined> {\n try {\n return await write()\n } catch (err) {\n ctx.log?.('warn', `trace write failed: ${errorMessage(err)}`)\n return undefined\n }\n}\n\nasync function flushAllRanges(\n ctx: TraceExportContext\n): Promise<TraceArtifact[]> {\n const artifacts: TraceArtifact[] = []\n // Bound each slice by the next range's start indices; the final range (no\n // nextRange) runs to the end of the arrays. Without this, every slice would\n // run to the end and each test slice would swallow all later tests.\n for (let i = 0; i < ctx.ranges.length; i++) {\n const range = ctx.ranges[i]!\n const nextRange = ctx.ranges[i + 1]\n const artifact = await safely(ctx, () =>\n flushRangeTrace(ctx, range, nextRange)\n )\n if (artifact) {\n artifacts.push(artifact)\n }\n }\n return artifacts\n}\n\n/**\n * Entry point for the after/end-of-run hook. No-op outside trace mode. Settles\n * in-flight captures and eager flushes, then fans out to per-spec, per-test, or\n * session writes and finally the artifacts manifest. `spec`/`test` granularity\n * with no recorded boundaries warns and falls back to a single session-level\n * trace.\n */\nexport async function finalizeTraceExport(\n ctx: TraceExportContext\n): Promise<TraceArtifact[]> {\n if (ctx.mode !== 'trace') {\n return []\n }\n // Settle in-flight snapshot captures AND the adapters' tracked fire-and-forget\n // eager flushes (Selenium/Nightwatch put both in awaitPending) before writing\n // anything: every eager-flushed range is a synchronous dedupe-hit in the\n // fan-out below, so this is the one place that awaits those eager-flush\n // promises. Without it the last eager write can race teardown and its artifact\n // can miss the manifest. A no-op for WDIO (awaits eager flushes inline).\n await awaitPendingCaptures(ctx)\n const artifacts = await fanOutTraceWrites(ctx)\n await maybeWriteManifest(ctx, artifacts)\n return artifacts\n}\n\n/** Session vs per-slice fan-out (with the no-boundaries fallback). */\nasync function fanOutTraceWrites(\n ctx: TraceExportContext\n): Promise<TraceArtifact[]> {\n const sliced = ctx.granularity === 'spec' || ctx.granularity === 'test'\n if (sliced && ctx.ranges.length > 0) {\n if (ctx.ranges.length > SLICE_COUNT_WARN_THRESHOLD) {\n ctx.log?.('warn', sliceCountWarning(ctx.ranges.length))\n }\n return flushAllRanges(ctx)\n }\n if (ctx.granularity === 'spec') {\n ctx.log?.('warn', SPEC_WITHOUT_BOUNDARIES_WARNING)\n } else if (ctx.granularity === 'test') {\n ctx.log?.('warn', TEST_WITHOUT_BOUNDARIES_WARNING)\n }\n const artifact = await safely(ctx, () => writeSessionTrace(ctx))\n return artifact ? [artifact] : []\n}\n\n/** Write the artifacts manifest when enabled, preferring the adapter's full\n * collected list (includes eager slices) over this pass's fan-out results.\n * Never throws — a manifest failure must not abort the run. */\nasync function maybeWriteManifest(\n ctx: TraceExportContext,\n fanOutArtifacts: readonly TraceArtifact[]\n): Promise<void> {\n if (!ctx.emitManifest) {\n return\n }\n const artifacts = ctx.collectedArtifacts ?? fanOutArtifacts\n try {\n const path = await writeArtifactsManifest(\n ctx.resolveOutputDir(),\n buildArtifactsManifest({\n sessionId: ctx.sessionId,\n format: ctx.format ?? 'zip',\n artifacts,\n testMetadata: ctx.testMetadata\n })\n )\n ctx.log?.('info', `Artifacts manifest written to ${path}`)\n } catch (err) {\n ctx.log?.(\n 'warn',\n `Failed to write artifacts manifest: ${errorMessage(err)}`\n )\n }\n}\n","import fs from 'node:fs/promises'\nimport path from 'node:path'\nimport { createRequire } from 'node:module'\n\nimport { errorMessage } from './error.js'\n\nconst require = createRequire(import.meta.url)\n\n/** Browser-side expression that is true once the injected collector has\n * initialised. Shared by the adapters' readiness polls and re-injection\n * checks so the collector's global name lives in one place. */\nexport const COLLECTOR_READY_EXPRESSION =\n 'typeof window.wdioTraceCollector !== \"undefined\"'\n\n/**\n * Browser-side expression that drains the collector in one round-trip, atomic\n * against a navigation replacing the document mid-check.\n *\n * `forceAnchor` forces a full-DOM anchor of the CURRENT document before\n * draining. The collector's initial anchor is scheduled asynchronously (after\n * `waitForBody`), so a drain issued right after injecting on a navigation\n * destination beats it and returns nothing — and the destination's buffer then\n * dies with the page, leaving the navigating action's DOM absent entirely.\n *\n * Shared by the two adapters that eval the drain as a string; the WDIO service\n * passes an equivalent function to `browser.execute` instead.\n */\nexport function collectorDrainExpression(forceAnchor = false): string {\n const call = forceAnchor\n ? 'window.wdioTraceCollector.captureCurrentDom();'\n : ''\n return `if (!(${COLLECTOR_READY_EXPRESSION})) { return null; } ${call} return window.wdioTraceCollector.getTraceData();`\n}\n\n/** The collector bundle's raw source. Callers wrap it for their own injection\n * mechanism — an async IIFE for a `<script>` body, a function declaration for a\n * BiDi preload script. */\nexport async function loadCollectorSource(): Promise<string> {\n const scriptPath = require.resolve('@wdio/devtools-script')\n const scriptDir = path.dirname(scriptPath)\n return fs.readFile(path.join(scriptDir, 'script.js'), 'utf-8')\n}\n\n/**\n * The collector bundle wrapped in an async IIFE so its top-level `await` works\n * inside a regular `<script>` element body. Used for instrumenting the document\n * that is loaded RIGHT NOW; documents created later are covered by the BiDi\n * preload script (`registerCollectorPreload`), which takes the raw source.\n */\nexport async function loadInjectableScript(): Promise<string> {\n return `(async function() { ${await loadCollectorSource()} })()`\n}\n\n/** Schemes that carry no test-relevant DOM — the browser's own start page,\n * inline documents, and driver error pages. */\nconst NON_INSTRUMENTABLE_SCHEMES = [\n 'about:',\n 'data:',\n 'chrome:',\n 'chrome-error:',\n 'edge:',\n 'moz-extension:',\n 'chrome-extension:'\n]\n\nfunction isInstrumentableDocument(url: string | undefined): boolean {\n if (!url) {\n return false\n }\n return !NON_INSTRUMENTABLE_SCHEMES.some((scheme) => url.startsWith(scheme))\n}\n\n/**\n * Drain the page-side collector, re-injecting it into the CURRENT document and\n * retrying once when it isn't there.\n *\n * Both injection mechanisms (BiDi preload script, `<script>` append) only cover\n * documents created AFTER the injection, so a document that loads across a\n * session swap — `browser.reloadSession()`, nightwatch cucumber's per-scenario\n * re-navigation — carries no collector. Without one the drain returns nothing\n * and the dashboard replays the PREVIOUS document's DOM for every action on the\n * new page (empty forms, stale screenshots) with no error anywhere. Re-injecting\n * into the live document costs one round trip and its startup anchors the\n * current DOM, so the replay recovers from the next action onward.\n *\n * `drain` must resolve to the collector payload, or a falsy value when the\n * collector isn't reachable — the same `typeof === 'undefined' ? null` atomic\n * probe every adapter already uses. `currentUrl` is only consulted on a miss,\n * to keep recovery off documents that aren't worth instrumenting.\n */\nexport async function drainCollectorWithRecovery<T>(opts: {\n drain: () => Promise<T | null>\n injectIntoCurrentDocument: () => Promise<void>\n currentUrl?: () => Promise<string | undefined>\n log?: (level: 'info' | 'warn', message: string) => void\n}): Promise<T | null> {\n const payload = await opts.drain()\n if (payload) {\n return payload\n }\n if (opts.currentUrl) {\n const url = await opts.currentUrl().catch(() => undefined)\n if (!isInstrumentableDocument(url)) {\n // A session's pre-navigation page has no collector by design; anchoring it\n // would add a phantom \"document loaded\" row to every run. An unreadable\n // url means the session is gone — nothing to recover into either.\n return null\n }\n }\n opts.log?.('info', 'Collector missing on the current document, re-injecting')\n try {\n await opts.injectIntoCurrentDocument()\n } catch (err) {\n // Best-effort: a failed recovery must not also lose the caller's drain.\n opts.log?.('warn', `Collector re-injection failed: ${errorMessage(err)}`)\n return null\n }\n const recovered = await opts.drain()\n if (!recovered) {\n opts.log?.(\n 'warn',\n 'Collector still unreachable after re-injection — DOM capture is stale for this document'\n )\n }\n return recovered\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","// Document-start injection of the collector via a BiDi preload script.\n//\n// The `<script>`-append injection in `script-loader.ts` only ever instruments\n// the document that is loaded at the time it runs, and a `<script>` dies with\n// its document. So a navigation always produces a document we learn about\n// afterwards, and everything built on that — when to re-inject, when to drain,\n// which action to credit the new DOM to — is reconstruction, and races. A\n// preload script registered globally runs in EVERY document before any of its\n// script does, so each one instruments itself and anchors its own DOM at its own\n// `performance.timeOrigin`. Nothing to detect, nothing to attribute.\n\nimport { loadSeleniumSubmodule } from './bidi.js'\nimport { errorMessage } from './error.js'\nimport { loadCollectorSource } from './script-loader.js'\n\ninterface ScriptManager {\n addPreloadScript: (\n functionDeclaration: string,\n argumentValueList?: unknown[],\n sandbox?: string | null\n ) => Promise<number>\n}\n\n/** `selenium-webdriver/bidi/scriptManager`'s default export. Cast at this\n * boundary — the module ships no types. */\ntype ScriptManagerFactory = (\n browsingContextIds: unknown,\n driver: unknown\n) => Promise<ScriptManager>\n\n/**\n * Register the collector to run at document-start in every document of this\n * session. Returns false when BiDi isn't available, so the caller can fall back\n * to per-document `<script>` injection.\n *\n * Requires the session to have been created with `webSocketUrl: true`; the\n * script manager throws without it.\n */\nexport async function registerCollectorPreload(\n driver: unknown,\n log?: (level: 'info' | 'warn', message: string) => void\n): Promise<boolean> {\n const factory =\n loadSeleniumSubmodule<ScriptManagerFactory>('bidi/scriptManager')\n if (!factory) {\n return false\n }\n try {\n // No browsing-context id, which registers the script GLOBALLY — contexts\n // created later are covered too. Scoping it to the current context would\n // miss exactly the documents this exists to catch.\n const manager = await factory(undefined, driver)\n // Raw source, not the IIFE-wrapped form: a preload script is a function\n // declaration, so the bundle's top-level await works in the body directly.\n await manager.addPreloadScript(\n `async () => { ${await loadCollectorSource()} }`\n )\n log?.('info', '✓ Collector registered at document-start (BiDi preload)')\n return true\n } catch (err) {\n log?.(\n 'warn',\n `BiDi preload unavailable, falling back to per-document injection: ${errorMessage(err)}`\n )\n return false\n }\n}\n","/**\n * Rate-limits high-frequency identical terminal (stdout/stderr) lines so a\n * polling framework that reprints the same line every ~100ms doesn't flood the\n * trace's console lane. The motivating case: WDIO's logger writes a\n * `COMMAND`/`RESULT` frame for every WebDriver command, so an `expect` that\n * polls for its full 10s timeout emits hundreds of near-identical lines.\n *\n * Only terminal-source capture goes through this — user `console.*` is\n * captured as `source: 'test'` and never throttled, so real user output is\n * untouched. Distinct lines always pass immediately; a repeat is emitted at\n * most once per window.\n */\n\n/**\n * A repeated identical terminal line is emitted at most once per this window\n * (ms). Sized so a 100ms poll collapses ~10:1 while a human-paced reprint of\n * the same line (>1s apart) still shows every time.\n */\nexport const TERMINAL_REPEAT_WINDOW_MS = 1000\n\n/**\n * Leading ISO-8601 timestamp emitted by most structured loggers (`@wdio/logger`'s\n * `%t %l %n:` template, pino, winston, …). It's the only volatile part of\n * otherwise-identical successive log frames, so it's stripped from the throttle\n * key — never from the emitted text.\n */\nconst LEADING_ISO_TIMESTAMP_RE =\n /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d\\d\\d)?Z {1,4}/\n\n/** Key a terminal line for repeat-detection: drop a leading ISO timestamp so\n * successive log frames of the same message collapse. */\nexport function terminalRepeatKey(line: string): string {\n return line.replace(LEADING_ISO_TIMESTAMP_RE, '')\n}\n\nexport class TerminalLineThrottle {\n #lastEmitted = new Map<string, number>()\n readonly #windowMs: number\n\n constructor(windowMs: number = TERMINAL_REPEAT_WINDOW_MS) {\n this.#windowMs = windowMs\n }\n\n /**\n * True if the line should be emitted, false if it's a within-window repeat of\n * a line already emitted. The window is anchored to the last *emit*, not the\n * last occurrence, so a sustained stream of one line still emits once per\n * window instead of going silent after the first. Expired keys are pruned on\n * each emit, so state stays bounded by the number of distinct lines seen\n * within the window.\n */\n shouldEmit(line: string, now: number = Date.now()): boolean {\n const key = terminalRepeatKey(line)\n const last = this.#lastEmitted.get(key)\n if (last !== undefined && now - last < this.#windowMs) {\n return false\n }\n this.#lastEmitted.set(key, now)\n this.#prune(now)\n return true\n }\n\n #prune(now: number): void {\n for (const [key, ts] of this.#lastEmitted) {\n if (now - ts >= this.#windowMs) {\n this.#lastEmitted.delete(key)\n }\n }\n }\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 // Create the (test-results) dir if absent, then confirm it's writable;\n // fall back to tmpdir on any failure so a bad path never aborts the run.\n fs.mkdirSync(candidate, { recursive: true })\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","import { isInputDispatchingCommand } from '@wdio/devtools-shared'\n\n// A screenshot landing inside chromedriver's click — after it computed the\n// element's coordinates, before it dispatches at them — makes the click report\n// success while activating nothing (CLAUDE.md § Known debt has the measurements).\n// One instance per adapter bundle, not per session: both halves are module-scoped\n// and must be imported from the same bundle to see the same gate.\n\n/** Bound on one command's suppression: a failing Nightwatch test discards its\n * command queue, so the callback that closes a window may never fire. */\nexport const INPUT_DISPATCH_MAX_HOLD_MS = 3000\n\nconst NOOP = (): void => {}\n\nexport class InputDispatchGate {\n readonly #openedAt = new Map<number, number>()\n #nextToken = 0\n\n constructor(private readonly maxHoldMs = INPUT_DISPATCH_MAX_HOLD_MS) {}\n\n /**\n * Open a suppression window for `command` and return its closer. Always\n * returns a callable so call sites need no branch — for a command that\n * dispatches no input the closer is a no-op and nothing is tracked.\n */\n open(command: string): () => void {\n if (!isInputDispatchingCommand(command)) {\n return NOOP\n }\n const token = this.#nextToken++\n this.#openedAt.set(token, Date.now())\n return () => {\n this.#openedAt.delete(token)\n }\n }\n\n /**\n * Whether an unexpired window is open. Expired windows are dropped here, so a\n * closer that never runs costs one bounded stall rather than a permanent one\n * and cannot leak entries for the rest of the run.\n */\n isOpen(): boolean {\n const cutoff = Date.now() - this.maxHoldMs\n for (const [token, openedAt] of this.#openedAt) {\n if (openedAt <= cutoff) {\n this.#openedAt.delete(token)\n }\n }\n return this.#openedAt.size > 0\n }\n}\n\nconst sharedGate = new InputDispatchGate()\n\n/** Adapter side: open the window around a command. Returns its closer. */\nexport function beginInputDispatch(command: string): () => void {\n return sharedGate.open(command)\n}\n\n/** Recorder side: is a driver command dispatching input right now? */\nexport function isInputDispatchInFlight(): boolean {\n return sharedGate.isOpen()\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\n/** All run output is grouped under this subfolder. */\nconst OUTPUT_SUBDIR = 'test-results'\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.). Every artifact is grouped under a\n * single `test-results/` subfolder so a run's output is\n * self-contained regardless of where the base directory resolves to.\n *\n * The base directory is resolved by 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 return path.join(resolveBaseDir(input), OUTPUT_SUBDIR)\n}\n\nfunction resolveBaseDir(input: ResolveAdapterOutputDirInput): 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","// Value → locator provenance: which element's read produced a given value.\n//\n// node:assert takes VALUES, not elements — `assert.equal(await el.getText(), …)`\n// names no element — so an assertion row can only name its target if something\n// remembers which element each read value came from. Adapters feed this from\n// their command path (where a read's result and its selector are both visible)\n// and read it back when a patched assert fires; the answer lands on\n// `CommandLog.selector` so the player's overlay boxes the assertion's subject.\n//\n// Framework-agnostic by construction: it stores primitives and selector strings\n// only. The complementary handle → locator map (an assert taking the element\n// object itself) is Selenium-specific and stays in that adapter.\n\n/** Bounds the registry. A run reads far more values than any assertion looks\n * back over, and only the recent ones can be an assert's subject. */\nconst MAX_READ_VALUES = 200\n\n/** Values longer than this are never an assertion's subject, and keying a map on\n * one costs real memory — a page source or a base64 screenshot is a command\n * result too. */\nconst MAX_READ_VALUE_LENGTH = 256\n\n// Value → the locator of the element whose read produced it, or null when the\n// most recent producer was a driver-level read that no element owns. The null is\n// load-bearing: a page-title assertion would otherwise inherit the locator of an\n// element that happened to have the same text (measured on the selenium mocha\n// example, where `h1` and the document title are both \"Example Domain\").\nconst selectorByReadValue = new Map<unknown, string | null>()\n\n// Only primitives: an object result is compared by identity, which no assertion\n// on a read value relies on, and holding one would pin it for the run.\nfunction isRememberableValue(value: unknown): boolean {\n if (typeof value === 'string') {\n return value.length > 0 && value.length <= MAX_READ_VALUE_LENGTH\n }\n return typeof value === 'number' || typeof value === 'boolean'\n}\n\n/** Attribute a command result to the element it was read from, so an assertion\n * on that value can name the element. Pass `selector: undefined` for a\n * driver-level read — that records the value as belonging to no element rather\n * than leaving an older element's claim standing. */\nexport function rememberReadValue(\n value: unknown,\n selector: string | undefined\n): void {\n if (!isRememberableValue(value)) {\n return\n }\n // Delete before set so map order is recency order: the newest producer of a\n // value wins the lookup, and the least recent is what eviction drops.\n selectorByReadValue.delete(value)\n selectorByReadValue.set(value, selector ?? null)\n if (selectorByReadValue.size > MAX_READ_VALUES) {\n const oldest = selectorByReadValue.keys().next()\n if (!oldest.done) {\n selectorByReadValue.delete(oldest.value)\n }\n }\n}\n\n/** The element locator this value was read from — undefined when an element read\n * wasn't its most recent producer. */\nexport function selectorForReadValue(value: unknown): string | undefined {\n return selectorByReadValue.get(value) ?? undefined\n}\n\n/**\n * The element an assert was about, from its RAW args (sanitizing loses the\n * object identity a handle is recognised by). First arg that resolves wins —\n * node:assert puts the subject first. `fromHandle` lets an adapter that also\n * tracks element handles answer for an element passed straight to the assert;\n * adapters whose asserts only ever see values omit it.\n */\nexport function resolveAssertTargetFromArgs(\n args: unknown[],\n fromHandle?: (arg: unknown) => string | undefined\n): string | undefined {\n for (const arg of args) {\n const selector = fromHandle?.(arg) ?? selectorForReadValue(arg)\n if (selector) {\n return selector\n }\n }\n return undefined\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","/**\n * Identity for one test run, shared by every process that reports into it.\n * The backend compares it across worker connects to tell \"next spec of this\n * run\" (keep the accumulated state) from \"a new run\" (wipe it).\n */\n\nimport { randomUUID } from 'node:crypto'\nimport { RUNNER_ENV } from '@wdio/devtools-shared'\n\n/**\n * This run's id, generating and publishing one on first call. Runners fork\n * workers with the launcher's environment, so a launcher that calls this\n * before the run starts gives every worker the same id; a worker that gets\n * here first can only speak for itself, and each sibling reads as its own run.\n */\nexport function resolveRunId(): string {\n const existing = process.env[RUNNER_ENV.RUN_ID]\n if (existing) {\n return existing\n }\n const runId = randomUUID()\n process.env[RUNNER_ENV.RUN_ID] = runId\n return runId\n}\n","import type { ScreencastFrame, ScreencastOptions } from '@wdio/devtools-shared'\nimport { SCREENCAST_DEFAULTS } from '@wdio/devtools-shared'\nimport { isInputDispatchInFlight } from './input-dispatch.js'\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.#appendFrame({ data, timestamp })\n }\n\n /**\n * Append a frame, decimating the buffer in place once it exceeds\n * `maxBufferFrames` so a long session can't grow it without bound.\n */\n #appendFrame(frame: ScreencastFrame): void {\n this.buffer.push(frame)\n // Decimation always keeps the first and last frame, so 2 is the hard floor;\n // clamp so a nonsensical sub-2 cap is honored as 2 rather than never firing.\n if (this.buffer.length > Math.max(2, this.options.maxBufferFrames)) {\n this.#decimateBuffer()\n }\n }\n\n /**\n * Halve the buffer by keeping every other frame plus the first and last, so\n * memory is bounded while the temporal spread survives (never tail-truncated).\n * `#startIndex` is remapped to the count of surviving pre-marker frames, which\n * keeps `frames`/`duration` excluding pre-marker frames across decimation.\n */\n #decimateBuffer(): void {\n const lastIndex = this.buffer.length - 1\n const kept: ScreencastFrame[] = []\n let preMarkerKept = 0\n for (let i = 0; i < this.buffer.length; i++) {\n if (i % 2 === 0 || i === lastIndex) {\n if (i < this.#startIndex) {\n preMarkerKept++\n }\n kept.push(this.buffer[i])\n }\n }\n this.buffer = kept\n this.#startIndex = preMarkerKept\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.#appendFrame({ data: first, timestamp: Date.now() })\n\n const intervalMs = this.options.pollIntervalMs\n this.#pollTimer = setInterval(async () => {\n // A screenshot that reaches the driver mid-click makes the click report\n // success while dispatching nothing, so the page never navigates. Drop\n // the tick rather than race it — the gate is bounded, so a command whose\n // completion never arrives costs frames only until it expires.\n if (isInputDispatchInFlight()) {\n return\n }\n try {\n const data = await this.takeScreenshot()\n if (data !== null) {\n this.#appendFrame({ 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 { WebSocket } from 'ws'\nimport type {\n CommandLog,\n ConsoleLog,\n LogLevel,\n LogSource,\n Metadata,\n NetworkRequest,\n SerializedError,\n TraceMutation\n} from '@wdio/devtools-shared'\nimport { WORKER_WS_QUERY, WS_PATHS, WS_SCOPE } from '@wdio/devtools-shared'\nimport { mapCommandToAction } from './action-mapping.js'\nimport { resolveRunId } from './run-id.js'\nimport { reattributeDomAnchors } from './trace-mutations.js'\nimport {\n CONSOLE_METHODS,\n LOG_SOURCES,\n SPINNER_RE,\n createConsoleLogEntry,\n detectLogLevel,\n isInternalStreamLine,\n stripAnsi\n} from './console.js'\nimport { TerminalLineThrottle } from './terminal-throttle.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. Every socket also carries the run id,\n * which covers the same-run case a process can't flag: the next spec's worker.\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 // Collapses high-frequency identical terminal lines (e.g. WDIO's per-command\n // COMMAND/RESULT logger frames reprinted every ~100ms during an expect poll)\n // so they don't flood the console lane. Terminal-source only — user\n // console.* is source='test' and never throttled.\n #terminalThrottle = new TerminalLineThrottle()\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 = new URLSearchParams({\n [WORKER_WS_QUERY.runId]: resolveRunId(),\n ...(reconnect ? { [WORKER_WS_QUERY.reconnect]: '1' } : {})\n })\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 * Mark the most recent user action of a test as failed — for framework\n * failures that aren't captured as their own command. Broadcasts the swap so\n * live mode highlights it too. Returns false when no eligible action is found,\n * OR when that most-recent action already carries an error: the failure is\n * then already represented (e.g. an expect matcher captured as its own row via\n * afterAssertion), so it must not bleed onto an earlier *passing* action.\n */\n failLastAction(testUid: string | undefined, error: SerializedError): boolean {\n for (let i = this.commandsLog.length - 1; i >= 0; i--) {\n const command = this.commandsLog[i]\n if (!mapCommandToAction(command.command)) {\n continue\n }\n if (testUid && command.testUid !== testUid) {\n continue\n }\n // First (most-recent) action for this test. If it's already failed, the\n // failure is captured — stop rather than marking an earlier passing one.\n if (command.error) {\n return false\n }\n command.error = error\n this.sendReplaceCommand(command.timestamp, command)\n return true\n }\n return false\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 * The action a document born at `time` belongs to, or undefined when the\n * anchor already sits where it should and must be left alone.\n *\n * Derived from the anchor rather than from the draining call, because the\n * post-command hooks fire at command START in some adapters: a navigation's\n * own drain then runs against the document it is leaving, and whichever later\n * drain does see the destination would otherwise be credited with it.\n *\n * Answers only when NO logged command completed after `time`. A command that\n * completed after the document was born is the one that navigated there, and\n * its own row already resolves the anchor — pulling the anchor earlier then\n * mis-credits it to a preceding action and steals the DOM from rows that were\n * genuinely still on the previous page. That is the whole difference between\n * an adapter that stamps commands at invocation and one that stamps them at\n * completion, so it stays a property of the data rather than a per-adapter flag.\n */\n protected anchorOwnerTimestamp(time: number): number | undefined {\n let best: number | undefined\n for (const cmd of this.commandsLog) {\n if (cmd.timestamp > time) {\n return undefined\n }\n if (best === undefined || cmd.timestamp > best) {\n best = cmd.timestamp\n }\n }\n return best\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: {\n skipConsoleLogs?: boolean\n skipNetworkRequests?: boolean\n /** The command this batch's anchors were CAUSED by, when the caller\n * observed the document being replaced right after it. Causal knowledge,\n * so it overrides the timestamp inference below. */\n anchorTimestamp?: number\n } = {}\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 const batch = mutations as TraceMutation[]\n reattributeDomAnchors(\n batch,\n (anchorOwnTime) =>\n opts.anchorTimestamp ?? this.anchorOwnerTimestamp(anchorOwnTime),\n this.mutations[this.mutations.length - 1]?.timestamp ?? 0\n )\n this.mutations.push(...batch)\n this.sendUpstream('mutations', batch)\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 !this.#terminalThrottle.shouldEmit(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","import type {\n SuiteStats,\n TestAncestor,\n TestMetadataMap,\n TestStats,\n TestStatus\n} 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\nfunction deriveAncestorKind(\n suite: SuiteStats,\n parentKind: TestAncestor['kind'] | undefined\n): TestAncestor['kind'] {\n if (parentKind === 'feature') {\n return 'scenario'\n }\n // Cucumber scenario suites carry the same .feature file as their parent —\n // only a suite not already under a feature/scenario counts as the feature.\n if (suite.file?.endsWith('.feature') && parentKind !== 'scenario') {\n return 'feature'\n }\n return 'suite'\n}\n\n/**\n * Recursive walk over suite trees collecting every test's metadata entry —\n * uid → title/specFile/state/attempt plus the ancestor chain (outermost\n * first, the test's own node excluded). Consolidates the per-adapter\n * collectTestMetadata walks.\n */\nexport function collectSuiteTestMetadata(\n suites: Iterable<SuiteStats>\n): TestMetadataMap {\n const metadata: TestMetadataMap = new Map()\n const walk = (suite: SuiteStats, ancestors: TestAncestor[]): void => {\n const parentKind = ancestors[ancestors.length - 1]?.kind\n const chain: TestAncestor[] = [\n ...ancestors,\n {\n uid: suite.uid,\n title: suite.title,\n kind: deriveAncestorKind(suite, parentKind)\n }\n ]\n // A cucumber scenario is modelled as a SUITE, not a test — but it IS the\n // test unit: the attempt ledger and the per-test slice boundary both key on\n // its uid, and so does every command's testUid. Without an entry here the\n // trace's group fell back to rendering that raw uid as the group's name.\n // `state` comes along so retention keeps evaluating a real outcome.\n if (deriveAncestorKind(suite, parentKind) === 'scenario') {\n metadata.set(suite.uid, {\n title: suite.title,\n specFile: suite.file,\n state: suite.state,\n ancestry: ancestors\n })\n }\n for (const entry of suite.tests ?? []) {\n // Trees can contain string placeholders for tests not yet reconciled.\n if (typeof entry !== 'object' || entry === null) {\n continue\n }\n metadata.set(entry.uid, {\n title: entry.fullTitle || entry.title,\n specFile: entry.file ?? suite.file,\n state: entry.state,\n attempt: entry.retries ?? 0,\n ancestry: chain\n })\n }\n for (const child of suite.suites ?? []) {\n walk(child, chain)\n }\n }\n for (const suite of suites) {\n if (typeof suite !== 'object' || suite === null) {\n continue\n }\n walk(suite, [])\n }\n return metadata\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 * Assembles the framework-agnostic trace-export context from plugin state.\n *\n * Extracted from `NightwatchDevToolsPlugin.#traceContext` so the assembly is\n * unit-testable and to keep `index.ts` under the file-size cap. Both the\n * per-spec boundary flush and the final trace write share this one builder.\n */\n\nimport {\n collectSuiteTestMetadata,\n resolveAdapterOutputDir,\n type RetryOutcomeView,\n type SpecRange,\n type TraceArtifact,\n type TraceExportContext\n} from '@wdio/devtools-core'\nimport type { SessionCapturer } from './session.js'\nimport type {\n DevToolsMode,\n ScreencastFrame,\n SuiteStats,\n TraceFormat,\n TraceGranularity,\n TraceRetentionPolicy\n} from './types.js'\n\nexport interface TraceContextInput {\n mode: DevToolsMode\n policy: TraceRetentionPolicy\n granularity: TraceGranularity\n format: TraceFormat\n capturer: SessionCapturer\n suites: Iterable<SuiteStats>\n outcomes?: RetryOutcomeView\n ranges: SpecRange[]\n flushed: Set<string>\n artifacts: TraceArtifact[]\n traceFlushes: Promise<unknown>[]\n screencastFrames?: readonly ScreencastFrame[]\n configPath: string | undefined\n testFilePath: string | undefined\n emitArtifactsManifest?: boolean\n log: (level: 'info' | 'warn', msg: string) => void\n}\n\nexport function buildTraceContext(\n input: TraceContextInput,\n sessionId: string\n): TraceExportContext {\n return {\n mode: input.mode,\n policy: input.policy,\n granularity: input.granularity,\n format: input.format,\n capturer: input.capturer,\n actionSnapshots: input.capturer.actionSnapshots,\n sessionId,\n testMetadata: collectSuiteTestMetadata(input.suites),\n outcomes: input.outcomes,\n ranges: input.ranges,\n flushed: input.flushed,\n screencastFrames: input.screencastFrames,\n resolveOutputDir: () =>\n resolveAdapterOutputDir({\n testFilePath: input.testFilePath,\n configPath: input.configPath\n }),\n awaitPending: [...input.capturer.snapshotCaptures, ...input.traceFlushes],\n // Nightwatch feeds real per-test attempt numbers via TestAttemptTracker\n // (B4), so retry-aware policies use per-test attempts, not the fallback.\n attemptInfoAvailable: true,\n // Off by default; opt-in only — Nightwatch has no live Allure signal to\n // auto-detect against (produce-only Allure), unlike WDIO/Selenium.\n emitManifest: input.emitArtifactsManifest ?? false,\n collectedArtifacts: input.artifacts,\n onArtifact: (a) => input.artifacts.push(a),\n log: input.log\n }\n}\n","/**\n * Produce-only per-test artifacts (screenshot + video) for the Nightwatch\n * plugin.\n *\n * Nightwatch has no live Allure attach API — its official `nightwatch-allure`\n * reporter is post-hoc, and `allure-js-commons`' `attachment()` no-ops in a\n * Nightwatch run (nothing wires the global test runtime). So we pass NO sink\n * (`attach: undefined`), which the core treats as produce-only: it writes the\n * file and records the manifest artifact via `onArtifact`, but skips the attach.\n *\n * Both the per-test (Mocha/Jasmine `afterEach`) and cucumber (per-scenario\n * finalize) paths funnel through here so the two produce calls live once. Core\n * gates both to trace mode + `test` granularity, so this no-ops otherwise.\n */\n\nimport {\n captureAndAttachScreenshot,\n captureAndAttachVideo,\n lastRenderedScreenshot,\n type TestOutcome,\n type TraceArtifact\n} from '@wdio/devtools-core'\nimport type {\n ActionSnapshot,\n DevToolsMode,\n ScreencastFrame,\n TraceGranularity,\n TraceScreenshotPolicy,\n TraceVideoPolicy\n} from '@wdio/devtools-shared'\n\nexport interface EmitTestArtifactsInput {\n mode: DevToolsMode\n granularity: TraceGranularity\n screenshotPolicy: TraceScreenshotPolicy\n videoPolicy: TraceVideoPolicy\n failed: boolean\n actionSnapshots: readonly ActionSnapshot[]\n frames: readonly ScreencastFrame[]\n startWallTime: number\n /** This test's attempt slots (already scoped via `forTest`) for retention. */\n outcomes: TestOutcome[]\n uid: string | undefined\n attempt: number | undefined\n sessionId: string | undefined\n outputDir: string\n captureFormat?: 'jpeg' | 'png'\n onArtifact: (artifact: TraceArtifact) => void\n onLog?: (level: 'info' | 'warn', message: string) => void\n}\n\n/**\n * Produce this test's screenshot then its video slice, both with `attach:\n * undefined` (produce-only). Each core call self-gates on mode/granularity/\n * policy, so a non-trace run or a non-`test` granularity is a no-op.\n */\nexport async function emitTestArtifacts(\n input: EmitTestArtifactsInput\n): Promise<void> {\n await captureAndAttachScreenshot({\n mode: input.mode,\n granularity: input.granularity,\n policy: input.screenshotPolicy,\n failed: input.failed,\n screenshotBase64: lastRenderedScreenshot(\n input.actionSnapshots,\n input.startWallTime\n ),\n sessionId: input.sessionId,\n outputDir: input.outputDir,\n testUid: input.uid,\n attach: undefined,\n onArtifact: input.onArtifact,\n onLog: input.onLog\n })\n await captureAndAttachVideo({\n mode: input.mode,\n granularity: input.granularity,\n policy: input.videoPolicy,\n frames: input.frames,\n startWallTime: input.startWallTime,\n outcomes: input.outcomes,\n attempt: input.attempt,\n outputDir: input.outputDir,\n testUid: input.uid,\n sessionId: input.sessionId,\n captureFormat: input.captureFormat,\n attach: undefined,\n onArtifact: input.onArtifact,\n onLog: input.onLog\n })\n}\n","// node:assert capture wiring: routes patched assertions into the session\n// capturer through the same captureCommand path driver commands use, so\n// retry bookkeeping and trace-mode action snapshots behave identically.\n\nimport logger from '@wdio/logger'\nimport {\n errorMessage,\n patchNodeAssert,\n resolveAssertTargetFromArgs,\n type CapturedAssert\n} from '@wdio/devtools-core'\nimport type { SessionCapturer } from '../session.js'\n\nconst log = logger('@wdio/nightwatch-devtools:assertCapture')\n\n/**\n * Patch node:assert once per process. Getters are read at capture time — the\n * capturer is created lazily in session init and the test UID changes per test.\n */\nexport function wireAssertCapture(\n getCapturer: () => SessionCapturer | undefined,\n getTestUid: () => string | undefined\n): void {\n patchNodeAssert(\n (cmd) => captureAssert(getCapturer(), getTestUid(), cmd),\n (level, message) => log[level](message),\n // No handle resolver: Nightwatch commands take selector strings, so an\n // element object never reaches an assert — only the value's provenance,\n // recorded by the command path (BrowserProxy), can name the target.\n (args) => resolveAssertTargetFromArgs(args)\n )\n}\n\nfunction captureAssert(\n capturer: SessionCapturer | undefined,\n testUid: string | undefined,\n cmd: CapturedAssert\n): void {\n if (!capturer) {\n return\n }\n capturer\n .captureCommand(cmd.command, cmd.args, cmd.result, cmd.error, {\n testUid,\n callSource: cmd.callSource,\n timestamp: cmd.timestamp,\n // The assert's args hold the compared VALUES, so provenance — recorded by\n // the command path — is the row's only route to a locator.\n selector: cmd.selector\n })\n .catch((err) =>\n log.warn(`Failed to capture ${cmd.command}: ${errorMessage(err)}`)\n )\n // captureCommand pushes synchronously; mirror captureCommandError's send.\n const last = capturer.commandsLog[capturer.commandsLog.length - 1]\n if (last) {\n capturer.sendCommand(last)\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 * Global key the plugin uses to hand itself to the Cucumber hooks\n * (`helpers/cucumberHooks.cts`). Single source of truth, re-exported by\n * `constants.ts`.\n *\n * It lives in its own leaf module with NO imports on purpose: the Cucumber\n * hooks are bundled standalone into a CJS file, and importing this from the\n * full `constants.ts` would drag in its ESM dependency graph (→ core), which\n * uses `import.meta.url` / `createRequire` and throws when bundled to CJS.\n */\nexport const PLUGIN_GLOBAL_KEY = '__nightwatchDevtoolsPlugin'\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 * Defined in its own leaf module so the Cucumber hooks can bundle it standalone\n * (see plugin-global-key.ts); re-exported here for the rest of the plugin.\n */\nexport { PLUGIN_GLOBAL_KEY } from './plugin-global-key.js'\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 TestRunnerId,\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\n/** This adapter's runner identity. Both Nightwatch runners take the same\n * locator dialect, but the id also reaches the dashboard's run capabilities,\n * where the cucumber variant differs. */\nexport function nightwatchRunnerId(isCucumberRunner: boolean): TestRunnerId {\n return isCucumberRunner ? 'nightwatch-cucumber' : 'nightwatch'\n}\n\nexport function buildPluginMetadataOptions(input: {\n isCucumberRunner: boolean\n configPath: string | undefined\n}) {\n return {\n framework: nightwatchRunnerId(input.isCucumberRunner),\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 * 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-run bringup (capturer + reporter chain + metadata + BiDi + screencast)\n * - Re-targeting that one capturer when the session under it is replaced\n * - Re-arming the per-session capture channels when a session is replaced\n * - Screencast finalize-and-clear\n *\n * The recorder's own session binding lives in `screencast-session.ts`.\n */\n\nimport logger from '@wdio/logger'\nimport {\n errorMessage,\n finalizeScreencast,\n registerCollectorPreload,\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 type { ScreencastRecorder } from './screencast.js'\nimport {\n rotateScreencastForSession,\n startScreencast\n} from './screencast-session.js'\nimport type {\n DevToolsMode,\n NightwatchBrowser,\n ScreencastOptions,\n SuiteStats,\n TestRunnerId,\n TestStats\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 readonly captureAssertions: boolean\n readonly runner: TestRunnerId\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 /** Session the BiDi attach + collector preload were last armed for — the\n * once-per-session guard for both, and the rotation detector. */\n armedSessionId: string | undefined\n srcFolders: string[]\n screencastRecorder: ScreencastRecorder | undefined\n screencastSessionId: string | undefined\n /** In-flight recorder rotation — see screencast-session.ts. */\n screencastRotation: Promise<void> | undefined\n configPath: string | undefined\n\n getCurrentTest(): unknown\n getCurrentScenarioSuite(): SuiteStats | null\n buildMetadataOptions(): unknown\n attemptFor(uid: string): number | undefined\n recordOutcome(uid: string, state: TestStats['state']): void\n /** Drains the live recorder's frames into the run's filmstrip buffer, then\n * stops and clears it. */\n finalizeCurrentScreencast(): Promise<void>\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(\n (suitesData) => {\n if (ctx.sessionCapturer) {\n ctx.sessionCapturer.sendUpstream('suites', suitesData)\n }\n },\n (uid) => ctx.attemptFor(uid)\n )\n ctx.testManager = new TestManager(ctx.testReporter, (uid, state) =>\n ctx.recordOutcome(uid, state)\n )\n ctx.suiteManager = new SuiteManager(ctx.testReporter)\n ctx.browserProxy = new BrowserProxy(\n ctx.sessionCapturer,\n ctx.testManager,\n () => ctx.getCurrentTest() ?? ctx.getCurrentScenarioSuite(),\n ctx.captureAssertions,\n // Nightwatch raises no session event and never wraps `end`, so a command\n // running under a new sessionId is the only trace a mid-run\n // `browser.end()` leaves behind. Both re-bindings hang off it; each latches\n // and gates itself, so neither can suppress the other.\n (browser) => {\n rotateScreencastForSession(ctx, browser)\n rearmCaptureForSession(ctx, browser)\n }\n )\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 runner: ctx.runner,\n url: ''\n }\n // Single-valued, so a run-spanning trace carries the LAST session's identity.\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. Attempted once per session (the caller's `armedSessionId` stamp\n// is that guard); on failure or unavailability the perf-log fallback path\n// continues to work.\nasync function tryAttachBidi(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): Promise<void> {\n if (!ctx.bidiEnabled) {\n return\n }\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/**\n * Register the collector to run at document-start, so every document — including\n * the ones a navigation creates without us noticing — instruments and anchors\n * itself.\n *\n * Deliberately NOT gated on the `bidi` option: that option exists to avoid\n * double-reporting console/network against the perf-log path, whereas this needs\n * nothing but a session created with `webSocketUrl: true`. Gating DOM capture on\n * an unrelated opt-in that defaults to false would leave the race in place for\n * almost every user. Self-degrades to the per-document `<script>` injection when\n * BiDi isn't there.\n */\nasync function tryRegisterPreload(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): Promise<void> {\n const driver = (browser as { driver?: unknown }).driver\n if (!driver) {\n return\n }\n ctx.sessionCapturer.preloadRegistered = await registerCollectorPreload(\n driver,\n (level, message) => log[level](message)\n )\n}\n\n/**\n * Arm the two capture channels that belong to ONE WebDriver session: the BiDi\n * inspectors (bound to that session's driver object) and the document-start\n * collector preload (registered on that session's BiDi channel). Neither\n * survives the session, so a replaced session needs both again.\n *\n * The stamp is written before any await: on rotation this runs from the command\n * hook, where the next command arrives long before a BiDi round trip completes,\n * and the stamp is what keeps that flood from re-arming.\n *\n * The preload goes FIRST. Behind the attach it lost the race it exists to win —\n * measured on a re-arm, the network subscribe took 6.2 s while the command that\n * triggered the re-arm navigated, so the registration landed two seconds after\n * the document it was meant to instrument was born. Sequential rather than\n * parallel because both steps open the session's BiDi channel through the same\n * unsynchronized `getBidi()` cache, and racing them opens two websockets of\n * which `quit()` closes one.\n */\nasync function armCaptureForSession(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): Promise<void> {\n ctx.armedSessionId = browser.sessionId ?? undefined\n await tryRegisterPreload(ctx, browser)\n await tryAttachBidi(ctx, browser)\n}\n\n/**\n * Whether the browser is executing under a session the capture channels were\n * never armed for. Requires an already-armed session: before bringup there is no\n * capturer to arm onto, and `ensureSessionInitialized` owns that first arm.\n */\nexport function needsCaptureRearm(\n ctx: Pick<SessionInitCtx, 'armedSessionId' | 'sessionCapturer'>,\n sessionId: string | undefined\n): boolean {\n return (\n Boolean(ctx.sessionCapturer) &&\n sessionId !== undefined &&\n ctx.armedSessionId !== undefined &&\n ctx.armedSessionId !== sessionId\n )\n}\n\n/**\n * Re-arm BiDi and the collector preload after the session was replaced. Without\n * it both are established exactly once, at bringup, and sessions 2..N silently\n * fall back to `<script>` injection — the whole navigation race class the\n * preload exists to remove comes back for the rest of the run.\n *\n * The `needsCaptureRearm` guard is what makes it idempotent: the command hook\n * and `ensureSessionInitialized` both detect the same replacement, and\n * `armCaptureForSession` stamps `armedSessionId` before its first await, so\n * whichever gets there first latches the other out.\n */\nasync function armReplacedSession(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): Promise<void> {\n const sessionId = browser.sessionId ?? undefined\n if (!needsCaptureRearm(ctx, sessionId)) {\n return\n }\n // Both claims belong to the session that just died. Until the new\n // registration lands this session's documents are back on the `<script>`\n // path — exactly what `anchorAfterNavigation` polls for — and a stale\n // `bidiActive` would keep the perf-log network path gated off behind\n // inspectors that no longer receive anything.\n ctx.sessionCapturer.preloadRegistered = false\n ctx.sessionCapturer.bidiActive = false\n log.info(`Session replaced — re-arming capture for ${sessionId}`)\n await armCaptureForSession(ctx, browser)\n}\n\n/** Fire-and-forget {@link armReplacedSession}, for the command hook — a command\n * must not wait on a BiDi handshake. */\nexport function rearmCaptureForSession(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): void {\n void armReplacedSession(ctx, browser).catch((err) =>\n log.warn(`Capture re-arm failed: ${errorMessage(err)}`)\n )\n}\n\n/** Re-run the per-session bringup against a replaced session, keeping the run's\n * one capturer. Both halves are the guarded helpers the command hook uses, so a\n * replacement both paths see is armed and rotated once. */\nasync function rebindSessionToBrowser(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): Promise<void> {\n log.info('Browser session changed — re-targeting the run capturer')\n // Also gates `wrapUrlMethod`, which is per browser OBJECT — cucumber hands\n // over a new one per scenario.\n ctx.isScriptInjected = false\n broadcastSessionMetadata(ctx, browser)\n await armReplacedSession(ctx, browser)\n rotateScreencastForSession(ctx, browser)\n await ctx.screencastRotation\n}\n\nexport async function ensureSessionInitialized(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): Promise<void> {\n const currentSessionId = browser.sessionId\n const isSessionChange =\n currentSessionId &&\n ctx.lastSessionId &&\n currentSessionId !== ctx.lastSessionId\n ctx.lastSessionId = currentSessionId ?? null\n if (ctx.sessionCapturer) {\n // Unconditional: cucumber hands over a new browser object per scenario, and\n // a stale reference answers neither probe the capturer runs against it.\n ctx.sessionCapturer.setBrowser(browser)\n if (isSessionChange) {\n await rebindSessionToBrowser(ctx, browser)\n }\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' ? {} : { port: ctx.port, hostname: ctx.hostname },\n browser\n )\n ctx.sessionCapturer.traceMode = ctx.mode\n ctx.sessionCapturer.runner = ctx.runner\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 initReporterChain(ctx)\n broadcastSessionMetadata(ctx, browser)\n await armCaptureForSession(ctx, browser)\n await startScreencast(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 if (ctx.mode === 'trace') {\n // Trace mode: the per-test video is written by the produce path and the\n // filmstrip frames are embedded in the trace itself, so stop the recorder\n // without encoding a session .webm nothing references (orphan file).\n await ctx.screencastRecorder.stop()\n } else {\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 }\n ctx.screencastRecorder = undefined\n ctx.screencastSessionId = undefined\n}\n","import logger from '@wdio/logger'\nimport {\n CAPTURE_PERFORMANCE_SCRIPT,\n COLLECTOR_READY_EXPRESSION,\n SessionCapturerBase,\n applyPerformanceData,\n collectorDrainExpression,\n drainCollectorWithRecovery,\n errorMessage,\n loadInjectableScript,\n mapChromeBrowserLogs,\n mapCommandToAction,\n pollUntilReady,\n serializeError,\n upsertRichestSnapshot,\n type CapturedPerformancePayload,\n type SessionCapturerOptions\n} from '@wdio/devtools-core'\nimport { captureActionSnapshot } from './action-snapshot.js'\nimport { nightwatchRunnerId } from './helpers/utils.js'\nimport { NAVIGATION_COMMANDS } from './constants.js'\nimport {\n parseNetworkFromPerfLogs,\n dedupeNetworkRequests,\n type NetworkEntry,\n type PerfLogEntry\n} from './helpers/perfLogs.js'\nimport { webdriverExecute, webdriverGet } from './helpers/webdriverHttp.js'\nimport type {\n ActionSnapshot,\n CommandCaptureMeta,\n CommandLog,\n DevToolsMode,\n NightwatchBrowser,\n TestRunnerId\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\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 /** True once the collector is registered to run at document-start. Every\n * document then instruments and anchors itself, so the paths that exist to\n * notice a navigation after the fact — re-injection and the settle poll — have\n * nothing left to do and stand down. */\n preloadRegistered = false\n\n // Populated by captureCommand when mode === 'trace' (set by the plugin).\n traceMode: DevToolsMode = 'live'\n\n /** Which Nightwatch runner is driving — set by the plugin, and passed to the\n * per-action capture so the injected scripts emit this runner's dialect. */\n runner: TestRunnerId = nightwatchRunnerId(false)\n readonly actionSnapshots: ActionSnapshot[] = []\n readonly snapshotCaptures: Promise<void>[] = []\n\n constructor(\n devtoolsOptions: SessionCapturerOptions = {},\n browser?: NightwatchBrowser\n ) {\n super(devtoolsOptions)\n this.#browser = browser\n this.patchConsole()\n this.patchStreams()\n }\n\n /** Re-target at the browser the run now drives — the capturer's only\n * session-bound state, so a replaced session is a re-target, not a rebuild\n * (mirrors selenium's `setDriver`). */\n setBrowser(browser: NightwatchBrowser): void {\n this.#browser = browser\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 meta: CommandCaptureMeta = {}\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: meta.timestamp || Date.now(),\n startTime: meta.startTime,\n sequence: meta.sequence,\n callSource: meta.callSource,\n testUid: meta.testUid,\n selector: meta.selector\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 (!error) {\n this.#queueActionSnapshot(command, commandLogEntry.timestamp)\n }\n\n return true\n }\n\n /** Fire-and-forget post-action snapshot, drained at finalize. Stamped with the\n * logged command's timestamp, not the capture's own: the capture resolves\n * after the command completes, and export claims snapshots by exact equality\n * with the command timestamp (FrameSnapshotIndex.claimAfter / elementsAt). */\n #queueActionSnapshot(command: string, timestamp: number): void {\n if (\n this.traceMode !== 'trace' ||\n !this.#browser ||\n !mapCommandToAction(command)\n ) {\n return\n }\n this.snapshotCaptures.push(\n captureActionSnapshot(\n this.#browser,\n command,\n timestamp,\n this.runner\n ).then((snap) => {\n if (snap) {\n upsertRichestSnapshot(this.actionSnapshots, snap)\n }\n })\n )\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 * Ingest a pre-built assertion entry (native `browser.assert`/`verify`\n * synthesized from Nightwatch's results, or a node:assert capture) into the\n * same command stream driver commands use. Unlike {@link captureCommand} this\n * preserves the entry's `title` (the human assertion message) and never\n * dedups — each call is a distinct action row. Assigns a fresh `_id` and a\n * matching stable public `id` (so a later `sendReplaceCommand` can update\n * this exact row in place — native asserts stream a pending row at call time,\n * then finalize their pass/fail), pushes, and broadcasts.\n */\n captureAssertCommand(entry: CommandLog): void {\n const withId = entry as CommandLog & { _id?: number }\n withId._id = this.commandCounter++\n withId.id = withId._id\n this.commandsLog.push(withId)\n // Deliberately no snapshot capture. These rows are emitted in a batch at\n // test-end but positioned back on their real execution window, so capturing\n // here would probe the page as it is NOW and stamp it at a moment seconds\n // earlier — an assert that ran on /secure rendered the /login page it later\n // logged out to. An assertion reads the page rather than changing it, so the\n // exporter lets it inherit the preceding action's capture instead\n // (FrameSnapshotIndex.claimAfter). The row's own `screenshot` is already\n // attached by the caller from the last resolved capture.\n this.sendCommand(withId)\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 meta: CommandCaptureMeta = {}\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: meta.timestamp || Date.now(),\n startTime: meta.startTime,\n sequence: meta.sequence,\n callSource: meta.callSource,\n testUid: meta.testUid,\n selector: meta.selector\n }\n this.commandsLog.push(entry)\n return { entry, oldTimestamp }\n }\n\n /**\n * Wait for a navigation to actually replace the document, then drain with a\n * forced anchor so the destination's DOM enters the stream.\n *\n * A Nightwatch click resolves BEFORE its navigation commits, so the drain that\n * runs at command completion still finds the outgoing page's collector: it\n * recovers nothing, and the destination is never anchored — every action on the\n * new page then replays the page it came from. Waiting for the collector to\n * disappear is the signal that the document was replaced; a command that\n * navigated nowhere polls out and costs one drain of an empty buffer.\n */\n async anchorAfterNavigation(browser: NightwatchBrowser): Promise<void> {\n const replaced = await pollUntilReady(\n async () =>\n (await webdriverExecute<boolean>(\n browser,\n `return ${COLLECTOR_READY_EXPRESSION};`\n )) !== true,\n // With the preload active every document instruments itself at\n // document-start, so there is normally nothing to catch — but ONE probe\n // still runs rather than standing down entirely, because a document created\n // before the registration landed has no collector and nothing else would\n // ever notice. Without the preload this is the only detector, so it keeps\n // the full settle poll.\n this.preloadRegistered\n ? { attempts: 1, intervalMs: 0 }\n : { attempts: 5, intervalMs: 150 }\n )\n if (!replaced) {\n return\n }\n // captureTrace's own recovery injects into the fresh document and re-drains,\n // and it gates that on the url being worth instrumenting — so a torn-down\n // session falls out here rather than logging an injection failure.\n //\n // The attribution is the newest command at THIS moment, not the one whose\n // completion started the poll: several polls overlap, so a field edit's poll\n // routinely observes the navigation the following click caused and would\n // credit itself — which pulled the anchor back before the edit's own row and\n // moved the wrong-DOM row one earlier instead of fixing it. Read at detection\n // time it is right for whichever poll gets there first.\n //\n // Unlike the inference in `anchorOwnerTimestamp`, this does not need the\n // \"nothing completed after the anchor\" guard: we just watched the document be\n // replaced, so a navigation demonstrably happened and the newest command is\n // the one that caused it. The guard exists for the case where that is a guess.\n await this.captureTrace(\n browser,\n true,\n this.commandsLog[this.commandsLog.length - 1]?.timestamp\n )\n }\n\n /**\n * Take a screenshot by calling the WebDriver HTTP endpoint directly, bypassing\n * Nightwatch's command queue so the request can't be appended after\n * `end()` / `quit()`.\n */\n takeScreenshotViaHttp(browser: NightwatchBrowser): Promise<string | null> {\n return webdriverGet<string>(browser, 'screenshot')\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 // Injecting over a live collector replaces `window.wdioTraceCollector`\n // with a fresh instance and DISCARDS whatever it had buffered — including\n // the destination's full-DOM anchor. Several paths can reach the same\n // document (navigation proxy, drain recovery), so guard here once.\n const alreadyPresent = await webdriverExecute<boolean>(\n browser,\n `return ${COLLECTOR_READY_EXPRESSION};`\n )\n if (alreadyPresent === true) {\n return\n }\n const scriptContent = await loadInjectableScript()\n // Inlined as a literal rather than passed via `arguments[0]`: the raw\n // /execute/sync transport takes its args separately and this keeps the\n // injection a single self-contained body.\n const injectionScript = `\n const script = document.createElement('script');\n script.textContent = ${JSON.stringify(scriptContent)};\n document.head.appendChild(script);\n return true;\n `\n await webdriverExecute(browser, injectionScript)\n\n const hasCollector = await pollUntilReady(\n async () =>\n (await webdriverExecute<boolean>(\n browser,\n `return ${COLLECTOR_READY_EXPRESSION};`\n )) === 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 // A perf-log entry that never saw its response event carries no type;\n // default it to the vocabulary's residual at this framework boundary.\n const normalized = deduped.map((d) => ({\n ...d,\n type: d.type ?? 'other'\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 /** `forceAnchor` forces a full-DOM anchor of the current document before\n * draining, so a freshly injected collector's async anchor isn't lost.\n * `anchorTimestamp` is the command this drain's anchors were CAUSED by — pass\n * it only when the caller observed the document being replaced right after\n * that command, since it overrides the inference at ingest. */\n async captureTrace(\n browser: NightwatchBrowser,\n forceAnchor = false,\n anchorTimestamp?: number\n ) {\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 traceData = await drainCollectorWithRecovery({\n drain: () =>\n webdriverExecute<Record<string, unknown> | null>(\n browser,\n collectorDrainExpression(forceAnchor)\n ),\n injectIntoCurrentDocument: () => this.injectScript(browser),\n currentUrl: () =>\n webdriverExecute<string>(browser, 'return location.href;').then(\n (u) => u ?? undefined\n ),\n log: (level, message) => log[level](message)\n })\n\n if (!traceData) {\n return\n }\n\n this.processTracePayload(traceData, { anchorTimestamp })\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","// Direct WebDriver HTTP transport for the capture probes.\n//\n// Every probe the trace capture makes — url, title, screenshot, in-page script\n// — has to bypass Nightwatch's command queue. `browser.*` commands are QUEUED,\n// so a probe the plugin issues from inside its own command hook enqueues behind\n// the command that is still running and cannot resolve until the queue drains.\n// Unguarded, one such probe stranded the whole per-action snapshot capture; the\n// in-page script probe merely timed out, leaving empty a11y trees.\n\nimport http from 'node:http'\nimport logger from '@wdio/logger'\nimport { errorMessage } from '@wdio/devtools-core'\nimport type { NightwatchBrowser } from '../types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:webdriverHttp')\n\n/** Ceiling on a single driver request — a driver that stops answering must not\n * hold a capture open longer than the adapter's settle window. */\nconst REQUEST_TIMEOUT_MS = 5000\n\ntype LooseRec = Record<string, unknown>\n\nconst getProp = (obj: unknown, key: string): unknown =>\n obj && typeof obj === 'object' ? (obj as LooseRec)[key] : undefined\n\nconst getPath = (obj: unknown, ...path: string[]): unknown =>\n path.reduce<unknown>((acc, k) => getProp(acc, k), obj)\n\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 type)\n * to find the underlying WebDriver host+port.\n */\nexport function 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\nfunction sessionEndpoint(\n browser: NightwatchBrowser,\n path: string\n): string | undefined {\n const sessionId = (browser as unknown as { sessionId?: string }).sessionId\n if (!sessionId) {\n return undefined\n }\n const { driverHost, driverPort } = resolveWebDriverAddress(browser)\n return `http://${driverHost}:${driverPort}/session/${sessionId}/${path}`\n}\n\n/** A W3C error payload: `value` carries `error`/`message` instead of the\n * command's result. Shape-checked rather than status-checked because chromedriver\n * answers some failures with a 200. */\nfunction isWebdriverError(value: unknown): boolean {\n return (\n typeof value === 'object' &&\n value !== null &&\n !Array.isArray(value) &&\n typeof (value as { error?: unknown }).error === 'string'\n )\n}\n\n/** Resolves the W3C `value` field, or null on any transport/parse/timeout\n * failure — a probe is best-effort and never fails the user's test. */\nfunction request<T>(\n endpoint: string,\n method: 'GET' | 'POST',\n body?: unknown\n): Promise<T | null> {\n const payload = body === undefined ? undefined : JSON.stringify(body)\n return new Promise((resolve) => {\n const req = http.request(\n endpoint,\n {\n method,\n headers: payload\n ? {\n 'content-type': 'application/json',\n 'content-length': Buffer.byteLength(payload)\n }\n : undefined\n },\n (res) => {\n let raw = ''\n res.on('data', (chunk: string | Buffer) => {\n raw += chunk\n })\n res.on('end', () => {\n try {\n const value = JSON.parse(raw).value\n // A W3C error answers 200-shaped JSON whose `value` is\n // `{error, message, stacktrace}` — an OBJECT where the caller\n // expects its payload. Casting that through as `T` put an error\n // object into a screencast frame's `data`, and the run's whole\n // trace was then lost to `Buffer.from(object)` at export.\n resolve(isWebdriverError(value) ? null : ((value as T) ?? null))\n } catch {\n log.warn(`Failed to parse response from ${endpoint}`)\n resolve(null)\n }\n })\n }\n )\n req.on('error', (err) => {\n log.warn(`Request failed (${endpoint}): ${errorMessage(err)}`)\n resolve(null)\n })\n req.setTimeout(REQUEST_TIMEOUT_MS, () => {\n log.warn(`Request timed out (${endpoint})`)\n req.destroy()\n resolve(null)\n })\n if (payload) {\n req.write(payload)\n }\n req.end()\n })\n}\n\n/** GET `/session/:id/<path>`. Null when the session is gone or the call fails. */\nexport function webdriverGet<T>(\n browser: NightwatchBrowser,\n path: string\n): Promise<T | null> {\n const endpoint = sessionEndpoint(browser, path)\n return endpoint ? request<T>(endpoint, 'GET') : Promise.resolve(null)\n}\n\n/** POST `/session/:id/<path>`. Null when the session is gone or the call fails. */\nexport function webdriverPost<T>(\n browser: NightwatchBrowser,\n path: string,\n body: unknown\n): Promise<T | null> {\n const endpoint = sessionEndpoint(browser, path)\n return endpoint ? request<T>(endpoint, 'POST', body) : Promise.resolve(null)\n}\n\n/** Run a script in the page, outside the command queue. `body` is a function\n * body, matching what `browser.execute` accepts. */\nexport function webdriverExecute<T>(\n browser: NightwatchBrowser,\n body: string\n): Promise<T | null> {\n return webdriverPost<T>(browser, 'execute/sync', { script: body, args: [] })\n}\n","// Nightwatch adapter: wires NightwatchBrowser into core's captureActionSnapshot.\n// Every probe goes over the raw WebDriver HTTP transport rather than `browser.*`:\n// those are QUEUED commands, so a probe issued from inside the plugin's own\n// command hook enqueues behind the command still running and can't resolve until\n// the queue drains. `browser.getCurrentUrl()`/`getTitle()` that way left every\n// snapshot with `url: undefined` (so `frameUrl` fell back to `about:blank`) and\n// hung the capture outright; the queued `execute` merely timed out, leaving an\n// empty a11y tree and no element rects.\n\nimport { captureActionSnapshot as coreCapture } from '@wdio/devtools-core'\nimport type { ActionSnapshot } from '@wdio/devtools-shared'\nimport { webdriverExecute, webdriverGet } from './helpers/webdriverHttp.js'\nimport type { NightwatchBrowser, TestRunnerId } from './types.js'\n\nexport function captureActionSnapshot(\n browser: NightwatchBrowser,\n command: string,\n timestamp?: number,\n runner?: TestRunnerId\n): Promise<ActionSnapshot | null> {\n return coreCapture({\n command,\n timestamp,\n runner,\n runScript: (src) => webdriverExecute(browser, `return (${src})`),\n takeScreenshot: () => webdriverGet<string>(browser, 'screenshot'),\n getUrl: () => webdriverGet<string>(browser, 'url'),\n getTitle: () => webdriverGet<string>(browser, 'title')\n })\n}\n","import type { RequestType } from '@wdio/devtools-shared'\n\nimport { 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 /** Set only by `getRequestType`, so it carries the shared vocabulary; absent\n * until the response event arrives. */\n type?: RequestType\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, type ReporterUpstream } 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 #attemptFor: (uid: string) => number | undefined\n\n constructor(\n report: ReporterUpstream,\n attemptFor: (uid: string) => number | undefined\n ) {\n super(report)\n this.#attemptFor = attemptFor\n }\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 uid = generateStableUid({\n file: suiteStats.file,\n fullTitle: `${suiteStats.title} ${testName}`\n } as TestStats)\n const skippedTest: TestStats = {\n uid,\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: this.#attemptFor(uid) ?? 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","/**\n * Which `it` the runner is executing RIGHT NOW.\n *\n * The plugin's `beforeEach` fires once per MODULE on the BDD `describe/it`\n * interface, so the lifecycle-level current test is the module's first `it` for\n * every row in the file. Nightwatch's own `setReporterCurrentTest()` — \"called\n * for every test case (not for hooks)\" — runs per `it` and installs\n * `browser.currentTest` as a live getter over `reporter.currentTest`, so the\n * running testcase's NAME is readable as state on every command. That name is\n * the per-`it` signal no hook or event exposes; mapping it back onto the\n * `TestStats` the suite already registered gives each `it` its own uid, and with\n * it its own group in the trace's action tree.\n */\n\nimport type { NightwatchCurrentTest, SuiteStats, TestStats } from '../types.js'\n\n/**\n * The registered test title a runtime testcase name refers to. Nightwatch\n * reports either the bare `it` title or one prefixed with its parents, so a\n * whole-title suffix match is accepted; anything else is no match — this never\n * guesses at \"the next unprocessed test\" the way the lifecycle picker does.\n */\nexport function matchTestName(\n runtimeName: string | undefined,\n testNames: readonly string[]\n): string | undefined {\n const name = typeof runtimeName === 'string' ? runtimeName.trim() : undefined\n if (!name) {\n return undefined\n }\n return testNames.find((title) => name === title || name.endsWith(` ${title}`))\n}\n\n/**\n * The suite's registered `TestStats` for the testcase Nightwatch reports as\n * running. Undefined when nothing matches: a command issued from a hook (where\n * Nightwatch leaves the previous testcase in place), a spec whose source\n * couldn't be parsed into test names, or the cucumber runner — whose client\n * carries no `currentTest` at all, which is what keeps its per-scenario\n * structure on the lifecycle path untouched.\n */\nexport function findRunningTest(\n suite: SuiteStats | undefined,\n currentTest: NightwatchCurrentTest | undefined\n): TestStats | undefined {\n const tests = (suite?.tests ?? []).filter(\n (t): t is TestStats => typeof t !== 'string'\n )\n const title = matchTestName(\n currentTest?.name,\n tests.map((t) => t.title)\n )\n return title ? tests.find((t) => t.title === title) : undefined\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 { findRunningTest } from './runningTest.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(\n private testReporter: TestReporter,\n /** Stamps a test's resolved terminal state onto the retry ledger. */\n private recordOutcome?: (uid: string, state: TestStats['state']) => void\n ) {}\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 this.recordOutcome?.(test.uid, state)\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 * The registered test for the testcase Nightwatch reports as running, read\n * from live `browser.currentTest` state rather than from a hook — the only\n * per-`it` signal the BDD interface gives a plugin (see ./runningTest.ts).\n * Synchronous by construction: the command path resolves a row's test on the\n * invocation stack, where an await would reorder capture.\n */\n runningTest(\n currentTest: NightwatchCurrentTest | undefined\n ): TestStats | undefined {\n return findRunningTest(this.testReporter.getCurrentSuite(), currentTest)\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 { callbackError } from './callbackError.js'\nimport { commandRowSelector, commandTargetSelector } from './assertTarget.js'\nimport { getCallSourceFromStack } from './utils.js'\nimport { serializeCommandResult } from './serializeCommandResult.js'\nimport { NativeAssertRecorder } from './nativeAssertRecorder.js'\nimport {\n RetryTracker,\n beginInputDispatch,\n rememberReadValue,\n toError\n} from '@wdio/devtools-core'\nimport type { SessionCapturer } from '../session.js'\nimport type { TestManager } from './testManager.js'\nimport type {\n CommandCaptureMeta,\n CommandInvocation,\n CommandLog,\n NativeAssertCall,\n NightwatchBrowser,\n NightwatchChainable,\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 /**\n * Monotonic issue-order counter. Stamped when the test ISSUES a row — a\n * driver command at invocation, an assert at enqueue — which is source order.\n * Both are recorded into `commandsLog` much later and out of that order (a\n * driver command at completion, an assert in the test-end batch), so this is\n * what lets the exporter recover the order the test wrote. Shared with the\n * assert recorder so both row kinds draw from one sequence.\n */\n private issueCounter = 0\n\n private assertRecorder: NativeAssertRecorder\n\n /**\n * The browser every captured row is issued on. Nightwatch mutates ONE browser\n * object in place — even across a mid-run session replacement — so this stays\n * valid for the whole run, and it is how `browser.currentTest` is read from\n * the assert path, which runs outside any command callback.\n */\n private browser: NightwatchBrowser | null = null\n\n constructor(\n private readonly sessionCapturer: SessionCapturer,\n private testManager: TestManager,\n private getCurrentTest: () => { uid?: string } | null,\n private captureAssertions = true,\n private observeSession?: (browser: NightwatchBrowser) => void\n ) {\n this.assertRecorder = new NativeAssertRecorder(\n sessionCapturer,\n () => this.rowTest(),\n () => this.issueCounter++\n )\n }\n\n /**\n * The test a captured row belongs to: the `it` Nightwatch reports as running\n * right now, falling back to the lifecycle test. On the BDD `describe/it`\n * interface the lifecycle test is the module's FIRST `it` for every row in the\n * file (its `beforeEach` fires once per module), so without the live read\n * every command in the module carried one uid and the trace's action tree\n * collapsed to a single test group. The fallback is what the runners with no\n * per-testcase state keep using — cucumber has no `currentTest` at all.\n */\n private rowTest(): { uid?: string } | null {\n return (\n this.testManager.runningTest(this.browser?.currentTest) ??\n this.getCurrentTest()\n )\n }\n\n /** The uid rows are stamped with — see {@link rowTest}. Public so the\n * node:assert capture wiring, which has no command of its own, tags its rows\n * onto the same test the surrounding commands landed on. */\n rowTestUid(): string | undefined {\n return this.rowTest()?.uid\n }\n\n /** Per-command-scope dedup state only. Cucumber calls this per STEP, so it\n * must not touch the native-assert buffer — that buffer spans the whole\n * scenario and is drained once, at the scenario's pre-quit hook. */\n resetCommandTracking(): void {\n this.commandStack = []\n this.lastCommandSig = null\n this.retryTracker.reset()\n }\n\n /** Everything scoped to one test unit: dedup state plus any native-assert\n * calls a previous unit left behind because its drain never ran. */\n resetTestTracking(): void {\n this.resetCommandTracking()\n this.assertRecorder.clear()\n }\n\n /** Hand off this test's recorded native-assertion calls and clear the\n * buffer so the next test starts fresh. */\n drainNativeAssertCalls(): NativeAssertCall[] {\n return this.assertRecorder.drain()\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 // `browser.url()` is BOTH a navigation and a getter: with no url it just\n // reads the current one, which is how `assert.urlContains` reads it from\n // inside Nightwatch's own queue. Treating that read as a navigation\n // re-injected the collector and forced an anchored drain mid-test — real\n // round trips against the page for a command that changed nothing — and\n // logged the capture callback's whole function body as the \"url\".\n if (typeof args[0] !== 'string') {\n return original(...args)\n }\n const result = original(...args)\n\n const injectAndCapture = () => {\n log.info(`[nav] ${methodName}(${args[0] ?? ''}) — injecting script`)\n return (\n sessionCapturer\n .injectScript(browser)\n // Anchored: the just-injected collector anchors the document\n // asynchronously, so an unanchored drain beats it and this page's\n // DOM never reaches the trace.\n .then(() => sessionCapturer.captureTrace(browser, true))\n .catch((err: Error) =>\n log.error(`Failed to inject script: ${(err as Error).message}`)\n )\n )\n }\n\n const chainable = result as NightwatchChainable | undefined\n if (chainable && typeof chainable.perform === 'function') {\n // Standard Nightwatch (chained API). Hung off the command's OWN\n // thenable, not a queued `perform()`: a queued callback is discarded\n // when a failing test aborts the rest of the queue, so the destination\n // of a navigation in a failing test was never instrumented at all\n // (measured: 2 of 3 navigations skipped, and with them the first\n // document's entire DOM). The chainable is itself thenable, and\n // resolving it means the navigation finished — the same signal\n // `perform` gave, minus the dependency on the queue surviving.\n //\n // `result` is returned unchanged so `browser.url(…).waitFor…()`\n // chaining still works; the injection runs as a side effect.\n void Promise.resolve(result)\n .then(injectAndCapture)\n .catch(() => {})\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 // Held before the already-wrapped guard: the reference is what `rowTest`\n // reads `currentTest` off, and a re-entry is exactly a run that never\n // re-wraps.\n this.browser = browser\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 // captureAssertions:false → leave assert/verify original so no native\n // assertion rows are recorded (the finalize paths are gated to match).\n if (this.captureAssertions) {\n this.assertRecorder.wrapNamespaces(browser)\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 meta: CommandCaptureMeta,\n commandError: Error | undefined\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 commandError,\n meta\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 meta: CommandCaptureMeta,\n cmdSig: string,\n commandError: Error | undefined\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(methodName, logArgs, serializedResult, commandError, meta)\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 /**\n * After a DOM-mutating command, drain the collector — TWICE, for two different\n * reasons that no single drain can serve.\n *\n * The first drain fires synchronously, with no setTimeout in front. It used to\n * defer 200ms to stay off Nightwatch's callback stack, safe only because the\n * drain went through `browser.execute`, a QUEUED command; over the raw HTTP\n * transport it touches no queue, and because a driver serialises requests per\n * session, a drain issued here is served BEFORE the next queued command. That\n * is what captures the OUTGOING page's field edits — deferred, a submit click\n * navigated first and they died with the page, so the `#password` fill row\n * replayed with an empty password box.\n *\n * The second drain is what anchors the DESTINATION, and it has to wait: a\n * Nightwatch click resolves before its navigation commits, so the first drain\n * still finds the outgoing page's collector, recovers nothing, and the new\n * document is never anchored — every action on it then replayed the page it\n * came from (measured: 5 of 20 rows). `anchorAfterNavigation` keys off the\n * document actually being replaced rather than off which command ran, so it\n * covers any route to a new document — a submit click, a JS redirect, a\n * same-command re-navigation — without a list of commands to keep in sync.\n */\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 this.sessionCapturer.captureTrace(browser, true).catch(() => {})\n // Tracked so finalize awaits it — an untracked anchor drain can still be in\n // flight when the trace is written, which loses the document entirely.\n this.sessionCapturer.snapshotCaptures.push(\n this.sessionCapturer.anchorAfterNavigation(browser).catch(() => {})\n )\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 /** Serialize the result and record which element produced it, so a later\n * node:assert on that value can name its target (core's value→locator\n * registry). Fed for every command, so a page-level read records \"no\n * element\" — see `rememberReadValue` for why that sentinel matters. Folded\n * into one call because `makeCaptureCallback` is already over the line cap. */\n private serializeAndAttributeResult(\n methodName: string,\n logArgs: unknown[],\n callbackResult: unknown\n ): unknown {\n const result = serializeCommandResult(callbackResult, methodName)\n rememberReadValue(result, commandTargetSelector(methodName, logArgs))\n return result\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 hasUserSource: boolean,\n invokedAt: number,\n sequence: number,\n testUid: string | undefined,\n userCallback: Function | null\n ): (callbackResult: unknown) => unknown {\n return (callbackResult: unknown) => {\n // Stamped HERE, when the command actually finished in the browser — not\n // when the test enqueued it. Nightwatch runs commands off a queue, so\n // invocation leads real execution by hundreds of ms, and the page-side\n // mutation stream is on real time: a row stamped at invocation replayed\n // the page from BEFORE its own effect (the `#username` fill rendered an\n // empty field, the `#password` fill rendered only the username). The\n // invocation time becomes the row's `startTime`, so the row also spans its\n // real duration instead of a synthetic 1ms.\n const commandTimestamp = Date.now()\n this.popCommandStackIfMatches(methodName, cmdSig)\n const commandError = callbackError(callbackResult)\n const serializedResult = commandError\n ? undefined\n : this.serializeAndAttributeResult(methodName, logArgs, callbackResult)\n const effectiveUid = this.rowTestUid() ?? testUid\n // Only surface commands that originate from a user-code frame. Commands\n // Nightwatch issues from inside its own queue (e.g. the getTitle a\n // `browser.assert.titleContains` runs) execute in a detached tick with no\n // user frame on the stack, so they'd otherwise leak as top-level actions\n // with an \"unknown\" source. Mirrors the service's user-spec-source guard.\n if (effectiveUid && hasUserSource) {\n const meta: CommandCaptureMeta = {\n testUid: effectiveUid,\n callSource,\n timestamp: commandTimestamp,\n startTime: invokedAt,\n sequence,\n selector: commandRowSelector(methodName, logArgs)\n }\n if (this.retryTracker.isRetry(cmdSig)) {\n this.handleRetryReplacement(\n browser,\n methodName,\n logArgs,\n serializedResult,\n meta,\n commandError\n )\n } else {\n this.captureFreshCommand(\n browser,\n methodName,\n logArgs,\n serializedResult,\n meta,\n cmdSig,\n commandError\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 describeInvocation(\n methodName: string,\n args: unknown[]\n ): CommandInvocation {\n const callInfo = getCallSourceFromStack()\n if (callInfo.filePath && !this.currentTestFullPath) {\n this.currentTestFullPath = callInfo.filePath\n }\n const lastArg = args[args.length - 1]\n const hasUserCallback = typeof lastArg === 'function'\n const logArgs = hasUserCallback ? args.slice(0, -1) : args\n const cmdSig = JSON.stringify({\n command: methodName,\n args: logArgs,\n src: callInfo.callSource\n })\n this.pushCommandStackIfNew(methodName, cmdSig, callInfo.callSource)\n return {\n userCallback: hasUserCallback\n ? (lastArg as (result: unknown) => unknown)\n : null,\n logArgs,\n cmdSig,\n callSource: callInfo.callSource,\n hasUserSource: callInfo.filePath !== undefined\n }\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 // The command's session id is the only evidence a mid-run `browser.end()`\n // leaves behind — Nightwatch raises no event and `end` is never wrapped.\n this.observeSession?.(browser)\n this.testManager.startTestIfPending(\n this.testManager.detectTestBoundary(\n browserAny.currentTest as NightwatchCurrentTest\n )\n )\n\n const { userCallback, logArgs, cmdSig, callSource, hasUserSource } =\n this.describeInvocation(methodName, args)\n // Invocation time only — the row's real completion stamp is taken inside the\n // capture callback (see makeCaptureCallback).\n const invokedAt = Date.now()\n const sequence = this.issueCounter++\n const captureCallback = this.makeCaptureCallback(\n browser,\n methodName,\n logArgs,\n cmdSig,\n callSource,\n hasUserSource,\n invokedAt,\n sequence,\n this.rowTestUid(),\n userCallback\n )\n // Suppress screencast polling while this command may be dispatching input: a\n // screenshot landing mid-click makes the click activate nothing. Enqueue is\n // the only hook the adapter has — Nightwatch executes off a queue it exposes\n // no start event for — so an awaited call (enqueue ≈ execution) is covered,\n // while one queued behind a long wait can see its window expire before it\n // runs. The bound is what keeps that from stalling the recorder instead.\n const closeInputWindow = beginInputDispatch(methodName)\n const gatedCallback = (callbackResult: unknown) => {\n closeInputWindow()\n return captureCallback(callbackResult)\n }\n const modifiedArgs = [...logArgs, gatedCallback]\n try {\n return originalMethod(...modifiedArgs)\n } catch (error) {\n closeInputWindow()\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.rowTest()\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(methodName, args, undefined, normalizedError, {\n testUid: currentTest.uid,\n callSource,\n selector: commandRowSelector(methodName, args)\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","/**\n * The failure a Nightwatch command reports by invoking its callback with an\n * error-shaped result instead of throwing.\n *\n * Only a SYNCHRONOUS failure reaches the try/catch around the wrapped method; an\n * async one (a command that times out waiting for an element) arrives here as a\n * result object. Left in `result`, the row kept `error: undefined` and rendered\n * as a success — the failure was readable only as raw text in the result pane,\n * with no red row and nothing in the Errors tab.\n *\n * `passed` present means this is an assertion result, which carries its own\n * pass/fail handling and must not be reinterpreted here.\n */\nexport function callbackError(result: unknown, depth = 0): Error | undefined {\n if (result instanceof Error) {\n return result\n }\n if (!result || typeof result !== 'object') {\n return undefined\n }\n const r = result as Record<string, unknown>\n // W3C driver responses nest their payload under `value` and Nightwatch passes\n // that wrapper through untouched, so the failure sits one level down — reading\n // only the top level found no `error` and the row stayed green. Depth-bounded:\n // this runs inside the command hook, so a self-referencing result must not be\n // able to overflow the stack and take the user's test down with it.\n if ('value' in r) {\n return depth < 2 ? callbackError(r.value, depth + 1) : undefined\n }\n if ('passed' in r || !r.error) {\n return undefined\n }\n const err = new Error(String(r.message ?? r.error))\n if (typeof r.stack === 'string') {\n err.stack = r.stack\n }\n return err\n}\n","// Which element a Nightwatch row is about — for a native `browser.assert.*` /\n// `browser.verify.*` row, and for a driver command whose result an assertion may\n// later be made on.\n//\n// Nightwatch names the target in the CALL: every element assertion in\n// `lib/api/assertions/*.js` takes the element definition as its FIRST argument\n// (`textContains(definition, expected)`), while the page-level ones take the\n// expected value there instead (`urlContains(expected)`). Element commands\n// follow the same shape (`getText(definition, callback)`), so both answers come\n// off arg 0 — under a per-kind allowlist, because the same namespaces also carry\n// calls whose arg 0 is a plain value or a url.\n//\n// node:assert is the case this can't answer: it takes VALUES and names no\n// element, so `commandTargetSelector` feeds core's value→locator registry from\n// the command path and the assert row reads its target back from there.\n//\n// Neither allowlist is derivable from shared's `ACTION_MAP`: that table says how\n// a command RENDERS (its `Element` entries include WDIO commands called on a\n// handle, with no selector argument at all), not whether arg 0 is a definition.\n\n/**\n * Assertions whose first argument is an element definition, transcribed from\n * Nightwatch's own assertion signatures. An allowlist, not a heuristic: the same\n * namespace also carries the page-level assertions (`titleContains`,\n * `urlContains`) and node:assert's value-based mirrors (`ok`, `equal`, …), whose\n * first argument is a plain value — attaching it as a locator is how a\n * page-title assertion ends up boxing an unrelated element.\n */\nconst ELEMENT_TARGET_ASSERTIONS: ReadonlySet<string> = new Set([\n 'attributeContains',\n 'attributeEquals',\n 'attributeMatches',\n 'containsText',\n 'cssClassNotPresent',\n 'cssClassPresent',\n 'cssProperty',\n 'domPropertyContains',\n 'domPropertyEquals',\n 'domPropertyMatches',\n 'elementNotPresent',\n 'elementPresent',\n 'elementsCount',\n 'enabled',\n 'hasAttribute',\n 'hasClass',\n 'hasDescendants',\n 'hidden',\n 'selected',\n 'textContains',\n 'textEquals',\n 'textMatches',\n 'value',\n 'valueContains',\n 'valueEquals',\n 'visible'\n])\n\n/**\n * Commands whose RESULT is a read of the element named by their first argument,\n * transcribed from Nightwatch's element-command signatures. An allowlist for the\n * same reason as above: `browser.url('https://…')` takes a string first too, and\n * reading it as a locator would attribute the page's own reads to an element.\n * Everything absent — every driver-level read included — records its value as\n * belonging to no element (core's `rememberReadValue` sentinel).\n */\nconst ELEMENT_READ_COMMANDS: ReadonlySet<string> = new Set([\n 'getAccessibleName',\n 'getAriaRole',\n 'getAttribute',\n 'getCssProperty',\n 'getElementProperty',\n 'getElementSize',\n 'getLocation',\n 'getLocationInView',\n 'getRect',\n 'getTagName',\n 'getText',\n 'getValue',\n 'isEnabled',\n 'isPresent',\n 'isSelected',\n 'isVisible',\n 'waitForElementNotPresent',\n 'waitForElementNotVisible',\n 'waitForElementPresent',\n 'waitForElementVisible'\n])\n\n/**\n * Element commands that ACT on the element named by their first argument.\n * Deliberately a second set rather than an extension of the reads above: that\n * one also decides which results enter the value→locator registry, and a click's\n * result is not a value any assertion can be made about. Same allowlist\n * discipline — `browser.keys('abc')` takes a string first too.\n */\nconst ELEMENT_ACTION_COMMANDS: ReadonlySet<string> = new Set([\n 'clearValue',\n 'click',\n 'doubleClick',\n 'moveToElement',\n 'rightClick',\n 'setValue',\n 'submitForm',\n 'updateValue'\n])\n\n/** `assert.not.visible('#x')` is recorded under the dotted path `not.visible`;\n * negating an assertion doesn't change which argument is the element. */\nfunction baseMethod(method: string): string {\n return method.startsWith('not.') ? method.slice('not.'.length) : method\n}\n\n/**\n * The selector an element definition carries: a raw string, or the `selector`\n * property of a `{selector, locateStrategy}` bag / a Nightwatch `Element`\n * (duck-typed, so no dependency on Nightwatch's class identity). A definition\n * built from an already-resolved handle carries no selector at all — such a row\n * gets none rather than a wrong one.\n */\nfunction definitionSelector(definition: unknown): string | undefined {\n if (typeof definition === 'string') {\n return definition || undefined\n }\n if (definition && typeof definition === 'object') {\n const selector = (definition as { selector?: unknown }).selector\n if (typeof selector === 'string' && selector) {\n return selector\n }\n }\n return undefined\n}\n\n/** Locator of the element a native assertion was about, or undefined when the\n * assertion targets no element (page title, url, a value comparison). Lands on\n * `CommandLog.selector` so the player's overlay boxes the target the way it\n * does for WDIO's folded matcher rows. */\nexport function assertTargetSelector(\n method: string,\n args: unknown[]\n): string | undefined {\n if (!ELEMENT_TARGET_ASSERTIONS.has(baseMethod(method))) {\n return undefined\n }\n return definitionSelector(args[0])\n}\n\n/** Locator a driver-command row is about — the element it read or acted on.\n * Lands on `CommandLog.selector` so the row says what it targeted instead of\n * leaving the exporter to fall back to `args[0]`. Wider than\n * {@link commandTargetSelector}, which answers the narrower provenance question\n * (whose read produced this value) for the value→locator registry. */\nexport function commandRowSelector(\n method: string,\n args: unknown[]\n): string | undefined {\n if (\n !ELEMENT_READ_COMMANDS.has(method) &&\n !ELEMENT_ACTION_COMMANDS.has(method)\n ) {\n return undefined\n }\n return definitionSelector(args[0])\n}\n\n/** Locator of the element a driver command read from, or undefined when the\n * command reads the page rather than an element. Feeds core's value→locator\n * registry: `undefined` records the result as belonging to no element, which is\n * the sentinel a later node:assert on a page-level value relies on. */\nexport function commandTargetSelector(\n method: string,\n args: unknown[]\n): string | undefined {\n if (!ELEMENT_READ_COMMANDS.has(method)) {\n return undefined\n }\n return definitionSelector(args[0])\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","// Native-assertion capture: turns explicit `browser.assert.*` /\n// `browser.verify.*` calls into concise trace/UI action rows with real args,\n// a clickable source location, and pass/fail colour.\n//\n// Nightwatch exposes no per-assertion hook. Each explicit call is intercepted\n// at CALL TIME (NativeAssertRecorder) and BUFFERED — concise title, real args,\n// callSource, the per-test testUid, and the preceding screenshot — but NOT\n// streamed, because streaming a neutral row mid-run would render every assert\n// green before its outcome is known. At test-end `captureNativeAssertions` reads\n// the pass/fail truth + verbose failure message and emits each row once,\n// positioned on its real execution window (the exporter re-sorts by the buffered\n// call timestamp). Correlating the recorded calls (not the raw results) also\n// excludes Nightwatch's implicit command-generated assertions (e.g.\n// waitForElementVisible's \"element was visible\" entry).\n//\n// Two outcome sources, in this order: the per-test results bag (`correlate`),\n// then the settlement of the call's own promise (`observedAssertOutcome`). The\n// cucumber runner has no results bag at all, so the settlement is its only\n// source; the describe/it interface has a bag but no per-assertion window.\n//\n// The plugin `afterEach` fires once per describe-suite (not per `it`), so the\n// finalize reads assertions from `results.testcases` (all tests) — see\n// gatherResultAssertions.\n\nimport logger from '@wdio/logger'\nimport {\n errorMessage,\n matcherAssertionToCommandLog,\n safeSerializeAssertArg,\n stripAnsi\n} from '@wdio/devtools-core'\nimport { assertTargetSelector } from './assertTarget.js'\nimport type { SessionCapturer } from '../session.js'\nimport type {\n AssertSettlement,\n CollapsedAssertResult,\n CommandLog,\n NativeAssertCall,\n NightwatchBrowser,\n NightwatchCurrentTest,\n ObservedAssertOutcome\n} from '../types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:nativeAssertions')\n\n/**\n * One entry Nightwatch pushes to `results.assertions` (from\n * `NightwatchAssertion.getAssertResult`, lib/assertion/assertion.js). Carries\n * only a human message — no method/args. `failure === false` is the reliable\n * pass signal; any truthy `failure` (message string, or `true`) means failed.\n */\ninterface NwAssertionEntry {\n message?: string\n fullMsg?: string\n failure?: string | boolean\n}\n\n/** One entry Nightwatch pushes to `results.commands` (lib/reporter/index.js\n * logCommandResult). For an assert/verify `name` is the namespaced method and\n * `startTime`/`endTime` are the real queue-execution window in ms\n * (treenode.js). Read only to position the finalized row on the timeline. */\ninterface NwCommandEntry {\n name?: string\n startTime?: number\n endTime?: number\n}\n\nconst ASSERT_CMD_RE = /^(assert|verify)\\.\\w+$/\n\n/** Real per-assertion execution windows, in call order, aligned to `count`\n * recorded calls. Nightwatch enqueues assertions synchronously (all at once)\n * but runs them later one at a time, so the enqueue timestamp clusters the\n * rows; this recovers each row's true execution time so the trace timeline\n * spreads them out. Returns `null` per slot when the executed-command count\n * doesn't line up (e.g. retries) — the enqueue timestamp is kept then. */\nfunction assertCommandTimings(\n commands: NwCommandEntry[],\n count: number\n): Array<{ startTime: number; endTime: number } | null> {\n const executed = commands.filter(\n (c) => typeof c?.name === 'string' && ASSERT_CMD_RE.test(c.name)\n )\n if (executed.length !== count) {\n return new Array(count).fill(null)\n }\n return executed.map((c) =>\n typeof c.startTime === 'number' && typeof c.endTime === 'number'\n ? { startTime: c.startTime, endTime: c.endTime }\n : null\n )\n}\n\n/** Nightwatch embeds the assertion arguments in the message text\n * (AssertionInstance.initialize → Logger.formatMessage), so a passing entry\n * for `titleContains('Example')` reads \"Testing if the page title contains\n * 'Example'\". Match a call to its result entry when every string/number arg\n * appears in the message. */\nfunction messageMatchesArgs(entry: NwAssertionEntry, args: unknown[]): boolean {\n const text = String(entry.fullMsg ?? entry.message ?? '')\n const literals = args.filter(\n (a): a is string | number => typeof a === 'string' || typeof a === 'number'\n )\n return literals.length > 0 && literals.every((a) => text.includes(String(a)))\n}\n\ninterface Outcome {\n passed: boolean\n message: string\n}\n\n/** Pass/fail read off the assertion's own promise — the only outcome evidence\n * cucumber has, since its client is built with no reporter so `results` is\n * never populated. A fulfilment of `undefined` stays neutral: that is an\n * assertion enqueued but never executed, not a pass. Semantics and measurements\n * in CLAUDE.md § Known debt. */\nexport function observedAssertOutcome({\n rejected,\n value,\n settledAt\n}: AssertSettlement): ObservedAssertOutcome | undefined {\n if (rejected || value instanceof Error) {\n return {\n passed: false,\n message: stripAnsi(errorMessage(value)).trim(),\n settledAt\n }\n }\n if (value === undefined) {\n return undefined\n }\n return { passed: true, message: '', settledAt }\n}\n\n/** Execution window recovered from the observed settlement: enqueue → settle.\n * The cucumber runner logs no commands, so `assertCommandTimings` yields\n * nothing there and the row would otherwise span a synthetic 1 ms. */\nfunction observedTiming(\n call: NativeAssertCall\n): { startTime: number; endTime: number } | null {\n return call.observed\n ? { startTime: call.timestamp, endTime: call.observed.settledAt }\n : null\n}\n\n/**\n * All assertion entries for the suite, in declaration order. Nightwatch's\n * plugin `afterEach` fires once per describe-suite (not per `it`), so the flat\n * `results.assertions` reflects only the last testcase; the full per-test\n * breakdown lives in `results.testcases[title].assertions`. Flattening every\n * testcase's entries lets each buffered call correlate to its own test's\n * outcome (without this, only the last test's asserts get a pass/fail and the\n * rest render neutral). Falls back to the flat list for single-test modules or\n * older Nightwatch that doesn't populate `testcases`.\n */\nfunction gatherResultAssertions(\n results:\n | {\n assertions?: NwAssertionEntry[]\n testcases?: Record<string, { assertions?: unknown[] }>\n }\n | undefined\n): NwAssertionEntry[] {\n const testcases = results?.testcases\n if (testcases && typeof testcases === 'object') {\n const all: NwAssertionEntry[] = []\n for (const tc of Object.values(testcases)) {\n if (Array.isArray(tc?.assertions)) {\n all.push(...(tc.assertions as NwAssertionEntry[]))\n }\n }\n if (all.length > 0) {\n return all\n }\n }\n return Array.isArray(results?.assertions) ? results.assertions : []\n}\n\n/**\n * Pair each recorded call with its `results.assertions` entry for pass/fail +\n * verbose message. Both lists are in call/execution order and each explicit\n * call produces exactly one assertion entry, so: match by args-in-message\n * first (most specific, skips interleaved implicit entries), then fall back to\n * the next unconsumed entry positionally. A call with no matching entry left\n * (never happens for a real assertion) is dropped (`null`).\n */\nfunction correlate(\n calls: NativeAssertCall[],\n assertions: NwAssertionEntry[]\n): Array<Outcome | null> {\n const consumed = new Array(assertions.length).fill(false)\n const toOutcome = (idx: number): Outcome => {\n consumed[idx] = true\n const entry = assertions[idx]\n return {\n passed: !entry.failure,\n message: stripAnsi(String(entry.fullMsg ?? entry.message ?? '')).trim()\n }\n }\n const matched = calls.map((call) => {\n const idx = assertions.findIndex(\n (entry, i) => !consumed[i] && messageMatchesArgs(entry, call.args)\n )\n return idx === -1 ? null : toOutcome(idx)\n })\n return matched.map((outcome) => {\n if (outcome) {\n return outcome\n }\n const idx = consumed.findIndex((used) => !used)\n return idx === -1 ? null : toOutcome(idx)\n })\n}\n\n/** Last already-resolved screenshot in the command log — the DOM the assertion\n * evaluated against (title/most asserts don't mutate it). Synchronous, so it's\n * usable from the call-time wrapper. */\nexport function latestResolvedScreenshot(\n capturer: SessionCapturer\n): string | null {\n for (let i = capturer.commandsLog.length - 1; i >= 0; i--) {\n const shot = capturer.commandsLog[i]?.screenshot\n if (shot) {\n return shot\n }\n }\n return null\n}\n\n/** Reuse the nearest preceding command's screenshot; if the fire-and-forget\n * capture hasn't resolved yet (race), fall back to a fresh end-of-test one. */\nasync function resolveAssertionScreenshot(\n capturer: SessionCapturer,\n browser: NightwatchBrowser\n): Promise<string | null> {\n return (\n latestResolvedScreenshot(capturer) ??\n (await capturer.takeScreenshotViaHttp(browser))\n )\n}\n\n/** `assert.titleContains('SOFT_FAIL_ME')` — the concise row label. Strings are\n * quoted, objects elided; never the verbose failure message. */\nfunction conciseTitle(call: NativeAssertCall): string {\n const preview = call.args\n .map((a) =>\n typeof a === 'string'\n ? `'${a}'`\n : a !== null && typeof a === 'object'\n ? '…'\n : String(a)\n )\n .join(', ')\n return `${call.prefix}.${call.method}(${preview})`\n}\n\n/**\n * Build the neutral \"pending\" row emitted the moment an assert/verify is\n * called — everything known at call time (concise title, real args, callSource,\n * screenshot) but NO result/error yet, so it renders neutral (not red/green)\n * and streams in like a normal in-flight command. `startTime`/`timestamp` are\n * the call time; the row is finalized in place later by\n * {@link captureNativeAssertions}.\n */\nexport function pendingAssertionCommand(\n call: NativeAssertCall,\n testUid: string | undefined,\n screenshot: string | null\n): CommandLog {\n const entry: CommandLog = {\n command: `${call.prefix}.${call.method}`,\n args: call.args.map(safeSerializeAssertArg),\n title: conciseTitle(call),\n timestamp: call.timestamp,\n startTime: call.timestamp\n }\n if (call.callSource) {\n entry.callSource = call.callSource\n }\n if (testUid) {\n entry.testUid = testUid\n }\n if (screenshot) {\n entry.screenshot = screenshot\n }\n // Only element assertions name a target; a title/url/value one gets none, so\n // the overlay never boxes an element the assertion wasn't about.\n const selector = assertTargetSelector(call.method, call.args)\n if (selector) {\n entry.selector = selector\n }\n return entry\n}\n\n/** Nightwatch failure messages end with `… but got: \"<actual>\"`. Pull out the\n * real observed value: Nightwatch passes only the EXPECTED as an arg, so the\n * actual lives in the message (and only on failure). Undefined when absent.\n * Uses indexOf + slice (no backtracking-prone regex) on the message tail. */\nfunction parseActualFromMessage(message: string): string | undefined {\n const marker = 'but got:'\n const idx = message.lastIndexOf(marker)\n if (idx === -1) {\n return undefined\n }\n let rest = message.slice(idx + marker.length).trim()\n rest = rest.replace(/ \\(\\d+ms\\)$/, '').trim() // drop trailing \"(123ms)\"\n rest = rest.replace(/^[\"']/, '').replace(/[\"']$/, '').trim() // strip quotes\n return rest || undefined\n}\n\n/** Build the collapsed `{passed, expected, actual?, message}` result core's\n * `buildAssertParams` prefers over the positional `[actual, expected]` arg\n * convention — which is wrong for Nightwatch, whose asserts pass only the\n * expected value (`titleContains('x')`), never the actual. */\nfunction collapsedAssertResult(\n call: NativeAssertCall,\n outcome: Outcome\n): CollapsedAssertResult {\n const result: CollapsedAssertResult = {\n passed: outcome.passed,\n expected: call.args.length <= 1 ? call.args[0] : call.args\n }\n // Omitted rather than empty for a pass observed off the settlement: the\n // promise carries no prose, and an empty message would render as a blank row.\n if (outcome.message) {\n result.message = outcome.message\n }\n const actual = outcome.passed\n ? undefined\n : parseActualFromMessage(outcome.message)\n if (actual !== undefined) {\n result.actual = actual\n }\n return result\n}\n\n/** Emit one assertion row at test-end: apply pass/fail + verbose error, a\n * screenshot, and its real execution window, then send it. Rows are NOT\n * streamed at call time, so this is the single emit (no neutral pending row). */\nfunction finalizeAssertionRow(\n capturer: SessionCapturer,\n call: NativeAssertCall,\n outcome: Outcome,\n timing: { startTime: number; endTime: number } | null,\n screenshot: string | null,\n testUid: string | undefined\n): void {\n const entry = call.entry!\n const finalized = matcherAssertionToCommandLog(\n {\n prefix: call.prefix,\n method: call.method,\n args: call.args,\n passed: outcome.passed,\n message: outcome.message || `${call.prefix}.${call.method} failed`,\n callSource: call.callSource,\n title: entry.title\n },\n testUid\n )\n // Collapsed object result (not the plain 'passed' string) so the trace's\n // action params show a true actual-vs-expected diff instead of mislabelling\n // Nightwatch's single expected-arg as the actual.\n entry.result = collapsedAssertResult(call, outcome)\n entry.error = finalized.error\n if (!entry.screenshot && screenshot) {\n entry.screenshot = screenshot\n }\n // Position the row on its REAL execution window (Nightwatch enqueues all\n // asserts at once, so the call-time timestamp clustered them). The row was\n // never streamed, so send it now — a single emit with the final outcome.\n if (timing) {\n entry.startTime = timing.startTime\n entry.timestamp =\n timing.endTime > timing.startTime ? timing.endTime : timing.startTime\n }\n capturer.captureAssertCommand(entry)\n log.info(`[assert] ${entry.title} → ${outcome.passed ? 'pass' : 'fail'}`)\n}\n\n/**\n * Emit the native assertion rows at test-end: correlate the recorded calls with\n * `results.assertions`, then send each row with pass/fail (`result`) + the\n * verbose failure message (`error`, failures only) + a screenshot + its real\n * execution window. Rows are buffered (not streamed) at call time — Nightwatch\n * has no per-assertion result hook, so streaming them during the run would show\n * every assert as a neutral (green) row before its outcome is known. A call with\n * no matching result is skipped (defensive).\n */\nexport async function captureNativeAssertions(\n capturer: SessionCapturer,\n browser: NightwatchBrowser,\n currentTest: NightwatchCurrentTest | undefined,\n testUid: string | undefined,\n calls: NativeAssertCall[]\n): Promise<void> {\n if (calls.length === 0) {\n return\n }\n // Boundary cast: `results` is Nightwatch's loosely-typed per-test bag; we read\n // only the assertions + commands arrays whose shapes are documented above.\n const results = currentTest?.results as\n | {\n assertions?: NwAssertionEntry[]\n commands?: NwCommandEntry[]\n testcases?: Record<string, { assertions?: unknown[] }>\n }\n | undefined\n const assertions = gatherResultAssertions(results)\n const outcomes = correlate(calls, assertions)\n const timings = assertCommandTimings(\n Array.isArray(results?.commands) ? results.commands : [],\n calls.length\n )\n // One shared screenshot for all rows — same DOM, ran consecutively.\n const screenshot = await resolveAssertionScreenshot(capturer, browser)\n\n calls.forEach((call, index) => {\n if (!call.entry) {\n return\n }\n // Results-bag correlation first (it also carries the verbose per-assertion\n // message, which a settlement can't supply for a pass); the observed\n // settlement is the fallback that makes cucumber rows coloured at all. The\n // timing comes from whichever source supplied the outcome — a correlated row\n // keeps its enqueue timestamp when the bag reports no window (the BDD\n // interface always does), which is what `sequence` ordering relies on.\n const correlated = outcomes[index]\n const outcome = correlated ?? call.observed\n if (outcome) {\n finalizeAssertionRow(\n capturer,\n call,\n outcome,\n correlated ? timings[index] : observedTiming(call),\n screenshot,\n testUid\n )\n return\n }\n // No execution outcome correlated to this call (defensive): emit the\n // buffered row in its neutral state so the assertion still appears — never\n // dropped, never mis-coloured as passed/failed. Rows are only buffered at\n // call time, so without this an uncorrelated assert would vanish.\n if (!call.entry.screenshot && screenshot) {\n call.entry.screenshot = screenshot\n }\n capturer.captureAssertCommand(call.entry)\n })\n}\n","/**\n * Call-time recorder for explicit `browser.assert.*` / `browser.verify.*`.\n *\n * Extracted from BrowserProxy: the recording Proxy, the per-test buffer and the\n * settlement observation are one concern (getting a native assertion's args,\n * source location and outcome), separate from driver-command interception.\n * `nativeAssertions.ts` consumes the drained buffer at test-end.\n */\n\nimport {\n observedAssertOutcome,\n latestResolvedScreenshot,\n pendingAssertionCommand\n} from './nativeAssertions.js'\nimport { getCallSourceFromStack } from './utils.js'\nimport type { SessionCapturer } from '../session.js'\nimport type { NativeAssertCall, NightwatchBrowser } from '../types.js'\n\nexport class NativeAssertRecorder {\n /**\n * Per-test-unit buffer of recorded calls. Nightwatch exposes no per-assertion\n * hook and its test-end results carry no source location for passing\n * assertions, so call-time capture is the only way to get real args +\n * callSource. Drained once per test unit (`afterEach`, or the cucumber\n * scenario's pre-quit hook) — never per step.\n */\n private calls: NativeAssertCall[] = []\n\n constructor(\n private readonly capturer: SessionCapturer,\n private getCurrentTest: () => { uid?: string } | null,\n private nextSequence: () => number\n ) {}\n\n clear(): void {\n this.calls = []\n }\n\n /** Hand off this test's recorded calls and clear the buffer so the next test\n * unit starts fresh. */\n drain(): NativeAssertCall[] {\n const calls = this.calls\n this.calls = []\n return calls\n }\n\n /**\n * Replace `browser.assert` / `browser.verify` (both are Nightwatch Proxies\n * whose `get` returns a fresh function per access) with a recording Proxy:\n * on each method call it captures `{prefix, method, args, callSource}` from a\n * user-code frame, buffers a pending row, then delegates to the ORIGINAL\n * namespace method so Nightwatch's queue, chaining, and abortOnFailure\n * semantics (assert aborts, verify does not) are byte-for-byte unchanged.\n * Called once per browser.\n */\n wrapNamespaces(browser: NightwatchBrowser): void {\n // Cast once: the assert/verify namespaces aren't on the public type; each\n // is a dynamic method bag reached by property name.\n const b = browser as unknown as Record<string, unknown>\n ;(['assert', 'verify'] as const).forEach((prefix) => {\n const original = b[prefix]\n if (!original || typeof original !== 'object') {\n return\n }\n b[prefix] = this.namespaceProxy(original, prefix, [])\n })\n }\n\n /**\n * Recording Proxy over one assert/verify namespace. A function property\n * becomes a call-time recorder keyed by its full dotted path\n * (`titleContains`, `not.titleContains`); a nested namespace object recurses\n * through the SAME wrapper — Nightwatch exposes `assert.not` as its own Proxy,\n * so negated asserts are recorded via the identical mechanism as positive\n * ones instead of a parallel path. Non-method, non-namespace props pass\n * through untouched.\n */\n private namespaceProxy(\n target: object,\n prefix: 'assert' | 'verify',\n path: readonly string[]\n ): object {\n return new Proxy(target, {\n get: (t, name, receiver) => {\n const orig = Reflect.get(t, name, receiver)\n if (typeof name !== 'string') {\n return orig\n }\n if (orig !== null && typeof orig === 'object') {\n return this.namespaceProxy(orig, prefix, [...path, name])\n }\n if (typeof orig !== 'function') {\n return orig\n }\n return (...args: unknown[]) => {\n const callInfo = getCallSourceFromStack()\n const call =\n callInfo.filePath === undefined\n ? undefined\n : this.buffer({\n prefix,\n method: [...path, name].join('.'),\n args,\n callSource: callInfo.callSource,\n timestamp: Date.now()\n })\n const result = (orig as (...a: unknown[]) => unknown)(...args)\n if (call) {\n this.observe(call, result)\n }\n return result\n }\n }\n })\n }\n\n /** Buffer an explicit assert/verify call at invocation time (prebuilding its\n * row with args/callSource/screenshot), but do NOT stream it yet. Nightwatch\n * exposes no per-assertion result hook, so streaming here would show every\n * assert as a neutral (green) row while the test is still Running — before\n * its pass/fail is known. `captureNativeAssertions` emits the rows at\n * test-end with real outcomes + execution timing instead. */\n private buffer(call: NativeAssertCall): NativeAssertCall {\n const entry = pendingAssertionCommand(\n call,\n this.getCurrentTest()?.uid,\n latestResolvedScreenshot(this.capturer)\n )\n entry.sequence = this.nextSequence()\n call.entry = entry\n this.calls.push(call)\n return call\n }\n\n /**\n * Watch the assertion's OWN returned promise for its outcome — see\n * `observedAssertOutcome` for the settlement semantics and why this is the\n * cucumber runner's only pass/fail source. `result` is handed back to the\n * caller untouched; this only adds a second handler, so Nightwatch's own\n * chaining and rejection propagation are unchanged. A non-thenable result\n * (Nightwatch's synchronous `api` return, when commands aren't ES6-async)\n * leaves the call unobserved and the results-bag path supplies the outcome.\n */\n private observe(call: NativeAssertCall, result: unknown): void {\n const thenable = result as { then?: unknown } | null | undefined\n if (!thenable || typeof thenable.then !== 'function') {\n return\n }\n void (result as Promise<unknown>).then(\n (value) => {\n call.observed = observedAssertOutcome({\n rejected: false,\n value,\n settledAt: Date.now()\n })\n },\n (reason) => {\n call.observed = observedAssertOutcome({\n rejected: true,\n value: reason,\n settledAt: Date.now()\n })\n }\n )\n }\n}\n","/**\n * Binds the screencast recorder to a browser session, and rebinds it — and\n * nothing else — when that session is replaced mid-run.\n *\n * Nightwatch raises no session event, and `end` sits in\n * INTERNAL_COMMANDS_TO_IGNORE, so the command proxy never sees the call that\n * destroys the session. The BDD `describe/it` interface compounds it: the\n * plugin's `beforeEach` fires once per MODULE, so `ensureSessionInitialized` —\n * the only place that detects a session change — is never re-entered for a\n * `browser.end()` between `it`s. A command executing under a different\n * `sessionId` is the sole evidence the session was replaced.\n *\n * Rotation touches the recorder and nothing else — the run's SessionCapturer\n * outlives every session it is pointed at. It leaves `lastSessionId` alone\n * because that field is `ensureSessionInitialized`'s own bookkeeping. The other\n * half of a session rebind, re-arming BiDi and the collector preload, hangs off\n * the same detection in `session-init.ts` `rearmCaptureForSession`. Both halves\n * are self-guarding, so `ensureSessionInitialized` reuses them for the session\n * replacement it detects itself.\n */\n\nimport logger from '@wdio/logger'\nimport { errorMessage } from '@wdio/devtools-core'\nimport { ScreencastRecorder } from './screencast.js'\nimport type { SessionInitCtx } from './session-init.js'\nimport type { NightwatchBrowser } from './types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:screencast-session')\n\n/**\n * Start a fresh recorder for `sessionId` — every session gets its own, matching\n * the WDIO service. Polling mode only (Nightwatch exposes no stable CDP escape\n * hatch). Idempotent, so bringup and rotation share one path.\n */\nexport async function startScreencast(\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\n/**\n * Whether the recorder is bound to a session the browser has already left.\n * Requires BOTH a live recorder and a live session id: starting one from\n * nothing would strand a poll interval past the plugin's own teardown, which\n * finalizes the recorder before its last browser calls.\n */\nexport function needsScreencastRotation(\n ctx: Pick<SessionInitCtx, 'screencastRecorder' | 'screencastSessionId'>,\n sessionId: string | undefined\n): boolean {\n return (\n ctx.screencastRecorder !== undefined &&\n sessionId !== undefined &&\n ctx.screencastSessionId !== sessionId\n )\n}\n\n/**\n * Observe one command. Rotation is fire-and-forget — a command must not wait on\n * a video encode — and parked on `ctx.screencastRotation`, which both latches\n * out the flood of commands arriving before it completes and gives teardown\n * something to await, so a rotation cannot start a recorder after the plugin\n * has already finalized.\n */\nexport function rotateScreencastForSession(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): void {\n if (ctx.screencastRotation || !ctx.screencastOptions.enabled) {\n return\n }\n const sessionId = browser.sessionId ?? undefined\n if (!needsScreencastRotation(ctx, sessionId)) {\n return\n }\n log.info(`🎬 Session replaced — rotating screencast to ${sessionId}`)\n ctx.screencastRotation = ctx\n .finalizeCurrentScreencast()\n .then(() => startScreencast(ctx, browser, sessionId))\n .catch((err) =>\n log.warn(`Screencast rotation failed: ${errorMessage(err)}`)\n )\n .finally(() => {\n ctx.screencastRotation = undefined\n })\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","/**\n * The plugin's screencast seam: what a run's mode and filmstrip/video options\n * imply for the recorder, plus the per-run frame buffer the trace filmstrip and\n * the per-test video slices are cut from.\n *\n * The recorder itself is `screencast.ts`, and binding it to a session — or\n * rotating it when the session is replaced — is `screencast-session.ts`. This is\n * the plugin-level state those two hang off, split out of `index.ts` so the\n * plugin keeps only its accessor bag and thin delegators.\n */\n\nimport { accumulatedScreencastFrames } from '@wdio/devtools-core'\nimport { SCREENCAST_DEFAULTS } from '@wdio/devtools-shared'\nimport { finalizeCurrentScreencast } from './session-init.js'\nimport type { SessionInitCtx } from './session-init.js'\nimport type { ScreencastRecorder } from './screencast.js'\nimport type {\n DevToolsMode,\n ScreencastFrame,\n ScreencastOptions,\n TraceVideoPolicy\n} from './types.js'\n\n/** The already-defaulted options the recorder decision is made from — read the\n * resolved `filmstrip`/`video`, never the raw `options.filmstrip`, or a\n * default-on filmstrip leaves the recorder inert while the plugin's own options\n * claim it is running. */\nexport interface ScreencastResolutionInput {\n mode: DevToolsMode\n filmstrip: boolean\n video: TraceVideoPolicy\n screencast: ScreencastOptions | undefined\n}\n\nexport interface ResolvedScreencast {\n /** What the plugin exposes as `options.screencast`. */\n options: ScreencastOptions\n /** Config warning to log, when the mode makes the option meaningless. */\n warning?: string\n}\n\n/**\n * Whether this run records a screencast, and under which options. Filmstrip OR a\n * produce-only per-test video drives the recorder in trace mode; bare screencast\n * with neither is a live-mode feature and is dropped there.\n */\nexport function resolveScreencastOptions(\n input: ScreencastResolutionInput\n): ResolvedScreencast {\n const screencast = input.screencast ?? {}\n if (input.mode !== 'trace') {\n return { options: screencast }\n }\n if (input.filmstrip || input.video !== 'off') {\n return { options: { ...screencast, enabled: true } }\n }\n if (screencast.enabled === true) {\n return {\n options: {},\n warning: 'trace mode: ignoring screencast option (live-mode feature)'\n }\n }\n return { options: screencast }\n}\n\n/**\n * One run's screencast state: the live recorder and the session it is bound to,\n * an in-flight rotation, and the frames drained from recorders already finalized.\n */\nexport class PluginScreencast {\n /** Recorder options with the shared defaults applied. */\n readonly options: ScreencastOptions\n recorder?: ScreencastRecorder\n sessionId?: string\n /** In-flight recorder rotation — see `screencast-session.ts`. */\n rotation?: Promise<void>\n\n // Snapshotted before each recorder is nulled, so the export isn't blank.\n #accumulated: ScreencastFrame[] = []\n readonly #keepFrames: boolean\n\n /** `keepFrames` when the trace filmstrip or a per-test video will consume\n * them; without either, a finalized recorder's frames are of no further use. */\n constructor(options: ScreencastOptions, keepFrames: boolean) {\n this.options = { ...SCREENCAST_DEFAULTS, ...options }\n this.#keepFrames = keepFrames\n }\n\n /** Every frame this run has produced — the finalized recorders' plus the live\n * one's, so a mid-run slice flush (which fires before the recorder is\n * drained) isn't blank. */\n get frames(): ScreencastFrame[] {\n return accumulatedScreencastFrames(this.#accumulated, this.recorder)\n }\n\n /** Drain the live recorder into the run buffer, then stop and clear it. The\n * drain has to precede the stop: `finalizeCurrentScreencast` nulls the\n * recorder, and with it the only reference to its frames. */\n async finalize(ctx: SessionInitCtx): Promise<void> {\n if (this.#keepFrames && this.recorder) {\n this.#accumulated.push(...this.recorder.frames)\n }\n await finalizeCurrentScreencast(ctx)\n }\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 {\n WS_SCOPE,\n type CucumberPickle,\n type CucumberPickleStep\n} from '@wdio/devtools-shared'\n\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 { captureNativeAssertions } from './helpers/nativeAssertions.js'\nimport { scanFeatureFile } from './helpers/featureFileScan.js'\nimport { parseCucumberScenario } from './helpers/utils.js'\nimport {\n recordTestSliceBoundary,\n flushTestSlice,\n type TestSliceCtx\n} from './trace-slices.js'\n\nconst log = logger('@wdio/nightwatch-devtools:cucumber')\n\n/** Cucumber's result shape varies across major versions; we pin only the\n * status field we read. Pickle shapes come from shared. */\nexport interface CucumberResult {\n status?: string\n}\n\nexport interface CucumberLifecycleCtx extends TestSliceCtx {\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 recordAttempt(uid: string, specFile?: string): number\n recordOutcome(uid: string, state: TestStats['state']): void\n emitTestArtifacts(uid: string | undefined, failed: boolean): Promise<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\nfunction captureFeatureSources(\n ctx: CucumberLifecycleCtx,\n paths: string[]\n): void {\n for (const p of paths) {\n ctx.sessionCapturer.captureSource(p).catch(() => {})\n }\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 captureFeatureSources(ctx, capturedPaths)\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 recordAttempt: (uid, specFile) => ctx.recordAttempt(uid, specFile)\n })\n attachScenarioToFeature(ctx, featureSuite, scenarioSuite)\n // The scenario is the `test` unit; its steps are the leaf metadata entries.\n recordTestSliceBoundary(ctx, featureUri, scenarioSuite.uid)\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 // Stamp this attempt's real outcome so spec/session retention doesn't\n // collapse to the retry-stable suite's last-overwritten state.\n ctx.recordOutcome(scenario.uid, scenarioState)\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 // The trace capture, slice flush, and per-test artifacts need a live\n // WebDriver session — by this hook (cucumber After order:-1) the browser is\n // already quit. They run in captureCucumberScenarioBeforeQuit (order:1000,\n // pre-quit); this hook keeps only the settled-outcome bookkeeping.\n } catch (err) {\n log.error(`Failed to finalize Cucumber scenario: ${errorMessage(err)}`)\n }\n}\n\n/**\n * Live-session work for a cucumber scenario — runs at the cucumber `After`\n * `order:1000` hook, BEFORE Nightwatch quits the browser (its quit hook is\n * `order:0`; `finalizeCucumberScenario` is `order:-1`, after quit, so it reads\n * the settled outcome). Trace capture, the slice flush, and per-test artifacts\n * all need a live WebDriver session — at finalize the session is gone and the\n * flush bails, writing no zip. Trace test-state may be pre-outcome for a\n * scenario that fails on a Nightwatch assertion (see CLAUDE.md known debt).\n */\nexport async function captureCucumberScenarioBeforeQuit(\n ctx: CucumberLifecycleCtx,\n browser: NightwatchBrowser,\n result: CucumberResult\n): Promise<void> {\n const scenario = ctx.getCurrentScenarioSuite()\n try {\n // Emit native asserts (assert.titleContains, …) while the session is live —\n // Nightwatch's afterEach path skips the cucumber runner, so this is the only\n // place scenario asserts get captured. drainNativeAssertCalls() is empty when\n // captureAssertions is off. `currentTest` is undefined because a cucumber run\n // installs Nightwatch's no-op SimplifiedReporter and never fills a results\n // bag; each row's pass/fail comes from its own settled promise instead\n // (`observedAssertOutcome`).\n const assertCalls = ctx.browserProxy?.drainNativeAssertCalls() ?? []\n if (assertCalls.length > 0) {\n await captureNativeAssertions(\n ctx.sessionCapturer,\n browser,\n undefined,\n scenario?.uid,\n assertCalls\n )\n }\n // Anchored: a scenario ending on a navigation leaves the destination's\n // async initial anchor unrun, so its DOM would be absent from the slice.\n await ctx.sessionCapturer.captureTrace(browser, true)\n flushTestSlice(ctx)\n await ctx.emitTestArtifacts(\n scenario?.uid,\n cucumberResultToTestState(result) === TEST_STATE.FAILED\n )\n } catch (err) {\n log.error(\n `Failed to capture Cucumber scenario before quit: ${errorMessage(err)}`\n )\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 /** Records the scenario start under its (retry-stable) uid + feature file and\n * returns the 0-based attempt number stamped on every step. Omitted → 0. */\n recordAttempt?: (scenarioUid: string, specFile?: string) => number\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 attempt: number,\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 // Scope by the scenario uid (which carries the scenario line) so identical\n // step text in sibling scenarios and outline example rows stays distinct.\n uid: deterministicUid(featureUri, `step:${scenarioUid}:${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 // Scenario-level attempt from the tracker (0 first run, +1 per retry);\n // flows to TestOutcome.attempt via collectSuiteTestMetadata.\n retries: attempt,\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. The\n // scenario line disambiguates outline example rows that share a name.\n const scenarioUid = deterministicUid(\n featureUri,\n `scenario:${scenarioName}:${scenarioLine}`\n )\n const attempt = input.recordAttempt?.(scenarioUid, featureUri) ?? 0\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(\n buildScenarioStepTest(input, scenarioUid, attempt, i)\n )\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 * Trace-slice boundary recording for `spec` and `test` granularity.\n *\n * `spec` slices are recorded/flushed at each spec-file transition (unchanged\n * from the original inline `beforeEach` logic). For `test`, Nightwatch builds\n * test outcomes into the suite tree in place — a retry overwrites the previous\n * attempt's state (regular tests reuse the same test object; cucumber replaces\n * the scenario suite). So each attempt's slice is flushed at its own\n * test/scenario END, before the next attempt can overwrite the outcome the\n * flush reads. Regular tests drive this from test-lifecycle, cucumber scenarios\n * from cucumber-lifecycle; both share this module.\n */\n\nimport {\n findFlushableRange,\n recordSliceBoundary,\n recordSpecBoundary,\n type SpecBoundaryContext,\n type SpecRange,\n type TraceArtifact\n} from '@wdio/devtools-core'\nimport type { SessionCapturer } from './session.js'\nimport type { TraceGranularity } from './types.js'\n\nexport interface TestSliceCtx {\n readonly sessionCapturer: SessionCapturer\n readonly traceMode: boolean\n readonly traceGranularity: TraceGranularity\n readonly specRanges: SpecRange[]\n readonly flushedSpecs: Set<string>\n flushTraceRange(range: SpecRange): Promise<TraceArtifact | undefined>\n}\n\nfunction sliceActive(ctx: TestSliceCtx): boolean {\n return ctx.traceMode && ctx.traceGranularity === 'test'\n}\n\nfunction boundaryContext(ctx: TestSliceCtx): SpecBoundaryContext {\n return {\n specRanges: ctx.specRanges,\n flushedSpecs: ctx.flushedSpecs,\n capturer: ctx.sessionCapturer\n }\n}\n\nfunction flushPrevious(ctx: TestSliceCtx, prevRange: SpecRange | null): void {\n if (!prevRange) {\n return\n }\n // flushTraceRange (→ core flushRangeLogged) logs+swallows on failure.\n void ctx.flushTraceRange(prevRange)\n}\n\n/**\n * Record a spec-file boundary at test/scenario start and eagerly flush the\n * previous spec's slice. No-op for `session`/`test` granularity (core returns\n * null). Preserves the original `beforeEach` behavior verbatim.\n */\nexport function recordSpecSliceBoundary(\n ctx: TestSliceCtx,\n specFile: string\n): void {\n const prevRange = recordSpecBoundary(\n boundaryContext(ctx),\n specFile,\n ctx.traceGranularity\n )\n flushPrevious(ctx, prevRange)\n}\n\n/**\n * Record a per-test slice boundary at test/scenario start. No-op outside trace\n * mode + `test` granularity. Retries push a distinct range (core keys a repeat\n * `${testUid}-retry${n}`), so each attempt becomes its own artifact.\n */\nexport function recordTestSliceBoundary(\n ctx: TestSliceCtx,\n specFile: string,\n testUid: string\n): void {\n if (!sliceActive(ctx)) {\n return\n }\n recordSliceBoundary(boundaryContext(ctx), 'test', specFile, testUid)\n}\n\n/**\n * Flush the current test's slice at test/scenario end — before a retry can\n * overwrite its outcome in the suite tree. Fire-and-forget; the finalize pass\n * is the safety net for any range this misses. No-op outside trace + `test`.\n */\nexport function flushTestSlice(ctx: TestSliceCtx): void {\n if (!sliceActive(ctx)) {\n return\n }\n const range = findFlushableRange(ctx.specRanges)\n if (!range) {\n return\n }\n // flushTraceRange (→ core flushRangeLogged) logs+swallows on failure.\n void ctx.flushTraceRange(range)\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 { 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 { matchTestName } from './helpers/runningTest.js'\nimport { extractTestMetadata, determineTestState } from './helpers/utils.js'\nimport { recordTestSliceBoundary, type TestSliceCtx } from './trace-slices.js'\n\nconst log = logger('@wdio/nightwatch-devtools:test-lifecycle')\n\nexport interface TestLifecycleCtx extends TestSliceCtx {\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 recordAttempt(uid: string, specFile?: string): number\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 return (\n matchTestName(currentTest?.name, testNames) ||\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 specFile: string | null\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 // Nightwatch has no per-test retry index; the tracker is the retry signal.\n test.retries = ctx.recordAttempt(test.uid, specFile ?? undefined)\n if (specFile) {\n recordTestSliceBoundary(ctx, specFile, test.uid)\n }\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.resetTestTracking()\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"],"mappings":"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","names":["xpath","xpath","path","xpath","resolve","start","fs","fs","path","fs","path","fs","path","fs","path","require","resolve","fs","path","fs","createRequire","require","createRequire","log","path","log","fs","path","createHash","resolve","resolve","errorMessage","request","path","fs","path","path","start","resolve","path","fs","path","createRequire","require","createRequire","path","fs","resolve","log","fs","os","path","log","path","fs","os","fs","path","fs","fs","resolve","fs","logger","fs","path","os","logger","fs","path","parseStackTrace","generateStableUid","parseStackTrace","log","logger","resolve","logger","logger","logger","log","logger","path","resolve","captureActionSnapshot","log","logger","captureActionSnapshot","resolve","logger","log","logger","generateStableUid","generateStableUid","logger","logger","log","logger","collapsedAssertResult","path","log","logger","path","logger","logger","log","logger","log","logger","log","logger","resolve","logger","log","logger","logger","fs","path","log","logger","logger","fs","path","path","fs","logger","log","logger","resolve","log","logger","resolve","log","logger"]}
1
+ {"version":3,"sources":["../src/index.ts","../../shared/src/files.ts","../../shared/src/locator-dialect.ts","../../shared/src/routes.ts","../../shared/src/runner.ts","../../shared/src/snapshot-format.ts","../../shared/src/timing.ts","../../shared/src/trace-actions.ts","../../shared/src/trace-file.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/artifact-naming.ts","../../core/src/artifacts-manifest.ts","../../core/src/allure-artifacts.ts","../../core/src/screenshot-artifact.ts","../../core/src/video-slice.ts","../../core/src/video-encoder.ts","../../core/src/trace-retention.ts","../../core/src/attempt-tracker.ts","../../core/src/assert-patcher.ts","../../core/src/stack.ts","../../core/src/sha1.ts","../../core/src/trace-sources.ts","../../core/src/trace-hierarchy.ts","../../core/src/trace-action-events.ts","../../core/src/trace-console.ts","../../core/src/trace-exporter.ts","../../core/src/trace-snapshots.ts","../../core/src/screencast-trace.ts","../../core/src/trace-frame-snapshots.ts","../../core/src/trace-har.ts","../../core/src/trace-zip-writer.ts","../../core/src/trace-mutations.ts","../../core/src/trace-transcript.ts","../../core/src/spec-trace-helpers.ts","../../core/src/uid.ts","../../core/src/trace-finalizer.ts","../../core/src/script-loader.ts","../../core/src/bidi-preload.ts","../../core/src/terminal-throttle.ts","../../core/src/finalize-screencast.ts","../../core/src/input-dispatch.ts","../../core/src/output-dir.ts","../../core/src/performance-capture.ts","../../core/src/read-value-locators.ts","../../core/src/retry-tracker.ts","../../core/src/run-id.ts","../../core/src/screencast.ts","../../core/src/session-capturer.ts","../../core/src/suite-helpers.ts","../../core/src/test-discovery.ts","../../core/src/test-reporter.ts","../src/trace-context.ts","../src/test-artifacts.ts","../src/helpers/assertCapture.ts","../src/run-lifecycle.ts","../src/plugin-global-key.ts","../src/constants.ts","../src/helpers/utils.ts","../src/session-init.ts","../src/session.ts","../src/helpers/webdriverHttp.ts","../src/action-snapshot.ts","../src/helpers/perfLogs.ts","../src/reporter.ts","../src/helpers/runningTest.ts","../src/helpers/testManager.ts","../src/helpers/suiteManager.ts","../src/helpers/browserProxy.ts","../src/helpers/callbackError.ts","../src/helpers/assertTarget.ts","../src/helpers/serializeCommandResult.ts","../src/helpers/nativeAssertions.ts","../src/helpers/nativeAssertRecorder.ts","../src/screencast-session.ts","../src/screencast.ts","../src/plugin-screencast.ts","../src/event-hub.ts","../src/cucumber-lifecycle.ts","../src/helpers/cucumberResult.ts","../src/helpers/cucumberScenarioBuilder.ts","../src/helpers/featureFileScan.ts","../src/trace-slices.ts","../src/test-lifecycle.ts","../src/helpers/specFileResolver.ts","../src/helpers/closePreviousTest.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 finalizeTraceExport,\n flushRangeLogged,\n resolveAdapterOutputDir,\n TestAttemptTracker,\n tracePolicyModeWarning,\n type SpecRange,\n type TraceArtifact,\n type TraceExportContext\n} from '@wdio/devtools-core'\nimport { buildTraceContext } from './trace-context.js'\nimport { emitTestArtifacts } from './test-artifacts.js'\nimport { wireAssertCapture } from './helpers/assertCapture.js'\nimport { stop as stopBackend } from '@wdio/devtools-backend'\nimport {\n REUSE_ENV,\n type CucumberPickle,\n type CucumberPickleStep\n} 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 {\n PluginScreencast,\n resolveScreencastOptions\n} from './plugin-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 SuiteStats,\n TestStats\n} from './types.js'\nimport { registerEventHandlers as registerEventHandlersImpl } from './event-hub.js'\nimport {\n cucumberBefore as cucumberLifecycleBefore,\n cucumberAfter as cucumberLifecycleAfter,\n captureCucumberScenarioBeforeQuit as cucumberLifecycleBeforeQuit,\n cucumberBeforeStep as cucumberLifecycleBeforeStep,\n cucumberAfterStep as cucumberLifecycleAfterStep,\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 { ensureSessionInitialized } from './session-init.js'\nimport { captureNativeAssertions } from './helpers/nativeAssertions.js'\nimport { flushTestSlice, recordSpecSliceBoundary } from './trace-slices.js'\nimport {\n getTestIcon,\n incrementCounters,\n buildPluginMetadataOptions,\n nightwatchRunnerId\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 /** Every trace/video artifact seen this run (retained or not), for the\n * end-of-run artifacts manifest. Populated via the context's onArtifact. */\n #artifacts: TraceArtifact[] = []\n\n /** In-flight fire-and-forget eager/boundary slice flushes, awaited at finalize\n * so the last slice's write + its manifest entry land before teardown. */\n #traceFlushes: Promise<unknown>[] = []\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 #screencast: PluginScreencast\n #bidiEnabled = false\n #armedSessionId: string | undefined\n\n /** Wall-clock ms at the current test/scenario start — the lower bound of that\n * test's action-snapshot and video-frame window (per-test artifact slicing). */\n #currentTestStartWallTime = 0\n\n // Nightwatch `--retries` and cross-worker reruns may reset this in-process\n // tracker; only retries that re-enter this process's start hook are counted.\n #attemptTracker = new TestAttemptTracker()\n\n constructor(options: DevToolsOptions = {}) {\n const mode = options.mode ?? 'live'\n // Resolve the defaults ONCE and gate on the resolved values: reading the raw\n // `options.filmstrip === true` left the default-on filmstrip inert unless a\n // config set it explicitly, so the recorder never started while\n // `this.options.filmstrip` claimed it was on.\n const filmstrip = options.filmstrip ?? true\n const video = options.video ?? 'off'\n const screencast = resolveScreencastOptions({\n mode,\n filmstrip,\n video,\n screencast: options.screencast\n })\n if (screencast.warning) {\n log.warn(screencast.warning)\n }\n this.options = {\n port: options.port ?? 3000,\n hostname: options.hostname ?? 'localhost',\n screencast: screencast.options,\n bidi: options.bidi ?? false,\n captureAssertions: options.captureAssertions ?? true,\n mode,\n traceFormat: options.traceFormat ?? 'zip',\n traceGranularity: options.traceGranularity ?? 'session',\n tracePolicy: options.tracePolicy ?? 'on',\n filmstrip,\n screenshot: options.screenshot ?? 'off',\n video,\n // No live Allure signal in Nightwatch (produce-only), so no auto-detect:\n // off by default, opt-in via the option.\n emitArtifactsManifest: options.emitArtifactsManifest ?? false\n }\n const policyWarning = tracePolicyModeWarning(options.tracePolicy, mode)\n if (policyWarning) {\n log.warn(policyWarning)\n }\n this.#screencast = new PluginScreencast(\n screencast.options,\n filmstrip || video !== 'off'\n )\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.#screencast.options\n },\n get bidiEnabled() {\n return self.#bidiEnabled\n },\n get captureAssertions() {\n return self.options.captureAssertions\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 armedSessionId() {\n return self.#armedSessionId\n },\n set armedSessionId(v) {\n self.#armedSessionId = 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.#screencast.recorder\n },\n set screencastRecorder(v) {\n self.#screencast.recorder = v\n },\n get screencastSessionId() {\n return self.#screencast.sessionId\n },\n set screencastSessionId(v) {\n self.#screencast.sessionId = v\n },\n get screencastRotation() {\n return self.#screencast.rotation\n },\n set screencastRotation(v) {\n self.#screencast.rotation = v\n },\n get configPath() {\n return self.#configPath\n },\n get runner() {\n return nightwatchRunnerId(self.#isCucumberRunner)\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 self.#attemptTracker.reset()\n },\n recordAttempt: (uid, specFile) =>\n self.#attemptTracker.recordStart(uid, specFile),\n recordOutcome: (uid, state) =>\n self.#attemptTracker.recordOutcome(uid, state),\n attemptFor: (uid) => self.#attemptTracker.attemptFor(uid),\n buildMetadataOptions: () => self.#buildMetadataOptions(),\n ensureSessionInitialized: (b) => self.#ensureSessionInitialized(b),\n finalizeCurrentScreencast: () => self.#finalizeCurrentScreencast(),\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 get traceMode() {\n return self.options.mode === 'trace'\n },\n get traceGranularity() {\n return self.options.traceGranularity\n },\n get specRanges() {\n return self.#specRanges\n },\n get flushedSpecs() {\n return self.#flushedSpecs\n },\n flushTraceRange: (range) => self.#flushSpecTrace(range),\n emitTestArtifacts: (uid, failed) => self.#emitTestArtifacts(uid, failed)\n }\n return this.#internals\n }\n\n /** Boundary cast: currentTest is Nightwatch's loose bag; only uid is read. */\n #currentTestUid(): string | undefined {\n return (this.#currentTest as { uid?: string } | null)?.uid\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 if (this.options.captureAssertions) {\n wireAssertCapture(\n () => this.sessionCapturer,\n // Rows are tagged per-`it` (BrowserProxy.rowTestUid); `#currentTestUid`\n // — the lifecycle test, which the BDD interface pins to the module's\n // first `it` — is only the fallback, and stays the artifact/slice key.\n () => this.browserProxy?.rowTestUid() ?? this.#currentTestUid()\n )\n }\n }\n\n async #ensureSessionInitialized(browser: NightwatchBrowser) {\n await ensureSessionInitialized(this.#getInternals(), browser)\n }\n\n async #finalizeCurrentScreencast(): Promise<void> {\n await this.#screencast.finalize(this.#getInternals())\n }\n\n async cucumberBefore(browser: NightwatchBrowser, pickle: CucumberPickle) {\n this.#currentTestStartWallTime = Date.now()\n await cucumberLifecycleBefore(this.#getInternals(), browser, pickle)\n }\n\n async cucumberBeforeQuit(browser: NightwatchBrowser, result: CucumberResult) {\n await cucumberLifecycleBeforeQuit(this.#getInternals(), browser, result)\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 specFile: string | null\n ): Promise<void> {\n await startNextTest(\n this.#getInternals(),\n currentSuite,\n currentTestName,\n processedTests,\n specFile\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 this.#currentTestStartWallTime = Date.now()\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 if (fullPath) {\n recordSpecSliceBoundary(this.#getInternals(), fullPath)\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(\n currentSuite,\n currentTestName,\n processedTests,\n fullPath\n )\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 if (this.options.captureAssertions) {\n await captureNativeAssertions(\n this.sessionCapturer,\n browser,\n browser.currentTest as NightwatchCurrentTest | undefined,\n this.#currentTestUid(),\n this.browserProxy.drainNativeAssertCalls()\n )\n }\n // Anchored: a test ending on a navigation leaves the destination's\n // async initial anchor unrun, so its DOM would be absent from the slice.\n await this.sessionCapturer.captureTrace(browser, true)\n // Flush this test's slice before the next test overwrites its outcome.\n flushTestSlice(this.#getInternals())\n const results = (browser.currentTest as NightwatchCurrentTest)?.results\n const failed =\n !!results && ((results.errors ?? 0) > 0 || (results.failed ?? 0) > 0)\n await this.#emitTestArtifacts(this.#currentTestUid(), failed)\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 /** Directory for produced artifacts — next to the test file, else the config,\n * else cwd (mirrors #traceContext's resolution). */\n get #outputDir(): string {\n return resolveAdapterOutputDir({\n testFilePath: this.browserProxy?.getCurrentTestFullPath?.() ?? undefined,\n configPath: this.#configPath\n })\n }\n\n /** Produce (never attach — no live Allure API) this test's per-test screenshot\n * and video slice. No-op outside trace mode + `test` granularity (core-gated).\n * Shared by the per-test `afterEach` and the cucumber per-scenario finalize. */\n async #emitTestArtifacts(\n uid: string | undefined,\n failed: boolean\n ): Promise<void> {\n const attempt = uid ? this.#attemptTracker.attemptFor(uid) : undefined\n await emitTestArtifacts({\n mode: this.options.mode,\n granularity: this.options.traceGranularity,\n screenshotPolicy: this.options.screenshot,\n videoPolicy: this.options.video,\n failed,\n actionSnapshots: this.sessionCapturer.actionSnapshots,\n frames: this.#screencast.frames,\n startWallTime: this.#currentTestStartWallTime,\n outcomes: uid ? this.#attemptTracker.forTest(uid, attempt) : [],\n uid,\n attempt,\n sessionId: this.sessionCapturer?.metadata?.sessionId,\n outputDir: this.#outputDir,\n captureFormat: this.#screencast.options.captureFormat,\n onArtifact: (a) => this.#artifacts.push(a),\n onLog: (level, msg) => log[level](msg)\n })\n }\n\n async after(browser?: NightwatchBrowser) {\n // Drain first: a rotation still in flight would otherwise start its new\n // recorder after this finalize and strand the poll interval.\n await this.#screencast.rotation\n await this.#finalizeCurrentScreencast()\n try {\n await this.#finalizeAllSuites(browser)\n this.#logRunSummary()\n if (this.options.mode === 'trace') {\n await this.#writeTraceIfNeeded()\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 /** Thin wrapper so boundary flushes and the final flush share one path.\n * flushRangeLogged logs+swallows a failed flush (shared spec/test string) so\n * the fire-and-forget boundary callers don't each re-implement the guard. */\n #flushSpecTrace(range: SpecRange): Promise<TraceArtifact | undefined> {\n const sessionId = this.sessionCapturer.metadata?.sessionId\n if (!sessionId) {\n return Promise.resolve(undefined)\n }\n // Track the promise (before it's added to awaitPending) so finalize awaits\n // this fire-and-forget flush — its context snapshot already excludes it.\n const flush = flushRangeLogged(this.#traceContext(sessionId), range)\n this.#traceFlushes.push(flush)\n return flush\n }\n\n /** Assemble the framework-agnostic trace-export context from plugin state.\n * Output dir ignores the spec range — nightwatch writes next to config. */\n #traceContext(sessionId: string): TraceExportContext {\n return buildTraceContext(\n {\n mode: this.options.mode,\n policy: this.options.tracePolicy,\n granularity: this.options.traceGranularity,\n format: this.options.traceFormat,\n capturer: this.sessionCapturer,\n suites: this.suiteManager.getAllSuites().values(),\n outcomes: this.#attemptTracker,\n ranges: this.#specRanges,\n flushed: this.#flushedSpecs,\n artifacts: this.#artifacts,\n traceFlushes: this.#traceFlushes,\n emitArtifactsManifest: this.options.emitArtifactsManifest,\n screencastFrames: this.options.filmstrip\n ? this.#screencast.frames\n : undefined,\n configPath: this.#configPath,\n testFilePath:\n this.browserProxy?.getCurrentTestFullPath?.() ?? undefined,\n log: (level, msg) => log[level](msg)\n },\n sessionId\n )\n }\n\n async #writeTraceIfNeeded(): Promise<void> {\n const sessionId = this.sessionCapturer?.metadata?.sessionId\n if (this.options.mode !== 'trace' || !this.sessionCapturer || !sessionId) {\n return\n }\n await finalizeTraceExport(this.#traceContext(sessionId))\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","// Which locator each runner is best handed: the grammar its text form is\n// written in, where that text form sits in the capture's branch order, and what\n// it needs told when the grammar is XPath. The one fact table behind both sides\n// — the page capture reads it to decide what to emit, the player reads it to\n// caption what it hands the user.\n//\n// WebdriverIO has its own `tag*=text` form and auto-detects a leading `//`;\n// Selenium and Nightwatch have no text form, and neither auto-detects XPath —\n// Selenium takes it via `By.xpath`, Nightwatch via its `'xpath'` strategy.\n\nimport type { TestRunnerId } from './types.js'\n\n/** Text-locator grammar a runner resolves natively. Only the capture's text\n * branch varies by it; every other branch is portable CSS. */\nexport type TextLocatorDialect = 'webdriverio' | 'xpath'\n\n/** Where the text branch sits among the capture's branches. `first` prefers the\n * readable text form; `fallback` reaches it only when no unique CSS locator\n * exists, still ahead of the meaningless positional path. */\nexport type TextBranchOrder = 'first' | 'fallback'\n\nexport interface LocatorDialect {\n text: TextLocatorDialect\n textBranch: TextBranchOrder\n /** The call that makes the runner treat a captured XPath locator as XPath.\n * Undefined where naming one would be wrong (WebdriverIO needs none) or a\n * guess (an unidentified recorder). */\n xpathHint?: string\n}\n\n/** An unidentified recorder gets XPath text first: it is the portable form and\n * the readable one, so it is the safe answer rather than a guess. */\nexport function locatorDialect(\n runner: TestRunnerId | undefined\n): LocatorDialect {\n switch (runner) {\n case 'mocha':\n case 'jasmine':\n case 'cucumber':\n return { text: 'webdriverio', textBranch: 'first' }\n // Every Selenium locator is wrapped in a `By.…`, so `By.xpath` asks no more\n // of the user than `By.css` — the text form stays first.\n case 'selenium-webdriver':\n return { text: 'xpath', textBranch: 'first', xpathHint: 'By.xpath()' }\n // The only runner where a bare locator string is read under a default CSS\n // strategy, so it is the only one where XPath costs the user something.\n case 'nightwatch':\n case 'nightwatch-cucumber':\n return {\n text: 'xpath',\n textBranch: 'fallback',\n xpathHint: \"useXpath() or locateStrategy: 'xpath'\"\n }\n case undefined:\n return { text: 'xpath', textBranch: 'first' }\n }\n}\n\n/** A captured locator is portable CSS, or XPath where the element's own text\n * identifies it */\nexport function isXPathLocator(locator: string): boolean {\n // No CSS selector can open with `/` or `(`, so this never claims one that\n // querySelector could have parsed. Covers `//a[…]`, `/html/…`, `./a`, `.//a`\n // and the indexed `(//a[…])[2]`.\n return /^\\(*\\.{0,2}\\//.test(locator)\n}\n\n/** Tag name an XPath locator is scoped to, for a consumer that reports the tag\n * it matched. Undefined for CSS and for a tag-less expression (`//*[…]`). */\nexport function xpathLocatorTag(locator: string): string | undefined {\n return /^\\/\\/([a-z][a-z0-9-]*)\\[/.exec(locator)?.[1]\n}\n\n/** WebdriverIO's text-locator forms — `tag*=Contains`, `tag=Exact`, tag optional. */\nconst TEXT_LOCATOR_RE = /^([a-z][a-z0-9-]*)?(\\*?)=(.+)$/i\n\n/** A text locator in either dialect, decomposed. `tag` is `*` when the form\n * names none; `partial` marks a contains-match rather than an exact one.\n * Undefined for every non-text locator, including the `concat()`-stitched\n * literal that text carrying both quote kinds produces — that one is left to\n * exact comparison rather than parsed back. */\nfunction textLocatorParts(\n locator: string\n): { tag: string; text: string; partial: boolean } | undefined {\n const xpath =\n /^\\/\\/([a-z][a-z0-9-]*|\\*)\\[contains\\(\\.,\\s*([\"'])(.*)\\2\\)\\]$/i.exec(\n locator\n )\n if (xpath) {\n return { tag: xpath[1]!.toLowerCase(), text: xpath[3]!, partial: true }\n }\n const wdio = TEXT_LOCATOR_RE.exec(locator)\n if (!wdio) {\n return undefined\n }\n return {\n tag: wdio[1]?.toLowerCase() ?? '*',\n text: wdio[3]!,\n partial: wdio[2] === '*'\n }\n}\n\n/** Whether a locator the page capture generated and one a test wrote denote the\n * same element. Both sides are decomposed from either dialect, because which of\n * the two the capture emits depends on the runner while what a test writes does\n * not — a `===` drops the input point whenever they differ. */\nexport function locatorsMatch(captured: string, written: string): boolean {\n if (captured === written) {\n return true\n }\n const target = textLocatorParts(captured)\n const test = textLocatorParts(written)\n if (!target || !test) {\n return false\n }\n if (target.tag !== '*' && test.tag !== '*' && target.tag !== test.tag) {\n return false\n }\n // The capture stores the element's whole trimmed text, so a contains-match has\n // to be a substring of it while an exact one names it in full.\n return test.partial\n ? target.text.includes(test.text)\n : target.text === test.text\n}\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 * Query params on the worker upgrade. Both answer the same question for the\n * backend — does this socket continue the current run, or start a new one? —\n * which decides whether accumulated run state is kept or wiped. A run opens\n * one worker socket per spec file, so treating every connect as a new run\n * drops earlier specs' data and Preserve & Rerun then has nothing to diff.\n */\nexport const WORKER_WS_QUERY = {\n /** `1` when the adapter reopened this socket mid-run (e.g. after a session\n * change from `browser.end()` or `reloadSession()`). */\n reconnect: 'reconnect',\n /** Identity of the run this worker belongs to; shared by every worker the\n * same run forks. */\n runId: 'runId'\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. RUN_ID is stamped\n * before any worker forks so every worker of one run inherits it and reports\n * the same run identity (see `WORKER_WS_QUERY.runId`).\n */\nexport const RUNNER_ENV = {\n RUN_ID: 'DEVTOOLS_RUN_ID',\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","// Single source of truth for the accessibility-snapshot text format (the\n// `-snapshot.txt` trace resource). The producer (core's element-snapshot\n// serializers) and the consumer (the app's A11y-tree parser) both reference\n// these, so the written and parsed grammar can't drift. Keep one canonical form\n// per concept — see CLAUDE.md § \"One source of truth per concept\".\n\n/** Indent step per tree depth (the header sits at one unit; nodes at depth+1). */\nexport const SNAPSHOT_INDENT_UNIT = ' '\n\n/** Prefix of the web page-header line (`[Page: <title> — <url>]`). */\nexport const SNAPSHOT_PAGE_HEADER = '[Page'\n\n/** Native-mobile platforms the mobile serializer writes a header for. */\nexport const SNAPSHOT_NATIVE_PLATFORMS = ['android', 'ios'] as const\n\nexport type SnapshotNativePlatform = (typeof SNAPSHOT_NATIVE_PLATFORMS)[number]\n\n/** Prefix of the native-mobile header line — bare `[android]` when the capture\n * knew neither device nor viewport (what the per-action capture passes), else\n * `[android — <device> (<width>×<height>)]`. */\nexport function snapshotNativeHeader(platform: SnapshotNativePlatform): string {\n return `[${platform}`\n}\n\n/** Whether a line is a snapshot's header rather than a tree node. Every\n * serializer opens with exactly one — `[Page …]` for web, `[<platform> …]` for\n * native — so both forms live in this one predicate and a consumer that only\n * knew the web form cannot render a native header as a node. */\nexport function isSnapshotHeaderLine(line: string): boolean {\n return (\n line.startsWith(SNAPSHOT_PAGE_HEADER) ||\n SNAPSHOT_NATIVE_PLATFORMS.some((platform) =>\n line.startsWith(snapshotNativeHeader(platform))\n )\n )\n}\n\n/** Separator between a node and its captured locator (rendered space-padded). */\nexport const SNAPSHOT_LOCATOR_DELIM = '→'\n\n/** Marks an inferred purpose before the locator (`<role> ∈ \"<purpose>\"`). */\nexport const SNAPSHOT_PURPOSE_TOKEN = '∈'\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\n/** Trace action class assertion commands map to. */\nexport const ASSERT_ACTION_CLASS = 'Assert'\n\n/** node:assert methods the core assert patcher wraps; the reader derives its\n * `Assert.<m>` → `assert.<m>` reverse entries from the same list. */\nexport const TRACKED_ASSERT_METHODS = [\n 'equal',\n 'strictEqual',\n 'deepEqual',\n 'deepStrictEqual',\n 'notEqual',\n 'notStrictEqual',\n 'notDeepEqual',\n 'notDeepStrictEqual',\n 'ok',\n 'fail',\n 'throws',\n 'doesNotThrow',\n 'rejects',\n 'doesNotReject',\n 'match',\n 'doesNotMatch'\n] as const\n\n// assert.<m> (node:assert), verify.<m> (nightwatch soft variants), and\n// expect.<m> (synthesized failing-matcher entries) all render as Assert.\n// Only FAILING expect-webdriverio matchers reach the command log today (via the\n// reporter); recording passing matchers needs a per-adapter capture change.\nconst ASSERT_COMMAND_RE = /^(?:assert|verify|expect)\\.(\\w+)$/\n\nexport function mapAssertCommand(command: string): TraceAction | null {\n const match = ASSERT_COMMAND_RE.exec(command)\n return match ? { class: ASSERT_ACTION_CLASS, method: match[1] } : null\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 // WDIO element reads — surfaced so query steps appear in the timeline the way\n // locator queries do in standard trace viewers. Adapters already capture\n // these; only the export allow-list kept them out.\n getText: { class: 'Element', method: 'getText' },\n getValue: { class: 'Element', method: 'getValue' },\n getAttribute: { class: 'Element', method: 'getAttribute' },\n getProperty: { class: 'Element', method: 'getProperty' },\n getCSSProperty: { class: 'Element', method: 'getCSSProperty' },\n getTagName: { class: 'Element', method: 'getTagName' },\n getLocation: { class: 'Element', method: 'getLocation' },\n getSize: { class: 'Element', method: 'getSize' },\n isDisplayed: { class: 'Element', method: 'isDisplayed' },\n isExisting: { class: 'Element', method: 'isExisting' },\n isEnabled: { class: 'Element', method: 'isEnabled' },\n isSelected: { class: 'Element', method: 'isSelected' },\n isClickable: { class: 'Element', method: 'isClickable' },\n isFocused: { class: 'Element', method: 'isFocused' },\n // Explicit user-facing waits (not the internal polling loops behind them).\n waitForDisplayed: { class: 'Element', method: 'waitForDisplayed' },\n waitForExist: { class: 'Element', method: 'waitForExist' },\n waitForEnabled: { class: 'Element', method: 'waitForEnabled' },\n waitForClickable: { class: 'Element', method: 'waitForClickable' },\n waitUntil: { class: 'Browser', method: 'waitForFunction' },\n // Nightwatch's wait family, normalized onto the WDIO names above. These are\n // written by the user and are the commands a Nightwatch test most often fails\n // on, so leaving them out of the allow-list meant the failing step produced no\n // row at all — and with no row, its error had nowhere to attach and neither the\n // action list nor the Errors tab showed the failure.\n waitForElementVisible: { class: 'Element', method: 'waitForDisplayed' },\n waitForElementNotVisible: { class: 'Element', method: 'waitForHidden' },\n waitForElementPresent: { class: 'Element', method: 'waitForExist' },\n waitForElementNotPresent: { class: 'Element', method: 'waitForNotExist' },\n // WDIO page/browser reads\n getTitle: { class: 'Page', method: 'getTitle' },\n getUrl: { class: 'Page', method: 'getUrl' },\n getPageSource: { class: 'Page', method: 'getPageSource' },\n // Selenium read aliases — normalized onto the WDIO names above so both runners\n // read identically. getText/getAttribute/getTagName/isDisplayed/isEnabled/\n // isSelected share the command name across runners and need no alias.\n getCssValue: { class: 'Element', method: 'getCSSProperty' },\n getRect: { class: 'Element', method: 'getRect' },\n getCurrentUrl: { class: 'Page', method: 'getUrl' }\n}\n\n/** Trace methods (ACTION_MAP values) that act at a point on the page — the\n * exporter emits a hit `point` for these so the player can mark where the\n * action landed. `fill` is included (typing focuses the element first). */\nexport const POINTABLE_METHODS: ReadonlySet<string> = new Set([\n 'click',\n 'dblclick',\n 'hover',\n 'tap',\n 'dragTo',\n 'scrollIntoViewIfNeeded',\n 'selectOption',\n 'fill'\n])\n\n/** Runner command names (native or trace method) that type into an element —\n * drives the timeline's keyboard glyph vs the pointer glyph. */\nconst KEYBOARD_COMMANDS: ReadonlySet<string> = new Set([\n 'setValue',\n 'sendKeys',\n 'addValue',\n 'clearValue',\n 'clear',\n 'keys',\n 'fill',\n 'press'\n])\n\nexport function isKeyboardCommand(command: string): boolean {\n return KEYBOARD_COMMANDS.has(command)\n}\n\n/** Runner-native pointer commands the trace vocabulary deliberately doesn't\n * name — Nightwatch's low-level mouse API has no meaningful trace method, so\n * adding it to ACTION_MAP would put noise in the timeline. Listed here because\n * they still dispatch, and a command that dispatches unrecognized keeps the\n * screenshot poll running straight through it. */\nconst UNMAPPED_POINTER_COMMANDS: ReadonlySet<string> = new Set([\n 'moveToElement',\n 'mouseButtonClick',\n 'mouseButtonDown',\n 'mouseButtonUp',\n 'rightClick',\n 'submitForm'\n])\n\n/**\n * Whether a runner command makes the driver dispatch trusted input into the\n * page. Derived from the vocabulary above rather than declared as a second list:\n * a command qualifies when it types (`isKeyboardCommand`) or when its trace\n * method acts at a point (`POINTABLE_METHODS`). A command wrongly classified as\n * non-dispatching is the costly direction — it leaves the screencast poll racing\n * the dispatch — so the unmapped pointer commands above are named explicitly.\n */\nexport function isInputDispatchingCommand(command: string): boolean {\n if (isKeyboardCommand(command) || UNMAPPED_POINTER_COMMANDS.has(command)) {\n return true\n }\n const action = ACTION_MAP[command]\n return action !== undefined && POINTABLE_METHODS.has(action.method)\n}\n","// The trace.zip file layout — the one definition of how a trace is packaged.\n//\n// `core` writes these entries and `backend` reads them, and the two packages\n// cannot import each other (backend depends on shared only). Before this module\n// both sides carried their own copies of these strings, so a rename in the\n// writer left every reader test green and the mismatch only surfaced when\n// someone opened a real trace. Import from here on both sides instead.\n//\n// The *event interfaces* deliberately stay separate: `core`'s are strict (the\n// writer always emits `pageId`, `apiName`, `params`, `title`) while the reader's\n// are permissive, because it also parses traces this repo did not write — see\n// `backend/src/trace-reader-types.ts`. Strict producer, lenient consumer; only\n// the names below have to agree.\n\n/** Entry names inside a trace.zip, and the directory prefix for blobs. Also the\n * filenames used by the unpacked `ndjson-directory` format. */\nexport const TRACE_ZIP_ENTRIES = {\n /** NDJSON action events. */\n trace: 'trace.trace',\n /** NDJSON HAR resource snapshots. Omitted when nothing was captured. */\n network: 'trace.network',\n /** NDJSON DOM mutation stream. Omitted when nothing was captured. */\n mutations: 'trace.mutations',\n /** Human/LLM-readable Markdown run transcript. */\n transcript: 'transcript.md',\n /** Screenshots, element snapshots and network bodies live under here. */\n resourcesDir: 'resources'\n} as const\n\n/** Stream suffixes the reader matches on. A zip may carry several streams (one\n * per page/session), so entries are matched by suffix rather than exact name;\n * `.stacks` is a sidecar the writer does not emit but foreign traces do. */\nexport const TRACE_STREAM_SUFFIXES = {\n trace: '.trace',\n network: '.network',\n mutations: '.mutations',\n stacks: '.stacks'\n} as const\n\n/** `type` discriminants of the NDJSON events in a `.trace` stream. */\nexport const TRACE_EVENT_TYPES = {\n before: 'before',\n after: 'after',\n frameSnapshot: 'frame-snapshot',\n screencastFrame: 'screencast-frame',\n contextOptions: 'context-options',\n console: 'console',\n stdout: 'stdout',\n stderr: 'stderr'\n} as const\n\nexport type TraceEventType =\n (typeof TRACE_EVENT_TYPES)[keyof typeof TRACE_EVENT_TYPES]\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 const LOG_LEVELS = [\n 'trace',\n 'debug',\n 'log',\n 'info',\n 'warn',\n 'error'\n] as const\nexport type LogLevel = (typeof LOG_LEVELS)[number]\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 open in any standard trace viewer — the unpacked\n * 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; `test` writes one trace\n * per test. Only applies in trace mode. */\nexport type TraceGranularity = 'session' | 'spec' | 'test'\n\n/** Retention policy for written traces. Only applies in trace mode; `on` is\n * the current always-write behavior (there is no `off` — that's simply not\n * using trace mode). */\nexport type TraceRetentionPolicy =\n | 'on'\n | 'retain-on-failure'\n | 'retain-on-first-failure'\n | 'on-first-retry'\n | 'on-all-retries'\n | 'retain-on-failure-and-retries'\n\n/** Per-test screenshot capture policy, mirroring Playwright's `screenshot`\n * option. `only-on-failure` shoots after a failing test; `on` after every\n * test; `off` (default) never. Only applies in trace mode. */\nexport type TraceScreenshotPolicy = 'off' | 'on' | 'only-on-failure'\n\n/** Per-test video capture policy. `off` (default) records nothing; any other\n * value records the screencast and keeps each test's video slice per the same\n * retention semantics as `tracePolicy`. Only applies in trace mode at\n * `traceGranularity: 'test'` (the per-test scope videos attach to). */\nexport type TraceVideoPolicy = 'off' | TraceRetentionPolicy\n\n/** One node in a test's ancestor chain, outermost first. */\nexport interface TestAncestor {\n uid: string\n title: string\n kind: 'feature' | 'scenario' | 'suite' | 'test' | 'step' | 'hook'\n}\n\n/** Per-test metadata for Tracing.tracingGroup events in trace output. */\nexport interface TestMetadataEntry {\n title: string\n specFile: string\n state?: TestStatus\n attempt?: number\n ancestry?: TestAncestor[]\n}\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, TestMetadataEntry>\n\n/**\n * Normalized assertion result an adapter may attach to `CommandLog.result` for\n * an assertion command. The trace exporter's assert-param builder prefers this\n * over the positional `[actual, expected]` arg convention — correct for\n * frameworks whose asserts pass only an expected value (a matcher like\n * `titleContains('x')`), where args[0] is the expected, not the actual.\n * Cross-package contract: adapters produce it, core's exporter consumes it.\n */\nexport interface CollapsedAssertResult {\n passed: boolean\n actual?: unknown\n expected?: unknown\n message?: string\n}\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 const TEST_RUNNER_IDS = [\n 'mocha',\n 'jasmine',\n 'cucumber',\n 'nightwatch',\n 'nightwatch-cucumber',\n 'selenium-webdriver'\n] as const\nexport type TestRunnerId = (typeof TEST_RUNNER_IDS)[number]\n\n/**\n * Narrow an untrusted value onto {@link TestRunnerId}. Needed wherever the id\n * arrives from outside this build — a framework's own config value, or a trace\n * zip recorded by another version — since the static type can't vouch for it.\n */\nexport function isTestRunnerId(value: unknown): value is TestRunnerId {\n return (\n typeof value === 'string' &&\n (TEST_RUNNER_IDS as readonly string[]).includes(value)\n )\n}\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 /** Monotonic issue order, stamped when the test issued the command. Breaks\n * ties the millisecond clock can't: a deferred row (a Nightwatch native\n * assert, finalized in a batch at test-end) is appended long after the driver\n * row it was issued before, so an equal `startTime` otherwise resolved in\n * insertion order and put the assert after the command that followed it. */\n sequence?: number\n callSource?: string\n screenshot?: string\n testUid?: string\n performance?: PerformanceData\n cookies?: string\n documentInfo?: DocumentInfo\n id?: number\n /** Cucumber step this command ran under — nests below testUid in the trace's\n * group tree (Feature → Scenario → Step). Set by the adapter step hooks. */\n stepUid?: string\n /** Depth-indented accessibility-tree text for the page state at this command\n * (from the per-action `-snapshot.txt` resource). Reconstructed by the trace\n * reader; drives the player's A11y tab. */\n snapshotText?: string\n /** Page-coordinate hit point for pointer actions — the centre of the matched\n * element at capture time. Synthesized in the exporter; drives the snapshot\n * click marker and the timeline pointer glyph. */\n point?: { x: number; y: number }\n /** Locator of the element this command acted on. Survives assertion folding\n * (where `args` becomes the expected value), so the player's element overlay\n * can box an assertion target like `#flash`. Carried through the trace as the\n * `locator` action param — kept distinct from the `selector` param so it\n * never feeds `paramsToArgs`. */\n selector?: string\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\n/**\n * Every word a captured `NetworkRequest.type` can carry. The capture side\n * classifies once — `core`'s `getRequestType` for driver-observed traffic, the\n * page collector for its patched `fetch`/`XMLHttpRequest` — and the app only\n * translates these words into display buckets. Adding a category here is the\n * compile-time trigger that forces every consumer's mapping to handle it.\n * `other` is the residual for traffic no producer could classify (a failed\n * request that never got a content-type, a foreign trace's unknown mime).\n */\nexport const REQUEST_TYPES = [\n 'document',\n 'stylesheet',\n 'script',\n 'image',\n 'font',\n 'fetch',\n 'xhr',\n 'other'\n] as const\nexport type RequestType = (typeof REQUEST_TYPES)[number]\n\n/**\n * Narrow an untrusted value onto {@link RequestType}. Needed wherever a type\n * arrives from outside this build — a trace file written by an older version,\n * or an adapter payload off the wire — since the static type can't vouch for it.\n */\nexport function isRequestType(value: unknown): value is RequestType {\n return (\n typeof value === 'string' &&\n (REQUEST_TYPES as readonly string[]).includes(value)\n )\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: RequestType\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 /** Cap on frames held in memory (default: 2000 ≈ several minutes at ~5 fps); the buffer is decimated in place past this. */\n maxBufferFrames?: 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 maxBufferFrames: 2000\n}\n\n/**\n * Options every framework adapter accepts. Each adapter's own options interface\n * extends this and adds only its framework-specific fields (e.g. WDIO's\n * devtoolsCapabilities, Selenium's openUi, Nightwatch's bidi).\n */\nexport interface BaseDevToolsOptions {\n /** Port to launch the application on (default: random). */\n port?: number\n /** Hostname to launch the application on. @default localhost */\n hostname?: string\n /** Screencast recording options. When enabled, a continuous video of the\n * browser session is recorded and saved as a .webm file. */\n screencast?: ScreencastOptions\n /** Capture node:assert assertions (and framework `expect` matchers where\n * supported) as first-class commands. Default true. */\n captureAssertions?: boolean\n /** `live` (default) launches the DevTools UI; `trace` skips it. */\n mode?: DevToolsMode\n /** Trace output layout — `zip` (default) writes a single archive,\n * `ndjson-directory` unpacks into `trace-<id>/`. Only applies in trace mode. */\n traceFormat?: TraceFormat\n /** Trace output granularity — `session` (default) writes one trace per\n * worker session; `spec` writes one per spec file. Only applies in trace mode. */\n traceGranularity?: TraceGranularity\n /** Trace retention policy — gates which traces are kept (e.g.\n * `retain-on-failure`). Default `on` (keep all). Only applies in trace mode. */\n tracePolicy?: TraceRetentionPolicy\n /** Record a dense, continuous screencast filmstrip into the trace for\n * scrubbable playback in the trace player — not just one frame per action.\n * The dense frames are added alongside the per-action frames (which carry the\n * DOM snapshots). Runs the screencast recorder (CDP push on Chrome, polling\n * elsewhere). Default true. Set `false` to record only one frame per action\n * (smaller trace, no continuous recorder). Only applies in trace mode. */\n filmstrip?: boolean\n /** Write the `devtools-artifacts-<sessionId>.json` manifest next to the trace\n * — the generic index reporters/CI consume to discover produced artifacts.\n * Off by default; auto-enabled when an Allure reporter is detected (WDIO:\n * `@wdio/allure-reporter` in config; Selenium: an active `allure-js-commons`\n * runtime). Nightwatch has no live Allure signal, so it stays opt-in there.\n * Only applies in trace mode. */\n emitArtifactsManifest?: boolean\n}\n\n/** Minimal Cucumber pickle-step shape — only the fields the adapters read.\n * Cucumber's own types vary across versions, so we pin just these. */\nexport interface CucumberPickleStep {\n text?: string\n astNodeIds?: string[]\n location?: { line?: number }\n}\n\n/** Minimal Cucumber pickle shape — only the fields the adapters read. `steps`\n * is present only where the adapter walks step boundaries (Nightwatch). */\nexport interface CucumberPickle {\n name?: string\n uri?: string\n location?: { line?: number }\n astNodeIds?: string[]\n steps?: CucumberPickleStep[]\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 /** Runner that recorded this stream. Drives the locator dialect the page\n * capture emits and the player's locator hint; undefined for a trace zip\n * recorded before the field existed, or by a foreign tool. */\n runner?: TestRunnerId\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 * Trailing sentinel line in a `trace.mutations` NDJSON stream, marking that\n * `dropped` late mutations were discarded under the size cap. A distinct shape\n * (not a `TraceMutation.type`) so it never collides with a real mutation. This\n * is the writer↔reader contract shared by core's exporter and the backend\n * reader, so both agree on the key.\n */\nexport interface MutationsTruncationMarker {\n __truncated__: true\n dropped: number\n}\n\n/** True when an NDJSON entry is the truncation sentinel, not a mutation. */\nexport function isMutationsTruncationMarker(\n entry: unknown\n): entry is MutationsTruncationMarker {\n return (\n typeof entry === 'object' &&\n entry !== null &&\n (entry as { __truncated__?: unknown }).__truncated__ === true\n )\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 /** Dense screencast frames (continuous recorder buffer) for the trace-mode\n * filmstrip. Present only when `filmstrip` is enabled; thinned + content-\n * addressed at export time. */\n screencastFrames?: ScreencastFrame[]\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 {\n ACTION_MAP,\n ASSERT_ACTION_CLASS,\n mapAssertCommand,\n type TraceAction\n} from '@wdio/devtools-shared'\n\nexport type { TraceAction }\nexport { ASSERT_ACTION_CLASS, mapAssertCommand }\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// $ / $$ / findElement(s) / getElement(s) — locator resolution fires on every\n// element access; high-frequency internal machinery, not a timeline step.\n// Passing expect-webdriverio matchers — never reach the command log (only\n// failures do, via the reporter); surfacing them is a per-adapter change.\n\nexport function mapCommandToAction(command: string): TraceAction | null {\n return ACTION_MAP[command] ?? mapAssertCommand(command)\n}\n\nconst ASSERT_TITLE_VALUE_MAX = 40\n\nfunction formatAssertValue(value: unknown): string {\n let text: string\n if (typeof value === 'object' && value !== null) {\n try {\n text = JSON.stringify(value)\n } catch {\n text = String(value)\n }\n } else {\n text = typeof value === 'string' ? JSON.stringify(value) : String(value)\n }\n return text.length > ASSERT_TITLE_VALUE_MAX\n ? `${text.slice(0, ASSERT_TITLE_VALUE_MAX - 1)}…`\n : text\n}\n\n// The label mirrors the CALL the user wrote — its positional args\n// (`assert.equal(a, b)`, `titleContains('x')`). The normalized actual/expected\n// params drive the result diff, NOT the label: a single-arg assert like\n// titleContains passes only the expected, so labelling it with the derived\n// actual would misrepresent the call. Fall back to actual/expected only when\n// no args survived (reader round-trips that dropped them).\nfunction formatAssertTitle(\n action: TraceAction,\n args: unknown[],\n params?: Record<string, unknown>,\n command?: string\n): string {\n const values =\n args.length > 0 ? args.slice(0, 2) : [params?.actual, params?.expected]\n const label = values\n .filter((value) => value !== undefined)\n .map(formatAssertValue)\n .join(', ')\n return `${command ?? `assert.${action.method}`}(${label})`\n}\n\nexport function formatActionTitle(\n action: TraceAction,\n args: unknown[],\n params?: Record<string, unknown>,\n command?: string\n): string {\n if (action.class === ASSERT_ACTION_CLASS) {\n return formatAssertTitle(action, args, params, command)\n }\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\nimport { locatorDialect } from '@wdio/devtools-shared'\nimport type {\n LocatorDialect,\n TestRunnerId,\n TextLocatorDialect\n} from '@wdio/devtools-shared'\n\n/** Shared by both injected scripts below — the same visibility gate decides\n * which elements each one reports. */\nconst IS_VISIBLE_SCRIPT = `\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\n/** XPath 1.0 has no string escape, so a value carrying both quote kinds is\n * stitched from single-kind literals; a literal double quote can only enter the\n * expression as its own single-quoted token. */\nconst XPATH_TEXT_LITERAL_SCRIPT = `\n function xpathTextLiteral(value) {\n if (value.indexOf('\"') === -1) { return '\"' + value + '\"' }\n if (value.indexOf(\"'\") === -1) { return \"'\" + value + \"'\" }\n var quoteToken = \"'\" + '\"' + \"'\"\n var parts = value.split('\"')\n var pieces = []\n for (var p = 0; p < parts.length; p++) {\n if (parts[p]) { pieces.push('\"' + parts[p] + '\"') }\n if (p < parts.length - 1) { pieces.push(quoteToken) }\n }\n // concat() takes at least two arguments — a value that is nothing but\n // double quotes yields one piece and needs no concat.\n return pieces.length > 1 ? 'concat(' + pieces.join(', ') + ')' : pieces[0]\n }\n`\n\n/** The meaning-bearing CSS branches: portable across all runners, so they are\n * dialect-independent. Null when none of them identifies the element uniquely,\n * which is what lets a caller order them against the text branch. */\nconst SEMANTIC_CSS_SELECTOR_SCRIPT = `\n function semanticCssSelector(element, tag) {\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) {\n var idSel = '#' + CSS.escape(element.id)\n if (document.querySelectorAll(idSel).length === 1) { return idSel }\n }\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 var typeAttr = element.getAttribute('type')\n if (typeAttr) {\n var typeSel = tag + '[type=\"' + CSS.escape(typeAttr) + '\"]'\n if (document.querySelectorAll(typeSel).length === 1) { return typeSel }\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 return null\n }\n`\n\n/** Last resort: a positional `:nth-of-type` path, which always resolves but\n * carries no meaning — hence every other branch getting first refusal. */\nconst POSITIONAL_SELECTOR_SCRIPT = `\n function positionalSelector(element) {\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\n/** The text branch's return expression. WebdriverIO's `tag*=text` compiles\n * internally to XPath with `\"` quoting, so a text carrying a double quote would\n * yield a broken expression — those keep the XPath form, which it also resolves. */\nfunction textLocatorExpression(dialect: TextLocatorDialect): string {\n const xpath = \"'//' + tag + '[contains(., ' + xpathTextLiteral(text) + ')]'\"\n return dialect === 'webdriverio'\n ? `text.indexOf('\"') === -1 ? tag + '*=' + text : ${xpath}`\n : xpath\n}\n\n/** Identify the element by its own text, in `dialect`'s grammar. Null when the\n * text neither exists nor singles it out, so it composes with the CSS branches\n * in either order. */\nfunction textSelectorScript(dialect: TextLocatorDialect): string {\n return `\n function textSelector(element, tag) {\n var text = (element.textContent || '').trim().replace(/\\\\s+/g, ' ')\n if (!text || text.length > 120) { return null }\n var sameTagElements = document.querySelectorAll(tag)\n var matchCount = 0\n sameTagElements.forEach(function(el) { if (el.textContent.includes(text)) { matchCount++ } })\n // The DOM predicate this counts is exactly XPath's\n // \\`//tag[contains(., text)]\\`, so a single match here is a unique match\n // there — the emitted expression carries the uniqueness just checked.\n if (matchCount !== 1) { return null }\n return ${textLocatorExpression(dialect)}\n }\n`\n}\n\n/** Shared by both injected scripts below, so one grammar produces the locator in\n * `-snapshot.txt` and `-elements.json`. The dialect decides both what the text\n * branch emits and where it sits; the positional path stays last either way. */\nfunction getSelectorScript(dialect: LocatorDialect): string {\n const preferred =\n dialect.textBranch === 'first'\n ? ['textSelector(element, tag)', 'semanticCssSelector(element, tag)']\n : ['semanticCssSelector(element, tag)', 'textSelector(element, tag)']\n return `\n ${XPATH_TEXT_LITERAL_SCRIPT}\n ${SEMANTIC_CSS_SELECTOR_SCRIPT}\n ${POSITIONAL_SELECTOR_SCRIPT}\n ${textSelectorScript(dialect.text)}\n\n function getSelector(element) {\n var tag = element.tagName.toLowerCase()\n return ${preferred[0]} || ${preferred[1]} || positionalSelector(element)\n }\n`\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 locator, in `runner`'s own text\n * dialect — omit it for the portable XPath form every runner resolves.\n */\nexport function accessibilityTreeScript(\n inViewportOnly: boolean,\n runner?: TestRunnerId\n): 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 ${getSelectorScript(locatorDialect(runner))}\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 ${IS_VISIBLE_SCRIPT}\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 locator, in `runner`'s own text\n * dialect — omit it for the portable XPath form every runner resolves.\n */\nexport function elementsScript(\n includeBounds: boolean,\n inViewportOnly: boolean,\n runner?: TestRunnerId\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 ${IS_VISIBLE_SCRIPT}\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 ${getSelectorScript(locatorDialect(runner))}\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. `&nbsp;`,\n * `&copy;`, bare `&` in URLs) → replaced with `&amp;`.\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 '&amp;'\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'\n\nimport {\n SNAPSHOT_INDENT_UNIT,\n SNAPSHOT_PAGE_HEADER,\n SNAPSHOT_LOCATOR_DELIM,\n SNAPSHOT_PURPOSE_TOKEN,\n xpathLocatorTag\n} from '@wdio/devtools-shared'\n\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 = SNAPSHOT_PAGE_HEADER\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 = SNAPSHOT_INDENT_UNIT.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}\" ${SNAPSHOT_PURPOSE_TOKEN} \"${purpose}\" ${SNAPSHOT_LOCATOR_DELIM} ${node.selector}`\n : `${indent}${roleLabel} \"${node.name}\" ${SNAPSHOT_LOCATOR_DELIM} ${node.selector}`\n )\n } else if (purpose) {\n lines.push(\n `${indent}${roleLabel} ${SNAPSHOT_PURPOSE_TOKEN} \"${purpose}\" ${SNAPSHOT_LOCATOR_DELIM} ${node.selector}`\n )\n } else {\n lines.push(\n `${indent}${roleLabel} ${SNAPSHOT_LOCATOR_DELIM} ${node.selector}`\n )\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 = SNAPSHOT_INDENT_UNIT.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}\" ${SNAPSHOT_PURPOSE_TOKEN} \"${purpose}\" ${SNAPSHOT_LOCATOR_DELIM} ${selector}`\n : `${indent}${node.role} \"${node.name}\" ${SNAPSHOT_LOCATOR_DELIM} ${selector}`\n )\n } else if (purpose) {\n lines.push(\n `${indent}${node.role} ${SNAPSHOT_PURPOSE_TOKEN} \"${purpose}\" ${SNAPSHOT_LOCATOR_DELIM} ${selector}`\n )\n } else {\n lines.push(\n `${indent}${node.role} ${SNAPSHOT_LOCATOR_DELIM} ${selector}`\n )\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 locator prefix (e.g. `//button[contains(., \"Submit\")]`\n * or \"button*=Submit\" → \"button\"). */\nfunction extractTagFromSelector(selector: string, fallback: string): string {\n const xpathTag = xpathLocatorTag(selector)\n if (xpathTag) {\n return xpathTag\n }\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\n/** Ceiling for a driver round-trip probe (screenshot, url, title). Larger than\n * the in-page ceiling because encoding a full-page screenshot legitimately\n * takes longer than reading the DOM, but still inside the adapters' 5s\n * settle window so a hung probe can't strand the whole capture. */\nexport const SNAPSHOT_DRIVER_PROBE_TIMEOUT_MS = 4000\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 {\n SNAPSHOT_DRIVER_PROBE_TIMEOUT_MS,\n SNAPSHOT_PROBE_TIMEOUT_MS,\n withTimeout\n} from './with-timeout.js'\nimport type { ActionSnapshot, TestRunnerId } from '@wdio/devtools-shared'\n\nexport type ScriptRunner = (scriptSrc: string) => Promise<unknown>\n\nexport interface CaptureActionSnapshotInput {\n command: string\n /** The logged command's timestamp. Export claims snapshots by exact equality\n * (`claimAfter`/`elementsAt`) and slices them by command time, so a snapshot\n * stamped with its own capture time binds to no action at all. */\n timestamp?: number\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 | null | undefined>\n getTitle?: () => Promise<string | null | 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 /** Runner this capture belongs to — decides which text-locator dialect the\n * injected scripts emit. Omitted → the portable XPath form. */\n runner?: TestRunnerId\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 // A driver can answer `null` rather than reject (no-such-session, a script\n // error swallowed by the transport). Passing that through hands the\n // serializers a non-array where they expect one, which throws and loses the\n // WHOLE snapshot — screenshot, url and all — not just the probe.\n runScript(scriptSrc).then((r) =>\n r === null || r === undefined ? fallback : (r as T)\n ),\n SNAPSHOT_PROBE_TIMEOUT_MS,\n fallback\n ).catch(() => fallback)\n}\n\n/**\n * Run one driver-side probe under a timeout, resolving to `undefined` on a\n * timeout, a rejection, or a synchronous throw.\n *\n * Every probe is guarded, not just the in-page scripts: a framework whose\n * url/title/screenshot readers go through its own command queue blocks\n * indefinitely when called from inside a command hook, and an unguarded probe\n * in this `Promise.all` stranded the entire capture — observed as 10 of 14\n * Nightwatch captures never settling, so those actions reached the trace with\n * no DOM, no a11y tree and no element rects at all.\n */\nfunction probe<T>(\n read: (() => Promise<T | null | undefined>) | undefined\n): Promise<T | undefined> {\n if (!read) {\n return Promise.resolve(undefined)\n }\n // `Promise.resolve().then(read)` so a probe that throws synchronously becomes\n // a rejection this catch can absorb, rather than escaping the Promise.all.\n return withTimeout(\n Promise.resolve()\n .then(read)\n .then((value) => value ?? undefined),\n SNAPSHOT_DRIVER_PROBE_TIMEOUT_MS,\n undefined\n ).catch(() => undefined)\n}\n\n/** Native-mobile snapshot text + locators, derived from the page-source XML. */\nfunction fromPageSource(\n pageSource: string,\n platform: 'android' | 'ios'\n): { snapshotText: string; elements: unknown[] } {\n const jsonTree = xmlToJSON(pageSource)\n let snapshotText = `[${platform}]`\n if (jsonTree) {\n jsonTree.attributes._sourceXML = pageSource\n snapshotText = serializeMobileSnapshot(jsonTree, {\n platform,\n sourceXML: pageSource\n })\n }\n try {\n const filters = getDefaultFilters(platform, false)\n return {\n snapshotText,\n elements: generateAllElementLocators(pageSource, {\n platform,\n viewportSize: { width: 9999, height: 9999 },\n filters,\n inViewportOnly: false\n })\n }\n } catch {\n // Non-fatal — snapshot text is the primary deliverable.\n return { snapshotText, elements: [] }\n }\n}\n\nexport async function captureActionSnapshot(\n input: CaptureActionSnapshotInput\n): Promise<ActionSnapshot | null> {\n try {\n const timestamp = input.timestamp ?? Date.now()\n const isNativeMobile = !input.runScript && !!input.getPageSource\n\n // Probe order is load-bearing, not cosmetic. A driver serialises requests\n // per session, so `Promise.all` starting them together still has them served\n // in the order issued — and the screenshot is by far the slowest. Issued\n // first, it delayed the cheap reads behind it by hundreds of ms, long enough\n // for the next command to navigate: a fill on the login page reported the\n // page its submit had already reached. Cheapest-and-most-order-sensitive\n // first, screenshot last; the screenshot is served no later than before.\n const [url, title, pageSource, tree, elements, shot] = await Promise.all([\n probe(input.getUrl),\n probe(input.getTitle),\n isNativeMobile ? probe(input.getPageSource) : undefined,\n runWith<AccessibilityNode[]>(\n input.runScript,\n accessibilityTreeScript(true, input.runner),\n []\n ),\n runWith<BrowserElementInfo[]>(\n input.runScript,\n // includeBounds: the per-action element rects drive A8 input points.\n elementsScript(true, true, input.runner),\n []\n ),\n probe(input.takeScreenshot)\n ])\n\n const mobile =\n isNativeMobile && pageSource\n ? fromPageSource(pageSource, input.platform ?? 'android')\n : undefined\n\n return {\n timestamp,\n command: input.command,\n url,\n title,\n screenshot: shot ?? undefined,\n elements: mobile?.elements ?? elements,\n snapshotText:\n mobile?.snapshotText ?? serializeWebSnapshot(tree, { url, title })\n }\n } catch {\n return null\n }\n}\n","/**\n * Filename helpers for per-test artifacts (screenshots, videos). Shared so the\n * screenshot and video writers slug a test uid identically.\n */\n\n/** Strip leading and trailing occurrences of `char` via a linear scan. A regex\n * like `/^c+|c+$/` is O(n²) on long runs of `c` (the anchored `+` is retried\n * at each position) — flagged by CodeQL's js/polynomial-redos — so this avoids\n * regex entirely. `char` must be a single character. */\nexport function trimChar(value: string, char: string): string {\n let start = 0\n let end = value.length\n while (start < end && value[start] === char) {\n start++\n }\n while (end > start && value[end - 1] === char) {\n end--\n }\n return value.slice(start, end)\n}\n\n/** File-safe slug of a value: keep alphanumerics, `_` and `-`; collapse the\n * rest to a single `-`; trim leading/trailing dashes. */\nexport function fileSlug(value: string): string {\n return trimChar(value.replace(/[^a-zA-Z0-9_-]+/g, '-'), '-')\n}\n","/**\n * The generic artifacts manifest: a single JSON side-file\n * (`devtools-artifacts-<sessionId>.json`) written next to the trace/video\n * artifacts at end-of-run. It enumerates every artifact a trace-mode session\n * produced — including the ones a retention policy decided against\n * (`retained: false`) — alongside each test's final state and attempt. Any\n * ecosystem reporter (Allure, a CI collector, a custom dashboard) can read this\n * one file to discover what to attach and how each test fared, instead of\n * re-deriving it from framework internals. The WDIO Allure glue consumes it\n * directly; Selenium/Nightwatch document reader recipes against it.\n */\n\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport type {\n TestMetadataEntry,\n TestMetadataMap,\n TraceFormat\n} from '@wdio/devtools-shared'\nimport type { TraceArtifact } from './trace-finalizer.js'\n\n/** One test's outcome as recorded at export time. Field types track\n * TestMetadataEntry in shared; adds the keying uid, drops trace-only ancestry. */\nexport interface ArtifactManifestTest extends Pick<\n TestMetadataEntry,\n 'title' | 'specFile' | 'state' | 'attempt'\n> {\n uid: string\n}\n\nexport interface ArtifactsManifest {\n sessionId: string\n format: TraceFormat\n artifacts: TraceArtifact[]\n tests: ArtifactManifestTest[]\n}\n\n/** Deterministic manifest filename for a session (used by writers and readers). */\nexport function artifactsManifestFilename(sessionId: string): string {\n return `devtools-artifacts-${sessionId}.json`\n}\n\n/** Assemble the manifest from the artifacts collected across the run and the\n * test-metadata map. Pure — the write is separate so it stays unit-testable. */\nexport function buildArtifactsManifest(input: {\n sessionId: string\n format: TraceFormat\n artifacts: readonly TraceArtifact[]\n testMetadata: TestMetadataMap\n}): ArtifactsManifest {\n const tests: ArtifactManifestTest[] = []\n for (const [uid, meta] of input.testMetadata) {\n tests.push({\n uid,\n title: meta.title,\n specFile: meta.specFile,\n state: meta.state,\n attempt: meta.attempt\n })\n }\n return {\n sessionId: input.sessionId,\n format: input.format,\n artifacts: [...input.artifacts],\n tests\n }\n}\n\n/** Write the manifest to `outputDir`, returning its absolute path. */\nexport async function writeArtifactsManifest(\n outputDir: string,\n manifest: ArtifactsManifest\n): Promise<string> {\n await fs.mkdir(outputDir, { recursive: true })\n const filePath = path.join(\n outputDir,\n artifactsManifestFilename(manifest.sessionId)\n )\n await fs.writeFile(filePath, JSON.stringify(manifest, null, 2), 'utf8')\n return filePath\n}\n","// Framework-agnostic capture-and-attach of per-test trace artifacts to Allure.\n// The *produce* half (screenshot/video write, retention decision) already lives\n// in core; this module adds the orchestration + the attach, behind a pluggable\n// `AllureAttachSink` so each adapter supplies only its reporter binding:\n// - WDIO service → @wdio/allure-reporter's addAttachment\n// - Selenium → allure-js-commons' attachment() (runtime-agnostic)\n// - Nightwatch → no sink (produce-only; its Allure path is post-hoc, with no\n// live attach API), so artifacts land in the manifest only.\n// A missing/undefined sink means \"produce but don't attach\" — never an error.\n\nimport fs from 'node:fs/promises'\nimport { basename } from 'node:path'\nimport type {\n ActionSnapshot,\n DevToolsMode,\n ScreencastFrame,\n TraceGranularity,\n TraceScreenshotPolicy,\n TraceVideoPolicy\n} from '@wdio/devtools-shared'\nimport {\n shouldCaptureScreenshot,\n writeScreenshotArtifact\n} from './screenshot-artifact.js'\nimport { encodePerTestVideo, sliceFramesFrom } from './video-slice.js'\nimport { shouldRetainTrace, type TestOutcome } from './trace-retention.js'\nimport type { TraceArtifact } from './trace-finalizer.js'\n\n/** Adapter-supplied binding to the active Allure reporter. Attaches one file to\n * the currently-executing test. Undefined = attach unsupported (produce-only). */\nexport type AllureAttachSink = (\n name: string,\n content: Buffer,\n contentType: string\n) => void | Promise<void>\n\ntype LogFn = (level: 'info' | 'warn', message: string) => void\n\n// Trace stays a plain zip download (the viewer is the user's choice, not a\n// third-party viewer Allure would open for a trace-specific content type);\n// video/image render inline.\nconst CONTENT_TYPE_BY_KIND: Record<TraceArtifact['kind'], string> = {\n trace: 'application/zip',\n video: 'video/webm',\n screenshot: 'image/png'\n}\n\n/**\n * Read a retained artifact and hand it to the sink for attachment to the current\n * Allure test. No-op when the artifact wasn't retained, has no path, the sink is\n * absent, or the path is a directory (the ndjson-directory trace format). Never\n * throws — a missing/unreadable artifact must not reject the caller's hook.\n */\nexport async function attachTraceArtifact(\n artifact: TraceArtifact,\n sink: AllureAttachSink | undefined,\n onLog?: LogFn\n): Promise<void> {\n if (!sink || !artifact.retained || !artifact.path) {\n return\n }\n try {\n const stat = await fs.stat(artifact.path)\n if (!stat.isFile()) {\n return\n }\n const content = await fs.readFile(artifact.path)\n await sink(\n basename(artifact.path),\n content,\n CONTENT_TYPE_BY_KIND[artifact.kind]\n )\n } catch (err) {\n onLog?.(\n 'warn',\n `Allure attach skipped for ${artifact.path}: ${String(err)}`\n )\n }\n}\n\n/**\n * The base64 of the last rendered action snapshot for the current test, skipping\n * the end-of-scenario `__final__` frame (captured post-teardown, often blank when\n * a reloadSession runs before the after-hook). Scoped to `>= startWallTime` so a\n * test that captured nothing doesn't borrow the previous test's frame. Reused as\n * the per-test screenshot — reload-immune and one fewer WebDriver command than a\n * fresh end-of-test capture.\n */\nexport function lastRenderedScreenshot(\n snapshots: readonly ActionSnapshot[],\n startWallTime: number\n): string | undefined {\n for (let i = snapshots.length - 1; i >= 0; i--) {\n const snap = snapshots[i]!\n if (snap.timestamp < startWallTime) {\n return undefined\n }\n if (snap.command !== '__final__' && snap.screenshot) {\n return snap.screenshot\n }\n }\n return undefined\n}\n\n/**\n * Write the per-test screenshot (per policy) from an already-captured base64\n * frame, record it in the manifest, and attach it via the sink. No-op outside\n * `test`-granularity trace mode, without a uid/session/frame, or when the policy\n * declines. Gated to `test` granularity so the whole per-test inline story\n * (trace + screenshot + video) is one rule.\n */\nexport async function captureAndAttachScreenshot(input: {\n mode: DevToolsMode | undefined\n granularity: TraceGranularity | undefined\n policy: TraceScreenshotPolicy | undefined\n failed: boolean\n /** Base64 of the last rendered action snapshot (reload-immune). */\n screenshotBase64: string | undefined\n sessionId: string | undefined\n outputDir: string\n testUid: string | undefined\n attach: AllureAttachSink | undefined\n onArtifact: (artifact: TraceArtifact) => void\n onLog?: LogFn\n}): Promise<void> {\n const { mode, granularity, policy, failed, screenshotBase64, sessionId } =\n input\n if (\n mode !== 'trace' ||\n granularity !== 'test' ||\n !screenshotBase64 ||\n !sessionId ||\n !input.testUid ||\n !shouldCaptureScreenshot(policy, failed)\n ) {\n return\n }\n const artifact = await writeScreenshotArtifact({\n outputDir: input.outputDir,\n testUid: input.testUid,\n sessionId,\n base64: screenshotBase64\n })\n input.onArtifact(artifact)\n await attachTraceArtifact(artifact, input.attach, input.onLog)\n}\n\n/**\n * Slice the continuous screencast to this test's window, encode a `.webm`,\n * record it in the manifest, and attach it via the sink — when trace mode +\n * `test` granularity + a non-`off` `video` policy retains this test's outcome.\n * Empty outcomes (test never started) skip rather than fail-open onto the\n * previous test's frames.\n */\nexport async function captureAndAttachVideo(input: {\n mode: DevToolsMode | undefined\n granularity: TraceGranularity | undefined\n policy: TraceVideoPolicy | undefined\n frames: readonly ScreencastFrame[] | undefined\n startWallTime: number\n outcomes: TestOutcome[]\n attempt: number | undefined\n outputDir: string\n testUid: string | undefined\n sessionId: string | undefined\n captureFormat?: 'jpeg' | 'png'\n attach: AllureAttachSink | undefined\n onArtifact: (artifact: TraceArtifact) => void\n onLog?: LogFn\n}): Promise<void> {\n const { mode, granularity, policy, frames, testUid, sessionId } = input\n if (\n mode !== 'trace' ||\n granularity !== 'test' ||\n !policy ||\n policy === 'off' ||\n !frames ||\n !testUid ||\n !sessionId ||\n input.outcomes.length === 0\n ) {\n return\n }\n if (\n !shouldRetainTrace(policy, {\n outcomes: input.outcomes,\n attemptInfoAvailable: true\n }).retain\n ) {\n return\n }\n const artifact = await encodePerTestVideo({\n frames: sliceFramesFrom(frames, input.startWallTime),\n outputDir: input.outputDir,\n testUid,\n sessionId,\n attempt: input.attempt,\n captureFormat: input.captureFormat,\n onLog: input.onLog\n })\n if (!artifact) {\n return\n }\n input.onArtifact(artifact)\n await attachTraceArtifact(artifact, input.attach, input.onLog)\n}\n","/**\n * Per-test screenshot artifact: the policy decision and the file write, kept\n * framework-agnostic so every adapter feeds it the same way (the adapter only\n * supplies the base64 capture from its own driver). Mirrors Playwright's\n * `screenshot` option — `on` after every test, `only-on-failure` after a\n * failing one — and produces a TraceArtifact the manifest and the Allure glue\n * consume like any other artifact.\n */\n\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport type { TraceScreenshotPolicy } from '@wdio/devtools-shared'\nimport { fileSlug } from './artifact-naming.js'\nimport type { TraceArtifact } from './trace-finalizer.js'\n\n/** Whether a per-test screenshot should be captured for this outcome. */\nexport function shouldCaptureScreenshot(\n policy: TraceScreenshotPolicy | undefined,\n failed: boolean\n): boolean {\n if (policy === 'on') {\n return true\n }\n if (policy === 'only-on-failure') {\n return failed\n }\n return false\n}\n\n/**\n * Write a base64 PNG (from the driver's screenshot command) next to the trace\n * output and return the artifact describing it. Retained is always true — a\n * screenshot is only ever written when the policy already decided to capture.\n */\nexport async function writeScreenshotArtifact(input: {\n outputDir: string\n testUid: string\n sessionId: string\n base64: string\n}): Promise<TraceArtifact> {\n await fs.mkdir(input.outputDir, { recursive: true })\n const filename = `screenshot-${fileSlug(input.testUid)}-${input.sessionId.slice(0, 8)}.png`\n const filePath = path.join(input.outputDir, filename)\n await fs.writeFile(filePath, Buffer.from(input.base64, 'base64'))\n return {\n kind: 'screenshot',\n path: filePath,\n scope: 'test',\n key: input.testUid,\n testUids: [input.testUid],\n retained: true\n }\n}\n","/**\n * Per-test video: slice the continuous screencast frame buffer to one test's\n * wall-clock window and encode that slice to a `.webm`. Framework-agnostic —\n * the adapter supplies the recorder's frames and the test's start time; the\n * recorder itself (CDP push on Chrome, screenshot polling elsewhere) lives in\n * each adapter. Best-effort: too few frames or a missing ffmpeg yields no\n * artifact rather than an error, so video never aborts a run.\n */\n\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport type { ScreencastFrame } from '@wdio/devtools-shared'\nimport { fileSlug } from './artifact-naming.js'\nimport { encodeToVideo } from './video-encoder.js'\nimport { errorMessage } from './error.js'\nimport type { TraceArtifact } from './trace-finalizer.js'\n\n/** Frames captured at or after `startWallTime` (ms) — one test's window, since\n * the buffer runs continuously and the next test's frames arrive later. */\nexport function sliceFramesFrom(\n frames: readonly ScreencastFrame[],\n startWallTime: number\n): ScreencastFrame[] {\n return frames.filter((f) => f.timestamp >= startWallTime)\n}\n\n/**\n * Encode a test's frame slice to `<outputDir>/video-<uid>-<session>.webm` and\n * return the artifact. Returns undefined when there are too few frames to make\n * a video or when encoding fails (e.g. fluent-ffmpeg absent) — video is\n * best-effort and must never abort the run.\n */\nexport async function encodePerTestVideo(input: {\n frames: readonly ScreencastFrame[]\n outputDir: string\n testUid: string\n sessionId: string\n /** 0-based attempt; a `-retry<n>` suffix for n>0 keeps retries from\n * overwriting each other's video (mirrors the trace slice's retry keys). */\n attempt?: number\n captureFormat?: 'jpeg' | 'png'\n minFrames?: number\n onLog?: (level: 'info' | 'warn', message: string) => void\n}): Promise<TraceArtifact | undefined> {\n const minFrames = input.minFrames ?? 2\n if (input.frames.length < minFrames) {\n return undefined\n }\n await fs.mkdir(input.outputDir, { recursive: true })\n const retrySuffix = input.attempt ? `-retry${input.attempt}` : ''\n const filename = `video-${fileSlug(input.testUid)}-${input.sessionId.slice(0, 8)}${retrySuffix}.webm`\n const filePath = path.join(input.outputDir, filename)\n try {\n await encodeToVideo([...input.frames], filePath, {\n captureFormat: input.captureFormat\n })\n } catch (err) {\n input.onLog?.('warn', `Per-test video encode failed: ${errorMessage(err)}`)\n return undefined\n }\n return {\n kind: 'video',\n path: filePath,\n scope: 'test',\n key: input.testUid,\n testUids: [input.testUid],\n retained: true\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 type {\n DevToolsMode,\n TestStatus,\n TraceRetentionPolicy\n} from '@wdio/devtools-shared'\n\n/**\n * Pure retention policy evaluation for trace mode. Adapters collect the\n * observed test outcomes for a trace scope (session/spec/test) and ask\n * whether the written trace should be kept.\n */\n\n/** Every policy the evaluator recognizes. An unknown string (from a JS config\n * that slipped past the type) is treated as `on` — fail open, never silently\n * drop a trace the user might need. */\nconst KNOWN_POLICIES = new Set<TraceRetentionPolicy>([\n 'on',\n 'retain-on-failure',\n 'retain-on-first-failure',\n 'on-first-retry',\n 'on-all-retries',\n 'retain-on-failure-and-retries'\n])\n\nexport interface TestOutcome {\n /** Retry-stable test identity. Outcomes sharing a uid are one test's attempts\n * (attempt 0, 1, …); an outcome with no uid is its own single-attempt group,\n * so a flat/uid-less feed evaluates exactly as one-outcome-per-test. */\n uid?: string\n state?: TestStatus\n attempt?: number\n}\n\nexport interface RetentionInput {\n outcomes: Iterable<TestOutcome>\n /** Whether the adapter can distinguish retries (per-outcome `attempt`). */\n attemptInfoAvailable: boolean\n}\n\nexport interface RetentionDecision {\n retain: boolean\n /** Set when a retry-aware policy fell back to `retain-on-failure` because attempt info was unavailable. */\n degradedToFailure?: boolean\n /** Set when no outcomes were observed (standalone scripts) — retained rather than risk losing a needed trace. */\n failOpen?: boolean\n}\n\nexport function shouldRetainTrace(\n policy: TraceRetentionPolicy | undefined,\n input: RetentionInput\n): RetentionDecision {\n if (policy === undefined || policy === 'on' || !KNOWN_POLICIES.has(policy)) {\n return { retain: true }\n }\n const outcomes = Array.from(input.outcomes)\n if (outcomes.length === 0) {\n return { retain: true, failOpen: true }\n }\n const anyFailed = outcomes.some((o) => o.state === 'failed')\n if (!input.attemptInfoAvailable && policy !== 'retain-on-failure') {\n return { retain: anyFailed, degradedToFailure: true }\n }\n // Group a test's attempts so failure policies key on the RIGHT attempt: the\n // *final* attempt (retain-on-failure — a fail-then-pass ends passed) vs the\n // *first* attempt (retain-on-first-failure). A uid-less feed makes every\n // outcome its own group, so this reduces to the flat one-outcome-per-test\n // logic and is byte-identical for callers that don't supply per-attempt uids.\n const groups = groupByTest(outcomes)\n switch (policy) {\n case 'retain-on-failure':\n return { retain: groups.some((g) => finalAttempt(g).state === 'failed') }\n case 'retain-on-first-failure':\n return {\n retain: groups.some((g) => firstAttempt(g)?.state === 'failed')\n }\n case 'on-first-retry':\n return { retain: groups.some((g) => g.some((o) => o.attempt === 1)) }\n case 'on-all-retries':\n return { retain: groups.some((g) => g.some((o) => attemptOf(o) >= 1)) }\n case 'retain-on-failure-and-retries':\n return {\n retain: groups.some(\n (g) =>\n finalAttempt(g).state === 'failed' ||\n g.some((o) => attemptOf(o) >= 1)\n )\n }\n }\n}\n\nconst attemptOf = (o: TestOutcome): number => o.attempt ?? 0\n\n/** Group outcomes by `uid`; a uid-less outcome becomes its own singleton group\n * (preserving flat, one-outcome-per-test evaluation for callers without uids). */\nfunction groupByTest(outcomes: TestOutcome[]): TestOutcome[][] {\n const byUid = new Map<string, TestOutcome[]>()\n const groups: TestOutcome[][] = []\n for (const outcome of outcomes) {\n if (outcome.uid === undefined) {\n groups.push([outcome])\n continue\n }\n const existing = byUid.get(outcome.uid)\n if (existing) {\n existing.push(outcome)\n } else {\n const group = [outcome]\n byUid.set(outcome.uid, group)\n groups.push(group)\n }\n }\n return groups\n}\n\n/** The highest-numbered attempt's outcome — the test's final result. */\nfunction finalAttempt(group: TestOutcome[]): TestOutcome {\n return group.reduce((best, o) => (attemptOf(o) >= attemptOf(best) ? o : best))\n}\n\n/** The attempt-0 outcome, if the group recorded one. */\nfunction firstAttempt(group: TestOutcome[]): TestOutcome | undefined {\n return group.find((o) => attemptOf(o) === 0)\n}\n\n/**\n * Warning text when a retention policy is configured outside trace mode, where\n * it has no effect (the finalizer no-ops in live mode). Returns undefined when\n * there is nothing to warn about, so each adapter can `if (msg) log.warn(msg)`.\n */\nexport function tracePolicyModeWarning(\n policy: TraceRetentionPolicy | undefined,\n mode: DevToolsMode | undefined\n): string | undefined {\n if (policy === undefined || mode === 'trace') {\n return undefined\n }\n return 'tracePolicy only applies in trace mode; ignoring it because mode is not \"trace\".'\n}\n","import type { TestStatus } from '@wdio/devtools-shared'\nimport type { TestOutcome } from './trace-retention.js'\n\n/**\n * Framework-agnostic per-test attempt ledger. Every supported runner re-enters\n * its per-test start hook when a test is retried, so recording the same uid\n * again appends a new attempt slot (0-based: first run is attempt 0, first retry\n * is attempt 1). Once the outcome is known the adapter stamps it onto the slot.\n * This is the primary, runner-independent retry signal feeding the retry-aware\n * trace policies — the finalizer reads the scoped views to evaluate retention\n * per test/spec/session with real per-attempt outcomes (see trace-retention.ts).\n */\nexport interface RetryOutcomeView {\n /** Every attempt of every test — for session-scope retention. */\n all(): TestOutcome[]\n /** Every attempt of the tests recorded against `specFile` — for spec scope. */\n forSpec(specFile: string): TestOutcome[]\n /** A single test's attempts; pass `attempt` to scope to one attempt's slice. */\n forTest(uid: string, attempt?: number): TestOutcome[]\n}\n\nexport class TestAttemptTracker implements RetryOutcomeView {\n #ledger = new Map<string, { specFile?: string; attempts: TestOutcome[] }>()\n #sawRetry = false\n\n /** Record a starting test; returns its attempt number (0 first, +1 per rerun).\n * `specFile` (optional) enables spec-scoped retention lookups. */\n recordStart(uid: string, specFile?: string): number {\n const entry = this.#ledger.get(uid)\n if (entry && entry.attempts.length > 0) {\n this.#sawRetry = true\n // A retry only follows a failure. Some runners (e.g. Mocha through a\n // --require plugin) can't observe the failed attempt's state from their\n // outcome hook and report it as passed — the retry starting IS the\n // reliable failure signal, so stamp the prior attempt failed here.\n entry.attempts[entry.attempts.length - 1].state = 'failed'\n }\n const attempt = entry ? entry.attempts.length : 0\n const slot: TestOutcome = { uid, attempt }\n if (entry) {\n entry.attempts.push(slot)\n if (specFile !== undefined) {\n entry.specFile = specFile\n }\n } else {\n this.#ledger.set(uid, { specFile, attempts: [slot] })\n }\n return attempt\n }\n\n /** Stamp the resolved state onto uid's most recent attempt slot once the\n * outcome is known. `attempt` overrides the slot's number when the adapter\n * resolved a more authoritative value (e.g. WDIO's result.retries). */\n recordOutcome(\n uid: string,\n state: TestStatus | undefined,\n attempt?: number\n ): void {\n const attempts = this.#ledger.get(uid)?.attempts\n const slot = attempts?.[attempts.length - 1]\n if (!slot) {\n return\n }\n slot.state = state\n if (attempt !== undefined) {\n slot.attempt = attempt\n }\n }\n\n /** Latest attempt recorded for `uid`, or undefined if it never started. */\n attemptFor(uid: string): number | undefined {\n const entry = this.#ledger.get(uid)\n return entry ? entry.attempts.length - 1 : undefined\n }\n\n /** True once any test has started more than once (a retry occurred). */\n get sawRetry(): boolean {\n return this.#sawRetry\n }\n\n all(): TestOutcome[] {\n const out: TestOutcome[] = []\n for (const entry of this.#ledger.values()) {\n out.push(...entry.attempts)\n }\n return out\n }\n\n forSpec(specFile: string): TestOutcome[] {\n const out: TestOutcome[] = []\n for (const entry of this.#ledger.values()) {\n if (entry.specFile === specFile) {\n out.push(...entry.attempts)\n }\n }\n return out\n }\n\n forTest(uid: string, attempt?: number): TestOutcome[] {\n const attempts = this.#ledger.get(uid)?.attempts ?? []\n return attempt === undefined\n ? attempts\n : attempts.filter((a) => a.attempt === attempt)\n }\n\n /** Clear all state — used at session boundaries and reuse-mode reconnects. */\n reset(): void {\n this.#ledger.clear()\n this.#sawRetry = false\n }\n}\n","import { createRequire } from 'node:module'\nimport {\n TRACKED_ASSERT_METHODS,\n type CollapsedAssertResult,\n type CommandLog\n} from '@wdio/devtools-shared'\nimport { getCallSourceFromStack, isAssertFromUserCode } from './stack.js'\nimport { toError } from './error.js'\nimport { stripAnsi } from './console.js'\n\nexport { TRACKED_ASSERT_METHODS }\n\nconst require = createRequire(import.meta.url)\n\n/** Per-process guard so a second `patchNodeAssert()` call is a no-op. */\nexport const ASSERT_PATCHED_SYMBOL = Symbol.for(\n '@wdio/devtools-core/assert-patched'\n)\n\n/**\n * Minimum shape `patchNodeAssert` emits. Adapters that need extra bookkeeping\n * (selenium adds `fromElement` and `rawResult`) wrap the callback to extend\n * the object before forwarding to their own `onCommand` sink.\n */\nexport interface CapturedAssert {\n command: string\n args: unknown[]\n /** `'passed'` for a passing assert; a `CollapsedAssertResult` for a failing\n * node:assert (its clean `actual`/`expected` props), so the trace shows\n * labelled rows rather than node's ANSI-stripped char-diff. */\n result: 'passed' | CollapsedAssertResult | undefined\n error: Error | undefined\n callSource: string | undefined\n timestamp: number\n /** Locator of the element the assertion was about, when the adapter can name\n * one from the raw args (see `patchNodeAssert`'s `resolveAssertTarget`).\n * Lands on `CommandLog.selector` so the player's overlay boxes the target the\n * way it does for a folded matcher row. */\n selector?: string\n}\n\n/**\n * JSON-safe stringify of an assert argument. Non-serialisable inputs degrade\n * gracefully: functions → '[Function]', RegExp → `/.../i`, cyclic objects →\n * `String(value)`. Exported so adapters can mirror the shape if they wrap.\n */\nexport function safeSerializeAssertArg(value: unknown): unknown {\n if (value === null || value === undefined) {\n return value\n }\n if (value instanceof RegExp) {\n return value.toString()\n }\n if (typeof value === 'function') {\n return '[Function]'\n }\n if (typeof value === 'object') {\n try {\n return JSON.parse(JSON.stringify(value))\n } catch {\n return String(value)\n }\n }\n return value\n}\n\ninterface NodeAssertionError {\n actual?: unknown\n expected?: unknown\n generatedMessage?: boolean\n}\n\n/** Human-readable rendering of an assert value for the message body. */\nfunction displayAssertValue(value: unknown): string {\n if (typeof value === 'string') {\n return `'${value}'`\n }\n try {\n return JSON.stringify(value) ?? String(value)\n } catch {\n return String(value)\n }\n}\n\n/**\n * Turn a thrown assert error into the captured shape. node:assert's\n * AssertionError carries clean `actual`/`expected` props plus, when it\n * auto-generated the message, a per-character COLORED diff whose body becomes\n * unreadable noise once ANSI is stripped (`'ExampleThis DIs Nomt...'`). Pull the\n * clean values into a CollapsedAssertResult AND rebuild an auto-generated\n * message (and its echo in the stack) as a value-bearing `Expected:/Received:`\n * block, so every consumer — the trace's Errors tab, the runner console, and\n * allure-mocha's error box (which strips the diff's ANSI to mush) — shows\n * readable values. A user-supplied message and errors without `actual`/\n * `expected` (e.g. `assert.ok`, a thrown non-assert error) pass through.\n */\nfunction describeAssertFailure(err: unknown): {\n result: CollapsedAssertResult | undefined\n error: Error\n} {\n const error = toError(err)\n const e = (typeof err === 'object' && err ? err : {}) as NodeAssertionError\n if (!('actual' in e) && !('expected' in e)) {\n return { result: undefined, error }\n }\n const rawMessage = stripAnsi(error.message)\n const cleanMessage = e.generatedMessage\n ? `${rawMessage.split('\\n')[0] ?? 'Assertion failed'}\\n\\n` +\n `Expected: ${displayAssertValue(e.expected)}\\n` +\n `Received: ${displayAssertValue(e.actual)}`\n : rawMessage\n error.message = cleanMessage\n if (error.stack) {\n error.stack = stripAnsi(error.stack).replace(rawMessage, cleanMessage)\n }\n return {\n result: {\n passed: false,\n actual: safeSerializeAssertArg(e.actual),\n expected: safeSerializeAssertArg(e.expected)\n },\n error\n }\n}\n\nfunction makeAssertEmitters(\n methodName: string,\n args: unknown[],\n onCommand: (cmd: CapturedAssert) => void,\n callerStack: string | undefined,\n resolveAssertTarget?: (args: unknown[]) => string | undefined\n): { passed: () => void; failed: (err: unknown) => void } {\n const callInfo = getCallSourceFromStack(callerStack)\n // Drop asserts the user's test didn't fire directly: either no user-code frame\n // on the stack at all, or the assert's immediate caller is a dependency (a\n // framework/library assert firing during a user operation) — both are noise.\n // callerStack is captured in patchedAssert so the frame offsets line up.\n if (callInfo.filePath === undefined || !isAssertFromUserCode(callerStack)) {\n return { passed: () => {}, failed: () => {} }\n }\n const startedAt = Date.now()\n // Resolved from the RAW args: sanitizing loses the object identity an element\n // handle is recognised by. After the user-code gate, so a dropped assert\n // doesn't pay for it.\n const selector = resolveAssertTarget?.(args)\n const sanitizedArgs = args.map(safeSerializeAssertArg)\n const emit = (result: CapturedAssert['result'], error: Error | undefined) => {\n const cmd: CapturedAssert = {\n command: `assert.${methodName}`,\n args: sanitizedArgs,\n result,\n error,\n callSource: callInfo.callSource,\n timestamp: startedAt\n }\n if (selector) {\n cmd.selector = selector\n }\n onCommand(cmd)\n }\n return {\n passed: () => emit('passed', undefined),\n failed: (err: unknown) => {\n const { result, error } = describeAssertFailure(err)\n emit(result, error)\n }\n }\n}\n\nfunction makePatchedAssertMethod(\n methodName: string,\n assertObj: Record<string | symbol, unknown>,\n original: (...a: unknown[]) => unknown,\n onCommand: (cmd: CapturedAssert) => void,\n resolveAssertTarget?: (args: unknown[]) => string | undefined\n): (...args: unknown[]) => unknown {\n return function patchedAssert(this: unknown, ...args: unknown[]) {\n // Captured HERE so frames[0] is this wrapper and frames[1] is the assert's\n // caller — a fixed offset isAssertFromUserCode reads (minification-robust).\n const callerStack = new Error().stack\n const { passed, failed } = makeAssertEmitters(\n methodName,\n args,\n onCommand,\n callerStack,\n resolveAssertTarget\n )\n let result: unknown\n // Node's internalMatch dispatches on `fn === assert.match` (Node ≤20), so\n // a wrapper installed on that property silently inverts `match` into\n // `doesNotMatch`. Restore the original binding for the call so identity\n // checks inside node:assert see the real method.\n assertObj[methodName] = original\n try {\n result = original.apply(this, args)\n } catch (err) {\n failed(err)\n throw err\n } finally {\n assertObj[methodName] = patchedAssert\n }\n // Async assert methods (rejects/doesNotReject) return a Promise.\n const maybe = result as { then?: unknown } | null | undefined\n if (maybe && typeof maybe.then === 'function') {\n return (result as Promise<unknown>).then(\n (v) => {\n passed()\n return v\n },\n (err) => {\n failed(err)\n throw err\n }\n )\n }\n passed()\n return result\n }\n}\n\n/**\n * Convert a `CapturedAssert` into the shared `CommandLog` shape adapters push\n * into their session capturer. Asserts are effectively instantaneous, so the\n * capture timestamp doubles as `startTime`.\n */\nexport function capturedAssertToCommandLog(\n cmd: CapturedAssert,\n testUid?: string\n): CommandLog {\n const entry: CommandLog = {\n command: cmd.command,\n args: cmd.args,\n result: cmd.result,\n timestamp: cmd.timestamp,\n startTime: cmd.timestamp\n }\n if (cmd.error) {\n entry.error = {\n name: cmd.error.name,\n message: cmd.error.message,\n stack: cmd.error.stack\n }\n }\n if (cmd.callSource) {\n entry.callSource = cmd.callSource\n }\n if (cmd.selector) {\n entry.selector = cmd.selector\n }\n if (testUid) {\n entry.testUid = testUid\n }\n return entry\n}\n\n/**\n * Params any adapter's matcher tap produces. `prefix` selects the command\n * namespace (`expect` / `assert` / `verify`), all of which map to an `Assert`\n * action via the shared action map. Adapters do the thin framework-specific\n * extraction (matcher name, args, pass flag, message); this conversion is the\n * single shared path — the generic counterpart to `capturedAssertToCommandLog`\n * for libraries that aren't node:assert (expect-webdriverio, chai, Nightwatch).\n */\nexport interface MatcherAssertion {\n prefix?: string\n method: string\n args?: unknown[]\n passed: boolean\n message?: string | (() => string)\n callSource?: string\n /** Explicit display label for the action row. Falls back to `command` when\n * absent — set it when the framework carries a richer human message than\n * `prefix.method` (e.g. Nightwatch's \"Testing if the page title contains …\"). */\n title?: string\n}\n\nexport function matcherAssertionToCommandLog(\n input: MatcherAssertion,\n testUid?: string\n): CommandLog {\n const command = `${input.prefix ?? 'expect'}.${input.method}`\n const message =\n typeof input.message === 'function' ? input.message() : input.message\n const entry = capturedAssertToCommandLog(\n {\n command,\n args: (input.args ?? []).map(safeSerializeAssertArg),\n result: input.passed ? 'passed' : undefined,\n error: input.passed\n ? undefined\n : new Error(stripAnsi(message ?? `${command} failed`)),\n callSource: input.callSource,\n timestamp: Date.now()\n },\n testUid\n )\n if (input.title) {\n entry.title = input.title\n }\n return entry\n}\n\n/** Wrap every tracked method on one assert namespace object in place; returns\n * how many were patched. Used for both `assert.*` and `assert.strict.*`. */\nfunction patchAssertNamespace(\n nsObj: Record<string | symbol, unknown>,\n onCommand: (cmd: CapturedAssert) => void,\n resolveAssertTarget?: (args: unknown[]) => string | undefined\n): number {\n let count = 0\n for (const methodName of TRACKED_ASSERT_METHODS) {\n const original = nsObj[methodName]\n if (typeof original !== 'function') {\n continue\n }\n nsObj[methodName] = makePatchedAssertMethod(\n methodName,\n nsObj,\n original as (...a: unknown[]) => unknown,\n onCommand,\n resolveAssertTarget\n )\n count++\n }\n return count\n}\n\n/**\n * Patch `node:assert` so each tracked method emits a `CapturedAssert` to the\n * supplied hook. Idempotent across calls (guarded by `ASSERT_PATCHED_SYMBOL`).\n * Returns `true` on success, `false` when node:assert can't be resolved\n * (rare — browser-only Node-incompatible runtimes).\n *\n * Wraps both the function-form (`assert(...)`) and the namespace methods\n * (`assert.equal(...)`). User code that imported the methods BEFORE this\n * patcher loaded keeps stale references — adapters should import node:assert\n * from their main entry before user test files load.\n *\n * @param onCommand Callback invoked once per assert call (sync OR async).\n * Receives the captured shape; do NOT throw — the wrapper\n * re-throws the original assert error after the callback.\n * @param onLog Optional logger for lifecycle events. Default: silent.\n * @param resolveAssertTarget\n * Optional adapter hook naming the element an assert was about,\n * from its raw args. node:assert takes values, not elements, so\n * only the adapter that captured the reads can make the link;\n * the result rides along as `CapturedAssert.selector`.\n */\nexport function patchNodeAssert(\n onCommand: (cmd: CapturedAssert) => void,\n onLog?: (level: 'info' | 'warn', message: string) => void,\n resolveAssertTarget?: (args: unknown[]) => string | undefined\n): boolean {\n const log = (level: 'info' | 'warn', message: string) =>\n onLog?.(level, message)\n\n let assertModule: unknown\n try {\n assertModule = require('node:assert')\n } catch {\n log('warn', 'node:assert not available — skipping assertion capture')\n return false\n }\n\n // Node's `assert` is a function with methods on it — cast once for the\n // symbol + dynamic method access we do here.\n const assertObj = assertModule as Record<string | symbol, unknown>\n if (assertObj[ASSERT_PATCHED_SYMBOL]) {\n return true\n }\n assertObj[ASSERT_PATCHED_SYMBOL] = true\n\n let patched = patchAssertNamespace(assertObj, onCommand, resolveAssertTarget)\n // `import { strict as assert }` and `assert.strict.*` reference a separate\n // object with its own method identities; patch it too so strict-mode\n // assertions are captured the same as the default ones. `assert.strict` is a\n // callable (strict-mode `assert()`) with the methods hung off it, so it's a\n // function, not a plain object.\n const strict = assertObj.strict\n if (strict && (typeof strict === 'object' || typeof strict === 'function')) {\n patched += patchAssertNamespace(\n strict as Record<string | symbol, unknown>,\n onCommand,\n resolveAssertTarget\n )\n }\n\n log('info', `Patched ${patched} node:assert method(s)`)\n return true\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 * True when a tracked assert was called DIRECTLY by user code. `stack` MUST be\n * captured inside the `patchedAssert` wrapper (`new Error().stack` on its first\n * line), so frames[0] is the wrapper and frames[1] is whoever called the\n * assert. A non-user immediate caller means a framework/dependency assert fired\n * *during* a user operation (which `getCallSourceFromStack` would otherwise\n * mis-attribute to the far-off user frame and surface as a noisy row) — drop it.\n * Uses the fixed frame offset rather than the wrapper's name, so it survives a\n * bundler minifying/renaming the wrapper. No stack → keep (never lose a real\n * assert to an absent stack).\n */\nexport function isAssertFromUserCode(stack: string | undefined): boolean {\n if (!stack) {\n return true\n }\n const caller = parseStackTrace(stack)[1]\n return !!caller && isUserCodeFrame(caller)\n}\n\n/**\n * Capture `{ filePath, callSource }` for the first user-code frame on the\n * stack. `callSource` is `<file>:<line>` for the UI's source-location displays;\n * returns `'unknown:0'` (and `undefined` filePath) when no user frame can be\n * found. `stack` defaults to the live stack; callers with a pre-captured stack\n * (e.g. the assert wrapper) pass it so the frame offsets line up.\n */\nexport function getCallSourceFromStack(\n stack: string | undefined = new Error().stack\n): {\n filePath: string | undefined\n callSource: string\n} {\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","import { createHash } from 'node:crypto'\n\n/** Hex SHA-1 used for content-addressed trace resources. */\nexport function sha1Hex(data: Buffer | string): string {\n return createHash('sha1').update(data).digest('hex')\n}\n","// Source files and per-action call stacks for the trace: sources are written\n// as path-addressed `src@<sha1(path)>.txt` resources, and each action's\n// captured `<file>:<line>[:<column>]` callSource becomes an inline stack frame.\n\nimport { sha1Hex } from './sha1.js'\nimport type { TraceZipResource } from './trace-zip-writer.js'\n\nexport interface StackFrame {\n file: string\n line: number\n column: number\n}\n\n// Test files are small; anything bigger is generated/bundled and not worth shipping.\nconst MAX_SOURCE_BYTES = 2 * 1024 * 1024\n\nexport function sourceResourceName(filePath: string): string {\n return `src@${sha1Hex(filePath)}.txt`\n}\n\n// Splits one trailing `:<digits>` segment; `sep <= 0` keeps Windows drive\n// letters (`C:...`) and colon-less paths intact.\nfunction splitNumericSuffix(value: string): { rest: string; num?: number } {\n const sep = value.lastIndexOf(':')\n if (sep <= 0) {\n return { rest: value }\n }\n const digits = value.slice(sep + 1)\n if (!/^\\d+$/.test(digits)) {\n return { rest: value }\n }\n return { rest: value.slice(0, sep), num: Number(digits) }\n}\n\n/** Inline stack from a captured `<file>:<line>[:<column>]` callSource. */\nexport function callSourceToStack(\n callSource?: string\n): StackFrame[] | undefined {\n if (!callSource || callSource === 'unknown:0') {\n return undefined\n }\n const last = splitNumericSuffix(callSource)\n if (last.num === undefined) {\n return [{ file: callSource, line: 0, column: 0 }]\n }\n const prev = splitNumericSuffix(last.rest)\n if (prev.num === undefined) {\n return [{ file: last.rest, line: last.num, column: 0 }]\n }\n return [{ file: prev.rest, line: prev.num, column: last.num }]\n}\n\nexport function buildSourceResources(\n sources: Record<string, string>\n): TraceZipResource[] {\n const out: TraceZipResource[] = []\n for (const [filePath, text] of Object.entries(sources)) {\n const data = Buffer.from(text, 'utf8')\n if (data.byteLength > MAX_SOURCE_BYTES) {\n continue\n }\n out.push({ resourceName: sourceResourceName(filePath), data })\n }\n return out\n}\n","// The ordered open-group path for a command — outermost first — that the action\n// exporter's group stack turns into balanced nested Tracing.tracingGroup\n// markers. The path is the test's ancestry (feature/scenario/suite, from the\n// suite-metadata walk) + the test itself + its step, when a stepUid is set.\n\nimport type { CommandLog, TestMetadataMap } from '@wdio/devtools-shared'\n\nexport interface GroupNode {\n uid: string\n title: string\n}\n\n/** A node's own name, with an enclosing group's title stripped off the front.\n * Some runners report a test's FULL title (`<suite> <test>`), so nesting it\n * under its own suite renders the suite name twice. Display only — the entry's\n * stored title still keys artifacts and slices. Falls back to the full title\n * when stripping would leave nothing, so a test named after its suite keeps a\n * label. */\nfunction ownTitle(title: string, parent: GroupNode | undefined): string {\n if (!parent || !title.startsWith(parent.title)) {\n return title\n }\n return title.slice(parent.title.length).trim() || title\n}\n\nexport function buildGroupPath(\n cmd: CommandLog,\n testMetadata?: TestMetadataMap\n): GroupNode[] {\n const path: GroupNode[] = []\n if (cmd.testUid) {\n const entry = testMetadata?.get(cmd.testUid)\n for (const ancestor of entry?.ancestry ?? []) {\n path.push({\n uid: ancestor.uid,\n title: ownTitle(ancestor.title, path[path.length - 1])\n })\n }\n path.push({\n uid: cmd.testUid,\n title: ownTitle(entry?.title ?? cmd.testUid, path[path.length - 1])\n })\n }\n if (cmd.stepUid) {\n const stepEntry = testMetadata?.get(cmd.stepUid)\n path.push({\n uid: cmd.stepUid,\n title: ownTitle(stepEntry?.title ?? cmd.stepUid, path[path.length - 1])\n })\n }\n return path\n}\n","// Builds the before/after action events of the exported trace stream,\n// including tracingGroup test boundaries and frame-snapshot ref stamping.\n\nimport type {\n CollapsedAssertResult,\n CommandLog,\n TestMetadataMap\n} from '@wdio/devtools-shared'\nimport { locatorsMatch, POINTABLE_METHODS } from '@wdio/devtools-shared'\nimport {\n ASSERT_ACTION_CLASS,\n formatActionTitle,\n mapCommandToAction,\n FILL_METHODS,\n type TraceAction\n} from './action-mapping.js'\nimport { callSourceToStack, type StackFrame } from './trace-sources.js'\nimport type { FrameSnapshotIndex } from './trace-frame-snapshots.js'\nimport { buildGroupPath } from './trace-hierarchy.js'\n\nexport interface 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 /** Trace-viewer 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 /** User-code frame the command was issued from, when captured. */\n stack?: StackFrame[]\n /** Frame-snapshot name rendered as the action's before state. */\n beforeSnapshot?: string\n}\n\nexport interface AfterEvent {\n type: 'after'\n callId: string\n endTime: number\n error?: { message: string }\n /** Command return value (e.g. the text getText resolved to). */\n result?: unknown\n /** CallId of the Tracing.tracingGroup that wraps this action (if any). */\n parentId?: string\n /** Frame-snapshot name rendered as the action's after state. */\n afterSnapshot?: string\n /** Page-coordinate hit point (centre of the matched element) for pointer\n * actions — drives the player's click marker + timeline pointer glyph. */\n point?: { x: number; y: number }\n}\n\n// Serialized command results over this size are dropped from the trace — a\n// huge execute() return shouldn't bloat every action line.\nconst MAX_RESULT_BYTES = 64 * 1024\n\n/** JSON-safe command result within the size cap; undefined when absent,\n * oversized, or not serializable. */\nfunction serializableResult(result: unknown): unknown {\n if (result === undefined) {\n return undefined\n }\n try {\n const json = JSON.stringify(result)\n if (json === undefined || json.length > MAX_RESULT_BYTES) {\n return undefined\n }\n return JSON.parse(json)\n } catch {\n return undefined\n }\n}\n\nexport type ActionEvent = BeforeEvent | AfterEvent\n\ninterface ActionStream {\n events: ActionEvent[]\n prevEndMs: number\n callCounter: number\n /** Currently-open tracingGroup path, outermost first (feature→scenario→step).\n * Actions nest under the innermost entry. */\n openGroups: { uid: string; callId: string }[]\n}\n\n// An adapter may attach a normalized CollapsedAssertResult (see shared) to an\n// assertion command — prefer its actual/expected over the positional args,\n// which are only correct for node:assert-style `[actual, expected]` calls.\nfunction collapsedAssertResult(\n result: unknown\n): CollapsedAssertResult | undefined {\n if (typeof result === 'object' && result !== null && 'passed' in result) {\n return result as CollapsedAssertResult\n }\n return undefined\n}\n\n// Assert params: node:assert positional order (actual, expected, message?),\n// plus a numeric echo of the raw args so the reader's paramsToArgs inverse\n// reconstructs the original arg list without assert-specific knowledge.\nfunction buildAssertParams(cmd: CommandLog): Record<string, unknown> {\n const params: Record<string, unknown> = Object.fromEntries(\n cmd.args.map((arg, index) => [String(index), arg])\n )\n const [actual, expected, message] = cmd.args\n const collapsed = collapsedAssertResult(cmd.result)\n const semantic = {\n actual: collapsed?.actual ?? actual,\n expected: collapsed?.expected ?? expected,\n message: collapsed?.message ?? message\n }\n for (const [key, value] of Object.entries(semantic)) {\n if (value !== undefined) {\n params[key] = value\n }\n }\n return params\n}\n\n// Semantic params from positional args (selector/value/url), falling back to\n// index keys; the reader's paramsToArgs is the inverse.\nfunction buildActionParams(\n action: TraceAction,\n rawArgs: unknown[]\n): Record<string, unknown> {\n const isValueMethod = FILL_METHODS.has(action.method)\n if (action.class === 'Element' && isValueMethod && rawArgs.length >= 2) {\n return { selector: rawArgs[0], value: rawArgs[1] }\n }\n if (action.class === 'Element' && isValueMethod && rawArgs.length === 1) {\n return { value: rawArgs[0] }\n }\n if (\n action.class === 'Element' &&\n rawArgs.length === 1 &&\n typeof rawArgs[0] === 'string'\n ) {\n return { selector: rawArgs[0] }\n }\n if (rawArgs.length === 1 && typeof rawArgs[0] === 'string') {\n return { url: rawArgs[0] }\n }\n return Object.fromEntries(rawArgs.map((a, i) => [String(i), a]))\n}\n\nfunction closeGroupsFrom(stream: ActionStream, from: number): void {\n // Innermost-first so nested after events stay balanced.\n for (let i = stream.openGroups.length - 1; i >= from; i--) {\n stream.events.push({\n type: 'after',\n callId: stream.openGroups[i].callId,\n endTime: stream.prevEndMs\n })\n }\n stream.openGroups.length = Math.max(0, from)\n}\n\n// Diff the command's desired group path (feature→scenario→step, or just the\n// test) against the open stack: close the diverged tail, then open the new tail\n// with parentId chaining so the reader nests them. Actions then reference the\n// innermost open group. A path of length ≤1 with no ancestry/step reproduces\n// the previous single-group-per-test output.\nfunction syncGroups(\n stream: ActionStream,\n cmd: CommandLog,\n pageId: string,\n wallTime: number,\n testMetadata?: TestMetadataMap\n): void {\n const desired = buildGroupPath(cmd, testMetadata)\n let common = 0\n while (\n common < stream.openGroups.length &&\n common < desired.length &&\n stream.openGroups[common].uid === desired[common].uid\n ) {\n common++\n }\n closeGroupsFrom(stream, common)\n for (let i = common; i < desired.length; i++) {\n stream.callCounter++\n const callId = `call@${stream.callCounter}`\n const parentId = stream.openGroups[i - 1]?.callId\n const groupBefore: BeforeEvent = {\n type: 'before',\n callId,\n startTime: Math.max(\n stream.prevEndMs,\n (cmd.startTime ?? cmd.timestamp) - wallTime\n ),\n class: 'Tracing',\n method: 'tracingGroup',\n pageId,\n params: { name: desired[i].title },\n title: desired[i].title,\n apiName: 'tracing.tracingGroup'\n }\n if (parentId) {\n groupBefore.parentId = parentId\n }\n stream.events.push(groupBefore)\n stream.openGroups.push({ uid: desired[i].uid, callId })\n }\n}\n\nfunction buildParamsAndTitle(\n action: TraceAction,\n cmd: CommandLog\n): { params: Record<string, unknown>; title: string } {\n const isAssert = action.class === ASSERT_ACTION_CLASS\n const params = isAssert\n ? buildAssertParams(cmd)\n : buildActionParams(action, cmd.args)\n // The element this command targeted, under a key distinct from `selector` so\n // it never feeds `paramsToArgs` — lets the reader restore CommandLog.selector\n // for the player's overlay even when args carry an assertion's expected value.\n if (cmd.selector && params.locator === undefined) {\n params.locator = cmd.selector\n }\n return {\n params,\n title: formatActionTitle(\n action,\n cmd.args,\n params,\n isAssert ? cmd.command : undefined\n )\n }\n}\n\nfunction actionError(\n cmd: CommandLog,\n action: TraceAction\n): { message: string } | undefined {\n if (cmd.error) {\n const err = cmd.error as { message?: string }\n return { message: err.message ?? String(cmd.error) }\n }\n const isAssert = action.class === ASSERT_ACTION_CLASS\n // A command whose own result reports `passed: false` failed, whatever class it\n // maps to — not just the assert rows this used to be gated on.\n const collapsed = collapsedAssertResult(cmd.result)\n if (collapsed && collapsed.passed === false) {\n return {\n message: String(\n collapsed.message ??\n (isAssert ? 'Assertion failed' : `${cmd.command} failed`)\n )\n }\n }\n // A wait that resolved `false` timed out. Nightwatch reports its\n // waitForElement* timeouts through its own assertion channel rather than the\n // command callback, which hands back `{value: null}` — so the row's only\n // evidence of the failure is that boolean, and without this the step a test\n // most often fails on rendered green with nothing in the Errors tab. Scoped to\n // waits because `false` is a legitimate value for a boolean read like\n // `isDisplayed`, and WDIO's waits throw rather than return false.\n if (cmd.result === false && action.method.startsWith('waitFor')) {\n const target = cmd.args?.find((a) => typeof a === 'string')\n return {\n message: target\n ? `${cmd.command} timed out waiting for ${String(target)}`\n : `${cmd.command} timed out`\n }\n }\n return undefined\n}\n\n/** Centre of the element a pointer action matched, from the captured element\n * rects at the command's completion. Undefined for non-pointer actions, a\n * non-string selector, or a selector absent from the captured elements (e.g. a\n * WDIO text/xpath locator `getSelector` didn't reproduce). A runner that acts\n * through a resolved element handle leaves nothing selector-shaped in `args`\n * (Selenium: the handle is the receiver, the args hold only the typed value), so\n * the row's recorded locator is read first. */\nfunction resolveActionPoint(\n action: TraceAction,\n cmd: CommandLog,\n snapshotIndex?: FrameSnapshotIndex\n): { x: number; y: number } | undefined {\n if (action.class !== 'Element' || !POINTABLE_METHODS.has(action.method)) {\n return undefined\n }\n const selector = cmd.selector ?? cmd.args?.[0]\n if (typeof selector !== 'string') {\n return undefined\n }\n const elements = snapshotIndex?.elementsAt(cmd.timestamp)\n for (const el of elements ?? []) {\n // Narrow the unknown element record at the boundary (element-scripts shape).\n const e = el as {\n selector?: string\n boundingBox?: { x: number; y: number; width: number; height: number }\n }\n if (e.selector && locatorsMatch(e.selector, selector) && e.boundingBox) {\n const bb = e.boundingBox\n return { x: bb.x + bb.width / 2, y: bb.y + bb.height / 2 }\n }\n }\n return undefined\n}\n\nfunction buildAfterEvent(\n cmd: CommandLog,\n action: TraceAction,\n callId: string,\n endMs: number,\n snapshotIndex?: FrameSnapshotIndex\n): AfterEvent {\n const afterEvent: AfterEvent = { type: 'after', callId, endTime: endMs }\n const error = actionError(cmd, action)\n if (error) {\n afterEvent.error = error\n }\n const result = serializableResult(cmd.result)\n if (result !== undefined) {\n afterEvent.result = result\n }\n const afterName = snapshotIndex?.claimAfter(cmd.timestamp, callId)\n if (afterName) {\n afterEvent.afterSnapshot = afterName\n }\n const point = resolveActionPoint(action, cmd, snapshotIndex)\n if (point) {\n afterEvent.point = point\n }\n return afterEvent\n}\n\nfunction pushActionPair(\n stream: ActionStream,\n cmd: CommandLog,\n action: TraceAction,\n pageId: string,\n wallTime: number,\n snapshotIndex?: FrameSnapshotIndex\n): void {\n stream.callCounter++\n const callId = `call@${stream.callCounter}`\n // Command invocation timestamp, falling back to completion when absent.\n const rawStartMs = (cmd.startTime ?? cmd.timestamp) - wallTime\n const rawEndMs = cmd.timestamp - wallTime\n // Floor at prevEndMs to prevent visual overlap with the previous action.\n const startMs = Math.max(stream.prevEndMs, rawStartMs)\n // +1ms minimum duration so an `after` never precedes its parsed `before`.\n const endMs = Math.max(startMs + 1, rawEndMs)\n const { params, title } = buildParamsAndTitle(action, cmd)\n const beforeEvent: BeforeEvent = {\n type: 'before',\n callId,\n startTime: startMs,\n class: action.class,\n method: action.method,\n pageId,\n params,\n title,\n apiName: `${action.class.toLowerCase()}.${action.method}`,\n parentId: stream.openGroups[stream.openGroups.length - 1]?.callId\n }\n const stack = callSourceToStack(cmd.callSource)\n if (stack) {\n beforeEvent.stack = stack\n }\n const beforeName = snapshotIndex?.beforeName()\n if (beforeName) {\n beforeEvent.beforeSnapshot = beforeName\n }\n stream.events.push(beforeEvent)\n stream.events.push(buildAfterEvent(cmd, action, callId, endMs, snapshotIndex))\n stream.prevEndMs = endMs\n}\n\nexport function buildActionEvents(\n commands: CommandLog[],\n pageId: string,\n wallTime: number,\n testMetadata?: TestMetadataMap,\n snapshotIndex?: FrameSnapshotIndex\n): ActionEvent[] {\n const stream: ActionStream = {\n events: [],\n prevEndMs: 0,\n callCounter: 0,\n openGroups: []\n }\n // Process in chronological order, not insertion order. Deferred rows (e.g.\n // Nightwatch native asserts, finalized in one batch at test-end) are appended\n // late but carry their real call-time `startTime`; since pushActionPair floors\n // each start at the running prevEndMs, out-of-order input would clamp those\n // late rows to the end of the timeline (clustering them after the last real\n // command). A stable sort by start time restores true positions. `sequence`\n // then breaks ties the millisecond clock can't: a deferred row issued\n // microseconds before the command that follows it reads as the same\n // `startTime`, and insertion order put it after — which is how an assert that\n // ran on the secure page landed below the logout click it preceded. Each\n // command owns its own before/after pair, so pairing is unaffected.\n const ordered = [...commands].sort(\n (a, b) =>\n (a.startTime ?? a.timestamp) - (b.startTime ?? b.timestamp) ||\n (a.sequence ?? 0) - (b.sequence ?? 0)\n )\n for (const cmd of ordered) {\n const action = mapCommandToAction(cmd.command)\n if (!action) {\n continue\n }\n syncGroups(stream, cmd, pageId, wallTime, testMetadata)\n pushActionPair(stream, cmd, action, pageId, wallTime, snapshotIndex)\n }\n closeGroupsFrom(stream, 0)\n return stream.events\n}\n","// Maps captured ConsoleLog entries into trace-event vocabulary: browser\n// console entries become `console` events; test/terminal output becomes\n// `stdout`/`stderr` events (which carry no location semantics — matching\n// what we capture for Node-side lines).\n\nimport type { ConsoleLog, LogSource } from '@wdio/devtools-shared'\n\nexport interface ConsoleEvent {\n type: 'console'\n time: number\n pageId?: string\n messageType: string\n text: string\n args?: { preview: string; value: unknown }[]\n location: { url: string; lineNumber: number; columnNumber: number }\n}\n\nexport interface StdioEvent {\n type: 'stdout' | 'stderr'\n timestamp: number\n text?: string\n /** Extension field: test-vs-terminal origin; foreign viewers ignore it. */\n source?: Extract<LogSource, 'test' | 'terminal'>\n}\n\n// Caps pathological runs (console.log in a loop) so the trace stays openable.\nconst MAX_CONSOLE_EVENTS = 10_000\n\n/** Trace vocabulary uses 'warning'; 'trace' maps to the nearest severity, 'debug'. */\nfunction toTraceLevel(level: ConsoleLog['type']): string {\n if (level === 'warn') {\n return 'warning'\n }\n if (level === 'trace') {\n return 'debug'\n }\n return level\n}\n\nfunction previewArg(arg: unknown): string {\n if (typeof arg === 'string') {\n return arg\n }\n try {\n return JSON.stringify(arg) ?? String(arg)\n } catch {\n return String(arg)\n }\n}\n\nexport function buildConsoleEvents(\n logs: ConsoleLog[],\n pageId: string,\n wallTime: number\n): (ConsoleEvent | StdioEvent)[] {\n const capped = logs.slice(0, MAX_CONSOLE_EVENTS)\n const events: (ConsoleEvent | StdioEvent)[] = capped.map((log) => {\n const time = Math.max(0, log.timestamp - wallTime)\n const text = log.args.map(previewArg).join(' ')\n // Untagged entries predate source tagging; they came from the page.\n if (log.source === 'browser' || log.source === undefined) {\n return {\n type: 'console',\n time,\n pageId,\n messageType: toTraceLevel(log.type),\n text,\n args: log.args.map((arg) => ({ preview: previewArg(arg), value: arg })),\n // Location isn't captured at the patch site; the required field ships zeroed.\n location: { url: '', lineNumber: 0, columnNumber: 0 }\n } satisfies ConsoleEvent\n }\n return {\n type: log.type === 'error' || log.type === 'warn' ? 'stderr' : 'stdout',\n timestamp: time,\n text,\n source: log.source\n } satisfies StdioEvent\n })\n if (logs.length > MAX_CONSOLE_EVENTS) {\n const last = capped[capped.length - 1]\n events.push({\n type: 'stderr',\n timestamp: last ? Math.max(0, last.timestamp - wallTime) : 0,\n text: `[devtools] console truncated: dropped ${logs.length - MAX_CONSOLE_EVENTS} entries`\n })\n }\n return events\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 { TRACE_ZIP_ENTRIES } from '@wdio/devtools-shared'\nimport type {\n ActionSnapshot,\n CommandLog,\n ConsoleLog,\n Metadata,\n NetworkRequest,\n ScreencastFrame,\n TestMetadataMap,\n TestRunnerId,\n TraceFormat,\n TraceLog,\n TraceMutation\n} from '@wdio/devtools-shared'\nimport { mapCommandToAction } from './action-mapping.js'\nimport {\n buildConsoleEvents,\n type ConsoleEvent,\n type StdioEvent\n} from './trace-console.js'\nimport {\n buildFilmstripEvents,\n buildSnapshotResources,\n type ScreencastFrameEvent\n} from './trace-snapshots.js'\nimport { buildDenseScreencast } from './screencast-trace.js'\nimport {\n buildImageFrameSnapshots,\n FrameSnapshotIndex,\n type FrameSnapshotEvent\n} from './trace-frame-snapshots.js'\nimport { buildActionEvents, type ActionEvent } from './trace-action-events.js'\nimport { buildSourceResources } from './trace-sources.js'\nimport { networkRequestToHar } from './trace-har.js'\nimport { buildTraceZip, type TraceZipResource } from './trace-zip-writer.js'\nimport { buildMutationsNdjson } from './trace-mutations.js'\nimport { generateTranscript } from './trace-transcript.js'\nimport { sha1Hex } from './sha1.js'\n\n// Transcript building moved to its own module; re-exported here because this is\n// the name the package barrel and downstream adapters already import.\nexport { generateTranscript }\n\nconst TRACE_VERSION = 8\nconst LIBRARY_NAME = '@wdio/devtools-core'\nconst LIBRARY_VERSION = '1.0.0'\n\n/** Response bodies above this size are not embedded in the trace. */\nconst MAX_BODY_RESOURCE_BYTES = 1024 * 1024\n/** Per-trace ceiling on total embedded response-body bytes. */\nconst MAX_TOTAL_BODY_RESOURCE_BYTES = 20 * 1024 * 1024\n\nexport interface NetworkBodyCaps {\n maxBodyBytes: number\n maxTotalBytes: number\n}\n\nexport interface NetworkBodyResources {\n resources: TraceZipResource[]\n sha1ByRequestId: Map<string, string>\n}\n\n/** Content-addressed `resources/<sha1>` entries for captured response bodies. */\nexport function buildNetworkBodyResources(\n requests: NetworkRequest[],\n caps: NetworkBodyCaps = {\n maxBodyBytes: MAX_BODY_RESOURCE_BYTES,\n maxTotalBytes: MAX_TOTAL_BODY_RESOURCE_BYTES\n }\n): NetworkBodyResources {\n const resources: TraceZipResource[] = []\n const sha1ByRequestId = new Map<string, string>()\n const stored = new Set<string>()\n let totalBytes = 0\n // Cap skips are silent by design; a warn hook would slot into these branches.\n for (const request of requests) {\n if (request.responseBody === undefined) {\n continue\n }\n const data = Buffer.from(request.responseBody, 'utf8')\n if (data.byteLength > caps.maxBodyBytes) {\n continue\n }\n const sha1 = sha1Hex(data)\n if (stored.has(sha1)) {\n sha1ByRequestId.set(request.id, sha1)\n continue\n }\n if (totalBytes + data.byteLength > caps.maxTotalBytes) {\n continue\n }\n stored.add(sha1)\n totalBytes += data.byteLength\n resources.push({ resourceName: sha1, data })\n sha1ByRequestId.set(request.id, sha1)\n }\n return { resources, sha1ByRequestId }\n}\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 /** Extension field: the runner that recorded this zip, so the player can tell\n * the user how to resolve a captured locator in their own framework. Absent\n * when the capture didn't identify itself, and in foreign zips. */\n runner?: TestRunnerId\n}\n\ntype TraceEvent =\n | ContextOptionsEvent\n | ActionEvent\n | ScreencastFrameEvent\n | ConsoleEvent\n | StdioEvent\n | FrameSnapshotEvent\n\nfunction shortId(sessionId?: string): string {\n return (sessionId ?? Math.random().toString(36).slice(2, 10)).slice(0, 8)\n}\n\nfunction allocateTraceIds(sessionId?: string): {\n contextId: string\n pageId: string\n} {\n const idPrefix = shortId(sessionId)\n return { contextId: `context@${idPrefix}`, pageId: `page@${idPrefix}` }\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> | 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 ...(trace.metadata.runner ? { runner: trace.metadata.runner } : {})\n }\n}\n\nfunction buildNetworkNdjson(\n requests: NetworkRequest[],\n wallTime: number,\n pageId: string,\n sha1ByRequestId: Map<string, string>\n): Buffer {\n if (!requests.length) {\n return Buffer.alloc(0)\n }\n const lines = requests.map((r) => {\n const entry = networkRequestToHar(r, {\n bodySha1: sha1ByRequestId.get(r.id)\n }) 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\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 case 'frame-snapshot':\n return e.snapshot.timestamp\n case 'console':\n return e.time\n case 'stdout':\n case 'stderr':\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 console output observed\n * at the boundary, then the next action's `before`. Matches the viewer's\n * expectation that the screencast frame shows the state between the\n * 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 case 'frame-snapshot':\n return 2\n case 'console':\n case 'stdout':\n case 'stderr':\n return 3\n case 'before':\n return 4\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\ninterface TraceBundle {\n traceNdjson: string\n networkNdjson: Buffer\n transcriptMd: string\n mutationsNdjson: Buffer\n resources: TraceZipResource[]\n}\n\nfunction buildEventStream(\n trace: TraceLog,\n ctxOptions: ContextOptionsEvent,\n pageId: string,\n wallTime: number,\n testMetadata?: TestMetadataMap,\n denseFrameEvents: ScreencastFrameEvent[] = []\n): TraceEvent[] {\n const viewport = trace.metadata.viewport ?? { width: 1280, height: 720 }\n const snapshots = trace.actionSnapshots ?? []\n const snapshotIndex = new FrameSnapshotIndex(snapshots)\n // Dense frames supersede the sparse per-action filmstrip; per-action DOM is\n // carried by the frame-snapshot events, so sparse frames would only duplicate.\n const filmstrip =\n denseFrameEvents.length > 0\n ? denseFrameEvents\n : buildFilmstripEvents(snapshots, pageId, wallTime, viewport)\n const events: TraceEvent[] = [\n ctxOptions,\n ...filmstrip,\n ...buildActionEvents(\n trace.commands,\n pageId,\n wallTime,\n testMetadata,\n snapshotIndex\n ),\n ...buildImageFrameSnapshots(\n snapshotIndex.refs(),\n pageId,\n wallTime,\n viewport\n ),\n ...buildConsoleEvents(trace.consoleLogs, pageId, wallTime)\n ]\n events.sort(compareEvents)\n return events\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 { contextId, pageId } = allocateTraceIds(opts.sessionId)\n const ctxOptions = buildContextOptions(trace, contextId, wallTime)\n const dense = buildDenseScreencast(\n trace.screencastFrames ?? [],\n pageId,\n wallTime,\n trace.metadata.viewport ?? { width: 1280, height: 720 }\n )\n const events = buildEventStream(\n trace,\n ctxOptions,\n pageId,\n wallTime,\n opts.testMetadata,\n dense.events\n )\n const networkBodies = buildNetworkBodyResources(trace.networkRequests)\n return {\n traceNdjson: events.map((e) => JSON.stringify(e)).join('\\n') + '\\n',\n networkNdjson: buildNetworkNdjson(\n trace.networkRequests,\n wallTime,\n pageId,\n networkBodies.sha1ByRequestId\n ),\n transcriptMd: generateTranscript(\n trace.commands,\n wallTime,\n ctxOptions.title\n ),\n mutationsNdjson: buildMutationsNdjson(trace.mutations ?? []).ndjson,\n resources: [\n ...buildSnapshotResources(trace.actionSnapshots ?? [], pageId),\n ...dense.resources,\n ...buildSourceResources(trace.sources),\n ...networkBodies.resources\n ]\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 mutationsNdjson: bundle.mutationsNdjson\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 const entry = (name: string) => path.join(targetDir, name)\n const {\n trace: T,\n network,\n mutations,\n transcript,\n resourcesDir\n } = TRACE_ZIP_ENTRIES\n await fs.mkdir(entry(resourcesDir), { recursive: true })\n await Promise.all([\n fs.writeFile(entry(T), bundle.traceNdjson),\n fs.writeFile(entry(transcript), bundle.transcriptMd, 'utf8'),\n bundle.networkNdjson.length\n ? fs.writeFile(entry(network), bundle.networkNdjson)\n : Promise.resolve(),\n bundle.mutationsNdjson.length\n ? fs.writeFile(entry(mutations), bundle.mutationsNdjson)\n : Promise.resolve(),\n ...bundle.resources.map((r) =>\n fs.writeFile(\n path.join(targetDir, TRACE_ZIP_ENTRIES.resourcesDir, r.resourceName),\n r.data\n )\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 /** Dense screencast frames for the filmstrip. Thinned + content-addressed at\n * export time; adapters pass the slice's windowed frames (or all, session\n * scope). Omitted → no dense filmstrip (byte-stable with today's output). */\n screencastFrames?: readonly ScreencastFrame[]\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 /** Base name for the artifact (zip file stem / directory name). Defaults to\n * `trace-<sessionId>`; per-test slices pass `'trace'` inside a named folder. */\n fileStem?: string\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 ...(opts.screencastFrames?.length\n ? { screencastFrames: [...opts.screencastFrames] }\n : {})\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 const stem = opts.fileStem ?? `trace-${opts.sessionId}`\n if (opts.format === 'ndjson-directory') {\n const dir = path.join(opts.outputDir, stem)\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, `${stem}.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","// Per-action snapshot resources and the screencast-frame filmstrip derived\n// from them. Split from trace-exporter.ts; the exporter composes these into\n// the trace event stream.\n\nimport type { ActionSnapshot } from '@wdio/devtools-shared'\nimport type { TraceZipResource } from './trace-zip-writer.js'\n\nexport interface 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\n// The one merge rule for same-timestamp snapshots: richest wins per field,\n// first-seen wins for identity (command/url/title, which no export path reads).\n// Merging per field matters because the real screenshot and the real elements\n// can land on different captures of the same action.\nfunction mergeSnapshotInto(\n existing: ActionSnapshot,\n snap: ActionSnapshot\n): void {\n if ((snap.screenshot?.length ?? 0) > (existing.screenshot?.length ?? 0)) {\n existing.screenshot = snap.screenshot\n }\n if (!existing.elements?.length && snap.elements?.length) {\n existing.elements = snap.elements\n }\n if (!existing.snapshotText && snap.snapshotText) {\n existing.snapshotText = snap.snapshotText\n }\n}\n\n// Two snapshots at the same timestamp (a real post-action capture and a blank\n// end-of-scenario one) map to the same `${pageId}-${ts}` resource name, so a\n// last-wins write lets a blank frame clobber the real one. Collapse to one per\n// timestamp so the resource writer never sees a collision.\nfunction collapseByTimestamp(snapshots: ActionSnapshot[]): ActionSnapshot[] {\n const byTs = new Map<number, ActionSnapshot>()\n const order: number[] = []\n for (const snap of snapshots) {\n const existing = byTs.get(snap.timestamp)\n if (!existing) {\n byTs.set(snap.timestamp, { ...snap })\n order.push(snap.timestamp)\n continue\n }\n mergeSnapshotInto(existing, snap)\n }\n return order.map((ts) => byTs.get(ts)!)\n}\n\n/** Insert a snapshot, or merge it into the one already holding its timestamp.\n * Applies the collapse rule at capture time so the accumulator holds one record\n * per timestamp: a mid-run slice flush reads this array live, before export\n * would have collapsed it, and would otherwise emit a blank duplicate. */\nexport function upsertRichestSnapshot(\n snapshots: ActionSnapshot[],\n snap: ActionSnapshot\n): void {\n const idx = snapshots.findIndex((s) => s.timestamp === snap.timestamp)\n if (idx === -1) {\n snapshots.push(snap)\n return\n }\n mergeSnapshotInto(snapshots[idx]!, snap)\n}\n\nexport function buildSnapshotResources(\n rawSnapshots: ActionSnapshot[],\n pageId: string\n): TraceZipResource[] {\n const snapshots = collapseByTimestamp(rawSnapshots)\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 frameForSnapshot(\n snap: ActionSnapshot,\n pageId: string,\n timestamp: number,\n viewport: { width: number; height: number }\n): ScreencastFrameEvent {\n const base = `${pageId}-${snap.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\n }\n if (snap.elements && snap.elements.length) {\n frame.elements = `${base}-elements.json`\n }\n if (snap.snapshotText) {\n frame.snapshot = `${base}-snapshot.txt`\n }\n return frame\n}\n\n/** Filmstrip events; the first snapshot is re-anchored to t=0 for pre-interaction state. */\nexport function buildFilmstripEvents(\n rawSnapshots: ActionSnapshot[],\n pageId: string,\n wallTime: number,\n viewport: { width: number; height: number }\n): ScreencastFrameEvent[] {\n const snapshots = collapseByTimestamp(rawSnapshots)\n const firstSnap = snapshots.find((s) => s.screenshot)\n const events: ScreencastFrameEvent[] = []\n if (firstSnap) {\n events.push(frameForSnapshot(firstSnap, pageId, 0, viewport))\n }\n for (const snap of snapshots) {\n if (snap === firstSnap || !snap.screenshot) {\n continue\n }\n events.push(\n frameForSnapshot(\n snap,\n pageId,\n Math.max(0, snap.timestamp - wallTime),\n viewport\n )\n )\n }\n return events\n}\n","/**\n * Dense screencast filmstrip for trace mode. Turns the recorder's continuous\n * frame buffer into `screencast-frame` events plus content-addressed image\n * resources, so the trace player scrubs smooth playback instead of one frame\n * per action.\n *\n * Two independent bounds keep the cost in check: thinning caps the number of\n * emitted events (min inter-frame gap + drop consecutive byte-identical frames\n * + even downsample to a hard cap), and content-addressing dedupes the image\n * bytes (identical frames — a static wait polled repeatedly — collapse to one\n * resource). Framework-agnostic: adapters feed `recorder.frames`; the recorder\n * itself (CDP push on Chrome, screenshot polling elsewhere) lives per-adapter.\n */\n\nimport { createHash } from 'node:crypto'\nimport type { ScreencastFrame } from '@wdio/devtools-shared'\nimport type { TraceZipResource } from './trace-zip-writer.js'\nimport type { ScreencastFrameEvent } from './trace-snapshots.js'\n\nexport interface DenseScreencastOptions {\n /** Hard cap on emitted frame events (default 600). */\n maxFrames?: number\n /** Minimum ms between kept frames (default 100 ≈ 10 fps). */\n minFrameIntervalMs?: number\n}\n\nconst DEFAULT_MAX_FRAMES = 600\nconst DEFAULT_MIN_INTERVAL_MS = 100\n\n/**\n * A run's whole filmstrip: the frames drained from sessions that have already\n * ended plus the one still recording. Both sources are always needed — a\n * mid-run per-spec/per-test flush fires before the live recorder is drained, so\n * its frames exist only on the recorder, while at the final write the recorder\n * is already gone. Reading either alone silently truncates the filmstrip.\n */\nexport function accumulatedScreencastFrames(\n drained: readonly ScreencastFrame[],\n recording: { readonly frames: readonly ScreencastFrame[] } | undefined\n): ScreencastFrame[] {\n return [...drained, ...(recording?.frames ?? [])]\n}\n\n/**\n * Thin a continuous frame buffer down to a bounded event set. Keeps a frame\n * only when it is at least `minFrameIntervalMs` after the last kept frame and\n * its bytes differ from the last kept frame (a static run collapses to its\n * first frame — the player holds it until the next change, which is the big win\n * for polling mode where every poll re-shoots an unchanged page). A final even\n * downsample enforces the hard cap while always keeping the first and last\n * frames so the timeline still spans the full run.\n */\nexport function thinScreencastFrames(\n frames: readonly ScreencastFrame[],\n options: DenseScreencastOptions = {}\n): ScreencastFrame[] {\n const minInterval = options.minFrameIntervalMs ?? DEFAULT_MIN_INTERVAL_MS\n const maxFrames = options.maxFrames ?? DEFAULT_MAX_FRAMES\n\n const kept: ScreencastFrame[] = []\n let lastKeptTs = -Infinity\n let lastData: string | undefined\n for (const frame of frames) {\n if (frame.timestamp - lastKeptTs < minInterval) {\n continue\n }\n if (frame.data === lastData) {\n continue\n }\n kept.push(frame)\n lastKeptTs = frame.timestamp\n lastData = frame.data\n }\n\n if (kept.length <= maxFrames) {\n return kept\n }\n if (maxFrames <= 1) {\n return kept.length ? [kept[0]!] : []\n }\n const out: ScreencastFrame[] = []\n const step = (kept.length - 1) / (maxFrames - 1)\n for (let i = 0; i < maxFrames; i++) {\n out.push(kept[Math.round(i * step)]!)\n }\n return out\n}\n\n/**\n * Build the dense filmstrip: `screencast-frame` events (offsets rebased against\n * `wallTime`, matching the sparse filmstrip) and their image resources named by\n * content hash so byte-identical frames share a single resource. Adapters that\n * don't record frames pass an empty array and get an empty result — byte-stable\n * with today's output.\n */\nexport function buildDenseScreencast(\n frames: readonly ScreencastFrame[],\n pageId: string,\n wallTime: number,\n viewport: { width: number; height: number },\n options: DenseScreencastOptions = {}\n): { events: ScreencastFrameEvent[]; resources: TraceZipResource[] } {\n const events: ScreencastFrameEvent[] = []\n const resources: TraceZipResource[] = []\n const seen = new Set<string>()\n for (const frame of thinScreencastFrames(frames, options)) {\n // One malformed frame must not cost the run its whole trace: a screenshot\n // probe that answered a driver error object instead of base64 reached here\n // and `Buffer.from` threw inside the export, losing every other event too.\n if (typeof frame.data !== 'string' || !frame.data) {\n continue\n }\n const data = Buffer.from(frame.data, 'base64')\n const sha1 = createHash('sha1').update(data).digest('hex')\n const resourceName = `${sha1}.jpeg`\n if (!seen.has(sha1)) {\n seen.add(sha1)\n resources.push({ resourceName, data })\n }\n events.push({\n type: 'screencast-frame',\n pageId,\n sha1: resourceName,\n width: viewport.width,\n height: viewport.height,\n timestamp: Math.max(0, frame.timestamp - wallTime)\n })\n }\n return { events, resources }\n}\n","// Image-backed frame-snapshot synthesis: each action screenshot becomes a\n// minimal DOM document so standard trace viewers render the action pane.\n// Compatibility shim until real DOM snapshots are captured.\n\nimport type { ActionSnapshot } from '@wdio/devtools-shared'\n\nconst SNAPSHOT_DOCTYPE = 'html'\nconst FALLBACK_FRAME_URL = 'about:blank'\nconst BODY_STYLE = 'margin:0'\nconst IMAGE_STYLE = 'display:block;width:100vw;height:100vh;object-fit:contain'\n\n/** Serialized DOM node: text, or a [TAG, attributes, ...children] tuple. */\nexport type FrameSnapshotNode =\n string | [string, Record<string, string>, ...FrameSnapshotNode[]]\n\nexport interface FrameSnapshotResourceOverride {\n url: string\n sha1?: string\n ref?: number\n}\n\nexport interface FrameSnapshot {\n callId: string\n snapshotName: string\n pageId: string\n frameId: string\n frameUrl: string\n doctype?: string\n html: FrameSnapshotNode\n viewport: { width: number; height: number }\n timestamp: number\n wallTime: number\n collectionTime: number\n resourceOverrides: FrameSnapshotResourceOverride[]\n isMainFrame: boolean\n}\n\nexport interface FrameSnapshotEvent {\n type: 'frame-snapshot'\n snapshot: FrameSnapshot\n}\n\nexport interface FrameSnapshotRef {\n callId: string\n snapshotName: string\n snapshot: ActionSnapshot\n}\n\n/** Correlates captured screenshots to callIds as the exporter assigns them. */\nexport class FrameSnapshotIndex {\n #byTimestamp = new Map<number, ActionSnapshot>()\n // Element rects by timestamp, indexed independently of the screenshot gate so\n // A8 input points resolve even when the frame carries no screenshot.\n #elementsByTimestamp = new Map<number, unknown[]>()\n #refs: FrameSnapshotRef[] = []\n #lastName?: string\n\n constructor(snapshots: ActionSnapshot[]) {\n for (const snap of snapshots) {\n if (snap.elements) {\n const currentEls = this.#elementsByTimestamp.get(snap.timestamp)\n if (!currentEls || snap.elements.length > currentEls.length) {\n this.#elementsByTimestamp.set(snap.timestamp, snap.elements)\n }\n }\n if (!snap.screenshot) {\n continue\n }\n const current = this.#byTimestamp.get(snap.timestamp)\n // Same-timestamp duplicates keep the richest capture (dedupe parity).\n if (\n !current ||\n snap.screenshot.length > (current.screenshot?.length ?? 0)\n ) {\n this.#byTimestamp.set(snap.timestamp, snap)\n }\n }\n }\n\n /** Captured element rects at a command's completion timestamp, if any. */\n elementsAt(timestamp: number): unknown[] | undefined {\n return this.#elementsByTimestamp.get(timestamp)\n }\n\n /** Snapshot name representing the page state before the next action. */\n beforeName(): string | undefined {\n return this.#lastName\n }\n\n /**\n * The capture describing the page when this command completed: the one at its\n * exact timestamp, else the most recent one before it.\n *\n * Non-consuming, and falls back rather than returning nothing, because several\n * actions legitimately share one page state. Nightwatch emits its native\n * assertion rows in a batch whose execution windows collapse onto the same\n * instant, so handing the capture to whichever claimed first left the rest of\n * the batch with no DOM, no a11y tree and no screenshot in the action pane.\n * And an assertion reads the page rather than changing it, so it takes no\n * capture of its own and correctly inherits the preceding action's.\n */\n claimAfter(timestamp: number, callId: string): string | undefined {\n const snap =\n this.#byTimestamp.get(timestamp) ?? this.#latestAtOrBefore(timestamp)\n if (!snap) {\n return undefined\n }\n const snapshotName = `after@${callId}`\n this.#refs.push({ callId, snapshotName, snapshot: snap })\n this.#lastName = snapshotName\n return snapshotName\n }\n\n #latestAtOrBefore(timestamp: number): ActionSnapshot | undefined {\n let best: ActionSnapshot | undefined\n for (const [ts, snap] of this.#byTimestamp) {\n if (ts <= timestamp && (!best || ts > best.timestamp)) {\n best = snap\n }\n }\n return best\n }\n\n refs(): FrameSnapshotRef[] {\n return this.#refs\n }\n}\n\nfunction frameIdForPage(pageId: string): string {\n const suffix = pageId.startsWith('page@')\n ? pageId.slice('page@'.length)\n : pageId\n return `frame@${suffix}`\n}\n\n// Captures come from WebDriver screenshots (PNG) or CDP screencasts (JPEG),\n// so the mime is sniffed from the base64 magic rather than assumed.\nfunction imageMimeType(base64: string): string {\n return base64.startsWith('iVBOR') ? 'image/png' : 'image/jpeg'\n}\n\nfunction imageDocument(snap: ActionSnapshot): FrameSnapshotNode {\n const screenshot = snap.screenshot ?? ''\n return [\n 'HTML',\n {},\n ['HEAD', {}, ['BASE', { href: snap.url ?? FALLBACK_FRAME_URL }]],\n [\n 'BODY',\n { style: BODY_STYLE },\n [\n 'IMG',\n {\n src: `data:${imageMimeType(screenshot)};base64,${screenshot}`,\n style: IMAGE_STYLE\n }\n ]\n ]\n ]\n}\n\n/** One frame-snapshot event per claimed screenshot, viewer-shape exact. */\nexport function buildImageFrameSnapshots(\n refs: FrameSnapshotRef[],\n pageId: string,\n wallTime: number,\n viewport: { width: number; height: number }\n): FrameSnapshotEvent[] {\n const frameId = frameIdForPage(pageId)\n return refs.map((ref) => ({\n type: 'frame-snapshot' as const,\n snapshot: {\n callId: ref.callId,\n snapshotName: ref.snapshotName,\n pageId,\n frameId,\n frameUrl: ref.snapshot.url ?? FALLBACK_FRAME_URL,\n doctype: SNAPSHOT_DOCTYPE,\n html: imageDocument(ref.snapshot),\n viewport: { width: viewport.width, height: viewport.height },\n timestamp: Math.max(0, ref.snapshot.timestamp - wallTime),\n wallTime: ref.snapshot.timestamp,\n collectionTime: 0,\n resourceOverrides: [],\n isMainFrame: true\n }\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\n/** Bodies below this inline as plain `text`; larger ones rely on `_sha1` only. */\nconst INLINE_BODY_TEXT_MAX_BYTES = 8 * 1024\n\nexport interface HarResponseContent {\n size: number\n mimeType: string\n /** Inline body fallback for small textual bodies. */\n text?: string\n /** Content-addressed body resource name under `resources/`. */\n _sha1?: string\n}\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: HarResponseContent\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\nfunction toHarContent(\n entry: NetworkRequest,\n mimeType: string,\n bodySha1: string | undefined\n): HarResponseContent {\n const content: HarResponseContent = { size: entry.size ?? 0, mimeType }\n const body = entry.responseBody\n if (body === undefined) {\n return content\n }\n if (Buffer.byteLength(body, 'utf8') < INLINE_BODY_TEXT_MAX_BYTES) {\n content.text = body\n }\n if (bodySha1) {\n content._sha1 = bodySha1\n }\n return content\n}\n\nexport function networkRequestToHar(\n entry: NetworkRequest,\n opts: { bodySha1?: string } = {}\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: toHarContent(entry, mimeType, opts.bodySha1),\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 { TRACE_ZIP_ENTRIES } from '@wdio/devtools-shared'\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 /** NDJSON DOM mutation stream (one mutation per line). Omitted/empty → no entry. */\n mutationsNdjson?: Buffer\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(\n Buffer.from(inputs.traceNdjson, 'utf8'),\n TRACE_ZIP_ENTRIES.trace\n )\n zipFile.addBuffer(inputs.networkNdjson, TRACE_ZIP_ENTRIES.network)\n if (inputs.transcriptMd) {\n zipFile.addBuffer(\n Buffer.from(inputs.transcriptMd, 'utf8'),\n TRACE_ZIP_ENTRIES.transcript\n )\n }\n if (inputs.mutationsNdjson?.length) {\n zipFile.addBuffer(inputs.mutationsNdjson, TRACE_ZIP_ENTRIES.mutations)\n }\n for (const resource of inputs.resources) {\n zipFile.addBuffer(\n resource.data,\n `${TRACE_ZIP_ENTRIES.resourcesDir}/${resource.resourceName}`\n )\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","// Serialize the captured DOM mutation stream into the trace zip's\n// `trace.mutations` NDJSON entry so the offline player can replay DOM\n// time-travel. Standard trace viewers ignore the unknown entry, so it's\n// compat-safe. Keep-earliest under the byte cap — the initial full-DOM\n// childList and early diffs survive so replay-from-start stays intact; only\n// late mutations drop, and a trailing sentinel records how many.\n\nimport type {\n MutationsTruncationMarker,\n TraceMutation\n} from '@wdio/devtools-shared'\n\n/** Ceiling on the serialized `trace.mutations` payload — keeps archives bounded\n * on mutation-heavy SPAs. Late mutations drop first (replay-from-start holds). */\nexport const MAX_MUTATIONS_NDJSON_BYTES = 50 * 1024 * 1024\n\n/** A full-document anchor rather than an observed diff. `url` is the reliable\n * discriminator: only the collector's `captureCurrentDom` sets it, and neither\n * the MutationObserver serializer nor the synthetic field-state records do. */\nfunction isDomAnchor(mutation: TraceMutation): boolean {\n return mutation.type === 'childList' && mutation.url !== undefined\n}\n\n/**\n * Re-stamp the full-document anchors in a freshly drained batch onto the action\n * that produced the document.\n *\n * An anchor carries the document's own birth time, which lands a few ms *after*\n * the command that navigated there — so the navigating action's row, which ends\n * the moment the command was logged, still resolves to the PREVIOUS page's DOM.\n *\n * `resolve` maps an anchor's own timestamp to the action it belongs to. Deriving\n * it from the anchor rather than from whichever drain collected it is what makes\n * this race-free: several drains compete for a fresh collector's buffer (the\n * first `getTraceData` empties it) and the winner is routinely a later command's\n * post-DOM drain, so trusting the collecting drain mis-credited the anchor.\n *\n * `floor` is the newest timestamp already in the accumulated stream. An anchor is\n * never pulled ahead of mutations belonging to the document it replaces — replay\n * would then apply those stale refs to the new tree — and never pushed later than\n * the page stamped it, so an overshooting attribution is simply ignored.\n */\nexport function reattributeDomAnchors(\n batch: TraceMutation[],\n resolve: (anchorOwnTime: number) => number | undefined,\n floor = 0\n): void {\n let lowerBound = floor\n for (const mutation of batch) {\n if (isDomAnchor(mutation)) {\n const attributed = resolve(mutation.timestamp)\n if (attributed !== undefined) {\n const target = Math.max(attributed, lowerBound)\n if (target < mutation.timestamp) {\n mutation.timestamp = target\n }\n }\n }\n lowerBound = Math.max(lowerBound, mutation.timestamp)\n }\n}\n\nexport interface MutationsNdjsonResult {\n /** NDJSON payload (one mutation per line, optional trailing marker). Empty\n * buffer when there are no mutations. */\n ndjson: Buffer\n truncated: boolean\n /** Count actually written (excludes the dropped tail and the marker line). */\n written: number\n}\n\n/**\n * Serialize mutations to NDJSON under `cap` bytes, keeping the earliest. The\n * first mutation is always emitted (even if it alone exceeds the cap) so the\n * initial full-DOM snapshot is never lost; when any are dropped a\n * `MutationsTruncationMarker` line is appended.\n */\nexport function buildMutationsNdjson(\n mutations: readonly TraceMutation[],\n cap: number = MAX_MUTATIONS_NDJSON_BYTES\n): MutationsNdjsonResult {\n if (!mutations.length) {\n return { ndjson: Buffer.alloc(0), truncated: false, written: 0 }\n }\n const lines: string[] = []\n let bytes = 0\n for (const mutation of mutations) {\n const line = JSON.stringify(mutation)\n // +1 for the '\\n' that will join this line to the previous one.\n const add = Buffer.byteLength(line, 'utf8') + (lines.length ? 1 : 0)\n if (lines.length > 0 && bytes + add > cap) {\n break\n }\n lines.push(line)\n bytes += add\n }\n const written = lines.length\n const dropped = mutations.length - written\n if (dropped > 0) {\n const marker: MutationsTruncationMarker = { __truncated__: true, dropped }\n lines.push(JSON.stringify(marker))\n }\n return {\n ndjson: Buffer.from(lines.join('\\n'), 'utf8'),\n truncated: dropped > 0,\n written\n }\n}\n","// Builds the trace's `transcript.md` — a Markdown step list read by humans and\n// fed to an LLM. Runner-agnostic; the exporter writes whatever this returns.\n\nimport type { CommandLog } from '@wdio/devtools-shared'\nimport {\n formatActionTitle,\n mapCommandToAction,\n FILL_METHODS,\n type TraceAction\n} from './action-mapping.js'\nimport { stripAnsi } from './console.js'\n\n/** Render `text` as one numbered markdown list item, indenting every line after\n * the first to the marker's content column (`'1. '` → 3, `'10. '` → 4 — an\n * ordered list's continuation indent tracks the marker width, unlike a\n * bullet's fixed two). A step interpolates captured strings that may hold\n * newlines — a framework error routinely does (expect-webdriverio puts\n * `Expected:` and `Received:` on their own lines) and a typed value can too —\n * and an unindented tail leaves the list, reading as top-level prose\n * unattributed to the step that produced it. */\nfunction asNumberedItem(marker: string, text: string): string {\n const indent = ' '.repeat(marker.length)\n const [head, ...tail] = text.split('\\n')\n return [\n `${marker}${head}`,\n // A whitespace-only line stays empty: indenting it would only add trailing\n // whitespace, and a blank line inside an indented item is still inside it.\n ...tail.map((line) => (line.trim() ? `${indent}${line}` : ''))\n ].join('\\n')\n}\n\nfunction errorMessage(error: NonNullable<CommandLog['error']>): string {\n const raw =\n typeof error === 'object' && error !== null && 'message' in error\n ? String((error as { message: unknown }).message)\n : String(error)\n // Runner errors carry terminal colour (node's AssertionError diff is\n // colour-coded), which is noise in a document read by a model.\n return stripAnsi(raw).trim()\n}\n\n/** Commands that map to a trace action, in invocation order. */\nfunction capturedSteps(\n commands: CommandLog[]\n): { entry: CommandLog; action: TraceAction }[] {\n // Sort by invocation time so batched commands land at their real timeline\n // positions — Nightwatch buffers native asserts and emits them at test-end,\n // so raw order clusters all asserts after the navigations. The Actions tree\n // stays correct because buildActionEvents applies the same sort; mirror it\n // here so the transcript matches execution order. Stable + a no-op for\n // already-ordered WDIO/Selenium command logs.\n const ordered = [...commands].sort(\n (a, b) =>\n (a.startTime ?? a.timestamp) - (b.startTime ?? b.timestamp) ||\n (a.sequence ?? 0) - (b.sequence ?? 0)\n )\n const captured: { entry: CommandLog; action: TraceAction }[] = []\n for (const c of ordered) {\n const action = mapCommandToAction(String(c.command))\n if (action) {\n captured.push({ entry: c, action })\n }\n }\n return captured\n}\n\n/**\n * Generate a human/LLM-readable Markdown transcript from captured commands.\n */\nexport function 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 capturedSteps(commands).forEach(({ entry, action }, idx) => {\n const rawArgs = entry.args as unknown[]\n const parts: string[] = [stripAnsi(formatActionTitle(action, rawArgs))]\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=\"${stripAnsi(String(rawArgs[valueIdx]))}\"`)\n }\n }\n\n if (entry.error) {\n parts.push(`ERROR: ${errorMessage(entry.error)}`)\n }\n\n lines.push(asNumberedItem(`${idx + 1}. `, parts.join(' ')))\n })\n\n return lines.join('\\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 path from 'node:path'\nimport type {\n ActionSnapshot,\n CommandLog,\n ScreencastFrame,\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, isStepUidOf } from './uid.js'\nimport { trimChar } from './artifact-naming.js'\n\n// ─── SpecRange ────────────────────────────────────────────────────────────────\n\n/** Index ranges into a SessionCapturer's flat arrays for one trace slice\n * (a spec file, or a single test under `test` granularity). */\nexport interface SpecRange {\n specFile: string\n /** Dedupe/identity key: spec path for spec slices; testUid for test slices,\n * or `${testUid}-retry${n}` so each retried attempt is its own slice. */\n key: string\n /** Present only for test-granularity slices; the base (non-retry) testUid. */\n testUid?: string\n commandStartIdx: number\n consoleStartIdx: number\n networkStartIdx: number\n mutationStartIdx: number\n traceLogStartIdx: number\n}\n\n/**\n * The slice range to eager-flush for a just-ended test. When the caller knows\n * the test's uid (e.g. WDIO's `afterTest`), reverse-scan for it — retries push\n * a new range under the same uid, and the next test's boundary may already be\n * recorded, so the last range isn't reliably this test's. When the uid isn't\n * independently known (Nightwatch/Selenium discover it from the range itself),\n * the last recorded range is the just-ended test's. Undefined when there is no\n * range to flush. One helper for what the three adapters each open-coded.\n */\nexport function findFlushableRange(\n ranges: readonly SpecRange[],\n testUid?: string\n): SpecRange | undefined {\n if (testUid !== undefined) {\n for (let i = ranges.length - 1; i >= 0; i--) {\n if (ranges[i]!.testUid === testUid) {\n return ranges[i]\n }\n }\n return undefined\n }\n return ranges[ranges.length - 1]\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 const cleaned = trimChar(\n specFile\n .replace(/^.*[/\\\\]/, '')\n .replace(/\\.[^.]+$/, '')\n .replace(/[^a-zA-Z0-9_-]/g, '_'),\n '_'\n )\n return cleaned || 'unknown-spec'\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// ─── Test slice session ID ──────────────────────────────────────────────────\n\n/**\n * Build a collision-safe test-level session ID from the slice's spec file, its\n * identity `key` (testUid, or `${testUid}-retry${n}` for retries), and the\n * parent session ID. Hashing the key keeps retries and sibling tests in the\n * same spec from colliding on filename, while the spec basename keeps the name\n * human-readable.\n */\nexport function buildTestSliceSessionId(\n specFile: string,\n key: string,\n sessionId: string\n): string {\n const base = sanitizeSpecName(specFile)\n const hash = deterministicUid(key).split('-').pop()!.slice(0, 8)\n return `${base}-${hash}-${sessionId.slice(0, 8)}`\n}\n\n// ─── Test slice output folder ────────────────────────────────────────────────\n\n/** Max slug length so a title doesn't blow past filesystem path limits. */\nconst MAX_SLUG_LENGTH = 60\n\n/** Lowercase, collapse runs of non-alphanumerics to `-`, trim edge dashes,\n * and cap length (trimming a dash the cut may leave behind). */\nfunction slugify(text: string): string {\n const collapsed = trimChar(\n text.toLowerCase().replace(/[^a-z0-9]+/g, '-'),\n '-'\n )\n // Trim again after the length cap in case it sliced mid-dash-run.\n return trimChar(collapsed.slice(0, MAX_SLUG_LENGTH), '-')\n}\n\n/**\n * Build the per-test output folder name: `<spec>-<title>-<browser>[-retryN]`.\n * The spec basename is sanitized via {@link sanitizeSpecName}; title and browser\n * are slugified. An empty title falls back to a short hash of `key`, and a\n * `${uid}-retry${n}` key appends a `-retry<N>` suffix so retries don't collide.\n */\nexport function buildTestSliceFolder(\n specFile: string,\n testTitle: string | undefined,\n browser: string | undefined,\n key: string\n): string {\n const specBase = sanitizeSpecName(path.basename(specFile))\n // A short hash of the slice key disambiguates two tests that share a title\n // (Scenario Outline examples with a placeholder-free name, or duplicate `it`\n // titles) — without it their folders collide and one trace overwrites the\n // other. Mirrors buildTestSliceSessionId, which hashes the key for the id.\n const keyHash = deterministicUid(key).split('-').pop()!.slice(0, 8)\n const titleSlug = slugify(testTitle ?? '') || keyHash\n const browserSlug = slugify(browser ?? '') || 'browser'\n const retryMatch = key.match(/-retry(\\d+)$/)\n const retrySuffix = retryMatch ? `-retry${retryMatch[1]}` : ''\n return `${specBase}-${titleSlug}-${browserSlug}-${keyHash}${retrySuffix}`\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/**\n * Filter a full `testUid → metadata` map down to a single test's entry and its\n * step entries. The per-test analog of {@link filterTestMetadataBySpec}. The\n * steps have to come along: `buildGroupPath` names each step group from this\n * map and falls back to the raw uid when the entry is missing, so dropping them\n * renders a scenario's steps as `stable-…:step:1` instead of their Gherkin text.\n */\nexport function filterTestMetadataByUid(\n allMetadata: TestMetadataMap,\n testUid: string\n): TestMetadataMap {\n const filtered: TestMetadataMap = new Map()\n const entry = allMetadata.get(testUid)\n if (entry) {\n filtered.set(testUid, entry)\n }\n for (const [uid, meta] of allMetadata) {\n if (isStepUidOf(uid, testUid)) {\n filtered.set(uid, meta)\n }\n }\n return filtered\n}\n\n// ─── Spec boundary recording ──────────────────────────────────────────────────\n\n/**\n * Minimal context needed by `recordSliceBoundary` to detect spec-file / test\n * transitions and capture array index ranges. `flushedSpecs` holds already-\n * flushed slice keys (spec paths or test keys), shared with the finalizer.\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}\n\n/** Push a new slice range and return the previous (unflushed) range to flush.\n * `suppressSameKey` skips recording when the incoming key matches the last\n * range's — used for spec granularity so consecutive tests in one file share\n * a slice; test granularity records every attempt (retries included). */\nfunction pushSliceRange(\n ctx: SpecBoundaryContext,\n specFile: string,\n key: string,\n testUid: string | undefined,\n suppressSameKey: boolean\n): SpecRange | null {\n const lastRange = ctx.specRanges[ctx.specRanges.length - 1]\n if (suppressSameKey && lastRange && lastRange.key === key) {\n return null\n }\n const prevRange =\n lastRange && !ctx.flushedSpecs.has(lastRange.key) ? lastRange : null\n\n ctx.specRanges.push({\n specFile,\n key,\n testUid,\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 })\n\n return prevRange\n}\n\n/**\n * Record a trace-slice boundary. For `spec` granularity, a new slice starts\n * when the spec file changes (existing behavior). For `test` granularity, a\n * new slice starts on every recorded test — including retries: a repeated\n * `testUid` is keyed `${testUid}-retry${n}` so each attempt is its own slice.\n * Returns the previous, not-yet-flushed range so the caller can flush it, or\n * `null` when nothing needs flushing (same spec, missing testUid, or a\n * non-sliced granularity).\n */\nexport function recordSliceBoundary(\n ctx: SpecBoundaryContext,\n granularity: TraceGranularity | undefined,\n specFile: string,\n testUid?: string\n): SpecRange | null {\n if (granularity === 'spec') {\n return pushSliceRange(ctx, specFile, specFile, undefined, true)\n }\n if (granularity === 'test' && testUid !== undefined) {\n const priorAttempts = ctx.specRanges.filter(\n (r) => r.testUid === testUid\n ).length\n const key =\n priorAttempts === 0 ? testUid : `${testUid}-retry${priorAttempts}`\n return pushSliceRange(ctx, specFile, key, testUid, false)\n }\n return null\n}\n\n/**\n * Record a spec-file boundary. Thin back-compat wrapper over\n * {@link recordSliceBoundary}; behavior is unchanged for `spec` granularity\n * and returns `null` for every other granularity.\n */\nexport function recordSpecBoundary(\n ctx: SpecBoundaryContext,\n specFile: string,\n traceGranularity: TraceGranularity | undefined\n): SpecRange | null {\n return recordSliceBoundary(ctx, traceGranularity, specFile)\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 /** Full session frame buffer; windowed to this slice's wall-clock span. */\n screencastFrames?: readonly ScreencastFrame[]\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/** Timestamped items inside a slice's wall-clock window `[start, end)`. An\n * undefined `start` (a slice with nothing to anchor on) yields nothing; an\n * undefined `end` (the last slice) is open-ended, so a trailing assertion-wait\n * stays with its test. Used for both action snapshots and screencast frames so\n * they partition on the same reliable boundary as the sliced commands. */\nfunction sliceByWindow<T extends { timestamp: number }>(\n items: readonly T[],\n start: number | undefined,\n end: number | undefined\n): T[] {\n if (start === undefined) {\n return []\n }\n return items.filter(\n (i) => i.timestamp >= start && (end === undefined || i.timestamp < end)\n )\n}\n\n/** Slice the parent capturer/snapshots for one range and write the artifact\n * under `sliceSessionId` with the pre-filtered `testMetadata`. Shared by the\n * spec and test write paths so both slice identically. `overrides` lets the\n * test path redirect into a named folder with a fixed `trace` file stem. */\nasync function writeSliceTrace(\n input: WriteSpecTraceInput,\n sliceSessionId: string,\n testMetadata: TestMetadataMap,\n overrides: { outputDir?: string; fileStem?: string } = {}\n): Promise<string> {\n const sliceCapturer = buildSpecCapturer(\n input.capturer,\n input.range,\n input.nextRange\n )\n\n // Anchor the slice on its own commands, which buildSpecCapturer index-slices\n // reliably — parent-array index lookups desync under reloadSession and would\n // sweep an earlier test's snapshots/frames in. Snapshots and frames are then\n // windowed by that wall-clock span, and the slice is rebased to its own start\n // (start === wallTime) so a per-test trace begins at 0, not the session\n // offset. `windowEnd` is the next slice's start; open for the last. Anchor on\n // the command's `startTime` (invocation), not `timestamp` (completion), so the\n // frames captured *during* the first action — e.g. the page load of an opening\n // `url()` — fall inside this slice rather than being dropped or bleeding into\n // the previous one.\n const commandStart = (c: CommandLog | undefined): number | undefined =>\n c ? (c.startTime ?? c.timestamp) : undefined\n // A command-less slice (assertion-only test) has no command to anchor on; fall\n // back to the earliest of its own index-sliced console/network timestamps so\n // its snapshots/frames still window+rebase rather than drop to the session\n // offset. Empty of both keeps today's undefined → no-window behavior.\n // Both are epoch ms (Date.now), matching the command anchor and the frame/\n // snapshot clock — NOT NetworkRequest.startTime, which is performance.now\n // (relative ms) and would anchor the window ~54,000 years off.\n const fallbackStart = (): number | undefined => {\n const times = [\n ...sliceCapturer.consoleLogs.map((c) => c.timestamp),\n ...sliceCapturer.networkRequests.map((n) => n.timestamp)\n ]\n return times.length ? Math.min(...times) : undefined\n }\n const windowStart =\n commandStart(sliceCapturer.commandsLog[0]) ?? fallbackStart()\n const windowEnd = input.nextRange\n ? commandStart(input.capturer.commandsLog[input.nextRange.commandStartIdx])\n : undefined\n\n const sliceSnapshots = sliceByWindow(\n input.actionSnapshots,\n windowStart,\n windowEnd\n )\n const sliceFrames = input.screencastFrames\n ? sliceByWindow(input.screencastFrames, windowStart, windowEnd)\n : undefined\n\n if (windowStart !== undefined) {\n sliceCapturer.startWallTime = windowStart\n }\n\n return writeTraceZip(sliceCapturer, {\n outputDir: overrides.outputDir ?? input.outputDir,\n sessionId: sliceSessionId,\n fileStem: overrides.fileStem,\n capabilities: input.capabilities,\n actionSnapshots: sliceSnapshots.length > 0 ? sliceSnapshots : undefined,\n screencastFrames: sliceFrames?.length ? sliceFrames : undefined,\n format: input.format,\n testMetadata\n })\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 return writeSliceTrace(\n input,\n buildSpecSessionId(input.range.specFile, input.sessionId),\n filterTestMetadataBySpec(input.testMetadata, input.range.specFile)\n )\n}\n\n/**\n * Write a standalone trace artifact for a single test slice into its own\n * folder: `<outputDir>/<specBasename>-<titleSlug>-<browserSlug>[-retryN]/trace.zip`.\n * Reuses {@link WriteSpecTraceInput}; the folder is the slice's external\n * identity (title/browser/retry), while {@link buildTestSliceSessionId} names\n * the sessionId embedded inside the archive.\n */\nexport async function writeTestSliceTrace(\n input: WriteSpecTraceInput\n): Promise<string> {\n const testUid = input.range.testUid ?? input.range.key\n const title = input.testMetadata.get(testUid)?.title\n // capabilities is framework-typed unknown; read only browserName here.\n const browserName = (\n input.capabilities as { browserName?: string } | undefined\n )?.browserName\n const folder = buildTestSliceFolder(\n input.range.specFile,\n title,\n browserName,\n input.range.key\n )\n return writeSliceTrace(\n input,\n buildTestSliceSessionId(\n input.range.specFile,\n input.range.key,\n input.sessionId\n ),\n filterTestMetadataByUid(input.testMetadata, testUid),\n { outputDir: path.join(input.outputDir, folder), fileStem: 'trace' }\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\nconst STEP_UID_SEPARATOR = ':step:'\n\n/**\n * Key for one step (a Cucumber `Given`/`When`/`Then`) inside a test, derived\n * from the test's own uid plus a per-test index — repeated step text can't\n * collide. Derived rather than hashed so a step's owning test is recoverable\n * from the key alone, which is what {@link isStepUidOf} relies on.\n */\nexport function stepMetadataUid(testUid: string, index: number): string {\n return `${testUid}${STEP_UID_SEPARATOR}${index}`\n}\n\n/** True when `uid` is a step of `testUid`. Anchored on the full test uid plus\n * the separator, so a sibling test whose uid merely starts with the same\n * characters doesn't match. */\nexport function isStepUidOf(uid: string, testUid: string): boolean {\n return uid.startsWith(`${testUid}${STEP_UID_SEPARATOR}`)\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","/**\n * Framework-agnostic orchestration of the trace-mode export at end-of-run:\n * session vs per-spec fan-out, the spec-granularity-without-boundaries\n * fallback, retention gating, and per-write error isolation. The three\n * adapters assemble a TraceExportContext from their own state and call\n * finalizeTraceExport — all sequencing lives here.\n */\n\nimport type {\n ActionSnapshot,\n DevToolsMode,\n ScreencastFrame,\n TestMetadataMap,\n TraceFormat,\n TraceGranularity,\n TraceRetentionPolicy\n} from '@wdio/devtools-shared'\nimport { errorMessage } from './error.js'\nimport {\n buildArtifactsManifest,\n writeArtifactsManifest\n} from './artifacts-manifest.js'\nimport {\n filterTestMetadataBySpec,\n filterTestMetadataByUid,\n writeSpecTrace,\n writeTestSliceTrace,\n type SpecRange\n} from './spec-trace-helpers.js'\nimport { shouldRetainTrace, type TestOutcome } from './trace-retention.js'\nimport type { RetryOutcomeView } from './attempt-tracker.js'\nimport { writeTraceZip, type TraceCapturer } from './trace-exporter.js'\n\n/** One artifact produced (or, when `retained` is false, decided-against) by a\n * trace-mode run — a trace slice, a screencast video, or a per-test\n * screenshot. */\nexport interface TraceArtifact {\n kind: 'trace' | 'video' | 'screenshot'\n path: string\n scope: 'session' | 'spec' | 'test'\n /** specFile for spec scope, testUid for test scope. */\n key?: string\n testUids: string[]\n /** false = decided-not-to-write (reported, not written). Always true today. */\n retained: boolean\n}\n\nexport interface TraceExportContext {\n mode?: DevToolsMode\n /** undefined → treated as `on` (always retain) by the retention evaluator. */\n policy?: TraceRetentionPolicy\n /** True when the adapter fed real per-test attempt numbers (B4); retry-aware\n * policies degrade to retain-on-failure when this is false. */\n attemptInfoAvailable?: boolean\n /** Per-attempt outcome ledger. When present, retention is evaluated against\n * real per-attempt outcomes (scoped per session/spec/test) instead of the\n * collapsed final-attempt state in `testMetadata` — this is what lets\n * retain-on-first-failure see a failed-then-passed first attempt and stops\n * retain-on-failure over-retaining it. Absent → falls back to `testMetadata`\n * (unchanged behavior). */\n outcomes?: RetryOutcomeView\n granularity?: TraceGranularity\n format?: TraceFormat\n capturer: TraceCapturer\n actionSnapshots?: ActionSnapshot[]\n /** Continuous screencast frame buffer for the dense filmstrip (filmstrip\n * option on). Session write uses all; slice writes window per test. */\n screencastFrames?: readonly ScreencastFrame[]\n sessionId: string\n capabilities?: unknown\n testMetadata: TestMetadataMap\n /** Recorded spec boundaries; empty for session granularity. */\n ranges: SpecRange[]\n /** Spec-file dedupe set shared with the adapter's boundary flushes. */\n flushed: Set<string>\n /** Adapters keep their differing dir logic; range is set for spec writes. */\n resolveOutputDir: (range?: SpecRange) => string\n /** Pending snapshot captures to settle before writing (selenium/nightwatch). */\n awaitPending?: Promise<unknown>[]\n log?: (level: 'info' | 'warn', msg: string) => void\n onArtifact?: (a: TraceArtifact) => void\n /** When true, finalize writes a `devtools-artifacts-<sessionId>.json` manifest\n * enumerating every artifact (retained or not) plus per-test states, for\n * ecosystem reporters (Allure, CI collectors) to consume. */\n emitManifest?: boolean\n /** Every artifact seen via `onArtifact` across the run (including eager\n * mid-run test-slice flushes, which the end-of-run fan-out dedupes away).\n * Pass the same list the adapter accumulates so the manifest is complete;\n * finalize falls back to its own fan-out results when this is omitted. */\n collectedArtifacts?: readonly TraceArtifact[]\n}\n\nconst SPEC_WITHOUT_BOUNDARIES_WARNING =\n 'traceGranularity is \"spec\" but no spec boundaries were detected ' +\n '(the runner may not expose per-test hooks). Falling back to ' +\n 'session-level trace.'\n\nconst TEST_WITHOUT_BOUNDARIES_WARNING =\n 'traceGranularity is \"test\" but no test boundaries were detected ' +\n '(the runner may not expose per-test hooks). Falling back to ' +\n 'session-level trace.'\n\n/** Above this many slices, warn to pair granularity with a retention policy. */\nconst SLICE_COUNT_WARN_THRESHOLD = 200\n\n/** Cap on how long finalize waits for still-in-flight snapshot captures. A\n * fire-and-forget capture against a tearing-down session can hang forever\n * (never resolves nor rejects); blocking the export on it would deadlock the\n * whole run (finalize never returns → the runner never tears the session down\n * → the capture stays stuck). Past this bound we write whatever snapshots\n * resolved in time — the same \"flush from what exists\" rule slices use. */\nconst PENDING_SETTLE_TIMEOUT_MS = 5000\n\n/**\n * `Promise.allSettled` bounded by a timeout. Resolves when every pending\n * capture settles OR the cap elapses, whichever comes first — a stuck capture\n * can never hang the export. Returns whether it timed out so the caller can\n * warn. The timer is unref'd so it can't itself keep the process alive.\n */\nasync function settlePending(\n pending: Promise<unknown>[],\n timeoutMs: number\n): Promise<boolean> {\n let timer: ReturnType<typeof setTimeout> | undefined\n const timedOut = new Promise<true>((resolve) => {\n timer = setTimeout(() => resolve(true), timeoutMs)\n timer.unref?.()\n })\n const settled = Promise.allSettled(pending).then(() => false)\n const result = await Promise.race([settled, timedOut])\n if (timer) {\n clearTimeout(timer)\n }\n return result\n}\n\n/** Settle in-flight snapshot captures under the timeout cap, warning if the\n * bound elapses. Never throws; never hangs. */\nasync function awaitPendingCaptures(ctx: TraceExportContext): Promise<void> {\n if (!ctx.awaitPending?.length) {\n return\n }\n const timedOut = await settlePending(\n ctx.awaitPending,\n PENDING_SETTLE_TIMEOUT_MS\n )\n if (timedOut) {\n ctx.log?.(\n 'warn',\n `One or more of ${ctx.awaitPending.length} snapshot capture(s) did not ` +\n `settle within ${PENDING_SETTLE_TIMEOUT_MS}ms; writing trace with the ` +\n 'snapshots captured so far.'\n )\n }\n}\n\nfunction sliceCountWarning(count: number): string {\n return (\n `traceGranularity produced ${count} trace slices. Consider pairing it ` +\n 'with a retention policy (e.g. tracePolicy: \"retain-on-failure\") to ' +\n 'avoid writing hundreds of trace archives.'\n )\n}\n\n/** Project a metadata slice onto the retention evaluator's outcome shape. */\nfunction toOutcomes(metadata: TestMetadataMap): TestOutcome[] {\n return Array.from(metadata.values(), (m) => ({\n state: m.state,\n attempt: m.attempt\n }))\n}\n\n/**\n * Evaluate the retention policy for one trace slice. Adapters that feed real\n * per-test attempt numbers also set `attemptInfoAvailable`; when they don't,\n * retry-aware policies degrade to retain-on-failure (see trace-retention.ts).\n */\nfunction shouldRetain(\n ctx: TraceExportContext,\n metadata: TestMetadataMap,\n ledgerOutcomes?: TestOutcome[]\n): boolean {\n // An empty scoped ledger view (adapter fed outcomes but not for this scope)\n // falls back to metadata rather than fail-opening the evaluator into retaining\n // everything — only a genuinely empty metadata slice fails open.\n return shouldRetainTrace(ctx.policy, {\n outcomes: ledgerOutcomes?.length ? ledgerOutcomes : toOutcomes(metadata),\n attemptInfoAvailable: ctx.attemptInfoAvailable ?? false\n }).retain\n}\n\n/** Attempt number encoded in a test-slice key (`<uid>-retry<n>`); 0 when the\n * key is the base uid (the first attempt / no retry suffix). */\nfunction attemptFromKey(key: string): number {\n const match = /-retry(\\d+)$/.exec(key)\n return match ? Number(match[1]) : 0\n}\n\n/** Scope the per-attempt ledger to a slice: a test slice sees only its own\n * attempt (so a passing retry isn't retained on first-failure); a spec slice\n * sees every attempt of its tests. */\nfunction sliceOutcomeView(\n ctx: TraceExportContext,\n range: SpecRange,\n isTestSlice: boolean\n): TestOutcome[] | undefined {\n if (!ctx.outcomes) {\n return undefined\n }\n return isTestSlice\n ? ctx.outcomes.forTest(range.testUid!, attemptFromKey(range.key))\n : ctx.outcomes.forSpec(range.specFile)\n}\n\n/**\n * Policy-aware single-range flush: dedupes via `ctx.flushed` on the slice\n * `key`, applies the retention decision, and delegates the byte-level\n * slicing/naming to `writeSpecTrace` (spec slices) or `writeTestSliceTrace`\n * (test slices, distinguished by `range.testUid`). Returns the artifact, or\n * undefined when the range was already flushed.\n */\nexport async function flushRangeTrace(\n ctx: TraceExportContext,\n range: SpecRange,\n nextRange?: SpecRange\n): Promise<TraceArtifact | undefined> {\n // Eager mid-run flushes reach here directly, bypassing finalizeTraceExport's\n // gate, so enforce trace-mode here too — a live run must not write slices.\n if (ctx.mode !== 'trace') {\n return undefined\n }\n if (ctx.flushed.has(range.key)) {\n return undefined\n }\n ctx.flushed.add(range.key)\n\n const isTestSlice = range.testUid !== undefined\n const sliceMetadata = isTestSlice\n ? filterTestMetadataByUid(ctx.testMetadata, range.testUid!)\n : filterTestMetadataBySpec(ctx.testMetadata, range.specFile)\n const outcomes = sliceOutcomeView(ctx, range, isTestSlice)\n const artifact: TraceArtifact = {\n kind: 'trace',\n path: '',\n scope: isTestSlice ? 'test' : 'spec',\n key: range.key,\n testUids: Array.from(sliceMetadata.keys()),\n retained: shouldRetain(ctx, sliceMetadata, outcomes)\n }\n if (!artifact.retained) {\n ctx.onArtifact?.(artifact)\n return artifact\n }\n\n const writeSlice = isTestSlice ? writeTestSliceTrace : writeSpecTrace\n artifact.path = await writeSlice({\n range,\n nextRange,\n capturer: ctx.capturer,\n actionSnapshots: ctx.actionSnapshots ?? [],\n screencastFrames: ctx.screencastFrames,\n sessionId: ctx.sessionId,\n outputDir: ctx.resolveOutputDir(range),\n format: ctx.format,\n testMetadata: ctx.testMetadata,\n capabilities: ctx.capabilities\n })\n ctx.log?.(\n 'info',\n `Trace for ${isTestSlice ? 'test' : 'spec'} \"${range.key}\" saved to ${artifact.path}`\n )\n ctx.onArtifact?.(artifact)\n return artifact\n}\n\n/**\n * Flush one slice via {@link flushRangeTrace}, logging the shared spec/test\n * error string on failure so a failed boundary flush can't abort the next test.\n * All three adapters wrapped this identically; the label + identity are derived\n * from `range.testUid` (test slice → `test \"<key>\"`, else `spec \"<specFile>\"`),\n * so each call site keeps its exact message. Errors are logged and swallowed\n * (resolves `undefined`), so callers `await` it when the write must land before\n * a retry overwrites metadata, or fire-and-forget (`void`, or tracked in an\n * in-flight list) otherwise. Callers keep their own find-current-range strategy\n * and pass the resolved range in.\n */\nexport async function flushRangeLogged(\n ctx: TraceExportContext,\n range: SpecRange\n): Promise<TraceArtifact | undefined> {\n try {\n return await flushRangeTrace(ctx, range)\n } catch (err) {\n const label =\n range.testUid !== undefined\n ? `test \"${range.key}\"`\n : `spec \"${range.specFile}\"`\n ctx.log?.(\n 'warn',\n `Failed to flush trace for ${label}: ${errorMessage(err)}`\n )\n return undefined\n }\n}\n\nasync function writeSessionTrace(\n ctx: TraceExportContext\n): Promise<TraceArtifact | undefined> {\n const snapshots = ctx.actionSnapshots ?? []\n const artifact: TraceArtifact = {\n kind: 'trace',\n path: '',\n scope: 'session',\n testUids: Array.from(ctx.testMetadata.keys()),\n retained: shouldRetain(ctx, ctx.testMetadata, ctx.outcomes?.all())\n }\n if (!artifact.retained) {\n ctx.onArtifact?.(artifact)\n return artifact\n }\n\n artifact.path = await writeTraceZip(ctx.capturer, {\n outputDir: ctx.resolveOutputDir(),\n sessionId: ctx.sessionId,\n capabilities: ctx.capabilities,\n actionSnapshots: snapshots.length ? snapshots : undefined,\n screencastFrames: ctx.screencastFrames,\n format: ctx.format,\n testMetadata: ctx.testMetadata\n })\n ctx.log?.('info', `Trace saved to ${artifact.path}`)\n ctx.onArtifact?.(artifact)\n return artifact\n}\n\n/** Run one write, logging and swallowing its error so siblings still write. */\nasync function safely(\n ctx: TraceExportContext,\n write: () => Promise<TraceArtifact | undefined>\n): Promise<TraceArtifact | undefined> {\n try {\n return await write()\n } catch (err) {\n ctx.log?.('warn', `trace write failed: ${errorMessage(err)}`)\n return undefined\n }\n}\n\nasync function flushAllRanges(\n ctx: TraceExportContext\n): Promise<TraceArtifact[]> {\n const artifacts: TraceArtifact[] = []\n // Bound each slice by the next range's start indices; the final range (no\n // nextRange) runs to the end of the arrays. Without this, every slice would\n // run to the end and each test slice would swallow all later tests.\n for (let i = 0; i < ctx.ranges.length; i++) {\n const range = ctx.ranges[i]!\n const nextRange = ctx.ranges[i + 1]\n const artifact = await safely(ctx, () =>\n flushRangeTrace(ctx, range, nextRange)\n )\n if (artifact) {\n artifacts.push(artifact)\n }\n }\n return artifacts\n}\n\n/**\n * Entry point for the after/end-of-run hook. No-op outside trace mode. Settles\n * in-flight captures and eager flushes, then fans out to per-spec, per-test, or\n * session writes and finally the artifacts manifest. `spec`/`test` granularity\n * with no recorded boundaries warns and falls back to a single session-level\n * trace.\n */\nexport async function finalizeTraceExport(\n ctx: TraceExportContext\n): Promise<TraceArtifact[]> {\n if (ctx.mode !== 'trace') {\n return []\n }\n // Settle in-flight snapshot captures AND the adapters' tracked fire-and-forget\n // eager flushes (Selenium/Nightwatch put both in awaitPending) before writing\n // anything: every eager-flushed range is a synchronous dedupe-hit in the\n // fan-out below, so this is the one place that awaits those eager-flush\n // promises. Without it the last eager write can race teardown and its artifact\n // can miss the manifest. A no-op for WDIO (awaits eager flushes inline).\n await awaitPendingCaptures(ctx)\n const artifacts = await fanOutTraceWrites(ctx)\n await maybeWriteManifest(ctx, artifacts)\n return artifacts\n}\n\n/** Session vs per-slice fan-out (with the no-boundaries fallback). */\nasync function fanOutTraceWrites(\n ctx: TraceExportContext\n): Promise<TraceArtifact[]> {\n const sliced = ctx.granularity === 'spec' || ctx.granularity === 'test'\n if (sliced && ctx.ranges.length > 0) {\n if (ctx.ranges.length > SLICE_COUNT_WARN_THRESHOLD) {\n ctx.log?.('warn', sliceCountWarning(ctx.ranges.length))\n }\n return flushAllRanges(ctx)\n }\n if (ctx.granularity === 'spec') {\n ctx.log?.('warn', SPEC_WITHOUT_BOUNDARIES_WARNING)\n } else if (ctx.granularity === 'test') {\n ctx.log?.('warn', TEST_WITHOUT_BOUNDARIES_WARNING)\n }\n const artifact = await safely(ctx, () => writeSessionTrace(ctx))\n return artifact ? [artifact] : []\n}\n\n/** Write the artifacts manifest when enabled, preferring the adapter's full\n * collected list (includes eager slices) over this pass's fan-out results.\n * Never throws — a manifest failure must not abort the run. */\nasync function maybeWriteManifest(\n ctx: TraceExportContext,\n fanOutArtifacts: readonly TraceArtifact[]\n): Promise<void> {\n if (!ctx.emitManifest) {\n return\n }\n const artifacts = ctx.collectedArtifacts ?? fanOutArtifacts\n try {\n const path = await writeArtifactsManifest(\n ctx.resolveOutputDir(),\n buildArtifactsManifest({\n sessionId: ctx.sessionId,\n format: ctx.format ?? 'zip',\n artifacts,\n testMetadata: ctx.testMetadata\n })\n )\n ctx.log?.('info', `Artifacts manifest written to ${path}`)\n } catch (err) {\n ctx.log?.(\n 'warn',\n `Failed to write artifacts manifest: ${errorMessage(err)}`\n )\n }\n}\n","import fs from 'node:fs/promises'\nimport path from 'node:path'\nimport { createRequire } from 'node:module'\n\nimport { errorMessage } from './error.js'\n\nconst require = createRequire(import.meta.url)\n\n/** Browser-side expression that is true once the injected collector has\n * initialised. Shared by the adapters' readiness polls and re-injection\n * checks so the collector's global name lives in one place. */\nexport const COLLECTOR_READY_EXPRESSION =\n 'typeof window.wdioTraceCollector !== \"undefined\"'\n\n/**\n * Browser-side expression that drains the collector in one round-trip, atomic\n * against a navigation replacing the document mid-check.\n *\n * `forceAnchor` forces a full-DOM anchor of the CURRENT document before\n * draining. The collector's initial anchor is scheduled asynchronously (after\n * `waitForBody`), so a drain issued right after injecting on a navigation\n * destination beats it and returns nothing — and the destination's buffer then\n * dies with the page, leaving the navigating action's DOM absent entirely.\n *\n * Shared by the two adapters that eval the drain as a string; the WDIO service\n * passes an equivalent function to `browser.execute` instead.\n */\nexport function collectorDrainExpression(forceAnchor = false): string {\n const call = forceAnchor\n ? 'window.wdioTraceCollector.captureCurrentDom();'\n : ''\n return `if (!(${COLLECTOR_READY_EXPRESSION})) { return null; } ${call} return window.wdioTraceCollector.getTraceData();`\n}\n\n/** The collector bundle's raw source. Callers wrap it for their own injection\n * mechanism — an async IIFE for a `<script>` body, a function declaration for a\n * BiDi preload script. */\nexport async function loadCollectorSource(): Promise<string> {\n const scriptPath = require.resolve('@wdio/devtools-script')\n const scriptDir = path.dirname(scriptPath)\n return fs.readFile(path.join(scriptDir, 'script.js'), 'utf-8')\n}\n\n/**\n * The collector bundle wrapped in an async IIFE so its top-level `await` works\n * inside a regular `<script>` element body. Used for instrumenting the document\n * that is loaded RIGHT NOW; documents created later are covered by the BiDi\n * preload script (`registerCollectorPreload`), which takes the raw source.\n */\nexport async function loadInjectableScript(): Promise<string> {\n return `(async function() { ${await loadCollectorSource()} })()`\n}\n\n/** Schemes that carry no test-relevant DOM — the browser's own start page,\n * inline documents, and driver error pages. */\nconst NON_INSTRUMENTABLE_SCHEMES = [\n 'about:',\n 'data:',\n 'chrome:',\n 'chrome-error:',\n 'edge:',\n 'moz-extension:',\n 'chrome-extension:'\n]\n\nfunction isInstrumentableDocument(url: string | undefined): boolean {\n if (!url) {\n return false\n }\n return !NON_INSTRUMENTABLE_SCHEMES.some((scheme) => url.startsWith(scheme))\n}\n\n/**\n * Drain the page-side collector, re-injecting it into the CURRENT document and\n * retrying once when it isn't there.\n *\n * Both injection mechanisms (BiDi preload script, `<script>` append) only cover\n * documents created AFTER the injection, so a document that loads across a\n * session swap — `browser.reloadSession()`, nightwatch cucumber's per-scenario\n * re-navigation — carries no collector. Without one the drain returns nothing\n * and the dashboard replays the PREVIOUS document's DOM for every action on the\n * new page (empty forms, stale screenshots) with no error anywhere. Re-injecting\n * into the live document costs one round trip and its startup anchors the\n * current DOM, so the replay recovers from the next action onward.\n *\n * `drain` must resolve to the collector payload, or a falsy value when the\n * collector isn't reachable — the same `typeof === 'undefined' ? null` atomic\n * probe every adapter already uses. `currentUrl` is only consulted on a miss,\n * to keep recovery off documents that aren't worth instrumenting.\n */\nexport async function drainCollectorWithRecovery<T>(opts: {\n drain: () => Promise<T | null>\n injectIntoCurrentDocument: () => Promise<void>\n currentUrl?: () => Promise<string | undefined>\n log?: (level: 'info' | 'warn', message: string) => void\n}): Promise<T | null> {\n const payload = await opts.drain()\n if (payload) {\n return payload\n }\n if (opts.currentUrl) {\n const url = await opts.currentUrl().catch(() => undefined)\n if (!isInstrumentableDocument(url)) {\n // A session's pre-navigation page has no collector by design; anchoring it\n // would add a phantom \"document loaded\" row to every run. An unreadable\n // url means the session is gone — nothing to recover into either.\n return null\n }\n }\n opts.log?.('info', 'Collector missing on the current document, re-injecting')\n try {\n await opts.injectIntoCurrentDocument()\n } catch (err) {\n // Best-effort: a failed recovery must not also lose the caller's drain.\n opts.log?.('warn', `Collector re-injection failed: ${errorMessage(err)}`)\n return null\n }\n const recovered = await opts.drain()\n if (!recovered) {\n opts.log?.(\n 'warn',\n 'Collector still unreachable after re-injection — DOM capture is stale for this document'\n )\n }\n return recovered\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","// Document-start injection of the collector via a BiDi preload script.\n//\n// The `<script>`-append injection in `script-loader.ts` only ever instruments\n// the document that is loaded at the time it runs, and a `<script>` dies with\n// its document. So a navigation always produces a document we learn about\n// afterwards, and everything built on that — when to re-inject, when to drain,\n// which action to credit the new DOM to — is reconstruction, and races. A\n// preload script registered globally runs in EVERY document before any of its\n// script does, so each one instruments itself and anchors its own DOM at its own\n// `performance.timeOrigin`. Nothing to detect, nothing to attribute.\n\nimport { loadSeleniumSubmodule } from './bidi.js'\nimport { errorMessage } from './error.js'\nimport { loadCollectorSource } from './script-loader.js'\n\ninterface ScriptManager {\n addPreloadScript: (\n functionDeclaration: string,\n argumentValueList?: unknown[],\n sandbox?: string | null\n ) => Promise<number>\n}\n\n/** `selenium-webdriver/bidi/scriptManager`'s default export. Cast at this\n * boundary — the module ships no types. */\ntype ScriptManagerFactory = (\n browsingContextIds: unknown,\n driver: unknown\n) => Promise<ScriptManager>\n\n/**\n * Register the collector to run at document-start in every document of this\n * session. Returns false when BiDi isn't available, so the caller can fall back\n * to per-document `<script>` injection.\n *\n * Requires the session to have been created with `webSocketUrl: true`; the\n * script manager throws without it.\n */\nexport async function registerCollectorPreload(\n driver: unknown,\n log?: (level: 'info' | 'warn', message: string) => void\n): Promise<boolean> {\n const factory =\n loadSeleniumSubmodule<ScriptManagerFactory>('bidi/scriptManager')\n if (!factory) {\n return false\n }\n try {\n // No browsing-context id, which registers the script GLOBALLY — contexts\n // created later are covered too. Scoping it to the current context would\n // miss exactly the documents this exists to catch.\n const manager = await factory(undefined, driver)\n // Raw source, not the IIFE-wrapped form: a preload script is a function\n // declaration, so the bundle's top-level await works in the body directly.\n await manager.addPreloadScript(\n `async () => { ${await loadCollectorSource()} }`\n )\n log?.('info', '✓ Collector registered at document-start (BiDi preload)')\n return true\n } catch (err) {\n log?.(\n 'warn',\n `BiDi preload unavailable, falling back to per-document injection: ${errorMessage(err)}`\n )\n return false\n }\n}\n","/**\n * Rate-limits high-frequency identical terminal (stdout/stderr) lines so a\n * polling framework that reprints the same line every ~100ms doesn't flood the\n * trace's console lane. The motivating case: WDIO's logger writes a\n * `COMMAND`/`RESULT` frame for every WebDriver command, so an `expect` that\n * polls for its full 10s timeout emits hundreds of near-identical lines.\n *\n * Only terminal-source capture goes through this — user `console.*` is\n * captured as `source: 'test'` and never throttled, so real user output is\n * untouched. Distinct lines always pass immediately; a repeat is emitted at\n * most once per window.\n */\n\n/**\n * A repeated identical terminal line is emitted at most once per this window\n * (ms). Sized so a 100ms poll collapses ~10:1 while a human-paced reprint of\n * the same line (>1s apart) still shows every time.\n */\nexport const TERMINAL_REPEAT_WINDOW_MS = 1000\n\n/**\n * Leading ISO-8601 timestamp emitted by most structured loggers (`@wdio/logger`'s\n * `%t %l %n:` template, pino, winston, …). It's the only volatile part of\n * otherwise-identical successive log frames, so it's stripped from the throttle\n * key — never from the emitted text.\n */\nconst LEADING_ISO_TIMESTAMP_RE =\n /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d\\d\\d)?Z {1,4}/\n\n/** Key a terminal line for repeat-detection: drop a leading ISO timestamp so\n * successive log frames of the same message collapse. */\nexport function terminalRepeatKey(line: string): string {\n return line.replace(LEADING_ISO_TIMESTAMP_RE, '')\n}\n\nexport class TerminalLineThrottle {\n #lastEmitted = new Map<string, number>()\n readonly #windowMs: number\n\n constructor(windowMs: number = TERMINAL_REPEAT_WINDOW_MS) {\n this.#windowMs = windowMs\n }\n\n /**\n * True if the line should be emitted, false if it's a within-window repeat of\n * a line already emitted. The window is anchored to the last *emit*, not the\n * last occurrence, so a sustained stream of one line still emits once per\n * window instead of going silent after the first. Expired keys are pruned on\n * each emit, so state stays bounded by the number of distinct lines seen\n * within the window.\n */\n shouldEmit(line: string, now: number = Date.now()): boolean {\n const key = terminalRepeatKey(line)\n const last = this.#lastEmitted.get(key)\n if (last !== undefined && now - last < this.#windowMs) {\n return false\n }\n this.#lastEmitted.set(key, now)\n this.#prune(now)\n return true\n }\n\n #prune(now: number): void {\n for (const [key, ts] of this.#lastEmitted) {\n if (now - ts >= this.#windowMs) {\n this.#lastEmitted.delete(key)\n }\n }\n }\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 // Create the (test-results) dir if absent, then confirm it's writable;\n // fall back to tmpdir on any failure so a bad path never aborts the run.\n fs.mkdirSync(candidate, { recursive: true })\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","import { isInputDispatchingCommand } from '@wdio/devtools-shared'\n\n// A screenshot landing inside chromedriver's click — after it computed the\n// element's coordinates, before it dispatches at them — makes the click report\n// success while activating nothing (CLAUDE.md § Known debt has the measurements).\n// One instance per adapter bundle, not per session: both halves are module-scoped\n// and must be imported from the same bundle to see the same gate.\n\n/** Bound on one command's suppression: a failing Nightwatch test discards its\n * command queue, so the callback that closes a window may never fire. */\nexport const INPUT_DISPATCH_MAX_HOLD_MS = 3000\n\nconst NOOP = (): void => {}\n\nexport class InputDispatchGate {\n readonly #openedAt = new Map<number, number>()\n #nextToken = 0\n\n constructor(private readonly maxHoldMs = INPUT_DISPATCH_MAX_HOLD_MS) {}\n\n /**\n * Open a suppression window for `command` and return its closer. Always\n * returns a callable so call sites need no branch — for a command that\n * dispatches no input the closer is a no-op and nothing is tracked.\n */\n open(command: string): () => void {\n if (!isInputDispatchingCommand(command)) {\n return NOOP\n }\n const token = this.#nextToken++\n this.#openedAt.set(token, Date.now())\n return () => {\n this.#openedAt.delete(token)\n }\n }\n\n /**\n * Whether an unexpired window is open. Expired windows are dropped here, so a\n * closer that never runs costs one bounded stall rather than a permanent one\n * and cannot leak entries for the rest of the run.\n */\n isOpen(): boolean {\n const cutoff = Date.now() - this.maxHoldMs\n for (const [token, openedAt] of this.#openedAt) {\n if (openedAt <= cutoff) {\n this.#openedAt.delete(token)\n }\n }\n return this.#openedAt.size > 0\n }\n}\n\nconst sharedGate = new InputDispatchGate()\n\n/** Adapter side: open the window around a command. Returns its closer. */\nexport function beginInputDispatch(command: string): () => void {\n return sharedGate.open(command)\n}\n\n/** Recorder side: is a driver command dispatching input right now? */\nexport function isInputDispatchInFlight(): boolean {\n return sharedGate.isOpen()\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\n/** All run output is grouped under this subfolder. */\nconst OUTPUT_SUBDIR = 'test-results'\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.). Every artifact is grouped under a\n * single `test-results/` subfolder so a run's output is\n * self-contained regardless of where the base directory resolves to.\n *\n * The base directory is resolved by 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 return path.join(resolveBaseDir(input), OUTPUT_SUBDIR)\n}\n\nfunction resolveBaseDir(input: ResolveAdapterOutputDirInput): 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","// Value → locator provenance: which element's read produced a given value.\n//\n// node:assert takes VALUES, not elements — `assert.equal(await el.getText(), …)`\n// names no element — so an assertion row can only name its target if something\n// remembers which element each read value came from. Adapters feed this from\n// their command path (where a read's result and its selector are both visible)\n// and read it back when a patched assert fires; the answer lands on\n// `CommandLog.selector` so the player's overlay boxes the assertion's subject.\n//\n// Framework-agnostic by construction: it stores primitives and selector strings\n// only. The complementary handle → locator map (an assert taking the element\n// object itself) is Selenium-specific and stays in that adapter.\n\n/** Bounds the registry. A run reads far more values than any assertion looks\n * back over, and only the recent ones can be an assert's subject. */\nconst MAX_READ_VALUES = 200\n\n/** Values longer than this are never an assertion's subject, and keying a map on\n * one costs real memory — a page source or a base64 screenshot is a command\n * result too. */\nconst MAX_READ_VALUE_LENGTH = 256\n\n// Value → the locator of the element whose read produced it, or null when the\n// most recent producer was a driver-level read that no element owns. The null is\n// load-bearing: a page-title assertion would otherwise inherit the locator of an\n// element that happened to have the same text (measured on the selenium mocha\n// example, where `h1` and the document title are both \"Example Domain\").\nconst selectorByReadValue = new Map<unknown, string | null>()\n\n// Only primitives: an object result is compared by identity, which no assertion\n// on a read value relies on, and holding one would pin it for the run.\nfunction isRememberableValue(value: unknown): boolean {\n if (typeof value === 'string') {\n return value.length > 0 && value.length <= MAX_READ_VALUE_LENGTH\n }\n return typeof value === 'number' || typeof value === 'boolean'\n}\n\n/** Attribute a command result to the element it was read from, so an assertion\n * on that value can name the element. Pass `selector: undefined` for a\n * driver-level read — that records the value as belonging to no element rather\n * than leaving an older element's claim standing. */\nexport function rememberReadValue(\n value: unknown,\n selector: string | undefined\n): void {\n if (!isRememberableValue(value)) {\n return\n }\n // Delete before set so map order is recency order: the newest producer of a\n // value wins the lookup, and the least recent is what eviction drops.\n selectorByReadValue.delete(value)\n selectorByReadValue.set(value, selector ?? null)\n if (selectorByReadValue.size > MAX_READ_VALUES) {\n const oldest = selectorByReadValue.keys().next()\n if (!oldest.done) {\n selectorByReadValue.delete(oldest.value)\n }\n }\n}\n\n/** The element locator this value was read from — undefined when an element read\n * wasn't its most recent producer. */\nexport function selectorForReadValue(value: unknown): string | undefined {\n return selectorByReadValue.get(value) ?? undefined\n}\n\n/**\n * The element an assert was about, from its RAW args (sanitizing loses the\n * object identity a handle is recognised by). First arg that resolves wins —\n * node:assert puts the subject first. `fromHandle` lets an adapter that also\n * tracks element handles answer for an element passed straight to the assert;\n * adapters whose asserts only ever see values omit it.\n */\nexport function resolveAssertTargetFromArgs(\n args: unknown[],\n fromHandle?: (arg: unknown) => string | undefined\n): string | undefined {\n for (const arg of args) {\n const selector = fromHandle?.(arg) ?? selectorForReadValue(arg)\n if (selector) {\n return selector\n }\n }\n return undefined\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","/**\n * Identity for one test run, shared by every process that reports into it.\n * The backend compares it across worker connects to tell \"next spec of this\n * run\" (keep the accumulated state) from \"a new run\" (wipe it).\n */\n\nimport { randomUUID } from 'node:crypto'\nimport { RUNNER_ENV } from '@wdio/devtools-shared'\n\n/**\n * This run's id, generating and publishing one on first call. Runners fork\n * workers with the launcher's environment, so a launcher that calls this\n * before the run starts gives every worker the same id; a worker that gets\n * here first can only speak for itself, and each sibling reads as its own run.\n */\nexport function resolveRunId(): string {\n const existing = process.env[RUNNER_ENV.RUN_ID]\n if (existing) {\n return existing\n }\n const runId = randomUUID()\n process.env[RUNNER_ENV.RUN_ID] = runId\n return runId\n}\n","import type { ScreencastFrame, ScreencastOptions } from '@wdio/devtools-shared'\nimport { SCREENCAST_DEFAULTS } from '@wdio/devtools-shared'\nimport { isInputDispatchInFlight } from './input-dispatch.js'\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.#appendFrame({ data, timestamp })\n }\n\n /**\n * Append a frame, decimating the buffer in place once it exceeds\n * `maxBufferFrames` so a long session can't grow it without bound.\n */\n #appendFrame(frame: ScreencastFrame): void {\n this.buffer.push(frame)\n // Decimation always keeps the first and last frame, so 2 is the hard floor;\n // clamp so a nonsensical sub-2 cap is honored as 2 rather than never firing.\n if (this.buffer.length > Math.max(2, this.options.maxBufferFrames)) {\n this.#decimateBuffer()\n }\n }\n\n /**\n * Halve the buffer by keeping every other frame plus the first and last, so\n * memory is bounded while the temporal spread survives (never tail-truncated).\n * `#startIndex` is remapped to the count of surviving pre-marker frames, which\n * keeps `frames`/`duration` excluding pre-marker frames across decimation.\n */\n #decimateBuffer(): void {\n const lastIndex = this.buffer.length - 1\n const kept: ScreencastFrame[] = []\n let preMarkerKept = 0\n for (let i = 0; i < this.buffer.length; i++) {\n if (i % 2 === 0 || i === lastIndex) {\n if (i < this.#startIndex) {\n preMarkerKept++\n }\n kept.push(this.buffer[i])\n }\n }\n this.buffer = kept\n this.#startIndex = preMarkerKept\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.#appendFrame({ data: first, timestamp: Date.now() })\n\n const intervalMs = this.options.pollIntervalMs\n this.#pollTimer = setInterval(async () => {\n // A screenshot that reaches the driver mid-click makes the click report\n // success while dispatching nothing, so the page never navigates. Drop\n // the tick rather than race it — the gate is bounded, so a command whose\n // completion never arrives costs frames only until it expires.\n if (isInputDispatchInFlight()) {\n return\n }\n try {\n const data = await this.takeScreenshot()\n if (data !== null) {\n this.#appendFrame({ 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 { WebSocket } from 'ws'\nimport type {\n CommandLog,\n ConsoleLog,\n LogLevel,\n LogSource,\n Metadata,\n NetworkRequest,\n SerializedError,\n TraceMutation\n} from '@wdio/devtools-shared'\nimport { WORKER_WS_QUERY, WS_PATHS, WS_SCOPE } from '@wdio/devtools-shared'\nimport { mapCommandToAction } from './action-mapping.js'\nimport { resolveRunId } from './run-id.js'\nimport { reattributeDomAnchors } from './trace-mutations.js'\nimport {\n CONSOLE_METHODS,\n LOG_SOURCES,\n SPINNER_RE,\n createConsoleLogEntry,\n detectLogLevel,\n isInternalStreamLine,\n stripAnsi\n} from './console.js'\nimport { TerminalLineThrottle } from './terminal-throttle.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. Every socket also carries the run id,\n * which covers the same-run case a process can't flag: the next spec's worker.\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 // Collapses high-frequency identical terminal lines (e.g. WDIO's per-command\n // COMMAND/RESULT logger frames reprinted every ~100ms during an expect poll)\n // so they don't flood the console lane. Terminal-source only — user\n // console.* is source='test' and never throttled.\n #terminalThrottle = new TerminalLineThrottle()\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 = new URLSearchParams({\n [WORKER_WS_QUERY.runId]: resolveRunId(),\n ...(reconnect ? { [WORKER_WS_QUERY.reconnect]: '1' } : {})\n })\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 * Mark the most recent user action of a test as failed — for framework\n * failures that aren't captured as their own command. Broadcasts the swap so\n * live mode highlights it too. Returns false when no eligible action is found,\n * OR when that most-recent action already carries an error: the failure is\n * then already represented (e.g. an expect matcher captured as its own row via\n * afterAssertion), so it must not bleed onto an earlier *passing* action.\n */\n failLastAction(testUid: string | undefined, error: SerializedError): boolean {\n for (let i = this.commandsLog.length - 1; i >= 0; i--) {\n const command = this.commandsLog[i]\n if (!mapCommandToAction(command.command)) {\n continue\n }\n if (testUid && command.testUid !== testUid) {\n continue\n }\n // First (most-recent) action for this test. If it's already failed, the\n // failure is captured — stop rather than marking an earlier passing one.\n if (command.error) {\n return false\n }\n command.error = error\n this.sendReplaceCommand(command.timestamp, command)\n return true\n }\n return false\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 * The action a document born at `time` belongs to, or undefined when the\n * anchor already sits where it should and must be left alone.\n *\n * Derived from the anchor rather than from the draining call, because the\n * post-command hooks fire at command START in some adapters: a navigation's\n * own drain then runs against the document it is leaving, and whichever later\n * drain does see the destination would otherwise be credited with it.\n *\n * Answers only when NO logged command completed after `time`. A command that\n * completed after the document was born is the one that navigated there, and\n * its own row already resolves the anchor — pulling the anchor earlier then\n * mis-credits it to a preceding action and steals the DOM from rows that were\n * genuinely still on the previous page. That is the whole difference between\n * an adapter that stamps commands at invocation and one that stamps them at\n * completion, so it stays a property of the data rather than a per-adapter flag.\n */\n protected anchorOwnerTimestamp(time: number): number | undefined {\n let best: number | undefined\n for (const cmd of this.commandsLog) {\n if (cmd.timestamp > time) {\n return undefined\n }\n if (best === undefined || cmd.timestamp > best) {\n best = cmd.timestamp\n }\n }\n return best\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: {\n skipConsoleLogs?: boolean\n skipNetworkRequests?: boolean\n /** The command this batch's anchors were CAUSED by, when the caller\n * observed the document being replaced right after it. Causal knowledge,\n * so it overrides the timestamp inference below. */\n anchorTimestamp?: number\n } = {}\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 const batch = mutations as TraceMutation[]\n reattributeDomAnchors(\n batch,\n (anchorOwnTime) =>\n opts.anchorTimestamp ?? this.anchorOwnerTimestamp(anchorOwnTime),\n this.mutations[this.mutations.length - 1]?.timestamp ?? 0\n )\n this.mutations.push(...batch)\n this.sendUpstream('mutations', batch)\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 !this.#terminalThrottle.shouldEmit(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","import type {\n SuiteStats,\n TestAncestor,\n TestMetadataMap,\n TestStats,\n TestStatus\n} 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\nfunction deriveAncestorKind(\n suite: SuiteStats,\n parentKind: TestAncestor['kind'] | undefined\n): TestAncestor['kind'] {\n if (parentKind === 'feature') {\n return 'scenario'\n }\n // Cucumber scenario suites carry the same .feature file as their parent —\n // only a suite not already under a feature/scenario counts as the feature.\n if (suite.file?.endsWith('.feature') && parentKind !== 'scenario') {\n return 'feature'\n }\n return 'suite'\n}\n\n/**\n * Recursive walk over suite trees collecting every test's metadata entry —\n * uid → title/specFile/state/attempt plus the ancestor chain (outermost\n * first, the test's own node excluded). Consolidates the per-adapter\n * collectTestMetadata walks.\n */\nexport function collectSuiteTestMetadata(\n suites: Iterable<SuiteStats>\n): TestMetadataMap {\n const metadata: TestMetadataMap = new Map()\n const walk = (suite: SuiteStats, ancestors: TestAncestor[]): void => {\n const parentKind = ancestors[ancestors.length - 1]?.kind\n const chain: TestAncestor[] = [\n ...ancestors,\n {\n uid: suite.uid,\n title: suite.title,\n kind: deriveAncestorKind(suite, parentKind)\n }\n ]\n // A cucumber scenario is modelled as a SUITE, not a test — but it IS the\n // test unit: the attempt ledger and the per-test slice boundary both key on\n // its uid, and so does every command's testUid. Without an entry here the\n // trace's group fell back to rendering that raw uid as the group's name.\n // `state` comes along so retention keeps evaluating a real outcome.\n if (deriveAncestorKind(suite, parentKind) === 'scenario') {\n metadata.set(suite.uid, {\n title: suite.title,\n specFile: suite.file,\n state: suite.state,\n ancestry: ancestors\n })\n }\n for (const entry of suite.tests ?? []) {\n // Trees can contain string placeholders for tests not yet reconciled.\n if (typeof entry !== 'object' || entry === null) {\n continue\n }\n metadata.set(entry.uid, {\n title: entry.fullTitle || entry.title,\n specFile: entry.file ?? suite.file,\n state: entry.state,\n attempt: entry.retries ?? 0,\n ancestry: chain\n })\n }\n for (const child of suite.suites ?? []) {\n walk(child, chain)\n }\n }\n for (const suite of suites) {\n if (typeof suite !== 'object' || suite === null) {\n continue\n }\n walk(suite, [])\n }\n return metadata\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 * Assembles the framework-agnostic trace-export context from plugin state.\n *\n * Extracted from `NightwatchDevToolsPlugin.#traceContext` so the assembly is\n * unit-testable and to keep `index.ts` under the file-size cap. Both the\n * per-spec boundary flush and the final trace write share this one builder.\n */\n\nimport {\n collectSuiteTestMetadata,\n resolveAdapterOutputDir,\n type RetryOutcomeView,\n type SpecRange,\n type TraceArtifact,\n type TraceExportContext\n} from '@wdio/devtools-core'\nimport type { SessionCapturer } from './session.js'\nimport type {\n DevToolsMode,\n ScreencastFrame,\n SuiteStats,\n TraceFormat,\n TraceGranularity,\n TraceRetentionPolicy\n} from './types.js'\n\nexport interface TraceContextInput {\n mode: DevToolsMode\n policy: TraceRetentionPolicy\n granularity: TraceGranularity\n format: TraceFormat\n capturer: SessionCapturer\n suites: Iterable<SuiteStats>\n outcomes?: RetryOutcomeView\n ranges: SpecRange[]\n flushed: Set<string>\n artifacts: TraceArtifact[]\n traceFlushes: Promise<unknown>[]\n screencastFrames?: readonly ScreencastFrame[]\n configPath: string | undefined\n testFilePath: string | undefined\n emitArtifactsManifest?: boolean\n log: (level: 'info' | 'warn', msg: string) => void\n}\n\nexport function buildTraceContext(\n input: TraceContextInput,\n sessionId: string\n): TraceExportContext {\n return {\n mode: input.mode,\n policy: input.policy,\n granularity: input.granularity,\n format: input.format,\n capturer: input.capturer,\n actionSnapshots: input.capturer.actionSnapshots,\n sessionId,\n testMetadata: collectSuiteTestMetadata(input.suites),\n outcomes: input.outcomes,\n ranges: input.ranges,\n flushed: input.flushed,\n screencastFrames: input.screencastFrames,\n resolveOutputDir: () =>\n resolveAdapterOutputDir({\n testFilePath: input.testFilePath,\n configPath: input.configPath\n }),\n awaitPending: [...input.capturer.snapshotCaptures, ...input.traceFlushes],\n // Nightwatch feeds real per-test attempt numbers via TestAttemptTracker\n // (B4), so retry-aware policies use per-test attempts, not the fallback.\n attemptInfoAvailable: true,\n // Off by default; opt-in only — Nightwatch has no live Allure signal to\n // auto-detect against (produce-only Allure), unlike WDIO/Selenium.\n emitManifest: input.emitArtifactsManifest ?? false,\n collectedArtifacts: input.artifacts,\n onArtifact: (a) => input.artifacts.push(a),\n log: input.log\n }\n}\n","/**\n * Produce-only per-test artifacts (screenshot + video) for the Nightwatch\n * plugin.\n *\n * Nightwatch has no live Allure attach API — its official `nightwatch-allure`\n * reporter is post-hoc, and `allure-js-commons`' `attachment()` no-ops in a\n * Nightwatch run (nothing wires the global test runtime). So we pass NO sink\n * (`attach: undefined`), which the core treats as produce-only: it writes the\n * file and records the manifest artifact via `onArtifact`, but skips the attach.\n *\n * Both the per-test (Mocha/Jasmine `afterEach`) and cucumber (per-scenario\n * finalize) paths funnel through here so the two produce calls live once. Core\n * gates both to trace mode + `test` granularity, so this no-ops otherwise.\n */\n\nimport {\n captureAndAttachScreenshot,\n captureAndAttachVideo,\n lastRenderedScreenshot,\n type TestOutcome,\n type TraceArtifact\n} from '@wdio/devtools-core'\nimport type {\n ActionSnapshot,\n DevToolsMode,\n ScreencastFrame,\n TraceGranularity,\n TraceScreenshotPolicy,\n TraceVideoPolicy\n} from '@wdio/devtools-shared'\n\nexport interface EmitTestArtifactsInput {\n mode: DevToolsMode\n granularity: TraceGranularity\n screenshotPolicy: TraceScreenshotPolicy\n videoPolicy: TraceVideoPolicy\n failed: boolean\n actionSnapshots: readonly ActionSnapshot[]\n frames: readonly ScreencastFrame[]\n startWallTime: number\n /** This test's attempt slots (already scoped via `forTest`) for retention. */\n outcomes: TestOutcome[]\n uid: string | undefined\n attempt: number | undefined\n sessionId: string | undefined\n outputDir: string\n captureFormat?: 'jpeg' | 'png'\n onArtifact: (artifact: TraceArtifact) => void\n onLog?: (level: 'info' | 'warn', message: string) => void\n}\n\n/**\n * Produce this test's screenshot then its video slice, both with `attach:\n * undefined` (produce-only). Each core call self-gates on mode/granularity/\n * policy, so a non-trace run or a non-`test` granularity is a no-op.\n */\nexport async function emitTestArtifacts(\n input: EmitTestArtifactsInput\n): Promise<void> {\n await captureAndAttachScreenshot({\n mode: input.mode,\n granularity: input.granularity,\n policy: input.screenshotPolicy,\n failed: input.failed,\n screenshotBase64: lastRenderedScreenshot(\n input.actionSnapshots,\n input.startWallTime\n ),\n sessionId: input.sessionId,\n outputDir: input.outputDir,\n testUid: input.uid,\n attach: undefined,\n onArtifact: input.onArtifact,\n onLog: input.onLog\n })\n await captureAndAttachVideo({\n mode: input.mode,\n granularity: input.granularity,\n policy: input.videoPolicy,\n frames: input.frames,\n startWallTime: input.startWallTime,\n outcomes: input.outcomes,\n attempt: input.attempt,\n outputDir: input.outputDir,\n testUid: input.uid,\n sessionId: input.sessionId,\n captureFormat: input.captureFormat,\n attach: undefined,\n onArtifact: input.onArtifact,\n onLog: input.onLog\n })\n}\n","// node:assert capture wiring: routes patched assertions into the session\n// capturer through the same captureCommand path driver commands use, so\n// retry bookkeeping and trace-mode action snapshots behave identically.\n\nimport logger from '@wdio/logger'\nimport {\n errorMessage,\n patchNodeAssert,\n resolveAssertTargetFromArgs,\n type CapturedAssert\n} from '@wdio/devtools-core'\nimport type { SessionCapturer } from '../session.js'\n\nconst log = logger('@wdio/nightwatch-devtools:assertCapture')\n\n/**\n * Patch node:assert once per process. Getters are read at capture time — the\n * capturer is created lazily in session init and the test UID changes per test.\n */\nexport function wireAssertCapture(\n getCapturer: () => SessionCapturer | undefined,\n getTestUid: () => string | undefined\n): void {\n patchNodeAssert(\n (cmd) => captureAssert(getCapturer(), getTestUid(), cmd),\n (level, message) => log[level](message),\n // No handle resolver: Nightwatch commands take selector strings, so an\n // element object never reaches an assert — only the value's provenance,\n // recorded by the command path (BrowserProxy), can name the target.\n (args) => resolveAssertTargetFromArgs(args)\n )\n}\n\nfunction captureAssert(\n capturer: SessionCapturer | undefined,\n testUid: string | undefined,\n cmd: CapturedAssert\n): void {\n if (!capturer) {\n return\n }\n capturer\n .captureCommand(cmd.command, cmd.args, cmd.result, cmd.error, {\n testUid,\n callSource: cmd.callSource,\n timestamp: cmd.timestamp,\n // The assert's args hold the compared VALUES, so provenance — recorded by\n // the command path — is the row's only route to a locator.\n selector: cmd.selector\n })\n .catch((err) =>\n log.warn(`Failed to capture ${cmd.command}: ${errorMessage(err)}`)\n )\n // captureCommand pushes synchronously; mirror captureCommandError's send.\n const last = capturer.commandsLog[capturer.commandsLog.length - 1]\n if (last) {\n capturer.sendCommand(last)\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 * Global key the plugin uses to hand itself to the Cucumber hooks\n * (`helpers/cucumberHooks.cts`). Single source of truth, re-exported by\n * `constants.ts`.\n *\n * It lives in its own leaf module with NO imports on purpose: the Cucumber\n * hooks are bundled standalone into a CJS file, and importing this from the\n * full `constants.ts` would drag in its ESM dependency graph (→ core), which\n * uses `import.meta.url` / `createRequire` and throws when bundled to CJS.\n */\nexport const PLUGIN_GLOBAL_KEY = '__nightwatchDevtoolsPlugin'\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 * Defined in its own leaf module so the Cucumber hooks can bundle it standalone\n * (see plugin-global-key.ts); re-exported here for the rest of the plugin.\n */\nexport { PLUGIN_GLOBAL_KEY } from './plugin-global-key.js'\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 TestRunnerId,\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\n/** This adapter's runner identity. Both Nightwatch runners take the same\n * locator dialect, but the id also reaches the dashboard's run capabilities,\n * where the cucumber variant differs. */\nexport function nightwatchRunnerId(isCucumberRunner: boolean): TestRunnerId {\n return isCucumberRunner ? 'nightwatch-cucumber' : 'nightwatch'\n}\n\nexport function buildPluginMetadataOptions(input: {\n isCucumberRunner: boolean\n configPath: string | undefined\n}) {\n return {\n framework: nightwatchRunnerId(input.isCucumberRunner),\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 * 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-run bringup (capturer + reporter chain + metadata + BiDi + screencast)\n * - Re-targeting that one capturer when the session under it is replaced\n * - Re-arming the per-session capture channels when a session is replaced\n * - Screencast finalize-and-clear\n *\n * The recorder's own session binding lives in `screencast-session.ts`.\n */\n\nimport logger from '@wdio/logger'\nimport {\n errorMessage,\n finalizeScreencast,\n registerCollectorPreload,\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 type { ScreencastRecorder } from './screencast.js'\nimport {\n rotateScreencastForSession,\n startScreencast\n} from './screencast-session.js'\nimport type {\n DevToolsMode,\n NightwatchBrowser,\n ScreencastOptions,\n SuiteStats,\n TestRunnerId,\n TestStats\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 readonly captureAssertions: boolean\n readonly runner: TestRunnerId\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 /** Session the BiDi attach + collector preload were last armed for — the\n * once-per-session guard for both, and the rotation detector. */\n armedSessionId: string | undefined\n srcFolders: string[]\n screencastRecorder: ScreencastRecorder | undefined\n screencastSessionId: string | undefined\n /** In-flight recorder rotation — see screencast-session.ts. */\n screencastRotation: Promise<void> | undefined\n configPath: string | undefined\n\n getCurrentTest(): unknown\n getCurrentScenarioSuite(): SuiteStats | null\n buildMetadataOptions(): unknown\n attemptFor(uid: string): number | undefined\n recordOutcome(uid: string, state: TestStats['state']): void\n /** Drains the live recorder's frames into the run's filmstrip buffer, then\n * stops and clears it. */\n finalizeCurrentScreencast(): Promise<void>\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(\n (suitesData) => {\n if (ctx.sessionCapturer) {\n ctx.sessionCapturer.sendUpstream('suites', suitesData)\n }\n },\n (uid) => ctx.attemptFor(uid)\n )\n ctx.testManager = new TestManager(ctx.testReporter, (uid, state) =>\n ctx.recordOutcome(uid, state)\n )\n ctx.suiteManager = new SuiteManager(ctx.testReporter)\n ctx.browserProxy = new BrowserProxy(\n ctx.sessionCapturer,\n ctx.testManager,\n () => ctx.getCurrentTest() ?? ctx.getCurrentScenarioSuite(),\n ctx.captureAssertions,\n // Nightwatch raises no session event and never wraps `end`, so a command\n // running under a new sessionId is the only trace a mid-run\n // `browser.end()` leaves behind. Both re-bindings hang off it; each latches\n // and gates itself, so neither can suppress the other.\n (browser) => {\n rotateScreencastForSession(ctx, browser)\n rearmCaptureForSession(ctx, browser)\n }\n )\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 runner: ctx.runner,\n url: ''\n }\n // Single-valued, so a run-spanning trace carries the LAST session's identity.\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. Attempted once per session (the caller's `armedSessionId` stamp\n// is that guard); on failure or unavailability the perf-log fallback path\n// continues to work.\nasync function tryAttachBidi(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): Promise<void> {\n if (!ctx.bidiEnabled) {\n return\n }\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/**\n * Register the collector to run at document-start, so every document — including\n * the ones a navigation creates without us noticing — instruments and anchors\n * itself.\n *\n * Deliberately NOT gated on the `bidi` option: that option exists to avoid\n * double-reporting console/network against the perf-log path, whereas this needs\n * nothing but a session created with `webSocketUrl: true`. Gating DOM capture on\n * an unrelated opt-in that defaults to false would leave the race in place for\n * almost every user. Self-degrades to the per-document `<script>` injection when\n * BiDi isn't there.\n */\nasync function tryRegisterPreload(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): Promise<void> {\n const driver = (browser as { driver?: unknown }).driver\n if (!driver) {\n return\n }\n ctx.sessionCapturer.preloadRegistered = await registerCollectorPreload(\n driver,\n (level, message) => log[level](message)\n )\n}\n\n/**\n * Arm the two capture channels that belong to ONE WebDriver session: the BiDi\n * inspectors (bound to that session's driver object) and the document-start\n * collector preload (registered on that session's BiDi channel). Neither\n * survives the session, so a replaced session needs both again.\n *\n * The stamp is written before any await: on rotation this runs from the command\n * hook, where the next command arrives long before a BiDi round trip completes,\n * and the stamp is what keeps that flood from re-arming.\n *\n * The preload goes FIRST. Behind the attach it lost the race it exists to win —\n * measured on a re-arm, the network subscribe took 6.2 s while the command that\n * triggered the re-arm navigated, so the registration landed two seconds after\n * the document it was meant to instrument was born. Sequential rather than\n * parallel because both steps open the session's BiDi channel through the same\n * unsynchronized `getBidi()` cache, and racing them opens two websockets of\n * which `quit()` closes one.\n */\nasync function armCaptureForSession(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): Promise<void> {\n ctx.armedSessionId = browser.sessionId ?? undefined\n await tryRegisterPreload(ctx, browser)\n await tryAttachBidi(ctx, browser)\n}\n\n/**\n * Whether the browser is executing under a session the capture channels were\n * never armed for. Requires an already-armed session: before bringup there is no\n * capturer to arm onto, and `ensureSessionInitialized` owns that first arm.\n */\nexport function needsCaptureRearm(\n ctx: Pick<SessionInitCtx, 'armedSessionId' | 'sessionCapturer'>,\n sessionId: string | undefined\n): boolean {\n return (\n Boolean(ctx.sessionCapturer) &&\n sessionId !== undefined &&\n ctx.armedSessionId !== undefined &&\n ctx.armedSessionId !== sessionId\n )\n}\n\n/**\n * Re-arm BiDi and the collector preload after the session was replaced. Without\n * it both are established exactly once, at bringup, and sessions 2..N silently\n * fall back to `<script>` injection — the whole navigation race class the\n * preload exists to remove comes back for the rest of the run.\n *\n * The `needsCaptureRearm` guard is what makes it idempotent: the command hook\n * and `ensureSessionInitialized` both detect the same replacement, and\n * `armCaptureForSession` stamps `armedSessionId` before its first await, so\n * whichever gets there first latches the other out.\n */\nasync function armReplacedSession(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): Promise<void> {\n const sessionId = browser.sessionId ?? undefined\n if (!needsCaptureRearm(ctx, sessionId)) {\n return\n }\n // Both claims belong to the session that just died. Until the new\n // registration lands this session's documents are back on the `<script>`\n // path — exactly what `anchorAfterNavigation` polls for — and a stale\n // `bidiActive` would keep the perf-log network path gated off behind\n // inspectors that no longer receive anything.\n ctx.sessionCapturer.preloadRegistered = false\n ctx.sessionCapturer.bidiActive = false\n log.info(`Session replaced — re-arming capture for ${sessionId}`)\n await armCaptureForSession(ctx, browser)\n}\n\n/** Fire-and-forget {@link armReplacedSession}, for the command hook — a command\n * must not wait on a BiDi handshake. */\nexport function rearmCaptureForSession(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): void {\n void armReplacedSession(ctx, browser).catch((err) =>\n log.warn(`Capture re-arm failed: ${errorMessage(err)}`)\n )\n}\n\n/** Re-run the per-session bringup against a replaced session, keeping the run's\n * one capturer. Both halves are the guarded helpers the command hook uses, so a\n * replacement both paths see is armed and rotated once. */\nasync function rebindSessionToBrowser(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): Promise<void> {\n log.info('Browser session changed — re-targeting the run capturer')\n // Also gates `wrapUrlMethod`, which is per browser OBJECT — cucumber hands\n // over a new one per scenario.\n ctx.isScriptInjected = false\n broadcastSessionMetadata(ctx, browser)\n await armReplacedSession(ctx, browser)\n rotateScreencastForSession(ctx, browser)\n await ctx.screencastRotation\n}\n\nexport async function ensureSessionInitialized(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): Promise<void> {\n const currentSessionId = browser.sessionId\n const isSessionChange =\n currentSessionId &&\n ctx.lastSessionId &&\n currentSessionId !== ctx.lastSessionId\n ctx.lastSessionId = currentSessionId ?? null\n if (ctx.sessionCapturer) {\n // Unconditional: cucumber hands over a new browser object per scenario, and\n // a stale reference answers neither probe the capturer runs against it.\n ctx.sessionCapturer.setBrowser(browser)\n if (isSessionChange) {\n await rebindSessionToBrowser(ctx, browser)\n }\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' ? {} : { port: ctx.port, hostname: ctx.hostname },\n browser\n )\n ctx.sessionCapturer.traceMode = ctx.mode\n ctx.sessionCapturer.runner = ctx.runner\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 initReporterChain(ctx)\n broadcastSessionMetadata(ctx, browser)\n await armCaptureForSession(ctx, browser)\n await startScreencast(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 if (ctx.mode === 'trace') {\n // Trace mode: the per-test video is written by the produce path and the\n // filmstrip frames are embedded in the trace itself, so stop the recorder\n // without encoding a session .webm nothing references (orphan file).\n await ctx.screencastRecorder.stop()\n } else {\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 }\n ctx.screencastRecorder = undefined\n ctx.screencastSessionId = undefined\n}\n","import logger from '@wdio/logger'\nimport {\n CAPTURE_PERFORMANCE_SCRIPT,\n COLLECTOR_READY_EXPRESSION,\n SessionCapturerBase,\n applyPerformanceData,\n collectorDrainExpression,\n drainCollectorWithRecovery,\n errorMessage,\n loadInjectableScript,\n mapChromeBrowserLogs,\n mapCommandToAction,\n pollUntilReady,\n serializeError,\n upsertRichestSnapshot,\n type CapturedPerformancePayload,\n type SessionCapturerOptions\n} from '@wdio/devtools-core'\nimport { captureActionSnapshot } from './action-snapshot.js'\nimport { nightwatchRunnerId } from './helpers/utils.js'\nimport { NAVIGATION_COMMANDS } from './constants.js'\nimport {\n parseNetworkFromPerfLogs,\n dedupeNetworkRequests,\n type NetworkEntry,\n type PerfLogEntry\n} from './helpers/perfLogs.js'\nimport { webdriverExecute, webdriverGet } from './helpers/webdriverHttp.js'\nimport type {\n ActionSnapshot,\n CommandCaptureMeta,\n CommandLog,\n DevToolsMode,\n NightwatchBrowser,\n TestRunnerId\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\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 /** True once the collector is registered to run at document-start. Every\n * document then instruments and anchors itself, so the paths that exist to\n * notice a navigation after the fact — re-injection and the settle poll — have\n * nothing left to do and stand down. */\n preloadRegistered = false\n\n // Populated by captureCommand when mode === 'trace' (set by the plugin).\n traceMode: DevToolsMode = 'live'\n\n /** Which Nightwatch runner is driving — set by the plugin, and passed to the\n * per-action capture so the injected scripts emit this runner's dialect. */\n runner: TestRunnerId = nightwatchRunnerId(false)\n readonly actionSnapshots: ActionSnapshot[] = []\n readonly snapshotCaptures: Promise<void>[] = []\n\n constructor(\n devtoolsOptions: SessionCapturerOptions = {},\n browser?: NightwatchBrowser\n ) {\n super(devtoolsOptions)\n this.#browser = browser\n this.patchConsole()\n this.patchStreams()\n }\n\n /** Re-target at the browser the run now drives — the capturer's only\n * session-bound state, so a replaced session is a re-target, not a rebuild\n * (mirrors selenium's `setDriver`). */\n setBrowser(browser: NightwatchBrowser): void {\n this.#browser = browser\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 meta: CommandCaptureMeta = {}\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: meta.timestamp || Date.now(),\n startTime: meta.startTime,\n sequence: meta.sequence,\n callSource: meta.callSource,\n testUid: meta.testUid,\n selector: meta.selector\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 (!error) {\n this.#queueActionSnapshot(command, commandLogEntry.timestamp)\n }\n\n return true\n }\n\n /** Fire-and-forget post-action snapshot, drained at finalize. Stamped with the\n * logged command's timestamp, not the capture's own: the capture resolves\n * after the command completes, and export claims snapshots by exact equality\n * with the command timestamp (FrameSnapshotIndex.claimAfter / elementsAt). */\n #queueActionSnapshot(command: string, timestamp: number): void {\n if (\n this.traceMode !== 'trace' ||\n !this.#browser ||\n !mapCommandToAction(command)\n ) {\n return\n }\n this.snapshotCaptures.push(\n captureActionSnapshot(\n this.#browser,\n command,\n timestamp,\n this.runner\n ).then((snap) => {\n if (snap) {\n upsertRichestSnapshot(this.actionSnapshots, snap)\n }\n })\n )\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 * Ingest a pre-built assertion entry (native `browser.assert`/`verify`\n * synthesized from Nightwatch's results, or a node:assert capture) into the\n * same command stream driver commands use. Unlike {@link captureCommand} this\n * preserves the entry's `title` (the human assertion message) and never\n * dedups — each call is a distinct action row. Assigns a fresh `_id` and a\n * matching stable public `id` (so a later `sendReplaceCommand` can update\n * this exact row in place — native asserts stream a pending row at call time,\n * then finalize their pass/fail), pushes, and broadcasts.\n */\n captureAssertCommand(entry: CommandLog): void {\n const withId = entry as CommandLog & { _id?: number }\n withId._id = this.commandCounter++\n withId.id = withId._id\n this.commandsLog.push(withId)\n // Deliberately no snapshot capture. These rows are emitted in a batch at\n // test-end but positioned back on their real execution window, so capturing\n // here would probe the page as it is NOW and stamp it at a moment seconds\n // earlier — an assert that ran on /secure rendered the /login page it later\n // logged out to. An assertion reads the page rather than changing it, so the\n // exporter lets it inherit the preceding action's capture instead\n // (FrameSnapshotIndex.claimAfter). The row's own `screenshot` is already\n // attached by the caller from the last resolved capture.\n this.sendCommand(withId)\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 meta: CommandCaptureMeta = {}\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: meta.timestamp || Date.now(),\n startTime: meta.startTime,\n sequence: meta.sequence,\n callSource: meta.callSource,\n testUid: meta.testUid,\n selector: meta.selector\n }\n this.commandsLog.push(entry)\n return { entry, oldTimestamp }\n }\n\n /** Birth time of the document this capturer last observed, so a replacement\n * is still visible to a poll that starts after the navigation committed. */\n private lastDocumentOrigin: number | undefined\n\n /** The current document's own birth time, which is what a DOM anchor is\n * stamped with. Undefined when the session cannot answer. */\n private async documentOrigin(\n browser: NightwatchBrowser\n ): Promise<number | undefined> {\n return webdriverExecute<number>(browser, 'return performance.timeOrigin;')\n .then((origin) => origin ?? undefined)\n .catch(() => undefined)\n }\n\n /**\n * Wait for a navigation to actually replace the document, then drain with a\n * forced anchor so the destination's DOM enters the stream.\n *\n * A Nightwatch click resolves BEFORE its navigation commits, so the drain that\n * runs at command completion still finds the outgoing page's collector: it\n * recovers nothing, and the destination is never anchored — every action on the\n * new page then replays the page it came from.\n *\n * The replacement signal is the document's own `performance.timeOrigin`\n * changing, NOT the collector going missing. Under the preload every document\n * instruments itself at document-start, so the collector is never absent and\n * that older signal stood permanently false — the destination was then only\n * ever anchored by a drain that happened to be slow enough to land after the\n * navigation, and tightening that latency lost the destination page entirely.\n * A same-URL re-navigation is a new document and a new origin, so this covers\n * it too; a command that navigated nowhere polls out and costs one drain of an\n * empty buffer.\n *\n * The baseline is the last origin THIS capturer saw, never one read on entry.\n * Reading it here would compare the document against itself: a command that\n * completes AFTER its navigation committed (`browser.url` waits for load, so\n * that is the normal case) would baseline the destination and then report no\n * change, dropping the very anchor this exists to take. With no baseline yet\n * a navigation cannot be ruled out either, so the first DOM-mutating command\n * of a run anchors unconditionally rather than guessing — `captureCurrentDom`\n * is idempotent per document, so an already-anchored page costs one drain.\n */\n async anchorAfterNavigation(browser: NightwatchBrowser): Promise<void> {\n const before = this.lastDocumentOrigin\n const replaced = await pollUntilReady(\n async () => {\n const now = await this.documentOrigin(browser)\n if (now === undefined) {\n return false\n }\n this.lastDocumentOrigin = now\n return before === undefined || now !== before\n },\n { attempts: 5, intervalMs: 150 }\n )\n if (!replaced) {\n return\n }\n // captureTrace's own recovery injects into the fresh document and re-drains,\n // and it gates that on the url being worth instrumenting — so a torn-down\n // session falls out here rather than logging an injection failure.\n //\n // The attribution is the newest command at THIS moment, not the one whose\n // completion started the poll: several polls overlap, so a field edit's poll\n // routinely observes the navigation the following click caused and would\n // credit itself — which pulled the anchor back before the edit's own row and\n // moved the wrong-DOM row one earlier instead of fixing it. Read at detection\n // time it is right for whichever poll gets there first.\n //\n // Unlike the inference in `anchorOwnerTimestamp`, this does not need the\n // \"nothing completed after the anchor\" guard: we just watched the document be\n // replaced, so a navigation demonstrably happened and the newest command is\n // the one that caused it. The guard exists for the case where that is a guess.\n await this.captureTrace(\n browser,\n true,\n this.commandsLog[this.commandsLog.length - 1]?.timestamp\n )\n }\n\n /**\n * Take a screenshot by calling the WebDriver HTTP endpoint directly, bypassing\n * Nightwatch's command queue so the request can't be appended after\n * `end()` / `quit()`.\n */\n takeScreenshotViaHttp(browser: NightwatchBrowser): Promise<string | null> {\n return webdriverGet<string>(browser, 'screenshot')\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 // Injecting over a live collector replaces `window.wdioTraceCollector`\n // with a fresh instance and DISCARDS whatever it had buffered — including\n // the destination's full-DOM anchor. Several paths can reach the same\n // document (navigation proxy, drain recovery), so guard here once.\n const alreadyPresent = await webdriverExecute<boolean>(\n browser,\n `return ${COLLECTOR_READY_EXPRESSION};`\n )\n if (alreadyPresent === true) {\n return\n }\n const scriptContent = await loadInjectableScript()\n // Inlined as a literal rather than passed via `arguments[0]`: the raw\n // /execute/sync transport takes its args separately and this keeps the\n // injection a single self-contained body.\n const injectionScript = `\n const script = document.createElement('script');\n script.textContent = ${JSON.stringify(scriptContent)};\n document.head.appendChild(script);\n return true;\n `\n await webdriverExecute(browser, injectionScript)\n\n const hasCollector = await pollUntilReady(\n async () =>\n (await webdriverExecute<boolean>(\n browser,\n `return ${COLLECTOR_READY_EXPRESSION};`\n )) === 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 // A perf-log entry that never saw its response event carries no type;\n // default it to the vocabulary's residual at this framework boundary.\n const normalized = deduped.map((d) => ({\n ...d,\n type: d.type ?? 'other'\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 /** `forceAnchor` forces a full-DOM anchor of the current document before\n * draining, so a freshly injected collector's async anchor isn't lost.\n * `anchorTimestamp` is the command this drain's anchors were CAUSED by — pass\n * it only when the caller observed the document being replaced right after\n * that command, since it overrides the inference at ingest. */\n async captureTrace(\n browser: NightwatchBrowser,\n forceAnchor = false,\n anchorTimestamp?: number\n ) {\n // Performance logs only accumulate, so they lose nothing by waiting, while\n // the drain is racing the page it reads from (`drainOutgoingPage`).\n await this.drainCollector(browser, forceAnchor, anchorTimestamp)\n await this.captureNetworkFromPerformanceLogs(browser)\n }\n\n private async drainCollector(\n browser: NightwatchBrowser,\n forceAnchor: boolean,\n anchorTimestamp?: number\n ) {\n // 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 traceData = await drainCollectorWithRecovery({\n drain: () =>\n webdriverExecute<Record<string, unknown> | null>(\n browser,\n collectorDrainExpression(forceAnchor)\n ),\n injectIntoCurrentDocument: () => this.injectScript(browser),\n currentUrl: () =>\n webdriverExecute<string>(browser, 'return location.href;').then(\n (u) => u ?? undefined\n ),\n log: (level, message) => log[level](message)\n })\n\n if (!traceData) {\n return\n }\n\n this.processTracePayload(traceData, { anchorTimestamp })\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","// Direct WebDriver HTTP transport for the capture probes.\n//\n// Every probe the trace capture makes — url, title, screenshot, in-page script\n// — has to bypass Nightwatch's command queue. `browser.*` commands are QUEUED,\n// so a probe the plugin issues from inside its own command hook enqueues behind\n// the command that is still running and cannot resolve until the queue drains.\n// Unguarded, one such probe stranded the whole per-action snapshot capture; the\n// in-page script probe merely timed out, leaving empty a11y trees.\n\nimport http from 'node:http'\nimport logger from '@wdio/logger'\nimport { errorMessage } from '@wdio/devtools-core'\nimport type { NightwatchBrowser } from '../types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:webdriverHttp')\n\n/** Ceiling on a single driver request — a driver that stops answering must not\n * hold a capture open longer than the adapter's settle window. */\nconst REQUEST_TIMEOUT_MS = 5000\n\ntype LooseRec = Record<string, unknown>\n\nconst getProp = (obj: unknown, key: string): unknown =>\n obj && typeof obj === 'object' ? (obj as LooseRec)[key] : undefined\n\nconst getPath = (obj: unknown, ...path: string[]): unknown =>\n path.reduce<unknown>((acc, k) => getProp(acc, k), obj)\n\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 type)\n * to find the underlying WebDriver host+port.\n */\nexport function 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\nfunction sessionEndpoint(\n browser: NightwatchBrowser,\n path: string\n): string | undefined {\n const sessionId = (browser as unknown as { sessionId?: string }).sessionId\n if (!sessionId) {\n return undefined\n }\n const { driverHost, driverPort } = resolveWebDriverAddress(browser)\n return `http://${driverHost}:${driverPort}/session/${sessionId}/${path}`\n}\n\n/** A W3C error payload: `value` carries `error`/`message` instead of the\n * command's result. Shape-checked rather than status-checked because chromedriver\n * answers some failures with a 200. */\nfunction isWebdriverError(value: unknown): boolean {\n return (\n typeof value === 'object' &&\n value !== null &&\n !Array.isArray(value) &&\n typeof (value as { error?: unknown }).error === 'string'\n )\n}\n\n/** Resolves the W3C `value` field, or null on any transport/parse/timeout\n * failure — a probe is best-effort and never fails the user's test. */\nfunction request<T>(\n endpoint: string,\n method: 'GET' | 'POST',\n body?: unknown\n): Promise<T | null> {\n const payload = body === undefined ? undefined : JSON.stringify(body)\n return new Promise((resolve) => {\n const req = http.request(\n endpoint,\n {\n method,\n headers: payload\n ? {\n 'content-type': 'application/json',\n 'content-length': Buffer.byteLength(payload)\n }\n : undefined\n },\n (res) => {\n let raw = ''\n res.on('data', (chunk: string | Buffer) => {\n raw += chunk\n })\n res.on('end', () => {\n try {\n const value = JSON.parse(raw).value\n // A W3C error answers 200-shaped JSON whose `value` is\n // `{error, message, stacktrace}` — an OBJECT where the caller\n // expects its payload. Casting that through as `T` put an error\n // object into a screencast frame's `data`, and the run's whole\n // trace was then lost to `Buffer.from(object)` at export.\n resolve(isWebdriverError(value) ? null : ((value as T) ?? null))\n } catch {\n log.warn(`Failed to parse response from ${endpoint}`)\n resolve(null)\n }\n })\n }\n )\n req.on('error', (err) => {\n log.warn(`Request failed (${endpoint}): ${errorMessage(err)}`)\n resolve(null)\n })\n req.setTimeout(REQUEST_TIMEOUT_MS, () => {\n log.warn(`Request timed out (${endpoint})`)\n req.destroy()\n resolve(null)\n })\n if (payload) {\n req.write(payload)\n }\n req.end()\n })\n}\n\n/** GET `/session/:id/<path>`. Null when the session is gone or the call fails. */\nexport function webdriverGet<T>(\n browser: NightwatchBrowser,\n path: string\n): Promise<T | null> {\n const endpoint = sessionEndpoint(browser, path)\n return endpoint ? request<T>(endpoint, 'GET') : Promise.resolve(null)\n}\n\n/** POST `/session/:id/<path>`. Null when the session is gone or the call fails. */\nexport function webdriverPost<T>(\n browser: NightwatchBrowser,\n path: string,\n body: unknown\n): Promise<T | null> {\n const endpoint = sessionEndpoint(browser, path)\n return endpoint ? request<T>(endpoint, 'POST', body) : Promise.resolve(null)\n}\n\n/** Run a script in the page, outside the command queue. `body` is a function\n * body, matching what `browser.execute` accepts. */\nexport function webdriverExecute<T>(\n browser: NightwatchBrowser,\n body: string\n): Promise<T | null> {\n return webdriverPost<T>(browser, 'execute/sync', { script: body, args: [] })\n}\n","// Nightwatch adapter: wires NightwatchBrowser into core's captureActionSnapshot.\n// Every probe goes over the raw WebDriver HTTP transport rather than `browser.*`:\n// those are QUEUED commands, so a probe issued from inside the plugin's own\n// command hook enqueues behind the command still running and can't resolve until\n// the queue drains. `browser.getCurrentUrl()`/`getTitle()` that way left every\n// snapshot with `url: undefined` (so `frameUrl` fell back to `about:blank`) and\n// hung the capture outright; the queued `execute` merely timed out, leaving an\n// empty a11y tree and no element rects.\n\nimport { captureActionSnapshot as coreCapture } from '@wdio/devtools-core'\nimport type { ActionSnapshot } from '@wdio/devtools-shared'\nimport { webdriverExecute, webdriverGet } from './helpers/webdriverHttp.js'\nimport type { NightwatchBrowser, TestRunnerId } from './types.js'\n\nexport function captureActionSnapshot(\n browser: NightwatchBrowser,\n command: string,\n timestamp?: number,\n runner?: TestRunnerId\n): Promise<ActionSnapshot | null> {\n return coreCapture({\n command,\n timestamp,\n runner,\n runScript: (src) => webdriverExecute(browser, `return (${src})`),\n takeScreenshot: () => webdriverGet<string>(browser, 'screenshot'),\n getUrl: () => webdriverGet<string>(browser, 'url'),\n getTitle: () => webdriverGet<string>(browser, 'title')\n })\n}\n","import type { RequestType } from '@wdio/devtools-shared'\n\nimport { 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 /** Set only by `getRequestType`, so it carries the shared vocabulary; absent\n * until the response event arrives. */\n type?: RequestType\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, type ReporterUpstream } 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 #attemptFor: (uid: string) => number | undefined\n\n constructor(\n report: ReporterUpstream,\n attemptFor: (uid: string) => number | undefined\n ) {\n super(report)\n this.#attemptFor = attemptFor\n }\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 uid = generateStableUid({\n file: suiteStats.file,\n fullTitle: `${suiteStats.title} ${testName}`\n } as TestStats)\n const skippedTest: TestStats = {\n uid,\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: this.#attemptFor(uid) ?? 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","/**\n * Which `it` the runner is executing RIGHT NOW.\n *\n * The plugin's `beforeEach` fires once per MODULE on the BDD `describe/it`\n * interface, so the lifecycle-level current test is the module's first `it` for\n * every row in the file. Nightwatch's own `setReporterCurrentTest()` — \"called\n * for every test case (not for hooks)\" — runs per `it` and installs\n * `browser.currentTest` as a live getter over `reporter.currentTest`, so the\n * running testcase's NAME is readable as state on every command. That name is\n * the per-`it` signal no hook or event exposes; mapping it back onto the\n * `TestStats` the suite already registered gives each `it` its own uid, and with\n * it its own group in the trace's action tree.\n */\n\nimport type { NightwatchCurrentTest, SuiteStats, TestStats } from '../types.js'\n\n/**\n * The registered test title a runtime testcase name refers to. Nightwatch\n * reports either the bare `it` title or one prefixed with its parents, so a\n * whole-title suffix match is accepted; anything else is no match — this never\n * guesses at \"the next unprocessed test\" the way the lifecycle picker does.\n */\nexport function matchTestName(\n runtimeName: string | undefined,\n testNames: readonly string[]\n): string | undefined {\n const name = typeof runtimeName === 'string' ? runtimeName.trim() : undefined\n if (!name) {\n return undefined\n }\n return testNames.find((title) => name === title || name.endsWith(` ${title}`))\n}\n\n/**\n * The suite's registered `TestStats` for the testcase Nightwatch reports as\n * running. Undefined when nothing matches: a command issued from a hook (where\n * Nightwatch leaves the previous testcase in place), a spec whose source\n * couldn't be parsed into test names, or the cucumber runner — whose client\n * carries no `currentTest` at all, which is what keeps its per-scenario\n * structure on the lifecycle path untouched.\n */\nexport function findRunningTest(\n suite: SuiteStats | undefined,\n currentTest: NightwatchCurrentTest | undefined\n): TestStats | undefined {\n const tests = (suite?.tests ?? []).filter(\n (t): t is TestStats => typeof t !== 'string'\n )\n const title = matchTestName(\n currentTest?.name,\n tests.map((t) => t.title)\n )\n return title ? tests.find((t) => t.title === title) : undefined\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 { findRunningTest } from './runningTest.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(\n private testReporter: TestReporter,\n /** Stamps a test's resolved terminal state onto the retry ledger. */\n private recordOutcome?: (uid: string, state: TestStats['state']) => void\n ) {}\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 this.recordOutcome?.(test.uid, state)\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 * The registered test for the testcase Nightwatch reports as running, read\n * from live `browser.currentTest` state rather than from a hook — the only\n * per-`it` signal the BDD interface gives a plugin (see ./runningTest.ts).\n * Synchronous by construction: the command path resolves a row's test on the\n * invocation stack, where an await would reorder capture.\n */\n runningTest(\n currentTest: NightwatchCurrentTest | undefined\n ): TestStats | undefined {\n return findRunningTest(this.testReporter.getCurrentSuite(), currentTest)\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 { callbackError } from './callbackError.js'\nimport { commandRowSelector, commandTargetSelector } from './assertTarget.js'\nimport { getCallSourceFromStack } from './utils.js'\nimport { serializeCommandResult } from './serializeCommandResult.js'\nimport { NativeAssertRecorder } from './nativeAssertRecorder.js'\nimport {\n RetryTracker,\n beginInputDispatch,\n rememberReadValue,\n toError\n} from '@wdio/devtools-core'\nimport type { SessionCapturer } from '../session.js'\nimport type { TestManager } from './testManager.js'\nimport type {\n CommandCaptureMeta,\n CommandInvocation,\n CommandLog,\n NativeAssertCall,\n NightwatchBrowser,\n NightwatchChainable,\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 /**\n * Monotonic issue-order counter. Stamped when the test ISSUES a row — a\n * driver command at invocation, an assert at enqueue — which is source order.\n * Both are recorded into `commandsLog` much later and out of that order (a\n * driver command at completion, an assert in the test-end batch), so this is\n * what lets the exporter recover the order the test wrote. Shared with the\n * assert recorder so both row kinds draw from one sequence.\n */\n private issueCounter = 0\n\n private assertRecorder: NativeAssertRecorder\n\n /**\n * The browser every captured row is issued on. Nightwatch mutates ONE browser\n * object in place — even across a mid-run session replacement — so this stays\n * valid for the whole run, and it is how `browser.currentTest` is read from\n * the assert path, which runs outside any command callback.\n */\n private browser: NightwatchBrowser | null = null\n\n constructor(\n private readonly sessionCapturer: SessionCapturer,\n private testManager: TestManager,\n private getCurrentTest: () => { uid?: string } | null,\n private captureAssertions = true,\n private observeSession?: (browser: NightwatchBrowser) => void\n ) {\n this.assertRecorder = new NativeAssertRecorder(\n sessionCapturer,\n () => this.rowTest(),\n () => this.issueCounter++\n )\n }\n\n /**\n * The test a captured row belongs to: the `it` Nightwatch reports as running\n * right now, falling back to the lifecycle test. On the BDD `describe/it`\n * interface the lifecycle test is the module's FIRST `it` for every row in the\n * file (its `beforeEach` fires once per module), so without the live read\n * every command in the module carried one uid and the trace's action tree\n * collapsed to a single test group. The fallback is what the runners with no\n * per-testcase state keep using — cucumber has no `currentTest` at all.\n */\n private rowTest(): { uid?: string } | null {\n return (\n this.testManager.runningTest(this.browser?.currentTest) ??\n this.getCurrentTest()\n )\n }\n\n /** The uid rows are stamped with — see {@link rowTest}. Public so the\n * node:assert capture wiring, which has no command of its own, tags its rows\n * onto the same test the surrounding commands landed on. */\n rowTestUid(): string | undefined {\n return this.rowTest()?.uid\n }\n\n /** Per-command-scope dedup state only. Cucumber calls this per STEP, so it\n * must not touch the native-assert buffer — that buffer spans the whole\n * scenario and is drained once, at the scenario's pre-quit hook. */\n resetCommandTracking(): void {\n this.commandStack = []\n this.lastCommandSig = null\n this.retryTracker.reset()\n }\n\n /** Everything scoped to one test unit: dedup state plus any native-assert\n * calls a previous unit left behind because its drain never ran. */\n resetTestTracking(): void {\n this.resetCommandTracking()\n this.assertRecorder.clear()\n }\n\n /** Hand off this test's recorded native-assertion calls and clear the\n * buffer so the next test starts fresh. */\n drainNativeAssertCalls(): NativeAssertCall[] {\n return this.assertRecorder.drain()\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 // `browser.url()` is BOTH a navigation and a getter: with no url it just\n // reads the current one, which is how `assert.urlContains` reads it from\n // inside Nightwatch's own queue. Treating that read as a navigation\n // re-injected the collector and forced an anchored drain mid-test — real\n // round trips against the page for a command that changed nothing — and\n // logged the capture callback's whole function body as the \"url\".\n if (typeof args[0] !== 'string') {\n return original(...args)\n }\n const result = original(...args)\n\n const injectAndCapture = () => {\n log.info(`[nav] ${methodName}(${args[0] ?? ''}) — injecting script`)\n return (\n sessionCapturer\n .injectScript(browser)\n // Anchored: the just-injected collector anchors the document\n // asynchronously, so an unanchored drain beats it and this page's\n // DOM never reaches the trace.\n .then(() => sessionCapturer.captureTrace(browser, true))\n .catch((err: Error) =>\n log.error(`Failed to inject script: ${(err as Error).message}`)\n )\n )\n }\n\n const chainable = result as NightwatchChainable | undefined\n if (chainable && typeof chainable.perform === 'function') {\n // Standard Nightwatch (chained API). Hung off the command's OWN\n // thenable, not a queued `perform()`: a queued callback is discarded\n // when a failing test aborts the rest of the queue, so the destination\n // of a navigation in a failing test was never instrumented at all\n // (measured: 2 of 3 navigations skipped, and with them the first\n // document's entire DOM). The chainable is itself thenable, and\n // resolving it means the navigation finished — the same signal\n // `perform` gave, minus the dependency on the queue surviving.\n //\n // `result` is returned unchanged so `browser.url(…).waitFor…()`\n // chaining still works; the injection runs as a side effect.\n void Promise.resolve(result)\n .then(injectAndCapture)\n .catch(() => {})\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 // Held before the already-wrapped guard: the reference is what `rowTest`\n // reads `currentTest` off, and a re-entry is exactly a run that never\n // re-wraps.\n this.browser = browser\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 // captureAssertions:false → leave assert/verify original so no native\n // assertion rows are recorded (the finalize paths are gated to match).\n if (this.captureAssertions) {\n this.assertRecorder.wrapNamespaces(browser)\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 meta: CommandCaptureMeta,\n commandError: Error | undefined\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 commandError,\n meta\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 meta: CommandCaptureMeta,\n cmdSig: string,\n commandError: Error | undefined\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 // Issued first, and only this one — see `drainOutgoingPage`.\n this.drainOutgoingPage(browser, methodName)\n this.sessionCapturer\n .captureCommand(methodName, logArgs, serializedResult, commandError, meta)\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.anchorDestination(browser, methodName)\n }\n\n private isDomMutating(methodName: string): boolean {\n return (\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 )\n }\n\n /**\n * Drains the OUTGOING page's field edits, which die with the document the\n * moment a submit navigates. It goes over the raw HTTP transport, so it\n * touches no Nightwatch queue, and a driver serialises requests per session —\n * so it is served before the next queued command only for as long as it stays\n * the FIRST request this class issues when a command completes. That call\n * position, and the drain-before-perf-logs order inside `captureTrace`, are\n * both load-bearing: behind the snapshot probes and the screenshot it needed\n * about 130 ms of head start and got 18 ms, and the last field a test filled\n * before submitting replayed as an empty box.\n *\n * It deliberately carries only this one request. `anchorAfterNavigation` is\n * issued later, from `anchorDestination`, so the row's own screenshot and DOM\n * snapshot are not pushed behind two round trips.\n */\n private drainOutgoingPage(\n browser: NightwatchBrowser,\n methodName: string\n ): void {\n if (!this.isDomMutating(methodName)) {\n return\n }\n this.sessionCapturer.captureTrace(browser, true).catch(() => {})\n }\n\n /**\n * Anchors the DESTINATION, and it has to wait: a Nightwatch click resolves\n * before its navigation commits, so the outgoing drain still finds the old\n * page's collector, recovers nothing, and the new document is never anchored —\n * every action on it then replayed the page it came from (measured: 5 of 20\n * rows). `anchorAfterNavigation` keys off the document actually being replaced\n * rather than off which command ran, so it covers any route to a new document —\n * a submit click, a JS redirect, a same-command re-navigation — without a list\n * of commands to keep in sync.\n */\n private anchorDestination(\n browser: NightwatchBrowser,\n methodName: string\n ): void {\n if (!this.isDomMutating(methodName)) {\n return\n }\n // Tracked so finalize awaits it — an untracked anchor drain can still be in\n // flight when the trace is written, which loses the document entirely.\n this.sessionCapturer.snapshotCaptures.push(\n this.sessionCapturer.anchorAfterNavigation(browser).catch(() => {})\n )\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 /** Serialize the result and record which element produced it, so a later\n * node:assert on that value can name its target (core's value→locator\n * registry). Fed for every command, so a page-level read records \"no\n * element\" — see `rememberReadValue` for why that sentinel matters. Folded\n * into one call because `makeCaptureCallback` is already over the line cap. */\n private serializeAndAttributeResult(\n methodName: string,\n logArgs: unknown[],\n callbackResult: unknown\n ): unknown {\n const result = serializeCommandResult(callbackResult, methodName)\n rememberReadValue(result, commandTargetSelector(methodName, logArgs))\n return result\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 hasUserSource: boolean,\n invokedAt: number,\n sequence: number,\n testUid: string | undefined,\n userCallback: Function | null\n ): (callbackResult: unknown) => unknown {\n return (callbackResult: unknown) => {\n // Stamped HERE, when the command actually finished in the browser — not\n // when the test enqueued it. Nightwatch runs commands off a queue, so\n // invocation leads real execution by hundreds of ms, and the page-side\n // mutation stream is on real time: a row stamped at invocation replayed\n // the page from BEFORE its own effect (the `#username` fill rendered an\n // empty field, the `#password` fill rendered only the username). The\n // invocation time becomes the row's `startTime`, so the row also spans its\n // real duration instead of a synthetic 1ms.\n const commandTimestamp = Date.now()\n this.popCommandStackIfMatches(methodName, cmdSig)\n const commandError = callbackError(callbackResult)\n const serializedResult = commandError\n ? undefined\n : this.serializeAndAttributeResult(methodName, logArgs, callbackResult)\n const effectiveUid = this.rowTestUid() ?? testUid\n // Only surface commands that originate from a user-code frame. Commands\n // Nightwatch issues from inside its own queue (e.g. the getTitle a\n // `browser.assert.titleContains` runs) execute in a detached tick with no\n // user frame on the stack, so they'd otherwise leak as top-level actions\n // with an \"unknown\" source. Mirrors the service's user-spec-source guard.\n if (effectiveUid && hasUserSource) {\n const meta: CommandCaptureMeta = {\n testUid: effectiveUid,\n callSource,\n timestamp: commandTimestamp,\n startTime: invokedAt,\n sequence,\n selector: commandRowSelector(methodName, logArgs)\n }\n if (this.retryTracker.isRetry(cmdSig)) {\n this.handleRetryReplacement(\n browser,\n methodName,\n logArgs,\n serializedResult,\n meta,\n commandError\n )\n } else {\n this.captureFreshCommand(\n browser,\n methodName,\n logArgs,\n serializedResult,\n meta,\n cmdSig,\n commandError\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 describeInvocation(\n methodName: string,\n args: unknown[]\n ): CommandInvocation {\n const callInfo = getCallSourceFromStack()\n if (callInfo.filePath && !this.currentTestFullPath) {\n this.currentTestFullPath = callInfo.filePath\n }\n const lastArg = args[args.length - 1]\n const hasUserCallback = typeof lastArg === 'function'\n const logArgs = hasUserCallback ? args.slice(0, -1) : args\n const cmdSig = JSON.stringify({\n command: methodName,\n args: logArgs,\n src: callInfo.callSource\n })\n this.pushCommandStackIfNew(methodName, cmdSig, callInfo.callSource)\n return {\n userCallback: hasUserCallback\n ? (lastArg as (result: unknown) => unknown)\n : null,\n logArgs,\n cmdSig,\n callSource: callInfo.callSource,\n hasUserSource: callInfo.filePath !== undefined\n }\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 // The command's session id is the only evidence a mid-run `browser.end()`\n // leaves behind — Nightwatch raises no event and `end` is never wrapped.\n this.observeSession?.(browser)\n this.testManager.startTestIfPending(\n this.testManager.detectTestBoundary(\n browserAny.currentTest as NightwatchCurrentTest\n )\n )\n\n const { userCallback, logArgs, cmdSig, callSource, hasUserSource } =\n this.describeInvocation(methodName, args)\n // Invocation time only — the row's real completion stamp is taken inside the\n // capture callback (see makeCaptureCallback).\n const invokedAt = Date.now()\n const sequence = this.issueCounter++\n const captureCallback = this.makeCaptureCallback(\n browser,\n methodName,\n logArgs,\n cmdSig,\n callSource,\n hasUserSource,\n invokedAt,\n sequence,\n this.rowTestUid(),\n userCallback\n )\n // Suppress screencast polling while this command may be dispatching input: a\n // screenshot landing mid-click makes the click activate nothing. Enqueue is\n // the only hook the adapter has — Nightwatch executes off a queue it exposes\n // no start event for — so an awaited call (enqueue ≈ execution) is covered,\n // while one queued behind a long wait can see its window expire before it\n // runs. The bound is what keeps that from stalling the recorder instead.\n const closeInputWindow = beginInputDispatch(methodName)\n const gatedCallback = (callbackResult: unknown) => {\n closeInputWindow()\n return captureCallback(callbackResult)\n }\n const modifiedArgs = [...logArgs, gatedCallback]\n try {\n return originalMethod(...modifiedArgs)\n } catch (error) {\n closeInputWindow()\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.rowTest()\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(methodName, args, undefined, normalizedError, {\n testUid: currentTest.uid,\n callSource,\n selector: commandRowSelector(methodName, args)\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","/**\n * The failure a Nightwatch command reports by invoking its callback with an\n * error-shaped result instead of throwing.\n *\n * Only a SYNCHRONOUS failure reaches the try/catch around the wrapped method; an\n * async one (a command that times out waiting for an element) arrives here as a\n * result object. Left in `result`, the row kept `error: undefined` and rendered\n * as a success — the failure was readable only as raw text in the result pane,\n * with no red row and nothing in the Errors tab.\n *\n * `passed` present means this is an assertion result, which carries its own\n * pass/fail handling and must not be reinterpreted here.\n */\nexport function callbackError(result: unknown, depth = 0): Error | undefined {\n if (result instanceof Error) {\n return result\n }\n if (!result || typeof result !== 'object') {\n return undefined\n }\n const r = result as Record<string, unknown>\n // W3C driver responses nest their payload under `value` and Nightwatch passes\n // that wrapper through untouched, so the failure sits one level down — reading\n // only the top level found no `error` and the row stayed green. Depth-bounded:\n // this runs inside the command hook, so a self-referencing result must not be\n // able to overflow the stack and take the user's test down with it.\n if ('value' in r) {\n return depth < 2 ? callbackError(r.value, depth + 1) : undefined\n }\n if ('passed' in r || !r.error) {\n return undefined\n }\n const err = new Error(String(r.message ?? r.error))\n if (typeof r.stack === 'string') {\n err.stack = r.stack\n }\n return err\n}\n","// Which element a Nightwatch row is about — for a native `browser.assert.*` /\n// `browser.verify.*` row, and for a driver command whose result an assertion may\n// later be made on.\n//\n// Nightwatch names the target in the CALL: every element assertion in\n// `lib/api/assertions/*.js` takes the element definition as its FIRST argument\n// (`textContains(definition, expected)`), while the page-level ones take the\n// expected value there instead (`urlContains(expected)`). Element commands\n// follow the same shape (`getText(definition, callback)`), so both answers come\n// off arg 0 — under a per-kind allowlist, because the same namespaces also carry\n// calls whose arg 0 is a plain value or a url.\n//\n// node:assert is the case this can't answer: it takes VALUES and names no\n// element, so `commandTargetSelector` feeds core's value→locator registry from\n// the command path and the assert row reads its target back from there.\n//\n// Neither allowlist is derivable from shared's `ACTION_MAP`: that table says how\n// a command RENDERS (its `Element` entries include WDIO commands called on a\n// handle, with no selector argument at all), not whether arg 0 is a definition.\n\n/**\n * Assertions whose first argument is an element definition, transcribed from\n * Nightwatch's own assertion signatures. An allowlist, not a heuristic: the same\n * namespace also carries the page-level assertions (`titleContains`,\n * `urlContains`) and node:assert's value-based mirrors (`ok`, `equal`, …), whose\n * first argument is a plain value — attaching it as a locator is how a\n * page-title assertion ends up boxing an unrelated element.\n */\nconst ELEMENT_TARGET_ASSERTIONS: ReadonlySet<string> = new Set([\n 'attributeContains',\n 'attributeEquals',\n 'attributeMatches',\n 'containsText',\n 'cssClassNotPresent',\n 'cssClassPresent',\n 'cssProperty',\n 'domPropertyContains',\n 'domPropertyEquals',\n 'domPropertyMatches',\n 'elementNotPresent',\n 'elementPresent',\n 'elementsCount',\n 'enabled',\n 'hasAttribute',\n 'hasClass',\n 'hasDescendants',\n 'hidden',\n 'selected',\n 'textContains',\n 'textEquals',\n 'textMatches',\n 'value',\n 'valueContains',\n 'valueEquals',\n 'visible'\n])\n\n/**\n * Commands whose RESULT is a read of the element named by their first argument,\n * transcribed from Nightwatch's element-command signatures. An allowlist for the\n * same reason as above: `browser.url('https://…')` takes a string first too, and\n * reading it as a locator would attribute the page's own reads to an element.\n * Everything absent — every driver-level read included — records its value as\n * belonging to no element (core's `rememberReadValue` sentinel).\n */\nconst ELEMENT_READ_COMMANDS: ReadonlySet<string> = new Set([\n 'getAccessibleName',\n 'getAriaRole',\n 'getAttribute',\n 'getCssProperty',\n 'getElementProperty',\n 'getElementSize',\n 'getLocation',\n 'getLocationInView',\n 'getRect',\n 'getTagName',\n 'getText',\n 'getValue',\n 'isEnabled',\n 'isPresent',\n 'isSelected',\n 'isVisible',\n 'waitForElementNotPresent',\n 'waitForElementNotVisible',\n 'waitForElementPresent',\n 'waitForElementVisible'\n])\n\n/**\n * Element commands that ACT on the element named by their first argument.\n * Deliberately a second set rather than an extension of the reads above: that\n * one also decides which results enter the value→locator registry, and a click's\n * result is not a value any assertion can be made about. Same allowlist\n * discipline — `browser.keys('abc')` takes a string first too.\n */\nconst ELEMENT_ACTION_COMMANDS: ReadonlySet<string> = new Set([\n 'clearValue',\n 'click',\n 'doubleClick',\n 'moveToElement',\n 'rightClick',\n 'setValue',\n 'submitForm',\n 'updateValue'\n])\n\n/** `assert.not.visible('#x')` is recorded under the dotted path `not.visible`;\n * negating an assertion doesn't change which argument is the element. */\nfunction baseMethod(method: string): string {\n return method.startsWith('not.') ? method.slice('not.'.length) : method\n}\n\n/**\n * The selector an element definition carries: a raw string, or the `selector`\n * property of a `{selector, locateStrategy}` bag / a Nightwatch `Element`\n * (duck-typed, so no dependency on Nightwatch's class identity). A definition\n * built from an already-resolved handle carries no selector at all — such a row\n * gets none rather than a wrong one.\n */\nfunction definitionSelector(definition: unknown): string | undefined {\n if (typeof definition === 'string') {\n return definition || undefined\n }\n if (definition && typeof definition === 'object') {\n const selector = (definition as { selector?: unknown }).selector\n if (typeof selector === 'string' && selector) {\n return selector\n }\n }\n return undefined\n}\n\n/** Locator of the element a native assertion was about, or undefined when the\n * assertion targets no element (page title, url, a value comparison). Lands on\n * `CommandLog.selector` so the player's overlay boxes the target the way it\n * does for WDIO's folded matcher rows. */\nexport function assertTargetSelector(\n method: string,\n args: unknown[]\n): string | undefined {\n if (!ELEMENT_TARGET_ASSERTIONS.has(baseMethod(method))) {\n return undefined\n }\n return definitionSelector(args[0])\n}\n\n/** Locator a driver-command row is about — the element it read or acted on.\n * Lands on `CommandLog.selector` so the row says what it targeted instead of\n * leaving the exporter to fall back to `args[0]`. Wider than\n * {@link commandTargetSelector}, which answers the narrower provenance question\n * (whose read produced this value) for the value→locator registry. */\nexport function commandRowSelector(\n method: string,\n args: unknown[]\n): string | undefined {\n if (\n !ELEMENT_READ_COMMANDS.has(method) &&\n !ELEMENT_ACTION_COMMANDS.has(method)\n ) {\n return undefined\n }\n return definitionSelector(args[0])\n}\n\n/** Locator of the element a driver command read from, or undefined when the\n * command reads the page rather than an element. Feeds core's value→locator\n * registry: `undefined` records the result as belonging to no element, which is\n * the sentinel a later node:assert on a page-level value relies on. */\nexport function commandTargetSelector(\n method: string,\n args: unknown[]\n): string | undefined {\n if (!ELEMENT_READ_COMMANDS.has(method)) {\n return undefined\n }\n return definitionSelector(args[0])\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","// Native-assertion capture: turns explicit `browser.assert.*` /\n// `browser.verify.*` calls into concise trace/UI action rows with real args,\n// a clickable source location, and pass/fail colour.\n//\n// Nightwatch exposes no per-assertion hook. Each explicit call is intercepted\n// at CALL TIME (NativeAssertRecorder) and BUFFERED — concise title, real args,\n// callSource, the per-test testUid, and the preceding screenshot — but NOT\n// streamed, because streaming a neutral row mid-run would render every assert\n// green before its outcome is known. At test-end `captureNativeAssertions` reads\n// the pass/fail truth + verbose failure message and emits each row once,\n// positioned on its real execution window (the exporter re-sorts by the buffered\n// call timestamp). Correlating the recorded calls (not the raw results) also\n// excludes Nightwatch's implicit command-generated assertions (e.g.\n// waitForElementVisible's \"element was visible\" entry).\n//\n// Two outcome sources, in this order: the per-test results bag (`correlate`),\n// then the settlement of the call's own promise (`observedAssertOutcome`). The\n// cucumber runner has no results bag at all, so the settlement is its only\n// source; the describe/it interface has a bag but no per-assertion window.\n//\n// The plugin `afterEach` fires once per describe-suite (not per `it`), so the\n// finalize reads assertions from `results.testcases` (all tests) — see\n// gatherResultAssertions.\n\nimport logger from '@wdio/logger'\nimport {\n errorMessage,\n matcherAssertionToCommandLog,\n safeSerializeAssertArg,\n stripAnsi\n} from '@wdio/devtools-core'\nimport { assertTargetSelector } from './assertTarget.js'\nimport type { SessionCapturer } from '../session.js'\nimport type {\n AssertSettlement,\n CollapsedAssertResult,\n CommandLog,\n NativeAssertCall,\n NightwatchBrowser,\n NightwatchCurrentTest,\n ObservedAssertOutcome\n} from '../types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:nativeAssertions')\n\n/**\n * One entry Nightwatch pushes to `results.assertions` (from\n * `NightwatchAssertion.getAssertResult`, lib/assertion/assertion.js). Carries\n * only a human message — no method/args. `failure === false` is the reliable\n * pass signal; any truthy `failure` (message string, or `true`) means failed.\n */\ninterface NwAssertionEntry {\n message?: string\n fullMsg?: string\n failure?: string | boolean\n}\n\n/** One entry Nightwatch pushes to `results.commands` (lib/reporter/index.js\n * logCommandResult). For an assert/verify `name` is the namespaced method and\n * `startTime`/`endTime` are the real queue-execution window in ms\n * (treenode.js). Read only to position the finalized row on the timeline. */\ninterface NwCommandEntry {\n name?: string\n startTime?: number\n endTime?: number\n}\n\nconst ASSERT_CMD_RE = /^(assert|verify)\\.\\w+$/\n\n/** Real per-assertion execution windows, in call order, aligned to `count`\n * recorded calls. Nightwatch enqueues assertions synchronously (all at once)\n * but runs them later one at a time, so the enqueue timestamp clusters the\n * rows; this recovers each row's true execution time so the trace timeline\n * spreads them out. Returns `null` per slot when the executed-command count\n * doesn't line up (e.g. retries) — the enqueue timestamp is kept then. */\nfunction assertCommandTimings(\n commands: NwCommandEntry[],\n count: number\n): Array<{ startTime: number; endTime: number } | null> {\n const executed = commands.filter(\n (c) => typeof c?.name === 'string' && ASSERT_CMD_RE.test(c.name)\n )\n if (executed.length !== count) {\n return new Array(count).fill(null)\n }\n return executed.map((c) =>\n typeof c.startTime === 'number' && typeof c.endTime === 'number'\n ? { startTime: c.startTime, endTime: c.endTime }\n : null\n )\n}\n\n/** Nightwatch embeds the assertion arguments in the message text\n * (AssertionInstance.initialize → Logger.formatMessage), so a passing entry\n * for `titleContains('Example')` reads \"Testing if the page title contains\n * 'Example'\". Match a call to its result entry when every string/number arg\n * appears in the message. */\nfunction messageMatchesArgs(entry: NwAssertionEntry, args: unknown[]): boolean {\n const text = String(entry.fullMsg ?? entry.message ?? '')\n const literals = args.filter(\n (a): a is string | number => typeof a === 'string' || typeof a === 'number'\n )\n return literals.length > 0 && literals.every((a) => text.includes(String(a)))\n}\n\ninterface Outcome {\n passed: boolean\n message: string\n}\n\n/** Pass/fail read off the assertion's own promise — the only outcome evidence\n * cucumber has, since its client is built with no reporter so `results` is\n * never populated. A fulfilment of `undefined` stays neutral: that is an\n * assertion enqueued but never executed, not a pass. Semantics and measurements\n * in CLAUDE.md § Known debt. */\nexport function observedAssertOutcome({\n rejected,\n value,\n settledAt\n}: AssertSettlement): ObservedAssertOutcome | undefined {\n if (rejected || value instanceof Error) {\n return {\n passed: false,\n message: stripAnsi(errorMessage(value)).trim(),\n settledAt\n }\n }\n if (value === undefined) {\n return undefined\n }\n return { passed: true, message: '', settledAt }\n}\n\n/** Execution window recovered from the observed settlement: enqueue → settle.\n * The cucumber runner logs no commands, so `assertCommandTimings` yields\n * nothing there and the row would otherwise span a synthetic 1 ms. */\nfunction observedTiming(\n call: NativeAssertCall\n): { startTime: number; endTime: number } | null {\n return call.observed\n ? { startTime: call.timestamp, endTime: call.observed.settledAt }\n : null\n}\n\n/**\n * All assertion entries for the suite, in declaration order. Nightwatch's\n * plugin `afterEach` fires once per describe-suite (not per `it`), so the flat\n * `results.assertions` reflects only the last testcase; the full per-test\n * breakdown lives in `results.testcases[title].assertions`. Flattening every\n * testcase's entries lets each buffered call correlate to its own test's\n * outcome (without this, only the last test's asserts get a pass/fail and the\n * rest render neutral). Falls back to the flat list for single-test modules or\n * older Nightwatch that doesn't populate `testcases`.\n */\nfunction gatherResultAssertions(\n results:\n | {\n assertions?: NwAssertionEntry[]\n testcases?: Record<string, { assertions?: unknown[] }>\n }\n | undefined\n): NwAssertionEntry[] {\n const testcases = results?.testcases\n if (testcases && typeof testcases === 'object') {\n const all: NwAssertionEntry[] = []\n for (const tc of Object.values(testcases)) {\n if (Array.isArray(tc?.assertions)) {\n all.push(...(tc.assertions as NwAssertionEntry[]))\n }\n }\n if (all.length > 0) {\n return all\n }\n }\n return Array.isArray(results?.assertions) ? results.assertions : []\n}\n\n/**\n * Pair each recorded call with its `results.assertions` entry for pass/fail +\n * verbose message. Both lists are in call/execution order and each explicit\n * call produces exactly one assertion entry, so: match by args-in-message\n * first (most specific, skips interleaved implicit entries), then fall back to\n * the next unconsumed entry positionally. A call with no matching entry left\n * (never happens for a real assertion) is dropped (`null`).\n */\nfunction correlate(\n calls: NativeAssertCall[],\n assertions: NwAssertionEntry[]\n): Array<Outcome | null> {\n const consumed = new Array(assertions.length).fill(false)\n const toOutcome = (idx: number): Outcome => {\n consumed[idx] = true\n const entry = assertions[idx]\n return {\n passed: !entry.failure,\n message: stripAnsi(String(entry.fullMsg ?? entry.message ?? '')).trim()\n }\n }\n const matched = calls.map((call) => {\n const idx = assertions.findIndex(\n (entry, i) => !consumed[i] && messageMatchesArgs(entry, call.args)\n )\n return idx === -1 ? null : toOutcome(idx)\n })\n return matched.map((outcome) => {\n if (outcome) {\n return outcome\n }\n const idx = consumed.findIndex((used) => !used)\n return idx === -1 ? null : toOutcome(idx)\n })\n}\n\n/** Last already-resolved screenshot in the command log — the DOM the assertion\n * evaluated against (title/most asserts don't mutate it). Synchronous, so it's\n * usable from the call-time wrapper. */\nexport function latestResolvedScreenshot(\n capturer: SessionCapturer\n): string | null {\n for (let i = capturer.commandsLog.length - 1; i >= 0; i--) {\n const shot = capturer.commandsLog[i]?.screenshot\n if (shot) {\n return shot\n }\n }\n return null\n}\n\n/** Reuse the nearest preceding command's screenshot; if the fire-and-forget\n * capture hasn't resolved yet (race), fall back to a fresh end-of-test one. */\nasync function resolveAssertionScreenshot(\n capturer: SessionCapturer,\n browser: NightwatchBrowser\n): Promise<string | null> {\n return (\n latestResolvedScreenshot(capturer) ??\n (await capturer.takeScreenshotViaHttp(browser))\n )\n}\n\n/** `assert.titleContains('SOFT_FAIL_ME')` — the concise row label. Strings are\n * quoted, objects elided; never the verbose failure message. */\nfunction conciseTitle(call: NativeAssertCall): string {\n const preview = call.args\n .map((a) =>\n typeof a === 'string'\n ? `'${a}'`\n : a !== null && typeof a === 'object'\n ? '…'\n : String(a)\n )\n .join(', ')\n return `${call.prefix}.${call.method}(${preview})`\n}\n\n/**\n * Build the neutral \"pending\" row emitted the moment an assert/verify is\n * called — everything known at call time (concise title, real args, callSource,\n * screenshot) but NO result/error yet, so it renders neutral (not red/green)\n * and streams in like a normal in-flight command. `startTime`/`timestamp` are\n * the call time; the row is finalized in place later by\n * {@link captureNativeAssertions}.\n */\nexport function pendingAssertionCommand(\n call: NativeAssertCall,\n testUid: string | undefined,\n screenshot: string | null\n): CommandLog {\n const entry: CommandLog = {\n command: `${call.prefix}.${call.method}`,\n args: call.args.map(safeSerializeAssertArg),\n title: conciseTitle(call),\n timestamp: call.timestamp,\n startTime: call.timestamp\n }\n if (call.callSource) {\n entry.callSource = call.callSource\n }\n if (testUid) {\n entry.testUid = testUid\n }\n if (screenshot) {\n entry.screenshot = screenshot\n }\n // Only element assertions name a target; a title/url/value one gets none, so\n // the overlay never boxes an element the assertion wasn't about.\n const selector = assertTargetSelector(call.method, call.args)\n if (selector) {\n entry.selector = selector\n }\n return entry\n}\n\n/** Nightwatch failure messages end with `… but got: \"<actual>\"`. Pull out the\n * real observed value: Nightwatch passes only the EXPECTED as an arg, so the\n * actual lives in the message (and only on failure). Undefined when absent.\n * Uses indexOf + slice (no backtracking-prone regex) on the message tail. */\nfunction parseActualFromMessage(message: string): string | undefined {\n const marker = 'but got:'\n const idx = message.lastIndexOf(marker)\n if (idx === -1) {\n return undefined\n }\n let rest = message.slice(idx + marker.length).trim()\n rest = rest.replace(/ \\(\\d+ms\\)$/, '').trim() // drop trailing \"(123ms)\"\n rest = rest.replace(/^[\"']/, '').replace(/[\"']$/, '').trim() // strip quotes\n return rest || undefined\n}\n\n/** Build the collapsed `{passed, expected, actual?, message}` result core's\n * `buildAssertParams` prefers over the positional `[actual, expected]` arg\n * convention — which is wrong for Nightwatch, whose asserts pass only the\n * expected value (`titleContains('x')`), never the actual. */\nfunction collapsedAssertResult(\n call: NativeAssertCall,\n outcome: Outcome\n): CollapsedAssertResult {\n const result: CollapsedAssertResult = {\n passed: outcome.passed,\n expected: call.args.length <= 1 ? call.args[0] : call.args\n }\n // Omitted rather than empty for a pass observed off the settlement: the\n // promise carries no prose, and an empty message would render as a blank row.\n if (outcome.message) {\n result.message = outcome.message\n }\n const actual = outcome.passed\n ? undefined\n : parseActualFromMessage(outcome.message)\n if (actual !== undefined) {\n result.actual = actual\n }\n return result\n}\n\n/** Emit one assertion row at test-end: apply pass/fail + verbose error, a\n * screenshot, and its real execution window, then send it. Rows are NOT\n * streamed at call time, so this is the single emit (no neutral pending row). */\nfunction finalizeAssertionRow(\n capturer: SessionCapturer,\n call: NativeAssertCall,\n outcome: Outcome,\n timing: { startTime: number; endTime: number } | null,\n screenshot: string | null,\n testUid: string | undefined\n): void {\n const entry = call.entry!\n const finalized = matcherAssertionToCommandLog(\n {\n prefix: call.prefix,\n method: call.method,\n args: call.args,\n passed: outcome.passed,\n message: outcome.message || `${call.prefix}.${call.method} failed`,\n callSource: call.callSource,\n title: entry.title\n },\n testUid\n )\n // Collapsed object result (not the plain 'passed' string) so the trace's\n // action params show a true actual-vs-expected diff instead of mislabelling\n // Nightwatch's single expected-arg as the actual.\n entry.result = collapsedAssertResult(call, outcome)\n entry.error = finalized.error\n if (!entry.screenshot && screenshot) {\n entry.screenshot = screenshot\n }\n // Position the row on its REAL execution window (Nightwatch enqueues all\n // asserts at once, so the call-time timestamp clustered them). The row was\n // never streamed, so send it now — a single emit with the final outcome.\n if (timing) {\n entry.startTime = timing.startTime\n entry.timestamp =\n timing.endTime > timing.startTime ? timing.endTime : timing.startTime\n }\n capturer.captureAssertCommand(entry)\n log.info(`[assert] ${entry.title} → ${outcome.passed ? 'pass' : 'fail'}`)\n}\n\n/**\n * Emit the native assertion rows at test-end: correlate the recorded calls with\n * `results.assertions`, then send each row with pass/fail (`result`) + the\n * verbose failure message (`error`, failures only) + a screenshot + its real\n * execution window. Rows are buffered (not streamed) at call time — Nightwatch\n * has no per-assertion result hook, so streaming them during the run would show\n * every assert as a neutral (green) row before its outcome is known. A call with\n * no matching result is skipped (defensive).\n */\nexport async function captureNativeAssertions(\n capturer: SessionCapturer,\n browser: NightwatchBrowser,\n currentTest: NightwatchCurrentTest | undefined,\n testUid: string | undefined,\n calls: NativeAssertCall[]\n): Promise<void> {\n if (calls.length === 0) {\n return\n }\n // Boundary cast: `results` is Nightwatch's loosely-typed per-test bag; we read\n // only the assertions + commands arrays whose shapes are documented above.\n const results = currentTest?.results as\n | {\n assertions?: NwAssertionEntry[]\n commands?: NwCommandEntry[]\n testcases?: Record<string, { assertions?: unknown[] }>\n }\n | undefined\n const assertions = gatherResultAssertions(results)\n const outcomes = correlate(calls, assertions)\n const timings = assertCommandTimings(\n Array.isArray(results?.commands) ? results.commands : [],\n calls.length\n )\n // One shared screenshot for all rows — same DOM, ran consecutively.\n const screenshot = await resolveAssertionScreenshot(capturer, browser)\n\n calls.forEach((call, index) => {\n if (!call.entry) {\n return\n }\n // Results-bag correlation first (it also carries the verbose per-assertion\n // message, which a settlement can't supply for a pass); the observed\n // settlement is the fallback that makes cucumber rows coloured at all. The\n // timing comes from whichever source supplied the outcome — a correlated row\n // keeps its enqueue timestamp when the bag reports no window (the BDD\n // interface always does), which is what `sequence` ordering relies on.\n const correlated = outcomes[index]\n const outcome = correlated ?? call.observed\n if (outcome) {\n finalizeAssertionRow(\n capturer,\n call,\n outcome,\n correlated ? timings[index] : observedTiming(call),\n screenshot,\n testUid\n )\n return\n }\n // No execution outcome correlated to this call (defensive): emit the\n // buffered row in its neutral state so the assertion still appears — never\n // dropped, never mis-coloured as passed/failed. Rows are only buffered at\n // call time, so without this an uncorrelated assert would vanish.\n if (!call.entry.screenshot && screenshot) {\n call.entry.screenshot = screenshot\n }\n capturer.captureAssertCommand(call.entry)\n })\n}\n","/**\n * Call-time recorder for explicit `browser.assert.*` / `browser.verify.*`.\n *\n * Extracted from BrowserProxy: the recording Proxy, the per-test buffer and the\n * settlement observation are one concern (getting a native assertion's args,\n * source location and outcome), separate from driver-command interception.\n * `nativeAssertions.ts` consumes the drained buffer at test-end.\n */\n\nimport {\n observedAssertOutcome,\n latestResolvedScreenshot,\n pendingAssertionCommand\n} from './nativeAssertions.js'\nimport { getCallSourceFromStack } from './utils.js'\nimport type { SessionCapturer } from '../session.js'\nimport type { NativeAssertCall, NightwatchBrowser } from '../types.js'\n\nexport class NativeAssertRecorder {\n /**\n * Per-test-unit buffer of recorded calls. Nightwatch exposes no per-assertion\n * hook and its test-end results carry no source location for passing\n * assertions, so call-time capture is the only way to get real args +\n * callSource. Drained once per test unit (`afterEach`, or the cucumber\n * scenario's pre-quit hook) — never per step.\n */\n private calls: NativeAssertCall[] = []\n\n constructor(\n private readonly capturer: SessionCapturer,\n private getCurrentTest: () => { uid?: string } | null,\n private nextSequence: () => number\n ) {}\n\n clear(): void {\n this.calls = []\n }\n\n /** Hand off this test's recorded calls and clear the buffer so the next test\n * unit starts fresh. */\n drain(): NativeAssertCall[] {\n const calls = this.calls\n this.calls = []\n return calls\n }\n\n /**\n * Replace `browser.assert` / `browser.verify` (both are Nightwatch Proxies\n * whose `get` returns a fresh function per access) with a recording Proxy:\n * on each method call it captures `{prefix, method, args, callSource}` from a\n * user-code frame, buffers a pending row, then delegates to the ORIGINAL\n * namespace method so Nightwatch's queue, chaining, and abortOnFailure\n * semantics (assert aborts, verify does not) are byte-for-byte unchanged.\n * Called once per browser.\n */\n wrapNamespaces(browser: NightwatchBrowser): void {\n // Cast once: the assert/verify namespaces aren't on the public type; each\n // is a dynamic method bag reached by property name.\n const b = browser as unknown as Record<string, unknown>\n ;(['assert', 'verify'] as const).forEach((prefix) => {\n const original = b[prefix]\n if (!original || typeof original !== 'object') {\n return\n }\n b[prefix] = this.namespaceProxy(original, prefix, [])\n })\n }\n\n /**\n * Recording Proxy over one assert/verify namespace. A function property\n * becomes a call-time recorder keyed by its full dotted path\n * (`titleContains`, `not.titleContains`); a nested namespace object recurses\n * through the SAME wrapper — Nightwatch exposes `assert.not` as its own Proxy,\n * so negated asserts are recorded via the identical mechanism as positive\n * ones instead of a parallel path. Non-method, non-namespace props pass\n * through untouched.\n */\n private namespaceProxy(\n target: object,\n prefix: 'assert' | 'verify',\n path: readonly string[]\n ): object {\n return new Proxy(target, {\n get: (t, name, receiver) => {\n const orig = Reflect.get(t, name, receiver)\n if (typeof name !== 'string') {\n return orig\n }\n if (orig !== null && typeof orig === 'object') {\n return this.namespaceProxy(orig, prefix, [...path, name])\n }\n if (typeof orig !== 'function') {\n return orig\n }\n return (...args: unknown[]) => {\n const callInfo = getCallSourceFromStack()\n const call =\n callInfo.filePath === undefined\n ? undefined\n : this.buffer({\n prefix,\n method: [...path, name].join('.'),\n args,\n callSource: callInfo.callSource,\n timestamp: Date.now()\n })\n const result = (orig as (...a: unknown[]) => unknown)(...args)\n if (call) {\n this.observe(call, result)\n }\n return result\n }\n }\n })\n }\n\n /** Buffer an explicit assert/verify call at invocation time (prebuilding its\n * row with args/callSource/screenshot), but do NOT stream it yet. Nightwatch\n * exposes no per-assertion result hook, so streaming here would show every\n * assert as a neutral (green) row while the test is still Running — before\n * its pass/fail is known. `captureNativeAssertions` emits the rows at\n * test-end with real outcomes + execution timing instead. */\n private buffer(call: NativeAssertCall): NativeAssertCall {\n const entry = pendingAssertionCommand(\n call,\n this.getCurrentTest()?.uid,\n latestResolvedScreenshot(this.capturer)\n )\n entry.sequence = this.nextSequence()\n call.entry = entry\n this.calls.push(call)\n return call\n }\n\n /**\n * Watch the assertion's OWN returned promise for its outcome — see\n * `observedAssertOutcome` for the settlement semantics and why this is the\n * cucumber runner's only pass/fail source. `result` is handed back to the\n * caller untouched; this only adds a second handler, so Nightwatch's own\n * chaining and rejection propagation are unchanged. A non-thenable result\n * (Nightwatch's synchronous `api` return, when commands aren't ES6-async)\n * leaves the call unobserved and the results-bag path supplies the outcome.\n */\n private observe(call: NativeAssertCall, result: unknown): void {\n const thenable = result as { then?: unknown } | null | undefined\n if (!thenable || typeof thenable.then !== 'function') {\n return\n }\n void (result as Promise<unknown>).then(\n (value) => {\n call.observed = observedAssertOutcome({\n rejected: false,\n value,\n settledAt: Date.now()\n })\n },\n (reason) => {\n call.observed = observedAssertOutcome({\n rejected: true,\n value: reason,\n settledAt: Date.now()\n })\n }\n )\n }\n}\n","/**\n * Binds the screencast recorder to a browser session, and rebinds it — and\n * nothing else — when that session is replaced mid-run.\n *\n * Nightwatch raises no session event, and `end` sits in\n * INTERNAL_COMMANDS_TO_IGNORE, so the command proxy never sees the call that\n * destroys the session. The BDD `describe/it` interface compounds it: the\n * plugin's `beforeEach` fires once per MODULE, so `ensureSessionInitialized` —\n * the only place that detects a session change — is never re-entered for a\n * `browser.end()` between `it`s. A command executing under a different\n * `sessionId` is the sole evidence the session was replaced.\n *\n * Rotation touches the recorder and nothing else — the run's SessionCapturer\n * outlives every session it is pointed at. It leaves `lastSessionId` alone\n * because that field is `ensureSessionInitialized`'s own bookkeeping. The other\n * half of a session rebind, re-arming BiDi and the collector preload, hangs off\n * the same detection in `session-init.ts` `rearmCaptureForSession`. Both halves\n * are self-guarding, so `ensureSessionInitialized` reuses them for the session\n * replacement it detects itself.\n */\n\nimport logger from '@wdio/logger'\nimport { errorMessage } from '@wdio/devtools-core'\nimport { ScreencastRecorder } from './screencast.js'\nimport type { SessionInitCtx } from './session-init.js'\nimport type { NightwatchBrowser } from './types.js'\n\nconst log = logger('@wdio/nightwatch-devtools:screencast-session')\n\n/**\n * Start a fresh recorder for `sessionId` — every session gets its own, matching\n * the WDIO service. Polling mode only (Nightwatch exposes no stable CDP escape\n * hatch). Idempotent, so bringup and rotation share one path.\n */\nexport async function startScreencast(\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\n/**\n * Whether the recorder is bound to a session the browser has already left.\n * Requires BOTH a live recorder and a live session id: starting one from\n * nothing would strand a poll interval past the plugin's own teardown, which\n * finalizes the recorder before its last browser calls.\n */\nexport function needsScreencastRotation(\n ctx: Pick<SessionInitCtx, 'screencastRecorder' | 'screencastSessionId'>,\n sessionId: string | undefined\n): boolean {\n return (\n ctx.screencastRecorder !== undefined &&\n sessionId !== undefined &&\n ctx.screencastSessionId !== sessionId\n )\n}\n\n/**\n * Observe one command. Rotation is fire-and-forget — a command must not wait on\n * a video encode — and parked on `ctx.screencastRotation`, which both latches\n * out the flood of commands arriving before it completes and gives teardown\n * something to await, so a rotation cannot start a recorder after the plugin\n * has already finalized.\n */\nexport function rotateScreencastForSession(\n ctx: SessionInitCtx,\n browser: NightwatchBrowser\n): void {\n if (ctx.screencastRotation || !ctx.screencastOptions.enabled) {\n return\n }\n const sessionId = browser.sessionId ?? undefined\n if (!needsScreencastRotation(ctx, sessionId)) {\n return\n }\n log.info(`🎬 Session replaced — rotating screencast to ${sessionId}`)\n ctx.screencastRotation = ctx\n .finalizeCurrentScreencast()\n .then(() => startScreencast(ctx, browser, sessionId))\n .catch((err) =>\n log.warn(`Screencast rotation failed: ${errorMessage(err)}`)\n )\n .finally(() => {\n ctx.screencastRotation = undefined\n })\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","/**\n * The plugin's screencast seam: what a run's mode and filmstrip/video options\n * imply for the recorder, plus the per-run frame buffer the trace filmstrip and\n * the per-test video slices are cut from.\n *\n * The recorder itself is `screencast.ts`, and binding it to a session — or\n * rotating it when the session is replaced — is `screencast-session.ts`. This is\n * the plugin-level state those two hang off, split out of `index.ts` so the\n * plugin keeps only its accessor bag and thin delegators.\n */\n\nimport { accumulatedScreencastFrames } from '@wdio/devtools-core'\nimport { SCREENCAST_DEFAULTS } from '@wdio/devtools-shared'\nimport { finalizeCurrentScreencast } from './session-init.js'\nimport type { SessionInitCtx } from './session-init.js'\nimport type { ScreencastRecorder } from './screencast.js'\nimport type {\n DevToolsMode,\n ScreencastFrame,\n ScreencastOptions,\n TraceVideoPolicy\n} from './types.js'\n\n/** The already-defaulted options the recorder decision is made from — read the\n * resolved `filmstrip`/`video`, never the raw `options.filmstrip`, or a\n * default-on filmstrip leaves the recorder inert while the plugin's own options\n * claim it is running. */\nexport interface ScreencastResolutionInput {\n mode: DevToolsMode\n filmstrip: boolean\n video: TraceVideoPolicy\n screencast: ScreencastOptions | undefined\n}\n\nexport interface ResolvedScreencast {\n /** What the plugin exposes as `options.screencast`. */\n options: ScreencastOptions\n /** Config warning to log, when the mode makes the option meaningless. */\n warning?: string\n}\n\n/**\n * Whether this run records a screencast, and under which options. Filmstrip OR a\n * produce-only per-test video drives the recorder in trace mode; bare screencast\n * with neither is a live-mode feature and is dropped there.\n */\nexport function resolveScreencastOptions(\n input: ScreencastResolutionInput\n): ResolvedScreencast {\n const screencast = input.screencast ?? {}\n if (input.mode !== 'trace') {\n return { options: screencast }\n }\n if (input.filmstrip || input.video !== 'off') {\n return { options: { ...screencast, enabled: true } }\n }\n if (screencast.enabled === true) {\n return {\n options: {},\n warning: 'trace mode: ignoring screencast option (live-mode feature)'\n }\n }\n return { options: screencast }\n}\n\n/**\n * One run's screencast state: the live recorder and the session it is bound to,\n * an in-flight rotation, and the frames drained from recorders already finalized.\n */\nexport class PluginScreencast {\n /** Recorder options with the shared defaults applied. */\n readonly options: ScreencastOptions\n recorder?: ScreencastRecorder\n sessionId?: string\n /** In-flight recorder rotation — see `screencast-session.ts`. */\n rotation?: Promise<void>\n\n // Snapshotted before each recorder is nulled, so the export isn't blank.\n #accumulated: ScreencastFrame[] = []\n readonly #keepFrames: boolean\n\n /** `keepFrames` when the trace filmstrip or a per-test video will consume\n * them; without either, a finalized recorder's frames are of no further use. */\n constructor(options: ScreencastOptions, keepFrames: boolean) {\n this.options = { ...SCREENCAST_DEFAULTS, ...options }\n this.#keepFrames = keepFrames\n }\n\n /** Every frame this run has produced — the finalized recorders' plus the live\n * one's, so a mid-run slice flush (which fires before the recorder is\n * drained) isn't blank. */\n get frames(): ScreencastFrame[] {\n return accumulatedScreencastFrames(this.#accumulated, this.recorder)\n }\n\n /** Drain the live recorder into the run buffer, then stop and clear it. The\n * drain has to precede the stop: `finalizeCurrentScreencast` nulls the\n * recorder, and with it the only reference to its frames. */\n async finalize(ctx: SessionInitCtx): Promise<void> {\n if (this.#keepFrames && this.recorder) {\n this.#accumulated.push(...this.recorder.frames)\n }\n await finalizeCurrentScreencast(ctx)\n }\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 {\n WS_SCOPE,\n type CucumberPickle,\n type CucumberPickleStep\n} from '@wdio/devtools-shared'\n\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 { captureNativeAssertions } from './helpers/nativeAssertions.js'\nimport { scanFeatureFile } from './helpers/featureFileScan.js'\nimport { parseCucumberScenario } from './helpers/utils.js'\nimport {\n recordTestSliceBoundary,\n flushTestSlice,\n type TestSliceCtx\n} from './trace-slices.js'\n\nconst log = logger('@wdio/nightwatch-devtools:cucumber')\n\n/** Cucumber's result shape varies across major versions; we pin only the\n * status field we read. Pickle shapes come from shared. */\nexport interface CucumberResult {\n status?: string\n}\n\nexport interface CucumberLifecycleCtx extends TestSliceCtx {\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 recordAttempt(uid: string, specFile?: string): number\n recordOutcome(uid: string, state: TestStats['state']): void\n emitTestArtifacts(uid: string | undefined, failed: boolean): Promise<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\nfunction captureFeatureSources(\n ctx: CucumberLifecycleCtx,\n paths: string[]\n): void {\n for (const p of paths) {\n ctx.sessionCapturer.captureSource(p).catch(() => {})\n }\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 captureFeatureSources(ctx, capturedPaths)\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 recordAttempt: (uid, specFile) => ctx.recordAttempt(uid, specFile)\n })\n attachScenarioToFeature(ctx, featureSuite, scenarioSuite)\n // The scenario is the `test` unit; its steps are the leaf metadata entries.\n recordTestSliceBoundary(ctx, featureUri, scenarioSuite.uid)\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 // Stamp this attempt's real outcome so spec/session retention doesn't\n // collapse to the retry-stable suite's last-overwritten state.\n ctx.recordOutcome(scenario.uid, scenarioState)\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 // The trace capture, slice flush, and per-test artifacts need a live\n // WebDriver session — by this hook (cucumber After order:-1) the browser is\n // already quit. They run in captureCucumberScenarioBeforeQuit (order:1000,\n // pre-quit); this hook keeps only the settled-outcome bookkeeping.\n } catch (err) {\n log.error(`Failed to finalize Cucumber scenario: ${errorMessage(err)}`)\n }\n}\n\n/**\n * Live-session work for a cucumber scenario — runs at the cucumber `After`\n * `order:1000` hook, BEFORE Nightwatch quits the browser (its quit hook is\n * `order:0`; `finalizeCucumberScenario` is `order:-1`, after quit, so it reads\n * the settled outcome). Trace capture, the slice flush, and per-test artifacts\n * all need a live WebDriver session — at finalize the session is gone and the\n * flush bails, writing no zip. Trace test-state may be pre-outcome for a\n * scenario that fails on a Nightwatch assertion (see CLAUDE.md known debt).\n */\nexport async function captureCucumberScenarioBeforeQuit(\n ctx: CucumberLifecycleCtx,\n browser: NightwatchBrowser,\n result: CucumberResult\n): Promise<void> {\n const scenario = ctx.getCurrentScenarioSuite()\n try {\n // Emit native asserts (assert.titleContains, …) while the session is live —\n // Nightwatch's afterEach path skips the cucumber runner, so this is the only\n // place scenario asserts get captured. drainNativeAssertCalls() is empty when\n // captureAssertions is off. `currentTest` is undefined because a cucumber run\n // installs Nightwatch's no-op SimplifiedReporter and never fills a results\n // bag; each row's pass/fail comes from its own settled promise instead\n // (`observedAssertOutcome`).\n const assertCalls = ctx.browserProxy?.drainNativeAssertCalls() ?? []\n if (assertCalls.length > 0) {\n await captureNativeAssertions(\n ctx.sessionCapturer,\n browser,\n undefined,\n scenario?.uid,\n assertCalls\n )\n }\n // Anchored: a scenario ending on a navigation leaves the destination's\n // async initial anchor unrun, so its DOM would be absent from the slice.\n await ctx.sessionCapturer.captureTrace(browser, true)\n flushTestSlice(ctx)\n await ctx.emitTestArtifacts(\n scenario?.uid,\n cucumberResultToTestState(result) === TEST_STATE.FAILED\n )\n } catch (err) {\n log.error(\n `Failed to capture Cucumber scenario before quit: ${errorMessage(err)}`\n )\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 /** Records the scenario start under its (retry-stable) uid + feature file and\n * returns the 0-based attempt number stamped on every step. Omitted → 0. */\n recordAttempt?: (scenarioUid: string, specFile?: string) => number\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 attempt: number,\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 // Scope by the scenario uid (which carries the scenario line) so identical\n // step text in sibling scenarios and outline example rows stays distinct.\n uid: deterministicUid(featureUri, `step:${scenarioUid}:${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 // Scenario-level attempt from the tracker (0 first run, +1 per retry);\n // flows to TestOutcome.attempt via collectSuiteTestMetadata.\n retries: attempt,\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. The\n // scenario line disambiguates outline example rows that share a name.\n const scenarioUid = deterministicUid(\n featureUri,\n `scenario:${scenarioName}:${scenarioLine}`\n )\n const attempt = input.recordAttempt?.(scenarioUid, featureUri) ?? 0\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(\n buildScenarioStepTest(input, scenarioUid, attempt, i)\n )\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 * Trace-slice boundary recording for `spec` and `test` granularity.\n *\n * `spec` slices are recorded/flushed at each spec-file transition (unchanged\n * from the original inline `beforeEach` logic). For `test`, Nightwatch builds\n * test outcomes into the suite tree in place — a retry overwrites the previous\n * attempt's state (regular tests reuse the same test object; cucumber replaces\n * the scenario suite). So each attempt's slice is flushed at its own\n * test/scenario END, before the next attempt can overwrite the outcome the\n * flush reads. Regular tests drive this from test-lifecycle, cucumber scenarios\n * from cucumber-lifecycle; both share this module.\n */\n\nimport {\n findFlushableRange,\n recordSliceBoundary,\n recordSpecBoundary,\n type SpecBoundaryContext,\n type SpecRange,\n type TraceArtifact\n} from '@wdio/devtools-core'\nimport type { SessionCapturer } from './session.js'\nimport type { TraceGranularity } from './types.js'\n\nexport interface TestSliceCtx {\n readonly sessionCapturer: SessionCapturer\n readonly traceMode: boolean\n readonly traceGranularity: TraceGranularity\n readonly specRanges: SpecRange[]\n readonly flushedSpecs: Set<string>\n flushTraceRange(range: SpecRange): Promise<TraceArtifact | undefined>\n}\n\nfunction sliceActive(ctx: TestSliceCtx): boolean {\n return ctx.traceMode && ctx.traceGranularity === 'test'\n}\n\nfunction boundaryContext(ctx: TestSliceCtx): SpecBoundaryContext {\n return {\n specRanges: ctx.specRanges,\n flushedSpecs: ctx.flushedSpecs,\n capturer: ctx.sessionCapturer\n }\n}\n\nfunction flushPrevious(ctx: TestSliceCtx, prevRange: SpecRange | null): void {\n if (!prevRange) {\n return\n }\n // flushTraceRange (→ core flushRangeLogged) logs+swallows on failure.\n void ctx.flushTraceRange(prevRange)\n}\n\n/**\n * Record a spec-file boundary at test/scenario start and eagerly flush the\n * previous spec's slice. No-op for `session`/`test` granularity (core returns\n * null). Preserves the original `beforeEach` behavior verbatim.\n */\nexport function recordSpecSliceBoundary(\n ctx: TestSliceCtx,\n specFile: string\n): void {\n const prevRange = recordSpecBoundary(\n boundaryContext(ctx),\n specFile,\n ctx.traceGranularity\n )\n flushPrevious(ctx, prevRange)\n}\n\n/**\n * Record a per-test slice boundary at test/scenario start. No-op outside trace\n * mode + `test` granularity. Retries push a distinct range (core keys a repeat\n * `${testUid}-retry${n}`), so each attempt becomes its own artifact.\n */\nexport function recordTestSliceBoundary(\n ctx: TestSliceCtx,\n specFile: string,\n testUid: string\n): void {\n if (!sliceActive(ctx)) {\n return\n }\n recordSliceBoundary(boundaryContext(ctx), 'test', specFile, testUid)\n}\n\n/**\n * Flush the current test's slice at test/scenario end — before a retry can\n * overwrite its outcome in the suite tree. Fire-and-forget; the finalize pass\n * is the safety net for any range this misses. No-op outside trace + `test`.\n */\nexport function flushTestSlice(ctx: TestSliceCtx): void {\n if (!sliceActive(ctx)) {\n return\n }\n const range = findFlushableRange(ctx.specRanges)\n if (!range) {\n return\n }\n // flushTraceRange (→ core flushRangeLogged) logs+swallows on failure.\n void ctx.flushTraceRange(range)\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 { 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 { matchTestName } from './helpers/runningTest.js'\nimport { extractTestMetadata, determineTestState } from './helpers/utils.js'\nimport { recordTestSliceBoundary, type TestSliceCtx } from './trace-slices.js'\n\nconst log = logger('@wdio/nightwatch-devtools:test-lifecycle')\n\nexport interface TestLifecycleCtx extends TestSliceCtx {\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 recordAttempt(uid: string, specFile?: string): number\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 return (\n matchTestName(currentTest?.name, testNames) ||\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 specFile: string | null\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 // Nightwatch has no per-test retry index; the tracker is the retry signal.\n test.retries = ctx.recordAttempt(test.uid, specFile ?? undefined)\n if (specFile) {\n recordTestSliceBoundary(ctx, specFile, test.uid)\n }\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.resetTestTracking()\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"],"mappings":"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","names":["xpath","xpath","path","xpath","resolve","start","fs","fs","path","fs","path","fs","path","fs","path","require","resolve","fs","path","fs","createRequire","require","createRequire","log","path","log","fs","path","createHash","resolve","resolve","errorMessage","request","path","fs","path","path","start","resolve","path","fs","path","createRequire","require","createRequire","path","fs","resolve","log","fs","os","path","log","path","fs","os","fs","path","fs","fs","resolve","fs","logger","fs","path","os","logger","fs","path","parseStackTrace","generateStableUid","parseStackTrace","log","logger","resolve","logger","logger","logger","log","logger","path","resolve","captureActionSnapshot","log","logger","captureActionSnapshot","resolve","logger","log","logger","generateStableUid","generateStableUid","logger","logger","log","logger","collapsedAssertResult","path","log","logger","path","logger","logger","log","logger","log","logger","log","logger","resolve","logger","log","logger","logger","fs","path","log","logger","logger","fs","path","path","fs","logger","log","logger","resolve","log","logger","resolve","log","logger"]}