tscircuit/ti
ti tscircuit library
- Version
- 1.0.198
- License
- unset
- Stars
- 0
system-block-ui/src/model/system-diagram-svg.test.ts
import { describe, expect, test } from "bun:test";
import type {
BlockInstance,
ResolvedConnection,
SubcircuitDefinition,
} from "./types";
import { renderSystemDiagramSvg } from "./system-diagram-svg";
const makeDefinition = (
id: string,
title: string,
kinds: readonly ("power" | "data")[] = [],
): SubcircuitDefinition => ({
id,
title,
category: "Test & validation",
componentName: `Component_${id}`,
importPath: "test-components",
sourcePath: `${id}.circuit.tsx`,
ports: kinds.map((kind) => ({
id: `${kind}-port`,
label: kind,
kind,
role: kind === "power" ? "consumer" : "device",
protocol: kind === "power" ? "power" : "i2c",
signals: [],
})),
});
const connection = ({
id,
fromBlockId,
toBlockId,
kind,
protocol,
}: {
id: string;
fromBlockId: string;
toBlockId: string;
kind: "power" | "data";
protocol?: string;
}): ResolvedConnection => ({
id,
fromBlockId,
toBlockId,
kind,
protocol,
fromPortId: `${fromBlockId}-out`,
toPortId: `${toBlockId}-in`,
score: 1,
traces: [
{
signal: kind === "power" ? "positive" : "signal",
fromBlockId,
toBlockId,
fromSelector: ".OUT",
toSelector: ".IN",
},
],
});
const expectTransparentCanvas = (svg: string): void => {
const dimensions = svg.match(/^<svg[^>]* width="(\d+)" height="(\d+)"/);
expect(dimensions).not.toBeNull();
if (!dimensions) throw new Error("Missing generated SVG dimensions");
expect(svg).not.toContain(
`<rect width="${dimensions[1]}" height="${dimensions[2]}"`,
);
};
describe("system diagram SVG", () => {
test("is deterministic, escapes XML, and renders every resolved connection", () => {
const catalog = [
makeDefinition("source", 'Main <Power> & "Battery"', ["power"]),
makeDefinition("controller", "Controller `${unsafe}`", ["power", "data"]),
makeDefinition("sensor", "Sensor", ["power", "data"]),
];
const blocks: BlockInstance[] = [
{
id: "source&one",
definitionId: "source",
name: "main_source",
position: { x: -180, y: -40 },
},
{
id: "controller",
definitionId: "controller",
name: "controller",
position: { x: 180, y: 20 },
},
{
id: "sensor",
definitionId: "sensor",
name: "sensor",
position: { x: 560, y: 180 },
},
];
const resolvedConnections = [
connection({
id: "power-source-controller",
fromBlockId: "source&one",
toBlockId: "controller",
kind: "power",
}),
connection({
id: "power-source-sensor",
fromBlockId: "source&one",
toBlockId: "sensor",
kind: "power",
}),
connection({
id: "data-controller-sensor",
fromBlockId: "controller",
toBlockId: "sensor",
kind: "data",
protocol: "i2c",
}),
];
const forward = renderSystemDiagramSvg({
blocks,
resolvedConnections,
catalog,
});
const reversed = renderSystemDiagramSvg({
blocks: [...blocks].reverse(),
resolvedConnections: [...resolvedConnections].reverse(),
catalog: [...catalog].reverse(),
});
expect(forward).toBe(reversed);
expect(forward).toStartWith(
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ',
);
expect(forward).toContain('data-block-id="source&one"');
expect(forward).toContain("Main <Power> & "Battery"");
expect(forward).not.toContain("Main <Power>");
expect(forward.match(/data-kind="power"/g)).toHaveLength(2);
expect(forward.match(/data-kind="data"/g)).toHaveLength(1);
expect(forward).toContain(
'data-connection-id="power-source-controller" data-kind="power"',
);
expect(forward).toContain(
'data-connection-id="power-source-sensor" data-kind="power"',
);
expect(forward).not.toContain("__power-summary__");
expect(forward).toContain("3 resolved semantic connections");
expect(forward).toContain("3 blocks · 3 resolved links");
expect(forward).toContain("Data · I²C");
expectTransparentCanvas(forward);
expect(forward).not.toContain('width="44" height="44" rx="9"');
expect(forward).not.toContain('fill="#fff1f2"');
expect(forward).not.toContain(">SOUR</text>");
expect(forward).toContain('<text x="64" y="159" fill="#1f2937"');
expect(forward).toContain('height="136" rx="12" fill="#ffffff"');
expect(forward).toContain('height="28" rx="14" fill="#ffffff"');
});
test("uses a stable grid whenever a graph has missing positions", () => {
const catalog = [
makeDefinition("alpha", "Alpha"),
makeDefinition("beta", "Beta"),
makeDefinition("gamma", "Gamma"),
];
const blocks: BlockInstance[] = [
{ id: "gamma", definitionId: "gamma" },
{
id: "alpha",
definitionId: "alpha",
position: { x: 9_999, y: 9_999 },
},
{ id: "beta", definitionId: "beta" },
];
const first = renderSystemDiagramSvg({
blocks,
resolvedConnections: [],
catalog,
});
const second = renderSystemDiagramSvg({
blocks: [...blocks].reverse(),
resolvedConnections: [],
catalog,
});
expect(first).toBe(second);
expect(first).not.toContain("9999");
expect(first.match(/data-block-id=/g)).toHaveLength(3);
});
test("renders an informative standalone page for an empty graph", () => {
const svg = renderSystemDiagramSvg({
blocks: [],
resolvedConnections: [],
catalog: [],
});
expect(svg).toContain('viewBox="0 0 900 520"');
expect(svg).toContain("No system blocks yet");
expect(svg).toContain("0 blocks · 0 resolved links");
expectTransparentCanvas(svg);
expect(svg).toContain('rx="14" fill="#ffffff"');
});
});