clone

a deno framework for local-first web apps that open instantly, work offline and sync when users choose

feat/prf-provision-custody
lofi / package / testing / readiness.ts
readiness.ts 2.4K
1import type { Page } from "npm:playwright@1.61.1";23/** Options controlling {@link waitForReady}'s description, timeout, and polling. */4export interface ReadinessOptions {5 /** A useful name for the condition, included when Playwright times out. */6 description?: string;7 /** Defaults to 10 seconds. */8 timeoutMs?: number;9 /** Uses Playwright's event-loop polling; no fixed sleep is introduced. */10 polling?: "raf" | number;11}1213/** Thrown when the readiness predicate does not become true before the timeout. */14export class ReadinessError extends Error {15 /** Always `"ReadinessError"`. */16 override readonly name = "ReadinessError";1718 /**19 * Builds the error, embedding the condition that failed to become ready.20 * @param description the readiness condition that timed out, named in the message21 * @param options standard error options, e.g. a `cause` from the underlying timeout22 */23 constructor(description: string, options?: ErrorOptions) {24 super(`Page did not become ready: ${description}`, options);25 }26}2728/**29 * Wait for an app-owned browser predicate. The predicate runs in the page and30 * Playwright owns the timeout/polling, so tests do not need arbitrary sleeps.31 *32 * @example33 * ```ts34 * import { waitForReady } from "@nzip/lofi/testing";35 *36 * await waitForReady(client.page, () => document.querySelector(".task-list") !== null, undefined, {37 * description: "task list rendered",38 * });39 * ```40 *41 * @param page The Playwright page to poll.42 * @param predicate An app-owned readiness check evaluated inside the page.43 * @param argument A serializable value passed to the predicate in the page.44 * @param options Optional description, timeout, and polling configuration.45 * @returns Resolves when the predicate becomes true; rejects with {@link ReadinessError} on timeout.46 */47export async function waitForReady<Argument>(48 page: Page,49 predicate: (argument: Argument) => boolean | Promise<boolean>,50 argument: Argument,51 options: ReadinessOptions = {},52): Promise<void> {53 const description = options.description ?? "application readiness predicate";54 try {55 // Playwright internally maps serializable values to its Unboxed<T> type.56 await page.waitForFunction(predicate as never, argument as never, {57 polling: options.polling ?? "raf",58 timeout: options.timeoutMs ?? 10_000,59 });60 } catch (error) {61 throw new ReadinessError(description, { cause: error });62 }63}