fix: reduce landing to existing documents and their tree
Reference site / build (push) Canceled after 0s
Reference site / build (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { readdir, readFile } from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import test from 'node:test';
|
||||
import { contentsRoot } from '../plugins/document-links.mjs';
|
||||
import { documentTree, childrenOf, visibleNodes, treePrefix, serializeTree } from '../src/lib/landing-tree.ts';
|
||||
|
||||
async function sourceFiles(directory, prefix = '') {
|
||||
const entries = await readdir(directory, { withFileTypes: true });
|
||||
return (await Promise.all(entries.map(async (entry) => {
|
||||
const relative = prefix + entry.name;
|
||||
if (entry.isDirectory()) return sourceFiles(path.join(directory, entry.name), relative + '/');
|
||||
return entry.name.endsWith('.md') ? [relative] : [];
|
||||
}))).flat();
|
||||
}
|
||||
const files = await sourceFiles(contentsRoot);
|
||||
const documents = await Promise.all(files.map(async (file) => {
|
||||
const body = await readFile(path.join(contentsRoot, file), 'utf8');
|
||||
return { id: file.slice(0, -3), title: body.match(/^#\s+(.+)$/m)?.[1], href: '/book/' + file.slice(0, -3) + '/', html: body };
|
||||
}));
|
||||
const nodes = documentTree(documents);
|
||||
|
||||
test('tree mirrors only real content files and their directory parents', () => {
|
||||
assert.deepEqual(nodes.filter((node) => node.href).map((node) => node.path).sort(), files.map((file) => '/' + file).sort());
|
||||
const directories = new Set(['/']);
|
||||
for (const file of files) {
|
||||
const segments = file.split('/');
|
||||
for (let length = 1; length < segments.length; length++) directories.add('/' + segments.slice(0, length).join('/'));
|
||||
}
|
||||
assert.deepEqual(new Set(nodes.filter((node) => !node.href).map((node) => node.path)), directories);
|
||||
assert.equal(new Set(nodes.map((node) => node.path)).size, nodes.length);
|
||||
for (const node of nodes.slice(1)) {
|
||||
assert.equal(node.name, node.path.split('/').at(-1));
|
||||
assert.equal(node.depth, node.path.split('/').length);
|
||||
assert.ok(childrenOf(nodes, node.parent).includes(node));
|
||||
assert.ok(nodes.findIndex((parent) => parent.path === node.parent) < nodes.indexOf(node));
|
||||
}
|
||||
});
|
||||
|
||||
test('existing titles, hrefs and content pass through without added descriptions or operations', () => {
|
||||
for (const document of documents) {
|
||||
const node = nodes.find((node) => node.path === '/' + document.id + '.md');
|
||||
assert.equal(node.html, document.html);
|
||||
assert.equal(node.title, document.title);
|
||||
assert.equal(node.href, document.href);
|
||||
assert.equal('description' in node, false);
|
||||
assert.equal('interface' in node, false);
|
||||
}
|
||||
for (const folder of nodes.filter((node) => !node.href)) {
|
||||
assert.equal(folder.html, undefined);
|
||||
assert.equal(folder.title, undefined);
|
||||
}
|
||||
});
|
||||
|
||||
test('collapse hides descendants only and joins follow the visible tree', () => {
|
||||
assert.deepEqual(visibleNodes(nodes, new Set()), nodes);
|
||||
assert.deepEqual(visibleNodes(nodes, new Set(['/'])), [nodes[0]]);
|
||||
const collapsed = visibleNodes(nodes, new Set(['/draft']));
|
||||
assert.ok(collapsed.some((node) => node.path === '/draft'));
|
||||
assert.ok(!collapsed.some((node) => node.path.startsWith('/draft/')));
|
||||
for (const shown of [nodes, collapsed]) {
|
||||
for (const node of shown) {
|
||||
const prefix = treePrefix(node, nodes, shown);
|
||||
assert.equal(prefix.length, (node.depth - 1) * 4);
|
||||
if (node.parent) assert.ok(prefix.endsWith(childrenOf(shown, node.parent).at(-1) === node ? '└── ' : '├── '));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('embedded data cannot close its JSON script and roundtrips exact content', () => {
|
||||
const input = documentTree([{ id: 'source', title: '<title>', href: '/book/source/', html: '</script><script>alert(1)</script>\u2028&' }]);
|
||||
const serialized = serializeTree(input);
|
||||
assert.equal(serialized.includes('<'), false);
|
||||
assert.deepEqual(JSON.parse(serialized), input);
|
||||
});
|
||||
Reference in New Issue
Block a user