import { test } from 'node:test';
import assert from 'node:assert/strict';
import { createRequire } from 'node:module';
import { parseMarkdown, toMarkdown, mdToHtml, htmlToMd } from '../src/lib/markdown.js';
import { headingOf, linkLabel, normHeading, refPage } from '../src/lib/links.js';
import { headingsOf, sectionOf, staticHtml } from '../src/lib/embed.js';
const require = createRequire(import.meta.url);
const { findLinks, rewriteLinks, backlinks, graph } = require('../electron/links.cjs');
test('heading links: label, parts and round trip', () => {
assert.equal(refPage('Plans/Road map#Q3'), 'Plans/Road map');
assert.equal(headingOf('Road map#Q3 goals'), 'Q3 goals');
assert.equal(headingOf('Page#One#Two'), 'Two');
assert.equal(headingOf('Page#^abc123'), '');
assert.equal(headingOf('Page'), '');
assert.equal(linkLabel('Plans/Road map#Q3'), 'Road map › Q3');
assert.equal(linkLabel('#Local'), 'Local');
assert.equal(linkLabel('Page'), 'Page');
assert.equal(normHeading(' Q3 Goals '), 'q3 goals');
const md = 'See [[Road map#Q3]], [[#Here]] and [[Page#Part|the part]].';
const html = mdToHtml(md);
assert.match(html, /data-ref="Road map#Q3">Road map › Q3);
assert.match(html, /data-ref="#Here">Here);
assert.match(html, /data-ref="Page#Part">the part);
assert.equal(htmlToMd(html), md);
});
test('![[embeds]] are blocks; pictures and other files are not', () => {
const md = 'Before\n\n![[Roadmap]]\n\n![[Plans/Roadmap#Q3 goals|Goals]]\n\n![[photo.png]]\n\n![[slides.pdf]]\n\nAfter\n';
const blocks = parseMarkdown(md);
assert.deepEqual(blocks.map((b) => b.type), ['p', 'embed', 'embed', 'image', 'p', 'p']);
assert.equal(blocks[1].ref, 'Roadmap');
assert.equal(blocks[2].ref, 'Plans/Roadmap#Q3 goals');
assert.equal(blocks[2].alias, 'Goals');
assert.equal(blocks[4].html, '![[slides.pdf]]'); // unsupported files stay as text
assert.equal(toMarkdown(blocks), md);
});
const DOC = parseMarkdown('Intro\n\n# Top\n\nunder top\n\n## Alpha\n\n- one\n- [x] two\n\n### Deep\n\nlow\n\n## Beta\n\nbeta text\n');
test('sections: a heading and everything below it, up to the next heading of the same or a higher level', () => {
assert.deepEqual(headingsOf(DOC).map((h) => `${h.level}:${h.text}`), ['1:Top', '2:Alpha', '3:Deep', '2:Beta']);
assert.deepEqual(sectionOf(DOC, 'alpha').map((b) => b.type), ['h2', 'bullet', 'todo', 'h3', 'p']);
assert.equal(sectionOf(DOC, ' ALPHA ').length, 5);
assert.deepEqual(sectionOf(DOC, 'Beta').map((b) => b.type), ['h2', 'p']);
assert.equal(sectionOf(DOC, 'Top').length, 9); // everything after the intro line
assert.equal(sectionOf(DOC, 'Nope'), null);
});
test('embedded content renders read-only, escaped, and with no editor hooks', () => {
const blocks = parseMarkdown('## Part\n\nSome **bold** & \n\n- [x] done\n\n```js\nlet a = 1 < 2\n```\n\n| a | b |\n|---|---|\n| 1 | 2 |\n\n\n\n![[Other]]\n\n> [!warning]\n> careful\n');
const html = staticHtml(blocks, { imageUrl: (s) => `media://x/${s}` });
assert.ok(!html.includes('contenteditable="true"'), 'nothing editable');
assert.ok(!html.includes('class="block'), 'no editor block wrappers');
assert.ok(!html.includes('