#!/usr/bin/env node /** * ObsiGate — pure tests for the navigation page facets/filters (#158): * `frontend/js/navfacets.js` (no DOM, real module import). * * Usage: node tests/frontend/navfacets.test.mjs */ import { strict as assert } from "node:assert"; import { buildNavFacets, filterNavFiles, sortNavFiles, navDisplayName } from "../../frontend/js/navfacets.js"; const files = [ { name: "note2.md", path: "note2.md", vault: "V1", extension: "md", modified: 100, tags: ["docker"] }, { name: "note1.md", path: "note1.md", vault: "V1", extension: "md", modified: 300, tags: ["python", "docker"] }, { name: "photo.png", path: "sub/photo.png", vault: "V1", extension: ".PNG", modified: 200, tags: [] }, { name: "sans-titre.md", path: "sans-titre.md", vault: "V1", extension: "md", modified: 400 }, ]; let pass = 0; function test(name, fn) { try { fn(); pass++; console.log(` ✓ ${name}`); } catch (err) { console.error(` ✗ ${name}\n ${err.message}`); process.exitCode = 1; } } test("buildNavFacets counts vaults/tags/extensions, sorted desc", () => { const f = buildNavFacets(files); assert.deepStrictEqual(f.vaults, { V1: 4 }); assert.deepStrictEqual(f.tags, { docker: 2, python: 1 }); // leading dot stripped + lowercased, untagged file contributes nothing assert.deepStrictEqual(f.extensions, { md: 3, png: 1 }); }); test("buildNavFacets tolerates empty/missing input", () => { assert.deepStrictEqual(buildNavFacets([]), { vaults: {}, tags: {}, extensions: {} }); assert.deepStrictEqual(buildNavFacets(undefined), { vaults: {}, tags: {}, extensions: {} }); }); test("filterNavFiles: no filter returns the input untouched", () => { assert.strictEqual(filterNavFiles(files, {}), files); assert.strictEqual(filterNavFiles(files, { tag: "", ext: "" }), files); }); test("filterNavFiles: tag filter keeps files carrying the tag", () => { const out = filterNavFiles(files, { tag: "docker" }); assert.deepStrictEqual(out.map((f) => f.name).sort(), ["note1.md", "note2.md"]); }); test("filterNavFiles: extension filter is dot-insensitive", () => { assert.deepStrictEqual(filterNavFiles(files, { ext: "png" }).map((f) => f.name), ["photo.png"]); assert.deepStrictEqual(filterNavFiles(files, { ext: ".png" }).map((f) => f.name), ["photo.png"]); }); test("filterNavFiles: tag + ext combine (AND) and can empty the list", () => { assert.deepStrictEqual(filterNavFiles(files, { tag: "docker", ext: "md" }).map((f) => f.name).sort(), ["note1.md", "note2.md"]); assert.deepStrictEqual(filterNavFiles(files, { tag: "python", ext: "png" }), []); }); test("sortNavFiles 'modified' = newest first", () => { assert.deepStrictEqual(sortNavFiles(files, "modified").map((f) => f.modified), [400, 300, 200, 100]); }); test("sortNavFiles 'relevance' = alphabetical, .md stripped, case-insensitive", () => { const names = sortNavFiles(files, "relevance").map(navDisplayName); assert.deepStrictEqual(names, ["note1", "note2", "photo.png", "sans-titre"]); }); test("sortNavFiles does not mutate its input", () => { const before = files.map((f) => f.name); sortNavFiles(files, "relevance"); sortNavFiles(files, "modified"); assert.deepStrictEqual(files.map((f) => f.name), before); }); test("navDisplayName strips only a trailing .md", () => { assert.strictEqual(navDisplayName({ name: "a.md" }), "a"); assert.strictEqual(navDisplayName({ name: "a.markdown" }), "a.markdown"); assert.strictEqual(navDisplayName({ path: "dir/b.md" }), "b"); }); console.log(`\n${pass} passed`);