From 89a9a747b603ae21945aa0cbcdf13560d3e7cea1 Mon Sep 17 00:00:00 2001 From: John Resig Date: Sun, 8 Jul 2007 05:00:24 +0000 Subject: [PATCH] Automated test running using Rhino (no browser required!). Still very buggy, but off to a good start. --- Makefile | 7 + build/runtest/env.js | 393 ++++++++++++++++++++++++++++++++++++ build/runtest/test.js | 12 ++ build/runtest/testrunner.js | 163 +++++++++++++++ 4 files changed, 575 insertions(+) create mode 100644 build/runtest/env.js create mode 100644 build/runtest/test.js create mode 100644 build/runtest/testrunner.js diff --git a/Makefile b/Makefile index ff694884..ad7171ff 100644 --- a/Makefile +++ b/Makefile @@ -127,6 +127,13 @@ test: ${JQ} @@echo "Test Suite Built" @@echo +runtest: ${JQ} test + @@echo "Running Automated Test Suite" + @@${JAR} ${BUILD_DIR}/runtest/test.js + + @@echo "Test Suite Finished" + @@echo + docs: ${JQ} @@echo "Building Documentation" diff --git a/build/runtest/env.js b/build/runtest/env.js new file mode 100644 index 00000000..0d3be388 --- /dev/null +++ b/build/runtest/env.js @@ -0,0 +1,393 @@ +// The window Object +var window = this; + +(function(){ + + // Browser Navigator + + window.navigator = { + get userAgent(){ + return "Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.3) Gecko/20070309 Firefox/2.0.0.3"; + } + }; + + window.__defineSetter__("location", function(url){ + window.document = new DOMDocument(url); + }); + + window.__defineGetter__("location", function(url){ + return { + get protocol(){ + return "file:"; + } + }; + }); + + // Timers + + var timers = []; + + window.setTimeout = function(fn, time){ + var num; + return num = setInterval(function(){ + fn(); + clearInterval(num); + }, time); + }; + + window.setInterval = function(fn, time){ + var num = timers.length; + + timers[num] = new java.lang.Thread(new java.lang.Runnable({ + run: function(){ + while (true){ + java.lang.Thread.currentThread().sleep(time); + fn(); + } + } + })); + + timers[num].start(); + + return num; + }; + + window.clearInterval = function(num){ + if ( timers[num] ) { + timers[num].stop(); + delete timers[num]; + } + }; + + // Window Events + + window.addEventListener = function(){}; + window.removeEventListener = function(){}; + + // DOM Document + + window.DOMDocument = function(file){ + this._file = file; + this._dom = Packages.javax.xml.parsers. + DocumentBuilderFactory.newInstance() + .newDocumentBuilder().parse(file); + }; + + DOMDocument.prototype = { + createTextNode: function(text){ + return makeNode( this._dom.createTextNode(text) ); + }, + createElement: function(name){ + return makeNode( this._dom.createElement(name) ); + }, + getElementsByTagName: function(name){ + return new DOMNodeList( this._dom.getElementsByTagName(name) ); + }, + getElementById: function(id){ + var elems = this._dom.getElementsByTagName("*"); + + for ( var i = 0; i < elems.length; i++ ) { + var elem = elems.item(i); + if ( elem.getAttribute("id") == id ) + return makeNode(elem); + } + + return null; + }, + get body(){ + return this.getElementsByTagName("body")[0]; + }, + defaultView: { + getComputedStyle: { + getPropertyValue: function(){ } + } + }, + get documentElement(){ + return makeNode( this._dom.getDocumentElement() ); + }, + get ownerDocument(){ + return this; + }, + addEventListener: function(){}, + removeEventListener: function(){}, + get nodeName() { + return "#document"; + }, + importNode: function(node, deep){ + return makeNode( this._dom.importNode(node._dom, deep) ); + }, + toString: function(){ + return "Document" + (typeof this._file == "string" ? + ": " + this._file : ""); + }, + + get defaultView(){ + return { + getComputedStyle: function(){ + return { + getPropertyValue: function(){ + return ""; + } + } + } + }; + } + }; + + // DOM NodeList + + window.DOMNodeList = function(list){ + this._dom = list; + this.length = list.getLength(); + + for ( var i = 0; i < this.length; i++ ) { + var node = list.item(i); + this[i] = makeNode( node ); + } + }; + + DOMNodeList.prototype = { + toString: function(){ + return "[ " + + Array.prototype.join.call( this, ", " ) + " ]"; + }, + valueOf: function(){ + return Array.prototype.map.call( + this, function(node){return node.valueOf();}).join(''); + } + }; + + // DOM Node + + window.DOMNode = function(node){ + this._dom = node; + }; + + DOMNode.prototype = { + get nodeType(){ + return this._dom.getNodeType(); + }, + get nodeValue(){ + return this._dom.getNodeValue(); + }, + get nodeName() { + return this._dom.getNodeName(); + }, + cloneNode: function(deep){ + return makeNode( this._dom.cloneNode(deep) ); + }, + get ownerDocument(){ + return document; + }, + get documentElement(){ + return document.documentElement; + }, + get parentNode() { + return makeNode( this._dom.getParentNode() ); + }, + get nextSibling() { + return makeNode( this._dom.getNextSibling() ); + }, + get previousSibling() { + return makeNode( this._dom.getPreviousSibling() ); + }, + toString: function(){ + return '"' + this.nodeValue + '"'; + }, + valueOf: function(){ + return this.nodeValue; + } + }; + + // DOM Element + + window.DOMElement = function(elem){ + this._dom = elem; + }; + + DOMElement.prototype = extend( new DOMNode(), { + get nodeName(){ + return this.tagName.toUpperCase(); + }, + get tagName(){ + return this._dom.getTagName(); + }, + toString: function(){ + return "<" + this.tagName + (this.id ? "#" + this.id : "" ) + ">"; + }, + valueOf: function(){ + var ret = "<" + this.tagName, attr = this.attributes; + + for ( var i in attr ) + ret += " " + i + "='" + attr[i] + "'"; + + if ( this.childNodes.length || this.nodeName == "SCRIPT" ) + ret += ">" + this.childNodes.valueOf() + + ""; + else + ret += "/>"; + + return ret; + }, + + get attributes(){ + var attr = {}, attrs = this._dom.getAttributes(); + + for ( var i = 0; i < attrs.getLength(); i++ ) + attr[ attrs.item(i).nodeName ] = attrs.item(i).nodeValue; + + return attr; + }, + + get innerHTML(){ + return this.childNodes.valueOf(); + }, + set innerHTML(html){ + var nodes = this.ownerDocument.importNode( + new DOMDocument( new java.io.ByteArrayInputStream( + (new java.lang.String("" + html + "")) + .getBytes())).documentElement, true).childNodes; + + while (this.firstChild) + this.removeChild( this.firstChild ); + + for ( var i = 0; i < nodes.length; i++ ) + this.appendChild( nodes[i] ); + }, + + get textContent(){ + return nav(this.childNodes); + + function nav(nodes){ + var str = ""; + for ( var i = 0; i < nodes.length; i++ ) + if ( nodes[i].nodeType == 3 ) + str += nodes[i].nodeValue; + else if ( nodes[i].nodeType == 1 ) + str += nav(nodes[i].childNodes); + return str; + } + }, + set textContent(text){ + while (this.firstChild) + this.removeChild( this.firstChild ); + this.appendChild( document.createTextNode(text) ); + this.innerHTML = document.createTextNode(text).nodeValue; + }, + + style: {}, + clientHeight: 0, + clientWidth: 0, + offsetHeight: 0, + offsetWidth: 0, + + get disabled() { return !!this.getAttribute("disabled"); }, + set disabled(val) { return this.setAttribute("disabled",val); }, + + get checked() { return !!this.getAttribute("checked"); }, + set checked(val) { return this.setAttribute("checked",val); }, + + get selected() { return !!this.getAttribute("selected"); }, + set selected(val) { return this.setAttribute("selected",val); }, + + get className() { return this.getAttribute("class") || ""; }, + set className(val) { return this.setAttribute("class",val); }, + + get type() { return this.getAttribute("type") || ""; }, + set type(val) { return this.setAttribute("type",val); }, + + get src() { return this.getAttribute("src") || ""; }, + set src(val) { return this.setAttribute("src",val); }, + + get id() { return this.getAttribute("id") || ""; }, + set id(val) { return this.setAttribute("id",val); }, + + getAttribute: function(name){ + return this._dom.hasAttribute(name) ? + this._dom.getAttribute(name) : + null; + }, + setAttribute: function(name,value){ + this._dom.setAttribute(name,value); + }, + removeAttribute: function(name){ + this._dom.removeAttribute(name); + }, + + get childNodes(){ + return new DOMNodeList( this._dom.getChildNodes() ); + }, + get firstChild(){ + return makeNode( this._dom.getFirstChild() ); + }, + get lastChild(){ + return makeNode( this._dom.getLastChild() ); + }, + appendChild: function(node){ + this._dom.appendChild( node._dom ); + }, + insertBefore: function(node,before){ + this._dom.insertBefore( node._dom, before ? before._dom : before ); + }, + removeChild: function(node){ + this._dom.removeChild( node._dom ); + }, + + getElementsByTagName: DOMDocument.prototype.getElementsByTagName, + addEventListener: function(){}, + removeEventListener: function(){}, + click: function(){}, + submit: function(){}, + focus: function(){}, + blur: function(){}, + elements: [] + }); + + // Helper method for extending one object with another + + function extend(a,b) { + for ( var i in b ) { + var g = b.__lookupGetter__(i), s = b.__lookupSetter__(i); + + if ( g || s ) { + if ( g ) + a.__defineGetter__(i, g); + if ( s ) + a.__defineSetter__(i, s); + } else + a[i] = b[i]; + } + return a; + } + + // Helper method for generating the right + // DOM objects based upon the type + + var obj_nodes = new java.util.HashMap(); + + function makeNode(node){ + if ( node ) { + if ( !obj_nodes.containsKey( node ) ) + obj_nodes.put( node, node.getNodeType() == + Packages.org.w3c.dom.Node.ELEMENT_NODE ? + new DOMElement( node ) : new DOMNode( node ) ); + + return obj_nodes.get(node); + } else + return null; + } + + // XMLHttpRequest + + window.XMLHttpRequest = function(){ }; + + XMLHttpRequest.prototype = { + open: function(){ }, + setRequestHeader: function(){ }, + getResponseHeader: function(){ }, + readyState: 0, + responseText: "", + responseXML: {}, + status: 0 + }; +})(); diff --git a/build/runtest/test.js b/build/runtest/test.js new file mode 100644 index 00000000..d793ca81 --- /dev/null +++ b/build/runtest/test.js @@ -0,0 +1,12 @@ +// Init +load("build/runtest/env.js"); +window.location = "test/index.html"; + +// Load the test runner +load("dist/jquery.js","build/runtest/testrunner.js"); + +// Load the tests +load("src/jquery/coreTest.js","src/selector/selectorTest.js"); + +// Display the results +results(); diff --git a/build/runtest/testrunner.js b/build/runtest/testrunner.js new file mode 100644 index 00000000..97441001 --- /dev/null +++ b/build/runtest/testrunner.js @@ -0,0 +1,163 @@ +function test(name, fn){ + expected = -1; + numTests = 0; + reset(); + + fn(); + + if ( expected != -1 && expected != numTests ) + log( false, "Wrong number of tests run. " + numTests + " ran, expected " + expected ); +} + +var orig = document.getElementById('main').innerHTML; + +/** + * Resets the test setup. Useful for tests that modify the DOM. + */ +function reset() { + document.getElementById('main').innerHTML = orig; +} + +var currentModule = ""; + +// call on start of module test to prepend name to all tests +function module(moduleName) { + currentModule = moduleName; +} + +var expected = -1; + +/** + * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through. + */ +function expect(asserts) { + expected = asserts; +} + +/** + * Asserts true. + * @example ok( $("a").size() > 5, "There must be at least 5 anchors" ); + */ +function ok(a, msg) { + log( !!a, msg ); +} + +/** + * Asserts that two arrays are the same + */ +function isSet(a, b, msg) { + var ret = true; + if ( a && b && a.length != undefined && a.length == b.length ) { + for ( var i = 0; i < a.length; i++ ) { + if ( a[i] != b[i] ) + ret = false; + } + } else + ret = false; + if ( !ret ) + log( ret, msg + " expected: " + serialArray(b) + " result: " + serialArray(a) ); + else + log( ret, msg ); +} + +/** + * Asserts that two objects are equivalent + */ +function isObj(a, b, msg) { + var ret = true; + + if ( a && b ) { + for ( var i in a ) + if ( a[i] != b[i] ) + ret = false; + + for ( i in b ) + if ( a[i] != b[i] ) + ret = false; + } else + ret = false; + + log( ret, msg ); +} + +function serialArray( a ) { + var r = []; + + if ( a && a.length ) + for ( var i = 0; i < a.length; i++ ) { + var str = a[i] ? a[i].nodeName : ""; + if ( str ) { + str = str.toLowerCase(); + if ( a[i].id ) + str += "#" + a[i].id; + } else + str = a[i]; + r.push( str ); + } + + return "[ " + r.join(", ") + " ]" +} + +/** + * Returns an array of elements with the given IDs, eg. + * @example q("main", "foo", "bar") + * @result [
, , ] + */ +function q() { + var r = []; + for ( var i = 0; i < arguments.length; i++ ) + r.push( document.getElementById( arguments[i] ) ); + return r; +} + +/** + * Asserts that a select matches the given IDs + * @example t("Check for something", "//[a]", ["foo", "baar"]); + * @result returns true if "//[a]" return two elements with the IDs 'foo' and 'baar' + */ +function t(a,b,c) { + var f = jQuery(b); + var s = ""; + for ( var i = 0; i < f.length; i++ ) + s += (s && ",") + '"' + f[i].id + '"'; + isSet(f, q.apply(q,c), a + " (" + b + ")"); +} + +/** + * Checks that the first two arguments are equal, with an optional message. + * Prints out both expected and actual values on failure. + * + * Prefered to ok( expected == actual, message ) + * + * @example equals( "Expected 2 characters.", v.formatMessage("Expected {0} characters.", 2) ); + * + * @param Object expected + * @param Object actual + * @param String message (optional) + */ +function equals(expected, actual, message) { + var result = expected == actual; + message = message || (result ? "okay" : "failed"); + log( result, result ? message + ": " + expected : message + " expected: " + expected + " actual: " + actual ); +} + +var numTests = 0, total = 0, pass = 0, fail = 0; + +function log(state, msg){ + print( (state ? "PASS" : "FAIL") + " (" + (++total) + ") " + + (currentModule ? "[" + currentModule + "] " : "") + msg ); + + numTests++; + + if ( state ) + pass++; + else + fail++; +} + +function results(){ + print( pass + " Passed, " + fail + " Failed" ); +} + +function start(){} +function stop(){} \ No newline at end of file