| /*! |
| * Nodeunit |
| * Copyright (c) 2010 Caolan McMahon |
| * MIT Licensed |
| */ |
| |
| /** |
| * Module dependencies |
| */ |
| |
| var nodeunit = require('../nodeunit'), |
| utils = require('../utils'), |
| fs = require('fs'), |
| track = require('../track'), |
| path = require('path'), |
| AssertionError = require('../assert').AssertionError; |
| |
| /** |
| * Reporter info string |
| */ |
| |
| exports.info = "Nested test reporter"; |
| |
| |
| /** |
| * Run all tests within each module, reporting the results to the command-line. |
| * |
| * @param {Array} files |
| * @api public |
| */ |
| |
| exports.run = function (files, options, callback) { |
| |
| if (!options) { |
| // load default options |
| var content = fs.readFileSync( |
| __dirname + '/../../bin/nodeunit.json', |
| 'utf8' |
| ); |
| options = JSON.parse(content); |
| } |
| |
| var error = function (str) { |
| return options.error_prefix + str + options.error_suffix; |
| }; |
| var ok = function (str) { |
| return options.ok_prefix + str + options.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; |
| }; |
| |
| var spaces_per_indent = options.spaces_per_indent || 4; |
| |
| var start = new Date().getTime(); |
| var paths = files.map(function (p) { |
| return path.resolve(p); |
| }); |
| var tracker = track.createTracker(function (tracker) { |
| var i, names; |
| if (tracker.unfinished()) { |
| console.log(''); |
| console.log(error(bold( |
| 'FAILURES: Undone tests (or their setups/teardowns): ' |
| ))); |
| names = tracker.names(); |
| for (i = 0; i < names.length; i += 1) { |
| console.log('- ' + names[i]); |
| } |
| console.log(''); |
| console.log('To fix this, make sure all tests call test.done()'); |
| process.reallyExit(tracker.unfinished()); |
| } |
| }); |
| |
| // Object to hold status of each 'part' of the testCase/name array, |
| // i.e., whether this part has been printed yet. |
| tracker.already_printed = {}; |
| |
| var pass_text = function (txt) { |
| // Print in bold green. |
| return bold(ok(txt + " (pass)")); |
| }; |
| |
| var fail_text = function (txt) { |
| return bold(error(txt + " (fail) ✖ ")); |
| }; |
| |
| var status_text = function (txt, status) { |
| if (status === 'pass') { |
| return pass_text(txt); |
| } else { |
| return fail_text(txt); |
| } |
| }; |
| |
| /** |
| * Slices an array, returns a string by joining the sliced elements. |
| * @example |
| * > name_slice(['TC1', 'TC1.1', 'mytest'], 1); |
| * "TC1,TC1.1" |
| */ |
| var name_slice = function (name_arr, end_index) { |
| return name_arr.slice(0, end_index + 1).join(","); |
| }; |
| |
| var indent = (function () { |
| var txt = ''; |
| var i; |
| for (i = 0; i < spaces_per_indent; i++) { |
| txt += ' '; |
| } |
| return txt; |
| }()); |
| |
| // Indent once for each indent_level |
| var add_indent = function (txt, indent_level) { |
| var k; |
| for (k = 0; k < indent_level; k++) { |
| txt += indent; |
| } |
| return txt; |
| }; |
| |
| // If it's not the last element of the name_arr, it's a testCase. |
| var is_testCase = function (name_arr, index) { |
| return index === name_arr.length - 1 ? false : true; |
| }; |
| |
| var testCase_line = function (txt) { |
| return txt + "\n"; |
| }; |
| |
| /** |
| * Prints (console.log) the nested test status line(s). |
| * |
| * @param {Array} name_arr - Array of name elements. |
| * @param {String} status - either 'pass' or 'fail'. |
| * @example |
| * > print_status(['TC1', 'TC1.1', 'mytest'], 'pass'); |
| * TC1 |
| * TC1.1 |
| * mytest (pass) |
| */ |
| var print_status = function (name_arr, status) { |
| var txt = ''; |
| var _name_slice, part, i; |
| for (i = 0; i < name_arr.length; i++) { |
| _name_slice = name_slice(name_arr, i); |
| part = name_arr[i]; |
| if (!tracker.already_printed[_name_slice]) { |
| txt = add_indent(txt, i); |
| if (is_testCase(name_arr, i)) { |
| txt += testCase_line(part); |
| } else { |
| txt += status_text(part, status); |
| } |
| tracker.already_printed[_name_slice] = true; |
| } |
| } |
| console.log(txt); |
| }; |
| |
| nodeunit.runFiles(paths, { |
| testspec: options.testspec, |
| testFullSpec: options.testFullSpec, |
| moduleStart: function (name) { |
| console.log('\n' + bold(name)); |
| }, |
| testDone: function (name, assertions) { |
| tracker.remove(name); |
| |
| if (!assertions.failures()) { |
| print_status(name, 'pass'); |
| } else { |
| print_status(name, 'fail'); |
| assertions.forEach(function (a) { |
| if (a.failed()) { |
| a = utils.betterErrors(a); |
| if (a.error instanceof AssertionError && a.message) { |
| console.log( |
| 'Assertion Message: ' + |
| assertion_message(a.message) |
| ); |
| } |
| console.log(a.error.stack + '\n'); |
| } |
| }); |
| } |
| }, |
| done: function (assertions, end) { |
| end = end || new Date().getTime(); |
| var duration = end - start; |
| if (assertions.failures()) { |
| console.log( |
| '\n' + bold(error('FAILURES: ')) + assertions.failures() + |
| '/' + assertions.length + ' assertions failed (' + |
| assertions.duration + 'ms)' |
| ); |
| } else { |
| console.log( |
| '\n' + bold(ok('OK: ')) + assertions.length + |
| ' assertions (' + assertions.duration + 'ms)' |
| ); |
| } |
| |
| if (callback) callback(assertions.failures() ? new Error('We have got test failures.') : undefined); |
| }, |
| testStart: function (name) { |
| tracker.put(name); |
| } |
| }); |
| }; |