| 1 | // node --test src/diagrams/*.test.ts (in apps/docs) |
| 2 | import assert from 'node:assert/strict'; |
| 3 | import { test } from 'node:test'; |
| 4 | |
| 5 | import { type Graph, type GraphNode, render, textWidth, toMarkdown } from './engine.ts'; |
| 6 | import { DIAGRAMS } from './index.ts'; |
| 7 | |
| 8 | const graphs = Object.entries(DIAGRAMS).filter((entry): entry is [string, Graph] => entry[1].kind === 'graph'); |
| 9 | const size = (node: GraphNode) => ({ w: node.w ?? 150, h: node.h ?? 52 }); |
| 10 | |
| 11 | test('every diagram draws, and reads as text', () => { |
| 12 | for (const [name, spec] of Object.entries(DIAGRAMS)) { |
| 13 | const svg = render(`d-${name}`, spec); |
| 14 | assert.match(svg, /^<svg /, name); |
| 15 | assert.ok(toMarkdown(spec).startsWith(`**Diagram: ${spec.caption}**`), name); |
| 16 | } |
| 17 | }); |
| 18 | |
| 19 | test("every box is inside its diagram, and no two boxes overlap", () => { |
| 20 | for (const [name, graph] of graphs) { |
| 21 | const ids = new Set<string>(); |
| 22 | for (const node of graph.nodes) { |
| 23 | assert.ok(!ids.has(node.id), `${name}: two boxes are called ${node.id}`); |
| 24 | ids.add(node.id); |
| 25 | const { w, h } = size(node); |
| 26 | assert.ok(node.x >= 0 && node.y >= 0 && node.x + w <= graph.width && node.y + h <= graph.height, `${name}: ${node.id} is outside the diagram`); |
| 27 | } |
| 28 | for (const [i, a] of graph.nodes.entries()) { |
| 29 | for (const b of graph.nodes.slice(i + 1)) { |
| 30 | const sa = size(a); |
| 31 | const sb = size(b); |
| 32 | const apart = a.x + sa.w <= b.x || b.x + sb.w <= a.x || a.y + sa.h <= b.y || b.y + sb.h <= a.y; |
| 33 | assert.ok(apart, `${name}: ${a.id} and ${b.id} overlap`); |
| 34 | } |
| 35 | } |
| 36 | } |
| 37 | }); |
| 38 | |
| 39 | test("every box's text fits inside it", () => { |
| 40 | for (const [name, graph] of graphs) { |
| 41 | for (const node of graph.nodes) { |
| 42 | const { w } = size(node); |
| 43 | assert.ok(textWidth(node.title, 12.5) <= w - 8, `${name}: the title of ${node.id} is wider than its box`); |
| 44 | // The second line is set in the mono face. |
| 45 | if (node.sub) assert.ok(textWidth(`\`${node.sub.replace(/`/g, '')}\``, 10) <= w - 8, `${name}: the second line of ${node.id} is wider than its box`); |
| 46 | } |
| 47 | } |
| 48 | }); |
| 49 | |
| 50 | test('an arrow to a box that does not exist is an error, not a missing line', () => { |
| 51 | const broken: Graph = { kind: 'graph', caption: 'x', width: 100, height: 100, nodes: [], edges: [{ from: 'a', to: 'b' }] }; |
| 52 | assert.throws(() => render('d-broken', broken), /not a box/); |
| 53 | }); |