Skip to content

Commit dc9a746

Browse files
author
cloudhead
committed
Decouple the reporting system.
1 parent 1882f19 commit dc9a746

File tree

3 files changed

+151
-65
lines changed

3 files changed

+151
-65
lines changed

lib/vows.js

Lines changed: 75 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,21 @@ var sys = require('sys'),
2222
eyes = require('eyes').inspector({ writer: null }),
2323
vows = exports;
2424

25+
// Options
26+
vows.options = {
27+
Emitter: events.EventEmitter,
28+
brief: false,
29+
json: false,
30+
matcher: /.*/,
31+
printer: require('vows/printers/console')
32+
};
33+
34+
vows.__defineGetter__('printer', function () {
35+
return vows.options.printer;
36+
});
37+
38+
var stylize = require('vows/printers/console').stylize;
39+
2540
// Keeps track of the outcome of vows.
2641
var total = 0, honored = 0,
2742
broken = 0, errored = 0,
@@ -36,6 +51,32 @@ var lastContext;
3651
// Output buffer
3752
var buffer;
3853

54+
var argv = [];
55+
//
56+
// Parse command-line parameters
57+
//
58+
for (var i = 0, arg; i < process.argv.length; i++) {
59+
arg = process.argv[i];
60+
61+
if (arg === __filename) { continue }
62+
63+
if (arg[0] !== '-') {
64+
argv.push(arg);
65+
} else {
66+
arg = arg.match(/^--?(.*)/)[1];
67+
68+
if (arg[0] === 'R') {
69+
vows.options.matcher = new(RegExp)(arg.slice(1));
70+
} else if (arg in vows.options) {
71+
vows.options[arg] = true;
72+
}
73+
}
74+
}
75+
76+
// Get rid of process runner
77+
// ('node' in most cases)
78+
argv = argv.slice(1);
79+
3980
//
4081
// Assertion Macros
4182
//
@@ -96,54 +137,55 @@ function addVow(/* description & callback */) {
96137
args.unshift(null);
97138
}
98139
runTest(args);
140+
tryFinish(vows.remaining, vow.promise);
99141

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

103145
if (vow.callback.length >= 2) {
104146
runTest([err]);
105147
} else {
106-
exception = " * " + stylize('The promise returned an error: ' +
107-
stylize(err, 'bold'), 'red');
148+
exception = { type: 'promise', error: err };
108149
errored++;
109-
output('- ' + stylize(vow.description, 'red'), exception + "\n");
150+
output('errored', exception);
110151
}
152+
tryFinish(vows.remaining, vow.promise);
111153
});
112154

113155
function runTest(args) {
114-
var title = ' - ', exception, topic, msg;
156+
var exception, topic, status;
115157

116158
// Run the test, and try to catch `AssertionError`s and other exceptions;
117159
// increment counters accordingly.
118160
try {
119161
vow.callback.apply(vow.binding || null, args);
120-
title += stylize(vow.description, 'green');
162+
output('honored', exception);
121163
honored++;
122164
} catch (e) {
123165
if (e.name && e.name.match(/AssertionError/)) {
124-
title += stylize(vow.description, 'yellow');
125-
exception = ' ~ ' + e.toString();
166+
exception = e.toString();
167+
output('broken', exception);
126168
broken++;
127169
} else {
128-
title += stylize(vow.description, 'red');
129-
msg = e.stack || e.message || e.toString() || e;
130-
exception = ' ! ' + stylize(msg, 'red');
170+
exception = e.stack || e.message || e.toString() || e;
131171
errored++;
172+
output('errored', exception);
132173
}
133174
}
134-
output(title, exception);
135175
}
136176

137-
function output(title, exception) {
177+
function output(status, exception) {
138178
if (exception || !vows.options.brief) {
139179
if (vow.context && lastContext !== vow.context) {
140180
lastContext = vow.context;
141-
puts(vow.context);
181+
vows.printer.print(['context', vow.context]);
142182
}
143-
puts(title);
144-
if (exception) puts(exception);
183+
vows.printer.print(['vow', {
184+
title: vow.description,
185+
status: status,
186+
exception: exception || null
187+
}]);
145188
}
146-
tryFinish(vows.remaining, vow.promise);
147189
}
148190
};
149191

@@ -287,49 +329,40 @@ function addVows(tests) {
287329
return promise;
288330
}
289331

