US09-01: Serve the Documentation Inside the Application (#107)
This commit was merged in pull request #107.
This commit is contained in:
@@ -7,6 +7,7 @@ import { createStore } from "../store.js";
|
||||
import { parseHash, navigate } from "../router.js";
|
||||
import { api, cancellable } from "../api.js";
|
||||
import { subscribeJob } from "../events.js";
|
||||
import { assignHeadingIds, documentIndex, resolveDocLink, rewriteLinks, slug } from "../docs.js";
|
||||
|
||||
const cases = [];
|
||||
function ok(name, cond) {
|
||||
@@ -142,6 +143,62 @@ async function run() {
|
||||
window.EventSource = realES;
|
||||
}
|
||||
|
||||
// ── documentation view (US09-01) ─────────────────────────────────────────
|
||||
{
|
||||
ok("slug lowercases and dashes a heading", slug("Key Flows") === "key-flows");
|
||||
ok(
|
||||
"slug drops punctuation but keeps words",
|
||||
slug("What it *will not* do:") === "what-it-will-not-do"
|
||||
);
|
||||
ok("slug keeps non-ASCII letters", slug("Größe & Gewicht") === "größe-gewicht");
|
||||
ok("slug never yields an empty anchor", slug("!!!") === "section");
|
||||
|
||||
const doc = document.createElement("div");
|
||||
doc.innerHTML = "<h2>Notes</h2><h3>Notes</h3><h2>Notes</h2>";
|
||||
const ids = [...assignHeadingIds(doc.querySelectorAll("h2, h3"))].map((h) => h.id);
|
||||
ok("repeated headings get distinct anchors", ids.join(",") === "notes,notes-2,notes-3");
|
||||
|
||||
ok(
|
||||
"a sibling document link becomes an in-app route",
|
||||
resolveDocLink("index", "overview.md")?.page === "overview"
|
||||
);
|
||||
const nested = resolveDocLink("guides/install", "../overview.md#running-it");
|
||||
ok("a relative link resolves against the current page", nested?.page === "overview");
|
||||
ok("a link's anchor survives the rewrite", nested?.anchor === "running-it");
|
||||
ok(
|
||||
"a bare anchor stays on the current page",
|
||||
resolveDocLink("overview", "#the-workflow")?.page === "overview"
|
||||
);
|
||||
ok("an external link is left alone", resolveDocLink("index", "https://example.test/x") === null);
|
||||
ok("a non-markdown relative link is left alone", resolveDocLink("index", "images/a.png") === null);
|
||||
// A climb cannot leave the documentation tree: it is clamped at the root, so the
|
||||
// page it names is still fetched from under /docs and simply does not exist.
|
||||
ok(
|
||||
"a link that climbs above the docs root is clamped",
|
||||
resolveDocLink("index", "../../etc/passwd.md")?.page === "etc/passwd"
|
||||
);
|
||||
ok(
|
||||
"a page name that is not a page name is refused",
|
||||
resolveDocLink("index", "..%2f..%2fetc%2fpasswd.md") === null
|
||||
);
|
||||
|
||||
const index = document.createElement("div");
|
||||
index.innerHTML =
|
||||
'<a href="overview.md">Overview</a><a href="overview.md">again</a>' +
|
||||
'<a href="index.md">itself</a><a href="https://example.test">out</a>';
|
||||
const listed = documentIndex(rewriteLinks(index, "index"));
|
||||
ok(
|
||||
"an external link is marked safe to open away from the app",
|
||||
index.querySelector('a[href^="https"]').rel === "noreferrer noopener"
|
||||
);
|
||||
ok(
|
||||
"an in-app link points at the docs route",
|
||||
index.querySelector("a").getAttribute("href") === "#/docs?page=overview"
|
||||
);
|
||||
ok("the index lists each page once, in order", listed.length === 1);
|
||||
ok("the index takes its titles from the link text", listed[0].title === "Overview");
|
||||
}
|
||||
|
||||
await tick();
|
||||
const failed = cases.filter((c) => !c.ok);
|
||||
window.__RESULTS__ = { passed: cases.length - failed.length, failed: failed.length, cases };
|
||||
|
||||
Reference in New Issue
Block a user