// @vitest-environment jsdom import { act } from "react"; import { createRoot } from "react-dom/client"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { renderToStaticMarkup } from "react-dom/server"; import { ThemeProvider } from "../context/ThemeContext"; import { ChatMarkdownMessage } from "./ChatMarkdownMessage"; // eslint-disable-next-line @typescript-eslint/no-explicit-any (globalThis as any).IS_REACT_ACT_ENVIRONMENT = true; // Mock clipboard API (not available in jsdom by default) const writeText = vi.fn().mockResolvedValue(undefined); Object.defineProperty(navigator, "clipboard", { value: { writeText }, writable: true, configurable: true, }); describe("ChatMarkdownMessage", () => { let container: HTMLDivElement; beforeEach(() => { container = document.createElement("div"); document.body.appendChild(container); writeText.mockClear(); }); afterEach(() => { container.remove(); }); it("renders markdown headings", () => { act(() => { createRoot(container).render( , ); }); const heading = container.querySelector("h1"); expect(heading).not.toBeNull(); expect(heading?.textContent).toBe("Hello World"); }); it("renders markdown bold and italic", () => { act(() => { createRoot(container).render( , ); }); expect(container.querySelector("strong")).not.toBeNull(); expect(container.querySelector("em")).not.toBeNull(); }); it("renders markdown lists", () => { act(() => { createRoot(container).render( , ); }); expect(container.querySelector("ul")).not.toBeNull(); const items = container.querySelectorAll("li"); expect(items.length).toBe(2); }); it("renders markdown links", () => { act(() => { createRoot(container).render( , ); }); const link = container.querySelector("a"); expect(link).not.toBeNull(); expect(link?.getAttribute("href")).toBe("https://example.com"); }); it("renders markdown tables (GFM)", () => { const tableContent = "| A | B |\n|---|---|\n| 1 | 2 |"; act(() => { createRoot(container).render( , ); }); expect(container.querySelector("table")).not.toBeNull(); expect(container.querySelector("th")).not.toBeNull(); }); it("renders code block with copy button", () => { act(() => { createRoot(container).render( , ); }); const copyBtn = container.querySelector('[aria-label="Copy code"]'); expect(copyBtn).not.toBeNull(); }); it("renders code block with language label when language is specified", () => { act(() => { createRoot(container).render( , ); }); expect(container.textContent).toContain("typescript"); }); it("clicking copy button calls clipboard.writeText with code content", async () => { act(() => { createRoot(container).render( , ); }); const copyBtn = container.querySelector('[aria-label="Copy code"]') as HTMLButtonElement | null; expect(copyBtn).not.toBeNull(); await act(async () => { copyBtn?.click(); }); expect(writeText).toHaveBeenCalledTimes(1); expect(writeText).toHaveBeenCalledWith(expect.stringContaining("const y = 2;")); }); it("inline code renders without copy button", () => { act(() => { createRoot(container).render( , ); }); const copyBtn = container.querySelector('[aria-label="Copy code"]'); expect(copyBtn).toBeNull(); expect(container.querySelector("code")).not.toBeNull(); }); it("renders inline images with img tag", () => { act(() => { createRoot(container).render( , ); }); const img = container.querySelector("img"); expect(img).not.toBeNull(); expect(img?.getAttribute("src")).toBe("https://example.com/img.png"); expect(img?.getAttribute("alt")).toBe("alt text"); }); });