import test from "node:test"; import assert from "node:assert/strict"; import { compile } from "../src/compile.js"; import fixture from "../examples/esp32-audio.json" with { type: "json" }; test("compile returns svg and topology for valid model", () => { const result = compile(fixture); assert.equal(result.ok, true); assert.ok(result.svg.includes(" { const bad = { symbols: {}, instances: [], nets: [] }; const result = compile(bad); assert.equal(result.ok, false); assert.ok(result.errors.length > 0); assert.equal(result.layout_metrics.total_bends, 0); assert.equal(result.layout_metrics.detour_ratio, 1); }); test("compile accepts render mode options", () => { const result = compile(fixture, { render_mode: "explicit" }); assert.equal(result.ok, true); assert.equal(result.render_mode_used, "explicit"); }); test("compile auto-creates generic symbols for unknown instances", () => { const model = { meta: { title: "Generic Demo" }, symbols: {}, instances: [ { ref: "X1", symbol: "mystery_block", properties: { value: "Mystery" }, placement: { x: null, y: null, rotation: 0, locked: false } }, { ref: "X2", symbol: "mystery_sensor", properties: { value: "Sensor" }, placement: { x: null, y: null, rotation: 0, locked: false } } ], nets: [ { name: "SIG_A", class: "signal", nodes: [ { ref: "X1", pin: "IN" }, { ref: "X2", pin: "OUT" } ] }, { name: "GND", class: "ground", nodes: [ { ref: "X1", pin: "GND" }, { ref: "X2", pin: "GND" } ] } ], constraints: {}, annotations: [] }; const result = compile(model); assert.equal(result.ok, true); assert.ok(result.svg.includes(" w.code === "auto_generic_symbol_created")); }); test("compile auto-creates passive templates for common refs", () => { const model = { meta: { title: "Passive Template Demo" }, symbols: {}, instances: [ { ref: "R1", symbol: "resistor_generic", properties: { value: "10k resistor" }, placement: { x: null, y: null, rotation: 0, locked: false } }, { ref: "U1", symbol: "mystery_logic", properties: { value: "Logic" }, placement: { x: null, y: null, rotation: 0, locked: false } } ], nets: [ { name: "SIG", class: "signal", nodes: [ { ref: "R1", pin: "1" }, { ref: "U1", pin: "IN" } ] }, { name: "GND", class: "ground", nodes: [ { ref: "R1", pin: "2" }, { ref: "U1", pin: "GND" } ] } ], constraints: {}, annotations: [] }; const result = compile(model); assert.equal(result.ok, true); assert.ok(result.warnings.some((w) => w.code === "auto_template_symbol_created")); }); test("compile accepts minimal shorthand symbols and hydrates fields", () => { const model = { meta: { title: "Shorthand Symbols" }, symbols: { resistor_short: { template_name: "resistor" }, generic_short: { category: "generic" } }, instances: [ { ref: "R1", symbol: "resistor_short", properties: { value: "1k" }, placement: { x: null, y: null, rotation: 0, locked: false } }, { ref: "X1", symbol: "generic_short", properties: { value: "Mystery" }, placement: { x: null, y: null, rotation: 0, locked: false } } ], nets: [ { name: "SIG", class: "signal", nodes: [ { ref: "R1", pin: "1" }, { ref: "X1", pin: "IO" } ] }, { name: "GND", class: "ground", nodes: [ { ref: "R1", pin: "2" }, { ref: "X1", pin: "GND" } ] } ], constraints: {}, annotations: [] }; const result = compile(model); assert.equal(result.ok, true); assert.ok(result.svg.includes(" w.code === "auto_template_symbol_hydrated")); assert.ok(result.warnings.some((w) => w.code === "auto_generic_symbol_hydrated")); }); test("compile supports instance.part without explicit symbols", () => { const model = { meta: { title: "Part Shortcut" }, symbols: {}, instances: [ { ref: "R1", part: "resistor", properties: { value: "10k" }, placement: { x: null, y: null, rotation: 0, locked: false } }, { ref: "C1", part: "capacitor", properties: { value: "100nF" }, placement: { x: null, y: null, rotation: 0, locked: false } } ], nets: [ { name: "SIG", class: "signal", nodes: [ { ref: "R1", pin: "1" }, { ref: "C1", pin: "1" } ] }, { name: "GND", class: "ground", nodes: [ { ref: "R1", pin: "2" }, { ref: "C1", pin: "2" } ] } ], constraints: {}, annotations: [] }; const result = compile(model); assert.equal(result.ok, true); assert.ok(result.svg.includes(" { const model = { meta: { title: "Group packing" }, symbols: { q: { symbol_id: "q", category: "analog", body: { width: 90, height: 70 }, pins: [ { name: "B", number: "1", side: "left", offset: 35, type: "analog" }, { name: "C", number: "2", side: "top", offset: 45, type: "analog" }, { name: "E", number: "3", side: "bottom", offset: 45, type: "analog" } ] }, adc: { symbol_id: "adc", category: "generic", body: { width: 120, height: 60 }, pins: [ { name: "IN", number: "1", side: "left", offset: 30, type: "analog" }, { name: "3V3", number: "2", side: "top", offset: 30, type: "power_in" }, { name: "GND", number: "3", side: "bottom", offset: 30, type: "ground" } ] } }, instances: [ { ref: "Q1", symbol: "q", properties: {}, placement: { x: null, y: null, rotation: 0, locked: false } }, { ref: "R1", part: "resistor", properties: {}, placement: { x: null, y: null, rotation: 0, locked: false } }, { ref: "R2", part: "resistor", properties: {}, placement: { x: null, y: null, rotation: 0, locked: false } }, { ref: "C1", part: "capacitor", properties: {}, placement: { x: null, y: null, rotation: 0, locked: false } }, { ref: "U1", symbol: "adc", properties: {}, placement: { x: null, y: null, rotation: 0, locked: false } }, { ref: "R3", part: "resistor", properties: {}, placement: { x: null, y: null, rotation: 0, locked: false } }, { ref: "R4", part: "resistor", properties: {}, placement: { x: null, y: null, rotation: 0, locked: false } }, { ref: "C2", part: "capacitor", properties: {}, placement: { x: null, y: null, rotation: 0, locked: false } } ], nets: [ { name: "N1", class: "analog", nodes: [{ ref: "Q1", pin: "C" }, { ref: "U1", pin: "IN" }] }, { name: "N2", class: "ground", nodes: [{ ref: "Q1", pin: "E" }, { ref: "U1", pin: "GND" }] }, { name: "N3", class: "power", nodes: [{ ref: "R1", pin: "1" }, { ref: "U1", pin: "3V3" }] } ], constraints: { groups: [ { name: "front", members: ["Q1", "R1", "R2", "C1"], layout: "cluster" }, { name: "adc", members: ["U1", "R3", "R4", "C2"], layout: "cluster" } ] }, annotations: [] }; const result = compile(model); assert.equal(result.ok, true); const xs = result.layout.placed.map((p) => p.x); const ys = result.layout.placed.map((p) => p.y); const widthSpread = Math.max(...xs) - Math.min(...xs); const heightSpread = Math.max(...ys) - Math.min(...ys); assert.ok(widthSpread > 500); assert.ok(heightSpread < 900); }); test("multi-node signal nets render explicit junction dots", () => { const model = { meta: { title: "Junction coverage" }, symbols: {}, instances: [ { ref: "R1", part: "resistor", properties: {}, placement: { x: null, y: null, rotation: 0, locked: false } }, { ref: "R2", part: "resistor", properties: {}, placement: { x: null, y: null, rotation: 0, locked: false } }, { ref: "R3", part: "resistor", properties: {}, placement: { x: null, y: null, rotation: 0, locked: false } } ], nets: [ { name: "SIG", class: "signal", nodes: [ { ref: "R1", pin: "1" }, { ref: "R2", pin: "1" }, { ref: "R3", pin: "1" } ] }, { name: "GND", class: "ground", nodes: [ { ref: "R1", pin: "2" }, { ref: "R2", pin: "2" }, { ref: "R3", pin: "2" } ] } ], constraints: {}, annotations: [] }; const result = compile(model, { render_mode: "explicit" }); assert.equal(result.ok, true); assert.ok(result.svg.includes('data-net-junction="SIG"')); }); test("auto-rotation chooses non-zero orientation when it improves pin alignment", () => { const model = { meta: { title: "Rotation heuristic" }, symbols: { n1: { symbol_id: "n1", category: "generic", body: { width: 120, height: 80 }, pins: [ { name: "L", number: "1", side: "left", offset: 40, type: "passive" }, { name: "R", number: "2", side: "right", offset: 40, type: "passive" } ] }, n2: { symbol_id: "n2", category: "generic", body: { width: 120, height: 80 }, pins: [ { name: "T", number: "1", side: "top", offset: 60, type: "passive" }, { name: "B", number: "2", side: "bottom", offset: 60, type: "passive" } ] } }, instances: [ { ref: "A1", symbol: "n1", properties: {}, placement: { x: null, y: null, rotation: 0, locked: false } }, { ref: "A2", symbol: "n2", properties: {}, placement: { x: null, y: null, rotation: 0, locked: false } } ], nets: [ { name: "N1", class: "signal", nodes: [{ ref: "A1", pin: "R" }, { ref: "A2", pin: "T" }] }, { name: "N2", class: "ground", nodes: [{ ref: "A1", pin: "L" }, { ref: "A2", pin: "B" }] } ], constraints: {}, annotations: [] }; const result = compile(model, { render_mode: "explicit" }); assert.equal(result.ok, true); assert.ok(result.layout.placed.some((p) => (p.rotation ?? 0) % 360 !== 0)); });