UNPKG

noflo

Version:

Flow-Based Programming environment for JavaScript

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