import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; import { copyToClipboard, readFromClipboard } from "../../lib/clipboard"; describe("copyToClipboard", () => { const originalClipboard = navigator.clipboard; beforeEach(() => { delete (window as { electronClipboard?: unknown }).electronClipboard; }); afterEach(() => { Object.defineProperty(navigator, "clipboard", { value: originalClipboard, configurable: true, }); vi.restoreAllMocks(); }); function setClipboard(value: unknown) { Object.defineProperty(navigator, "clipboard", { value, configurable: true, }); } it("uses the electron bridge when present", async () => { const writeText = vi.fn().mockResolvedValue(true); (window as { electronClipboard?: unknown }).electronClipboard = { writeText, readText: vi.fn(), }; const ok = await copyToClipboard("hello"); expect(ok).toBe(true); expect(writeText).toHaveBeenCalledWith("hello"); }); it("uses navigator.clipboard when available", async () => { const writeText = vi.fn().mockResolvedValue(undefined); setClipboard({ writeText }); const ok = await copyToClipboard("world"); expect(ok).toBe(true); expect(writeText).toHaveBeenCalledWith("world"); }); it("falls back to execCommand when navigator.clipboard is undefined (Brave / non-HTTPS)", async () => { setClipboard(undefined); const execCommand = vi.fn().mockReturnValue(true); (document as unknown as { execCommand: typeof execCommand }).execCommand = execCommand; const ok = await copyToClipboard("fallback"); expect(ok).toBe(true); expect(execCommand).toHaveBeenCalledWith("copy"); }); it("falls back to execCommand when the async write throws", async () => { setClipboard({ writeText: vi.fn().mockRejectedValue(new Error("denied")), }); const execCommand = vi.fn().mockReturnValue(true); (document as unknown as { execCommand: typeof execCommand }).execCommand = execCommand; const ok = await copyToClipboard("retry"); expect(ok).toBe(true); expect(execCommand).toHaveBeenCalledWith("copy"); }); it("returns false when every approach fails", async () => { setClipboard(undefined); (document as unknown as { execCommand: () => boolean }).execCommand = () => { throw new Error("no execCommand"); }; const ok = await copyToClipboard("nope"); expect(ok).toBe(false); }); }); describe("readFromClipboard", () => { const originalClipboard = navigator.clipboard; beforeEach(() => { delete (window as { electronClipboard?: unknown }).electronClipboard; }); afterEach(() => { Object.defineProperty(navigator, "clipboard", { value: originalClipboard, configurable: true, }); vi.restoreAllMocks(); }); function setClipboard(value: unknown) { Object.defineProperty(navigator, "clipboard", { value, configurable: true, }); } it("uses the electron bridge when present", async () => { const readText = vi.fn().mockResolvedValue("hello"); (window as { electronClipboard?: unknown }).electronClipboard = { writeText: vi.fn(), readText, }; const text = await readFromClipboard(); expect(text).toBe("hello"); expect(readText).toHaveBeenCalled(); }); it("uses navigator.clipboard when available", async () => { const readText = vi.fn().mockResolvedValue("world"); setClipboard({ readText }); const text = await readFromClipboard(); expect(text).toBe("world"); expect(readText).toHaveBeenCalled(); }); it("falls back to execCommand when navigator.clipboard.readText is undefined (non-HTTPS)", async () => { setClipboard({}); const execCommand = vi.fn().mockImplementation((command: string) => { if (command === "paste") { const active = document.activeElement as HTMLTextAreaElement; active.value = "fallback"; return true; } return false; }); (document as unknown as { execCommand: typeof execCommand }).execCommand = execCommand; const text = await readFromClipboard(); expect(text).toBe("fallback"); expect(execCommand).toHaveBeenCalledWith("paste"); }); it("falls back to execCommand when the async read throws", async () => { setClipboard({ readText: vi.fn().mockRejectedValue(new Error("denied")), }); const execCommand = vi.fn().mockImplementation((command: string) => { if (command === "paste") { const active = document.activeElement as HTMLTextAreaElement; active.value = "retry"; return true; } return false; }); (document as unknown as { execCommand: typeof execCommand }).execCommand = execCommand; const text = await readFromClipboard(); expect(text).toBe("retry"); expect(execCommand).toHaveBeenCalledWith("paste"); }); it("returns an empty string when every approach fails", async () => { setClipboard({}); (document as unknown as { execCommand: () => boolean }).execCommand = () => false; const text = await readFromClipboard(); expect(text).toBe(""); }); });