From: Oleg Gaidarenko Date: Fri, 20 May 2016 10:38:12 +0000 (+0300) Subject: Revert "Build: Bump qunit version" X-Git-Tag: 2.2.4~2 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=4ed8b49453724195f0bb4d502c546e3ba26ec01d;p=jquery.git Revert "Build: Bump qunit version" This reverts commit afe2727c5610ba9cd5ffbec5c34f15d38538b05f. --- diff --git a/external/qunit/LICENSE.txt b/external/qunit/LICENSE.txt index a3a9b5120..fb928a543 100644 --- a/external/qunit/LICENSE.txt +++ b/external/qunit/LICENSE.txt @@ -30,6 +30,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ==== -All files located in the node_modules directory are externally maintained -libraries used by this software which have their own licenses; we -recommend you read them, as their terms may differ from the terms above. +All files located in the node_modules and external directories are +externally maintained libraries used by this software which have their +own licenses; we recommend you read them, as their terms may differ from +the terms above. diff --git a/external/qunit/qunit.css b/external/qunit/qunit.css index ae68fc412..0eb0b0171 100644 --- a/external/qunit/qunit.css +++ b/external/qunit/qunit.css @@ -1,27 +1,27 @@ /*! - * QUnit 1.23.1 - * https://qunitjs.com/ + * QUnit 1.17.1 + * http://qunitjs.com/ * * Copyright jQuery Foundation and other contributors * Released under the MIT license - * https://jquery.org/license + * http://jquery.org/license * - * Date: 2016-04-12T17:29Z + * Date: 2015-01-20T19:39Z */ /** Font Family and Sizes */ -#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult { +#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; } -#qunit-testrunner-toolbar, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } +#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } #qunit-tests { font-size: smaller; } /** Resets */ -#qunit-tests, #qunit-header, #qunit-banner, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter { +#qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter { margin: 0; padding: 0; } @@ -68,12 +68,6 @@ overflow: hidden; } -#qunit-filteredTest { - padding: 0.5em 1em 0.5em 1em; - background-color: #F4FF77; - color: #366097; -} - #qunit-userAgent { padding: 0.5em 1em 0.5em 1em; background-color: #2B81AF; @@ -120,19 +114,9 @@ display: list-item; } -#qunit-tests.hidepass { - position: relative; -} - #qunit-tests.hidepass li.running, #qunit-tests.hidepass li.pass { - visibility: hidden; - position: absolute; - width: 0; - height: 0; - padding: 0; - border: 0; - margin: 0; + display: none; } #qunit-tests li strong { @@ -148,11 +132,6 @@ color: #C2CCD1; text-decoration: none; } - -#qunit-tests li p a { - padding: 0.25em; - color: #6B6464; -} #qunit-tests li a:hover, #qunit-tests li a:focus { color: #000; @@ -172,10 +151,6 @@ border-radius: 5px; } -.qunit-source { - margin: 0.6em 0 0.3em; -} - .qunit-collapsed { display: none; } diff --git a/external/qunit/qunit.js b/external/qunit/qunit.js index 5df0822ea..006ca4747 100644 --- a/external/qunit/qunit.js +++ b/external/qunit/qunit.js @@ -1,259 +1,146 @@ /*! - * QUnit 1.23.1 - * https://qunitjs.com/ + * QUnit 1.17.1 + * http://qunitjs.com/ * * Copyright jQuery Foundation and other contributors * Released under the MIT license - * https://jquery.org/license + * http://jquery.org/license * - * Date: 2016-04-12T17:29Z + * Date: 2015-01-20T19:39Z */ -( function( global ) { +(function( window ) { -var QUnit = {}; - -var Date = global.Date; -var now = Date.now || function() { - return new Date().getTime(); -}; - -var setTimeout = global.setTimeout; -var clearTimeout = global.clearTimeout; - -// Store a local window from the global to allow direct references. -var window = global.window; - -var defined = { - document: window && window.document !== undefined, - setTimeout: setTimeout !== undefined, - sessionStorage: ( function() { - var x = "qunit-test-string"; - try { - sessionStorage.setItem( x, x ); - sessionStorage.removeItem( x ); - return true; - } catch ( e ) { - return false; - } - }() ) -}; - -var fileName = ( sourceFromStacktrace( 0 ) || "" ).replace( /(:\d+)+\)?/, "" ).replace( /.+\//, "" ); -var globalStartCalled = false; -var runStarted = false; - -var toString = Object.prototype.toString, - hasOwn = Object.prototype.hasOwnProperty; - -// Returns a new Array with the elements that are in a but not in b -function diff( a, b ) { - var i, j, - result = a.slice(); - - for ( i = 0; i < result.length; i++ ) { - for ( j = 0; j < b.length; j++ ) { - if ( result[ i ] === b[ j ] ) { - result.splice( i, 1 ); - i--; - break; - } - } - } - return result; -} - -// From jquery.js -function inArray( elem, array ) { - if ( array.indexOf ) { - return array.indexOf( elem ); - } - - for ( var i = 0, length = array.length; i < length; i++ ) { - if ( array[ i ] === elem ) { - return i; - } - } - - return -1; -} - -/** - * Makes a clone of an object using only Array or Object as base, - * and copies over the own enumerable properties. - * - * @param {Object} obj - * @return {Object} New object with only the own properties (recursively). - */ -function objectValues ( obj ) { - var key, val, - vals = QUnit.is( "array", obj ) ? [] : {}; - for ( key in obj ) { - if ( hasOwn.call( obj, key ) ) { - val = obj[ key ]; - vals[ key ] = val === Object( val ) ? objectValues( val ) : val; - } - } - return vals; -} - -function extend( a, b, undefOnly ) { - for ( var prop in b ) { - if ( hasOwn.call( b, prop ) ) { - - // Avoid "Member not found" error in IE8 caused by messing with window.constructor - // This block runs on every environment, so `global` is being used instead of `window` - // to avoid errors on node. - if ( prop !== "constructor" || a !== global ) { - if ( b[ prop ] === undefined ) { - delete a[ prop ]; - } else if ( !( undefOnly && typeof a[ prop ] !== "undefined" ) ) { - a[ prop ] = b[ prop ]; - } +var QUnit, + config, + onErrorFnPrev, + loggingCallbacks = {}, + fileName = ( sourceFromStacktrace( 0 ) || "" ).replace( /(:\d+)+\)?/, "" ).replace( /.+\//, "" ), + toString = Object.prototype.toString, + hasOwn = Object.prototype.hasOwnProperty, + // Keep a local reference to Date (GH-283) + Date = window.Date, + now = Date.now || function() { + return new Date().getTime(); + }, + globalStartCalled = false, + runStarted = false, + setTimeout = window.setTimeout, + clearTimeout = window.clearTimeout, + defined = { + document: window.document !== undefined, + setTimeout: window.setTimeout !== undefined, + sessionStorage: (function() { + var x = "qunit-test-string"; + try { + sessionStorage.setItem( x, x ); + sessionStorage.removeItem( x ); + return true; + } catch ( e ) { + return false; } - } - } - - return a; -} - -function objectType( obj ) { - if ( typeof obj === "undefined" ) { - return "undefined"; - } - - // Consider: typeof null === object - if ( obj === null ) { - return "null"; - } - - var match = toString.call( obj ).match( /^\[object\s(.*)\]$/ ), - type = match && match[ 1 ]; - - switch ( type ) { - case "Number": - if ( isNaN( obj ) ) { - return "nan"; + }()) + }, + /** + * Provides a normalized error string, correcting an issue + * with IE 7 (and prior) where Error.prototype.toString is + * not properly implemented + * + * Based on http://es5.github.com/#x15.11.4.4 + * + * @param {String|Error} error + * @return {String} error message + */ + errorString = function( error ) { + var name, message, + errorString = error.toString(); + if ( errorString.substring( 0, 7 ) === "[object" ) { + name = error.name ? error.name.toString() : "Error"; + message = error.message ? error.message.toString() : ""; + if ( name && message ) { + return name + ": " + message; + } else if ( name ) { + return name; + } else if ( message ) { + return message; + } else { + return "Error"; } - return "number"; - case "String": - case "Boolean": - case "Array": - case "Set": - case "Map": - case "Date": - case "RegExp": - case "Function": - case "Symbol": - return type.toLowerCase(); - } - if ( typeof obj === "object" ) { - return "object"; - } -} - -// Safe object type checking -function is( type, obj ) { - return QUnit.objectType( obj ) === type; -} - -// Doesn't support IE6 to IE9, it will return undefined on these browsers -// See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack -function extractStacktrace( e, offset ) { - offset = offset === undefined ? 4 : offset; - - var stack, include, i; - - if ( e.stack ) { - stack = e.stack.split( "\n" ); - if ( /^error$/i.test( stack[ 0 ] ) ) { - stack.shift(); + } else { + return errorString; } - 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" ); + }, + /** + * Makes a clone of an object using only Array or Object as base, + * and copies over the own enumerable properties. + * + * @param {Object} obj + * @return {Object} New object with only the own properties (recursively). + */ + objectValues = function( obj ) { + var key, val, + vals = QUnit.is( "array", obj ) ? [] : {}; + for ( key in obj ) { + if ( hasOwn.call( obj, key ) ) { + val = obj[ key ]; + vals[ key ] = val === Object( val ) ? objectValues( val ) : val; } } - return stack[ offset ]; - - // Support: Safari <=6 only - } else if ( e.sourceURL ) { - - // Exclude useless self-reference for generated Error objects - if ( /qunit.js$/.test( e.sourceURL ) ) { - return; - } - - // For actual exceptions, this is useful - return e.sourceURL + ":" + e.line; - } -} - -function sourceFromStacktrace( offset ) { - var error = new Error(); - - // Support: Safari <=7 only, IE <=10 - 11 only - // Not all browsers generate the `stack` property for `new Error()`, see also #636 - if ( !error.stack ) { - try { - throw error; - } catch ( err ) { - error = err; - } - } + return vals; + }; - return extractStacktrace( error, offset ); -} +QUnit = {}; /** * Config object: Maintain internal state * Later exposed as QUnit.config * `config` initialized at top of scope */ -var config = { - +config = { // The queue of tests to run queue: [], - // Block until document ready + // block until document ready blocking: true, - // By default, run previously failed tests first + // by default, run previously failed tests first // very useful in combination with "Hide passed tests" checked reorder: true, - // By default, modify document.title when suite is done + // by default, modify document.title when suite is done altertitle: true, - // HTML Reporter: collapse every test except the first failing test - // If false, all failing tests will be expanded - collapse: true, - - // By default, scroll to top of the page when suite is done + // by default, scroll to top of the page when suite is done scrolltop: true, - // Depth up-to which object will be dumped - maxDepth: 5, - - // When enabled, all tests must call expect() + // when enabled, all tests must call expect() requireExpects: false, - // Placeholder for user-configurable form-exposed URL parameters - urlConfig: [], + // add checkboxes that are persisted in the query-string + // when enabled, the id is set to `true` as a `QUnit.config` property + urlConfig: [ + { + id: "hidepassed", + label: "Hide passed tests", + tooltip: "Only show tests and assertions that fail. Stored as query-strings." + }, + { + id: "noglobals", + label: "Check for Globals", + tooltip: "Enabling this will test if any test introduces new properties on the " + + "`window` object. Stored as query-strings." + }, + { + id: "notrycatch", + label: "No try-catch", + tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " + + "exceptions in IE reasonable. Stored as query-strings." + } + ], // Set of all modules. modules: [], - // Stack of nested modules - moduleStack: [], - // The first unnamed module currentModule: { name: "", @@ -266,139 +153,63 @@ var config = { // Push a loose unnamed module to the modules collection config.modules.push( config.currentModule ); -var loggingCallbacks = {}; - -// Register logging callbacks -function registerLoggingCallbacks( obj ) { - var i, l, key, - callbackNames = [ "begin", "done", "log", "testStart", "testDone", - "moduleStart", "moduleDone" ]; - - function registerLoggingCallback( key ) { - var loggingCallback = function( callback ) { - if ( objectType( callback ) !== "function" ) { - throw new Error( - "QUnit logging methods require a callback function as their first parameters." - ); +// Initialize more QUnit.config and QUnit.urlParams +(function() { + var i, current, + location = window.location || { search: "", protocol: "file:" }, + params = location.search.slice( 1 ).split( "&" ), + length = params.length, + urlParams = {}; + + if ( params[ 0 ] ) { + for ( i = 0; i < length; i++ ) { + current = params[ i ].split( "=" ); + current[ 0 ] = decodeURIComponent( current[ 0 ] ); + + // allow just a key to turn on a flag, e.g., test.html?noglobals + current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true; + if ( urlParams[ current[ 0 ] ] ) { + urlParams[ current[ 0 ] ] = [].concat( urlParams[ current[ 0 ] ], current[ 1 ] ); + } else { + urlParams[ current[ 0 ] ] = current[ 1 ]; } - - config.callbacks[ key ].push( callback ); - }; - - // DEPRECATED: This will be removed on QUnit 2.0.0+ - // Stores the registered functions allowing restoring - // at verifyLoggingCallbacks() if modified - loggingCallbacks[ key ] = loggingCallback; - - return loggingCallback; - } - - for ( i = 0, l = callbackNames.length; i < l; i++ ) { - key = callbackNames[ i ]; - - // Initialize key collection of logging callback - if ( objectType( config.callbacks[ key ] ) === "undefined" ) { - config.callbacks[ key ] = []; } - - obj[ key ] = registerLoggingCallback( key ); } -} - -function runLoggingCallbacks( key, args ) { - var i, l, callbacks; - callbacks = config.callbacks[ key ]; - for ( i = 0, l = callbacks.length; i < l; i++ ) { - callbacks[ i ]( args ); + if ( urlParams.filter === true ) { + delete urlParams.filter; } -} - -// DEPRECATED: This will be removed on 2.0.0+ -// This function verifies if the loggingCallbacks were modified by the user -// If so, it will restore it, assign the given callback and print a console warning -function verifyLoggingCallbacks() { - var loggingCallback, userCallback; - - for ( loggingCallback in loggingCallbacks ) { - if ( QUnit[ loggingCallback ] !== loggingCallbacks[ loggingCallback ] ) { - userCallback = QUnit[ loggingCallback ]; + QUnit.urlParams = urlParams; - // Restore the callback function - QUnit[ loggingCallback ] = loggingCallbacks[ loggingCallback ]; + // String search anywhere in moduleName+testName + config.filter = urlParams.filter; - // Assign the deprecated given callback - QUnit[ loggingCallback ]( userCallback ); + config.testId = []; + if ( urlParams.testId ) { - if ( global.console && global.console.warn ) { - global.console.warn( - "QUnit." + loggingCallback + " was replaced with a new value.\n" + - "Please, check out the documentation on how to apply logging callbacks.\n" + - "Reference: https://api.qunitjs.com/category/callbacks/" - ); - } + // Ensure that urlParams.testId is an array + urlParams.testId = [].concat( urlParams.testId ); + for ( i = 0; i < urlParams.testId.length; i++ ) { + config.testId.push( urlParams.testId[ i ] ); } } -} - -( function() { - if ( !defined.document ) { - return; - } - - // `onErrorFnPrev` initialized at top of scope - // Preserve other handlers - var onErrorFnPrev = window.onerror; - - // Cover uncaught exceptions - // Returning true will suppress 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 suppressed. - if ( ret !== true ) { - if ( QUnit.config.current ) { - if ( QUnit.config.current.ignoreGlobalErrors ) { - return true; - } - QUnit.pushFailure( error, filePath + ":" + linerNr ); - } else { - QUnit.test( "global failure", extend( function() { - QUnit.pushFailure( error, filePath + ":" + linerNr ); - }, { validTest: true } ) ); - } - return false; - } - - return ret; - }; -}() ); - -// Figure out if we're running the tests from a server or not -QUnit.isLocal = !( defined.document && window.location.protocol !== "file:" ); -// Expose the current QUnit version -QUnit.version = "1.23.1"; + // Figure out if we're running the tests from a server or not + QUnit.isLocal = location.protocol === "file:"; +}()); +// Root QUnit object. +// `QUnit` initialized at top of scope extend( QUnit, { - // Call on start of module test to prepend name to all tests - module: function( name, testEnvironment, executeNow ) { - var module, moduleFns; - var currentModule = config.currentModule; - - if ( arguments.length === 2 ) { - if ( objectType( testEnvironment ) === "function" ) { - executeNow = testEnvironment; - testEnvironment = undefined; - } - } + // call on start of module test to prepend name to all tests + module: function( name, testEnvironment ) { + var currentModule = { + name: name, + testEnvironment: testEnvironment, + tests: [] + }; // DEPRECATED: handles setup/teardown functions, // beforeEach and afterEach should be used instead @@ -411,62 +222,46 @@ extend( QUnit, { delete testEnvironment.teardown; } - module = createModule(); - - moduleFns = { - beforeEach: setHook( module, "beforeEach" ), - afterEach: setHook( module, "afterEach" ) - }; + config.modules.push( currentModule ); + config.currentModule = currentModule; + }, - if ( objectType( executeNow ) === "function" ) { - config.moduleStack.push( module ); - setCurrentModule( module ); - executeNow.call( module.testEnvironment, moduleFns ); - config.moduleStack.pop(); - module = module.parentModule || currentModule; + // DEPRECATED: QUnit.asyncTest() will be removed in QUnit 2.0. + asyncTest: function( testName, expected, callback ) { + if ( arguments.length === 2 ) { + callback = expected; + expected = null; } - setCurrentModule( module ); - - function createModule() { - var parentModule = config.moduleStack.length ? - config.moduleStack.slice( -1 )[ 0 ] : null; - var moduleName = parentModule !== null ? - [ parentModule.name, name ].join( " > " ) : name; - var module = { - name: moduleName, - parentModule: parentModule, - tests: [], - moduleId: generateHash( moduleName ) - }; + QUnit.test( testName, expected, callback, true ); + }, - var env = {}; - if ( parentModule ) { - extend( env, parentModule.testEnvironment ); - delete env.beforeEach; - delete env.afterEach; - } - extend( env, testEnvironment ); - module.testEnvironment = env; + test: function( testName, expected, callback, async ) { + var test; - config.modules.push( module ); - return module; + if ( arguments.length === 2 ) { + callback = expected; + expected = null; } - function setCurrentModule( module ) { - config.currentModule = module; - } + test = new Test({ + testName: testName, + expected: expected, + async: async, + callback: callback + }); + test.queue(); }, - // DEPRECATED: QUnit.asyncTest() will be removed in QUnit 2.0. - asyncTest: asyncTest, - - test: test, + skip: function( testName ) { + var test = new Test({ + testName: testName, + skip: true + }); - skip: skip, - - only: only, + test.queue(); + }, // DEPRECATED: The functionality of QUnit.start() will be altered in QUnit 2.0. // In QUnit 2.0, invoking it will ONLY affect the `QUnit.config.autostart` blocking behavior. @@ -494,23 +289,12 @@ extend( QUnit, { // If a test is running, adjust its semaphore config.current.semaphore -= count || 1; - // If semaphore is non-numeric, throw error - if ( isNaN( config.current.semaphore ) ) { - config.current.semaphore = 0; - - QUnit.pushFailure( - "Called start() with a non-numeric decrement.", - sourceFromStacktrace( 2 ) - ); - return; - } - // Don't start until equal number of stop-calls if ( config.current.semaphore > 0 ) { return; } - // Throw an Error if start is called more often than stop + // throw an Error if start is called more often than stop if ( config.current.semaphore < 0 ) { config.current.semaphore = 0; @@ -541,9 +325,43 @@ extend( QUnit, { config: config, - is: is, + // Safe object type checking + is: function( type, obj ) { + return QUnit.objectType( obj ) === type; + }, + + objectType: function( obj ) { + if ( typeof obj === "undefined" ) { + return "undefined"; + } + + // Consider: typeof null === object + if ( obj === null ) { + return "null"; + } + + var match = toString.call( obj ).match( /^\[object\s(.*)\]$/ ), + type = match && match[ 1 ] || ""; - objectType: objectType, + switch ( type ) { + case "Number": + if ( isNaN( obj ) ) { + return "nan"; + } + return "number"; + case "String": + case "Boolean": + case "Array": + case "Date": + case "RegExp": + case "Function": + return type.toLowerCase(); + } + if ( typeof obj === "object" ) { + return "object"; + } + return undefined; + }, extend: extend, @@ -565,50 +383,176 @@ extend( QUnit, { if ( config.autostart ) { resumeProcessing(); } - }, - - stack: function( offset ) { - offset = ( offset || 0 ) + 2; - return sourceFromStacktrace( offset ); } -} ); +}); -registerLoggingCallbacks( QUnit ); +// Register logging callbacks +(function() { + var i, l, key, + callbacks = [ "begin", "done", "log", "testStart", "testDone", + "moduleStart", "moduleDone" ]; -function begin() { - var i, l, - modulesLog = []; + function registerLoggingCallback( key ) { + var loggingCallback = function( callback ) { + if ( QUnit.objectType( callback ) !== "function" ) { + throw new Error( + "QUnit logging methods require a callback function as their first parameters." + ); + } - // If the test run hasn't officially begun yet - if ( !config.started ) { + config.callbacks[ key ].push( callback ); + }; - // Record the time of the test run's beginning - config.started = now(); + // DEPRECATED: This will be removed on QUnit 2.0.0+ + // Stores the registered functions allowing restoring + // at verifyLoggingCallbacks() if modified + loggingCallbacks[ key ] = loggingCallback; - verifyLoggingCallbacks(); + return loggingCallback; + } - // Delete the loose unnamed module if unused. - if ( config.modules[ 0 ].name === "" && config.modules[ 0 ].tests.length === 0 ) { - config.modules.shift(); + for ( i = 0, l = callbacks.length; i < l; i++ ) { + key = callbacks[ i ]; + + // Initialize key collection of logging callback + if ( QUnit.objectType( config.callbacks[ key ] ) === "undefined" ) { + config.callbacks[ key ] = []; } - // Avoid unnecessary information by not logging modules' test environments - for ( i = 0, l = config.modules.length; i < l; i++ ) { - modulesLog.push( { - name: config.modules[ i ].name, - tests: config.modules[ i ].tests - } ); + QUnit[ key ] = registerLoggingCallback( key ); + } +})(); + +// `onErrorFnPrev` initialized at top of scope +// Preserve other handlers +onErrorFnPrev = window.onerror; + +// Cover uncaught exceptions +// Returning true will suppress 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 suppressed. + if ( ret !== true ) { + if ( QUnit.config.current ) { + if ( QUnit.config.current.ignoreGlobalErrors ) { + return true; + } + QUnit.pushFailure( error, filePath + ":" + linerNr ); + } else { + QUnit.test( "global failure", extend(function() { + QUnit.pushFailure( error, filePath + ":" + linerNr ); + }, { validTest: true } ) ); } + return false; + } - // The test run is officially beginning now - runLoggingCallbacks( "begin", { - totalTests: Test.count, - modules: modulesLog - } ); + return ret; +}; + +function done() { + var runtime, passed; + + config.autorun = true; + + // Log the last module results + if ( config.previousModule ) { + runLoggingCallbacks( "moduleDone", { + name: config.previousModule.name, + tests: config.previousModule.tests, + failed: config.moduleStats.bad, + passed: config.moduleStats.all - config.moduleStats.bad, + total: config.moduleStats.all, + runtime: now() - config.moduleStats.started + }); + } + delete config.previousModule; + + runtime = now() - config.started; + passed = config.stats.all - config.stats.bad; + + runLoggingCallbacks( "done", { + failed: config.stats.bad, + passed: passed, + total: config.stats.all, + runtime: runtime + }); +} + +// Doesn't support IE6 to IE9 +// See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack +function extractStacktrace( e, offset ) { + offset = offset === undefined ? 4 : offset; + + var stack, include, i; + + if ( e.stacktrace ) { + + // Opera 12.x + return e.stacktrace.split( "\n" )[ offset + 3 ]; + } else if ( e.stack ) { + + // Firefox, Chrome, Safari 6+, IE10+, PhantomJS and Node + stack = e.stack.split( "\n" ); + 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 < 6 + // exclude useless self-reference for generated Error objects + if ( /qunit.js$/.test( e.sourceURL ) ) { + return; + } + + // for actual exceptions, this is useful + return e.sourceURL + ":" + e.line; } +} - config.blocking = false; - process( true ); +function sourceFromStacktrace( offset ) { + var e = new Error(); + if ( !e.stack ) { + try { + throw e; + } catch ( err ) { + // This should already be true in most browsers + e = err; + } + } + return extractStacktrace( e, offset ); +} + +function synchronize( callback, last ) { + if ( QUnit.objectType( callback ) === "array" ) { + while ( callback.length ) { + synchronize( callback.shift() ); + } + return; + } + config.queue.push( callback ); + + if ( config.autorun && !config.blocking ) { + process( last ); + } } function process( last ) { @@ -638,21 +582,40 @@ function process( last ) { } } -function pauseProcessing() { - config.blocking = true; +function begin() { + var i, l, + modulesLog = []; - if ( config.testTimeout && defined.setTimeout ) { - clearTimeout( config.timeout ); - config.timeout = setTimeout( function() { - if ( config.current ) { - config.current.semaphore = 0; - QUnit.pushFailure( "Test timed out", sourceFromStacktrace( 2 ) ); - } else { - throw new Error( "Test timed out" ); - } - resumeProcessing(); - }, config.testTimeout ); + // If the test run hasn't officially begun yet + if ( !config.started ) { + + // Record the time of the test run's beginning + config.started = now(); + + verifyLoggingCallbacks(); + + // Delete the loose unnamed module if unused. + if ( config.modules[ 0 ].name === "" && config.modules[ 0 ].tests.length === 0 ) { + config.modules.shift(); + } + + // Avoid unnecessary information by not logging modules' test environments + for ( i = 0, l = config.modules.length; i < l; i++ ) { + modulesLog.push({ + name: config.modules[ i ].name, + tests: config.modules[ i ].tests + }); + } + + // The test run is officially beginning now + runLoggingCallbacks( "begin", { + totalTests: Test.count, + modules: modulesLog + }); } + + config.blocking = false; + process( true ); } function resumeProcessing() { @@ -660,7 +623,7 @@ function resumeProcessing() { // A slight delay to allow this iteration of the event loop to finish (more assertions, etc.) if ( defined.setTimeout ) { - setTimeout( function() { + setTimeout(function() { if ( config.current && config.current.semaphore > 0 ) { return; } @@ -675,83 +638,178 @@ function resumeProcessing() { } } -function done() { - var runtime, passed; - - config.autorun = true; - - // Log the last module results - if ( config.previousModule ) { - runLoggingCallbacks( "moduleDone", { - name: config.previousModule.name, - tests: config.previousModule.tests, - failed: config.moduleStats.bad, - passed: config.moduleStats.all - config.moduleStats.bad, - total: config.moduleStats.all, - runtime: now() - config.moduleStats.started - } ); - } - delete config.previousModule; - - runtime = now() - config.started; - passed = config.stats.all - config.stats.bad; - - runLoggingCallbacks( "done", { - failed: config.stats.bad, - passed: passed, - total: config.stats.all, - runtime: runtime - } ); -} +function pauseProcessing() { + config.blocking = true; -function setHook( module, hookName ) { - if ( module.testEnvironment === undefined ) { - module.testEnvironment = {}; + if ( config.testTimeout && defined.setTimeout ) { + clearTimeout( config.timeout ); + config.timeout = setTimeout(function() { + if ( config.current ) { + config.current.semaphore = 0; + QUnit.pushFailure( "Test timed out", sourceFromStacktrace( 2 ) ); + } else { + throw new Error( "Test timed out" ); + } + resumeProcessing(); + }, config.testTimeout ); } - - return function( callback ) { - module.testEnvironment[ hookName ] = callback; - }; } -var focused = false; -var priorityCount = 0; -var unitSampler; - -function Test( settings ) { - var i, l; - - ++Test.count; - - extend( this, settings ); - this.assertions = []; - this.semaphore = 0; - this.usedAsync = false; - this.module = config.currentModule; - this.stack = sourceFromStacktrace( 3 ); +function saveGlobal() { + config.pollution = []; - // Register unique strings - for ( i = 0, l = this.module.tests; i < l.length; i++ ) { - if ( this.module.tests[ i ].name === this.testName ) { - this.testName += " "; + if ( config.noglobals ) { + for ( var key in window ) { + if ( hasOwn.call( window, key ) ) { + // in Opera sometimes DOM element ids show up here, ignore them + if ( /^qunit-test-output/.test( key ) ) { + continue; + } + config.pollution.push( key ); + } } } +} - this.testId = generateHash( this.module.name, this.testName ); +function checkPollution() { + var newGlobals, + deletedGlobals, + old = config.pollution; - this.module.tests.push( { - name: this.testName, - testId: this.testId - } ); + saveGlobal(); - if ( settings.skip ) { + newGlobals = diff( config.pollution, old ); + if ( newGlobals.length > 0 ) { + QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join( ", " ) ); + } - // Skipped tests will fully ignore any sent callback - this.callback = function() {}; - this.async = false; - this.expected = 0; - } else { - this.assert = new Assert( this ); + deletedGlobals = diff( old, config.pollution ); + if ( deletedGlobals.length > 0 ) { + QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join( ", " ) ); + } +} + +// returns a new Array with the elements that are in a but not in b +function diff( a, b ) { + var i, j, + result = a.slice(); + + for ( i = 0; i < result.length; i++ ) { + for ( j = 0; j < b.length; j++ ) { + if ( result[ i ] === b[ j ] ) { + result.splice( i, 1 ); + i--; + break; + } + } + } + return result; +} + +function extend( a, b, undefOnly ) { + for ( var prop in b ) { + if ( hasOwn.call( b, prop ) ) { + + // Avoid "Member not found" error in IE8 caused by messing with window.constructor + if ( !( prop === "constructor" && a === window ) ) { + if ( b[ prop ] === undefined ) { + delete a[ prop ]; + } else if ( !( undefOnly && typeof a[ prop ] !== "undefined" ) ) { + a[ prop ] = b[ prop ]; + } + } + } + } + + return a; +} + +function runLoggingCallbacks( key, args ) { + var i, l, callbacks; + + callbacks = config.callbacks[ key ]; + for ( i = 0, l = callbacks.length; i < l; i++ ) { + callbacks[ i ]( args ); + } +} + +// DEPRECATED: This will be removed on 2.0.0+ +// This function verifies if the loggingCallbacks were modified by the user +// If so, it will restore it, assign the given callback and print a console warning +function verifyLoggingCallbacks() { + var loggingCallback, userCallback; + + for ( loggingCallback in loggingCallbacks ) { + if ( QUnit[ loggingCallback ] !== loggingCallbacks[ loggingCallback ] ) { + + userCallback = QUnit[ loggingCallback ]; + + // Restore the callback function + QUnit[ loggingCallback ] = loggingCallbacks[ loggingCallback ]; + + // Assign the deprecated given callback + QUnit[ loggingCallback ]( userCallback ); + + if ( window.console && window.console.warn ) { + window.console.warn( + "QUnit." + loggingCallback + " was replaced with a new value.\n" + + "Please, check out the documentation on how to apply logging callbacks.\n" + + "Reference: http://api.qunitjs.com/category/callbacks/" + ); + } + } + } +} + +// from jquery.js +function inArray( elem, array ) { + if ( array.indexOf ) { + return array.indexOf( elem ); + } + + for ( var i = 0, length = array.length; i < length; i++ ) { + if ( array[ i ] === elem ) { + return i; + } + } + + return -1; +} + +function Test( settings ) { + var i, l; + + ++Test.count; + + extend( this, settings ); + this.assertions = []; + this.semaphore = 0; + this.usedAsync = false; + this.module = config.currentModule; + this.stack = sourceFromStacktrace( 3 ); + + // Register unique strings + for ( i = 0, l = this.module.tests; i < l.length; i++ ) { + if ( this.module.tests[ i ].name === this.testName ) { + this.testName += " "; + } + } + + this.testId = generateHash( this.module.name, this.testName ); + + this.module.tests.push({ + name: this.testName, + testId: this.testId + }); + + if ( settings.skip ) { + + // Skipped tests will fully ignore any sent callback + this.callback = function() {}; + this.async = false; + this.expected = 0; + } else { + this.assert = new Assert( this ); } } @@ -778,30 +836,28 @@ Test.prototype = { passed: config.moduleStats.all - config.moduleStats.bad, total: config.moduleStats.all, runtime: now() - config.moduleStats.started - } ); + }); } config.previousModule = this.module; config.moduleStats = { all: 0, bad: 0, started: now() }; runLoggingCallbacks( "moduleStart", { name: this.module.name, tests: this.module.tests - } ); + }); } config.current = this; - if ( this.module.testEnvironment ) { - delete this.module.testEnvironment.beforeEach; - delete this.module.testEnvironment.afterEach; - } this.testEnvironment = extend( {}, this.module.testEnvironment ); + delete this.testEnvironment.beforeEach; + delete this.testEnvironment.afterEach; this.started = now(); runLoggingCallbacks( "testStart", { name: this.testName, module: this.module.name, testId: this.testId - } ); + }); if ( !config.pollution ) { saveGlobal(); @@ -820,17 +876,19 @@ Test.prototype = { this.callbackStarted = now(); if ( config.notrycatch ) { - runTest( this ); + promise = this.callback.call( this.testEnvironment, this.assert ); + this.resolvePromise( promise ); return; } try { - runTest( this ); + promise = this.callback.call( this.testEnvironment, this.assert ); + this.resolvePromise( promise ); } catch ( e ) { this.pushFailure( "Died on test #" + ( this.assertions.length + 1 ) + " " + this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) ); - // Else next test will carry the responsibility + // else next test will carry the responsibility saveGlobal(); // Restart the tests if they're blocking @@ -838,11 +896,6 @@ Test.prototype = { QUnit.start(); } } - - function runTest( test ) { - promise = test.callback.call( test.testEnvironment, test.assert ); - test.resolvePromise( promise ); - } }, after: function() { @@ -855,19 +908,16 @@ Test.prototype = { return function runHook() { config.current = test; if ( config.notrycatch ) { - callHook(); + promise = hook.call( test.testEnvironment, test.assert ); + test.resolvePromise( promise, hookName ); return; } try { - callHook(); - } catch ( error ) { - test.pushFailure( hookName + " failed on " + test.testName + ": " + - ( error.message || error ), extractStacktrace( error, 0 ) ); - } - - function callHook() { promise = hook.call( test.testEnvironment, test.assert ); test.resolvePromise( promise, hookName ); + } catch ( error ) { + test.pushFailure( hookName + " failed on " + test.testName + ": " + + ( error.message || error ), extractStacktrace( error, 0 ) ); } }; }, @@ -876,20 +926,16 @@ Test.prototype = { hooks: function( handler ) { var hooks = []; - function processHooks( test, module ) { - if ( module.parentModule ) { - processHooks( test, module.parentModule ); - } - if ( module.testEnvironment && - QUnit.objectType( module.testEnvironment[ handler ] ) === "function" ) { - hooks.push( test.queueHook( module.testEnvironment[ handler ], handler ) ); - } + // Hooks are ignored on skipped tests + if ( this.skip ) { + return hooks; } - // Hooks are ignored on skipped tests - if ( !this.skip ) { - processHooks( this, this.module ); + if ( this.module.testEnvironment && + QUnit.objectType( this.module.testEnvironment[ handler ] ) === "function" ) { + hooks.push( this.queueHook( this.module.testEnvironment[ handler ], handler ) ); } + return hooks; }, @@ -934,12 +980,9 @@ Test.prototype = { assertions: this.assertions, testId: this.testId, - // Source of Test - source: this.stack, - // DEPRECATED: this property will be removed in 2.0.0, use runtime instead duration: this.runtime - } ); + }); // QUnit.reset() is deprecated and will be replaced for a new // fixture reset function on QUnit 2.0/2.1. @@ -950,7 +993,7 @@ Test.prototype = { }, queue: function() { - var priority, + var bad, test = this; if ( !this.valid() ) { @@ -959,13 +1002,14 @@ Test.prototype = { function run() { - // Each of these can by async - synchronize( [ + // each of these can by async + synchronize([ function() { test.before(); }, test.hooks( "beforeEach" ), + function() { test.run(); }, @@ -978,33 +1022,35 @@ Test.prototype = { function() { test.finish(); } - ] ); + ]); } - // Prioritize previously failed tests, detected from sessionStorage - priority = QUnit.config.reorder && defined.sessionStorage && + // `bad` initialized at top of scope + // defer when previous test run passed, if storage is available + bad = QUnit.config.reorder && defined.sessionStorage && +sessionStorage.getItem( "qunit-test-" + this.module.name + "-" + this.testName ); - return synchronize( run, priority, config.seed ); + if ( bad ) { + run(); + } else { + synchronize( run, true ); + } }, - pushResult: function( resultInfo ) { - - // Destructure of resultInfo = { result, actual, expected, message, negative } + push: function( result, actual, expected, message ) { var source, details = { module: this.module.name, name: this.testName, - result: resultInfo.result, - message: resultInfo.message, - actual: resultInfo.actual, - expected: resultInfo.expected, + result: result, + message: message, + actual: actual, + expected: expected, testId: this.testId, - negative: resultInfo.negative || false, runtime: now() - this.started }; - if ( !resultInfo.result ) { + if ( !result ) { source = sourceFromStacktrace(); if ( source ) { @@ -1014,14 +1060,14 @@ Test.prototype = { runLoggingCallbacks( "log", details ); - this.assertions.push( { - result: !!resultInfo.result, - message: resultInfo.message - } ); + this.assertions.push({ + result: !!result, + message: message + }); }, pushFailure: function( message, source, actual ) { - if ( !( this instanceof Test ) ) { + if ( !this instanceof Test ) { throw new Error( "pushFailure() assertion outside test context, was " + sourceFromStacktrace( 2 ) ); } @@ -1042,10 +1088,10 @@ Test.prototype = { runLoggingCallbacks( "log", details ); - this.assertions.push( { + this.assertions.push({ result: false, message: message - } ); + }); }, resolvePromise: function( promise, phase ) { @@ -1057,14 +1103,14 @@ Test.prototype = { QUnit.stop(); then.call( promise, - function() { QUnit.start(); }, + QUnit.start, function( error ) { message = "Promise rejected " + ( !phase ? "during" : phase.replace( /Each$/, "" ) ) + " " + test.testName + ": " + ( error.message || error ); test.pushFailure( message, extractStacktrace( error, 0 ) ); - // Else next test will carry the responsibility + // else next test will carry the responsibility saveGlobal(); // Unblock @@ -1076,45 +1122,21 @@ Test.prototype = { }, valid: function() { - var filter = config.filter, - regexFilter = /^(!?)\/([\w\W]*)\/(i?$)/.exec( filter ), - module = config.module && config.module.toLowerCase(), - fullName = ( this.module.name + ": " + this.testName ); - - function moduleChainNameMatch( testModule ) { - var testModuleName = testModule.name ? testModule.name.toLowerCase() : null; - if ( testModuleName === module ) { - return true; - } else if ( testModule.parentModule ) { - return moduleChainNameMatch( testModule.parentModule ); - } else { - return false; - } - } - - function moduleChainIdMatch( testModule ) { - return inArray( testModule.moduleId, config.moduleId ) > -1 || - testModule.parentModule && moduleChainIdMatch( testModule.parentModule ); - } + var include, + filter = config.filter, + module = QUnit.urlParams.module && QUnit.urlParams.module.toLowerCase(), + fullName = ( this.module.name + ": " + this.testName ).toLowerCase(); // Internally-generated tests are always valid if ( this.callback && this.callback.validTest ) { return true; } - if ( config.moduleId && config.moduleId.length > 0 && - !moduleChainIdMatch( this.module ) ) { - - return false; - } - - if ( config.testId && config.testId.length > 0 && - inArray( this.testId, config.testId ) < 0 ) { - + if ( config.testId.length > 0 && inArray( this.testId, config.testId ) < 0 ) { return false; } - if ( module && !moduleChainNameMatch( this.module ) ) { + if ( module && ( !this.module.name || this.module.name.toLowerCase() !== module ) ) { return false; } @@ -1122,25 +1144,9 @@ Test.prototype = { return true; } - return regexFilter ? - this.regexFilter( !!regexFilter[ 1 ], regexFilter[ 2 ], regexFilter[ 3 ], fullName ) : - this.stringFilter( filter, fullName ); - }, - - regexFilter: function( exclude, pattern, flags, fullName ) { - var regex = new RegExp( pattern, flags ); - var match = regex.test( fullName ); - - return match !== exclude; - }, - - stringFilter: function( filter, fullName ) { - filter = filter.toLowerCase(); - fullName = fullName.toLowerCase(); - - var include = filter.charAt( 0 ) !== "!"; + include = filter.charAt( 0 ) !== "!"; if ( !include ) { - filter = filter.slice( 1 ); + filter = filter.toLowerCase().slice( 1 ); } // If the filter matches, we need to honour include @@ -1151,6 +1157,7 @@ Test.prototype = { // Otherwise, do the opposite return !include; } + }; // Resets the test setup. Useful for tests that modify the DOM. @@ -1163,7 +1170,7 @@ QUnit.reset = function() { // Return on non-browser environments // This is necessary to not break on node tests - if ( !defined.document ) { + if ( typeof window === "undefined" ) { return; } @@ -1211,157 +1218,6 @@ function generateHash( module, testName ) { return hex.slice( -8 ); } -function synchronize( callback, priority, seed ) { - var last = !priority, - index; - - if ( QUnit.objectType( callback ) === "array" ) { - while ( callback.length ) { - synchronize( callback.shift() ); - } - return; - } - - if ( priority ) { - config.queue.splice( priorityCount++, 0, callback ); - } else if ( seed ) { - if ( !unitSampler ) { - unitSampler = unitSamplerGenerator( seed ); - } - - // Insert into a random position after all priority items - index = Math.floor( unitSampler() * ( config.queue.length - priorityCount + 1 ) ); - config.queue.splice( priorityCount + index, 0, callback ); - } else { - config.queue.push( callback ); - } - - if ( config.autorun && !config.blocking ) { - process( last ); - } -} - -function unitSamplerGenerator( seed ) { - - // 32-bit xorshift, requires only a nonzero seed - // http://excamera.com/sphinx/article-xorshift.html - var sample = parseInt( generateHash( seed ), 16 ) || -1; - return function() { - sample ^= sample << 13; - sample ^= sample >>> 17; - sample ^= sample << 5; - - // ECMAScript has no unsigned number type - if ( sample < 0 ) { - sample += 0x100000000; - } - - return sample / 0x100000000; - }; -} - -function saveGlobal() { - config.pollution = []; - - if ( config.noglobals ) { - for ( var key in global ) { - if ( hasOwn.call( global, key ) ) { - - // In Opera sometimes DOM element ids show up here, ignore them - if ( /^qunit-test-output/.test( key ) ) { - continue; - } - config.pollution.push( key ); - } - } - } -} - -function checkPollution() { - var newGlobals, - deletedGlobals, - old = config.pollution; - - saveGlobal(); - - newGlobals = diff( config.pollution, old ); - if ( newGlobals.length > 0 ) { - QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join( ", " ) ); - } - - deletedGlobals = diff( old, config.pollution ); - if ( deletedGlobals.length > 0 ) { - QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join( ", " ) ); - } -} - -// Will be exposed as QUnit.asyncTest -function asyncTest( testName, expected, callback ) { - if ( arguments.length === 2 ) { - callback = expected; - expected = null; - } - - QUnit.test( testName, expected, callback, true ); -} - -// Will be exposed as QUnit.test -function test( testName, expected, callback, async ) { - if ( focused ) { return; } - - var newTest; - - if ( arguments.length === 2 ) { - callback = expected; - expected = null; - } - - newTest = new Test( { - testName: testName, - expected: expected, - async: async, - callback: callback - } ); - - newTest.queue(); -} - -// Will be exposed as QUnit.skip -function skip( testName ) { - if ( focused ) { return; } - - var test = new Test( { - testName: testName, - skip: true - } ); - - test.queue(); -} - -// Will be exposed as QUnit.only -function only( testName, expected, callback, async ) { - var newTest; - - if ( focused ) { return; } - - QUnit.config.queue.length = 0; - focused = true; - - if ( arguments.length === 2 ) { - callback = expected; - expected = null; - } - - newTest = new Test( { - testName: testName, - expected: expected, - async: async, - callback: callback - } ); - - newTest.queue(); -} - function Assert( testContext ) { this.test = testContext; } @@ -1379,55 +1235,30 @@ QUnit.assert = Assert.prototype = { } }, - // Increment this Test's semaphore counter, then return a function that + // Increment this Test's semaphore counter, then return a single-use function that // decrements that counter a maximum of once. - async: function( count ) { + async: function() { var test = this.test, - popped = false, - acceptCallCount = count; - - if ( typeof acceptCallCount === "undefined" ) { - acceptCallCount = 1; - } + popped = false; test.semaphore += 1; test.usedAsync = true; pauseProcessing(); return function done() { - - if ( popped ) { - test.pushFailure( "Too many calls to the `assert.async` callback", + if ( !popped ) { + test.semaphore -= 1; + popped = true; + resumeProcessing(); + } else { + test.pushFailure( "Called the callback returned from `assert.async` more than once", sourceFromStacktrace( 2 ) ); - return; - } - acceptCallCount -= 1; - if ( acceptCallCount > 0 ) { - return; } - - test.semaphore -= 1; - popped = true; - resumeProcessing(); }; }, // Exports test.push() to the user API - // Alias of pushResult. - push: function( result, actual, expected, message, negative ) { - var currentAssert = this instanceof Assert ? this : QUnit.config.current.assert; - return currentAssert.pushResult( { - result: result, - actual: actual, - expected: expected, - message: message, - negative: negative - } ); - }, - - pushResult: function( resultInfo ) { - - // Destructure of resultInfo = { result, actual, expected, message, negative } + push: function( /* result, actual, expected, message */ ) { var assert = this, currentTest = ( assert instanceof Assert && assert.test ) || QUnit.config.current; @@ -1450,119 +1281,98 @@ QUnit.assert = Assert.prototype = { if ( !( assert instanceof Assert ) ) { assert = currentTest.assert; } - - return assert.test.pushResult( resultInfo ); + return assert.test.push.apply( assert.test, arguments ); }, + /** + * Asserts rough true-ish result. + * @name ok + * @function + * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" ); + */ ok: function( result, message ) { message = message || ( result ? "okay" : "failed, expected argument to be truthy, was: " + QUnit.dump.parse( result ) ); - this.pushResult( { - result: !!result, - actual: result, - expected: true, - message: message - } ); - }, - - notOk: function( result, message ) { - message = message || ( !result ? "okay" : "failed, expected argument to be falsy, was: " + - QUnit.dump.parse( result ) ); - this.pushResult( { - result: !result, - actual: result, - expected: false, - message: message - } ); + this.push( !!result, result, true, message ); }, + /** + * Assert that the first two arguments are equal, with an optional message. + * Prints out both actual and expected values. + * @name equal + * @function + * @example equal( format( "{0} bytes.", 2), "2 bytes.", "replaces {0} with next argument" ); + */ equal: function( actual, expected, message ) { /*jshint eqeqeq:false */ - this.pushResult( { - result: expected == actual, - actual: actual, - expected: expected, - message: message - } ); + this.push( expected == actual, actual, expected, message ); }, + /** + * @name notEqual + * @function + */ notEqual: function( actual, expected, message ) { /*jshint eqeqeq:false */ - this.pushResult( { - result: expected != actual, - actual: actual, - expected: expected, - message: message, - negative: true - } ); + this.push( expected != actual, actual, expected, message ); }, + /** + * @name propEqual + * @function + */ propEqual: function( actual, expected, message ) { actual = objectValues( actual ); expected = objectValues( expected ); - this.pushResult( { - result: QUnit.equiv( actual, expected ), - actual: actual, - expected: expected, - message: message - } ); + this.push( QUnit.equiv( actual, expected ), actual, expected, message ); }, + /** + * @name notPropEqual + * @function + */ notPropEqual: function( actual, expected, message ) { actual = objectValues( actual ); expected = objectValues( expected ); - this.pushResult( { - result: !QUnit.equiv( actual, expected ), - actual: actual, - expected: expected, - message: message, - negative: true - } ); + this.push( !QUnit.equiv( actual, expected ), actual, expected, message ); }, + /** + * @name deepEqual + * @function + */ deepEqual: function( actual, expected, message ) { - this.pushResult( { - result: QUnit.equiv( actual, expected ), - actual: actual, - expected: expected, - message: message - } ); + this.push( QUnit.equiv( actual, expected ), actual, expected, message ); }, + /** + * @name notDeepEqual + * @function + */ notDeepEqual: function( actual, expected, message ) { - this.pushResult( { - result: !QUnit.equiv( actual, expected ), - actual: actual, - expected: expected, - message: message, - negative: true - } ); + this.push( !QUnit.equiv( actual, expected ), actual, expected, message ); }, + /** + * @name strictEqual + * @function + */ strictEqual: function( actual, expected, message ) { - this.pushResult( { - result: expected === actual, - actual: actual, - expected: expected, - message: message - } ); + this.push( expected === actual, actual, expected, message ); }, + /** + * @name notStrictEqual + * @function + */ notStrictEqual: function( actual, expected, message ) { - this.pushResult( { - result: expected !== actual, - actual: actual, - expected: expected, - message: message, - negative: true - } ); + this.push( expected !== actual, actual, expected, message ); }, "throws": function( block, expected, message ) { var actual, expectedType, expectedOutput = expected, - ok = false, - currentTest = ( this instanceof Assert && this.test ) || QUnit.config.current; + ok = false; // 'expected' is optional unless doing string comparison if ( message == null && typeof expected === "string" ) { @@ -1570,351 +1380,285 @@ QUnit.assert = Assert.prototype = { expected = null; } - currentTest.ignoreGlobalErrors = true; + this.test.ignoreGlobalErrors = true; try { - block.call( currentTest.testEnvironment ); - } catch ( e ) { + block.call( this.test.testEnvironment ); + } catch (e) { actual = e; } - currentTest.ignoreGlobalErrors = false; + this.test.ignoreGlobalErrors = false; if ( actual ) { expectedType = QUnit.objectType( expected ); - // We don't want to validate thrown error + // we don't want to validate thrown error if ( !expected ) { ok = true; expectedOutput = null; - // Expected is a regexp + // expected is a regexp } else if ( expectedType === "regexp" ) { ok = expected.test( errorString( actual ) ); - // Expected is a string + // expected is a string } else if ( expectedType === "string" ) { ok = expected === errorString( actual ); - // Expected is a constructor, maybe an Error constructor + // expected is a constructor, maybe an Error constructor } else if ( expectedType === "function" && actual instanceof expected ) { ok = true; - // Expected is an Error object + // expected is an Error object } else if ( expectedType === "object" ) { ok = actual instanceof expected.constructor && actual.name === expected.name && actual.message === expected.message; - // Expected is a validation function which returns true if validation passed + // expected is a validation function which returns true if validation passed } else if ( expectedType === "function" && expected.call( {}, actual ) === true ) { expectedOutput = null; ok = true; } - } - currentTest.assert.pushResult( { - result: ok, - actual: actual, - expected: expectedOutput, - message: message - } ); + this.push( ok, actual, expectedOutput, message ); + } else { + this.test.pushFailure( message, null, "No exception was thrown." ); + } } }; -// Provide an alternative to assert.throws(), for environments that consider throws a reserved word +// Provide an alternative to assert.throws(), for enviroments that consider throws a reserved word // Known to us are: Closure Compiler, Narwhal -( function() { +(function() { /*jshint sub:true */ - Assert.prototype.raises = Assert.prototype [ "throws" ]; //jscs:ignore requireDotNotation -}() ); - -function errorString( error ) { - var name, message, - resultErrorString = error.toString(); - if ( resultErrorString.substring( 0, 7 ) === "[object" ) { - name = error.name ? error.name.toString() : "Error"; - message = error.message ? error.message.toString() : ""; - if ( name && message ) { - return name + ": " + message; - } else if ( name ) { - return name; - } else if ( message ) { - return message; - } else { - return "Error"; - } - } else { - return resultErrorString; - } -} + Assert.prototype.raises = Assert.prototype[ "throws" ]; +}()); // Test for equality any JavaScript type. // Author: Philippe Rathé -QUnit.equiv = ( function() { - - // Stack to decide between skip/abort functions - var callers = []; - - // Stack to avoiding loops from circular referencing - var parents = []; - var parentsB = []; - - var getProto = Object.getPrototypeOf || function( obj ) { +QUnit.equiv = (function() { + + // Call the o related callback with the given arguments. + function bindCallbacks( o, callbacks, args ) { + var prop = QUnit.objectType( o ); + if ( prop ) { + if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) { + return callbacks[ prop ].apply( callbacks, args ); + } else { + return callbacks[ prop ]; // or undefined + } + } + } - /*jshint proto: true */ - return obj.__proto__; - }; + // the real equiv function + var innerEquiv, - function useStrictEquality( b, a ) { + // stack to decide between skip/abort functions + callers = [], - // To catch short annotation VS 'new' annotation of a declaration. e.g.: - // `var i = 1;` - // `var j = new Number(1);` - if ( typeof a === "object" ) { - a = a.valueOf(); - } - if ( typeof b === "object" ) { - b = b.valueOf(); - } + // stack to avoiding loops from circular referencing + parents = [], + parentsB = [], - return a === b; - } + getProto = Object.getPrototypeOf || function( obj ) { + /* jshint camelcase: false, proto: true */ + return obj.__proto__; + }, + callbacks = (function() { - function compareConstructors( a, b ) { - var protoA = getProto( a ); - var protoB = getProto( b ); + // for string, boolean, number and null + function useStrictEquality( b, a ) { - // Comparing constructors is more strict than using `instanceof` - if ( a.constructor === b.constructor ) { - return true; - } + /*jshint eqeqeq:false */ + if ( b instanceof a.constructor || a instanceof b.constructor ) { - // Ref #851 - // If the obj prototype descends from a null constructor, treat it - // as a null prototype. - if ( protoA && protoA.constructor === null ) { - protoA = null; - } - if ( protoB && protoB.constructor === null ) { - protoB = null; - } + // to catch short annotation VS 'new' annotation of a + // declaration + // e.g. var i = 1; + // var j = new Number(1); + return a == b; + } else { + return a === b; + } + } - // Allow objects with no prototype to be equivalent to - // objects with Object as their constructor. - if ( ( protoA === null && protoB === Object.prototype ) || - ( protoB === null && protoA === Object.prototype ) ) { - return true; - } + return { + "string": useStrictEquality, + "boolean": useStrictEquality, + "number": useStrictEquality, + "null": useStrictEquality, + "undefined": useStrictEquality, - return false; - } + "nan": function( b ) { + return isNaN( b ); + }, - function getRegExpFlags( regexp ) { - return "flags" in regexp ? regexp.flags : regexp.toString().match( /[gimuy]*$/ )[ 0 ]; - } + "date": function( b, a ) { + return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf(); + }, - var callbacks = { - "string": useStrictEquality, - "boolean": useStrictEquality, - "number": useStrictEquality, - "null": useStrictEquality, - "undefined": useStrictEquality, - "symbol": useStrictEquality, - "date": useStrictEquality, + "regexp": function( b, a ) { + return QUnit.objectType( b ) === "regexp" && - "nan": function() { - return true; - }, + // the regex itself + a.source === b.source && - "regexp": function( b, a ) { - return a.source === b.source && + // and its modifiers + a.global === b.global && - // Include flags in the comparison - getRegExpFlags( a ) === getRegExpFlags( b ); - }, + // (gmi) ... + a.ignoreCase === b.ignoreCase && + a.multiline === b.multiline && + a.sticky === b.sticky; + }, - // - skip when the property is a method of an instance (OOP) - // - abort otherwise, - // initial === would have catch identical references anyway - "function": function() { - var caller = callers[ callers.length - 1 ]; - return caller !== Object && typeof caller !== "undefined"; - }, + // - skip when the property is a method of an instance (OOP) + // - abort otherwise, + // initial === would have catch identical references anyway + "function": function() { + var caller = callers[ callers.length - 1 ]; + return caller !== Object && typeof caller !== "undefined"; + }, - "array": function( b, a ) { - var i, j, len, loop, aCircular, bCircular; + "array": function( b, a ) { + var i, j, len, loop, aCircular, bCircular; - len = a.length; - if ( len !== b.length ) { + // b could be an object literal here + if ( QUnit.objectType( b ) !== "array" ) { + return false; + } - // Safe and faster - return false; - } + len = a.length; + if ( len !== b.length ) { + // safe and faster + return false; + } - // Track reference to avoid circular references - parents.push( a ); - parentsB.push( b ); - for ( i = 0; i < len; i++ ) { - loop = false; - for ( j = 0; j < parents.length; j++ ) { - aCircular = parents[ j ] === a[ i ]; - bCircular = parentsB[ j ] === b[ i ]; - if ( aCircular || bCircular ) { - if ( a[ i ] === b[ i ] || aCircular && bCircular ) { - loop = true; - } else { + // track reference to avoid circular references + parents.push( a ); + parentsB.push( b ); + for ( i = 0; i < len; i++ ) { + loop = false; + for ( j = 0; j < parents.length; j++ ) { + aCircular = parents[ j ] === a[ i ]; + bCircular = parentsB[ j ] === b[ i ]; + if ( aCircular || bCircular ) { + if ( a[ i ] === b[ i ] || aCircular && bCircular ) { + loop = true; + } else { + parents.pop(); + parentsB.pop(); + return false; + } + } + } + if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) { parents.pop(); parentsB.pop(); return false; } } - } - if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) { parents.pop(); parentsB.pop(); - return false; - } - } - parents.pop(); - parentsB.pop(); - return true; - }, - - "set": function( b, a ) { - var innerEq, - outerEq = true; - - if ( a.size !== b.size ) { - return false; - } - - a.forEach( function( aVal ) { - innerEq = false; - - b.forEach( function( bVal ) { - if ( innerEquiv( bVal, aVal ) ) { - innerEq = true; - } - } ); - - if ( !innerEq ) { - outerEq = false; - } - } ); - - return outerEq; - }, + return true; + }, - "map": function( b, a ) { - var innerEq, - outerEq = true; + "object": function( b, a ) { - if ( a.size !== b.size ) { - return false; - } + /*jshint forin:false */ + var i, j, loop, aCircular, bCircular, + // Default to true + eq = true, + aProperties = [], + bProperties = []; - a.forEach( function( aVal, aKey ) { - innerEq = false; + // comparing constructors is more strict than using + // instanceof + if ( a.constructor !== b.constructor ) { - b.forEach( function( bVal, bKey ) { - if ( innerEquiv( [ bVal, bKey ], [ aVal, aKey ] ) ) { - innerEq = true; + // Allow objects with no prototype to be equivalent to + // objects with Object as their constructor. + if ( !( ( getProto( a ) === null && getProto( b ) === Object.prototype ) || + ( getProto( b ) === null && getProto( a ) === Object.prototype ) ) ) { + return false; + } } - } ); - - if ( !innerEq ) { - outerEq = false; - } - } ); - - return outerEq; - }, - - "object": function( b, a ) { - var i, j, loop, aCircular, bCircular; - - // Default to true - var eq = true; - var aProperties = []; - var bProperties = []; - - if ( compareConstructors( a, b ) === false ) { - return false; - } - // Stack constructor before traversing properties - callers.push( a.constructor ); - - // Track reference to avoid circular references - parents.push( a ); - parentsB.push( b ); - - // Be strict: don't ensure hasOwnProperty and go deep - for ( i in a ) { - loop = false; - for ( j = 0; j < parents.length; j++ ) { - aCircular = parents[ j ] === a[ i ]; - bCircular = parentsB[ j ] === b[ i ]; - if ( aCircular || bCircular ) { - if ( a[ i ] === b[ i ] || aCircular && bCircular ) { - loop = true; - } else { + // stack constructor before traversing properties + callers.push( a.constructor ); + + // track reference to avoid circular references + parents.push( a ); + parentsB.push( b ); + + // be strict: don't ensure hasOwnProperty and go deep + for ( i in a ) { + loop = false; + for ( j = 0; j < parents.length; j++ ) { + aCircular = parents[ j ] === a[ i ]; + bCircular = parentsB[ j ] === b[ i ]; + if ( aCircular || bCircular ) { + if ( a[ i ] === b[ i ] || aCircular && bCircular ) { + loop = true; + } else { + eq = false; + break; + } + } + } + aProperties.push( i ); + if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) { eq = false; break; } } - } - aProperties.push( i ); - if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) { - eq = false; - break; - } - } - - parents.pop(); - parentsB.pop(); - // Unstack, we are done - callers.pop(); + parents.pop(); + parentsB.pop(); + callers.pop(); // unstack, we are done - for ( i in b ) { + for ( i in b ) { + bProperties.push( i ); // collect b's properties + } - // Collect b's properties - bProperties.push( i ); - } + // Ensures identical properties name + return eq && innerEquiv( aProperties.sort(), bProperties.sort() ); + } + }; + }()); - // Ensures identical properties name - return eq && innerEquiv( aProperties.sort(), bProperties.sort() ); + innerEquiv = function() { // can take multiple arguments + var args = [].slice.apply( arguments ); + if ( args.length < 2 ) { + return true; // end transition } - }; - - function typeEquiv( a, b ) { - var type = QUnit.objectType( a ); - return QUnit.objectType( b ) === type && callbacks[ type ]( b, a ); - } - - // The real equiv function - function innerEquiv( a, b ) { - // We're done when there's nothing more to compare - if ( arguments.length < 2 ) { - return true; - } + return ( (function( a, b ) { + if ( a === b ) { + return true; // catch the most you can + } else if ( a === null || b === null || typeof a === "undefined" || + typeof b === "undefined" || + QUnit.objectType( a ) !== QUnit.objectType( b ) ) { - // Require type-specific equality - return ( a === b || typeEquiv( a, b ) ) && + // don't lose time with error prone cases + return false; + } else { + return bindCallbacks( a, callbacks, [ b, a ] ); + } - // ...across all consecutive argument pairs - ( arguments.length === 2 || innerEquiv.apply( this, [].slice.call( arguments, 1 ) ) ); - } + // apply transition with (1..n) arguments + }( args[ 0 ], args[ 1 ] ) ) && + innerEquiv.apply( this, args.splice( 1, args.length - 1 ) ) ); + }; return innerEquiv; -}() ); +}()); // Based on jsDump by Ariel Flesler // http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html -QUnit.dump = ( function() { +QUnit.dump = (function() { function quote( str ) { - return "\"" + str.toString().replace( /\\/g, "\\\\" ).replace( /"/g, "\\\"" ) + "\""; + return "\"" + str.toString().replace( /"/g, "\\\"" ) + "\""; } function literal( o ) { return o + ""; @@ -1950,7 +1694,7 @@ QUnit.dump = ( function() { var reName = /^function (\w+)/, dump = { - // The objType is used mostly internally, you can fix a (custom) type in advance + // objType is used mostly internally, you can fix a (custom) type in advance parse: function( obj, objType, stack ) { stack = stack || []; var res, parser, parserType, @@ -1994,7 +1738,7 @@ QUnit.dump = ( function() { type = "node"; } else if ( - // Native arrays + // native arrays toString.call( obj ) === "[object Array]" || // NodeList objects @@ -2010,12 +1754,10 @@ QUnit.dump = ( function() { } return type; }, - separator: function() { return this.multiline ? this.HTML ? "
" : "\n" : this.HTML ? " " : " "; }, - - // Extra can be a number, shortcut for increasing-calling-decreasing + // extra can be a number, shortcut for increasing-calling-decreasing indent: function( extra ) { if ( !this.multiline ) { return ""; @@ -2035,13 +1777,13 @@ QUnit.dump = ( function() { setParser: function( name, parser ) { this.parsers[ name ] = parser; }, - // The next 3 are exposed so you can use them quote: quote, literal: literal, join: join, + // depth: 1, - maxDepth: QUnit.config.maxDepth, + maxDepth: 5, // This is the list of parsers, to modify them, use dump.setParser parsers: { @@ -2056,13 +1798,13 @@ QUnit.dump = ( function() { "function": function( fn ) { var ret = "function", - // Functions never have name in IE + // functions never have name in IE name = "name" in fn ? fn.name : ( reName.exec( fn ) || [] )[ 1 ]; if ( name ) { ret += " " + name; } - ret += "("; + ret += "( "; ret = [ ret, dump.parse( fn, "functionArgs" ), "){" ].join( "" ); return join( ret, dump.parse( fn, "functionCode" ), "}" ); @@ -2088,7 +1830,7 @@ QUnit.dump = ( function() { nonEnumerableProperties = [ "message", "name" ]; for ( i in nonEnumerableProperties ) { key = nonEnumerableProperties[ i ]; - if ( key in map && inArray( key, keys ) < 0 ) { + if ( key in map && !( key in keys ) ) { keys.push( key ); } } @@ -2133,7 +1875,7 @@ QUnit.dump = ( function() { return ret + open + "/" + tag + close; }, - // Function calls it internally, it's the arguments part of the function + // function calls it internally, it's the arguments part of the function functionArgs: function( fn ) { var args, l = fn.length; @@ -2150,14 +1892,11 @@ QUnit.dump = ( function() { } return " " + args.join( ", " ) + " "; }, - - // Object calls it internally, the key part of an item in a map + // object calls it internally, the key part of an item in a map key: quote, - - // Function calls it internally, it's the content of the function + // function calls it internally, it's the content of the function functionCode: "[code]", - - // Node calls it internally, it's a html attribute value + // node calls it internally, it's an html attribute value attribute: quote, string: quote, date: quote, @@ -2165,45 +1904,42 @@ QUnit.dump = ( function() { number: literal, "boolean": literal }, - - // If true, entities are escaped ( <, >, \t, space and \n ) + // if true, entities are escaped ( <, >, \t, space and \n ) HTML: false, - - // Indentation unit + // indentation unit indentChar: " ", - - // If true, items in a collection, are separated by a \n, else just a space. + // if true, items in a collection, are separated by a \n, else just a space. multiline: true }; return dump; -}() ); +}()); -// Back compat +// back compat QUnit.jsDump = QUnit.dump; -// Deprecated -// Extend assert methods to QUnit for Backwards compatibility -( function() { - var i, - assertions = Assert.prototype; +// For browser, export only select globals +if ( typeof window !== "undefined" ) { - function applyCurrent( current ) { - return function() { - var assert = new Assert( QUnit.config.current ); - current.apply( assert, arguments ); - }; - } + // Deprecated + // Extend assert methods to QUnit and Global scope through Backwards compatibility + (function() { + var i, + assertions = Assert.prototype; - for ( i in assertions ) { - QUnit[ i ] = applyCurrent( assertions[ i ] ); - } -}() ); + function applyCurrent( current ) { + return function() { + var assert = new Assert( QUnit.config.current ); + current.apply( assert, arguments ); + }; + } -// For browser, export only select globals -if ( defined.document ) { + for ( i in assertions ) { + QUnit[ i ] = applyCurrent( assertions[ i ] ); + } + })(); - ( function() { + (function() { var i, l, keys = [ "test", @@ -2213,7 +1949,6 @@ if ( defined.document ) { "start", "stop", "ok", - "notOk", "equal", "notEqual", "propEqual", @@ -2222,14 +1957,13 @@ if ( defined.document ) { "notDeepEqual", "strictEqual", "notStrictEqual", - "throws", - "raises" + "throws" ]; for ( i = 0, l = keys.length; i < l; i++ ) { window[ keys[ i ] ] = QUnit[ keys[ i ] ]; } - }() ); + })(); window.QUnit = QUnit; } @@ -2247,122 +1981,168 @@ if ( typeof exports !== "undefined" && exports ) { exports.QUnit = QUnit; } -if ( typeof define === "function" && define.amd ) { - define( function() { - return QUnit; - } ); - QUnit.config.autostart = false; -} - // Get a reference to the global object, like window in browsers -}( ( function() { +}( (function() { return this; -}() ) ) ); - -( function() { +})() )); -// Only interact with URLs via window.location -var location = typeof window !== "undefined" && window.location; -if ( !location ) { - return; -} - -var urlParams = getUrlParams(); - -QUnit.urlParams = urlParams; +/*istanbul ignore next */ +// jscs:disable maximumLineLength +/* + * Javascript Diff Algorithm + * By John Resig (http://ejohn.org/) + * Modified by Chu Alan "sprite" + * + * Released under the MIT license. + * + * More Info: + * http://ejohn.org/projects/javascript-diff-algorithm/ + * + * Usage: QUnit.diff(expected, actual) + * + * QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) == "the quick brown fox jumped jumps over" + */ +QUnit.diff = (function() { + var hasOwn = Object.prototype.hasOwnProperty; -// Match module/test by inclusion in an array -QUnit.config.moduleId = [].concat( urlParams.moduleId || [] ); -QUnit.config.testId = [].concat( urlParams.testId || [] ); + /*jshint eqeqeq:false, eqnull:true */ + function diff( o, n ) { + var i, + ns = {}, + os = {}; + + for ( i = 0; i < n.length; i++ ) { + if ( !hasOwn.call( ns, n[ i ] ) ) { + ns[ n[ i ] ] = { + rows: [], + o: null + }; + } + ns[ n[ i ] ].rows.push( i ); + } + + for ( i = 0; i < o.length; i++ ) { + if ( !hasOwn.call( os, o[ i ] ) ) { + os[ o[ i ] ] = { + rows: [], + n: null + }; + } + os[ o[ i ] ].rows.push( i ); + } + + for ( i in ns ) { + if ( hasOwn.call( ns, i ) ) { + if ( ns[ i ].rows.length === 1 && hasOwn.call( os, i ) && os[ i ].rows.length === 1 ) { + n[ ns[ i ].rows[ 0 ] ] = { + text: n[ ns[ i ].rows[ 0 ] ], + row: os[ i ].rows[ 0 ] + }; + o[ os[ i ].rows[ 0 ] ] = { + text: o[ os[ i ].rows[ 0 ] ], + row: ns[ i ].rows[ 0 ] + }; + } + } + } -// Exact case-insensitive match of the module name -QUnit.config.module = urlParams.module; + for ( i = 0; i < n.length - 1; i++ ) { + if ( n[ i ].text != null && n[ i + 1 ].text == null && n[ i ].row + 1 < o.length && o[ n[ i ].row + 1 ].text == null && + n[ i + 1 ] == o[ n[ i ].row + 1 ] ) { -// Regular expression or case-insenstive substring match against "moduleName: testName" -QUnit.config.filter = urlParams.filter; + n[ i + 1 ] = { + text: n[ i + 1 ], + row: n[ i ].row + 1 + }; + o[ n[ i ].row + 1 ] = { + text: o[ n[ i ].row + 1 ], + row: i + 1 + }; + } + } -// Test order randomization -if ( urlParams.seed === true ) { + for ( i = n.length - 1; i > 0; i-- ) { + if ( n[ i ].text != null && n[ i - 1 ].text == null && n[ i ].row > 0 && o[ n[ i ].row - 1 ].text == null && + n[ i - 1 ] == o[ n[ i ].row - 1 ] ) { - // Generate a random seed if the option is specified without a value - QUnit.config.seed = Math.random().toString( 36 ).slice( 2 ); -} else if ( urlParams.seed ) { - QUnit.config.seed = urlParams.seed; -} + n[ i - 1 ] = { + text: n[ i - 1 ], + row: n[ i ].row - 1 + }; + o[ n[ i ].row - 1 ] = { + text: o[ n[ i ].row - 1 ], + row: i - 1 + }; + } + } -// Add URL-parameter-mapped config values with UI form rendering data -QUnit.config.urlConfig.push( - { - id: "hidepassed", - label: "Hide passed tests", - tooltip: "Only show tests and assertions that fail. Stored as query-strings." - }, - { - id: "noglobals", - label: "Check for Globals", - tooltip: "Enabling this will test if any test introduces new properties on the " + - "global object (`window` in Browsers). Stored as query-strings." - }, - { - id: "notrycatch", - label: "No try-catch", - tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " + - "exceptions in IE reasonable. Stored as query-strings." + return { + o: o, + n: n + }; } -); -QUnit.begin( function() { - var i, option, - urlConfig = QUnit.config.urlConfig; + return function( o, n ) { + o = o.replace( /\s+$/, "" ); + n = n.replace( /\s+$/, "" ); - for ( i = 0; i < urlConfig.length; i++ ) { + var i, pre, + str = "", + out = diff( o === "" ? [] : o.split( /\s+/ ), n === "" ? [] : n.split( /\s+/ ) ), + oSpace = o.match( /\s+/g ), + nSpace = n.match( /\s+/g ); - // Options can be either strings or objects with nonempty "id" properties - option = QUnit.config.urlConfig[ i ]; - if ( typeof option !== "string" ) { - option = option.id; + if ( oSpace == null ) { + oSpace = [ " " ]; + } else { + oSpace.push( " " ); } - if ( QUnit.config[ option ] === undefined ) { - QUnit.config[ option ] = urlParams[ option ]; + if ( nSpace == null ) { + nSpace = [ " " ]; + } else { + nSpace.push( " " ); } - } -} ); - -function getUrlParams() { - var i, param, name, value; - var urlParams = {}; - var params = location.search.slice( 1 ).split( "&" ); - var length = params.length; - - for ( i = 0; i < length; i++ ) { - if ( params[ i ] ) { - param = params[ i ].split( "=" ); - name = decodeURIComponent( param[ 0 ] ); - - // Allow just a key to turn on a flag, e.g., test.html?noglobals - value = param.length === 1 || - decodeURIComponent( param.slice( 1 ).join( "=" ) ) ; - if ( urlParams[ name ] ) { - urlParams[ name ] = [].concat( urlParams[ name ], value ); - } else { - urlParams[ name ] = value; + + if ( out.n.length === 0 ) { + for ( i = 0; i < out.o.length; i++ ) { + str += "" + out.o[ i ] + oSpace[ i ] + ""; + } + } else { + if ( out.n[ 0 ].text == null ) { + for ( n = 0; n < out.o.length && out.o[ n ].text == null; n++ ) { + str += "" + out.o[ n ] + oSpace[ n ] + ""; + } + } + + for ( i = 0; i < out.n.length; i++ ) { + if ( out.n[ i ].text == null ) { + str += "" + out.n[ i ] + nSpace[ i ] + ""; + } else { + + // `pre` initialized at top of scope + pre = ""; + + for ( n = out.n[ i ].row + 1; n < out.o.length && out.o[ n ].text == null; n++ ) { + pre += "" + out.o[ n ] + oSpace[ n ] + ""; + } + str += " " + out.n[ i ].text + nSpace[ i ] + pre; + } } } - } - return urlParams; -} + return str; + }; +}()); +// jscs:enable -// Don't load the HTML Reporter on non-browser environments -if ( typeof window === "undefined" || !window.document ) { - return; -} +(function() { // Deprecated QUnit.init - Ref #530 // Re-initialize the configuration options QUnit.init = function() { - var config = QUnit.config; + var tests, banner, result, qunit, + config = QUnit.config; config.stats = { all: 0, bad: 0 }; config.moduleStats = { all: 0, bad: 0 }; @@ -2374,17 +2154,57 @@ QUnit.init = function() { config.filter = ""; config.queue = []; - appendInterface(); + // Return on non-browser environments + // This is necessary to not break on node tests + if ( typeof window === "undefined" ) { + return; + } + + qunit = id( "qunit" ); + if ( qunit ) { + qunit.innerHTML = + "

" + escapeText( document.title ) + "

" + + "

" + + "
" + + "

" + + "
    "; + } + + tests = id( "qunit-tests" ); + banner = id( "qunit-banner" ); + result = id( "qunit-testresult" ); + + if ( tests ) { + tests.innerHTML = ""; + } + + if ( banner ) { + banner.className = ""; + } + + if ( result ) { + result.parentNode.removeChild( result ); + } + + if ( tests ) { + result = document.createElement( "p" ); + result.id = "qunit-testresult"; + result.className = "result"; + tests.parentNode.insertBefore( result, tests ); + result.innerHTML = "Running...
     "; + } }; +// Don't load the HTML Reporter on non-Browser environments +if ( typeof window === "undefined" ) { + return; +} + var config = QUnit.config, - document = window.document, - collapseNext = false, hasOwn = Object.prototype.hasOwnProperty, - unfilteredUrl = setUrl( { filter: undefined, module: undefined, - moduleId: undefined, testId: undefined } ), defined = { - sessionStorage: ( function() { + document: window.document !== undefined, + sessionStorage: (function() { var x = "qunit-test-string"; try { sessionStorage.setItem( x, x ); @@ -2393,7 +2213,7 @@ var config = QUnit.config, } catch ( e ) { return false; } - }() ) + }()) }, modulesList = []; @@ -2420,7 +2240,7 @@ function escapeText( s ) { case "&": return "&"; } - } ); + }); } /** @@ -2435,15 +2255,8 @@ function addEvent( elem, type, fn ) { elem.addEventListener( type, fn, false ); } else if ( elem.attachEvent ) { - // Support: IE <9 - elem.attachEvent( "on" + type, function() { - var event = window.event; - if ( !event.target ) { - event.target = event.srcElement || document; - } - - fn.call( elem, event ); - } ); + // support: IE <9 + elem.attachEvent( "on" + type, fn ); } } @@ -2469,11 +2282,11 @@ function addClass( elem, name ) { } } -function toggleClass( elem, name, force ) { - if ( force || typeof force === "undefined" && !hasClass( elem, name ) ) { - addClass( elem, name ); - } else { +function toggleClass( elem, name ) { + if ( hasClass( elem, name ) ) { removeClass( elem, name ); + } else { + addClass( elem, name ); } } @@ -2485,24 +2298,22 @@ function removeClass( elem, name ) { set = set.replace( " " + name + " ", " " ); } - // Trim for prettiness + // trim for prettiness elem.className = typeof set.trim === "function" ? set.trim() : set.replace( /^\s+|\s+$/g, "" ); } function id( name ) { - return document.getElementById && document.getElementById( name ); + return defined.document && document.getElementById && document.getElementById( name ); } function getUrlConfigHtml() { var i, j, val, escaped, escapedTooltip, selection = false, - urlConfig = config.urlConfig, + len = config.urlConfig.length, urlConfigHtml = ""; - for ( i = 0; i < urlConfig.length; i++ ) { - - // Options can be either strings or objects with nonempty "id" properties + for ( i = 0; i < len; i++ ) { val = config.urlConfig[ i ]; if ( typeof val === "string" ) { val = { @@ -2514,6 +2325,10 @@ function getUrlConfigHtml() { escaped = escapeText( val.id ); escapedTooltip = escapeText( val.tooltip ); + if ( config[ val.id ] === undefined ) { + config[ val.id ] = QUnit.urlParams[ val.id ]; + } + if ( !val.value || typeof val.value === "string" ) { urlConfigHtml += "