diff --git a/bin/nodeunit b/bin/nodeunit index 6d30ebb7e..f6b2c94e2 100755 --- a/bin/nodeunit +++ b/bin/nodeunit @@ -12,6 +12,7 @@ var files = []; var testrunner, config_file, config_param_found = false, + output_param_found = false, reporter_file = 'default', reporter_param_found = false; @@ -23,6 +24,11 @@ var usage = "Usage: nodeunit [options] testmodule1.js testfolder [...] \n" + " -h, --help display this help and exit\n" + " -v, --version output version information and exit"; + +// load default options +var content = fs.readFileSync(__dirname + '/nodeunit.json', 'utf8'); +var options = JSON.parse(content); + // a very basic pseudo --options parser args.forEach(function (arg) { if (arg.slice(0, 9) === "--config=") { @@ -32,6 +38,13 @@ args.forEach(function (arg) { } else if (config_param_found) { config_file = arg; config_param_found = false; + } else if (arg.slice(0, 9) === "--output=") { + options.output = arg.slice(9); + } else if (arg === '--output') { + output_param_found = true; + } else if (output_param_found) { + options.output = arg; + output_param_found = false; } else if (arg.slice(0, 11) === "--reporter=") { reporter_file = arg.slice(11); } else if (arg === '--reporter') { @@ -73,10 +86,6 @@ if (files.length === 0) { process.exit(1); } -// load default options -var content = fs.readFileSync(__dirname + '/nodeunit.json', 'utf8'); -var options = JSON.parse(content); - if (config_file) { content = fs.readFileSync(config_file, 'utf8'); var custom_options = JSON.parse(content); diff --git a/lib/reporters/html.js b/lib/reporters/html.js index f2612a33f..9686f75c2 100644 --- a/lib/reporters/html.js +++ b/lib/reporters/html.js @@ -29,14 +29,6 @@ exports.info = "Report tests result as HTML"; exports.run = function (files, options) { - if (!options) { - // load default options - var content = fs.readFileSync( - __dirname + '/../../bin/nodeunit.json', 'utf8' - ); - options = JSON.parse(content); - } - var start = new Date().getTime(); var paths = files.map(function (p) { return path.join(process.cwd(), p); diff --git a/lib/reporters/junit.js b/lib/reporters/junit.js index 92b115482..195799d3f 100644 --- a/lib/reporters/junit.js +++ b/lib/reporters/junit.js @@ -12,44 +12,84 @@ var nodeunit = require('../nodeunit'), fs = require('fs'), sys = require('sys'), path = require('path'), + async = require('../../deps/async'), AssertionError = require('assert').AssertionError, + child_process = require('child_process'), ejs = require('../../deps/ejs'); + /** * Reporter info string */ -exports.info = "jUnit tests reporter"; +exports.info = "jUnit XML test reports"; + + +/** + * Ensures a directory exists using mkdir -p. + * + * @param {String} path + * @param {Function} callback + * @api private + */ + +var ensureDir = function (path, callback) { + var mkdir = child_process.spawn('mkdir', ['-p', path]); + mkdir.on('error', function (err) { + callback(err); + callback = function(){}; + }); + mkdir.on('exit', function (code) { + if (code === 0) callback(); + else callback(new Error('mkdir exited with code: ' + code)); + }); +}; + /** - * Run all tests within each module, reporting the results to the command-line. + * Returns absolute version of a path. Relative paths are interpreted + * relative to process.cwd() or the cwd parameter. Paths that are already + * absolute are returned unaltered. + * + * @param {String} p + * @param {String} cwd + * @return {String} + * @api public + */ + +var abspath = function (p, /*optional*/cwd) { + if (p[0] === '/') return p; + cwd = cwd || process.cwd(); + return path.normalize(path.join(cwd, p)); +}; + + +/** + * Run all tests within each module, reporting the results to the command-line, + * then writes out junit-compatible xml documents. * * @param {Array} files * @api public */ exports.run = function (files, opts, callback) { - opts.outputDirectory = opts.outputDirectory; - var options; - if (!options) { - // load default options - var content = fs.readFileSync( - __dirname + '/../../bin/nodeunit.json', 'utf8' + if (!opts.output) { + console.error( + 'Error: No output directory defined.\n' + + '\tEither add an "output" property to your nodeunit.json config ' + + 'file, or\n\tuse the --output command line option.' ); - options = JSON.parse(content); + return; } - + opts.output = abspath(opts.output); var error = function (str) { - return options.error_prefix + str + options.error_suffix; + return opts.error_prefix + str + opts.error_suffix; }; var ok = function (str) { - return options.ok_prefix + str + options.ok_suffix; + return opts.ok_prefix + str + opts.ok_suffix; }; var bold = function (str) { - return options.bold_prefix + str + options.bold_suffix; - }; - var assertion_message = function (str) { - return options.assertion_prefix + str + options.assertion_suffix; + return opts.bold_prefix + str + opts.bold_suffix; }; var start = new Date().getTime(); @@ -60,103 +100,82 @@ exports.run = function (files, opts, callback) { var modules = {} var curModule; - if (typeof StopIteration == "undefined") { - StopIteration = new Error("StopIteration"); - } - nodeunit.runFiles(paths, { moduleStart: function (name) { - curModule = { errorCount: 0 - , failureCount: 0 - , tests: 0 - , testcases: [] - , name: name - }; - modules[name] = curModule; - }, - testDone: function (name, assertions) { - var testcase = { + curModule = { + errorCount: 0, + failureCount: 0, + tests: 0, + testcases: [], name: name }; - curModule.tests++; - if (!assertions.failures) { - sys.puts('✔ ' + name); - } - else { - sys.puts(error('✖ ' + name) + '\n'); - - // wrap in a try/catch block so we can 'break' - try { - assertions.forEach(function (a) { - if (!a.failed()) - return; - - testcase.failure = { - message: a.message, - backtrace: a.error.stack - }; - - if (a.error instanceof AssertionError) { + modules[name] = curModule; + }, + testDone: function (name, assertions) { + var testcase = {name: name}; + for (var i=0; i