From: Jörn Zaefferer Date: Wed, 13 Jun 2012 22:21:54 +0000 (+0200) Subject: Update QUnit to 1.8.0 X-Git-Tag: 1.9.0-beta.1~50 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=679f7f31721dc6004aadba10bf82e41dcd84a3ff;p=jquery-ui.git Update QUnit to 1.8.0 --- diff --git a/external/qunit.css b/external/qunit.css index 23235ec84..5684a4485 100644 --- a/external/qunit.css +++ b/external/qunit.css @@ -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 * diff --git a/external/qunit.js b/external/qunit.js index 2c277fab5..c1570c252 100644 --- a/external/qunit.js +++ b/external/qunit.js @@ -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 + " (" + bad + ", " + good + ", " + this.assertions.length + ")"; - // `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 = "" + config.currentModule + ": " + 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";