// @vitest-environment jsdom
import { flushSync } from "react-dom";
import { act } from "react";
import { createRoot } from "react-dom/client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { afterEach, describe, expect, it, vi } from "vitest";
import { ThemeProvider } from "../context/ThemeContext";
import { MarkdownBody } from "./MarkdownBody";
import { CoordinatorPresentation } from "./coordinator/CoordinatorPresentation";
import { issuesApi } from "../api/issues";
import { ApiError } from "../api/client";
vi.mock("@/lib/router", () => ({
Link: ({
children,
to,
...props
}: { children: React.ReactNode; to: string } & React.ComponentProps<"a">) => (
{children}
),
}));
vi.mock("../api/issues", () => ({
issuesApi: {
get: vi.fn(),
},
}));
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true;
let root: ReturnType | null = null;
let container: HTMLDivElement | null = null;
afterEach(() => {
if (root) {
flushSync(() => root?.unmount());
}
root = null;
container?.remove();
container = null;
});
function renderMarkdown(children: string, coordinator = false) {
container = document.createElement("div");
document.body.appendChild(container);
root = createRoot(container);
const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
},
},
});
flushSync(() => {
root?.render(
{children}
,
);
});
return container;
}
function click(element: Element | null) {
if (!element) throw new Error("Expected element to exist");
flushSync(() => {
element.dispatchEvent(new MouseEvent("click", { bubbles: true }));
});
}
describe("MarkdownBody code block interactions", () => {
it("retains unavailable-task handling for compact document links", async () => {
vi.mocked(issuesApi.get).mockRejectedValueOnce(new ApiError("Not found", 404, null));
const node = renderMarkdown("[View recommendations](/issues/TES-6#document-recommendations)", true);
const link = node.querySelector('[data-sidebar-document-link]')!;
expect(link).toBeTruthy();
await act(async () => { link.focus(); });
await act(async () => { await new Promise(resolve => setTimeout(resolve, 20)); });
expect(node.querySelector('[data-testid="locked-issue-chip"]')).toBeTruthy();
expect(node.querySelector("a")).toBeNull();
expect(node.textContent).not.toContain("View recommendations");
});
it("toggles line wrapping for indented preformatted markdown blocks", () => {
const node = renderMarkdown("Plan:\n\n source fetch/sync -> signal inbox");
const pre = node.querySelector("pre");
const wrapButton = node.querySelector(".paperclip-markdown-codeblock-wrap");
expect(pre?.style.whiteSpace).toBe("");
expect(wrapButton?.getAttribute("aria-label")).toBe("Wrap lines");
click(wrapButton);
expect(pre?.style.whiteSpace).toBe("pre-wrap");
expect(pre?.style.overflowWrap).toBe("anywhere");
expect(wrapButton?.getAttribute("aria-pressed")).toBe("true");
expect(wrapButton?.getAttribute("aria-label")).toBe("Unwrap lines");
click(wrapButton);
expect(pre?.style.whiteSpace).toBe("");
expect(wrapButton?.getAttribute("aria-pressed")).toBe("false");
expect(wrapButton?.getAttribute("aria-label")).toBe("Wrap lines");
});
});