Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Modify runner and reporters to use JSON serializable results #685

Closed
wants to merge 1 commit into from

3 participants

@ForbesLindesay

The first step down the road to turn reporters into separate modules

Extremely useful for writing CI systems as you can serialize the complete state of the world and let people view it in a local command line, the browser etc. using whichever reporter they want. This would make tj/mocha-cloud#5 much simpler as it wouldn't need to be able to serialize circular data structures with prototype chains.

I've specified the resulting API here so you can read through the basics of what's changed in plain English.

@tj
Owner
tj commented

I dont see a ton of immediate value in having CI related stuff utilizing regular reporters, if you're running tests in 8+ browsers whatever the least noisy solution is is the best IMO

@ForbesLindesay

I think they'd be useless as the only reporter. But really usefull as a drildown. For example I don't have easy access to an ie 7.0 browser so if it fails for that browser I want all the info that the browser reporter provides.

@ForbesLindesay

The other thing I think would be really useful is getting output from the more special reporters such as html-coverage and markdown documentation. Markdown documentation as a pre-made website that's always up to date would be a nice thing to be able to link to (and more people might find out that the feature exists to generate it themselves). Code coverage would be nice mostly because it's a pain to set up the build yourself (especially if your on windows), so having it set up for you online would be great. I do realise that getting code-coverage to work the same would require some extra work, but I think this represents the first step.

@ForbesLindesay

It would also helpfully let someone re-use the same reporters with a completely different test framework.

@tj
Owner
tj commented

the only thing I would want to see from a CI is which browsers failed and what the error(s) are, the rest doesn't matter at all really

@ForbesLindesay

I find it helpful to be able to view which tests failed and passed as well as often that narrows down what could've caused the error a lot. I think if people in general were happy with that we'd have far fewer and far simpler reporters.

@ForbesLindesay

Perhaps the ci thing is leaving us with a little bit of a red herring here (I think we use them in quite different ways).

I think it presents a cleaner simpler abstraction to reporters and is advantageous because of that, and I think it would be nice to have the ability to run the reporters as separate command line apps and pipe data from the test runner to the reporters (e.g. For aggregating multiple tests or using non-mocha test frameworks with mocha reporters) and I can't see any way of doing that without simplifying the format to be JSON serializable.

@tj
Owner
tj commented

yeah I dont disagree there, I'd like to do that eventually anyway, but we already have json-stream working just fine, they would just be built on that or really similar

@ForbesLindesay

OK, but JSON stream doesn't output anywhere near enough information to build the other reporters on top of it.

@tj
Owner
tj commented

