126 lines
3.7 KiB
JavaScript
126 lines
3.7 KiB
JavaScript
"use strict";
|
|
|
|
const test = require("node:test");
|
|
const assert = require("node:assert/strict");
|
|
|
|
class FakeEventTarget {
|
|
constructor() {
|
|
this.listeners = new Map();
|
|
}
|
|
|
|
addEventListener(type, listener) {
|
|
const listeners = this.listeners.get(type) || [];
|
|
listeners.push(listener);
|
|
this.listeners.set(type, listeners);
|
|
}
|
|
|
|
removeEventListener(type, listener) {
|
|
this.listeners.set(type, (this.listeners.get(type) || [])
|
|
.filter((candidate) => candidate !== listener));
|
|
}
|
|
|
|
dispatchEvent(event) {
|
|
for (const listener of this.listeners.get(event.type) || []) listener(event);
|
|
}
|
|
}
|
|
|
|
class FakeElement extends FakeEventTarget {
|
|
constructor(tagName) {
|
|
super();
|
|
this.tagName = tagName.toUpperCase();
|
|
this.style = {};
|
|
this.attributes = {};
|
|
this.childNodes = [];
|
|
this.parentNode = null;
|
|
this.scrollLeft = 0;
|
|
this.scrollTop = 0;
|
|
this.scrollWidth = 1000;
|
|
this.scrollHeight = 800;
|
|
this.clientWidth = 1000;
|
|
this.clientHeight = 800;
|
|
this.textContent = "";
|
|
}
|
|
|
|
appendChild(child) {
|
|
if (child.parentNode) child.parentNode.removeChild(child);
|
|
child.parentNode = this;
|
|
this.childNodes.push(child);
|
|
return child;
|
|
}
|
|
|
|
removeChild(child) {
|
|
const index = this.childNodes.indexOf(child);
|
|
if (index >= 0) this.childNodes.splice(index, 1);
|
|
child.parentNode = null;
|
|
return child;
|
|
}
|
|
|
|
setAttribute(name, value) {
|
|
this.attributes[name] = String(value);
|
|
}
|
|
|
|
getBoundingClientRect() {
|
|
return { left: 0, top: 0, right: this.clientWidth, bottom: this.clientHeight };
|
|
}
|
|
|
|
set innerHTML(markup) {
|
|
this.childNodes = [];
|
|
if (!String(markup).includes("<svg>")) return;
|
|
const svg = new FakeElement("svg");
|
|
svg.appendChild(new FakeElement("path"));
|
|
svg.appendChild(new FakeElement("path"));
|
|
this.appendChild(svg);
|
|
this.appendChild(new FakeElement("div"));
|
|
}
|
|
}
|
|
|
|
function settleRendering() {
|
|
return new Promise((resolve) => setImmediate(() => setImmediate(resolve)));
|
|
}
|
|
|
|
test("relations render and hit-test behind concept nodes", async () => {
|
|
const root = new FakeElement("div");
|
|
const fakeDocument = new FakeEventTarget();
|
|
fakeDocument.body = root;
|
|
fakeDocument.createElement = (tagName) => new FakeElement(tagName);
|
|
global.document = fakeDocument;
|
|
global.window = {
|
|
requestAnimationFrame: (callback) => setImmediate(callback)
|
|
};
|
|
|
|
const { DiagramEngine } = await import("../static/cmap/cmap.js");
|
|
assert.equal(global.window.Cmap, undefined,
|
|
"the ES-module engine does not install a legacy global");
|
|
const map = new DiagramEngine(root);
|
|
const concept = map.node({ x: 40, y: 0, width: 120, height: 50 });
|
|
const otherConcept = map.node({ x: 220, y: 0, width: 120, height: 50 });
|
|
const relation = map.link({
|
|
sourceX: 0, sourceY: 25,
|
|
cx: 150, cy: 25,
|
|
targetX: 360, targetY: 25
|
|
});
|
|
relation.sourceNode(concept).targetNode(otherConcept).straighten();
|
|
assert.equal(relation.sourceNode(), concept);
|
|
assert.equal(relation.targetNode(), otherConcept);
|
|
await settleRendering();
|
|
|
|
relation.toFront();
|
|
await settleRendering();
|
|
assert.ok(Number(relation.element().style.zIndex) < Number(concept.element().style.zIndex));
|
|
assert.ok(Number(relation.element().style.zIndex) < Number(otherConcept.element().style.zIndex));
|
|
|
|
let selected = null;
|
|
map.onSelection((component) => { selected = component; });
|
|
const surface = root.childNodes[0];
|
|
surface.dispatchEvent({
|
|
type: "mousedown",
|
|
pageX: 100,
|
|
pageY: 25,
|
|
clientX: 100,
|
|
clientY: 25,
|
|
preventDefault() {}
|
|
});
|
|
fakeDocument.dispatchEvent({ type: "mouseup", pageX: 100, pageY: 25 });
|
|
assert.equal(selected, concept, "the concept wins when a relation crosses behind it");
|
|
});
|