60 lines
2.4 KiB
TypeScript
60 lines
2.4 KiB
TypeScript
|
|
import { describe, expect, it } from "vitest";
|
||
|
|
import { layout } from "./layout";
|
||
|
|
import type { Topology } from "./api";
|
||
|
|
|
||
|
|
const TOPO: Topology = {
|
||
|
|
trees: [
|
||
|
|
{ id: "core", label: "Core", root: true },
|
||
|
|
{ id: "sdlc", label: "SDLC", root: false },
|
||
|
|
],
|
||
|
|
nodes: [
|
||
|
|
{ id: "intake", label: "Intake", agent: "coordinator", tree: "core", kind: "phase", gated: false },
|
||
|
|
{ id: "clarify", label: "Clarify", agent: "Claude", tree: "sdlc", kind: "gate", gated: false },
|
||
|
|
{ id: "plan", label: "Plan", agent: "Claude", tree: "sdlc", kind: "phase", gated: false },
|
||
|
|
{ id: "review", label: "Review", agent: "GPT-4.1", tree: "sdlc", kind: "phase", gated: false },
|
||
|
|
],
|
||
|
|
edges: [
|
||
|
|
{ from: "intake", to: "clarify", kind: "spine" },
|
||
|
|
{ from: "clarify", to: "plan", kind: "spine" },
|
||
|
|
{ from: "plan", to: "review", kind: "spine" },
|
||
|
|
{ from: "review", to: "plan", kind: "loopback" },
|
||
|
|
],
|
||
|
|
};
|
||
|
|
|
||
|
|
describe("layout", () => {
|
||
|
|
it("produces a laid-out node per topology node", () => {
|
||
|
|
const { nodes, edges } = layout(TOPO, {}, null);
|
||
|
|
expect(nodes).toHaveLength(4);
|
||
|
|
expect(edges).toHaveLength(4);
|
||
|
|
// every node has a numeric position assigned by dagre
|
||
|
|
for (const n of nodes) {
|
||
|
|
expect(typeof n.position.x).toBe("number");
|
||
|
|
expect(typeof n.position.y).toBe("number");
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
it("applies live state + count to the matching node", () => {
|
||
|
|
const { nodes } = layout(TOPO, { clarify: { state: "awaiting_human", count: 2 } }, null);
|
||
|
|
const clarify = nodes.find((n) => n.id === "clarify")!;
|
||
|
|
expect(clarify.data.state).toBe("awaiting_human");
|
||
|
|
expect(clarify.data.count).toBe(2);
|
||
|
|
const plan = nodes.find((n) => n.id === "plan")!;
|
||
|
|
expect(plan.data.state).toBe("idle");
|
||
|
|
});
|
||
|
|
|
||
|
|
it("marks the selected task's path and dims the rest", () => {
|
||
|
|
const path = new Set(["intake", "clarify"]);
|
||
|
|
const { nodes } = layout(TOPO, {}, path);
|
||
|
|
expect(nodes.find((n) => n.id === "intake")!.data.onPath).toBe(true);
|
||
|
|
expect(nodes.find((n) => n.id === "intake")!.data.dimmed).toBe(false);
|
||
|
|
expect(nodes.find((n) => n.id === "review")!.data.onPath).toBe(false);
|
||
|
|
expect(nodes.find((n) => n.id === "review")!.data.dimmed).toBe(true);
|
||
|
|
});
|
||
|
|
|
||
|
|
it("styles loopback edges distinctly (dashed)", () => {
|
||
|
|
const { edges } = layout(TOPO, {}, null);
|
||
|
|
const loop = edges.find((e) => e.id === "review->plan")!;
|
||
|
|
expect(loop.style?.strokeDasharray).toBeTruthy();
|
||
|
|
});
|
||
|
|
});
|