Writing your own project

Testing

First in line for testing, we have fbp-spec

Just add a fbpspec.coffee file in /spec directory

Important to note, you cannot send brackets or do any sort of special operations using fbp-spec. To get around that, you will have to write components exclusively for testing, and fbp graphs as fixtures.

The command we use for noflo, and the flags can be found at noflo-nodejs flags

fbpspec = require 'fbp-spec'

nodeRuntime =
  label: "NoFlo node.js"
  description: ""
  type: "noflo"
  protocol: "websocket"
  secret: 'notasecret'
  address: "ws://localhost:3333"
  id: "7807f4d8-63e0-4a89-a577-2770c14f8106"
  command: './node_modules/.bin/noflo-nodejs --verbose --catch-exceptions=false --secret notasecret --port=3333 --host=localhost --register=false --capture-output=true --debug=true'

fbpspec.mocha.run nodeRuntime, './spec',
  fixturetimeout: 20000
  starttimeout: 100000

Then, for each test, just add a yaml file in the /spec directory, each yaml file in /spec is loaded by the fbp-spec.

topic: "canadianness/FindWords"
name: "Find words fbpspec"
cases:
-
  name: 'content eh'
  assertion: 'should be find one `eh`'
  inputs:
    word: 'eh'
    surrounding: false
    content: 'eh'
  expect:
    matches:
      equals: 'eh'

Then traditional: (as you would any other mocha test)

noflo = require 'noflo'

# if you want to run your tests on the browser builds using phantom
unless noflo.isBrowser()
  chai = require 'chai'
  path = require 'path'
  baseDir = path.resolve __dirname, '../'
else
  baseDir = 'canadianness'

describe 'FindWords component', ->
  # we scope the instance of the component or graph
  # as well as the sockets we attach to the ports
  # because we load it inside of the `before`
  # and use it inside of the actual test
  c = null
  word = null
  surrounding = null
  content = null
  matches = null

  before (done) ->
    @timeout 4000
    loader = new noflo.ComponentLoader baseDir
    loader.load 'canadianness/FindWords', (err, instance) ->
      return done err if err
      c = instance
      word = noflo.internalSocket.createSocket()
      content = noflo.internalSocket.createSocket()
      surrounding = noflo.internalSocket.createSocket()
      c.inPorts.word.attach word
      c.inPorts.content.attach content
      c.inPorts.surrounding.attach surrounding
      done()

  # before each test we want to attach all of the outports
  # and we want to detach the outports after each,
  # otherwise the event listeners in the test that ran before
  # would get triggered again
  beforeEach ->
    matches = noflo.internalSocket.createSocket()
    c.outPorts.matches.attach matches
  afterEach ->
    c.outPorts.matches.detach matches

  describe 'with content eh', ->
    it 'should be find one `eh`', (done) ->
      # we listen to the port events, receive the data
      # and do our assertions
      matches.on 'data', (data) ->
        chai.expect(data).to.eql 'eh'
        done()

      word.send 'eh'
      surrounding.send false
      content.send 'eh'

  describe 'with content that has no `eh`s', ->
    it 'should send an empty array', (done) ->
      matches.on 'data', (data) ->
        chai.expect(data).to.eql []
        done()
      word.send 'eh'
      surrounding.send false
      content.send 'A string without it is a sad string.'

  describe 'with content that has multiple `eh`s', ->
    it 'should send an array of ehs', (done) ->
      expect = ['Eh...', 'eh?', 'EH!']
      matches.on 'ip', (ip) ->
        if ip.type is 'data'
          chai.expect(ip.data).to.eql expect.shift()
        if ip.type is 'closeBracket'
          done()

      word.send 'eh'
      surrounding.send true
      content.send 'Eh... eh? EH!'

And finally, noflo-tester (as you would any other mocha test):

Tester = require 'noflo-tester'
chai = require 'chai'

