]> source.dussan.org Git - jquery-ui.git/commitdiff
Update QUnit to 1.8.0
authorJörn Zaefferer <joern.zaefferer@gmail.com>
Wed, 13 Jun 2012 22:21:54 +0000 (00:21 +0200)
committerJörn Zaefferer <joern.zaefferer@gmail.com>
Wed, 13 Jun 2012 22:21:54 +0000 (00:21 +0200)
external/qunit.css
external/qunit.js

index 23235ec84ad38b63e270f2a95f3e4078b38fc0b8..5684a4485977a607f5a1ce969a715ca6d6fe5120 100644 (file)
@@ -1,5 +1,5 @@
 /**
- * QUnit v1.6.0 - A JavaScript Unit Testing Framework
+ * QUnit v1.8.0 - A JavaScript Unit Testing Framework
  *
  * http://docs.jquery.com/QUnit
  *
index 2c277fab51e1fce5e20a1747e8d06da1e05a408d..c1570c2520a911f862f3ccd327d4e796916d610a 100644 (file)
@@ -1,5 +1,5 @@
 /**
- * QUnit v1.6.0 - A JavaScript Unit Testing Framework
+ * QUnit v1.8.0 - A JavaScript Unit Testing Framework
  *
  * http://docs.jquery.com/QUnit
  *
@@ -12,7 +12,9 @@
 
 var QUnit,
        config,
+       onErrorFnPrev,
        testId = 0,
+       fileName = (sourceFromStacktrace( 0 ) || "" ).replace(/(:\d+)+\)?/, "").replace(/.+\//, ""),
        toString = Object.prototype.toString,
        hasOwn = Object.prototype.hasOwnProperty,
        defined = {
@@ -29,26 +31,31 @@ var QUnit,
        }())
 };
 
-function Test( name, testName, expected, async, callback ) {
-       this.name = name;
-       this.testName = testName;
-       this.expected = expected;
-       this.async = async;
-       this.callback = callback;
+function Test( settings ) {
+       extend( this, settings );
        this.assertions = [];
+       this.testNumber = ++Test.count;
 }
 
+Test.count = 0;
+
 Test.prototype = {
        init: function() {
-               var b, li,
+               var a, b, li,
         tests = id( "qunit-tests" );
 
                if ( tests ) {
                        b = document.createElement( "strong" );
-                       b.innerHTML = "Running " + this.name;
+                       b.innerHTML = this.name;
+
+                       // `a` initialized at top of scope
+                       a = document.createElement( "a" );
+                       a.innerHTML = "Rerun";
+                       a.href = QUnit.url({ testNumber: this.testNumber });
 
                        li = document.createElement( "li" );
                        li.appendChild( b );
+                       li.appendChild( a );
                        li.className = "running";
                        li.id = this.id = "qunit-test-output" + testId++;
 
@@ -119,14 +126,14 @@ Test.prototype = {
                }
 
                if ( config.notrycatch ) {
-                       this.callback.call( this.testEnvironment );
+                       this.callback.call( this.testEnvironment, QUnit.assert );
                        return;
                }
 
                try {
-                       this.callback.call( this.testEnvironment );
+                       this.callback.call( this.testEnvironment, QUnit.assert );
                } catch( e ) {
-                       QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + ": " + e.message, extractStacktrace( e, 1 ) );
+                       QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + e.message, extractStacktrace( e, 0 ) );
                        // else next test will carry the responsibility
                        saveGlobal();
 
@@ -152,13 +159,16 @@ Test.prototype = {
        },
        finish: function() {
                config.current = this;
-               if ( this.expected != null && this.expected != this.assertions.length ) {
+               if ( config.requireExpects && this.expected == null ) {
+                       QUnit.pushFailure( "Expected number of assertions to be defined, but expect() was not called.", this.stack );
+               } else if ( this.expected != null && this.expected != this.assertions.length ) {
                        QUnit.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack );
                } else if ( this.expected == null && !this.assertions.length ) {
                        QUnit.pushFailure( "Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.", this.stack );
                }
 
                var assertion, a, b, i, li, ol,
+                       test = this,
                        good = 0,
                        bad = 0,
                        tests = id( "qunit-tests" );
@@ -203,11 +213,6 @@ Test.prototype = {
                        b = document.createElement( "strong" );
                        b.innerHTML = this.name + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>";
 
-                       // `a` initialized at top of scope
-                       a = document.createElement( "a" );
-                       a.innerHTML = "Rerun";
-                       a.href = QUnit.url({ filter: getText([b]).replace( /\([^)]+\)$/, "" ).replace( /(^\s*|\s*$)/g, "" ) });
-
                        addEvent(b, "click", function() {
                                var next = b.nextSibling.nextSibling,
                                        display = next.style.display;
@@ -220,9 +225,7 @@ Test.prototype = {
                                        target = target.parentNode;
                                }
                                if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
-                                       window.location = QUnit.url({
-                                               filter: getText([target]).replace( /\([^)]+\)$/, "" ).replace( /(^\s*|\s*$)/g, "" )
-                                       });
+                                       window.location = QUnit.url({ testNumber: test.testNumber });
                                }
                        });
 
@@ -230,8 +233,9 @@ Test.prototype = {
                        li = id( this.id );
                        li.className = bad ? "fail" : "pass";
                        li.removeChild( li.firstChild );
+                       a = li.firstChild;
                        li.appendChild( b );
-                       li.appendChild( a );
+                       li.appendChild ( a );
                        li.appendChild( ol );
 
                } else {
@@ -253,6 +257,8 @@ Test.prototype = {
                });
 
                QUnit.reset();
+
+               config.current = undefined;
        },
 
        queue: function() {
@@ -291,6 +297,7 @@ Test.prototype = {
        }
 };
 
+// Root QUnit object.
 // `QUnit` initialized at top of scope
 QUnit = {
 
@@ -322,14 +329,21 @@ QUnit = {
                        name = "<span class='module-name'>" + config.currentModule + "</span>: " + name;
                }
 
-               if ( !validTest(config.currentModule + ": " + testName) ) {
+               test = new Test({
+                       name: name,
+                       testName: testName,
+                       expected: expected,
+                       async: async,
+                       callback: callback,
+                       module: config.currentModule,
+                       moduleTestEnvironment: config.currentModuleTestEnviroment,
+                       stack: sourceFromStacktrace( 2 )
+               });
+
+               if ( !validTest( test ) ) {
                        return;
                }
 
-               test = new Test( name, testName, expected, async, callback );
-               test.module = config.currentModule;
-               test.moduleTestEnvironment = config.currentModuleTestEnviroment;
-               test.stack = sourceFromStacktrace( 2 );
                test.queue();
        },
 
@@ -338,8 +352,59 @@ QUnit = {
                config.current.expected = asserts;
        },
 
-       // Asserts true.
-       // @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
+       start: function( count ) {
+               config.semaphore -= count || 1;
+               // don't start until equal number of stop-calls
+               if ( config.semaphore > 0 ) {
+                       return;
+               }
+               // ignore if start is called more often then stop
+               if ( config.semaphore < 0 ) {
+                       config.semaphore = 0;
+               }
+               // A slight delay, to avoid any current callbacks
+               if ( defined.setTimeout ) {
+                       window.setTimeout(function() {
+                               if ( config.semaphore > 0 ) {
+                                       return;
+                               }
+                               if ( config.timeout ) {
+                                       clearTimeout( config.timeout );
+                               }
+
+                               config.blocking = false;
+                               process( true );
+                       }, 13);
+               } else {
+                       config.blocking = false;
+                       process( true );
+               }
+       },
+
+       stop: function( count ) {
+               config.semaphore += count || 1;
+               config.blocking = true;
+
+               if ( config.testTimeout && defined.setTimeout ) {
+                       clearTimeout( config.timeout );
+                       config.timeout = window.setTimeout(function() {
+                               QUnit.ok( false, "Test timed out" );
+                               config.semaphore = 1;
+                               QUnit.start();
+                       }, config.testTimeout );
+               }
+       }
+};
+
+// Asssert helpers
+// All of these must call either QUnit.push() or manually do:
+// - runLoggingCallbacks( "log", .. );
+// - config.current.assertions.push({ .. });
+QUnit.assert = {
+       /**
+        * Asserts rough true-ish result.
+        * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
+        */
        ok: function( result, msg ) {
                if ( !config.current ) {
                        throw new Error( "ok() assertion outside test context, was " + sourceFromStacktrace(2) );
@@ -369,8 +434,11 @@ QUnit = {
                });
        },
 
