131 lines · 4.4 KB · TypeScript
RawDownload
import { afterEach, describe, expect, it, vi } from "vite-plus/test"; import {  ClipboardApiUnavailableError,  ClipboardWriteError,  writeTextToClipboard,} from "./useCopyToClipboard"; describe("writeTextToClipboard", () => {  afterEach(() => {    vi.unstubAllGlobals();  });   it("reports unavailable clipboard support with structural context", async () => {    vi.stubGlobal("window", {});    vi.stubGlobal("navigator", {});    vi.stubGlobal("document", undefined);     const error = await writeTextToClipboard("plan contents", "plan").then(      () => undefined,      (cause: unknown) => cause,    );     expect(error).toBeInstanceOf(ClipboardApiUnavailableError);    expect(error).toMatchObject({      target: "plan",    });    expect((error as Error).message).not.toContain("plan contents");  });   it.each(["success", "denied", "throws"] as const)(    "cleans up the Clipboard API fallback when copying %s",    async (result) => {      const focus = vi.fn();      const restoreFocus = vi.fn();      const appendChild = vi.fn();      const execCommand = vi.fn(() => {        if (result === "throws") throw new Error("copy command failed");        return result === "success";      });      const remove = vi.fn();      const select = vi.fn();      const setAttribute = vi.fn();      const setSelectionRange = vi.fn();      const textarea = {        focus,        remove,        select,        setAttribute,        setSelectionRange,        style: {},        value: "",      };       vi.stubGlobal("window", {});      vi.stubGlobal("navigator", {});      vi.stubGlobal("document", {        activeElement: { focus: restoreFocus },        body: { appendChild },        createElement: vi.fn(() => textarea),        execCommand,      });       const pendingCopy = writeTextToClipboard("remote command", "command");      // The fallback must run during the original user gesture, before any await.      expect(execCommand).toHaveBeenCalledWith("copy");      if (result === "success") {        await expect(pendingCopy).resolves.toBe(true);      } else {        await expect(pendingCopy).rejects.toBeInstanceOf(ClipboardApiUnavailableError);      }       expect(textarea.value).toBe("remote command");      expect(textarea.style).toMatchObject({ fontSize: "16px" });      expect(appendChild).toHaveBeenCalledWith(textarea);      expect(focus).toHaveBeenCalledWith({ preventScroll: true });      expect(select).toHaveBeenCalledOnce();      expect(setSelectionRange).toHaveBeenCalledWith(0, "remote command".length);      expect(remove).toHaveBeenCalledOnce();      expect(restoreFocus).toHaveBeenCalledOnce();    },  );   it("uses the Clipboard API without touching the fallback when it is available", async () => {    const writeText = vi.fn().mockResolvedValue(undefined);    const execCommand = vi.fn();    vi.stubGlobal("window", {});    vi.stubGlobal("navigator", { clipboard: { writeText } });    vi.stubGlobal("document", { execCommand });     await expect(writeTextToClipboard("remote command", "command")).resolves.toBe(true);     expect(writeText).toHaveBeenCalledWith("remote command");    expect(execCommand).not.toHaveBeenCalled();  });   it("preserves the exact clipboard failure without exposing copied contents", async () => {    const cause = new Error("browser clipboard failure");    const writeText = vi.fn().mockRejectedValue(cause);    vi.stubGlobal("window", {});    vi.stubGlobal("navigator", { clipboard: { writeText } });     const error = await writeTextToClipboard("secret clipboard contents", "error-message").then(      () => undefined,      (failure: unknown) => failure,    );     expect(writeText).toHaveBeenCalledWith("secret clipboard contents");    expect(error).toBeInstanceOf(ClipboardWriteError);    expect(error).toMatchObject({      target: "error-message",      cause,    });    expect((error as Error).message).not.toContain("secret clipboard contents");  });   it.each([true, false])(    "keeps empty values as a no-op with Clipboard API support: %s",    async (available) => {      const writeText = vi.fn();      vi.stubGlobal("window", {});      const execCommand = vi.fn();      vi.stubGlobal("navigator", available ? { clipboard: { writeText } } : {});      vi.stubGlobal("document", { execCommand });       await expect(writeTextToClipboard("", "plan")).resolves.toBe(false);      expect(writeText).not.toHaveBeenCalled();      expect(execCommand).not.toHaveBeenCalled();    },  );});