Permalink
Browse files

Better console rendering allowing suites

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...
1 parent 7bc5d06 commit 932200dcfccb9751d1fe8ef7bfb66061338fa744 @joelplane joelplane committed Jun 14, 2011
Showing with 135 additions and 27 deletions.
  1. +44 −2 context.js
  2. +11 −0 examples/basicTest.js
  3. +13 −0 examples/basicTest2.js
  4. +11 −0 examples/failingTest.js
  5. +7 −6 noodleTest.js
  6. +41 −18 noodleTestConsole.js
  7. +3 −0 runner.js
  8. +5 −1 test.js
View
@@ -2,6 +2,14 @@ module.exports = (function(Test){
var events = require('./events');
var EventEmitter = events.EventEmitter;
var sys = require('sys');
+
+ var first = function(a) {
+ return a[0];
+ };
+ var last = function(a) {
+ return a[a.length-1];
+ };
+
function Context(name, parentContext) {
this._name = name;
this._parentContext = parentContext;
@@ -20,9 +28,30 @@ module.exports = (function(Test){
Context.prototype.context = function(name, callback) {
var ctx = new Context(name, this);
this._subContexts.push(ctx);
- this._emit('pushContext', {name: name, context: ctx});
+ //this._emit('pushContext', {name: name, context: 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) {
this._tests.push(new Test(this, name, callback));
@@ -36,5 +65,18 @@ module.exports = (function(Test){
}
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;
});
View
@@ -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();
+ });
+ });
+});
View
@@ -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);
+ });
+ });
+});
View
@@ -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();
+ });
+ });
+});
View
@@ -32,6 +32,11 @@ module.exports = (function(){
var Context = require('./context')(Test);
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 */
Test.on('new', function(t){
events.relayEvents(t, main, ['assertionPassed', 'assertionFailed', 'testFlunk', 'testStarted', 'testTimeout', 'testDone']);
@@ -40,14 +45,10 @@ module.exports = (function(){
events.relayEvents(ctx, main, ['pushContext', 'popContext']);
});
- var topLevelContexts = [];
+ var topContext = new Context("Top Level Context", null);
main.context = function(name, callback) {
- var ctx = new Context(name, null);
- topLevelContexts.push(ctx);
- main.emit('pushContext', {name: name, context: ctx});
- callback.call(ctx, ctx);
- main.emit('popContext', {name: name, context: ctx});
+ topContext.context(name, callback);
};
addSystemConcerns(main);
View
@@ -4,6 +4,8 @@ module.exports = (function(test){
var out = this;
+ var indentSpace = ' ';
+
var lastDot = false;
var log = function(indent, text, colour) {
if (lastDot) {
@@ -12,7 +14,7 @@ module.exports = (function(test){
var spaces = '';
var i=0;
for(i=0; i<indent; i++) {
- spaces = spaces + ' ';
+ spaces = spaces + indentSpace;
}
if (text.constructor == Array) {
text.forEach(function(tex){
@@ -38,7 +40,7 @@ module.exports = (function(test){
if (!lastDot) {
var i=0;
for(i=0; i<initialIndent; i++) {
- spaces = spaces + ' ';
+ spaces = spaces + indentSpace;
}
lastDot = true;
}
@@ -47,59 +49,80 @@ module.exports = (function(test){
var last = null;
- test.on('pushContext', function(o){
- var contextIndent = o.context._depth();
- log(contextIndent, color(o.context._name, "yellow"));
+ var loggedContexts = {};
+ var logContext = function(ctx) {
+ if (!loggedContexts[ctx._uniqueId()]) {
+ loggedContexts[ctx._uniqueId()] = true;
+
+ var contextIndent = ctx._depth();
+ log(contextIndent-2, color(ctx._name, "yellow"));
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){
+ eachContextForTestTopDown(t, function(ctx){
+ logContext(ctx);
+ });
var contextIndent = t._context._depth();
- log(contextIndent+1, "it " + color(t._name, "yellow"));
+ log(contextIndent-1, "it " + color(t._name, "yellow"));
last = 'testStarted';
});
test.on('testTimeout', function(t){
var contextIndent = t._context._depth();
- log(contextIndent, '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, 'Did you remember to call done() for test "'+t._name+'"?', '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;
});
test.on('testDone', function(t){
var contextIndent = t._context._depth();
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');
- log(contextIndent+2, message);
+ log(contextIndent, message);
t._failures.forEach(function(assertion){
var callString = assertion.callString();
var failureMessage = assertion.failureMessage();
- log(contextIndent+2, callString + ' : ' + failureMessage, 'red+bold');
- log(contextIndent+2, assertion.stack, 'white');
+ log(contextIndent, callString + ' : ' + failureMessage, 'red+bold');
+ log(contextIndent, assertion.stack, 'white');
log(0, '');
});
last = null;
});
test.on('testFlunk', function(o){
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;
});
test.on('assertionPassed', function(o){
var contextIndent = o.context._depth();
- dot(contextIndent+2, color('+','green'));
+ dot(contextIndent, color('+','green'));
last = 'assertionPassed';
});
test.on('assertionFailed', function(o){
var contextIndent = o.context._depth();
- dot(contextIndent+2, color('x','red'));
+ dot(contextIndent, color('x','red'));
last = 'assertionFailed';
});
View
@@ -15,6 +15,7 @@ var sys = require('sys');
var glob = require('glob');
var control = new (require('events').EventEmitter);
test.onFailureExitNonZero();
+test.suiteMode();
var dir = process.ARGV[2];
if (!dir) {
@@ -34,7 +35,9 @@ var getFiles = function() {
control.on('getFilesDone', function(){
sys.puts(files);
files.forEach(function(file){
+ sys.puts('Loading file ' + file);
require('./' + file);
+ sys.puts('Loaded file ' + file);
});
});
View
@@ -15,7 +15,11 @@ module.exports = (function(Assertion, testQueue, timeout){
delete this['emit'];
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);
events.classEvents(Test);

0 comments on commit 932200d

Please sign in to comment.