UNPKG

noflo

Version:

Flow-Based Programming environment for JavaScript

1,101 lines (1,080 loc) 35.1 kB
/* eslint-disable max-classes-per-file */ let shippingLanguage; let urlPrefix; if ((typeof process !== 'undefined') && process.execPath && process.execPath.match(/node|iojs/)) { shippingLanguage = 'javascript'; urlPrefix = './'; } else { shippingLanguage = 'javascript'; urlPrefix = '/base/'; } describe('ComponentLoader with no external packages installed', () => { let l = new noflo.ComponentLoader(baseDir); class Split extends noflo.Component { constructor() { const options = { inPorts: { in: {}, }, outPorts: { out: {}, }, process(input, output) { output.sendDone(input.get('in')); }, }; super(options); } } Split.getComponent = () => new Split(); const Merge = function () { const inst = new noflo.Component(); inst.inPorts.add('in'); inst.outPorts.add('out'); inst.process((input, output) => output.sendDone(input.get('in'))); return inst; }; it('should initially know of no components', () => { chai.expect(l.components).to.be.null; }); 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.a('null'); }); it('should not have any packages in the checked list', () => { chai.expect(l.checked).to.not.exist; }); describe('normalizing names', () => { it('should return simple module names as-is', () => { const normalized = l.getModulePrefix('foo'); chai.expect(normalized).to.equal('foo'); }); it('should return empty for NoFlo core', () => { const normalized = l.getModulePrefix('noflo'); chai.expect(normalized).to.equal(''); }); it('should strip noflo-', () => { const normalized = l.getModulePrefix('noflo-image'); chai.expect(normalized).to.equal('image'); }); it('should strip NPM scopes', () => { const normalized = l.getModulePrefix('@noflo/foo'); chai.expect(normalized).to.equal('foo'); }); it('should strip NPM scopes and noflo-', () => { const normalized = l.getModulePrefix('@noflo/noflo-image'); chai.expect(normalized).to.equal('image'); }); }); it('should be able to read a list of components', function (done) { this.timeout(60 * 1000); l.listComponents((err, components) => { if (err) { done(err); return; } chai.expect(l.processing, 'should have stopped processing').to.equal(null); chai.expect(l.components, 'should contain components').not.to.be.empty; chai.expect(components, 'should have returned the full list').to.equal(l.components); chai.expect(l.ready, 'should have been set ready').to.equal(true); done(); }); if (!noflo.isBrowser()) { // Browser component registry can be synchronous chai.expect(l.processing, 'should have started processing').to.be.a('promise'); } }); describe('calling listComponents twice simultaneously', () => { it('should return the same results', (done) => { const loader = new noflo.ComponentLoader(baseDir); const received = []; loader.listComponents((err, components) => { if (err) { done(err); return; } received.push(components); if (received.length !== 2) { return; } chai.expect(received[0]).to.equal(received[1]); done(); }); loader.listComponents((err, components) => { if (err) { done(err); return; } received.push(components); if (received.length !== 2) { return; } chai.expect(received[0]).to.equal(received[1]); done(); }); }); }); 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', () => { let instance = null; it('should be able to load the component', (done) => { l.load('Graph', (err, inst) => { if (err) { done(err); return; } 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.getDescription()).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'); }); it('should be able to load the component with non-ready ComponentLoader', (done) => { const loader = new noflo.ComponentLoader(baseDir); loader.load('Graph', (err, inst) => { if (err) { done(err); return; } chai.expect(inst).to.be.an('object'); chai.expect(inst.componentName).to.equal('Graph'); instance = inst; done(); }); }); }); describe('loading a subgraph', () => { l = new noflo.ComponentLoader(baseDir); const file = `${urlPrefix}spec/fixtures/subgraph.fbp`; it('should remove `graph` and `start` ports', (done) => { l.listComponents((err) => { if (err) { done(err); return; } l.components.Merge = Merge; l.components.Subgraph = file; l.components.Split = Split; l.load('Subgraph', (err, inst) => { if (err) { done(err); return; } 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((err) => { if (err) { done(err); return; } l.components.Merge = Merge; l.components.Subgraph = file; l.components.Split = Split; l.load('Subgraph', (err, inst) => { if (err) { done(err); return; } chai.expect(inst).to.be.an('object'); inst.once('ready', () => { chai.expect(inst.started).to.equal(false); done(); }); }); }); }); it('should also work with a passed graph object', (done) => { noflo.graph.loadFile(file, (err, graph) => { if (err) { done(err); return; } l.listComponents((err) => { if (err) { done(err); return; } l.components.Merge = Merge; l.components.Subgraph = graph; l.components.Split = Split; l.load('Subgraph', (err, inst) => { if (err) { done(err); return; } 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(); }); }); }); }); }); }); describe('loading the Graph component', () => { let instance = null; it('should be able to load the component', (done) => { l.load('Graph', (err, graph) => { if (err) { done(err); return; } 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', () => { let loader = null; before((done) => { loader = new noflo.ComponentLoader(baseDir); loader.listComponents(done); }); it('should return an error on an invalid component type', (done) => { loader.components.InvalidComponent = true; loader.load('InvalidComponent', (err) => { chai.expect(err).to.be.an('error'); chai.expect(err.message).to.equal('Invalid type boolean for component InvalidComponent.'); done(); }); }); it('should return an error on a missing component path', (done) => { let str; loader.components.InvalidComponent = 'missing-file.js'; if (noflo.isBrowser()) { str = 'Dynamic loading of'; } else { str = 'Cannot find module'; } loader.load('InvalidComponent', (err) => { chai.expect(err).to.be.an('error'); chai.expect(err.message).to.contain(str); done(); }); }); }); describe('register a component at runtime', () => { class FooSplit extends noflo.Component { constructor() { const options = { inPorts: { in: {}, }, outPorts: { out: {}, }, }; super(options); } } FooSplit.getComponent = () => new FooSplit(); let instance = null; l.libraryIcons.foo = 'star'; it('should be available in the components list', () => { l.registerComponent('foo', 'Split', FooSplit); 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) => { if (err) { done(err); return; } chai.expect(split).to.be.an('object'); instance = split; done(); }); }); it('should have the correct ports', () => { chai.expect(instance.inPorts.ports).to.have.keys(['in']); chai.expect(instance.outPorts.ports).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) => { if (err) { done(err); return; } chai.expect(split).to.be.an('object'); chai.expect(split.getIcon()).to.equal('star'); done(); }); }); // TODO reconsider this test after full decaffeination it.skip('after setting an icon for the Component class, new instances should have that', (done) => { FooSplit.prototype.icon = 'trophy'; l.load('foo/Split', (err, split) => { if (err) { done(err); return; } 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) => { if (err) { done(err); return; } 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) => { chai.expect(err).to.be.an('error'); done(); }); }); it('should return an error for non-file components', (done) => { if (noflo.isBrowser()) { // Browser runtime actually supports this via toString() done(); return; } l.getSource('foo/Split', (err) => { chai.expect(err).to.be.an('error'); done(); }); }); it('should be able to provide source for a graph file component', (done) => { const file = `${urlPrefix}spec/fixtures/subgraph.fbp`; l.components.Subgraph = file; l.getSource('Subgraph', (err, src) => { if (err) { done(err); return; } chai.expect(src.code).to.not.be.empty; chai.expect(src.language).to.equal('json'); done(); }); }); it('should be able to provide source for a graph object component', (done) => { const file = `${urlPrefix}spec/fixtures/subgraph.fbp`; noflo.graph.loadFile(file, (err, graph) => { if (err) { done(err); return; } l.components.Subgraph2 = graph; l.getSource('Subgraph2', (err, src) => { if (err) { done(err); return; } chai.expect(src.code).to.not.be.empty; chai.expect(src.language).to.equal('json'); done(); }); }); }); it('should be able to get the source for non-ready ComponentLoader', (done) => { const loader = new noflo.ComponentLoader(baseDir); loader.getSource('Graph', (err, component) => { if (err) { done(err); return; } 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(); }); }); }); describe('getting supported languages', () => { it('should include the expected ones', () => { const expectedLanguages = ['es2015', 'javascript']; if (!noflo.isBrowser()) { expectedLanguages.push('coffeescript'); expectedLanguages.push('typescript'); } expectedLanguages.sort(); const supportedLanguages = l.getLanguages(); supportedLanguages.sort(); chai.expect(supportedLanguages).to.eql(expectedLanguages); }); }); describe('writing sources', () => { let localNofloPath; if (!noflo.isBrowser()) { localNofloPath = JSON.stringify(path.resolve(__dirname, '../lib/NoFlo')); } describe('with working code', () => { describe('with ES5', () => { let workingSource = `\ var noflo = require('noflo'); exports.getComponent = function() { var c = new noflo.Component(); c.inPorts.add('in'); c.outPorts.add('out'); c.process(function (input, output) { output.sendDone(input.get('in')); }); return c; };`; it('should be able to set the source', function (done) { this.timeout(10000); if (!noflo.isBrowser()) { workingSource = workingSource.replace("'noflo'", localNofloPath); } l.setSource('foo', 'RepeatData', workingSource, 'javascript', (err) => { if (err) { done(err); return; } done(); }); }); it('should be a loadable component', (done) => { l.load('foo/RepeatData', (err, inst) => { if (err) { done(err); return; } chai.expect(inst).to.be.an('object'); chai.expect(inst.inPorts).to.contain.keys(['in']); chai.expect(inst.outPorts).to.contain.keys(['out']); const ins = new noflo.internalSocket.InternalSocket(); const out = new noflo.internalSocket.InternalSocket(); inst.inPorts.in.attach(ins); inst.outPorts.out.attach(out); out.on('ip', (ip) => { chai.expect(ip.type).to.equal('data'); chai.expect(ip.data).to.equal('ES5'); done(); }); ins.send('ES5'); }); }); it('should return sources in the same format', (done) => { l.getSource('foo/RepeatData', (err, source) => { if (err) { done(err); return; } chai.expect(source.language).to.equal('javascript'); chai.expect(source.code).to.equal(workingSource); done(); }); }); it('should be able to set the source for non-ready ComponentLoader', function (done) { this.timeout(10000); const loader = new noflo.ComponentLoader(baseDir); loader.setSource('foo', 'RepeatData', workingSource, 'javascript', done); }); }); describe('with ES6', () => { before(function () { if (l.getLanguages().indexOf('es2015') === -1) { this.skip(); } }); let workingSource = `\ const noflo = require('noflo'); exports.getComponent = () => { const c = new noflo.Component(); c.inPorts.add('in'); c.outPorts.add('out'); c.process((input, output) => { output.sendDone(input.get('in')); }); return c; };`; it('should be able to set the source', function (done) { this.timeout(10000); if (!noflo.isBrowser()) { workingSource = workingSource.replace("'noflo'", localNofloPath); } l.setSource('foo', 'RepeatDataES6', workingSource, 'es2015', (err) => { if (err) { done(err); return; } done(); }); }); it('should be a loadable component', (done) => { l.load('foo/RepeatDataES6', (err, inst) => { if (err) { done(err); return; } chai.expect(inst).to.be.an('object'); chai.expect(inst.inPorts).to.contain.keys(['in']); chai.expect(inst.outPorts).to.contain.keys(['out']); const ins = new noflo.internalSocket.InternalSocket(); const out = new noflo.internalSocket.InternalSocket(); inst.inPorts.in.attach(ins); inst.outPorts.out.attach(out); out.on('ip', (ip) => { chai.expect(ip.type).to.equal('data'); chai.expect(ip.data).to.equal('ES6'); done(); }); ins.send('ES6'); }); }); it('should return sources in the same format', (done) => { l.getSource('foo/RepeatDataES6', (err, source) => { if (err) { done(err); return; } chai.expect(source.language).to.equal('es2015'); chai.expect(source.code).to.equal(workingSource); done(); }); }); }); describe('with CoffeeScript', () => { before(function () { if (l.getLanguages().indexOf('coffeescript') === -1) { this.skip(); } }); let workingSource = `\ noflo = require 'noflo' exports.getComponent = -> c = new noflo.Component c.inPorts.add 'in' c.outPorts.add 'out' c.process (input, output) -> output.sendDone input.get 'in'\ `; it('should be able to set the source', function (done) { this.timeout(10000); if (!noflo.isBrowser()) { workingSource = workingSource.replace("'noflo'", localNofloPath); } l.setSource('foo', 'RepeatDataCoffee', workingSource, 'coffeescript', (err) => { if (err) { done(err); return; } done(); }); }); it('should be a loadable component', (done) => { l.load('foo/RepeatDataCoffee', (err, inst) => { if (err) { done(err); return; } chai.expect(inst).to.be.an('object'); chai.expect(inst.inPorts).to.contain.keys(['in']); chai.expect(inst.outPorts).to.contain.keys(['out']); const ins = new noflo.internalSocket.InternalSocket(); const out = new noflo.internalSocket.InternalSocket(); inst.inPorts.in.attach(ins); inst.outPorts.out.attach(out); out.on('ip', (ip) => { chai.expect(ip.type).to.equal('data'); chai.expect(ip.data).to.equal('CoffeeScript'); done(); }); ins.send('CoffeeScript'); }); }); it('should return sources in the same format', (done) => { l.getSource('foo/RepeatDataCoffee', (err, source) => { if (err) { done(err); return; } chai.expect(source.language).to.equal('coffeescript'); chai.expect(source.code).to.equal(workingSource); done(); }); }); }); describe('with TypeScript', () => { before(function () { if (l.getLanguages().indexOf('typescript') === -1) { this.skip(); } }); let workingSource = `\ import { Component } from 'noflo'; exports.getComponent = (): Component => { const c = new noflo.Component(); c.inPorts.add('in'); c.outPorts.add('out'); c.process((input, output): void => { output.sendDone(input.get('in')); }); return c; }; `; it('should be able to set the source', function (done) { this.timeout(10000); if (!noflo.isBrowser()) { workingSource = workingSource.replace("'noflo'", localNofloPath); } l.setSource('foo', 'RepeatDataTypeScript', workingSource, 'typescript', (err) => { if (err) { done(err); return; } done(); }); }); it('should be a loadable component', (done) => { l.load('foo/RepeatDataTypeScript', (err, inst) => { if (err) { done(err); return; } chai.expect(inst).to.be.an('object'); chai.expect(inst.inPorts).to.contain.keys(['in']); chai.expect(inst.outPorts).to.contain.keys(['out']); const ins = new noflo.internalSocket.InternalSocket(); const out = new noflo.internalSocket.InternalSocket(); inst.inPorts.in.attach(ins); inst.outPorts.out.attach(out); out.on('ip', (ip) => { chai.expect(ip.type).to.equal('data'); chai.expect(ip.data).to.equal('TypeScript'); done(); }); ins.send('TypeScript'); }); }); it('should return sources in the same format', (done) => { l.getSource('foo/RepeatDataTypeScript', (err, source) => { if (err) { done(err); return; } chai.expect(source.language).to.equal('typescript'); chai.expect(source.code).to.equal(workingSource); done(); }); }); }); }); describe('with non-working code', () => { describe('without exports', () => { let nonWorkingSource = `\ var noflo = require('noflo'); var 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 not be able to set the source', (done) => { if (!noflo.isBrowser()) { nonWorkingSource = nonWorkingSource.replace("'noflo'", localNofloPath); } l.setSource('foo', 'NotWorking', nonWorkingSource, 'js', (err) => { chai.expect(err).to.be.an('error'); chai.expect(err.message).to.contain('runnable component'); 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('with non-existing import', () => { let 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 not be able to set the source', (done) => { if (!noflo.isBrowser()) { nonWorkingSource = nonWorkingSource.replace("'noflo'", localNofloPath); } 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', () => { let l = null; before(function () { if (noflo.isBrowser()) { this.skip(); } }); it('should be possible to instantiate', () => { l = new noflo.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) => { l.listComponents((err, components) => { if (err) { done(err); return; } chai.expect(l.processing).to.equal(null); chai.expect(l.components).not.to.be.empty; chai.expect(components).to.equal(l.components); chai.expect(l.ready).to.equal(true); done(); }); chai.expect(l.processing).to.be.a('promise'); }); it('should be able to load a local JavaScript component', (done) => { l.load('componentloader/Output', (err, instance) => { if (err) { done(err); return; } chai.expect(instance.description).to.equal('Output stuff'); chai.expect(instance.icon).to.equal('cloud'); done(); }); }); it('should be able to load a local CoffeeScript component', (done) => { l.load('componentloader/RepeatAsync', (err, instance) => { if (err) { done(err); return; } chai.expect(instance.description).to.equal('Repeat stuff async'); chai.expect(instance.icon).to.equal('forward'); done(); }); }); it('should be able to load a local TypeScript component', (done) => { l.load('componentloader/Repeat', (err, instance) => { if (err) { done(err); return; } chai.expect(instance.description).to.equal('Repeat stuff'); chai.expect(instance.icon).to.equal('cloud'); done(); }); }); it('should be able to find specs for a local TypeScript component', (done) => { l.getSource('componentloader/Repeat', (err, source) => { if (err) { done(err); return; } chai.expect(source.tests).to.include('componentloader/Repeat'); done(); }); }); it('should be able to load a JavaScript component from a dependency', (done) => { l.load('example/Forward', (err, instance) => { if (err) { done(err); return; } chai.expect(instance.description).to.equal('Forward stuff'); chai.expect(instance.icon).to.equal('car'); done(); }); }); it('should be able to load a CoffeeScript component from a dependency', (done) => { l.load('example/RepeatAsync', (err, instance) => { if (err) { done(err); return; } chai.expect(instance.description).to.equal('Repeat stuff async'); chai.expect(instance.icon).to.equal('forward'); done(); }); }); it('should be able to find specs for a CoffeeScript component from a dependency', (done) => { l.getSource('example/RepeatAsync', (err, source) => { if (err) { done(err); return; } chai.expect(source.tests).to.include('example/RepeatAsync'); done(); }); }); it('should be able to load a TypeScript component from a dependency', (done) => { l.load('example/Repeat', (err, instance) => { if (err) { done(err); return; } chai.expect(instance.description).to.equal('Repeat 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) => { if (err) { done(err); return; } 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) => { if (err) { done(err); return; } chai.expect(instance.icon).to.equal('sitemap'); done(); }); }); it('should fail loading a missing component', (done) => { l.load('componentloader/Missing', (err) => { chai.expect(err).to.be.an('error'); done(); }); }); }); describe('ComponentLoader with a fixture project and caching', () => { let l = null; let fixtureRoot = null; before(function () { if (noflo.isBrowser()) { this.skip(); return; } fixtureRoot = path.resolve(__dirname, 'fixtures/componentloader'); }); after((done) => { if (noflo.isBrowser()) { done(); return; } const manifestPath = path.resolve(fixtureRoot, 'fbp.json'); const { unlink } = require('fs'); unlink(manifestPath, done); }); it('should be possible to pre-heat the cache file', function (done) { this.timeout(8000); const { exec } = require('child_process'); exec(`node ${path.resolve(__dirname, '../bin/noflo-cache-preheat')}`, { cwd: fixtureRoot }, done); }); it('should have populated a fbp-manifest file', (done) => { const manifestPath = path.resolve(fixtureRoot, 'fbp.json'); const { stat } = require('fs'); stat(manifestPath, (err, stats) => { if (err) { done(err); return; } chai.expect(stats.isFile()).to.equal(true); done(); }); }); it('should be possible to instantiate', () => { l = new noflo.ComponentLoader(fixtureRoot, { cache: true }); }); 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) => { l.listComponents((err, components) => { if (err) { done(err); return; } chai.expect(l.processing).to.equal(null); chai.expect(l.components).not.to.be.empty; chai.expect(components).to.equal(l.components); chai.expect(l.ready).to.equal(true); done(); }); chai.expect(l.processing).to.be.a('promise'); }); it('should be able to load a local component', (done) => { l.load('componentloader/Output', (err, instance) => { if (err) { done(err); return; } 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) => { if (err) { done(err); return; } 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) => { if (err) { done(err); return; } 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) => { if (err) { done(err); return; } chai.expect(instance.icon).to.equal('sitemap'); done(); }); }); it('should fail loading a missing component', (done) => { l.load('componentloader/Missing', (err) => { chai.expect(err).to.be.an('error'); done(); }); }); it('should fail with missing manifest without discover option', (done) => { l = new noflo.ComponentLoader(fixtureRoot, { cache: true, discover: false, manifest: 'fbp2.json', }); l.listComponents((err) => { chai.expect(err).to.be.an('error'); done(); }); }); it('should be able to use a custom manifest file', function (done) { this.timeout(8000); l = new noflo.ComponentLoader(fixtureRoot, { cache: true, discover: true, manifest: 'fbp2.json', }); l.listComponents((err) => { if (err) { done(err); return; } chai.expect(l.processing).to.equal(null); chai.expect(l.components).not.to.be.empty; done(); }); }); it('should have saved the new manifest', (done) => { const manifestPath = path.resolve(fixtureRoot, 'fbp2.json'); const { unlink } = require('fs'); unlink(manifestPath, done); }); });