Skip to content

Commit

Permalink
Decouple the reporting system.
Browse files Browse the repository at this point in the history
  • Loading branch information
cloudhead committed May 17, 2010
1 parent 1882f19 commit dc9a746
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 65 deletions.
140 changes: 75 additions & 65 deletions lib/vows.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,21 @@ var sys = require('sys'),
eyes = require('eyes').inspector({ writer: null }),
vows = exports;

// Options
vows.options = {
Emitter: events.EventEmitter,
brief: false,
json: false,
matcher: /.*/,
printer: require('vows/printers/console')
};

vows.__defineGetter__('printer', function () {
return vows.options.printer;
});

var stylize = require('vows/printers/console').stylize;

// Keeps track of the outcome of vows.
var total = 0, honored = 0,
broken = 0, errored = 0,
Expand All @@ -36,6 +51,32 @@ var lastContext;
// Output buffer
var buffer;

var argv = [];
//
// Parse command-line parameters
//
for (var i = 0, arg; i < process.argv.length; i++) {
arg = process.argv[i];

if (arg === __filename) { continue }

if (arg[0] !== '-') {
argv.push(arg);
} else {
arg = arg.match(/^--?(.*)/)[1];

if (arg[0] === 'R') {
vows.options.matcher = new(RegExp)(arg.slice(1));
} else if (arg in vows.options) {
vows.options[arg] = true;
}
}
}

// Get rid of process runner
// ('node' in most cases)
argv = argv.slice(1);

//
// Assertion Macros
//
Expand Down Expand Up @@ -96,54 +137,55 @@ function addVow(/* description & callback */) {
args.unshift(null);
}
runTest(args);
tryFinish(vows.remaining, vow.promise);

}).addListener("error", function (err) {
var exception;

if (vow.callback.length >= 2) {
runTest([err]);
} else {
exception = " * " + stylize('The promise returned an error: ' +
stylize(err, 'bold'), 'red');
exception = { type: 'promise', error: err };
errored++;
output('- ' + stylize(vow.description, 'red'), exception + "\n");
output('errored', exception);
}
tryFinish(vows.remaining, vow.promise);
});

function runTest(args) {
var title = ' - ', exception, topic, msg;
var exception, topic, status;

// Run the test, and try to catch `AssertionError`s and other exceptions;
// increment counters accordingly.
try {
vow.callback.apply(vow.binding || null, args);
title += stylize(vow.description, 'green');
output('honored', exception);
honored++;
} catch (e) {
if (e.name && e.name.match(/AssertionError/)) {
title += stylize(vow.description, 'yellow');
exception = ' ~ ' + e.toString();
exception = e.toString();
output('broken', exception);
broken++;
} else {
title += stylize(vow.description, 'red');
msg = e.stack || e.message || e.toString() || e;
exception = ' ! ' + stylize(msg, 'red');
exception = e.stack || e.message || e.toString() || e;
errored++;
output('errored', exception);
}
}
output(title, exception);
}

function output(title, exception) {
function output(status, exception) {
if (exception || !vows.options.brief) {
if (vow.context && lastContext !== vow.context) {
lastContext = vow.context;
puts(vow.context);
vows.printer.print(['context', vow.context]);
}
puts(title);
if (exception) puts(exception);
vows.printer.print(['vow', {
title: vow.description,
status: status,
exception: exception || null
}]);
}
tryFinish(vows.remaining, vow.promise);
}
};

Expand Down Expand Up @@ -287,49 +329,40 @@ function addVows(tests) {
return promise;
}

function puts() {
var args = Array.prototype.slice.call(arguments);
if (vows.promises.length && vows.promises[suites - 1].listeners('finish').length > 0) {
buffer.push(args.join('\n'));
} else {
sys.puts.apply(null, args);
}
}

