Files
fusion/scripts/__tests__/plugin-authoring-docs.test.mjs
Fusion d051c09a1d docs(FN-3468): fix plugin authoring section numbering
- Add missing top-level dashboard views section to the PLUGIN_AUTHORING table of contents
- Renumber downstream section headings and TOC entries to keep numbering sequential
- Add a node:test suite that validates heading order and TOC anchor alignment

Fusion-Task-Id: FN-3468
2026-05-05 00:51:09 -07:00

88 lines
2.8 KiB
JavaScript

import test from "node:test";
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const docPath = path.resolve(__dirname, "../../docs/PLUGIN_AUTHORING.md");
const doc = readFileSync(docPath, "utf8");
const expectedSections = [
"Getting Started",
"Plugin Manifest Reference",
"Plugin Settings Schema",
"Available Hooks and Signatures",
"Registering Tools",
"Registering Routes",
"Registering UI Slots",
"Registering Top-Level Dashboard Views",
"Registering Agent Runtimes",
"Plugin Context API Reference",
"Plugin Lifecycle States",
"Testing Plugins",
"Publishing Plugins",
"Example Plugins",
"Registering Skills",
"Registering Workflow Steps",
"Plugin Prompt Contributions",
"Plugin Binary Setup Hooks",
];
function slugifyHeading(text) {
return text
.trim()
.toLowerCase()
.replace(/[^a-z0-9\s-]/g, "")
.replace(/\s+/g, "-");
}
test("PLUGIN_AUTHORING headings are sequentially numbered 1..18 with expected titles", () => {
const headingMatches = [...doc.matchAll(/^##\s+(\d+)\.\s+(.+)$/gm)];
const numbered = headingMatches.map((m) => ({ number: Number(m[1]), title: m[2].trim() }));
assert.equal(numbered.length, expectedSections.length);
for (let i = 0; i < expectedSections.length; i += 1) {
assert.equal(numbered[i].number, i + 1);
assert.equal(numbered[i].title, expectedSections[i]);
}
});
test("PLUGIN_AUTHORING TOC includes top-level dashboard views and anchors align to headings", () => {
const tocMatch = doc.match(/## Table of Contents\n\n([\s\S]*?)\n---/);
assert.ok(tocMatch, "Table of Contents block should exist");
const tocLines = tocMatch[1]
.split("\n")
.map((line) => line.trim())
.filter(Boolean);
const tocEntries = tocLines.map((line) => {
const m = line.match(/^(\d+)\.\s+\[(.+)\]\(#(.+)\)$/);
assert.ok(m, `Invalid TOC line: ${line}`);
return {
number: Number(m[1]),
title: m[2],
anchor: m[3],
};
});
assert.equal(tocEntries.length, expectedSections.length);
for (let i = 0; i < expectedSections.length; i += 1) {
const sectionNumber = i + 1;
const expectedTitle = expectedSections[i];
const expectedAnchor = slugifyHeading(`${sectionNumber}. ${expectedTitle}`);
assert.equal(tocEntries[i].number, sectionNumber);
assert.equal(tocEntries[i].title, expectedTitle);
assert.equal(tocEntries[i].anchor, expectedAnchor);
}
const topLevelEntry = tocEntries.find((entry) => entry.number === 8);
assert.ok(topLevelEntry, "TOC should include section 8");
assert.equal(topLevelEntry.title, "Registering Top-Level Dashboard Views");
});