runDemoWalk
ts
function isE2eDemo(env?: Record<string, string | undefined>): boolean
function demoTimeout(fastMs: number, demoMs?: number): number
// demoMs default 900_000
function withDemoPages<P extends string>(
fixtures: { browser: Browser; page: Page },
personas: readonly P[],
options?: { device?: DemoDevice },
): Promise<DemoWalk<P>>
function runDemoWalk<P extends string>(
fixtures: { browser: Browser; page: Page },
personas: readonly P[],
body: (walk: DemoWalk<P>) => Promise<void>,
options?: { device?: DemoDevice },
): Promise<void>
function resumeOnSharedPage(
walk: { multi: boolean },
fn: () => Promise<void>,
): Promise<void>ts
type DemoWalk<P extends string> = {
page: Record<P, Page>;
multi: boolean;
close: () => Promise<void>;
};runDemoWalk always close()s in a finally (transcript, optional video).
withDemoPages throws if personas is empty. On the fast path it installs the teaching cursor on the shared fixture page (hidden until aimed), sizes the viewport to options.device (or E2E_DEMO_DEVICE), and brands each walk.page[id] so demoFocus can update the HUD chip. On the demo path it closes the fixture page and opens one context per persona (overlay + cursor), then goto(startPath).
resumeOnSharedPage runs fn only when walk.multi is false.