feat(ui): fenêtres View système séparées — WebviewWindow Tauri + port WindowGateway (#23)
Détache les vues dans de vraies fenêtres système Tauri (multi-écran, fullscreen). Backend : commandes de gestion de WebviewWindow, capabilities et composition root. Frontend : nouveau port WindowGateway et son adaptateur window, entrée panel-only ViewWindow/ViewPanelBody, détachement câblé dans ProjectsView. QA vert : app-tauri 63, frontend typecheck + vitest 546/546. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
62
frontend/src/app/ViewWindow.test.tsx
Normal file
62
frontend/src/app/ViewWindow.test.tsx
Normal file
@ -0,0 +1,62 @@
|
||||
/**
|
||||
* #23 — the panel-only entry. `parseViewWindowParams` validates the
|
||||
* `?panel=&project=` query, and `ViewWindow` resolves the project through the
|
||||
* gateway then renders only the requested view (here: the tickets view).
|
||||
*/
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { render, screen, waitFor } from "@testing-library/react";
|
||||
|
||||
import { DIProvider } from "@/app/di";
|
||||
import {
|
||||
MockAgentGateway,
|
||||
MockProjectGateway,
|
||||
MockSystemGateway,
|
||||
MockTicketGateway,
|
||||
} from "@/adapters/mock";
|
||||
import type { Gateways } from "@/ports";
|
||||
import { ViewWindow, parseViewWindowParams } from "./ViewWindow";
|
||||
|
||||
describe("parseViewWindowParams (#23)", () => {
|
||||
it("parses a valid panel + project", () => {
|
||||
expect(parseViewWindowParams("?panel=tickets&project=p-1")).toEqual({
|
||||
panel: "tickets",
|
||||
projectId: "p-1",
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects a missing project, missing panel, or the non-detachable projects panel", () => {
|
||||
expect(parseViewWindowParams("?panel=tickets")).toBeNull();
|
||||
expect(parseViewWindowParams("?project=p-1")).toBeNull();
|
||||
expect(parseViewWindowParams("?panel=projects&project=p-1")).toBeNull();
|
||||
expect(parseViewWindowParams("?panel=bogus&project=p-1")).toBeNull();
|
||||
expect(parseViewWindowParams("")).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("ViewWindow (#23)", () => {
|
||||
it("resolves the project and renders only the requested view", async () => {
|
||||
const system = new MockSystemGateway();
|
||||
const project = new MockProjectGateway();
|
||||
const created = await project.createProject("alpha", "/p/a");
|
||||
const gateways = {
|
||||
system,
|
||||
project,
|
||||
agent: new MockAgentGateway(),
|
||||
ticket: new MockTicketGateway(system),
|
||||
} as unknown as Gateways;
|
||||
|
||||
render(
|
||||
<DIProvider gateways={gateways}>
|
||||
<ViewWindow panel="tickets" projectId={created.id} />
|
||||
</DIProvider>,
|
||||
);
|
||||
|
||||
// The window chrome shows the panel title and the resolved project name.
|
||||
expect(screen.getByRole("heading", { name: "Tickets" })).toBeTruthy();
|
||||
await waitFor(() => expect(screen.getByText("· alpha")).toBeTruthy());
|
||||
// The tickets view itself mounted (its search box is present).
|
||||
await waitFor(() =>
|
||||
expect(screen.getByLabelText("search tickets")).toBeTruthy(),
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user