import { resolve } from "node:path"; import { initTheme, keyHint } from "@earendil-works/pi-coding-agent"; import { afterEach, beforeAll, describe, expect, it } from "vitest"; import { createTestSession, type TestSession } from "../../../test/harness"; const extensionPath = resolve(import.meta.dirname, "../index.ts"); describe("chrome-cdp Pi integration", () => { let t: TestSession | undefined; beforeAll(() => initTheme("default", false)); afterEach(() => { t?.dispose(); t = undefined; }); it("registers an inactive tool and toggles it without starting Chrome", async () => { t = await createTestSession({ extensions: [extensionPath] }); const runner = t.session.extensionRunner; const extension = runner.extensions[0]; expect(extension?.path).toBe(extensionPath); const tool = extension?.tools.get("chrome_cdp")?.definition; expect(tool?.description).toContain("Chrome DevTools Protocol"); expect( tool?.promptGuidelines?.some((line) => line.includes("sessionId")), ).toBe(true); expect(t.session.getActiveToolNames()).not.toContain("chrome_cdp"); const command = runner.getCommand("chrome"); expect(command).toBeDefined(); const ctx = { ...runner.createCommandContext(), mode: "print" as const, hasUI: false, }; await command?.handler("", ctx); expect(t.session.getActiveToolNames()).toContain("chrome_cdp"); await command?.handler("", ctx); expect(t.session.getActiveToolNames()).not.toContain("chrome_cdp"); await command?.handler("close", ctx); expect(t.session.getActiveToolNames()).not.toContain("chrome_cdp"); }); it("warns cheaply when Chrome binary is unavailable", async () => { t = await createTestSession({ extensions: [extensionPath], env: { PATH: "/nonexistent" }, }); const runner = t.session.extensionRunner; const command = runner.getCommand("chrome"); const ctx = { ...runner.createCommandContext(), mode: "print" as const, hasUI: false, }; await command?.handler("", ctx); expect(t.session.getActiveToolNames()).toContain("chrome_cdp"); const notifications = t.events.uiCallsFor("notify"); expect(String(notifications.at(-2)?.args[0])).toContain( "Chrome unavailable: Chrome not found", ); expect(notifications.at(-2)?.args[1]).toBe("warning"); }); it("renders commands without exposing CDP parameters", async () => { t = await createTestSession({ extensions: [extensionPath] }); const tool = t.session.extensionRunner.extensions[0]?.tools.get( "chrome_cdp", )?.definition; const theme = { fg: (_color: string, value: string) => value, bold: (value: string) => value, }; const call = tool?.renderCall?.( { operations: [ { kind: "cdp", method: "Browser.getVersion", params: { marker: "private-value" }, }, ], }, theme as never, { expanded: true } as never, ); const text = call?.render(100).join("\n"); expect(text).toContain("Browser.getVersion"); expect(text).not.toContain("private-value"); }); it("labels batches, advertises expansion, and previews results only when expanded", async () => { t = await createTestSession({ extensions: [extensionPath] }); const tool = t.session.extensionRunner.extensions[0]?.tools.get( "chrome_cdp", )?.definition; expect(tool).toBeDefined(); const theme = { fg: (_color: string, value: string) => value, bold: (value: string) => value, }; const args = { operations: [ { kind: "cdp", method: "Page.navigate", params: { url: "https://private.example" }, }, { kind: "wait", event: "Page.loadEventFired" }, ], }; const call = (expanded: boolean) => tool ?.renderCall?.(args as never, theme as never, { expanded } as never) .render(100) .join("\n"); expect(call(false)).toContain(" Chrome"); expect(call(false)).toContain("Page.navigate"); expect(call(false)).toContain("wait Page.loadEventFired"); expect(call(false)).toContain(keyHint("app.tools.expand", "to expand")); expect(call(false)).not.toContain("private.example"); expect(call(true)).not.toContain("to expand"); const result = { content: [ { type: "text" as const, text: JSON.stringify({ output: [ { command: "Browser.getVersion", raw: { id: 2, result: { product: "Chrome/154" } }, }, ], events: [], }), }, ], details: { completed: 1, total: 1, eventCount: 0, operations: ["Browser.getVersion"], }, }; const rendered = (expanded: boolean) => tool ?.renderResult?.( result, { expanded, isPartial: false } as never, theme as never, { isError: false } as never, ) .render(100) .join("\n"); expect(rendered(false)).not.toContain("Chrome/154"); expect(rendered(true)).toContain("Chrome/154"); expect(rendered(true)).toContain("Browser.getVersion"); const status = { ...result, details: { ...result.details, operations: ["status"], browser: "Chrome/154", pages: 2, }, }; const statusText = tool ?.renderResult?.( status, { expanded: false, isPartial: false } as never, theme as never, { isError: false } as never, ) .render(100) .join("\n"); expect(statusText).toContain("Chrome/154 · 2 pages"); }); });