155 lines
6.1 KiB
JavaScript
155 lines
6.1 KiB
JavaScript
"use strict";
|
|
|
|
const test = require("node:test");
|
|
const assert = require("node:assert/strict");
|
|
|
|
function fakeNode(attributes) {
|
|
const state = { ...attributes };
|
|
return {
|
|
attr(value) {
|
|
if (typeof value === "string") return state[value];
|
|
Object.assign(state, value);
|
|
return this;
|
|
},
|
|
onRendered() {}, onMove() {}, onMoveEnd() {}, visible() {}, redraw() {},
|
|
toFront() {}, remove() {}, element: () => null
|
|
};
|
|
}
|
|
|
|
async function loadEditorFactory() {
|
|
global.Element = class Element {};
|
|
global.document = {
|
|
currentScript: null,
|
|
styleSheets: [],
|
|
body: { append() {} },
|
|
createElement: () => ({ style: {}, remove() {} })
|
|
};
|
|
global.window = {
|
|
Cmap: null,
|
|
setTimeout,
|
|
clearTimeout,
|
|
requestAnimationFrame: (callback) => callback()
|
|
};
|
|
await import("../static/cmap/cmap-racket-wiki.js");
|
|
return global.window.RacketWikiCmap;
|
|
}
|
|
|
|
test("the last selected concept determines target size and alignment", async () => {
|
|
const factory = await loadEditorFactory();
|
|
const map = {
|
|
onSelection() {},
|
|
onActivation() {},
|
|
node: (attributes) => fakeNode(attributes)
|
|
};
|
|
const canvas = {
|
|
addEventListener() {}, querySelector: () => null,
|
|
clientWidth: 1200, clientHeight: 800, scrollLeft: 0, scrollTop: 0,
|
|
isConnected: true
|
|
};
|
|
const editor = factory.createEditor(canvas, { Cmap: () => map });
|
|
const first = editor.addItem({
|
|
id: 1, label: "First", externalUrl: "https://example.com/first",
|
|
x: 10, y: 10, width: 100, height: 40
|
|
});
|
|
const second = editor.addItem({ id: 2, label: "Second", x: 80, y: 100, width: 120, height: 45 });
|
|
const third = editor.addItem({ id: 3, label: "Third", x: 250, y: 250, width: 200, height: 60 });
|
|
assert.equal(editor.conceptRepository().concept(first.conceptId).label, "First");
|
|
assert.equal(editor.conceptMapModel().item(first.id).conceptId, first.conceptId);
|
|
assert.equal("node" in editor.conceptRepository().concept(first.conceptId), false,
|
|
"repository concepts never contain drawing nodes");
|
|
assert.equal("node" in editor.conceptMapModel().item(first.id), false,
|
|
"map placements never contain drawing nodes");
|
|
const savedDocument = editor.toDocument();
|
|
assert.equal(
|
|
savedDocument.concepts.find((concept) => concept.id === first.conceptId).externalUrl,
|
|
"https://example.com/first",
|
|
"external links are serialized as shared concept content"
|
|
);
|
|
const reloaded = factory.createEditor(canvas, { Cmap: () => map });
|
|
reloaded.loadModel(editor.currentModel());
|
|
assert.equal(
|
|
reloaded.toDocument().concepts.find((concept) => concept.id === first.conceptId).externalUrl,
|
|
"https://example.com/first",
|
|
"external links survive an editor model round-trip"
|
|
);
|
|
editor.selectItem(first);
|
|
editor.selectItem(third, { additive: true });
|
|
editor.selectItem(second, { additive: true });
|
|
editor.resetHistory();
|
|
|
|
assert.equal(editor.selected(), second, "the last selected concept is the target object");
|
|
assert.equal(editor.canLayoutSelection("distribute-horizontal"), true);
|
|
assert.equal(editor.canLayoutSelection("distribute-vertical"), true);
|
|
editor.applySelectionLayout("align-right");
|
|
assert.deepEqual([first, second, third].map((item) => Number(item.node.attr("x"))),
|
|
[100, 80, 0]);
|
|
|
|
first.node.attr({ x: 10, y: 10 });
|
|
second.node.attr({ x: 80, y: 100 });
|
|
third.node.attr({ x: 250, y: 250 });
|
|
editor.applySelectionLayout("align-center");
|
|
assert.deepEqual([first, second, third].map((item) => Number(item.node.attr("x"))),
|
|
[90, 80, 40]);
|
|
|
|
first.node.attr({ x: 10, y: 10 });
|
|
second.node.attr({ x: 80, y: 100 });
|
|
third.node.attr({ x: 250, y: 250 });
|
|
editor.applySelectionLayout("align-left");
|
|
assert.deepEqual([first, second, third].map((item) => Number(item.node.attr("x"))),
|
|
[80, 80, 80]);
|
|
|
|
first.node.attr({ x: 10, y: 10 });
|
|
second.node.attr({ x: 80, y: 100 });
|
|
third.node.attr({ x: 250, y: 250 });
|
|
editor.applySelectionLayout("align-bottom");
|
|
assert.deepEqual([first, second, third].map((item) => Number(item.node.attr("y"))),
|
|
[105, 100, 85]);
|
|
|
|
first.node.attr({ x: 10, y: 10 });
|
|
second.node.attr({ x: 80, y: 100 });
|
|
third.node.attr({ x: 250, y: 250 });
|
|
editor.applySelectionLayout("align-middle");
|
|
assert.deepEqual([first, second, third].map((item) => Number(item.node.attr("y"))),
|
|
[102.5, 100, 92.5]);
|
|
|
|
first.node.attr({ x: 10, y: 10 });
|
|
second.node.attr({ x: 80, y: 100 });
|
|
third.node.attr({ x: 250, y: 250 });
|
|
editor.applySelectionLayout("align-top");
|
|
assert.deepEqual([first, second, third].map((item) => Number(item.node.attr("y"))),
|
|
[100, 100, 100]);
|
|
|
|
first.node.attr({ x: 10, y: 10 });
|
|
second.node.attr({ x: 80, y: 100 });
|
|
third.node.attr({ x: 250, y: 250 });
|
|
editor.applySelectionLayout("distribute-horizontal");
|
|
assert.deepEqual([first, second, third].map((item) => Number(item.node.attr("x"))),
|
|
[10, 120, 250], "outer items stay fixed and horizontal gaps become equal");
|
|
|
|
first.node.attr({ x: 10, y: 10 });
|
|
second.node.attr({ x: 80, y: 100 });
|
|
third.node.attr({ x: 250, y: 250 });
|
|
editor.applySelectionLayout("distribute-vertical");
|
|
assert.deepEqual([first, second, third].map((item) => Number(item.node.attr("y"))),
|
|
[10, 127.5, 250], "outer items stay fixed and the middle item receives an equal gap");
|
|
|
|
editor.applySelectionLayout("same-width");
|
|
assert.deepEqual([first, second, third].map((item) => Number(item.node.attr("width"))),
|
|
[120, 120, 120]);
|
|
editor.applySelectionLayout("same-height");
|
|
assert.deepEqual([first, second, third].map((item) => Number(item.node.attr("height"))),
|
|
[45, 45, 45]);
|
|
|
|
first.node.attr({ width: 90, height: 35 });
|
|
third.node.attr({ width: 180, height: 70 });
|
|
editor.applySelectionLayout("same-size");
|
|
assert.deepEqual([first, second, third].map((item) =>
|
|
[Number(item.node.attr("width")), Number(item.node.attr("height"))]),
|
|
[[120, 45], [120, 45], [120, 45]]);
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
assert.equal(editor.canUndo(), true, "layout commands participate in undo and autosave history");
|
|
assert.deepEqual(editor.toDocument().items.map((item) => [item.width, item.height]),
|
|
[[120, 45], [120, 45], [120, 45]]);
|
|
});
|