From 19788358e85a22c65f9dad5df5e2b2b3e0da5089 Mon Sep 17 00:00:00 2001 From: Michael Demmer Date: Tue, 11 Jun 2013 16:00:11 -0700 Subject: [PATCH 1/3] Add a feature to optionally direct the test output to a file. The implementation creates a new output library and changes all of the test reporters to call output.log or output.write instead of directly calling console.log or process.stdout.write. Added a test rule to the Makefile that lists all of the reporters and verifies that the output is properly generated when the option is enabled. --- Makefile | 19 ++- bin/_mocha | 5 + lib/mocha.js | 20 ++- lib/output.js | 64 +++++++++ lib/reporters/base.js | 26 ++-- lib/reporters/doc.js | 15 +- lib/reporters/dot.js | 19 +-- lib/reporters/html-cov.js | 5 +- lib/reporters/json-cov.js | 5 +- lib/reporters/json-stream.js | 11 +- lib/reporters/json.js | 5 +- lib/reporters/landing.js | 7 +- lib/reporters/list.js | 11 +- lib/reporters/markdown.js | 9 +- lib/reporters/min.js | 7 +- lib/reporters/nyan.js | 3 +- lib/reporters/progress.js | 17 +-- lib/reporters/spec.js | 17 +-- lib/reporters/tap.js | 17 +-- lib/reporters/teamcity.js | 15 +- lib/reporters/xunit.js | 11 +- mocha.js | 260 +++++++++++++++++++++++++---------- 22 files changed, 405 insertions(+), 163 deletions(-) create mode 100644 lib/output.js diff --git a/Makefile b/Makefile index e8b7658e8b..e1f547e175 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,7 @@ TM_DEST = ~/Library/Application\ Support/TextMate/Bundles TM_BUNDLE = JavaScript\ mocha.tmbundle SRC = $(shell find lib -name "*.js" -type f | sort) SUPPORT = $(wildcard support/*.js) +REPORTERS := $(shell bin/mocha --reporters | sed -e 's/ - .*//') all: mocha.js @@ -33,7 +34,7 @@ lib-cov: test: test-unit -test-all: test-bdd test-tdd test-qunit test-exports test-unit test-grep test-jsapi test-compilers test-glob +test-all: test-bdd test-tdd test-qunit test-exports test-unit test-grep test-jsapi test-compilers test-glob test-reporter-output test-jsapi: @node test/jsapi @@ -104,6 +105,22 @@ test-async-only: test-glob: @./test/acceptance/glob/glob.sh + +# The html reporter isn't supported at the command line +TEST_REPORTERS := $(patsubst %,test-reporter-output-%,$(REPORTERS)) +TEST_REPORTERS := $(filter-out test-reporter-output-html,$(TEST_REPORTERS)) + +.PHONY: $(REPORTERS) +test-reporter-output: $(TEST_REPORTERS) +test-reporter-output-%: % + @echo "Testing file output for reporter $<" + @./bin/mocha --no-color --reporter $< test/acceptance/interfaces/bdd 2>&1 > /tmp/$<.stdout + @./bin/mocha --no-color --reporter $< test/acceptance/interfaces/bdd -O /tmp/$<.file 2>&1 > /tmp/dot.file.stdout + @test -s /tmp/$<.file || \ + (echo "ERROR: reporter $< does not support file output" && exit 1) + @test ! -s /tmp/$<.file.stdout || \ + (echo "ERROR: reporter $< file output wrote to stdout" && exit 1) + non-tty: @./bin/mocha \ --reporter dot \ diff --git a/bin/_mocha b/bin/_mocha index 7329505159..182c3db590 100755 --- a/bin/_mocha +++ b/bin/_mocha @@ -59,6 +59,7 @@ program .usage('[debug] [options] [files]') .option('-r, --require ', 'require the given module') .option('-R, --reporter ', 'specify the reporter to use', 'dot') + .option('-O, --output ', 'specify the output file', '-') .option('-u, --ui ', 'specify user-interface (bdd|tdd|exports)', 'bdd') .option('-g, --grep ', 'only run tests matching ') .option('-i, --invert', 'inverts --grep matches') @@ -177,6 +178,10 @@ Error.stackTraceLimit = Infinity; // TODO: config mocha.reporter(program.reporter); +// output + +mocha.output(program.output); + // interface mocha.ui(program.ui); diff --git a/lib/mocha.js b/lib/mocha.js index cd3d303268..4fa35085bf 100644 --- a/lib/mocha.js +++ b/lib/mocha.js @@ -27,6 +27,7 @@ exports.reporters = require('./reporters'); exports.Runnable = require('./runnable'); exports.Context = require('./context'); exports.Runner = require('./runner'); +exports.output = require('./output'); exports.Suite = require('./suite'); exports.Hook = require('./hook'); exports.Test = require('./test'); @@ -70,6 +71,7 @@ function Mocha(options) { this.ui(options.ui); this.bail(options.bail); this.reporter(options.reporter); + this.output(options.output); if (options.timeout) this.timeout(options.timeout); if (options.slow) this.slow(options.slow); } @@ -121,6 +123,19 @@ Mocha.prototype.reporter = function(reporter){ return this; }; +/** + * Set output to `output`, defaults to "-". + * + * @param {String} target file name + * @api public + */ + +Mocha.prototype.output = function(target) { + if (target !== undefined) { + exports.output.initialize(target); + } +}; + /** * Set test UI `name`, defaults to "bdd". * @@ -312,5 +327,8 @@ Mocha.prototype.run = function(fn){ if (options.grep) runner.grep(options.grep, options.invert); if (options.globals) runner.globals(options.globals); if (options.growl) this._growl(runner, reporter); - return runner.run(fn); + var self = this; + return runner.run(function(errCount) { + exports.output.end(errCount, fn); + }); }; diff --git a/lib/output.js b/lib/output.js new file mode 100644 index 0000000000..797dffc251 --- /dev/null +++ b/lib/output.js @@ -0,0 +1,64 @@ +var debug = require('debug')('mocha:output'); +var fs = require('fs'); + +// Initialize to be in stdout mode +var stream = exports.stream = process.stdout; +var _console = console; + +/** + * Write preformatted output directly to the stream. + */ +var write = exports.write = function(data) { + stream.write(data); +}; + + +/** + * Wrapper for console.log output. + */ +var log = exports.log = function() { + _console.log.apply(_console, arguments); +}; + +/** + * Wrapper for console.error output. + */ +var error = exports.error = function() { + _console.error.apply(_console, arguments); +}; + +/** + * Hook that's called when the test process is done to close the + * output stream. + */ +var end = exports.end = function(errCount, fn) { + if (stream === process.stdout) { + // nothing to do + return fn(errCount); + } + + stream.end(function() { + debug("stream ended"); + fn(errCount); + + // Reset the stream + stream = process.stdout; + } ); +}; + +/** + * Initialize the output layer. + * + * If target is undefined or "-" then use stdout, otherwise open the + * target file. + */ +var initialize = exports.initialize = function(target) { + if (target === undefined || target === "-") { + return; // nothing to do - stay in stdout mode + } + + stream = exports.stream = fs.createWriteStream(target); + _console = new console.Console(stream); +}; + + diff --git a/lib/reporters/base.js b/lib/reporters/base.js index 215be4562c..9657b2d747 100644 --- a/lib/reporters/base.js +++ b/lib/reporters/base.js @@ -5,7 +5,8 @@ var tty = require('tty') , diff = require('diff') - , ms = require('../ms'); + , ms = require('../ms') + , output = require('../output'); /** * Save timer references to avoid Sinon interfering (see GH-237). @@ -29,6 +30,12 @@ var isatty = tty.isatty(1) && tty.isatty(2); exports = module.exports = Base; +/** + * Expose the output hooks + */ +exports.output = output; + + /** * Enable coloring by default. */ @@ -108,6 +115,7 @@ exports.window = { : 75 }; + /** * Expose some basic cursor interactions * that are common among reporters. @@ -144,7 +152,7 @@ exports.cursor = { */ exports.list = function(failures){ - console.error(); + output.error(); failures.forEach(function(test, i){ // format var fmt = color('error title', ' %s) %s:\n') @@ -206,7 +214,7 @@ exports.list = function(failures){ stack = stack.slice(index ? index + 1 : index) .replace(/^/gm, ' '); - console.error(fmt, (i + 1), test.fullTitle(), msg, stack); + output.error(fmt, (i + 1), test.fullTitle(), msg, stack); }); }; @@ -288,14 +296,14 @@ Base.prototype.epilogue = function(){ var tests; var fmt; - console.log(); + output.log(); // passes fmt = color('bright pass', ' ') + color('green', ' %d passing') + color('light', ' (%s)'); - console.log(fmt, + output.log(fmt, stats.passes || 0, ms(stats.duration)); @@ -304,21 +312,21 @@ Base.prototype.epilogue = function(){ fmt = color('pending', ' ') + color('pending', ' %d pending'); - console.log(fmt, stats.pending); + output.log(fmt, stats.pending); } // failures if (stats.failures) { fmt = color('fail', ' %d failing'); - console.error(fmt, + output.error(fmt, stats.failures); Base.list(this.failures); - console.error(); + output.error(); } - console.log(); + output.log(); }; /** diff --git a/lib/reporters/doc.js b/lib/reporters/doc.js index 2e5bf57fc4..48986d464f 100644 --- a/lib/reporters/doc.js +++ b/lib/reporters/doc.js @@ -4,6 +4,7 @@ */ var Base = require('./base') + , output = Base.output , utils = require('../utils'); /** @@ -34,23 +35,23 @@ function Doc(runner) { runner.on('suite', function(suite){ if (suite.root) return; ++indents; - console.log('%s
', indent()); + output.log('%s
', indent()); ++indents; - console.log('%s

%s

', indent(), utils.escape(suite.title)); - console.log('%s
', indent()); + output.log('%s

%s

', indent(), utils.escape(suite.title)); + output.log('%s
', indent()); }); runner.on('suite end', function(suite){ if (suite.root) return; - console.log('%s
', indent()); + output.log('%s
', indent()); --indents; - console.log('%s
', indent()); + output.log('%s
', indent()); --indents; }); runner.on('pass', function(test){ - console.log('%s
%s
', indent(), utils.escape(test.title)); + output.log('%s
%s
', indent(), utils.escape(test.title)); var code = utils.escape(utils.clean(test.fn.toString())); - console.log('%s
%s
', indent(), code); + output.log('%s
%s
', indent(), code); }); } diff --git a/lib/reporters/dot.js b/lib/reporters/dot.js index 0c298ba71d..67c98c2984 100644 --- a/lib/reporters/dot.js +++ b/lib/reporters/dot.js @@ -4,6 +4,7 @@ */ var Base = require('./base') + , output = Base.output , color = Base.color; /** @@ -28,29 +29,29 @@ function Dot(runner) { , n = 0; runner.on('start', function(){ - process.stdout.write('\n '); + output.write('\n '); }); runner.on('pending', function(test){ - process.stdout.write(color('pending', Base.symbols.dot)); + output.write(color('pending', Base.symbols.dot)); }); runner.on('pass', function(test){ - if (++n % width == 0) process.stdout.write('\n '); + if (++n % width == 0) output.write('\n '); if ('slow' == test.speed) { - process.stdout.write(color('bright yellow', Base.symbols.dot)); + output.write(color('bright yellow', Base.symbols.dot)); } else { - process.stdout.write(color(test.speed, Base.symbols.dot)); + output.write(color(test.speed, Base.symbols.dot)); } }); runner.on('fail', function(test, err){ - if (++n % width == 0) process.stdout.write('\n '); - process.stdout.write(color('fail', Base.symbols.dot)); + if (++n % width == 0) output.write('\n '); + output.write(color('fail', Base.symbols.dot)); }); runner.on('end', function(){ - console.log(); + output.log(); self.epilogue(); }); } @@ -59,4 +60,4 @@ function Dot(runner) { * Inherit from `Base.prototype`. */ -Dot.prototype.__proto__ = Base.prototype; \ No newline at end of file +Dot.prototype.__proto__ = Base.prototype; diff --git a/lib/reporters/html-cov.js b/lib/reporters/html-cov.js index bfb27ffdf6..8083f07244 100644 --- a/lib/reporters/html-cov.js +++ b/lib/reporters/html-cov.js @@ -4,6 +4,7 @@ */ var JSONCov = require('./json-cov') + , output = require('../output') , fs = require('fs'); /** @@ -29,7 +30,7 @@ function HTMLCov(runner) { JSONCov.call(this, runner, false); runner.on('end', function(){ - process.stdout.write(fn({ + output.write(fn({ cov: self.cov , coverageClass: coverageClass })); @@ -48,4 +49,4 @@ function coverageClass(n) { if (n >= 50) return 'medium'; if (n >= 25) return 'low'; return 'terrible'; -} \ No newline at end of file +} diff --git a/lib/reporters/json-cov.js b/lib/reporters/json-cov.js index 73d0009377..7cac21560a 100644 --- a/lib/reporters/json-cov.js +++ b/lib/reporters/json-cov.js @@ -3,7 +3,8 @@ * Module dependencies. */ -var Base = require('./base'); +var Base = require('./base') + , _output = Base.output; /** * Expose `JSONCov`. @@ -49,7 +50,7 @@ function JSONCov(runner, output) { result.failures = failures.map(clean); result.passes = passes.map(clean); if (!output) return; - process.stdout.write(JSON.stringify(result, null, 2 )); + _output.write(JSON.stringify(result, null, 2 )); }); } diff --git a/lib/reporters/json-stream.js b/lib/reporters/json-stream.js index 7cb8fbede7..645eb70771 100644 --- a/lib/reporters/json-stream.js +++ b/lib/reporters/json-stream.js @@ -4,6 +4,7 @@ */ var Base = require('./base') + , output = Base.output , color = Base.color; /** @@ -27,19 +28,19 @@ function List(runner) { , total = runner.total; runner.on('start', function(){ - console.log(JSON.stringify(['start', { total: total }])); + output.log(JSON.stringify(['start', { total: total }])); }); runner.on('pass', function(test){ - console.log(JSON.stringify(['pass', clean(test)])); + output.log(JSON.stringify(['pass', clean(test)])); }); runner.on('fail', function(test, err){ - console.log(JSON.stringify(['fail', clean(test)])); + output.log(JSON.stringify(['fail', clean(test)])); }); runner.on('end', function(){ - process.stdout.write(JSON.stringify(['end', self.stats])); + output.write(JSON.stringify(['end', self.stats])); }); } @@ -58,4 +59,4 @@ function clean(test) { , fullTitle: test.fullTitle() , duration: test.duration } -} \ No newline at end of file +} diff --git a/lib/reporters/json.js b/lib/reporters/json.js index a699f5014a..1f1576d9de 100644 --- a/lib/reporters/json.js +++ b/lib/reporters/json.js @@ -4,6 +4,7 @@ */ var Base = require('./base') + , output = Base.output , cursor = Base.cursor , color = Base.color; @@ -48,7 +49,7 @@ function JSONReporter(runner) { , passes: passes.map(clean) }; - process.stdout.write(JSON.stringify(obj, null, 2)); + output.write(JSON.stringify(obj, null, 2)); }); } @@ -67,4 +68,4 @@ function clean(test) { , fullTitle: test.fullTitle() , duration: test.duration } -} \ No newline at end of file +} diff --git a/lib/reporters/landing.js b/lib/reporters/landing.js index bf064f64b2..90e47a1c97 100644 --- a/lib/reporters/landing.js +++ b/lib/reporters/landing.js @@ -4,6 +4,7 @@ */ var Base = require('./base') + , output = Base.output , cursor = Base.cursor , color = Base.color; @@ -45,7 +46,7 @@ function Landing(runner) { , stats = this.stats , width = Base.window.width * .75 | 0 , total = runner.total - , stream = process.stdout + , stream = output.stream , plane = color('plane', '✈') , crashed = -1 , n = 0; @@ -85,7 +86,7 @@ function Landing(runner) { runner.on('end', function(){ cursor.show(); - console.log(); + output.log(); self.epilogue(); }); } @@ -94,4 +95,4 @@ function Landing(runner) { * Inherit from `Base.prototype`. */ -Landing.prototype.__proto__ = Base.prototype; \ No newline at end of file +Landing.prototype.__proto__ = Base.prototype; diff --git a/lib/reporters/list.js b/lib/reporters/list.js index 3328e157a8..a5e5f936f9 100644 --- a/lib/reporters/list.js +++ b/lib/reporters/list.js @@ -4,6 +4,7 @@ */ var Base = require('./base') + , output = Base.output , cursor = Base.cursor , color = Base.color; @@ -28,17 +29,17 @@ function List(runner) { , n = 0; runner.on('start', function(){ - console.log(); + output.log(); }); runner.on('test', function(test){ - process.stdout.write(color('pass', ' ' + test.fullTitle() + ': ')); + output.write(color('pass', ' ' + test.fullTitle() + ': ')); }); runner.on('pending', function(test){ var fmt = color('checkmark', ' -') + color('pending', ' %s'); - console.log(fmt, test.fullTitle()); + output.log(fmt, test.fullTitle()); }); runner.on('pass', function(test){ @@ -46,12 +47,12 @@ function List(runner) { + color('pass', ' %s: ') + color(test.speed, '%dms'); cursor.CR(); - console.log(fmt, test.fullTitle(), test.duration); + output.log(fmt, test.fullTitle(), test.duration); }); runner.on('fail', function(test, err){ cursor.CR(); - console.log(color('fail', ' %d) %s'), ++n, test.fullTitle()); + output.log(color('fail', ' %d) %s'), ++n, test.fullTitle()); }); runner.on('end', self.epilogue.bind(self)); diff --git a/lib/reporters/markdown.js b/lib/reporters/markdown.js index 6383a64248..68304c3b2f 100644 --- a/lib/reporters/markdown.js +++ b/lib/reporters/markdown.js @@ -3,6 +3,7 @@ */ var Base = require('./base') + , output = Base.output , utils = require('../utils'); /** @@ -84,8 +85,8 @@ function Markdown(runner) { }); runner.on('end', function(){ - process.stdout.write('# TOC\n'); - process.stdout.write(generateTOC(runner.suite)); - process.stdout.write(buf); + output.write('# TOC\n'); + output.write(generateTOC(runner.suite)); + output.write(buf); }); -} \ No newline at end of file +} diff --git a/lib/reporters/min.js b/lib/reporters/min.js index 1b6117d065..d3152c5e76 100644 --- a/lib/reporters/min.js +++ b/lib/reporters/min.js @@ -3,7 +3,8 @@ * Module dependencies. */ -var Base = require('./base'); +var Base = require('./base') + , output = Base.output; /** * Expose `Min`. @@ -23,9 +24,9 @@ function Min(runner) { runner.on('start', function(){ // clear screen - process.stdout.write('\u001b[2J'); + output.write('\u001b[2J'); // set cursor position - process.stdout.write('\u001b[1;3H'); + output.write('\u001b[1;3H'); }); runner.on('end', this.epilogue.bind(this)); diff --git a/lib/reporters/nyan.js b/lib/reporters/nyan.js index b43e2440d0..9d7281c50b 100644 --- a/lib/reporters/nyan.js +++ b/lib/reporters/nyan.js @@ -3,6 +3,7 @@ */ var Base = require('./base') + , output = Base.output , color = Base.color; /** @@ -243,7 +244,7 @@ NyanCat.prototype.rainbowify = function(str){ */ function write(string) { - process.stdout.write(string); + output.write(string); } /** diff --git a/lib/reporters/progress.js b/lib/reporters/progress.js index 59536386cb..ff22151434 100644 --- a/lib/reporters/progress.js +++ b/lib/reporters/progress.js @@ -4,6 +4,7 @@ */ var Base = require('./base') + , output = Base.output , cursor = Base.cursor , color = Base.color; @@ -47,7 +48,7 @@ function Progress(runner, options) { // tests started runner.on('start', function(){ - console.log(); + output.log(); cursor.hide(); }); @@ -60,13 +61,13 @@ function Progress(runner, options) { , i = width - n; cursor.CR(); - process.stdout.write('\u001b[J'); - process.stdout.write(color('progress', ' ' + options.open)); - process.stdout.write(Array(n).join(options.complete)); - process.stdout.write(Array(i).join(options.incomplete)); - process.stdout.write(color('progress', options.close)); + output.write('\u001b[J'); + output.write(color('progress', ' ' + options.open)); + output.write(Array(n).join(options.complete)); + output.write(Array(i).join(options.incomplete)); + output.write(color('progress', options.close)); if (options.verbose) { - process.stdout.write(color('progress', ' ' + complete + ' of ' + total)); + output.write(color('progress', ' ' + complete + ' of ' + total)); } }); @@ -74,7 +75,7 @@ function Progress(runner, options) { // and the failures if any runner.on('end', function(){ cursor.show(); - console.log(); + output.log(); self.epilogue(); }); } diff --git a/lib/reporters/spec.js b/lib/reporters/spec.js index cd97261c12..4c4243cb23 100644 --- a/lib/reporters/spec.js +++ b/lib/reporters/spec.js @@ -4,6 +4,7 @@ */ var Base = require('./base') + , output = Base.output , cursor = Base.cursor , color = Base.color; @@ -33,26 +34,26 @@ function Spec(runner) { } runner.on('start', function(){ - console.log(); + output.log(); }); runner.on('suite', function(suite){ ++indents; - console.log(color('suite', '%s%s'), indent(), suite.title); + output.log(color('suite', '%s%s'), indent(), suite.title); }); runner.on('suite end', function(suite){ --indents; - if (1 == indents) console.log(); + if (1 == indents) output.log(); }); runner.on('test', function(test){ - process.stdout.write(indent() + color('pass', ' ◦ ' + test.title + ': ')); + output.write(indent() + color('pass', ' ◦ ' + test.title + ': ')); }); runner.on('pending', function(test){ var fmt = indent() + color('pending', ' - %s'); - console.log(fmt, test.title); + output.log(fmt, test.title); }); runner.on('pass', function(test){ @@ -61,20 +62,20 @@ function Spec(runner) { + color('checkmark', ' ' + Base.symbols.ok) + color('pass', ' %s '); cursor.CR(); - console.log(fmt, test.title); + output.log(fmt, test.title); } else { var fmt = indent() + color('checkmark', ' ' + Base.symbols.ok) + color('pass', ' %s ') + color(test.speed, '(%dms)'); cursor.CR(); - console.log(fmt, test.title, test.duration); + output.log(fmt, test.title, test.duration); } }); runner.on('fail', function(test, err){ cursor.CR(); - console.log(indent() + color('fail', ' %d) %s'), ++n, test.title); + output.log(indent() + color('fail', ' %d) %s'), ++n, test.title); }); runner.on('end', self.epilogue.bind(self)); diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index 2bcd995baa..d7c0920127 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -4,6 +4,7 @@ */ var Base = require('./base') + , output = Base.output , cursor = Base.cursor , color = Base.color; @@ -31,7 +32,7 @@ function TAP(runner) { runner.on('start', function(){ var total = runner.grepTotal(runner.suite); - console.log('%d..%d', 1, total); + output.log('%d..%d', 1, total); }); runner.on('test end', function(){ @@ -39,24 +40,24 @@ function TAP(runner) { }); runner.on('pending', function(test){ - console.log('ok %d %s # SKIP -', n, title(test)); + output.log('ok %d %s # SKIP -', n, title(test)); }); runner.on('pass', function(test){ passes++; - console.log('ok %d %s', n, title(test)); + output.log('ok %d %s', n, title(test)); }); runner.on('fail', function(test, err){ failures++; - console.log('not ok %d %s', n, title(test)); - if (err.stack) console.log(err.stack.replace(/^/gm, ' ')); + output.log('not ok %d %s', n, title(test)); + if (err.stack) output.log(err.stack.replace(/^/gm, ' ')); }); runner.on('end', function(){ - console.log('# tests ' + (passes + failures)); - console.log('# pass ' + passes); - console.log('# fail ' + failures); + output.log('# tests ' + (passes + failures)); + output.log('# pass ' + passes); + output.log('# fail ' + failures); }); } diff --git a/lib/reporters/teamcity.js b/lib/reporters/teamcity.js index 032eea7d2b..e92a2bb571 100644 --- a/lib/reporters/teamcity.js +++ b/lib/reporters/teamcity.js @@ -3,7 +3,8 @@ * Module dependencies. */ -var Base = require('./base'); +var Base = require('./base') + , output = Base.output; /** * Expose `Teamcity`. @@ -23,27 +24,27 @@ function Teamcity(runner) { var stats = this.stats; runner.on('start', function() { - console.log("##teamcity[testSuiteStarted name='mocha.suite']"); + output.log("##teamcity[testSuiteStarted name='mocha.suite']"); }); runner.on('test', function(test) { - console.log("##teamcity[testStarted name='" + escape(test.fullTitle()) + "']"); + output.log("##teamcity[testStarted name='" + escape(test.fullTitle()) + "']"); }); runner.on('fail', function(test, err) { - console.log("##teamcity[testFailed name='" + escape(test.fullTitle()) + "' message='" + escape(err.message) + "']"); + output.log("##teamcity[testFailed name='" + escape(test.fullTitle()) + "' message='" + escape(err.message) + "']"); }); runner.on('pending', function(test) { - console.log("##teamcity[testIgnored name='" + escape(test.fullTitle()) + "' message='pending']"); + output.log("##teamcity[testIgnored name='" + escape(test.fullTitle()) + "' message='pending']"); }); runner.on('test end', function(test) { - console.log("##teamcity[testFinished name='" + escape(test.fullTitle()) + "' duration='" + test.duration + "']"); + output.log("##teamcity[testFinished name='" + escape(test.fullTitle()) + "' duration='" + test.duration + "']"); }); runner.on('end', function() { - console.log("##teamcity[testSuiteFinished name='mocha.suite' duration='" + stats.duration + "']"); + output.log("##teamcity[testSuiteFinished name='mocha.suite' duration='" + stats.duration + "']"); }); } diff --git a/lib/reporters/xunit.js b/lib/reporters/xunit.js index 702aa3cee2..b9940acf5b 100644 --- a/lib/reporters/xunit.js +++ b/lib/reporters/xunit.js @@ -4,6 +4,7 @@ */ var Base = require('./base') + , output = Base.output , utils = require('../utils') , escape = utils.escape; @@ -45,7 +46,7 @@ function XUnit(runner) { }); runner.on('end', function(){ - console.log(tag('testsuite', { + output.log(tag('testsuite', { name: 'Mocha Tests' , tests: stats.tests , failures: stats.failures @@ -56,7 +57,7 @@ function XUnit(runner) { }, false)); tests.forEach(test); - console.log(''); + output.log(''); }); } @@ -80,11 +81,11 @@ function test(test) { if ('failed' == test.state) { var err = test.err; attrs.message = escape(err.message); - console.log(tag('testcase', attrs, false, tag('failure', attrs, false, cdata(err.stack)))); + output.log(tag('testcase', attrs, false, tag('failure', attrs, false, cdata(err.stack)))); } else if (test.pending) { - console.log(tag('testcase', attrs, false, tag('skipped', {}, true))); + output.log(tag('testcase', attrs, false, tag('skipped', {}, true))); } else { - console.log(tag('testcase', attrs, true) ); + output.log(tag('testcase', attrs, true) ); } } diff --git a/mocha.js b/mocha.js index 2b35a88302..ee972135d6 100644 --- a/mocha.js +++ b/mocha.js @@ -672,8 +672,14 @@ exports.isatty = function(){ }; exports.getWindowSize = function(){ - return [window.innerHeight, window.innerWidth]; + if ('innerHeight' in global) { + return [global.innerHeight, global.innerWidth]; + } else { + // In a Web Worker, the DOM Window is not available. + return [640, 480]; + } }; + }); // module: browser/tty.js require.register("context.js", function(module, exports, require){ @@ -1225,6 +1231,17 @@ module.exports = function(suite){ return suite; }; + /** + * Pending suite. + */ + context.suite.skip = function(title, fn) { + var suite = Suite.create(suites[0], title); + suite.pending = true; + suites.unshift(suite); + fn.call(suite); + suites.shift(); + }; + /** * Exclusive test-case. */ @@ -1241,8 +1258,10 @@ module.exports = function(suite){ */ context.test = function(title, fn){ + var suite = suites[0]; + if (suite.pending) var fn = null; var test = new Test(title, fn); - suites[0].addTest(test); + suite.addTest(test); return test; }; @@ -1297,6 +1316,7 @@ exports.reporters = require('./reporters'); exports.Runnable = require('./runnable'); exports.Context = require('./context'); exports.Runner = require('./runner'); +exports.output = require('./output'); exports.Suite = require('./suite'); exports.Hook = require('./hook'); exports.Test = require('./test'); @@ -1340,6 +1360,7 @@ function Mocha(options) { this.ui(options.ui); this.bail(options.bail); this.reporter(options.reporter); + this.output(options.output); if (options.timeout) this.timeout(options.timeout); if (options.slow) this.slow(options.slow); } @@ -1391,6 +1412,19 @@ Mocha.prototype.reporter = function(reporter){ return this; }; +/** + * Set output to `output`, defaults to "-". + * + * @param {String} target file name + * @api public + */ + +Mocha.prototype.output = function(target) { + if (target !== undefined) { + exports.output.initialize(target); + } +}; + /** * Set test UI `name`, defaults to "bdd". * @@ -1582,7 +1616,10 @@ Mocha.prototype.run = function(fn){ if (options.grep) runner.grep(options.grep, options.invert); if (options.globals) runner.globals(options.globals); if (options.growl) this._growl(runner, reporter); - return runner.run(fn); + var self = this; + return runner.run(function(errCount) { + exports.output.end(errCount, fn); + }); }; }); // module: mocha.js @@ -1671,6 +1708,74 @@ function format(ms) { } }); // module: ms.js +require.register("output.js", function(module, exports, require){ +var debug = require('browser/debug')('mocha:output'); +var fs = require('browser/fs'); + +// Initialize to be in stdout mode +var stream = process.stdout; +var _console = console; + +/** + * Write preformatted output directly to the stream. + */ +var write = exports.write = function(data) { + stream.write(data); +}; + + +/** + * Wrapper for console.log output. + */ +var log = exports.log = function() { + _console.log.apply(_console, arguments); +}; + +/** + * Wrapper for console.error output. + */ +var error = exports.error = function() { + _console.error.apply(_console, arguments); +}; + +/** + * Hook that's called when the test process is done to close the + * output stream. + */ +var end = exports.end = function(errCount, fn) { + if (stream === process.stdout) { + // nothing to do + return fn(errCount); + } + + stream.end(function() { + debug("stream ended"); + fn(errCount); + + // Reset the stream + stream = process.stdout; + } ); +}; + +/** + * Initialize the output layer. + * + * If target is undefined or "-" then use stdout, otherwise open the + * target file. + */ +var initialize = exports.initialize = function(target) { + if (target === undefined || target === "-") { + return; // nothing to do - stay in stdout mode + } + + stream = fs.createWriteStream(target); + _console = new console.Console(stream); +}; + + + +}); // module: output.js + require.register("reporters/base.js", function(module, exports, require){ /** @@ -1679,7 +1784,8 @@ require.register("reporters/base.js", function(module, exports, require){ var tty = require('browser/tty') , diff = require('browser/diff') - , ms = require('../ms'); + , ms = require('../ms') + , output = require('../output'); /** * Save timer references to avoid Sinon interfering (see GH-237). @@ -1703,6 +1809,12 @@ var isatty = tty.isatty(1) && tty.isatty(2); exports = module.exports = Base; +/** + * Expose the output hooks + */ +exports.output = output; + + /** * Enable coloring by default. */ @@ -1782,6 +1894,7 @@ exports.window = { : 75 }; + /** * Expose some basic cursor interactions * that are common among reporters. @@ -1818,7 +1931,7 @@ exports.cursor = { */ exports.list = function(failures){ - console.error(); + output.error(); failures.forEach(function(test, i){ // format var fmt = color('error title', ' %s) %s:\n') @@ -1835,6 +1948,11 @@ exports.list = function(failures){ , expected = err.expected , escape = true; + // uncaught + if (err.uncaught) { + msg = 'Uncaught ' + msg; + } + // explicitly show diff if (err.showDiff) { escape = false; @@ -1875,7 +1993,7 @@ exports.list = function(failures){ stack = stack.slice(index ? index + 1 : index) .replace(/^/gm, ' '); - console.error(fmt, (i + 1), test.fullTitle(), msg, stack); + output.error(fmt, (i + 1), test.fullTitle(), msg, stack); }); }; @@ -1953,51 +2071,41 @@ function Base(runner) { */ Base.prototype.epilogue = function(){ - var stats = this.stats - , fmt - , tests; - - console.log(); - - function pluralize(n) { - return 1 == n ? 'test' : 'tests'; - } - - // failure - if (stats.failures) { - fmt = color('bright fail', ' ' + exports.symbols.err) - + color('fail', ' %d of %d %s failed') - + color('light', ':') + var stats = this.stats; + var tests; + var fmt; - console.error(fmt, - stats.failures, - this.runner.total, - pluralize(this.runner.total)); + output.log(); - Base.list(this.failures); - console.error(); - return; - } - - // pass + // passes fmt = color('bright pass', ' ') - + color('green', ' %d %s complete') + + color('green', ' %d passing') + color('light', ' (%s)'); - console.log(fmt, - stats.tests || 0, - pluralize(stats.tests), + output.log(fmt, + stats.passes || 0, ms(stats.duration)); // pending if (stats.pending) { fmt = color('pending', ' ') - + color('pending', ' %d %s pending'); + + color('pending', ' %d pending'); + + output.log(fmt, stats.pending); + } - console.log(fmt, stats.pending, pluralize(stats.pending)); + // failures + if (stats.failures) { + fmt = color('fail', ' %d failing'); + + output.error(fmt, + stats.failures); + + Base.list(this.failures); + output.error(); } - console.log(); + output.log(); }; /** @@ -2120,6 +2228,7 @@ require.register("reporters/dot.js", function(module, exports, require){ */ var Base = require('./base') + , output = Base.output , color = Base.color; /** @@ -2144,29 +2253,29 @@ function Dot(runner) { , n = 0; runner.on('start', function(){ - process.stdout.write('\n '); + output.write('\n '); }); runner.on('pending', function(test){ - process.stdout.write(color('pending', Base.symbols.dot)); + output.write(color('pending', Base.symbols.dot)); }); runner.on('pass', function(test){ - if (++n % width == 0) process.stdout.write('\n '); + if (++n % width == 0) output.write('\n '); if ('slow' == test.speed) { - process.stdout.write(color('bright yellow', Base.symbols.dot)); + output.write(color('bright yellow', Base.symbols.dot)); } else { - process.stdout.write(color(test.speed, Base.symbols.dot)); + output.write(color(test.speed, Base.symbols.dot)); } }); runner.on('fail', function(test, err){ - if (++n % width == 0) process.stdout.write('\n '); - process.stdout.write(color('fail', Base.symbols.dot)); + if (++n % width == 0) output.write('\n '); + output.write(color('fail', Base.symbols.dot)); }); runner.on('end', function(){ - console.log(); + output.writeln(); self.epilogue(); }); } @@ -2180,6 +2289,7 @@ F.prototype = Base.prototype; Dot.prototype = new F; Dot.prototype.constructor = Dot; + }); // module: reporters/dot.js require.register("reporters/html-cov.js", function(module, exports, require){ @@ -3729,6 +3839,7 @@ require.register("reporters/xunit.js", function(module, exports, require){ */ var Base = require('./base') + , output = Base.output , utils = require('../utils') , escape = utils.escape; @@ -3770,7 +3881,7 @@ function XUnit(runner) { }); runner.on('end', function(){ - console.log(tag('testsuite', { + output.writeln(tag('testsuite', { name: 'Mocha Tests' , tests: stats.tests , failures: stats.failures @@ -3781,7 +3892,7 @@ function XUnit(runner) { }, false)); tests.forEach(test); - console.log(''); + output.writeln(''); }); } @@ -3809,11 +3920,11 @@ function test(test) { if ('failed' == test.state) { var err = test.err; attrs.message = escape(err.message); - console.log(tag('testcase', attrs, false, tag('failure', attrs, false, cdata(err.stack)))); + output.writeln(tag('testcase', attrs, false, tag('failure', attrs, false, cdata(err.stack)))); } else if (test.pending) { - console.log(tag('testcase', attrs, false, tag('skipped', {}, true))); + output.writeln(tag('testcase', attrs, false, tag('skipped', {}, true))); } else { - console.log(tag('testcase', attrs, true) ); + output.writeln(tag('testcase', attrs, true) ); } } @@ -3983,16 +4094,14 @@ Runnable.prototype.inspect = function(){ */ Runnable.prototype.resetTimeout = function(){ - var self = this - , ms = this.timeout(); + var self = this; + var ms = this.timeout() || 1e9; this.clearTimeout(); - if (ms) { - this.timer = setTimeout(function(){ - self.callback(new Error('timeout of ' + ms + 'ms exceeded')); - self.timedOut = true; - }, ms); - } + this.timer = setTimeout(function(){ + self.callback(new Error('timeout of ' + ms + 'ms exceeded')); + self.timedOut = true; + }, ms); }; /** @@ -4073,7 +4182,6 @@ Runnable.prototype.run = function(fn){ }); // module: runnable.js require.register("runner.js", function(module, exports, require){ - /** * Module dependencies. */ @@ -4119,6 +4227,7 @@ module.exports = Runner; * - `hook end` (hook) hook complete * - `pass` (test) test passed * - `fail` (test, err) test failed + * - `pending` (test) test pending * * @api public */ @@ -4311,6 +4420,7 @@ Runner.prototype.hook = function(name, fn){ function next(i) { var hook = hooks[i]; if (!hook) return fn(); + if (self.failures && suite.bail()) return fn(); self.currentRunnable = hook; self.emit('hook', hook); @@ -5252,16 +5362,18 @@ exports.highlightTags = function(name) { }; }); // module: utils.js +// The global object is "self" in Web Workers. +global = (function() { return this; })(); /** * Save timer references to avoid Sinon interfering (see GH-237). */ -var Date = window.Date; -var setTimeout = window.setTimeout; -var setInterval = window.setInterval; -var clearTimeout = window.clearTimeout; -var clearInterval = window.clearInterval; +var Date = global.Date; +var setTimeout = global.setTimeout; +var setInterval = global.setInterval; +var clearTimeout = global.clearTimeout; +var clearInterval = global.clearInterval; /** * Node shims. @@ -5275,7 +5387,6 @@ var clearInterval = window.clearInterval; var process = {}; process.exit = function(status){}; process.stdout = {}; -global = window; /** * Remove uncaughtException listener. @@ -5283,7 +5394,7 @@ global = window; process.removeListener = function(e){ if ('uncaughtException' == e) { - window.onerror = null; + global.onerror = function() {}; } }; @@ -5293,7 +5404,7 @@ process.removeListener = function(e){ process.on = function(e, fn){ if ('uncaughtException' == e) { - window.onerror = function(err, url, line){ + global.onerror = function(err, url, line){ fn(new Error(err + ' (' + url + ':' + line + ')')); }; } @@ -5303,8 +5414,8 @@ process.on = function(e, fn){ * Expose mocha. */ -var Mocha = window.Mocha = require('mocha'), - mocha = window.mocha = new Mocha({ reporter: 'html' }); +var Mocha = global.Mocha = require('mocha'), + mocha = global.mocha = new Mocha({ reporter: 'html' }); var immediateQueue = [] , immediateTimeout; @@ -5339,7 +5450,7 @@ Mocha.Runner.immediately = function(callback) { mocha.ui = function(ui){ Mocha.prototype.ui.call(this, ui); - this.suite.emit('pre-require', window, null, this); + this.suite.emit('pre-require', global, null, this); return this; }; @@ -5361,12 +5472,15 @@ mocha.run = function(fn){ var options = mocha.options; mocha.globals('location'); - var query = Mocha.utils.parseQuery(window.location.search || ''); + var query = Mocha.utils.parseQuery(global.location.search || ''); if (query.grep) mocha.grep(query.grep); if (query.invert) mocha.invert(); return Mocha.prototype.run.call(mocha, function(){ - Mocha.utils.highlightTags('code'); + // The DOM Document is not available in Web Workers. + if (global.document) { + Mocha.utils.highlightTags('code'); + } if (fn) fn(); }); }; From 45ece9962ff0c38883f016306e540133ca116b0e Mon Sep 17 00:00:00 2001 From: Michael Demmer Date: Thu, 13 Jun 2013 13:04:48 -0700 Subject: [PATCH 2/3] Rework implementation to work on older versions of node.js. The previous implementation used a non-standard console.Console object that didn't work on older versions of node.js. Rework it to explicitly check whether to call console.{log,error} directly or to use util.format before calling into the file stream. --- lib/output.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/output.js b/lib/output.js index 797dffc251..35f596fe03 100644 --- a/lib/output.js +++ b/lib/output.js @@ -1,9 +1,9 @@ var debug = require('debug')('mocha:output'); +var util = require('util'); var fs = require('fs'); // Initialize to be in stdout mode var stream = exports.stream = process.stdout; -var _console = console; /** * Write preformatted output directly to the stream. @@ -14,17 +14,25 @@ var write = exports.write = function(data) { /** - * Wrapper for console.log output. + * Wrapper for log output. */ var log = exports.log = function() { - _console.log.apply(_console, arguments); + if (stream === process.stdout) { + console.log.apply(console, arguments); + } else { + stream.write(util.format.apply(util, arguments) + "\n"); + } }; /** - * Wrapper for console.error output. + * Wrapper for error output. */ var error = exports.error = function() { - _console.error.apply(_console, arguments); + if (stream === process.stdout) { + console.error.apply(console, arguments); + } else { + stream.write(util.format.apply(util, arguments) + "\n"); + } }; /** @@ -58,7 +66,6 @@ var initialize = exports.initialize = function(target) { } stream = exports.stream = fs.createWriteStream(target); - _console = new console.Console(stream); }; From aa71b6c872029116781f7690971d05539d6dc340 Mon Sep 17 00:00:00 2001 From: Michael Demmer Date: Mon, 1 Jul 2013 14:38:30 -0700 Subject: [PATCH 3/3] Tweak file output implementation based on pull request comments. Pass the output stream to each of the reporters in the constructor to make integration simpler for reporters defined outside of the mocha source tree and to simplify the changes. Also add support to pass an arbitrary stream to output.initialize and rename the 'stream' variable to 'ostream' so it doesn't clash with the stream module. --- lib/mocha.js | 10 +++---- lib/output.js | 53 ++++++++++++++++++++---------------- lib/reporters/base.js | 5 +++- lib/reporters/doc.js | 4 +-- lib/reporters/dot.js | 21 +++++++------- lib/reporters/html-cov.js | 7 ++--- lib/reporters/html.js | 4 +-- lib/reporters/json-cov.js | 11 ++++---- lib/reporters/json-stream.js | 13 ++++----- lib/reporters/json.js | 7 ++--- lib/reporters/landing.js | 26 ++++++++---------- lib/reporters/list.js | 4 +-- lib/reporters/markdown.js | 11 ++++---- lib/reporters/min.js | 11 ++++---- lib/reporters/nyan.js | 6 ++-- lib/reporters/progress.js | 21 +++++++------- lib/reporters/spec.js | 4 +-- lib/reporters/tap.js | 4 +-- lib/reporters/teamcity.js | 4 +-- lib/reporters/xunit.js | 4 +-- 20 files changed, 114 insertions(+), 116 deletions(-) diff --git a/lib/mocha.js b/lib/mocha.js index 4fa35085bf..e52fde65df 100644 --- a/lib/mocha.js +++ b/lib/mocha.js @@ -124,16 +124,14 @@ Mocha.prototype.reporter = function(reporter){ }; /** - * Set output to `output`, defaults to "-". + * Set output to `output`. * - * @param {String} target file name + * @param {stream|String} target output stream, filename, or "-" for stdout * @api public */ Mocha.prototype.output = function(target) { - if (target !== undefined) { - exports.output.initialize(target); - } + this._output = exports.output.initialize(target); }; /** @@ -321,7 +319,7 @@ Mocha.prototype.run = function(fn){ var suite = this.suite; var options = this.options; var runner = new exports.Runner(suite); - var reporter = new this._reporter(runner); + var reporter = new this._reporter(runner, this._output); runner.ignoreLeaks = false !== options.ignoreLeaks; runner.asyncOnly = options.asyncOnly; if (options.grep) runner.grep(options.grep, options.invert); diff --git a/lib/output.js b/lib/output.js index 35f596fe03..a6eb370d46 100644 --- a/lib/output.js +++ b/lib/output.js @@ -1,71 +1,78 @@ var debug = require('debug')('mocha:output'); var util = require('util'); var fs = require('fs'); +var stream = require('stream'); -// Initialize to be in stdout mode -var stream = exports.stream = process.stdout; +// Initialize in stdout mode +var ostream = exports.ostream = process.stdout; /** * Write preformatted output directly to the stream. */ -var write = exports.write = function(data) { - stream.write(data); +exports.write = function(data) { + ostream.write(data); }; /** * Wrapper for log output. */ -var log = exports.log = function() { - if (stream === process.stdout) { +exports.log = function() { + if (ostream === process.stdout) { console.log.apply(console, arguments); } else { - stream.write(util.format.apply(util, arguments) + "\n"); + ostream.write(util.format.apply(util, arguments) + "\n"); } }; /** * Wrapper for error output. */ -var error = exports.error = function() { - if (stream === process.stdout) { +exports.error = function() { + if (ostream === process.stdout) { console.error.apply(console, arguments); } else { - stream.write(util.format.apply(util, arguments) + "\n"); + ostream.write(util.format.apply(util, arguments) + "\n"); } }; /** * Hook that's called when the test process is done to close the - * output stream. + * ostream ostream. */ -var end = exports.end = function(errCount, fn) { - if (stream === process.stdout) { +exports.end = function(errCount, fn) { + if (ostream === process.stdout) { // nothing to do return fn(errCount); } - stream.end(function() { - debug("stream ended"); + ostream.end(function() { + debug("ostream ended"); fn(errCount); - // Reset the stream - stream = process.stdout; + // Reset the ostream + ostream = process.stdout; } ); }; /** - * Initialize the output layer. + * Initialize the output module. * * If target is undefined or "-" then use stdout, otherwise open the * target file. */ var initialize = exports.initialize = function(target) { - if (target === undefined || target === "-") { - return; // nothing to do - stay in stdout mode + if (target === undefined) { + return ostream; // don't change } - stream = exports.stream = fs.createWriteStream(target); -}; - + if (target === "-") { + ostream = exports.ostream = process.stdout; + } else if (target instanceof stream) { + ostream = exports.ostream = target; + } else { + ostream = exports.ostream = fs.createWriteStream(target); + } + return ostream; +}; diff --git a/lib/reporters/base.js b/lib/reporters/base.js index 9657b2d747..1265bff7fa 100644 --- a/lib/reporters/base.js +++ b/lib/reporters/base.js @@ -230,7 +230,7 @@ exports.list = function(failures){ * @api public */ -function Base(runner) { +function Base(runner, ostream) { var self = this , stats = this.stats = { suites: 0, tests: 0, passes: 0, pending: 0, failures: 0 } , failures = this.failures = []; @@ -238,6 +238,9 @@ function Base(runner) { if (!runner) return; this.runner = runner; + if (!ostream) return; + this.ostream = ostream; + runner.stats = stats; runner.on('start', function(){ diff --git a/lib/reporters/doc.js b/lib/reporters/doc.js index 48986d464f..11e06a4311 100644 --- a/lib/reporters/doc.js +++ b/lib/reporters/doc.js @@ -20,8 +20,8 @@ exports = module.exports = Doc; * @api public */ -function Doc(runner) { - Base.call(this, runner); +function Doc(runner, ostream) { + Base.call(this, runner, ostream); var self = this , stats = this.stats diff --git a/lib/reporters/dot.js b/lib/reporters/dot.js index 67c98c2984..86d031ef5f 100644 --- a/lib/reporters/dot.js +++ b/lib/reporters/dot.js @@ -4,7 +4,6 @@ */ var Base = require('./base') - , output = Base.output , color = Base.color; /** @@ -20,8 +19,8 @@ exports = module.exports = Dot; * @api public */ -function Dot(runner) { - Base.call(this, runner); +function Dot(runner, ostream) { + Base.call(this, runner, ostream); var self = this , stats = this.stats @@ -29,29 +28,29 @@ function Dot(runner) { , n = 0; runner.on('start', function(){ - output.write('\n '); + ostream.write('\n '); }); runner.on('pending', function(test){ - output.write(color('pending', Base.symbols.dot)); + ostream.write(color('pending', Base.symbols.dot)); }); runner.on('pass', function(test){ - if (++n % width == 0) output.write('\n '); + if (++n % width == 0) ostream.write('\n '); if ('slow' == test.speed) { - output.write(color('bright yellow', Base.symbols.dot)); + ostream.write(color('bright yellow', Base.symbols.dot)); } else { - output.write(color(test.speed, Base.symbols.dot)); + ostream.write(color(test.speed, Base.symbols.dot)); } }); runner.on('fail', function(test, err){ - if (++n % width == 0) output.write('\n '); - output.write(color('fail', Base.symbols.dot)); + if (++n % width == 0) ostream.write('\n '); + ostream.write(color('fail', Base.symbols.dot)); }); runner.on('end', function(){ - output.log(); + ostream.write("\n"); self.epilogue(); }); } diff --git a/lib/reporters/html-cov.js b/lib/reporters/html-cov.js index 8083f07244..dc5a67d7e9 100644 --- a/lib/reporters/html-cov.js +++ b/lib/reporters/html-cov.js @@ -4,7 +4,6 @@ */ var JSONCov = require('./json-cov') - , output = require('../output') , fs = require('fs'); /** @@ -20,17 +19,17 @@ exports = module.exports = HTMLCov; * @api public */ -function HTMLCov(runner) { +function HTMLCov(runner, ostream) { var jade = require('jade') , file = __dirname + '/templates/coverage.jade' , str = fs.readFileSync(file, 'utf8') , fn = jade.compile(str, { filename: file }) , self = this; - JSONCov.call(this, runner, false); + JSONCov.call(this, runner, ostream, false); runner.on('end', function(){ - output.write(fn({ + ostream.write(fn({ cov: self.cov , coverageClass: coverageClass })); diff --git a/lib/reporters/html.js b/lib/reporters/html.js index f09279be20..ce679fc01c 100644 --- a/lib/reporters/html.js +++ b/lib/reporters/html.js @@ -42,8 +42,8 @@ var statsTemplate = '
    ' * @api public */ -function HTML(runner, root) { - Base.call(this, runner); +function HTML(runner, ostream, root) { + Base.call(this, ostream, runner); var self = this , stats = this.stats diff --git a/lib/reporters/json-cov.js b/lib/reporters/json-cov.js index 7cac21560a..77ec2b273b 100644 --- a/lib/reporters/json-cov.js +++ b/lib/reporters/json-cov.js @@ -3,8 +3,7 @@ * Module dependencies. */ -var Base = require('./base') - , _output = Base.output; +var Base = require('./base'); /** * Expose `JSONCov`. @@ -20,11 +19,11 @@ exports = module.exports = JSONCov; * @api public */ -function JSONCov(runner, output) { +function JSONCov(runner, ostream, output) { var self = this - , output = 1 == arguments.length ? true : output; + , output = 2 == arguments.length ? true : output; - Base.call(this, runner); + Base.call(this, runner, ostream); var tests = [] , failures = [] @@ -50,7 +49,7 @@ function JSONCov(runner, output) { result.failures = failures.map(clean); result.passes = passes.map(clean); if (!output) return; - _output.write(JSON.stringify(result, null, 2 )); + ostream.write(JSON.stringify(result, null, 2 )); }); } diff --git a/lib/reporters/json-stream.js b/lib/reporters/json-stream.js index 645eb70771..fe18eefe78 100644 --- a/lib/reporters/json-stream.js +++ b/lib/reporters/json-stream.js @@ -4,7 +4,6 @@ */ var Base = require('./base') - , output = Base.output , color = Base.color; /** @@ -20,27 +19,27 @@ exports = module.exports = List; * @api public */ -function List(runner) { - Base.call(this, runner); +function List(runner, ostream) { + Base.call(this, runner, ostream); var self = this , stats = this.stats , total = runner.total; runner.on('start', function(){ - output.log(JSON.stringify(['start', { total: total }])); + ostream.write(JSON.stringify(['start', { total: total }]) + "\n"); }); runner.on('pass', function(test){ - output.log(JSON.stringify(['pass', clean(test)])); + ostream.write(JSON.stringify(['pass', clean(test)]) + "\n"); }); runner.on('fail', function(test, err){ - output.log(JSON.stringify(['fail', clean(test)])); + ostream.write(JSON.stringify(['fail', clean(test)]) + "\n"); }); runner.on('end', function(){ - output.write(JSON.stringify(['end', self.stats])); + ostream.write(JSON.stringify(['end', self.stats]) + "\n"); }); } diff --git a/lib/reporters/json.js b/lib/reporters/json.js index 1f1576d9de..674440bf25 100644 --- a/lib/reporters/json.js +++ b/lib/reporters/json.js @@ -4,7 +4,6 @@ */ var Base = require('./base') - , output = Base.output , cursor = Base.cursor , color = Base.color; @@ -21,9 +20,9 @@ exports = module.exports = JSONReporter; * @api public */ -function JSONReporter(runner) { +function JSONReporter(runner, ostream) { var self = this; - Base.call(this, runner); + Base.call(this, runner, ostream); var tests = [] , failures = [] @@ -49,7 +48,7 @@ function JSONReporter(runner) { , passes: passes.map(clean) }; - output.write(JSON.stringify(obj, null, 2)); + ostream.write(JSON.stringify(obj, null, 2)); }); } diff --git a/lib/reporters/landing.js b/lib/reporters/landing.js index 90e47a1c97..8ece1cd2a4 100644 --- a/lib/reporters/landing.js +++ b/lib/reporters/landing.js @@ -4,7 +4,6 @@ */ var Base = require('./base') - , output = Base.output , cursor = Base.cursor , color = Base.color; @@ -39,14 +38,13 @@ Base.colors.runway = 90; * @api public */ -function Landing(runner) { - Base.call(this, runner); +function Landing(runner, ostream) { + Base.call(this, runner, ostream); var self = this , stats = this.stats , width = Base.window.width * .75 | 0 , total = runner.total - , stream = output.stream , plane = color('plane', '✈') , crashed = -1 , n = 0; @@ -57,7 +55,7 @@ function Landing(runner) { } runner.on('start', function(){ - stream.write('\n '); + ostream.write('\n '); cursor.hide(); }); @@ -74,19 +72,19 @@ function Landing(runner) { } // render landing strip - stream.write('\u001b[4F\n\n'); - stream.write(runway()); - stream.write('\n '); - stream.write(color('runway', Array(col).join('⋅'))); - stream.write(plane) - stream.write(color('runway', Array(width - col).join('⋅') + '\n')); - stream.write(runway()); - stream.write('\u001b[0m'); + ostream.write('\u001b[4F\n\n'); + ostream.write(runway()); + ostream.write('\n '); + ostream.write(color('runway', Array(col).join('⋅'))); + ostream.write(plane); + ostream.write(color('runway', Array(width - col).join('⋅') + '\n')); + ostream.write(runway()); + ostream.write('\u001b[0m'); }); runner.on('end', function(){ cursor.show(); - output.log(); + ostream.write("\n"); self.epilogue(); }); } diff --git a/lib/reporters/list.js b/lib/reporters/list.js index a5e5f936f9..dcae654a33 100644 --- a/lib/reporters/list.js +++ b/lib/reporters/list.js @@ -21,8 +21,8 @@ exports = module.exports = List; * @api public */ -function List(runner) { - Base.call(this, runner); +function List(runner, ostream) { + Base.call(this, runner, ostream); var self = this , stats = this.stats diff --git a/lib/reporters/markdown.js b/lib/reporters/markdown.js index 68304c3b2f..70f20e26ac 100644 --- a/lib/reporters/markdown.js +++ b/lib/reporters/markdown.js @@ -3,7 +3,6 @@ */ var Base = require('./base') - , output = Base.output , utils = require('../utils'); /** @@ -19,8 +18,8 @@ exports = module.exports = Markdown; * @api public */ -function Markdown(runner) { - Base.call(this, runner); +function Markdown(runner, ostream) { + Base.call(this, runner, ostream); var self = this , stats = this.stats @@ -85,8 +84,8 @@ function Markdown(runner) { }); runner.on('end', function(){ - output.write('# TOC\n'); - output.write(generateTOC(runner.suite)); - output.write(buf); + ostream.write('# TOC\n'); + ostream.write(generateTOC(runner.suite)); + ostream.write(buf); }); } diff --git a/lib/reporters/min.js b/lib/reporters/min.js index d3152c5e76..c495fc0fce 100644 --- a/lib/reporters/min.js +++ b/lib/reporters/min.js @@ -3,8 +3,7 @@ * Module dependencies. */ -var Base = require('./base') - , output = Base.output; +var Base = require('./base'); /** * Expose `Min`. @@ -19,14 +18,14 @@ exports = module.exports = Min; * @api public */ -function Min(runner) { - Base.call(this, runner); +function Min(runner, ostream) { + Base.call(this, runner, ostream); runner.on('start', function(){ // clear screen - output.write('\u001b[2J'); + ostream.write('\u001b[2J'); // set cursor position - output.write('\u001b[1;3H'); + ostream.write('\u001b[1;3H'); }); runner.on('end', this.epilogue.bind(this)); diff --git a/lib/reporters/nyan.js b/lib/reporters/nyan.js index 9d7281c50b..099329e409 100644 --- a/lib/reporters/nyan.js +++ b/lib/reporters/nyan.js @@ -19,8 +19,8 @@ exports = module.exports = NyanCat; * @api public */ -function NyanCat(runner) { - Base.call(this, runner); +function NyanCat(runner, ostream) { + Base.call(this, runner, ostream); var self = this , stats = this.stats @@ -240,7 +240,7 @@ NyanCat.prototype.rainbowify = function(str){ }; /** - * Stdout helper. + * Output helper. */ function write(string) { diff --git a/lib/reporters/progress.js b/lib/reporters/progress.js index ff22151434..19b40549f2 100644 --- a/lib/reporters/progress.js +++ b/lib/reporters/progress.js @@ -4,7 +4,6 @@ */ var Base = require('./base') - , output = Base.output , cursor = Base.cursor , color = Base.color; @@ -28,8 +27,8 @@ Base.colors.progress = 90; * @api public */ -function Progress(runner, options) { - Base.call(this, runner); +function Progress(runner, ostream, options) { + Base.call(this, runner, ostream); var self = this , options = options || {} @@ -48,7 +47,7 @@ function Progress(runner, options) { // tests started runner.on('start', function(){ - output.log(); + ostream.write("\n"); cursor.hide(); }); @@ -61,13 +60,13 @@ function Progress(runner, options) { , i = width - n; cursor.CR(); - output.write('\u001b[J'); - output.write(color('progress', ' ' + options.open)); - output.write(Array(n).join(options.complete)); - output.write(Array(i).join(options.incomplete)); - output.write(color('progress', options.close)); + ostream.write('\u001b[J'); + ostream.write(color('progress', ' ' + options.open)); + ostream.write(Array(n).join(options.complete)); + ostream.write(Array(i).join(options.incomplete)); + ostream.write(color('progress', options.close)); if (options.verbose) { - output.write(color('progress', ' ' + complete + ' of ' + total)); + ostream.write(color('progress', ' ' + complete + ' of ' + total)); } }); @@ -75,7 +74,7 @@ function Progress(runner, options) { // and the failures if any runner.on('end', function(){ cursor.show(); - output.log(); + ostream.write("\n"); self.epilogue(); }); } diff --git a/lib/reporters/spec.js b/lib/reporters/spec.js index 4c4243cb23..18d491fe26 100644 --- a/lib/reporters/spec.js +++ b/lib/reporters/spec.js @@ -21,8 +21,8 @@ exports = module.exports = Spec; * @api public */ -function Spec(runner) { - Base.call(this, runner); +function Spec(runner, ostream) { + Base.call(this, runner, ostream); var self = this , stats = this.stats diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index d7c0920127..b2043a24ff 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -21,8 +21,8 @@ exports = module.exports = TAP; * @api public */ -function TAP(runner) { - Base.call(this, runner); +function TAP(runner, ostream) { + Base.call(this, runner, ostream); var self = this , stats = this.stats diff --git a/lib/reporters/teamcity.js b/lib/reporters/teamcity.js index e92a2bb571..1572e3efa5 100644 --- a/lib/reporters/teamcity.js +++ b/lib/reporters/teamcity.js @@ -19,8 +19,8 @@ exports = module.exports = Teamcity; * @api public */ -function Teamcity(runner) { - Base.call(this, runner); +function Teamcity(runner, ostream) { + Base.call(this, runner, ostream); var stats = this.stats; runner.on('start', function() { diff --git a/lib/reporters/xunit.js b/lib/reporters/xunit.js index b9940acf5b..6871e03e09 100644 --- a/lib/reporters/xunit.js +++ b/lib/reporters/xunit.js @@ -31,8 +31,8 @@ exports = module.exports = XUnit; * @api public */ -function XUnit(runner) { - Base.call(this, runner); +function XUnit(runner, ostream) { + Base.call(this, runner, ostream); var stats = this.stats , tests = [] , self = this;