Upgrade QUnit to the current master

Exact commit ID: 96e42601cadcba989f80bd4c294b7e0ee4ff1d29
redux
David Majda 13 years ago
parent 468597be27
commit a92676edce

@ -1,7 +1,17 @@
/**
* QUnit - A JavaScript Unit Testing Framework
*
* http://docs.jquery.com/QUnit
*
* Copyright (c) 2011 John Resig, Jörn Zaefferer
* Dual licensed under the MIT (MIT-LICENSE.txt)
* or GPL (GPL-LICENSE.txt) licenses.
*/
/** Font Family and Sizes */ /** Font Family and Sizes */
#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial; font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
} }
#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
@ -74,10 +84,24 @@
list-style-position: inside; list-style-position: inside;
} }
#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running {
display: none;
}
#qunit-tests li strong { #qunit-tests li strong {
cursor: pointer; cursor: pointer;
} }
#qunit-tests li a {
padding: 0.5em;
color: #c2ccd1;
text-decoration: none;
}
#qunit-tests li a:hover,
#qunit-tests li a:focus {
color: #000;
}
#qunit-tests ol { #qunit-tests ol {
margin-top: 0.5em; margin-top: 0.5em;
padding: 0.5em; padding: 0.5em;
@ -162,6 +186,14 @@
color: #710909; color: #710909;
background-color: #fff; background-color: #fff;
border-left: 26px solid #EE5757; border-left: 26px solid #EE5757;
white-space: pre;
}
#qunit-tests > li:last-child {
border-radius: 0 0 15px 15px;
-moz-border-radius: 0 0 15px 15px;
-webkit-border-bottom-right-radius: 15px;
-webkit-border-bottom-left-radius: 15px;
} }
#qunit-tests .fail { color: #000000; background-color: #EE5757; } #qunit-tests .fail { color: #000000; background-color: #EE5757; }
@ -174,7 +206,7 @@
#qunit-banner.qunit-fail { background-color: #EE5757; } #qunit-banner.qunit-fail { background-color: #EE5757; }
/** Footer */ /** Result */
#qunit-testresult { #qunit-testresult {
padding: 0.5em 0.5em 0.5em 2.5em; padding: 0.5em 0.5em 0.5em 2.5em;
@ -182,10 +214,7 @@
color: #2b81af; color: #2b81af;
background-color: #D2E0E6; background-color: #D2E0E6;
border-radius: 0 0 15px 15px; border-bottom: 1px solid white;
-moz-border-radius: 0 0 15px 15px;
-webkit-border-bottom-right-radius: 15px;
-webkit-border-bottom-left-radius: 15px;
} }
/** Fixture */ /** Fixture */

