Luigit
repositories / pi-ext

pi-ext

bugabingas pi extensions

owned by admin

extensions/chrome-cdp/__tests__/harness.test.ts

Raw
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");
	});
});