/** * Consumer test harness (spec: "Consumer Testing Surface — Headless Harness * & Readiness"). Mounts a manifest headlessly — no Deck, no WebGL, no canvas * — in a consumer's own jsdom/happy-dom test environment, with a real * WebMercatorViewport doing the projection math and synthetic picks feeding * the exact selection path deck.gl's real picks take. * * Assertions are deliberately plain DOM: widgets and overlays use OPEN * shadow roots, so `el.shadowRoot.textContent` works with standard tooling — * there is no bespoke assertion API to learn. */ import type { OmMapElement } from "./elements/om-map"; import type { LayerIR } from "./ir"; export interface MountOptions { /** Headless viewport size — jsdom layout reports 0×0, so size is declared, not measured. Default 800×600. */ width?: number; height?: number; } export interface PickOptions { /** The `` to pick from. */ layer: string; /** Feature id (matched the same way the highlight-feature action matches ids) — or give an explicit `index`. */ featureId?: string; index?: number; /** * Pick type — drives which `` fires. Default * "click". "drag" takes the drag-dispatch path (fires `on="drag"` * behaviors, never touches ctx.selection — mirroring live semantics). */ type?: "hover" | "click" | "drag"; } export interface ViewOptions { center?: [number, number]; zoom?: number; pitch?: number; bearing?: number; } export interface TestHarness { /** The upgraded element — query it (and shadow roots) with plain DOM. */ map: OmMapElement; /** The wrapper div the manifest was mounted into. */ container: HTMLElement; /** The live layer IRs — the same descriptors snapshotIR serializes. */ layers(): readonly LayerIR[]; /** * Synthetic pick: resolves the feature, derives its coordinate through the * layer's own compiled getPosition (columnar layers pick object-less with * an index, exactly as deck.gl does), and routes it through the same * selection path a real pick takes. Settles before resolving. */ pick(opts: PickOptions): Promise; /** * Ends the current selection with an empty pick. Default kind "hover" — a * hover-off, the path that auto-hides tooltips. Pass "click" for a click on * empty space, which is what dismisses a `selection-type="click"` popup. */ clearSelection(kind?: "hover" | "click"): Promise; /** * Feeds a map coordinate through the same path a real deck click/hover * takes — drives the draw controller and fires the `om-map-point` event. * For testing custom capture tools (sketch/AOI) without a GPU. * `pointerType` ("touch" | "pen" | "mouse") simulates that input modality — * pass "touch" to exercise touch-only behavior like the draw controller's * double-tap completion; omitted means a synthetic pick with no modality. */ mapPoint(coordinate: [number, number] | null, kind?: "click" | "hover", pointerType?: string): Promise; /** * Sets the camera directly (center/zoom/pitch/bearing) — everything * viewport-derived reacts for real: `viewport`-watching widgets, * viewport-scoped stats, overlay projection/culling. This drives the * camera the way `flyTo` does; gesture→camera translation (deck.gl's * controller) is deliberately not simulated. */ setView(view: ViewOptions): Promise; /** Dispatches an action through the normal payload contract (same as ctx.emit / data-emit). Settles before resolving. */ emit(action: string, payload?: Record): Promise; /** Settles pending work: microtask-batched reconciles and the rAF overlay flush. */ flush(): Promise; /** * Deterministic story control (spec: "Map Stories"): `advance(ms)` drives * the story's clock manually — rAF never runs for a harness-driven story, * so "after 5s the overlay is visible" is exact, not timing-dependent. */ story(id?: string): StoryHandle; unmount(): void; } export interface StoryHandle { play(): Promise; pause(): Promise; seek(ms: number): Promise; /** Advance the manual clock by ms (starts playback if idle). */ advance(ms: number): Promise; readonly state: string; readonly currentTime: number; readonly duration: number; } /** * Mounts a manifest for behavioral testing and resolves once the map is * ready (renderer up — immediate when headless — and no declared data URL * still in flight). Forces `headless` + an explicit size onto the * `` before the element upgrades. */ export declare function mountForTest(html: string, opts?: MountOptions): Promise;