Updating jquery.qunit from upstream
authorKrinkle <krinkle@users.mediawiki.org>
Thu, 20 Oct 2011 22:35:27 +0000 (22:35 +0000)
committerKrinkle <krinkle@users.mediawiki.org>
Thu, 20 Oct 2011 22:35:27 +0000 (22:35 +0000)
* Source: https://github.com/jquery/qunit/tree/ee156923cdb01820e35e6bb579d5cf6bf55736d4
-- Several bug fixes
-- New hooks system
-- Version numbers, finally (this is 1.2.0pre)
-- (BREAKING CHANGE) start/stop now takes count instead of timeout as first argument (read more at https://github.com/jquery/qunit/pull/125 and http://docs.jquery.com/QUnit/stop)

resources/jquery/jquery.qunit.css
resources/jquery/jquery.qunit.js

index c85f36a..e114ea0 100644 (file)
@@ -1,5 +1,5 @@
 /**
- * QUnit - A JavaScript Unit Testing Framework
+ * QUnit 1.2.0pre - A JavaScript Unit Testing Framework
  *
  * http://docs.jquery.com/QUnit
  *
index a711c82..d630c43 100644 (file)
@@ -1,5 +1,5 @@
 /**
- * QUnit - A JavaScript Unit Testing Framework
+ * QUnit 1.2.0pre - A JavaScript Unit Testing Framework
  *
  * http://docs.jquery.com/QUnit
  *
@@ -21,7 +21,9 @@ var defined = {
        })()
 };
 
-var testId = 0;
+var    testId = 0,
+       toString = Object.prototype.toString,
+       hasOwn = Object.prototype.hasOwnProperty;
 
 var Test = function(name, testName, expected, testEnvironmentArg, async, callback) {
        this.name = name;
@@ -48,7 +50,7 @@ Test.prototype = {
        setup: function() {
                if (this.module != config.previousModule) {
                        if ( config.previousModule ) {
-                               QUnit.moduleDone( {
+                               runLoggingCallbacks('moduleDone', QUnit, {
                                        name: config.previousModule,
                                        failed: config.moduleStats.bad,
                                        passed: config.moduleStats.all - config.moduleStats.bad,
@@ -57,7 +59,7 @@ Test.prototype = {
                        }
                        config.previousModule = this.module;
                        config.moduleStats = { all: 0, bad: 0 };
-                       QUnit.moduleStart( {
+                       runLoggingCallbacks( 'moduleStart', QUnit, {
                                name: this.module
                        } );
                }
@@ -71,9 +73,10 @@ Test.prototype = {
                        extend(this.testEnvironment, this.testEnvironmentArg);
                }
 
-               QUnit.testStart( {
-                       name: this.testName
-               } );
+               runLoggingCallbacks( 'testStart', QUnit, {
+                       name: this.testName,
+                       module: this.module
+               });
 
                // allow utility functions to access the current test environment
                // TODO why??
@@ -90,6 +93,7 @@ Test.prototype = {
                }
        },
        run: function() {
+               config.current = this;
                if ( this.async ) {
                        QUnit.stop();
                }
@@ -108,11 +112,12 @@ Test.prototype = {
 
                        // Restart the tests if they're blocking
                        if ( config.blocking ) {
-                               start();
+                               QUnit.start();
                        }
                }
        },
        teardown: function() {
+               config.current = this;
                try {
                        this.testEnvironment.teardown.call(this.testEnvironment);
                        checkPollution();
@@ -121,7 +126,8 @@ Test.prototype = {
                }
        },
        finish: function() {
-               if ( this.expected && this.expected != this.assertions.length ) {
+               config.current = this;
+               if ( this.expected != null && this.expected != this.assertions.length ) {
                        QUnit.ok( false, "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run" );
                }
 
@@ -210,8 +216,9 @@ Test.prototype = {
                        fail("reset() failed, following Test " + this.testName + ", exception and reset fn follows", e, QUnit.reset);
                }
 
-               QUnit.testDone( {
+               runLoggingCallbacks( 'testDone', QUnit, {
                        name: this.testName,
+                       module: this.module,
                        failed: bad,
                        passed: this.assertions.length - bad,
                        total: this.assertions.length
@@ -243,7 +250,7 @@ Test.prototype = {
                if (bad) {
                        run();
                } else {
-                       synchronize(run);
+                       synchronize(run, true);
                };
        }
 
@@ -260,7 +267,7 @@ var QUnit = {
        asyncTest: function(testName, expected, callback) {
                if ( arguments.length === 2 ) {
                        callback = expected;
-                       expected = 0;
+                       expected = null;
                }
 
                QUnit.test(testName, expected, callback, true);
@@ -310,8 +317,8 @@ var QUnit = {
                        result: a,
                        message: msg
                };
-               msg = escapeHtml(msg);
-               QUnit.log(details);
+               msg = escapeInnerText(msg);
+               runLoggingCallbacks( 'log', QUnit, details );
                config.current.assertions.push({
                        result: a,
                        message: msg
@@ -387,8 +394,8 @@ var QUnit = {
                QUnit.ok(ok, message);
        },
 
-       start: function() {
-               config.semaphore--;
+       start: function(count) {
+               config.semaphore -= count || 1;
                if (config.semaphore > 0) {
                        // don't start until equal number of stop-calls
                        return;
@@ -408,28 +415,38 @@ var QUnit = {
                                }
 
                                config.blocking = false;
-                               process();
+                               process(true);
                        }, 13);
                } else {
                        config.blocking = false;
-                       process();
+                       process(true);
                }
        },
 
-       stop: function(timeout) {
-               config.semaphore++;
+       stop: function(count) {
+               config.semaphore += count || 1;
                config.blocking = true;
 
-               if ( timeout && defined.setTimeout ) {
+               if ( config.testTimeout && defined.setTimeout ) {
                        clearTimeout(config.timeout);
                        config.timeout = window.setTimeout(function() {
                                QUnit.ok( false, "Test timed out" );
+                               config.semaphore = 1;
                                QUnit.start();
-                       }, timeout);
+                       }, config.testTimeout);
                }
        }
 };
 
+//We want access to the constructor's prototype
+(function() {
+       function F(){};
+       F.prototype = QUnit;
+       QUnit = new F();
+       //Make F QUnit's constructor so that we can add to the prototype later
+       QUnit.constructor = F;
+})();
+
 // Backwards compatibility, deprecated
 QUnit.equals = QUnit.equal;
 QUnit.same = QUnit.deepEqual;
@@ -453,7 +470,16 @@ var config = {
        // by default, modify document.title when suite is done
        altertitle: true,
 
-       urlConfig: ['noglobals', 'notrycatch']
+       urlConfig: ['noglobals', 'notrycatch'],
+
+       //logging callback queues
+       begin: [],
+       done: [],
+       log: [],
+       testStart: [],
+       testDone: [],
+       moduleStart: [],
+       moduleDone: []
 };
 
 // Load paramaters
@@ -586,8 +612,7 @@ extend(QUnit, {
                                return "null";
                }
 
-               var type = Object.prototype.toString.call( obj )
-                       .match(/^\[object\s(.*)\]$/)[1] || '';
+               var type = toString.call( obj ).match(/^\[object\s(.*)\]$/)[1] || '';
 
                switch (type) {
                                case 'Number':
@@ -618,10 +643,10 @@ extend(QUnit, {
                        expected: expected
                };
 
-               message = escapeHtml(message) || (result ? "okay" : "failed");
+               message = escapeInnerText(message) || (result ? "okay" : "failed");
                message = '<span class="test-message">' + message + "</span>";
-               expected = escapeHtml(QUnit.jsDump.parse(expected));
-               actual = escapeHtml(QUnit.jsDump.parse(actual));
+               expected = escapeInnerText(QUnit.jsDump.parse(expected));
+               actual = escapeInnerText(QUnit.jsDump.parse(actual));
                var output = message + '<table><tr class="test-expected"><th>Expected: </th><td><pre>' + expected + '</pre></td></tr>';
                if (actual != expected) {
                        output += '<tr class="test-actual"><th>Result: </th><td><pre>' + actual + '</pre></td></tr>';
@@ -631,12 +656,12 @@ extend(QUnit, {
                        var source = sourceFromStacktrace();
                        if (source) {
                                details.source = source;
-                               output += '<tr class="test-source"><th>Source: </th><td><pre>' + escapeHtml(source) + '</pre></td></tr>';
+                               output += '<tr class="test-source"><th>Source: </th><td><pre>' + escapeInnerText(source) + '</pre></td></tr>';
                        }
                }
                output += "</table>";
 
-               QUnit.log(details);
+               runLoggingCallbacks( 'log', QUnit, details );
 
                config.current.assertions.push({
                        result: !!result,
@@ -649,6 +674,9 @@ extend(QUnit, {
                var querystring = "?",
                        key;
                for ( key in params ) {
+                       if ( !hasOwn.call( params, key ) ) {
+                               continue;
+                       }
                        querystring += encodeURIComponent( key ) + "=" +
                                encodeURIComponent( params[ key ] ) + "&";
                }
@@ -657,23 +685,28 @@ extend(QUnit, {
 
        extend: extend,
        id: id,
-       addEvent: addEvent,
+       addEvent: addEvent
+});
 
+//QUnit.constructor is set to the empty F() above so that we can add to it's prototype later
+//Doing this allows us to tell if the following methods have been overwritten on the actual
+//QUnit object, which is a deprecated way of using the callbacks.
+extend(QUnit.constructor.prototype, {
        // Logging callbacks; all receive a single argument with the listed properties
        // run test/logs.html for any related changes
-       begin: function() {},
+       begin: registerLoggingCallback('begin'),
        // done: { failed, passed, total, runtime }
-       done: function() {},
+       done: registerLoggingCallback('done'),
        // log: { result, actual, expected, message }
-       log: function() {},
+       log: registerLoggingCallback('log'),
        // testStart: { name }
-       testStart: function() {},
+       testStart: registerLoggingCallback('testStart'),
        // testDone: { name, failed, passed, total }
-       testDone: function() {},
+       testDone: registerLoggingCallback('testDone'),
        // moduleStart: { name }
-       moduleStart: function() {},
+       moduleStart: registerLoggingCallback('moduleStart'),
        // moduleDone: { name, failed, passed, total }
-       moduleDone: function() {}
+       moduleDone: registerLoggingCallback('moduleDone')
 });
 
 if ( typeof document === "undefined" || document.readyState === "complete" ) {
@@ -681,7 +714,7 @@ if ( typeof document === "undefined" || document.readyState === "complete" ) {
 }
 
 QUnit.load = function() {
-       QUnit.begin({});
+       runLoggingCallbacks( 'begin', QUnit, {} );
 
        // Initialize the config, saving the execution queue
        var oldconfig = extend({}, config);
@@ -756,12 +789,23 @@ QUnit.load = function() {
 
 addEvent(window, "load", QUnit.load);
 
+// addEvent(window, "error") gives us a useless event object
+window.onerror = function( message, file, line ) {
+       if ( QUnit.config.current ) {
+               ok( false, message + ", " + file + ":" + line );
+       } else {
+               test( "global failure", function() {
+                       ok( false, message + ", " + file + ":" + line );
+               });
+       }
+};
+
 function done() {
        config.autorun = true;
 
        // Log the last module results
        if ( config.currentModule ) {
-               QUnit.moduleDone( {
+               runLoggingCallbacks( 'moduleDone', QUnit, {
                        name: config.currentModule,
                        failed: config.moduleStats.bad,
                        passed: config.moduleStats.all - config.moduleStats.bad,
@@ -803,7 +847,7 @@ function done() {
                ].join(" ");
        }
 
-       QUnit.done( {
+       runLoggingCallbacks( 'done', QUnit, {
                failed: config.stats.bad,
                passed: passed,
                total: config.stats.all,
@@ -855,16 +899,14 @@ function sourceFromStacktrace() {
        }
 }
 
-function escapeHtml(s) {
+function escapeInnerText(s) {
        if (!s) {
                return "";
        }
        s = s + "";
-       return s.replace(/[\&"<>\\]/g, function(s) {
+       return s.replace(/[\&<>]/g, function(s) {
                switch(s) {
                        case "&": return "&amp;";
-                       case "\\": return "\\\\";
-                       case '"': return '\"';
                        case "<": return "&lt;";
                        case ">": return "&gt;";
                        default: return s;
@@ -872,26 +914,30 @@ function escapeHtml(s) {
        });
 }
 
-function synchronize( callback ) {
+function synchronize( callback, last ) {
        config.queue.push( callback );
 
        if ( config.autorun && !config.blocking ) {
-               process();
+               process(last);
        }
 }
 
-function process() {
-       var start = (new Date()).getTime();
+function process( last ) {
+       var start = new Date().getTime();
+       config.depth = config.depth ? config.depth + 1 : 1;
 
        while ( config.queue.length && !config.blocking ) {
-               if ( config.updateRate <= 0 || (((new Date()).getTime() - start) < config.updateRate) ) {
+               if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) {
                        config.queue.shift()();
                } else {
-                       window.setTimeout( process, 13 );
+                       window.setTimeout( function(){
+                               process( last );
+                       }, 13 );
                        break;
                }
        }
-       if (!config.blocking && !config.queue.length) {
+       config.depth--;
+       if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) {
                done();
        }
 }
@@ -901,6 +947,9 @@ function saveGlobal() {
 
        if ( config.noglobals ) {
                for ( var key in window ) {
+                       if ( !hasOwn.call( window, key ) ) {
+                               continue;
+                       }
                        config.pollution.push( key );
                }
        }
@@ -951,7 +1000,9 @@ function extend(a, b) {
        for ( var prop in b ) {
                if ( b[prop] === undefined ) {
                        delete a[prop];
-               } else {
+
+               // Avoid "Member not found" error in IE8 caused by setting window.constructor
+               } else if ( prop !== "constructor" || a !== window ) {
                        a[prop] = b[prop];
                }
        }
@@ -974,9 +1025,27 @@ function id(name) {
                document.getElementById( name );
 }
 
+function registerLoggingCallback(key){
+       return function(callback){
+               config[key].push( callback );
+       };
+}
+
+// Supports deprecated method of completely overwriting logging callbacks
+function runLoggingCallbacks(key, scope, args) {
+       //debugger;
+       var callbacks;
+       if ( QUnit.hasOwnProperty(key) ) {
+               QUnit[key].call(scope, args);
+       } else {
+               callbacks = config[key];
+               for( var i = 0; i < callbacks.length; i++ ) {
+                       callbacks[i].call( scope, args );
+               }
+       }
+}
+
 // Test for equality any JavaScript type.
-// Discussions and reference: http://philrathe.com/articles/equiv
-// Test suites: http://philrathe.com/tests/equiv
 // Author: Philippe Rathé <prathe@gmail.com>
 QUnit.equiv = function () {
 
@@ -1226,7 +1295,12 @@ QUnit.jsDump = (function() {
                                type = "document";
                        } else if (obj.nodeType) {
                                type = "node";
-                       } else if (typeof obj === "object" && typeof obj.length === "number" && obj.length >= 0) {
+                       } else if (
+                               // native arrays
+                               toString.call( obj ) === "[object Array]" ||
+                               // NodeList objects
+                               ( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) )
+                       ) {
                                type = "array";
                        } else {
                                type = typeof obj;
@@ -1408,6 +1482,9 @@ QUnit.diff = (function() {
                }
 
                for (var i in ns) {
+                       if ( !hasOwn.call( ns, i ) ) {
+                               continue;
+                       }
                        if (ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1) {
                                n[ns[i].rows[0]] = {
                                        text: n[ns[i].rows[0]],