feat: add main features
Agents for developpement added + frontend add + backend added. Git viewer created + agent and template creator + layout and project creator
This commit is contained in:
164
frontend/src/adapters/mock/layout.test.ts
Normal file
164
frontend/src/adapters/mock/layout.test.ts
Normal file
@ -0,0 +1,164 @@
|
||||
/**
|
||||
* 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();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user