Luigit
repositories / pi-ext

pi-ext

bugabingas pi extensions

owned by admin

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

Raw
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import path from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import { createTestSession, type TestSession } from "../../../test/harness";

const extensionPath = path.resolve(import.meta.dirname, "../index.ts");

type Case = {
	user?: Record<string, unknown>;
	project?: Record<string, unknown>;
	flag?: boolean;
	untrusted?: boolean;
};

describe("chrome-cdp settings resolution", () => {
	let t: TestSession | undefined;
	let root: string | undefined;
	afterEach(() => {
		t?.dispose();
		t = undefined;
		if (root) rmSync(root, { recursive: true, force: true });
		root = undefined;
	});

	async function start(input: Case) {
		root = mkdtempSync(path.join(tmpdir(), "pi-ext-chrome-cdp-settings-"));
		const write = (dir: string, value: Record<string, unknown>) => {
			mkdirSync(dir, { recursive: true });
			writeFileSync(
				path.join(dir, "settings.json"),
				JSON.stringify({ "chrome-cdp": value }),
			);
		};
		if (input.user) write(path.join(root, ".test-home/.pi/agent"), input.user);
		if (input.project) write(path.join(root, ".pi"), input.project);
		t = await createTestSession({ cwd: root, extensions: [extensionPath] });
		if (input.untrusted) t.session.settingsManager.setProjectTrusted(false);
		const runner = t.session.extensionRunner;
		if (input.flag !== undefined) runner.setFlagValue("chrome", input.flag);
		await runner.emit({ type: "session_start", reason: "startup" });
		return t;
	}

	const warnings = (session: TestSession) => [
		...new Set(
			session.events
				.uiCallsFor("notify")
				.filter((call) => call.args[1] === "warning")
				.map((call) => String(call.args[0])),
		),
	];
	const active = (session: TestSession) =>
		session.session.getActiveToolNames().includes("chrome_cdp");

	it("registers --chrome without a default so omission stays distinct", async () => {
		const session = await start({});
		const runner = session.session.extensionRunner;
		expect(active(session)).toBe(false);
		expect(runner.getFlags().get("chrome")?.default).toBe(undefined);
		expect(runner.getFlagValues().has("chrome")).toBe(false);
	});

	it.each([
		["user true", { user: { enable: true } }, true],
		[
			"project false over user true",
			{ user: { enable: true }, project: { enable: false } },
			false,
		],
		[
			"project true over user false",
			{ user: { enable: false }, project: { enable: true } },
			true,
		],
		[
			"flag over project false",
			{ project: { enable: false }, flag: true },
			true,
		],
		[
			"untrusted project ignored",
			{ user: { enable: true }, project: { enable: false }, untrusted: true },
			true,
		],
	] satisfies [string, Case, boolean][])(
		"enable: %s",
		async (_name, input, expected) => {
			const session = await start(input);
			expect(active(session)).toBe(expected);
			expect(warnings(session)).toEqual([]);
		},
	);

	it.each([
		[
			"string project",
			{ user: { enable: true }, project: { enable: "true" } },
			"project settings",
		],
		["numeric user", { user: { enable: 1 } }, "user settings"],
	] satisfies [string, Case, string][])(
		"enable: rejects %s without falling through",
		async (_name, input, source) => {
			const session = await start(input);
			expect(active(session)).toBe(false);
			expect(warnings(session)).toEqual([
				`Invalid chrome-cdp.enable from ${source}; chrome_cdp off`,
			]);
		},
	);

	// findExecutable names the configured path in its warning, exposing the resolved value.
	async function toggle(input: Case) {
		const session = await start(input);
		const runner = session.session.extensionRunner;
		await runner.getCommand("chrome")?.handler("", {
			...runner.createCommandContext(),
			mode: "print" as const,
			hasUI: false,
		});
		return warnings(session);
	}

	it.each([
		[
			"user only",
			{ user: { executablePath: "/missing/user" } },
			"/missing/user",
		],
		[
			"project over user",
			{
				user: { executablePath: "/missing/user" },
				project: { executablePath: "/missing/project" },
			},
			"/missing/project",
		],
		[
			"untrusted project ignored",
			{
				user: { executablePath: "/missing/user" },
				project: { executablePath: "/missing/project" },
				untrusted: true,
			},
			"/missing/user",
		],
	] satisfies [string, Case, string][])(
		"executablePath: %s",
		async (_name, input, expected) => {
			expect(await toggle(input)).toEqual([
				`Chrome unavailable: Chrome not found; set chrome-cdp.executablePath (checked: ${expected})`,
			]);
		},
	);

	it("executablePath: rejects invalid project value without falling through", async () => {
		expect(
			await toggle({
				user: { executablePath: "/missing/user" },
				project: { executablePath: 1 },
			}),
		).toEqual([
			"Chrome unavailable: Invalid chrome-cdp.executablePath from project settings",
		]);
	});

	it.each([
		["flags", { flags: ["--ok", 1] }],
		["profile", { profile: 1 }],
		["artifactDir", { artifactDir: [] }],
	] satisfies [string, Record<string, unknown>][])(
		"%s: invalid value fails the tool before launch",
		async (key, project) => {
			const session = await start({ project });
			const runner = session.session.extensionRunner;
			const tool = runner.extensions[0]?.tools.get("chrome_cdp")?.definition;
			await expect(
				tool?.execute(
					"id",
					{ operations: [{ kind: "status" }] } as never,
					undefined,
					undefined,
					runner.createCommandContext(),
				),
			).rejects.toThrow(`Invalid chrome-cdp.${key} from project settings`);
		},
	);
});