summaryrefslogtreecommitdiffstatshomepage
path: root/includes/js/util/doh
diff options
context:
space:
mode:
Diffstat (limited to 'includes/js/util/doh')
-rw-r--r--includes/js/util/doh/LICENSE195
-rw-r--r--includes/js/util/doh/README12
-rw-r--r--includes/js/util/doh/_browserRunner.js465
-rw-r--r--includes/js/util/doh/_rhinoRunner.js17
-rw-r--r--includes/js/util/doh/_sounds/LICENSE10
-rw-r--r--includes/js/util/doh/_sounds/doh.wavbin0 -> 2878 bytes
-rw-r--r--includes/js/util/doh/_sounds/dohaaa.wavbin0 -> 14318 bytes
-rw-r--r--includes/js/util/doh/_sounds/woohoo.wavbin0 -> 3814 bytes
-rw-r--r--includes/js/util/doh/runner.html283
-rw-r--r--includes/js/util/doh/runner.js948
-rw-r--r--includes/js/util/doh/runner.sh3
-rw-r--r--includes/js/util/doh/small_logo.pngbin0 -> 1893 bytes
12 files changed, 1933 insertions, 0 deletions
diff --git a/includes/js/util/doh/LICENSE b/includes/js/util/doh/LICENSE
new file mode 100644
index 0000000..3fa2720
--- /dev/null
+++ b/includes/js/util/doh/LICENSE
@@ -0,0 +1,195 @@
+Dojo is available under *either* the terms of the modified BSD license *or* the
+Academic Free License version 2.1. As a recipient of Dojo, you may choose which
+license to receive this code under (except as noted in per-module LICENSE
+files). Some modules may not be the copyright of the Dojo Foundation. These
+modules contain explicit declarations of copyright in both the LICENSE files in
+the directories in which they reside and in the code itself. No external
+contributions are allowed under licenses which are fundamentally incompatible
+with the AFL or BSD licenses that Dojo is distributed under.
+
+The text of the AFL and BSD licenses is reproduced below.
+
+-------------------------------------------------------------------------------
+The "New" BSD License:
+**********************
+
+Copyright (c) 2005-2008, The Dojo Foundation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * Neither the name of the Dojo Foundation nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+-------------------------------------------------------------------------------
+The Academic Free License, v. 2.1:
+**********************************
+
+This Academic Free License (the "License") applies to any original work of
+authorship (the "Original Work") whose owner (the "Licensor") has placed the
+following notice immediately following the copyright notice for the Original
+Work:
+
+Licensed under the Academic Free License version 2.1
+
+1) Grant of Copyright License. Licensor hereby grants You a world-wide,
+royalty-free, non-exclusive, perpetual, sublicenseable license to do the
+following:
+
+a) to reproduce the Original Work in copies;
+
+b) to prepare derivative works ("Derivative Works") based upon the Original
+Work;
+
+c) to distribute copies of the Original Work and Derivative Works to the
+public;
+
+d) to perform the Original Work publicly; and
+
+e) to display the Original Work publicly.
+
+2) Grant of Patent License. Licensor hereby grants You a world-wide,
+royalty-free, non-exclusive, perpetual, sublicenseable license, under patent
+claims owned or controlled by the Licensor that are embodied in the Original
+Work as furnished by the Licensor, to make, use, sell and offer for sale the
+Original Work and Derivative Works.
+
+3) Grant of Source Code License. The term "Source Code" means the preferred
+form of the Original Work for making modifications to it and all available
+documentation describing how to modify the Original Work. Licensor hereby
+agrees to provide a machine-readable copy of the Source Code of the Original
+Work along with each copy of the Original Work that Licensor distributes.
+Licensor reserves the right to satisfy this obligation by placing a
+machine-readable copy of the Source Code in an information repository
+reasonably calculated to permit inexpensive and convenient access by You for as
+long as Licensor continues to distribute the Original Work, and by publishing
+the address of that information repository in a notice immediately following
+the copyright notice that applies to the Original Work.
+
+4) Exclusions From License Grant. Neither the names of Licensor, nor the names
+of any contributors to the Original Work, nor any of their trademarks or
+service marks, may be used to endorse or promote products derived from this
+Original Work without express prior written permission of the Licensor. Nothing
+in this License shall be deemed to grant any rights to trademarks, copyrights,
+patents, trade secrets or any other intellectual property of Licensor except as
+expressly stated herein. No patent license is granted to make, use, sell or
+offer to sell embodiments of any patent claims other than the licensed claims
+defined in Section 2. No right is granted to the trademarks of Licensor even if
+such marks are included in the Original Work. Nothing in this License shall be
+interpreted to prohibit Licensor from licensing under different terms from this
+License any Original Work that Licensor otherwise would have a right to
+license.
+
+5) This section intentionally omitted.
+
+6) Attribution Rights. You must retain, in the Source Code of any Derivative
+Works that You create, all copyright, patent or trademark notices from the
+Source Code of the Original Work, as well as any notices of licensing and any
+descriptive text identified therein as an "Attribution Notice." You must cause
+the Source Code for any Derivative Works that You create to carry a prominent
+Attribution Notice reasonably calculated to inform recipients that You have
+modified the Original Work.
+
+7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that
+the copyright in and to the Original Work and the patent rights granted herein
+by Licensor are owned by the Licensor or are sublicensed to You under the terms
+of this License with the permission of the contributor(s) of those copyrights
+and patent rights. Except as expressly stated in the immediately proceeding
+sentence, the Original Work is provided under this License on an "AS IS" BASIS
+and WITHOUT WARRANTY, either express or implied, including, without limitation,
+the warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU.
+This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No
+license to Original Work is granted hereunder except under this disclaimer.
+
+8) Limitation of Liability. Under no circumstances and under no legal theory,
+whether in tort (including negligence), contract, or otherwise, shall the
+Licensor be liable to any person for any direct, indirect, special, incidental,
+or consequential damages of any character arising as a result of this License
+or the use of the Original Work including, without limitation, damages for loss
+of goodwill, work stoppage, computer failure or malfunction, or any and all
+other commercial damages or losses. This limitation of liability shall not
+apply to liability for death or personal injury resulting from Licensor's
+negligence to the extent applicable law prohibits such limitation. Some
+jurisdictions do not allow the exclusion or limitation of incidental or
+consequential damages, so this exclusion and limitation may not apply to You.
+
+9) Acceptance and Termination. If You distribute copies of the Original Work or
+a Derivative Work, You must make a reasonable effort under the circumstances to
+obtain the express assent of recipients to the terms of this License. Nothing
+else but this License (or another written agreement between Licensor and You)
+grants You permission to create Derivative Works based upon the Original Work
+or to exercise any of the rights granted in Section 1 herein, and any attempt
+to do so except under the terms of this License (or another written agreement
+between Licensor and You) is expressly prohibited by U.S. copyright law, the
+equivalent laws of other countries, and by international treaty. Therefore, by
+exercising any of the rights granted to You in Section 1 herein, You indicate
+Your acceptance of this License and all of its terms and conditions.
+
+10) Termination for Patent Action. This License shall terminate automatically
+and You may no longer exercise any of the rights granted to You by this License
+as of the date You commence an action, including a cross-claim or counterclaim,
+against Licensor or any licensee alleging that the Original Work infringes a
+patent. This termination provision shall not apply for an action alleging
+patent infringement by combinations of the Original Work with other software or
+hardware.
+
+11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this
+License may be brought only in the courts of a jurisdiction wherein the
+Licensor resides or in which Licensor conducts its primary business, and under
+the laws of that jurisdiction excluding its conflict-of-law provisions. The
+application of the United Nations Convention on Contracts for the International
+Sale of Goods is expressly excluded. Any use of the Original Work outside the
+scope of this License or after its termination shall be subject to the
+requirements and penalties of the U.S. Copyright Act, 17 U.S.C. § 101 et
+seq., the equivalent laws of other countries, and international treaty. This
+section shall survive the termination of this License.
+
+12) Attorneys Fees. In any action to enforce the terms of this License or
+seeking damages relating thereto, the prevailing party shall be entitled to
+recover its costs and expenses, including, without limitation, reasonable
+attorneys' fees and costs incurred in connection with such action, including
+any appeal of such action. This section shall survive the termination of this
+License.
+
+13) Miscellaneous. This License represents the complete agreement concerning
+the subject matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent necessary to
+make it enforceable.
+
+14) Definition of "You" in This License. "You" throughout this License, whether
+in upper or lower case, means an individual or a legal entity exercising rights
+under, and complying with all of the terms of, this License. For legal
+entities, "You" includes any entity that controls, is controlled by, or is
+under common control with you. For purposes of this definition, "control" means
+(i) the power, direct or indirect, to cause the direction or management of such
+entity, whether by contract or otherwise, or (ii) ownership of fifty percent
+(50%) or more of the outstanding shares, or (iii) beneficial ownership of such
+entity.
+
+15) Right to Use. You may use the Original Work in all ways not otherwise
+restricted or conditioned by this License or by law, and Licensor promises not
+to interfere with or be responsible for such uses by You.
+
+This license is Copyright (C) 2003-2004 Lawrence E. Rosen. All rights reserved.
+Permission is hereby granted to copy and distribute this license without
+modification. This license may not be modified without the express written
+permission of its copyright owner.
diff --git a/includes/js/util/doh/README b/includes/js/util/doh/README
new file mode 100644
index 0000000..c4439dd
--- /dev/null
+++ b/includes/js/util/doh/README
@@ -0,0 +1,12 @@
+DOH may be run standalone by issuing a command like the following:
+
+java -jar ../shrinksafe/custom_rhino.jar runner.js testModule=tests.colors
+
+where the testModule argument is optional and custom_rhino.jar is just a
+convenient copy of the Rhino JavaScript engine -- the custom patch is not
+required.
+
+Optional arguments include:
+ * dojoUrl - specifies the location of dojo.js
+ * testUrl - specifies a Javascript file to load with initialization code
+ * testModule - specifies a test module in the dojo package namespace
diff --git a/includes/js/util/doh/_browserRunner.js b/includes/js/util/doh/_browserRunner.js
new file mode 100644
index 0000000..9e9e3f3
--- /dev/null
+++ b/includes/js/util/doh/_browserRunner.js
@@ -0,0 +1,465 @@
+if(window["dojo"]){
+ dojo.provide("doh._browserRunner");
+}
+
+// FIXME: need to add prompting for monkey-do testing
+// FIXME: need to implement progress bar
+// FIXME: need to implement errors in progress bar
+
+(function(){
+ if(window.parent == window){
+ // we're the top-dog window.
+
+ // borrowed from Dojo, etc.
+ var byId = function(id){
+ return document.getElementById(id);
+ }
+
+ var _addOnEvt = function( type, // string
+ refOrName, // function or string
+ scope){ // object, defaults is window
+
+ if(!scope){ scope = window; }
+
+ var funcRef = refOrName;
+ if(typeof refOrName == "string"){
+ funcRef = scope[refOrName];
+ }
+ var enclosedFunc = function(){ return funcRef.apply(scope, arguments); };
+
+ if((window["dojo"])&&(type == "load")){
+ dojo.addOnLoad(enclosedFunc);
+ }else{
+ if(window["attachEvent"]){
+ window.attachEvent("on"+type, enclosedFunc);
+ }else if(window["addEventListener"]){
+ window.addEventListener(type, enclosedFunc, false);
+ }else if(document["addEventListener"]){
+ document.addEventListener(type, enclosedFunc, false);
+ }
+ }
+ };
+
+ //
+ // Over-ride or implement base runner.js-provided methods
+ //
+ var _logBacklog = [];
+ var sendToLogPane = function(args, skip){
+ var msg = "";
+ for(var x=0; x<args.length; x++){
+ msg += " "+args[x];
+ }
+ // workarounds for IE. Wheeee!!!
+ msg = msg.replace("\t", "&nbsp;&nbsp;&nbsp;&nbsp;");
+ msg = msg.replace(" ", "&nbsp;");
+ msg = msg.replace("\n", "<br>&nbsp;");
+ if(!byId("logBody")){
+ _logBacklog.push(msg);
+ return;
+ }else if((_logBacklog.length)&&(!skip)){
+ var tm;
+ while(tm=_logBacklog.shift()){
+ sendToLogPane(tm, true);
+ }
+ }
+ var tn = document.createElement("div");
+ tn.innerHTML = msg;
+ byId("logBody").appendChild(tn);
+ }
+
+ doh._init = (function(oi){
+ return function(){
+ var lb = byId("logBody");
+ if(lb){
+ // clear the console before each run
+ while(lb.firstChild){
+ lb.removeChild(lb.firstChild);
+ }
+ }
+ oi.apply(doh, arguments);
+ }
+ })(doh._init);
+
+ if(this["opera"] && opera.postError){
+ doh.debug = function(){
+ var msg = "";
+ for(var x=0; x<arguments.length; x++){
+ msg += " "+arguments[x];
+ }
+ sendToLogPane([msg]);
+ opera.postError("DEBUG:"+msg);
+ }
+ }else if(window["console"]){
+ if(console.info){
+ doh.debug = function(){
+ sendToLogPane.call(window, arguments);
+ console.debug.apply(console, arguments);
+ }
+ }else{
+ doh.debug = function(){
+ var msg = "";
+ for(var x=0; x<arguments.length; x++){
+ msg += " "+arguments[x];
+ }
+ sendToLogPane([msg]);
+ console.log("DEBUG:"+msg);
+ }
+ }
+ }else{
+ doh.debug = function(){
+ sendToLogPane.call(window, arguments);
+ }
+ }
+
+ var loaded = false;
+ var groupTemplate = null;
+ var testTemplate = null;
+
+ var groupNodes = {};
+
+ var _groupTogglers = {};
+
+ var _getGroupToggler = function(group, toggle){
+ if(_groupTogglers[group]){ return _groupTogglers[group]; }
+ var rolledUp = true;
+ return _groupTogglers[group] = function(evt, forceOpen){
+ var nodes = groupNodes[group].__items;
+ if(rolledUp||forceOpen){
+ rolledUp = false;
+ for(var x=0; x<nodes.length; x++){
+ nodes[x].style.display = "";
+ }
+ toggle.innerHTML = "&#054;";
+ }else{
+ rolledUp = true;
+ for(var x=0; x<nodes.length; x++){
+ nodes[x].style.display = "none";
+ }
+ toggle.innerHTML = "&#052;";
+ }
+ };
+ }
+
+ var addGroupToList = function(group){
+ if(!byId("testList")){ return; }
+ var tb = byId("testList").tBodies[0];
+ var tg = groupTemplate.cloneNode(true);
+ var tds = tg.getElementsByTagName("td");
+ var toggle = tds[0];
+ toggle.onclick = _getGroupToggler(group, toggle);
+ var cb = tds[1].getElementsByTagName("input")[0];
+ cb.group = group;
+ cb.onclick = function(evt){
+ doh._groups[group].skip = (!this.checked);
+ }
+ tds[2].innerHTML = group;
+ tds[3].innerHTML = "";
+
+ tb.appendChild(tg);
+ return tg;
+ }
+
+ var addFixtureToList = function(group, fixture){
+ if(!testTemplate){ return; }
+ var cgn = groupNodes[group];
+ if(!cgn["__items"]){ cgn.__items = []; }
+ var tn = testTemplate.cloneNode(true);
+ var tds = tn.getElementsByTagName("td");
+
+ tds[2].innerHTML = fixture.name;
+ tds[3].innerHTML = "";
+
+ var nn = (cgn.__lastFixture||cgn.__groupNode).nextSibling;
+ if(nn){
+ nn.parentNode.insertBefore(tn, nn);
+ }else{
+ cgn.__groupNode.parentNode.appendChild(tn);
+ }
+ // FIXME: need to make group display toggleable!!
+ tn.style.display = "none";
+ cgn.__items.push(tn);
+ return cgn.__lastFixture = tn;
+ }
+
+ var getFixtureNode = function(group, fixture){
+ if(groupNodes[group]){
+ return groupNodes[group][fixture.name];
+ }
+ return null;
+ }
+
+ var getGroupNode = function(group){
+ if(groupNodes[group]){
+ return groupNodes[group].__groupNode;
+ }
+ return null;
+ }
+
+ var updateBacklog = [];
+ doh._updateTestList = function(group, fixture, unwindingBacklog){
+ if(!loaded){
+ if(group && fixture){
+ updateBacklog.push([group, fixture]);
+ }
+ return;
+ }else if((updateBacklog.length)&&(!unwindingBacklog)){
+ var tr;
+ while(tr=updateBacklog.shift()){
+ doh._updateTestList(tr[0], tr[1], true);
+ }
+ }
+ if(group && fixture){
+ if(!groupNodes[group]){
+ groupNodes[group] = {
+ "__groupNode": addGroupToList(group)
+ };
+ }
+ if(!groupNodes[group][fixture.name]){
+ groupNodes[group][fixture.name] = addFixtureToList(group, fixture)
+ }
+ }
+ }
+
+ doh._testRegistered = doh._updateTestList;
+
+ doh._groupStarted = function(group){
+ // console.debug("_groupStarted", group);
+ var gn = getGroupNode(group);
+ if(gn){
+ gn.className = "inProgress";
+ }
+ }
+
+ doh._groupFinished = function(group, success){
+ // console.debug("_groupFinished", group);
+ var gn = getGroupNode(group);
+ if(gn){
+ gn.className = (success) ? "success" : "failure";
+ }
+ }
+
+ doh._testStarted = function(group, fixture){
+ // console.debug("_testStarted", group, fixture.name);
+ var fn = getFixtureNode(group, fixture);
+ if(fn){
+ fn.className = "inProgress";
+ }
+ }
+
+ var _nameTimes = {};
+ var _playSound = function(name){
+ if(byId("hiddenAudio") && byId("audio") && byId("audio").checked){
+ // console.debug("playing:", name);
+ var nt = _nameTimes[name];
+ // only play sounds once every second or so
+ if((!nt)||(((new Date)-nt) > 700)){
+ _nameTimes[name] = new Date();
+ var tc = document.createElement("span");
+ byId("hiddenAudio").appendChild(tc);
+ tc.innerHTML = '<embed src="_sounds/'+name+'.wav" autostart="true" loop="false" hidden="true" width="1" height="1"></embed>';
+ }
+ }
+ }
+
+ doh._testFinished = function(group, fixture, success){
+ var fn = getFixtureNode(group, fixture);
+ if(fn){
+ fn.getElementsByTagName("td")[3].innerHTML = (fixture.endTime-fixture.startTime)+"ms";
+ fn.className = (success) ? "success" : "failure";
+
+ if(!success){
+ _playSound("doh");
+ var gn = getGroupNode(group);
+ if(gn){
+ gn.className = "failure";
+ _getGroupToggler(group)(null, true);
+ }
+ }
+ }
+ this.debug(((success) ? "PASSED" : "FAILED"), "test:", fixture.name);
+ }
+
+ // FIXME: move implementation to _browserRunner?
+ doh.registerUrl = function( /*String*/ group,
+ /*String*/ url,
+ /*Integer*/ timeout){
+ var tg = new String(group);
+ this.register(group, {
+ name: url,
+ setUp: function(){
+ doh.currentGroupName = tg;
+ doh.currentGroup = this;
+ doh.currentUrl = url;
+ this.d = new doh.Deferred();
+ doh.currentTestDeferred = this.d;
+ showTestPage();
+ byId("testBody").src = url;
+ },
+ timeout: timeout||10000, // 10s
+ // timeout: timeout||1000, // 10s
+ runTest: function(){
+ // FIXME: implement calling into the url's groups here!!
+ return this.d;
+ },
+ tearDown: function(){
+ doh.currentGroupName = null;
+ doh.currentGroup = null;
+ doh.currentTestDeferred = null;
+ doh.currentUrl = null;
+ // this.d.errback(false);
+ // byId("testBody").src = "about:blank";
+ showLogPage();
+ }
+ });
+ }
+
+ //
+ // Utility code for runner.html
+ //
+ // var isSafari = navigator.appVersion.indexOf("Safari") >= 0;
+ var tabzidx = 1;
+ var _showTab = function(toShow, toHide){
+ // FIXME: I don't like hiding things this way.
+ byId(toHide).style.display = "none";
+ with(byId(toShow).style){
+ display = "";
+ zIndex = ++tabzidx;
+ }
+ }
+
+ showTestPage = function(){
+ _showTab("testBody", "logBody");
+ }
+
+ showLogPage = function(){
+ _showTab("logBody", "testBody");
+ }
+
+ var runAll = true;
+ toggleRunAll = function(){
+ // would be easier w/ query...sigh
+ runAll = (!runAll);
+ if(!byId("testList")){ return; }
+ var tb = byId("testList").tBodies[0];
+ var inputs = tb.getElementsByTagName("input");
+ var x=0; var tn;
+ while(tn=inputs[x++]){
+ tn.checked = runAll;
+ doh._groups[tn.group].skip = (!runAll);
+ }
+ }
+
+ var listHeightTimer = null;
+ var setListHeight = function(){
+ if(listHeightTimer){
+ clearTimeout(listHeightTimer);
+ }
+ var tl = byId("testList");
+ if(!tl){ return; }
+ listHeightTimer = setTimeout(function(){
+ tl.style.display = "none";
+ tl.style.display = "";
+
+ }, 10);
+ }
+
+ _addOnEvt("resize", setListHeight);
+ _addOnEvt("load", setListHeight);
+ _addOnEvt("load", function(){
+ if(loaded){ return; }
+ loaded = true;
+ groupTemplate = byId("groupTemplate");
+ if(!groupTemplate){
+ // make sure we've got an ammenable DOM structure
+ return;
+ }
+ groupTemplate.parentNode.removeChild(groupTemplate);
+ groupTemplate.style.display = "";
+ testTemplate = byId("testTemplate");
+ testTemplate.parentNode.removeChild(testTemplate);
+ testTemplate.style.display = "";
+ doh._updateTestList();
+ });
+
+ _addOnEvt("load",
+ function(){
+ doh._onEnd = function(){
+ if(doh._failureCount == 0){
+ doh.debug("WOOHOO!!");
+ _playSound("woohoo");
+ }else{
+ console.debug("doh._failureCount:", doh._failureCount);
+ }
+ if(byId("play")){
+ toggleRunning();
+ }
+ }
+ if(!byId("play")){
+ // make sure we've got an ammenable DOM structure
+ return;
+ }
+ var isRunning = false;
+ var toggleRunning = function(){
+ // ugg, this would be so much better w/ dojo.query()
+ if(isRunning){
+ byId("play").style.display = byId("pausedMsg").style.display = "";
+ byId("playingMsg").style.display = byId("pause").style.display = "none";
+ isRunning = false;
+ }else{
+ byId("play").style.display = byId("pausedMsg").style.display = "none";
+ byId("playingMsg").style.display = byId("pause").style.display = "";
+ isRunning = true;
+ }
+ }
+ doh.run = (function(oldRun){
+ return function(){
+ if(!doh._currentGroup){
+ toggleRunning();
+ }
+ return oldRun.apply(doh, arguments);
+ }
+ })(doh.run);
+ var btns = byId("toggleButtons").getElementsByTagName("span");
+ var node; var idx=0;
+ while(node=btns[idx++]){
+ node.onclick = toggleRunning;
+ }
+ }
+ );
+ }else{
+ // we're in an iframe environment. Time to mix it up a bit.
+
+ _doh = window.parent.doh;
+ var _thisGroup = _doh.currentGroupName;
+ var _thisUrl = _doh.currentUrl;
+ if(_thisGroup){
+ doh._testRegistered = function(group, tObj){
+ _doh._updateTestList(_thisGroup, tObj);
+ }
+ doh._onEnd = function(){
+ _doh._errorCount += doh._errorCount;
+ _doh._failureCount += doh._failureCount;
+ _doh._testCount += doh._testCount;
+ // should we be really adding raw group counts?
+ _doh._groupCount += doh._groupCount;
+ _doh.currentTestDeferred.callback(true);
+ }
+ var otr = doh._getTestObj;
+ doh._getTestObj = function(){
+ var tObj = otr.apply(doh, arguments);
+ tObj.name = _thisUrl+"::"+arguments[0]+"::"+tObj.name;
+ return tObj;
+ }
+ doh.debug = doh.hitch(_doh, "debug");
+ doh.registerUrl = doh.hitch(_doh, "registerUrl");
+ doh._testStarted = function(group, fixture){
+ _doh._testStarted(_thisGroup, fixture);
+ }
+ doh._testFinished = function(g, f, s){
+ _doh._testFinished(_thisGroup, f, s);
+ }
+ doh._report = function(){};
+ }
+ }
+
+})();
diff --git a/includes/js/util/doh/_rhinoRunner.js b/includes/js/util/doh/_rhinoRunner.js
new file mode 100644
index 0000000..ae47597
--- /dev/null
+++ b/includes/js/util/doh/_rhinoRunner.js
@@ -0,0 +1,17 @@
+if(this["dojo"]){
+ dojo.provide("doh._rhinoRunner");
+}
+
+doh.debug = print;
+
+// Override the doh._report method to make it quit with an
+// appropriate exit code in case of test failures.
+(function(){
+ var oldReport = doh._report;
+ doh._report = function(){
+ oldReport.apply(doh, arguments);
+ if(this._failureCount > 0 || this._errorCount > 0){
+ quit(1);
+ }
+ }
+})();
diff --git a/includes/js/util/doh/_sounds/LICENSE b/includes/js/util/doh/_sounds/LICENSE
new file mode 100644
index 0000000..e8e11d4
--- /dev/null
+++ b/includes/js/util/doh/_sounds/LICENSE
@@ -0,0 +1,10 @@
+License Disclaimer:
+
+All contents of this directory are Copyright (c) the Dojo Foundation, with the
+following exceptions:
+-------------------------------------------------------------------------------
+
+woohoo.wav, doh.wav, dohaaa.wav:
+ * Copyright original authors.
+ Copied from:
+ http://simpson-homer.com/homer-simpson-soundboard.html
diff --git a/includes/js/util/doh/_sounds/doh.wav b/includes/js/util/doh/_sounds/doh.wav
new file mode 100644
index 0000000..5e8a583
--- /dev/null
+++ b/includes/js/util/doh/_sounds/doh.wav
Binary files differ
diff --git a/includes/js/util/doh/_sounds/dohaaa.wav b/includes/js/util/doh/_sounds/dohaaa.wav
new file mode 100644
index 0000000..2220921
--- /dev/null
+++ b/includes/js/util/doh/_sounds/dohaaa.wav
Binary files differ
diff --git a/includes/js/util/doh/_sounds/woohoo.wav b/includes/js/util/doh/_sounds/woohoo.wav
new file mode 100644
index 0000000..eb69217
--- /dev/null
+++ b/includes/js/util/doh/_sounds/woohoo.wav
Binary files differ
diff --git a/includes/js/util/doh/runner.html b/includes/js/util/doh/runner.html
new file mode 100644
index 0000000..dbcd68c
--- /dev/null
+++ b/includes/js/util/doh/runner.html
@@ -0,0 +1,283 @@
+<html>
+ <!--
+ NOTE: we are INTENTIONALLY in quirks mode. It makes it much easier to
+ get a "full screen" UI w/ straightforward CSS.
+ -->
+ <!--
+ // TODO: implement global progress bar
+ // TODO: provide a UI for prompted tests
+ -->
+ <head>
+ <title>The Dojo Unit Test Harness, $Rev$</title>
+ <script type="text/javascript">
+ window.dojoUrl = "../../dojo/dojo.js";
+ window.testUrl = "";
+ window.testModule = "";
+
+ // parse out our test URL and our Dojo URL from the query string
+ var qstr = window.location.search.substr(1);
+ if(qstr.length){
+ var qparts = qstr.split("&");
+ for(var x=0; x<qparts.length; x++){
+ var tp = qparts[x].split("=");
+ if(tp[0] == "dojoUrl"){
+ window.dojoUrl = tp[1];
+ }
+ if(tp[0] == "testUrl"){
+ window.testUrl = tp[1];
+ }
+ if(tp[0] == "testModule"){
+ window.testModule = tp[1];
+ }
+ }
+ }
+
+ document.write("<scr"+"ipt type='text/javascript' djConfig='isDebug: true' src='"+dojoUrl+"'></scr"+"ipt>");
+ </script>
+ <script type="text/javascript">
+ try{
+ dojo.require("doh.runner");
+ }catch(e){
+ document.write("<scr"+"ipt type='text/javascript' src='runner.js'></scr"+"ipt>");
+ document.write("<scr"+"ipt type='text/javascript' src='_browserRunner.js'></scr"+"ipt>");
+ }
+ if(testUrl.length){
+ document.write("<scr"+"ipt type='text/javascript' src='"+testUrl+".js'></scr"+"ipt>");
+ }
+ </script>
+ <style type="text/css">
+ @import "../../dojo/resources/dojo.css";
+ /*
+ body {
+ margin: 0px;
+ padding: 0px;
+ font-size: 13px;
+ color: #292929;
+ font-family: Myriad, Lucida Grande, Bitstream Vera Sans, Arial, Helvetica, sans-serif;
+ *font-size: small;
+ *font: x-small;
+ }
+
+ th, td {
+ font-size: 13px;
+ color: #292929;
+ font-family: Myriad, Lucida Grande, Bitstream Vera Sans, Arial, Helvetica, sans-serif;
+ font-weight: normal;
+ }
+
+ * body {
+ line-height: 1.25em;
+ }
+
+ table {
+ border-collapse: collapse;
+ }
+ */
+
+ #testLayout {
+ position: relative;
+ left: 0px;
+ top: 0px;
+ width: 100%;
+ height: 100%;
+ border: 1px solid black;
+ border: 0px;
+ }
+
+ .tabBody {
+ margin: 0px;
+ padding: 0px;
+ /*
+ border: 1px solid black;
+ */
+ background-color: #DEDEDE;
+ border: 0px;
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ left: 0px;
+ top: 0px;
+ overflow: auto;
+ }
+
+ #logBody {
+ padding-left: 5px;
+ padding-top: 5px;
+ font-family: Monaco, monospace;
+ font-size: 11px;
+ white-space: pre;
+ }
+
+ #progressOuter {
+ background:#e9e9e9 url("http://svn.dojotoolkit.org/dojo/dijit/trunk/themes/tundra/images/dojoTundraGradientBg.png") repeat-x 0 0;
+ /*
+ border-color: #e8e8e8;
+ */
+ }
+
+ #progressInner {
+ background: blue;
+ width: 0%;
+ position: relative;
+ left: 0px;
+ top: 0px;
+ height: 100%;
+ }
+
+ #play, #pause {
+ font-family: Webdings;
+ font-size: 1.4em;
+ border: 1px solid #DEDEDE;
+ cursor: pointer;
+ padding-right: 0.5em;
+ }
+
+ .header {
+ border: 1px solid #DEDEDE;
+ }
+
+ button.tab {
+ border-width: 1px 1px 0px 1px;
+ border-style: solid;
+ border-color: #DEDEDE;
+ margin-right: 5px;
+ }
+
+ #testListContainer {
+ /*
+ border: 1px solid black;
+ */
+ position: relative;
+ height: 99%;
+ width: 100%;
+ overflow: auto;
+ }
+
+ #testList {
+ border-collapse: collapse;
+ position: absolute;
+ left: 0px;
+ width: 100%;
+ }
+
+ #testList > tbody > tr > td {
+ border-bottom: 1px solid #DEDEDE;
+ border-right : 1px solid #DEDEDE;
+ padding: 3px;
+ }
+
+ #testListHeader th {
+ border-bottom: 1px solid #DEDEDE;
+ border-right : 1px solid #DEDEDE;
+ padding: 3px;
+ font-weight: bolder;
+ font-style: italic;
+ }
+
+ #toggleButtons {
+ float: left;
+ background-color: #DEDEDE;
+ }
+
+ tr.inProgress {
+ background-color: #85afde;
+ }
+
+ tr.success {
+ background-color: #7cdea7;
+ }
+
+ tr.failure {
+ background-color: #de827b;
+ }
+ </style>
+ </head>
+ <body>
+ <table id="testLayout" cellpadding="0" cellspacing="0" style="margin: 0;">
+ <tr valign="top" height="40">
+ <td colspan="2" id="logoBar">
+ <h3 style="margin: 5px 5px 0px 5px; float: left;">D.O.H.: The Dojo Objective Harness</h3>
+ <img src="small_logo.png" height="40" style="margin: 0px 5px 0px 5px; float: right;">
+ <span style="margin: 10px 5px 0px 5px; float: right;">
+ <input type="checkbox" id="audio" name="audio">
+ <label for="audio">sounds?</label>
+ </span>
+ </td>
+ </tr>
+ <!--
+ <tr valign="top" height="10">
+ <td colspan="2" id="progressOuter">
+ <div id="progressInner">blah</div>
+ </td>
+ </tr>
+ -->
+ <tr valign="top" height="30">
+ <td width="30%" class="header">
+ <span id="toggleButtons" onclick="doh.togglePaused();">
+ <button id="play">&#052;</button>
+ <button id="pause" style="display: none;">&#059;</button>
+ </span>
+ <span id="runningStatus">
+ <span id="pausedMsg">Stopped</span>
+ <span id="playingMsg" style="display: none;">Tests Running</span>
+ </span>
+ </td>
+ <td width="*" class="header" valign="bottom">
+ <button class="tab" onclick="showTestPage();">Test Page</button>
+ <button class="tab" onclick="showLogPage();">Log</button>
+ </td>
+ </tr>
+ <tr valign="top" style="border: 0; padding: 0; margin: 0;">
+ <td height="100%" style="border: 0; padding: 0; margin: 0;">
+ <div id="testListContainer">
+ <table cellpadding="0" cellspacing="0" border="0"
+ width="100%" id="testList" style="margin: 0;">
+ <thead>
+ <tr id="testListHeader" style="border: 0; padding: 0; margin: 0;" >
+ <th>&nbsp;</th>
+ <th width="20">
+ <input type="checkbox" checked
+ onclick="toggleRunAll();">
+ </th>
+ <th width="*" style="text-align: left;">test</th>
+ <th width="50">time</th>
+ </tr>
+ </thead>
+ <tbody valign="top">
+ <tr id="groupTemplate" style="display: none;">
+ <td style="font-family: Webdings; width: 15px;">&#052;</td>
+ <td>
+ <input type="checkbox" checked>
+ </td>
+ <td>group name</td>
+ <td>10ms</td>
+ </tr>
+ <tr id="testTemplate" style="display: none;">
+ <td>&nbsp;</td>
+ <td>&nbsp;</td>
+ <td style="padding-left: 20px;">test name</td>
+ <td>10ms</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </td>
+ <td>
+ <div style="position: relative; width: 99%; height: 100%; top: 0px; left: 0px;">
+ <div class="tabBody"
+ style="z-index: 1;">
+<pre id="logBody"></pre>
+ </div>
+ <iframe id="testBody" class="tabBody"
+ style="z-index: 0;"></iframe>
+ <!--
+ src="http://redesign.dojotoolkit.org"></iframe>
+ -->
+ </div>
+ </td>
+ </tr>
+ </table>
+ <span id="hiddenAudio"></span>
+ </body>
+</html>
+
diff --git a/includes/js/util/doh/runner.js b/includes/js/util/doh/runner.js
new file mode 100644
index 0000000..f5e47e1
--- /dev/null
+++ b/includes/js/util/doh/runner.js
@@ -0,0 +1,948 @@
+// FIXME: need to add async tests
+// FIXME: need to handle URL wrapping and test registration/running from URLs
+
+// package system gunk.
+try{
+ dojo.provide("doh.runner");
+}catch(e){
+ if(!this["doh"]){
+ doh = {};
+ }
+}
+
+//
+// Utility Functions and Classes
+//
+
+doh.selfTest = false;
+
+doh.hitch = function(/*Object*/thisObject, /*Function|String*/method /*, ...*/){
+ var args = [];
+ for(var x=2; x<arguments.length; x++){
+ args.push(arguments[x]);
+ }
+ var fcn = ((typeof method == "string") ? thisObject[method] : method) || function(){};
+ return function(){
+ var ta = args.concat([]); // make a copy
+ for(var x=0; x<arguments.length; x++){
+ ta.push(arguments[x]);
+ }
+ return fcn.apply(thisObject, ta); // Function
+ };
+}
+
+doh._mixin = function(/*Object*/ obj, /*Object*/ props){
+ // summary:
+ // Adds all properties and methods of props to obj. This addition is
+ // "prototype extension safe", so that instances of objects will not
+ // pass along prototype defaults.
+ var tobj = {};
+ for(var x in props){
+ // the "tobj" condition avoid copying properties in "props"
+ // inherited from Object.prototype. For example, if obj has a custom
+ // toString() method, don't overwrite it with the toString() method
+ // that props inherited from Object.protoype
+ if((typeof tobj[x] == "undefined") || (tobj[x] != props[x])){
+ obj[x] = props[x];
+ }
+ }
+ // IE doesn't recognize custom toStrings in for..in
+ if( this["document"]
+ && document.all
+ && (typeof props["toString"] == "function")
+ && (props["toString"] != obj["toString"])
+ && (props["toString"] != tobj["toString"])
+ ){
+ obj.toString = props.toString;
+ }
+ return obj; // Object
+}
+
+doh.mixin = function(/*Object*/obj, /*Object...*/props){
+ // summary: Adds all properties and methods of props to obj.
+ for(var i=1, l=arguments.length; i<l; i++){
+ doh._mixin(obj, arguments[i]);
+ }
+ return obj; // Object
+}
+
+doh.extend = function(/*Object*/ constructor, /*Object...*/ props){
+ // summary:
+ // Adds all properties and methods of props to constructor's
+ // prototype, making them available to all instances created with
+ // constructor.
+ for(var i=1, l=arguments.length; i<l; i++){
+ doh._mixin(constructor.prototype, arguments[i]);
+ }
+ return constructor; // Object
+}
+
+
+doh._line = "------------------------------------------------------------";
+
+/*
+doh._delegate = function(obj, props){
+ // boodman-crockford delegation
+ function TMP(){};
+ TMP.prototype = obj;
+ var tmp = new TMP();
+ if(props){
+ dojo.lang.mixin(tmp, props);
+ }
+ return tmp;
+}
+*/
+
+doh.debug = function(){
+ // summary:
+ // takes any number of arguments and sends them to whatever debugging
+ // or logging facility is available in this environment
+
+ // YOUR TEST RUNNER NEEDS TO IMPLEMENT THIS
+}
+
+doh._AssertFailure = function(msg){
+ // idea for this as way of dis-ambiguating error types is from JUM.
+ // The JUM is dead! Long live the JUM!
+
+ if(!(this instanceof doh._AssertFailure)){
+ return new doh._AssertFailure(msg);
+ }
+ this.message = new String(msg||"");
+ return this;
+}
+doh._AssertFailure.prototype = new Error();
+doh._AssertFailure.prototype.constructor = doh._AssertFailure;
+doh._AssertFailure.prototype.name = "doh._AssertFailure";
+
+doh.Deferred = function(canceller){
+ this.chain = [];
+ this.id = this._nextId();
+ this.fired = -1;
+ this.paused = 0;
+ this.results = [null, null];
+ this.canceller = canceller;
+ this.silentlyCancelled = false;
+};
+
+doh.extend(doh.Deferred, {
+ getTestCallback: function(cb, scope){
+ var _this = this;
+ return function(){
+ try{
+ cb.apply(scope||dojo.global||_this, arguments);
+ }catch(e){
+ _this.errback(e);
+ return;
+ }
+ _this.callback(true);
+ }
+ },
+
+ getFunctionFromArgs: function(){
+ var a = arguments;
+ if((a[0])&&(!a[1])){
+ if(typeof a[0] == "function"){
+ return a[0];
+ }else if(typeof a[0] == "string"){
+ return dojo.global[a[0]];
+ }
+ }else if((a[0])&&(a[1])){
+ return doh.hitch(a[0], a[1]);
+ }
+ return null;
+ },
+
+ makeCalled: function() {
+ var deferred = new doh.Deferred();
+ deferred.callback();
+ return deferred;
+ },
+
+ _nextId: (function(){
+ var n = 1;
+ return function(){ return n++; };
+ })(),
+
+ cancel: function(){
+ if(this.fired == -1){
+ if (this.canceller){
+ this.canceller(this);
+ }else{
+ this.silentlyCancelled = true;
+ }
+ if(this.fired == -1){
+ this.errback(new Error("Deferred(unfired)"));
+ }
+ }else if( (this.fired == 0)&&
+ (this.results[0] instanceof doh.Deferred)){
+ this.results[0].cancel();
+ }
+ },
+
+
+ _pause: function(){
+ this.paused++;
+ },
+
+ _unpause: function(){
+ this.paused--;
+ if ((this.paused == 0) && (this.fired >= 0)) {
+ this._fire();
+ }
+ },
+
+ _continue: function(res){
+ this._resback(res);
+ this._unpause();
+ },
+
+ _resback: function(res){
+ this.fired = ((res instanceof Error) ? 1 : 0);
+ this.results[this.fired] = res;
+ this._fire();
+ },
+
+ _check: function(){
+ if(this.fired != -1){
+ if(!this.silentlyCancelled){
+ throw new Error("already called!");
+ }
+ this.silentlyCancelled = false;
+ return;
+ }
+ },
+
+ callback: function(res){
+ this._check();
+ this._resback(res);
+ },
+
+ errback: function(res){
+ this._check();
+ if(!(res instanceof Error)){
+ res = new Error(res);
+ }
+ this._resback(res);
+ },
+
+ addBoth: function(cb, cbfn){
+ var enclosed = this.getFunctionFromArgs(cb, cbfn);
+ if(arguments.length > 2){
+ enclosed = doh.hitch(null, enclosed, arguments, 2);
+ }
+ return this.addCallbacks(enclosed, enclosed);
+ },
+
+ addCallback: function(cb, cbfn){
+ var enclosed = this.getFunctionFromArgs(cb, cbfn);
+ if(arguments.length > 2){
+ enclosed = doh.hitch(null, enclosed, arguments, 2);
+ }
+ return this.addCallbacks(enclosed, null);
+ },
+
+ addErrback: function(cb, cbfn){
+ var enclosed = this.getFunctionFromArgs(cb, cbfn);
+ if(arguments.length > 2){
+ enclosed = doh.hitch(null, enclosed, arguments, 2);
+ }
+ return this.addCallbacks(null, enclosed);
+ },
+
+ addCallbacks: function(cb, eb){
+ this.chain.push([cb, eb])
+ if(this.fired >= 0){
+ this._fire();
+ }
+ return this;
+ },
+
+ _fire: function(){
+ var chain = this.chain;
+ var fired = this.fired;
+ var res = this.results[fired];
+ var self = this;
+ var cb = null;
+ while (chain.length > 0 && this.paused == 0){
+ // Array
+ var pair = chain.shift();
+ var f = pair[fired];
+ if(f == null){
+ continue;
+ }
+ try {
+ res = f(res);
+ fired = ((res instanceof Error) ? 1 : 0);
+ if(res instanceof doh.Deferred){
+ cb = function(res){
+ self._continue(res);
+ }
+ this._pause();
+ }
+ }catch(err){
+ fired = 1;
+ res = err;
+ }
+ }
+ this.fired = fired;
+ this.results[fired] = res;
+ if((cb)&&(this.paused)){
+ res.addBoth(cb);
+ }
+ }
+});
+
+//
+// State Keeping and Reporting
+//
+
+doh._testCount = 0;
+doh._groupCount = 0;
+doh._errorCount = 0;
+doh._failureCount = 0;
+doh._currentGroup = null;
+doh._currentTest = null;
+doh._paused = true;
+
+doh._init = function(){
+ this._currentGroup = null;
+ this._currentTest = null;
+ this._errorCount = 0;
+ this._failureCount = 0;
+ this.debug(this._testCount, "tests to run in", this._groupCount, "groups");
+}
+
+// doh._urls = [];
+doh._groups = {};
+
+//
+// Test Registration
+//
+
+doh.registerTestNs = function(/*String*/ group, /*Object*/ ns){
+ // summary:
+ // adds the passed namespace object to the list of objects to be
+ // searched for test groups. Only "public" functions (not prefixed
+ // with "_") will be added as tests to be run. If you'd like to use
+ // fixtures (setUp(), tearDown(), and runTest()), please use
+ // registerTest() or registerTests().
+ for(var x in ns){
+ if( (x.charAt(0) != "_") &&
+ (typeof ns[x] == "function") ){
+ this.registerTest(group, ns[x]);
+ }
+ }
+}
+
+doh._testRegistered = function(group, fixture){
+ // slot to be filled in
+}
+
+doh._groupStarted = function(group){
+ // slot to be filled in
+}
+
+doh._groupFinished = function(group, success){
+ // slot to be filled in
+}
+
+doh._testStarted = function(group, fixture){
+ // slot to be filled in
+}
+
+doh._testFinished = function(group, fixture, success){
+ // slot to be filled in
+}
+
+doh.registerGroup = function( /*String*/ group,
+ /*Array||Function||Object*/ tests,
+ /*Function*/ setUp,
+ /*Function*/ tearDown){
+ // summary:
+ // registers an entire group of tests at once and provides a setUp and
+ // tearDown facility for groups. If you call this method with only
+ // setUp and tearDown parameters, they will replace previously
+ // installed setUp or tearDown functions for the group with the new
+ // methods.
+ // group:
+ // string name of the group
+ // tests:
+ // either a function or an object or an array of functions/objects. If
+ // an object, it must contain at *least* a "runTest" method, and may
+ // also contain "setUp" and "tearDown" methods. These will be invoked
+ // on either side of the "runTest" method (respectively) when the test
+ // is run. If an array, it must contain objects matching the above
+ // description or test functions.
+ // setUp: a function for initializing the test group
+ // tearDown: a function for initializing the test group
+ if(tests){
+ this.register(group, tests);
+ }
+ if(setUp){
+ this._groups[group].setUp = setUp;
+ }
+ if(tearDown){
+ this._groups[group].tearDown = tearDown;
+ }
+}
+
+doh._getTestObj = function(group, test){
+ var tObj = test;
+ if(typeof test == "string"){
+ if(test.substr(0, 4)=="url:"){
+ return this.registerUrl(group, test);
+ }else{
+ tObj = {
+ name: test.replace("/\s/g", "_")
+ };
+ tObj.runTest = new Function("t", test);
+ }
+ }else if(typeof test == "function"){
+ // if we didn't get a fixture, wrap the function
+ tObj = { "runTest": test };
+ if(test["name"]){
+ tObj.name = test.name;
+ }else{
+ try{
+ var fStr = "function ";
+ var ts = tObj.runTest+"";
+ if(0 <= ts.indexOf(fStr)){
+ tObj.name = ts.split(fStr)[1].split("(", 1)[0];
+ }
+ // doh.debug(tObj.runTest.toSource());
+ }catch(e){
+ }
+ }
+ // FIXME: try harder to get the test name here
+ }
+ return tObj;
+}
+
+doh.registerTest = function(/*String*/ group, /*Function||Object*/ test){
+ // summary:
+ // add the provided test function or fixture object to the specified
+ // test group.
+ // group:
+ // string name of the group to add the test to
+ // test:
+ // either a function or an object. If an object, it must contain at
+ // *least* a "runTest" method, and may also contain "setUp" and
+ // "tearDown" methods. These will be invoked on either side of the
+ // "runTest" method (respectively) when the test is run.
+ if(!this._groups[group]){
+ this._groupCount++;
+ this._groups[group] = [];
+ this._groups[group].inFlight = 0;
+ }
+ var tObj = this._getTestObj(group, test);
+ if(!tObj){ return; }
+ this._groups[group].push(tObj);
+ this._testCount++;
+ this._testRegistered(group, tObj);
+ return tObj;
+}
+
+doh.registerTests = function(/*String*/ group, /*Array*/ testArr){
+ // summary:
+ // registers a group of tests, treating each element of testArr as
+ // though it were being (along with group) passed to the registerTest
+ // method.
+ for(var x=0; x<testArr.length; x++){
+ this.registerTest(group, testArr[x]);
+ }
+}
+
+// FIXME: move implementation to _browserRunner?
+doh.registerUrl = function( /*String*/ group,
+ /*String*/ url,
+ /*Integer*/ timeout){
+ this.debug("ERROR:");
+ this.debug("\tNO registerUrl() METHOD AVAILABLE.");
+ // this._urls.push(url);
+}
+
+doh.registerString = function(group, str){
+}
+
+// FIXME: remove the doh.add alias SRTL.
+doh.register = doh.add = function(groupOrNs, testOrNull){
+ // summary:
+ // "magical" variant of registerTests, registerTest, and
+ // registerTestNs. Will accept the calling arguments of any of these
+ // methods and will correctly guess the right one to register with.
+ if( (arguments.length == 1)&&
+ (typeof groupOrNs == "string") ){
+ if(groupOrNs.substr(0, 4)=="url:"){
+ this.registerUrl(groupOrNs);
+ }else{
+ this.registerTest("ungrouped", groupOrNs);
+ }
+ }
+ if(arguments.length == 1){
+ this.debug("invalid args passed to doh.register():", groupOrNs, ",", testOrNull);
+ return;
+ }
+ if(typeof testOrNull == "string"){
+ if(testOrNull.substr(0, 4)=="url:"){
+ this.registerUrl(testOrNull);
+ }else{
+ this.registerTest(groupOrNs, testOrNull);
+ }
+ // this.registerTestNs(groupOrNs, testOrNull);
+ return;
+ }
+ if(doh._isArray(testOrNull)){
+ this.registerTests(groupOrNs, testOrNull);
+ return;
+ }
+ this.registerTest(groupOrNs, testOrNull);
+}
+
+//
+// Assertions and In-Test Utilities
+//
+
+doh.t = doh.assertTrue = function(/*Object*/ condition){
+ // summary:
+ // is the passed item "truthy"?
+ if(arguments.length != 1){
+ throw doh._AssertFailure("assertTrue failed because it was not passed exactly 1 argument");
+ }
+ if(!eval(condition)){
+ throw doh._AssertFailure("assertTrue('" + condition + "') failed");
+ }
+}
+
+doh.f = doh.assertFalse = function(/*Object*/ condition){
+ // summary:
+ // is the passed item "falsey"?
+ if(arguments.length != 1){
+ throw doh._AssertFailure("assertFalse failed because it was not passed exactly 1 argument");
+ }
+ if(eval(condition)){
+ throw doh._AssertFailure("assertFalse('" + condition + "') failed");
+ }
+}
+
+doh.e = doh.assertError = function(/*Error object*/expectedError, /*Object*/scope, /*String*/functionName, /*Array*/args){
+ // summary:
+ // Test for a certain error to be thrown by the given function.
+ // example:
+ // t.assertError(dojox.data.QueryReadStore.InvalidAttributeError, store, "getValue", [item, "NOT THERE"]);
+ // t.assertError(dojox.data.QueryReadStore.InvalidItemError, store, "getValue", ["not an item", "NOT THERE"]);
+ try{
+ scope[functionName].apply(scope, args);
+ }catch (e){
+ if(e instanceof expectedError){
+ return true;
+ }else{
+ throw new doh._AssertFailure("assertError() failed:\n\texpected error\n\t\t"+expectedError+"\n\tbut got\n\t\t"+e+"\n\n");
+ }
+ }
+ throw new doh._AssertFailure("assertError() failed:\n\texpected error\n\t\t"+expectedError+"\n\tbut no error caught\n\n");
+}
+
+
+doh.is = doh.assertEqual = function(/*Object*/ expected, /*Object*/ actual){
+ // summary:
+ // are the passed expected and actual objects/values deeply
+ // equivalent?
+
+ // Compare undefined always with three equal signs, because undefined==null
+ // is true, but undefined===null is false.
+ if((expected === undefined)&&(actual === undefined)){
+ return true;
+ }
+ if(arguments.length < 2){
+ throw doh._AssertFailure("assertEqual failed because it was not passed 2 arguments");
+ }
+ if((expected === actual)||(expected == actual)){
+ return true;
+ }
+ if( (this._isArray(expected) && this._isArray(actual))&&
+ (this._arrayEq(expected, actual)) ){
+ return true;
+ }
+ if( ((typeof expected == "object")&&((typeof actual == "object")))&&
+ (this._objPropEq(expected, actual)) ){
+ return true;
+ }
+ throw new doh._AssertFailure("assertEqual() failed:\n\texpected\n\t\t"+expected+"\n\tbut got\n\t\t"+actual+"\n\n");
+}
+
+doh._arrayEq = function(expected, actual){
+ if(expected.length != actual.length){ return false; }
+ // FIXME: we're not handling circular refs. Do we care?
+ for(var x=0; x<expected.length; x++){
+ if(!doh.assertEqual(expected[x], actual[x])){ return false; }
+ }
+ return true;
+}
+
+doh._objPropEq = function(expected, actual){
+ if(expected instanceof Date){
+ return actual instanceof Date && expected.getTime()==actual.getTime();
+ }
+ // Make sure ALL THE SAME properties are in both objects!
+ for(var x in actual){ // Lets check "actual" here, expected is checked below.
+ if(expected[x] === undefined){
+ return false;
+ }
+ };
+
+ for(var x in expected){
+ if(!doh.assertEqual(expected[x], actual[x])){
+ return false;
+ }
+ }
+ return true;
+}
+
+doh._isArray = function(it){
+ return (it && it instanceof Array || typeof it == "array" || (dojo["NodeList"] !== undefined && it instanceof dojo.NodeList));
+}
+
+//
+// Runner-Wrapper
+//
+
+doh._setupGroupForRun = function(/*String*/ groupName, /*Integer*/ idx){
+ var tg = this._groups[groupName];
+ this.debug(this._line);
+ this.debug("GROUP", "\""+groupName+"\"", "has", tg.length, "test"+((tg.length > 1) ? "s" : "")+" to run");
+}
+
+doh._handleFailure = function(groupName, fixture, e){
+ // this.debug("FAILED test:", fixture.name);
+ // mostly borrowed from JUM
+ this._groups[groupName].failures++;
+ var out = "";
+ if(e instanceof this._AssertFailure){
+ this._failureCount++;
+ if(e["fileName"]){ out += e.fileName + ':'; }
+ if(e["lineNumber"]){ out += e.lineNumber + ' '; }
+ out += e+": "+e.message;
+ this.debug("\t_AssertFailure:", out);
+ }else{
+ this._errorCount++;
+ }
+ this.debug(e);
+ if(fixture.runTest["toSource"]){
+ var ss = fixture.runTest.toSource();
+ this.debug("\tERROR IN:\n\t\t", ss);
+ }else{
+ this.debug("\tERROR IN:\n\t\t", fixture.runTest);
+ }
+
+ if(e.rhinoException){
+ e.rhinoException.printStackTrace();
+ }else if(e.javaException){
+ e.javaException.printStackTrace();
+ }
+}
+
+try{
+ setTimeout(function(){}, 0);
+}catch(e){
+ setTimeout = function(func){
+ return func();
+ }
+}
+
+doh._runFixture = function(groupName, fixture){
+ var tg = this._groups[groupName];
+ this._testStarted(groupName, fixture);
+ var threw = false;
+ var err = null;
+ // run it, catching exceptions and reporting them
+ try{
+ // let doh reference "this.group.thinger..." which can be set by
+ // another test or group-level setUp function
+ fixture.group = tg;
+ // only execute the parts of the fixture we've got
+ if(fixture["setUp"]){ fixture.setUp(this); }
+ if(fixture["runTest"]){ // should we error out of a fixture doesn't have a runTest?
+ fixture.startTime = new Date();
+ var ret = fixture.runTest(this);
+ fixture.endTime = new Date();
+ // if we get a deferred back from the test runner, we know we're
+ // gonna wait for an async result. It's up to the test code to trap
+ // errors and give us an errback or callback.
+ if(ret instanceof doh.Deferred){
+
+ tg.inFlight++;
+ ret.groupName = groupName;
+ ret.fixture = fixture;
+
+ ret.addErrback(function(err){
+ doh._handleFailure(groupName, fixture, err);
+ });
+
+ var retEnd = function(){
+ if(fixture["tearDown"]){ fixture.tearDown(doh); }
+ tg.inFlight--;
+ if((!tg.inFlight)&&(tg.iterated)){
+ doh._groupFinished(groupName, (!tg.failures));
+ }
+ doh._testFinished(groupName, fixture, ret.results[0]);
+ if(doh._paused){
+ doh.run();
+ }
+ }
+
+ var timer = setTimeout(function(){
+ // ret.cancel();
+ // retEnd();
+ ret.errback(new Error("test timeout in "+fixture.name.toString()));
+ }, fixture["timeout"]||1000);
+
+ ret.addBoth(function(arg){
+ clearTimeout(timer);
+ retEnd();
+ });
+ if(ret.fired < 0){
+ doh.pause();
+ }
+ return ret;
+ }
+ }
+ if(fixture["tearDown"]){ fixture.tearDown(this); }
+ }catch(e){
+ threw = true;
+ err = e;
+ if(!fixture.endTime){
+ fixture.endTime = new Date();
+ }
+ }
+ var d = new doh.Deferred();
+ setTimeout(this.hitch(this, function(){
+ if(threw){
+ this._handleFailure(groupName, fixture, err);
+ }
+ this._testFinished(groupName, fixture, (!threw));
+
+ if((!tg.inFlight)&&(tg.iterated)){
+ doh._groupFinished(groupName, (!tg.failures));
+ }else if(tg.inFlight > 0){
+ setTimeout(this.hitch(this, function(){
+ doh.runGroup(groupName); // , idx);
+ }), 100);
+ this._paused = true;
+ }
+ if(doh._paused){
+ doh.run();
+ }
+ }), 30);
+ doh.pause();
+ return d;
+}
+
+doh._testId = 0;
+doh.runGroup = function(/*String*/ groupName, /*Integer*/ idx){
+ // summary:
+ // runs the specified test group
+
+ // the general structure of the algorithm is to run through the group's
+ // list of doh, checking before and after each of them to see if we're in
+ // a paused state. This can be caused by the test returning a deferred or
+ // the user hitting the pause button. In either case, we want to halt
+ // execution of the test until something external to us restarts it. This
+ // means we need to pickle off enough state to pick up where we left off.
+
+ // FIXME: need to make fixture execution async!!
+
+ var tg = this._groups[groupName];
+ if(tg.skip === true){ return; }
+ if(this._isArray(tg)){
+ if(idx<=tg.length){
+ if((!tg.inFlight)&&(tg.iterated == true)){
+ if(tg["tearDown"]){ tg.tearDown(this); }
+ doh._groupFinished(groupName, (!tg.failures));
+ return;
+ }
+ }
+ if(!idx){
+ tg.inFlight = 0;
+ tg.iterated = false;
+ tg.failures = 0;
+ }
+ doh._groupStarted(groupName);
+ if(!idx){
+ this._setupGroupForRun(groupName, idx);
+ if(tg["setUp"]){ tg.setUp(this); }
+ }
+ for(var y=(idx||0); y<tg.length; y++){
+ if(this._paused){
+ this._currentTest = y;
+ // this.debug("PAUSED at:", tg[y].name, this._currentGroup, this._currentTest);
+ return;
+ }
+ doh._runFixture(groupName, tg[y]);
+ if(this._paused){
+ this._currentTest = y+1;
+ if(this._currentTest == tg.length){
+ tg.iterated = true;
+ }
+ // this.debug("PAUSED at:", tg[y].name, this._currentGroup, this._currentTest);
+ return;
+ }
+ }
+ tg.iterated = true;
+ if(!tg.inFlight){
+ if(tg["tearDown"]){ tg.tearDown(this); }
+ doh._groupFinished(groupName, (!tg.failures));
+ }
+ }
+}
+
+doh._onEnd = function(){}
+
+doh._report = function(){
+ // summary:
+ // a private method to be implemented/replaced by the "locally
+ // appropriate" test runner
+
+ // this.debug("ERROR:");
+ // this.debug("\tNO REPORTING OUTPUT AVAILABLE.");
+ // this.debug("\tIMPLEMENT doh._report() IN YOUR TEST RUNNER");
+
+ this.debug(this._line);
+ this.debug("| TEST SUMMARY:");
+ this.debug(this._line);
+ this.debug("\t", this._testCount, "tests in", this._groupCount, "groups");
+ this.debug("\t", this._errorCount, "errors");
+ this.debug("\t", this._failureCount, "failures");
+}
+
+doh.togglePaused = function(){
+ this[(this._paused) ? "run" : "pause"]();
+}
+
+doh.pause = function(){
+ // summary:
+ // halt test run. Can be resumed.
+ this._paused = true;
+}
+
+doh.run = function(){
+ // summary:
+ // begins or resumes the test process.
+ // this.debug("STARTING");
+ this._paused = false;
+ var cg = this._currentGroup;
+ var ct = this._currentTest;
+ var found = false;
+ if(!cg){
+ this._init(); // we weren't paused
+ found = true;
+ }
+ this._currentGroup = null;
+ this._currentTest = null;
+
+ for(var x in this._groups){
+ if(
+ ( (!found)&&(x == cg) )||( found )
+ ){
+ if(this._paused){ return; }
+ this._currentGroup = x;
+ if(!found){
+ found = true;
+ this.runGroup(x, ct);
+ }else{
+ this.runGroup(x);
+ }
+ if(this._paused){ return; }
+ }
+ }
+ this._currentGroup = null;
+ this._currentTest = null;
+ this._paused = false;
+ this._onEnd();
+ this._report();
+}
+
+tests = doh;
+
+(function(){
+ // scop protection
+ try{
+ if(typeof dojo != "undefined"){
+ dojo.platformRequire({
+ browser: ["doh._browserRunner"],
+ rhino: ["doh._rhinoRunner"],
+ spidermonkey: ["doh._rhinoRunner"]
+ });
+ var _shouldRequire = (dojo.isBrowser) ? (dojo.global == dojo.global["parent"]) : true;
+ if(_shouldRequire){
+ if(dojo.isBrowser){
+ dojo.addOnLoad(function(){
+ if(dojo.byId("testList")){
+ var _tm = ( (dojo.global.testModule && dojo.global.testModule.length) ? dojo.global.testModule : "dojo.tests.module");
+ dojo.forEach(_tm.split(","), dojo.require, dojo);
+ setTimeout(function(){
+ doh.run();
+ }, 500);
+ }
+ });
+ }else{
+ // dojo.require("doh._base");
+ }
+ }
+ }else{
+ if(
+ (typeof load == "function")&&
+ ( (typeof Packages == "function")||
+ (typeof Packages == "object") )
+ ){
+ throw new Error();
+ }else if(typeof load == "function"){
+ throw new Error();
+ }
+ }
+ }catch(e){
+ print("\n"+doh._line);
+ print("The Dojo Unit Test Harness, $Rev$");
+ print("Copyright (c) 2007, The Dojo Foundation, All Rights Reserved");
+ print(doh._line, "\n");
+
+ load("_rhinoRunner.js");
+
+ try{
+ var dojoUrl = "../../dojo/dojo.js";
+ var testUrl = "";
+ var testModule = "dojo.tests.module";
+ for(var x=0; x<arguments.length; x++){
+ if(arguments[x].indexOf("=") > 0){
+ var tp = arguments[x].split("=");
+ if(tp[0] == "dojoUrl"){
+ dojoUrl = tp[1];
+ }
+ if(tp[0] == "testUrl"){
+ testUrl = tp[1];
+ }
+ if(tp[0] == "testModule"){
+ testModule = tp[1];
+ }
+ }
+ }
+ if(dojoUrl.length){
+ if(!this["djConfig"]){
+ djConfig = {};
+ }
+ djConfig.baseUrl = dojoUrl.split("dojo.js")[0];
+ load(dojoUrl);
+ }
+ if(testUrl.length){
+ load(testUrl);
+ }
+ if(testModule.length){
+ dojo.forEach(testModule.split(","), dojo.require, dojo);
+ }
+ }catch(e){
+ print("An exception occurred: " + e);
+ }
+
+ doh.run();
+ }
+}).apply(this, typeof arguments != "undefined" ? arguments : [null]);
diff --git a/includes/js/util/doh/runner.sh b/includes/js/util/doh/runner.sh
new file mode 100644
index 0000000..21b5cf7
--- /dev/null
+++ b/includes/js/util/doh/runner.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+java -jar ../shrinksafe/custom_rhino.jar runner.js "$@"
diff --git a/includes/js/util/doh/small_logo.png b/includes/js/util/doh/small_logo.png
new file mode 100644
index 0000000..2fda23c
--- /dev/null
+++ b/includes/js/util/doh/small_logo.png
Binary files differ