// tests/integration/annotation-block-boundary.test.mjs // Step 17 — verify relocateAnchorsToBlockBoundaries pure-function transforms // markdown anchors away from atomic-block interiors (fenced code, tables, // deeply-nested lists) toward the block-boundary line. // // Function lives in playground/voyage-playground.html as inline-script (file:// // compat). We extract it via balanced-brace scan and exercise via Function(). import { test } from 'node:test'; import { strict as assert } from 'node:assert'; import { readFileSync } from 'node:fs'; import { dirname, resolve, join } from 'node:path'; import { fileURLToPath } from 'node:url'; const __dirname = dirname(fileURLToPath(import.meta.url)); const ROOT = resolve(__dirname, '..', '..'); const HTML = join(ROOT, 'playground', 'voyage-playground.html'); function extractFunctionSource(text, fnName) { const needle = `function ${fnName}`; const start = text.indexOf(needle); if (start === -1) return null; const braceStart = text.indexOf('{', start); if (braceStart === -1) return null; let depth = 0; for (let i = braceStart; i < text.length; i++) { if (text[i] === '{') depth++; else if (text[i] === '}') { depth--; if (depth === 0) return text.slice(start, i + 1); } } return null; } function loadRelocate() { const html = readFileSync(HTML, 'utf-8'); const src = extractFunctionSource(html, 'relocateAnchorsToBlockBoundaries'); if (!src) throw new Error('relocateAnchorsToBlockBoundaries not found in HTML'); // Function() factory creates an isolated scope; safe for pure function. // eslint-disable-next-line no-new-func const factory = new Function(`${src}; return relocateAnchorsToBlockBoundaries;`); return factory(); } const relocate = loadRelocate(); test('relocateAnchorsToBlockBoundaries returns input unchanged when anchors empty', () => { const md = 'Line 1\nLine 2\nLine 3\n'; assert.equal(relocate(md, []), md); }); test('relocateAnchorsToBlockBoundaries leaves anchor outside atomic block at original line', () => { const lines = []; for (let i = 1; i <= 20; i++) lines.push(`Line ${i}`); const md = lines.join('\n'); const out = relocate(md, [{ id: 'ANN-0001', target: 'sec-a', line: 5 }]); const outLines = out.split('\n'); // Anchor injected at output line 5 (1-indexed = index 4); blank line at index 5 assert.match(outLines[4], /