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