import { act, cleanup, fireEvent, render, screen, waitFor } from "@testing-library/react"; import { afterEach, describe, expect, it } from "vitest"; import { Tooltip, TooltipContent, TooltipTrigger } from "@kandev/ui/tooltip"; import { pluginModalManager } from "@/lib/plugins/modal-manager"; import { PluginModalHost } from "./plugin-modal-host"; function cleanupModals(pluginId: string) { pluginModalManager.closeAllForPlugin(pluginId); } describe("PluginModalHost", () => { afterEach(() => { cleanup(); cleanupModals("plugin-a"); }); it("renders nothing when no plugin has an open modal", () => { const { container } = render(); expect(container.innerHTML).toBe(""); }); it("renders an open modal's title and content", () => { pluginModalManager.openModal("plugin-a", { title: "My Modal", content: () =>
Hello
, }); render(); expect(screen.getByText("My Modal")).not.toBeNull(); expect(screen.getByTestId("modal-content")).not.toBeNull(); }); it("renders a modal description inside the host-owned header", () => { pluginModalManager.openModal("plugin-a", { title: "Link Bitbucket pull request", description: "Use a Bitbucket pull request URL for this task.", content: () =>
Hello
, } as never); render(); expect(screen.getByText("Use a Bitbucket pull request URL for this task.")).not.toBeNull(); }); it("keeps dialog content in a local scroll body while retaining the close control", () => { pluginModalManager.openModal("plugin-a", { title: "Growing modal", content: () =>
Long plugin content
, }); render(); const dialog = screen.getByTestId(/^plugin-modal-dialog-/); const body = screen.getByTestId(/^plugin-modal-body-/); expect(dialog.getAttribute("data-layout")).toBe("contained"); expect(dialog.contains(body)).toBe(true); expect(screen.getByRole("button", { name: "Close" })).not.toBeNull(); }); it("keeps titleless task-link content in the bounded scroll row", () => { pluginModalManager.openTaskLinkDialog("plugin-a", { content: () =>
Long task-link content
, }); render(); const dialog = screen.getByTestId(/^plugin-modal-dialog-/); const body = screen.getByTestId(/^plugin-modal-body-/); expect(dialog.getAttribute("data-layout")).toBe("contained"); expect(body.className).toContain("row-start-2"); expect(screen.getByRole("dialog", { name: "Plugin dialog" })).toBeTruthy(); }); it("does not add a close control to a nondismissible dialog", () => { pluginModalManager.openModal("plugin-a", { title: "Locked modal", content: () =>
Content
, dismissible: false, }); render(); expect(screen.getByTestId(/^plugin-modal-dialog-/)).not.toBeNull(); expect(screen.queryByRole("button", { name: "Close" })).toBeNull(); }); it("renders a host-owned drawer when the plugin requests mobile presentation", () => { pluginModalManager.openModal("plugin-a", { title: "Link pull request", content: () =>
Mobile action
, presentation: "drawer", }); render(); expect(document.querySelector('[data-slot="drawer-content"]')).not.toBeNull(); expect(screen.getByTestId("drawer-content")).not.toBeNull(); }); it("gives title-less plugin surfaces an accessible fallback name", () => { pluginModalManager.openModal("plugin-a", { content: () =>
Untitled content
, presentation: "drawer", }); render(); expect(screen.getByRole("dialog", { name: "Plugin dialog" })).not.toBeNull(); }); it("removes the modal from the DOM once its handle is closed", () => { const handle = pluginModalManager.openModal("plugin-a", { content: () =>
Hello
, }); render(); expect(screen.getByTestId("modal-content")).not.toBeNull(); act(() => { handle.close(); }); expect(screen.queryByTestId("modal-content")).toBeNull(); }); }); /** * The host mounts as a sibling of `` (src/main.tsx), so it is * outside the app-wide `TooltipProvider` in app/layout.tsx. Without a * provider of its own, a `Tooltip` anywhere in plugin modal content throws on * render and the whole modal is lost to the error boundary. * * jsdom does not reliably open a Radix tooltip from synthetic hover (see * apps/web/CLAUDE.md), so the open assertion goes through focus. Pointer * hover is a Playwright concern. */ describe("PluginModalHost — tooltips in modal content", () => { afterEach(() => { cleanup(); cleanupModals("plugin-a"); }); function TooltipContentComponent() { return ( Trigger Helpful hint ); } it("renders modal content containing a Tooltip without throwing", () => { pluginModalManager.openModal("plugin-a", { title: "Tooltip Modal", content: TooltipContentComponent, }); render(); expect(screen.getByTestId("tooltip-trigger")).not.toBeNull(); }); it("opens the tooltip on focus, proving a TooltipProvider is in scope", async () => { pluginModalManager.openModal("plugin-a", { content: TooltipContentComponent }); render(); fireEvent.focus(screen.getByTestId("tooltip-trigger")); await waitFor(() => { expect(screen.getAllByText("Helpful hint").length).toBeGreaterThan(0); }); }); });