@ -1,4 +1,4 @@
/* /**
* QUnit - A JavaScript Unit Testing Framework * QUnit - A JavaScript Unit Testing Framework
* *
* http://docs.jquery.com/QUnit * http://docs.jquery.com/QUnit
@ -15,11 +15,11 @@ var defined = {
sessionStorage: (function() { sessionStorage: (function() {
try { try {
return !!sessionStorage.getItem; return !!sessionStorage.getItem;
} catch(e){ } catch(e) {
return false; return false;
} }
})() })()
} };
var testId = 0; var testId = 0;
@ -40,6 +40,7 @@ Test.prototype = {
b.innerHTML = "Running " + this.name; b.innerHTML = "Running " + this.name;
var li = document.createElement("li"); var li = document.createElement("li");
li.appendChild( b ); li.appendChild( b );
li.className = "running";
li.id = this.id = "test-output" + testId++; li.id = this.id = "test-output" + testId++;
tests.appendChild( li ); tests.appendChild( li );
} }
@ -47,7 +48,7 @@ Test.prototype = {
setup: function() { setup: function() {
if (this.module != config.previousModule) { if (this.module != config.previousModule) {
if ( config.previousModule ) { if ( config.previousModule ) {
QUnit.moduleDone( { runLoggingCallbacks('moduleDone', QUnit, {
name: config.previousModule, name: config.previousModule,
failed: config.moduleStats.bad, failed: config.moduleStats.bad,
passed: config.moduleStats.all - config.moduleStats.bad, passed: config.moduleStats.all - config.moduleStats.bad,
@ -56,7 +57,7 @@ Test.prototype = {
} }
config.previousModule = this.module; config.previousModule = this.module;
config.moduleStats = { all: 0, bad: 0 }; config.moduleStats = { all: 0, bad: 0 };
QUnit.moduleStart( { runLoggingCallbacks( 'moduleStart', QUnit, {
name: this.module name: this.module
} ); } );
} }
@ -70,7 +71,7 @@ Test.prototype = {
extend(this.testEnvironment, this.testEnvironmentArg); extend(this.testEnvironment, this.testEnvironmentArg);
} }
QUnit.testStart( { runLoggingCallbacks( 'testStart', QUnit, {
name: this.testName name: this.testName
} ); } );
@ -113,8 +114,8 @@ Test.prototype = {
}, },
teardown: function() { teardown: function() {
try { try {
checkPollution();
this.testEnvironment.teardown.call(this.testEnvironment); this.testEnvironment.teardown.call(this.testEnvironment);
checkPollution();
} catch(e) { } catch(e) {
QUnit.ok( false, "Teardown failed on " + this.testName + ": " + e.message ); QUnit.ok( false, "Teardown failed on " + this.testName + ": " + e.message );
} }
@ -151,7 +152,13 @@ Test.prototype = {
} }
// store result when possible // store result when possible
defined.sessionStorage && sessionStorage.setItem("qunit-" + this.testName, bad); if ( QUnit.config.reorder && defined.sessionStorage ) {
if (bad) {
sessionStorage.setItem("qunit-" + this.module + "-" + this.testName, bad);
} else {
sessionStorage.removeItem("qunit-" + this.module + "-" + this.testName);
}
}
if (bad == 0) { if (bad == 0) {
ol.style.display = "none"; ol.style.display = "none";
@ -160,8 +167,13 @@ Test.prototype = {
var b = document.createElement("strong"); var b = document.createElement("strong");
b.innerHTML = this.name + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>"; b.innerHTML = this.name + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>";
var a = document.createElement("a");
a.innerHTML = "Rerun";
a.href = QUnit.url({ filter: getText([b]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") });
addEvent(b, "click", function() { addEvent(b, "click", function() {
var next = b.nextSibling, display = next.style.display; var next = b.nextSibling.nextSibling,
display = next.style.display;
next.style.display = display === "none" ? "block" : "none"; next.style.display = display === "none" ? "block" : "none";
}); });
@ -171,15 +183,15 @@ Test.prototype = {
target = target.parentNode; target = target.parentNode;
} }
if ( window.location && target.nodeName.toLowerCase() === "strong" ) { if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
window.location.search = "?" + encodeURIComponent(getText([target]).replace(/\(.+\)$/, "").replace(/(^\s*|\s*$)/g, "")); window.location = QUnit.url({ filter: getText([target]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") });
} }
}); });
var li = id(this.id); var li = id(this.id);
li.className = bad ? "fail" : "pass"; li.className = bad ? "fail" : "pass";
li.style.display = resultDisplayStyle(!bad);
li.removeChild( li.firstChild ); li.removeChild( li.firstChild );
li.appendChild( b ); li.appendChild( b );
li.appendChild( a );
li.appendChild( ol ); li.appendChild( ol );
} else { } else {
@ -198,7 +210,7 @@ Test.prototype = {
fail("reset() failed, following Test " + this.testName + ", exception and reset fn follows", e, QUnit.reset); fail("reset() failed, following Test " + this.testName + ", exception and reset fn follows", e, QUnit.reset);
} }
QUnit.testDone( { runLoggingCallbacks( 'testDone', QUnit, {
name: this.testName, name: this.testName,
failed: bad, failed: bad,
passed: this.assertions.length - bad, passed: this.assertions.length - bad,
@ -227,7 +239,7 @@ Test.prototype = {
}); });
} }
// defer when previous test run passed, if storage is available // defer when previous test run passed, if storage is available
var bad = defined.sessionStorage && +sessionStorage.getItem("qunit-" + this.testName); var bad = QUnit.config.reorder && defined.sessionStorage && +sessionStorage.getItem("qunit-" + this.module + "-" + this.testName);
if (bad) { if (bad) {
run(); run();
} else { } else {
@ -235,7 +247,7 @@ Test.prototype = {
}; };
} }
} };
var QUnit = { var QUnit = {
@ -299,7 +311,7 @@ var QUnit = {
message: msg message: msg
}; };
msg = escapeHtml(msg); msg = escapeHtml(msg);
QUnit.log(details); runLoggingCallbacks( 'log', QUnit, details );
config.current.assertions.push({ config.current.assertions.push({
result: a, result: a,
message: msg message: msg
@ -388,6 +400,9 @@ var QUnit = {
// A slight delay, to avoid any current callbacks // A slight delay, to avoid any current callbacks
if ( defined.setTimeout ) { if ( defined.setTimeout ) {
window.setTimeout(function() { window.setTimeout(function() {
if (config.semaphore > 0) {
return;
}
if ( config.timeout ) { if ( config.timeout ) {
clearTimeout(config.timeout); clearTimeout(config.timeout);
} }
@ -413,9 +428,17 @@ var QUnit = {
}, timeout); }, timeout);
} }
} }
}; };
//We want access to the constructor's prototype
(function() {
function F(){};
F.prototype = QUnit;
QUnit = new F();
//Make F QUnit's constructor so that we can add to the prototype later
QUnit.constructor = F;
})();
// Backwards compatibility, deprecated // Backwards compatibility, deprecated
QUnit.equals = QUnit.equal; QUnit.equals = QUnit.equal;
QUnit.same = QUnit.deepEqual; QUnit.same = QUnit.deepEqual;
@ -426,32 +449,51 @@ var config = {
queue: [], queue: [],
// block until document ready // block until document ready
blocking: true blocking: true,
// when enabled, show only failing tests
// gets persisted through sessionStorage and can be changed in UI via checkbox
hidepassed: false,
// 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
altertitle: true,
urlConfig: ['noglobals', 'notrycatch'],
//logging callback queues
begin: [],
done: [],
log: [],
testStart: [],
testDone: [],
moduleStart: [],
moduleDone: []
}; };
// Load paramaters // Load paramaters
(function() { (function() {
var location = window.location || { search: "", protocol: "file:" }, var location = window.location || { search: "", protocol: "file:" },
GETParams = location.search.slice(1).split('&'); params = location.search.slice( 1 ).split( "&" ),
length = params.length,
urlParams = {},
current;
for ( var i = 0; i < GETParams.length; i++ ) { if ( params[ 0 ] ) {
GETParams[i] = decodeURIComponent( GETParams[i] ); for ( var i = 0; i < length; i++ ) {
if ( GETParams[i] === "noglobals" ) { current = params[ i ].split( "=" );
GETParams.splice( i, 1 ); current[ 0 ] = decodeURIComponent( current[ 0 ] );
i--; // allow just a key to turn on a flag, e.g., test.html?noglobals
config.noglobals = true; current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
} else if ( GETParams[i] === "notrycatch" ) { urlParams[ current[ 0 ] ] = current[ 1 ];
GETParams.splice( i, 1 );
i--;
config.notrycatch = true;
} else if ( GETParams[i].search('=') > -1 ) {
GETParams.splice( i, 1 );
i--;
} }
} }
// restrict modules/tests by get parameters QUnit.urlParams = urlParams;
config.filters = GETParams; config.filter = urlParams.filter;
// Figure out if we're running the tests from a server or not // Figure out if we're running the tests from a server or not
QUnit.isLocal = !!(location.protocol === 'file:'); QUnit.isLocal = !!(location.protocol === 'file:');
@ -481,14 +523,14 @@ extend(QUnit, {
blocking: false, blocking: false,
autostart: true, autostart: true,
autorun: false, autorun: false,
filters: [], filter: "",
queue: [], queue: [],
semaphore: 0 semaphore: 0
}); });
var tests = id("qunit-tests"), var tests = id( "qunit-tests" ),
banner = id("qunit-banner"), banner = id( "qunit-banner" ),
result = id("qunit-testresult"); result = id( "qunit-testresult" );
if ( tests ) { if ( tests ) {
tests.innerHTML = ""; tests.innerHTML = "";
@ -501,6 +543,14 @@ extend(QUnit, {
if ( result ) { if ( result ) {
result.parentNode.removeChild( 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/>&nbsp;';
}
}, },
/** /**
@ -510,9 +560,9 @@ extend(QUnit, {
*/ */
reset: function() { reset: function() {
if ( window.jQuery ) { if ( window.jQuery ) {
jQuery( "#main, #qunit-fixture" ).html( config.fixture ); jQuery( "#qunit-fixture" ).html( config.fixture );
} else { } else {
var main = id( 'main' ) || id( 'qunit-fixture' ); var main = id( 'qunit-fixture' );
if ( main ) { if ( main ) {
main.innerHTML = config.fixture; main.innerHTML = config.fixture;
} }
@ -599,12 +649,12 @@ extend(QUnit, {
var source = sourceFromStacktrace(); var source = sourceFromStacktrace();
if (source) { if (source) {
details.source = source; details.source = source;
output += '<tr class="test-source"><th>Source: </th><td><pre>' + source +'</pre></td></tr>'; output += '<tr class="test-source"><th>Source: </th><td><pre>' + escapeHtml(source) + '</pre></td></tr>';
} }
} }
output += "</table>"; output += "</table>";
QUnit.log(details); runLoggingCallbacks( 'log', QUnit, details );
config.current.assertions.push({ config.current.assertions.push({
result: !!result, result: !!result,
@ -612,29 +662,49 @@ extend(QUnit, {
}); });
}, },
url: function( params ) {
params = extend( extend( {}, QUnit.urlParams ), params );
var querystring = "?",
key;
for ( key in params ) {
querystring += encodeURIComponent( key ) + "=" +
encodeURIComponent( params[ key ] ) + "&";
}
return window.location.pathname + querystring.slice( 0, -1 );
},
extend: extend,
id: id,
addEvent: addEvent,
});
//QUnit.constructor is set to the empty F() above so that we can add to it's prototype later
//Doing this allows us to tell if the following methods have been overwritten on the actual
//QUnit object, which is a deprecated way of using the callbacks.
extend(QUnit.constructor.prototype, {
// Logging callbacks; all receive a single argument with the listed properties // Logging callbacks; all receive a single argument with the listed properties
// run test/logs.html for any related changes // run test/logs.html for any related changes
begin: function() {}, begin: registerLoggingCallback('begin'),
// done: { failed, passed, total, runtime } // done: { failed, passed, total, runtime }
done: function() {}, done: registerLoggingCallback('done'),
// log: { result, actual, expected, message } // log: { result, actual, expected, message }
log: function() {}, log: registerLoggingCallback('log'),
// testStart: { name } // testStart: { name }
testStart: function() {}, testStart: registerLoggingCallback('testStart'),
// testDone: { name, failed, passed, total } // testDone: { name, failed, passed, total }
testDone: function() {}, testDone: registerLoggingCallback('testDone'),
// moduleStart: { name } // moduleStart: { name }
moduleStart: function() {}, moduleStart: registerLoggingCallback('moduleStart'),
// moduleDone: { name, failed, passed, total } // moduleDone: { name, failed, passed, total }
moduleDone: function() {} moduleDone: registerLoggingCallback('moduleDone'),
}); });
if ( typeof document === "undefined" || document.readyState === "complete" ) { if ( typeof document === "undefined" || document.readyState === "complete" ) {
config.autorun = true; config.autorun = true;
} }
addEvent(window, "load", function() { QUnit.load = function() {
QUnit.begin({}); runLoggingCallbacks( 'begin', QUnit, {} );
// Initialize the config, saving the execution queue // Initialize the config, saving the execution queue
var oldconfig = extend({}, config); var oldconfig = extend({}, config);
@ -643,22 +713,24 @@ addEvent(window, "load", function() {
config.blocking = false; config.blocking = false;
var urlConfigHtml = '', len = config.urlConfig.length;
for ( var i = 0, val; i < len, val = config.urlConfig[i]; i++ ) {
config[val] = QUnit.urlParams[val];
urlConfigHtml += '<label><input name="' + val + '" type="checkbox"' + ( config[val] ? ' checked="checked"' : '' ) + '>' + val + '</label>';
}
var userAgent = id("qunit-userAgent"); var userAgent = id("qunit-userAgent");
if ( userAgent ) { if ( userAgent ) {
userAgent.innerHTML = navigator.userAgent; userAgent.innerHTML = navigator.userAgent;
} }
var banner = id("qunit-header"); var banner = id("qunit-header");
if ( banner ) { if ( banner ) {
var paramsIndex = location.href.lastIndexOf(location.search); banner.innerHTML = '<a href="' + QUnit.url({ filter: undefined }) + '"> ' + banner.innerHTML + '</a> ' + urlConfigHtml;
if ( paramsIndex > -1 ) { addEvent( banner, "change", function( event ) {
var mainPageLocation = location.href.slice(0, paramsIndex); var params = {};
if ( mainPageLocation == location.href ) { params[ event.target.name ] = event.target.checked ? true : undefined;
banner.innerHTML = '<a href=""> ' + banner.innerHTML + '</a> '; window.location = QUnit.url( params );
} else { });
var testName = decodeURIComponent(location.search.slice(1));
banner.innerHTML = '<a href="' + mainPageLocation + '">' + banner.innerHTML + '</a> &#8250; <a href="">' + testName + '</a>';
}
}
} }
var toolbar = id("qunit-testrunner-toolbar"); var toolbar = id("qunit-testrunner-toolbar");
@ -667,18 +739,25 @@ addEvent(window, "load", function() {
filter.type = "checkbox"; filter.type = "checkbox";
filter.id = "qunit-filter-pass"; filter.id = "qunit-filter-pass";
addEvent( filter, "click", function() { addEvent( filter, "click", function() {
var li = document.getElementsByTagName("li"); var ol = document.getElementById("qunit-tests");
for ( var i = 0; i < li.length; i++ ) { if ( filter.checked ) {
if ( li[i].className.indexOf("pass") > -1 ) { ol.className = ol.className + " hidepass";
li[i].style.display = filter.checked ? "none" : ""; } else {
} var tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " ";
ol.className = tmp.replace(/ hidepass /, " ");
} }
if ( defined.sessionStorage ) { if ( defined.sessionStorage ) {
sessionStorage.setItem("qunit-filter-passed-tests", filter.checked ? "true" : ""); if (filter.checked) {
sessionStorage.setItem("qunit-filter-passed-tests", "true");
} else {
sessionStorage.removeItem("qunit-filter-passed-tests");
}
} }
}); });
if ( defined.sessionStorage && sessionStorage.getItem("qunit-filter-passed-tests") ) { if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem("qunit-filter-passed-tests") ) {
filter.checked = true; filter.checked = true;
var ol = document.getElementById("qunit-tests");
ol.className = ol.className + " hidepass";
} }
toolbar.appendChild( filter ); toolbar.appendChild( filter );
@ -688,7 +767,7 @@ addEvent(window, "load", function() {
toolbar.appendChild( label ); toolbar.appendChild( label );
} }
var main = id('main') || id('qunit-fixture'); var main = id('qunit-fixture');
if ( main ) { if ( main ) {
config.fixture = main.innerHTML; config.fixture = main.innerHTML;
} }
@ -696,14 +775,16 @@ addEvent(window, "load", function() {
if (config.autostart) { if (config.autostart) {
QUnit.start(); QUnit.start();
} }
}); };
addEvent(window, "load", QUnit.load);
function done() { function done() {
config.autorun = true; config.autorun = true;
// Log the last module results // Log the last module results
if ( config.currentModule ) { if ( config.currentModule ) {
QUnit.moduleDone( { runLoggingCallbacks( 'moduleDone', QUnit, {
name: config.currentModule, name: config.currentModule,
failed: config.moduleStats.bad, failed: config.moduleStats.bad,
passed: config.moduleStats.all - config.moduleStats.bad, passed: config.moduleStats.all - config.moduleStats.bad,
@ -733,19 +814,19 @@ function done() {
} }
if ( tests ) { if ( tests ) {
var result = id("qunit-testresult"); id( "qunit-testresult" ).innerHTML = html;
if ( !result ) {
result = document.createElement("p");
result.id = "qunit-testresult";
result.className = "result";
tests.parentNode.insertBefore( result, tests.nextSibling );
} }
result.innerHTML = html; if ( config.altertitle && typeof document !== "undefined" && 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 = [
(config.stats.bad ? "\u2716" : "\u2714"),
document.title.replace(/^[\u2714\u2716] /i, "")
].join(" ");
} }
QUnit.done( { runLoggingCallbacks( 'done', QUnit, {
failed: config.stats.bad, failed: config.stats.bad,
passed: passed, passed: passed,
total: config.stats.all, total: config.stats.all,
@ -754,29 +835,25 @@ function done() {
} }
function validTest( name ) { function validTest( name ) {
var i = config.filters.length, var filter = config.filter,
run = false; run = false;
if ( !i ) { if ( !filter ) {
return true; return true;
} }
while ( i-- ) { var not = filter.charAt( 0 ) === "!";
var filter = config.filters[i],
not = filter.charAt(0) == '!';
if ( not ) { if ( not ) {
filter = filter.slice(1); filter = filter.slice( 1 );
} }
if ( name.indexOf(filter) !== -1 ) { if ( name.indexOf( filter ) !== -1 ) {
return !not; return !not;
} }
if ( not ) { if ( not ) {
run = true; run = true;
} }
}
return run; return run;
} }
@ -793,14 +870,14 @@ function sourceFromStacktrace() {
} else if (e.stack) { } else if (e.stack) {
// Firefox, Chrome // Firefox, Chrome
return e.stack.split("\n")[4]; return e.stack.split("\n")[4];
} else if (e.sourceURL) {
// Safari, PhantomJS
// TODO sourceURL points at the 'throw new Error' line above, useless
//return e.sourceURL + ":" + e.line;
} }
} }
} }
function resultDisplayStyle(passed) {
return passed && id("qunit-filter-pass") && id("qunit-filter-pass").checked ? 'none' : '';
}
function escapeHtml(s) { function escapeHtml(s) {
if (!s) { if (!s) {
return ""; return "";
@ -856,16 +933,14 @@ function checkPollution( name ) {
var old = config.pollution; var old = config.pollution;
saveGlobal(); saveGlobal();
var newGlobals = diff( old, config.pollution ); var newGlobals = diff( config.pollution, old );
if ( newGlobals.length > 0 ) { if ( newGlobals.length > 0 ) {
ok( false, "Introduced global variable(s): " + newGlobals.join(", ") ); ok( false, "Introduced global variable(s): " + newGlobals.join(", ") );
config.current.expected++;
} }
var deletedGlobals = diff( config.pollution, old ); var deletedGlobals = diff( old, config.pollution );
if ( deletedGlobals.length > 0 ) { if ( deletedGlobals.length > 0 ) {
ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") ); ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") );
config.current.expected++;
} }
} }
@ -897,8 +972,12 @@ function fail(message, exception, callback) {
function extend(a, b) { function extend(a, b) {
for ( var prop in b ) { for ( var prop in b ) {
if ( b[prop] === undefined ) {
delete a[prop];
} else {
a[prop] = b[prop]; a[prop] = b[prop];
} }
}
return a; return a;
} }
@ -918,9 +997,27 @@ function id(name) {
document.getElementById( name ); document.getElementById( name );
} }
function registerLoggingCallback(key){
return function(callback){
config[key].push( callback );
};
}
// Supports deprecated method of completely overwriting logging callbacks
function runLoggingCallbacks(key, scope, args) {
//debugger;
var callbacks;
if ( QUnit.hasOwnProperty(key) ) {
QUnit[key].call(scope, args);
} else {
callbacks = config[key];
for( var i = 0; i < callbacks.length; i++ ) {
callbacks[i].call( scope, args );
}
}
}
// Test for equality any JavaScript type. // Test for equality any JavaScript type.
// Discussions and reference: http://philrathe.com/articles/equiv
// Test suites: http://philrathe.com/tests/equiv
// Author: Philippe Rathé <prathe@gmail.com> // Author: Philippe Rathé <prathe@gmail.com>
QUnit.equiv = function () { QUnit.equiv = function () {
@ -945,7 +1042,8 @@ QUnit.equiv = function () {
// for string, boolean, number and null // for string, boolean, number and null
function useStrictEquality(b, a) { function useStrictEquality(b, a) {
if (b instanceof a.constructor || a instanceof b.constructor) { if (b instanceof a.constructor || a instanceof b.constructor) {
// to catch short annotaion VS 'new' annotation of a declaration // to catch short annotaion VS 'new' annotation of a
// declaration
// e.g. var i = 1; // e.g. var i = 1;
// var j = new Number(1); // var j = new Number(1);
return a == b; return a == b;
@ -955,43 +1053,44 @@ QUnit.equiv = function () {
} }
return { return {
"string": useStrictEquality, "string" : useStrictEquality,
"boolean": useStrictEquality, "boolean" : useStrictEquality,
"number": useStrictEquality, "number" : useStrictEquality,
"null": useStrictEquality, "null" : useStrictEquality,
"undefined": useStrictEquality, "undefined" : useStrictEquality,
"nan": function (b) { "nan" : function(b) {
return isNaN(b); return isNaN(b);
}, },
"date": function (b, a) { "date" : function(b, a) {
return QUnit.objectType(b) === "date" && a.valueOf() === b.valueOf(); return QUnit.objectType(b) === "date"
&& a.valueOf() === b.valueOf();
}, },
"regexp": function (b, a) { "regexp" : function(b, a) {
return QUnit.objectType(b) === "regexp" && return QUnit.objectType(b) === "regexp"
a.source === b.source && // the regex itself && a.source === b.source && // the regex itself
a.global === b.global && // and its modifers (gmi) ... a.global === b.global && // and its modifers
a.ignoreCase === b.ignoreCase && // (gmi) ...
a.multiline === b.multiline; a.ignoreCase === b.ignoreCase
&& a.multiline === b.multiline;
}, },
// - skip when the property is a method of an instance (OOP) // - skip when the property is a method of an instance (OOP)
// - abort otherwise, // - abort otherwise,
// initial === would have catch identical references anyway // initial === would have catch identical references anyway
"function": function () { "function" : function() {
var caller = callers[callers.length - 1]; var caller = callers[callers.length - 1];
return caller !== Object && return caller !== Object && typeof caller !== "undefined";
typeof caller !== "undefined";
}, },
"array": function (b, a) { "array" : function(b, a) {
var i, j, loop; var i, j, loop;
var len; var len;
// b could be an object literal here // b could be an object literal here
if ( ! (QUnit.objectType(b) === "array")) { if (!(QUnit.objectType(b) === "array")) {
return false; return false;
} }
@ -1000,16 +1099,16 @@ QUnit.equiv = function () {
return false; return false;
} }
//track reference to avoid circular references // track reference to avoid circular references
parents.push(a); parents.push(a);
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
loop = false; loop = false;
for(j=0;j<parents.length;j++){ for (j = 0; j < parents.length; j++) {
if(parents[j] === a[i]){ if (parents[j] === a[i]) {
loop = true;//dont rewalk array loop = true;// dont rewalk array
} }
} }
if (!loop && ! innerEquiv(a[i], b[i])) { if (!loop && !innerEquiv(a[i], b[i])) {
parents.pop(); parents.pop();
return false; return false;
} }
@ -1018,30 +1117,34 @@ QUnit.equiv = function () {
return true; return true;
}, },
"object": function (b, a) { "object" : function(b, a) {
var i, j, loop; var i, j, loop;
var eq = true; // unless we can proove it var eq = true; // unless we can proove it
var aProperties = [], bProperties = []; // collection of strings var aProperties = [], bProperties = []; // collection of
// strings
// comparing constructors is more strict than using instanceof // comparing constructors is more strict than using
if ( a.constructor !== b.constructor) { // instanceof
if (a.constructor !== b.constructor) {
return false; return false;
} }
// stack constructor before traversing properties // stack constructor before traversing properties
callers.push(a.constructor); callers.push(a.constructor);
//track reference to avoid circular references // track reference to avoid circular references
parents.push(a); parents.push(a);
for (i in a) { // be strict: don't ensures hasOwnProperty and go deep for (i in a) { // be strict: don't ensures hasOwnProperty
// and go deep
loop = false; loop = false;
for(j=0;j<parents.length;j++){ for (j = 0; j < parents.length; j++) {
if(parents[j] === a[i]) if (parents[j] === a[i])
loop = true; //don't go down the same path twice loop = true; // don't go down the same path
// twice
} }
aProperties.push(i); // collect a's properties aProperties.push(i); // collect a's properties
if (!loop && ! innerEquiv(a[i], b[i])) { if (!loop && !innerEquiv(a[i], b[i])) {
eq = false; eq = false;
break; break;
} }
@ -1055,28 +1158,34 @@ QUnit.equiv = function () {
} }
// Ensures identical properties name // Ensures identical properties name
return eq && innerEquiv(aProperties.sort(), bProperties.sort()); return eq
&& innerEquiv(aProperties.sort(), bProperties
.sort());
} }
}; };
}(); }();
innerEquiv = function () { // can take multiple arguments innerEquiv = function() { // can take multiple arguments
var args = Array.prototype.slice.apply(arguments); var args = Array.prototype.slice.apply(arguments);
if (args.length < 2) { if (args.length < 2) {
return true; // end transition return true; // end transition
} }
return (function (a, b) { return (function(a, b) {
if (a === b) { if (a === b) {
return true; // catch the most you can return true; // catch the most you can
} else if (a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || QUnit.objectType(a) !== QUnit.objectType(b)) { } else if (a === null || b === null || typeof a === "undefined"
|| typeof b === "undefined"
|| QUnit.objectType(a) !== QUnit.objectType(b)) {
return false; // don't lose time with error prone cases return false; // don't lose time with error prone cases
} else { } else {
return bindCallbacks(a, callbacks, [b, a]); return bindCallbacks(a, callbacks, [ b, a ]);
} }
// apply transition with (1..n) arguments // apply transition with (1..n) arguments
})(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length -1)); })(args[0], args[1])
&& arguments.callee.apply(this, args.splice(1,
args.length - 1));
}; };
return innerEquiv; return innerEquiv;
@ -1084,10 +1193,10 @@ QUnit.equiv = function () {
}(); }();
/** /**
* jsDump * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com |
* Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com * http://flesler.blogspot.com Licensed under BSD
* Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php) * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008
* Date: 5/15/2008 *
* @projectDescription Advanced and extensible data dumping for Javascript. * @projectDescription Advanced and extensible data dumping for Javascript.
* @version 1.0.0 * @version 1.0.0
* @author Ariel Flesler * @author Ariel Flesler
@ -1110,11 +1219,11 @@ QUnit.jsDump = (function() {
return pre + post; return pre + post;
return [ pre, inner + arr, base + post ].join(s); return [ pre, inner + arr, base + post ].join(s);
}; };
function array( arr ) { function array( arr, stack ) {
var i = arr.length, ret = Array(i); var i = arr.length, ret = Array(i);
this.up(); this.up();
while ( i-- ) while ( i-- )
ret[i] = this.parse( arr[i] ); ret[i] = this.parse( arr[i] , undefined , stack);
this.down(); this.down();
return join( '[', ret, ']' ); return join( '[', ret, ']' );
}; };
@ -1122,13 +1231,23 @@ QUnit.jsDump = (function() {
var reName = /^function (\w+)/; var reName = /^function (\w+)/;
var jsDump = { var jsDump = {
parse:function( obj, type ) { //type is used mostly internally, you can fix a (custom)type in advance parse:function( obj, type, stack ) { //type is used mostly internally, you can fix a (custom)type in advance
stack = stack || [ ];
var parser = this.parsers[ type || this.typeOf(obj) ]; var parser = this.parsers[ type || this.typeOf(obj) ];
type = typeof parser; type = typeof parser;
var inStack = inArray(obj, stack);
return type == 'function' ? parser.call( this, obj ) : if (inStack != -1) {
type == 'string' ? parser : return 'recursion('+(inStack - stack.length)+')';
this.parsers.error; }
//else
if (type == 'function') {
stack.push(obj);
var res = parser.call( this, obj, stack );
stack.pop();
return res;
}
// else
return (type == 'string') ? parser : this.parsers.error;
}, },
typeOf:function( obj ) { typeOf:function( obj ) {
var type; var type;
@ -1188,7 +1307,7 @@ QUnit.jsDump = (function() {
error:'[ERROR]', //when no parser is found, shouldn't happen error:'[ERROR]', //when no parser is found, shouldn't happen
unknown: '[Unknown]', unknown: '[Unknown]',
'null':'null', 'null':'null',
undefined:'undefined', 'undefined':'undefined',
'function':function( fn ) { 'function':function( fn ) {
var ret = 'function', var ret = 'function',
name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE
@ -1202,11 +1321,13 @@ QUnit.jsDump = (function() {
array: array, array: array,
nodelist: array, nodelist: array,
arguments: array, arguments: array,
object:function( map ) { object:function( map, stack ) {
var ret = [ ]; var ret = [ ];
QUnit.jsDump.up(); QUnit.jsDump.up();
for ( var key in map ) for ( var key in map ) {
ret.push( QUnit.jsDump.parse(key,'key') + ': ' + QUnit.jsDump.parse(map[key]) ); var val = map[key];
ret.push( QUnit.jsDump.parse(key,'key') + ': ' + QUnit.jsDump.parse(val, undefined, stack));
}
QUnit.jsDump.down(); QUnit.jsDump.down();
return join( '{', ret, '}' ); return join( '{', ret, '}' );
}, },
@ -1275,6 +1396,21 @@ function getText( elems ) {
return ret; return ret;
}; };
//from jquery.js
function inArray( elem, array ) {
if ( array.indexOf ) {
return array.indexOf( elem );
}
for ( var i = 0, length = array.length; i < length; i++ ) {
if ( array[ i ] === elem ) {
return i;
}
}
return -1;
}
/* /*
* Javascript Diff Algorithm * Javascript Diff Algorithm
* By John Resig (http://ejohn.org/) * By John Resig (http://ejohn.org/)
@ -1290,14 +1426,14 @@ function getText( elems ) {
* QUnit.diff("the quick brown fox jumped over", "the quick fox jumps over") == "the quick <del>brown </del> fox <del>jumped </del><ins>jumps </ins> over" * QUnit.diff("the quick brown fox jumped over", "the quick fox jumps over") == "the quick <del>brown </del> fox <del>jumped </del><ins>jumps </ins> over"
*/ */
QUnit.diff = (function() { QUnit.diff = (function() {
function diff(o, n){ function diff(o, n) {
var ns = new Object(); var ns = {};
var os = new Object(); var os = {};
for (var i = 0; i < n.length; i++) { for (var i = 0; i < n.length; i++) {
if (ns[n[i]] == null) if (ns[n[i]] == null)
ns[n[i]] = { ns[n[i]] = {
rows: new Array(), rows: [],
o: null o: null
}; };
ns[n[i]].rows.push(i); ns[n[i]].rows.push(i);
@ -1306,7 +1442,7 @@ QUnit.diff = (function() {
for (var i = 0; i < o.length; i++) { for (var i = 0; i < o.length; i++) {
if (os[o[i]] == null) if (os[o[i]] == null)
os[o[i]] = { os[o[i]] = {
rows: new Array(), rows: [],
n: null n: null
}; };
os[o[i]].rows.push(i); os[o[i]].rows.push(i);
@ -1359,7 +1495,7 @@ QUnit.diff = (function() {
}; };
} }
return function(o, n){ return function(o, n) {
o = o.replace(/\s+$/, ''); o = o.replace(/\s+$/, '');
n = n.replace(/\s+$/, ''); n = n.replace(/\s+$/, '');
var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/)); var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/));

Loading…
Cancel
Save