diff --git a/lib/launcher.js b/lib/launcher.js index fef68e646..752950955 100644 --- a/lib/launcher.js +++ b/lib/launcher.js @@ -137,17 +137,13 @@ var init = function(argv) { childForks.forEach(function(childFork) { childFork.process = child.fork( - __dirname + "/runFromLauncher.js", [], + __dirname + "/runFromLauncher.js", process.argv.slice(2), {silent: true, cwd: process.cwd()}); childFork.output = ''; // stdin pipe childFork.process.stdout.on('data', function(chunk) { - // Output something so processes know we haven't stalled. - // TODO - consider replacing this with a message system which would - // output a dot per test. - process.stdout.write('.'); childFork.output += chunk; }); @@ -156,6 +152,17 @@ var init = function(argv) { childFork.output += chunk; }); + childFork.process.on('message', function(m) { + switch (m.event) { + case 'testPass': + process.stdout.write('.'); + break; + case 'testFail': + process.stdout.write('F'); + break; + } + }); + // err handler childFork.process.on('error', function(err) { log_('Runner Process(' + childFork.process.pid + ') Error: ' + err); diff --git a/lib/runFromLauncher.js b/lib/runFromLauncher.js index 14a7c0572..c05ffe2d8 100644 --- a/lib/runFromLauncher.js +++ b/lib/runFromLauncher.js @@ -29,6 +29,25 @@ process.on('message', function(m) { // Launch test run. var runner = new Runner(config); + + // Pipe events back to the launcher. + runner.on('testPass', function() { + process.send({ + event: 'testPass' + }); + }); + runner.on('testFail', function() { + process.send({ + event: 'testFail' + }); + }); + runner.on('testsDone', function(results) { + process.send({ + event: 'testsDone', + results: results + }); + }); + runner.run().then(function(exitCode) { process.exit(exitCode); }).catch(function(err) { diff --git a/lib/runner.js b/lib/runner.js index 46286e530..d458171d8 100644 --- a/lib/runner.js +++ b/lib/runner.js @@ -4,12 +4,18 @@ var protractor = require('./protractor'), path = require('path'), util = require('util'), fs = require('fs'), - q = require('q'); + q = require('q'), + EventEmitter = require('events').EventEmitter; /* * Runner is responsible for starting the execution of a test run and triggering * setup, teardown, managing config, etc through its various dependencies. * + * The Protractor Runner is a node EventEmitter with the following events: + * - testPass + * - testFail + * - testsDone + * * @param {Object} config * @constructor */ @@ -42,6 +48,8 @@ var Runner = function(config) { this.loadDriverProvider_(config); }; +util.inherits(Runner, EventEmitter); + /** * Execute the Runner's test cases through Jasmine. @@ -55,6 +63,31 @@ Runner.prototype.runJasmine_ = function(specs, done) { self = this; require('../jasminewd'); + + var RunnerReporter = function(emitter) { + this.emitter = emitter; + }; + + RunnerReporter.prototype.reportRunnerStarting = function() {}; + RunnerReporter.prototype.reportRunnerResults = function() {}; + RunnerReporter.prototype.reportSuiteResults = function() {}; + RunnerReporter.prototype.reportSpecStarting = function() {}; + RunnerReporter.prototype.reportSpecResults = function(spec) { + if (spec.results().passed()) { + this.emitter.emit('testPass'); + } else { + this.emitter.emit('testFail'); + } + }; + RunnerReporter.prototype.log = function() {}; + + // On timeout, the flow should be reset. This will prevent webdriver tasks + // from overflowing into the next test and causing it to fail or timeout + // as well. This is done in the reporter instead of an afterEach block + // to ensure that it runs after any afterEach() blocks with webdriver tasks + // get to complete first. + jasmine.getEnv().addReporter(new RunnerReporter(this)); + webdriver.promise.controlFlow().execute(function() { self.runTestPreparers_(); }, 'run test preparers').then(function() { @@ -101,6 +134,14 @@ Runner.prototype.runMocha_ = function(specs, done) { global.it.skip = global.xit = mochaAdapters.xit; }); + mocha.suite.on('pass', function() { + this.emit('testPass'); + }); + + mocha.suite.on('fail', function() { + this.emit('testFail'); + }); + mocha.loadFiles(); webdriver.promise.controlFlow().execute(function() { @@ -132,6 +173,7 @@ Runner.prototype.runMocha_ = function(specs, done) { * @param done A callback for when tests are finished. */ Runner.prototype.runCucumber_ = function(specs, done) { + // TODO - add the event interface for cucumber. var Cucumber = require('cucumber'), self = this, execOptions = ['node', 'node_modules/.bin/cucumber-js'], @@ -371,6 +413,7 @@ Runner.prototype.run = function() { // 3) Teardown }).then(function(result) { + self.emit('testsDone', result); testResult = result; if (self.driverprovider_.updateJob) { return self.driverprovider_.updateJob({