Skip to content
File

Blob: tests/client/sites/islands/copy-code-button.dom.test.tsx

typescript78 lines
1import { act } from "react";
2import { createRoot, type Root } from "react-dom/client";
3import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
4 
5import { CopyCodeButton } from "@/client/sites/islands/copy-code-button";
6 
7let host: HTMLElement;
8let root: Root;
9const writeText = vi.fn().mockResolvedValue(undefined);
10 
11beforeEach(() => {
12 vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
13 vi.useFakeTimers();
14 writeText.mockReset();
15 writeText.mockResolvedValue(undefined);
16 Object.defineProperty(navigator, "clipboard", {
17 configurable: true,
18 value: { writeText },
19 });
20 
21 document.body.innerHTML = `
22 <div class="tiptap-code-block-wrapper">
23 <pre><code class="tiptap-code-block-content">const answer = 42;</code></pre>
24 <div id="island-host"></div>
25 </div>
26 `;
27 host = document.getElementById("island-host") as HTMLElement;
28 root = createRoot(host);
29 act(() => {
30 root.render(<CopyCodeButton />);
31 });
32});
33 
34afterEach(() => {
35 act(() => {
36 root.unmount();
37 });
38 document.body.innerHTML = "";
39 Reflect.deleteProperty(navigator, "clipboard");
40 vi.useRealTimers();
41 vi.unstubAllGlobals();
42});
43 
44describe("CopyCodeButton", () => {
45 it("copies the nearest code text and toggles feedback for 2s", async () => {
46 const button = host.querySelector<HTMLButtonElement>(".tiptap-code-block-copy-btn")!;
47 act(() => {
48 button.click();
49 });
50 await act(async () => {
51 await Promise.resolve();
52 });
53 
54 expect(writeText).toHaveBeenCalledWith("const answer = 42;");
55 expect(button.dataset.copied).toBe("true");
56 expect(button.getAttribute("aria-label")).toBe("Code copied");
57 
58 act(() => {
59 vi.advanceTimersByTime(2000);
60 });
61 expect(button.dataset.copied).toBeUndefined();
62 expect(button.getAttribute("aria-label")).toBe("Copy code");
63 });
64 
65 it("does not throw when clipboard.writeText rejects", async () => {
66 writeText.mockRejectedValueOnce(new Error("denied"));
67 const button = host.querySelector<HTMLButtonElement>(".tiptap-code-block-copy-btn")!;
68 act(() => {
69 button.click();
70 });
71 await act(async () => {
72 await Promise.resolve();
73 await Promise.resolve();
74 });
75 expect(button.dataset.copied).toBeUndefined();
76 });
77});