Polished command-center for FlowMaster with two data modes:
- SNAPSHOT: bundled src/scenarios.json from demo.flow-master.ai
- LIVE: in-browser fetch via src/lib/api.ts (dev-login + bearer)
Scenarios:
- procurement, extra-1, extra-2 (live from EA2)
- ar, hcm, gl, service (industry blueprints, same typed shell)
Honesty pass after Oracle review:
- No invented numbers (Telemetry derives SLA + agent acceptance from real data)
- Preview-only actions fire toasts naming the endpoint to wire them
- Blueprint tours framed as 'industry blueprint', not 'we don't have this yet'
- Mode pill + last-fetch age + refresh in topbar
- Dev CORS dodged via vite proxy; production deploys same-origin
18 vitest tests + 26 playwright smoke assertions + DOM layout audit.
Constraint: cross-origin live mode rejected by browser → fall back to snapshot
Rejected: hardcoded SLA % | dishonest demo metrics
Directive: wire preview-only action handlers to /api/runtime/transactions/{id}/actions to ship them for real
Confidence: high
Scope-risk: narrow
Not-tested: production deployment via flowmaster-ops overlay
40 lines
1.6 KiB
TypeScript
40 lines
1.6 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import { layoutGraph } from "./layout";
|
|
import type { ProcessStep, FlowEdge } from "../data/types";
|
|
|
|
const linearSteps: ProcessStep[] = [
|
|
{ id: "a", name: "A", kind: "start", owner: "system", governs: [], state: "done", actions: [] },
|
|
{ id: "b", name: "B", kind: "human", owner: "human", governs: [], state: "running", actions: [] },
|
|
{ id: "c", name: "C", kind: "end", owner: "system", governs: [], state: "idle", actions: [] },
|
|
];
|
|
const linearEdges: FlowEdge[] = [
|
|
{ id: "ab", source: "a", target: "b" },
|
|
{ id: "bc", source: "b", target: "c" },
|
|
];
|
|
|
|
describe("layoutGraph", () => {
|
|
it("returns one position per step", () => {
|
|
const positions = layoutGraph(linearSteps, linearEdges);
|
|
expect(positions.length).toBe(linearSteps.length);
|
|
expect(new Set(positions.map((p) => p.id))).toEqual(new Set(["a", "b", "c"]));
|
|
});
|
|
|
|
it("produces strictly-increasing x positions for a linear chain (LR layout)", () => {
|
|
const positions = layoutGraph(linearSteps, linearEdges);
|
|
const byId = Object.fromEntries(positions.map((p) => [p.id, p.position]));
|
|
expect(byId.a.x).toBeLessThan(byId.b.x);
|
|
expect(byId.b.x).toBeLessThan(byId.c.x);
|
|
});
|
|
|
|
it("handles disconnected nodes without crashing", () => {
|
|
const positions = layoutGraph(
|
|
[
|
|
{ id: "x", name: "X", kind: "start", owner: "system", governs: [], state: "idle", actions: [] },
|
|
{ id: "y", name: "Y", kind: "end", owner: "system", governs: [], state: "idle", actions: [] },
|
|
],
|
|
[],
|
|
);
|
|
expect(positions.length).toBe(2);
|
|
});
|
|
});
|