refactoring van de cmap structuren bijna compleet
This commit is contained in:
@@ -5,7 +5,7 @@ import {
|
||||
CmapAppearance,
|
||||
cmapFontSizeInPoints,
|
||||
normalizedCmapFontSize
|
||||
} from "../static/cmap/model/appearance.js";
|
||||
} from "../static/js/cmap/model/appearance.js";
|
||||
|
||||
const defaultValues = {
|
||||
backgroundColor: "#fff4cf", textColor: "#222222",
|
||||
|
||||
@@ -88,7 +88,7 @@ async function loadEditorFactory() {
|
||||
clearTimeout,
|
||||
requestAnimationFrame: (callback) => callback()
|
||||
};
|
||||
await import("../static/cmap/cmap-racket-wiki.js");
|
||||
await import("../static/js/cmap/cmap-racket-wiki.js");
|
||||
global.window.RacketWikiCmap = global.window.RacketWikiCmap || previousFactory;
|
||||
return global.window.RacketWikiCmap;
|
||||
}
|
||||
@@ -175,7 +175,7 @@ test("a dragged connector endpoint survives collapsing and expanding its submap"
|
||||
}]);
|
||||
});
|
||||
|
||||
test("moving a concept also moves connected concepts that link to another CMap", async () => {
|
||||
test("moving a concept does not move connected concepts or linking phrases", async () => {
|
||||
const factory = await loadEditorFactory();
|
||||
const map = {
|
||||
onSelection() {},
|
||||
@@ -206,7 +206,7 @@ test("moving a concept also moves connected concepts that link to another CMap",
|
||||
editor.addConnector(phrase, linked, true, { id: 2 });
|
||||
|
||||
editor.selectItem(owner);
|
||||
editor.handleItemMove(owner, 180, 140);
|
||||
owner.node.attr(editor.handleItemMove(owner, 180, 140));
|
||||
|
||||
assert.deepEqual({
|
||||
owner: [owner.node.attr("x"), owner.node.attr("y")],
|
||||
@@ -214,8 +214,8 @@ test("moving a concept also moves connected concepts that link to another CMap",
|
||||
linked: [linked.node.attr("x"), linked.node.attr("y")]
|
||||
}, {
|
||||
owner: [180, 140],
|
||||
phrase: [340, 140],
|
||||
linked: [500, 140]
|
||||
phrase: [260, 100],
|
||||
linked: [420, 100]
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ async function loadEditorFactory() {
|
||||
createElement: () => fakeElement()
|
||||
};
|
||||
global.window = {};
|
||||
await import("../static/cmap/cmap-racket-wiki.js");
|
||||
await import("../static/js/cmap/cmap-racket-wiki.js");
|
||||
return global.window.RacketWikiCmap;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { DiagramGroup } from "../static/cmap/engine/diagram-group.js";
|
||||
import { DiagramComponent } from "../static/cmap/engine/diagram-component.js";
|
||||
import { DiagramEngine } from "../static/cmap/engine/diagram-engine.js";
|
||||
import { DiagramGroup } from "../static/js/cmap/engine/diagram-group.js";
|
||||
import { DiagramComponent } from "../static/js/cmap/engine/diagram-component.js";
|
||||
import { DiagramEngine } from "../static/js/cmap/engine/diagram-engine.js";
|
||||
|
||||
function component(engine, attributes) {
|
||||
const state = { ...attributes };
|
||||
|
||||
@@ -6,7 +6,7 @@ let CmapMarkdownExporter;
|
||||
|
||||
test.before(async () => {
|
||||
({ CmapMarkdownExporter } = await import(
|
||||
"../static/cmap/model/markdown-exporter.js"));
|
||||
"../static/js/cmap/model/markdown-exporter.js"));
|
||||
});
|
||||
|
||||
function map(slug, title, document) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { CmapHistory } from "../static/cmap/controller/cmap-history.js";
|
||||
import { CmapHistory } from "../static/js/cmap/controller/cmap-history.js";
|
||||
|
||||
function history(initial = "one") {
|
||||
let value = initial;
|
||||
|
||||
@@ -15,7 +15,7 @@ test.before(async () => {
|
||||
preparedMapDocument,
|
||||
attachmentUrls,
|
||||
replaceAttachmentUrls
|
||||
} = await import("../static/cmap/model/interchange.js"));
|
||||
} = await import("../static/js/cmap/model/interchange.js"));
|
||||
});
|
||||
|
||||
const conceptId = "24d27086-9f8b-4b57-a7fb-47b513277555";
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { CmapJsonExporter } from "../static/cmap/model/json-exporter.js";
|
||||
import { CmapJsonImporter } from "../static/cmap/model/json-importer.js";
|
||||
import { CmapJsonExporter } from "../static/js/cmap/model/json-exporter.js";
|
||||
import { CmapJsonImporter } from "../static/js/cmap/model/json-importer.js";
|
||||
import {
|
||||
CmapRepository,
|
||||
StoredConceptMap
|
||||
} from "../static/cmap/model/cmap-repository.js";
|
||||
import { CmapModel } from "../static/cmap/model/concept-map.js";
|
||||
} from "../static/js/cmap/model/cmap-repository.js";
|
||||
import { CmapModel } from "../static/js/cmap/model/concept-map.js";
|
||||
|
||||
const conceptId = "11111111-1111-4111-8111-111111111111";
|
||||
const rootMap = {
|
||||
|
||||
@@ -88,7 +88,7 @@ test("relations render and hit-test behind concept nodes", async () => {
|
||||
requestAnimationFrame: (callback) => setImmediate(callback)
|
||||
};
|
||||
|
||||
const { DiagramEngine } = await import("../static/cmap/cmap.js");
|
||||
const { DiagramEngine } = await import("../static/js/cmap/cmap.js");
|
||||
assert.equal(global.window.Cmap, undefined,
|
||||
"the ES-module engine does not install a legacy global");
|
||||
const map = new DiagramEngine(root);
|
||||
|
||||
@@ -6,15 +6,16 @@ import {
|
||||
ConceptOwnership,
|
||||
ConceptRelation,
|
||||
ConceptRepository
|
||||
} from "../static/cmap/model/concept-repository.js";
|
||||
} from "../static/js/cmap/model/concept-repository.js";
|
||||
import {
|
||||
CmapModel,
|
||||
ConceptMap,
|
||||
ConceptMapConcept,
|
||||
ConceptMapConnector,
|
||||
ConceptMapPhrase
|
||||
} from "../static/cmap/model/concept-map.js";
|
||||
import { buildBundle } from "../static/cmap/model/interchange.js";
|
||||
} from "../static/js/cmap/model/concept-map.js";
|
||||
import { buildBundle } from "../static/js/cmap/model/interchange.js";
|
||||
import { CmapReferenceIndex } from "../static/js/cmap/model/cmap-reference-index.js";
|
||||
|
||||
const firstId = "11111111-1111-4111-8111-111111111111";
|
||||
const secondId = "22222222-2222-4222-8222-222222222222";
|
||||
@@ -24,6 +25,7 @@ function sampleDocument() {
|
||||
return {
|
||||
schemaVersion: 2,
|
||||
metadata: {
|
||||
namespace: "",
|
||||
tags: ["architecture"],
|
||||
summary: "System overview",
|
||||
explanationPageSlug: "cmap:architecture"
|
||||
@@ -246,6 +248,7 @@ test("an embedded submap becomes an independent map linked from its owner", () =
|
||||
{ tags: ["retained"], summary: "Edited child metadata", explanationPageSlug: "" }
|
||||
);
|
||||
assert.deepEqual(convertedDerivedMap.childModel.metadata(), {
|
||||
namespace: "",
|
||||
tags: ["retained"], summary: "Edited child metadata", explanationPageSlug: ""
|
||||
});
|
||||
});
|
||||
@@ -274,3 +277,74 @@ test("a model document is accepted by the complete JSON bundle export", async ()
|
||||
assert.deepEqual(bundle.cmaps[0].document.items, model.toDocument().items);
|
||||
assert.equal(bundle.concepts[0].label, "Application");
|
||||
});
|
||||
|
||||
test("CMap reference index stores lightweight destinations per concept", () => {
|
||||
const index = new CmapReferenceIndex().load([
|
||||
{ conceptId: firstId, label: "Application", cmapSlug: "architecture", count: 2 },
|
||||
{ conceptId: firstId, label: "Application", cmapSlug: "operations", count: 1 }
|
||||
], [
|
||||
{ slug: "architecture", title: "Architecture" },
|
||||
{ slug: "operations", title: "Operations" }
|
||||
]);
|
||||
|
||||
assert.deepEqual(index.mapsFor(firstId), [
|
||||
{ slug: "architecture", title: "Architecture", count: 2 },
|
||||
{ slug: "operations", title: "Operations", count: 1 }
|
||||
]);
|
||||
assert.equal(index.countFor(firstId), 3);
|
||||
assert.equal(index.conceptIdForName("Application"), firstId);
|
||||
});
|
||||
|
||||
test("model finds boundary references from active submap crossings", () => {
|
||||
const model = CmapModel.fromDocument({
|
||||
concepts: [
|
||||
{ id: firstId, label: "Submap" },
|
||||
{ id: secondId, label: "Inside" },
|
||||
{ id: thirdId, label: "Remote", cmapSlug: "remote-map" }
|
||||
],
|
||||
items: [
|
||||
{ id: 10, kind: "submap", conceptId: firstId, x: 0, y: 0, width: 800, height: 500 },
|
||||
{ id: 1, conceptId: secondId, parentSubmapId: 10, x: 100, y: 120, width: 200, height: 60 },
|
||||
{ id: 2, conceptId: thirdId, x: 420, y: 120, width: 200, height: 60 }
|
||||
],
|
||||
connectors: [{ id: 1, sourceId: 1, targetId: 2 }],
|
||||
conceptMaps: [{ id: "remote-map", title: "Remote map" }]
|
||||
});
|
||||
|
||||
assert.deepEqual(model.boundaryReferencesFor(10), [{
|
||||
connector: {
|
||||
id: 1, sourceId: 1, targetId: 2, hasArrow: true,
|
||||
lineColor: "#333", lineWidth: 2
|
||||
},
|
||||
inside: { id: 1, x: 100, y: 120, width: 200, height: 60 },
|
||||
outside: { id: 2, x: 420, y: 120, width: 200, height: 60 },
|
||||
sourceInside: true,
|
||||
conceptId: thirdId,
|
||||
label: "Remote",
|
||||
linkingPhraseLabel: null,
|
||||
targetMaps: []
|
||||
}]);
|
||||
});
|
||||
|
||||
test("model anchors boundary references on the connected concept beyond a phrase", () => {
|
||||
const model = CmapModel.fromDocument({
|
||||
concepts: [
|
||||
{ id: firstId, label: "Submap" },
|
||||
{ id: secondId, label: "Inside" },
|
||||
{ id: thirdId, label: "Remote", cmapSlug: "remote-map" }
|
||||
],
|
||||
items: [
|
||||
{ id: 10, kind: "submap", conceptId: firstId, x: 0, y: 0, width: 800, height: 500 },
|
||||
{ id: 1, conceptId: secondId, parentSubmapId: 10, x: 100, y: 120, width: 200, height: 60 },
|
||||
{ id: 2, kind: "phrase", label: "relates", parentSubmapId: 10, x: 330, y: 120, width: 80, height: 30 },
|
||||
{ id: 3, conceptId: thirdId, x: 480, y: 120, width: 200, height: 60 }
|
||||
],
|
||||
connectors: [
|
||||
{ id: 1, sourceId: 1, targetId: 2 },
|
||||
{ id: 2, sourceId: 2, targetId: 3 }
|
||||
],
|
||||
conceptMaps: [{ id: "remote-map", title: "Remote map" }]
|
||||
});
|
||||
|
||||
assert.equal(model.boundaryReferencesFor(10)[0].inside.id, 1);
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { PeopleRepository } from "../static/cmap/model/people-repository.js";
|
||||
import { PeopleRepository } from "../static/js/cmap/model/people-repository.js";
|
||||
|
||||
test("PeopleRepository keeps people API routes behind its public methods", async () => {
|
||||
const calls = [];
|
||||
|
||||
@@ -4,8 +4,8 @@ import assert from "node:assert/strict";
|
||||
import {
|
||||
CmapRepository,
|
||||
StoredConceptMap
|
||||
} from "../static/cmap/model/cmap-repository.js";
|
||||
import { CmapModel } from "../static/cmap/model/concept-map.js";
|
||||
} from "../static/js/cmap/model/cmap-repository.js";
|
||||
import { CmapModel } from "../static/js/cmap/model/concept-map.js";
|
||||
|
||||
const documentValue = {
|
||||
schemaVersion: 2,
|
||||
@@ -19,7 +19,8 @@ test("CmapRepository decodes backend documents and normalizes legacy concept ids
|
||||
slug: "roadmap",
|
||||
title: "Roadmap",
|
||||
currentVersion: 3,
|
||||
document: JSON.stringify(JSON.stringify(documentValue))
|
||||
document: JSON.stringify(JSON.stringify(documentValue)),
|
||||
renderedSvg: "<svg></svg>"
|
||||
}));
|
||||
|
||||
const storedMap = await repository.load("roadmap");
|
||||
@@ -29,6 +30,7 @@ test("CmapRepository decodes backend documents and normalizes legacy concept ids
|
||||
assert.equal(storedMap.currentVersion, 3);
|
||||
assert.equal(storedMap.toDocument().concepts[0].id, "legacy:roadmap:concept-1");
|
||||
assert.equal(storedMap.toDocument().items[0].conceptId, "legacy:roadmap:concept-1");
|
||||
assert.equal(storedMap.renderedSvg, "<svg></svg>");
|
||||
});
|
||||
|
||||
test("CmapRepository serializes model saves with backend version information", async () => {
|
||||
@@ -47,7 +49,8 @@ test("CmapRepository serializes model saves with backend version information", a
|
||||
const savedMap = await repository.save(storedMap, storedMap.model, {
|
||||
summary: "Manual save",
|
||||
saveKind: "manual",
|
||||
snapshot: true
|
||||
snapshot: true,
|
||||
renderedSvg: "<svg xmlns=\"http://www.w3.org/2000/svg\"></svg>"
|
||||
});
|
||||
|
||||
assert.equal(savedMap.currentVersion, 2);
|
||||
@@ -57,6 +60,7 @@ test("CmapRepository serializes model saves with backend version information", a
|
||||
assert.equal(body.summary, "Manual save");
|
||||
assert.equal(body.saveKind, "manual");
|
||||
assert.equal(body.snapshot, true);
|
||||
assert.equal(body.renderedSvg, "<svg xmlns=\"http://www.w3.org/2000/svg\"></svg>");
|
||||
assert.deepEqual(body.document.concepts, storedMap.toDocument().concepts);
|
||||
});
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ async function loadEditorFactory() {
|
||||
clearTimeout,
|
||||
requestAnimationFrame: (callback) => callback()
|
||||
};
|
||||
await import("../static/cmap/cmap-racket-wiki.js");
|
||||
await import("../static/js/cmap/cmap-racket-wiki.js");
|
||||
return global.window.RacketWikiCmap;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import { CmapAppearance } from "../static/cmap/model/appearance.js";
|
||||
import { CmapAppearanceRepository } from "../static/cmap/model/appearance-repository.js";
|
||||
import { CmapSettingsRepository } from "../static/cmap/model/settings-repository.js";
|
||||
import { CmapAppearance } from "../static/js/cmap/model/appearance.js";
|
||||
import { CmapAppearanceRepository } from "../static/js/cmap/model/appearance-repository.js";
|
||||
import { CmapSettingsRepository } from "../static/js/cmap/model/settings-repository.js";
|
||||
|
||||
test("appearance repository owns the complete backend appearance envelope", async () => {
|
||||
const requests = [];
|
||||
|
||||
@@ -46,7 +46,7 @@ async function loadEditorFactory() {
|
||||
clearTimeout,
|
||||
requestAnimationFrame: (callback) => callback()
|
||||
};
|
||||
await import("../static/cmap/cmap-racket-wiki.js");
|
||||
await import("../static/js/cmap/cmap-racket-wiki.js");
|
||||
editorFactory = global.window.RacketWikiCmap;
|
||||
return editorFactory;
|
||||
}
|
||||
@@ -139,7 +139,7 @@ test("single-click selects and double-click edits every kind of concept", async
|
||||
test("a linked CMap opens only through its arrow decorator", async () => {
|
||||
await loadEditorFactory();
|
||||
const { CmapItemDecorator } = await import(
|
||||
"../static/cmap/view/cmap-item-decorator.js");
|
||||
"../static/js/cmap/view/cmap-item-decorator.js");
|
||||
const listeners = new Map();
|
||||
const linkedButton = {
|
||||
dataset: {},
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
expandTodoMarkup,
|
||||
expandWikiMentions,
|
||||
extractCmapEmbeds,
|
||||
protectCamelCaseWikiWords,
|
||||
restoreCmapEmbeds
|
||||
} from "../static/js/wiki/markdown.js";
|
||||
|
||||
@@ -66,6 +67,12 @@ test("the WikiWord escape remains literal in protected Markdown", () => {
|
||||
assert.equal(expandWikiMentions(markdown, [], [], []), markdown);
|
||||
});
|
||||
|
||||
test("protects unprotected CamelCase WikiWords without changing Markdown syntax", () => {
|
||||
const markdown = "HomeopathieWiki and [ExistingLink](https://example.test)\n`CodeWord`\n!AlreadyLiteral\n IndentedCodeWord";
|
||||
assert.equal(protectCamelCaseWikiWords(markdown),
|
||||
"!HomeopathieWiki and [ExistingLink](https://example.test)\n`CodeWord`\n!AlreadyLiteral\n IndentedCodeWord");
|
||||
});
|
||||
|
||||
test("ambiguous CMap mentions are not resolved", () => {
|
||||
const conceptMaps = [
|
||||
{ slug: "first", title: "Zelfde Kaart" },
|
||||
|
||||
Reference in New Issue
Block a user