Files
IdeaSDK/frontend/src/features/projects/ProjectsView.docking.test.tsx
Blomios 09e7f212b7 feat(ui): refonte menus & fenêtres — menu unique Panneaux et onglets projet (#26)
Sous-menus flyout dans MenuBar ; ProjectsView expose un menu unique
Panneaux et supprime les menus View/Window/File ainsi que le sélecteur
de projet ; ProjectTabs gère les onglets et le bouton +. Tests migrés
en conséquence.

QA vert : tsc --noEmit exit 0, vitest 62 fichiers / 616 tests passés,
invariants préservés.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 15:08:34 +02:00

130 lines
4.8 KiB
TypeScript

/**
* #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(
<DIProvider gateways={gateways}>
<ProjectsView />
</DIProvider>,
);
// 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();
});
});