diff options
| author | Adam Malczewski <[email protected]> | 2026-09-05 00:34:45 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-09-05 00:34:45 +0900 |
| commit | a104c094b588560f955ff52dc7f949cf3c355f94 (patch) | |
| tree | 61983d5568f47fd23739705aaec815be1336b595 /src/features/layout/logic/seed.test.ts | |
| parent | 60a726883be85c174e88ae832ef95f3c0f084ec9 (diff) | |
| download | dispatch-web-a104c094b588560f955ff52dc7f949cf3c355f94.tar.gz dispatch-web-a104c094b588560f955ff52dc7f949cf3c355f94.zip | |
feat(layout): plan 01 — tile engine + view registry
Diffstat (limited to 'src/features/layout/logic/seed.test.ts')
| -rw-r--r-- | src/features/layout/logic/seed.test.ts | 99 |
1 files changed, 99 insertions, 0 deletions
diff --git a/src/features/layout/logic/seed.test.ts b/src/features/layout/logic/seed.test.ts new file mode 100644 index 0000000..dfe166a --- /dev/null +++ b/src/features/layout/logic/seed.test.ts @@ -0,0 +1,99 @@ +import { DEFAULT_LAYOUT, type LayoutTree, type SplitNode } from "@dispatch/layout-contract"; +import { describe, expect, it } from "vitest"; +import { DEFAULT_PANEL_VIEWS, overlaySidePanels, seedLayout } from "./seed"; +import { firstCollapsibleNodeId, firstPanelsNode } from "./tree-query"; + +function sideViews(tree: LayoutTree): string[] { + const root = tree.root as SplitNode; + const side = root.children[1]?.node; + return side?.kind === "panels" ? side.panels.map((p) => p.view) : []; +} + +describe("seedLayout", () => { + it("uses the default panels when nothing is stored", () => { + const tree = seedLayout(null, ["tabs", "model", "settings"]); + expect(sideViews(tree)).toEqual(DEFAULT_PANEL_VIEWS); + }); + + it("overlays the stored selection, dropping unknown/removed kinds", () => { + const tree = seedLayout( + ["extensions", "ghost-view", "model"], + ["tabs", "model", "extensions", "settings"], + ); + expect(sideViews(tree)).toEqual(["extensions", "model"]); + }); + + it("falls back to the defaults when the stored selection is all-unknown or empty", () => { + expect(sideViews(seedLayout(["ghost"], ["tabs", "model"]))).toEqual(["tabs", "model"]); + expect(sideViews(seedLayout([], ["tabs", "model"]))).toEqual(["tabs", "model"]); + }); + + it("falls back to the defaults when the stored value is not a string array", () => { + expect(sideViews(seedLayout("junk", ["tabs", "model"]))).toEqual(["tabs", "model"]); + expect(sideViews(seedLayout([42], ["tabs", "model"]))).toEqual(["tabs", "model"]); + expect(sideViews(seedLayout({ a: 1 }, ["tabs", "model"]))).toEqual(["tabs", "model"]); + }); + + it("preserves the default tree's ids and the titlebar-unset invariant", () => { + const tree = seedLayout(["model"], ["tabs", "model"]); + expect((tree.root as SplitNode).id).toBe("root"); + expect(tree.titlebar).toBeUndefined(); + }); +}); + +describe("overlaySidePanels", () => { + it("replaces only the side panels of the given base tree", () => { + const tree = overlaySidePanels(DEFAULT_LAYOUT, ["settings", "tasks"]); + expect(sideViews(tree)).toEqual(["settings", "tasks"]); + // The rest of the tree is untouched. + expect((tree.root as SplitNode).children[0]?.node).toEqual( + (DEFAULT_LAYOUT.root as SplitNode).children[0]?.node, + ); + }); +}); + +describe("tree queries", () => { + it("firstPanelsNode finds the side panels with its path", () => { + const tree = seedLayout(["model"], ["tabs", "model"]); + const found = firstPanelsNode(tree); + expect(found?.path).toEqual([1]); + expect(found?.node.panels.map((p) => p.view)).toEqual(["model"]); + }); + + it("firstCollapsibleNodeId returns the default tree's side node id", () => { + expect(firstCollapsibleNodeId(DEFAULT_LAYOUT)).toBe("side"); + }); + + it("firstCollapsibleNodeId falls back to a path key when the node has no id", () => { + const tree: LayoutTree = { + version: 1, + root: { + kind: "split", + dir: "row", + children: [ + { size: 1, node: { kind: "leaf", view: "chat" } }, + { + size: 0, + basis: 320, + collapsible: true, + node: { kind: "panels", panels: [{ kind: "leaf", view: "tabs" }] }, + }, + ], + }, + }; + // The collapsible child sits at root path index 1 → key "1". + expect(firstCollapsibleNodeId(tree)).toBe("1"); + }); + + it("firstCollapsibleNodeId returns null when nothing is collapsible", () => { + const tree: LayoutTree = { + version: 1, + root: { + kind: "split", + dir: "row", + children: [{ size: 1, node: { kind: "leaf", view: "chat" } }], + }, + }; + expect(firstCollapsibleNodeId(tree)).toBeNull(); + }); +}); |