describe 'FindWords component', ->
  t = new Tester 'canadianness/FindWords'
  before (done) ->
    t.start (err, instance) ->
      return throw err if err
      done()

  describe 'with content eh', ->
    it 'should be find one `eh`', (done) ->
      # will only listen once and trigger after disconnect
      t.receive 'matches', (data) ->
        chai.expect(data).to.eql 'eh'
        done()

      t.ins.word.send 'eh'
      t.ins.surrounding.send false
      t.ins.content.send 'eh'

  describe 'with content that has no `eh`s', ->
    it 'should send an empty array', (done) ->
      t.receive 'matches', (data) ->
        chai.expect(data).to.eql []
        done()
      t.ins.word.send 'eh'
      t.ins.surrounding.send false
      t.ins.content.send 'A string without it is a sad string.'

  describe 'with content that has multiple `eh`s', ->
    it 'should send an array of ehs', (done) ->
      expect = ['Eh...', 'eh?', 'EH!']
      t.outs.matches.on 'ip', (ip) ->
        if ip.type is 'data'
          chai.expect(ip.data).to.eql expect.shift()
        if ip.type is 'closeBracket'
          done()

      t.ins.word.send 'eh'
      t.ins.surrounding.send true
      t.ins.content.send 'Eh... eh? EH!'

Loading Components inline

Components can be also defined and loaded in one file: Components and graphs can be loaded using the ComponentLoader Say you were using a database query, querying a list of names from people

noflo = require 'noflo'

unless noflo.isBrowser()
  chai = require 'chai'
  path = require 'path'
  baseDir = path.resolve __dirname, '../'
else
  baseDir = 'canadianness'

fetchPeople = ->
  new noflo.Component
    inPorts:
      eh:
        datatype: 'all'
    outPorts:
      out:
        datatype: 'object'
        description: 'random data'
      error:
        datatype: 'object'
        description: 'if something goes horribly wrong'

    forwardBrackets:
      eh: ['out', 'error']
    process: (input, output) ->
      eh = input.getData 'eh'
      output.send out: new noflo.IP 'openBracket'
      output.send out: eh
      output.send out: new noflo.IP 'closeBracket'
      output.done()
log = ->
  c = new noflo.Component
    inPorts:
      in:
        datatype: 'all'
        description: 'data to log'
    outPorts:
      out:
        datatype: 'all'
        description: 'when finished'
  # we don't want to automatically forward from `in` so we set it to empty
  c.forwardBrackets = {}
  c.process (input, output) ->
    return unless input.hasStream 'in'
    # the stream will contain all the forwarded openBrackets
    console.log input.getStream 'in'
    output.sendDone out: true

loader = new noflo.ComponentLoader baseDir
loader.listComponents (err) ->
  return throw err if err
  loader.registerComponent 'processapi', 'Log', log
  loader.registerComponent 'processapi', 'FetchPeople', fetchPeople

  fbpData = "
  INPORT=FetchPeople.EH:EH
  OUTPORT=Log.OUT:OUT
  FetchPeople(processapi/FetchPeople) OUT -> IN Log(processapi/Log)
  "
  noflo.graph.loadFBP fbpData, (err, g) ->
    return throw err if err
    loader.registerComponent 'processapi', 'Connected', g
    loader.load 'processapi/Connected', (err, instance) ->
      return throw err if err

      # instance of the graph we defined in `fbpData`
      c = instance

      # create and attach sockets
      eh = noflo.internalSocket.createSocket()
      out = noflo.internalSocket.createSocket()
      c.inPorts.eh.attach eh
      c.outPorts.out.attach out

      # will be openBracket, data, closeBracket
      # and those will be forwarded to Log `in` port
      # which will wrap the brackets that FetchPeople itself sends
      eh.send new noflo.IP 'openBracket'
      eh.send 'message'
      eh.send new noflo.IP 'closeBracket'

      out.on 'data', (data) ->
        console.log 'done!'

see the tests in the project