import { fireEvent, render, screen, waitFor } from "@testing-library/react"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { resetWorkspace, useWorkspace } from "../workspace"; import { PreviewPane } from "./PreviewPane"; const { readFileMock } = vi.hoisted(() => ({ readFileMock: vi.fn() })); vi.mock("../api", () => ({ readFile: readFileMock, rawFileUrl: (path: string) => `/api/raw?path=${encodeURIComponent(path)}`, })); vi.mock("./DrawioView", () => ({ DrawioView: ({ xml }: { xml: string }) => (
{xml}
), })); // The heavy viewers are replaced by inspectable stubs. vi.mock("./CodeView", () => ({ CodeView: ({ content, language }: { content: string; language: string }) => (
			{content}
		
), })); vi.mock("./AsciiDocView", () => ({ AsciiDocView: ({ content }: { content: string }) => (
{content}
), })); describe("PreviewPane", () => { beforeEach(() => { resetWorkspace(); readFileMock.mockReset(); }); it("asks for a selection when no file is open", () => { render(); expect(screen.getByText(/Select a file/)).toBeDefined(); }); it("previews code with the detected language", async () => { readFileMock.mockResolvedValue({ path: "/w/main.go", content: "package main", }); render(); const view = await screen.findByTestId("code-view"); expect(view.getAttribute("data-language")).toBe("go"); expect(view.textContent).toBe("package main"); // Code has no rendered form: no mode toggle. expect(screen.queryByRole("button", { name: "Rendered" })).toBeNull(); }); it("renders markdown by default and toggles to source", async () => { readFileMock.mockResolvedValue({ path: "/w/README.md", content: "# Title", }); render(); expect(await screen.findByRole("heading", { name: "Title" })).toBeDefined(); fireEvent.click(screen.getByRole("button", { name: "Source" })); const view = await screen.findByTestId("code-view"); expect(view.getAttribute("data-language")).toBe("markdown"); expect(view.textContent).toBe("# Title"); }); it("renders asciidoc through the AsciiDoc viewer", async () => { readFileMock.mockResolvedValue({ path: "/w/book.adoc", content: "= Book" }); render(); expect(await screen.findByTestId("asciidoc-view")).toBeDefined(); }); it("sends the file to the editor with the Edit button", async () => { readFileMock.mockResolvedValue({ path: "/w/main.go", content: "package main", }); render(); await screen.findByTestId("code-view"); fireEvent.click(screen.getByRole("button", { name: "Edit" })); expect(useWorkspace.getState()).toMatchObject({ tab: "editor", editorPath: "/w/main.go", }); }); it("shows images from the raw endpoint without reading them as text", async () => { render(); const img = (await screen.findByRole("img", { name: "logo.png", })) as HTMLImageElement; expect(img.getAttribute("src")).toBe( "/api/raw?path=%2Fw%2Fdocs%2Flogo.png", ); expect(readFileMock).not.toHaveBeenCalled(); // Images have no text form: no toggle, no Edit button. expect(screen.queryByRole("button", { name: "Source" })).toBeNull(); expect(screen.queryByRole("button", { name: "Edit" })).toBeNull(); Object.defineProperty(img, "naturalWidth", { value: 640 }); Object.defineProperty(img, "naturalHeight", { value: 480 }); fireEvent.load(img); expect(screen.getByText("640 × 480 px")).toBeDefined(); }); it("treats .drawio.svg exports as images", async () => { render(); expect( await screen.findByRole("img", { name: "flow.drawio.svg" }), ).toBeDefined(); }); it("renders draw.io diagrams through the viewer and toggles to the XML source", async () => { readFileMock.mockResolvedValue({ path: "/w/docs/arch.drawio", content: "", }); render(); const view = await screen.findByTestId("drawio-view"); expect(view.textContent).toBe(""); fireEvent.click(screen.getByRole("button", { name: "Source" })); const code = await screen.findByTestId("code-view"); expect(code.getAttribute("data-language")).toBe("xml"); expect(code.textContent).toBe(""); }); it("surfaces read errors", async () => { readFileMock.mockRejectedValue(new Error("not a text file")); render(); await waitFor(() => expect(screen.getByText("not a text file")).toBeDefined(), ); }); });