290-
function puts() {
291-
var args = Array.prototype.slice.call(arguments);
292-
if (vows.promises.length && vows.promises[suites - 1].listeners('finish').length > 0) {
293-
buffer.push(args.join('\n'));
294-
} else {
295-
sys.puts.apply(null, args);
296-
}
297-
}
298-
299332
//
300333
// Checks if all the required tests have been run,
301334
// and either triggers the next test suite, if any,
302335
// or exits the process.
303336
//
304337
function tryFinish(remaining, promise) {
305-
var result, style;
338+
var result, style, time;
306339

307340
// Output results once all the vows have been checked
308341
if (honored + broken + errored === total && remaining === 0) {
309-
result = honored + " honored, " +
310-
broken + " broken, " +
311-
errored + " errored",
312-
style = honored === total ? ('green')
313-
: (errored === 0 ? 'yellow' : 'red');
314-
315342
// If this isn't the last test suite in the chain,
316343
// emit 'end', to trigger the next test suite.
317344
if (promise && promise.listeners('end').length > 0) {
318-
sys.print('\n');
345+
if (vows.options.json) {
346+
puts(['end']);
347+
} else {
348+
sys.print('\n');
349+
}
319350
promise.emit('end', honored, broken, errored);
320351
} else {
321-
if (!vows.options.brief) {
322-
puts("\nVerified " + total + " vows in " +
323-
(((new(Date)) - start) / 1000) + " seconds.");
324-
puts("\n" + stylize(result, style));
325-
}
326-
352+
time = (new(Date) - start) / 1000;
327353
// The 'finish' event is triggered once all the tests have been run.
328354
// It's used by bin/vows
329355
vows.promises[suites - 1].emit("finish", honored, broken, errored);
330356

331-
if ((broken || errored) && buffer.length) { sys.puts(buffer.join('\n') + '\n') }
357+
vows.printer.print([ 'finish', {
358+
honored: honored,
359+
broken: broken,
360+
errored: errored,
361+
total: total,
362+
time: time
363+
}]);
332364

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

@@ -375,13 +408,6 @@ vows.prepare = function (obj, targets) {
375408
return obj;
376409
};
377410

378-
// Options
379-
vows.options = {
380-
Emitter: events.EventEmitter,
381-
brief: false,
382-
matcher: /.*/
383-
};
384-
385411
// Run all vows/tests.
386412
// It can take either a function as `tests`,
387413
// or an object literal.
@@ -397,7 +423,7 @@ vows.describe = function (subject) {
397423
buffer = [], suites = 0;
398424

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

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

415-
vows.tell = vows.describe;
416-
417441
// Return the `vows` object after setting some options
418442
vows.config = function (opts) {
419443
for (var k in opts) { this.options[k] = opts[k] }
@@ -428,17 +452,3 @@ function inspect(val) {
428452
return '\033[1m' + eyes(val) + '\033[22m';
429453
}
430454

431-
// Stylize a string
432-
function stylize(str, style) {
433-
var styles = {
434-
'bold' : [1, 22],
435-
'underline' : [4, 24],
436-
'yellow' : [33, 39],
437-
'green' : [32, 39],
438-
'red' : [31, 39],
439-
'grey' : [90, 39]
440-
};
441-
return '\033[' + styles[style][0] + 'm' + str +
442-
'\033[' + styles[style][1] + 'm';
443-
}
444-

lib/vows/printers/console.js

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
var sys = require('sys');
2+
//
3+
// Console printer
4+
//
5+
this.print = function (data) {
6+
var event = data[1];
7+
8+
switch (data[0]) {
9+
case 'subject':
10+
puts('\n' + stylize(event, 'underline') + '\n');
11+
break;
12+
case 'context':
13+
puts(event);
14+
break;
15+
case 'vow':
16+
puts(' - ' + stylize(event.title, ({
17+
honored: 'green', broken: 'yellow', errored: 'red'
18+
})[event.status]));
19+
if (event.status === 'broken') {
20+
puts(' ~ ' + event.exception);
21+
} else if (event.status === 'errored') {
22+
if (event.exception.type === 'promise') {
23+
puts(' * ' + stylize("An 'error' event was caught: " +
24+
stylize(event.exception.error, 'bold'), 'red'));
25+
} else {
26+
puts(' ! ' + stylize(event.exception, 'red'));
27+
}
28+
}
29+
break;
30+
case 'end':
31+
sys.print('\n');
32+
break;
33+
case 'finish':
34+
var result = event.honored + " honored, " +
35+
event.broken + " broken, " +
36+
event.errored + " errored",
37+
style = event.honored === event.total ? ('green')
38+
: (event.errored === 0 ? 'yellow' : 'red');
39+
40+
puts("\nVerified " + event.total + " vows in " +
41+
(event.time + " seconds."));
42+
puts("\n" + stylize(result, style));
43+
break;
44+
case 'error':
45+
puts('\n * ' + stylize(event.error, 'red'));
46+
break;
47+
}
48+
};
49+
50+
function puts(args) {
51+
args = Array.prototype.slice.call(arguments).map(function (a) {
52+
return a.replace(/`([^`]+)`/g, function (_, capture) { return stylize(capture, 'italic') })
53+
.replace(/\*([^*]+)\*/g, function (_, capture) { return stylize(capture, 'bold') });
54+
});
55+
sys.puts.apply(null, args);
56+
}
57+
58+
// Stylize a string
59+
function stylize(str, style) {
60+
var styles = {
61+
'bold' : [1, 22],
62+
'italic' : [3, 23],
63+
'underline' : [4, 24],
64+
'yellow' : [33, 39],
65+
'green' : [32, 39],
66+
'red' : [31, 39],
67+
'grey' : [90, 39],
68+
'green-hi' : [92, 32],
69+
};
70+
return '\033[' + styles[style][0] + 'm' + str +
71+
'\033[' + styles[style][1] + 'm';
72+
}
73+
this.stylize = stylize;

lib/vows/printers/json.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
this.print = function (obj) {
2+
sys.puts(JSON.stringify(obj));
3+
};

0 commit comments

Comments
 (0)