/** * The footer's links, tested where a mistake is invisible to every other check. * * --------------------------------------------------------------------------------------- * WHY THIS FILE EXISTS * --------------------------------------------------------------------------------------- * The footer shipped with all three "Documentation" links pointing at `/docs/`. Three * labels — Getting started, Administration, Building a module — and one destination. It * reached production and stayed there through eleven checks and two test suites, because * none of them could see it: * * - `checkLinks.mjs` resolves every internal link against the build. `/docs/` resolves. * Three links to a page that exists are three valid links. * - `checkSidebar.mjs` compares the docs tree to the planned tree. The footer is not the * sidebar and was never in scope. * - `checkA11y.mjs` checks structure. Three correctly-marked-up links are correct markup. * * The bug is not a broken link. It is a link that goes somewhere other than where its label * says, which is the one property nothing was asserting. So that is what this file asserts, * and the reason `src/data/footer.mjs` exists at all — a column list inside an `.astro` * component cannot be imported by a test. */ import { test } from 'node:test'; import assert from 'node:assert/strict'; import { footerColumns, docsEntryPoints } from '../src/data/footer.mjs'; /** A stand-in for the rendered brand; only the two Project links read it. */ const brand = { giteaOrg: 'https://gitea.example.com/Org', discordInvite: 'https://discord.gg/example', }; const columns = footerColumns(brand); const documentation = columns.find((c) => c.heading === 'Documentation'); test('every link in a column has its own destination', () => { for (const column of columns) { const hrefs = column.links.map((l) => l.href); assert.equal( new Set(hrefs).size, hrefs.length, `the ${column.heading} column has two links pointing at the same page: ${hrefs.join(', ')}`, ); } }); test('no two columns offer the same destination twice', () => { const all = columns.flatMap((c) => c.links.map((l) => l.href)); assert.equal(new Set(all).size, all.length, `a footer destination is repeated: ${all.join(', ')}`); }); test('each documentation link lands inside the section its label names', () => { // The exact failure that shipped: `/docs/` under all three labels satisfies "starts with // /docs/" but names no section, so the prefixes below are section prefixes, not `/docs/`. const expected = [ ['Getting started', '/docs/getting-started/'], ['Administration', '/docs/administration/'], ['Building a module', '/docs/modules/'], ]; for (const [label, prefix] of expected) { const link = documentation.links.find((l) => l.label === label); assert.ok(link, `the Documentation column no longer has a "${label}" link`); assert.ok( link.href.startsWith(prefix), `"${label}" points at ${link.href}, which is not inside ${prefix}`, ); } }); test('no documentation link is the docs home, which the header already carries', () => { for (const link of documentation.links) { assert.notEqual( link.href, '/docs/', `"${link.label}" points at the docs home; the header's Docs link is that page`, ); } }); test('every documentation entry point is a directory URL', () => { // Astro builds these as directories with an index.html; a missing trailing slash costs a // redirect on every click and reads as a broken path in the status bar. for (const [section, href] of Object.entries(docsEntryPoints)) { assert.ok(href.endsWith('/'), `the ${section} entry point (${href}) needs a trailing slash`); assert.ok(href.startsWith('/docs/'), `the ${section} entry point (${href}) is not under /docs/`); } }); test('the brand supplies the two Project links rather than the code', () => { const project = columns.find((c) => c.heading === 'Project'); const hrefs = project.links.map((l) => l.href); assert.ok(hrefs.includes(brand.giteaOrg), 'the Source link no longer reads brand.giteaOrg'); assert.ok(hrefs.includes(brand.discordInvite), 'the Discord link no longer reads brand.discordInvite'); });