Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

initial commit with test.

  • Loading branch information...
commit 159aec99c20d0787c51000eee8c006e7e25d1a13 0 parents
@jrburke authored
2  .gitignore
@@ -0,0 +1,2 @@
+.DS_Store
+tests/simple-built.js
58 LICENSE
@@ -0,0 +1,58 @@
+almond is released under two licenses: new BSD, and MIT. You may pick the
+license that best suits your development needs. The text of both licenses are
+provided below.
+
+
+The "New" BSD License:
+----------------------
+
+Copyright (c) 2010-2011, 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.
+
+
+
+MIT License
+-----------
+
+Copyright (c) 2010-2011, The Dojo Foundation
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
23 README.md
@@ -0,0 +1,23 @@
+#almond
+
+An AMD a replacement loader for RequireJS that is only good for apps
+that use AMD modules and:
+
+* have already optimized all the modules into one file.
+* therefore, all modules have IDs and dependency arrays in their define() calls.
+* do not use loader plugins.
+* do not use requirejs.ready().
+* avoid circular dependencies.
+* do not use RequireJS multiversion support/contexts.
+* does not use require.toUrl() or require.nameToUrl().
+* does not use packages/packagePaths config.
+* no auto-detection of jQuery as a module, only uses the global jQuery object.
+
+What is supported:
+
+* dependencies with relative IDs
+* define('id', {}) definitions.
+* define(), require() and requirejs() calls.
+
+Still under development.
+
244 almond.js
@@ -0,0 +1,244 @@
+/**
+ * almond 0.0.0 Copyright (c) 2011, The Dojo Foundation All Rights Reserved.
+ * Available via the MIT or new BSD license.
+ * see: http://github.com/jrburke/almond for details
+ */
+/*jslint strict: false, plusplus: false */
+/*global */
+
+var requirejs, require, define;
+(function () {
+
+ var defined = {},
+ aps = Array.prototype.slice,
+ ostring = Object.prototype.toString,
+ req;
@jdalton
jdalton added a note

You can simplify this to aps = [].slice, ostring = {}.toString;

@jrburke Owner
jrburke added a note

Thanks! Applied in this commit

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+
+ function isFunction(it) {
+ return ostring.call(it) === "[object Function]";
+ }
+
+ function isArray(it) {
+ return ostring.call(it) === "[object Array]";
+ }
+
+ if (typeof define === "function" && define.amd) {
+ //If a define is already in play via another AMD loader,
+ //do not overwrite.
+ return;
+ }
+
+ /**
+ * Trims the . and .. from an array of path segments.
+ * It will keep a leading path segment if a .. will become
+ * the first path segment, to help with module name lookups,
+ * which act like paths, but can be remapped. But the end result,
+ * all paths that use this function should look normalized.
+ * NOTE: this method MODIFIES the input array.
+ * @param {Array} ary the array of path segments.
+ */
+ function trimDots(ary) {
+ var i, part;
+ for (i = 0; (part = ary[i]); i++) {
+ if (part === ".") {
+ ary.splice(i, 1);
+ i -= 1;
+ } else if (part === "..") {
+ if (i === 1 && (ary[2] === '..' || ary[0] === '..')) {
+ //End of the line. Keep at least one non-dot
+ //path segment at the front so it can be mapped
+ //correctly to disk. Otherwise, there is likely
+ //no path mapping for a path starting with '..'.
+ //This can still fail, but catches the most reasonable
+ //uses of ..
+ break;
+ } else if (i > 0) {
+ ary.splice(i - 1, 2);
+ i -= 2;
+ }
+ }
+ }
+ }
+
+ /**
+ * Given a relative module name, like ./something, normalize it to
+ * a real name that can be mapped to a path.
+ * @param {String} name the relative name
+ * @param {String} baseName a real name that the name arg is relative
+ * to.
+ * @returns {String} normalized name
+ */
+ function normalize(name, baseName) {
+ //Adjust any relative paths.
+ if (name && name.charAt(0) === ".") {
+ //If have a base name, try to normalize against it,
+ //otherwise, assume it is a top-level require that will
+ //be relative to baseUrl in the end.
+ if (baseName) {
+ //Convert baseName to array, and lop off the last part,
+ //so that . matches that "directory" and not name of the baseName's
+ //module. For instance, baseName of "one/two/three", maps to
+ //"one/two/three.js", but we want the directory, "one/two" for
+ //this normalization.
+ baseName = baseName.split("/");
+ baseName = baseName.slice(0, baseName.length - 1);
+
+ name = baseName.concat(name.split("/"));
+ trimDots(name);
+
+ name = name.join("/");
+ }
+ }
+ return name;
+ }
+
+ /**
+ * Helper function that creates a setExports function for a "module"
+ * CommonJS dependency. Do this here to avoid creating a closure that
+ * is part of a loop.
+ */
+ function makeSetExports(moduleObj) {
+ return function (exports) {
+ moduleObj.exports = exports;
+ };
+ }
+
+ function makeRequire(relName) {
+ return function () {
+ //A version of a require function that passes a moduleName
+ //value for items that may need to
+ //look up paths relative to the moduleName
+ var args = aps.call(arguments, 0);
+ args.push(relName);
+ return req.apply(null, args);
+ };
+ }
+
+ function main(name, deps, callback, relName) {
+ var args = [],
+ usingExports = false,
+ cjsModule, depName, i, ret;
+
+ //Call the callback to define the module, if necessary.
+ if (isFunction(callback)) {
+ //Pull out the defined dependencies and pass the ordered
+ //values to the callback.
+ if (deps) {
+ for (i = 0; i < deps.length; i++) {
+ depName = normalize(deps[i], (name || relName));
+
+ //Fast path CommonJS standard dependencies.
+ if (depName === "require") {
+ args[i] = makeRequire(name);
+ } else if (depName === "exports") {
+ //CommonJS module spec 1.1
+ args[i] = defined[name] = {};
+ usingExports = true;
+ } else if (depName === "module") {
+ //CommonJS module spec 1.1
+ cjsModule = args[i] = {
+ id: name,
+ uri: '',
+ exports: defined[name]
+ };
+ cjsModule.setExports = makeSetExports(cjsModule);
+ } else {
+ args[i] = defined[depName];
+ }
+ }
+ }
+
+ ret = callback.apply(defined[name], args);
+
+ if (name) {
+ //If setting exports via "module" is in play,
+ //favor that over return value and exports. After that,
+ //favor a non-undefined return value over exports use.
+ if (cjsModule && cjsModule.exports !== undefined) {
+ ret = defined[name] = cjsModule.exports;
+ } else if (ret === undefined && usingExports) {
+ //exports already set the defined value.
+ ret = defined[name];
+ } else {
+ //Use the return value from the function.
+ defined[name] = ret;
+ }
+ }
+ } else if (name) {
+ //May just be an object definition for the module. Only
+ //worry about defining if have a module name.
+ defined[name] = callback;
+ }
+ }
+
+ requirejs = req = function (deps, callback, relName) {
+ var moduleName, fullName, config;
+
+ //Determine if have config object in the call.
+ //Drop the config stuff on the ground.
+ if (!isArray(deps) && typeof deps !== "string") {
+ // deps is a config object
+ config = deps;
+ if (isArray(callback)) {
+ // Adjust args if there are dependencies
+ deps = callback;
+ callback = arguments[2];
+ } else {
+ deps = [];
+ }
+ }
+
+ if (typeof deps === "string") {
+
+ //Just return the module wanted. In this scenario, the
+ //second arg (if passed) is just the relModuleMap.
+ moduleName = deps;
+ relName = callback;
+
+ //Normalize module name, if it contains . or ..
+ fullName = normalize(moduleName, relName);
+
+ if (!(fullName in defined)) {
+ throw new Error("Module name '" +
+ fullName +
+ "' has not been loaded.");
+ }
+ return defined[fullName];
+ }
+
+ //Simulate async callback;
+ setTimeout(function () {
+ main(null, deps, callback, relName);
+ }, 15);
+
+ return req;
+ };
+
+ /**
+ * Support require.config() to make it easier to cooperate with other
+ * AMD loaders on globally agreed names.
+ */
+ req.config = function (config) {
+ return req(config);
+ };
+
+ /**
+ * Export require as a global, but only if it does not already exist.
+ */
+ if (typeof require === "undefined") {
+ require = req;
+ }
+
+ define = function (name, deps, callback) {
+
+ //This module may not have dependencies
+ if (!isArray(deps)) {
+ callback = deps;
+ deps = [];
+ }
+
+ main(name, deps, callback);
+ };
+
+ define.amd = {};
+}());
3  buildTest.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+cat almond.js > tests/simple-built.js
+cat tests/simple.js >> tests/simple-built.js
2  tests/all.js
@@ -0,0 +1,2 @@
+
+doh.registerUrl("simple", "../simple.html");
195 tests/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-2009, 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.
12 tests/doh/README
@@ -0,0 +1,12 @@
+DOH may be run standalone by issuing a command like the following:
+
+java -jar ../shrinksafe/js.jar runner.js testModule=tests.colors
+
+where the testModule argument is optional and shrinksafe/js.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
855 tests/doh/_browserRunner.js
@@ -0,0 +1,855 @@
+if(window["dojo"]){
+ dojo.provide("doh._browserRunner");
+}
+
+// FIXME: need to add prompting for monkey-do testing
+
+(function(){
+
+ doh.setTimeout = function (fn, time) {
+ return setTimeout(fn, time);
+ };
+
+ try{
+ var topdog = (window.parent == window) || !Boolean(window.parent.doh);
+ }catch(e){
+ //can't access window.parent.doh, then consider ourselves as topdog
+ topdog=true;
+ }
+ if(topdog){
+ // 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 escapeXml = function(str){
+ //summary:
+ // Adds escape sequences for special characters in XML: &<>"'
+ // Optionally skips escapes for single quotes
+ return str.replace(/&/gm, "&amp;").replace(/</gm, "&lt;").replace(/>/gm, "&gt;").replace(/"/gm, "&quot;"); // string
+ };
+
+ var _logBacklog = [], _loggedMsgLen = 0;
+ var sendToLogPane = function(args, skip){
+ var msg = "";
+ for(var x=0; x<args.length; x++){
+ msg += " "+args[x];
+ }
+
+ msg = escapeXml(msg);
+
+ // workarounds for IE. Wheeee!!!
+ msg = msg.replace("\t", "&nbsp;&nbsp;&nbsp;&nbsp;")
+ .replace(" ", "&nbsp;")
+ .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 logBody=byId("logBody");
+ var tn = document.createElement("div");
+ tn.innerHTML = msg;
+ //tn.id="logmsg_"+logBody.childNodes.length;
+ logBody.appendChild(tn);
+ _loggedMsgLen++;
+ }
+
+ var findTarget = function(n){
+ while(n && !n.getAttribute('_target')){
+ n=n.parentNode;
+ if(!n.getAttribute){
+ n=null;
+ }
+ }
+ return n;
+ }
+
+ doh._jumpToLog = function(e){
+ //console.log(e);
+
+ var node = findTarget(e?e.target:window.event.srcElement);
+ if(!node){
+ return;
+ }
+ var _t = Number(node.getAttribute('_target'));
+ var lb = byId("logBody");
+ if(_t>=lb.childNodes.length){
+ return;
+ }
+ var t = lb.childNodes[_t];
+ t.scrollIntoView();
+ if(window.dojo){
+ //t.parentNode.parentNode is <div class="tabBody">, only it has a explicitly set background-color,
+ //all children of it are transparent
+ var bgColor = dojo.style(t.parentNode.parentNode,'backgroundColor');
+ //node.parentNode is the tr which has background-color set explicitly
+ var hicolor = dojo.style(node.parentNode,'backgroundColor');
+ var unhilight = dojo.animateProperty({
+ node: t,
+ duration: 500,
+ properties:
+ {
+ backgroundColor: { start:hicolor, end: bgColor }
+ },
+ onEnd: function(){
+ t.style.backgroundColor="";
+ }
+ });
+ var hilight = dojo.animateProperty({
+ node: t,
+ duration: 500,
+ properties:
+ {
+ backgroundColor: { start:bgColor, end: hicolor }
+ },
+ onEnd: function(){
+ unhilight.play();
+ }
+ });
+ hilight.play();
+ }
+ };
+
+ doh._jumpToSuite = function(e){
+ var node = findTarget(e ? e.target : window.event.srcElement);
+ if(!node){
+ return;
+ }
+ var _g = node.getAttribute('_target');
+ var gn = getGroupNode(_g);
+ if(!gn){
+ return;
+ }
+ gn.scrollIntoView();
+ };
+
+ 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);
+ }
+ _loggedMsgLen = 0;
+ }
+ this._totalTime = 0;
+ this._suiteCount = 0;
+ oi.apply(doh, arguments);
+ }
+ })(doh._init);
+
+ doh._setupGroupForRun = (function(os){
+ //overload _setupGroupForRun to record which log line to jump to when a suite is clicked
+ return function(groupName){
+ var tg = doh._groups[groupName];
+ doh._curTestCount = tg.length;
+ doh._curGroupCount = 1;
+ var gn = getGroupNode(groupName);
+ if(gn){
+ //two lines will be added, scroll the second line into view
+ gn.getElementsByTagName("td")[2].setAttribute('_target',_loggedMsgLen+1);
+ }
+ os.apply(doh,arguments);
+ }
+ })(doh._setupGroupForRun);
+
+ doh._report = (function(or){
+ //overload _report to insert a tfoot
+ return function(){
+ var tb = byId("testList");
+ if(tb){
+ var tfoots=tb.getElementsByTagName('tfoot');
+ if(tfoots.length){
+ tb.removeChild(tfoots[0]);
+ }
+ var foot = tb.createTFoot();
+ var row = foot.insertRow(-1);
+ row.className = 'inProgress';
+ var cell=row.insertCell(-1);
+ cell.colSpan=2;
+ cell.innerHTML="Result";
+ cell = row.insertCell(-1);
+ cell.innerHTML=this._testCount+" tests in "+this._groupCount+" groups /<span class='failure'>"+this._errorCount+"</span> errors, <span class='failure'>"+this._failureCount+"</span> failures";
+ cell.setAttribute('_target',_loggedMsgLen+1);
+ row.insertCell(-1).innerHTML=doh._totalTime+"ms";
+ }
+
+ //This location can do the final performance rendering for the results
+ //of any performance tests.
+ var plotResults = null;
+ var standby;
+ if(doh.perfTestResults){
+ if(window.dojo){
+ //If we have dojo and here are perf tests results,
+ //well, we'll use the dojo charting functions
+ dojo.require("dojox.charting.Chart2D");
+ dojo.require("dojox.charting.DataChart");
+ dojo.require("dojox.charting.plot2d.Scatter");
+ dojo.require("dojox.charting.plot2d.Lines");
+ dojo.require("dojo.data.ItemFileReadStore");
+ plotResults = doh._dojoPlotPerfResults;
+ }else{
+ plotResults = doh._asciiPlotPerfResults;
+ }
+ try{
+ var g;
+ var pBody = byId("perfTestsBody");
+ var chartsToRender = [];
+
+ if(doh.perfTestResults){
+ doh.showPerfTestsPage();
+ }
+ for(g in doh.perfTestResults){
+ var grp = doh.perfTestResults[g];
+ var hdr = document.createElement("h1");
+ hdr.appendChild(document.createTextNode("Group: " + g));
+ pBody.appendChild(hdr);
+ var ind = document.createElement("blockquote");
+ pBody.appendChild(ind);
+ var f;
+ for(f in grp){
+ var fResults = grp[f];
+ if(!fResults){ continue; }
+ var fhdr = document.createElement("h3");
+ fhdr.appendChild(document.createTextNode("TEST: " + f));
+ fhdr.style.textDecoration = "underline";
+ ind.appendChild(fhdr);
+ var div = document.createElement("div");
+ ind.appendChild(div);
+
+ //Figure out the basic info
+ var results = "<b>TRIAL SIZE: </b>" + fResults.trials[0].testIterations + " iterations<br>" +
+ "<b>NUMBER OF TRIALS: </b>" + fResults.trials.length + "<br>";
+
+ //Figure out the average test pass cost.
+ var i;
+ var iAvgArray = [];
+ var tAvgArray = [];
+ for(i = 0; i < fResults.trials.length; i++){
+ iAvgArray.push(fResults.trials[i].average);
+ tAvgArray.push(fResults.trials[i].executionTime);
+ }
+ results += "<b>AVERAGE TRIAL EXECUTION TIME: </b>" + doh.average(tAvgArray).toFixed(10) + "ms.<br>";
+ results += "<b>MAXIMUM TEST ITERATION TIME: </b>" + doh.max(iAvgArray).toFixed(10) + "ms.<br>";
+ results += "<b>MINIMUM TEST ITERATION TIME: </b>" + doh.min(iAvgArray).toFixed(10) + "ms.<br>";
+ results += "<b>AVERAGE TEST ITERATION TIME: </b>" + doh.average(iAvgArray).toFixed(10) + "ms.<br>";
+ results += "<b>MEDIAN TEST ITERATION TIME: </b>" + doh.median(iAvgArray).toFixed(10) + "ms.<br>";
+ results += "<b>VARIANCE TEST ITERATION TIME: </b>" + doh.variance(iAvgArray).toFixed(10) + "ms.<br>";
+ results += "<b>STANDARD DEVIATION ON TEST ITERATION TIME: </b>" + doh.standardDeviation(iAvgArray).toFixed(10) + "ms.<br>";
+
+ //Okay, attach it all in.
+ div.innerHTML = results;
+
+ div = document.createElement("div");
+ div.innerHTML = "<h3>Average Test Execution Time (in milliseconds, with median line)</h3>";
+ ind.appendChild(div);
+ div = document.createElement("div");
+ dojo.style(div, "width", "600px");
+ dojo.style(div, "height", "250px");
+ ind.appendChild(div);
+ chartsToRender.push({
+ div: div,
+ title: "Average Test Execution Time",
+ data: iAvgArray
+ });
+
+ div = document.createElement("div");
+ div.innerHTML = "<h3>Average Trial Execution Time (in milliseconds, with median line)</h3>";
+ ind.appendChild(div);
+ div = document.createElement("div");
+ dojo.style(div, "width", "600px");
+ dojo.style(div, "height", "250px");
+ ind.appendChild(div);
+ chartsToRender.push({
+ div: div,
+ title: "Average Trial Execution Time",
+ data: tAvgArray
+ });
+ }
+ }
+
+ //Lazy-render these to give the browser time and not appear locked.
+ var delayedRenders = function() {
+ if(chartsToRender.length){
+ var chartData = chartsToRender.shift();
+ plotResults(chartData.div, chartData.title, chartData.data);
+ }
+ doh.setTimeout(delayedRenders, 50);
+ };
+ doh.setTimeout(delayedRenders, 150);
+ }catch(e){
+ doh.debug(e);
+ }
+ }
+ or.apply(doh,arguments);
+ }
+ })(doh._report);
+
+ 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"]){
+ 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;
+ var x;
+ if(rolledUp||forceOpen){
+ rolledUp = false;
+ for(x=0; x<nodes.length; x++){
+ nodes[x].style.display = "";
+ }
+ toggle.innerHTML = "&#9660;";
+ }else{
+ rolledUp = true;
+ for(x=0; x<nodes.length; x++){
+ nodes[x].style.display = "none";
+ }
+ toggle.innerHTML = "&#9658;";
+ }
+ });
+ };
+
+ 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 = "<div class='testGroupName'>"+group+"</div><div style='width:0;'>&nbsp;</div>";
+ 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){
+ if(this._suiteCount == 0){
+ this._runedSuite = 0;
+ this._currentGlobalProgressBarWidth = 0;
+ this._suiteCount = this._testCount;
+ }
+ // console.debug("_groupStarted", group);
+ if(doh._inGroup != group){
+ doh._groupTotalTime = 0;
+ doh._runed = 0;
+ doh._inGroup = group;
+ this._runedSuite++;
+ }
+ var gn = getGroupNode(group);
+ if(gn){
+ gn.className = "inProgress";
+ }
+ }
+
+ doh._groupFinished = function(group, success){
+ // console.debug("_groupFinished", group);
+ var gn = getGroupNode(group);
+ if(gn && doh._inGroup == group){
+ doh._totalTime += doh._groupTotalTime;
+ gn.getElementsByTagName("td")[3].innerHTML = doh._groupTotalTime+"ms";
+ gn.getElementsByTagName("td")[2].lastChild.className = "";
+ doh._inGroup = null;
+ //doh._runedSuite++;
+ var failure = doh._updateGlobalProgressBar(this._runedSuite/this._groupCount,success,group);
+ gn.className = failure ? "failure" : "success";
+ //doh._runedSuite--;
+ doh._currentGlobalProgressBarWidth = parseInt(this._runedSuite/this._groupCount*10000)/100;
+ //byId("progressOuter").style.width = parseInt(this._runedSuite/this._suiteCount*100)+"%";
+ }
+ if(doh._inGroup == group){
+ this.debug("Total time for GROUP \"",group,"\" is ",doh._groupTotalTime,"ms");
+ }
+ }
+
+ 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._updateGlobalProgressBar = function(p,success,group){
+ var outerContainer=byId("progressOuter");
+
+ var gdiv=outerContainer.childNodes[doh._runedSuite-1];
+ if(!gdiv){
+ gdiv=document.createElement('div');
+ outerContainer.appendChild(gdiv);
+ gdiv.className='success';
+ gdiv.setAttribute('_target',group);
+ }
+ if(!success && !gdiv._failure){
+ gdiv._failure=true;
+ gdiv.className='failure';
+ if(group){
+ gdiv.setAttribute('title','failed group '+group);
+ }
+ }
+ var tp=parseInt(p*10000)/100;
+ gdiv.style.width = (tp-doh._currentGlobalProgressBarWidth)+"%";
+ return gdiv._failure;
+ }
+ doh._testFinished = function(group, fixture, success){
+ var fn = getFixtureNode(group, fixture);
+ var elapsed = fixture.endTime-fixture.startTime;
+ if(fn){
+ fn.getElementsByTagName("td")[3].innerHTML = elapsed+"ms";
+ fn.className = (success) ? "success" : "failure";
+ fn.getElementsByTagName("td")[2].setAttribute('_target', _loggedMsgLen);
+ if(!success){
+ _playSound("doh");
+ var gn = getGroupNode(group);
+ if(gn){
+ gn.className = "failure";
+ _getGroupToggler(group)(null, true);
+ }
+ }
+ }
+ if(doh._inGroup == group){
+ var gn = getGroupNode(group);
+ doh._runed++;
+ if(gn && doh._curTestCount){
+ var p = doh._runed/doh._curTestCount;
+ var groupfail = this._updateGlobalProgressBar((doh._runedSuite+p-1)/doh._groupCount,success,group);
+
+ var pbar = gn.getElementsByTagName("td")[2].lastChild;
+ pbar.className = groupfail?"failure":"success";
+ pbar.style.width = parseInt(p*100)+"%";
+ gn.getElementsByTagName("td")[3].innerHTML = parseInt(p*10000)/100+"%";
+ }
+ }
+ this._groupTotalTime += elapsed;
+ this.debug((success ? "PASSED" : "FAILED"), "test:", fixture.name, elapsed, 'ms');
+ }
+
+ // 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;
+ doh.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";
+ doh.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.
+ var i;
+ for(i = 0; i < toHide.length; i++){
+ var node = byId(toHide[i]);
+ if(node){
+ node.style.display="none";
+ }
+ }
+ toShow = byId(toShow);
+ if(toShow){
+ with(toShow.style){
+ display = "";
+ zIndex = ++tabzidx;
+ }
+ }
+ }
+
+ doh.showTestPage = function(){
+ _showTab("testBody", ["logBody", "perfTestsBody"]);
+ }
+
+ doh.showLogPage = function(){
+ _showTab("logBody", ["testBody", "perfTestsBody"]);
+ }
+
+ doh.showPerfTestsPage = function(){
+ _showTab("perfTestsBody", ["testBody", "logBody"]);
+ }
+
+ var runAll = true;
+ doh.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 = doh.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(){
+ // let robot code run if it gets to this first
+ var __onEnd = doh._onEnd;
+ doh._onEnd = function(){
+ __onEnd.apply(doh, arguments);
+ 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 amenable 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;
+ }
+
+ //Performance report generating functions!
+ doh._dojoPlotPerfResults = function(div, name, dataArray) {
+ var median = doh.median(dataArray);
+ var medarray = [];
+
+ var i;
+ for(i = 0; i < dataArray.length; i++){
+ medarray.push(median);
+ }
+
+ var data = {
+ label: "name",
+ items: [
+ {name: name, trials: dataArray},
+ {name: "Median", trials: medarray}
+ ]
+ };
+ var ifs = new dojo.data.ItemFileReadStore({data: data});
+
+ var min = Math.floor(doh.min(dataArray));
+ var max = Math.ceil(doh.max(dataArray));
+ var step = (max - min)/10;
+
+ //Lets try to pad out the bottom and top a bit
+ //Then recalc the step.
+ if(min > 0){
+ min = min - step;
+ if(min < 0){
+ min = 0;
+ }
+ min = Math.floor(min);
+ }
+ if(max > 0){
+ max = max + step;
+ max = Math.ceil(max);
+ }
+ step = (max - min)/10;
+
+ var chart = new dojox.charting.DataChart(div, {
+ type: dojox.charting.plot2d.Lines,
+ displayRange:dataArray.length,
+ xaxis: {min: 1, max: dataArray.length, majorTickStep: Math.ceil((dataArray.length - 1)/10), htmlLabels: false},
+ yaxis: {min: min, max: max, majorTickStep: step, vertical: true, htmlLabels: false}
+ });
+ chart.setStore(ifs, {name:"*"}, "trials");
+ };
+
+ doh._asciiPlotPerfResults = function(){
+ //TODO: Implement!
+ };
+ }
+ );
+ }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);
+
+ //Okay, there may be performance info we need to filter back
+ //to the parent, so do that here.
+ if(doh.perfTestResults){
+ try{
+ gName = g.toString();
+ var localFName = f.name;
+ while(localFName.indexOf("::") >= 0){
+ localFName = localFName.substring(localFName.indexOf("::") + 2, localFName.length);
+ }
+ if(!_doh.perfTestResults){
+ _doh.perfTestResults = {};
+ }
+ if(!_doh.perfTestResults[gName]){
+ _doh.perfTestResults[gName] = {};
+ }
+ _doh.perfTestResults[gName][f.name] = doh.perfTestResults[gName][localFName];
+ }catch (e){
+ doh.debug(e);
+ }
+ }
+ }
+ doh._groupStarted = function(g){
+ if(!this._setParent){
+ _doh._curTestCount = this._testCount;
+ _doh._curGroupCount = this._groupCount;
+ this._setParent = true;
+ }
+ }
+ doh._report = function(){
+ };
+ }
+ }
+
+})();
20 tests/doh/_nodeRunner.js
@@ -0,0 +1,20 @@
+
+/*global doh: false, process: false */
+
+var aps = Array.prototype.slice;
+
+doh.debug = function () {
+ //Could have multiple args, join them all together.
+ var msg = aps.call(arguments, 0).join(' ');
+ console.log(msg);
+};
+
+// Override the doh._report method to make it quit with an
+// appropriate exit code in case of test failures.
+var oldReport = doh._report;
+doh._report = function () {
+ oldReport.apply(doh, arguments);
+ if (this._failureCount > 0 || this._errorCount > 0) {
+ process.exit(1);
+ }
+};
17 tests/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);
+ }
+ }
+})();
10 tests/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
BIN  tests/doh/_sounds/doh.wav
Binary file not shown
BIN  tests/doh/_sounds/dohaaa.wav
Binary file not shown
BIN  tests/doh/_sounds/woohoo.wav
Binary file not shown
316 tests/doh/runner.html
@@ -0,0 +1,316 @@
+<html>
+ <!--
+ NOTE: we are INTENTIONALLY in quirks mode. It makes it much easier to
+ get a "full screen" UI w/ straightforward CSS.
+ -->
+ <!--
+ // TODO: provide a UI for prompted tests
+ -->
+ <head>
+ <title>RequireJS Tests Via The Dojo Unit Test Harness, $Rev: 20149 $</title>
+
+ <script type="text/javascript">
+ // workaround for bug in Safari 3. See #7189
+ if (/3[\.0-9]+ Safari/.test(navigator.appVersion))
+ {
+ window.console = {
+ origConsole: window.console,
+ log: function(s){
+ this.origConsole.log(s);
+ },
+ info: function(s){
+ this.origConsole.info(s);
+ },
+ error: function(s){
+ this.origConsole.error(s);
+ },
+ warn: function(s){
+ this.origConsole.warn(s);
+ }
+ };
+ }
+ </script>
+
+ <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];
+ }
+ if(tp[0] == "registerModulePath"){
+ var modules = tp[1].split(";");
+ window.registerModulePath=[];
+ for (var i=0; i<modules.length;i++){
+ window.registerModulePath.push(modules[i].split(","));
+ }
+ }
+ }
+ }
+
+ //document.write("<scr"+"ipt type='text/javascript' djConfig='isDebug: true' src='"+dojoUrl+"'></scr"+"ipt>");
+ </script>
+ <script type="text/javascript" src="runner.js"></script>
+ <script type="text/javascript" src="_browserRunner.js"></script>
+
+ <script type="text/javascript">
+ 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://o.aolcdn.com/dojo/1.3/dijit/themes/tundra/images/dojoTundraGradientBg.png") repeat-x 0 0;
+ height: 1em;
+ /*the following trick is necessary to prevent IE from wrapping the last piece of progress bar into a new line*/
+ _margin:1px;
+ _padding: -1px;
+
+ /*
+ border-color: #e8e8e8;
+ */
+ }
+
+ #progressOuter .success, #progressOuter .failure{
+ float: left;
+ height: 1em;
+ }
+
+ #play, #pause {
+ font-family: Arial;
+ 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 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;
+ }
+
+ #testList tfoot {
+ font-weight: bold;
+ }
+
+ #toggleButtons {
+ float: left;
+ background-color: #DEDEDE;
+ }
+
+ div.testGroupName {
+ position: absolute;
+ }
+
+ .inProgress {
+ background-color: #85afde;
+ }
+
+ .success {
+ background-color: #7cdea7;
+ }
+
+ .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;">RequireJS Tests Via 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"><div id="progressOuter" onclick="doh._jumpToSuite(arguments[0]);"></div></td>
+ </tr>
+ <tr valign="top" height="30">
+ <td width="30%" class="header">
+ <span id="toggleButtons" onclick="doh.togglePaused();">
+ <button id="play">&#9658;</button>
+ <button id="pause" style="display: none;">&#9553;</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="doh.showTestPage();">Test Page</button>
+ <button class="tab" onclick="doh.showLogPage();">Log</button>
+ <button class="tab" onclick="doh.showPerfTestsPage();">Performance Tests Results</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;" onclick="doh._jumpToLog(arguments[0]);">
+ <thead>
+ <tr id="testListHeader" style="border: 0; padding: 0; margin: 0;" >
+ <th>&nbsp;</th>
+ <th width="20">
+ <input type="checkbox" checked
+ onclick="doh.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: Arial; width: 15px;">&#9658;</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 id="perfTestsBody" style="background-color: white;"></div>
+ </div>
+ <iframe id="testBody" class="tabBody"
+ style="z-index: -1;"></iframe>
+ <!--
+ src="http://redesign.dojotoolkit.org"></iframe>
+ -->
+ </div>
+ </td>
+ </tr>
+ </table>
+ <span id="hiddenAudio"></span>
+ </body>
+</html>
+
1,499 tests/doh/runner.js
@@ -0,0 +1,1499 @@
+// package system gunk.
+//try{
+// dojo.provide("doh.runner");
+//}catch(e){
+ if(!this["doh"]){
+ doh = {};
+ }
+//}
+
+//
+// Utility Functions and Classes
+//
+
+doh.selfTest = false;
+
+doh.global = this;
+
+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(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, hint){
+ // 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, hint);
+ }
+ if(hint){
+ msg = (new String(msg||""))+" with hint: \n\t\t"+(new String(hint)+"\n");
+ }
+ 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, {
+ getTestErrback: function(cb, scope){
+ // summary: Replaces outer getTextCallback's in nested situations to avoid multiple callback(true)'s
+ var _this = this;
+ return function(){
+ try{
+ cb.apply(scope||doh.global||_this, arguments);
+ }catch(e){
+ _this.errback(e);
+ }
+ };
+ },
+
+ getTestCallback: function(cb, scope){
+ var _this = this;
+ return function(){
+ try{
+ cb.apply(scope||doh.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 doh.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,
+ /*String*/ type){
+ // 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
+ // type: The type of tests these are, such as a group of performance tests
+ // null/undefied are standard DOH tests, the valye 'perf' enables
+ // registering them as performance tests.
+ if(tests){
+ this.register(group, tests, type);
+ }
+ if(setUp){
+ this._groups[group].setUp = setUp;
+ }
+ if(tearDown){
+ this._groups[group].tearDown = tearDown;
+ }
+}
+
+doh._getTestObj = function(group, test, type){
+ 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", "_") // FIXME: bad escapement
+ };
+ 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
+ }
+
+ //Augment the test with some specific options to make it identifiable as a
+ //particular type of test so it can be executed properly.
+ if(type === "perf" || tObj.testType === "perf"){
+ tObj.testType = "perf";
+
+ //Build an object on the root DOH class to contain all the test results.
+ //Cache it on the test object for quick lookup later for results storage.
+ if(!doh.perfTestResults){
+ doh.perfTestResults = {};
+ doh.perfTestResults[group] = {};
+ }
+ if(!doh.perfTestResults[group]){
+ doh.perfTestResults[group] = {};
+ }
+ if(!doh.perfTestResults[group][tObj.name]){
+ doh.perfTestResults[group][tObj.name] = {};
+ }
+ tObj.results = doh.perfTestResults[group][tObj.name];
+
+ //If it's not set, then set the trial duration
+ //default to 100ms.
+ if(!("trialDuration" in tObj)){
+ tObj.trialDuration = 100;
+ }
+
+ //If it's not set, then set the delay between trial runs to 100ms
+ //default to 100ms to allow for GC and to make IE happy.
+ if(!("trialDelay" in tObj)){
+ tObj.trialDelay = 100;
+ }
+
+ //If it's not set, then set number of times a trial is run to 10.
+ if(!("trialIterations" in tObj)){
+ tObj.trialIterations = 10;
+ }
+ }
+ return tObj;
+}
+
+doh.registerTest = function(/*String*/ group, /*Function||Object*/ test, /*String*/ type){
+ // 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.
+ // type:
+ // An identifier denoting the type of testing that the test performs, such
+ // as a performance test. If null, defaults to regular DOH test.
+ if(!this._groups[group]){
+ this._groupCount++;
+ this._groups[group] = [];
+ this._groups[group].inFlight = 0;
+ }
+ var tObj = this._getTestObj(group, test, type);
+ if(!tObj){ return null; }
+ this._groups[group].push(tObj);
+ this._testCount++;
+ this._testRegistered(group, tObj);
+ return tObj;
+}
+
+doh.registerTests = function(/*String*/ group, /*Array*/ testArr, /*String*/ type){
+ // summary:
+ // registers a group of tests, treating each element of testArr as
+ // though it were being (along with group) passed to the registerTest
+ // method. It also uses the type to decide how the tests should
+ // behave, by defining the type of tests these are, such as performance tests
+ for(var x=0; x<testArr.length; x++){
+ this.registerTest(group, testArr[x], type);
+ }
+}
+
+// FIXME: move implementation to _browserRunner?
+doh.registerUrl = function( /*String*/ group,
+ /*String*/ url,
+ /*Integer*/ timeout,
+ /*String*/ type){
+ this.debug("ERROR:");
+ this.debug("\tNO registerUrl() METHOD AVAILABLE.");
+ // this._urls.push(url);
+}
+
+doh.registerString = function(group, str, type){
+}
+
+// FIXME: remove the doh.add alias SRTL.
+doh.register = doh.add = function(groupOrNs, testOrNull, type){
+ // 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, null, null, type);
+ }else{
+ this.registerTest("ungrouped", groupOrNs, type);
+ }
+ }
+ 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, null, null, type);
+ }else{
+ this.registerTest(groupOrNs, testOrNull, type);
+ }
+ // this.registerTestNs(groupOrNs, testOrNull);
+ return;
+ }
+ if(doh._isArray(testOrNull)){
+ this.registerTests(groupOrNs, testOrNull, type);
+ return;
+ }
+ this.registerTest(groupOrNs, testOrNull, type);
+};
+
+doh.registerDocTests = function(module){
+ // no-op for when Dojo isn't loaded into the page
+ this.debug("registerDocTests() requires dojo to be loaded into the environment. Skipping doctest set for module:", module);
+};
+(function(){
+ if(typeof dojo != "undefined"){
+ try{
+ dojo.require("dojox.testing.DocTest");
+ }catch(e){
+ // if the DocTest module isn't available (e.g., the build we're
+ // running from doesn't include it), stub it out and log the error
+ console.debug(e);
+
+ doh.registerDocTests = function(){}
+ return;
+ }
+ doh.registerDocTests = function(module){
+ // summary:
+ // Get all the doctests from the given module and register each of them
+ // as a single test case here.
+ //
+
+ var docTest = new dojox.testing.DocTest();
+ var docTests = docTest.getTests(module);
+ var len = docTests.length;
+ var tests = [];
+ for (var i=0; i<len; i++){
+ var test = docTests[i];
+ // Extract comment on first line and add to test name.
+ var comment = "";
+ if (test.commands.length && test.commands[0].indexOf("//")!=-1) {
+ var parts = test.commands[0].split("//");
+ comment = ", "+parts[parts.length-1]; // Get all after the last //, so we dont get trapped by http:// or alikes :-).
+ }
+ tests.push({
+ runTest: (function(test){
+ return function(t){
+ var r = docTest.runTest(test.commands, test.expectedResult);
+ t.assertTrue(r.success);
+ }
+ })(test),
+ name:"Line "+test.line+comment
+ }
+ );
+ }
+ this.register("DocTests: "+module, tests);
+ }
+ }
+})();
+
+//
+// Assertions and In-Test Utilities
+//
+
+doh.t = doh.assertTrue = function(/*Object*/ condition, /*String?*/ hint){
+ // summary:
+ // is the passed item "truthy"?
+ if(arguments.length < 1){
+ throw new doh._AssertFailure("assertTrue failed because it was not passed at least 1 argument");
+ }
+ if(!eval(condition)){
+ throw new doh._AssertFailure("assertTrue('" + condition + "') failed", hint);
+ }
+}
+
+doh.f = doh.assertFalse = function(/*Object*/ condition, /*String?*/ hint){
+ // summary:
+ // is the passed item "falsey"?
+ if(arguments.length < 1){
+ throw new doh._AssertFailure("assertFalse failed because it was not passed at least 1 argument");
+ }
+ if(eval(condition)){
+ throw new doh._AssertFailure("assertFalse('" + condition + "') failed", hint);
+ }
+}
+
+doh.e = doh.assertError = function(/*Error object*/expectedError, /*Object*/scope, /*String*/functionName, /*Array*/args, /*String?*/ hint){
+ // 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", hint);
+ }
+ }
+ throw new doh._AssertFailure("assertError() failed:\n\texpected error\n\t\t"+expectedError+"\n\tbut no error caught\n\n", hint);
+}
+
+
+doh.is = doh.assertEqual = function(/*Object*/ expected, /*Object*/ actual, /*String?*/ hint){
+ // 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)||
+ ( typeof expected == "number" && typeof actual == "number" && isNaN(expected) && isNaN(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", hint);
+}
+
+doh.isNot = doh.assertNotEqual = function(/*Object*/ notExpected, /*Object*/ actual, /*String?*/ hint){
+ // summary:
+ // are the passed notexpected and actual objects/values deeply
+ // not equivalent?
+
+ // Compare undefined always with three equal signs, because undefined==null
+ // is true, but undefined===null is false.
+ if((notExpected === undefined)&&(actual === undefined)){
+ throw new doh._AssertFailure("assertNotEqual() failed: not expected |"+notExpected+"| but got |"+actual+"|", hint);
+ }
+ if(arguments.length < 2){
+ throw doh._AssertFailure("assertEqual failed because it was not passed 2 arguments");
+ }
+ if((notExpected === actual)||(notExpected == actual)){
+ throw new doh._AssertFailure("assertNotEqual() failed: not expected |"+notExpected+"| but got |"+actual+"|", hint);
+ }
+ if( (this._isArray(notExpected) && this._isArray(actual))&&
+ (this._arrayEq(notExpected, actual)) ){
+ throw new doh._AssertFailure("assertNotEqual() failed: not expected |"+notExpected+"| but got |"+actual+"|", hint);
+ }
+ if( ((typeof notExpected == "object")&&((typeof actual == "object")))&&
+ (this._objPropEq(notExpected, actual)) ){
+ throw new doh._AssertFailure("assertNotEqual() failed: not expected |"+notExpected+"| but got |"+actual+"|", hint);
+ }
+ return true;
+}
+
+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){
+ // Degenerate case: if they are both null, then their "properties" are equal.
+ if(expected === null && actual === null){
+ return true;
+ }
+ // If only one is null, they aren't equal.
+ if(expected === null || actual === null){
+ return false;
+ }
+ if(expected instanceof Date){
+ return actual instanceof Date && expected.getTime()==actual.getTime();
+ }
+ var x;
+ // Make sure ALL THE SAME properties are in both objects!
+ for(x in actual){ // Lets check "actual" here, expected is checked below.
+ if(expected[x] === undefined){
+ return false;
+ }
+ };
+
+ for(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" ||
+ (
+ !!doh.global["dojo"] &&
+ doh.global["dojo"]["NodeList"] !== undefined &&
+ it instanceof doh.global["dojo"]["NodeList"]
+ )
+ );
+}
+
+//
+// Runner-Wrapper
+//
+
+doh._setupGroupForRun = function(/*String*/ groupName, /*Integer*/ idx){
+ var tg = this._groups[groupName];