mirror of
https://github.com/The-Low-Code-Foundation/OpenNoodl.git
synced 2026-01-12 15:22:55 +01:00
Co-Authored-By: Eric Tuvesson <eric.tuvesson@gmail.com> Co-Authored-By: mikaeltellhed <2311083+mikaeltellhed@users.noreply.github.com> Co-Authored-By: kotte <14197736+mrtamagotchi@users.noreply.github.com> Co-Authored-By: Anders Larsson <64838990+anders-topp@users.noreply.github.com> Co-Authored-By: Johan <4934465+joolsus@users.noreply.github.com> Co-Authored-By: Tore Knudsen <18231882+torekndsn@users.noreply.github.com> Co-Authored-By: victoratndl <99176179+victoratndl@users.noreply.github.com>
575 lines
16 KiB
JavaScript
575 lines
16 KiB
JavaScript
const NodeGraphNode = require('@noodl-models/nodegraphmodel').NodeGraphNode;
|
|
const NodeGraphEditor = require('@noodl-views/nodegrapheditor').NodeGraphEditor;
|
|
const { ProjectModel } = require('@noodl-models/projectmodel');
|
|
const NodeLibrary = require('@noodl-models/nodelibrary').NodeLibrary;
|
|
const PopupLayer = require('@noodl-views/popuplayer');
|
|
const DebugInspector = require('@noodl-utils/debuginspector');
|
|
const ViewerConnection = require('../../src/editor/src/ViewerConnection');
|
|
|
|
describe('Node graph editor auto tests', function () {
|
|
var c1, g1;
|
|
|
|
// Records and store mouse calls to the node graph editor
|
|
var storeTimeout;
|
|
var recordedEvents = [];
|
|
|
|
function storeEvents() {
|
|
clearTimeout(storeTimeout);
|
|
storeTimeout = setTimeout(function () {
|
|
localStorage['recordedEvents'] = JSON.stringify(recordedEvents);
|
|
}, 1000);
|
|
}
|
|
|
|
function recordEvents() {
|
|
var types = ['mousedown', 'mousemove', 'mouseup'];
|
|
|
|
// mouse
|
|
var old = NodeGraphEditor.instance.mouse;
|
|
NodeGraphEditor.instance.mouse = function (type, pos, evt) {
|
|
var now = +new Date();
|
|
recordedEvents.push({
|
|
type: type,
|
|
time: now,
|
|
x: pos.x,
|
|
y: pos.y,
|
|
evt: evt
|
|
? {
|
|
button: evt.button,
|
|
shiftKey: evt.shiftKey ? true : undefined,
|
|
ctrlKey: evt.ctrlKey ? true : undefined,
|
|
spaceKey: evt.spaceKey ? true : undefined
|
|
}
|
|
: undefined
|
|
});
|
|
|
|
storeEvents();
|
|
|
|
return old.call(this, type, pos, evt);
|
|
};
|
|
|
|
// cut, copy, paste
|
|
var oldCut = NodeGraphEditor.instance.cut;
|
|
NodeGraphEditor.instance.cut = function () {
|
|
var now = +new Date();
|
|
recordedEvents.push({
|
|
type: 'cut',
|
|
time: now
|
|
});
|
|
return oldCut.call(this);
|
|
};
|
|
|
|
var oldCopy = NodeGraphEditor.instance.copy;
|
|
NodeGraphEditor.instance.copy = function () {
|
|
var now = +new Date();
|
|
recordedEvents.push({
|
|
type: 'copy',
|
|
time: now
|
|
});
|
|
return oldCopy.call(this);
|
|
};
|
|
|
|
var oldPaste = NodeGraphEditor.instance.paste;
|
|
NodeGraphEditor.instance.paste = function () {
|
|
var now = +new Date();
|
|
recordedEvents.push({
|
|
type: 'paste',
|
|
time: now
|
|
});
|
|
return oldPaste.call(this);
|
|
};
|
|
}
|
|
|
|
// Plays back previously stored mouse calls
|
|
function playEvents(events, done, realtime) {
|
|
var i = 0;
|
|
|
|
function play() {
|
|
var event = events[i];
|
|
var type = event.type;
|
|
if (type === 'cut') {
|
|
NodeGraphEditor.instance.cut();
|
|
} else if (type === 'copy') {
|
|
NodeGraphEditor.instance.copy();
|
|
} else if (type === 'paste') {
|
|
NodeGraphEditor.instance.paste();
|
|
} else {
|
|
NodeGraphEditor.instance.mouse(
|
|
event.type,
|
|
{
|
|
x: event.x,
|
|
y: event.y
|
|
},
|
|
event.evt
|
|
);
|
|
}
|
|
|
|
if (events[i + 1]) {
|
|
setTimeout(function () {
|
|
i++;
|
|
play();
|
|
}, events[i + 1].time - event.time);
|
|
} else {
|
|
done();
|
|
}
|
|
}
|
|
|
|
play();
|
|
}
|
|
|
|
// Sets up a fresh node graph editor
|
|
function setup() {
|
|
$('body').append(
|
|
'<div id="node-graph-editor" style="position:absolute; top:0px; right:0px; width:400px; height:800px; background-color:white;"></div>'
|
|
);
|
|
|
|
// Disable context menu
|
|
$('body').on('contextmenu', function () {
|
|
return false;
|
|
});
|
|
|
|
// NodeLibrary.instance = new NodeLibrary();
|
|
|
|
ProjectModel.instance = ProjectModel.fromJSON(project);
|
|
NodeLibrary.instance.registerModule(ProjectModel.instance);
|
|
|
|
c1 = ProjectModel.instance.getComponentWithName('Root');
|
|
g1 = c1.graph;
|
|
|
|
// Mocking panel instance
|
|
PopupLayer.instance = {
|
|
showTooltip: function () {},
|
|
hideTooltip: function () {},
|
|
showToast: function () {},
|
|
on: function () {},
|
|
hidePopup: function () {},
|
|
showPopup: function () {},
|
|
isDragging: function () {
|
|
return false;
|
|
},
|
|
showModal: function () {},
|
|
hideModal: function () {},
|
|
hideAllModalsAndPopups: function () {}
|
|
};
|
|
|
|
// Mocking viewer connection
|
|
ViewerConnection.instance = {
|
|
on: function () {},
|
|
sendNodeHighlighted: function () {}
|
|
};
|
|
|
|
NodeGraphEditor.instance = new NodeGraphEditor({
|
|
model: g1
|
|
});
|
|
NodeGraphEditor.instance.setPanAndScale({
|
|
scale: 1,
|
|
x: 0,
|
|
y: 0
|
|
});
|
|
NodeGraphEditor.instance.render();
|
|
$('#node-graph-editor').append(NodeGraphEditor.instance.el);
|
|
NodeGraphEditor.instance.resize({
|
|
x: 0,
|
|
y: 0,
|
|
width: 400,
|
|
height: 800
|
|
});
|
|
}
|
|
|
|
var project = {
|
|
components: [
|
|
{
|
|
name: 'Root',
|
|
ports: [],
|
|
visual: true,
|
|
visualRootId: '14e31556-f569-21bb-e948-65af515ae574',
|
|
canHaveVisualChildren: false,
|
|
graph: {
|
|
connections: [],
|
|
roots: []
|
|
}
|
|
}
|
|
]
|
|
};
|
|
|
|
function setupNodes() {
|
|
g1.addRoot(
|
|
NodeGraphNode.fromJSON({
|
|
id: '14e31556-f569-21bb-e948-65af515ae574',
|
|
type: 'group',
|
|
label: 'group1',
|
|
x: 49,
|
|
y: 75
|
|
})
|
|
);
|
|
|
|
g1.addRoot(
|
|
NodeGraphNode.fromJSON({
|
|
id: '33a2be5c-b341-27b4-292f-aee1b5bc30fe',
|
|
type: 'group',
|
|
label: 'group2',
|
|
x: 49,
|
|
y: 164
|
|
})
|
|
);
|
|
|
|
g1.addRoot(
|
|
NodeGraphNode.fromJSON({
|
|
id: '34ea0053-3334-d2cb-3a31-de577030102e',
|
|
type: 'group',
|
|
label: 'group3',
|
|
x: 49,
|
|
y: 267
|
|
})
|
|
);
|
|
}
|
|
|
|
function setupNodes2() {
|
|
g1.addRoot(
|
|
NodeGraphNode.fromJSON({
|
|
id: 'A',
|
|
type: 'group',
|
|
label: 'group4',
|
|
x: 49,
|
|
y: 367
|
|
})
|
|
);
|
|
}
|
|
|
|
function setupConnections() {
|
|
g1.addConnection({
|
|
fromId: '14e31556-f569-21bb-e948-65af515ae574',
|
|
fromProperty: 'screenX',
|
|
toId: '33a2be5c-b341-27b4-292f-aee1b5bc30fe',
|
|
toProperty: 'x'
|
|
});
|
|
}
|
|
|
|
function setupConnections2() {
|
|
g1.addConnection({
|
|
fromId: '34ea0053-3334-d2cb-3a31-de577030102e',
|
|
fromProperty: 'screenX',
|
|
toId: '33a2be5c-b341-27b4-292f-aee1b5bc30fe',
|
|
toProperty: 'x'
|
|
});
|
|
|
|
g1.addConnection({
|
|
fromId: '14e31556-f569-21bb-e948-65af515ae574',
|
|
fromProperty: 'screenX',
|
|
toId: '34ea0053-3334-d2cb-3a31-de577030102e',
|
|
toProperty: 'y'
|
|
});
|
|
}
|
|
|
|
function teardownNodes() {
|
|
g1.removeAllNodes();
|
|
}
|
|
|
|
// Closes and tears the node graph editor down
|
|
function teardown() {
|
|
$('#node-graph-editor').remove();
|
|
NodeGraphEditor.instance = undefined;
|
|
}
|
|
|
|
it('can setup view', function () {
|
|
setup();
|
|
expect(NodeGraphEditor.instance).not.toBe(undefined);
|
|
});
|
|
|
|
/* it('can record events',function(done) {
|
|
setupNodes();
|
|
// setupNodes2();
|
|
recordEvents();
|
|
});
|
|
return;*/
|
|
|
|
// Multi re arrange
|
|
xit('Multi rearrange nodes, attach, detach', function (done) {
|
|
setupNodes();
|
|
setupNodes2();
|
|
|
|
playEvents(require('../recordings/multirearrange.json'), function () {
|
|
expect(g1.roots.length).toBe(2);
|
|
|
|
var n1 = g1.findNodeWithId('14e31556-f569-21bb-e948-65af515ae574');
|
|
var n2 = g1.findNodeWithId('33a2be5c-b341-27b4-292f-aee1b5bc30fe');
|
|
var n3 = g1.findNodeWithId('34ea0053-3334-d2cb-3a31-de577030102e');
|
|
var n4 = g1.findNodeWithId('A');
|
|
|
|
expect(n1.children[0]).toBe(n3);
|
|
expect(n4.children[0]).toBe(n2);
|
|
|
|
expect(NodeGraphEditor.instance.verifyWithModel()).toBe(true);
|
|
teardownNodes();
|
|
done();
|
|
});
|
|
});
|
|
|
|
xit('cut n paste', function (done) {
|
|
setupNodes();
|
|
|
|
playEvents(require('../recordings/cutnpaste.json'), function () {
|
|
expect(g1.roots.length).toBe(3);
|
|
expect(g1.findNodeWithId('33a2be5c-b341-27b4-292f-aee1b5bc30fe')).not.toBe(undefined);
|
|
expect(g1.findNodeWithId('34ea0053-3334-d2cb-3a31-de577030102e')).not.toBe(undefined);
|
|
expect(g1.findNodeWithId('14e31556-f569-21bb-e948-65af515ae574')).not.toBe(undefined);
|
|
|
|
expect(NodeGraphEditor.instance.verifyWithModel()).toBe(true);
|
|
teardownNodes();
|
|
done();
|
|
});
|
|
});
|
|
|
|
xit('can undo attach, detach and move', function (done) {
|
|
setupNodes();
|
|
|
|
playEvents(require('../recordings/undoarrange.json'), function () {
|
|
// group3
|
|
expect(g1.findNodeWithId('34ea0053-3334-d2cb-3a31-de577030102e').parent).toBe(undefined);
|
|
|
|
// group2 child to group1
|
|
expect(g1.findNodeWithId('33a2be5c-b341-27b4-292f-aee1b5bc30fe').parent).toBe(
|
|
g1.findNodeWithId('14e31556-f569-21bb-e948-65af515ae574')
|
|
);
|
|
|
|
NodeGraphEditor.instance.undo(); // Undo detach
|
|
|
|
// group3 child to group1
|
|
expect(g1.findNodeWithId('34ea0053-3334-d2cb-3a31-de577030102e').parent).toBe(
|
|
g1.findNodeWithId('14e31556-f569-21bb-e948-65af515ae574')
|
|
);
|
|
|
|
NodeGraphEditor.instance.undo(); // Undo attach
|
|
|
|
expect(g1.findNodeWithId('34ea0053-3334-d2cb-3a31-de577030102e').parent).toBe(undefined);
|
|
expect(g1.findNodeWithId('33a2be5c-b341-27b4-292f-aee1b5bc30fe').parent).toBe(undefined);
|
|
|
|
NodeGraphEditor.instance.undo(); // Undo move
|
|
|
|
expect(g1.findNodeWithId('34ea0053-3334-d2cb-3a31-de577030102e').x).toBe(49);
|
|
expect(g1.findNodeWithId('34ea0053-3334-d2cb-3a31-de577030102e').y).toBe(267);
|
|
|
|
expect(NodeGraphEditor.instance.verifyWithModel()).toBe(true);
|
|
teardownNodes();
|
|
done();
|
|
});
|
|
});
|
|
|
|
// Single re arrange
|
|
xit('single rearrange nodes, attach, detach', function (done) {
|
|
setupNodes();
|
|
|
|
playEvents(require('../recordings/singlerearrange.json'), function () {
|
|
var n = g1.findNodeWithId('14e31556-f569-21bb-e948-65af515ae574');
|
|
expect(n.parent).toBe(undefined);
|
|
|
|
var n1 = g1.findNodeWithId('33a2be5c-b341-27b4-292f-aee1b5bc30fe');
|
|
var n2 = g1.findNodeWithId('34ea0053-3334-d2cb-3a31-de577030102e');
|
|
|
|
expect(n1.parent).toBe(n);
|
|
expect(n2.parent).toBe(n);
|
|
|
|
expect(n.children[0]).toBe(n2);
|
|
expect(n.children[1]).toBe(n1);
|
|
|
|
expect(g1.roots.length).toBe(1);
|
|
|
|
expect(NodeGraphEditor.instance.verifyWithModel()).toBe(true);
|
|
teardownNodes();
|
|
done();
|
|
});
|
|
});
|
|
|
|
// Multi select and mode nodes
|
|
xit('can multiselect and move nodes', function (done) {
|
|
setupNodes();
|
|
|
|
playEvents(require('../recordings/multimove.json'), function () {
|
|
var n1 = NodeGraphEditor.instance.model.findNodeWithId('14e31556-f569-21bb-e948-65af515ae574');
|
|
var n2 = NodeGraphEditor.instance.model.findNodeWithId('33a2be5c-b341-27b4-292f-aee1b5bc30fe');
|
|
|
|
expect(n1.x).toBe(50);
|
|
expect(n1.y).toBe(365);
|
|
|
|
expect(n2.x).toBe(50);
|
|
expect(n2.y).toBe(454);
|
|
|
|
expect(NodeGraphEditor.instance.verifyWithModel()).toBe(true);
|
|
teardownNodes();
|
|
done();
|
|
});
|
|
});
|
|
|
|
// Drag to move nodes
|
|
xit('can move nodes', function (done) {
|
|
setupNodes();
|
|
|
|
playEvents(require('../recordings/movenode.json'), function () {
|
|
var n = NodeGraphEditor.instance.model.findNodeWithId('14e31556-f569-21bb-e948-65af515ae574');
|
|
expect(n.x).toBe(47);
|
|
expect(n.y).toBe(408);
|
|
|
|
expect(NodeGraphEditor.instance.verifyWithModel()).toBe(true);
|
|
teardownNodes();
|
|
done();
|
|
});
|
|
});
|
|
|
|
xit('can undo cut and paste', function (done) {
|
|
setupNodes();
|
|
|
|
playEvents(require('../recordings/undocutpaste.json'), function () {
|
|
expect(g1.roots.length).toBe(3);
|
|
expect(g1.roots[1].label).toBe('group2');
|
|
expect(g1.roots[2].label).toBe('group3');
|
|
expect(g1.findNodeWithId('33a2be5c-b341-27b4-292f-aee1b5bc30fe')).toBe(undefined);
|
|
expect(g1.findNodeWithId('34ea0053-3334-d2cb-3a31-de577030102e')).toBe(undefined);
|
|
|
|
NodeGraphEditor.instance.undo(); // Undo paste
|
|
|
|
expect(g1.roots.length).toBe(1);
|
|
|
|
NodeGraphEditor.instance.undo(); // Undo cut
|
|
|
|
expect(g1.findNodeWithId('33a2be5c-b341-27b4-292f-aee1b5bc30fe')).not.toBe(undefined);
|
|
expect(g1.findNodeWithId('34ea0053-3334-d2cb-3a31-de577030102e')).not.toBe(undefined);
|
|
|
|
expect(NodeGraphEditor.instance.verifyWithModel()).toBe(true);
|
|
teardownNodes();
|
|
done();
|
|
});
|
|
});
|
|
|
|
// Undo cut children
|
|
xit('Can undo cut children', function (done) {
|
|
setupNodes();
|
|
setupNodes2();
|
|
|
|
playEvents(require('../recordings/undocutchildren.json'), function () {
|
|
expect(g1.findNodeWithId('14e31556-f569-21bb-e948-65af515ae574').children[0]).toBe(g1.findNodeWithId('A'));
|
|
|
|
NodeGraphEditor.instance.undo();
|
|
|
|
expect(g1.findNodeWithId('14e31556-f569-21bb-e948-65af515ae574').children[0]).toBe(
|
|
g1.findNodeWithId('33a2be5c-b341-27b4-292f-aee1b5bc30fe')
|
|
);
|
|
expect(g1.findNodeWithId('33a2be5c-b341-27b4-292f-aee1b5bc30fe').children[0]).toBe(
|
|
g1.findNodeWithId('34ea0053-3334-d2cb-3a31-de577030102e')
|
|
);
|
|
|
|
expect(NodeGraphEditor.instance.verifyWithModel()).toBe(true);
|
|
teardownNodes();
|
|
done();
|
|
});
|
|
});
|
|
|
|
// Undo cut connections
|
|
xit('Can undo cut connections', function (done) {
|
|
setupNodes();
|
|
setupConnections();
|
|
setupConnections2();
|
|
|
|
playEvents(require('../recordings/undocutconnections.json'), function () {
|
|
expect(g1.connections.length).toBe(0);
|
|
|
|
NodeGraphEditor.instance.undo();
|
|
|
|
expect(g1.connections.length).toBe(3);
|
|
|
|
expect(NodeGraphEditor.instance.verifyWithModel()).toBe(true);
|
|
teardownNodes();
|
|
done();
|
|
});
|
|
});
|
|
|
|
// Can delete and undo connection
|
|
xit('can delete and undo delete connection', function (done) {
|
|
setupNodes();
|
|
setupConnections();
|
|
|
|
playEvents(require('../recordings/deletecon.json'), function () {
|
|
expect(g1.connections.length).toBe(0);
|
|
NodeGraphEditor.instance.undo();
|
|
expect(g1.connections.length).toBe(1);
|
|
expect(g1.connections[0].fromId).toBe('14e31556-f569-21bb-e948-65af515ae574');
|
|
|
|
expect(NodeGraphEditor.instance.verifyWithModel()).toBe(true);
|
|
teardownNodes();
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
// Delete
|
|
xit('can delete nodes and undo', function (done) {
|
|
setupNodes();
|
|
setupConnections();
|
|
|
|
playEvents(require('../recordings/deleteandundo.json'), function () {
|
|
NodeGraphEditor.instance.delete();
|
|
|
|
expect(g1.findNodeWithId('14e31556-f569-21bb-e948-65af515ae574')).toBe(undefined);
|
|
expect(g1.findNodeWithId('33a2be5c-b341-27b4-292f-aee1b5bc30fe')).toBe(undefined);
|
|
expect(g1.findNodeWithId('34ea0053-3334-d2cb-3a31-de577030102e')).not.toBe(undefined);
|
|
expect(g1.connections.length).toBe(0);
|
|
|
|
NodeGraphEditor.instance.undo();
|
|
|
|
expect(g1.findNodeWithId('14e31556-f569-21bb-e948-65af515ae574')).not.toBe(undefined);
|
|
expect(g1.findNodeWithId('33a2be5c-b341-27b4-292f-aee1b5bc30fe')).not.toBe(undefined);
|
|
expect(g1.connections.length).toBe(1);
|
|
teardownNodes();
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
xit('Can create inspectors', function (done) {
|
|
setupNodes();
|
|
setupConnections();
|
|
setupConnections2();
|
|
|
|
playEvents(
|
|
require('../recordings/connectiondebugger.json'),
|
|
function () {
|
|
var model = DebugInspector.InspectorsModel.instanceForProject(ProjectModel.instance);
|
|
var inspectors = model.getInspectors();
|
|
expect(inspectors[1]).toEqual({
|
|
connectionKey: '14e31556-f569-21bb-e948-65af515ae574screenX33a2be5c-b341-27b4-292f-aee1b5bc30fex',
|
|
pinned: true,
|
|
position: 0.390625,
|
|
type: 'connection',
|
|
connection: {
|
|
fromId: '14e31556-f569-21bb-e948-65af515ae574',
|
|
fromProperty: 'screenX',
|
|
toId: '33a2be5c-b341-27b4-292f-aee1b5bc30fe',
|
|
toProperty: 'x'
|
|
}
|
|
});
|
|
expect(inspectors[0]).toEqual({
|
|
connectionKey: '34ea0053-3334-d2cb-3a31-de577030102escreenX33a2be5c-b341-27b4-292f-aee1b5bc30fex',
|
|
pinned: true,
|
|
position: 0.515625,
|
|
type: 'connection',
|
|
connection: {
|
|
fromId: '34ea0053-3334-d2cb-3a31-de577030102e',
|
|
fromProperty: 'screenX',
|
|
toId: '33a2be5c-b341-27b4-292f-aee1b5bc30fe',
|
|
toProperty: 'x'
|
|
}
|
|
});
|
|
expect(inspectors.length).toBe(2);
|
|
|
|
teardownNodes();
|
|
done();
|
|
},
|
|
true
|
|
); // Play in real time
|
|
});
|
|
|
|
// Multi select and mode nodes
|
|
it('can tear down', function () {
|
|
teardown();
|
|
expect(NodeGraphEditor.instance).toBe(undefined);
|
|
});
|
|
});
|