mirror of
https://github.com/fluxscape/fluxscape.git
synced 2026-01-12 15:22:54 +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:
372
packages/noodl-editor/tests/components/componentconnections.js
Normal file
372
packages/noodl-editor/tests/components/componentconnections.js
Normal file
@@ -0,0 +1,372 @@
|
||||
const NodeGraphModel = require('@noodl-models/nodegraphmodel').NodeGraphModel;
|
||||
const NodeGraphNode = require('@noodl-models/nodegraphmodel').NodeGraphNode;
|
||||
const { ComponentModel } = require('@noodl-models/componentmodel');
|
||||
const NodeLibrary = require('@noodl-models/nodelibrary').NodeLibrary;
|
||||
const { ProjectModel } = require('@noodl-models/projectmodel');
|
||||
|
||||
describe('Connecting component inputs and outputs', function () {
|
||||
var g1, c1;
|
||||
var ci, co, n1, n2, co2;
|
||||
var p;
|
||||
var con1, con2;
|
||||
|
||||
beforeEach(() => {
|
||||
window.NodeLibraryData = require('../nodegraph/nodelibrary');
|
||||
NodeLibrary.instance.loadLibrary();
|
||||
|
||||
g1 = new NodeGraphModel();
|
||||
|
||||
c1 = new ComponentModel({
|
||||
name: 'c1',
|
||||
graph: g1
|
||||
});
|
||||
|
||||
ci = NodeGraphNode.fromJSON({
|
||||
type: 'Component Inputs',
|
||||
id: 'A'
|
||||
});
|
||||
g1.addRoot(ci);
|
||||
|
||||
co = NodeGraphNode.fromJSON({
|
||||
type: 'Component Outputs',
|
||||
id: 'B'
|
||||
});
|
||||
g1.addRoot(co);
|
||||
|
||||
co2 = NodeGraphNode.fromJSON({
|
||||
type: 'Component Outputs',
|
||||
id: 'B2'
|
||||
});
|
||||
g1.addRoot(co2);
|
||||
|
||||
n1 = NodeGraphNode.fromJSON({
|
||||
type: 'group',
|
||||
id: 'C'
|
||||
});
|
||||
g1.addRoot(n1);
|
||||
|
||||
n2 = NodeGraphNode.fromJSON({
|
||||
type: 'group',
|
||||
id: 'D'
|
||||
});
|
||||
g1.addRoot(n2);
|
||||
|
||||
p = new ProjectModel({
|
||||
name: 'test'
|
||||
});
|
||||
p.addComponent(c1);
|
||||
|
||||
NodeLibrary.instance.registerModule(p);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
NodeLibrary.instance.unregisterModule(p);
|
||||
});
|
||||
|
||||
function addPorts() {
|
||||
c1.graph.findNodeWithId('B').addPort({
|
||||
name: 'p1',
|
||||
plug: 'input',
|
||||
type: '*',
|
||||
index: 0
|
||||
});
|
||||
|
||||
c1.graph.findNodeWithId('B').addPort({
|
||||
name: 'p2',
|
||||
plug: 'input',
|
||||
type: '*',
|
||||
index: 1
|
||||
});
|
||||
|
||||
c1.graph.findNodeWithId('A').addPort({
|
||||
name: 'p3',
|
||||
plug: 'output',
|
||||
type: '*',
|
||||
index: 2
|
||||
});
|
||||
|
||||
c1.graph.findNodeWithId('B2').addPort({
|
||||
name: 'p1',
|
||||
plug: 'input',
|
||||
type: '*',
|
||||
index: 0
|
||||
});
|
||||
}
|
||||
|
||||
function addConnections() {
|
||||
con1 = {
|
||||
fromId: n1.id,
|
||||
fromProperty: 'width',
|
||||
toId: co.id,
|
||||
toProperty: 'p1'
|
||||
};
|
||||
g1.addConnection(con1);
|
||||
|
||||
con2 = {
|
||||
fromId: ci.id,
|
||||
fromProperty: 'p3',
|
||||
toId: n1.id,
|
||||
toProperty: 'x'
|
||||
};
|
||||
g1.addConnection(con2);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------
|
||||
it('can add input ports to the component', function () {
|
||||
addPorts();
|
||||
|
||||
var ports = c1.getPorts();
|
||||
expect(ports.length).toBe(3);
|
||||
expect(ports[0].name).toBe('p1');
|
||||
expect(ports[0].plug).toBe('output');
|
||||
expect(ports[0].type).toBe('*');
|
||||
expect(ports[1].name).toBe('p2');
|
||||
expect(ports[1].plug).toBe('output');
|
||||
expect(ports[1].type).toBe('*');
|
||||
expect(ports[2].name).toBe('p3');
|
||||
expect(ports[2].plug).toBe('input');
|
||||
expect(ports[2].type).toBe('*');
|
||||
});
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------
|
||||
it('can list ports correctly for component input/outputs', function () {
|
||||
addPorts();
|
||||
|
||||
var ports = c1.graph.findNodeWithId('B').getPorts('input');
|
||||
|
||||
expect(ports[0].name).toBe('p1');
|
||||
expect(ports[1].name).toBe('p2');
|
||||
expect(ports[0].plug).toBe('input');
|
||||
expect(ports[1].plug).toBe('input');
|
||||
expect(ports[0].type).toBe('*');
|
||||
expect(ports[1].type).toBe('*');
|
||||
expect(ports.length).toBe(2);
|
||||
|
||||
var ports = c1.graph.findNodeWithId('A').getPorts('output');
|
||||
expect(ports[0].name).toBe('p3');
|
||||
expect(ports[0].plug).toBe('output');
|
||||
expect(ports[0].type).toBe('*');
|
||||
expect(ports.length).toBe(1);
|
||||
});
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------
|
||||
it('reports connection status properly', function () {
|
||||
addPorts();
|
||||
|
||||
// component input
|
||||
var status = g1.getConnectionStatus({
|
||||
sourceNode: ci,
|
||||
sourcePort: 'p1',
|
||||
targetNode: n1,
|
||||
targetPort: 'width'
|
||||
});
|
||||
expect(status.connectable).toBe(true);
|
||||
|
||||
// number to number
|
||||
var status = g1.getConnectionStatus({
|
||||
sourceNode: n1,
|
||||
sourcePort: 'screenX',
|
||||
targetNode: n2,
|
||||
targetPort: 'width'
|
||||
});
|
||||
expect(status.connectable).toBe(true);
|
||||
|
||||
// component outputs
|
||||
var status = g1.getConnectionStatus({
|
||||
sourceNode: n1,
|
||||
sourcePort: 'width',
|
||||
targetNode: co,
|
||||
targetPort: 'p1'
|
||||
});
|
||||
expect(status.connectable).toBe(true);
|
||||
});
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------
|
||||
it('can rename ports', function () {
|
||||
addPorts();
|
||||
addConnections();
|
||||
|
||||
expect(c1.graph.findNodeWithId('B').renamePortWithName('p1', 'p2')).toBe(false);
|
||||
expect(c1.graph.findNodeWithId('B').renamePortWithName('p1', 'p1b')).toBe(true);
|
||||
expect(c1.graph.findNodeWithId('A').renamePortWithName('p3', 'p3b')).toBe(true);
|
||||
|
||||
expect(con2.fromProperty).toBe('p3b');
|
||||
expect(con1.toProperty).toBe('p1b');
|
||||
|
||||
expect(c1.graph.findNodeWithId('B').renamePortWithName('p1b', 'p1')).toBe(true);
|
||||
expect(c1.graph.findNodeWithId('A').renamePortWithName('p3b', 'p3')).toBe(true);
|
||||
});
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------
|
||||
it('cannot remove ports with connections', function () {
|
||||
addPorts();
|
||||
addConnections();
|
||||
|
||||
expect(c1.graph.findNodeWithId('B').removePortWithName('p1')).toBe(false);
|
||||
expect(c1.graph.findNodeWithId('B').removePortWithName('p2')).toBe(true);
|
||||
expect(c1.graph.findNodeWithId('A').removePortWithName('p3')).toBe(false);
|
||||
|
||||
expect(c1.findPortWithName('p2')).toBe(undefined);
|
||||
expect(c1.findPortWithName('p1').name).toBe('p1');
|
||||
});
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------
|
||||
it('can add more connections are report type correctly', function () {
|
||||
addPorts();
|
||||
addConnections();
|
||||
|
||||
g1.removeConnection(con1);
|
||||
g1.removeConnection(con2);
|
||||
|
||||
// Single type, should just be number
|
||||
g1.addConnection({
|
||||
fromId: n1.id,
|
||||
fromProperty: 'screenX',
|
||||
toId: co2.id,
|
||||
toProperty: 'p1'
|
||||
});
|
||||
|
||||
expect(NodeLibrary.nameForPortType(c1.findPortWithName('p1').type)).toBe('number');
|
||||
|
||||
// Connect a number and boolean to the components outputs, but on different
|
||||
// nodes
|
||||
g1.addConnection({
|
||||
fromId: n1.id,
|
||||
fromProperty: 'clipOut',
|
||||
toId: co.id,
|
||||
toProperty: 'p1'
|
||||
});
|
||||
|
||||
// Resulting type should be boolean (the only type that can be converted to from both boolean and number)
|
||||
expect(NodeLibrary.nameForPortType(c1.findPortWithName('p1').type)).toBe('boolean');
|
||||
|
||||
// Adding a connection to a star type should not affect the resulting type
|
||||
g1.addConnection({
|
||||
fromId: ci.id,
|
||||
fromProperty: 'p3',
|
||||
toId: co2.id,
|
||||
toProperty: 'p1'
|
||||
});
|
||||
|
||||
expect(NodeLibrary.nameForPortType(c1.findPortWithName('p1').type)).toBe('boolean');
|
||||
|
||||
// Adding a reference type connection will cause it to break down, no type can be found
|
||||
g1.addConnection({
|
||||
fromId: n1.id,
|
||||
fromProperty: 'this',
|
||||
toId: co2.id,
|
||||
toProperty: 'p1'
|
||||
});
|
||||
|
||||
expect(c1.findPortWithName('p1').type).toBe('*');
|
||||
});
|
||||
|
||||
it('can report plug correctly', function () {
|
||||
addPorts();
|
||||
|
||||
expect(c1.findPortWithName('p1').plug).toBe('output');
|
||||
expect(c1.findPortWithName('p3').plug).toBe('input');
|
||||
|
||||
// Adding an extra port of mixed type for p1 should change the plug
|
||||
c1.graph.findNodeWithId('A').addPort({
|
||||
name: 'p1',
|
||||
plug: 'output',
|
||||
type: '*'
|
||||
});
|
||||
|
||||
expect(c1.getPorts().length).toBe(4);
|
||||
expect(c1.getPorts()).toContain({
|
||||
name: 'p1',
|
||||
type: '*',
|
||||
default: undefined,
|
||||
group: undefined,
|
||||
plug: 'output',
|
||||
index: 0
|
||||
});
|
||||
expect(c1.getPorts()).toContain({
|
||||
name: 'p1',
|
||||
type: '*',
|
||||
default: undefined,
|
||||
group: undefined,
|
||||
plug: 'input',
|
||||
index: 1
|
||||
});
|
||||
});
|
||||
|
||||
it('can ignore * types', function () {
|
||||
addPorts();
|
||||
addConnections();
|
||||
|
||||
// Remove all connections
|
||||
while (g1.connections.length > 0) {
|
||||
g1.removeConnection(g1.connections[0]);
|
||||
}
|
||||
|
||||
// Number type
|
||||
g1.addConnection({
|
||||
fromId: n1.id,
|
||||
fromProperty: 'screenX',
|
||||
toId: co.id,
|
||||
toProperty: 'p1'
|
||||
});
|
||||
|
||||
expect(c1.findPortWithName('p1').type).toBe('number');
|
||||
|
||||
// Add a connection to a * type
|
||||
g1.addConnection({
|
||||
fromId: ci.id,
|
||||
fromProperty: 'p3',
|
||||
toId: co.id,
|
||||
toProperty: 'p1'
|
||||
});
|
||||
|
||||
// Should just ignore * type and still be number
|
||||
expect(c1.findPortWithName('p1').type).toBe('number');
|
||||
});
|
||||
|
||||
it('can support units with default in types', function () {
|
||||
addPorts();
|
||||
addConnections();
|
||||
|
||||
// Remove all connections
|
||||
while (g1.connections.length > 0) {
|
||||
g1.removeConnection(g1.connections[0]);
|
||||
}
|
||||
|
||||
// Add a connection to a type with units
|
||||
g1.addConnection({
|
||||
fromId: ci.id,
|
||||
fromProperty: 'p3',
|
||||
toId: n1.id,
|
||||
toProperty: 'x'
|
||||
});
|
||||
|
||||
// Should just ignore * type and still be number
|
||||
expect(c1.findPortWithName('p3').type.name).toBe('number');
|
||||
expect(c1.findPortWithName('p3').type.units).toEqual(['px', '%']);
|
||||
expect(c1.findPortWithName('p3').default).toEqual({
|
||||
value: 10,
|
||||
unit: '%'
|
||||
});
|
||||
|
||||
expect(c1.findPortWithName('p3').group).toBe('test'); // Should inherit from connected port
|
||||
|
||||
//Change parameter value should propagate to default
|
||||
n1.setParameter('x', {
|
||||
value: 50,
|
||||
unit: 'px'
|
||||
});
|
||||
expect(c1.findPortWithName('p3').default).toEqual({
|
||||
value: 50,
|
||||
unit: 'px'
|
||||
});
|
||||
});
|
||||
|
||||
it('can rearrange ports', function () {
|
||||
addPorts();
|
||||
addConnections();
|
||||
|
||||
ci.arrangePort('p3', undefined, 'G1');
|
||||
expect(c1.findPortWithName('p3').group).toBe('G1');
|
||||
});
|
||||
});
|
||||
322
packages/noodl-editor/tests/components/componentinstances.js
Normal file
322
packages/noodl-editor/tests/components/componentinstances.js
Normal file
@@ -0,0 +1,322 @@
|
||||
const { ProjectModel } = require('@noodl-models/projectmodel');
|
||||
const NodeLibrary = require('@noodl-models/nodelibrary').NodeLibrary;
|
||||
const NodeGraphModel = require('@noodl-models/nodegraphmodel').NodeGraphModel;
|
||||
const NodeGraphNode = require('@noodl-models/nodegraphmodel').NodeGraphNode;
|
||||
|
||||
describe('Component instances', function () {
|
||||
var c1, c2;
|
||||
var p, p2;
|
||||
var w;
|
||||
|
||||
beforeEach(() => {
|
||||
window.NodeLibraryData = require('../nodegraph/nodelibrary');
|
||||
NodeLibrary.instance.loadLibrary();
|
||||
|
||||
p = ProjectModel.fromJSON(project);
|
||||
NodeLibrary.instance.registerModule(p);
|
||||
|
||||
c1 = p.getComponentWithName('test');
|
||||
c2 = p.getComponentWithName('Root');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
NodeLibrary.instance.unregisterModule(p);
|
||||
});
|
||||
|
||||
it('can load project', function () {
|
||||
expect(p).not.toBe(undefined);
|
||||
});
|
||||
|
||||
it('can rename component inputs and outputs', function () {
|
||||
expect(c1.graph.findNodeWithId('46a72429-263c-2ad1-3ada-ce8a98b27308').renamePortWithName('p1', 'p1b')).toBe(true);
|
||||
expect(c1.graph.findNodeWithId('c01d18bc-b8cd-e792-6fd8-89694ef8da48').renamePortWithName('p3', 'p3b')).toBe(true);
|
||||
|
||||
expect(c2.graph.connections[0].toProperty).toBe('p1b');
|
||||
expect(c2.graph.connections[1].fromProperty).toBe('p3b');
|
||||
|
||||
expect(c2.graph.findNodeWithId('56b67ac1-224e-c3c8-b058-6fee9c590bee').parameters['p1b']).toBe(50);
|
||||
expect(c2.graph.findNodeWithId('56b67ac1-224e-c3c8-b058-6fee9c590bee').parameters['p1']).toBe(undefined);
|
||||
});
|
||||
|
||||
xit('can detect unhealthy connections', function () {
|
||||
c2.graph.evaluateHealth();
|
||||
|
||||
expect(
|
||||
c2.graph.getConnectionHealth({
|
||||
sourceId: '56b67ac1-224e-c3c8-b058-6fee9c590bee',
|
||||
sourcePort: 'p3',
|
||||
targetId: 'c0210ab9-94ab-c4c8-313b-3b394d5361f6',
|
||||
targetPort: 'opacity'
|
||||
}).healthy
|
||||
).toBe(true);
|
||||
|
||||
// Remove p3 connection
|
||||
c1.graph.removeConnection(c1.graph.connections[2]);
|
||||
c2.graph.evaluateHealth();
|
||||
|
||||
expect(
|
||||
c2.graph.getConnectionHealth({
|
||||
sourceId: '56b67ac1-224e-c3c8-b058-6fee9c590bee',
|
||||
sourcePort: 'p3b',
|
||||
targetId: 'c0210ab9-94ab-c4c8-313b-3b394d5361f6',
|
||||
targetPort: 'opacity'
|
||||
}).healthy
|
||||
).toBe(false);
|
||||
|
||||
// Connect p3 to a port of different type
|
||||
c1.graph.addConnection({
|
||||
fromId: 'f89b4fcd-5cfe-c47e-7fab-03948aad878c',
|
||||
fromProperty: 'this',
|
||||
toId: 'c01d18bc-b8cd-e792-6fd8-89694ef8da48',
|
||||
toProperty: 'p3b'
|
||||
});
|
||||
|
||||
// Health should still be bad (wrong type)
|
||||
expect(
|
||||
c2.graph.getConnectionHealth({
|
||||
sourceId: '56b67ac1-224e-c3c8-b058-6fee9c590bee',
|
||||
sourcePort: 'p3b',
|
||||
targetId: 'c0210ab9-94ab-c4c8-313b-3b394d5361f6',
|
||||
targetPort: 'opacity'
|
||||
}).healthy
|
||||
).toBe(false);
|
||||
|
||||
// Remove and restore
|
||||
c1.graph.removeConnection(c1.graph.connections[2]);
|
||||
c1.graph.addConnection({
|
||||
fromId: 'f89b4fcd-5cfe-c47e-7fab-03948aad878c',
|
||||
fromProperty: 'screenY',
|
||||
toId: 'c01d18bc-b8cd-e792-6fd8-89694ef8da48',
|
||||
toProperty: 'p3b'
|
||||
});
|
||||
c2.graph.evaluateHealth();
|
||||
|
||||
// Health should be back up
|
||||
expect(
|
||||
c2.graph.getConnectionHealth({
|
||||
sourceId: '56b67ac1-224e-c3c8-b058-6fee9c590bee',
|
||||
sourcePort: 'p3b',
|
||||
targetId: 'c0210ab9-94ab-c4c8-313b-3b394d5361f6',
|
||||
targetPort: 'opacity'
|
||||
}).healthy
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('can detect unhealthy nodes', function () {
|
||||
c2.graph.evaluateHealth();
|
||||
|
||||
// Multiple roots with same category is OK
|
||||
var n = c2.graph.findNodeWithId('56b67ac1-224e-c3c8-b058-6fee9c590bee');
|
||||
expect(n.getHealth().healthy).toBe(true);
|
||||
|
||||
c2.graph.removeNode(c2.graph.findNodeWithId('97720c17-1ff1-f4b0-8f97-8d70cf795348'));
|
||||
c2.graph.removeNode(c2.graph.findNodeWithId('c0210ab9-94ab-c4c8-313b-3b394d5361f6'));
|
||||
c2.graph.evaluateHealth();
|
||||
|
||||
expect(n.getHealth().healthy).toBe(true);
|
||||
|
||||
// The component cannot have visual children
|
||||
var nn = NodeGraphNode.fromJSON({
|
||||
type: 'group',
|
||||
id: 'A'
|
||||
});
|
||||
n.addChild(nn);
|
||||
c2.graph.evaluateHealth();
|
||||
|
||||
expect(n.getHealth().healthy).toBe(false);
|
||||
|
||||
// Adding a component children node will do it
|
||||
c1.graph.findNodeWithId('f89b4fcd-5cfe-c47e-7fab-03948aad878c').addChild(
|
||||
NodeGraphNode.fromJSON({
|
||||
type: 'Component Children',
|
||||
id: 'CC'
|
||||
})
|
||||
);
|
||||
c2.graph.evaluateHealth();
|
||||
|
||||
expect(n.getHealth().healthy).toBe(true);
|
||||
|
||||
// Remove the visual root of the test component, health will become false again
|
||||
c1.graph.removeNode(c1.graph.findNodeWithId('f89b4fcd-5cfe-c47e-7fab-03948aad878c'));
|
||||
c2.graph.evaluateHealth();
|
||||
|
||||
expect(n.getHealth().healthy).toBe(false);
|
||||
});
|
||||
|
||||
xit('component renamed are propageted to component references', function () {
|
||||
var c3 = p.getComponentWithName('/has_comp_ref');
|
||||
var c4 = p.getComponentWithName('/to_be_renamed');
|
||||
|
||||
var n = c3.graph.findNodeWithId('has_comp_ref_group');
|
||||
expect(n.parameters['compref']).toBe('/to_be_renamed');
|
||||
|
||||
c4.rename('/is_renamed');
|
||||
expect(n.parameters['compref']).toBe('/is_renamed');
|
||||
});
|
||||
|
||||
var project = {
|
||||
name: 'proj1',
|
||||
components: [
|
||||
{
|
||||
name: 'outside',
|
||||
graph: {
|
||||
roots: [
|
||||
{
|
||||
id: 'AABB',
|
||||
type: 'Component Inputs',
|
||||
parameters: {},
|
||||
children: [],
|
||||
ports: [
|
||||
{
|
||||
name: 'p1',
|
||||
plug: 'output',
|
||||
type: '='
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Root',
|
||||
ports: [],
|
||||
graph: {
|
||||
connections: [
|
||||
{
|
||||
fromId: '97720c17-1ff1-f4b0-8f97-8d70cf795348',
|
||||
fromProperty: 'screenX',
|
||||
toId: '56b67ac1-224e-c3c8-b058-6fee9c590bee',
|
||||
toProperty: 'p1'
|
||||
},
|
||||
{
|
||||
fromId: '56b67ac1-224e-c3c8-b058-6fee9c590bee',
|
||||
fromProperty: 'p3',
|
||||
toId: 'c0210ab9-94ab-c4c8-313b-3b394d5361f6',
|
||||
toProperty: 'opacity'
|
||||
}
|
||||
],
|
||||
roots: [
|
||||
{
|
||||
id: '97720c17-1ff1-f4b0-8f97-8d70cf795348',
|
||||
type: 'group',
|
||||
x: 359,
|
||||
y: 74,
|
||||
parameters: {},
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 'c0210ab9-94ab-c4c8-313b-3b394d5361f6',
|
||||
type: 'group',
|
||||
x: 457,
|
||||
y: 276,
|
||||
parameters: {},
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: '56b67ac1-224e-c3c8-b058-6fee9c590bee',
|
||||
type: 'test',
|
||||
x: 430,
|
||||
y: 181,
|
||||
parameters: {
|
||||
p1: 50
|
||||
},
|
||||
children: []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'test',
|
||||
graph: {
|
||||
connections: [
|
||||
{
|
||||
fromId: '46a72429-263c-2ad1-3ada-ce8a98b27308',
|
||||
fromProperty: 'p2',
|
||||
toId: 'f89b4fcd-5cfe-c47e-7fab-03948aad878c',
|
||||
toProperty: 'clip'
|
||||
},
|
||||
{
|
||||
fromId: '46a72429-263c-2ad1-3ada-ce8a98b27308',
|
||||
fromProperty: 'p1',
|
||||
toId: 'f89b4fcd-5cfe-c47e-7fab-03948aad878c',
|
||||
toProperty: 'scaleX'
|
||||
},
|
||||
{
|
||||
fromId: 'f89b4fcd-5cfe-c47e-7fab-03948aad878c',
|
||||
fromProperty: 'screenX',
|
||||
toId: 'c01d18bc-b8cd-e792-6fd8-89694ef8da48',
|
||||
toProperty: 'p3'
|
||||
}
|
||||
],
|
||||
roots: [
|
||||
{
|
||||
id: '46a72429-263c-2ad1-3ada-ce8a98b27308',
|
||||
type: 'Component Inputs',
|
||||
x: 336,
|
||||
y: 115,
|
||||
parameters: {},
|
||||
children: [],
|
||||
ports: [
|
||||
{
|
||||
name: 'p1',
|
||||
plug: 'output',
|
||||
type: '='
|
||||
},
|
||||
{
|
||||
name: 'p2',
|
||||
plug: 'output',
|
||||
type: '='
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'f89b4fcd-5cfe-c47e-7fab-03948aad878c',
|
||||
type: 'group',
|
||||
x: 497,
|
||||
y: 211,
|
||||
parameters: {},
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 'c01d18bc-b8cd-e792-6fd8-89694ef8da48',
|
||||
type: 'Component Outputs',
|
||||
x: 646,
|
||||
y: 283,
|
||||
parameters: {},
|
||||
children: [],
|
||||
ports: [
|
||||
{
|
||||
name: 'p3',
|
||||
plug: 'input',
|
||||
type: '='
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
// Test component references
|
||||
{
|
||||
name: '/has_comp_ref',
|
||||
ports: [],
|
||||
graph: {
|
||||
roots: [
|
||||
{
|
||||
id: 'has_comp_ref_group',
|
||||
type: 'group',
|
||||
parameters: {
|
||||
compref: '/to_be_renamed'
|
||||
},
|
||||
children: []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '/to_be_renamed',
|
||||
graph: {}
|
||||
}
|
||||
]
|
||||
};
|
||||
});
|
||||
147
packages/noodl-editor/tests/components/componentports.js
Normal file
147
packages/noodl-editor/tests/components/componentports.js
Normal file
@@ -0,0 +1,147 @@
|
||||
const { ComponentPorts } = require('@noodl-views/panels/componentports');
|
||||
const { ProjectModel } = require('@noodl-models/projectmodel');
|
||||
const NodeLibrary = require('@noodl-models/nodelibrary').NodeLibrary;
|
||||
const { UndoQueue } = require('@noodl-models/undo-queue-model');
|
||||
|
||||
describe('Components ports panel unit tests', function () {
|
||||
var cp, c;
|
||||
|
||||
beforeEach(() => {
|
||||
var project = {
|
||||
components: [
|
||||
{
|
||||
name: 'Root',
|
||||
graph: {
|
||||
roots: [
|
||||
{
|
||||
type: 'Component Inputs',
|
||||
id: 'A'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
window.NodeLibraryData = require('../nodegraph/nodelibrary');
|
||||
NodeLibrary.instance.loadLibrary();
|
||||
|
||||
ProjectModel.instance = ProjectModel.fromJSON(project);
|
||||
// NodeLibrary.instance.registerModule(project);
|
||||
|
||||
c = ProjectModel.instance.getComponentWithName('Root');
|
||||
cp = new ComponentPorts({
|
||||
model: c.graph.findNodeWithId('A'),
|
||||
plug: 'input'
|
||||
});
|
||||
cp.render();
|
||||
|
||||
cp.canArrangeInGroups = true; // Enable arrange in groups
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
ProjectModel.instance = undefined;
|
||||
});
|
||||
|
||||
it('can add ports', function () {
|
||||
expect(cp.performAdd('test').success).toBe(true);
|
||||
expect(c.findPortWithName('test')).not.toBe(undefined);
|
||||
|
||||
// Cannot create with same name
|
||||
expect(cp.performAdd('test').success).toBe(false);
|
||||
|
||||
// Can add with different name
|
||||
expect(cp.performAdd('test2').success).toBe(true);
|
||||
expect(c.findPortWithName('test2')).not.toBe(undefined);
|
||||
});
|
||||
|
||||
it('can undo add ports', function () {
|
||||
expect(cp.performAdd('undome').success).toBe(true);
|
||||
expect(c.findPortWithName('undome')).not.toBe(undefined);
|
||||
|
||||
UndoQueue.instance.undo();
|
||||
|
||||
expect(c.findPortWithName('undome')).toBe(undefined);
|
||||
});
|
||||
|
||||
it('can rename+undo ports', function () {
|
||||
cp.performAdd('test');
|
||||
cp.performAdd('test2');
|
||||
|
||||
// cannot rename to existing name
|
||||
expect(
|
||||
cp.performRename({
|
||||
oldName: 'test',
|
||||
newName: 'test2'
|
||||
}).success
|
||||
).toBe(false);
|
||||
|
||||
expect(
|
||||
cp.performRename({
|
||||
oldName: 'test',
|
||||
newName: 'test3'
|
||||
}).success
|
||||
).toBe(true);
|
||||
expect(c.findPortWithName('test3')).not.toBe(undefined);
|
||||
expect(c.findPortWithName('test')).toBe(undefined);
|
||||
|
||||
// Undo rename
|
||||
UndoQueue.instance.undo();
|
||||
|
||||
expect(c.findPortWithName('test3')).toBe(undefined);
|
||||
expect(c.findPortWithName('test')).not.toBe(undefined);
|
||||
});
|
||||
|
||||
it('can delete+undo ports', function () {
|
||||
cp.performAdd('test');
|
||||
cp.renderPorts(true);
|
||||
|
||||
expect(cp.performDelete('test').success).toBe(true);
|
||||
expect(c.findPortWithName('test')).toBe(undefined);
|
||||
|
||||
UndoQueue.instance.undo();
|
||||
|
||||
expect(c.findPortWithName('test')).not.toBe(undefined);
|
||||
});
|
||||
|
||||
it('can add group and port', function () {
|
||||
expect(cp.performAddGroup('G1').success).toBe(true);
|
||||
expect(cp.performAdd('P1').success).toBe(true);
|
||||
cp.renderPorts(true);
|
||||
|
||||
expect(c.findPortWithName('P1').group).toBe('G1');
|
||||
});
|
||||
|
||||
it('can rename group', function () {
|
||||
cp.performAddGroup('G1');
|
||||
cp.performAdd('P1');
|
||||
cp.renderPorts(true);
|
||||
|
||||
expect(
|
||||
cp.performRenameGroup({
|
||||
newName: 'G2',
|
||||
item: cp.findGroupWithName('G1')
|
||||
}).success
|
||||
).toBe(true);
|
||||
|
||||
expect(c.findPortWithName('P1').group).toBe('G2');
|
||||
});
|
||||
|
||||
it('cannot add group with unqualified name', function () {
|
||||
cp.performAddGroup('G2');
|
||||
cp.performAdd('P1');
|
||||
cp.renderPorts(true);
|
||||
|
||||
expect(cp.performAddGroup('G2').success).toBe(false); // Existing group
|
||||
expect(cp.performAddGroup('').success).toBe(false); // Empty name
|
||||
});
|
||||
|
||||
it('can delete group', function () {
|
||||
cp.performAddGroup('G2');
|
||||
cp.performAdd('P1');
|
||||
cp.renderPorts(true);
|
||||
|
||||
expect(cp.performDeleteGroup(cp.findGroupWithName('G2')).success).toBe(true);
|
||||
expect(c.findPortWithName('P1').group).toBe(undefined);
|
||||
});
|
||||
});
|
||||
536
packages/noodl-editor/tests/components/componentspanel.js
Normal file
536
packages/noodl-editor/tests/components/componentspanel.js
Normal file
@@ -0,0 +1,536 @@
|
||||
const { ComponentsPanelView } = require('@noodl-views/panels/componentspanel/ComponentsPanel');
|
||||
const { ProjectModel } = require('@noodl-models/projectmodel');
|
||||
const { UndoQueue } = require('@noodl-models/undo-queue-model');
|
||||
const NodeGraphEditor = require('@noodl-views/nodegrapheditor').NodeGraphEditor;
|
||||
const ViewerConnection = require('../../src/editor/src/ViewerConnection');
|
||||
|
||||
describe('Components panel unit tests', function () {
|
||||
var cp;
|
||||
var p1;
|
||||
|
||||
var project = {
|
||||
components: [
|
||||
{
|
||||
name: 'Root',
|
||||
graph: {}
|
||||
},
|
||||
{
|
||||
name: '/test/f1/a',
|
||||
graph: {}
|
||||
},
|
||||
{
|
||||
name: '/test/f2/a',
|
||||
graph: {}
|
||||
},
|
||||
{
|
||||
name: '/b',
|
||||
graph: {}
|
||||
},
|
||||
{
|
||||
name: '/test/ff/a',
|
||||
graph: {}
|
||||
},
|
||||
{
|
||||
name: '/q',
|
||||
graph: {}
|
||||
},
|
||||
{
|
||||
name: '/a',
|
||||
graph: {}
|
||||
},
|
||||
{
|
||||
name: '/dup/f1/a',
|
||||
graph: {}
|
||||
},
|
||||
// Undo tests
|
||||
{
|
||||
name: '/delete_folder/delete_comp',
|
||||
graph: {}
|
||||
},
|
||||
{
|
||||
name: '/rename_folder/rename_comp',
|
||||
graph: {}
|
||||
},
|
||||
{
|
||||
name: '/drop/a',
|
||||
graph: {}
|
||||
},
|
||||
{
|
||||
name: '/drop2/a',
|
||||
graph: {}
|
||||
},
|
||||
{
|
||||
name: '/dropundo',
|
||||
graph: {}
|
||||
},
|
||||
{
|
||||
name: '/nested-target/a',
|
||||
graph: {}
|
||||
},
|
||||
{
|
||||
name: '/nested-dropme/test/b',
|
||||
graph: {}
|
||||
},
|
||||
{
|
||||
name: '/delete-me/with-content/a',
|
||||
graph: {}
|
||||
},
|
||||
{
|
||||
name: '/delete-me/b',
|
||||
graph: {}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
// Mock node graph editor
|
||||
NodeGraphEditor.instance = {
|
||||
getActiveComponent() {
|
||||
return p1.getComponentWithName('Root');
|
||||
},
|
||||
on() {},
|
||||
off() {},
|
||||
switchToComponent() {}
|
||||
};
|
||||
|
||||
// Viewerconnection mock
|
||||
ViewerConnection.instance = {
|
||||
on() {},
|
||||
off() {}
|
||||
};
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
NodeGraphEditor.instance = undefined;
|
||||
ViewerConnection.instance = undefined;
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
p1 = ProjectModel.instance = ProjectModel.fromJSON(project);
|
||||
cp = new ComponentsPanelView({});
|
||||
cp.setNodeGraphEditor(NodeGraphEditor.instance);
|
||||
cp.render();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cp.dispose();
|
||||
ProjectModel.instance = undefined;
|
||||
});
|
||||
|
||||
it('can setup view', function () {
|
||||
expect(cp).not.toBe(undefined);
|
||||
});
|
||||
|
||||
it('can add new folders', function () {
|
||||
// Existing folder
|
||||
expect(
|
||||
cp.performAdd({
|
||||
type: 'folder',
|
||||
name: 'test'
|
||||
}).success
|
||||
).toBe(false);
|
||||
|
||||
// Empty name
|
||||
expect(
|
||||
cp.performAdd({
|
||||
type: 'folder',
|
||||
name: ''
|
||||
}).success
|
||||
).toBe(false);
|
||||
|
||||
// Add
|
||||
expect(
|
||||
cp.performAdd({
|
||||
type: 'folder',
|
||||
name: 'f3'
|
||||
}).success
|
||||
).toBe(true);
|
||||
|
||||
expect(cp.getFolderWithPath('/f3/')).not.toBe(undefined);
|
||||
});
|
||||
|
||||
it('can add components', function () {
|
||||
// Existing name
|
||||
expect(
|
||||
cp.performAdd({
|
||||
type: 'component',
|
||||
name: 'b',
|
||||
parentPath: '/'
|
||||
}).success
|
||||
).toBe(false);
|
||||
|
||||
// Empty name
|
||||
expect(
|
||||
cp.performAdd({
|
||||
type: 'component',
|
||||
name: ''
|
||||
}).success
|
||||
).toBe(false);
|
||||
|
||||
// Add
|
||||
expect(
|
||||
cp.performAdd({
|
||||
type: 'component',
|
||||
name: 'c',
|
||||
parentPath: '/'
|
||||
}).success
|
||||
).toBe(true);
|
||||
|
||||
expect(p1.getComponentWithName('/c')).not.toBe(undefined);
|
||||
expect(cp.getFolderWithPath('/').hasComponentWithName('c')).toBe(true);
|
||||
|
||||
// Add to sub directory
|
||||
expect(
|
||||
cp.performAdd({
|
||||
type: 'component',
|
||||
name: 'subsub',
|
||||
parentPath: '/test/ff/'
|
||||
}).success
|
||||
).toBe(true);
|
||||
|
||||
expect(p1.getComponentWithName('/test/ff/subsub')).not.toBe(undefined);
|
||||
expect(cp.getFolderWithPath('/test/ff/').hasComponentWithName('subsub')).toBe(true);
|
||||
});
|
||||
|
||||
it('can rename folders', function () {
|
||||
// Existing name
|
||||
expect(
|
||||
cp.performRename({
|
||||
type: 'folder',
|
||||
name: 'f2',
|
||||
folder: cp.getFolderWithPath('/test/ff/')
|
||||
}).success
|
||||
).toBe(false);
|
||||
|
||||
// Empty name
|
||||
expect(
|
||||
cp.performRename({
|
||||
type: 'folder',
|
||||
name: '',
|
||||
folder: cp.getFolderWithPath('/test/ff/')
|
||||
}).success
|
||||
).toBe(false);
|
||||
|
||||
// Empty name
|
||||
expect(
|
||||
cp.performRename({
|
||||
type: 'folder',
|
||||
name: 'f4',
|
||||
folder: cp.getFolderWithPath('/test/ff/')
|
||||
}).success
|
||||
).toBe(true);
|
||||
|
||||
expect(p1.getComponentWithName('/test/ff/a')).toBe(undefined);
|
||||
expect(p1.getComponentWithName('/test/f4/a')).not.toBe(undefined);
|
||||
expect(cp.getFolderWithPath('/test/ff/')).toBe(undefined);
|
||||
expect(cp.getFolderWithPath('/test/f4/')).not.toBe(undefined);
|
||||
});
|
||||
|
||||
it('can rename components', function () {
|
||||
// Existing name
|
||||
expect(
|
||||
cp.performRename({
|
||||
type: 'component',
|
||||
name: 'b',
|
||||
folder: cp.getFolderWithPath('/'),
|
||||
component: p1.getComponentWithName('/q')
|
||||
}).success
|
||||
).toBe(false);
|
||||
|
||||
// Empty name
|
||||
expect(
|
||||
cp.performRename({
|
||||
type: 'component',
|
||||
name: '',
|
||||
folder: cp.getFolderWithPath('/'),
|
||||
component: p1.getComponentWithName('/q')
|
||||
}).success
|
||||
).toBe(false);
|
||||
|
||||
// Empty name
|
||||
expect(
|
||||
cp.performRename({
|
||||
type: 'component',
|
||||
name: 'q2',
|
||||
folder: cp.getFolderWithPath('/'),
|
||||
component: p1.getComponentWithName('/q')
|
||||
}).success
|
||||
).toBe(true);
|
||||
|
||||
expect(p1.getComponentWithName('/q')).toBe(undefined);
|
||||
expect(p1.getComponentWithName('/q2')).not.toBe(undefined);
|
||||
});
|
||||
|
||||
it('can detect duplicates', function () {
|
||||
// Cannot move to folder containing a comp with same name
|
||||
expect(
|
||||
cp.getAcceptableDropType({
|
||||
type: 'component',
|
||||
component: p1.getComponentWithName('/a'),
|
||||
targetFolder: cp.getFolderWithPath('/test/f1/')
|
||||
})
|
||||
).toBe(false);
|
||||
|
||||
// Cannot move folder to folder containing a folder with same name
|
||||
expect(
|
||||
cp.getAcceptableDropType({
|
||||
type: 'folder',
|
||||
folder: cp.getFolderWithPath('/dup/f1/'),
|
||||
targetFolder: cp.getFolderWithPath('/test/')
|
||||
})
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('can make correct drops of folders', function () {
|
||||
// Can move a folder into a folder
|
||||
expect(
|
||||
cp.getAcceptableDropType({
|
||||
type: 'folder',
|
||||
folder: cp.getFolderWithPath('/test/f1/'),
|
||||
targetFolder: cp.getFolderWithPath('/test/f2/')
|
||||
})
|
||||
).toBe('folder');
|
||||
|
||||
// Make the move
|
||||
cp.dropOn({
|
||||
type: 'folder',
|
||||
folder: cp.getFolderWithPath('/test/f1/'),
|
||||
targetFolder: cp.getFolderWithPath('/test/f2/')
|
||||
});
|
||||
|
||||
expect(p1.getComponentWithName('/test/f2/f1/a')).not.toBe(undefined);
|
||||
expect(cp.getFolderWithPath('/test/f2/f1/').name).toBe('f1');
|
||||
// expect(cp.getFolderWithPath('/test/f1/')).toBe(undefined);
|
||||
|
||||
// Moving to an ancestor or same folder should not be acceptable
|
||||
expect(
|
||||
cp.getAcceptableDropType({
|
||||
type: 'folder',
|
||||
folder: cp.getFolderWithPath('/test/f2/'),
|
||||
targetFolder: cp.getFolderWithPath('/test/f2/f1/')
|
||||
})
|
||||
).toBe(false);
|
||||
|
||||
expect(
|
||||
cp.getAcceptableDropType({
|
||||
type: 'folder',
|
||||
folder: cp.getFolderWithPath('/test/f2/'),
|
||||
targetFolder: cp.getFolderWithPath('/test/f2/')
|
||||
})
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('can make correct drops of components', function () {
|
||||
// Can move into a new folder
|
||||
expect(
|
||||
cp.getAcceptableDropType({
|
||||
type: 'component',
|
||||
folder: cp.getFolderWithPath('/'),
|
||||
component: p1.getComponentWithName('/b'),
|
||||
targetFolder: cp.getFolderWithPath('/test/f2/')
|
||||
})
|
||||
).toBe('component');
|
||||
|
||||
// Cannot drop to same folder
|
||||
expect(
|
||||
cp.getAcceptableDropType({
|
||||
type: 'component',
|
||||
folder: cp.getFolderWithPath('/'),
|
||||
component: p1.getComponentWithName('/b'),
|
||||
targetFolder: cp.getFolderWithPath('/')
|
||||
})
|
||||
).toBe(false);
|
||||
|
||||
// Make the drop
|
||||
cp.dropOn({
|
||||
type: 'component',
|
||||
folder: cp.getFolderWithPath('/'),
|
||||
component: p1.getComponentWithName('/b'),
|
||||
targetFolder: cp.getFolderWithPath('/test/f2/')
|
||||
});
|
||||
|
||||
expect(p1.getComponentWithName('/test/f2/b')).not.toBe(undefined);
|
||||
expect(cp.getFolderWithPath('/').hasComponentWithName('b')).toBe(false);
|
||||
expect(cp.getFolderWithPath('/test/f2/').hasComponentWithName('b')).toBe(true);
|
||||
expect(p1.getComponentWithName('/b')).toBe(undefined);
|
||||
});
|
||||
|
||||
//TODO: empty folders are removed when moved, but the undo function does not restore them. This is a bug.
|
||||
xit('can drop empty folders', function () {
|
||||
cp.performAdd({
|
||||
type: 'folder',
|
||||
name: 'empty_folder',
|
||||
parentFolder: cp.getFolderWithPath('/')
|
||||
});
|
||||
|
||||
expect(cp.getFolderWithPath('/empty_folder/')).not.toBe(undefined);
|
||||
|
||||
// Drop empty folder
|
||||
cp.dropOn({
|
||||
type: 'folder',
|
||||
folder: cp.getFolderWithPath('/empty_folder/'),
|
||||
targetFolder: cp.getFolderWithPath('/test/')
|
||||
});
|
||||
|
||||
expect(cp.getFolderWithPath('/empty_folder/')).toBe(undefined);
|
||||
//empty folders are removed when moved
|
||||
expect(cp.getFolderWithPath('/test/empty_folder/')).toBe(undefined);
|
||||
|
||||
UndoQueue.instance.undo();
|
||||
|
||||
expect(cp.getFolderWithPath('/empty_folder/')).not.toBe(undefined);
|
||||
// expect(cp.getFolderWithPath('/test/empty_folder/')).toBe(undefined);
|
||||
});
|
||||
|
||||
it('can undo add/delete/rename component and folder', function () {
|
||||
// Add component
|
||||
expect(
|
||||
cp.performAdd({
|
||||
type: 'component',
|
||||
name: 'undome',
|
||||
parentPath: '/'
|
||||
}).success
|
||||
).toBe(true);
|
||||
|
||||
expect(p1.getComponentWithName('/undome')).not.toBe(undefined);
|
||||
expect(UndoQueue.instance.undo().label).toBe('add component');
|
||||
expect(p1.getComponentWithName('/undome')).toBe(undefined);
|
||||
|
||||
// Add folder
|
||||
expect(
|
||||
cp.performAdd({
|
||||
type: 'folder',
|
||||
name: 'undome',
|
||||
parentPath: '/'
|
||||
}).success
|
||||
).toBe(true);
|
||||
|
||||
expect(cp.getFolderWithPath('/undome/')).not.toBe(undefined);
|
||||
expect(UndoQueue.instance.undo().label).toBe('add folder');
|
||||
expect(cp.getFolderWithPath('/undome/')).toBe(undefined);
|
||||
|
||||
// Delete component
|
||||
expect(
|
||||
cp.performDelete({
|
||||
type: 'component',
|
||||
folder: cp.getFolderWithPath('/delete_folder/'),
|
||||
component: p1.getComponentWithName('/delete_folder/delete_comp')
|
||||
}).success
|
||||
).toBe(true);
|
||||
|
||||
expect(p1.getComponentWithName('/delete_folder/delete_comp')).toBe(undefined);
|
||||
expect(UndoQueue.instance.undo().label).toBe('delete component');
|
||||
expect(p1.getComponentWithName('/delete_folder/delete_comp')).not.toBe(undefined);
|
||||
expect(UndoQueue.instance.redo().label).toBe('delete component'); // Folder must be empty for next test to run
|
||||
|
||||
// Delete folder
|
||||
expect(
|
||||
cp.performDelete({
|
||||
type: 'folder',
|
||||
folder: cp.getFolderWithPath('/delete_folder/')
|
||||
}).success
|
||||
).toBe(true);
|
||||
|
||||
expect(cp.getFolderWithPath('/delete_folder/')).toBe(undefined);
|
||||
expect(UndoQueue.instance.undo().label).toBe('delete folder');
|
||||
expect(cp.getFolderWithPath('/delete_folder/')).not.toBe(undefined);
|
||||
|
||||
// Rename component
|
||||
expect(
|
||||
cp.performRename({
|
||||
type: 'component',
|
||||
name: 'newname',
|
||||
folder: cp.getFolderWithPath('/rename_folder/'),
|
||||
component: p1.getComponentWithName('/rename_folder/rename_comp')
|
||||
}).success
|
||||
).toBe(true);
|
||||
|
||||
expect(p1.getComponentWithName('/rename_folder/newname')).not.toBe(undefined);
|
||||
expect(p1.getComponentWithName('/rename_folder/rename_comp')).toBe(undefined);
|
||||
expect(UndoQueue.instance.undo().label).toBe('rename component');
|
||||
expect(p1.getComponentWithName('/rename_folder/newname')).toBe(undefined);
|
||||
expect(p1.getComponentWithName('/rename_folder/rename_comp')).not.toBe(undefined);
|
||||
|
||||
// Rename folder
|
||||
expect(
|
||||
cp.performRename({
|
||||
type: 'folder',
|
||||
name: 'newname',
|
||||
folder: cp.getFolderWithPath('/rename_folder/')
|
||||
}).success
|
||||
).toBe(true);
|
||||
|
||||
expect(p1.getComponentWithName('/newname/rename_comp')).not.toBe(undefined);
|
||||
expect(p1.getComponentWithName('/rename_folder/rename_comp')).toBe(undefined);
|
||||
expect(cp.getFolderWithPath('/rename_folder/')).toBe(undefined);
|
||||
expect(cp.getFolderWithPath('/newname/')).not.toBe(undefined);
|
||||
expect(UndoQueue.instance.undo().label).toBe('rename folder');
|
||||
expect(p1.getComponentWithName('/newname/rename_comp')).toBe(undefined);
|
||||
expect(p1.getComponentWithName('/rename_folder/rename_comp')).not.toBe(undefined);
|
||||
expect(cp.getFolderWithPath('/rename_folder/')).not.toBe(undefined);
|
||||
expect(cp.getFolderWithPath('/newname/')).toBe(undefined);
|
||||
});
|
||||
|
||||
it('can undo drop on folder', function () {
|
||||
// Component on folder
|
||||
cp.dropOn({
|
||||
type: 'component',
|
||||
folder: cp.getFolderWithPath('/'),
|
||||
component: p1.getComponentWithName('/dropundo'),
|
||||
targetFolder: cp.getFolderWithPath('/drop/')
|
||||
});
|
||||
|
||||
expect(p1.getComponentWithName('/drop/dropundo')).not.toBe(undefined);
|
||||
expect(UndoQueue.instance.undo().label).toBe('move component to folder');
|
||||
// expect(p1.getComponentWithName('/drop/dropundo')).toBe(undefined);
|
||||
expect(p1.getComponentWithName('/dropundo')).not.toBe(undefined);
|
||||
expect(cp.getFolderWithPath('/drop/').hasComponentWithName('dropundo')).toBe(false);
|
||||
|
||||
// Folder on folder
|
||||
cp.dropOn({
|
||||
type: 'folder',
|
||||
folder: cp.getFolderWithPath('/drop/'),
|
||||
targetFolder: cp.getFolderWithPath('/drop2/')
|
||||
});
|
||||
expect(cp.getFolderWithPath('/drop2/drop/')).not.toBe(undefined);
|
||||
expect(UndoQueue.instance.undo().label).toBe('move folder to folder');
|
||||
// expect(cp.getFolderWithPath('/drop2/drop/')).toBe(undefined);
|
||||
});
|
||||
|
||||
it('can make correct drops of nested folders and undo', function () {
|
||||
cp.dropOn({
|
||||
type: 'folder',
|
||||
folder: cp.getFolderWithPath('/nested-dropme/'),
|
||||
targetFolder: cp.getFolderWithPath('/nested-target/')
|
||||
});
|
||||
expect(cp.getFolderWithPath('/nested-target/nested-dropme/')).not.toBe(undefined);
|
||||
expect(p1.getComponentWithName('/nested-target/nested-dropme/test/b')).not.toBe(undefined);
|
||||
expect(p1.getComponentWithName('/nested-dropme/test/b')).toBe(undefined);
|
||||
// expect(cp.getFolderWithPath('/nested-dropme/')).toBe(undefined);
|
||||
UndoQueue.instance.undo();
|
||||
// expect(cp.getFolderWithPath('/nested-target/nested-dropme/')).toBe(undefined);
|
||||
expect(p1.getComponentWithName('/nested-target/nested-dropme/test/b')).toBe(undefined);
|
||||
expect(p1.getComponentWithName('/nested-dropme/test/b')).not.toBe(undefined);
|
||||
expect(cp.getFolderWithPath('/nested-dropme/')).not.toBe(undefined);
|
||||
});
|
||||
|
||||
it('can delete folder with content', function () {
|
||||
// Delete folder
|
||||
expect(
|
||||
cp.performDelete({
|
||||
type: 'folder',
|
||||
folder: cp.getFolderWithPath('/delete-me/')
|
||||
}).success
|
||||
).toBe(true);
|
||||
|
||||
expect(cp.getFolderWithPath('/delete-me/')).toBe(undefined);
|
||||
expect(cp.getFolderWithPath('/delete-me/with-content/')).toBe(undefined);
|
||||
expect(p1.getComponentWithName('/delete-me/with-content/a')).toBe(undefined);
|
||||
expect(p1.getComponentWithName('/delete-me/b')).toBe(undefined);
|
||||
UndoQueue.instance.undo();
|
||||
expect(cp.getFolderWithPath('/delete-me/')).not.toBe(undefined);
|
||||
expect(cp.getFolderWithPath('/delete-me/with-content/')).not.toBe(undefined);
|
||||
expect(p1.getComponentWithName('/delete-me/with-content/a')).not.toBe(undefined);
|
||||
expect(p1.getComponentWithName('/delete-me/b')).not.toBe(undefined);
|
||||
});
|
||||
});
|
||||
48
packages/noodl-editor/tests/components/conditionalports.js
Normal file
48
packages/noodl-editor/tests/components/conditionalports.js
Normal file
@@ -0,0 +1,48 @@
|
||||
const { ProjectModel } = require('@noodl-models/projectmodel');
|
||||
|
||||
describe('Conditional ports tests', function () {
|
||||
beforeEach(() => {
|
||||
ProjectModel.instance = ProjectModel.fromJSON({
|
||||
components: [
|
||||
{
|
||||
name: 'comp1',
|
||||
graph: {
|
||||
roots: [
|
||||
{
|
||||
type: 'Anim',
|
||||
id: 'A-1',
|
||||
parameters: {
|
||||
type: 'typeA'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
xit('can get correct ports after load', function () {
|
||||
var ports = ProjectModel.instance.findNodeWithId('A-1').getPorts();
|
||||
expect(ports.length).toBe(2);
|
||||
expect(ports[1]).toEqual({
|
||||
name: 'from',
|
||||
plug: 'input',
|
||||
type: 'number',
|
||||
index: 1
|
||||
});
|
||||
});
|
||||
|
||||
xit('can react to param changes', function () {
|
||||
ProjectModel.instance.findNodeWithId('A-1').setParameter('type', 'typeB');
|
||||
|
||||
var ports = ProjectModel.instance.findNodeWithId('A-1').getPorts();
|
||||
expect(ports.length).toBe(2);
|
||||
expect(ports[1]).toEqual({
|
||||
name: 'to',
|
||||
plug: 'input',
|
||||
type: 'number',
|
||||
index: 1
|
||||
});
|
||||
});
|
||||
});
|
||||
89
packages/noodl-editor/tests/components/dynamicports.js
Normal file
89
packages/noodl-editor/tests/components/dynamicports.js
Normal file
@@ -0,0 +1,89 @@
|
||||
const { ProjectModel } = require('@noodl-models/projectmodel');
|
||||
|
||||
describe('Dynamic ports from viewer tests', function () {
|
||||
// Setup
|
||||
var projectJSON = {
|
||||
components: [
|
||||
{
|
||||
name: 'Root',
|
||||
graph: {
|
||||
connections: [
|
||||
{
|
||||
fromId: 'A',
|
||||
fromProperty: 'dynaA',
|
||||
toId: 'B',
|
||||
toProperty: 'x'
|
||||
},
|
||||
{
|
||||
fromId: 'B',
|
||||
fromProperty: 'y',
|
||||
toId: 'A',
|
||||
toProperty: 'dynaB'
|
||||
}
|
||||
],
|
||||
roots: [
|
||||
{
|
||||
type: 'rectangle',
|
||||
id: 'A',
|
||||
parameters: {
|
||||
dynaA: 10,
|
||||
dynaB: 'hej'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'rectangle',
|
||||
id: 'B'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
var project = ProjectModel.fromJSON(projectJSON);
|
||||
|
||||
it('can rename inputs', function () {
|
||||
var n = project.findNodeWithId('A');
|
||||
|
||||
n.setDynamicPorts([
|
||||
{
|
||||
name: 'dynaA',
|
||||
type: 'number'
|
||||
},
|
||||
{
|
||||
name: 'dynaB',
|
||||
type: 'string'
|
||||
}
|
||||
]);
|
||||
|
||||
// Now renamed
|
||||
n.setDynamicPorts(
|
||||
[
|
||||
{
|
||||
name: 'bosseA',
|
||||
type: 'number'
|
||||
},
|
||||
{
|
||||
name: 'bosseB',
|
||||
type: 'string'
|
||||
}
|
||||
],
|
||||
{
|
||||
renamed: {
|
||||
plug: 'input',
|
||||
patterns: ['{{*}}A', '{{*}}B'],
|
||||
before: 'dyna',
|
||||
after: 'bosse'
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
expect(n.parameters['bosseA']).toBe(10);
|
||||
expect(n.parameters['bosseB']).toBe('hej');
|
||||
expect(n.parameters['dynaA']).toBe(undefined);
|
||||
expect(n.parameters['dynaB']).toBe(undefined);
|
||||
|
||||
expect(project.getComponentWithName('Root').graph.connections[0].fromProperty).toBe('bosseA');
|
||||
expect(project.getComponentWithName('Root').graph.connections[1].toProperty).toBe('bosseB');
|
||||
});
|
||||
});
|
||||
82
packages/noodl-editor/tests/components/expandedports.js
Normal file
82
packages/noodl-editor/tests/components/expandedports.js
Normal file
@@ -0,0 +1,82 @@
|
||||
const { ProjectModel } = require('@noodl-models/projectmodel');
|
||||
|
||||
describe('Expanded ports tests', function () {
|
||||
beforeEach(() => {
|
||||
ProjectModel.instance = ProjectModel.fromJSON(getProject());
|
||||
});
|
||||
|
||||
xit('can get correct ports after load', function () {
|
||||
var ports = ProjectModel.instance.findNodeWithId('EP-1').getPorts();
|
||||
expect(ports.length).toBe(3);
|
||||
expect(ports[1]).toEqual({
|
||||
name: 'Affe.A',
|
||||
index: 101,
|
||||
plug: 'input',
|
||||
type: 'number'
|
||||
});
|
||||
expect(ports[2]).toEqual({
|
||||
name: 'Affe.B',
|
||||
index: 102,
|
||||
plug: 'input',
|
||||
type: 'number'
|
||||
});
|
||||
});
|
||||
|
||||
xit('can react to param changes', function () {
|
||||
ProjectModel.instance.findNodeWithId('EP-1').setParameter('Affe.A', 'something');
|
||||
|
||||
var ports = ProjectModel.instance.findNodeWithId('EP-1').getPorts();
|
||||
expect(ports.length).toBe(2);
|
||||
expect(ports[1]).toEqual({
|
||||
name: 'Affe.A',
|
||||
index: 101,
|
||||
plug: 'input',
|
||||
type: 'number'
|
||||
});
|
||||
});
|
||||
|
||||
xit('can rename ports and parameters are copied', function () {
|
||||
ProjectModel.instance.findNodeWithId('EP-2').setParameter('Bosse.A', 'grr');
|
||||
|
||||
ProjectModel.instance.findNodeWithId('EP-2').renamePortWithName('Bosse', 'Oscar');
|
||||
|
||||
expect(ProjectModel.instance.findNodeWithId('EP-2').parameters['Oscar.A']).toBe('grr');
|
||||
expect(ProjectModel.instance.findNodeWithId('EP-2').parameters['Bosse.A']).toBe(undefined);
|
||||
});
|
||||
|
||||
function getProject() {
|
||||
return {
|
||||
components: [
|
||||
{
|
||||
name: 'comp1',
|
||||
graph: {
|
||||
roots: [
|
||||
{
|
||||
type: 'ExpandPorts',
|
||||
id: 'EP-1',
|
||||
ports: [
|
||||
{
|
||||
type: 'number',
|
||||
name: 'Affe',
|
||||
plug: 'input'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'ExpandPorts',
|
||||
id: 'EP-2',
|
||||
ports: [
|
||||
{
|
||||
type: 'number',
|
||||
name: 'Bosse',
|
||||
plug: 'input'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
});
|
||||
9
packages/noodl-editor/tests/components/index.ts
Normal file
9
packages/noodl-editor/tests/components/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export * from './componentconnections';
|
||||
export * from './componentinstances';
|
||||
export * from './componentports';
|
||||
export * from './componentspanel';
|
||||
export * from './conditionalports';
|
||||
export * from './dynamicports';
|
||||
export * from './expandedports';
|
||||
export * from './numberedports';
|
||||
export * from './portchannels';
|
||||
100
packages/noodl-editor/tests/components/numberedports.js
Normal file
100
packages/noodl-editor/tests/components/numberedports.js
Normal file
@@ -0,0 +1,100 @@
|
||||
const NodeGraphModel = require('@noodl-models/nodegraphmodel').NodeGraphModel;
|
||||
const NodeGraphNode = require('@noodl-models/nodegraphmodel').NodeGraphNode;
|
||||
|
||||
describe('Numbered ports tests', function () {
|
||||
var g1, e1, e2, e3;
|
||||
|
||||
beforeEach(() => {
|
||||
g1 = new NodeGraphModel();
|
||||
e1 = NodeGraphNode.fromJSON({
|
||||
id: 'A',
|
||||
type: 'nodeWithNumberedPorts'
|
||||
});
|
||||
|
||||
g1.addRoot(e1);
|
||||
});
|
||||
|
||||
xit('can add new ports dynamically', function () {
|
||||
var ports = e1.getPorts();
|
||||
expect(ports.length).toBe(1);
|
||||
expect(ports[0].name).toBe('my number 0');
|
||||
expect(ports[0].displayName).toBe('My number 0');
|
||||
expect(ports[0].type).toBe('number');
|
||||
expect(ports[0].group).toBe('My group');
|
||||
|
||||
e1.setParameter('my number 0', 10);
|
||||
var ports = e1.getPorts();
|
||||
expect(ports.length).toBe(2);
|
||||
expect(ports[1].name).toBe('my number 1');
|
||||
expect(ports[1].displayName).toBe('My number 1');
|
||||
});
|
||||
|
||||
xit('can find highest parameter', function () {
|
||||
// The number of parameters should be defined by the highest parameter that is
|
||||
// not undefined
|
||||
e1.setParameter('my number 1', 20);
|
||||
var ports = e1.getPorts();
|
||||
expect(ports.length).toBe(3);
|
||||
|
||||
// Setting to undefined should still be there because 'my number 1' is 20
|
||||
e1.setParameter('my number 0', undefined);
|
||||
var ports = e1.getPorts();
|
||||
expect(ports.length).toBe(3);
|
||||
});
|
||||
|
||||
xit('can detect connections', function () {
|
||||
e2 = NodeGraphNode.fromJSON({
|
||||
id: 'B',
|
||||
type: 'nodeWithNumberedPorts'
|
||||
});
|
||||
g1.addRoot(e2);
|
||||
|
||||
g1.addConnection({
|
||||
fromId: 'A',
|
||||
fromProperty: 'my number 2',
|
||||
toId: 'B',
|
||||
toProperty: 'my number 1'
|
||||
});
|
||||
|
||||
// A should now have 4 ports (connected to my number 2)
|
||||
var ports = e1.getPorts();
|
||||
expect(ports.length).toBe(4);
|
||||
|
||||
// B should have 3 ports (connected to my number 1)
|
||||
var ports = e2.getPorts();
|
||||
expect(ports.length).toBe(3);
|
||||
});
|
||||
|
||||
xit('can generate selectors', function () {
|
||||
e3 = NodeGraphNode.fromJSON({
|
||||
id: 'C',
|
||||
type: 'nodeWithNumberedPortsAndSelectors'
|
||||
});
|
||||
g1.addRoot(e3);
|
||||
|
||||
var ports = e3.getPorts();
|
||||
expect(ports.length).toBe(2);
|
||||
expect(ports[1].name).toBe('startAt');
|
||||
expect(ports[1].group).toBe('My selectors');
|
||||
expect(ports[1].type.enums.length).toBe(0);
|
||||
expect(ports[1].type.name).toBe('enum');
|
||||
|
||||
// Add a dynamic port, should be listed in the selector
|
||||
e3.setParameter('my number 0', 10);
|
||||
var ports = e3.getPorts();
|
||||
expect(ports.length).toBe(3);
|
||||
expect(ports[2].type.enums.length).toBe(1);
|
||||
expect(ports[2].type.enums[0].value).toBe('my number 0');
|
||||
expect(ports[2].type.enums[0].label).toBe('My number 0');
|
||||
|
||||
// More
|
||||
e3.setParameter('my number 1', 20);
|
||||
var ports = e3.getPorts();
|
||||
expect(ports.length).toBe(4);
|
||||
expect(ports[3].type.enums.length).toBe(2);
|
||||
expect(ports[3].type.enums[0].value).toBe('my number 0');
|
||||
expect(ports[3].type.enums[0].label).toBe('My number 0');
|
||||
expect(ports[3].type.enums[1].value).toBe('my number 1');
|
||||
expect(ports[3].type.enums[1].label).toBe('My number 1');
|
||||
});
|
||||
});
|
||||
191
packages/noodl-editor/tests/components/portchannels.js
Normal file
191
packages/noodl-editor/tests/components/portchannels.js
Normal file
@@ -0,0 +1,191 @@
|
||||
const { ProjectModel } = require('@noodl-models/projectmodel');
|
||||
|
||||
describe('Port channels tests', function () {
|
||||
beforeEach(() => {
|
||||
ProjectModel.instance = ProjectModel.fromJSON(getProject());
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
//unregister the project model so subsequent tests arent affected
|
||||
//(the channels can affect other tests)
|
||||
ProjectModel.instance = undefined;
|
||||
});
|
||||
|
||||
xit('can collect channel names', function () {
|
||||
var ports = ProjectModel.instance.findNodeWithId('ER-1').getPorts();
|
||||
expect(ports.length).toBe(1);
|
||||
expect(ports[0].name).toBe('channel');
|
||||
expect(ports[0].type).toEqual({
|
||||
name: 'enum',
|
||||
enums: ['channelA', 'channelB'],
|
||||
allowEditOnly: true
|
||||
});
|
||||
});
|
||||
|
||||
xit('can collect ports when channel name is set', function () {
|
||||
ProjectModel.instance.findNodeWithId('ER-1').setParameter('channel', 'channelA');
|
||||
|
||||
var ports = ProjectModel.instance.findNodeWithId('ER-1').getPorts();
|
||||
expect(ports.length).toBe(4);
|
||||
expect(ports[1]).toEqual({
|
||||
name: 'p1',
|
||||
type: '*',
|
||||
plug: 'output',
|
||||
index: 1
|
||||
});
|
||||
expect(ports[2]).toEqual({
|
||||
name: 'p2',
|
||||
type: '*',
|
||||
plug: 'output',
|
||||
index: 2
|
||||
});
|
||||
expect(ports[3]).toEqual({
|
||||
name: 'p3',
|
||||
type: '*',
|
||||
plug: 'output',
|
||||
index: 3
|
||||
});
|
||||
});
|
||||
|
||||
xit('can react to port name change', function () {
|
||||
ProjectModel.instance.findNodeWithId('ER-1').setParameter('channel', 'channelA');
|
||||
ProjectModel.instance.findNodeWithId('ES-1').renamePortWithName('p2', 'p2b');
|
||||
|
||||
var ports = ProjectModel.instance.findNodeWithId('ER-1').getPorts();
|
||||
expect(ports.length).toBe(5);
|
||||
expect(ports[1]).toEqual({
|
||||
name: 'p1',
|
||||
type: '*',
|
||||
plug: 'output',
|
||||
index: 1
|
||||
});
|
||||
expect(ports[2]).toEqual({
|
||||
name: 'p2',
|
||||
type: '*',
|
||||
plug: 'output',
|
||||
index: 2
|
||||
});
|
||||
expect(ports[3]).toEqual({
|
||||
name: 'p2b',
|
||||
type: '*',
|
||||
plug: 'output',
|
||||
index: 3
|
||||
});
|
||||
expect(ports[4]).toEqual({
|
||||
name: 'p3',
|
||||
type: '*',
|
||||
plug: 'output',
|
||||
index: 4
|
||||
});
|
||||
});
|
||||
|
||||
xit('can react to channel changes', function () {
|
||||
ProjectModel.instance.findNodeWithId('ER-1').setParameter('channel', 'channelA');
|
||||
ProjectModel.instance.findNodeWithId('ES-1').setParameter('channel', 'channelC');
|
||||
|
||||
var ports = ProjectModel.instance.findNodeWithId('ER-1').getPorts();
|
||||
expect(ports.length).toBe(3);
|
||||
expect(ports[0].name).toBe('channel');
|
||||
expect(ports[0].type).toEqual({
|
||||
name: 'enum',
|
||||
enums: ['channelA', 'channelB', 'channelC'],
|
||||
allowEditOnly: true
|
||||
});
|
||||
expect(ports[1]).toEqual({
|
||||
name: 'p2',
|
||||
type: '*',
|
||||
plug: 'output',
|
||||
index: 1
|
||||
});
|
||||
expect(ports[2]).toEqual({
|
||||
name: 'p3',
|
||||
type: '*',
|
||||
plug: 'output',
|
||||
index: 2
|
||||
});
|
||||
});
|
||||
|
||||
function getProject() {
|
||||
return {
|
||||
components: [
|
||||
{
|
||||
name: 'comp1',
|
||||
graph: {
|
||||
roots: [
|
||||
{
|
||||
type: 'Event Sender',
|
||||
id: 'ES-1',
|
||||
parameters: {
|
||||
channel: 'channelA'
|
||||
},
|
||||
ports: [
|
||||
{
|
||||
name: 'p1',
|
||||
type: '*',
|
||||
plug: 'input'
|
||||
},
|
||||
{
|
||||
name: 'p2',
|
||||
type: '*',
|
||||
plug: 'input'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'Event Receiver',
|
||||
id: 'ER-1'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
name: 'comp2',
|
||||
graph: {
|
||||
roots: [
|
||||
{
|
||||
type: 'Event Sender',
|
||||
id: 'ES-2',
|
||||
parameters: {
|
||||
channel: 'channelA'
|
||||
},
|
||||
ports: [
|
||||
{
|
||||
name: 'p2',
|
||||
type: '*',
|
||||
plug: 'input'
|
||||
},
|
||||
{
|
||||
name: 'p3',
|
||||
type: '*',
|
||||
plug: 'input'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
type: 'Event Sender',
|
||||
id: 'ES-3',
|
||||
parameters: {
|
||||
channel: 'channelB'
|
||||
},
|
||||
ports: [
|
||||
{
|
||||
name: 'p3',
|
||||
type: '*',
|
||||
plug: 'input'
|
||||
},
|
||||
{
|
||||
name: 'p4',
|
||||
type: '*',
|
||||
plug: 'input'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user