UNPKG

noflo

Version:

Flow-Based Programming environment for JavaScript

345 lines (319 loc) 13.4 kB
if typeof process isnt 'undefined' and process.execPath and process.execPath.match /node|iojs/ chai = require 'chai' unless chai loader = require '../src/lib/nodejs/ComponentLoader.coffee' component = require '../src/lib/Component.coffee' port = require '../src/lib/Port.coffee' platform = require '../src/lib/Platform.coffee' path = require 'path' root = path.resolve __dirname, '../' shippingLanguage = 'coffeescript' urlPrefix = './' else loader = require 'noflo/src/lib/ComponentLoader.js' component = require 'noflo/src/lib/Component.js' platform = require 'noflo/src/lib/Platform.js' root = 'noflo' shippingLanguage = 'javascript' urlPrefix = '/' class Split extends component.Component constructor: -> @inPorts = in: new noflo.Port @outPorts = out: new noflo.ArrayPort @inPorts.in.on 'connect', (data) => @outPorts.out.connect() @inPorts.in.on 'data', (data) => @outPorts.out.send data @inPorts.in.on 'disconnect', => @outPorts.out.disconnect() Split.getComponent = -> new Split Merge = -> inst = new component.Component inst.inPorts.add 'in', (event, payload, instance) -> method = event method = 'send' if event is 'data' instance.outPorts[method] 'out', payload inst.outPorts.add 'out' inst describe 'ComponentLoader with no external packages installed', -> l = new loader.ComponentLoader root it 'should initially know of no components', -> chai.expect(l.components).to.be.null it 'should not initially require revalidation', -> chai.expect(l.revalidate).to.be.false it 'should not initially be ready', -> chai.expect(l.ready).to.be.false it 'should not initially be processing', -> chai.expect(l.processing).to.be.false it 'should not have any packages in the checked list', -> chai.expect(l.checked).to.be.empty describe 'normalizing names', -> it 'should return simple module names as-is', -> normalized = l.getModulePrefix 'foo' chai.expect(normalized).to.equal 'foo' it 'should return empty for NoFlo core', -> normalized = l.getModulePrefix 'noflo' chai.expect(normalized).to.equal '' it 'should strip noflo-', -> normalized = l.getModulePrefix 'noflo-image' chai.expect(normalized).to.equal 'image' it 'should strip NPM scopes', -> normalized = l.getModulePrefix '@noflo/foo' chai.expect(normalized).to.equal 'foo' it 'should strip NPM scopes and noflo-', -> normalized = l.getModulePrefix '@noflo/noflo-image' chai.expect(normalized).to.equal 'image' it 'should be able to read a list of components', (done) -> @timeout 60 * 1000 ready = false l.once 'ready', -> ready = true chai.expect(l.ready).to.equal true l.listComponents (err, components) -> return done err if err chai.expect(l.processing).to.equal false chai.expect(l.components).not.to.be.empty chai.expect(components).to.equal l.components chai.expect(l.ready).to.equal true chai.expect(ready).to.equal true done() chai.expect(l.processing).to.equal true describe 'after listing components', -> it 'should have the Graph component registered', -> chai.expect(l.components.Graph).not.to.be.empty describe 'loading the Graph component', -> instance = null it 'should be able to load the component', (done) -> l.load 'Graph', (err, inst) -> return done err if err chai.expect(inst).to.be.an 'object' chai.expect(inst.componentName).to.equal 'Graph' instance = inst done() it 'should contain input ports', -> chai.expect(instance.inPorts).to.be.an 'object' chai.expect(instance.inPorts.graph).to.be.an 'object' it 'should have "on" method on the input port', -> chai.expect(instance.inPorts.graph.on).to.be.a 'function' it 'it should know that Graph is a subgraph', -> chai.expect(instance.isSubgraph()).to.equal true it 'should know the description for the Graph', -> chai.expect(instance.description).to.be.a 'string' it 'should be able to provide an icon for the Graph', -> chai.expect(instance.getIcon()).to.be.a 'string' chai.expect(instance.getIcon()).to.equal 'sitemap' # describe 'loading a subgraph', -> # l = new loader.ComponentLoader root # file = "#{urlPrefix}spec/fixtures/subgraph.fbp" # it 'should remove `graph` and `start` ports', (done) -> # l.listComponents (components) -> # l.components.Merge = Merge # l.components.Subgraph = file # l.components.Split = Split # l.load 'Subgraph', (inst) -> # chai.expect(inst).to.be.an 'object' # inst.once 'ready', -> # chai.expect(inst.inPorts.ports).not.to.have.keys ['graph','start'] # chai.expect(inst.inPorts.ports).to.have.keys ['in'] # chai.expect(inst.outPorts.ports).to.have.keys ['out'] # done() # it 'should not automatically start the subgraph if there is no `start` port', (done) -> # l.listComponents (components) -> # l.components.Merge = Merge # l.components.Subgraph = file # l.components.Split = Split # l.load 'Subgraph', (inst) -> # chai.expect(inst).to.be.an 'object' # inst.once 'ready', -> # chai.expect(inst.started).to.equal(false) # done() describe 'loading the Graph component', -> instance = null it 'should be able to load the component', (done) -> l.load 'Graph', (err, graph) -> return done err if err chai.expect(graph).to.be.an 'object' instance = graph done() it 'should have a reference to the Component Loader\'s baseDir', -> chai.expect(instance.baseDir).to.equal l.baseDir describe 'loading a component', -> it 'should return an error on an invalid component type', (done) -> l.components['InvalidComponent'] = true l.load 'InvalidComponent', (err, c) -> chai.expect(err).to.be.instanceOf Error chai.expect(err.message).to.equal 'Invalid type boolean for component InvalidComponent.' done() describe 'register a component at runtime', -> class Split extends component.Component constructor: -> @inPorts = in: new port.Port @outPorts = out: new port.Port Split.getComponent = -> new Split instance = null l.libraryIcons.foo = 'star' it 'should be available in the components list', -> l.registerComponent 'foo', 'Split', Split chai.expect(l.components).to.contain.keys ['foo/Split', 'Graph'] it 'should be able to load the component', (done) -> l.load 'foo/Split', (err, split) -> return done err if err chai.expect(split).to.be.an 'object' instance = split done() it 'should have the correct ports', -> chai.expect(instance.inPorts).to.have.keys ['in'] chai.expect(instance.outPorts).to.have.keys ['out'] it 'should have inherited its icon from the library', -> chai.expect(instance.getIcon()).to.equal 'star' it 'should emit an event on icon change', (done) -> instance.once 'icon', (newIcon) -> chai.expect(newIcon).to.equal 'smile' done() instance.setIcon 'smile' it 'new instances should still contain the original icon', (done) -> l.load 'foo/Split', (err, split) -> return done err if err chai.expect(split).to.be.an 'object' chai.expect(split.getIcon()).to.equal 'star' done() it 'after setting an icon for the Component class, new instances should have that', (done) -> Split::icon = 'trophy' l.load 'foo/Split', (err, split) -> return done err if err chai.expect(split).to.be.an 'object' chai.expect(split.getIcon()).to.equal 'trophy' done() it 'should not affect the original instance', -> chai.expect(instance.getIcon()).to.equal 'smile' describe 'reading sources', -> it 'should be able to provide source code for a component', (done) -> l.getSource 'Graph', (err, component) -> return done err if err chai.expect(component).to.be.an 'object' chai.expect(component.code).to.be.a 'string' chai.expect(component.code.indexOf('noflo.Component')).to.not.equal -1 chai.expect(component.code.indexOf('exports.getComponent')).to.not.equal -1 chai.expect(component.name).to.equal 'Graph' chai.expect(component.library).to.equal '' chai.expect(component.language).to.equal shippingLanguage done() it 'should return an error for missing components', (done) -> l.getSource 'foo/BarBaz', (err, src) -> chai.expect(err).to.be.an 'error' done() it 'should return an error for non-file components', (done) -> l.getSource 'foo/Split', (err, src) -> chai.expect(err).to.be.an 'error' done() describe 'writing sources', -> describe 'with working code', -> workingSource = """ var noflo = require('noflo'); exports.getComponent = function() { var c = new noflo.Component(); c.inPorts.add('in', function(packet, outPorts) { if (packet.event !== 'data') { return; } // Do something with the packet, then c.outPorts.out.send(packet.data); }); c.outPorts.add('out'); return c; };""" it 'should be able to set the source', (done) -> @timeout 10000 unless platform.isBrowser() workingSource = workingSource.replace "'noflo'", "'../src/lib/NoFlo'" l.setSource 'foo', 'RepeatData', workingSource, 'js', (err) -> return done err if err done() it 'should be a loadable component', (done) -> l.load 'foo/RepeatData', (err, inst) -> return done err if err chai.expect(inst).to.be.an 'object' chai.expect(inst.inPorts).to.contain.keys ['in'] chai.expect(inst.outPorts).to.contain.keys ['out'] done() describe 'with non-working code', -> nonWorkingSource = """ var noflo = require('noflo'); var notFound = require('./this_file_does_not_exist.js'); exports.getComponent = function() { var c = new noflo.Component(); c.inPorts.add('in', function(packet, outPorts) { if (packet.event !== 'data') { return; } // Do something with the packet, then c.outPorts.out.send(packet.data); }); c.outPorts.add('out'); return c; };""" it 'should be able to set the source', (done) -> unless platform.isBrowser() nonWorkingSource = nonWorkingSource.replace "'noflo'", "'../src/lib/NoFlo'" l.setSource 'foo', 'NotWorking', nonWorkingSource, 'js', (err) -> chai.expect(err).to.be.an 'error' done() it 'should not be a loadable component', (done) -> l.load 'foo/NotWorking', (err, inst) -> chai.expect(err).to.be.an 'error' chai.expect(inst).to.be.an 'undefined' done() describe 'ComponentLoader with a fixture project', -> l = null before -> return @skip() if platform.isBrowser() it 'should be possible to instantiate', -> l = new loader.ComponentLoader path.resolve __dirname, 'fixtures/componentloader' it 'should initially know of no components', -> chai.expect(l.components).to.be.a 'null' it 'should not initially be ready', -> chai.expect(l.ready).to.be.false it 'should be able to read a list of components', (done) -> ready = false l.once 'ready', -> chai.expect(l.ready).to.equal true ready = l.ready l.listComponents (err, components) -> return done err if err chai.expect(l.processing).to.equal false chai.expect(l.components).not.to.be.empty chai.expect(components).to.equal l.components chai.expect(l.ready).to.equal true chai.expect(ready).to.equal true done() chai.expect(l.processing).to.equal true it 'should be able to load a local component', (done) -> l.load 'componentloader/Output', (err, instance) -> chai.expect(err).to.be.a 'null' chai.expect(instance.description).to.equal 'Output stuff' chai.expect(instance.icon).to.equal 'cloud' done() it 'should be able to load a component from a dependency', (done) -> l.load 'example/Forward', (err, instance) -> chai.expect(err).to.be.a 'null' chai.expect(instance.description).to.equal 'Forward stuff' chai.expect(instance.icon).to.equal 'car' done() it 'should be able to load a dynamically registered component from a dependency', (done) -> l.load 'example/Hello', (err, instance) -> chai.expect(err).to.be.a 'null' chai.expect(instance.description).to.equal 'Hello stuff' chai.expect(instance.icon).to.equal 'bicycle' done() it 'should be able to load core Graph component', (done) -> l.load 'Graph', (err, instance) -> chai.expect(err).to.be.a 'null' chai.expect(instance.icon).to.equal 'sitemap' done() it 'should fail loading a missing component', (done) -> l.load 'componentloader/Missing', (err, instance) -> chai.expect(err).to.be.an 'error' done()