UNPKG

node-jqunit

Version:

An XUnit unit testing framework (in the style of JUnit) for node.js, using QUnit as the underlying engine

209 lines (175 loc) 6.47 kB
/* Copyright 2010-2012 OCAD University Copyright 2010-2012 Antranig Basman Licensed under the Educational Community License (ECL), Version 2.0 or the New BSD license. You may not use this file except in compliance with one these Licenses. You may obtain a copy of the ECL 2.0 License and BSD License at https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt Includes code from Underscore.js 1.4.3 http://underscorejs.org (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. Underscore may be freely distributed under the MIT license. */ "use strict"; var fluid = require("infusion"); fluid.loadTestingSupport(); fluid.setLogging(true); // Debugging definition - node.js's default is only 10! TODO - place these into Infusion itself fluid.Error.stackTraceLimit = 100; Error.stackTraceLimit = 100; var QUnit = fluid.registerNamespace("QUnit"); var jqUnit = fluid.registerNamespace("jqUnit"); jqUnit.nodeFailureHandler = function (args, activity) { if (QUnit.config.current) { QUnit.ok(false, "Assertion failure (see console.log for expanded message): ".concat(args)); } else { jqUnit.globalFailure = args; } fluid.builtinFail(args, activity); // Note that since Infusion has overriden node's global uncaught exception handler, the process will not exit here }; fluid.failureEvent.addListener(jqUnit.nodeFailureHandler, "jqUnit", "before:fail"); QUnit.load(); // patch this function so it is not confused by the presence of the vestigial jQuery QUnit.reset = fluid.identity; // ensure that QUnit does not attempt to start synchronously and hence confuse the queue // hack inside the IoC testing system QUnit.config.autorun = false; // Begin callbacks for test state var colors = fluid.registerNamespace("colors"); colors.styles = { //styles "bold" : [1, 22], "italic" : [3, 23], "underline" : [4, 24], "inverse" : [7, 27], //grayscale "white" : [37, 39], "grey" : [90, 39], "black" : [90, 39], //colors "blue" : [34, 39], "cyan" : [36, 39], "green" : [32, 39], "magenta" : [35, 39], "red" : [31, 39], "yellow" : [33, 39] }; // stolen from "colors" npm module which grubbily works by polluting global prototypes colors.stylize = function (str, styles) { styles = fluid.makeArray(styles); var togo = str; for (var i = 0; i < styles.length; ++i) { togo = "\u001b[" + colors.styles[styles[i]][0] + "m" + togo + "\u001b[" + colors.styles[styles[i]][1] + "m"; } return togo; }; var testState = { currentModule: "", assertions: [], tests: [], global: null }; QUnit.testStart(function (test) { testState.currentModule = test.module || ""; }); jqUnit.log = function () { var args = fluid.makeArray(arguments); args.unshift("jq: "); fluid.log.apply(null, args); }; jqUnit.applyStyles = process.stdout.isTTY; // don't apply colour styles if not outputting to terminal jqUnit.stylize = function (str, styles) { return jqUnit.applyStyles ? colors.stylize(str, styles) : str; }; jqUnit.onAllTestsDone = fluid.makeEventFirer(); jqUnit.renderTestName = function (data, member) { return (data.module ? "Module \"" + data.module + "\" " : "") + "Test name \"" + data[member] + "\""; }; jqUnit.passFail = function (failures) { return failures === 0 ? jqUnit.stylize("PASS", ["green", "bold"]) : jqUnit.stylize("FAIL", ["red", "bold"]); }; jqUnit.passCount = function (data) { return data.passed + "/" + data.total; }; /** * Callback for each assertion. * @param {Object} data */ QUnit.log(function (data) { data.test = QUnit.config.current.testName; data.module = testState.currentModule; testState.assertions.push(data); if (!data.result) { jqUnit.log(jqUnit.passFail(1) + ": " + jqUnit.renderTestName(data, "test") + " - Message: " + data.message); if (data.expected !== undefined) { jqUnit.log("Expected: ", data.expected); jqUnit.log("Actual: ", data.actual); } if (data.source) { jqUnit.log("Source: " + data.source); } } }); /** * Callback for one done test. * @param {Object} test */ QUnit.testDone(function (data) { // use last module name if no module name defined data.module = data.module || testState.currentModule; testState.tests.push(data); jqUnit.log("Test concluded - " + jqUnit.renderTestName(data, "name") + ": " + jqUnit.passCount(data) + " passed - " + jqUnit.passFail(data.failed)); }); // Stolen from Underscore.js 1.4.3 - see licence at head of this file /* jshint ignore:start */ fluid.debounce = function (func, wait, immediate) { var timeout, result; return function () { var context = this, args = arguments; var later = function () { timeout = null; if (!immediate) result = func.apply(context, args); }; var callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if (callNow) result = func.apply(context, args); return result; }; }; /* jshint ignore:end */ jqUnit.allTestsDone = function (data) { if (jqUnit.globalFailure) { ++data.failed; ++data.total; } testState.global = data; var success = data.failed === 0 && data.total !== 0; var separator = jqUnit.stylize("***************", [success ? "green" : "red", "bold"]); jqUnit.log(separator); if (data.total === 0) { jqUnit.log("Failure in fixture file - no tests were queued - " + jqUnit.passFail(1)); } else { jqUnit.log("All tests concluded: " + jqUnit.passCount(data) + " total passed in " + data.runtime + "ms - " + jqUnit.passFail(data.failed)); } jqUnit.log(separator); jqUnit.onAllTestsDone.fire(data); process.exit(success ? 0 : 1); }; /** * Callback for all done tests in the file. * @param {Object} res */ // Debounce this to deal with case where QUnit notifies that all tests are done, when in fact // it has just synchronously executed the first of a set of synchronous tests and another is // just about to arrive. // This strategy taken from kof's node-qunit: https://github.com/kof/node-qunit/blob/master/lib/child.js QUnit.done(fluid.debounce(function (data) { jqUnit.allTestsDone(data); }), 100); jqUnit.testState = testState; module.exports = jqUnit;