import assert from "node:assert/strict"; import { describe, it } from "node:test"; import { cleanLibraryPath, cleanLibraryRoot, compareLessons, compareUnits, derivedSlug, derivedSlugCandidate, guessLibraryKind, isLibraryLesson, libraryCoverPaths, libraryFolder, libraryFreshSlug, libraryLessonFolders, libraryList, libraryRootError, libraryRootNested, libraryRowError, libraryRowForFolder, libraryTitleOf, libraryUrl, unitOfName, } from "../shared/library.ts"; describe("library rules", () => { it("reads a unit's kind and number off its folder name, sorting prefix and all", () => { assert.deepEqual(unitOfName("B2| Chapter 40"), { name: "Chapter 40", kind: "chapter", number: 40 }); assert.deepEqual(unitOfName("L3"), { name: "L3", kind: "lecture", number: 3 }); assert.deepEqual(unitOfName("Lecture 12 Raft"), { name: "Lecture 12 Raft", kind: "lecture", number: 12 }); assert.deepEqual(unitOfName("Week 02"), { name: "Week 02", kind: "week", number: 2 }); assert.deepEqual(unitOfName("Part I"), { name: "Part I", kind: "unit", number: null }); assert.deepEqual(unitOfName("Appendix"), { name: "Appendix", kind: "unit", number: null }); }); it("orders units by number, then name, numbered ones first", () => { const u = (key: string) => ({ key, ...unitOfName(key) }); const sorted = [u("L10"), u("Appendix"), u("L2"), u("L1")].sort(compareUnits).map((x) => x.key); assert.deepEqual(sorted, ["L1", "L2", "L10", "Appendix"]); }); it("puts a unit's hub note first and the rest in natural order", () => { const rows = [ { title: "Part 10", path: "b" }, { title: "Chapter 40", path: "hub" }, { title: "Part 2", path: "a" }, ].sort(compareLessons("Chapter 40")); assert.deepEqual(rows.map((r) => r.path), ["hub", "a", "b"]); }); it("normalises a folder and refuses what cannot be one", () => { assert.equal(libraryFolder(" /1 - Source Material/Books/Feynman Lectures/ "), "1 - Source Material/Books/Feynman Lectures"); assert.equal(libraryFolder("a\\b"), "a/b"); assert.equal(libraryFolder(""), null); assert.equal(libraryFolder("../etc"), null); assert.equal(libraryFolder("a//b"), null); }); it("judges a row by the same rule the server does", () => { assert.equal(libraryRowError({ title: "X", slug: "x", folder: "Books/X", kind: "book" }), null); assert.equal(libraryRowError({ title: "", slug: "x", folder: "Books/X", kind: "book" }), "title"); assert.equal(libraryRowError({ title: "X", slug: "Bad Slug", folder: "Books/X", kind: "book" }), "slug"); assert.equal(libraryRowError({ title: "X", slug: "x", folder: "", kind: "book" }), "folder"); assert.equal(libraryRowError({ title: "X", slug: "x", folder: "Books/X", kind: "film" }), "kind"); const clean = cleanLibraryPath( { title: " X ", slug: "X", folder: "Books/X/", kind: "course", blurb: " b ", cover: "c.png", source: "ftp://no", hidden: true }, () => "id1", ); assert.deepEqual(clean, { id: "id1", slug: "x", folder: "Books/X", kind: "course", title: "X", blurb: "b", cover: "c.png", hidden: true }); }); it("drops blank editor rows and trims the rest", () => { const list = libraryList([ { id: "a", slug: "", folder: "", kind: "book", title: "" }, { id: "b", slug: " Feyn ", folder: "/Books/F/", kind: "book", title: " Feynman " }, ]); assert.deepEqual(list, [{ id: "b", slug: "feyn", folder: "Books/F", kind: "book", title: "Feynman" }]); }); it("spells the three addresses", () => { assert.equal(libraryUrl(), "/library"); assert.equal(libraryUrl("feynman"), "/library/feynman"); assert.equal(libraryUrl("feynman", 7), "/library/feynman/7"); }); }); describe("a folder becoming a path", () => { it("guesses the kind from what is inside, then from the address", () => { assert.equal(guessLibraryKind("1 - Source Material/Lectures/6.824", ["L1", "L2", "L10"]), "course"); assert.equal(guessLibraryKind("Books/Feynman", ["B2| Chapter 39", "B2| Chapter 40"]), "book"); assert.equal(guessLibraryKind("Study/Algorithms", ["Week 1", "Week 2"]), "course"); assert.equal(guessLibraryKind("Talks/Rich Hickey", []), "series"); assert.equal(guessLibraryKind("Lectures/Physics", ["notes"]), "course"); assert.equal(guessLibraryKind("Anything/Else", []), "book"); }); it("titles from the leaf folder with the sorting prefix gone", () => { assert.equal(libraryTitleOf("1 - Source Material/Books/B2| Feynman Lectures"), "Feynman Lectures"); assert.equal(libraryTitleOf("Lectures/6.824"), "6.824"); }); it("gives a new row a fresh address that no row already holds", () => { const taken = [{ id: "a", slug: "feynman", folder: "x", kind: "book" as const, title: "Feynman" }]; const row = libraryRowForFolder("Books/Feynman", ["Chapter 1"], taken); assert.equal(row.slug, "feynman-2"); assert.equal(row.title, "Feynman"); assert.equal(row.kind, "book"); assert.equal(row.folder, "Books/Feynman"); assert.equal(libraryFreshSlug("The Feynman Lectures", []), "the-feynman-lectures"); assert.equal(libraryFreshSlug("محاضرات", []), "shelf"); }); }); describe("a lesson is not a post", () => { it("names the lesson folders of an enabled library, visible paths only", () => { const lib = { enabled: true, paths: [ { id: "a", slug: "a", folder: "Books/Feynman/", kind: "book" as const, title: "F" }, { id: "b", slug: "b", folder: "Lectures/6.824", kind: "course" as const, title: "L", hidden: true }, { id: "c", slug: "c", folder: "Books/Feynman", kind: "book" as const, title: "F again" }, ], }; assert.deepEqual(libraryLessonFolders(lib), ["Books/Feynman"]); assert.deepEqual(libraryLessonFolders({ ...lib, enabled: false }), []); assert.deepEqual(libraryLessonFolders(undefined), []); }); it("draws the boundary at the slash", () => { const folders = ["Books/Feynman"]; assert.equal(isLibraryLesson("Books/Feynman/Chapter 1/vortex.md", folders), true); assert.equal(isLibraryLesson("Books/Feynman Lectures/x.md", folders), false); assert.equal(isLibraryLesson("Books/Feynman.md", folders), false); assert.equal(isLibraryLesson("essays/x.md", []), false); }); }); describe("library covers a visitor may fetch", () => { it("lists vault covers of visible paths, never web ones or hidden paths", () => { const lib = { enabled: true, paths: [ { id: "a", slug: "a", folder: "Books/A", kind: "book" as const, title: "A", cover: "attachments/a.jpg" }, { id: "b", slug: "b", folder: "Books/B", kind: "book" as const, title: "B", cover: "https://x/y.jpg" }, { id: "c", slug: "c", folder: "Books/C", kind: "book" as const, title: "C", cover: "attachments/c.jpg", hidden: true }, { id: "d", slug: "d", folder: "Books/D", kind: "book" as const, title: "D" }, ], }; assert.deepEqual(libraryCoverPaths(lib), ["attachments/a.jpg"]); assert.deepEqual(libraryCoverPaths({ ...lib, enabled: false }), []); }); }); describe("shelf roots", () => { it("judges a root the way it judges a row", () => { assert.equal(libraryRootError({ folder: "Books", kind: "book" }), null); assert.equal(libraryRootError(null), "notObject"); assert.equal(libraryRootError({ folder: "", kind: "book" }), "vault"); assert.equal(libraryRootError({ folder: "/", kind: "book" }), "vault"); assert.equal(libraryRootError({ folder: "../etc", kind: "book" }), "folder"); assert.equal(libraryRootError({ folder: "Books", kind: "film" }), "kind"); }); it("refuses a root inside a root either way round, and a root inside a row", () => { assert.equal(libraryRootNested("Books", ["Lectures"]), false); assert.equal(libraryRootNested("Books/Old", ["Books"]), true); assert.equal(libraryRootNested("Books", ["Books/Old"]), true); assert.equal(libraryRootNested("Books", ["Books"]), true); // A root CONTAINING a row is the whole design; a root inside one is not. assert.equal(libraryRootNested("Books", [], ["Books/Calculus"]), false); assert.equal(libraryRootNested("Books/Calculus/Part 1", [], ["Books/Calculus"]), true); assert.equal(libraryRootNested("Books/Calculus", [], ["Books/Calculus"]), true); }); it("cleans a root, keeping a well-formed id and minting one otherwise", () => { assert.deepEqual(cleanLibraryRoot({ id: "r1", folder: " /Books/ ", kind: "course" }, () => "fresh"), { id: "r1", folder: "Books", kind: "course", }); assert.equal(cleanLibraryRoot({ folder: "Books", kind: "book" }, () => "fresh")?.id, "fresh"); assert.equal(cleanLibraryRoot({ folder: "", kind: "book" }, () => "fresh"), null); }); }); describe("a derived address", () => { it("prefers the folder note's slug, then the title's suggestion", () => { assert.equal(derivedSlugCandidate("The Fabric of Reality"), "the-fabric-of-reality"); assert.equal(derivedSlugCandidate("The Fabric of Reality", "fabric"), "fabric"); // A slug the rule refuses is not a slug, so the title still speaks. assert.equal(derivedSlugCandidate("The Fabric of Reality", "Bad Slug!"), "the-fabric-of-reality"); }); it("is nothing at all when the title makes no address", () => { assert.equal(derivedSlugCandidate("التفكير العلمي"), null); assert.equal(derivedSlugCandidate("التفكير العلمي", "scientific-thinking"), "scientific-thinking"); }); it("stands down rather than taking a counter when the address is claimed", () => { const taken = new Set(["calculus", "fabric"]); assert.equal(derivedSlug("Calculus", undefined, taken), null); assert.equal(derivedSlug("The Fabric of Reality", "fabric", taken), null); assert.equal(derivedSlug("Optics", undefined, taken), "optics"); }); });