diff --git a/bin/_mocha b/bin/_mocha index 86c67e2942..37d6d566f2 100755 --- a/bin/_mocha +++ b/bin/_mocha @@ -64,6 +64,7 @@ program .option('-A, --async-only', "force all tests to take a callback (async)") .option('-C, --no-colors', 'force disabling of colors') .option('-G, --growl', 'enable growl notification support') + .option('-O, --reporter-options ', 'reporter-specific options') .option('-R, --reporter ', 'specify the reporter to use', 'spec') .option('-S, --sort', "sort test files") .option('-b, --bail', "bail after first test failure") @@ -190,9 +191,22 @@ program.parse(process.argv); Error.stackTraceLimit = Infinity; // TODO: config +// reporter options + +var reporterOptions = {}; +if (program.reporterOptions !== undefined) { + program.reporterOptions.split(",").forEach(function(opt) { + var L = opt.split("="); + if (L.length != 2) { + throw new Error("invalid reporter option '" + opt + "'"); + } + reporterOptions[L[0]] = L[1]; + }); +} + // reporter -mocha.reporter(program.reporter); +mocha.reporter(program.reporter, reporterOptions); // interface diff --git a/lib/mocha.js b/lib/mocha.js index 69e273f9b7..730229c3b7 100644 --- a/lib/mocha.js +++ b/lib/mocha.js @@ -69,7 +69,7 @@ function Mocha(options) { this.suite = new exports.Suite('', new exports.Context); this.ui(options.ui); this.bail(options.bail); - this.reporter(options.reporter); + this.reporter(options.reporter, options.reporterOptions); if (null != options.timeout) this.timeout(options.timeout); this.useColors(options.useColors) if (options.slow) this.slow(options.slow); @@ -119,10 +119,10 @@ Mocha.prototype.addFile = function(file){ * Set reporter to `reporter`, defaults to "spec". * * @param {String|Function} reporter name or constructor + * @param {Object} reporterOptions optional options * @api public */ - -Mocha.prototype.reporter = function(reporter){ +Mocha.prototype.reporter = function(reporter, reporterOptions){ if ('function' == typeof reporter) { this._reporter = reporter; } else { @@ -137,6 +137,7 @@ Mocha.prototype.reporter = function(reporter){ if (!_reporter) throw new Error('invalid reporter "' + reporter + '"'); this._reporter = _reporter; } + this.options.reporterOptions = reporterOptions; return this; }; @@ -366,5 +367,14 @@ Mocha.prototype.run = function(fn){ if (options.growl) this._growl(runner, reporter); exports.reporters.Base.useColors = options.useColors; exports.reporters.Base.inlineDiffs = options.useInlineDiffs; - return runner.run(fn); + + function done(failures) { + if (reporter.done) { + reporter.done(failures, fn); + } else { + fn(failures); + } + } + + return runner.run(done); }; diff --git a/lib/reporters/xunit.js b/lib/reporters/xunit.js index 3506a07b21..c18afddce9 100644 --- a/lib/reporters/xunit.js +++ b/lib/reporters/xunit.js @@ -5,6 +5,7 @@ var Base = require('./base') , utils = require('../utils') + , fs = require('fs') , escape = utils.escape; /** @@ -30,12 +31,19 @@ exports = module.exports = XUnit; * @api public */ -function XUnit(runner) { +function XUnit(runner, options) { Base.call(this, runner); var stats = this.stats , tests = [] , self = this; + if (options.reporterOptions && options.reporterOptions.output) { + if (! fs.createWriteStream) { + throw new Error('file output not supported in browser'); + } + self.fileStream = fs.createWriteStream(options.reporterOptions.output); + } + runner.on('pending', function(test){ tests.push(test); }); @@ -49,7 +57,7 @@ function XUnit(runner) { }); runner.on('end', function(){ - console.log(tag('testsuite', { + self.write(tag('testsuite', { name: 'Mocha Tests' , tests: stats.tests , failures: stats.failures @@ -59,22 +67,46 @@ function XUnit(runner) { , time: (stats.duration / 1000) || 0 }, false)); - tests.forEach(test); - console.log(''); + tests.forEach(function(t) { self.test(t); }); + self.write(''); }); } +/** + * Override done to close the stream (if it's a file). + */ +XUnit.prototype.done = function(failures, fn) { + if (this.fileStream) { + this.fileStream.end(function() { + fn(failures); + }); + } else { + fn(failures); + } +}; + /** * Inherit from `Base.prototype`. */ XUnit.prototype.__proto__ = Base.prototype; +/** + * Write out the given line + */ +XUnit.prototype.write = function(line) { + if (this.fileStream) { + this.fileStream.write(line + '\n'); + } else { + console.log(line); + } +}; + /** * Output tag for the given `test.` */ -function test(test) { +XUnit.prototype.test = function(test, ostream) { var attrs = { classname: test.parent.fullTitle() , name: test.title @@ -83,13 +115,13 @@ function test(test) { if ('failed' == test.state) { var err = test.err; - console.log(tag('testcase', attrs, false, tag('failure', {}, false, cdata(escape(err.message) + "\n" + err.stack)))); + this.write(tag('testcase', attrs, false, tag('failure', {}, false, cdata(escape(err.message) + "\n" + err.stack)))); } else if (test.pending) { - console.log(tag('testcase', attrs, false, tag('skipped', {}, true))); + this.write(tag('testcase', attrs, false, tag('skipped', {}, true))); } else { - console.log(tag('testcase', attrs, true) ); + this.write(tag('testcase', attrs, true) ); } -} +}; /** * HTML tag helper.