noflo
Version:
Flow-Based Programming environment for JavaScript
662 lines (658 loc) • 26.9 kB
JavaScript
var chai, graph;
if (typeof process !== 'undefined' && process.execPath && process.execPath.match(/node|iojs/)) {
if (!chai) {
chai = require('chai');
}
graph = require('../src/lib/Graph.coffee');
} else {
graph = require('noflo/src/lib/Graph.js');
}
describe('Unnamed graph instance', function() {
return it('should have an empty name', function() {
var g;
g = new graph.Graph;
return chai.expect(g.name).to.equal('');
});
});
describe('Graph', function() {
describe('with new instance', function() {
var g;
g = null;
it('should get a name from constructor', function() {
g = new graph.Graph('Foo bar');
return chai.expect(g.name).to.equal('Foo bar');
});
it('should have no nodes initially', function() {
return chai.expect(g.nodes.length).to.equal(0);
});
it('should have no edges initially', function() {
return chai.expect(g.edges.length).to.equal(0);
});
it('should have no initializers initially', function() {
return chai.expect(g.initializers.length).to.equal(0);
});
it('should have no exports initially', function() {
chai.expect(g.exports.length).to.equal(0);
chai.expect(g.inports).to.be.empty;
return chai.expect(g.outports).to.be.empty;
});
describe('New node', function() {
var n;
n = null;
it('should emit an event', function(done) {
g.once('addNode', function(node) {
chai.expect(node.id).to.equal('Foo');
chai.expect(node.component).to.equal('Bar');
n = node;
return done();
});
return g.addNode('Foo', 'Bar');
});
it('should be in graph\'s list of nodes', function() {
chai.expect(g.nodes.length).to.equal(1);
return chai.expect(g.nodes.indexOf(n)).to.equal(0);
});
it('should be accessible via the getter', function() {
var node;
node = g.getNode('Foo');
chai.expect(node.id).to.equal('Foo');
return chai.expect(node).to.equal(n);
});
it('should have empty metadata', function() {
var node;
node = g.getNode('Foo');
chai.expect(JSON.stringify(node.metadata)).to.equal('{}');
return chai.expect(node.display).to.equal(void 0);
});
it('should be available in the JSON export', function() {
var json;
json = g.toJSON();
chai.expect(typeof json.processes.Foo).to.equal('object');
chai.expect(json.processes.Foo.component).to.equal('Bar');
return chai.expect(json.processes.Foo.display).to.not.exist;
});
it('removing should emit an event', function(done) {
g.once('removeNode', function(node) {
chai.expect(node.id).to.equal('Foo');
chai.expect(node).to.equal(n);
return done();
});
return g.removeNode('Foo');
});
return it('should not be available after removal', function() {
var node;
node = g.getNode('Foo');
chai.expect(node).to.not.exist;
chai.expect(g.nodes.length).to.equal(0);
return chai.expect(g.nodes.indexOf(n)).to.equal(-1);
});
});
describe('New edge', function() {
it('should emit an event', function(done) {
g.addNode('Foo', 'foo');
g.addNode('Bar', 'bar');
g.once('addEdge', function(edge) {
chai.expect(edge.from.node).to.equal('Foo');
chai.expect(edge.to.port).to.equal('in');
return done();
});
return g.addEdge('Foo', 'out', 'Bar', 'in');
});
it('should add an edge', function() {
g.addEdge('Foo', 'out', 'Bar', 'in2');
return chai.expect(g.edges.length).equal(2);
});
return it('should refuse to add a duplicate edge', function() {
var edge;
edge = g.edges[0];
g.addEdge(edge.from.node, edge.from.port, edge.to.node, edge.to.port);
return chai.expect(g.edges.length).equal(2);
});
});
return describe('New edge with index', function() {
it('should emit an event', function(done) {
g.once('addEdge', function(edge) {
chai.expect(edge.from.node).to.equal('Foo');
chai.expect(edge.to.port).to.equal('in');
chai.expect(edge.to.index).to.equal(1);
chai.expect(edge.from.index).to.be.an('undefined');
chai.expect(g.edges.length).equal(3);
return done();
});
return g.addEdgeIndex('Foo', 'out', null, 'Bar', 'in', 1);
});
return it('should add an edge', function() {
g.addEdgeIndex('Foo', 'out', 2, 'Bar', 'in2');
return chai.expect(g.edges.length).equal(4);
});
});
});
describe('loaded from JSON', function() {
var g, json, jsonString;
jsonString = "{\n \"properties\": {\n \"name\": \"Example\",\n \"foo\": \"Baz\",\n \"bar\": \"Foo\"\n },\n \"inports\": {\n \"in\": {\n \"process\": \"Foo\",\n \"port\": \"in\",\n \"metadata\": {\n \"x\": 5,\n \"y\": 100\n }\n }\n },\n \"outports\": {\n \"out\": {\n \"process\": \"Bar\",\n \"port\": \"out\",\n \"metadata\": {\n \"x\": 500,\n \"y\": 505\n }\n }\n },\n \"groups\": [\n {\n \"name\": \"first\",\n \"nodes\": [\n \"Foo\"\n ],\n \"metadata\": {\n \"label\": \"Main\"\n }\n },\n {\n \"name\": \"second\",\n \"nodes\": [\n \"Foo2\",\n \"Bar2\"\n ]\n }\n ],\n \"processes\": {\n \"Foo\": {\n \"component\": \"Bar\",\n \"metadata\": {\n \"display\": {\n \"x\": 100,\n \"y\": 200\n },\n \"routes\": [\n \"one\",\n \"two\"\n ],\n \"hello\": \"World\"\n }\n },\n \"Bar\": {\n \"component\": \"Baz\",\n \"metadata\": {}\n },\n \"Foo2\": {\n \"component\": \"foo\",\n \"metadata\": {}\n },\n \"Bar2\": {\n \"component\": \"bar\",\n \"metadata\": {}\n }\n },\n \"connections\": [\n {\n \"src\": {\n \"process\": \"Foo\",\n \"port\": \"out\"\n },\n \"tgt\": {\n \"process\": \"Bar\",\n \"port\": \"in\"\n },\n \"metadata\": {\n \"route\": \"foo\",\n \"hello\": \"World\"\n }\n },\n {\n \"src\": {\n \"process\": \"Foo\",\n \"port\": \"out2\"\n },\n \"tgt\": {\n \"process\": \"Bar\",\n \"port\": \"in2\",\n \"index\": 2\n },\n \"metadata\": {\n \"route\": \"foo\",\n \"hello\": \"World\"\n }\n },\n {\n \"data\": \"Hello, world!\",\n \"tgt\": {\n \"process\": \"Foo\",\n \"port\": \"in\"\n }\n },\n {\n \"data\": \"Hello, world, 2!\",\n \"tgt\": {\n \"process\": \"Foo\",\n \"port\": \"in2\"\n }\n },\n {\n \"data\": \"Cheers, world!\",\n \"tgt\": {\n \"process\": \"Foo\",\n \"port\": \"arr\",\n \"index\": 0\n }\n },\n {\n \"data\": \"Cheers, world, 2!\",\n \"tgt\": {\n \"process\": \"Foo\",\n \"port\": \"arr\",\n \"index\": 1\n }\n }\n ]\n}";
json = JSON.parse(jsonString);
g = null;
it('should produce a Graph', function(done) {
return graph.loadJSON(json, function(err, instance) {
if (err) {
return done(err);
}
g = instance;
chai.expect(g).to.be.an('object');
return done();
});
});
it('should have a name', function() {
return chai.expect(g.name).to.equal('Example');
});
it('should have graph metadata intact', function() {
return chai.expect(g.properties).to.eql({
foo: 'Baz',
bar: 'Foo'
});
});
it('should produce same JSON when serialized', function() {
return chai.expect(JSON.stringify(g.toJSON())).to.equal(JSON.stringify(json));
});
it('should allow modifying graph metadata', function(done) {
g.once('changeProperties', function(properties) {
chai.expect(properties).to.equal(g.properties);
chai.expect(g.properties).to.eql({
foo: 'Baz',
bar: 'Bar',
hello: 'World'
});
return done();
});
return g.setProperties({
hello: 'World',
bar: 'Bar'
});
});
it('should contain four nodes', function() {
return chai.expect(g.nodes.length).to.equal(4);
});
it('the first Node should have its metadata intact', function() {
var node;
node = g.getNode('Foo');
chai.expect(node.metadata).to.be.an('object');
chai.expect(node.metadata.display).to.be.an('object');
chai.expect(node.metadata.display.x).to.equal(100);
chai.expect(node.metadata.display.y).to.equal(200);
chai.expect(node.metadata.routes).to.be.an('array');
chai.expect(node.metadata.routes).to.contain('one');
return chai.expect(node.metadata.routes).to.contain('two');
});
it('should allow modifying node metadata', function(done) {
g.once('changeNode', function(node) {
chai.expect(node.id).to.equal('Foo');
chai.expect(node.metadata.routes).to.be.an('array');
chai.expect(node.metadata.routes).to.contain('one');
chai.expect(node.metadata.routes).to.contain('two');
chai.expect(node.metadata.hello).to.equal('World');
return done();
});
return g.setNodeMetadata('Foo', {
hello: 'World'
});
});
it('should contain two connections', function() {
return chai.expect(g.edges.length).to.equal(2);
});
it('the first Edge should have its metadata intact', function() {
var edge;
edge = g.edges[0];
chai.expect(edge.metadata).to.be.an('object');
return chai.expect(edge.metadata.route).equal('foo');
});
it('should allow modifying edge metadata', function(done) {
var e;
e = g.edges[0];
g.once('changeEdge', function(edge) {
chai.expect(edge).to.equal(e);
chai.expect(edge.metadata.route).to.equal('foo');
chai.expect(edge.metadata.hello).to.equal('World');
return done();
});
return g.setEdgeMetadata(e.from.node, e.from.port, e.to.node, e.to.port, {
hello: 'World'
});
});
it('should contain four IIPs', function() {
return chai.expect(g.initializers.length).to.equal(4);
});
it('should contain one published inport', function() {
return chai.expect(g.inports).to.not.be.empty;
});
it('should contain one published outport', function() {
return chai.expect(g.outports).to.not.be.empty;
});
it('should keep the output export metadata intact', function() {
var exp;
exp = g.outports.out;
chai.expect(exp.metadata.x).to.equal(500);
return chai.expect(exp.metadata.y).to.equal(505);
});
it('should contain two groups', function() {
return chai.expect(g.groups.length).to.equal(2);
});
it('should allow modifying group metadata', function(done) {
var group;
group = g.groups[0];
g.once('changeGroup', function(grp) {
chai.expect(grp).to.equal(group);
chai.expect(grp.metadata.label).to.equal('Main');
chai.expect(grp.metadata.foo).to.equal('Bar');
chai.expect(g.groups[1].metadata).to.be.empty;
return done();
});
return g.setGroupMetadata('first', {
foo: 'Bar'
});
});
it('should allow renaming groups', function(done) {
var group;
group = g.groups[0];
g.once('renameGroup', function(oldName, newName) {
chai.expect(oldName).to.equal('first');
chai.expect(newName).to.equal('renamed');
chai.expect(group.name).to.equal(newName);
return done();
});
return g.renameGroup('first', 'renamed');
});
describe('renaming a node', function() {
it('should emit an event', function(done) {
g.once('renameNode', function(oldId, newId) {
chai.expect(oldId).to.equal('Foo');
chai.expect(newId).to.equal('Baz');
return done();
});
return g.renameNode('Foo', 'Baz');
});
it('should be available with the new name', function() {
return chai.expect(g.getNode('Baz')).to.be.an('object');
});
it('shouldn\'t be available with the old name', function() {
return chai.expect(g.getNode('Foo')).to.be["null"];
});
it('should have the edge still going from it', function() {
var connection, edge, _i, _len, _ref;
connection = null;
_ref = g.edges;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
edge = _ref[_i];
if (edge.from.node === 'Baz') {
connection = edge;
}
}
return chai.expect(connection).to.be.an('object');
});
it('should still be exported', function() {
return chai.expect(g.inports["in"].process).to.equal('Baz');
});
it('should still be grouped', function() {
var group, groups, _i, _len, _ref;
groups = 0;
_ref = g.groups;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
group = _ref[_i];
if (group.nodes.indexOf('Baz') !== -1) {
groups++;
}
}
return chai.expect(groups).to.equal(1);
});
it('shouldn\'t be have edges with the old name', function() {
var connection, edge, _i, _len, _ref;
connection = null;
_ref = g.edges;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
edge = _ref[_i];
if (edge.from.node === 'Foo') {
connection = edge;
}
if (edge.to.node === 'Foo') {
connection = edge;
}
}
return chai.expect(connection).to.be.a('null');
});
it('should have the IIP still going to it', function() {
var edge, iip, _i, _len, _ref;
iip = null;
_ref = g.initializers;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
edge = _ref[_i];
if (edge.to.node === 'Baz') {
iip = edge;
}
}
return chai.expect(iip).to.be.an('object');
});
it('shouldn\'t have IIPs going to the old name', function() {
var edge, iip, _i, _len, _ref;
iip = null;
_ref = g.initializers;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
edge = _ref[_i];
if (edge.to.node === 'Foo') {
iip = edge;
}
}
return chai.expect(iip).to.be.a('null');
});
it('shouldn\'t be have export going to the old name', function() {
var exported, exportedNode, exportedPort, exports, _i, _len, _ref, _ref1;
exports = 0;
_ref = g.exports;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
exported = _ref[_i];
_ref1 = exported["private"].split('.'), exportedNode = _ref1[0], exportedPort = _ref1[1];
if (exportedNode === 'foo') {
exports++;
}
}
return chai.expect(exports).to.equal(0);
});
return it('shouldn\'t be grouped with the old name', function() {
var group, groups, _i, _len, _ref;
groups = 0;
_ref = g.groups;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
group = _ref[_i];
if (group.nodes.indexOf('Foo') !== -1) {
groups++;
}
}
return chai.expect(groups).to.equal(0);
});
});
describe('renaming an inport', function() {
return it('should emit an event', function(done) {
g.once('renameInport', function(oldName, newName) {
chai.expect(oldName).to.equal('in');
chai.expect(newName).to.equal('opt');
chai.expect(g.inports["in"]).to.be.an('undefined');
chai.expect(g.inports.opt).to.be.an('object');
chai.expect(g.inports.opt.process).to.equal('Baz');
chai.expect(g.inports.opt.port).to.equal('in');
return done();
});
return g.renameInport('in', 'opt');
});
});
describe('renaming an outport', function() {
return it('should emit an event', function(done) {
g.once('renameOutport', function(oldName, newName) {
chai.expect(oldName).to.equal('out');
chai.expect(newName).to.equal('foo');
chai.expect(g.outports.out).to.be.an('undefined');
chai.expect(g.outports.foo).to.be.an('object');
chai.expect(g.outports.foo.process).to.equal('Bar');
chai.expect(g.outports.foo.port).to.equal('out');
return done();
});
return g.renameOutport('out', 'foo');
});
});
return describe('removing a node', function() {
it('should emit an event', function(done) {
g.once('removeNode', function(node) {
chai.expect(node.id).to.equal('Baz');
return done();
});
return g.removeNode('Baz');
});
it('shouldn\'t have edges left behind', function() {
var connections, edge, _i, _len, _ref;
connections = 0;
_ref = g.edges;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
edge = _ref[_i];
if (edge.from.node === 'Baz') {
connections++;
}
if (edge.to.node === 'Baz') {
connections++;
}
}
return chai.expect(connections).to.equal(0);
});
it('shouldn\'t have IIPs left behind', function() {
var connections, edge, _i, _len, _ref;
connections = 0;
_ref = g.initializers;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
edge = _ref[_i];
if (edge.to.node === 'Baz') {
connections++;
}
}
return chai.expect(connections).to.equal(0);
});
it('shouldn\'t have exports left behind', function() {
var exported, exportedNode, exportedPort, exports, _i, _len, _ref, _ref1;
exports = 0;
_ref = g.exports;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
exported = _ref[_i];
_ref1 = exported["private"].split('.'), exportedNode = _ref1[0], exportedPort = _ref1[1];
if (exportedNode === 'baz') {
exports++;
}
}
return chai.expect(exports).to.equal(0);
});
it('shouldn\'t be grouped', function() {
var group, groups, _i, _len, _ref;
groups = 0;
_ref = g.groups;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
group = _ref[_i];
if (group.nodes.indexOf('Baz') !== -1) {
groups++;
}
}
return chai.expect(groups).to.equal(0);
});
return it('shouldn\'t affect other groups', function() {
var otherGroup;
otherGroup = g.groups[1];
return chai.expect(otherGroup.nodes.length).to.equal(2);
});
});
});
describe('with multiple connected ArrayPorts', function() {
var g;
g = new graph.Graph;
g.addNode('Split1', 'Split');
g.addNode('Split2', 'Split');
g.addNode('Merge1', 'Merge');
g.addNode('Merge2', 'Merge');
g.addEdge('Split1', 'out', 'Merge1', 'in');
g.addEdge('Split1', 'out', 'Merge2', 'in');
g.addEdge('Split2', 'out', 'Merge1', 'in');
g.addEdge('Split2', 'out', 'Merge2', 'in');
it('should contain four nodes', function() {
return chai.expect(g.nodes.length).to.equal(4);
});
it('should contain four edges', function() {
return chai.expect(g.edges.length).to.equal(4);
});
it('should allow a specific edge to be removed', function() {
g.removeEdge('Split1', 'out', 'Merge2', 'in');
return chai.expect(g.edges.length).to.equal(3);
});
it('shouldn\'t contain the removed connection from Split1', function() {
var connection, edge, _i, _len, _ref;
connection = null;
_ref = g.edges;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
edge = _ref[_i];
if (edge.from.node === 'Split1' && edge.to.node === 'Merge2') {
connection = edge;
}
}
return chai.expect(connection).to.be["null"];
});
return it('should still contain the other connection from Split1', function() {
var connection, edge, _i, _len, _ref;
connection = null;
_ref = g.edges;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
edge = _ref[_i];
if (edge.from.node === 'Split1' && edge.to.node === 'Merge1') {
connection = edge;
}
}
return chai.expect(connection).to.be.an('object');
});
});
describe('with an Initial Information Packet', function() {
var g;
g = new graph.Graph;
g.addNode('Split', 'Split');
g.addInitial('Foo', 'Split', 'in');
it('should contain one node', function() {
return chai.expect(g.nodes.length).to.equal(1);
});
it('should contain no edges', function() {
return chai.expect(g.edges.length).to.equal(0);
});
it('should contain one IIP', function() {
return chai.expect(g.initializers.length).to.equal(1);
});
return describe('on removing that IIP', function() {
it('should emit a removeInitial event', function(done) {
g.once('removeInitial', function(iip) {
chai.expect(iip.from.data).to.equal('Foo');
chai.expect(iip.to.node).to.equal('Split');
chai.expect(iip.to.port).to.equal('in');
return done();
});
return g.removeInitial('Split', 'in');
});
return it('should contain no IIPs', function() {
return chai.expect(g.initializers.length).to.equal(0);
});
});
});
describe('with an Inport Initial Information Packet', function() {
var g;
g = new graph.Graph;
g.addNode('Split', 'Split');
g.addInport('testinport', 'Split', 'in');
g.addGraphInitial('Foo', 'testinport');
it('should contain one node', function() {
return chai.expect(g.nodes.length).to.equal(1);
});
it('should contain no edges', function() {
return chai.expect(g.edges.length).to.equal(0);
});
it('should contain one IIP for the correct node', function() {
chai.expect(g.initializers.length).to.equal(1);
chai.expect(g.initializers[0].from.data).to.equal('Foo');
chai.expect(g.initializers[0].to.node).to.equal('Split');
return chai.expect(g.initializers[0].to.port).to.equal('in');
});
describe('on removing that IIP', function() {
it('should emit a removeInitial event', function(done) {
g.once('removeInitial', function(iip) {
chai.expect(iip.from.data).to.equal('Foo');
chai.expect(iip.to.node).to.equal('Split');
chai.expect(iip.to.port).to.equal('in');
return done();
});
return g.removeGraphInitial('testinport');
});
return it('should contain no IIPs', function() {
return chai.expect(g.initializers.length).to.equal(0);
});
});
return describe('on adding IIP for a non-existent inport', function() {
g.addGraphInitial('Bar', 'nonexistent');
return it('should not add any IIP', function() {
return chai.expect(g.initializers.length).to.equal(0);
});
});
});
describe('with an indexed Inport Initial Information Packet', function() {
var g;
g = new graph.Graph;
g.addNode('Split', 'Split');
g.addInport('testinport', 'Split', 'in');
g.addGraphInitialIndex('Foo', 'testinport', 1);
it('should contain one node', function() {
return chai.expect(g.nodes.length).to.equal(1);
});
it('should contain no edges', function() {
return chai.expect(g.edges.length).to.equal(0);
});
it('should contain one IIP for the correct node', function() {
chai.expect(g.initializers.length).to.equal(1);
chai.expect(g.initializers[0].from.data).to.equal('Foo');
chai.expect(g.initializers[0].to.node).to.equal('Split');
chai.expect(g.initializers[0].to.port).to.equal('in');
return chai.expect(g.initializers[0].to.index).to.equal(1);
});
describe('on removing that IIP', function() {
it('should emit a removeInitial event', function(done) {
g.once('removeInitial', function(iip) {
chai.expect(iip.from.data).to.equal('Foo');
chai.expect(iip.to.node).to.equal('Split');
chai.expect(iip.to.port).to.equal('in');
return done();
});
return g.removeGraphInitial('testinport');
});
return it('should contain no IIPs', function() {
return chai.expect(g.initializers.length).to.equal(0);
});
});
return describe('on adding IIP for a non-existent inport', function() {
g.addGraphInitialIndex('Bar', 'nonexistent', 1);
return it('should not add any IIP', function() {
return chai.expect(g.initializers.length).to.equal(0);
});
});
});
describe('with no nodes', function() {
var g;
g = new graph.Graph;
it('should not allow adding edges', function() {
g.addEdge('Foo', 'out', 'Bar', 'in');
return chai.expect(graph.edges).to.be.empty;
});
return it('should not allow adding IIPs', function() {
g.addInitial('Hello', 'Bar', 'in');
return chai.expect(graph.initializers).to.be.empty;
});
});
return describe('Legacy exports loaded via JSON', function() {
var g, json, jsonString;
jsonString = "{\n \"exports\": [\n {\n \"public\": \"in\",\n \"private\": \"foo.in\",\n \"metadata\": {\n \"x\": 5,\n \"y\": 100\n }\n },\n {\n \"public\": \"out\",\n \"private\": \"bar.out\"\n }\n ],\n \"processes\": {\n \"Foo\": {\n \"component\": \"Foooo\"\n },\n \"Bar\": {\n \"component\": \"Baaar\"\n }\n }\n}";
json = JSON.parse(jsonString);
g = null;
it('should produce a Graph', function(done) {
return graph.loadJSON(json, function(err, instance) {
if (err) {
return done(err);
}
g = instance;
chai.expect(g).to.be.an('object');
return done();
});
});
it('should have two legacy exports', function(done) {
chai.expect(g.exports).to.be.an('array');
chai.expect(g.exports.length).to.equal(2);
return done();
});
return it('should fix the case of the process key', function(done) {
chai.expect(g.exports[0].process).to.equal('Foo');
chai.expect(g.exports[1].process).to.equal('Bar');
return done();
});
});
});