yeah fair enough, at a glance I dont totally get what this pull-request adds, it seems to be a lot of noise from adding (time whatever that's for and a few additional props in the clean thing lower. I'll try and pull it down soon and have a better look

@ForbesLindesay

The time thing is so that all the timings are available as part of the test results (from the runner) and therefore the timings computed bor the test summary are correct even if the results were stored or processed in one go rather than streamed.

All the other properties are used by at least one reporter at some point.

@jbnicolai
Collaborator

@ForbesLindesay cleaning this PR up, as I believe it's no longer of interest. Sorry for the long silence, and feel free to add an explanation if you feel that this is still relevant.

Thanks for the effort :+1: and sorry for the long silence.

@jbnicolai jbnicolai closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
View
32 lib/reporters/base.js
@@ -204,7 +204,7 @@ exports.list = function(failures){
stack = stack.slice(index ? index + 1 : index)
.replace(/^/gm, ' ');
- console.error(fmt, (i + 1), test.fullTitle(), msg, stack);
+ console.error(fmt, (i + 1), test.fullTitle, msg, stack);
});
};
@@ -213,8 +213,8 @@ exports.list = function(failures){
*
* All other reporters generally
* inherit from this reporter, providing
- * stats such as test duration, number
- * of tests passed / failed etc.
+ * stats such as number of tests passed /
+ * failed etc.
*
* @param {Runner} runner
* @api public
@@ -230,43 +230,35 @@ function Base(runner) {
runner.stats = stats;
- runner.on('start', function(){
- stats.start = new Date;
+ runner.on('start', function(time){
+ stats.start = new Date(time);
});
- runner.on('suite', function(suite){
+ runner.on('suite start', function(time, suite){
stats.suites = stats.suites || 0;
suite.root || stats.suites++;
});
- runner.on('test end', function(test){
+ runner.on('test end', function(){
stats.tests = stats.tests || 0;
stats.tests++;
});
- runner.on('pass', function(test){
+ runner.on('pass', function(){
stats.passes = stats.passes || 0;
-
- var medium = test.slow() / 2;
- test.speed = test.duration > test.slow()
- ? 'slow'
- : test.duration > medium
- ? 'medium'
- : 'fast';
-
stats.passes++;
});
- runner.on('fail', function(test, err){
+ runner.on('fail', function(time, test, err){
stats.failures = stats.failures || 0;
stats.failures++;
test.err = err;
failures.push(test);
});
- runner.on('end', function(){
- stats.end = new Date;
- stats.duration = new Date - stats.start;
+ runner.on('end', function(time){
+ stats.end = new Date(time);
+ stats.duration = stats.end - stats.start;
});
runner.on('pending', function(){
View
8 lib/reporters/doc.js
@@ -31,7 +31,7 @@ function Doc(runner) {
return Array(indents).join(' ');
}
- runner.on('suite', function(suite){
+ runner.on('suite start', function(time, suite){
if (suite.root) return;
++indents;
console.log('%s<section class="suite">', indent());
@@ -40,7 +40,7 @@ function Doc(runner) {
console.log('%s<dl>', indent());
});
- runner.on('suite end', function(suite){
+ runner.on('suite end', function(time, suite){
if (suite.root) return;
console.log('%s</dl>', indent());
--indents;
@@ -48,9 +48,9 @@ function Doc(runner) {
--indents;
});
- runner.on('pass', function(test){
+ runner.on('pass', function(time, test){
console.log('%s <dt>%s</dt>', indent(), utils.escape(test.title));
- var code = utils.escape(utils.clean(test.fn.toString()));
+ var code = utils.escape(utils.clean(test.fn));
console.log('%s <dd><pre><code>%s</code></pre></dd>', indent(), code);
});
}
View
6 lib/reporters/dot.js
@@ -31,11 +31,11 @@ function Dot(runner) {
process.stdout.write('\n ');
});
- runner.on('pending', function(test){
+ runner.on('pending', function(){
process.stdout.write(color('pending', Base.symbols.dot));
});
- runner.on('pass', function(test){
+ runner.on('pass', function(time, test){
if (++n % width == 0) process.stdout.write('\n ');
if ('slow' == test.speed) {
process.stdout.write(color('bright yellow', Base.symbols.dot));
@@ -44,7 +44,7 @@ function Dot(runner) {
}
});
- runner.on('fail', function(test, err){
+ runner.on('fail', function(){
if (++n % width == 0) process.stdout.write('\n ');
process.stdout.write(color('fail', Base.symbols.dot));
});
View
12 lib/reporters/html.js
@@ -97,7 +97,7 @@ function HTML(runner, root) {
if (progress) progress.size(40);
- runner.on('suite', function(suite){
+ runner.on('suite start', function(time, suite){
if (suite.root) return;
// suite
@@ -110,16 +110,16 @@ function HTML(runner, root) {
el.appendChild(stack[0]);
});
- runner.on('suite end', function(suite){
+ runner.on('suite end', function(time, suite){
if (suite.root) return;
stack.shift();
});
- runner.on('fail', function(test, err){
- if ('hook' == test.type) runner.emit('test end', test);
+ runner.on('fail', function(time, test, err){
+ if ('hook' == test.type) runner.emit('test end', time, test);
});
- runner.on('test end', function(test){
+ runner.on('test end', function(time, test){
window.scrollTo(0, document.body.scrollHeight);
// TODO: add to stats
@@ -127,7 +127,7 @@ function HTML(runner, root) {
if (progress) progress.update(percent).draw(ctx);
// update stats
- var ms = new Date - stats.start;
+ var ms = new Date(time) - stats.start;
text(passes, stats.passes);
text(failures, stats.failures);
text(duration, (ms / 1000).toFixed(2));
View
6 lib/reporters/json-cov.js
@@ -29,15 +29,15 @@ function JSONCov(runner, output) {
, failures = []
, passes = [];
- runner.on('test end', function(test){
+ runner.on('test end', function(time, test){
tests.push(test);
});
- runner.on('pass', function(test){
+ runner.on('pass', function(time, test){
passes.push(test);
});
- runner.on('fail', function(test){
+ runner.on('fail', function(time, test){
failures.push(test);
});
View
6 lib/reporters/json-stream.js
@@ -30,11 +30,11 @@ function List(runner) {
console.log(JSON.stringify(['start', { total: total }]));
});
- runner.on('pass', function(test){
+ runner.on('pass', function(time, test){
console.log(JSON.stringify(['pass', clean(test)]));
});
- runner.on('fail', function(test, err){
+ runner.on('fail', function(time, test, err){
console.log(JSON.stringify(['fail', clean(test)]));
});
@@ -55,7 +55,7 @@ function List(runner) {
function clean(test) {
return {
title: test.title
- , fullTitle: test.fullTitle()
+ , fullTitle: test.fullTitle
, duration: test.duration
}
}
View
8 lib/reporters/json.js
@@ -28,15 +28,15 @@ function JSONReporter(runner) {
, failures = []
, passes = [];
- runner.on('test end', function(test){
+ runner.on('test end', function(time, test){
tests.push(test);
});
- runner.on('pass', function(test){
+ runner.on('pass', function(time, test){
passes.push(test);
});
- runner.on('fail', function(test){
+ runner.on('fail', function(time, test){
failures.push(test);
});
@@ -64,7 +64,7 @@ function JSONReporter(runner) {
function clean(test) {
return {
title: test.title
- , fullTitle: test.fullTitle()
+ , fullTitle: test.fullTitle
, duration: test.duration
}
}
View
192 lib/reporters/landing.js
@@ -1,97 +1,97 @@
-
-/**
- * Module dependencies.
- */
-
-var Base = require('./base')
- , cursor = Base.cursor
- , color = Base.color;
-
-/**
- * Expose `Landing`.
- */
-
-exports = module.exports = Landing;
-
-/**
- * Airplane color.
- */
-
-Base.colors.plane = 0;
-
-/**
- * Airplane crash color.
- */
-
-Base.colors['plane crash'] = 31;
-
-/**
- * Runway color.
- */
-
-Base.colors.runway = 90;
-
-/**
- * Initialize a new `Landing` reporter.
- *
- * @param {Runner} runner
- * @api public
- */
-
-function Landing(runner) {
- Base.call(this, runner);
-
- var self = this
- , stats = this.stats
- , width = Base.window.width * .75 | 0
- , total = runner.total
- , stream = process.stdout
- , plane = color('plane', '')
- , crashed = -1
- , n = 0;
-
- function runway() {
- var buf = Array(width).join('-');
- return ' ' + color('runway', buf);
- }
-
- runner.on('start', function(){
- stream.write('\n ');
- cursor.hide();
- });
-
- runner.on('test end', function(test){
- // check if the plane crashed
- var col = -1 == crashed
- ? width * ++n / total | 0
- : crashed;
-
- // show the crash
- if ('failed' == test.state) {
- plane = color('plane crash', '');
- crashed = col;
- }
-
- // 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');
- });
-
- runner.on('end', function(){
- cursor.show();
- console.log();
- self.epilogue();
- });
-}
-
-/**
- * Inherit from `Base.prototype`.
- */
-
+
+/**
+ * Module dependencies.
+ */
+
+var Base = require('./base')
+ , cursor = Base.cursor
+ , color = Base.color;
+
+/**
+ * Expose `Landing`.
+ */
+
+exports = module.exports = Landing;
+
+/**
+ * Airplane color.
+ */
+
+Base.colors.plane = 0;
+
+/**
+ * Airplane crash color.
+ */
+
+Base.colors['plane crash'] = 31;
+
+/**
+ * Runway color.
+ */
+
+Base.colors.runway = 90;
+
+/**
+ * Initialize a new `Landing` reporter.
+ *
+ * @param {Runner} runner
+ * @api public
+ */
+
+function Landing(runner) {
+ Base.call(this, runner);
+
+ var self = this
+ , stats = this.stats
+ , width = Base.window.width * .75 | 0
+ , total = runner.total
+ , stream = process.stdout
+ , plane = color('plane', '')
+ , crashed = -1
+ , n = 0;
+
+ function runway() {
+ var buf = Array(width).join('-');
+ return ' ' + color('runway', buf);
+ }
+
+ runner.on('start', function(){
+ stream.write('\n ');
+ cursor.hide();
+ });
+
+ runner.on('test end', function(time, test){
+ // check if the plane crashed
+ var col = -1 == crashed
+ ? width * ++n / total | 0
+ : crashed;
+
+ // show the crash
+ if ('failed' == test.state) {
+ plane = color('plane crash', '');
+ crashed = col;
+ }
+
+ // 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');
+ });
+
+ runner.on('end', function(){
+ cursor.show();
+ console.log();
+ self.epilogue();
+ });
+}
+
+/**
+ * Inherit from `Base.prototype`.
+ */
+
Landing.prototype.__proto__ = Base.prototype;
View
16 lib/reporters/list.js
@@ -31,27 +31,27 @@ function List(runner) {
console.log();
});
- runner.on('test', function(test){
- process.stdout.write(color('pass', ' ' + test.fullTitle() + ': '));
+ runner.on('test start', function(time, test){
+ process.stdout.write(color('pass', ' ' + test.fullTitle + ': '));
});
- runner.on('pending', function(test){
+ runner.on('pending', function(time, test){
var fmt = color('checkmark', ' -')
+ color('pending', ' %s');
- console.log(fmt, test.fullTitle());
+ console.log(fmt, test.fullTitle);
});
- runner.on('pass', function(test){
+ runner.on('pass', function(time, test){
var fmt = color('checkmark', ' '+Base.symbols.dot)
+ color('pass', ' %s: ')
+ color(test.speed, '%dms');
cursor.CR();
- console.log(fmt, test.fullTitle(), test.duration);
+ console.log(fmt, test.fullTitle, test.duration);
});
- runner.on('fail', function(test, err){
+ runner.on('fail', function(time, test, err){
cursor.CR();
- console.log(color('fail', ' %d) %s'), ++n, test.fullTitle());
+ console.log(color('fail', ' %d) %s'), ++n, test.fullTitle);
});
runner.on('end', self.epilogue.bind(self));
View
21 lib/reporters/markdown.js
@@ -63,21 +63,24 @@ function Markdown(runner) {
return stringifyTOC(obj, 0);
}
- generateTOC(runner.suite);
-
- runner.on('suite', function(suite){
+ runner.on('suite start', function(time, suite){
++level;
- var slug = utils.slug(suite.fullTitle());
- buf += '<a name="' + slug + '"></a>' + '\n';
- buf += title(suite.title) + '\n';
+ if (suite.root) {
+ generateTOC(runner.suite);
+ buf += '\n\n';
+ } else {
+ var slug = utils.slug(suite.fullTitle);
+ buf += '<a name="' + slug + '"></a>' + '\n';
+ buf += title(suite.title) + '\n\n';
+ }
});
- runner.on('suite end', function(suite){
+ runner.on('suite end', function(time, suite){
--level;
});
- runner.on('pass', function(test){
- var code = utils.clean(test.fn.toString());
+ runner.on('pass', function(time, test){
+ var code = utils.clean(test.fn);
buf += test.title + '.\n';
buf += '\n```js\n';
buf += code + '\n';
View
6 lib/reporters/nyan.js
@@ -40,15 +40,15 @@ function NyanCat(runner) {
self.draw('start');
});
- runner.on('pending', function(test){
+ runner.on('pending', function(){
self.draw('pending');
});
- runner.on('pass', function(test){
+ runner.on('pass', function(){
self.draw('pass');
});
- runner.on('fail', function(test, err){
+ runner.on('fail', function(){
self.draw('fail');
});
View
12 lib/reporters/spec.js
@@ -36,26 +36,26 @@ function Spec(runner) {
console.log();
});
- runner.on('suite', function(suite){
+ runner.on('suite start', function(time, suite){
++indents;
console.log(color('suite', '%s%s'), indent(), suite.title);
});
- runner.on('suite end', function(suite){
+ runner.on('suite end', function(time, suite){
--indents;
if (1 == indents) console.log();
});
- runner.on('test', function(test){
+ runner.on('test start', function(time, test){
process.stdout.write(indent() + color('pass', '' + test.title + ': '));
});
- runner.on('pending', function(test){
+ runner.on('pending', function(time, test){
var fmt = indent() + color('pending', ' - %s');
console.log(fmt, test.title);
});
- runner.on('pass', function(test){
+ runner.on('pass', function(time, test){
if ('fast' == test.speed) {
var fmt = indent()
+ color('checkmark', ' ' + Base.symbols.ok)
@@ -72,7 +72,7 @@ function Spec(runner) {
}
});
- runner.on('fail', function(test, err){
+ runner.on('fail', function(time, test, err){
cursor.CR();
console.log(indent() + color('fail', ' %d) %s'), ++n, test.title);
});
View
13 lib/reporters/tap.js
@@ -29,25 +29,24 @@ function TAP(runner) {
, passes = 0
, failures = 1;
- runner.on('start', function(){
- var total = runner.grepTotal(runner.suite);
- console.log('%d..%d', 1, total);
+ runner.on('suite start', function(time, suite){
+ if (suite.root) console.log('%d..%d', 1, suite.noOfTests);
});
runner.on('test end', function(){
++n;
});
- runner.on('pending', function(test){
+ runner.on('pending', function(time, test){
console.log('ok %d %s # SKIP -', n, title(test));
});
- runner.on('pass', function(test){
+ runner.on('pass', function(time, test){
passes++;
console.log('ok %d %s', n, title(test));
});
- runner.on('fail', function(test, err){
+ runner.on('fail', function(time, test, err){
failures++;
console.log('not ok %d %s', n, title(test));
if (err.stack) console.log(err.stack.replace(/^/gm, ' '));
@@ -69,5 +68,5 @@ function TAP(runner) {
*/
function title(test) {
- return test.fullTitle().replace(/#/g, '');
+ return test.fullTitle.replace(/#/g, '');
}
View
16 lib/reporters/teamcity.js
@@ -26,20 +26,20 @@ function Teamcity(runner) {
console.log("##teamcity[testSuiteStarted name='mocha.suite']");
});
- runner.on('test', function(test) {
- console.log("##teamcity[testStarted name='" + escape(test.fullTitle()) + "']");
+ runner.on('test start', function(time, test) {
+ console.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) + "']");
+ runner.on('test fail', function(time, test, err) {
+ console.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']");
+ runner.on('test pending', function(time, test) {
+ console.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 + "']");
+ runner.on('test end', function(time, test) {
+ console.log("##teamcity[testFinished name='" + escape(test.fullTitle) + "' duration='" + test.duration + "']");
});
runner.on('end', function() {
View
10 lib/reporters/xunit.js
@@ -36,22 +36,22 @@ function XUnit(runner) {
, tests = []
, self = this;
- runner.on('pass', function(test){
+ runner.on('time pass', function(time, test){
tests.push(test);
});
- runner.on('fail', function(test){
+ runner.on('time fail', function(time, test){
tests.push(test);
});
- runner.on('end', function(){
+ runner.on('end', function(time){
console.log(tag('testsuite', {
name: 'Mocha Tests'
, tests: stats.tests
, failures: stats.failures
, errors: stats.failures
, skip: stats.tests - stats.failures - stats.passes
- , timestamp: (new Date).toUTCString()
+ , timestamp: (new Date(time)).toUTCString()
, time: stats.duration / 1000
}, false));
@@ -72,7 +72,7 @@ XUnit.prototype.__proto__ = Base.prototype;
function test(test) {
var attrs = {
- classname: test.parent.fullTitle()
+ classname: test.parent.fullTitle
, name: test.title
, time: test.duration / 1000
};
View
117 lib/runner.js
@@ -35,16 +35,17 @@ module.exports = Runner;
*
* Events:
*
- * - `start` execution started
- * - `end` execution complete
- * - `suite` (suite) test suite execution started
- * - `suite end` (suite) all tests (and sub-suites) have finished
- * - `test` (test) test execution started
- * - `test end` (test) test completed
- * - `hook` (hook) hook execution started
- * - `hook end` (hook) hook complete
- * - `pass` (test) test passed
- * - `fail` (test, err) test failed
+ * - `start` (time) execution started
+ * - `end` (time) execution complete
+ * - `suite start`(time, suite)test suite execution started
+ * - `suite end` (time, suite)all tests (and sub-suites) have finished
+ * - `test start` (time, test) test execution started
+ * - `test end` (time, test) test completed
+ * - `hook start` (time, hook) hook execution started
+ * - `hook end` (time, hook) hook complete
+ * - `pass` (time, test) test passed
+ * - `fail` (time, test, err) test failed
+ * - `pending` (time, test) A test was skipped
*
* @api public
*/
@@ -55,8 +56,8 @@ function Runner(suite) {
this.suite = suite;
this.total = suite.total();
this.failures = 0;
- this.on('test end', function(test){ self.checkGlobals(test); });
- this.on('hook end', function(hook){ self.checkGlobals(hook); });
+ this.on('test end', function(time, test){ self.checkGlobals(test); });
+ this.on('hook end', function(time, hook){ self.checkGlobals(hook); });
this.grep(/.*/);
this.globals(this.globalProps().concat(['errno']));
}
@@ -186,7 +187,7 @@ Runner.prototype.fail = function(test, err){
err = new Error('the string "' + err + '" was thrown, throw an Error :)');
}
- this.emit('fail', test, err);
+ this.emit('fail', now(), cleanTest(test), err);
};
/**
@@ -204,7 +205,7 @@ Runner.prototype.fail = function(test, err){
Runner.prototype.failHook = function(hook, err){
this.fail(hook, err);
- this.emit('end');
+ this.emit('end', now());
};
/**
@@ -226,7 +227,7 @@ Runner.prototype.hook = function(name, fn){
if (!hook) return fn();
self.currentRunnable = hook;
- self.emit('hook', hook);
+ self.emit('hook start', now(), hook);
hook.on('error', function(err){
self.failHook(hook, err);
@@ -237,7 +238,7 @@ Runner.prototype.hook = function(name, fn){
var testError = hook.error();
if (testError) self.fail(self.test, testError);
if (err) return self.failHook(hook, err);
- self.emit('hook end', hook);
+ self.emit('hook end', now(), hook);
next(++i);
});
}
@@ -377,13 +378,13 @@ Runner.prototype.runTests = function(suite, fn){
// pending
if (test.pending) {
- self.emit('pending', test);
- self.emit('test end', test);
+ self.emit('pending', now(), cleanTest(test));
+ self.emit('test end', now(), cleanTest(test));
return next();
}
// execute test and hook(s)
- self.emit('test', self.test = test);
+ self.emit('test start', now(), cleanTest(self.test = test));
self.hookDown('beforeEach', function(){
self.currentRunnable = self.test;
self.runTest(function(err){
@@ -391,13 +392,13 @@ Runner.prototype.runTests = function(suite, fn){
if (err) {
self.fail(test, err);
- self.emit('test end', test);
+ self.emit('test end', now(), cleanTest(test));
return self.hookUp('afterEach', next);
}
test.state = 'passed';
- self.emit('pass', test);
- self.emit('test end', test);
+ self.emit('pass', now(), cleanTest(test));
+ self.emit('test end', now(), cleanTest(test));
self.hookUp('afterEach', next);
});
});
@@ -425,7 +426,7 @@ Runner.prototype.runSuite = function(suite, fn){
if (!total) return fn();
- this.emit('suite', this.suite = suite);
+ this.emit('suite start', now(), cleanSuite(this.suite = suite, self));
function next() {
var curr = suite.suites[i++];
@@ -436,7 +437,7 @@ Runner.prototype.runSuite = function(suite, fn){
function done() {
self.suite = suite;
self.hook('afterAll', function(){
- self.emit('suite end', suite);
+ self.emit('suite end', now(), cleanSuite(suite, self));
fn();
});
}
@@ -463,13 +464,13 @@ Runner.prototype.uncaught = function(err){
// recover from test
if ('test' == runnable.type) {
- this.emit('test end', runnable);
+ this.emit('test end', now(), cleanTest(runnable));
this.hookUp('afterEach', this.next);
return;
}
// bail on hooks
- this.emit('end');
+ this.emit('end', now());
};
/**
@@ -497,10 +498,10 @@ Runner.prototype.run = function(fn){
});
// run suites
- this.emit('start');
+ this.emit('start', now());
this.runSuite(this.suite, function(){
debug('finished running');
- self.emit('end');
+ self.emit('end', now());
});
// uncaught exception
@@ -531,3 +532,63 @@ function filterLeaks(ok, globals) {
return matched.length == 0 && (!global.navigator || 'onerror' !== key);
});
}
+
+/**
+ * Get the current time stamp
+ *
+ * @return {Number}
+ * @api private
+ */
+function now() {
+ var d = new Date();
+ return d.getTime();
+}
+
+/**
+ * Clean a test so it matches the mocha-result-spec
+ *
+ * @return {Object}
+ * @api private
+ */
+function cleanTest(test) {
+ if (typeof test.fullTitle === 'string') return test; //might already be clean
+ if (typeof test.fullTitle !== 'function') return test; //pass through anything that's not really a test
+
+ var speed = null;
+ if (test.state === 'passed') {
+ var medium = test.slow() / 2;
+ speed = test.duration > test.slow()
+ ? 'slow'
+ : test.duration > medium
+ ? 'medium'
+ : 'fast';
+ }
+ return {
+ title: test.title,
+ fullTitle: test.fullTitle(),
+ fn: test.fn && test.fn.toString(),
+ state: test.state,
+ pending: test.pending,
+ duration: test.duration,
+ parent: { fullTitle: test.parent.fullTitle() },
+ speed: speed,
+ type: test.type
+ };
+}
+
+/**
+ * Clean a suite so it matches the mocha-result-spec
+ *
+ * @return {Object}
+ * @api private
+ */
+function cleanSuite(suite, runner) {
+ if (typeof suite.fullTitle === 'string') return suite; //might already be clean
+ if (typeof suite.fullTitle !== 'function') return suite; //pass through anything that's not really a suite
+ return {
+ title: suite.title,
+ fullTitle: suite.fullTitle(),
+ root: suite.root,
+ noOfTests: runner.grepTotal(suite)
+ };
+}
View
10 test/runner.js
@@ -88,7 +88,7 @@ describe('Runner', function(){
it('should emit "fail" when a new global is introduced', function(done){
runner.checkGlobals();
global.foo = 'bar';
- runner.on('fail', function(test, err){
+ runner.on('fail', function(time, test, err){
test.should.equal('im a test');
err.message.should.equal('global leak detected: foo');
delete global.foo;
@@ -123,7 +123,7 @@ describe('Runner', function(){
runner.checkGlobals();
global.foo = 'bar';
global.bar = 'baz';
- runner.on('fail', function(test, err){
+ runner.on('fail', function(time, test, err){
test.should.equal('im a test');
err.message.should.equal('global leaks detected: foo, bar');
delete global.foo;
@@ -151,7 +151,7 @@ describe('Runner', function(){
it('should emit "fail"', function(done){
var test = {}, err = {};
- runner.on('fail', function(test, err){
+ runner.on('fail', function(time, test, err){
test.should.equal(test);
err.should.equal(err);
done();
@@ -171,7 +171,7 @@ describe('Runner', function(){
it('should emit "fail"', function(done){
var hook = {}, err = {};
- runner.on('fail', function(hook, err){
+ runner.on('fail', function(time, hook, err){
hook.should.equal(hook);
err.should.equal(err);
done();
@@ -181,7 +181,7 @@ describe('Runner', function(){
it('should emit "end"', function(done){
var hook = {}, err = {};
- runner.on('end', done);
+ runner.on('end', function () { done() });
runner.failHook(hook, err);
})
})
Something went wrong with that request. Please try again.