/** * #22 — view anchoring/docking in `ProjectsView`. A view opened from the menu * starts floating (modal window); its header controls move it into the in-flow * left/right {@link DockRegion} and back, and the one-slot-per-view invariant * holds (docking a floating view removes the modal window). */ import { describe, it, expect } from "vitest"; import { render, screen, within, waitFor, fireEvent } from "@testing-library/react"; import { MockAgentGateway, MockGitGateway, MockProfileGateway, MockProjectGateway, MockSystemGateway, MockTemplateGateway, MockWindowGateway, MockWorkStateGateway, } from "@/adapters/mock"; import type { Gateways } from "@/ports"; import { DIProvider } from "@/app/di"; import { ProjectsView } from "./ProjectsView"; async function renderWithProject() { const project = new MockProjectGateway(); await project.createProject("alpha", "/p/a"); const agentGateway = new MockAgentGateway(); const gateways = { system: new MockSystemGateway(), project, agent: agentGateway, profile: new MockProfileGateway(), template: new MockTemplateGateway(agentGateway), git: new MockGitGateway(), workState: new MockWorkStateGateway(), window: new MockWindowGateway(), } as unknown as Gateways; render( , ); // Open the seeded project so panels have an active project to render for. fireEvent.click(await screen.findByRole("button", { name: "Open" })); await waitFor(() => expect(within(screen.getByRole("tablist")).getAllByRole("tab")).toHaveLength(1), ); } /** * Opens a panel floating via the single « Panneaux » menu (#26): menu → panel * entry (opens its placement submenu) → « Flottant ». `panelTitle` is the * panel's human title ("Git", "Agents", …). */ function openPanelFloating(panelTitle: string) { fireEvent.click(screen.getByRole("button", { name: "Panneaux" })); fireEvent.click(screen.getByRole("button", { name: panelTitle })); fireEvent.click(screen.getByRole("button", { name: "Flottant" })); } describe("ProjectsView view docking (#22)", () => { it("opens a view floating, then docks it left as an in-flow column", async () => { await renderWithProject(); // View → Git opens a modal floating window (historical behaviour). openPanelFloating("Git"); const dialog = await screen.findByRole("dialog", { name: "Git" }); expect(dialog).toBeTruthy(); // Dock it to the left via the header control. fireEvent.click(within(dialog).getByRole("button", { name: "dock Git left" })); // The modal window is gone; the Git view now lives in the left dock column. await waitFor(() => expect(screen.queryByRole("dialog", { name: "Git" })).toBeNull(), ); const leftDock = screen.getByRole("complementary", { name: "left dock" }); expect(within(leftDock).getByLabelText("Git panel")).toBeTruthy(); // The main surface is still present beside the dock (in-flow, not overlay). expect(screen.getByRole("main")).toBeTruthy(); }); it("keeps one slot per view: docking right moves it off the left dock", async () => { await renderWithProject(); openPanelFloating("Git"); const dialog = await screen.findByRole("dialog", { name: "Git" }); fireEvent.click(within(dialog).getByRole("button", { name: "dock Git left" })); const leftDock = await screen.findByRole("complementary", { name: "left dock" }); const leftHeader = within(leftDock).getByLabelText("Git panel"); // Move it to the right dock from the docked header control. fireEvent.click( within(leftHeader).getByRole("button", { name: "dock Git right" }), ); await waitFor(() => expect(screen.queryByRole("complementary", { name: "left dock" })).toBeNull(), ); const rightDock = screen.getByRole("complementary", { name: "right dock" }); expect(within(rightDock).getByLabelText("Git panel")).toBeTruthy(); }); it("docks two different views side by side (left + right)", async () => { await renderWithProject(); // Dock Git left. openPanelFloating("Git"); fireEvent.click( within(await screen.findByRole("dialog", { name: "Git" })).getByRole( "button", { name: "dock Git left" }, ), ); // Dock Agents right. openPanelFloating("Agents"); fireEvent.click( within(await screen.findByRole("dialog", { name: "Agents" })).getByRole( "button", { name: "dock Agents right" }, ), ); const leftDock = await screen.findByRole("complementary", { name: "left dock" }); const rightDock = await screen.findByRole("complementary", { name: "right dock" }); expect(within(leftDock).getByLabelText("Git panel")).toBeTruthy(); expect(within(rightDock).getByLabelText("Agents panel")).toBeTruthy(); }); });