Agents for developpement added + frontend add + backend added. Git viewer created + agent and template creator + layout and project creator
165 lines
5.9 KiB
TypeScript
165 lines
5.9 KiB
TypeScript
/**
|
|
* L4 — behavioural tests for {@link MockLayoutGateway}: it defaults to a single
|
|
* empty leaf, applies mutations and returns the new tree, and keeps a separate
|
|
* in-memory tree per project.
|
|
*/
|
|
import { describe, it, expect } from "vitest";
|
|
|
|
import { leaves } from "@/features/layout/layout";
|
|
import { MockLayoutGateway } from "./index";
|
|
|
|
describe("MockLayoutGateway", () => {
|
|
it("loadLayout defaults to a single empty leaf", async () => {
|
|
const gw = new MockLayoutGateway();
|
|
const tree = await gw.loadLayout("p1");
|
|
expect(tree.root.type).toBe("leaf");
|
|
const ls = leaves(tree);
|
|
expect(ls).toHaveLength(1);
|
|
expect(ls[0].session ?? null).toBeNull();
|
|
});
|
|
|
|
it("loadLayout returns a stable tree for the same project", async () => {
|
|
const gw = new MockLayoutGateway();
|
|
const a = await gw.loadLayout("p1");
|
|
const b = await gw.loadLayout("p1");
|
|
expect(b).toEqual(a); // same persisted tree (id preserved)
|
|
});
|
|
|
|
it("mutateLayout applies the op and returns the mutated tree", async () => {
|
|
const gw = new MockLayoutGateway();
|
|
const initial = await gw.loadLayout("p1");
|
|
const leafId = leaves(initial)[0].id;
|
|
|
|
const next = await gw.mutateLayout("p1", {
|
|
type: "split",
|
|
target: leafId,
|
|
direction: "row",
|
|
newLeaf: "b",
|
|
container: "c",
|
|
});
|
|
expect(next.root.type).toBe("split");
|
|
expect(leaves(next)).toHaveLength(2);
|
|
});
|
|
|
|
it("persists the mutation in memory (next load reflects it)", async () => {
|
|
const gw = new MockLayoutGateway();
|
|
const initial = await gw.loadLayout("p1");
|
|
const leafId = leaves(initial)[0].id;
|
|
await gw.mutateLayout("p1", {
|
|
type: "setSession",
|
|
target: leafId,
|
|
session: "s9",
|
|
});
|
|
const reloaded = await gw.loadLayout("p1");
|
|
expect(leaves(reloaded)[0].session).toBe("s9");
|
|
});
|
|
|
|
it("keeps a separate tree per project", async () => {
|
|
const gw = new MockLayoutGateway();
|
|
const p1 = await gw.loadLayout("p1");
|
|
const p2 = await gw.loadLayout("p2");
|
|
await gw.mutateLayout("p1", {
|
|
type: "split",
|
|
target: leaves(p1)[0].id,
|
|
direction: "row",
|
|
newLeaf: "b",
|
|
container: "c",
|
|
});
|
|
// p2 is untouched.
|
|
const p2After = await gw.loadLayout("p2");
|
|
expect(p2After).toEqual(p2);
|
|
expect(leaves(p2After)).toHaveLength(1);
|
|
});
|
|
|
|
it("returns clones, so mutating a returned tree does not corrupt the store", async () => {
|
|
const gw = new MockLayoutGateway();
|
|
const tree = await gw.loadLayout("p1");
|
|
if (tree.root.type === "leaf") tree.root.node.session = "tampered";
|
|
const fresh = await gw.loadLayout("p1");
|
|
expect(leaves(fresh)[0].session ?? null).toBeNull();
|
|
});
|
|
|
|
// ── Multi-layout tests (#4) ────────────────────────────────────────────────
|
|
|
|
it("listLayouts returns a 'Default' layout on first call", async () => {
|
|
const gw = new MockLayoutGateway();
|
|
const { layouts, activeId } = await gw.listLayouts("p1");
|
|
expect(layouts).toHaveLength(1);
|
|
expect(layouts[0].name).toBe("Default");
|
|
expect(activeId).toBe(layouts[0].id);
|
|
});
|
|
|
|
it("createLayout adds a new named layout", async () => {
|
|
const gw = new MockLayoutGateway();
|
|
const { layoutId } = await gw.createLayout("p1", "Beta");
|
|
const { layouts } = await gw.listLayouts("p1");
|
|
expect(layouts).toHaveLength(2);
|
|
expect(layouts.some((l) => l.id === layoutId && l.name === "Beta")).toBe(true);
|
|
});
|
|
|
|
it("renameLayout changes the layout name", async () => {
|
|
const gw = new MockLayoutGateway();
|
|
const { layouts } = await gw.listLayouts("p1");
|
|
await gw.renameLayout("p1", layouts[0].id, "Renamed");
|
|
const { layouts: updated } = await gw.listLayouts("p1");
|
|
expect(updated[0].name).toBe("Renamed");
|
|
});
|
|
|
|
it("deleteLayout removes a layout and adjusts the active id", async () => {
|
|
const gw = new MockLayoutGateway();
|
|
const { layoutId: secondId } = await gw.createLayout("p1", "Second");
|
|
await gw.setActiveLayout("p1", secondId);
|
|
const { activeId } = await gw.deleteLayout("p1", secondId);
|
|
const { layouts, activeId: newActive } = await gw.listLayouts("p1");
|
|
expect(layouts).toHaveLength(1);
|
|
expect(layouts.some((l) => l.id === secondId)).toBe(false);
|
|
// active should have switched back to the Default.
|
|
expect(activeId).toBe(newActive);
|
|
});
|
|
|
|
it("setActiveLayout switches the active layout", async () => {
|
|
const gw = new MockLayoutGateway();
|
|
const { layoutId } = await gw.createLayout("p1", "Alt");
|
|
await gw.setActiveLayout("p1", layoutId);
|
|
const { activeId } = await gw.listLayouts("p1");
|
|
expect(activeId).toBe(layoutId);
|
|
});
|
|
|
|
it("loadLayout with a specific layoutId loads that layout's tree", async () => {
|
|
const gw = new MockLayoutGateway();
|
|
const { layoutId } = await gw.createLayout("p1", "Named");
|
|
// Mutate the named layout.
|
|
const tree = await gw.loadLayout("p1", layoutId);
|
|
const leafId = leaves(tree)[0].id;
|
|
await gw.mutateLayout("p1", { type: "setSession", target: leafId, session: "s-named" }, layoutId);
|
|
|
|
// The default (active) layout should be untouched.
|
|
const defaultTree = await gw.loadLayout("p1");
|
|
expect(leaves(defaultTree)[0].session ?? null).toBeNull();
|
|
|
|
// The named layout should have the session.
|
|
const namedTree = await gw.loadLayout("p1", layoutId);
|
|
expect(leaves(namedTree)[0].session).toBe("s-named");
|
|
});
|
|
|
|
it("setCellAgent persists and clears correctly in applyOperation", async () => {
|
|
const gw = new MockLayoutGateway();
|
|
const tree = await gw.loadLayout("p1");
|
|
const leafId = leaves(tree)[0].id;
|
|
|
|
const withAgent = await gw.mutateLayout("p1", {
|
|
type: "setCellAgent",
|
|
target: leafId,
|
|
agent: "agent-99",
|
|
});
|
|
expect(leaves(withAgent)[0].agent).toBe("agent-99");
|
|
|
|
const cleared = await gw.mutateLayout("p1", {
|
|
type: "setCellAgent",
|
|
target: leafId,
|
|
agent: null,
|
|
});
|
|
expect(leaves(cleared)[0].agent).toBeUndefined();
|
|
});
|
|
});
|