internal-links.test.mjs (2031B)
1 import assert from 'node:assert/strict'; 2 import { readdir, readFile } from 'node:fs/promises'; 3 import test from 'node:test'; 4 5 const SHEETS = new URL('../src/content/sheets/', import.meta.url); 6 7 // Every sheet on disk, keyed the way an in-page link addresses it. 8 const collectSheets = async () => { 9 const sheets = []; 10 for (const category of await readdir(SHEETS, { withFileTypes: true })) { 11 if (!category.isDirectory()) continue; 12 const dir = new URL(`${category.name}/`, SHEETS); 13 for (const file of await readdir(dir)) { 14 if (!file.endsWith('.md')) continue; 15 sheets.push({ 16 id: `${category.name}/${file.slice(0, -3)}`, 17 url: new URL(file, dir), 18 }); 19 } 20 } 21 return sheets; 22 }; 23 24 const LINK = /\]\(\/sheets\/([a-z0-9][a-z0-9-]*)\/([a-z0-9][a-z0-9-]*)(?=[)#])/g; 25 26 // Slug renames and category moves rot these links silently: the build still 27 // succeeds and the dead href only shows up when a reader clicks it. Two 28 // previous commits repaired them by hand, so the check lives here instead. 29 test('every internal /sheets/ link resolves to a sheet that exists', async () => { 30 const sheets = await collectSheets(); 31 const known = new Set(sheets.map((s) => s.id)); 32 const broken = []; 33 34 for (const sheet of sheets) { 35 const body = await readFile(sheet.url, 'utf8'); 36 for (const [, category, slug] of body.matchAll(LINK)) { 37 const target = `${category}/${slug}`; 38 if (!known.has(target)) broken.push(`${sheet.id} -> /sheets/${target}`); 39 } 40 } 41 42 assert.deepEqual(broken, [], `broken internal links:\n ${broken.join('\n ')}`); 43 }); 44 45 test('no sheet links to itself', async () => { 46 const sheets = await collectSheets(); 47 const selfLinks = []; 48 49 for (const sheet of sheets) { 50 const body = await readFile(sheet.url, 'utf8'); 51 for (const [, category, slug] of body.matchAll(LINK)) { 52 if (`${category}/${slug}` === sheet.id) selfLinks.push(sheet.id); 53 } 54 } 55 56 assert.deepEqual(selfLinks, [], `self-referential links:\n ${selfLinks.join('\n ')}`); 57 });