import { assert, assertEquals } from "@std/assert"; import { until } from "@berry/util"; import { viewContext, withDom } from "@berry/util/dom.ts"; import type { ViewContext } from "@berry/api/web.ts"; import { browserView } from "./view.ts"; import type { BrowserViewData, Snapshot } from "./types.ts"; const page = (tab: string, title: string): Snapshot => ({ tab, title, url: "https://example.com/", text: ` ${title}`, elements: [{ ref: "e1", role: "button", name: "Continue" }], truncated: false, frames: [], diagnostics: ["HTTP 404: https://example.com/missing"], }); const context = (request: ViewContext["request"], latest = 1) => viewContext({ tid: "conversation", latest, request }); Deno.test("the close button ends a live session and then shows it as ended", () => withDom(async () => { const view = browserView(); const requests: string[] = []; let active = true; view.update(context((name) => { requests.push(name); if (name === "close") active = false; return Promise.resolve( name === "close" ? null : { active, tabs: [page("t1", "First")], history: [], screenshots: [] }, ); })); try { const close = [...view.element.querySelectorAll("button")] .find((button) => button.textContent === "Close browser") as HTMLButtonElement; await until(() => !close.disabled); close.click(); await until(() => view.element.querySelector('[role="status"]')?.textContent?.includes("historical") ?? false ); assertEquals(requests, ["view", "close", "view"]); assert(close.disabled); } finally { view.dispose?.(); } })); Deno.test("outdated view responses and disposed requests cannot replace the current conversation", () => withDom(async () => { const view = browserView(); let resolveOld!: (value: BrowserViewData) => void; let oldSignal!: AbortSignal; const old = context((_, __, options) => { oldSignal = options!.signal!; return new Promise((resolve) => { resolveOld = resolve; }); }); view.update(old); const fresh: BrowserViewData = { active: true, tabs: [page("t8", "Current")], history: [], screenshots: [], }; view.update({ ...context(() => Promise.resolve(fresh)), tid: "another" }); await until(() => view.element.textContent?.includes("Current") ?? false); assert(oldSignal.aborted); resolveOld({ active: false, tabs: [page("t1", "Outdated")], history: [], screenshots: [] }); await Promise.resolve(); assert(!view.element.textContent?.includes("Outdated")); let signal!: AbortSignal; view.update(context((_, __, options) => { signal = options!.signal!; return new Promise(() => {}); }, 3)); view.dispose?.(); assert(signal.aborted); }));