test.build/core/factories/browser.factory.test.ts

test.build/core/factories/browser.factory.test.ts is a file in Codebase Testing. 87 lines of code and 0 definitions.

import type { BrowserHandle, BrowserWindow } from "@banes-lab/build-scripts/types/browser.types.ts";
import {
    browserArguments,
    freshProfile,
    launchBrowser,
    settleBrowser,
} from "@banes-lab/build-scripts/core/factories/browser.factory.ts";
import { describe, expect, it } from "vitest";
import { existsSync } from "node:fs";
import { join } from "node:path";
import { tmpdir } from "node:os";
import { writeVerbatim } from "@govlab/canonical-write";

const WINDOW: BrowserWindow = { height: 10, profileDir: "profile", software: true, width: 20 };
const MISSING = join(tmpdir(), "no-such-browser-binary");

describe("browserArguments", () => {
    it("asks the system for a free devtools port, and carries the profile and the viewport", () => {
        const args = browserArguments(WINDOW);
        expect(args).toContain("--remote-debugging-port=0");
        expect(args).toContain("--user-data-dir=profile");
        expect(args).toContain("--window-size=20,10");
    });

    it("renders headless in software unless gpu rendering is asked for", () => {
        const software = browserArguments(WINDOW);
        expect(software).toContain("--headless=new");
        expect(software).toContain("--use-angle=swiftshader");
    });

    it("renders gpu work in a headed window on Vulkan, because headless Chrome exposes no hardware WebGPU adapter", () => {
        const hardware = browserArguments({ ...WINDOW, software: false });
        expect(hardware).not.toContain("--headless=new");
        expect(hardware).not.toContain("--use-angle=swiftshader");
        expect(hardware).toContain("--enable-features=Vulkan");
    });
});

describe("launchBrowser", () => {
    it("closes a browser that never started and removes its profile with everything in it", async () => {
        const profileDir = freshProfile(tmpdir());
        writeVerbatim(join(profileDir, "Local State"), "{}");
        const handle = launchBrowser(MISSING, { ...WINDOW, profileDir });
        await handle.close();
        expect(existsSync(profileDir)).toBe(false);
    });
});

const PRIMARY = "the work failed";
const CLEANUP = "the profile stayed locked";

const handleClosing = function handleClosing(closes: boolean): BrowserHandle {
    return {
        close: async () => {
            await Promise.resolve();
            if (!closes) {
                throw new Error(CLEANUP);
            }
        },
        exitCode: () => null,
    };
};

const failing = async function failing(): Promise<number> {
    await Promise.resolve();
    throw new Error(PRIMARY);
};

describe("settleBrowser", () => {
    it("returns the work's value once the browser has closed", async () => {
        await expect(
            settleBrowser(handleClosing(true), async () => {
                await Promise.resolve();
                return 1;
            }),
        ).resolves.toBe(1);
    });

    it("keeps the work's failure first, and a cleanup failure beside it rather than in its place", async () => {
        await expect(settleBrowser(handleClosing(true), failing)).rejects.toThrow(PRIMARY);
        const both = await settleBrowser(handleClosing(false), failing).catch((error: unknown) => error);
        expect(both).toBeInstanceOf(AggregateError);
        expect(both instanceof AggregateError ? both.errors.map((held: Error) => held.message) : []).toStrictEqual([
            PRIMARY,
            CLEANUP,
        ]);
        expect(both instanceof Error ? both.message : "").toBe(PRIMARY);
    });

    it("fails on a cleanup failure after work that succeeded", async () => {
        await expect(
            settleBrowser(handleClosing(false), async () => {
                await Promise.resolve();
                return 1;
            }),
        ).rejects.toThrow(CLEANUP);
    });
});