mirror of
https://github.com/noodlapp/noodl.git
synced 2026-01-12 07:12:52 +01:00
Initial commit
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>
This commit is contained in:
574
packages/noodl-editor/tests/nodegraph/nodegrapheditor.js
Normal file
574
packages/noodl-editor/tests/nodegraph/nodegrapheditor.js
Normal file
@@ -0,0 +1,574 @@
|
||||
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);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user