/** * #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); }); });