import { type BrowserContext } from 'playwright-core'; import { type BrowserLaunchPlan, type RendererChoice } from '../browser-gl.js'; import { type VerifyObservations } from './classify.js'; import type { VerifyDeviceOptions } from './device.js'; import { type StartInteractionOptions } from './start.js'; export interface HeadlessCheckOptions { url: string; screenshotPath: string; consolePath: string; /** How long to let the game settle after load before observing. */ settleMs: number; /** * Probe for the canvas inside the first child frame whose URL contains this substring, instead * of in the top-level document. * * `bitmagic verify` loads the game directly and leaves this unset. `bitmagic publish`'s smoke * check loads a local harness that embeds the published bundle in an iframe (the shape the * portal uses), where the canvas lives in the child — a top-level query finds nothing and * classifies as "the game did not start" even when it rendered perfectly. */ probeFrameUrlIncludes?: string; /** * Start gameplay the way a player does — wait for the engine Play button and click it — * before settling, and report what happened in `VerifyObservations.start`. * * Only `bitmagic verify` sets this. `publish`'s smoke check loads the game in an iframe where * these top-level selectors would not reach, and its job is "does the published bundle come * up", not "does gameplay start". */ startInteraction?: StartInteractionOptions; /** * Which renderer the URL asked the game to boot with (`?renderer=`). When set, the run probes * the live engine for what actually drove the canvas and reports it in * `VerifyObservations.renderer`. Only `bitmagic verify` sets this; `publish`'s smoke check * leaves it unset and classifies exactly as before. */ requestedRenderer?: RendererChoice; /** * How to launch Chrome: headless or headed, and with which args. Callers that boot a specific * renderer pass a `browserLaunchPlan(...)` (the software-GL retry passes a * `softwareGlLaunchPlan(...)`); the headed variant only ever happens for WebGPU on a Linux * machine with a display, where headless Chrome either screenshots black or never yields a * WebGPU adapter (see browser-gl.ts). * * Optional so `publish`'s smoke check keeps launching exactly as before: headless with * `browserGlArgs(process.env)`. `--mute-audio` is always added here, not by callers. */ launch?: BrowserLaunchPlan; /** * Emulate a phone for this check — viewport, screen, deviceScaleFactor, `isMobile` and * `hasTouch` from `verify/device.ts`. Absent means the desktop 1280x720 context, byte-identical * to what every run did before this existed, which is what `publish`'s smoke check keeps using. * * Pair it with a `?platform=mobile` URL: this side makes the BROWSER a phone, that side makes * the ENGINE take its mobile branch, and only both together verify what a player gets. */ device?: VerifyDeviceOptions; /** * Capture the screenshot and measure its pixels (default true). `bitmagic verify --fast` * turns this off for the inner loop: `screenshotBytes`/`canvasFrame` come back null and * classify treats them as "not observed". Publish's smoke check never turns it off — its * screenshot is the published thumbnail candidate. */ captureScreenshot?: boolean; /** * Install network routes on the context before the page exists — `bitmagic ios build` uses * this to record which remote URLs the packaged game still requests, and then to prove it * requests none (every non-local route aborted). Absent means no interception, byte-identical * to what verify and the publish smoke check always did. */ routes?: (context: BrowserContext) => Promise; } /** * Drives the creator's installed Chrome. `channel: 'chrome'` deliberately uses the system * browser rather than a downloaded one, so installing the CLI stays a small, fast npm install. */ export declare function runHeadlessCheck(options: HeadlessCheckOptions): Promise; /** * A launched headless Chrome that can run any number of checks, each in a fresh browser context. * * One-shot verify launches, checks and closes (`runHeadlessCheck`). `--watch` keeps one session * alive across iterations — launching Chrome is by far the most expensive fixed cost of a run — * while the per-check context still gives every iteration a clean slate: fresh init scripts, * fresh listeners, fresh storage, and engine disposal via the page teardown. */ export declare class VerifyBrowserSession { private readonly browser; private constructor(); static launch(plan?: BrowserLaunchPlan): Promise; close(): Promise; check(options: HeadlessCheckOptions): Promise; }