import { afterEach, describe, expect, it, vi } from "vitest"; import { cleanup, render, screen } from "@testing-library/react"; let mockActiveTaskId: string | null = "task_1"; let mockActiveSessionId: string | null = "session_1"; vi.mock("@/components/state-provider", () => ({ useAppStore: (selector: (state: Record) => unknown) => selector({ tasks: { activeTaskId: mockActiveTaskId, activeSessionId: mockActiveSessionId }, }), })); import { pluginRegistry } from "@/lib/plugins/registry"; import { PluginTaskPanel } from "./plugin-task-panel"; afterEach(() => { cleanup(); pluginRegistry.unregisterPlugin("plugin-a"); mockActiveTaskId = "task_1"; mockActiveSessionId = "session_1"; }); describe("PluginTaskPanel", () => { it("renders the registered Component with PluginTaskPanelProps (AC2)", () => { function Notes(props: { panelId: string; taskId: string; sessionId: string | null; presentation: string; }) { return (
{props.panelId}|{props.taskId}|{props.sessionId}|{props.presentation}
); } pluginRegistry .forPlugin("plugin-a") .registerTaskPanel({ id: "notes", title: "Notes", Component: Notes }); render( , ); expect(screen.getByTestId("notes-body").textContent).toBe( "plugin:plugin-a:notes|task_1|session_1|desktop", ); }); it("renders a not-available fallback when the plugin is no longer registered (AC5)", () => { render( , ); expect(screen.getByText("This panel is no longer available.")).not.toBeNull(); }); it("renders the error boundary fallback when the plugin Component throws (AC6)", () => { function Throws(): never { throw new Error("boom"); } pluginRegistry .forPlugin("plugin-a") .registerTaskPanel({ id: "notes", title: "Notes", Component: Throws }); const originalConsoleError = console.error; console.error = () => {}; render( , ); expect(screen.getByText("This plugin panel failed to load.")).not.toBeNull(); console.error = originalConsoleError; }); });