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