| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119 |
- import { afterEach, describe, expect, test, vi } from "vitest";
- import { copyTextToClipboard, copyToClipboard, isClipboardSupported } from "@/lib/utils/clipboard";
- function stubSecureContext(value: boolean) {
- Object.defineProperty(window, "isSecureContext", {
- value,
- configurable: true,
- });
- }
- function stubClipboard(writeText: (text: string) => Promise<void> | void) {
- Object.defineProperty(navigator, "clipboard", {
- value: { writeText },
- configurable: true,
- });
- }
- function stubExecCommand(impl: (command: string) => boolean) {
- Object.defineProperty(document, "execCommand", {
- value: impl,
- configurable: true,
- });
- }
- afterEach(() => {
- vi.unstubAllGlobals();
- vi.restoreAllMocks();
- });
- describe("clipboard utils", () => {
- test("SSR 环境:isClipboardSupported/copyTextToClipboard 应返回 false", async () => {
- vi.stubGlobal("window", undefined as unknown as Window);
- expect(isClipboardSupported()).toBe(false);
- await expect(copyTextToClipboard("abc")).resolves.toBe(false);
- });
- test("isClipboardSupported: 仅在安全上下文且 Clipboard API 可用时为 true", () => {
- stubSecureContext(false);
- stubClipboard(vi.fn());
- expect(isClipboardSupported()).toBe(false);
- stubSecureContext(true);
- stubClipboard(vi.fn());
- expect(isClipboardSupported()).toBe(true);
- });
- test("copyTextToClipboard: Clipboard API 成功时返回 true", async () => {
- stubSecureContext(true);
- const writeText = vi.fn().mockResolvedValue(undefined);
- stubClipboard(writeText);
- const execCommand = vi.fn();
- stubExecCommand(execCommand);
- const before = document.querySelectorAll("textarea").length;
- await expect(copyTextToClipboard("abc")).resolves.toBe(true);
- const after = document.querySelectorAll("textarea").length;
- expect(writeText).toHaveBeenCalledWith("abc");
- expect(execCommand).not.toHaveBeenCalled();
- expect(after).toBe(before);
- });
- test("copyTextToClipboard: Clipboard API 失败时应 fallback 到 execCommand", async () => {
- stubSecureContext(true);
- const writeText = vi.fn().mockRejectedValue(new Error("fail"));
- stubClipboard(writeText);
- const execCommand = vi.fn(() => true);
- stubExecCommand(execCommand);
- const before = document.querySelectorAll("textarea").length;
- await expect(copyTextToClipboard("abc")).resolves.toBe(true);
- const after = document.querySelectorAll("textarea").length;
- expect(writeText).toHaveBeenCalledWith("abc");
- expect(execCommand).toHaveBeenCalledWith("copy");
- expect(after).toBe(before);
- });
- test("copyTextToClipboard: 无 Clipboard API 时走 fallback(execCommand 失败则返回 false)", async () => {
- stubSecureContext(false);
- Object.defineProperty(navigator, "clipboard", { value: undefined, configurable: true });
- stubExecCommand(() => false);
- await expect(copyTextToClipboard("abc")).resolves.toBe(false);
- });
- test("copyTextToClipboard: fallback 抛错时返回 false", async () => {
- stubSecureContext(false);
- Object.defineProperty(navigator, "clipboard", { value: undefined, configurable: true });
- stubExecCommand(() => {
- throw new Error("boom");
- });
- await expect(copyTextToClipboard("abc")).resolves.toBe(false);
- });
- test("copyToClipboard: 兼容旧 API(内部调用 copyTextToClipboard)", async () => {
- stubSecureContext(true);
- const writeText = vi.fn().mockResolvedValue(undefined);
- stubClipboard(writeText);
- await expect(copyToClipboard("abc")).resolves.toBe(true);
- expect(writeText).toHaveBeenCalledWith("abc");
- });
- test("copyTextToClipboard: 无 document 时 fallback 直接返回 false", async () => {
- stubSecureContext(false);
- Object.defineProperty(navigator, "clipboard", { value: undefined, configurable: true });
- vi.stubGlobal("document", undefined as unknown as Document);
- await expect(copyTextToClipboard("abc")).resolves.toBe(false);
- });
- });
|