-       // Checks that the first two arguments are equal, with an optional message. Prints out both actual and expected values.
-       // @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes." );
+       /**
+        * Assert that the first two arguments are equal, with an optional message.
+        * Prints out both actual and expected values.
+        * @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" );
+        */
        equal: function( actual, expected, message ) {
                QUnit.push( expected == actual, actual, expected, message );
        },
@@ -404,11 +472,13 @@ QUnit = {
                        expected = null;
                }
 
+               config.current.ignoreGlobalErrors = true;
                try {
                        block.call( config.current.testEnvironment );
                } catch (e) {
                        actual = e;
                }
+               config.current.ignoreGlobalErrors = false;
 
                if ( actual ) {
                        // we don't want to validate thrown error
@@ -426,51 +496,22 @@ QUnit = {
                        }
                }
 
-               QUnit.ok( ok, message );
-       },
-
-       start: function( count ) {
-               config.semaphore -= count || 1;
-               // don't start until equal number of stop-calls
-               if ( config.semaphore > 0 ) {
-                       return;
-               }
-               // ignore if start is called more often then stop
-               if ( config.semaphore < 0 ) {
-                       config.semaphore = 0;
-               }
-               // A slight delay, to avoid any current callbacks
-               if ( defined.setTimeout ) {
-                       window.setTimeout(function() {
-                               if ( config.semaphore > 0 ) {
-                                       return;
-                               }
-                               if ( config.timeout ) {
-                                       clearTimeout( config.timeout );
-                               }
-
-                               config.blocking = false;
-                               process( true );
-                       }, 13);
-               } else {
-                       config.blocking = false;
-                       process( true );
-               }
-       },
+               QUnit.push( ok, actual, null, message );
+       }
+};
 
