Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a feature to optionally direct the test output to a file. #897

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 \
Expand Down
5 changes: 5 additions & 0 deletions bin/_mocha
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ program
.usage('[debug] [options] [files]')
.option('-r, --require <name>', 'require the given module')
.option('-R, --reporter <name>', 'specify the reporter to use', 'dot')
.option('-O, --output <file>', 'specify the output file', '-')
.option('-u, --ui <name>', 'specify user-interface (bdd|tdd|exports)', 'bdd')
.option('-g, --grep <pattern>', 'only run tests matching <pattern>')
.option('-i, --invert', 'inverts --grep matches')
Expand Down Expand Up @@ -177,6 +178,10 @@ Error.stackTraceLimit = Infinity; // TODO: config

mocha.reporter(program.reporter);

// output

mocha.output(program.output);

// interface

mocha.ui(program.ui);
Expand Down
20 changes: 18 additions & 2 deletions lib/mocha.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -121,6 +123,17 @@ Mocha.prototype.reporter = function(reporter){
return this;
};

/**
* Set output to `output`.
*
* @param {stream|String} target output stream, filename, or "-" for stdout
* @api public
*/

Mocha.prototype.output = function(target) {
this._output = exports.output.initialize(target);
};

/**
* Set test UI `name`, defaults to "bdd".
*
Expand Down Expand Up @@ -306,11 +319,14 @@ 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);
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);
});
};
78 changes: 78 additions & 0 deletions lib/output.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
var debug = require('debug')('mocha:output');
var util = require('util');
var fs = require('fs');
var stream = require('stream');

// Initialize in stdout mode
var ostream = exports.ostream = process.stdout;

/**
* Write preformatted output directly to the stream.
*/
exports.write = function(data) {
ostream.write(data);
};


/**
* Wrapper for log output.
*/
exports.log = function() {
if (ostream === process.stdout) {
console.log.apply(console, arguments);
} else {
ostream.write(util.format.apply(util, arguments) + "\n");
}
};

/**
* Wrapper for error output.
*/
exports.error = function() {
if (ostream === process.stdout) {
console.error.apply(console, arguments);
} else {
ostream.write(util.format.apply(util, arguments) + "\n");
}
};

/**
* Hook that's called when the test process is done to close the
* ostream ostream.
*/
exports.end = function(errCount, fn) {
if (ostream === process.stdout) {
// nothing to do
return fn(errCount);
}

ostream.end(function() {
debug("ostream ended");
fn(errCount);

// Reset the ostream
ostream = process.stdout;
} );
};

/**
* 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) {
return ostream; // don't change
}

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;
};
31 changes: 21 additions & 10 deletions lib/reporters/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand All @@ -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.
*/
Expand Down Expand Up @@ -108,6 +115,7 @@ exports.window = {
: 75
};


/**
* Expose some basic cursor interactions
* that are common among reporters.
Expand Down Expand Up @@ -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')
Expand Down Expand Up @@ -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);
});
};

Expand All @@ -222,14 +230,17 @@ 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 = [];

if (!runner) return;
this.runner = runner;

if (!ostream) return;
this.ostream = ostream;

runner.stats = stats;

runner.on('start', function(){
Expand Down Expand Up @@ -288,14 +299,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));

Expand All @@ -304,21 +315,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();
};

/**
Expand Down
19 changes: 10 additions & 9 deletions lib/reporters/doc.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/

var Base = require('./base')
, output = Base.output
, utils = require('../utils');

/**
Expand All @@ -19,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
Expand All @@ -34,23 +35,23 @@ function Doc(runner) {
runner.on('suite', function(suite){
if (suite.root) return;
++indents;
console.log('%s<section class="suite">', indent());
output.log('%s<section class="suite">', indent());
++indents;
console.log('%s<h1>%s</h1>', indent(), utils.escape(suite.title));
console.log('%s<dl>', indent());
output.log('%s<h1>%s</h1>', indent(), utils.escape(suite.title));
output.log('%s<dl>', indent());
});

runner.on('suite end', function(suite){
if (suite.root) return;
console.log('%s</dl>', indent());
output.log('%s</dl>', indent());
--indents;
console.log('%s</section>', indent());
output.log('%s</section>', indent());
--indents;
});

runner.on('pass', function(test){
console.log('%s <dt>%s</dt>', indent(), utils.escape(test.title));
output.log('%s <dt>%s</dt>', indent(), utils.escape(test.title));
var code = utils.escape(utils.clean(test.fn.toString()));
console.log('%s <dd><pre><code>%s</code></pre></dd>', indent(), code);
output.log('%s <dd><pre><code>%s</code></pre></dd>', indent(), code);
});
}
Loading