/*! * Nodeunit * Copyright (c) 2010 Caolan McMahon * MIT Licensed * * THIS FILE SHOULD BE BROWSER-COMPATIBLE JS! * You can use @REMOVE_LINE_FOR_BROWSER to remove code from the browser build. * Only code on that line will be removed, it's mostly to avoid requiring code * that is node specific */ /** * Module dependencies */ var assert = require('./assert'), //@REMOVE_LINE_FOR_BROWSER async = require('../deps/async'); //@REMOVE_LINE_FOR_BROWSER /** * Creates assertion objects representing the result of an assert call. * Accepts an object or AssertionError as its argument. * * @param {object} obj * @api public */ exports.assertion = function (obj) { return { method: obj.method || '', message: obj.message || (obj.error && obj.error.message) || '', error: obj.error, passed: function () { return !this.error; }, failed: function () { return Boolean(this.error); } }; }; /** * Creates an assertion list object representing a group of assertions. * Accepts an array of assertion objects. * * @param {Array} arr * @param {Number} duration * @api public */ exports.assertionList = function (arr, duration) { var that = arr || []; that.failures = function () { var failures = 0; for (var i = 0; i < this.length; i += 1) { if (this[i].failed()) { failures += 1; } } return failures; }; that.passes = function () { return that.length - that.failures(); }; that.duration = duration || 0; return that; }; /** * Create a wrapper function for assert module methods. Executes a callback * after it's complete with an assertion object representing the result. * * @param {Function} callback * @api private */ var assertWrapper = function (callback) { return function (new_method, assert_method, arity) { return function () { var message = arguments[arity - 1]; var a = exports.assertion({method: new_method, message: message}); try { assert[assert_method].apply(null, arguments); } catch (e) { a.error = e; } callback(a); }; }; }; /** * Creates the 'test' object that gets passed to every test function. * Accepts the name of the test function as its first argument, followed by * the start time in ms, the options object and a callback function. * * @param {String} name * @param {Number} start * @param {Object} options * @param {Function} callback * @api public */ exports.test = function (name, start, options, callback) { var expecting; var a_list = []; var wrapAssert = assertWrapper(function (a) { a_list.push(a); if (options.log) { async.nextTick(function () { options.log(a); }); } }); var test = { done: function (err) { if (expecting !== undefined && expecting !== a_list.length) { var e = new Error( 'Expected ' + expecting + ' assertions, ' + a_list.length + ' ran' ); var a1 = exports.assertion({method: 'expect', error: e}); a_list.push(a1); if (options.log) { async.nextTick(function () { options.log(a1); }); } } if (err) { var a2 = exports.assertion({error: err}); a_list.push(a2); if (options.log) { async.nextTick(function () { options.log(a2); }); } } var end = new Date().getTime(); async.nextTick(function () { var assertion_list = exports.assertionList(a_list, end - start); options.testDone(name, assertion_list); callback(null, a_list); }); }, ok: wrapAssert('ok', 'ok', 2), same: wrapAssert('same', 'deepEqual', 3), equals: wrapAssert('equals', 'equal', 3), expect: function (num) { expecting = num; }, _assertion_list: a_list }; // add all functions from the assert module for (var k in assert) { if (assert.hasOwnProperty(k)) { test[k] = wrapAssert(k, k, assert[k].length); } } return test; }; /** * Ensures an options object has all callbacks, adding empty callback functions * if any are missing. * * @param {Object} opt * @return {Object} * @api public */ exports.options = function (opt) { var optionalCallback = function (name) { opt[name] = opt[name] || function () {}; }; optionalCallback('moduleStart'); optionalCallback('moduleDone'); optionalCallback('testStart'); optionalCallback('testReady'); optionalCallback('testDone'); //optionalCallback('log'); // 'done' callback is not optional. return opt; };