Skip to content

Commit

Permalink
Refactoring a lot. Use original qunit now which will solve a lot of bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
kof committed Sep 8, 2011
1 parent 10ebeff commit c51d3f9
Show file tree
Hide file tree
Showing 15 changed files with 762 additions and 6 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
@@ -1,3 +1,6 @@
[submodule "deps/jscoverage"]
path = deps/jscoverage
url = http://github.com/visionmedia/node-jscoverage.git
[submodule "deps/qunit"]
path = deps/qunit
url = https://github.com/jquery/qunit.git
2 changes: 1 addition & 1 deletion Makefile
Expand Up @@ -2,6 +2,6 @@ install:
cd deps/jscoverage && ./configure && make

test:
qunit -c ./lib/api.js -d testns1:sharedjs testns2:./test/filedeps/namespace.js ./test/filedeps/global.js -t ./test/api.js ./test/cli.js ./test/same.js
qunit -c ./lib/testrunner.js -t ./test/testrunner.js --cov false

.PHONY: install test
1 change: 1 addition & 0 deletions deps/qunit
Submodule qunit added at 28be47
2 changes: 1 addition & 1 deletion index.js
@@ -1 +1 @@
module.exports = require("./lib/qunit");
module.exports = require("./lib/testrunner");
74 changes: 74 additions & 0 deletions lib/child.js
@@ -0,0 +1,74 @@
var path = require('path'),
$ = require('sharedjs'),
options = JSON.parse(process.argv[2]);

var currentModule = path.basename(options.code.path, '.js'),
currentTest;

// make qunit api global, like it is in the browser
$.extend(global, require('../deps/qunit/qunit/qunit'));

function load(res) {
var requirePath = res.path.replace(/\.js$/, '');

// test resource can define'namespace'to expose its exports as a named object
if (res.namespace) {
global[res.namespace] = require(requirePath);
} else {
$.extend(global, require(requirePath));
}
}

// add paths to require
if (options.paths){
require.paths.push.apply(require.paths, options.paths);
}

QUnit.testStart = function(test) {
// currentTest is undefined while first test is not done yet
currentTest = test.name;
// use last module name if no module name defined
currentModule = test.module || currentModule;
};

QUnit.log = function(data) {
data.test = currentTest;
data.module = currentModule;

process.send({
event: 'assertionDone',
data: data
});
};

QUnit.testDone = function(test) {
// use last module name if no module name defined
test.module = test.module || currentModule;

process.send({
event: 'testDone',
data: test
});
};

QUnit.done = function done(res) {
// XXX don't know why, but qunit fires a lot of done callbacks,
// but only the last one has correct data #wtf
clearTimeout(done.timeout);
done.timeout = setTimeout(function() {
process.send({
event: 'done',
data: res
});
}, 300);
};


// require deps
options.deps.forEach(load);
// require code
load(options.code);
// require tests
options.tests.forEach(load);

QUnit.begin();
92 changes: 92 additions & 0 deletions lib/log.js
@@ -0,0 +1,92 @@
var util = require('util'),
Table = require('cli-table');

var logs = {
assertions: {
data: [],
table: null
},
tests: {
data: [],
table: null
},
summary: {
data: [],
table: null
}
};

exports.assertion = (function() {
var table,
currentModule, module,
currentTest, test;

table = logs.assertions.table = new Table({
head: ['Module', 'Test', 'Assertion', 'Result'],
colWidths: [40, 40, 40, 8]
});

return function(data) {
// just easier to read the table
if (data.module === currentModule) {
module = '';
} else {
module = currentModule = data.module;
}

// just easier to read the table
if (data.test === currentTest) {
test = '';
} else {
test = currentTest = data.test;
}

table.push([module, test, data.message, data.result ? 'ok' : 'fail']);

logs.assertions.data.push(data);
};
}());

exports.test = (function() {
var table,
currentModule, module;

table = logs.tests.table = new Table({
head: ['Module', 'Test', 'Failed', 'Passed', 'Total'],
colWidths: [40, 40, 8, 8, 8]
});

return function(data) {
// just easier to read the table
if (data.module === currentModule) {
module = '';
} else {
module = currentModule = data.module;
}

table.push([module, data.name, data.failed, data.passed, data.total]);

logs.tests.data.push(data);
};
}());

exports.summary = (function() {
var table = logs.summary.table = new Table({
head: ['Failed', 'Passed', 'Total', 'Runtime'],
colWidths: [10, 10, 10, 10]
});

return function(data) {
table.push([data.failed, data.passed, data.total, data.runtime]);

logs.summary.data.push(data);
};
}());

exports.print = function(name) {
if (logs[name].data.length) {
var upperName = name.charAt(0).toUpperCase() + name.substr(1);

util.print('\n' + upperName + '\n' + logs[name].table.toString() + '\n');
}
};
145 changes: 145 additions & 0 deletions lib/testrunner.js
@@ -0,0 +1,145 @@
var fs = require('fs'),
path = require('path'),
coverage = require('./coverage'),
cp = require('child_process'),
log = require('./log');

var options = exports.options = {
assertions: false,
tests: false,
summary: true,
coverage: true,
paths: null,
deps: null
};

/**
* Run one spawned instance with tests
* @param {Object} opts
* @param {Object} report
* @param {Function} callback
*/
function runOne(opts, callback) {
var child;

child = cp.fork(
__dirname + '/child.js',
[JSON.stringify(opts)],
{customFds: [0, -1, -1]}
);

// forward stderr and stdout streams from the child
child.stdout.pipe(process.stdout);
child.stderr.pipe(process.stderr);

child.on('message', function(msg) {
if (msg.event === 'assertionDone') {
log.assertion(msg.data);
} else if (msg.event === 'testDone') {
log.test(msg.data);
} else if (msg.event === 'done') {
log.summary(msg.data);

child.kill();

if (typeof callback === 'function') {
callback();
}
}
});
}

/**
* Make an absolute path from relative
* @param {String|Object} file
* @return {Object}
*/
function absPath(file) {
if (typeof file === 'string') {
file = {path: file};
}

if(file.path.charAt(0) === '.') {
file.path = path.join(process.cwd(), file.path);
}

return file;
}

/**
* Convert path or array of paths to array of abs paths
* @param {Array|String} files
* @return {Array}
*/
function toArray(files) {
var ret = [];

if (Array.isArray(files)) {
files.forEach(function(file) {
ret.push(absPath(file));
});
} else if (files) {
ret.push(absPath(files));
}

return ret;
}

/**
* Run tests in spawned node instance async for every test.
* @param {Object|Array} files
* @param {Function} callback optional
*/
exports.run = function(files, callback) {
if (!Array.isArray(files)) {
files = [files];
}

var report = {
files: 0,
tests: 0,
assertions: 0,
errors: 0,
success: 0,
time: Date.now()
};

files.forEach(function(opts) {

opts = {
deps: toArray(opts.deps || options.deps),
code: absPath(opts.code),
tests: toArray(opts.tests),
paths: opts.paths || options.paths,
coverage: opts.coverage || options.coverage
};

function finished(cov) {
if (report.files < files.length) {
return;
}

if (options.assertions) {
log.print('assertions');
}

if (options.tests) {
log.print('tests');
}

if (options.summary) {
log.print('summary');
}

if (typeof callback === 'function') {
callback(report);
}
}

if (opts.coverage) {

} else {
runOne(opts, report, finished);
}
});
};

0 comments on commit c51d3f9

Please sign in to comment.