summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--js_tests/qunit/qunit.css12
-rw-r--r--js_tests/qunit/qunit.js2478
2 files changed, 1335 insertions, 1155 deletions
diff --git a/js_tests/qunit/qunit.css b/js_tests/qunit/qunit.css
index a59e2824ce..ae68fc412e 100644
--- a/js_tests/qunit/qunit.css
+++ b/js_tests/qunit/qunit.css
@@ -1,12 +1,12 @@
/*!
- * QUnit 1.20.0
- * http://qunitjs.com/
+ * QUnit 1.23.1
+ * https://qunitjs.com/
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license
- * http://jquery.org/license
+ * https://jquery.org/license
*
- * Date: 2015-10-27T17:53Z
+ * Date: 2016-04-12T17:29Z
*/
/** Font Family and Sizes */
@@ -120,6 +120,10 @@
display: list-item;
}
+#qunit-tests.hidepass {
+ position: relative;
+}
+
#qunit-tests.hidepass li.running,
#qunit-tests.hidepass li.pass {
visibility: hidden;
diff --git a/js_tests/qunit/qunit.js b/js_tests/qunit/qunit.js
index 904943f088..5df0822ea4 100644
--- a/js_tests/qunit/qunit.js
+++ b/js_tests/qunit/qunit.js
@@ -1,15 +1,15 @@
/*!
- * QUnit 1.20.0
- * http://qunitjs.com/
+ * QUnit 1.23.1
+ * https://qunitjs.com/
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license
- * http://jquery.org/license
+ * https://jquery.org/license
*
- * Date: 2015-10-27T17:53Z
+ * Date: 2016-04-12T17:29Z
*/
-(function( global ) {
+( function( global ) {
var QUnit = {};
@@ -27,7 +27,7 @@ var window = global.window;
var defined = {
document: window && window.document !== undefined,
setTimeout: setTimeout !== undefined,
- sessionStorage: (function() {
+ sessionStorage: ( function() {
var x = "qunit-test-string";
try {
sessionStorage.setItem( x, x );
@@ -46,7 +46,7 @@ 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
+// 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();
@@ -63,7 +63,7 @@ function diff( a, b ) {
return result;
}
-// from jquery.js
+// From jquery.js
function inArray( elem, array ) {
if ( array.indexOf ) {
return array.indexOf( elem );
@@ -157,31 +157,6 @@ function is( type, obj ) {
return QUnit.objectType( obj ) === type;
}
-var getUrlParams = function() {
- var i, current;
- var urlParams = {};
- var location = window.location;
- var params = location.search.slice( 1 ).split( "&" );
- var length = params.length;
-
- 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 ];
- }
- }
- }
-
- return urlParams;
-};
-
// 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 ) {
@@ -211,12 +186,12 @@ function extractStacktrace( e, offset ) {
// Support: Safari <=6 only
} else if ( e.sourceURL ) {
- // exclude useless self-reference for generated Error objects
+ // Exclude useless self-reference for generated Error objects
if ( /qunit.js$/.test( e.sourceURL ) ) {
return;
}
- // for actual exceptions, this is useful
+ // For actual exceptions, this is useful
return e.sourceURL + ":" + e.line;
}
}
@@ -243,53 +218,35 @@ function sourceFromStacktrace( offset ) {
* `config` initialized at top of scope
*/
var 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
+ // 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,
- // 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 " +
- "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."
- }
- ],
+ // Placeholder for user-configurable form-exposed URL parameters
+ urlConfig: [],
// Set of all modules.
modules: [],
@@ -306,27 +263,9 @@ var config = {
callbacks: {}
};
-var urlParams = defined.document ? getUrlParams() : {};
-
// Push a loose unnamed module to the modules collection
config.modules.push( config.currentModule );
-if ( urlParams.filter === true ) {
- delete urlParams.filter;
-}
-
-// String search anywhere in moduleName+testName
-config.filter = urlParams.filter;
-
-config.testId = [];
-if ( urlParams.testId ) {
- // Ensure that urlParams.testId is an array
- urlParams.testId = decodeURIComponent( urlParams.testId ).split( "," );
- for (var i = 0; i < urlParams.testId.length; i++ ) {
- config.testId.push( urlParams.testId[ i ] );
- }
-}
-
var loggingCallbacks = {};
// Register logging callbacks
@@ -396,7 +335,7 @@ function verifyLoggingCallbacks() {
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: http://api.qunitjs.com/category/callbacks/"
+ "Reference: https://api.qunitjs.com/category/callbacks/"
);
}
}
@@ -430,7 +369,7 @@ function verifyLoggingCallbacks() {
}
QUnit.pushFailure( error, filePath + ":" + linerNr );
} else {
- QUnit.test( "global failure", extend(function() {
+ QUnit.test( "global failure", extend( function() {
QUnit.pushFailure( error, filePath + ":" + linerNr );
}, { validTest: true } ) );
}
@@ -439,25 +378,23 @@ function verifyLoggingCallbacks() {
return ret;
};
-} )();
-
-QUnit.urlParams = urlParams;
+}() );
// 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.20.0";
+QUnit.version = "1.23.1";
extend( QUnit, {
- // call on start of module test to prepend name to all tests
+ // 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 ( testEnvironment instanceof Function ) {
+ if ( objectType( testEnvironment ) === "function" ) {
executeNow = testEnvironment;
testEnvironment = undefined;
}
@@ -481,7 +418,7 @@ extend( QUnit, {
afterEach: setHook( module, "afterEach" )
};
- if ( executeNow instanceof Function ) {
+ if ( objectType( executeNow ) === "function" ) {
config.moduleStack.push( module );
setCurrentModule( module );
executeNow.call( module.testEnvironment, moduleFns );
@@ -499,7 +436,8 @@ extend( QUnit, {
var module = {
name: moduleName,
parentModule: parentModule,
- tests: []
+ tests: [],
+ moduleId: generateHash( moduleName )
};
var env = {};
@@ -572,7 +510,7 @@ extend( QUnit, {
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;
@@ -633,7 +571,7 @@ extend( QUnit, {
offset = ( offset || 0 ) + 2;
return sourceFromStacktrace( offset );
}
-});
+} );
registerLoggingCallbacks( QUnit );
@@ -656,17 +594,17 @@ function begin() {
// Avoid unnecessary information by not logging modules' test environments
for ( i = 0, l = config.modules.length; i < l; i++ ) {
- modulesLog.push({
+ 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;
@@ -705,7 +643,7 @@ function pauseProcessing() {
if ( config.testTimeout && defined.setTimeout ) {
clearTimeout( config.timeout );
- config.timeout = setTimeout(function() {
+ config.timeout = setTimeout( function() {
if ( config.current ) {
config.current.semaphore = 0;
QUnit.pushFailure( "Test timed out", sourceFromStacktrace( 2 ) );
@@ -722,7 +660,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;
}
@@ -751,7 +689,7 @@ function done() {
passed: config.moduleStats.all - config.moduleStats.bad,
total: config.moduleStats.all,
runtime: now() - config.moduleStats.started
- });
+ } );
}
delete config.previousModule;
@@ -763,7 +701,7 @@ function done() {
passed: passed,
total: config.stats.all,
runtime: runtime
- });
+ } );
}
function setHook( module, hookName ) {
@@ -777,6 +715,8 @@ function setHook( module, hookName ) {
}
var focused = false;
+var priorityCount = 0;
+var unitSampler;
function Test( settings ) {
var i, l;
@@ -799,10 +739,10 @@ function Test( settings ) {
this.testId = generateHash( this.module.name, this.testName );
- this.module.tests.push({
+ this.module.tests.push( {
name: this.testName,
testId: this.testId
- });
+ } );
if ( settings.skip ) {
@@ -838,14 +778,14 @@ 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;
@@ -861,7 +801,7 @@ Test.prototype = {
name: this.testName,
module: this.module.name,
testId: this.testId
- });
+ } );
if ( !config.pollution ) {
saveGlobal();
@@ -890,7 +830,7 @@ Test.prototype = {
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
@@ -999,7 +939,7 @@ Test.prototype = {
// 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.
@@ -1019,8 +959,8 @@ Test.prototype = {
function run() {
- // each of these can by async
- synchronize([
+ // Each of these can by async
+ synchronize( [
function() {
test.before();
},
@@ -1038,31 +978,33 @@ Test.prototype = {
function() {
test.finish();
}
- ]);
+ ] );
}
// Prioritize previously failed tests, detected from sessionStorage
priority = QUnit.config.reorder && defined.sessionStorage &&
+sessionStorage.getItem( "qunit-test-" + this.module.name + "-" + this.testName );
- return synchronize( run, priority );
+ return synchronize( run, priority, config.seed );
},
- push: function( result, actual, expected, message, negative ) {
+ pushResult: function( resultInfo ) {
+
+ // Destructure of resultInfo = { result, actual, expected, message, negative }
var source,
details = {
module: this.module.name,
name: this.testName,
- result: result,
- message: message,
- actual: actual,
- expected: expected,
+ result: resultInfo.result,
+ message: resultInfo.message,
+ actual: resultInfo.actual,
+ expected: resultInfo.expected,
testId: this.testId,
- negative: negative || false,
+ negative: resultInfo.negative || false,
runtime: now() - this.started
};
- if ( !result ) {
+ if ( !resultInfo.result ) {
source = sourceFromStacktrace();
if ( source ) {
@@ -1072,10 +1014,10 @@ Test.prototype = {
runLoggingCallbacks( "log", details );
- this.assertions.push({
- result: !!result,
- message: message
- });
+ this.assertions.push( {
+ result: !!resultInfo.result,
+ message: resultInfo.message
+ } );
},
pushFailure: function( message, source, actual ) {
@@ -1100,10 +1042,10 @@ Test.prototype = {
runLoggingCallbacks( "log", details );
- this.assertions.push({
+ this.assertions.push( {
result: false,
message: message
- });
+ } );
},
resolvePromise: function( promise, phase ) {
@@ -1122,7 +1064,7 @@ Test.prototype = {
" " + 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
@@ -1134,32 +1076,45 @@ Test.prototype = {
},
valid: function() {
- var include,
- filter = config.filter && config.filter.toLowerCase(),
- module = QUnit.urlParams.module && QUnit.urlParams.module.toLowerCase(),
- fullName = ( this.module.name + ": " + this.testName ).toLowerCase();
+ var filter = config.filter,
+ regexFilter = /^(!?)\/([\w\W]*)\/(i?$)/.exec( filter ),
+ module = config.module && config.module.toLowerCase(),
+ fullName = ( this.module.name + ": " + this.testName );
- function testInModuleChain( testModule ) {
+ function moduleChainNameMatch( testModule ) {
var testModuleName = testModule.name ? testModule.name.toLowerCase() : null;
if ( testModuleName === module ) {
return true;
} else if ( testModule.parentModule ) {
- return testInModuleChain( testModule.parentModule );
+ return moduleChainNameMatch( testModule.parentModule );
} else {
return false;
}
}
+ function moduleChainIdMatch( testModule ) {
+ return inArray( testModule.moduleId, config.moduleId ) > -1 ||
+ testModule.parentModule && moduleChainIdMatch( testModule.parentModule );
+ }
+
// Internally-generated tests are always valid
if ( this.callback && this.callback.validTest ) {
return true;
}
- if ( config.testId.length > 0 && inArray( this.testId, config.testId ) < 0 ) {
+ 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 ) {
+
return false;
}
- if ( module && !testInModuleChain( this.module ) ) {
+ if ( module && !moduleChainNameMatch( this.module ) ) {
return false;
}
@@ -1167,7 +1122,23 @@ Test.prototype = {
return true;
}
- include = filter.charAt( 0 ) !== "!";
+ 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 ) !== "!";
if ( !include ) {
filter = filter.slice( 1 );
}
@@ -1240,8 +1211,9 @@ function generateHash( module, testName ) {
return hex.slice( -8 );
}
-function synchronize( callback, priority ) {
- var last = !priority;
+function synchronize( callback, priority, seed ) {
+ var last = !priority,
+ index;
if ( QUnit.objectType( callback ) === "array" ) {
while ( callback.length ) {
@@ -1251,7 +1223,15 @@ function synchronize( callback, priority ) {
}
if ( priority ) {
- priorityFill( callback );
+ 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 );
}
@@ -1261,21 +1241,24 @@ function synchronize( callback, priority ) {
}
}
-// Place previously failed tests on a queue priority line, respecting the order they get assigned.
-function priorityFill( callback ) {
- var queue, prioritizedQueue;
-
- queue = config.queue.slice( priorityFill.pos );
- prioritizedQueue = config.queue.slice( 0, -config.queue.length + priorityFill.pos );
+function unitSamplerGenerator( seed ) {
- queue.unshift( callback );
- queue.unshift.apply( queue, prioritizedQueue );
+ // 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;
- config.queue = queue;
+ // ECMAScript has no unsigned number type
+ if ( sample < 0 ) {
+ sample += 0x100000000;
+ }
- priorityFill.pos += 1;
+ return sample / 0x100000000;
+ };
}
-priorityFill.pos = 0;
function saveGlobal() {
config.pollution = [];
@@ -1284,7 +1267,7 @@ function saveGlobal() {
for ( var key in global ) {
if ( hasOwn.call( global, key ) ) {
- // in Opera sometimes DOM element ids show up here, ignore them
+ // In Opera sometimes DOM element ids show up here, ignore them
if ( /^qunit-test-output/.test( key ) ) {
continue;
}
@@ -1333,12 +1316,12 @@ function test( testName, expected, callback, async ) {
expected = null;
}
- newTest = new Test({
+ newTest = new Test( {
testName: testName,
expected: expected,
async: async,
callback: callback
- });
+ } );
newTest.queue();
}
@@ -1347,10 +1330,10 @@ function test( testName, expected, callback, async ) {
function skip( testName ) {
if ( focused ) { return; }
- var test = new Test({
+ var test = new Test( {
testName: testName,
skip: true
- });
+ } );
test.queue();
}
@@ -1369,12 +1352,12 @@ function only( testName, expected, callback, async ) {
expected = null;
}
- newTest = new Test({
+ newTest = new Test( {
testName: testName,
expected: expected,
async: async,
callback: callback
- });
+ } );
newTest.queue();
}
@@ -1430,7 +1413,21 @@ QUnit.assert = Assert.prototype = {
},
// Exports test.push() to the user API
- push: function( /* result, actual, expected, message, negative */ ) {
+ // 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 }
var assert = this,
currentTest = ( assert instanceof Assert && assert.test ) || QUnit.config.current;
@@ -1453,57 +1450,112 @@ QUnit.assert = Assert.prototype = {
if ( !( assert instanceof Assert ) ) {
assert = currentTest.assert;
}
- return assert.test.push.apply( assert.test, arguments );
+
+ return assert.test.pushResult( resultInfo );
},
ok: function( result, message ) {
message = message || ( result ? "okay" : "failed, expected argument to be truthy, was: " +
QUnit.dump.parse( result ) );
- this.push( !!result, result, true, message );
+ 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.push( !result, result, false, message, true );
+ this.pushResult( {
+ result: !result,
+ actual: result,
+ expected: false,
+ message: message
+ } );
},
equal: function( actual, expected, message ) {
/*jshint eqeqeq:false */
- this.push( expected == actual, actual, expected, message );
+ this.pushResult( {
+ result: expected == actual,
+ actual: actual,
+ expected: expected,
+ message: message
+ } );
},
notEqual: function( actual, expected, message ) {
/*jshint eqeqeq:false */
- this.push( expected != actual, actual, expected, message, true );
+ this.pushResult( {
+ result: expected != actual,
+ actual: actual,
+ expected: expected,
+ message: message,
+ negative: true
+ } );
},
propEqual: function( actual, expected, message ) {
actual = objectValues( actual );
expected = objectValues( expected );
- this.push( QUnit.equiv( actual, expected ), actual, expected, message );
+ this.pushResult( {
+ result: QUnit.equiv( actual, expected ),
+ actual: actual,
+ expected: expected,
+ message: message
+ } );
},
notPropEqual: function( actual, expected, message ) {
actual = objectValues( actual );
expected = objectValues( expected );
- this.push( !QUnit.equiv( actual, expected ), actual, expected, message, true );
+ this.pushResult( {
+ result: !QUnit.equiv( actual, expected ),
+ actual: actual,
+ expected: expected,
+ message: message,
+ negative: true
+ } );
},
deepEqual: function( actual, expected, message ) {
- this.push( QUnit.equiv( actual, expected ), actual, expected, message );
+ this.pushResult( {
+ result: QUnit.equiv( actual, expected ),
+ actual: actual,
+ expected: expected,
+ message: message
+ } );
},
notDeepEqual: function( actual, expected, message ) {
- this.push( !QUnit.equiv( actual, expected ), actual, expected, message, true );
+ this.pushResult( {
+ result: !QUnit.equiv( actual, expected ),
+ actual: actual,
+ expected: expected,
+ message: message,
+ negative: true
+ } );
},
strictEqual: function( actual, expected, message ) {
- this.push( expected === actual, actual, expected, message );
+ this.pushResult( {
+ result: expected === actual,
+ actual: actual,
+ expected: expected,
+ message: message
+ } );
},
notStrictEqual: function( actual, expected, message ) {
- this.push( expected !== actual, actual, expected, message, true );
+ this.pushResult( {
+ result: expected !== actual,
+ actual: actual,
+ expected: expected,
+ message: message,
+ negative: true
+ } );
},
"throws": function( block, expected, message ) {
@@ -1521,7 +1573,7 @@ QUnit.assert = Assert.prototype = {
currentTest.ignoreGlobalErrors = true;
try {
block.call( currentTest.testEnvironment );
- } catch (e) {
+ } catch ( e ) {
actual = e;
}
currentTest.ignoreGlobalErrors = false;
@@ -1529,46 +1581,51 @@ QUnit.assert = Assert.prototype = {
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.push( ok, actual, expectedOutput, message );
+ currentTest.assert.pushResult( {
+ result: ok,
+ actual: actual,
+ expected: expectedOutput,
+ message: message
+ } );
}
};
// Provide an alternative to assert.throws(), for environments that consider throws a reserved word
// Known to us are: Closure Compiler, Narwhal
-(function() {
+( function() {
/*jshint sub:true */
- Assert.prototype.raises = Assert.prototype[ "throws" ];
-}());
+ Assert.prototype.raises = Assert.prototype [ "throws" ]; //jscs:ignore requireDotNotation
+}() );
function errorString( error ) {
var name, message,
@@ -1592,7 +1649,7 @@ function errorString( error ) {
// Test for equality any JavaScript type.
// Author: Philippe Rathé <prathe@gmail.com>
-QUnit.equiv = (function() {
+QUnit.equiv = ( function() {
// Stack to decide between skip/abort functions
var callers = [];
@@ -1601,26 +1658,28 @@ QUnit.equiv = (function() {
var parents = [];
var parentsB = [];
- function useStrictEquality( b, a ) {
+ var getProto = Object.getPrototypeOf || function( obj ) {
- /*jshint eqeqeq:false */
- if ( b instanceof a.constructor || a instanceof b.constructor ) {
+ /*jshint proto: true */
+ return obj.__proto__;
+ };
- // 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;
+ function useStrictEquality( b, a ) {
+
+ // 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();
}
+
+ return a === b;
}
function compareConstructors( a, b ) {
- var getProto = Object.getPrototypeOf || function( obj ) {
-
- /*jshint proto: true */
- return obj.__proto__;
- };
var protoA = getProto( a );
var protoB = getProto( b );
@@ -1649,6 +1708,10 @@ QUnit.equiv = (function() {
return false;
}
+ function getRegExpFlags( regexp ) {
+ return "flags" in regexp ? regexp.flags : regexp.toString().match( /[gimuy]*$/ )[ 0 ];
+ }
+
var callbacks = {
"string": useStrictEquality,
"boolean": useStrictEquality,
@@ -1656,28 +1719,17 @@ QUnit.equiv = (function() {
"null": useStrictEquality,
"undefined": useStrictEquality,
"symbol": useStrictEquality,
+ "date": useStrictEquality,
- "nan": function( b ) {
- return isNaN( b );
- },
-
- "date": function( b, a ) {
- return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf();
+ "nan": function() {
+ return true;
},
"regexp": function( b, a ) {
- return QUnit.objectType( b ) === "regexp" &&
-
- // The regex itself
- a.source === b.source &&
-
- // And its modifiers
- a.global === b.global &&
+ return a.source === b.source &&
- // (gmi) ...
- a.ignoreCase === b.ignoreCase &&
- a.multiline === b.multiline &&
- a.sticky === b.sticky;
+ // Include flags in the comparison
+ getRegExpFlags( a ) === getRegExpFlags( b );
},
// - skip when the property is a method of an instance (OOP)
@@ -1691,14 +1743,10 @@ QUnit.equiv = (function() {
"array": function( b, a ) {
var i, j, len, loop, aCircular, bCircular;
- // b could be an object literal here
- if ( QUnit.objectType( b ) !== "array" ) {
- return false;
- }
-
len = a.length;
if ( len !== b.length ) {
- // safe and faster
+
+ // Safe and faster
return false;
}
@@ -1732,43 +1780,53 @@ QUnit.equiv = (function() {
},
"set": function( b, a ) {
- var aArray, bArray;
+ var innerEq,
+ outerEq = true;
- // `b` could be any object here
- if ( QUnit.objectType( b ) !== "set" ) {
+ if ( a.size !== b.size ) {
return false;
}
- aArray = [];
- a.forEach( function( v ) {
- aArray.push( v );
- });
- bArray = [];
- b.forEach( function( v ) {
- bArray.push( v );
- });
+ a.forEach( function( aVal ) {
+ innerEq = false;
+
+ b.forEach( function( bVal ) {
+ if ( innerEquiv( bVal, aVal ) ) {
+ innerEq = true;
+ }
+ } );
+
+ if ( !innerEq ) {
+ outerEq = false;
+ }
+ } );
- return innerEquiv( bArray, aArray );
+ return outerEq;
},
"map": function( b, a ) {
- var aArray, bArray;
+ var innerEq,
+ outerEq = true;
- // `b` could be any object here
- if ( QUnit.objectType( b ) !== "map" ) {
+ if ( a.size !== b.size ) {
return false;
}
- aArray = [];
- a.forEach( function( v, k ) {
- aArray.push( [ k, v ] );
- });
- bArray = [];
- b.forEach( function( v, k ) {
- bArray.push( [ k, v ] );
- });
+ a.forEach( function( aVal, aKey ) {
+ innerEq = false;
+
+ b.forEach( function( bVal, bKey ) {
+ if ( innerEquiv( [ bVal, bKey ], [ aVal, aKey ] ) ) {
+ innerEq = true;
+ }
+ } );
- return innerEquiv( bArray, aArray );
+ if ( !innerEq ) {
+ outerEq = false;
+ }
+ } );
+
+ return outerEq;
},
"object": function( b, a ) {
@@ -1830,45 +1888,31 @@ QUnit.equiv = (function() {
};
function typeEquiv( a, b ) {
- var prop = QUnit.objectType( a );
- return callbacks[ prop ]( b, a );
+ var type = QUnit.objectType( a );
+ return QUnit.objectType( b ) === type && callbacks[ type ]( b, a );
}
// The real equiv function
- function innerEquiv() {
- var args = [].slice.apply( arguments );
- if ( args.length < 2 ) {
+ function innerEquiv( a, b ) {
- // End transition
+ // We're done when there's nothing more to compare
+ if ( arguments.length < 2 ) {
return true;
}
- return ( (function( a, b ) {
- if ( a === b ) {
+ // Require type-specific equality
+ return ( a === b || typeEquiv( a, b ) ) &&
- // Catch the most you can
- return true;
- } else if ( a === null || b === null || typeof a === "undefined" ||
- typeof b === "undefined" ||
- QUnit.objectType( a ) !== QUnit.objectType( b ) ) {
-
- // Don't lose time with error prone cases
- return false;
- } else {
- return typeEquiv( a, b );
- }
-
- // Apply transition with (1..n) arguments
- }( args[ 0 ], args[ 1 ] ) ) &&
- innerEquiv.apply( this, args.splice( 1, args.length - 1 ) ) );
+ // ...across all consecutive argument pairs
+ ( arguments.length === 2 || innerEquiv.apply( this, [].slice.call( arguments, 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, "\\\"" ) + "\"";
}
@@ -1906,7 +1950,7 @@ QUnit.dump = (function() {
var reName = /^function (\w+)/,
dump = {
- // objType is used mostly internally, you can fix a (custom) type in advance
+ // The objType is used mostly internally, you can fix a (custom) type in advance
parse: function( obj, objType, stack ) {
stack = stack || [];
var res, parser, parserType,
@@ -1950,7 +1994,7 @@ QUnit.dump = (function() {
type = "node";
} else if (
- // native arrays
+ // Native arrays
toString.call( obj ) === "[object Array]" ||
// NodeList objects
@@ -1966,10 +2010,12 @@ QUnit.dump = (function() {
}
return type;
},
+
separator: function() {
return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? "&#160;" : " ";
},
- // 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 "";
@@ -1989,11 +2035,11 @@ 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,
@@ -2010,13 +2056,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" ), "}" );
@@ -2087,7 +2133,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;
@@ -2104,11 +2150,14 @@ 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 an html attribute value
+
+ // Node calls it internally, it's a html attribute value
attribute: quote,
string: quote,
date: quote,
@@ -2116,42 +2165,45 @@ 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;
-// For browser, export only select globals
-if ( defined.document ) {
+// Deprecated
+// Extend assert methods to QUnit for Backwards compatibility
+( function() {
+ var i,
+ assertions = Assert.prototype;
- // Deprecated
- // Extend assert methods to QUnit and Global scope through Backwards compatibility
- (function() {
- var i,
- assertions = Assert.prototype;
+ function applyCurrent( current ) {
+ return function() {
+ var assert = new Assert( QUnit.config.current );
+ current.apply( assert, arguments );
+ };
+ }
- function applyCurrent( current ) {
- return function() {
- var assert = new Assert( QUnit.config.current );
- current.apply( assert, arguments );
- };
- }
+ for ( i in assertions ) {
+ QUnit[ i ] = applyCurrent( assertions[ i ] );
+ }
+}() );
- for ( i in assertions ) {
- QUnit[ i ] = applyCurrent( assertions[ i ] );
- }
- })();
+// For browser, export only select globals
+if ( defined.document ) {
- (function() {
+ ( function() {
var i, l,
keys = [
"test",
@@ -2177,7 +2229,7 @@ if ( defined.document ) {
for ( i = 0, l = keys.length; i < l; i++ ) {
window[ keys[ i ] ] = QUnit[ keys[ i ] ];
}
- })();
+ }() );
window.QUnit = QUnit;
}
@@ -2202,6 +2254,916 @@ if ( typeof define === "function" && define.amd ) {
QUnit.config.autostart = false;
}
+// Get a reference to the global object, like window in browsers
+}( ( 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;
+
+// Match module/test by inclusion in an array
+QUnit.config.moduleId = [].concat( urlParams.moduleId || [] );
+QUnit.config.testId = [].concat( urlParams.testId || [] );
+
+// Exact case-insensitive match of the module name
+QUnit.config.module = urlParams.module;
+
+// Regular expression or case-insenstive substring match against "moduleName: testName"
+QUnit.config.filter = urlParams.filter;
+
+// Test order randomization
+if ( urlParams.seed === true ) {
+
+ // 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;
+}
+
+// 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."
+ }
+);
+
+QUnit.begin( function() {
+ var i, option,
+ urlConfig = QUnit.config.urlConfig;
+
+ for ( i = 0; i < urlConfig.length; i++ ) {
+
+ // Options can be either strings or objects with nonempty "id" properties
+ option = QUnit.config.urlConfig[ i ];
+ if ( typeof option !== "string" ) {
+ option = option.id;
+ }
+
+ if ( QUnit.config[ option ] === undefined ) {
+ QUnit.config[ option ] = urlParams[ option ];
+ }
+ }
+} );
+
+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;
+ }
+ }
+ }
+
+ return urlParams;
+}
+
+// Don't load the HTML Reporter on non-browser environments
+if ( typeof window === "undefined" || !window.document ) {
+ return;
+}
+
+// Deprecated QUnit.init - Ref #530
+// Re-initialize the configuration options
+QUnit.init = function() {
+ var config = QUnit.config;
+
+ config.stats = { all: 0, bad: 0 };
+ config.moduleStats = { all: 0, bad: 0 };
+ config.started = 0;
+ config.updateRate = 1000;
+ config.blocking = false;
+ config.autostart = true;
+ config.autorun = false;
+ config.filter = "";
+ config.queue = [];
+
+ appendInterface();
+};
+
+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() {
+ var x = "qunit-test-string";
+ try {
+ sessionStorage.setItem( x, x );
+ sessionStorage.removeItem( x );
+ return true;
+ } catch ( e ) {
+ return false;
+ }
+ }() )
+ },
+ modulesList = [];
+
+/**
+* Escape text for attribute or text content.
+*/
+function escapeText( s ) {
+ if ( !s ) {
+ return "";
+ }
+ s = s + "";
+
+ // Both single quotes and double quotes (for attributes)
+ return s.replace( /['"<>&]/g, function( s ) {
+ switch ( s ) {
+ case "'":
+ return "&#039;";
+ case "\"":
+ return "&quot;";
+ case "<":
+ return "&lt;";
+ case ">":
+ return "&gt;";
+ case "&":
+ return "&amp;";
+ }
+ } );
+}
+
+/**
+ * @param {HTMLElement} elem
+ * @param {string} type
+ * @param {Function} fn
+ */
+function addEvent( elem, type, fn ) {
+ if ( elem.addEventListener ) {
+
+ // Standards-based browsers
+ 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 );
+ } );
+ }
+}
+
+/**
+ * @param {Array|NodeList} elems
+ * @param {string} type
+ * @param {Function} fn
+ */
+function addEvents( elems, type, fn ) {
+ var i = elems.length;
+ while ( i-- ) {
+ addEvent( elems[ i ], type, fn );
+ }
+}
+
+function hasClass( elem, name ) {
+ return ( " " + elem.className + " " ).indexOf( " " + name + " " ) >= 0;
+}
+
+function addClass( elem, name ) {
+ if ( !hasClass( elem, name ) ) {
+ elem.className += ( elem.className ? " " : "" ) + name;
+ }
+}
+
+function toggleClass( elem, name, force ) {
+ if ( force || typeof force === "undefined" && !hasClass( elem, name ) ) {
+ addClass( elem, name );
+ } else {
+ removeClass( elem, name );
+ }
+}
+
+function removeClass( elem, name ) {
+ var set = " " + elem.className + " ";
+
+ // Class name may appear multiple times
+ while ( set.indexOf( " " + name + " " ) >= 0 ) {
+ set = set.replace( " " + name + " ", " " );
+ }
+
+ // 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 );
+}
+
+function getUrlConfigHtml() {
+ var i, j, val,
+ escaped, escapedTooltip,
+ selection = false,
+ urlConfig = config.urlConfig,
+ urlConfigHtml = "";
+
+ for ( i = 0; i < urlConfig.length; i++ ) {
+
+ // Options can be either strings or objects with nonempty "id" properties
+ val = config.urlConfig[ i ];
+ if ( typeof val === "string" ) {
+ val = {
+ id: val,
+ label: val
+ };
+ }
+
+ escaped = escapeText( val.id );
+ escapedTooltip = escapeText( val.tooltip );
+
+ if ( !val.value || typeof val.value === "string" ) {
+ urlConfigHtml += "<input id='qunit-urlconfig-" + escaped +
+ "' name='" + escaped + "' type='checkbox'" +
+ ( val.value ? " value='" + escapeText( val.value ) + "'" : "" ) +
+ ( config[ val.id ] ? " checked='checked'" : "" ) +
+ " title='" + escapedTooltip + "' /><label for='qunit-urlconfig-" + escaped +
+ "' title='" + escapedTooltip + "'>" + val.label + "</label>";
+ } else {
+ urlConfigHtml += "<label for='qunit-urlconfig-" + escaped +
+ "' title='" + escapedTooltip + "'>" + val.label +
+ ": </label><select id='qunit-urlconfig-" + escaped +
+ "' name='" + escaped + "' title='" + escapedTooltip + "'><option></option>";
+
+ if ( QUnit.is( "array", val.value ) ) {
+ for ( j = 0; j < val.value.length; j++ ) {
+ escaped = escapeText( val.value[ j ] );
+ urlConfigHtml += "<option value='" + escaped + "'" +
+ ( config[ val.id ] === val.value[ j ] ?
+ ( selection = true ) && " selected='selected'" : "" ) +
+ ">" + escaped + "</option>";
+ }
+ } else {
+ for ( j in val.value ) {
+ if ( hasOwn.call( val.value, j ) ) {
+ urlConfigHtml += "<option value='" + escapeText( j ) + "'" +
+ ( config[ val.id ] === j ?
+ ( selection = true ) && " selected='selected'" : "" ) +
+ ">" + escapeText( val.value[ j ] ) + "</option>";
+ }
+ }
+ }
+ if ( config[ val.id ] && !selection ) {
+ escaped = escapeText( config[ val.id ] );
+ urlConfigHtml += "<option value='" + escaped +
+ "' selected='selected' disabled='disabled'>" + escaped + "</option>";
+ }
+ urlConfigHtml += "</select>";
+ }
+ }
+
+ return urlConfigHtml;
+}
+
+// Handle "click" events on toolbar checkboxes and "change" for select menus.
+// Updates the URL with the new state of `config.urlConfig` values.
+function toolbarChanged() {
+ var updatedUrl, value, tests,
+ field = this,
+ params = {};
+
+ // Detect if field is a select menu or a checkbox
+ if ( "selectedIndex" in field ) {
+ value = field.options[ field.selectedIndex ].value || undefined;
+ } else {
+ value = field.checked ? ( field.defaultValue || true ) : undefined;
+ }
+
+ params[ field.name ] = value;
+ updatedUrl = setUrl( params );
+
+ // Check if we can apply the change without a page refresh
+ if ( "hidepassed" === field.name && "replaceState" in window.history ) {
+ QUnit.urlParams[ field.name ] = value;
+ config[ field.name ] = value || false;
+ tests = id( "qunit-tests" );
+ if ( tests ) {
+ toggleClass( tests, "hidepass", value || false );
+ }
+ window.history.replaceState( null, "", updatedUrl );
+ } else {
+ window.location = updatedUrl;
+ }
+}
+
+function setUrl( params ) {
+ var key, arrValue, i,
+ querystring = "?",
+ location = window.location;
+
+ params = QUnit.extend( QUnit.extend( {}, QUnit.urlParams ), params );
+
+ for ( key in params ) {
+
+ // Skip inherited or undefined properties
+ if ( hasOwn.call( params, key ) && params[ key ] !== undefined ) {
+
+ // Output a parameter for each value of this key (but usually just one)
+ arrValue = [].concat( params[ key ] );
+ for ( i = 0; i < arrValue.length; i++ ) {
+ querystring += encodeURIComponent( key );
+ if ( arrValue[ i ] !== true ) {
+ querystring += "=" + encodeURIComponent( arrValue[ i ] );
+ }
+ querystring += "&";
+ }
+ }
+ }
+ return location.protocol + "//" + location.host +
+ location.pathname + querystring.slice( 0, -1 );
+}
+
+function applyUrlParams() {
+ var selectedModule,
+ modulesList = id( "qunit-modulefilter" ),
+ filter = id( "qunit-filter-input" ).value;
+
+ selectedModule = modulesList ?
+ decodeURIComponent( modulesList.options[ modulesList.selectedIndex ].value ) :
+ undefined;
+
+ window.location = setUrl( {
+ module: ( selectedModule === "" ) ? undefined : selectedModule,
+ filter: ( filter === "" ) ? undefined : filter,
+
+ // Remove moduleId and testId filters
+ moduleId: undefined,
+ testId: undefined
+ } );
+}
+
+function toolbarUrlConfigContainer() {
+ var urlConfigContainer = document.createElement( "span" );
+
+ urlConfigContainer.innerHTML = getUrlConfigHtml();
+ addClass( urlConfigContainer, "qunit-url-config" );
+
+ // For oldIE support:
+ // * Add handlers to the individual elements instead of the container
+ // * Use "click" instead of "change" for checkboxes
+ addEvents( urlConfigContainer.getElementsByTagName( "input" ), "click", toolbarChanged );
+ addEvents( urlConfigContainer.getElementsByTagName( "select" ), "change", toolbarChanged );
+
+ return urlConfigContainer;
+}
+
+function toolbarLooseFilter() {
+ var filter = document.createElement( "form" ),
+ label = document.createElement( "label" ),
+ input = document.createElement( "input" ),
+ button = document.createElement( "button" );
+
+ addClass( filter, "qunit-filter" );
+
+ label.innerHTML = "Filter: ";
+
+ input.type = "text";
+ input.value = config.filter || "";
+ input.name = "filter";
+ input.id = "qunit-filter-input";
+
+ button.innerHTML = "Go";
+
+ label.appendChild( input );
+
+ filter.appendChild( label );
+ filter.appendChild( button );
+ addEvent( filter, "submit", function( ev ) {
+ applyUrlParams();
+
+ if ( ev && ev.preventDefault ) {
+ ev.preventDefault();
+ }
+
+ return false;
+ } );
+
+ return filter;
+}
+
+function toolbarModuleFilterHtml() {
+ var i,
+ moduleFilterHtml = "";
+
+ if ( !modulesList.length ) {
+ return false;
+ }
+
+ moduleFilterHtml += "<label for='qunit-modulefilter'>Module: </label>" +
+ "<select id='qunit-modulefilter' name='modulefilter'><option value='' " +
+ ( QUnit.urlParams.module === undefined ? "selected='selected'" : "" ) +
+ ">< All Modules ></option>";
+
+ for ( i = 0; i < modulesList.length; i++ ) {
+ moduleFilterHtml += "<option value='" +
+ escapeText( encodeURIComponent( modulesList[ i ] ) ) + "' " +
+ ( QUnit.urlParams.module === modulesList[ i ] ? "selected='selected'" : "" ) +
+ ">" + escapeText( modulesList[ i ] ) + "</option>";
+ }
+ moduleFilterHtml += "</select>";
+
+ return moduleFilterHtml;
+}
+
+function toolbarModuleFilter() {
+ var toolbar = id( "qunit-testrunner-toolbar" ),
+ moduleFilter = document.createElement( "span" ),
+ moduleFilterHtml = toolbarModuleFilterHtml();
+
+ if ( !toolbar || !moduleFilterHtml ) {
+ return false;
+ }
+
+ moduleFilter.setAttribute( "id", "qunit-modulefilter-container" );
+ moduleFilter.innerHTML = moduleFilterHtml;
+
+ addEvent( moduleFilter.lastChild, "change", applyUrlParams );
+
+ toolbar.appendChild( moduleFilter );
+}
+
+function appendToolbar() {
+ var toolbar = id( "qunit-testrunner-toolbar" );
+
+ if ( toolbar ) {
+ toolbar.appendChild( toolbarUrlConfigContainer() );
+ toolbar.appendChild( toolbarLooseFilter() );
+ toolbarModuleFilter();
+ }
+}
+
+function appendHeader() {
+ var header = id( "qunit-header" );
+
+ if ( header ) {
+ header.innerHTML = "<a href='" + escapeText( unfilteredUrl ) + "'>" + header.innerHTML +
+ "</a> ";
+ }
+}
+
+function appendBanner() {
+ var banner = id( "qunit-banner" );
+
+ if ( banner ) {
+ banner.className = "";
+ }
+}
+
+function appendTestResults() {
+ var tests = id( "qunit-tests" ),
+ result = id( "qunit-testresult" );
+
+ if ( result ) {
+ result.parentNode.removeChild( result );
+ }
+
+ if ( tests ) {
+ tests.innerHTML = "";
+ result = document.createElement( "p" );
+ result.id = "qunit-testresult";
+ result.className = "result";
+ tests.parentNode.insertBefore( result, tests );
+ result.innerHTML = "Running...<br />&#160;";
+ }
+}
+
+function storeFixture() {
+ var fixture = id( "qunit-fixture" );
+ if ( fixture ) {
+ config.fixture = fixture.innerHTML;
+ }
+}
+
+function appendFilteredTest() {
+ var testId = QUnit.config.testId;
+ if ( !testId || testId.length <= 0 ) {
+ return "";
+ }
+ return "<div id='qunit-filteredTest'>Rerunning selected tests: " +
+ escapeText( testId.join( ", " ) ) +
+ " <a id='qunit-clearFilter' href='" +
+ escapeText( unfilteredUrl ) +
+ "'>Run all tests</a></div>";
+}
+
+function appendUserAgent() {
+ var userAgent = id( "qunit-userAgent" );
+
+ if ( userAgent ) {
+ userAgent.innerHTML = "";
+ userAgent.appendChild(
+ document.createTextNode(
+ "QUnit " + QUnit.version + "; " + navigator.userAgent
+ )
+ );
+ }
+}
+
+function appendInterface() {
+ var qunit = id( "qunit" );
+
+ if ( qunit ) {
+ qunit.innerHTML =
+ "<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
+ "<h2 id='qunit-banner'></h2>" +
+ "<div id='qunit-testrunner-toolbar'></div>" +
+ appendFilteredTest() +
+ "<h2 id='qunit-userAgent'></h2>" +
+ "<ol id='qunit-tests'></ol>";
+ }
+
+ appendHeader();
+ appendBanner();
+ appendTestResults();
+ appendUserAgent();
+ appendToolbar();
+}
+
+function appendTestsList( modules ) {
+ var i, l, x, z, test, moduleObj;
+
+ for ( i = 0, l = modules.length; i < l; i++ ) {
+ moduleObj = modules[ i ];
+
+ for ( x = 0, z = moduleObj.tests.length; x < z; x++ ) {
+ test = moduleObj.tests[ x ];
+
+ appendTest( test.name, test.testId, moduleObj.name );
+ }
+ }
+}
+
+function appendTest( name, testId, moduleName ) {
+ var title, rerunTrigger, testBlock, assertList,
+ tests = id( "qunit-tests" );
+
+ if ( !tests ) {
+ return;
+ }
+
+ title = document.createElement( "strong" );
+ title.innerHTML = getNameHtml( name, moduleName );
+
+ rerunTrigger = document.createElement( "a" );
+ rerunTrigger.innerHTML = "Rerun";
+ rerunTrigger.href = setUrl( { testId: testId } );
+
+ testBlock = document.createElement( "li" );
+ testBlock.appendChild( title );
+ testBlock.appendChild( rerunTrigger );
+ testBlock.id = "qunit-test-output-" + testId;
+
+ assertList = document.createElement( "ol" );
+ assertList.className = "qunit-assert-list";
+
+ testBlock.appendChild( assertList );
+
+ tests.appendChild( testBlock );
+}
+
+// HTML Reporter initialization and load
+QUnit.begin( function( details ) {
+ var i, moduleObj, tests;
+
+ // Sort modules by name for the picker
+ for ( i = 0; i < details.modules.length; i++ ) {
+ moduleObj = details.modules[ i ];
+ if ( moduleObj.name ) {
+ modulesList.push( moduleObj.name );
+ }
+ }
+ modulesList.sort( function( a, b ) {
+ return a.localeCompare( b );
+ } );
+
+ // Capture fixture HTML from the page
+ storeFixture();
+
+ // Initialize QUnit elements
+ appendInterface();
+ appendTestsList( details.modules );
+ tests = id( "qunit-tests" );
+ if ( tests && config.hidepassed ) {
+ addClass( tests, "hidepass" );
+ }
+} );
+
+QUnit.done( function( details ) {
+ var i, key,
+ banner = id( "qunit-banner" ),
+ tests = id( "qunit-tests" ),
+ html = [
+ "Tests completed in ",
+ details.runtime,
+ " milliseconds.<br />",
+ "<span class='passed'>",
+ details.passed,
+ "</span> assertions of <span class='total'>",
+ details.total,
+ "</span> passed, <span class='failed'>",
+ details.failed,
+ "</span> failed."
+ ].join( "" );
+
+ if ( banner ) {
+ banner.className = details.failed ? "qunit-fail" : "qunit-pass";
+ }
+
+ if ( tests ) {
+ id( "qunit-testresult" ).innerHTML = html;
+ }
+
+ if ( config.altertitle && document.title ) {
+
+ // Show ✖ for good, ✔ for bad suite result in title
+ // use escape sequences in case file gets loaded with non-utf-8-charset
+ document.title = [
+ ( details.failed ? "\u2716" : "\u2714" ),
+ document.title.replace( /^[\u2714\u2716] /i, "" )
+ ].join( " " );
+ }
+
+ // Clear own sessionStorage items if all tests passed
+ if ( config.reorder && defined.sessionStorage && details.failed === 0 ) {
+ for ( i = 0; i < sessionStorage.length; i++ ) {
+ key = sessionStorage.key( i++ );
+ if ( key.indexOf( "qunit-test-" ) === 0 ) {
+ sessionStorage.removeItem( key );
+ }
+ }
+ }
+
+ // Scroll back to top to show results
+ if ( config.scrolltop && window.scrollTo ) {
+ window.scrollTo( 0, 0 );
+ }
+} );
+
+function getNameHtml( name, module ) {
+ var nameHtml = "";
+
+ if ( module ) {
+ nameHtml = "<span class='module-name'>" + escapeText( module ) + "</span>: ";
+ }
+
+ nameHtml += "<span class='test-name'>" + escapeText( name ) + "</span>";
+
+ return nameHtml;
+}
+
+QUnit.testStart( function( details ) {
+ var running, testBlock, bad;
+
+ testBlock = id( "qunit-test-output-" + details.testId );
+ if ( testBlock ) {
+ testBlock.className = "running";
+ } else {
+
+ // Report later registered tests
+ appendTest( details.name, details.testId, details.module );
+ }
+
+ running = id( "qunit-testresult" );
+ if ( running ) {
+ bad = QUnit.config.reorder && defined.sessionStorage &&
+ +sessionStorage.getItem( "qunit-test-" + details.module + "-" + details.name );
+
+ running.innerHTML = ( bad ?
+ "Rerunning previously failed test: <br />" :
+ "Running: <br />" ) +
+ getNameHtml( details.name, details.module );
+ }
+
+} );
+
+function stripHtml( string ) {
+
+ // Strip tags, html entity and whitespaces
+ return string.replace( /<\/?[^>]+(>|$)/g, "" ).replace( /\&quot;/g, "" ).replace( /\s+/g, "" );
+}
+
+QUnit.log( function( details ) {
+ var assertList, assertLi,
+ message, expected, actual, diff,
+ showDiff = false,
+ testItem = id( "qunit-test-output-" + details.testId );
+
+ if ( !testItem ) {
+ return;
+ }
+
+ message = escapeText( details.message ) || ( details.result ? "okay" : "failed" );
+ message = "<span class='test-message'>" + message + "</span>";
+ message += "<span class='runtime'>@ " + details.runtime + " ms</span>";
+
+ // The pushFailure doesn't provide details.expected
+ // when it calls, it's implicit to also not show expected and diff stuff
+ // Also, we need to check details.expected existence, as it can exist and be undefined
+ if ( !details.result && hasOwn.call( details, "expected" ) ) {
+ if ( details.negative ) {
+ expected = "NOT " + QUnit.dump.parse( details.expected );
+ } else {
+ expected = QUnit.dump.parse( details.expected );
+ }
+
+ actual = QUnit.dump.parse( details.actual );
+ message += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" +
+ escapeText( expected ) +
+ "</pre></td></tr>";
+
+ if ( actual !== expected ) {
+
+ message += "<tr class='test-actual'><th>Result: </th><td><pre>" +
+ escapeText( actual ) + "</pre></td></tr>";
+
+ // Don't show diff if actual or expected are booleans
+ if ( !( /^(true|false)$/.test( actual ) ) &&
+ !( /^(true|false)$/.test( expected ) ) ) {
+ diff = QUnit.diff( expected, actual );
+ showDiff = stripHtml( diff ).length !==
+ stripHtml( expected ).length +
+ stripHtml( actual ).length;
+ }
+
+ // Don't show diff if expected and actual are totally different
+ if ( showDiff ) {
+ message += "<tr class='test-diff'><th>Diff: </th><td><pre>" +
+ diff + "</pre></td></tr>";
+ }
+ } else if ( expected.indexOf( "[object Array]" ) !== -1 ||
+ expected.indexOf( "[object Object]" ) !== -1 ) {
+ message += "<tr class='test-message'><th>Message: </th><td>" +
+ "Diff suppressed as the depth of object is more than current max depth (" +
+ QUnit.config.maxDepth + ").<p>Hint: Use <code>QUnit.dump.maxDepth</code> to " +
+ " run with a higher max depth or <a href='" +
+ escapeText( setUrl( { maxDepth: -1 } ) ) + "'>" +
+ "Rerun</a> without max depth.</p></td></tr>";
+ } else {
+ message += "<tr class='test-message'><th>Message: </th><td>" +
+ "Diff suppressed as the expected and actual results have an equivalent" +
+ " serialization</td></tr>";
+ }
+
+ if ( details.source ) {
+ message += "<tr class='test-source'><th>Source: </th><td><pre>" +
+ escapeText( details.source ) + "</pre></td></tr>";
+ }
+
+ message += "</table>";
+
+ // This occurs when pushFailure is set and we have an extracted stack trace
+ } else if ( !details.result && details.source ) {
+ message += "<table>" +
+ "<tr class='test-source'><th>Source: </th><td><pre>" +
+ escapeText( details.source ) + "</pre></td></tr>" +
+ "</table>";
+ }
+
+ assertList = testItem.getElementsByTagName( "ol" )[ 0 ];
+
+ assertLi = document.createElement( "li" );
+ assertLi.className = details.result ? "pass" : "fail";
+ assertLi.innerHTML = message;
+ assertList.appendChild( assertLi );
+} );
+
+QUnit.testDone( function( details ) {
+ var testTitle, time, testItem, assertList,
+ good, bad, testCounts, skipped, sourceName,
+ tests = id( "qunit-tests" );
+
+ if ( !tests ) {
+ return;
+ }
+
+ testItem = id( "qunit-test-output-" + details.testId );
+
+ assertList = testItem.getElementsByTagName( "ol" )[ 0 ];
+
+ good = details.passed;
+ bad = details.failed;
+
+ // Store result when possible
+ if ( config.reorder && defined.sessionStorage ) {
+ if ( bad ) {
+ sessionStorage.setItem( "qunit-test-" + details.module + "-" + details.name, bad );
+ } else {
+ sessionStorage.removeItem( "qunit-test-" + details.module + "-" + details.name );
+ }
+ }
+
+ if ( bad === 0 ) {
+
+ // Collapse the passing tests
+ addClass( assertList, "qunit-collapsed" );
+ } else if ( bad && config.collapse && !collapseNext ) {
+
+ // Skip collapsing the first failing test
+ collapseNext = true;
+ } else {
+
+ // Collapse remaining tests
+ addClass( assertList, "qunit-collapsed" );
+ }
+
+ // The testItem.firstChild is the test name
+ testTitle = testItem.firstChild;
+
+ testCounts = bad ?
+ "<b class='failed'>" + bad + "</b>, " + "<b class='passed'>" + good + "</b>, " :
+ "";
+
+ testTitle.innerHTML += " <b class='counts'>(" + testCounts +
+ details.assertions.length + ")</b>";
+
+ if ( details.skipped ) {
+ testItem.className = "skipped";
+ skipped = document.createElement( "em" );
+ skipped.className = "qunit-skipped-label";
+ skipped.innerHTML = "skipped";
+ testItem.insertBefore( skipped, testTitle );
+ } else {
+ addEvent( testTitle, "click", function() {
+ toggleClass( assertList, "qunit-collapsed" );
+ } );
+
+ testItem.className = bad ? "fail" : "pass";
+
+ time = document.createElement( "span" );
+ time.className = "runtime";
+ time.innerHTML = details.runtime + " ms";
+ testItem.insertBefore( time, assertList );
+ }
+
+ // Show the source of the test when showing assertions
+ if ( details.source ) {
+ sourceName = document.createElement( "p" );
+ sourceName.innerHTML = "<strong>Source: </strong>" + details.source;
+ addClass( sourceName, "qunit-source" );
+ if ( bad === 0 ) {
+ addClass( sourceName, "qunit-collapsed" );
+ }
+ addEvent( testTitle, "click", function() {
+ toggleClass( sourceName, "qunit-collapsed" );
+ } );
+ testItem.appendChild( sourceName );
+ }
+} );
+
+// Avoid readyState issue with phantomjs
+// Ref: #818
+var notPhantom = ( function( p ) {
+ return !( p && p.version && p.version.major > 0 );
+} )( window.phantom );
+
+if ( notPhantom && document.readyState === "complete" ) {
+ QUnit.load();
+} else {
+ addEvent( window, "load", QUnit.load );
+}
+
/*
* This file is a modified version of google-diff-match-patch's JavaScript implementation
* (https://code.google.com/p/google-diff-match-patch/source/browse/trunk/javascript/diff_match_patch_uncompressed.js),
@@ -2210,13 +3172,13 @@ if ( typeof define === "function" && define.amd ) {
* The original source of google-diff-match-patch is attributable and licensed as follows:
*
* Copyright 2006 Google Inc.
- * http://code.google.com/p/google-diff-match-patch/
+ * https://code.google.com/p/google-diff-match-patch/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -2321,14 +3283,19 @@ QUnit.diff = ( function() {
equalitiesLength = 0; // Keeping our own length var is faster in JS.
/** @type {?string} */
lastequality = null;
+
// Always equal to diffs[equalities[equalitiesLength - 1]][1]
pointer = 0; // Index of current position.
+
// Is there an insertion operation before the last equality.
preIns = false;
+
// Is there a deletion operation before the last equality.
preDel = false;
+
// Is there an insertion operation after the last equality.
postIns = false;
+
// Is there a deletion operation after the last equality.
postDel = false;
while ( pointer < diffs.length ) {
@@ -2383,6 +3350,7 @@ QUnit.diff = ( function() {
equalitiesLength--; // Throw away the equality we just deleted;
lastequality = null;
if ( preIns && preDel ) {
+
// No changes made which could affect previous entry, keep going.
postIns = postDel = true;
equalitiesLength = 0;
@@ -2416,13 +3384,13 @@ QUnit.diff = ( function() {
data = diffs[ x ][ 1 ]; // Text of change.
switch ( op ) {
case DIFF_INSERT:
- html[ x ] = "<ins>" + data + "</ins>";
+ html[ x ] = "<ins>" + escapeText( data ) + "</ins>";
break;
case DIFF_DELETE:
- html[ x ] = "<del>" + data + "</del>";
+ html[ x ] = "<del>" + escapeText( data ) + "</del>";
break;
case DIFF_EQUAL:
- html[ x ] = "<span>" + data + "</span>";
+ html[ x ] = "<span>" + escapeText( data ) + "</span>";
break;
}
}
@@ -2438,12 +3406,14 @@ QUnit.diff = ( function() {
*/
DiffMatchPatch.prototype.diffCommonPrefix = function( text1, text2 ) {
var pointermid, pointermax, pointermin, pointerstart;
+
// Quick check for common null cases.
if ( !text1 || !text2 || text1.charAt( 0 ) !== text2.charAt( 0 ) ) {
return 0;
}
+
// Binary search.
- // Performance analysis: http://neil.fraser.name/news/2007/10/09/
+ // Performance analysis: https://neil.fraser.name/news/2007/10/09/
pointermin = 0;
pointermax = Math.min( text1.length, text2.length );
pointermid = pointermax;
@@ -2469,14 +3439,16 @@ QUnit.diff = ( function() {
*/
DiffMatchPatch.prototype.diffCommonSuffix = function( text1, text2 ) {
var pointermid, pointermax, pointermin, pointerend;
+
// Quick check for common null cases.
if ( !text1 ||
!text2 ||
text1.charAt( text1.length - 1 ) !== text2.charAt( text2.length - 1 ) ) {
return 0;
}
+
// Binary search.
- // Performance analysis: http://neil.fraser.name/news/2007/10/09/
+ // Performance analysis: https://neil.fraser.name/news/2007/10/09/
pointermin = 0;
pointermax = Math.min( text1.length, text2.length );
pointermid = pointermax;
@@ -2512,6 +3484,7 @@ QUnit.diff = ( function() {
midCommon, diffsA, diffsB;
if ( !text1 ) {
+
// Just add some text (speedup).
return [
[ DIFF_INSERT, text2 ]
@@ -2519,6 +3492,7 @@ QUnit.diff = ( function() {
}
if ( !text2 ) {
+
// Just delete some text (speedup).
return [
[ DIFF_DELETE, text1 ]
@@ -2529,12 +3503,14 @@ QUnit.diff = ( function() {
shorttext = text1.length > text2.length ? text2 : text1;
i = longtext.indexOf( shorttext );
if ( i !== -1 ) {
+
// Shorter text is inside the longer text (speedup).
diffs = [
[ DIFF_INSERT, longtext.substring( 0, i ) ],
[ DIFF_EQUAL, shorttext ],
[ DIFF_INSERT, longtext.substring( i + shorttext.length ) ]
];
+
// Swap insertions for deletions if diff is reversed.
if ( text1.length > text2.length ) {
diffs[ 0 ][ 0 ] = diffs[ 2 ][ 0 ] = DIFF_DELETE;
@@ -2543,6 +3519,7 @@ QUnit.diff = ( function() {
}
if ( shorttext.length === 1 ) {
+
// Single character string.
// After the previous speedup, the character can't be an equality.
return [
@@ -2554,15 +3531,18 @@ QUnit.diff = ( function() {
// Check to see if the problem can be split in two.
hm = this.diffHalfMatch( text1, text2 );
if ( hm ) {
+
// A half-match was found, sort out the return data.
text1A = hm[ 0 ];
text1B = hm[ 1 ];
text2A = hm[ 2 ];
text2B = hm[ 3 ];
midCommon = hm[ 4 ];
+
// Send both pairs off for separate processing.
diffsA = this.DiffMain( text1A, text2A, checklines, deadline );
diffsB = this.DiffMain( text1B, text2B, checklines, deadline );
+
// Merge the results.
return diffsA.concat( [
[ DIFF_EQUAL, midCommon ]
@@ -2614,6 +3594,7 @@ QUnit.diff = ( function() {
function diffHalfMatchI( longtext, shorttext, i ) {
var seed, j, bestCommon, prefixLength, suffixLength,
bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB;
+
// Start with a 1/4 length substring at position i as a seed.
seed = longtext.substring( i, i + Math.floor( longtext.length / 4 ) );
j = -1;
@@ -2644,6 +3625,7 @@ QUnit.diff = ( function() {
// First check if the second quarter is the seed for a half-match.
hm1 = diffHalfMatchI( longtext, shorttext,
Math.ceil( longtext.length / 4 ) );
+
// Check again based on the third quarter.
hm2 = diffHalfMatchI( longtext, shorttext,
Math.ceil( longtext.length / 2 ) );
@@ -2654,6 +3636,7 @@ QUnit.diff = ( function() {
} else if ( !hm1 ) {
hm = hm2;
} else {
+
// Both matched. Select the longest.
hm = hm1[ 4 ].length > hm2[ 4 ].length ? hm1 : hm2;
}
@@ -2688,6 +3671,7 @@ QUnit.diff = ( function() {
DiffMatchPatch.prototype.diffLineMode = function( text1, text2, deadline ) {
var a, diffs, linearray, pointer, countInsert,
countDelete, textInsert, textDelete, j;
+
// Scan the text on a line-by-line basis first.
a = this.diffLinesToChars( text1, text2 );
text1 = a.chars1;
@@ -2698,6 +3682,7 @@ QUnit.diff = ( function() {
// Convert the diff back to original text.
this.diffCharsToLines( diffs, linearray );
+
// Eliminate freak matches (e.g. blank lines)
this.diffCleanupSemantic( diffs );
@@ -2720,8 +3705,10 @@ QUnit.diff = ( function() {
textDelete += diffs[ pointer ][ 1 ];
break;
case DIFF_EQUAL:
+
// Upon reaching an equality, check for prior redundancies.
if ( countDelete >= 1 && countInsert >= 1 ) {
+
// Delete the offending records and add the merged ones.
diffs.splice( pointer - countDelete - countInsert,
countDelete + countInsert );
@@ -2759,6 +3746,7 @@ QUnit.diff = ( function() {
var text1Length, text2Length, maxD, vOffset, vLength,
v1, v2, x, delta, front, k1start, k1end, k2start,
k2end, k2Offset, k1Offset, x1, x2, y1, y2, d, k1, k2;
+
// Cache the text lengths to prevent multiple calls.
text1Length = text1.length;
text2Length = text2.length;
@@ -2767,6 +3755,7 @@ QUnit.diff = ( function() {
vLength = 2 * maxD;
v1 = new Array( vLength );
v2 = new Array( vLength );
+
// Setting all elements to -1 is faster in Chrome & Firefox than mixing
// integers and undefined.
for ( x = 0; x < vLength; x++ ) {
@@ -2776,9 +3765,11 @@ QUnit.diff = ( function() {
v1[ vOffset + 1 ] = 0;
v2[ vOffset + 1 ] = 0;
delta = text1Length - text2Length;
+
// If the total number of characters is odd, then the front path will collide
// with the reverse path.
front = ( delta % 2 !== 0 );
+
// Offsets for start and end of k loop.
// Prevents mapping of space beyond the grid.
k1start = 0;
@@ -2786,6 +3777,7 @@ QUnit.diff = ( function() {
k2start = 0;
k2end = 0;
for ( d = 0; d < maxD; d++ ) {
+
// Bail out if deadline is reached.
if ( ( new Date() ).getTime() > deadline ) {
break;
@@ -2807,17 +3799,21 @@ QUnit.diff = ( function() {
}
v1[ k1Offset ] = x1;
if ( x1 > text1Length ) {
+
// Ran off the right of the graph.
k1end += 2;
} else if ( y1 > text2Length ) {
+
// Ran off the bottom of the graph.
k1start += 2;
} else if ( front ) {
k2Offset = vOffset + delta - k1;
if ( k2Offset >= 0 && k2Offset < vLength && v2[ k2Offset ] !== -1 ) {
+
// Mirror x2 onto top-left coordinate system.
x2 = text1Length - v2[ k2Offset ];
if ( x1 >= x2 ) {
+
// Overlap detected.
return this.diffBisectSplit( text1, text2, x1, y1, deadline );
}
@@ -2842,9 +3838,11 @@ QUnit.diff = ( function() {
}
v2[ k2Offset ] = x2;
if ( x2 > text1Length ) {
+
// Ran off the left of the graph.
k2end += 2;
} else if ( y2 > text2Length ) {
+
// Ran off the top of the graph.
k2start += 2;
} else if ( !front ) {
@@ -2852,9 +3850,11 @@ QUnit.diff = ( function() {
if ( k1Offset >= 0 && k1Offset < vLength && v1[ k1Offset ] !== -1 ) {
x1 = v1[ k1Offset ];
y1 = vOffset + x1 - k1Offset;
+
// Mirror x2 onto top-left coordinate system.
x2 = text1Length - x2;
if ( x1 >= x2 ) {
+
// Overlap detected.
return this.diffBisectSplit( text1, text2, x1, y1, deadline );
}
@@ -2862,6 +3862,7 @@ QUnit.diff = ( function() {
}
}
}
+
// Diff took too long and hit the deadline or
// number of diffs equals number of characters, no commonality at all.
return [
@@ -2908,11 +3909,14 @@ QUnit.diff = ( function() {
equalitiesLength = 0; // Keeping our own length var is faster in JS.
/** @type {?string} */
lastequality = null;
+
// Always equal to diffs[equalities[equalitiesLength - 1]][1]
pointer = 0; // Index of current position.
+
// Number of characters that changed prior to the equality.
lengthInsertions1 = 0;
lengthDeletions1 = 0;
+
// Number of characters that changed after the equality.
lengthInsertions2 = 0;
lengthDeletions2 = 0;
@@ -2930,6 +3934,7 @@ QUnit.diff = ( function() {
} else {
lengthDeletions2 += diffs[ pointer ][ 1 ].length;
}
+
// Eliminate an equality that is smaller or equal to the edits on both
// sides of it.
if ( lastequality && ( lastequality.length <=
@@ -2988,6 +3993,7 @@ QUnit.diff = ( function() {
if ( overlapLength1 >= overlapLength2 ) {
if ( overlapLength1 >= deletion.length / 2 ||
overlapLength1 >= insertion.length / 2 ) {
+
// Overlap found. Insert an equality and trim the surrounding edits.
diffs.splice(
pointer,
@@ -3037,13 +4043,16 @@ QUnit.diff = ( function() {
DiffMatchPatch.prototype.diffCommonOverlap = function( text1, text2 ) {
var text1Length, text2Length, textLength,
best, length, pattern, found;
+
// Cache the text lengths to prevent multiple calls.
text1Length = text1.length;
text2Length = text2.length;
+
// Eliminate the null case.
if ( text1Length === 0 || text2Length === 0 ) {
return 0;
}
+
// Truncate the longer string.
if ( text1Length > text2Length ) {
text1 = text1.substring( text1Length - text2Length );
@@ -3051,6 +4060,7 @@ QUnit.diff = ( function() {
text2 = text2.substring( 0, text1Length );
}
textLength = Math.min( text1Length, text2Length );
+
// Quick check for the worst case.
if ( text1 === text2 ) {
return textLength;
@@ -3058,7 +4068,7 @@ QUnit.diff = ( function() {
// Start by looking for a single character match
// and increase length until no match is found.
- // Performance analysis: http://neil.fraser.name/news/2010/11/04/
+ // Performance analysis: https://neil.fraser.name/news/2010/11/04/
best = 0;
length = 1;
while ( true ) {
@@ -3089,8 +4099,8 @@ QUnit.diff = ( function() {
*/
DiffMatchPatch.prototype.diffLinesToChars = function( text1, text2 ) {
var lineArray, lineHash, chars1, chars2;
- lineArray = []; // e.g. lineArray[4] === 'Hello\n'
- lineHash = {}; // e.g. lineHash['Hello\n'] === 4
+ lineArray = []; // E.g. lineArray[4] === 'Hello\n'
+ lineHash = {}; // E.g. lineHash['Hello\n'] === 4
// '\x00' is a valid character, but various debuggers don't like it.
// So we'll insert a junk entry to avoid generating a null character.
@@ -3107,11 +4117,13 @@ QUnit.diff = ( function() {
function diffLinesToCharsMunge( text ) {
var chars, lineStart, lineEnd, lineArrayLength, line;
chars = "";
+
// Walk the text, pulling out a substring for each line.
// text.split('\n') would would temporarily double our memory footprint.
// Modifying text would create many large strings to garbage collect.
lineStart = 0;
lineEnd = -1;
+
// Keeping our own length variable is faster than looking it up.
lineArrayLength = lineArray.length;
while ( lineEnd < text.length - 1 ) {
@@ -3190,10 +4202,12 @@ QUnit.diff = ( function() {
pointer++;
break;
case DIFF_EQUAL:
+
// Upon reaching an equality, check for prior redundancies.
if ( countDelete + countInsert > 1 ) {
if ( countDelete !== 0 && countInsert !== 0 ) {
- // Factor out any common prefixies.
+
+ // Factor out any common prefixes.
commonlength = this.diffCommonPrefix( textInsert, textDelete );
if ( commonlength !== 0 ) {
if ( ( pointer - countDelete - countInsert ) > 0 &&
@@ -3210,6 +4224,7 @@ QUnit.diff = ( function() {
textInsert = textInsert.substring( commonlength );
textDelete = textDelete.substring( commonlength );
}
+
// Factor out any common suffixies.
commonlength = this.diffCommonSuffix( textInsert, textDelete );
if ( commonlength !== 0 ) {
@@ -3221,6 +4236,7 @@ QUnit.diff = ( function() {
commonlength );
}
}
+
// Delete the offending records and add the merged ones.
if ( countDelete === 0 ) {
diffs.splice( pointer - countInsert,
@@ -3297,6 +4313,7 @@ QUnit.diff = ( function() {
}
pointer++;
}
+
// If shifts were made, the diff needs reordering and another shift sweep.
if ( changes ) {
this.diffCleanupMerge( diffs );
@@ -3314,845 +4331,4 @@ QUnit.diff = ( function() {
};
}() );
-// Get a reference to the global object, like window in browsers
-}( (function() {
- return this;
-})() ));
-
-(function() {
-
-// Don't load the HTML Reporter on non-Browser environments
-if ( typeof window === "undefined" || !window.document ) {
- return;
-}
-
-// Deprecated QUnit.init - Ref #530
-// Re-initialize the configuration options
-QUnit.init = function() {
- var tests, banner, result, qunit,
- config = QUnit.config;
-
- config.stats = { all: 0, bad: 0 };
- config.moduleStats = { all: 0, bad: 0 };
- config.started = 0;
- config.updateRate = 1000;
- config.blocking = false;
- config.autostart = true;
- config.autorun = false;
- config.filter = "";
- config.queue = [];
-
- // 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 =
- "<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
- "<h2 id='qunit-banner'></h2>" +
- "<div id='qunit-testrunner-toolbar'></div>" +
- "<h2 id='qunit-userAgent'></h2>" +
- "<ol id='qunit-tests'></ol>";
- }
-
- 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...<br />&#160;";
- }
-};
-
-var config = QUnit.config,
- collapseNext = false,
- hasOwn = Object.prototype.hasOwnProperty,
- defined = {
- document: window.document !== undefined,
- sessionStorage: (function() {
- var x = "qunit-test-string";
- try {
- sessionStorage.setItem( x, x );
- sessionStorage.removeItem( x );
- return true;
- } catch ( e ) {
- return false;
- }
- }())
- },
- modulesList = [];
-
-/**
-* Escape text for attribute or text content.
-*/
-function escapeText( s ) {
- if ( !s ) {
- return "";
- }
- s = s + "";
-
- // Both single quotes and double quotes (for attributes)
- return s.replace( /['"<>&]/g, function( s ) {
- switch ( s ) {
- case "'":
- return "&#039;";
- case "\"":
- return "&quot;";
- case "<":
- return "&lt;";
- case ">":
- return "&gt;";
- case "&":
- return "&amp;";
- }
- });
-}
-
-/**
- * @param {HTMLElement} elem
- * @param {string} type
- * @param {Function} fn
- */
-function addEvent( elem, type, fn ) {
- if ( elem.addEventListener ) {
-
- // Standards-based browsers
- 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 );
- });
- }
-}
-
-/**
- * @param {Array|NodeList} elems
- * @param {string} type
- * @param {Function} fn
- */
-function addEvents( elems, type, fn ) {
- var i = elems.length;
- while ( i-- ) {
- addEvent( elems[ i ], type, fn );
- }
-}
-
-function hasClass( elem, name ) {
- return ( " " + elem.className + " " ).indexOf( " " + name + " " ) >= 0;
-}
-
-function addClass( elem, name ) {
- if ( !hasClass( elem, name ) ) {
- elem.className += ( elem.className ? " " : "" ) + name;
- }
-}
-
-function toggleClass( elem, name ) {
- if ( hasClass( elem, name ) ) {
- removeClass( elem, name );
- } else {
- addClass( elem, name );
- }
-}
-
-function removeClass( elem, name ) {
- var set = " " + elem.className + " ";
-
- // Class name may appear multiple times
- while ( set.indexOf( " " + name + " " ) >= 0 ) {
- set = set.replace( " " + name + " ", " " );
- }
-
- // trim for prettiness
- elem.className = typeof set.trim === "function" ? set.trim() : set.replace( /^\s+|\s+$/g, "" );
-}
-
-function id( name ) {
- return defined.document && document.getElementById && document.getElementById( name );
-}
-
-function getUrlConfigHtml() {
- var i, j, val,
- escaped, escapedTooltip,
- selection = false,
- len = config.urlConfig.length,
- urlConfigHtml = "";
-
- for ( i = 0; i < len; i++ ) {
- val = config.urlConfig[ i ];
- if ( typeof val === "string" ) {
- val = {
- id: val,
- label: val
- };
- }
-
- 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 += "<input id='qunit-urlconfig-" + escaped +
- "' name='" + escaped + "' type='checkbox'" +
- ( val.value ? " value='" + escapeText( val.value ) + "'" : "" ) +
- ( config[ val.id ] ? " checked='checked'" : "" ) +
- " title='" + escapedTooltip + "' /><label for='qunit-urlconfig-" + escaped +
- "' title='" + escapedTooltip + "'>" + val.label + "</label>";
- } else {
- urlConfigHtml += "<label for='qunit-urlconfig-" + escaped +
- "' title='" + escapedTooltip + "'>" + val.label +
- ": </label><select id='qunit-urlconfig-" + escaped +
- "' name='" + escaped + "' title='" + escapedTooltip + "'><option></option>";
-
- if ( QUnit.is( "array", val.value ) ) {
- for ( j = 0; j < val.value.length; j++ ) {
- escaped = escapeText( val.value[ j ] );
- urlConfigHtml += "<option value='" + escaped + "'" +
- ( config[ val.id ] === val.value[ j ] ?
- ( selection = true ) && " selected='selected'" : "" ) +
- ">" + escaped + "</option>";
- }
- } else {
- for ( j in val.value ) {
- if ( hasOwn.call( val.value, j ) ) {
- urlConfigHtml += "<option value='" + escapeText( j ) + "'" +
- ( config[ val.id ] === j ?
- ( selection = true ) && " selected='selected'" : "" ) +
- ">" + escapeText( val.value[ j ] ) + "</option>";
- }
- }
- }
- if ( config[ val.id ] && !selection ) {
- escaped = escapeText( config[ val.id ] );
- urlConfigHtml += "<option value='" + escaped +
- "' selected='selected' disabled='disabled'>" + escaped + "</option>";
- }
- urlConfigHtml += "</select>";
- }
- }
-
- return urlConfigHtml;
-}
-
-// Handle "click" events on toolbar checkboxes and "change" for select menus.
-// Updates the URL with the new state of `config.urlConfig` values.
-function toolbarChanged() {
- var updatedUrl, value,
- field = this,
- params = {};
-
- // Detect if field is a select menu or a checkbox
- if ( "selectedIndex" in field ) {
- value = field.options[ field.selectedIndex ].value || undefined;
- } else {
- value = field.checked ? ( field.defaultValue || true ) : undefined;
- }
-
- params[ field.name ] = value;
- updatedUrl = setUrl( params );
-
- if ( "hidepassed" === field.name && "replaceState" in window.history ) {
- config[ field.name ] = value || false;
- if ( value ) {
- addClass( id( "qunit-tests" ), "hidepass" );
- } else {
- removeClass( id( "qunit-tests" ), "hidepass" );
- }
-
- // It is not necessary to refresh the whole page
- window.history.replaceState( null, "", updatedUrl );
- } else {
- window.location = updatedUrl;
- }
-}
-
-function setUrl( params ) {
- var key,
- querystring = "?";
-
- params = QUnit.extend( QUnit.extend( {}, QUnit.urlParams ), params );
-
- for ( key in params ) {
- if ( hasOwn.call( params, key ) ) {
- if ( params[ key ] === undefined ) {
- continue;
- }
- querystring += encodeURIComponent( key );
- if ( params[ key ] !== true ) {
- querystring += "=" + encodeURIComponent( params[ key ] );
- }
- querystring += "&";
- }
- }
- return location.protocol + "//" + location.host +
- location.pathname + querystring.slice( 0, -1 );
-}
-
-function applyUrlParams() {
- var selectedModule,
- modulesList = id( "qunit-modulefilter" ),
- filter = id( "qunit-filter-input" ).value;
-
- selectedModule = modulesList ?
- decodeURIComponent( modulesList.options[ modulesList.selectedIndex ].value ) :
- undefined;
-
- window.location = setUrl({
- module: ( selectedModule === "" ) ? undefined : selectedModule,
- filter: ( filter === "" ) ? undefined : filter,
-
- // Remove testId filter
- testId: undefined
- });
-}
-
-function toolbarUrlConfigContainer() {
- var urlConfigContainer = document.createElement( "span" );
-
- urlConfigContainer.innerHTML = getUrlConfigHtml();
- addClass( urlConfigContainer, "qunit-url-config" );
-
- // For oldIE support:
- // * Add handlers to the individual elements instead of the container
- // * Use "click" instead of "change" for checkboxes
- addEvents( urlConfigContainer.getElementsByTagName( "input" ), "click", toolbarChanged );
- addEvents( urlConfigContainer.getElementsByTagName( "select" ), "change", toolbarChanged );
-
- return urlConfigContainer;
-}
-
-function toolbarLooseFilter() {
- var filter = document.createElement( "form" ),
- label = document.createElement( "label" ),
- input = document.createElement( "input" ),
- button = document.createElement( "button" );
-
- addClass( filter, "qunit-filter" );
-
- label.innerHTML = "Filter: ";
-
- input.type = "text";
- input.value = config.filter || "";
- input.name = "filter";
- input.id = "qunit-filter-input";
-
- button.innerHTML = "Go";
-
- label.appendChild( input );
-
- filter.appendChild( label );
- filter.appendChild( button );
- addEvent( filter, "submit", function( ev ) {
- applyUrlParams();
-
- if ( ev && ev.preventDefault ) {
- ev.preventDefault();
- }
-
- return false;
- });
-
- return filter;
-}
-
-function toolbarModuleFilterHtml() {
- var i,
- moduleFilterHtml = "";
-
- if ( !modulesList.length ) {
- return false;
- }
-
- modulesList.sort(function( a, b ) {
- return a.localeCompare( b );
- });
-
- moduleFilterHtml += "<label for='qunit-modulefilter'>Module: </label>" +
- "<select id='qunit-modulefilter' name='modulefilter'><option value='' " +
- ( QUnit.urlParams.module === undefined ? "selected='selected'" : "" ) +
- ">< All Modules ></option>";
-
- for ( i = 0; i < modulesList.length; i++ ) {
- moduleFilterHtml += "<option value='" +
- escapeText( encodeURIComponent( modulesList[ i ] ) ) + "' " +
- ( QUnit.urlParams.module === modulesList[ i ] ? "selected='selected'" : "" ) +
- ">" + escapeText( modulesList[ i ] ) + "</option>";
- }
- moduleFilterHtml += "</select>";
-
- return moduleFilterHtml;
-}
-
-function toolbarModuleFilter() {
- var toolbar = id( "qunit-testrunner-toolbar" ),
- moduleFilter = document.createElement( "span" ),
- moduleFilterHtml = toolbarModuleFilterHtml();
-
- if ( !toolbar || !moduleFilterHtml ) {
- return false;
- }
-
- moduleFilter.setAttribute( "id", "qunit-modulefilter-container" );
- moduleFilter.innerHTML = moduleFilterHtml;
-
- addEvent( moduleFilter.lastChild, "change", applyUrlParams );
-
- toolbar.appendChild( moduleFilter );
-}
-
-function appendToolbar() {
- var toolbar = id( "qunit-testrunner-toolbar" );
-
- if ( toolbar ) {
- toolbar.appendChild( toolbarUrlConfigContainer() );
- toolbar.appendChild( toolbarLooseFilter() );
- }
-}
-
-function appendHeader() {
- var header = id( "qunit-header" );
-
- if ( header ) {
- header.innerHTML = "<a href='" +
- setUrl({ filter: undefined, module: undefined, testId: undefined }) +
- "'>" + header.innerHTML + "</a> ";
- }
-}
-
-function appendBanner() {
- var banner = id( "qunit-banner" );
-
- if ( banner ) {
- banner.className = "";
- }
-}
-
-function appendTestResults() {
- var tests = id( "qunit-tests" ),
- result = id( "qunit-testresult" );
-
- if ( result ) {
- result.parentNode.removeChild( result );
- }
-
- if ( tests ) {
- tests.innerHTML = "";
- result = document.createElement( "p" );
- result.id = "qunit-testresult";
- result.className = "result";
- tests.parentNode.insertBefore( result, tests );
- result.innerHTML = "Running...<br />&#160;";
- }
-}
-
-function storeFixture() {
- var fixture = id( "qunit-fixture" );
- if ( fixture ) {
- config.fixture = fixture.innerHTML;
- }
-}
-
-function appendFilteredTest() {
- var testId = QUnit.config.testId;
- if ( !testId || testId.length <= 0 ) {
- return "";
- }
- return "<div id='qunit-filteredTest'>Rerunning selected tests: " + testId.join(", ") +
- " <a id='qunit-clearFilter' href='" +
- setUrl({ filter: undefined, module: undefined, testId: undefined }) +
- "'>" + "Run all tests" + "</a></div>";
-}
-
-function appendUserAgent() {
- var userAgent = id( "qunit-userAgent" );
-
- if ( userAgent ) {
- userAgent.innerHTML = "";
- userAgent.appendChild(
- document.createTextNode(
- "QUnit " + QUnit.version + "; " + navigator.userAgent
- )
- );
- }
-}
-
-function appendTestsList( modules ) {
- var i, l, x, z, test, moduleObj;
-
- for ( i = 0, l = modules.length; i < l; i++ ) {
- moduleObj = modules[ i ];
-
- if ( moduleObj.name ) {
- modulesList.push( moduleObj.name );
- }
-
- for ( x = 0, z = moduleObj.tests.length; x < z; x++ ) {
- test = moduleObj.tests[ x ];
-
- appendTest( test.name, test.testId, moduleObj.name );
- }
- }
-}
-
-function appendTest( name, testId, moduleName ) {
- var title, rerunTrigger, testBlock, assertList,
- tests = id( "qunit-tests" );
-
- if ( !tests ) {
- return;
- }
-
- title = document.createElement( "strong" );
- title.innerHTML = getNameHtml( name, moduleName );
-
- rerunTrigger = document.createElement( "a" );
- rerunTrigger.innerHTML = "Rerun";
- rerunTrigger.href = setUrl({ testId: testId });
-
- testBlock = document.createElement( "li" );
- testBlock.appendChild( title );
- testBlock.appendChild( rerunTrigger );
- testBlock.id = "qunit-test-output-" + testId;
-
- assertList = document.createElement( "ol" );
- assertList.className = "qunit-assert-list";
-
- testBlock.appendChild( assertList );
-
- tests.appendChild( testBlock );
-}
-
-// HTML Reporter initialization and load
-QUnit.begin(function( details ) {
- var qunit = id( "qunit" );
-
- // Fixture is the only one necessary to run without the #qunit element
- storeFixture();
-
- if ( qunit ) {
- qunit.innerHTML =
- "<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
- "<h2 id='qunit-banner'></h2>" +
- "<div id='qunit-testrunner-toolbar'></div>" +
- appendFilteredTest() +
- "<h2 id='qunit-userAgent'></h2>" +
- "<ol id='qunit-tests'></ol>";
- }
-
- appendHeader();
- appendBanner();
- appendTestResults();
- appendUserAgent();
- appendToolbar();
- appendTestsList( details.modules );
- toolbarModuleFilter();
-
- if ( qunit && config.hidepassed ) {
- addClass( qunit.lastChild, "hidepass" );
- }
-});
-
-QUnit.done(function( details ) {
- var i, key,
- banner = id( "qunit-banner" ),
- tests = id( "qunit-tests" ),
- html = [
- "Tests completed in ",
- details.runtime,
- " milliseconds.<br />",
- "<span class='passed'>",
- details.passed,
- "</span> assertions of <span class='total'>",
- details.total,
- "</span> passed, <span class='failed'>",
- details.failed,
- "</span> failed."
- ].join( "" );
-
- if ( banner ) {
- banner.className = details.failed ? "qunit-fail" : "qunit-pass";
- }
-
- if ( tests ) {
- id( "qunit-testresult" ).innerHTML = html;
- }
-
- if ( config.altertitle && defined.document && document.title ) {
-
- // show ✖ for good, ✔ for bad suite result in title
- // use escape sequences in case file gets loaded with non-utf-8-charset
- document.title = [
- ( details.failed ? "\u2716" : "\u2714" ),
- document.title.replace( /^[\u2714\u2716] /i, "" )
- ].join( " " );
- }
-
- // clear own sessionStorage items if all tests passed
- if ( config.reorder && defined.sessionStorage && details.failed === 0 ) {
- for ( i = 0; i < sessionStorage.length; i++ ) {
- key = sessionStorage.key( i++ );
- if ( key.indexOf( "qunit-test-" ) === 0 ) {
- sessionStorage.removeItem( key );
- }
- }
- }
-
- // scroll back to top to show results
- if ( config.scrolltop && window.scrollTo ) {
- window.scrollTo( 0, 0 );
- }
-});
-
-function getNameHtml( name, module ) {
- var nameHtml = "";
-
- if ( module ) {
- nameHtml = "<span class='module-name'>" + escapeText( module ) + "</span>: ";
- }
-
- nameHtml += "<span class='test-name'>" + escapeText( name ) + "</span>";
-
- return nameHtml;
-}
-
-QUnit.testStart(function( details ) {
- var running, testBlock, bad;
-
- testBlock = id( "qunit-test-output-" + details.testId );
- if ( testBlock ) {
- testBlock.className = "running";
- } else {
-
- // Report later registered tests
- appendTest( details.name, details.testId, details.module );
- }
-
- running = id( "qunit-testresult" );
- if ( running ) {
- bad = QUnit.config.reorder && defined.sessionStorage &&
- +sessionStorage.getItem( "qunit-test-" + details.module + "-" + details.name );
-
- running.innerHTML = ( bad ?
- "Rerunning previously failed test: <br />" :
- "Running: <br />" ) +
- getNameHtml( details.name, details.module );
- }
-
-});
-
-function stripHtml( string ) {
- // strip tags, html entity and whitespaces
- return string.replace(/<\/?[^>]+(>|$)/g, "").replace(/\&quot;/g, "").replace(/\s+/g, "");
-}
-
-QUnit.log(function( details ) {
- var assertList, assertLi,
- message, expected, actual, diff,
- showDiff = false,
- testItem = id( "qunit-test-output-" + details.testId );
-
- if ( !testItem ) {
- return;
- }
-
- message = escapeText( details.message ) || ( details.result ? "okay" : "failed" );
- message = "<span class='test-message'>" + message + "</span>";
- message += "<span class='runtime'>@ " + details.runtime + " ms</span>";
-
- // pushFailure doesn't provide details.expected
- // when it calls, it's implicit to also not show expected and diff stuff
- // Also, we need to check details.expected existence, as it can exist and be undefined
- if ( !details.result && hasOwn.call( details, "expected" ) ) {
- if ( details.negative ) {
- expected = escapeText( "NOT " + QUnit.dump.parse( details.expected ) );
- } else {
- expected = escapeText( QUnit.dump.parse( details.expected ) );
- }
-
- actual = escapeText( QUnit.dump.parse( details.actual ) );
- message += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" +
- expected +
- "</pre></td></tr>";
-
- if ( actual !== expected ) {
-
- message += "<tr class='test-actual'><th>Result: </th><td><pre>" +
- actual + "</pre></td></tr>";
-
- // Don't show diff if actual or expected are booleans
- if ( !( /^(true|false)$/.test( actual ) ) &&
- !( /^(true|false)$/.test( expected ) ) ) {
- diff = QUnit.diff( expected, actual );
- showDiff = stripHtml( diff ).length !==
- stripHtml( expected ).length +
- stripHtml( actual ).length;
- }
-
- // Don't show diff if expected and actual are totally different
- if ( showDiff ) {
- message += "<tr class='test-diff'><th>Diff: </th><td><pre>" +
- diff + "</pre></td></tr>";
- }
- } else if ( expected.indexOf( "[object Array]" ) !== -1 ||
- expected.indexOf( "[object Object]" ) !== -1 ) {
- message += "<tr class='test-message'><th>Message: </th><td>" +
- "Diff suppressed as the depth of object is more than current max depth (" +
- QUnit.config.maxDepth + ").<p>Hint: Use <code>QUnit.dump.maxDepth</code> to " +
- " run with a higher max depth or <a href='" + setUrl({ maxDepth: -1 }) + "'>" +
- "Rerun</a> without max depth.</p></td></tr>";
- }
-
- if ( details.source ) {
- message += "<tr class='test-source'><th>Source: </th><td><pre>" +
- escapeText( details.source ) + "</pre></td></tr>";
- }
-
- message += "</table>";
-
- // this occours when pushFailure is set and we have an extracted stack trace
- } else if ( !details.result && details.source ) {
- message += "<table>" +
- "<tr class='test-source'><th>Source: </th><td><pre>" +
- escapeText( details.source ) + "</pre></td></tr>" +
- "</table>";
- }
-
- assertList = testItem.getElementsByTagName( "ol" )[ 0 ];
-
- assertLi = document.createElement( "li" );
- assertLi.className = details.result ? "pass" : "fail";
- assertLi.innerHTML = message;
- assertList.appendChild( assertLi );
-});
-
-QUnit.testDone(function( details ) {
- var testTitle, time, testItem, assertList,
- good, bad, testCounts, skipped, sourceName,
- tests = id( "qunit-tests" );
-
- if ( !tests ) {
- return;
- }
-
- testItem = id( "qunit-test-output-" + details.testId );
-
- assertList = testItem.getElementsByTagName( "ol" )[ 0 ];
-
- good = details.passed;
- bad = details.failed;
-
- // store result when possible
- if ( config.reorder && defined.sessionStorage ) {
- if ( bad ) {
- sessionStorage.setItem( "qunit-test-" + details.module + "-" + details.name, bad );
- } else {
- sessionStorage.removeItem( "qunit-test-" + details.module + "-" + details.name );
- }
- }
-
- if ( bad === 0 ) {
-
- // Collapse the passing tests
- addClass( assertList, "qunit-collapsed" );
- } else if ( bad && config.collapse && !collapseNext ) {
-
- // Skip collapsing the first failing test
- collapseNext = true;
- } else {
-
- // Collapse remaining tests
- addClass( assertList, "qunit-collapsed" );
- }
-
- // testItem.firstChild is the test name
- testTitle = testItem.firstChild;
-
- testCounts = bad ?
- "<b class='failed'>" + bad + "</b>, " + "<b class='passed'>" + good + "</b>, " :
- "";
-
- testTitle.innerHTML += " <b class='counts'>(" + testCounts +
- details.assertions.length + ")</b>";
-
- if ( details.skipped ) {
- testItem.className = "skipped";
- skipped = document.createElement( "em" );
- skipped.className = "qunit-skipped-label";
- skipped.innerHTML = "skipped";
- testItem.insertBefore( skipped, testTitle );
- } else {
- addEvent( testTitle, "click", function() {
- toggleClass( assertList, "qunit-collapsed" );
- });
-
- testItem.className = bad ? "fail" : "pass";
-
- time = document.createElement( "span" );
- time.className = "runtime";
- time.innerHTML = details.runtime + " ms";
- testItem.insertBefore( time, assertList );
- }
-
- // Show the source of the test when showing assertions
- if ( details.source ) {
- sourceName = document.createElement( "p" );
- sourceName.innerHTML = "<strong>Source: </strong>" + details.source;
- addClass( sourceName, "qunit-source" );
- if ( bad === 0 ) {
- addClass( sourceName, "qunit-collapsed" );
- }
- addEvent( testTitle, "click", function() {
- toggleClass( sourceName, "qunit-collapsed" );
- });
- testItem.appendChild( sourceName );
- }
-});
-
-if ( defined.document ) {
-
- // Avoid readyState issue with phantomjs
- // Ref: #818
- var notPhantom = ( function( p ) {
- return !( p && p.version && p.version.major > 0 );
- } )( window.phantom );
-
- if ( notPhantom && document.readyState === "complete" ) {
- QUnit.load();
- } else {
- addEvent( window, "load", QUnit.load );
- }
-} else {
- config.pageLoaded = true;
- config.autorun = true;
-}
-
-})();
+}() );