noflo
Version:
Flow-Based Programming environment for JavaScript
1,516 lines (1,513 loc) • 43.7 kB
JavaScript
var IP, chai, component, socket;
if (typeof process !== 'undefined' && process.execPath && process.execPath.match(/node|iojs/)) {
if (!chai) {
chai = require('chai');
}
component = require('../src/lib/Component.coffee');
socket = require('../src/lib/InternalSocket.coffee');
IP = require('../src/lib/IP.coffee');
} else {
component = require('noflo/src/lib/Component.js');
socket = require('noflo/src/lib/InternalSocket.js');
IP = require('noflo/src/lib/IP.js');
}
describe('Component', function() {
describe('with required ports', function() {
it('should throw an error upon sending packet to an unattached required port', function() {
var c, s2;
s2 = new socket.InternalSocket;
c = new component.Component({
outPorts: {
required_port: {
required: true
},
optional_port: {}
}
});
c.outPorts.optional_port.attach(s2);
return chai.expect(function() {
return c.outPorts.required_port.send('foo');
}).to["throw"]();
});
return it('should be cool with an attached port', function() {
var c, f, s1, s2;
s1 = new socket.InternalSocket;
s2 = new socket.InternalSocket;
c = new component.Component({
inPorts: {
required_port: {
required: true
},
optional_port: {}
}
});
c.inPorts.required_port.attach(s1);
c.inPorts.optional_port.attach(s2);
f = function() {
s1.send('some-more-data');
return s2.send('some-data');
};
return chai.expect(f).to.not["throw"]();
});
});
describe('with component creation shorthand', function() {
it('should make component creation easy', function(done) {
var c, s1, s2;
c = new component.Component({
inPorts: {
"in": {
datatype: 'string',
required: true,
process: function(event, packet, component) {
if (event !== 'data') {
return;
}
chai.expect(packet).to.equal('some-data');
return chai.expect(component).to.equal(c);
}
},
just_processor: function(event, packet, component) {
if (event !== 'data') {
return;
}
chai.expect(packet).to.equal('some-data');
chai.expect(component).to.equal(c);
return done();
}
}
});
s1 = new socket.InternalSocket;
c.inPorts["in"].attach(s1);
c.inPorts["in"].nodeInstance = c;
s2 = new socket.InternalSocket;
c.inPorts.just_processor.attach(s1);
c.inPorts.just_processor.nodeInstance = c;
s1.send('some-data');
return s2.send('some-data');
});
it('should throw errors if there is no error port', function(done) {
var c, s1;
c = new component.Component({
inPorts: {
"in": {
datatype: 'string',
required: true,
process: function(event, packet, component) {
if (event !== 'data') {
return;
}
chai.expect(packet).to.equal('some-data');
chai.expect(component).to.equal(c);
chai.expect(function() {
return c.error(new Error);
}).to["throw"](Error);
return done();
}
}
}
});
s1 = new socket.InternalSocket;
c.inPorts["in"].attach(s1);
c.inPorts["in"].nodeInstance = c;
return s1.send('some-data');
});
it('should throw errors if there is a non-attached error port', function(done) {
var c, s1;
c = new component.Component({
inPorts: {
"in": {
datatype: 'string',
required: true,
process: function(event, packet, component) {
if (event !== 'data') {
return;
}
chai.expect(packet).to.equal('some-data');
chai.expect(component).to.equal(c);
chai.expect(function() {
return c.error(new Error);
}).to["throw"](Error);
return done();
}
}
},
outPorts: {
error: {
datatype: 'object',
required: true
}
}
});
s1 = new socket.InternalSocket;
c.inPorts["in"].attach(s1);
c.inPorts["in"].nodeInstance = c;
return s1.send('some-data');
});
it('should not throw errors if there is a non-required error port', function(done) {
var c, s1;
c = new component.Component({
inPorts: {
"in": {
datatype: 'string',
required: true,
process: function(event, packet, component) {
if (event !== 'data') {
return;
}
chai.expect(packet).to.equal('some-data');
chai.expect(component).to.equal(c);
c.error(new Error);
return done();
}
}
},
outPorts: {
error: {
required: false
}
}
});
s1 = new socket.InternalSocket;
c.inPorts["in"].attach(s1);
c.inPorts["in"].nodeInstance = c;
return s1.send('some-data');
});
return it('should send errors if there is a connected error port', function(done) {
var c, groups, grps, s1, s2;
grps = [];
c = new component.Component({
inPorts: {
"in": {
datatype: 'string',
required: true,
process: function(event, packet, component) {
if (event === 'begingroup') {
grps.push(packet);
}
if (event !== 'data') {
return;
}
chai.expect(packet).to.equal('some-data');
chai.expect(component).to.equal(c);
return c.error(new Error, grps);
}
}
},
outPorts: {
error: {
datatype: 'object'
}
}
});
s1 = new socket.InternalSocket;
s2 = new socket.InternalSocket;
groups = ['foo', 'bar'];
s2.on('begingroup', function(grp) {
return chai.expect(grp).to.equal(groups.shift());
});
s2.on('data', function(err) {
chai.expect(err).to.be.an.instanceOf(Error);
chai.expect(groups.length).to.equal(0);
return done();
});
c.inPorts["in"].attach(s1);
c.outPorts.error.attach(s2);
c.inPorts["in"].nodeInstance = c;
s1.beginGroup('foo');
s1.beginGroup('bar');
return s1.send('some-data');
});
});
describe('defining ports with invalid names', function() {
it('should throw an error with uppercase letters in inport', function() {
var shorthand;
shorthand = function() {
var c;
return c = new component.Component({
inPorts: {
fooPort: {}
}
});
};
return chai.expect(shorthand).to["throw"]();
});
it('should throw an error with uppercase letters in outport', function() {
var shorthand;
shorthand = function() {
var c;
return c = new component.Component({
outPorts: {
BarPort: {}
}
});
};
return chai.expect(shorthand).to["throw"]();
});
return it('should throw an error with special characters in inport', function() {
var shorthand;
shorthand = function() {
var c;
return c = new component.Component({
inPorts: {
'$%^&*a': {}
}
});
};
return chai.expect(shorthand).to["throw"]();
});
});
describe('starting a component', function() {
return it('should flag the component as started', function() {
var c, i;
c = new component.Component({
inPorts: {
"in": {
datatype: 'string',
required: true
}
}
});
i = new socket.InternalSocket;
c.inPorts["in"].attach(i);
c.start();
chai.expect(c.started).to.equal(true);
return chai.expect(c.isStarted()).to.equal(true);
});
});
describe('shutting down a component', function() {
return it('should flag the component as not started', function() {
var c, i;
c = new component.Component({
inPorts: {
"in": {
datatype: 'string',
required: true
}
}
});
i = new socket.InternalSocket;
c.inPorts["in"].attach(i);
c.start();
c.shutdown();
chai.expect(c.started).to.equal(false);
return chai.expect(c.isStarted()).to.equal(false);
});
});
describe('with object-based IPs', function() {
it('should speak IP objects', function(done) {
var c, s1, s2;
c = new component.Component({
inPorts: {
"in": {
datatype: 'string',
handle: function(ip, component) {
chai.expect(ip).to.be.an('object');
chai.expect(ip.type).to.equal('data');
chai.expect(ip.groups).to.be.an('array');
chai.expect(ip.groups).to.eql(['foo']);
chai.expect(ip.data).to.be.a('string');
chai.expect(ip.data).to.equal('some-data');
return c.outPorts.out.data('bar', {
groups: ['foo']
});
}
}
},
outPorts: {
out: {
datatype: 'string'
}
}
});
s1 = new socket.InternalSocket;
s2 = new socket.InternalSocket;
s2.on('ip', function(ip) {
chai.expect(ip).to.be.an('object');
chai.expect(ip.type).to.equal('data');
chai.expect(ip.groups).to.be.an('array');
chai.expect(ip.groups).to.eql(['foo']);
chai.expect(ip.data).to.be.a('string');
chai.expect(ip.data).to.equal('bar');
return done();
});
c.inPorts["in"].attach(s1);
c.outPorts.out.attach(s2);
return s1.post(new IP('data', 'some-data', {
groups: ['foo']
}));
});
return it('should support substreams', function(done) {
var c, d, s1, s2, s3;
c = new component.Component({
inPorts: {
tags: {
datatype: 'string',
handle: function(ip) {
chai.expect(ip).to.be.an('object');
switch (ip.type) {
case 'openBracket':
c.str += "<" + ip.data + ">";
return c.level++;
case 'data':
return c.str += ip.data;
case 'closeBracket':
c.str += "</" + ip.data + ">";
c.level--;
if (c.level === 0) {
c.outPorts.html.data(c.str);
return c.str = '';
}
}
}
}
},
outPorts: {
html: {
datatype: 'string'
}
}
});
c.str = '';
c.level = 0;
d = new component.Component({
inPorts: {
bang: {
datatype: 'bang',
handle: function(ip) {
return d.outPorts.tags.openBracket('p').openBracket('em').data('Hello').closeBracket('em').data(', ').openBracket('strong').data('World!').closeBracket('strong').closeBracket('p');
}
}
},
outPorts: {
tags: {
datatype: 'string'
}
}
});
s1 = new socket.InternalSocket;
s2 = new socket.InternalSocket;
s3 = new socket.InternalSocket;
s3.on('ip', function(ip) {
chai.expect(ip).to.be.an('object');
chai.expect(ip.type).to.equal('data');
chai.expect(ip.data).to.equal('<p><em>Hello</em>, <strong>World!</strong></p>');
return done();
});
d.inPorts.bang.attach(s1);
d.outPorts.tags.attach(s2);
c.inPorts.tags.attach(s2);
c.outPorts.html.attach(s3);
return s1.post(new IP('data', 'start'));
});
});
return describe('with process function', function() {
var c, sin1, sin2, sin3, sout1, sout2;
c = null;
sin1 = null;
sin2 = null;
sin3 = null;
sout1 = null;
sout2 = null;
beforeEach(function(done) {
sin1 = new socket.InternalSocket;
sin2 = new socket.InternalSocket;
sin3 = new socket.InternalSocket;
sout1 = new socket.InternalSocket;
sout2 = new socket.InternalSocket;
return done();
});
it('should trigger on IPs', function(done) {
var count, hadIPs;
hadIPs = [];
c = new component.Component({
inPorts: {
foo: {
datatype: 'string'
},
bar: {
datatype: 'string'
}
},
outPorts: {
baz: {
datatype: 'boolean'
}
},
process: function(input, output) {
hadIPs = [];
if (input.has('foo')) {
hadIPs.push('foo');
}
if (input.has('bar')) {
hadIPs.push('bar');
}
return output.sendDone({
baz: true
});
}
});
c.inPorts.foo.attach(sin1);
c.inPorts.bar.attach(sin2);
c.outPorts.baz.attach(sout1);
count = 0;
sout1.on('ip', function(ip) {
count++;
if (count === 1) {
chai.expect(hadIPs).to.eql(['foo']);
}
if (count === 2) {
chai.expect(hadIPs).to.eql(['foo', 'bar']);
return done();
}
});
sin1.post(new IP('data', 'first'));
return sin2.post(new IP('data', 'second'));
});
it('should not be triggered by non-triggering ports', function(done) {
var count, triggered;
triggered = [];
c = new component.Component({
inPorts: {
foo: {
datatype: 'string',
triggering: false
},
bar: {
datatype: 'string'
}
},
outPorts: {
baz: {
datatype: 'boolean'
}
},
process: function(input, output) {
triggered.push(input.port.name);
return output.sendDone({
baz: true
});
}
});
c.inPorts.foo.attach(sin1);
c.inPorts.bar.attach(sin2);
c.outPorts.baz.attach(sout1);
count = 0;
sout1.on('ip', function(ip) {
count++;
if (count === 1) {
chai.expect(triggered).to.eql(['bar']);
}
if (count === 2) {
chai.expect(triggered).to.eql(['bar', 'bar']);
return done();
}
});
sin1.post(new IP('data', 'first'));
sin2.post(new IP('data', 'second'));
sin1.post(new IP('data', 'first'));
return sin2.post(new IP('data', 'second'));
});
it('should fetch undefined for premature data', function(done) {
c = new component.Component({
inPorts: {
foo: {
datatype: 'string'
},
bar: {
datatype: 'boolean',
triggering: false,
control: true
},
baz: {
datatype: 'string',
triggering: false,
control: true
}
},
process: function(input, output) {
var bar, baz, foo, _ref;
if (!input.has('foo')) {
return;
}
_ref = input.getData('foo', 'bar', 'baz'), foo = _ref[0], bar = _ref[1], baz = _ref[2];
chai.expect(foo).to.be.a('string');
chai.expect(bar).to.be.undefined;
chai.expect(baz).to.be.undefined;
return done();
}
});
c.inPorts.foo.attach(sin1);
c.inPorts.bar.attach(sin2);
c.inPorts.baz.attach(sin3);
sin1.post(new IP('data', 'AZ'));
sin2.post(new IP('data', true));
return sin3.post(new IP('data', 'first'));
});
it('should receive and send complete IP objects', function(done) {
c = new component.Component({
inPorts: {
foo: {
datatype: 'string'
},
bar: {
datatype: 'string'
}
},
outPorts: {
baz: {
datatype: 'object'
}
},
process: function(input, output) {
var bar, baz, foo, _ref;
if (!input.has('foo', 'bar')) {
return;
}
_ref = input.get('foo', 'bar'), foo = _ref[0], bar = _ref[1];
baz = {
foo: foo.data,
bar: bar.data,
groups: foo.groups,
type: bar.type
};
return output.sendDone({
baz: new IP('data', baz, {
groups: ['baz']
})
});
}
});
c.inPorts.foo.attach(sin1);
c.inPorts.bar.attach(sin2);
c.outPorts.baz.attach(sout1);
sout1.once('ip', function(ip) {
chai.expect(ip).to.be.an('object');
chai.expect(ip.type).to.equal('data');
chai.expect(ip.data.foo).to.equal('foo');
chai.expect(ip.data.bar).to.equal('bar');
chai.expect(ip.data.groups).to.eql(['foo']);
chai.expect(ip.data.type).to.equal('data');
chai.expect(ip.groups).to.eql(['baz']);
return done();
});
sin1.post(new IP('data', 'foo', {
groups: ['foo']
}));
return sin2.post(new IP('data', 'bar', {
groups: ['bar']
}));
});
it('should receive and send just IP data if wanted', function(done) {
c = new component.Component({
inPorts: {
foo: {
datatype: 'string'
},
bar: {
datatype: 'string'
}
},
outPorts: {
baz: {
datatype: 'object'
}
},
process: function(input, output) {
var bar, baz, foo, _ref;
if (!input.has('foo', 'bar')) {
return;
}
_ref = input.getData('foo', 'bar'), foo = _ref[0], bar = _ref[1];
baz = {
foo: foo,
bar: bar
};
return output.sendDone({
baz: baz
});
}
});
c.inPorts.foo.attach(sin1);
c.inPorts.bar.attach(sin2);
c.outPorts.baz.attach(sout1);
sout1.once('ip', function(ip) {
chai.expect(ip).to.be.an('object');
chai.expect(ip.type).to.equal('data');
chai.expect(ip.data.foo).to.equal('foo');
chai.expect(ip.data.bar).to.equal('bar');
return done();
});
sin1.post(new IP('data', 'foo', {
groups: ['foo']
}));
return sin2.post(new IP('data', 'bar', {
groups: ['bar']
}));
});
it('should keep last value for controls', function(done) {
c = new component.Component({
inPorts: {
foo: {
datatype: 'string'
},
bar: {
datatype: 'string',
control: true
}
},
outPorts: {
baz: {
datatype: 'object'
}
},
process: function(input, output) {
var bar, baz, foo, _ref;
if (!input.has('foo', 'bar')) {
return;
}
_ref = input.getData('foo', 'bar'), foo = _ref[0], bar = _ref[1];
baz = {
foo: foo,
bar: bar
};
return output.sendDone({
baz: baz
});
}
});
c.inPorts.foo.attach(sin1);
c.inPorts.bar.attach(sin2);
c.outPorts.baz.attach(sout1);
sout1.once('ip', function(ip) {
chai.expect(ip).to.be.an('object');
chai.expect(ip.type).to.equal('data');
chai.expect(ip.data.foo).to.equal('foo');
chai.expect(ip.data.bar).to.equal('bar');
return sout1.once('ip', function(ip) {
chai.expect(ip).to.be.an('object');
chai.expect(ip.type).to.equal('data');
chai.expect(ip.data.foo).to.equal('boo');
chai.expect(ip.data.bar).to.equal('bar');
return done();
});
});
sin1.post(new IP('data', 'foo'));
sin2.post(new IP('data', 'bar'));
return sin1.post(new IP('data', 'boo'));
});
it('should keep last data-typed IP packet for controls', function(done) {
c = new component.Component({
inPorts: {
foo: {
datatype: 'string'
},
bar: {
datatype: 'string',
control: true
}
},
outPorts: {
baz: {
datatype: 'object'
}
},
process: function(input, output) {
var bar, baz, foo, _ref;
if (!input.has('foo', 'bar')) {
return;
}
_ref = input.getData('foo', 'bar'), foo = _ref[0], bar = _ref[1];
baz = {
foo: foo,
bar: bar
};
return output.sendDone({
baz: baz
});
}
});
c.inPorts.foo.attach(sin1);
c.inPorts.bar.attach(sin2);
c.outPorts.baz.attach(sout1);
sout1.once('ip', function(ip) {
chai.expect(ip).to.be.an('object');
chai.expect(ip.type).to.equal('data');
chai.expect(ip.data.foo).to.equal('foo');
chai.expect(ip.data.bar).to.equal('bar');
return sout1.once('ip', function(ip) {
chai.expect(ip).to.be.an('object');
chai.expect(ip.type).to.equal('data');
chai.expect(ip.data.foo).to.equal('boo');
chai.expect(ip.data.bar).to.equal('bar');
return done();
});
});
sin1.post(new IP('data', 'foo'));
sin2.post(new IP('openBracket'));
sin2.post(new IP('data', 'bar'));
sin2.post(new IP('closeBracket'));
return sin1.post(new IP('data', 'boo'));
});
it('should isolate packets with different scopes', function(done) {
var bar1, bar2, foo1, foo2;
foo1 = 'Josh';
bar1 = 'Laura';
bar2 = 'Luke';
foo2 = 'Jane';
c = new component.Component({
inPorts: {
foo: {
datatype: 'string'
},
bar: {
datatype: 'string'
}
},
outPorts: {
baz: {
datatype: 'string'
}
},
process: function(input, output) {
var bar, foo, _ref;
if (!input.has('foo', 'bar')) {
return;
}
_ref = input.getData('foo', 'bar'), foo = _ref[0], bar = _ref[1];
return output.sendDone({
baz: "" + foo + " and " + bar
});
}
});
c.inPorts.foo.attach(sin1);
c.inPorts.bar.attach(sin2);
c.outPorts.baz.attach(sout1);
sout1.once('ip', function(ip) {
chai.expect(ip).to.be.an('object');
chai.expect(ip.type).to.equal('data');
chai.expect(ip.scope).to.equal('1');
chai.expect(ip.data).to.equal('Josh and Laura');
return sout1.once('ip', function(ip) {
chai.expect(ip).to.be.an('object');
chai.expect(ip.type).to.equal('data');
chai.expect(ip.scope).to.equal('2');
chai.expect(ip.data).to.equal('Jane and Luke');
return done();
});
});
sin1.post(new IP('data', 'Josh', {
scope: '1'
}));
sin2.post(new IP('data', 'Luke', {
scope: '2'
}));
sin2.post(new IP('data', 'Laura', {
scope: '1'
}));
return sin1.post(new IP('data', 'Jane', {
scope: '2'
}));
});
it('should be able to change scope', function(done) {
c = new component.Component({
inPorts: {
foo: {
datatype: 'string'
}
},
outPorts: {
baz: {
datatype: 'string'
}
},
process: function(input, output) {
var foo;
foo = input.getData('foo');
return output.sendDone({
baz: new IP('data', foo, {
scope: 'baz'
})
});
}
});
c.inPorts.foo.attach(sin1);
c.outPorts.baz.attach(sout1);
sout1.once('ip', function(ip) {
chai.expect(ip).to.be.an('object');
chai.expect(ip.type).to.equal('data');
chai.expect(ip.scope).to.equal('baz');
chai.expect(ip.data).to.equal('foo');
return done();
});
return sin1.post(new IP('data', 'foo', {
scope: 'foo'
}));
});
it('should preserve order between input and output', function(done) {
var ip, sample, _i, _len, _results;
c = new component.Component({
inPorts: {
msg: {
datatype: 'string'
},
delay: {
datatype: 'int'
}
},
outPorts: {
out: {
datatype: 'object'
}
},
ordered: true,
process: function(input, output) {
var delay, msg, _ref;
if (!input.has('msg', 'delay')) {
return;
}
_ref = input.getData('msg', 'delay'), msg = _ref[0], delay = _ref[1];
return setTimeout(function() {
return output.sendDone({
out: {
msg: msg,
delay: delay
}
});
}, delay);
}
});
c.inPorts.msg.attach(sin1);
c.inPorts.delay.attach(sin2);
c.outPorts.out.attach(sout1);
sample = [
{
delay: 30,
msg: "one"
}, {
delay: 0,
msg: "two"
}, {
delay: 20,
msg: "three"
}, {
delay: 10,
msg: "four"
}
];
sout1.on('ip', function(ip) {
chai.expect(ip.data).to.eql(sample.shift());
if (sample.length === 0) {
return done();
}
});
_results = [];
for (_i = 0, _len = sample.length; _i < _len; _i++) {
ip = sample[_i];
sin1.post(new IP('data', ip.msg));
_results.push(sin2.post(new IP('data', ip.delay)));
}
return _results;
});
it('should ignore order between input and output', function(done) {
var count, ip, sample, _i, _len, _results;
c = new component.Component({
inPorts: {
msg: {
datatype: 'string'
},
delay: {
datatype: 'int'
}
},
outPorts: {
out: {
datatype: 'object'
}
},
ordered: false,
process: function(input, output) {
var delay, msg, _ref;
if (!input.has('msg', 'delay')) {
return;
}
_ref = input.getData('msg', 'delay'), msg = _ref[0], delay = _ref[1];
return setTimeout(function() {
return output.sendDone({
out: {
msg: msg,
delay: delay
}
});
}, delay);
}
});
c.inPorts.msg.attach(sin1);
c.inPorts.delay.attach(sin2);
c.outPorts.out.attach(sout1);
sample = [
{
delay: 30,
msg: "one"
}, {
delay: 0,
msg: "two"
}, {
delay: 20,
msg: "three"
}, {
delay: 10,
msg: "four"
}
];
count = 0;
sout1.on('ip', function(ip) {
var src;
count++;
switch (count) {
case 1:
src = sample[1];
break;
case 2:
src = sample[3];
break;
case 3:
src = sample[2];
break;
case 4:
src = sample[0];
}
chai.expect(ip.data).to.eql(src);
if (count === 4) {
return done();
}
});
_results = [];
for (_i = 0, _len = sample.length; _i < _len; _i++) {
ip = sample[_i];
sin1.post(new IP('data', ip.msg));
_results.push(sin2.post(new IP('data', ip.delay)));
}
return _results;
});
it('should throw errors if there is no error port', function(done) {
c = new component.Component({
inPorts: {
"in": {
datatype: 'string',
required: true
}
},
process: function(input, output) {
var packet;
packet = input.get('in');
chai.expect(packet.data).to.equal('some-data');
chai.expect(function() {
return output.done(new Error('Should fail'));
}).to["throw"](Error);
return done();
}
});
c.inPorts["in"].attach(sin1);
return sin1.post(new IP('data', 'some-data'));
});
it('should throw errors if there is a non-attached error port', function(done) {
c = new component.Component({
inPorts: {
"in": {
datatype: 'string',
required: true
}
},
outPorts: {
error: {
datatype: 'object',
required: true
}
},
process: function(input, output) {
var packet;
packet = input.get('in');
chai.expect(packet.data).to.equal('some-data');
chai.expect(function() {
return output.sendDone(new Error('Should fail'));
}).to["throw"](Error);
return done();
}
});
c.inPorts["in"].attach(sin1);
return sin1.post(new IP('data', 'some-data'));
});
it('should not throw errors if there is a non-required error port', function(done) {
c = new component.Component({
inPorts: {
"in": {
datatype: 'string',
required: true
}
},
outPorts: {
error: {
required: false
}
},
process: function(input, output) {
var packet;
packet = input.get('in');
chai.expect(packet.data).to.equal('some-data');
output.sendDone(new Error('Should not fail'));
return done();
}
});
c.inPorts["in"].attach(sin1);
return sin1.post(new IP('data', 'some-data'));
});
it('should send errors if there is a connected error port', function(done) {
c = new component.Component({
inPorts: {
"in": {
datatype: 'string',
required: true
}
},
outPorts: {
error: {
datatype: 'object'
}
},
process: function(input, output) {
var packet;
packet = input.get('in');
chai.expect(packet.data).to.equal('some-data');
chai.expect(packet.scope).to.equal('some-scope');
return output.sendDone(new Error('Should fail'));
}
});
sout1.on('ip', function(ip) {
chai.expect(ip).to.be.an('object');
chai.expect(ip.data).to.be.an.instanceOf(Error);
chai.expect(ip.scope).to.equal('some-scope');
return done();
});
c.inPorts["in"].attach(sin1);
c.outPorts.error.attach(sout1);
return sin1.post(new IP('data', 'some-data', {
scope: 'some-scope'
}));
});
it('should send substreams with multiple errors per activation', function(done) {
var actual, count, expected;
c = new component.Component({
inPorts: {
"in": {
datatype: 'string',
required: true
}
},
outPorts: {
error: {
datatype: 'object'
}
},
process: function(input, output) {
var errors, packet;
packet = input.get('in');
chai.expect(packet.data).to.equal('some-data');
chai.expect(packet.scope).to.equal('some-scope');
errors = [];
errors.push(new Error('One thing is invalid'));
errors.push(new Error('Another thing is invalid'));
return output.sendDone(errors);
}
});
expected = ['<', 'One thing is invalid', 'Another thing is invalid', '>'];
actual = [];
count = 0;
sout1.on('ip', function(ip) {
count++;
chai.expect(ip).to.be.an('object');
chai.expect(ip.scope).to.equal('some-scope');
if (ip.type === 'openBracket') {
actual.push('<');
}
if (ip.type === 'closeBracket') {
actual.push('>');
}
if (ip.type === 'data') {
chai.expect(ip.data).to.be.an.instanceOf(Error);
actual.push(ip.data.message);
}
if (count === 4) {
chai.expect(actual).to.eql(expected);
return done();
}
});
c.inPorts["in"].attach(sin1);
c.outPorts.error.attach(sout1);
return sin1.post(new IP('data', 'some-data', {
scope: 'some-scope'
}));
});
it('should forward brackets for map-style components', function(done) {
var actual, count, data, source, _i, _len, _results;
c = new component.Component({
inPorts: {
"in": {
datatype: 'string'
}
},
outPorts: {
out: {
datatype: 'string'
},
error: {
datatype: 'object'
}
},
process: function(input, output) {
var str;
str = input.getData();
if (typeof str !== 'string') {
return output.sendDone(new Error('Input is not string'));
}
return output.pass(str.toUpperCase());
}
});
c.inPorts["in"].attach(sin1);
c.outPorts.out.attach(sout1);
c.outPorts.error.attach(sout2);
source = ['<', 'foo', 'bar', '>'];
actual = [];
count = 0;
sout1.on('ip', function(ip) {
var data;
data = (function() {
switch (ip.type) {
case 'openBracket':
return '<';
case 'closeBracket':
return '>';
default:
return ip.data;
}
})();
chai.expect(data).to.equal(source[count].toUpperCase());
count++;
if (count === 4) {
return done();
}
});
sout2.on('ip', function(ip) {
console.log('Unexpected error', ip);
return done(ip.data);
});
_results = [];
for (_i = 0, _len = source.length; _i < _len; _i++) {
data = source[_i];
switch (data) {
case '<':
_results.push(sin1.post(new IP('openBracket')));
break;
case '>':
_results.push(sin1.post(new IP('closeBracket')));
break;
default:
_results.push(sin1.post(new IP('data', data)));
}
}
return _results;
});
it('should support custom bracket forwarding mappings with auto-ordering', function(done) {
var count, errCount, ip, sample, _i, _len;
c = new component.Component({
inPorts: {
msg: {
datatype: 'string'
},
delay: {
datatype: 'int'
}
},
outPorts: {
out: {
datatype: 'string'
},
error: {
datatype: 'object'
}
},
forwardBrackets: {
msg: ['out', 'error'],
delay: ['error']
},
process: function(input, output) {
var delay, msg, _ref;
if (!input.has('msg', 'delay')) {
return;
}
_ref = input.getData('msg', 'delay'), msg = _ref[0], delay = _ref[1];
if (delay < 0) {
return output.sendDone(new Error('Delay is negative'));
}
return setTimeout(function() {
return output.sendDone({
out: {
msg: msg,
delay: delay
}
});
}, delay);
}
});
c.inPorts.msg.attach(sin1);
c.inPorts.delay.attach(sin2);
c.outPorts.out.attach(sout1);
c.outPorts.error.attach(sout2);
sample = [
{
delay: 30,
msg: "one"
}, {
delay: 0,
msg: "two"
}, {
delay: 20,
msg: "three"
}, {
delay: 10,
msg: "four"
}, {
delay: -40,
msg: 'five'
}
];
count = 0;
errCount = 0;
sout1.on('ip', function(ip) {
var src;
src = null;
switch (count) {
case 0:
chai.expect(ip.type).to.equal('openBracket');
chai.expect(ip.data).to.equal('msg');
break;
case 5:
chai.expect(ip.type).to.equal('closeBracket');
chai.expect(ip.data).to.equal('msg');
break;
default:
src = sample[count - 1];
}
if (src) {
chai.expect(ip.data).to.eql(src);
}
return count++;
});
sout2.on('ip', function(ip) {
switch (errCount) {
case 0:
chai.expect(ip.type).to.equal('openBracket');
chai.expect(ip.data).to.equal('msg');
break;
case 1:
chai.expect(ip.type).to.equal('openBracket');
chai.expect(ip.data).to.equal('delay');
break;
case 2:
chai.expect(ip.type).to.equal('data');
chai.expect(ip.data).to.be.an.error;
break;
case 3:
chai.expect(ip.type).to.equal('closeBracket');
chai.expect(ip.data).to.equal('delay');
break;
case 4:
chai.expect(ip.type).to.equal('closeBracket');
chai.expect(ip.data).to.equal('msg');
}
errCount++;
if (errCount === 5) {
return done();
}
});
sin1.post(new IP('openBracket', 'msg'));
sin2.post(new IP('openBracket', 'delay'));
for (_i = 0, _len = sample.length; _i < _len; _i++) {
ip = sample[_i];
sin1.post(new IP('data', ip.msg));
sin2.post(new IP('data', ip.delay));
}
sin1.post(new IP('closeBracket', 'msg'));
return sin2.post(new IP('closeBracket', 'delay'));
});
it('should forward IP metadata for map-style components', function(done) {
var count, n, source, str, _i, _len, _results;
c = new component.Component({
inPorts: {
"in": {
datatype: 'string'
}
},
outPorts: {
out: {
datatype: 'string'
},
error: {
datatype: 'object'
}
},
process: function(input, output) {
var str;
str = input.getData();
if (typeof str !== 'string') {
return output.sendDone(new Error('Input is not string'));
}
return output.pass(str.toUpperCase());
}
});
c.inPorts["in"].attach(sin1);
c.outPorts.out.attach(sout1);
c.outPorts.error.attach(sout2);
source = ['foo', 'bar', 'baz'];
count = 0;
sout1.on('ip', function(ip) {
chai.expect(ip.type).to.equal('data');
chai.expect(ip.count).to.be.a('number');
chai.expect(ip.length).to.be.a('number');
chai.expect(ip.data).to.equal(source[ip.count].toUpperCase());
chai.expect(ip.length).to.equal(source.length);
count++;
if (count === source.length) {
return done();
}
});
sout2.on('ip', function(ip) {
console.log('Unexpected error', ip);
return done(ip.data);
});
n = 0;
_results = [];
for (_i = 0, _len = source.length; _i < _len; _i++) {
str = source[_i];
_results.push(sin1.post(new IP('data', str, {
count: n++,
length: source.length
})));
}
return _results;
});
return describe('with custom callbacks', function() {
beforeEach(function(done) {
c = new component.Component({
inPorts: {
foo: {
datatype: 'string'
},
bar: {
datatype: 'int',
control: true
}
},
outPorts: {
baz: {
datatype: 'object'
},
err: {
datatype: 'object'
}
},
ordered: true,
activateOnInput: false,
process: function(input, output, done) {
var bar, baz, foo, _ref;
if (!input.has('foo', 'bar')) {
return;
}
_ref = input.getData('foo', 'bar'), foo = _ref[0], bar = _ref[1];
if (bar < 0 || bar > 1000) {
return output.sendDone({
err: new Error("Bar is not correct: " + bar)
});
}
input.activate();
output.send({
baz: new IP('openBracket')
});
baz = {
foo: foo,
bar: bar
};
output.send({
baz: baz
});
return setTimeout(function() {
output.send({
baz: new IP('closeBracket')
});
return done();
}, bar);
}
});
c.inPorts.foo.attach(sin1);
c.inPorts.bar.attach(sin2);
c.outPorts.baz.attach(sout1);
c.outPorts.err.attach(sout2);
return done();
});
it('should fail on wrong input', function(done) {
sout1.once('ip', function(ip) {
return done(new Error('Unexpected baz'));
});
sout2.once('ip', function(ip) {
chai.expect(ip).to.be.an('object');
chai.expect(ip.data).to.be.an.error;
chai.expect(ip.data.message).to.contain('Bar');
return done();
});
sin1.post(new IP('data', 'fff'));
return sin2.post(new IP('data', -120));
});
return it('should send substreams', function(done) {
var actual, count, expected, item, sample, _i, _len, _results;
sample = [
{
bar: 30,
foo: "one"
}, {
bar: 0,
foo: "two"
}
];
expected = ['<', 'one', '>', '<', 'two', '>'];
actual = [];
count = 0;
sout1.on('ip', function(ip) {
count++;
switch (ip.type) {
case 'openBracket':
actual.push('<');
break;
case 'closeBracket':
actual.push('>');
break;
default:
actual.push(ip.data.foo);
}
if (count === 6) {
chai.expect(actual).to.eql(expected);
return done();
}
});
sout2.once('ip', function(ip) {
return done(ip.data);
});
_results = [];
for (_i = 0, _len = sample.length; _i < _len; _i++) {
item = sample[_i];
sin2.post(new IP('data', item.bar));
_results.push(sin1.post(new IP('data', item.foo)));
}
return _results;
});
});
});
});