//
// Checks if all the required tests have been run,
// and either triggers the next test suite, if any,
// or exits the process.
//
function tryFinish(remaining, promise) {
var result, style;
var result, style, time;

// Output results once all the vows have been checked
if (honored + broken + errored === total && remaining === 0) {
result = honored + " honored, " +
broken + " broken, " +
errored + " errored",
style = honored === total ? ('green')
: (errored === 0 ? 'yellow' : 'red');

// If this isn't the last test suite in the chain,
// emit 'end', to trigger the next test suite.
if (promise && promise.listeners('end').length > 0) {
sys.print('\n');
if (vows.options.json) {
puts(['end']);
} else {
sys.print('\n');
}
promise.emit('end', honored, broken, errored);
} else {
if (!vows.options.brief) {
puts("\nVerified " + total + " vows in " +
(((new(Date)) - start) / 1000) + " seconds.");
puts("\n" + stylize(result, style));
}

time = (new(Date) - start) / 1000;
// The 'finish' event is triggered once all the tests have been run.
// It's used by bin/vows
vows.promises[suites - 1].emit("finish", honored, broken, errored);

if ((broken || errored) && buffer.length) { sys.puts(buffer.join('\n') + '\n') }
vows.printer.print([ 'finish', {
honored: honored,
broken: broken,
errored: errored,
total: total,
time: time
}]);

if ((broken || errored) && buffer.length) { sys.puts(buffer.join('\n') + '\n') }
// Don't exit until stdout is empty
process.stdout.addListener('drain', function () {
process.exit(broken || errored ? 1 : 0);
Expand All @@ -344,7 +377,7 @@ function tryFinish(remaining, promise) {
//
process.addListener('exit', function () {
if (honored + broken + errored < total) {
sys.puts('\n* ' + stylize("An EventEmitter has failed to fire.", 'red'));
vows.printer.print(['error', { error: "An EventEmitter has failed to fire.", type: 'promise' }]);
}
});

Expand Down Expand Up @@ -375,13 +408,6 @@ vows.prepare = function (obj, targets) {
return obj;
};

// Options
vows.options = {
Emitter: events.EventEmitter,
brief: false,
matcher: /.*/
};

// Run all vows/tests.
// It can take either a function as `tests`,
// or an object literal.
Expand All @@ -397,7 +423,7 @@ vows.describe = function (subject) {
buffer = [], suites = 0;

if (!vows.options.brief) {
puts('\n' + stylize(subject, 'underline') + '\n');
this.printer.print(['subject', subject]);
}

return new(events.EventEmitter)().addListener('newListener', function (e, listener) {
Expand All @@ -412,8 +438,6 @@ vows.describe = function (subject) {
});
};

vows.tell = vows.describe;

// Return the `vows` object after setting some options
vows.config = function (opts) {
for (var k in opts) { this.options[k] = opts[k] }
Expand All @@ -428,17 +452,3 @@ function inspect(val) {
return '\033[1m' + eyes(val) + '\033[22m';
}

// Stylize a string
function stylize(str, style) {
var styles = {
'bold' : [1, 22],
'underline' : [4, 24],
'yellow' : [33, 39],
'green' : [32, 39],
'red' : [31, 39],
'grey' : [90, 39]
};
return '\033[' + styles[style][0] + 'm' + str +
'\033[' + styles[style][1] + 'm';
}

73 changes: 73 additions & 0 deletions lib/vows/printers/console.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
var sys = require('sys');
//
// Console printer
//
this.print = function (data) {
var event = data[1];

switch (data[0]) {
case 'subject':
puts('\n' + stylize(event, 'underline') + '\n');
break;
case 'context':
puts(event);
break;
case 'vow':
puts(' - ' + stylize(event.title, ({
honored: 'green', broken: 'yellow', errored: 'red'
})[event.status]));
if (event.status === 'broken') {
puts(' ~ ' + event.exception);
} else if (event.status === 'errored') {
if (event.exception.type === 'promise') {
puts(' * ' + stylize("An 'error' event was caught: " +
stylize(event.exception.error, 'bold'), 'red'));
} else {
puts(' ! ' + stylize(event.exception, 'red'));
}
}
break;
case 'end':
sys.print('\n');
break;
case 'finish':
var result = event.honored + " honored, " +
event.broken + " broken, " +
event.errored + " errored",
style = event.honored === event.total ? ('green')
: (event.errored === 0 ? 'yellow' : 'red');

puts("\nVerified " + event.total + " vows in " +
(event.time + " seconds."));
puts("\n" + stylize(result, style));
break;
case 'error':
puts('\n * ' + stylize(event.error, 'red'));
break;
}
};

function puts(args) {
args = Array.prototype.slice.call(arguments).map(function (a) {
return a.replace(/`([^`]+)`/g, function (_, capture) { return stylize(capture, 'italic') })
.replace(/\*([^*]+)\*/g, function (_, capture) { return stylize(capture, 'bold') });
});
sys.puts.apply(null, args);
}

// Stylize a string
function stylize(str, style) {
var styles = {
'bold' : [1, 22],
'italic' : [3, 23],
'underline' : [4, 24],
'yellow' : [33, 39],
'green' : [32, 39],
'red' : [31, 39],
'grey' : [90, 39],
'green-hi' : [92, 32],
};
return '\033[' + styles[style][0] + 'm' + str +
'\033[' + styles[style][1] + 'm';
}
this.stylize = stylize;
3 changes: 3 additions & 0 deletions lib/vows/printers/json.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
this.print = function (obj) {
sys.puts(JSON.stringify(obj));
};

0 comments on commit dc9a746

Please sign in to comment.