import { describe, it, expect, vi, beforeEach } from "vitest"; import type { Page } from "playwright"; import { pw_keyboard_sequence, type KbStep } from "./pw_keyboard_sequence"; // --------------------------------------------------------------------------- // Mock helpers // --------------------------------------------------------------------------- const mockFocus = vi.fn().mockResolvedValue(undefined); const mockFirst = vi.fn().mockReturnValue({ focus: mockFocus }); const mockLocator = vi.fn().mockReturnValue({ first: mockFirst }); const mockType = vi.fn().mockResolvedValue(undefined); const mockPress = vi.fn().mockResolvedValue(undefined); const mockWaitForTimeout = vi.fn().mockResolvedValue(undefined); const mockPage = { locator: mockLocator, keyboard: { type: mockType, press: mockPress, }, waitForTimeout: mockWaitForTimeout, } as unknown as Page; beforeEach(() => { vi.clearAllMocks(); mockFirst.mockReturnValue({ focus: mockFocus }); mockLocator.mockReturnValue({ first: mockFirst }); mockFocus.mockResolvedValue(undefined); mockType.mockResolvedValue(undefined); mockPress.mockResolvedValue(undefined); mockWaitForTimeout.mockResolvedValue(undefined); }); // --------------------------------------------------------------------------- describe("pw_keyboard_sequence", () => { it("focus calls focus", async () => { const sequence: KbStep[] = [{ kind: "focus", selector: "input[name=requester]" }]; await pw_keyboard_sequence(mockPage, sequence); expect(mockLocator).toHaveBeenCalledWith("input[name=requester]"); expect(mockFirst).toHaveBeenCalledOnce(); expect(mockFocus).toHaveBeenCalledOnce(); }); it("type uses default delay", async () => { const sequence: KbStep[] = [{ kind: "type", text: "Enmanuel" }]; await pw_keyboard_sequence(mockPage, sequence); expect(mockType).toHaveBeenCalledWith("Enmanuel", { delay: 30 }); }); it("type with explicit delayMs uses that value", async () => { const sequence: KbStep[] = [{ kind: "type", text: "hello", delayMs: 100 }]; await pw_keyboard_sequence(mockPage, sequence); expect(mockType).toHaveBeenCalledWith("hello", { delay: 100 }); }); it("press routes correctly", async () => { const sequence: KbStep[] = [ { kind: "press", key: "ArrowDown" }, { kind: "press", key: "Enter" }, ]; await pw_keyboard_sequence(mockPage, sequence); expect(mockPress).toHaveBeenCalledTimes(2); expect(mockPress).toHaveBeenNthCalledWith(1, "ArrowDown"); expect(mockPress).toHaveBeenNthCalledWith(2, "Enter"); }); it("wait calls waitForTimeout", async () => { const sequence: KbStep[] = [{ kind: "wait", ms: 200 }]; await pw_keyboard_sequence(mockPage, sequence); expect(mockWaitForTimeout).toHaveBeenCalledWith(200); }); it("unknown kind throws", async () => { const sequence = [{ kind: "unknown_step" }] as unknown as KbStep[]; await expect(pw_keyboard_sequence(mockPage, sequence)).rejects.toThrow( 'pw_keyboard_sequence: unknown step kind "unknown_step"' ); }); it("order preserved", async () => { const callOrder: string[] = []; mockFocus.mockImplementationOnce(async () => { callOrder.push("focus"); }); mockType.mockImplementationOnce(async () => { callOrder.push("type"); }); mockWaitForTimeout.mockImplementationOnce(async () => { callOrder.push("wait"); }); mockPress.mockImplementationOnce(async () => { callOrder.push("press"); }); const sequence: KbStep[] = [ { kind: "focus", selector: "input" }, { kind: "type", text: "abc" }, { kind: "wait", ms: 50 }, { kind: "press", key: "Enter" }, ]; await pw_keyboard_sequence(mockPage, sequence); expect(callOrder).toEqual(["focus", "type", "wait", "press"]); }); });