import { type BrowserAssetName } from "../runtime/browsers.js"; import { appSurfacePreflight, type AppSessionState } from "./tests/appSurface.js"; import { type ActiveSurfaceTracker } from "./tests/activeSurface.js"; import { type WarmPlanDeps, type WarmOutcome } from "./warmPhase.js"; import { acquireDevice, type DeviceRegistry } from "./tests/androidEmulator.js"; export { runSpecs, runViaApi, getRunner, ensureChromeAvailable, ensureContextBrowserInstalled, combinationKey, warmUpDecision, selectWarmUpTargets, getDriverCapabilities, withChromedriverPort, getDefaultBrowser, buildFallbackCandidates, driverSkipDiagnostic, resolveBrowserFallbackPolicy, resolveRetryPolicy, runContextWithRetries, shouldRepairBeforeFallback, isSupportedContext, contextRequirementsSkipMessage, resolveAutoScreenshot, resolveAutoRecord, buildAutoRecordStep, specIsRouted, killTree, jobDisplayResources, buildWarmPlanDeps, warmBrowserInstall, prefetchMobileChromedriver, appiumIsReady, }; declare function killTree(pid?: number, timeoutMs?: number): Promise; /** * Stable identity for a "context combination" — the platform + browser pairing * that determines whether a driver session can be created. The runner memoizes * warm-up outcomes by this key so a combination that fails to start once isn't * re-attempted (with its slow driverStart backoff) for every later context. * headless is intentionally excluded: headed/headless are two attempts at the * same combination (the loop retries headless on failure), not distinct ones. * `webkit` is normalized to `safari` so the key matches getAvailableApps naming. */ declare function combinationKey(context: any): string; /** * Decide whether a context combination should be attempted or skipped, given * its prior warm-up outcome in this run. Pure so the memoization branching is * unit-testable without spinning up Appium. A previously-failed combination is * skipped outright; everything else is attempted (and its outcome recorded by * the caller). */ declare function warmUpDecision(prev: "ok" | "failed" | undefined): "attempt" | "skip"; /** * Bind the real selection predicates for the warm-phase planner * (planWarmTasks in warmPhase.ts). The planner takes these as an injected * bag because most of them live in this module — importing them from * warmPhase.ts would create a tests.ts ⇄ warmPhase.ts cycle — and because * the bag keeps the planner hermetically unit-testable. Exported so planner * tests exercise production selection logic, not stand-ins. */ declare function buildWarmPlanDeps(): WarmPlanDeps; declare function getDriverCapabilities({ runnerDetails, name, options }: { runnerDetails: any; name: any; options: any; }): any; declare function withChromedriverPort(capabilities: any, port: number): any; declare function jobDisplayResources(job: any, ctx: { platform: string; xvfbAvailable: boolean; allowOverlappingCaptures?: boolean; runHasDisplayRecording: boolean; }): string[]; declare function isSupportedContext({ context, apps, platform }: { context: any; apps: any[]; platform: any; }): boolean; export declare function browserJobCount(jobs: any[]): number; declare function contextRequirementsSkipMessage({ context, deps, }: { context: any; deps?: any; }): string | null; export declare function collectDeviceDescriptors(context: any): any[]; declare function getDefaultBrowser({ runnerDetails }: { runnerDetails: any; }): any; /** * Build the ordered list of browser engines to attempt for a context — the * heart of the any-browser → any-available-browser fallback. The requested * engine is tried first (when it's actually available); then, when the * `browserFallback` policy permits, every *other* available engine follows in * a stable preference order. Every returned name maps to an available engine — * the requested name is preserved as authored (so `webkit` can be returned even * though `availableApps` lists it as `safari`), while fallback names come * straight from `availableApps`; `webkit` is normalized to `safari` only for * the availability lookup and dedupe. getDriverCapabilities accepts both * aliases, so either resolves to real binary/driver paths. * * Policy: * - "auto" → fall back for both auto-selected and explicitly pinned browsers. * - "explicit" → fall back only when the browser was auto-selected (not pinned). * - "off" → never fall back; only the requested engine (if available). * * Pure and exported so the precedence is unit-testable without a driver. */ declare function buildFallbackCandidates({ requestedName, explicit, policy, availableApps, }: { requestedName: string; explicit: boolean; policy: string; availableApps: any[]; }): string[]; /** * Resolve the effective `browserFallback` policy for a context. A context-level * value (authored on the `runOn` entry) overrides the config-level value, which * itself defaults to `auto`. Pure and exported so the precedence is testable. */ declare function resolveBrowserFallbackPolicy({ context, config, }: { context: any; config: any; }): string; declare function resolveRetryPolicy({ context, config, }: { context: any; config: any; }): number; /** * Whether to attempt a driver repair before falling back away from a browser * whose session just failed to start. We only repair the *requested* engine * (so a fallback substitute that fails isn't itself repaired), only when it has * installable driver assets (Safari ships with the OS — nothing to repair), and * only once per browser per run (a prior attempt already recorded an outcome in * `installAttempts`). This is what keeps a present-but-broken driver from * causing an unnecessary fallback when a reinstall would have fixed it. Pure * and exported so the decision is unit-testable. */ declare function shouldRepairBeforeFallback({ candidateName, requestedName, installAttempts, }: { candidateName: string; requestedName: string; installAttempts: Map; }): boolean; /** * The diagnostic message recorded when no engine could start a session. Names * the requested engine and whether a cross-engine fallback was even attempted, * so a present-but-broken driver reads as actionable rather than a generic * "Failed to start context" skip. */ declare function driverSkipDiagnostic({ requestedName, platform, platformMatches, attemptedFallback, lastError, }: { requestedName: string; platform: string; platformMatches: boolean; attemptedFallback: boolean; lastError?: string; }): string; declare function runViaApi({ resolvedTests, apiKey, config }: { resolvedTests: any; apiKey: any; config?: any; }): Promise; /** * Orchestrates execution of resolved test specifications and returns a hierarchical run report. * * Flattens every context across all specs and tests into one job list and runs it through a * worker pool sized by config.concurrentRunners (default 1 = sequential). Conditionally starts * Appium and browser drivers, applies viewport/window sizing, handles unsafe-step policies and * recording, then rolls per-step, per-context, per-test, and per-spec results up in a * deterministic post-pass. Report order always matches input order. * * @param {Object} resolvedTests - Resolved test bundle containing configuration and specs to run. * @param {Object} resolvedTests.config - Runner configuration used during execution. * @param {Array} resolvedTests.specs - Array of spec objects to execute. * @returns {Object} A report object summarizing results with structure: * { * summary: { specs: {...}, tests: {...}, contexts: {...}, steps: {...} }, * specs: [ { specId, description, contentPath, result, tests: [ { testId, description, contentPath, result, contexts: [ { platform, browser, result, steps: [...] } ] } ] } ] * } */ declare function runSpecs({ resolvedTests }: { resolvedTests: any; }): Promise; /** * Pick which contexts warmUpContexts should warm up: one representative per * unique platform::browser combination among the driver-required jobs. Applies * the same platform default and default-browser resolution runContext uses, so * the combination keys it produces match the ones runContext looks up in the * pool. Non-driver and browserless contexts are excluded. Mutates * context.platform / context.browser in place — idempotent, since runContext * applies the identical defaults. Pure (no I/O) so the selection + de-dup + * normalization logic is unit-testable without Appium. */ declare function selectWarmUpTargets(jobs: any[], runnerDetails: any): Array<{ context: any; combo: string; }>; /** * On-demand browser install + first-attempt re-detect — the install half of * warmUpContexts, extracted so the warm phase's browser-install task and the * session-probe loop share ONE implementation of the mirror contract: the * `installAttempts` / `runnerDetails.availableApps` state left behind is * exactly what the first same-browser consuming context would have produced * serially (no more, no less), so every later gate collapses to a cache hit. * Deps are injected for hermetic tests; production callers use the defaults. */ declare function warmBrowserInstall({ browserName, config, runnerDetails, installAttempts, deps, }: { browserName: string | undefined; config: any; runnerDetails: any; installAttempts: Map; deps?: { ensureBrowser?: (asset: any, options: any) => Promise; clearAppCache?: (config: any) => void; getAvailableApps?: (args: { config: any; }) => Promise; }; }): Promise<{ outcome: WarmOutcome; note?: string; }>; /** * Pre-pay the on-device chromedriver download for android mobile-web: the * UiAutomator2 server only fetches a chromedriver matching the device's * Chrome at SESSION creation, so this task awaits the device (the one warm * task that blocks on readiness), opens a disposable mobile-web session on a * dedicated short-lived Appium server with the scoped autodownload feature — * the exact shape runContext's mobile-web branch uses — and tears both down. * The downloaded chromedriver lands in the shared cache, so the first real * session skips the download. Because the warm phase is awaited before * Phase 2 dispatch, this throwaway session can never overlap the first real * session on the same device. A throw is the executor's problem (recorded * as failed, run proceeds). Effects are injected for hermetic tests. */ declare function prefetchMobileChromedriver({ config, desc, deviceRegistry, getAndroidEnv, deps, }: { config: any; desc: any; deviceRegistry: DeviceRegistry; getAndroidEnv: () => Promise<{ sdkRoot: string; deviceDeps: any; } | null>; deps?: { appSurfacePreflight?: typeof appSurfacePreflight; acquireDevice?: typeof acquireDevice; startAppiumServer?: typeof startAppiumServer; driverStart?: typeof driverStart; killTree?: typeof killTree; acquireEmulatorLease?: () => Promise<() => void>; }; }): Promise<{ outcome: WarmOutcome; note?: string; }>; /** * Pure predicate: does this spec carry TEST-level routing? True iff ANY of its * tests has a non-empty `onPass`/`onFail`/`onWarning`/`onSkip` array. This is * the switch between the runner's two execution paths: * - false (every spec today) -> the unchanged flat concurrent job pool; the * report is byte-identical to the pre-routing runner. * - true -> the sequential `runRoutedSpec` sequencer, which evaluates * test-level routing between tests. * * Step-level handlers (a step's own on*) and a guard `if` do NOT count — those * are orthogonal features handled inside `runContext` and the guard preflight. * An empty handler array (`onFail: []`) is "no routing" and stays on the flat * path. Defensive against a missing/empty `tests` array. */ declare function specIsRouted(spec: any): boolean; declare function resolveAutoScreenshot({ config, spec, test, }: { config: any; spec: any; test: any; }): boolean; declare function resolveAutoRecord({ config, spec, test, }: { config: any; spec: any; test: any; }): boolean; declare function buildAutoRecordStep({ config, spec, test, context, }: { config: any; spec: any; test: any; context: any; }): any | null; declare function runContextWithRetries(args: any, runContextFn?: (a: any) => Promise, delayMs?: (attempt: number) => number): Promise; declare function runStep({ config, context, step, driver, metaValues, options, processRegistry, appSession, surfaceTracker, }: { config?: any; context?: any; step: any; driver: any; metaValues?: any; options?: any; processRegistry?: Map; appSession?: AppSessionState; surfaceTracker?: ActiveSurfaceTracker; }): Promise; declare function startAppiumServer(appiumEntry: string, config: any, display?: string, extraEnv?: Record, extraArgs?: string[]): Promise<{ port: number; process: any; display?: string; }>; declare function appiumIsReady(port: number, timeoutMs?: number, deps?: { probe?: (port: number) => Promise; sleep?: (ms: number) => Promise; }): Promise; declare function driverStart(capabilities: any, port: number, maxAttempts?: number, ctx?: { cacheDir?: string; }): Promise; /** * Resolve the available-apps list with Chrome guaranteed present, lazy- * installing the browser runtime on a miss before giving up. This is the * runtime counterpart to the runTests pre-flight: it runs regardless of * DOC_DETECTIVE_AUTOINSTALL (that env var only governs the *eager* postinstall * download — first use should still self-provision). A provisioning failure * (e.g. offline) is swallowed so the caller sees the clear "not available" * error rather than a raw npm/network stack. Deps are injected for testing. * * @returns the available-apps array, with a chrome entry present. * @throws if chrome is still unavailable after a provisioning attempt. */ declare function ensureChromeAvailable(config: any, deps: { detect: (config: any) => Promise; provision: (config: any) => Promise; invalidate: (config: any) => void; log?: (config: any, level: string, msg: string) => void; }): Promise; /** * On-demand, per-context browser/driver install used by the runner when a * context's browser isn't yet available (e.g. Firefox declared but geckodriver * missing). Attempts to install every asset the browser needs, memoizing the * outcome in `installAttempts` so a failed (or no-op) attempt isn't repeated * for every later context that shares the browser. Like ensureChromeAvailable, * this self-provisions regardless of DOC_DETECTIVE_AUTOINSTALL (that env var * only governs the eager postinstall). Deps are injected for testing. * * @returns "installed" when all assets installed, "failed" when an install * threw, or "notInstallable" for browsers with no installable asset (safari). */ declare function ensureContextBrowserInstalled({ browserName, config, installAttempts, deps, repair, }: { browserName: string | undefined; config: any; installAttempts: Map; deps: { ensureBrowser: (asset: BrowserAssetName, options: any) => Promise; log?: (config: any, level: string, msg: string) => void; }; repair?: boolean; }): Promise<"installed" | "failed" | "notInstallable">; declare function getRunner(options?: any): Promise<{ runner: any; appium: import("node:child_process").ChildProcessWithoutNullStreams; cleanup: () => Promise; runStep: typeof runStep; }>; //# sourceMappingURL=tests.d.ts.map