Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

first commit

  • Loading branch information...
commit 2832ba020a9c234c09dd2a74efc45c2c065f3b1f 0 parents
@phstc authored
0  README
No changes.
70 Test.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>JavaScript unit test file</title>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <script src="assets/jsunittest.js" type="text/javascript"></script>
+
+ <script src="jquery-1.4.min.js" type="text/javascript"></script>
+ <script src="jquery.dateFormat-1.0.js" type="text/javascript"></script>
+
+ <link rel="stylesheet" href="assets/unittest.css" type="text/css" />
+</head>
+<body>
+
+<div id="content">
+
+ <div id="header">
+ <h1>JavaScript unit test file</h1>
+ </div>
+
+ <!-- Log output (one per Runner, via {testLog: "testlog"} option)-->
+ <div id="testlog"></div>
+
+ <!-- Put sample/test html here -->
+ <div id="sample">
+ </div>
+</div>
+
+<script type="text/javascript"><!--
+// <![CDATA[
+ new Test.Unit.Runner({
+ setup: function() {
+ },
+
+ teardown: function() {
+
+ },
+
+ testTruth: function() { with(this) {
+ assert(true);
+ }},
+
+ testFormatDateType1 : function() { with(this) {
+ assertEqual("18/12/2009", $.format.date("2009-12-18 10:54:50.546", "dd/MM/yyyy"));
+ assertEqual("2009", $.format.date("2009-12-18 10:54:50.546", "yyyy"));
+ assertEqual("12", $.format.date("2009-12-18 10:54:50.546", "MM"));
+ assertEqual("10:54:50", $.format.date("2009-12-18 10:54:50.546", "hh:mm:ss"));
+ assertEqual("10", $.format.date("2009-12-18 10:54:50.546", "hh"));
+ assertEqual("54", $.format.date("2009-12-18 10:54:50.546", "mm"));
+ assertEqual("50", $.format.date("2009-12-18 10:54:50.546", "ss"));
+ assertEqual("18/12/2009 10:54:50", $.format.date("2009-12-18 10:54:50.546", "dd/MM/yyyy hh:mm:ss"));
+ assertEqual("a18/b12/c2009 d10:e54:f50", $.format.date("2009-12-18 10:54:50.546", "add/bMM/cyyyy dhh:emm:fss"));
+ }},
+ testFormatDateType2 : function() { with(this) {
+ assertEqual("13/01/2010", $.format.date("Wed Jan 13 10:43:41 CET 2010", "dd/MM/yyyy"));
+ assertEqual("2010", $.format.date("Wed Jan 13 10:43:41 CET 2010", "yyyy"));
+ assertEqual("01", $.format.date("Wed Jan 13 10:43:41 CET 2010", "MM"));
+ assertEqual("10:43:41", $.format.date("Wed Jan 13 10:43:41 CET 2010", "hh:mm:ss"));
+ assertEqual("10", $.format.date("Wed Jan 13 10:43:41 CET 2010", "hh"));
+ assertEqual("43", $.format.date("Wed Jan 13 10:43:41 CET 2010", "mm"));
+ assertEqual("41", $.format.date("Wed Jan 13 10:43:41 CET 2010", "ss"));
+ assertEqual("13/01/2010 10:43:41", $.format.date("Wed Jan 13 10:43:41 CET 2010", "dd/MM/yyyy hh:mm:ss"));
+ assertEqual("a13/b01/c2010 d10:e43:f41", $.format.date("Wed Jan 13 10:43:41 CET 2010", "add/bMM/cyyyy dhh:emm:fss"));
+ }}
+ });
+// ]]>
+</script>
+</body>
+</html>
17 assets/.svn/all-wcprops
@@ -0,0 +1,17 @@
+K 25
+svn:wc:ra_dav:version-url
+V 77
+/svn/procode/!svn/ver/1060/trunk/casino/casino-site/src/main/webapp/js/assets
+END
+unittest.css
+K 25
+svn:wc:ra_dav:version-url
+V 90
+/svn/procode/!svn/ver/1060/trunk/casino/casino-site/src/main/webapp/js/assets/unittest.css
+END
+jsunittest.js
+K 25
+svn:wc:ra_dav:version-url
+V 91
+/svn/procode/!svn/ver/1060/trunk/casino/casino-site/src/main/webapp/js/assets/jsunittest.js
+END
103 assets/.svn/entries
@@ -0,0 +1,103 @@
+9
+
+dir
+1060
+http://192.168.1.10/svn/procode/trunk/casino/casino-site/src/main/webapp/js/assets
+http://192.168.1.10/svn/procode
+
+
+
+2010-01-20T16:00:55.624677Z
+1060
+pablo
+
+
+svn:special svn:externals svn:needs-lock
+
+
+
+
+
+
+
+
+
+
+
+f65fe1d0-ca4a-48a2-bd1b-8669af1882df
+
+
+
+
+
+
+0
+
+unittest.css
+file
+
+
+
+
+2010-01-20T15:44:04.281250Z
+b5c1cce0185feace410e8caf9685f75a
+2010-01-20T16:00:55.624677Z
+1060
+pablo
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+822
+
+jsunittest.js
+file
+
+
+
+
+2010-01-20T15:44:04.250000Z
+4f3d16e24df4358814595ca6107568e4
+2010-01-20T16:00:55.624677Z
+1060
+pablo
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+35185
+
1  assets/.svn/format
@@ -0,0 +1 @@
+9
1,017 assets/.svn/text-base/jsunittest.js.svn-base
@@ -0,0 +1,1017 @@
+/* Jsunittest, version 0.7.3
+ * (c) 2008 Dr Nic Williams
+ *
+ * Jsunittest is freely distributable under
+ * the terms of an MIT-style license.
+ * For details, see the web site: http://jsunittest.rubyforge.org
+ *
+ *--------------------------------------------------------------------------*/
+
+var JsUnitTest = {
+ Unit: {},
+ inspect: function(object) {
+ try {
+ if (typeof object == "undefined") return 'undefined';
+ if (object === null) return 'null';
+ if (typeof object == "string") {
+ var useDoubleQuotes = arguments[1];
+ var escapedString = this.gsub(object, /[\x00-\x1f\\]/, function(match) {
+ var character = String.specialChar[match[0]];
+ return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
+ });
+ if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
+ return "'" + escapedString.replace(/'/g, '\\\'') + "'";
+ };
+ return String(object);
+ } catch (e) {
+ if (e instanceof RangeError) return '...';
+ throw e;
+ }
+ },
+ $: function(element) {
+ if (arguments.length > 1) {
+ for (var i = 0, elements = [], length = arguments.length; i < length; i++)
+ elements.push(this.$(arguments[i]));
+ return elements;
+ }
+ if (typeof element == "string")
+ element = document.getElementById(element);
+ return element;
+ },
+ gsub: function(source, pattern, replacement) {
+ var result = '', match;
+ replacement = arguments.callee.prepareReplacement(replacement);
+
+ while (source.length > 0) {
+ if (match = source.match(pattern)) {
+ result += source.slice(0, match.index);
+ result += JsUnitTest.String.interpret(replacement(match));
+ source = source.slice(match.index + match[0].length);
+ } else {
+ result += source, source = '';
+ }
+ }
+ return result;
+ },
+ scan: function(source, pattern, iterator) {
+ this.gsub(source, pattern, iterator);
+ return String(source);
+ },
+ escapeHTML: function(data) {
+ return data.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
+ },
+ arrayfromargs: function(args) {
+ var myarray = new Array();
+ var i;
+
+ for (i=0;i<args.length;i++)
+ myarray[i] = args[i];
+
+ return myarray;
+ },
+ hashToSortedArray: function(hash) {
+ var results = [];
+ for (key in hash) {
+ results.push([key, hash[key]]);
+ }
+ return results.sort();
+ },
+ flattenArray: function(array) {
+ var results = arguments[1] || [];
+ for (var i=0; i < array.length; i++) {
+ var object = array[i];
+ if (object != null && typeof object == "object" &&
+ 'splice' in object && 'join' in object) {
+ this.flattenArray(object, results);
+ } else {
+ results.push(object);
+ }
+ };
+ return results;
+ },
+ selectorMatch: function(expression, element) {
+ var tokens = [];
+ var patterns = {
+ // combinators must be listed first
+ // (and descendant needs to be last combinator)
+ laterSibling: /^\s*~\s*/,
+ child: /^\s*>\s*/,
+ adjacent: /^\s*\+\s*/,
+ descendant: /^\s/,
+
+ // selectors follow
+ tagName: /^\s*(\*|[\w\-]+)(\b|$)?/,
+ id: /^#([\w\-\*]+)(\b|$)/,
+ className: /^\.([\w\-\*]+)(\b|$)/,
+ pseudo:
+ /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/,
+ attrPresence: /^\[((?:[\w]+:)?[\w]+)\]/,
+ attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
+ };
+
+ var assertions = {
+ tagName: function(element, matches) {
+ return matches[1].toUpperCase() == element.tagName.toUpperCase();
+ },
+
+ className: function(element, matches) {
+ return Element.hasClassName(element, matches[1]);
+ },
+
+ id: function(element, matches) {
+ return element.id === matches[1];
+ },
+
+ attrPresence: function(element, matches) {
+ return Element.hasAttribute(element, matches[1]);
+ },
+
+ attr: function(element, matches) {
+ var nodeValue = Element.readAttribute(element, matches[1]);
+ return nodeValue && operators[matches[2]](nodeValue, matches[5] || matches[6]);
+ }
+ };
+ var e = this.expression, ps = patterns, as = assertions;
+ var le, p, m;
+
+ while (e && le !== e && (/\S/).test(e)) {
+ le = e;
+ for (var i in ps) {
+ p = ps[i];
+ if (m = e.match(p)) {
+ // use the Selector.assertions methods unless the selector
+ // is too complex.
+ if (as[i]) {
+ tokens.push([i, Object.clone(m)]);
+ e = e.replace(m[0], '');
+ }
+ }
+ }
+ }
+
+ var match = true, name, matches;
+ for (var i = 0, token; token = tokens[i]; i++) {
+ name = token[0], matches = token[1];
+ if (!assertions[name](element, matches)) {
+ match = false; break;
+ }
+ }
+
+ return match;
+ },
+ toQueryParams: function(query, separator) {
+ var query = query || window.location.search;
+ var match = query.replace(/^\s+/, '').replace(/\s+$/, '').match(/([^?#]*)(#.*)?$/);
+ if (!match) return { };
+
+ var hash = {};
+ var parts = match[1].split(separator || '&');
+ for (var i=0; i < parts.length; i++) {
+ var pair = parts[i].split('=');
+ if (pair[0]) {
+ var key = decodeURIComponent(pair.shift());
+ var value = pair.length > 1 ? pair.join('=') : pair[0];
+ if (value != undefined) value = decodeURIComponent(value);
+
+ if (key in hash) {
+ var object = hash[key];
+ var isArray = object != null && typeof object == "object" &&
+ 'splice' in object && 'join' in object
+ if (!isArray) hash[key] = [hash[key]];
+ hash[key].push(value);
+ }
+ else hash[key] = value;
+ }
+ };
+ return hash;
+ },
+
+ String: {
+ interpret: function(value) {
+ return value == null ? '' : String(value);
+ }
+ }
+};
+
+JsUnitTest.gsub.prepareReplacement = function(replacement) {
+ if (typeof replacement == "function") return replacement;
+ var template = new Template(replacement);
+ return function(match) { return template.evaluate(match) };
+};
+
+JsUnitTest.Version = '0.7.3';
+
+JsUnitTest.Template = function(template, pattern) {
+ this.template = template; //template.toString();
+ this.pattern = pattern || JsUnitTest.Template.Pattern;
+};
+
+JsUnitTest.Template.prototype.evaluate = function(object) {
+ if (typeof object.toTemplateReplacements == "function")
+ object = object.toTemplateReplacements();
+
+ return JsUnitTest.gsub(this.template, this.pattern, function(match) {
+ if (object == null) return '';
+
+ var before = match[1] || '';
+ if (before == '\\') return match[2];
+
+ var ctx = object, expr = match[3];
+ var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
+ match = pattern.exec(expr);
+ if (match == null) return before;
+
+ while (match != null) {
+ var comp = (match[1].indexOf('[]') === 0) ? match[2].gsub('\\\\]', ']') : match[1];
+ ctx = ctx[comp];
+ if (null == ctx || '' == match[3]) break;
+ expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
+ match = pattern.exec(expr);
+ }
+
+ return before + JsUnitTest.String.interpret(ctx);
+ });
+}
+
+JsUnitTest.Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
+JsUnitTest.Event = {};
+// written by Dean Edwards, 2005
+// with input from Tino Zijdel, Matthias Miller, Diego Perini
+// namespaced by Dr Nic Williams 2008
+
+// http://dean.edwards.name/weblog/2005/10/add-event/
+// http://dean.edwards.name/weblog/2005/10/add-event2/
+JsUnitTest.Event.addEvent = function(element, type, handler) {
+ if (element.addEventListener) {
+ element.addEventListener(type, handler, false);
+ } else {
+ // assign each event handler a unique ID
+ if (!handler.$$guid) handler.$$guid = JsUnitTest.Event.addEvent.guid++;
+ // create a hash table of event types for the element
+ if (!element.events) element.events = {};
+ // create a hash table of event handlers for each element/event pair
+ var handlers = element.events[type];
+ if (!handlers) {
+ handlers = element.events[type] = {};
+ // store the existing event handler (if there is one)
+ if (element["on" + type]) {
+ handlers[0] = element["on" + type];
+ }
+ }
+ // store the event handler in the hash table
+ handlers[handler.$$guid] = handler;
+ // assign a global event handler to do all the work
+ element["on" + type] = this.handleEvent;
+ }
+};
+// a counter used to create unique IDs
+JsUnitTest.Event.addEvent.guid = 1;
+
+JsUnitTest.Event.removeEvent = function(element, type, handler) {
+ if (element.removeEventListener) {
+ element.removeEventListener(type, handler, false);
+ } else {
+ // delete the event handler from the hash table
+ if (element.events && element.events[type]) {
+ delete element.events[type][handler.$$guid];
+ }
+ }
+};
+
+JsUnitTest.Event.handleEvent = function(event) {
+ var returnValue = true;
+ // grab the event object (IE uses a global event object)
+ event = event || JsUnitTest.Event.fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event);
+ // get a reference to the hash table of event handlers
+ var handlers = this.events[event.type];
+ // execute each event handler
+ for (var i in handlers) {
+ this.$$handleEvent = handlers[i];
+ if (this.$$handleEvent(event) === false) {
+ returnValue = false;
+ }
+ }
+ return returnValue;
+};
+
+JsUnitTest.Event.fixEvent = function(event) {
+ // add W3C standard event methods
+ event.preventDefault = this.fixEvent.preventDefault;
+ event.stopPropagation = this.fixEvent.stopPropagation;
+ return event;
+};
+JsUnitTest.Event.fixEvent.preventDefault = function() {
+ this.returnValue = false;
+};
+JsUnitTest.Event.fixEvent.stopPropagation = function() {
+ this.cancelBubble = true;
+};
+
+JsUnitTest.Unit.Logger = function(element) {
+ this.element = JsUnitTest.$(element);
+ if (this.element) this._createLogTable();
+};
+
+JsUnitTest.Unit.Logger.prototype.start = function(testName) {
+ if (!this.element) return;
+ var tbody = this.element.getElementsByTagName('tbody')[0];
+
+ var tr = document.createElement('tr');
+ var td;
+
+ //testname
+ td = document.createElement('td');
+ td.appendChild(document.createTextNode(testName));
+ tr.appendChild(td)
+
+ tr.appendChild(document.createElement('td'));//status
+ tr.appendChild(document.createElement('td'));//message
+
+ tbody.appendChild(tr);
+};
+
+JsUnitTest.Unit.Logger.prototype.setStatus = function(status) {
+ var logline = this.getLastLogLine();
+ logline.className = status;
+ var statusCell = logline.getElementsByTagName('td')[1];
+ statusCell.appendChild(document.createTextNode(status));
+};
+
+JsUnitTest.Unit.Logger.prototype.finish = function(status, summary) {
+ if (!this.element) return;
+ this.setStatus(status);
+ this.message(summary);
+};
+
+JsUnitTest.Unit.Logger.prototype.message = function(message) {
+ if (!this.element) return;
+ var cell = this.getMessageCell();
+
+ // cell.appendChild(document.createTextNode(this._toHTML(message)));
+ cell.innerHTML = this._toHTML(message);
+};
+
+JsUnitTest.Unit.Logger.prototype.summary = function(summary) {
+ if (!this.element) return;
+ var div = this.element.getElementsByTagName('div')[0];
+ div.innerHTML = this._toHTML(summary);
+};
+
+JsUnitTest.Unit.Logger.prototype.getLastLogLine = function() {
+ var tbody = this.element.getElementsByTagName('tbody')[0];
+ var loglines = tbody.getElementsByTagName('tr');
+ return loglines[loglines.length - 1];
+};
+
+JsUnitTest.Unit.Logger.prototype.getMessageCell = function() {
+ var logline = this.getLastLogLine();
+ return logline.getElementsByTagName('td')[2];
+};
+
+JsUnitTest.Unit.Logger.prototype._createLogTable = function() {
+ var html = '<div class="logsummary">running...</div>' +
+ '<table class="logtable">' +
+ '<thead><tr><th>Status</th><th>Test</th><th>Message</th></tr></thead>' +
+ '<tbody class="loglines"></tbody>' +
+ '</table>';
+ this.element.innerHTML = html;
+};
+
+JsUnitTest.Unit.Logger.prototype.appendActionButtons = function(actions) {
+ // actions = $H(actions);
+ // if (!actions.any()) return;
+ // var div = new Element("div", {className: 'action_buttons'});
+ // actions.inject(div, function(container, action) {
+ // var button = new Element("input").setValue(action.key).observe("click", action.value);
+ // button.type = "button";
+ // return container.insert(button);
+ // });
+ // this.getMessageCell().insert(div);
+};
+
+JsUnitTest.Unit.Logger.prototype._toHTML = function(txt) {
+ return JsUnitTest.escapeHTML(txt).replace(/\n/g,"<br/>");
+};
+JsUnitTest.Unit.MessageTemplate = function(string) {
+ var parts = [];
+ var str = JsUnitTest.scan((string || ''), /(?=[^\\])\?|(?:\\\?|[^\?])+/, function(part) {
+ parts.push(part[0]);
+ });
+ this.parts = parts;
+};
+
+JsUnitTest.Unit.MessageTemplate.prototype.evaluate = function(params) {
+ var results = [];
+ for (var i=0; i < this.parts.length; i++) {
+ var part = this.parts[i];
+ var result = (part == '?') ? JsUnitTest.inspect(params.shift()) : part.replace(/\\\?/, '?');
+ results.push(result);
+ };
+ return results.join('');
+};
+// A generic function for performming AJAX requests
+// It takes one argument, which is an object that contains a set of options
+// All of which are outline in the comments, below
+// From John Resig's book Pro JavaScript Techniques
+// published by Apress, 2006-8
+JsUnitTest.ajax = function( options ) {
+
+ // Load the options object with defaults, if no
+ // values were provided by the user
+ options = {
+ // The type of HTTP Request
+ type: options.type || "POST",
+
+ // The URL the request will be made to
+ url: options.url || "",
+
+ // How long to wait before considering the request to be a timeout
+ timeout: options.timeout || 5000,
+
+ // Functions to call when the request fails, succeeds,
+ // or completes (either fail or succeed)
+ onComplete: options.onComplete || function(){},
+ onError: options.onError || function(){},
+ onSuccess: options.onSuccess || function(){},
+
+ // The data type that'll be returned from the server
+ // the default is simply to determine what data was returned from the
+ // and act accordingly.
+ data: options.data || ""
+ };
+
+ // Create the request object
+ var xml = window.ActiveXObject ? new ActiveXObject('Microsoft.XMLHTTP') : new XMLHttpRequest();
+
+ // Open the asynchronous POST request
+ xml.open(options.type, options.url, true);
+
+ // We're going to wait for a request for 5 seconds, before giving up
+ var timeoutLength = 5000;
+
+ // Keep track of when the request has been succesfully completed
+ var requestDone = false;
+
+ // Initalize a callback which will fire 5 seconds from now, cancelling
+ // the request (if it has not already occurred).
+ setTimeout(function(){
+ requestDone = true;
+ }, timeoutLength);
+
+ // Watch for when the state of the document gets updated
+ xml.onreadystatechange = function(){
+ // Wait until the data is fully loaded,
+ // and make sure that the request hasn't already timed out
+ if ( xml.readyState == 4 && !requestDone ) {
+
+ // Check to see if the request was successful
+ if ( httpSuccess( xml ) ) {
+
+ // Execute the success callback with the data returned from the server
+ options.onSuccess( httpData( xml, options.type ) );
+
+ // Otherwise, an error occurred, so execute the error callback
+ } else {
+ options.onError();
+ }
+
+ // Call the completion callback
+ options.onComplete();
+
+ // Clean up after ourselves, to avoid memory leaks
+ xml = null;
+ }
+ };
+
+ // Establish the connection to the server
+ xml.send(null);
+
+ // Determine the success of the HTTP response
+ function httpSuccess(r) {
+ try {
+ // If no server status is provided, and we're actually
+ // requesting a local file, then it was successful
+ return !r.status && location.protocol == "file:" ||
+
+ // Any status in the 200 range is good
+ ( r.status >= 200 && r.status < 300 ) ||
+
+ // Successful if the document has not been modified
+ r.status == 304 ||
+
+ // Safari returns an empty status if the file has not been modified
+ navigator.userAgent.indexOf("Safari") >= 0 && typeof r.status == "undefined";
+ } catch(e){}
+
+ // If checking the status failed, then assume that the request failed too
+ return false;
+ }
+
+ // Extract the correct data from the HTTP response
+ function httpData(r,type) {
+ // Get the content-type header
+ var ct = r.getResponseHeader("content-type");
+
+ // If no default type was provided, determine if some
+ // form of XML was returned from the server
+ var data = !type && ct && ct.indexOf("xml") >= 0;
+
+ // Get the XML Document object if XML was returned from
+ // the server, otherwise return the text contents returned by the server
+ data = type == "xml" || data ? r.responseXML : r.responseText;
+
+ // If the specified type is "script", execute the returned text
+ // response as if it was JavaScript
+ if ( type == "script" )
+ eval.call( window, data );
+
+ // Return the response data (either an XML Document or a text string)
+ return data;
+ }
+
+};
+JsUnitTest.Unit.Assertions = {
+ buildMessage: function(message, template) {
+ var args = JsUnitTest.arrayfromargs(arguments).slice(2);
+ return (message ? message + '\n' : '') +
+ new JsUnitTest.Unit.MessageTemplate(template).evaluate(args);
+ },
+
+ flunk: function(message) {
+ this.assertBlock(message || 'Flunked', function() { return false });
+ },
+
+ assertBlock: function(message, block) {
+ try {
+ block.call(this) ? this.pass() : this.fail(message);
+ } catch(e) { this.error(e) }
+ },
+
+ assert: function(expression, message) {
+ message = this.buildMessage(message || 'assert', 'got <?>', expression);
+ this.assertBlock(message, function() { return expression });
+ },
+
+ assertEqual: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertEqual', 'expected <?>, actual: <?>', expected, actual);
+ this.assertBlock(message, function() { return expected == actual });
+ },
+
+ assertNotEqual: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertNotEqual', 'expected <?>, actual: <?>', expected, actual);
+ this.assertBlock(message, function() { return expected != actual });
+ },
+
+ assertEnumEqual: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertEnumEqual', 'expected <?>, actual: <?>', expected, actual);
+ var expected_array = JsUnitTest.flattenArray(expected);
+ var actual_array = JsUnitTest.flattenArray(actual);
+ this.assertBlock(message, function() {
+ if (expected_array.length == actual_array.length) {
+ for (var i=0; i < expected_array.length; i++) {
+ if (expected_array[i] != actual_array[i]) return false;
+ };
+ return true;
+ }
+ return false;
+ });
+ },
+
+ assertEnumNotEqual: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertEnumNotEqual', '<?> was the same as <?>', expected, actual);
+ var expected_array = JsUnitTest.flattenArray(expected);
+ var actual_array = JsUnitTest.flattenArray(actual);
+ this.assertBlock(message, function() {
+ if (expected_array.length == actual_array.length) {
+ for (var i=0; i < expected_array.length; i++) {
+ if (expected_array[i] != actual_array[i]) return true;
+ };
+ return false;
+ }
+ return true;
+ });
+ },
+
+ assertHashEqual: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertHashEqual', 'expected <?>, actual: <?>', expected, actual);
+ var expected_array = JsUnitTest.flattenArray(JsUnitTest.hashToSortedArray(expected));
+ var actual_array = JsUnitTest.flattenArray(JsUnitTest.hashToSortedArray(actual));
+ var block = function() {
+ if (expected_array.length == actual_array.length) {
+ for (var i=0; i < expected_array.length; i++) {
+ if (expected_array[i] != actual_array[i]) return false;
+ };
+ return true;
+ }
+ return false;
+ };
+ this.assertBlock(message, block);
+ },
+
+ assertHashNotEqual: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertHashNotEqual', '<?> was the same as <?>', expected, actual);
+ var expected_array = JsUnitTest.flattenArray(JsUnitTest.hashToSortedArray(expected));
+ var actual_array = JsUnitTest.flattenArray(JsUnitTest.hashToSortedArray(actual));
+ // from now we recursively zip & compare nested arrays
+ var block = function() {
+ if (expected_array.length == actual_array.length) {
+ for (var i=0; i < expected_array.length; i++) {
+ if (expected_array[i] != actual_array[i]) return true;
+ };
+ return false;
+ }
+ return true;
+ };
+ this.assertBlock(message, block);
+ },
+
+ assertIdentical: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertIdentical', 'expected <?>, actual: <?>', expected, actual);
+ this.assertBlock(message, function() { return expected === actual });
+ },
+
+ assertNotIdentical: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertNotIdentical', 'expected <?>, actual: <?>', expected, actual);
+ this.assertBlock(message, function() { return expected !== actual });
+ },
+
+ assertNull: function(obj, message) {
+ message = this.buildMessage(message || 'assertNull', 'got <?>', obj);
+ this.assertBlock(message, function() { return obj === null });
+ },
+
+ assertNotNull: function(obj, message) {
+ message = this.buildMessage(message || 'assertNotNull', 'got <?>', obj);
+ this.assertBlock(message, function() { return obj !== null });
+ },
+
+ assertUndefined: function(obj, message) {
+ message = this.buildMessage(message || 'assertUndefined', 'got <?>', obj);
+ this.assertBlock(message, function() { return typeof obj == "undefined" });
+ },
+
+ assertNotUndefined: function(obj, message) {
+ message = this.buildMessage(message || 'assertNotUndefined', 'got <?>', obj);
+ this.assertBlock(message, function() { return typeof obj != "undefined" });
+ },
+
+ assertNullOrUndefined: function(obj, message) {
+ message = this.buildMessage(message || 'assertNullOrUndefined', 'got <?>', obj);
+ this.assertBlock(message, function() { return obj == null });
+ },
+
+ assertNotNullOrUndefined: function(obj, message) {
+ message = this.buildMessage(message || 'assertNotNullOrUndefined', 'got <?>', obj);
+ this.assertBlock(message, function() { return obj != null });
+ },
+
+ assertMatch: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertMatch', 'regex <?> did not match <?>', expected, actual);
+ this.assertBlock(message, function() { return new RegExp(expected).exec(actual) });
+ },
+
+ assertNoMatch: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertNoMatch', 'regex <?> matched <?>', expected, actual);
+ this.assertBlock(message, function() { return !(new RegExp(expected).exec(actual)) });
+ },
+
+ assertHasClass: function(element, klass, message) {
+ element = JsUnitTest.$(element);
+ message = this.buildMessage(message || 'assertHasClass', '? doesn\'t have class <?>.', element, klass);
+ this.assertBlock(message, function() {
+ var elementClassName = element.className;
+ return (elementClassName.length > 0 && (elementClassName == klass ||
+ new RegExp("(^|\\s)" + klass + "(\\s|$)").test(elementClassName)));
+ // return !!element.className.match(new RegExp(klass))
+ });
+ },
+
+ assertNotHasClass: function(element, klass, message) {
+ element = JsUnitTest.$(element);
+ message = this.buildMessage(message || 'assertNotHasClass', '? does have class <?>.', element, klass);
+ this.assertBlock(message, function() {
+ var elementClassName = element.className;
+ return !(elementClassName.length > 0 && (elementClassName == klass ||
+ new RegExp("(^|\\s)" + klass + "(\\s|$)").test(elementClassName)));
+ });
+ },
+
+ assertHidden: function(element, message) {
+ element = JsUnitTest.$(element);
+ message = this.buildMessage(message || 'assertHidden', '? isn\'t hidden.', element);
+ this.assertBlock(message, function() { return !element.style.display || element.style.display == 'none' });
+ },
+
+ assertInstanceOf: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertInstanceOf', '<?> was not an instance of the expected type', actual);
+ this.assertBlock(message, function() { return actual instanceof expected });
+ },
+
+ assertNotInstanceOf: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertNotInstanceOf', '<?> was an instance of the expected type', actual);
+ this.assertBlock(message, function() { return !(actual instanceof expected) });
+ },
+
+ assertRespondsTo: function(method, obj, message) {
+ message = this.buildMessage(message || 'assertRespondsTo', 'object doesn\'t respond to <?>', method);
+ this.assertBlock(message, function() { return (method in obj && typeof obj[method] == 'function') });
+ },
+
+ assertRaise: function(exceptionName, method, message) {
+ message = this.buildMessage(message || 'assertRaise', '<?> exception expected but none was raised', exceptionName);
+ var block = function() {
+ try {
+ method();
+ return false;
+ } catch(e) {
+ if (e.name == exceptionName) return true;
+ else throw e;
+ }
+ };
+ this.assertBlock(message, block);
+ },
+
+ assertNothingRaised: function(method, message) {
+ try {
+ method();
+ this.assert(true, "Expected nothing to be thrown");
+ } catch(e) {
+ message = this.buildMessage(message || 'assertNothingRaised', '<?> was thrown when nothing was expected.', e);
+ this.flunk(message);
+ }
+ },
+
+ _isVisible: function(element) {
+ element = JsUnitTest.$(element);
+ if(!element.parentNode) return true;
+ this.assertNotNull(element);
+ if(element.style && (element.style.display == 'none'))
+ return false;
+
+ return arguments.callee.call(this, element.parentNode);
+ },
+
+ assertVisible: function(element, message) {
+ message = this.buildMessage(message, '? was not visible.', element);
+ this.assertBlock(message, function() { return this._isVisible(element) });
+ },
+
+ assertNotVisible: function(element, message) {
+ message = this.buildMessage(message, '? was not hidden and didn\'t have a hidden parent either.', element);
+ this.assertBlock(message, function() { return !this._isVisible(element) });
+ },
+
+ assertElementsMatch: function() {
+ var pass = true, expressions = JsUnitTest.arrayfromargs(arguments);
+ var elements = expressions.shift();
+ if (elements.length != expressions.length) {
+ message = this.buildMessage('assertElementsMatch', 'size mismatch: ? elements, ? expressions (?).', elements.length, expressions.length, expressions);
+ this.flunk(message);
+ pass = false;
+ }
+ for (var i=0; i < expressions.length; i++) {
+ var expression = expressions[i];
+ var element = JsUnitTest.$(elements[i]);
+ if (JsUnitTest.selectorMatch(expression, element)) {
+ pass = true;
+ break;
+ }
+ message = this.buildMessage('assertElementsMatch', 'In index <?>: expected <?> but got ?', index, expression, element);
+ this.flunk(message);
+ pass = false;
+ };
+ this.assert(pass, "Expected all elements to match.");
+ },
+
+ assertElementMatches: function(element, expression, message) {
+ this.assertElementsMatch([element], expression);
+ }
+};
+JsUnitTest.Unit.Runner = function(testcases) {
+ var argumentOptions = arguments[1] || {};
+ var options = this.options = {};
+ options.testLog = ('testLog' in argumentOptions) ? argumentOptions.testLog : 'testlog';
+ options.resultsURL = this.queryParams.resultsURL;
+ options.testLog = JsUnitTest.$(options.testLog);
+
+ this.tests = this.getTests(testcases);
+ this.currentTest = 0;
+ this.logger = new JsUnitTest.Unit.Logger(options.testLog);
+
+ var self = this;
+ JsUnitTest.Event.addEvent(window, "load", function() {
+ setTimeout(function() {
+ self.runTests();
+ }, 0.1);
+ });
+};
+
+JsUnitTest.Unit.Runner.prototype.queryParams = JsUnitTest.toQueryParams();
+
+JsUnitTest.Unit.Runner.prototype.portNumber = function() {
+ if (window.location.search.length > 0) {
+ var matches = window.location.search.match(/\:(\d{3,5})\//);
+ if (matches) {
+ return parseInt(matches[1]);
+ }
+ }
+ return null;
+};
+
+JsUnitTest.Unit.Runner.prototype.getTests = function(testcases) {
+ var tests = [], options = this.options;
+ if (this.queryParams.tests) tests = this.queryParams.tests.split(',');
+ else if (options.tests) tests = options.tests;
+ else if (options.test) tests = [option.test];
+ else {
+ for (testname in testcases) {
+ if (testname.match(/^test/)) tests.push(testname);
+ }
+ }
+ var results = [];
+ for (var i=0; i < tests.length; i++) {
+ var test = tests[i];
+ if (testcases[test])
+ results.push(
+ new JsUnitTest.Unit.Testcase(test, testcases[test], testcases.setup, testcases.teardown)
+ );
+ };
+ return results;
+};
+
+JsUnitTest.Unit.Runner.prototype.getResult = function() {
+ var results = {
+ tests: this.tests.length,
+ assertions: 0,
+ failures: 0,
+ errors: 0,
+ warnings: 0
+ };
+
+ for (var i=0; i < this.tests.length; i++) {
+ var test = this.tests[i];
+ results.assertions += test.assertions;
+ results.failures += test.failures;
+ results.errors += test.errors;
+ results.warnings += test.warnings;
+ };
+ return results;
+};
+
+JsUnitTest.Unit.Runner.prototype.postResults = function() {
+ if (this.options.resultsURL) {
+ // new Ajax.Request(this.options.resultsURL,
+ // { method: 'get', parameters: this.getResult(), asynchronous: false });
+ var results = this.getResult();
+ var url = this.options.resultsURL + "?";
+ url += "tests="+ this.tests.length + "&";
+ url += "assertions="+ results.assertions + "&";
+ url += "warnings=" + results.warnings + "&";
+ url += "failures=" + results.failures + "&";
+ url += "errors=" + results.errors;
+ JsUnitTest.ajax({
+ url: url,
+ type: 'GET'
+ })
+ }
+};
+
+JsUnitTest.Unit.Runner.prototype.runTests = function() {
+ var test = this.tests[this.currentTest], actions;
+
+ if (!test) return this.finish();
+ if (!test.isWaiting) this.logger.start(test.name);
+ test.run();
+ var self = this;
+ if(test.isWaiting) {
+ this.logger.message("Waiting for " + test.timeToWait + "ms");
+ // setTimeout(this.runTests.bind(this), test.timeToWait || 1000);
+ setTimeout(function() {
+ self.runTests();
+ }, test.timeToWait || 1000);
+ return;
+ }
+
+ this.logger.finish(test.status(), test.summary());
+ if (actions = test.actions) this.logger.appendActionButtons(actions);
+ this.currentTest++;
+ // tail recursive, hopefully the browser will skip the stackframe
+ this.runTests();
+};
+
+JsUnitTest.Unit.Runner.prototype.finish = function() {
+ this.postResults();
+ this.logger.summary(this.summary());
+};
+
+JsUnitTest.Unit.Runner.prototype.summary = function() {
+ return new JsUnitTest.Template('#{tests} tests, #{assertions} assertions, #{failures} failures, #{errors} errors, #{warnings} warnings').evaluate(this.getResult());
+};
+JsUnitTest.Unit.Testcase = function(name, test, setup, teardown) {
+ this.name = name;
+ this.test = test || function() {};
+ this.setup = setup || function() {};
+ this.teardown = teardown || function() {};
+ this.messages = [];
+ this.actions = {};
+};
+// import JsUnitTest.Unit.Assertions
+
+for (method in JsUnitTest.Unit.Assertions) {
+ JsUnitTest.Unit.Testcase.prototype[method] = JsUnitTest.Unit.Assertions[method];
+}
+
+JsUnitTest.Unit.Testcase.prototype.isWaiting = false;
+JsUnitTest.Unit.Testcase.prototype.timeToWait = 1000;
+JsUnitTest.Unit.Testcase.prototype.assertions = 0;
+JsUnitTest.Unit.Testcase.prototype.failures = 0;
+JsUnitTest.Unit.Testcase.prototype.errors = 0;
+JsUnitTest.Unit.Testcase.prototype.warnings = 0;
+JsUnitTest.Unit.Testcase.prototype.isRunningFromRake = window.location.port;
+
+// JsUnitTest.Unit.Testcase.prototype.isRunningFromRake = window.location.port == 4711;
+
+JsUnitTest.Unit.Testcase.prototype.wait = function(time, nextPart) {
+ this.isWaiting = true;
+ this.test = nextPart;
+ this.timeToWait = time;
+};
+
+JsUnitTest.Unit.Testcase.prototype.run = function(rethrow) {
+ try {
+ try {
+ if (!this.isWaiting) this.setup();
+ this.isWaiting = false;
+ this.test();
+ } finally {
+ if(!this.isWaiting) {
+ this.teardown();
+ }
+ }
+ }
+ catch(e) {
+ if (rethrow) throw e;
+ this.error(e, this);
+ }
+};
+
+JsUnitTest.Unit.Testcase.prototype.summary = function() {
+ var msg = '#{assertions} assertions, #{failures} failures, #{errors} errors, #{warnings} warnings\n';
+ return new JsUnitTest.Template(msg).evaluate(this) +
+ this.messages.join("\n");
+};
+
+JsUnitTest.Unit.Testcase.prototype.pass = function() {
+ this.assertions++;
+};
+
+JsUnitTest.Unit.Testcase.prototype.fail = function(message) {
+ this.failures++;
+ var line = "";
+ try {
+ throw new Error("stack");
+ } catch(e){
+ line = (/\.html:(\d+)/.exec(e.stack || '') || ['',''])[1];
+ }
+ this.messages.push("Failure: " + message + (line ? " Line #" + line : ""));
+};
+
+JsUnitTest.Unit.Testcase.prototype.warning = function(message) {
+ this.warnings++;
+ var line = "";
+ try {
+ throw new Error("stack");
+ } catch(e){
+ line = (/\.html:(\d+)/.exec(e.stack || '') || ['',''])[1];
+ }
+ this.messages.push("Warning: " + message + (line ? " Line #" + line : ""));
+};
+JsUnitTest.Unit.Testcase.prototype.warn = JsUnitTest.Unit.Testcase.prototype.warning;
+
+JsUnitTest.Unit.Testcase.prototype.info = function(message) {
+ this.messages.push("Info: " + message);
+};
+
+JsUnitTest.Unit.Testcase.prototype.error = function(error, test) {
+ this.errors++;
+ this.actions['retry with throw'] = function() { test.run(true) };
+ this.messages.push(error.name + ": "+ error.message + "(" + JsUnitTest.inspect(error) + ")");
+};
+
+JsUnitTest.Unit.Testcase.prototype.status = function() {
+ if (this.failures > 0) return 'failed';
+ if (this.errors > 0) return 'error';
+ if (this.warnings > 0) return 'warning';
+ return 'passed';
+};
+
+JsUnitTest.Unit.Testcase.prototype.benchmark = function(operation, iterations) {
+ var startAt = new Date();
+ (iterations || 1).times(operation);
+ var timeTaken = ((new Date())-startAt);
+ this.info((arguments[2] || 'Operation') + ' finished ' +
+ iterations + ' iterations in ' + (timeTaken/1000)+'s' );
+ return timeTaken;
+};
+
+Test = JsUnitTest
54 assets/.svn/text-base/unittest.css.svn-base
@@ -0,0 +1,54 @@
+body, div, p, h1, h2, h3, ul, ol, span, a, table, td, form, img, li {
+ font-family: sans-serif;
+}
+
+body {
+ font-size:0.8em;
+}
+
+#log {
+ padding-bottom: 1em;
+ border-bottom: 2px solid #000;
+ margin-bottom: 2em;
+}
+
+.logsummary {
+ margin-top: 1em;
+ margin-bottom: 1em;
+ padding: 1ex;
+ border: 1px solid #000;
+ font-weight: bold;
+}
+
+.logtable {
+ width:100%;
+ border-collapse: collapse;
+ border: 1px dotted #666;
+}
+
+.logtable td, .logtable th {
+ text-align: left;
+ padding: 3px 8px;
+ border: 1px dotted #666;
+}
+
+.logtable .passed {
+ background-color: #cfc;
+}
+
+.logtable .failed, .logtable .error {
+ background-color: #fcc;
+}
+
+.logtable .warning {
+ background-color: #FC6;
+}
+
+.logtable td div.action_buttons {
+ display: inline;
+}
+
+.logtable td div.action_buttons input {
+ margin: 0 5px;
+ font-size: 10px;
+}
1,017 assets/jsunittest.js
@@ -0,0 +1,1017 @@
+/* Jsunittest, version 0.7.3
+ * (c) 2008 Dr Nic Williams
+ *
+ * Jsunittest is freely distributable under
+ * the terms of an MIT-style license.
+ * For details, see the web site: http://jsunittest.rubyforge.org
+ *
+ *--------------------------------------------------------------------------*/
+
+var JsUnitTest = {
+ Unit: {},
+ inspect: function(object) {
+ try {
+ if (typeof object == "undefined") return 'undefined';
+ if (object === null) return 'null';
+ if (typeof object == "string") {
+ var useDoubleQuotes = arguments[1];
+ var escapedString = this.gsub(object, /[\x00-\x1f\\]/, function(match) {
+ var character = String.specialChar[match[0]];
+ return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
+ });
+ if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
+ return "'" + escapedString.replace(/'/g, '\\\'') + "'";
+ };
+ return String(object);
+ } catch (e) {
+ if (e instanceof RangeError) return '...';
+ throw e;
+ }
+ },
+ $: function(element) {
+ if (arguments.length > 1) {
+ for (var i = 0, elements = [], length = arguments.length; i < length; i++)
+ elements.push(this.$(arguments[i]));
+ return elements;
+ }
+ if (typeof element == "string")
+ element = document.getElementById(element);
+ return element;
+ },
+ gsub: function(source, pattern, replacement) {
+ var result = '', match;
+ replacement = arguments.callee.prepareReplacement(replacement);
+
+ while (source.length > 0) {
+ if (match = source.match(pattern)) {
+ result += source.slice(0, match.index);
+ result += JsUnitTest.String.interpret(replacement(match));
+ source = source.slice(match.index + match[0].length);
+ } else {
+ result += source, source = '';
+ }
+ }
+ return result;
+ },
+ scan: function(source, pattern, iterator) {
+ this.gsub(source, pattern, iterator);
+ return String(source);
+ },
+ escapeHTML: function(data) {
+ return data.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
+ },
+ arrayfromargs: function(args) {
+ var myarray = new Array();
+ var i;
+
+ for (i=0;i<args.length;i++)
+ myarray[i] = args[i];
+
+ return myarray;
+ },
+ hashToSortedArray: function(hash) {
+ var results = [];
+ for (key in hash) {
+ results.push([key, hash[key]]);
+ }
+ return results.sort();
+ },
+ flattenArray: function(array) {
+ var results = arguments[1] || [];
+ for (var i=0; i < array.length; i++) {
+ var object = array[i];
+ if (object != null && typeof object == "object" &&
+ 'splice' in object && 'join' in object) {
+ this.flattenArray(object, results);
+ } else {
+ results.push(object);
+ }
+ };
+ return results;
+ },
+ selectorMatch: function(expression, element) {
+ var tokens = [];
+ var patterns = {
+ // combinators must be listed first
+ // (and descendant needs to be last combinator)
+ laterSibling: /^\s*~\s*/,
+ child: /^\s*>\s*/,
+ adjacent: /^\s*\+\s*/,
+ descendant: /^\s/,
+
+ // selectors follow
+ tagName: /^\s*(\*|[\w\-]+)(\b|$)?/,
+ id: /^#([\w\-\*]+)(\b|$)/,
+ className: /^\.([\w\-\*]+)(\b|$)/,
+ pseudo:
+ /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/,
+ attrPresence: /^\[((?:[\w]+:)?[\w]+)\]/,
+ attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
+ };
+
+ var assertions = {
+ tagName: function(element, matches) {
+ return matches[1].toUpperCase() == element.tagName.toUpperCase();
+ },
+
+ className: function(element, matches) {
+ return Element.hasClassName(element, matches[1]);
+ },
+
+ id: function(element, matches) {
+ return element.id === matches[1];
+ },
+
+ attrPresence: function(element, matches) {
+ return Element.hasAttribute(element, matches[1]);
+ },
+
+ attr: function(element, matches) {
+ var nodeValue = Element.readAttribute(element, matches[1]);
+ return nodeValue && operators[matches[2]](nodeValue, matches[5] || matches[6]);
+ }
+ };
+ var e = this.expression, ps = patterns, as = assertions;
+ var le, p, m;
+
+ while (e && le !== e && (/\S/).test(e)) {
+ le = e;
+ for (var i in ps) {
+ p = ps[i];
+ if (m = e.match(p)) {
+ // use the Selector.assertions methods unless the selector
+ // is too complex.
+ if (as[i]) {
+ tokens.push([i, Object.clone(m)]);
+ e = e.replace(m[0], '');
+ }
+ }
+ }
+ }
+
+ var match = true, name, matches;
+ for (var i = 0, token; token = tokens[i]; i++) {
+ name = token[0], matches = token[1];
+ if (!assertions[name](element, matches)) {
+ match = false; break;
+ }
+ }
+
+ return match;
+ },
+ toQueryParams: function(query, separator) {
+ var query = query || window.location.search;
+ var match = query.replace(/^\s+/, '').replace(/\s+$/, '').match(/([^?#]*)(#.*)?$/);
+ if (!match) return { };
+
+ var hash = {};
+ var parts = match[1].split(separator || '&');
+ for (var i=0; i < parts.length; i++) {
+ var pair = parts[i].split('=');
+ if (pair[0]) {
+ var key = decodeURIComponent(pair.shift());
+ var value = pair.length > 1 ? pair.join('=') : pair[0];
+ if (value != undefined) value = decodeURIComponent(value);
+
+ if (key in hash) {
+ var object = hash[key];
+ var isArray = object != null && typeof object == "object" &&
+ 'splice' in object && 'join' in object
+ if (!isArray) hash[key] = [hash[key]];
+ hash[key].push(value);
+ }
+ else hash[key] = value;
+ }
+ };
+ return hash;
+ },
+
+ String: {
+ interpret: function(value) {
+ return value == null ? '' : String(value);
+ }
+ }
+};
+
+JsUnitTest.gsub.prepareReplacement = function(replacement) {
+ if (typeof replacement == "function") return replacement;
+ var template = new Template(replacement);
+ return function(match) { return template.evaluate(match) };
+};
+
+JsUnitTest.Version = '0.7.3';
+
+JsUnitTest.Template = function(template, pattern) {
+ this.template = template; //template.toString();
+ this.pattern = pattern || JsUnitTest.Template.Pattern;
+};
+
+JsUnitTest.Template.prototype.evaluate = function(object) {
+ if (typeof object.toTemplateReplacements == "function")
+ object = object.toTemplateReplacements();
+
+ return JsUnitTest.gsub(this.template, this.pattern, function(match) {
+ if (object == null) return '';
+
+ var before = match[1] || '';
+ if (before == '\\') return match[2];
+
+ var ctx = object, expr = match[3];
+ var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
+ match = pattern.exec(expr);
+ if (match == null) return before;
+
+ while (match != null) {
+ var comp = (match[1].indexOf('[]') === 0) ? match[2].gsub('\\\\]', ']') : match[1];
+ ctx = ctx[comp];
+ if (null == ctx || '' == match[3]) break;
+ expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
+ match = pattern.exec(expr);
+ }
+
+ return before + JsUnitTest.String.interpret(ctx);
+ });
+}
+
+JsUnitTest.Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
+JsUnitTest.Event = {};
+// written by Dean Edwards, 2005
+// with input from Tino Zijdel, Matthias Miller, Diego Perini
+// namespaced by Dr Nic Williams 2008
+
+// http://dean.edwards.name/weblog/2005/10/add-event/
+// http://dean.edwards.name/weblog/2005/10/add-event2/
+JsUnitTest.Event.addEvent = function(element, type, handler) {
+ if (element.addEventListener) {
+ element.addEventListener(type, handler, false);
+ } else {
+ // assign each event handler a unique ID
+ if (!handler.$$guid) handler.$$guid = JsUnitTest.Event.addEvent.guid++;
+ // create a hash table of event types for the element
+ if (!element.events) element.events = {};
+ // create a hash table of event handlers for each element/event pair
+ var handlers = element.events[type];
+ if (!handlers) {
+ handlers = element.events[type] = {};
+ // store the existing event handler (if there is one)
+ if (element["on" + type]) {
+ handlers[0] = element["on" + type];
+ }
+ }
+ // store the event handler in the hash table
+ handlers[handler.$$guid] = handler;
+ // assign a global event handler to do all the work
+ element["on" + type] = this.handleEvent;
+ }
+};
+// a counter used to create unique IDs
+JsUnitTest.Event.addEvent.guid = 1;
+
+JsUnitTest.Event.removeEvent = function(element, type, handler) {
+ if (element.removeEventListener) {
+ element.removeEventListener(type, handler, false);
+ } else {
+ // delete the event handler from the hash table
+ if (element.events && element.events[type]) {
+ delete element.events[type][handler.$$guid];
+ }
+ }
+};
+
+JsUnitTest.Event.handleEvent = function(event) {
+ var returnValue = true;
+ // grab the event object (IE uses a global event object)
+ event = event || JsUnitTest.Event.fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event);
+ // get a reference to the hash table of event handlers
+ var handlers = this.events[event.type];
+ // execute each event handler
+ for (var i in handlers) {
+ this.$$handleEvent = handlers[i];
+ if (this.$$handleEvent(event) === false) {
+ returnValue = false;
+ }
+ }
+ return returnValue;
+};
+
+JsUnitTest.Event.fixEvent = function(event) {
+ // add W3C standard event methods
+ event.preventDefault = this.fixEvent.preventDefault;
+ event.stopPropagation = this.fixEvent.stopPropagation;
+ return event;
+};
+JsUnitTest.Event.fixEvent.preventDefault = function() {
+ this.returnValue = false;
+};
+JsUnitTest.Event.fixEvent.stopPropagation = function() {
+ this.cancelBubble = true;
+};
+
+JsUnitTest.Unit.Logger = function(element) {
+ this.element = JsUnitTest.$(element);
+ if (this.element) this._createLogTable();
+};
+
+JsUnitTest.Unit.Logger.prototype.start = function(testName) {
+ if (!this.element) return;
+ var tbody = this.element.getElementsByTagName('tbody')[0];
+
+ var tr = document.createElement('tr');
+ var td;
+
+ //testname
+ td = document.createElement('td');
+ td.appendChild(document.createTextNode(testName));
+ tr.appendChild(td)
+
+ tr.appendChild(document.createElement('td'));//status
+ tr.appendChild(document.createElement('td'));//message
+
+ tbody.appendChild(tr);
+};
+
+JsUnitTest.Unit.Logger.prototype.setStatus = function(status) {
+ var logline = this.getLastLogLine();
+ logline.className = status;
+ var statusCell = logline.getElementsByTagName('td')[1];
+ statusCell.appendChild(document.createTextNode(status));
+};
+
+JsUnitTest.Unit.Logger.prototype.finish = function(status, summary) {
+ if (!this.element) return;
+ this.setStatus(status);
+ this.message(summary);
+};
+
+JsUnitTest.Unit.Logger.prototype.message = function(message) {
+ if (!this.element) return;
+ var cell = this.getMessageCell();
+
+ // cell.appendChild(document.createTextNode(this._toHTML(message)));
+ cell.innerHTML = this._toHTML(message);
+};
+
+JsUnitTest.Unit.Logger.prototype.summary = function(summary) {
+ if (!this.element) return;
+ var div = this.element.getElementsByTagName('div')[0];
+ div.innerHTML = this._toHTML(summary);
+};
+
+JsUnitTest.Unit.Logger.prototype.getLastLogLine = function() {
+ var tbody = this.element.getElementsByTagName('tbody')[0];
+ var loglines = tbody.getElementsByTagName('tr');
+ return loglines[loglines.length - 1];
+};
+
+JsUnitTest.Unit.Logger.prototype.getMessageCell = function() {
+ var logline = this.getLastLogLine();
+ return logline.getElementsByTagName('td')[2];
+};
+
+JsUnitTest.Unit.Logger.prototype._createLogTable = function() {
+ var html = '<div class="logsummary">running...</div>' +
+ '<table class="logtable">' +
+ '<thead><tr><th>Status</th><th>Test</th><th>Message</th></tr></thead>' +
+ '<tbody class="loglines"></tbody>' +
+ '</table>';
+ this.element.innerHTML = html;
+};
+
+JsUnitTest.Unit.Logger.prototype.appendActionButtons = function(actions) {
+ // actions = $H(actions);
+ // if (!actions.any()) return;
+ // var div = new Element("div", {className: 'action_buttons'});
+ // actions.inject(div, function(container, action) {
+ // var button = new Element("input").setValue(action.key).observe("click", action.value);
+ // button.type = "button";
+ // return container.insert(button);
+ // });
+ // this.getMessageCell().insert(div);
+};
+
+JsUnitTest.Unit.Logger.prototype._toHTML = function(txt) {
+ return JsUnitTest.escapeHTML(txt).replace(/\n/g,"<br/>");
+};
+JsUnitTest.Unit.MessageTemplate = function(string) {
+ var parts = [];
+ var str = JsUnitTest.scan((string || ''), /(?=[^\\])\?|(?:\\\?|[^\?])+/, function(part) {
+ parts.push(part[0]);
+ });
+ this.parts = parts;
+};
+
+JsUnitTest.Unit.MessageTemplate.prototype.evaluate = function(params) {
+ var results = [];
+ for (var i=0; i < this.parts.length; i++) {
+ var part = this.parts[i];
+ var result = (part == '?') ? JsUnitTest.inspect(params.shift()) : part.replace(/\\\?/, '?');
+ results.push(result);
+ };
+ return results.join('');
+};
+// A generic function for performming AJAX requests
+// It takes one argument, which is an object that contains a set of options
+// All of which are outline in the comments, below
+// From John Resig's book Pro JavaScript Techniques
+// published by Apress, 2006-8
+JsUnitTest.ajax = function( options ) {
+
+ // Load the options object with defaults, if no
+ // values were provided by the user
+ options = {
+ // The type of HTTP Request
+ type: options.type || "POST",
+
+ // The URL the request will be made to
+ url: options.url || "",
+
+ // How long to wait before considering the request to be a timeout
+ timeout: options.timeout || 5000,
+
+ // Functions to call when the request fails, succeeds,
+ // or completes (either fail or succeed)
+ onComplete: options.onComplete || function(){},
+ onError: options.onError || function(){},
+ onSuccess: options.onSuccess || function(){},
+
+ // The data type that'll be returned from the server
+ // the default is simply to determine what data was returned from the
+ // and act accordingly.
+ data: options.data || ""
+ };
+
+ // Create the request object
+ var xml = window.ActiveXObject ? new ActiveXObject('Microsoft.XMLHTTP') : new XMLHttpRequest();
+
+ // Open the asynchronous POST request
+ xml.open(options.type, options.url, true);
+
+ // We're going to wait for a request for 5 seconds, before giving up
+ var timeoutLength = 5000;
+
+ // Keep track of when the request has been succesfully completed
+ var requestDone = false;
+
+ // Initalize a callback which will fire 5 seconds from now, cancelling
+ // the request (if it has not already occurred).
+ setTimeout(function(){
+ requestDone = true;
+ }, timeoutLength);
+
+ // Watch for when the state of the document gets updated
+ xml.onreadystatechange = function(){
+ // Wait until the data is fully loaded,
+ // and make sure that the request hasn't already timed out
+ if ( xml.readyState == 4 && !requestDone ) {
+
+ // Check to see if the request was successful
+ if ( httpSuccess( xml ) ) {
+
+ // Execute the success callback with the data returned from the server
+ options.onSuccess( httpData( xml, options.type ) );
+
+ // Otherwise, an error occurred, so execute the error callback
+ } else {
+ options.onError();
+ }
+
+ // Call the completion callback
+ options.onComplete();
+
+ // Clean up after ourselves, to avoid memory leaks
+ xml = null;
+ }
+ };
+
+ // Establish the connection to the server
+ xml.send(null);
+
+ // Determine the success of the HTTP response
+ function httpSuccess(r) {
+ try {
+ // If no server status is provided, and we're actually
+ // requesting a local file, then it was successful
+ return !r.status && location.protocol == "file:" ||
+
+ // Any status in the 200 range is good
+ ( r.status >= 200 && r.status < 300 ) ||
+
+ // Successful if the document has not been modified
+ r.status == 304 ||
+
+ // Safari returns an empty status if the file has not been modified
+ navigator.userAgent.indexOf("Safari") >= 0 && typeof r.status == "undefined";
+ } catch(e){}
+
+ // If checking the status failed, then assume that the request failed too
+ return false;
+ }
+
+ // Extract the correct data from the HTTP response
+ function httpData(r,type) {
+ // Get the content-type header
+ var ct = r.getResponseHeader("content-type");
+
+ // If no default type was provided, determine if some
+ // form of XML was returned from the server
+ var data = !type && ct && ct.indexOf("xml") >= 0;
+
+ // Get the XML Document object if XML was returned from
+ // the server, otherwise return the text contents returned by the server
+ data = type == "xml" || data ? r.responseXML : r.responseText;
+
+ // If the specified type is "script", execute the returned text
+ // response as if it was JavaScript
+ if ( type == "script" )
+ eval.call( window, data );
+
+ // Return the response data (either an XML Document or a text string)
+ return data;
+ }
+
+};
+JsUnitTest.Unit.Assertions = {
+ buildMessage: function(message, template) {
+ var args = JsUnitTest.arrayfromargs(arguments).slice(2);
+ return (message ? message + '\n' : '') +
+ new JsUnitTest.Unit.MessageTemplate(template).evaluate(args);
+ },
+
+ flunk: function(message) {
+ this.assertBlock(message || 'Flunked', function() { return false });
+ },
+
+ assertBlock: function(message, block) {
+ try {
+ block.call(this) ? this.pass() : this.fail(message);
+ } catch(e) { this.error(e) }
+ },
+
+ assert: function(expression, message) {
+ message = this.buildMessage(message || 'assert', 'got <?>', expression);
+ this.assertBlock(message, function() { return expression });
+ },
+
+ assertEqual: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertEqual', 'expected <?>, actual: <?>', expected, actual);
+ this.assertBlock(message, function() { return expected == actual });
+ },
+
+ assertNotEqual: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertNotEqual', 'expected <?>, actual: <?>', expected, actual);
+ this.assertBlock(message, function() { return expected != actual });
+ },
+
+ assertEnumEqual: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertEnumEqual', 'expected <?>, actual: <?>', expected, actual);
+ var expected_array = JsUnitTest.flattenArray(expected);
+ var actual_array = JsUnitTest.flattenArray(actual);
+ this.assertBlock(message, function() {
+ if (expected_array.length == actual_array.length) {
+ for (var i=0; i < expected_array.length; i++) {
+ if (expected_array[i] != actual_array[i]) return false;
+ };
+ return true;
+ }
+ return false;
+ });
+ },
+
+ assertEnumNotEqual: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertEnumNotEqual', '<?> was the same as <?>', expected, actual);
+ var expected_array = JsUnitTest.flattenArray(expected);
+ var actual_array = JsUnitTest.flattenArray(actual);
+ this.assertBlock(message, function() {
+ if (expected_array.length == actual_array.length) {
+ for (var i=0; i < expected_array.length; i++) {
+ if (expected_array[i] != actual_array[i]) return true;
+ };
+ return false;
+ }
+ return true;
+ });
+ },
+
+ assertHashEqual: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertHashEqual', 'expected <?>, actual: <?>', expected, actual);
+ var expected_array = JsUnitTest.flattenArray(JsUnitTest.hashToSortedArray(expected));
+ var actual_array = JsUnitTest.flattenArray(JsUnitTest.hashToSortedArray(actual));
+ var block = function() {
+ if (expected_array.length == actual_array.length) {
+ for (var i=0; i < expected_array.length; i++) {
+ if (expected_array[i] != actual_array[i]) return false;
+ };
+ return true;
+ }
+ return false;
+ };
+ this.assertBlock(message, block);
+ },
+
+ assertHashNotEqual: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertHashNotEqual', '<?> was the same as <?>', expected, actual);
+ var expected_array = JsUnitTest.flattenArray(JsUnitTest.hashToSortedArray(expected));
+ var actual_array = JsUnitTest.flattenArray(JsUnitTest.hashToSortedArray(actual));
+ // from now we recursively zip & compare nested arrays
+ var block = function() {
+ if (expected_array.length == actual_array.length) {
+ for (var i=0; i < expected_array.length; i++) {
+ if (expected_array[i] != actual_array[i]) return true;
+ };
+ return false;
+ }
+ return true;
+ };
+ this.assertBlock(message, block);
+ },
+
+ assertIdentical: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertIdentical', 'expected <?>, actual: <?>', expected, actual);
+ this.assertBlock(message, function() { return expected === actual });
+ },
+
+ assertNotIdentical: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertNotIdentical', 'expected <?>, actual: <?>', expected, actual);
+ this.assertBlock(message, function() { return expected !== actual });
+ },
+
+ assertNull: function(obj, message) {
+ message = this.buildMessage(message || 'assertNull', 'got <?>', obj);
+ this.assertBlock(message, function() { return obj === null });
+ },
+
+ assertNotNull: function(obj, message) {
+ message = this.buildMessage(message || 'assertNotNull', 'got <?>', obj);
+ this.assertBlock(message, function() { return obj !== null });
+ },
+
+ assertUndefined: function(obj, message) {
+ message = this.buildMessage(message || 'assertUndefined', 'got <?>', obj);
+ this.assertBlock(message, function() { return typeof obj == "undefined" });
+ },
+
+ assertNotUndefined: function(obj, message) {
+ message = this.buildMessage(message || 'assertNotUndefined', 'got <?>', obj);
+ this.assertBlock(message, function() { return typeof obj != "undefined" });
+ },
+
+ assertNullOrUndefined: function(obj, message) {
+ message = this.buildMessage(message || 'assertNullOrUndefined', 'got <?>', obj);
+ this.assertBlock(message, function() { return obj == null });
+ },
+
+ assertNotNullOrUndefined: function(obj, message) {
+ message = this.buildMessage(message || 'assertNotNullOrUndefined', 'got <?>', obj);
+ this.assertBlock(message, function() { return obj != null });
+ },
+
+ assertMatch: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertMatch', 'regex <?> did not match <?>', expected, actual);
+ this.assertBlock(message, function() { return new RegExp(expected).exec(actual) });
+ },
+
+ assertNoMatch: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertNoMatch', 'regex <?> matched <?>', expected, actual);
+ this.assertBlock(message, function() { return !(new RegExp(expected).exec(actual)) });
+ },
+
+ assertHasClass: function(element, klass, message) {
+ element = JsUnitTest.$(element);
+ message = this.buildMessage(message || 'assertHasClass', '? doesn\'t have class <?>.', element, klass);
+ this.assertBlock(message, function() {
+ var elementClassName = element.className;
+ return (elementClassName.length > 0 && (elementClassName == klass ||
+ new RegExp("(^|\\s)" + klass + "(\\s|$)").test(elementClassName)));
+ // return !!element.className.match(new RegExp(klass))
+ });
+ },
+
+ assertNotHasClass: function(element, klass, message) {
+ element = JsUnitTest.$(element);
+ message = this.buildMessage(message || 'assertNotHasClass', '? does have class <?>.', element, klass);
+ this.assertBlock(message, function() {
+ var elementClassName = element.className;
+ return !(elementClassName.length > 0 && (elementClassName == klass ||
+ new RegExp("(^|\\s)" + klass + "(\\s|$)").test(elementClassName)));
+ });
+ },
+
+ assertHidden: function(element, message) {
+ element = JsUnitTest.$(element);
+ message = this.buildMessage(message || 'assertHidden', '? isn\'t hidden.', element);
+ this.assertBlock(message, function() { return !element.style.display || element.style.display == 'none' });
+ },
+
+ assertInstanceOf: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertInstanceOf', '<?> was not an instance of the expected type', actual);
+ this.assertBlock(message, function() { return actual instanceof expected });
+ },
+
+ assertNotInstanceOf: function(expected, actual, message) {
+ message = this.buildMessage(message || 'assertNotInstanceOf', '<?> was an instance of the expected type', actual);
+ this.assertBlock(message, function() { return !(actual instanceof expected) });
+ },
+
+ assertRespondsTo: function(method, obj, message) {
+ message = this.buildMessage(message || 'assertRespondsTo', 'object doesn\'t respond to <?>', method);
+ this.assertBlock(message, function() { return (method in obj && typeof obj[method] == 'function') });
+ },
+
+ assertRaise: function(exceptionName, method, message) {
+ message = this.buildMessage(message || 'assertRaise', '<?> exception expected but none was raised', exceptionName);
+ var block = function() {
+ try {
+ method();
+ return false;
+ } catch(e) {
+ if (e.name == exceptionName) return true;
+ else throw e;
+ }
+ };
+ this.assertBlock(message, block);
+ },
+
+ assertNothingRaised: function(method, message) {
+ try {
+ method();
+ this.assert(true, "Expected nothing to be thrown");
+ } catch(e) {
+ message = this.buildMessage(message || 'assertNothingRaised', '<?> was thrown when nothing was expected.', e);
+ this.flunk(message);
+ }
+ },
+
+ _isVisible: function(element) {
+ element = JsUnitTest.$(element);
+ if(!element.parentNode) return true;
+ this.assertNotNull(element);
+ if(element.style && (element.style.display == 'none'))
+ return false;
+
+ return arguments.callee.call(this, element.parentNode);
+ },
+
+ assertVisible: function(element, message) {
+ message = this.buildMessage(message, '? was not visible.', element);
+ this.assertBlock(message, function() { return this._isVisible(element) });
+ },
+
+ assertNotVisible: function(element, message) {
+ message = this.buildMessage(message, '? was not hidden and didn\'t have a hidden parent either.', element);
+ this.assertBlock(message, function() { return !this._isVisible(element) });
+ },
+
+ assertElementsMatch: function() {
+ var pass = true, expressions = JsUnitTest.arrayfromargs(arguments);
+ var elements = expressions.shift();
+ if (elements.length != expressions.length) {
+ message = this.buildMessage('assertElementsMatch', 'size mismatch: ? elements, ? expressions (?).', elements.length, expressions.length, expressions);
+ this.flunk(message);
+ pass = false;
+ }
+ for (var i=0; i < expressions.length; i++) {
+ var expression = expressions[i];
+ var element = JsUnitTest.$(elements[i]);
+ if (JsUnitTest.selectorMatch(expression, element)) {
+ pass = true;
+ break;
+ }
+ message = this.buildMessage('assertElementsMatch', 'In index <?>: expected <?> but got ?', index, expression, element);
+ this.flunk(message);
+ pass = false;
+ };
+ this.assert(pass, "Expected all elements to match.");
+ },
+
+ assertElementMatches: function(element, expression, message) {
+ this.assertElementsMatch([element], expression);
+ }
+};
+JsUnitTest.Unit.Runner = function(testcases) {
+ var argumentOptions = arguments[1] || {};
+ var options = this.options = {};
+ options.testLog = ('testLog' in argumentOptions) ? argumentOptions.testLog : 'testlog';
+ options.resultsURL = this.queryParams.resultsURL;
+ options.testLog = JsUnitTest.$(options.testLog);
+
+ this.tests = this.getTests(testcases);
+ this.currentTest = 0;
+ this.logger = new JsUnitTest.Unit.Logger(options.testLog);
+
+ var self = this;
+ JsUnitTest.Event.addEvent(window, "load", function() {
+ setTimeout(function() {
+ self.runTests();
+ }, 0.1);
+ });
+};
+
+JsUnitTest.Unit.Runner.prototype.queryParams = JsUnitTest.toQueryParams();
+
+JsUnitTest.Unit.Runner.prototype.portNumber = function() {
+ if (window.location.search.length > 0) {
+ var matches = window.location.search.match(/\:(\d{3,5})\//);
+ if (matches) {
+ return parseInt(matches[1]);
+ }
+ }
+ return null;
+};
+
+JsUnitTest.Unit.Runner.prototype.getTests = function(testcases) {
+ var tests = [], options = this.options;
+ if (this.queryParams.tests) tests = this.queryParams.tests.split(',');
+ else if (options.tests) tests = options.tests;
+ else if (options.test) tests = [option.test];
+ else {
+ for (testname in testcases) {
+ if (testname.match(/^test/)) tests.push(testname);
+ }
+ }
+ var results = [];
+ for (var i=0; i < tests.length; i++) {
+ var test = tests[i];
+ if (testcases[test])
+ results.push(
+ new JsUnitTest.Unit.Testcase(test, testcases[test], testcases.setup, testcases.teardown)
+ );
+ };
+ return results;
+};
+
+JsUnitTest.Unit.Runner.prototype.getResult = function() {
+ var results = {
+ tests: this.tests.length,
+ assertions: 0,
+ failures: 0,
+ errors: 0,
+ warnings: 0
+ };
+
+ for (var i=0; i < this.tests.length; i++) {
+ var test = this.tests[i];
+ results.assertions += test.assertions;
+ results.failures += test.failures;
+ results.errors += test.errors;
+ results.warnings += test.warnings;
+ };
+ return results;
+};
+
+JsUnitTest.Unit.Runner.prototype.postResults = function() {
+ if (this.options.resultsURL) {
+ // new Ajax.Request(this.options.resultsURL,
+ // { method: 'get', parameters: this.getResult(), asynchronous: false });
+ var results = this.getResult();
+ var url = this.options.resultsURL + "?";
+ url += "tests="+ this.tests.length + "&";
+ url += "assertions="+ results.assertions + "&";
+ url += "warnings=" + results.warnings + "&";
+ url += "failures=" + results.failures + "&";
+ url += "errors=" + results.errors;
+ JsUnitTest.ajax({
+ url: url,
+ type: 'GET'
+ })
+ }
+};
+
+JsUnitTest.Unit.Runner.prototype.runTests = function() {
+ var test = this.tests[this.currentTest], actions;
+
+ if (!test) return this.finish();
+ if (!test.isWaiting) this.logger.start(test.name);
+ test.run();
+ var self = this;
+ if(test.isWaiting) {
+ this.logger.message("Waiting for " + test.timeToWait + "ms");
+ // setTimeout(this.runTests.bind(this), test.timeToWait || 1000);
+ setTimeout(function() {
+ self.runTests();
+ }, test.timeToWait || 1000);
+ return;
+ }
+
+ this.logger.finish(test.status(), test.summary());
+ if (actions = test.actions) this.logger.appendActionButtons(actions);
+ this.currentTest++;
+ // tail recursive, hopefully the browser will skip the stackframe
+ this.runTests();
+};
+
+JsUnitTest.Unit.Runner.prototype.finish = function() {
+ this.postResults();
+ this.logger.summary(this.summary());
+};
+
+JsUnitTest.Unit.Runner.prototype.summary = function() {
+ return new JsUnitTest.Template('#{tests} tests, #{assertions} assertions, #{failures} failures, #{errors} errors, #{warnings} warnings').evaluate(this.getResult());
+};
+JsUnitTest.Unit.Testcase = function(name, test, setup, teardown) {
+ this.name = name;
+ this.test = test || function() {};
+ this.setup = setup || function() {};
+ this.teardown = teardown || function() {};
+ this.messages = [];
+ this.actions = {};
+};
+// import JsUnitTest.Unit.Assertions
+
+for (method in JsUnitTest.Unit.Assertions) {
+ JsUnitTest.Unit.Testcase.prototype[method] = JsUnitTest.Unit.Assertions[method];
+}
+
+JsUnitTest.Unit.Testcase.prototype.isWaiting = false;
+JsUnitTest.Unit.Testcase.prototype.timeToWait = 1000;
+JsUnitTest.Unit.Testcase.prototype.assertions = 0;
+JsUnitTest.Unit.Testcase.prototype.failures = 0;
+JsUnitTest.Unit.Testcase.prototype.errors = 0;
+JsUnitTest.Unit.Testcase.prototype.warnings = 0;
+JsUnitTest.Unit.Testcase.prototype.isRunningFromRake = window.location.port;
+
+// JsUnitTest.Unit.Testcase.prototype.isRunningFromRake = window.location.port == 4711;
+
+JsUnitTest.Unit.Testcase.prototype.wait = function(time, nextPart) {
+ this.isWaiting = true;
+ this.test = nextPart;
+ this.timeToWait = time;
+};
+
+JsUnitTest.Unit.Testcase.prototype.run = function(rethrow) {
+ try {
+ try {
+ if (!this.isWaiting) this.setup();
+ this.isWaiting = false;
+ this.test();
+ } finally {
+ if(!this.isWaiting) {
+ this.teardown();
+ }
+ }
+ }
+ catch(e) {
+ if (rethrow) throw e;
+ this.error(e, this);
+ }
+};
+
+JsUnitTest.Unit.Testcase.prototype.summary = function() {
+ var msg = '#{assertions} assertions, #{failures} failures, #{errors} errors, #{warnings} warnings\n';
+ return new JsUnitTest.Template(msg).evaluate(this) +
+ this.messages.join("\n");
+};
+
+JsUnitTest.Unit.Testcase.prototype.pass = function() {
+ this.assertions++;
+};
+
+JsUnitTest.Unit.Testcase.prototype.fail = function(message) {
+ this.failures++;
+ var line = "";
+ try {
+ throw new Error("stack");
+ } catch(e){
+ line = (/\.html:(\d+)/.exec(e.stack || '') || ['',''])[1];
+ }
+ this.messages.push("Failure: " + message + (line ? " Line #" + line : ""));
+};
+
+JsUnitTest.Unit.Testcase.prototype.warning = function(message) {
+ this.warnings++;
+ var line = "";
+ try {
+ throw new Error("stack");
+ } catch(e){
+ line = (/\.html:(\d+)/.exec(e.stack || '') || ['',''])[1];
+ }
+ this.messages.push("Warning: " + message + (line ? " Line #" + line : ""));
+};
+JsUnitTest.Unit.Testcase.prototype.warn = JsUnitTest.Unit.Testcase.prototype.warning;
+
+JsUnitTest.Unit.Testcase.prototype.info = function(message) {
+ this.messages.push("Info: " + message);
+};
+
+JsUnitTest.Unit.Testcase.prototype.error = function(error, test) {
+ this.errors++;
+ this.actions['retry with throw'] = function() { test.run(true) };
+ this.messages.push(error.name + ": "+ error.message + "(" + JsUnitTest.inspect(error) + ")");
+};
+
+JsUnitTest.Unit.Testcase.prototype.status = function() {
+ if (this.failures > 0) return 'failed';
+ if (this.errors > 0) return 'error';
+ if (this.warnings > 0) return 'warning';
+ return 'passed';
+};
+
+JsUnitTest.Unit.Testcase.prototype.benchmark = function(operation, iterations) {
+ var startAt = new Date();
+ (iterations || 1).times(operation);
+ var timeTaken = ((new Date())-startAt);
+ this.info((arguments[2] || 'Operation') + ' finished ' +
+ iterations + ' iterations in ' + (timeTaken/1000)+'s' );
+ return timeTaken;
+};
+
+Test = JsUnitTest
54 assets/unittest.css
@@ -0,0 +1,54 @@
+body, div, p, h1, h2, h3, ul, ol, span, a, table, td, form, img, li {
+ font-family: sans-serif;
+}
+
+body {
+ font-size:0.8em;
+}
+
+#log {
+ padding-bottom: 1em;
+ border-bottom: 2px solid #000;
+ margin-bottom: 2em;
+}
+
+.logsummary {
+ margin-top: 1em;
+ margin-bottom: 1em;
+ padding: 1ex;
+ border: 1px solid #000;
+ font-weight: bold;
+}
+
+.logtable {
+ width:100%;
+ border-collapse: collapse;
+ border: 1px dotted #666;
+}
+
+.logtable td, .logtable th {
+ text-align: left;
+ padding: 3px 8px;
+ border: 1px dotted #666;
+}
+
+.logtable .passed {
+ background-color: #cfc;
+}
+
+.logtable .failed, .logtable .error {
+ background-color: #fcc;
+}
+
+.logtable .warning {
+ background-color: #FC6;
+}
+
+.logtable td div.action_buttons {
+ display: inline;
+}
+
+.logtable td div.action_buttons input {
+ margin: 0 5px;
+ font-size: 10px;
+}
151 jquery-1.4.min.js
@@ -0,0 +1,151 @@
+/*!
+ * jQuery JavaScript Library v1.4
+ * http://jquery.com/
+ *
+ * Copyright 2010, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://docs.jquery.com/License
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2010, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Wed Jan 13 15:23:05 2010 -0500
+ */
+(function(A,w){function oa(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(oa,1);return}c.ready()}}function La(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function $(a,b,d,f,e,i){var j=a.length;if(typeof b==="object"){for(var o in b)$(a,o,b[o],f,e,d);return a}if(d!==w){f=!i&&f&&c.isFunction(d);for(o=0;o<j;o++)e(a[o],b,f?d.call(a[o],o,e(a[o],b)):d,i);return a}return j?
+e(a[0],b):null}function K(){return(new Date).getTime()}function aa(){return false}function ba(){return true}function pa(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function qa(a){var b=true,d=[],f=[],e=arguments,i,j,o,p,n,t=c.extend({},c.data(this,"events").live);for(p in t){j=t[p];if(j.live===a.type||j.altLive&&c.inArray(a.type,j.altLive)>-1){i=j.data;i.beforeFilter&&i.beforeFilter[a.type]&&!i.beforeFilter[a.type](a)||f.push(j.selector)}else delete t[p]}i=c(a.target).closest(f,a.currentTarget);
+n=0;for(l=i.length;n<l;n++)for(p in t){j=t[p];o=i[n].elem;f=null;if(i[n].selector===j.selector){if(j.live==="mouseenter"||j.live==="mouseleave")f=c(a.relatedTarget).closest(j.selector)[0];if(!f||f!==o)d.push({elem:o,fn:j})}}n=0;for(l=d.length;n<l;n++){i=d[n];a.currentTarget=i.elem;a.data=i.fn.data;if(i.fn.apply(i.elem,e)===false){b=false;break}}return b}function ra(a,b){return["live",a,b.replace(/\./g,"`").replace(/ /g,"&")].join(".")}function sa(a){return!a||!a.parentNode||a.parentNode.nodeType===
+11}function ta(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var i in f)for(var j in f[i])c.event.add(this,i,f[i][j],f[i][j].data)}}})}function ua(a,b,d){var f,e,i;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&a[0].indexOf("<option")<0){e=true;if(i=c.fragments[a[0]])if(i!==1)f=i}if(!f){b=b&&b[0]?b[0].ownerDocument||b[0]:s;f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=
+i?f:1;return{fragment:f,cacheable:e}}function T(a){for(var b=0,d,f;(d=a[b])!=null;b++)if(!c.noData[d.nodeName.toLowerCase()]&&(f=d[H]))delete c.cache[f]}function L(a,b){var d={};c.each(va.concat.apply([],va.slice(0,b)),function(){d[this]=a});return d}function wa(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Ma=A.jQuery,Na=A.$,s=A.document,U,Oa=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Pa=/^.[^:#\[\.,]*$/,Qa=/\S/,
+Ra=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Sa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],M,ca=Object.prototype.toString,da=Object.prototype.hasOwnProperty,ea=Array.prototype.push,R=Array.prototype.slice,V=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(typeof a==="string")if((d=Oa.exec(a))&&(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Sa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];
+c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=ua([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return U.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a)}else return!b||b.jquery?(b||U).find(a):c(b).find(a);else if(c.isFunction(a))return U.ready(a);if(a.selector!==w){this.selector=a.selector;
+this.context=a.context}return c.isArray(a)?this.setArray(a):c.makeArray(a,this)},selector:"",jquery:"1.4",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){a=c(a||null);a.prevObject=this;a.context=this.context;if(b==="find")a.selector=this.selector+(this.selector?" ":"")+d;else if(b)a.selector=this.selector+"."+b+"("+d+")";return a},setArray:function(a){this.length=
+0;ea.apply(this,a);return this},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||
+c(null)},push:ea,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,i,j,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(i in e){j=a[i];o=e[i];if(a!==o)if(f&&o&&(c.isPlainObject(o)||c.isArray(o))){j=j&&(c.isPlainObject(j)||c.isArray(j))?j:c.isArray(o)?[]:{};a[i]=c.extend(f,j,o)}else if(o!==w)a[i]=
+o}return a};c.extend({noConflict:function(a){A.$=Na;if(a)A.jQuery=Ma;return c},isReady:false,ready:function(){if(!c.isReady){if(!s.body)return setTimeout(c.ready,13);c.isReady=true;if(Q){for(var a,b=0;a=Q[b++];)a.call(s,c);Q=null}c.fn.triggerHandler&&c(s).triggerHandler("ready")}},bindReady:function(){if(!xa){xa=true;if(s.readyState==="complete")return c.ready();if(s.addEventListener){s.addEventListener("DOMContentLoaded",M,false);A.addEventListener("load",c.ready,false)}else if(s.attachEvent){s.attachEvent("onreadystatechange",
+M);A.attachEvent("onload",c.ready);var a=false;try{a=A.frameElement==null}catch(b){}s.documentElement.doScroll&&a&&oa()}}},isFunction:function(a){return ca.call(a)==="[object Function]"},isArray:function(a){return ca.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||ca.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!da.call(a,"constructor")&&!da.call(a.constructor.prototype,"isPrototypeOf"))return false;var b;for(b in a);return b===w||da.call(a,b)},
+isEmptyObject:function(a){for(var b in a)return false;return true},noop:function(){},globalEval:function(a){if(a&&Qa.test(a)){var b=s.getElementsByTagName("head")[0]||s.documentElement,d=s.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(s.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,i=a.length,j=i===w||c.isFunction(a);
+if(d)if(j)for(f in a){if(b.apply(a[f],d)===false)break}else for(;e<i;){if(b.apply(a[e++],d)===false)break}else if(j)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<i&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Ra,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ea.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=
+0,f=b.length;d<f;d++)if(b[d]===a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==w;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,i=a.length;e<i;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,i=0,j=a.length;i<j;i++){e=b(a[i],i,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b===
+"string"){d=a;a=d[b];b=w}else if(b&&!c.isFunction(b)){d=b;b=w}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){var b={browser:""};a=a.toLowerCase();if(/webkit/.test(a))b={browser:"webkit",version:/webkit[\/ ]([\w.]+)/};else if(/opera/.test(a))b={browser:"opera",version:/version/.test(a)?/version[\/ ]([\w.]+)/:/opera[\/ ]([\w.]+)/};else if(/msie/.test(a))b={browser:"msie",version:/msie ([\w.]+)/};else if(/mozilla/.test(a)&&
+!/compatible/.test(a))b={browser:"mozilla",version:/rv:([\w.]+)/};b.version=(b.version&&b.version.exec(a)||[0,"0"])[1];return b},browser:{}});P=c.uaMatch(P);if(P.browser){c.browser[P.browser]=true;c.browser.version=P.version}if(c.browser.webkit)c.browser.safari=true;if(V)c.inArray=function(a,b){return V.call(b,a)};U=c(s);if(s.addEventListener)M=function(){s.removeEventListener("DOMContentLoaded",M,false);c.ready()};else if(s.attachEvent)M=function(){if(s.readyState==="complete"){s.detachEvent("onreadystatechange",
+M);c.ready()}};if(V)c.inArray=function(a,b){return V.call(b,a)};(function(){c.support={};var a=s.documentElement,b=s.createElement("script"),d=s.createElement("div"),f="script"+K();d.style.display="none";d.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";var e=d.getElementsByTagName("*"),i=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!i)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,
+htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(i.getAttribute("style")),hrefNormalized:i.getAttribute("href")==="/a",opacity:/^0.55$/.test(i.style.opacity),cssFloat:!!i.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(j){}a.insertBefore(b,
+a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",