fix(view-windows): réconcilier l'état des fenêtres détachées au boot (#50)
Branche le consommateur frontend sur la commande backend `list_open_view_windows` : le port window expose la liste des fenêtres de panneaux détachées réellement ouvertes côté OS, l'adaptateur Tauri (et son double mock) l'implémente, et `ProjectsView` réconcilie le placement des vues au démarrage à partir de ce snapshot au lieu de supposer un état. Couvre `viewPlacement` et la réconciliation par des tests dédiés. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
89
frontend/src/features/projects/viewPlacement.test.ts
Normal file
89
frontend/src/features/projects/viewPlacement.test.ts
Normal file
@ -0,0 +1,89 @@
|
||||
/**
|
||||
* #50 — startup reconciliation of `placements` with the detached view windows
|
||||
* already open at the OS level. Unit-tests the pure `isDetachablePanel` guard
|
||||
* and `reconcileDetachedWindows`, covering every Architect invariant:
|
||||
* only-visible, known-detachable-only (projects/unknown excluded), and the
|
||||
* no-clobber rule (never overwrite a non-closed placement).
|
||||
*/
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
import type { ViewWindowSnapshot } from "@/ports";
|
||||
import {
|
||||
isDetachablePanel,
|
||||
reconcileDetachedWindows,
|
||||
type ViewPlacements,
|
||||
} from "./viewPlacement";
|
||||
|
||||
const snap = (
|
||||
panel: string,
|
||||
visible = true,
|
||||
): ViewWindowSnapshot => ({ panel, label: panel, visible });
|
||||
|
||||
describe("isDetachablePanel (#50)", () => {
|
||||
it("accepts every known panel except projects", () => {
|
||||
expect(isDetachablePanel("git")).toBe(true);
|
||||
expect(isDetachablePanel("tickets")).toBe(true);
|
||||
expect(isDetachablePanel("memory")).toBe(true);
|
||||
});
|
||||
|
||||
it("excludes projects (main-window-only chrome) and unknown panels", () => {
|
||||
expect(isDetachablePanel("projects")).toBe(false);
|
||||
expect(isDetachablePanel("bogus")).toBe(false);
|
||||
expect(isDetachablePanel("")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("reconcileDetachedWindows (#50)", () => {
|
||||
it("marks a visible detachable snapshot as detached from an empty state", () => {
|
||||
const next = reconcileDetachedWindows({}, [snap("git")]);
|
||||
expect(next).toEqual({ git: "detached" });
|
||||
});
|
||||
|
||||
it("reconciles several panels at once", () => {
|
||||
const next = reconcileDetachedWindows({}, [snap("git"), snap("tickets")]);
|
||||
expect(next).toEqual({ git: "detached", tickets: "detached" });
|
||||
});
|
||||
|
||||
it("ignores non-visible snapshots", () => {
|
||||
const prev: ViewPlacements = {};
|
||||
const next = reconcileDetachedWindows(prev, [snap("git", false)]);
|
||||
expect(next).toBe(prev); // referentially unchanged
|
||||
});
|
||||
|
||||
it("excludes the projects panel", () => {
|
||||
const prev: ViewPlacements = {};
|
||||
const next = reconcileDetachedWindows(prev, [snap("projects")]);
|
||||
expect(next).toBe(prev);
|
||||
});
|
||||
|
||||
it("ignores unknown panel ids", () => {
|
||||
const prev: ViewPlacements = {};
|
||||
const next = reconcileDetachedWindows(prev, [snap("bogus")]);
|
||||
expect(next).toBe(prev);
|
||||
});
|
||||
|
||||
it("never clobbers a non-closed placement (floating/dock/detached preserved)", () => {
|
||||
const prev: ViewPlacements = {
|
||||
git: "floating",
|
||||
tickets: { dock: "left" },
|
||||
memory: "detached",
|
||||
};
|
||||
const next = reconcileDetachedWindows(prev, [
|
||||
snap("git"),
|
||||
snap("tickets"),
|
||||
snap("memory"),
|
||||
]);
|
||||
expect(next).toBe(prev); // nothing changed → same reference
|
||||
});
|
||||
|
||||
it("reconciles only the closed panels, leaving others intact", () => {
|
||||
const prev: ViewPlacements = { git: "floating" };
|
||||
const next = reconcileDetachedWindows(prev, [snap("git"), snap("agents")]);
|
||||
expect(next).toEqual({ git: "floating", agents: "detached" });
|
||||
});
|
||||
|
||||
it("returns the same reference when there is nothing to reconcile", () => {
|
||||
const prev: ViewPlacements = {};
|
||||
expect(reconcileDetachedWindows(prev, [])).toBe(prev);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user