clipboard.test.js 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. /* @vitest-environment node */
  2. import { describe, it, expect, vi, afterEach } from "vitest";
  3. import { writeTextToClipboard } from "./clipboard.js";
  4. function createDocumentStub({ copyResult = true } = {}) {
  5. const textarea = {
  6. value: "",
  7. style: {},
  8. setAttribute: vi.fn(),
  9. focus: vi.fn(),
  10. select: vi.fn(),
  11. setSelectionRange: vi.fn(),
  12. remove: vi.fn(),
  13. };
  14. const documentStub = {
  15. createElement: vi.fn().mockReturnValue(textarea),
  16. body: {
  17. appendChild: vi.fn(),
  18. },
  19. execCommand: vi.fn().mockReturnValue(copyResult),
  20. };
  21. return { documentStub, textarea };
  22. }
  23. describe("lib/frontend/ui/clipboard", () => {
  24. afterEach(() => {
  25. vi.unstubAllGlobals();
  26. vi.restoreAllMocks();
  27. });
  28. it("uses navigator.clipboard when available", async () => {
  29. const writeText = vi.fn().mockResolvedValue(undefined);
  30. vi.stubGlobal("navigator", {
  31. clipboard: { writeText },
  32. });
  33. const { documentStub } = createDocumentStub({ copyResult: true });
  34. vi.stubGlobal("document", documentStub);
  35. const result = await writeTextToClipboard("secret");
  36. expect(writeText).toHaveBeenCalledWith("secret");
  37. expect(documentStub.execCommand).not.toHaveBeenCalled();
  38. expect(result).toEqual({ ok: true, method: "clipboard" });
  39. });
  40. it("falls back to execCommand when Clipboard API is unavailable", async () => {
  41. vi.stubGlobal("navigator", {});
  42. const { documentStub, textarea } = createDocumentStub({ copyResult: true });
  43. vi.stubGlobal("document", documentStub);
  44. const result = await writeTextToClipboard("secret");
  45. expect(documentStub.createElement).toHaveBeenCalledWith("textarea");
  46. expect(documentStub.body.appendChild).toHaveBeenCalledWith(textarea);
  47. expect(documentStub.execCommand).toHaveBeenCalledWith("copy");
  48. expect(textarea.remove).toHaveBeenCalledTimes(1);
  49. expect(result).toEqual({ ok: true, method: "execCommand" });
  50. });
  51. it("falls back to execCommand when Clipboard API throws", async () => {
  52. const writeText = vi.fn().mockRejectedValue(new Error("not allowed"));
  53. vi.stubGlobal("navigator", {
  54. clipboard: { writeText },
  55. });
  56. const { documentStub } = createDocumentStub({ copyResult: true });
  57. vi.stubGlobal("document", documentStub);
  58. const result = await writeTextToClipboard("secret");
  59. expect(writeText).toHaveBeenCalledWith("secret");
  60. expect(documentStub.execCommand).toHaveBeenCalledWith("copy");
  61. expect(result).toEqual({ ok: true, method: "execCommand" });
  62. });
  63. it("returns ok=false when no copy mechanism is available", async () => {
  64. vi.stubGlobal("navigator", {});
  65. vi.stubGlobal("document", undefined);
  66. const result = await writeTextToClipboard("secret");
  67. expect(result).toEqual({ ok: false, method: null });
  68. });
  69. });