The JSON reporter

The JSON reporter is especially versatile. Since it produces a literal serialization of the reporter event stream, it can be used to transport reporter events to another process and format them there. For example, say we build a WebSocket server that, when a new connection is made, runs our example test suite as a child process using the json format, and pipes the output into the socket.

For example if you open a connection to ws://localhost:8888/?test=foo this server will run this process:

jstest -f json example/spec/set_spec.js -t foo

Here’s the server code:

// example/socket/server.js

var child     = require('child_process'),
    http      = require('http'),
    url       = require('url'),
    split     = require('split'),
    WebSocket = require('faye-websocket')

var bin  = 'jstest',
    argv = ['-f', 'json', 'example/spec/set_spec.js']

var server = http.createServer()

server.on('upgrade', function(request, socket, body) {
  var ws = new WebSocket(request, socket, body),

      // parse the request's query string
      params  = url.parse(request.url, true).query,
      tests   = JSON.parse(params.test),

      // construct ARGV for the child process
      options = tests.reduce(function(o, t) { return o.concat(['-t', t]) }, []),
      proc    = child.spawn(bin, argv.concat(options))

  proc.stdout.pipe(split()).pipe(ws)
})

server.listen(8888)

Run the server with Node:

$ node example/socket/server.js

This will send a stream of messages over the WebSocket with the format:

{"jstest": [eventType, data]}

jstest includes a class called JS.Test.Reporters.JSON.Reader, whose job is to process this stream of messages and use them to notify another reporter. We could use it to accept the data from the WebSocket and display the test results in the browser, like this:

<!-- example/socket/browser.html -->

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <title>jstest</title>
  </head>
  <body>

    <script src="../../build/jstest.js"></script>

    <script>
      var options = new JS.Test.Runner().getOptions(),

          R       = JS.Test.Reporters,
          browser = new R.Browser(options),
          reader  = new R.JSON.Reader(browser)

      var test = encodeURIComponent(JSON.stringify(options.test)),
          ws   = new WebSocket('ws://localhost:8888/?test=' + test)

      ws.onmessage = function(event) {
        reader.read(event.data)
      }
    </script>

  </body>
</html>

If you open this page, it will open a WebSocket, which will trigger a test run. The socket will receive the test output as a JSON stream, which it hands off to the JSON.Reader, which displays the results with the Browser reporter. Now you can selectively run your server-side tests from the browser!

You can do a similar thing to send browser results to the terminal by overriding where the JSON reporter writes output to. When you run your tests, try this:

var ws   = new WebSocket('ws://localhost:8888/'),
    json = new JS.Test.Reporters.JSON()

json.puts = function(message) {
  ws.send(message)
}

ws.onopen = function() {
  JS.Test.autorun(function(runner) {
    runner.addReporter(json)
  })
}

This will send the browser results over the socket as JSON, and you can use a Reader on the server to print the results in the terminal.