UNPKG

noflo

Version:

Flow-Based Programming environment for JavaScript

1,516 lines (1,513 loc) 43.7 kB
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; }); }); }); });