From 0820d3771f8b773fc00cd343f919199196fd4daf Mon Sep 17 00:00:00 2001 From: Timo Stollenwerk Date: Sat, 30 Oct 2010 10:22:21 +0000 Subject: [PATCH] Basic js-test-driver configuration added. svn path=/plone.app.discussion/trunk/; revision=40892 --- plone/app/discussion/tests/jsTestDriver.conf | 13 ++ plone/app/discussion/tests/jsTestDriver.txt | 5 + .../discussion/tests/qunit/QUnitAdapter.js | 85 ++++++++ plone/app/discussion/tests/qunit/equiv.js | 185 ++++++++++++++++++ 4 files changed, 288 insertions(+) create mode 100644 plone/app/discussion/tests/jsTestDriver.conf create mode 100644 plone/app/discussion/tests/jsTestDriver.txt create mode 100644 plone/app/discussion/tests/qunit/QUnitAdapter.js create mode 100644 plone/app/discussion/tests/qunit/equiv.js diff --git a/plone/app/discussion/tests/jsTestDriver.conf b/plone/app/discussion/tests/jsTestDriver.conf new file mode 100644 index 0000000..3a69f40 --- /dev/null +++ b/plone/app/discussion/tests/jsTestDriver.conf @@ -0,0 +1,13 @@ +server: http://localhost:9876 + +load: + # Add these lines to load the equiv function and adapter in order, before the + # tests (assuming they are saved to tests/qunit/) + - qunit/equiv.js + - qunit/QUnitAdapter.js + + # This is where we load the qunit tests + - javascripts/*.js + + # And this loads the source files we are testing + - ../browser/javascripts/*.js diff --git a/plone/app/discussion/tests/jsTestDriver.txt b/plone/app/discussion/tests/jsTestDriver.txt new file mode 100644 index 0000000..7751bb8 --- /dev/null +++ b/plone/app/discussion/tests/jsTestDriver.txt @@ -0,0 +1,5 @@ +============== +JS TEST DRIVER +============== + +java -jar JsTestDriver.jar --port 9876 --config jsTestDriver.conf --browser /usr/bin/firefox --tests all \ No newline at end of file diff --git a/plone/app/discussion/tests/qunit/QUnitAdapter.js b/plone/app/discussion/tests/qunit/QUnitAdapter.js new file mode 100644 index 0000000..5521ae2 --- /dev/null +++ b/plone/app/discussion/tests/qunit/QUnitAdapter.js @@ -0,0 +1,85 @@ +/* +QUnitAdapter +Version: 1.1.0 + +Run qunit tests using JS Test Driver + +This provides almost the same api as qunit. + +Tests must run sychronously, which means no use of stop and start methods. +You can use jsUnit Clock object to deal with timeouts and intervals: +http://googletesting.blogspot.com/2007/03/javascript-simulating-time-in-jsunit.html + +The qunit #main DOM element is not included. If you need to do any DOM manipulation +you need to set it up and tear it down in each test. + +*/ +(function() { + + if(!(window.equiv)) { + throw new Error("QUnitAdapter.js - Unable to find equiv function. Ensure you have added equiv.js to the load section of your jsTestDriver.conf"); + } + + var QUnitTestCase; + + window.module = function(name, lifecycle) { + QUnitTestCase = TestCase(name); + QUnitTestCase.prototype.lifecycle = lifecycle || {}; + }; + + window.test = function(name, expected, test) { + QUnitTestCase.prototype['test ' + name] = function() { + if(this.lifecycle.setup) { + this.lifecycle.setup(); + } + if(expected.constructor === Number) { + expectAsserts(expected); + } else { + test = expected; + } + test.call(this.lifecycle); + + if(this.lifecycle.teardown) { + this.lifecycle.teardown(); + } + }; + }; + + window.expect = function(count) { + expectAsserts(count); + }; + + window.ok = function(actual, msg) { + assertTrue(msg ? msg : '', !!actual); + }; + + window.equals = function(a, b, msg) { + assertEquals(msg ? msg : '', b, a); + }; + + window.start = window.stop = function() { + fail('start and stop methods are not available when using JS Test Driver.\n' + + 'Use jsUnit Clock object to deal with timeouts and intervals:\n' + + 'http://googletesting.blogspot.com/2007/03/javascript-simulating-time-in-jsunit.html.'); + }; + + window.same = function(a, b, msg) { + assertTrue(msg ? msg : '', window.equiv(b, a)); + }; + + window.reset = function() { + fail('reset method is not available when using JS Test Driver'); + }; + + window.isLocal = function() { + return false; + }; + + window.QUnit = { + equiv: window.equiv, + ok: window.ok + }; + + module('Default Module'); + +})(); diff --git a/plone/app/discussion/tests/qunit/equiv.js b/plone/app/discussion/tests/qunit/equiv.js new file mode 100644 index 0000000..2f5b9f3 --- /dev/null +++ b/plone/app/discussion/tests/qunit/equiv.js @@ -0,0 +1,185 @@ + +// Tests for equality any JavaScript type and structure without unexpected results. +// Discussions and reference: http://philrathe.com/articles/equiv +// Test suites: http://philrathe.com/tests/equiv +// Author: Philippe Rath +window.equiv = function () { + + var innerEquiv; // the real equiv function + var callers = []; // stack to decide between skip/abort functions + + // Determine what is o. + function hoozit(o) { + if (typeof o === "string") { + return "string"; + + } else if (typeof o === "boolean") { + return "boolean"; + + } else if (typeof o === "number") { + + if (isNaN(o)) { + return "nan"; + } else { + return "number"; + } + + } else if (typeof o === "undefined") { + return "undefined"; + + // consider: typeof null === object + } else if (o === null) { + return "null"; + + // consider: typeof [] === object + } else if (o instanceof Array) { + return "array"; + + // consider: typeof new Date() === object + } else if (o instanceof Date) { + return "date"; + + // consider: /./ instanceof Object; + // /./ instanceof RegExp; + // typeof /./ === "function"; // => false in IE and Opera, + // true in FF and Safari + } else if (o instanceof RegExp) { + return "regexp"; + + } else if (typeof o === "object") { + return "object"; + + } else if (o instanceof Function) { + return "function"; + } + } + + // Call the o related callback with the given arguments. + function bindCallbacks(o, callbacks, args) { + var prop = hoozit(o); + if (prop) { + if (hoozit(callbacks[prop]) === "function") { + return callbacks[prop].apply(callbacks, args); + } else { + return callbacks[prop]; // or undefined + } + } + } + + var callbacks = function () { + + // for string, boolean, number and null + function useStrictEquality(b, a) { + return a === b; + } + + return { + "string": useStrictEquality, + "boolean": useStrictEquality, + "number": useStrictEquality, + "null": useStrictEquality, + "undefined": useStrictEquality, + + "nan": function (b) { + return isNaN(b); + }, + + "date": function (b, a) { + return hoozit(b) === "date" && a.valueOf() === b.valueOf(); + }, + + "regexp": function (b, a) { + return hoozit(b) === "regexp" && + a.source === b.source && // the regex itself + a.global === b.global && // and its modifers (gmi) ... + a.ignoreCase === b.ignoreCase && + a.multiline === b.multiline; + }, + + // - skip when the property is a method of an instance (OOP) + // - abort otherwise, + // initial === would have catch identical references anyway + "function": function () { + var caller = callers[callers.length - 1]; + return caller !== Object && + typeof caller !== "undefined"; + }, + + "array": function (b, a) { + var i; + var len; + + // b could be an object literal here + if ( ! (hoozit(b) === "array")) { + return false; + } + + len = a.length; + if (len !== b.length) { // safe and faster + return false; + } + for (i = 0; i < len; i++) { + if( ! innerEquiv(a[i], b[i])) { + return false; + } + } + return true; + }, + + "object": function (b, a) { + var i; + var eq = true; // unless we can proove it + var aProperties = [], bProperties = []; // collection of strings + + // comparing constructors is more strict than using instanceof + if ( a.constructor !== b.constructor) { + return false; + } + + // stack constructor before traversing properties + callers.push(a.constructor); + + for (i in a) { // be strict: don't ensures hasOwnProperty and go deep + + aProperties.push(i); // collect a's properties + + if ( ! innerEquiv(a[i], b[i])) { + eq = false; + } + } + + callers.pop(); // unstack, we are done + + for (i in b) { + bProperties.push(i); // collect b's properties + } + + // Ensures identical properties name + return eq && innerEquiv(aProperties.sort(), bProperties.sort()); + } + }; + }(); + + innerEquiv = function () { // can take multiple arguments + var args = Array.prototype.slice.apply(arguments); + if (args.length < 2) { + return true; // end transition + } + + return (function (a, b) { + if (a === b) { + return true; // catch the most you can + + } else if (typeof a !== typeof b || a === null || b === null || typeof a === "undefined" || typeof b === "undefined") { + return false; // don't lose time with error prone cases + + } else { + return bindCallbacks(a, callbacks, [b, a]); + } + + // apply transition with (1..n) arguments + })(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length -1)); + }; + + return innerEquiv; +}(); // equiv