import { describe, it, expect, vi, beforeEach } from "vitest"; // --- mocks ---------------------------------------------------------------- const mockPage = { goto: vi.fn().mockResolvedValue(undefined), }; const mockContext = { newPage: vi.fn().mockResolvedValue(mockPage), close: vi.fn().mockResolvedValue(undefined), }; const mockBrowser = { newContext: vi.fn().mockResolvedValue(mockContext), close: vi.fn().mockResolvedValue(undefined), }; const mockChromium = { launch: vi.fn().mockResolvedValue(mockBrowser), }; vi.mock("playwright", () => ({ chromium: mockChromium, })); // Mock fs.existsSync — return true by default; individual tests override as needed. vi.mock("fs", () => ({ existsSync: vi.fn().mockReturnValue(true), })); import { existsSync } from "fs"; import { pw_launch_browser } from "./pw_launch_browser"; // -------------------------------------------------------------------------- beforeEach(() => { vi.clearAllMocks(); // Reset page.goto and existsSync defaults after clearing mockPage.goto.mockResolvedValue(undefined); mockContext.newPage.mockResolvedValue(mockPage); mockContext.close.mockResolvedValue(undefined); mockBrowser.newContext.mockResolvedValue(mockContext); mockBrowser.close.mockResolvedValue(undefined); mockChromium.launch.mockResolvedValue(mockBrowser); vi.mocked(existsSync).mockReturnValue(true); }); // -------------------------------------------------------------------------- describe("pw_launch_browser", () => { it("launch defaults", async () => { const handles = await pw_launch_browser(); // chromium.launch called with defaults expect(mockChromium.launch).toHaveBeenCalledWith({ headless: true, slowMo: 0 }); // context created with default viewport expect(mockBrowser.newContext).toHaveBeenCalledWith( expect.objectContaining({ viewport: { width: 1280, height: 800 } }) ); // no storageState in context options when not provided const ctxCall = mockBrowser.newContext.mock.calls[0][0] as Record; expect(ctxCall.storageState).toBeUndefined(); // page navigated to default baseUrl expect(mockPage.goto).toHaveBeenCalledWith("http://localhost:5180"); // handles returned expect(handles.browser).toBe(mockBrowser); expect(handles.context).toBe(mockContext); expect(handles.page).toBe(mockPage); expect(typeof handles.close).toBe("function"); }); it("launch with storageState", async () => { vi.mocked(existsSync).mockReturnValue(true); const handles = await pw_launch_browser({ headless: false, baseUrl: "http://localhost:5173", viewport: { width: 1920, height: 1080 }, storageStatePath: "/tmp/auth.json", slowMo: 50, }); expect(mockChromium.launch).toHaveBeenCalledWith({ headless: false, slowMo: 50 }); expect(mockBrowser.newContext).toHaveBeenCalledWith( expect.objectContaining({ viewport: { width: 1920, height: 1080 }, storageState: "/tmp/auth.json", }) ); expect(mockPage.goto).toHaveBeenCalledWith("http://localhost:5173"); expect(handles.page).toBe(mockPage); }); it("close calls both", async () => { const handles = await pw_launch_browser(); await handles.close(); expect(mockContext.close).toHaveBeenCalledOnce(); expect(mockBrowser.close).toHaveBeenCalledOnce(); // context must close before browser (call order) const contextCloseOrder = mockContext.close.mock.invocationCallOrder[0]; const browserCloseOrder = mockBrowser.close.mock.invocationCallOrder[0]; expect(contextCloseOrder).toBeLessThan(browserCloseOrder); }); it("storageState skipped when file missing", async () => { vi.mocked(existsSync).mockReturnValue(false); await pw_launch_browser({ storageStatePath: "/tmp/nonexistent.json" }); const ctxCall = mockBrowser.newContext.mock.calls[0][0] as Record; expect(ctxCall.storageState).toBeUndefined(); }); });