Files
2026-10-15 11:57:09 +02:00

58 lines
3.3 KiB
JavaScript

import { test } from 'node:test';
import assert from 'node:assert/strict';
import { createRequire } from 'node:module';
import { createSimulation, neighbourhood } from '../src/lib/graph.js';
import { nodeColor } from '../src/lib/graphColors.js';
const require = createRequire(import.meta.url);
const { graph } = require('../electron/links.cjs');
test('graph data: one edge per linked pair, ghosts for missing pages, code ignored', () => {
const P = (id, body) => ({ id, title: id.split('/').pop(), parent: id.includes('/') ? id.slice(0, id.lastIndexOf('/')) : null, exists: true, body });
const g = graph([P('A', '[[B]] [[B]] [[Nope]] `[[C]]`'), P('B', '[[A]] [[a]] [[B]]'), P('C', ''), P('X/Y', '[[C]]'), { id: 'X', title: 'X', exists: false, body: '' }]);
assert.deepEqual(g.nodes.map((n) => n.id), ['A', 'B', 'C', 'X/Y']);
assert.deepEqual(g.edges, [['A', 'B'], ['A', '?nope'], ['B', 'A'], ['X/Y', 'C']]);
assert.deepEqual(g.ghosts, [{ id: '?nope', title: 'Nope' }]);
});
test('simulation settles: linked notes close, clusters apart, nothing stacked', () => {
const ids = Array.from({ length: 40 }, (_, i) => 'n' + i);
const edges = [];
for (let i = 1; i < 20; i++) edges.push(['n0', 'n' + i]);
for (let i = 21; i < 36; i++) edges.push(['n20', 'n' + i]);
const sim = createSimulation().setData(ids.map((id) => ({ id })), edges);
let n = 0;
while (sim.running && n < 2000) { sim.tick(); n++; }
assert.ok(n < 600, `settles (${n} ticks)`);
const N = sim.nodes, d = (a, b) => Math.hypot(N[a].x - N[b].x, N[a].y - N[b].y);
const spoke = d(0, 1);
assert.ok(spoke > 40 && spoke < 400, `spoke length ${spoke}`);
assert.ok(d(0, 20) > spoke * 1.5, 'the two hubs push apart');
let closest = Infinity;
for (let i = 0; i < 40; i++) for (let j = i + 1; j < 40; j++) closest = Math.min(closest, d(i, j));
assert.ok(closest > 5, `no stacked nodes (closest ${closest})`);
assert.equal(N[0].degree, 19);
});
test('dragged (fixed) nodes stay put; stacked starts separate; neighbourhood depth', () => {
const sim = createSimulation().setData([{ id: 'a', x: 0, y: 0 }, { id: 'b', x: 0, y: 0 }, { id: 'c', x: 0, y: 0 }], [['a', 'b']]);
sim.nodes[0].fx = 50;
sim.nodes[0].fy = 50;
sim.tick(200);
assert.deepEqual([sim.nodes[0].x, sim.nodes[0].y], [50, 50]);
assert.ok(Math.hypot(sim.nodes[1].x - sim.nodes[2].x, sim.nodes[1].y - sim.nodes[2].y) > 5);
const edges = [['a', 'b'], ['b', 'c'], ['c', 'd'], ['x', 'y']];
assert.deepEqual([...neighbourhood('b', edges, 1)].sort(), ['a', 'b', 'c']);
assert.deepEqual([...neighbourhood('a', edges, 3)].sort(), ['a', 'b', 'c', 'd']);
});
test('graph colours: a coloured tag colours its pages; everything else stays plain', () => {
const colors = { project: 'purple', asm: 'green' };
assert.equal(nodeColor(['project'], colors, '#plain'), '#9a6de0');
assert.equal(nodeColor(['study', 'asm', 'project'], colors, '#plain'), '#44ad6e', 'the first of its tags that has a colour');
assert.equal(nodeColor(['study'], colors, '#plain'), '#plain', 'a tag without a colour does not colour its pages');
assert.equal(nodeColor(['study'], { study: 'not-a-colour' }, '#plain'), '#plain', 'an unknown colour name is ignored');
assert.equal(nodeColor([], colors, '#plain'), '#plain');
assert.equal(nodeColor(undefined, colors, '#plain'), '#plain');
});