Skip to content

Commit

Permalink
Better console rendering allowing suites
Browse files Browse the repository at this point in the history
Console rendering listens only to test start/done events, and
not the hacky context push/pop events. It inspects the test on
the start event and prints out any necessary contexts.

This allows runner.js to run multiple files and produce sane
output.
  • Loading branch information
joelplane committed Jun 14, 2011
1 parent 7bc5d06 commit 932200d
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 27 deletions.
46 changes: 44 additions & 2 deletions context.js
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@ module.exports = (function(Test){
var events = require('./events'); var events = require('./events');
var EventEmitter = events.EventEmitter; var EventEmitter = events.EventEmitter;
var sys = require('sys'); var sys = require('sys');

var first = function(a) {
return a[0];
};
var last = function(a) {
return a[a.length-1];
};

function Context(name, parentContext) { function Context(name, parentContext) {
this._name = name; this._name = name;
this._parentContext = parentContext; this._parentContext = parentContext;
Expand All @@ -20,9 +28,30 @@ module.exports = (function(Test){
Context.prototype.context = function(name, callback) { Context.prototype.context = function(name, callback) {
var ctx = new Context(name, this); var ctx = new Context(name, this);
this._subContexts.push(ctx); this._subContexts.push(ctx);
this._emit('pushContext', {name: name, context: ctx}); //this._emit('pushContext', {name: name, context: ctx});
callback.call(ctx, ctx); callback.call(ctx, ctx);
this._emit('popContext', {name: name, context: ctx}); //this._emit('popContext', {name: name, context: ctx});

var self = this;
if (ctx._tests.length > 0) {
first(ctx._tests).on('testStarted', function() {
//sys.puts('first test started');
self._emit('pushContext', {name: name, context: ctx});
});
last(ctx._tests).on('testDone', function() {
//sys.puts('last test done');
self._emit('popContext', {name: name, context: ctx});
});
}
if (ctx._subContexts.length > 0) {
first(ctx._subContexts).on('pushContext', function() {
//sys.puts('first context started');
self._emit('pushContext', {name: name, context: ctx});
});
last(ctx._subContexts).on('popContext', function() {
self._emit('popContext', {name: name, context: ctx});
});
}
}; };
Context.prototype.it = function(name, callback) { Context.prototype.it = function(name, callback) {
this._tests.push(new Test(this, name, callback)); this._tests.push(new Test(this, name, callback));
Expand All @@ -36,5 +65,18 @@ module.exports = (function(Test){
} }
return i; return i;
}; };
Context.prototype._uniqueId = function() {
if (!this._uniqueId_) {
var parent = this;
var idArray = [];
idArray.push(parent._name);
while(parent) {
parent = parent._parentContext;
if (parent) idArray.push(parent._name);
}
this._uniqueId_ = idArray.join('<');
}
return this._uniqueId_;
};
return Context; return Context;
}); });
11 changes: 11 additions & 0 deletions examples/basicTest.js
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,11 @@
var test = require('../noodleTest')();
test.onFailureExitNonZero();

test.context("Example Test Suite", function() {
this.context("Example Sub-Context", function() {
this.it("Basic Test Example", function(test) {
test.assert(true);
test.done();
});
});
});
13 changes: 13 additions & 0 deletions examples/basicTest2.js
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,13 @@
var test = require('../noodleTest')();
test.onFailureExitNonZero();

test.context("Example Test Suite", function() {
this.context("Example Sub-Context", function() {
this.it("Another Basic Test Example", function(test) {
setTimeout(function(){
test.assert(true);
test.done();
}, 2000);
});
});
});
11 changes: 11 additions & 0 deletions examples/failingTest.js
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,11 @@
var test = require('../noodleTest')();
test.onFailureExitNonZero();

test.context("Example Test Suite", function() {
this.context("Example Sub-Context", function() {
this.it("A Failing Test Example", function(test) {
test.assert(false);
test.done();
});
});
});
13 changes: 7 additions & 6 deletions noodleTest.js
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ module.exports = (function(){
var Context = require('./context')(Test); var Context = require('./context')(Test);
var main = new EventEmitter(); var main = new EventEmitter();


main._suiteMode = false;
main.suiteMode = function() {
main._suiteMode = true;
};

/* Relay events emitted by Test and Context instances to the main object */ /* Relay events emitted by Test and Context instances to the main object */
Test.on('new', function(t){ Test.on('new', function(t){
events.relayEvents(t, main, ['assertionPassed', 'assertionFailed', 'testFlunk', 'testStarted', 'testTimeout', 'testDone']); events.relayEvents(t, main, ['assertionPassed', 'assertionFailed', 'testFlunk', 'testStarted', 'testTimeout', 'testDone']);
Expand All @@ -40,14 +45,10 @@ module.exports = (function(){
events.relayEvents(ctx, main, ['pushContext', 'popContext']); events.relayEvents(ctx, main, ['pushContext', 'popContext']);
}); });


var topLevelContexts = []; var topContext = new Context("Top Level Context", null);


main.context = function(name, callback) { main.context = function(name, callback) {
var ctx = new Context(name, null); topContext.context(name, callback);
topLevelContexts.push(ctx);
main.emit('pushContext', {name: name, context: ctx});
callback.call(ctx, ctx);
main.emit('popContext', {name: name, context: ctx});
}; };


addSystemConcerns(main); addSystemConcerns(main);
Expand Down
59 changes: 41 additions & 18 deletions noodleTestConsole.js
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ module.exports = (function(test){


var out = this; var out = this;


var indentSpace = ' ';

var lastDot = false; var lastDot = false;
var log = function(indent, text, colour) { var log = function(indent, text, colour) {
if (lastDot) { if (lastDot) {
Expand All @@ -12,7 +14,7 @@ module.exports = (function(test){
var spaces = ''; var spaces = '';
var i=0; var i=0;
for(i=0; i<indent; i++) { for(i=0; i<indent; i++) {
spaces = spaces + ' '; spaces = spaces + indentSpace;
} }
if (text.constructor == Array) { if (text.constructor == Array) {
text.forEach(function(tex){ text.forEach(function(tex){
Expand All @@ -38,7 +40,7 @@ module.exports = (function(test){
if (!lastDot) { if (!lastDot) {
var i=0; var i=0;
for(i=0; i<initialIndent; i++) { for(i=0; i<initialIndent; i++) {
spaces = spaces + ' '; spaces = spaces + indentSpace;
} }
lastDot = true; lastDot = true;
} }
Expand All @@ -47,59 +49,80 @@ module.exports = (function(test){


var last = null; var last = null;


test.on('pushContext', function(o){ var loggedContexts = {};
var contextIndent = o.context._depth(); var logContext = function(ctx) {
log(contextIndent, color(o.context._name, "yellow")); if (!loggedContexts[ctx._uniqueId()]) {
loggedContexts[ctx._uniqueId()] = true;

var contextIndent = ctx._depth();
log(contextIndent-2, color(ctx._name, "yellow"));
last = null; last = null;
});


// test.on('popContext', function(o){ }
// log(0, ''); };
// });
var eachContextForTestBottomUp = function(test, callback) {
var ctx;
for (ctx=test._context; ctx._parentContext !== null; ctx = ctx._parentContext) {
callback(ctx);
}
};
var eachContextForTestTopDown = function(test, callback) {
var contexts = [];
eachContextForTestBottomUp(test, function(ctx){
contexts.push(ctx);
});
for(var i=contexts.length-1; i>=0; i--) {
callback(contexts[i]);
}
};


test.on('testStarted', function(t){ test.on('testStarted', function(t){
eachContextForTestTopDown(t, function(ctx){
logContext(ctx);
});
var contextIndent = t._context._depth(); var contextIndent = t._context._depth();
log(contextIndent+1, "it " + color(t._name, "yellow")); log(contextIndent-1, "it " + color(t._name, "yellow"));
last = 'testStarted'; last = 'testStarted';
}); });


test.on('testTimeout', function(t){ test.on('testTimeout', function(t){
var contextIndent = t._context._depth(); var contextIndent = t._context._depth();
log(contextIndent, 'Did you remember to call done() for test "'+t._name+'"?', 'magenta'); log(contextIndent-2, 'Did you remember to call done() for test "'+t._name+'"?', 'magenta');
log(contextIndent, 'Make sure you do. We don\'t print out the stack traces until done() has been called' +"\n", 'magenta'); log(contextIndent-2, 'Make sure you do. We don\'t print out the stack traces until done() has been called' +"\n", 'magenta');
last = null; last = null;
}); });


test.on('testDone', function(t){ test.on('testDone', function(t){
var contextIndent = t._context._depth(); var contextIndent = t._context._depth();
var n = t._failures.length || t._passes.length; var n = t._failures.length || t._passes.length;
var message = (t._failures.length == 0) ? color(n + ' assertion'+(n==1?'':'s')+' passed','green') : color(n + ' assertion'+(n==1?'':'s')+" failed\n",'red'); var message = (t._failures.length == 0) ? color(n + ' assertion'+(n==1?'':'s')+' passed','green') : color(n + ' assertion'+(n==1?'':'s')+" failed\n",'red');
log(contextIndent+2, message); log(contextIndent, message);
t._failures.forEach(function(assertion){ t._failures.forEach(function(assertion){
var callString = assertion.callString(); var callString = assertion.callString();
var failureMessage = assertion.failureMessage(); var failureMessage = assertion.failureMessage();
log(contextIndent+2, callString + ' : ' + failureMessage, 'red+bold'); log(contextIndent, callString + ' : ' + failureMessage, 'red+bold');
log(contextIndent+2, assertion.stack, 'white'); log(contextIndent, assertion.stack, 'white');
log(0, ''); log(0, '');
}); });
last = null; last = null;
}); });


test.on('testFlunk', function(o){ test.on('testFlunk', function(o){
var contextIndent = o.context._depth(); var contextIndent = o.context._depth();
log(contextIndent+2, o.context + ': '+color('Failed','red')+': ' + o.message); log(contextIndent, o.context + ': '+color('Failed','red')+': ' + o.message);
last = null; last = null;
}); });


test.on('assertionPassed', function(o){ test.on('assertionPassed', function(o){
var contextIndent = o.context._depth(); var contextIndent = o.context._depth();
dot(contextIndent+2, color('+','green')); dot(contextIndent, color('+','green'));
last = 'assertionPassed'; last = 'assertionPassed';
}); });


test.on('assertionFailed', function(o){ test.on('assertionFailed', function(o){
var contextIndent = o.context._depth(); var contextIndent = o.context._depth();
dot(contextIndent+2, color('x','red')); dot(contextIndent, color('x','red'));
last = 'assertionFailed'; last = 'assertionFailed';
}); });


Expand Down
3 changes: 3 additions & 0 deletions runner.js
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ var sys = require('sys');
var glob = require('glob'); var glob = require('glob');
var control = new (require('events').EventEmitter); var control = new (require('events').EventEmitter);
test.onFailureExitNonZero(); test.onFailureExitNonZero();
test.suiteMode();


var dir = process.ARGV[2]; var dir = process.ARGV[2];
if (!dir) { if (!dir) {
Expand All @@ -34,7 +35,9 @@ var getFiles = function() {
control.on('getFilesDone', function(){ control.on('getFilesDone', function(){
sys.puts(files); sys.puts(files);
files.forEach(function(file){ files.forEach(function(file){
sys.puts('Loading file ' + file);
require('./' + file); require('./' + file);
sys.puts('Loaded file ' + file);
}); });
}); });


Expand Down
6 changes: 5 additions & 1 deletion test.js
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ module.exports = (function(Assertion, testQueue, timeout){
delete this['emit']; delete this['emit'];


Test.emit('new', this); Test.emit('new', this);
testQueue.put(this); var self = this;
// must do this next tick so context can listen to this test's events beforehand
process.nextTick(function(){
testQueue.put(self);
});
}; };
sys.inherits(Test, EventEmitter); sys.inherits(Test, EventEmitter);
events.classEvents(Test); events.classEvents(Test);
Expand Down

0 comments on commit 932200d

Please sign in to comment.