import { beforeEach, describe, expect, it, vi } from "vitest"; import { fireEvent, render, screen, waitFor } from "@testing-library/react"; import type { ButtonHTMLAttributes, ReactNode } from "react"; import { MAX_PANEL_COUNT, MissionControlPanel, type PanelConfig } from "./MissionControlPanel"; interface MockButtonProps extends ButtonHTMLAttributes { children: ReactNode; } interface MockOrchestratorPanelProps { sessionId?: string; onClose?: () => void; closeDisabled?: boolean; onExpand?: () => void; expanded?: boolean; } const mockOrchestratorPanel = vi.fn<(props: MockOrchestratorPanelProps) => React.JSX.Element>(); vi.mock("@/components/mission-control/OrchestratorPanel", () => ({ OrchestratorPanel: (props: MockOrchestratorPanelProps): React.JSX.Element => mockOrchestratorPanel(props), })); vi.mock("@/components/ui/button", () => ({ Button: ({ children, ...props }: MockButtonProps): React.JSX.Element => ( ), })); function buildPanels(count: number): PanelConfig[] { return Array.from({ length: count }, (_, index) => ({ sessionId: `session-${String(index + 1)}`, })); } describe("MissionControlPanel", (): void => { beforeEach((): void => { vi.clearAllMocks(); mockOrchestratorPanel.mockImplementation( ({ sessionId, closeDisabled, expanded }: MockOrchestratorPanelProps): React.JSX.Element => (
) ); }); it("renders the panel grid and default heading", (): void => { render( void>()} onRemovePanel={vi.fn<(index: number) => void>()} onExpandPanel={vi.fn<(index: number) => void>()} /> ); expect(screen.getByRole("heading", { name: "Panels" })).toBeInTheDocument(); expect(screen.getAllByTestId("orchestrator-panel")).toHaveLength(1); }); it("calls onAddPanel when the add button is clicked", (): void => { const onAddPanel = vi.fn<() => void>(); render( void>()} onExpandPanel={vi.fn<(index: number) => void>()} /> ); fireEvent.click(screen.getByRole("button", { name: "Add panel" })); expect(onAddPanel).toHaveBeenCalledTimes(1); }); it("disables add panel at the configured maximum", (): void => { render( void>()} onRemovePanel={vi.fn<(index: number) => void>()} onExpandPanel={vi.fn<(index: number) => void>()} /> ); const addButton = screen.getByRole("button", { name: "Add panel" }); expect(addButton).toBeDisabled(); expect(addButton).toHaveAttribute("title", "Maximum of 6 panels"); }); it("passes closeDisabled=false when more than one panel exists", (): void => { render( void>()} onRemovePanel={vi.fn<(index: number) => void>()} onExpandPanel={vi.fn<(index: number) => void>()} /> ); const renderedPanels = screen.getAllByTestId("orchestrator-panel"); expect(renderedPanels).toHaveLength(2); for (const panel of renderedPanels) { expect(panel).toHaveAttribute("data-close-disabled", "false"); } }); it("renders only the expanded panel in focused mode", (): void => { render( void>()} onRemovePanel={vi.fn<(index: number) => void>()} onExpandPanel={vi.fn<(index: number) => void>()} /> ); const renderedPanels = screen.getAllByTestId("orchestrator-panel"); expect(renderedPanels).toHaveLength(1); expect(renderedPanels[0]).toHaveAttribute("data-session-id", "session-2"); expect(renderedPanels[0]).toHaveAttribute("data-expanded", "true"); }); it("handles Escape key by toggling expanded panel", async (): Promise => { const onExpandPanel = vi.fn<(index: number) => void>(); render( void>()} onRemovePanel={vi.fn<(index: number) => void>()} onExpandPanel={onExpandPanel} /> ); fireEvent.keyDown(window, { key: "Escape" }); await waitFor((): void => { expect(onExpandPanel).toHaveBeenCalledWith(0); }); }); });