"use strict"; const test = require("node:test"); const assert = require("node:assert/strict"); let CmapMarkdownExporter; test.before(async () => { ({ CmapMarkdownExporter } = await import( "../static/cmap/model/markdown-exporter.js")); }); function map(slug, title, document) { return { slug, title, toDocument: () => document }; } function repository(maps) { return { load: async (slug) => maps[slug] }; } const root = map("root", "Root map", { schemaVersion: 2, metadata: { tags: ["architecture", "report"], summary: "Summary of the root map.", explanationPageSlug: "cmap:root-explanation" }, concepts: [ { id: "concept-a", label: "Decision", synopsis: "Choose the approach.", aspects: ["governance"], pageSlug: "decisions:approach", descriptionPageSlug: "cmap:decision", cmapSlug: "child", externalUrl: "https://example.com/decision", tags: [{ type: "person", value: "Alex Morgan" }, { type: "person", value: "Sam de Vries" }] }, { id: "concept-b", label: "Delivery" } ], items: [ { id: 1, conceptId: "concept-a", kind: "concept" }, { id: 2, kind: "phrase", label: "enables" }, { id: 3, conceptId: "concept-b", kind: "concept" } ], connectors: [ { id: 1, sourceId: 1, targetId: 2, hasArrow: false }, { id: 2, sourceId: 2, targetId: 3, hasArrow: true } ] }); const child = map("child", "Child map", { schemaVersion: 2, metadata: { tags: ["detail"], summary: "Child summary." }, concepts: [{ id: "concept-c", label: "Child concept", cmapSlug: "root" }], items: [{ id: 1, conceptId: "concept-c", kind: "concept" }], connectors: [] }); const pages = new Map([ ["cmap:root-explanation", { slug: "cmap:root-explanation", title: "Root explanation", tags: ["explanation"], markdown: "# Why\n\nBecause." }], ["decisions:approach", { slug: "decisions:approach", title: "Approach", tags: ["decision"], markdown: "# Decision\n\nUse the simple option." }], ["cmap:decision", { slug: "cmap:decision", title: "Decision explanation", tags: [], markdown: "Details." }] ]); test("exports metadata, person tags, relations and linked maps to the selected depth", async () => { const exporter = new CmapMarkdownExporter( repository({ root, child }), async (slug) => pages.get(slug)); const markdown = await exporter.export(root, { maxDepth: 1, includeWikiPages: false, language: "nl" }); assert.match(markdown, /CMap: Root map \(niveau 0\)/); assert.match(markdown, /CMap: Child map \(niveau 1\)/); assert.match(markdown, /`architecture`, `report`/); assert.match(markdown, /Personen \(verantwoordelijkheid\/actie\):\*\* Alex Morgan, Sam de Vries/); assert.match(markdown, /Decision — \*\*enables\*\* → Delivery/); assert.match(markdown, /### CMap-uitleg/); assert.match(markdown, /Webpagina:\*\* https:\/\/example\.com\/decision/); assert.doesNotMatch(markdown, /## Gekoppelde wikipagina's/); assert.equal((markdown.match(/## CMap: Root map/g) || []).length, 1, "cycles are exported once"); }); test("depth zero exports only the selected map", async () => { const exporter = new CmapMarkdownExporter( repository({ root, child }), async (slug) => pages.get(slug)); const markdown = await exporter.export(root, { maxDepth: 0, includeWikiPages: false }); assert.match(markdown, /Root map/); assert.doesNotMatch(markdown, /## CMap: Child map/); }); test("optionally appends linked wiki pages and concept explanations", async () => { const exporter = new CmapMarkdownExporter( repository({ root, child }), async (slug) => pages.get(slug)); const markdown = await exporter.export(root, { maxDepth: 0, includeWikiPages: true, language: "en" }); assert.match(markdown, /## Linked wiki pages/); assert.match(markdown, /### Approach/); assert.match(markdown, /### Decision explanation/); assert.equal((markdown.match(/Root explanation/g) || []).length, 0, "the explanation page is embedded under the CMap rather than duplicated as an appendix heading"); assert.match(markdown, /Because\./); }); test("derived views contain their root subtree and inherit useful root metadata", async () => { const source = { schemaVersion: 2, concepts: [ { id: "root-concept", label: "Team", synopsis: "Team scope", aspects: ["people"], descriptionPageSlug: "cmap:team" }, { id: "child-concept", label: "Work" }, { id: "outside-concept", label: "Outside" } ], items: [ { id: 10, conceptId: "root-concept", kind: "submap" }, { id: 11, conceptId: "child-concept", kind: "concept", parentSubmapId: 10 }, { id: 12, conceptId: "outside-concept", kind: "concept" } ], connectors: [ { id: 1, sourceId: 10, targetId: 11 }, { id: 2, sourceId: 11, targetId: 12 } ] }; const derived = map("team", "Team", { derivedView: { sourceCmapSlug: "source", rootItemId: 10 } }); const sourceMap = map("source", "Source", source); const exporter = new CmapMarkdownExporter( repository({ source: sourceMap }), async (slug) => ({ slug, title: slug, markdown: "Team explanation", tags: [] })); const markdown = await exporter.export(derived, { maxDepth: 0 }); assert.match(markdown, /Team scope/); assert.match(markdown, /`people`/); assert.match(markdown, /#### Work/); assert.doesNotMatch(markdown, /#### Outside/); assert.match(markdown, /Team explanation/); }); test("direct and phrase relations receive readable Markdown", async () => { const documentValue = { items: [ { id: 1, label: "A" }, { id: 2, kind: "phrase", label: "supports" }, { id: 3, label: "B" }, { id: 4, label: "C" } ], connectors: [ { sourceId: 1, targetId: 2, hasArrow: false }, { sourceId: 2, targetId: 3, hasArrow: true }, { sourceId: 3, targetId: 4, hasArrow: true } ] }; const relationMap = map("relations", "Relations", documentValue); const exporter = new CmapMarkdownExporter( repository({ relations: relationMap }), async () => null); const markdown = await exporter.export(relationMap); assert.match(markdown, /A — \*\*supports\*\* → B/); assert.match(markdown, /B → C/); });