-       stop: function( count ) {
-               config.semaphore += count || 1;
-               config.blocking = true;
+// @deprecated: Kept assertion helpers in root for backwards compatibility
+extend( QUnit, QUnit.assert );
 
-               if ( config.testTimeout && defined.setTimeout ) {
-                       clearTimeout( config.timeout );
-                       config.timeout = window.setTimeout(function() {
-                               QUnit.ok( false, "Test timed out" );
-                               config.semaphore = 1;
-                               QUnit.start();
-                       }, config.testTimeout );
-               }
-       }
+/**
+ * @deprecated: Kept for backwards compatibility
+ * next step: remove entirely
+ */
+QUnit.equals = function() {
+       QUnit.push( false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead" );
+};
+QUnit.same = function() {
+       QUnit.push( false, false, false, "QUnit.same has been deprecated since 2009 (e88049a0), use QUnit.deepEqual instead" );
 };
 
 // We want access to the constructor's prototype
@@ -482,17 +523,11 @@ QUnit = {
        QUnit.constructor = F;
 }());
 
-// deprecated; still export them to window to provide clear error messages
-// next step: remove entirely
-QUnit.equals = function() {
-       QUnit.push( false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead" );
-};
-QUnit.same = function() {
-       QUnit.push( false, false, false, "QUnit.same has been deprecated since 2009 (e88049a0), use QUnit.deepEqual instead" );
-};
-
-// Maintain internal state
-// `config` initialized at top of scope
+/**
+ * Config object: Maintain internal state
+ * Later exposed as QUnit.config
+ * `config` initialized at top of scope
+ */
 config = {
        // The queue of tests to run
        queue: [],
@@ -511,6 +546,9 @@ config = {
        // by default, modify document.title when suite is done
        altertitle: true,
 
+       // when enabled, all tests must call expect()
+       requireExpects: false,
+
        urlConfig: [ "noglobals", "notrycatch" ],
 
        // logging callback queues
@@ -523,7 +561,7 @@ config = {
        moduleDone: []
 };
 
-// Load paramaters
+// Initialize more QUnit.config and QUnit.urlParams
 (function() {
        var i,
                location = window.location || { search: "", protocol: "file:" },
@@ -543,20 +581,30 @@ config = {
        }
 
        QUnit.urlParams = urlParams;
+
+       // String search anywhere in moduleName+testName
        config.filter = urlParams.filter;
 
+       // Exact match of the module name
+       config.module = urlParams.module;
+
+       config.testNumber = parseInt( urlParams.testNumber, 10 ) || null;
+
        // Figure out if we're running the tests from a server or not
        QUnit.isLocal = location.protocol === "file:";
 }());
 
-// Expose the API as global variables, unless an 'exports' object exists,
-// in that case we assume we're in CommonJS - export everything at the end
+// Export global variables, unless an 'exports' object exists,
+// in that case we assume we're in CommonJS (dealt with on the bottom of the script)
 if ( typeof exports === "undefined" ) {
        extend( window, QUnit );
+
+       // Expose QUnit object
        window.QUnit = QUnit;
 }
 
-// define these after exposing globals to keep them in these QUnit namespace only
+// Extend QUnit object,
+// these after set here because they should not be exposed as global functions
 extend( QUnit, {
        config: config,
 
@@ -723,6 +771,10 @@ extend( QUnit, {
        },
 
        pushFailure: function( message, source ) {
+               if ( !config.current ) {
+                       throw new Error( "pushFailure() assertion outside test context, was " + sourceFromStacktrace(2) );
+               }
+
                var output,
                        details = {
                                result: false,
@@ -764,25 +816,37 @@ extend( QUnit, {
        extend: extend,
        id: id,
        addEvent: addEvent
+       // load, equiv, jsDump, diff: Attached later
 });
 
-// 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.
+/**
+ * @deprecated: Created for backwards compatibility with test runner that set the hook function
+ * into QUnit.{hook}, instead of invoking it and passing the hook function.
+ * QUnit.constructor is set to the empty F() above so that we can add to it's prototype here.
+ * Doing this allows us to tell if the following methods have been overwritten on the actual
+ * QUnit object.
+ */
 extend( QUnit.constructor.prototype, {
+
        // Logging callbacks; all receive a single argument with the listed properties
        // run test/logs.html for any related changes
        begin: registerLoggingCallback( "begin" ),
+
        // done: { failed, passed, total, runtime }
        done: registerLoggingCallback( "done" ),
+
        // log: { result, actual, expected, message }
        log: registerLoggingCallback( "log" ),
+
        // testStart: { name }
        testStart: registerLoggingCallback( "testStart" ),
+
        // testDone: { name, failed, passed, total }
        testDone: registerLoggingCallback( "testDone" ),
+
        // moduleStart: { name }
        moduleStart: registerLoggingCallback( "moduleStart" ),
+
        // moduleDone: { name, failed, passed, total }
        moduleDone: registerLoggingCallback( "moduleDone" )
 });
@@ -884,15 +948,36 @@ 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 ) {
-               QUnit.pushFailure( message, file + ":" + line );
-       } else {
-               QUnit.test( "global failure", function() {
-                       QUnit.pushFailure( message, file + ":" + line );
-               });
+// `onErrorFnPrev` initialized at top of scope
+// Preserve other handlers
+onErrorFnPrev = window.onerror;
+
+// Cover uncaught exceptions
+// Returning true will surpress the default browser handler,
+// returning false will let it run.
+window.onerror = function ( error, filePath, linerNr ) {
+       var ret = false;
+       if ( onErrorFnPrev ) {
+               ret = onErrorFnPrev( error, filePath, linerNr );
        }
+
+       // Treat return value as window.onerror itself does,
+       // Only do our handling if not surpressed.
+       if ( ret !== true ) {
+               if ( QUnit.config.current ) {
+                       if ( QUnit.config.current.ignoreGlobalErrors ) {
+                               return true;
+                       }
+                       QUnit.pushFailure( error, filePath + ":" + linerNr );
+               } else {
+                       QUnit.test( "global failure", function() {
+                               QUnit.pushFailure( error, filePath + ":" + linerNr );
+                       });
+               }
+               return false;
+       }
+
+       return ret;
 };
 
 function done() {
@@ -962,39 +1047,46 @@ function done() {
        });
 }
 
-function validTest( name ) {
-       var not,
-               filter = config.filter,
-               run = false;
+/** @return Boolean: true if this test should be ran */
+function validTest( test ) {
+       var include,
+               filter = config.filter && config.filter.toLowerCase(),
+               module = config.module,
+               fullName = (test.module + ": " + test.testName).toLowerCase();
 
-       if ( !filter ) {
-               return true;
+       if ( config.testNumber ) {
+               return test.testNumber === config.testNumber;
        }
 
-       not = filter.charAt( 0 ) === "!";
+       if ( module && test.module !== module ) {
+               return false;
+       }
 
-       if ( not ) {
-               filter = filter.slice( 1 );
+       if ( !filter ) {
+               return true;
        }
 
-       if ( name.indexOf( filter ) !== -1 ) {
-               return !not;
+       include = filter.charAt( 0 ) !== "!";
+       if ( !include ) {
+               filter = filter.slice( 1 );
        }
 
-       if ( not ) {
-               run = true;
+       // If the filter matches, we need to honour include
+       if ( fullName.indexOf( filter ) !== -1 ) {
+               return include;
        }
 
-       return run;
+       // Otherwise, do the opposite
+       return !include;
 }
 
 // so far supports only Firefox, Chrome and Opera (buggy), Safari (for real exceptions)
 // Later Safari and IE10 are supposed to support error.stack as well
 // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
 function extractStacktrace( e, offset ) {
-       offset = offset || 3;
+       offset = offset === undefined ? 3 : offset;
 
-       var stack;
+       var stack, include, i, regex;
 
        if ( e.stacktrace ) {
                // Opera
@@ -1005,6 +1097,18 @@ function extractStacktrace( e, offset ) {
                if (/^error$/i.test( stack[0] ) ) {
                        stack.shift();
                }
+               if ( fileName ) {
+                       include = [];
+                       for ( i = offset; i < stack.length; i++ ) {
+                               if ( stack[ i ].indexOf( fileName ) != -1 ) {
+                                       break;
+                               }
+                               include.push( stack[ i ] );
+                       }
+                       if ( include.length ) {
+                               return include.join( "\n" );
+                       }
+               }
                return stack[ offset ];
        } else if ( e.sourceURL ) {
                // Safari, PhantomJS
@@ -1419,11 +1523,11 @@ QUnit.jsDump = (function() {
                                        type = "null";
                                } else if ( typeof obj === "undefined" ) {
                                        type = "undefined";
-                               } else if ( QUnit.is( "RegExp", obj) ) {
+                               } else if ( QUnit.is( "regexp", obj) ) {
                                        type = "regexp";
-                               } else if ( QUnit.is( "Date", obj) ) {
+                               } else if ( QUnit.is( "date", obj) ) {
                                        type = "date";
-                               } else if ( QUnit.is( "Function", obj) ) {
+                               } else if ( QUnit.is( "function", obj) ) {
                                        type = "function";
                                } else if ( typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined" ) {
                                        type = "window";