diff --git a/addons/web/static/lib/qunit/qunit.css b/addons/web/static/lib/qunit/qunit.css
index 257b224ff44..7ba3f9a30b8 100644
--- a/addons/web/static/lib/qunit/qunit.css
+++ b/addons/web/static/lib/qunit/qunit.css
@@ -1,11 +1,11 @@
/**
- * QUnit v1.9.0 - A JavaScript Unit Testing Framework
+ * QUnit v1.12.0 - A JavaScript Unit Testing Framework
*
- * http://docs.jquery.com/QUnit
+ * http://qunitjs.com
*
- * Copyright (c) 2012 John Resig, Jörn Zaefferer
- * Dual licensed under the MIT (MIT-LICENSE.txt)
- * or GPL (GPL-LICENSE.txt) licenses.
+ * Copyright 2012 jQuery Foundation and other contributors
+ * Released under the MIT license.
+ * http://jquery.org/license
*/
/** Font Family and Sizes */
@@ -20,7 +20,7 @@
/** Resets */
-#qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult {
+#qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
margin: 0;
padding: 0;
}
@@ -67,6 +67,7 @@
padding: 0.5em 0 0.5em 2em;
color: #5E740B;
background-color: #eee;
+ overflow: hidden;
}
#qunit-userAgent {
@@ -76,6 +77,9 @@
text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
}
+#qunit-modulefilter-container {
+ float: right;
+}
/** Tests: Pass/Fail */
@@ -107,7 +111,12 @@
color: #000;
}
-#qunit-tests ol {
+#qunit-tests li .runtime {
+ float: right;
+ font-size: smaller;
+}
+
+.qunit-assert-list {
margin-top: 0.5em;
padding: 0.5em;
@@ -118,6 +127,10 @@
-webkit-border-radius: 5px;
}
+.qunit-collapsed {
+ display: none;
+}
+
#qunit-tests table {
border-collapse: collapse;
margin-top: .2em;
diff --git a/addons/web/static/lib/qunit/qunit.js b/addons/web/static/lib/qunit/qunit.js
index 65625336453..c36724e33b2 100644
--- a/addons/web/static/lib/qunit/qunit.js
+++ b/addons/web/static/lib/qunit/qunit.js
@@ -1,35 +1,88 @@
/**
- * QUnit v1.9.0 - A JavaScript Unit Testing Framework
+ * QUnit v1.12.0 - A JavaScript Unit Testing Framework
*
- * http://docs.jquery.com/QUnit
+ * http://qunitjs.com
*
- * Copyright (c) 2012 John Resig, Jörn Zaefferer
- * Dual licensed under the MIT (MIT-LICENSE.txt)
- * or GPL (GPL-LICENSE.txt) licenses.
+ * Copyright 2013 jQuery Foundation and other contributors
+ * Released under the MIT license.
+ * https://jquery.org/license/
*/
(function( window ) {
var QUnit,
+ assert,
config,
onErrorFnPrev,
testId = 0,
fileName = (sourceFromStacktrace( 0 ) || "" ).replace(/(:\d+)+\)?/, "").replace(/.+\//, ""),
toString = Object.prototype.toString,
hasOwn = Object.prototype.hasOwnProperty,
+ // Keep a local reference to Date (GH-283)
+ Date = window.Date,
+ setTimeout = window.setTimeout,
defined = {
- setTimeout: typeof window.setTimeout !== "undefined",
- sessionStorage: (function() {
- var x = "qunit-test-string";
- try {
- sessionStorage.setItem( x, x );
- sessionStorage.removeItem( x );
- return true;
- } catch( e ) {
- return false;
+ setTimeout: typeof window.setTimeout !== "undefined",
+ sessionStorage: (function() {
+ var x = "qunit-test-string";
+ try {
+ sessionStorage.setItem( x, x );
+ sessionStorage.removeItem( x );
+ return true;
+ } catch( e ) {
+ return false;
+ }
+ }())
+ },
+ /**
+ * Provides a normalized error string, correcting an issue
+ * with IE 7 (and prior) where Error.prototype.toString is
+ * not properly implemented
+ *
+ * Based on http://es5.github.com/#x15.11.4.4
+ *
+ * @param {String|Error} error
+ * @return {String} error message
+ */
+ errorString = function( error ) {
+ var name, message,
+ errorString = error.toString();
+ if ( errorString.substring( 0, 7 ) === "[object" ) {
+ name = error.name ? error.name.toString() : "Error";
+ message = error.message ? error.message.toString() : "";
+ if ( name && message ) {
+ return name + ": " + message;
+ } else if ( name ) {
+ return name;
+ } else if ( message ) {
+ return message;
+ } else {
+ return "Error";
+ }
+ } else {
+ return errorString;
}
- }())
-};
+ },
+ /**
+ * Makes a clone of an object using only Array or Object as base,
+ * and copies over the own enumerable properties.
+ *
+ * @param {Object} obj
+ * @return {Object} New object with only the own properties (recursively).
+ */
+ objectValues = function( obj ) {
+ // Grunt 0.3.x uses an older version of jshint that still has jshint/jshint#392.
+ /*jshint newcap: false */
+ var key, val,
+ vals = QUnit.is( "array", obj ) ? [] : {};
+ for ( key in obj ) {
+ if ( hasOwn.call( obj, key ) ) {
+ val = obj[key];
+ vals[key] = val === Object(val) ? objectValues(val) : val;
+ }
+ }
+ return vals;
+ };
function Test( settings ) {
extend( this, settings );
@@ -42,11 +95,11 @@ Test.count = 0;
Test.prototype = {
init: function() {
var a, b, li,
- tests = id( "qunit-tests" );
+ tests = id( "qunit-tests" );
if ( tests ) {
b = document.createElement( "strong" );
- b.innerHTML = this.name;
+ b.innerHTML = this.nameHtml;
// `a` initialized at top of scope
a = document.createElement( "a" );
@@ -63,8 +116,16 @@ Test.prototype = {
}
},
setup: function() {
- if ( this.module !== config.previousModule ) {
- if ( config.previousModule ) {
+ if (
+ // Emit moduleStart when we're switching from one module to another
+ this.module !== config.previousModule ||
+ // They could be equal (both undefined) but if the previousModule property doesn't
+ // yet exist it means this is the first test in a suite that isn't wrapped in a
+ // module, in which case we'll just emit a moduleStart event for 'undefined'.
+ // Without this, reporters can get testStart before moduleStart which is a problem.
+ !hasOwn.call( config, "previousModule" )
+ ) {
+ if ( hasOwn.call( config, "previousModule" ) ) {
runLoggingCallbacks( "moduleDone", QUnit, {
name: config.previousModule,
failed: config.moduleStats.bad,
@@ -77,10 +138,6 @@ Test.prototype = {
runLoggingCallbacks( "moduleStart", QUnit, {
name: this.module
});
- } else if ( config.autorun ) {
- runLoggingCallbacks( "moduleStart", QUnit, {
- name: this.module
- });
}
config.current = this;
@@ -90,26 +147,35 @@ Test.prototype = {
teardown: function() {}
}, this.moduleTestEnvironment );
+ this.started = +new Date();
runLoggingCallbacks( "testStart", QUnit, {
name: this.testName,
module: this.module
});
- // allow utility functions to access the current test environment
- // TODO why??
+ /*jshint camelcase:false */
+
+
+ /**
+ * Expose the current test environment.
+ *
+ * @deprecated since 1.12.0: Use QUnit.config.current.testEnvironment instead.
+ */
QUnit.current_testEnvironment = this.testEnvironment;
+ /*jshint camelcase:true */
+
if ( !config.pollution ) {
saveGlobal();
}
if ( config.notrycatch ) {
- this.testEnvironment.setup.call( this.testEnvironment );
+ this.testEnvironment.setup.call( this.testEnvironment, QUnit.assert );
return;
}
try {
- this.testEnvironment.setup.call( this.testEnvironment );
+ this.testEnvironment.setup.call( this.testEnvironment, QUnit.assert );
} catch( e ) {
- QUnit.pushFailure( "Setup failed on " + this.testName + ": " + e.message, extractStacktrace( e, 1 ) );
+ QUnit.pushFailure( "Setup failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) );
}
},
run: function() {
@@ -118,22 +184,28 @@ Test.prototype = {
var running = id( "qunit-testresult" );
if ( running ) {
- running.innerHTML = "Running:
" + this.name;
+ running.innerHTML = "Running:
" + this.nameHtml;
}
if ( this.async ) {
QUnit.stop();
}
+ this.callbackStarted = +new Date();
+
if ( config.notrycatch ) {
this.callback.call( this.testEnvironment, QUnit.assert );
+ this.callbackRuntime = +new Date() - this.callbackStarted;
return;
}
try {
this.callback.call( this.testEnvironment, QUnit.assert );
+ this.callbackRuntime = +new Date() - this.callbackStarted;
} catch( e ) {
- QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + e.message, extractStacktrace( e, 0 ) );
+ this.callbackRuntime = +new Date() - this.callbackStarted;
+
+ QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) );
// else next test will carry the responsibility
saveGlobal();
@@ -146,38 +218,43 @@ Test.prototype = {
teardown: function() {
config.current = this;
if ( config.notrycatch ) {
- this.testEnvironment.teardown.call( this.testEnvironment );
+ if ( typeof this.callbackRuntime === "undefined" ) {
+ this.callbackRuntime = +new Date() - this.callbackStarted;
+ }
+ this.testEnvironment.teardown.call( this.testEnvironment, QUnit.assert );
return;
} else {
try {
- this.testEnvironment.teardown.call( this.testEnvironment );
+ this.testEnvironment.teardown.call( this.testEnvironment, QUnit.assert );
} catch( e ) {
- QUnit.pushFailure( "Teardown failed on " + this.testName + ": " + e.message, extractStacktrace( e, 1 ) );
+ QUnit.pushFailure( "Teardown failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) );
}
}
checkPollution();
},
finish: function() {
config.current = this;
- if ( config.requireExpects && this.expected == null ) {
+ if ( config.requireExpects && this.expected === null ) {
QUnit.pushFailure( "Expected number of assertions to be defined, but expect() was not called.", this.stack );
- } else if ( this.expected != null && this.expected != this.assertions.length ) {
+ } else if ( this.expected !== null && this.expected !== this.assertions.length ) {
QUnit.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack );
- } else if ( this.expected == null && !this.assertions.length ) {
+ } else if ( this.expected === null && !this.assertions.length ) {
QUnit.pushFailure( "Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.", this.stack );
}
- var assertion, a, b, i, li, ol,
+ var i, assertion, a, b, time, li, ol,
test = this,
good = 0,
bad = 0,
tests = id( "qunit-tests" );
+ this.runtime = +new Date() - this.started;
config.stats.all += this.assertions.length;
config.moduleStats.all += this.assertions.length;
if ( tests ) {
ol = document.createElement( "ol" );
+ ol.className = "qunit-assert-list";
for ( i = 0; i < this.assertions.length; i++ ) {
assertion = this.assertions[i];
@@ -206,22 +283,22 @@ Test.prototype = {
}
if ( bad === 0 ) {
- ol.style.display = "none";
+ addClass( ol, "qunit-collapsed" );
}
// `b` initialized at top of scope
b = document.createElement( "strong" );
- b.innerHTML = this.name + " (" + bad + ", " + good + ", " + this.assertions.length + ")";
+ b.innerHTML = this.nameHtml + " (" + bad + ", " + good + ", " + this.assertions.length + ")";
addEvent(b, "click", function() {
- var next = b.nextSibling.nextSibling,
- display = next.style.display;
- next.style.display = display === "none" ? "block" : "none";
+ var next = b.parentNode.lastChild,
+ collapsed = hasClass( next, "qunit-collapsed" );
+ ( collapsed ? removeClass : addClass )( next, "qunit-collapsed" );
});
addEvent(b, "dblclick", function( e ) {
var target = e && e.target ? e.target : window.event.srcElement;
- if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) {
+ if ( target.nodeName.toLowerCase() === "span" || target.nodeName.toLowerCase() === "b" ) {
target = target.parentNode;
}
if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
@@ -229,13 +306,19 @@ Test.prototype = {
}
});
+ // `time` initialized at top of scope
+ time = document.createElement( "span" );
+ time.className = "runtime";
+ time.innerHTML = this.runtime + " ms";
+
// `li` initialized at top of scope
li = id( this.id );
li.className = bad ? "fail" : "pass";
li.removeChild( li.firstChild );
a = li.firstChild;
li.appendChild( b );
- li.appendChild ( a );
+ li.appendChild( a );
+ li.appendChild( time );
li.appendChild( ol );
} else {
@@ -253,7 +336,8 @@ Test.prototype = {
module: this.module,
failed: bad,
passed: this.assertions.length - bad,
- total: this.assertions.length
+ total: this.assertions.length,
+ duration: this.runtime
});
QUnit.reset();
@@ -304,7 +388,8 @@ QUnit = {
// call on start of module test to prepend name to all tests
module: function( name, testEnvironment ) {
config.currentModule = name;
- config.currentModuleTestEnviroment = testEnvironment;
+ config.currentModuleTestEnvironment = testEnvironment;
+ config.modules[name] = true;
},
asyncTest: function( testName, expected, callback ) {
@@ -318,7 +403,7 @@ QUnit = {
test: function( testName, expected, callback, async ) {
var test,
- name = "" + escapeInnerText( testName ) + "";
+ nameHtml = "" + escapeText( testName ) + "";
if ( arguments.length === 2 ) {
callback = expected;
@@ -326,17 +411,17 @@ QUnit = {
}
if ( config.currentModule ) {
- name = "" + config.currentModule + ": " + name;
+ nameHtml = "" + escapeText( config.currentModule ) + ": " + nameHtml;
}
test = new Test({
- name: name,
+ nameHtml: nameHtml,
testName: testName,
expected: expected,
async: async,
callback: callback,
module: config.currentModule,
- moduleTestEnvironment: config.currentModuleTestEnviroment,
+ moduleTestEnvironment: config.currentModuleTestEnvironment,
stack: sourceFromStacktrace( 2 )
});
@@ -347,12 +432,28 @@ QUnit = {
test.queue();
},
- // Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.
+ // Specify the number of expected assertions to guarantee that failed test (no assertions are run at all) don't slip through.
expect: function( asserts ) {
- config.current.expected = asserts;
+ if (arguments.length === 1) {
+ config.current.expected = asserts;
+ } else {
+ return config.current.expected;
+ }
},
start: function( count ) {
+ // QUnit hasn't been initialized yet.
+ // Note: RequireJS (et al) may delay onLoad
+ if ( config.semaphore === undefined ) {
+ QUnit.begin(function() {
+ // This is triggered at the top of QUnit.load, push start() to the event loop, to allow QUnit.load to finish first
+ setTimeout(function() {
+ QUnit.start( count );
+ });
+ });
+ return;
+ }
+
config.semaphore -= count || 1;
// don't start until equal number of stop-calls
if ( config.semaphore > 0 ) {
@@ -360,11 +461,13 @@ QUnit = {
}
// ignore if start is called more often then stop
if ( config.semaphore < 0 ) {
- config.semaphore = 0;
+ config.semaphore = 0;
+ QUnit.pushFailure( "Called start() while already started (QUnit.config.semaphore was 0 already)", null, sourceFromStacktrace(2) );
+ return;
}
// A slight delay, to avoid any current callbacks
if ( defined.setTimeout ) {
- window.setTimeout(function() {
+ setTimeout(function() {
if ( config.semaphore > 0 ) {
return;
}
@@ -387,7 +490,7 @@ QUnit = {
if ( config.testTimeout && defined.setTimeout ) {
clearTimeout( config.timeout );
- config.timeout = window.setTimeout(function() {
+ config.timeout = setTimeout(function() {
QUnit.ok( false, "Test timed out" );
config.semaphore = 1;
QUnit.start();
@@ -396,11 +499,14 @@ QUnit = {
}
};
-// Asssert helpers
-// All of these must call either QUnit.push() or manually do:
+// `assert` initialized at top of scope
+// Assert helpers
+// All of these must either call QUnit.push() or manually do:
// - runLoggingCallbacks( "log", .. );
// - config.current.assertions.push({ .. });
-QUnit.assert = {
+// We attach it to the QUnit object *after* we expose the public API,
+// otherwise `assert` will become a global variable in browsers (#341).
+assert = {
/**
* Asserts rough true-ish result.
* @name ok
@@ -412,21 +518,23 @@ QUnit.assert = {
throw new Error( "ok() assertion outside test context, was " + sourceFromStacktrace(2) );
}
result = !!result;
+ msg = msg || (result ? "okay" : "failed" );
var source,
details = {
+ module: config.current.module,
+ name: config.current.testName,
result: result,
message: msg
};
- msg = escapeInnerText( msg || (result ? "okay" : "failed" ) );
- msg = "" + msg + "";
+ msg = "" + escapeText( msg ) + "";
if ( !result ) {
source = sourceFromStacktrace( 2 );
if ( source ) {
details.source = source;
- msg += "
Source: | " + escapeInnerText( source ) + " |
---|
";
+ msg += "Source: | " + escapeText( source ) + " |
---|
";
}
}
runLoggingCallbacks( "log", QUnit, details );
@@ -444,6 +552,7 @@ QUnit.assert = {
* @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" );
*/
equal: function( actual, expected, message ) {
+ /*jshint eqeqeq:false */
QUnit.push( expected == actual, actual, expected, message );
},
@@ -452,9 +561,30 @@ QUnit.assert = {
* @function
*/
notEqual: function( actual, expected, message ) {
+ /*jshint eqeqeq:false */
QUnit.push( expected != actual, actual, expected, message );
},
+ /**
+ * @name propEqual
+ * @function
+ */
+ propEqual: function( actual, expected, message ) {
+ actual = objectValues(actual);
+ expected = objectValues(expected);
+ QUnit.push( QUnit.equiv(actual, expected), actual, expected, message );
+ },
+
+ /**
+ * @name notPropEqual
+ * @function
+ */
+ notPropEqual: function( actual, expected, message ) {
+ actual = objectValues(actual);
+ expected = objectValues(expected);
+ QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );
+ },
+
/**
* @name deepEqual
* @function
@@ -487,8 +617,9 @@ QUnit.assert = {
QUnit.push( expected !== actual, actual, expected, message );
},
- throws: function( block, expected, message ) {
+ "throws": function( block, expected, message ) {
var actual,
+ expectedOutput = expected,
ok = false;
// 'expected' is optional
@@ -509,35 +640,38 @@ QUnit.assert = {
// we don't want to validate thrown error
if ( !expected ) {
ok = true;
+ expectedOutput = null;
// expected is a regexp
} else if ( QUnit.objectType( expected ) === "regexp" ) {
- ok = expected.test( actual );
+ ok = expected.test( errorString( actual ) );
// expected is a constructor
} else if ( actual instanceof expected ) {
ok = true;
// expected is a validation function which returns true is validation passed
} else if ( expected.call( {}, actual ) === true ) {
+ expectedOutput = null;
ok = true;
}
- QUnit.push( ok, actual, null, message );
+ QUnit.push( ok, actual, expectedOutput, message );
} else {
- QUnit.pushFailure( message, null, 'No exception was thrown.' );
+ QUnit.pushFailure( message, null, "No exception was thrown." );
}
}
};
/**
- * @deprecate since 1.8.0
- * Kept assertion helpers in root for backwards compatibility
+ * @deprecated since 1.8.0
+ * Kept assertion helpers in root for backwards compatibility.
*/
-extend( QUnit, QUnit.assert );
+extend( QUnit, assert );
/**
* @deprecated since 1.9.0
- * Kept global "raises()" for backwards compatibility
+ * Kept root "raises()" for backwards compatibility.
+ * (Note that we don't introduce assert.raises).
*/
-QUnit.raises = QUnit.assert.throws;
+QUnit.raises = assert[ "throws" ];
/**
* @deprecated since 1.0.0, replaced with error pushes since 1.3.0
@@ -600,6 +734,9 @@ config = {
}
],
+ // Set of all modules.
+ modules: {},
+
// logging callback queues
begin: [],
done: [],
@@ -610,6 +747,15 @@ config = {
moduleDone: []
};
+// Export global variables, unless an 'exports' object exists,
+// in that case we assume we're in CommonJS (dealt with on the bottom of the script)
+if ( typeof exports === "undefined" ) {
+ extend( window, QUnit.constructor.prototype );
+
+ // Expose QUnit object
+ window.QUnit = QUnit;
+}
+
// Initialize more QUnit.config and QUnit.urlParams
(function() {
var i,
@@ -643,18 +789,11 @@ config = {
QUnit.isLocal = location.protocol === "file:";
}());
-// Export global variables, unless an 'exports' object exists,
-// in that case we assume we're in CommonJS (dealt with on the bottom of the script)
-if ( typeof exports === "undefined" ) {
- extend( window, QUnit );
-
- // Expose QUnit object
- window.QUnit = QUnit;
-}
-
// Extend QUnit object,
// these after set here because they should not be exposed as global functions
extend( QUnit, {
+ assert: assert,
+
config: config,
// Initialize the configuration options
@@ -669,7 +808,7 @@ extend( QUnit, {
autorun: false,
filter: "",
queue: [],
- semaphore: 0
+ semaphore: 1
});
var tests, banner, result,
@@ -677,7 +816,7 @@ extend( QUnit, {
if ( qunit ) {
qunit.innerHTML =
- "" +
+ "" +
"" +
"" +
"" +
@@ -710,17 +849,15 @@ extend( QUnit, {
},
// Resets the test setup. Useful for tests that modify the DOM.
- // If jQuery is available, uses jQuery's html(), otherwise just innerHTML.
+ /*
+ DEPRECATED: Use multiple tests instead of resetting inside a test.
+ Use testStart or testDone for custom cleanup.
+ This method will throw an error in 2.0, and will be removed in 2.1
+ */
reset: function() {
- var fixture;
-
- if ( window.jQuery ) {
- jQuery( "#qunit-fixture" ).html( config.fixture );
- } else {
- fixture = id( "qunit-fixture" );
- if ( fixture ) {
- fixture.innerHTML = config.fixture;
- }
+ var fixture = id( "qunit-fixture" );
+ if ( fixture ) {
+ fixture.innerHTML = config.fixture;
}
},
@@ -740,7 +877,7 @@ extend( QUnit, {
// Safe object type checking
is: function( type, obj ) {
- return QUnit.objectType( obj ) == type;
+ return QUnit.objectType( obj ) === type;
},
objectType: function( obj ) {
@@ -752,7 +889,8 @@ extend( QUnit, {
return "null";
}
- var type = toString.call( obj ).match(/^\[object\s(.*)\]$/)[1] || "";
+ var match = toString.call( obj ).match(/^\[object\s(.*)\]$/),
+ type = match && match[1] || "";
switch ( type ) {
case "Number":
@@ -781,22 +919,24 @@ extend( QUnit, {
var output, source,
details = {
+ module: config.current.module,
+ name: config.current.testName,
result: result,
message: message,
actual: actual,
expected: expected
};
- message = escapeInnerText( message ) || ( result ? "okay" : "failed" );
+ message = escapeText( message ) || ( result ? "okay" : "failed" );
message = "" + message + "";
output = message;
if ( !result ) {
- expected = escapeInnerText( QUnit.jsDump.parse(expected) );
- actual = escapeInnerText( QUnit.jsDump.parse(actual) );
+ expected = escapeText( QUnit.jsDump.parse(expected) );
+ actual = escapeText( QUnit.jsDump.parse(actual) );
output += "Expected: | " + expected + " |
";
- if ( actual != expected ) {
+ if ( actual !== expected ) {
output += "Result: | " + actual + " |
";
output += "Diff: | " + QUnit.diff( expected, actual ) + " |
";
}
@@ -805,7 +945,7 @@ extend( QUnit, {
if ( source ) {
details.source = source;
- output += "Source: | " + escapeInnerText( source ) + " |
";
+ output += "Source: | " + escapeText( source ) + " |
";
}
output += "
";
@@ -826,23 +966,25 @@ extend( QUnit, {
var output,
details = {
+ module: config.current.module,
+ name: config.current.testName,
result: false,
message: message
};
- message = escapeInnerText( message ) || "error";
+ message = escapeText( message ) || "error";
message = "" + message + "";
output = message;
output += "";
if ( actual ) {
- output += "Result: | " + escapeInnerText( actual ) + " |
";
+ output += "Result: | " + escapeText( actual ) + " |
";
}
if ( source ) {
details.source = source;
- output += "Source: | " + escapeInnerText( source ) + " |
";
+ output += "Source: | " + escapeText( source ) + " |
";
}
output += "
";
@@ -861,18 +1003,21 @@ extend( QUnit, {
querystring = "?";
for ( key in params ) {
- if ( !hasOwn.call( params, key ) ) {
- continue;
+ if ( hasOwn.call( params, key ) ) {
+ querystring += encodeURIComponent( key ) + "=" +
+ encodeURIComponent( params[ key ] ) + "&";
}
- querystring += encodeURIComponent( key ) + "=" +
- encodeURIComponent( params[ key ] ) + "&";
}
- return window.location.pathname + querystring.slice( 0, -1 );
+ return window.location.protocol + "//" + window.location.host +
+ window.location.pathname + querystring.slice( 0, -1 );
},
extend: extend,
id: id,
- addEvent: addEvent
+ addEvent: addEvent,
+ addClass: addClass,
+ hasClass: hasClass,
+ removeClass: removeClass
// load, equiv, jsDump, diff: Attached later
});
@@ -898,7 +1043,7 @@ extend( QUnit.constructor.prototype, {
// testStart: { name }
testStart: registerLoggingCallback( "testStart" ),
- // testDone: { name, failed, passed, total }
+ // testDone: { name, failed, passed, total, duration }
testDone: registerLoggingCallback( "testDone" ),
// moduleStart: { name }
@@ -916,7 +1061,11 @@ QUnit.load = function() {
runLoggingCallbacks( "begin", QUnit, {} );
// Initialize the config, saving the execution queue
- var banner, filter, i, label, len, main, ol, toolbar, userAgent, val, urlConfigCheckboxes,
+ var banner, filter, i, label, len, main, ol, toolbar, userAgent, val,
+ urlConfigCheckboxesContainer, urlConfigCheckboxes, moduleFilter,
+ numModules = 0,
+ moduleNames = [],
+ moduleFilterHtml = "",
urlConfigHtml = "",
oldconfig = extend( {}, config );
@@ -937,8 +1086,33 @@ QUnit.load = function() {
};
}
config[ val.id ] = QUnit.urlParams[ val.id ];
- urlConfigHtml += "";
+ urlConfigHtml += "";
}
+ for ( i in config.modules ) {
+ if ( config.modules.hasOwnProperty( i ) ) {
+ moduleNames.push(i);
+ }
+ }
+ numModules = moduleNames.length;
+ moduleNames.sort( function( a, b ) {
+ return a.localeCompare( b );
+ });
+ moduleFilterHtml += "";
// `userAgent` initialized at top of scope
userAgent = id( "qunit-userAgent" );
@@ -990,18 +1164,42 @@ QUnit.load = function() {
// `label` initialized at top of scope
label = document.createElement( "label" );
label.setAttribute( "for", "qunit-filter-pass" );
- label.setAttribute( "title", "Only show tests and assertons that fail. Stored in sessionStorage." );
+ label.setAttribute( "title", "Only show tests and assertions that fail. Stored in sessionStorage." );
label.innerHTML = "Hide passed tests";
toolbar.appendChild( label );
- urlConfigCheckboxes = document.createElement( 'span' );
- urlConfigCheckboxes.innerHTML = urlConfigHtml;
- addEvent( urlConfigCheckboxes, "change", function( event ) {
- var params = {};
- params[ event.target.name ] = event.target.checked ? true : undefined;
+ urlConfigCheckboxesContainer = document.createElement("span");
+ urlConfigCheckboxesContainer.innerHTML = urlConfigHtml;
+ urlConfigCheckboxes = urlConfigCheckboxesContainer.getElementsByTagName("input");
+ // For oldIE support:
+ // * Add handlers to the individual elements instead of the container
+ // * Use "click" instead of "change"
+ // * Fallback from event.target to event.srcElement
+ addEvents( urlConfigCheckboxes, "click", function( event ) {
+ var params = {},
+ target = event.target || event.srcElement;
+ params[ target.name ] = target.checked ? true : undefined;
window.location = QUnit.url( params );
});
- toolbar.appendChild( urlConfigCheckboxes );
+ toolbar.appendChild( urlConfigCheckboxesContainer );
+
+ if (numModules > 1) {
+ moduleFilter = document.createElement( "span" );
+ moduleFilter.setAttribute( "id", "qunit-modulefilter-container" );
+ moduleFilter.innerHTML = moduleFilterHtml;
+ addEvent( moduleFilter.lastChild, "change", function() {
+ var selectBox = moduleFilter.getElementsByTagName("select")[0],
+ selectedModule = decodeURIComponent(selectBox.options[selectBox.selectedIndex].value);
+
+ window.location = QUnit.url({
+ module: ( selectedModule === "" ) ? undefined : selectedModule,
+ // Remove any existing filters
+ filter: undefined,
+ testNumber: undefined
+ });
+ });
+ toolbar.appendChild(moduleFilter);
+ }
}
// `main` initialized at top of scope
@@ -1022,7 +1220,7 @@ addEvent( window, "load", QUnit.load );
onErrorFnPrev = window.onerror;
// Cover uncaught exceptions
-// Returning true will surpress the default browser handler,
+// Returning true will suppress the default browser handler,
// returning false will let it run.
window.onerror = function ( error, filePath, linerNr ) {
var ret = false;
@@ -1031,7 +1229,7 @@ window.onerror = function ( error, filePath, linerNr ) {
}
// Treat return value as window.onerror itself does,
- // Only do our handling if not surpressed.
+ // Only do our handling if not suppressed.
if ( ret !== true ) {
if ( QUnit.config.current ) {
if ( QUnit.config.current.ignoreGlobalErrors ) {
@@ -1039,9 +1237,9 @@ window.onerror = function ( error, filePath, linerNr ) {
}
QUnit.pushFailure( error, filePath + ":" + linerNr );
} else {
- QUnit.test( "global failure", function() {
+ QUnit.test( "global failure", extend( function() {
QUnit.pushFailure( error, filePath + ":" + linerNr );
- });
+ }, { validTest: validTest } ) );
}
return false;
}
@@ -1061,6 +1259,7 @@ function done() {
total: config.moduleStats.all
});
}
+ delete config.previousModule;
var i, key,
banner = id( "qunit-banner" ),
@@ -1073,7 +1272,7 @@ function done() {
" milliseconds.
",
"",
passed,
- " tests of ",
+ " assertions of ",
config.stats.all,
" passed, ",
config.stats.bad,
@@ -1108,6 +1307,11 @@ function done() {
}
}
+ // scroll back to top to show results
+ if ( window.scrollTo ) {
+ window.scrollTo(0, 0);
+ }
+
runLoggingCallbacks( "done", QUnit, {
failed: config.stats.bad,
passed: passed,
@@ -1123,6 +1327,12 @@ function validTest( test ) {
module = config.module && config.module.toLowerCase(),
fullName = (test.module + ": " + test.testName).toLowerCase();
+ // Internally-generated tests are always valid
+ if ( test.callback && test.callback.validTest === validTest ) {
+ delete test.callback.validTest;
+ return true;
+ }
+
if ( config.testNumber ) {
return test.testNumber === config.testNumber;
}
@@ -1155,7 +1365,7 @@ function validTest( test ) {
function extractStacktrace( e, offset ) {
offset = offset === undefined ? 3 : offset;
- var stack, include, i, regex;
+ var stack, include, i;
if ( e.stacktrace ) {
// Opera
@@ -1169,7 +1379,7 @@ function extractStacktrace( e, offset ) {
if ( fileName ) {
include = [];
for ( i = offset; i < stack.length; i++ ) {
- if ( stack[ i ].indexOf( fileName ) != -1 ) {
+ if ( stack[ i ].indexOf( fileName ) !== -1 ) {
break;
}
include.push( stack[ i ] );
@@ -1198,17 +1408,27 @@ function sourceFromStacktrace( offset ) {
}
}
-function escapeInnerText( s ) {
+/**
+ * Escape text for attribute or text content.
+ */
+function escapeText( s ) {
if ( !s ) {
return "";
}
s = s + "";
- return s.replace( /[\&<>]/g, function( s ) {
+ // Both single quotes and double quotes (for attributes)
+ return s.replace( /['"<>&]/g, function( s ) {
switch( s ) {
- case "&": return "&";
- case "<": return "<";
- case ">": return ">";
- default: return s;
+ case "'":
+ return "'";
+ case "\"":
+ return """;
+ case "<":
+ return "<";
+ case ">":
+ return ">";
+ case "&":
+ return "&";
}
});
}
@@ -1232,7 +1452,7 @@ function process( last ) {
if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) {
config.queue.shift()();
} else {
- window.setTimeout( next, 13 );
+ setTimeout( next, 13 );
break;
}
}
@@ -1247,16 +1467,18 @@ function saveGlobal() {
if ( config.noglobals ) {
for ( var key in window ) {
- // in Opera sometimes DOM element ids show up here, ignore them
- if ( !hasOwn.call( window, key ) || /^qunit-test-output/.test( key ) ) {
- continue;
+ if ( hasOwn.call( window, key ) ) {
+ // in Opera sometimes DOM element ids show up here, ignore them
+ if ( /^qunit-test-output/.test( key ) ) {
+ continue;
+ }
+ config.pollution.push( key );
}
- config.pollution.push( key );
}
}
}
-function checkPollution( name ) {
+function checkPollution() {
var newGlobals,
deletedGlobals,
old = config.pollution;
@@ -1293,28 +1515,68 @@ function diff( a, b ) {
function extend( a, b ) {
for ( var prop in b ) {
- if ( b[ prop ] === undefined ) {
- delete a[ prop ];
-
- // Avoid "Member not found" error in IE8 caused by setting window.constructor
- } else if ( prop !== "constructor" || a !== window ) {
- a[ prop ] = b[ prop ];
+ if ( hasOwn.call( b, prop ) ) {
+ // Avoid "Member not found" error in IE8 caused by messing with window.constructor
+ if ( !( prop === "constructor" && a === window ) ) {
+ if ( b[ prop ] === undefined ) {
+ delete a[ prop ];
+ } else {
+ a[ prop ] = b[ prop ];
+ }
+ }
}
}
return a;
}
+/**
+ * @param {HTMLElement} elem
+ * @param {string} type
+ * @param {Function} fn
+ */
function addEvent( elem, type, fn ) {
+ // Standards-based browsers
if ( elem.addEventListener ) {
elem.addEventListener( type, fn, false );
- } else if ( elem.attachEvent ) {
- elem.attachEvent( "on" + type, fn );
+ // IE
} else {
- fn();
+ elem.attachEvent( "on" + type, fn );
}
}
+/**
+ * @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 + " ") > -1;
+}
+
+function addClass( elem, name ) {
+ if ( !hasClass( elem, name ) ) {
+ elem.className += (elem.className ? " " : "") + name;
+ }
+}
+
+function removeClass( elem, name ) {
+ var set = " " + elem.className + " ";
+ // Class name may appear multiple times
+ while ( set.indexOf(" " + name + " ") > -1 ) {
+ set = set.replace(" " + name + " " , " ");
+ }
+ // If possible, trim it for prettiness, but not necessarily
+ elem.className = typeof set.trim === "function" ? set.trim() : set.replace(/^\s+|\s+$/g, "");
+}
+
function id( name ) {
return !!( typeof document !== "undefined" && document && document.getElementById ) &&
document.getElementById( name );
@@ -1328,7 +1590,6 @@ function registerLoggingCallback( key ) {
// Supports deprecated method of completely overwriting logging callbacks
function runLoggingCallbacks( key, scope, args ) {
- //debugger;
var i, callbacks;
if ( QUnit.hasOwnProperty( key ) ) {
QUnit[ key ].call(scope, args );
@@ -1362,16 +1623,19 @@ QUnit.equiv = (function() {
callers = [],
// stack to avoiding loops from circular referencing
parents = [],
+ parentsB = [],
getProto = Object.getPrototypeOf || function ( obj ) {
+ /*jshint camelcase:false */
return obj.__proto__;
},
callbacks = (function () {
// for string, boolean, number and null
function useStrictEquality( b, a ) {
+ /*jshint eqeqeq:false */
if ( b instanceof a.constructor || a instanceof b.constructor ) {
- // to catch short annotaion VS 'new' annotation of a
+ // to catch short annotation VS 'new' annotation of a
// declaration
// e.g. var i = 1;
// var j = new Number(1);
@@ -1400,11 +1664,12 @@ QUnit.equiv = (function() {
return QUnit.objectType( b ) === "regexp" &&
// the regex itself
a.source === b.source &&
- // and its modifers
+ // and its modifiers
a.global === b.global &&
// (gmi) ...
a.ignoreCase === b.ignoreCase &&
- a.multiline === b.multiline;
+ a.multiline === b.multiline &&
+ a.sticky === b.sticky;
},
// - skip when the property is a method of an instance (OOP)
@@ -1416,7 +1681,7 @@ QUnit.equiv = (function() {
},
"array": function( b, a ) {
- var i, j, len, loop;
+ var i, j, len, loop, aCircular, bCircular;
// b could be an object literal here
if ( QUnit.objectType( b ) !== "array" ) {
@@ -1431,24 +1696,36 @@ QUnit.equiv = (function() {
// track reference to avoid circular references
parents.push( a );
+ parentsB.push( b );
for ( i = 0; i < len; i++ ) {
loop = false;
for ( j = 0; j < parents.length; j++ ) {
- if ( parents[j] === a[i] ) {
- loop = true;// dont rewalk array
+ aCircular = parents[j] === a[i];
+ bCircular = parentsB[j] === b[i];
+ if ( aCircular || bCircular ) {
+ if ( a[i] === b[i] || aCircular && bCircular ) {
+ loop = true;
+ } else {
+ parents.pop();
+ parentsB.pop();
+ return false;
+ }
}
}
if ( !loop && !innerEquiv(a[i], b[i]) ) {
parents.pop();
+ parentsB.pop();
return false;
}
}
parents.pop();
+ parentsB.pop();
return true;
},
"object": function( b, a ) {
- var i, j, loop,
+ /*jshint forin:false */
+ var i, j, loop, aCircular, bCircular,
// Default to true
eq = true,
aProperties = [],
@@ -1467,28 +1744,36 @@ QUnit.equiv = (function() {
// stack constructor before traversing properties
callers.push( a.constructor );
+
// track reference to avoid circular references
parents.push( a );
+ parentsB.push( b );
- for ( i in a ) { // be strict: don't ensures hasOwnProperty
- // and go deep
+ // be strict: don't ensure hasOwnProperty and go deep
+ for ( i in a ) {
loop = false;
for ( j = 0; j < parents.length; j++ ) {
- if ( parents[j] === a[i] ) {
- // don't go down the same path twice
- loop = true;
+ aCircular = parents[j] === a[i];
+ bCircular = parentsB[j] === b[i];
+ if ( aCircular || bCircular ) {
+ if ( a[i] === b[i] || aCircular && bCircular ) {
+ loop = true;
+ } else {
+ eq = false;
+ break;
+ }
}
}
- aProperties.push(i); // collect a's properties
-
- if (!loop && !innerEquiv( a[i], b[i] ) ) {
+ aProperties.push(i);
+ if ( !loop && !innerEquiv(a[i], b[i]) ) {
eq = false;
break;
}
}
- callers.pop(); // unstack, we are done
parents.pop();
+ parentsB.pop();
+ callers.pop(); // unstack, we are done
for ( i in b ) {
bProperties.push( i ); // collect b's properties
@@ -1518,7 +1803,7 @@ QUnit.equiv = (function() {
}
// apply transition with (1..n) arguments
- }( args[0], args[1] ) && arguments.callee.apply( this, args.splice(1, args.length - 1 )) );
+ }( args[0], args[1] ) && innerEquiv.apply( this, args.splice(1, args.length - 1 )) );
};
return innerEquiv;
@@ -1536,7 +1821,7 @@ QUnit.equiv = (function() {
*/
QUnit.jsDump = (function() {
function quote( str ) {
- return '"' + str.toString().replace( /"/g, '\\"' ) + '"';
+ return "\"" + str.toString().replace( /"/g, "\\\"" ) + "\"";
}
function literal( o ) {
return o + "";
@@ -1565,26 +1850,25 @@ QUnit.jsDump = (function() {
var reName = /^function (\w+)/,
jsDump = {
- parse: function( obj, type, stack ) { //type is used mostly internally, you can fix a (custom)type in advance
- stack = stack || [ ];
+ // type is used mostly internally, you can fix a (custom)type in advance
+ parse: function( obj, type, stack ) {
+ stack = stack || [{}];
var inStack, res,
parser = this.parsers[ type || this.typeOf(obj) ];
type = typeof parser;
inStack = inArray( obj, stack );
- if ( inStack != -1 ) {
+ if ( inStack !== -1 ) {
return "recursion(" + (inStack - stack.length) + ")";
}
- //else
- if ( type == "function" ) {
+ if ( type === "function" ) {
stack.push( obj );
res = parser.call( this, obj, stack );
stack.pop();
return res;
}
- // else
- return ( type == "string" ) ? parser : this.parsers.error;
+ return ( type === "string" ) ? parser : this.parsers.error;
},
typeOf: function( obj ) {
var type;
@@ -1611,6 +1895,8 @@ QUnit.jsDump = (function() {
( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) )
) {
type = "array";
+ } else if ( obj.constructor === Error.prototype.constructor ) {
+ type = "error";
} else {
type = typeof obj;
}
@@ -1619,7 +1905,8 @@ QUnit.jsDump = (function() {
separator: function() {
return this.multiline ? this.HTML ? "
" : "\n" : this.HTML ? " " : " ";
},
- indent: function( extra ) {// 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 "";
}
@@ -1627,13 +1914,13 @@ QUnit.jsDump = (function() {
if ( this.HTML ) {
chr = chr.replace( /\t/g, " " ).replace( / /g, " " );
}
- return new Array( this._depth_ + (extra||0) ).join(chr);
+ return new Array( this.depth + ( extra || 0 ) ).join(chr);
},
up: function( a ) {
- this._depth_ += a || 1;
+ this.depth += a || 1;
},
down: function( a ) {
- this._depth_ -= a || 1;
+ this.depth -= a || 1;
},
setParser: function( name, parser ) {
this.parsers[name] = parser;
@@ -1643,18 +1930,21 @@ QUnit.jsDump = (function() {
literal: literal,
join: join,
//
- _depth_: 1,
+ depth: 1,
// This is the list of parsers, to modify them, use jsDump.setParser
parsers: {
window: "[Window]",
document: "[Document]",
- error: "[ERROR]", //when no parser is found, shouldn"t happen
+ error: function(error) {
+ return "Error(\"" + error.message + "\")";
+ },
unknown: "[Unknown]",
"null": "null",
"undefined": "undefined",
"function": function( fn ) {
var ret = "function",
- name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1];//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;
@@ -1668,15 +1958,12 @@ QUnit.jsDump = (function() {
nodelist: array,
"arguments": array,
object: function( map, stack ) {
+ /*jshint forin:false */
var ret = [ ], keys, key, val, i;
QUnit.jsDump.up();
- if ( Object.keys ) {
- keys = Object.keys( map );
- } else {
- keys = [];
- for ( key in map ) {
- keys.push( key );
- }
+ keys = [];
+ for ( key in map ) {
+ keys.push( key );
}
keys.sort();
for ( i = 0; i < keys.length; i++ ) {
@@ -1688,21 +1975,34 @@ QUnit.jsDump = (function() {
return join( "{", ret, "}" );
},
node: function( node ) {
- var a, val,
+ var len, i, val,
open = QUnit.jsDump.HTML ? "<" : "<",
close = QUnit.jsDump.HTML ? ">" : ">",
tag = node.nodeName.toLowerCase(),
- ret = open + tag;
+ ret = open + tag,
+ attrs = node.attributes;
- for ( a in QUnit.jsDump.DOMAttrs ) {
- val = node[ QUnit.jsDump.DOMAttrs[a] ];
- if ( val ) {
- ret += " " + a + "=" + QUnit.jsDump.parse( val, "attribute" );
+ if ( attrs ) {
+ for ( i = 0, len = attrs.length; i < len; i++ ) {
+ val = attrs[i].nodeValue;
+ // IE6 includes all attributes in .attributes, even ones not explicitly set.
+ // Those have values like undefined, null, 0, false, "" or "inherit".
+ if ( val && val !== "inherit" ) {
+ ret += " " + attrs[i].nodeName + "=" + QUnit.jsDump.parse( val, "attribute" );
+ }
}
}
- return ret + close + open + "/" + tag + close;
+ ret += close;
+
+ // Show content of TextNode or CDATASection
+ if ( node.nodeType === 3 || node.nodeType === 4 ) {
+ ret += node.nodeValue;
+ }
+
+ return ret + open + "/" + tag + close;
},
- functionArgs: function( fn ) {//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;
@@ -1712,54 +2012,34 @@ QUnit.jsDump = (function() {
args = new Array(l);
while ( l-- ) {
- args[l] = String.fromCharCode(97+l);//97 is 'a'
+ // 97 is 'a'
+ args[l] = String.fromCharCode(97+l);
}
return " " + args.join( ", " ) + " ";
},
- key: quote, //object calls it internally, the key part of an item in a map
- functionCode: "[code]", //function calls it internally, it's the content of the function
- attribute: quote, //node calls it internally, it's an html attribute value
+ // 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
+ functionCode: "[code]",
+ // node calls it internally, it's an html attribute value
+ attribute: quote,
string: quote,
date: quote,
- regexp: literal, //regex
+ regexp: literal,
number: literal,
"boolean": literal
},
- DOMAttrs: {
- //attributes to dump from nodes, name=>realName
- id: "id",
- name: "name",
- "class": "className"
- },
- HTML: false,//if true, entities are escaped ( <, >, \t, space and \n )
- indentChar: " ",//indentation unit
- multiline: true //if true, items in a collection, are separated by a \n, else just a space.
+ // if true, entities are escaped ( <, >, \t, space and \n )
+ HTML: false,
+ // indentation unit
+ indentChar: " ",
+ // if true, items in a collection, are separated by a \n, else just a space.
+ multiline: true
};
return jsDump;
}());
-// from Sizzle.js
-function getText( elems ) {
- var i, elem,
- ret = "";
-
- for ( i = 0; elems[i]; i++ ) {
- elem = elems[i];
-
- // Get the text from text nodes and CDATA nodes
- if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
- ret += elem.nodeValue;
-
- // Traverse everything else, except comment nodes
- } else if ( elem.nodeType !== 8 ) {
- ret += getText( elem.childNodes );
- }
- }
-
- return ret;
-}
-
// from jquery.js
function inArray( elem, array ) {
if ( array.indexOf ) {
@@ -1790,13 +2070,14 @@ function inArray( elem, array ) {
* QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) == "the quick brown fox jumped jumps over"
*/
QUnit.diff = (function() {
+ /*jshint eqeqeq:false, eqnull:true */
function diff( o, n ) {
var i,
ns = {},
os = {};
for ( i = 0; i < n.length; i++ ) {
- if ( ns[ n[i] ] == null ) {
+ if ( !hasOwn.call( ns, n[i] ) ) {
ns[ n[i] ] = {
rows: [],
o: null
@@ -1806,7 +2087,7 @@ QUnit.diff = (function() {
}
for ( i = 0; i < o.length; i++ ) {
- if ( os[ o[i] ] == null ) {
+ if ( !hasOwn.call( os, o[i] ) ) {
os[ o[i] ] = {
rows: [],
n: null
@@ -1816,18 +2097,17 @@ QUnit.diff = (function() {
}
for ( i in ns ) {
- if ( !hasOwn.call( ns, i ) ) {
- continue;
- }
- if ( ns[i].rows.length == 1 && typeof os[i] != "undefined" && os[i].rows.length == 1 ) {
- n[ ns[i].rows[0] ] = {
- text: n[ ns[i].rows[0] ],
- row: os[i].rows[0]
- };
- o[ os[i].rows[0] ] = {
- text: o[ os[i].rows[0] ],
- row: ns[i].rows[0]
- };
+ if ( hasOwn.call( ns, i ) ) {
+ if ( ns[i].rows.length === 1 && hasOwn.call( os, i ) && os[i].rows.length === 1 ) {
+ n[ ns[i].rows[0] ] = {
+ text: n[ ns[i].rows[0] ],
+ row: os[i].rows[0]
+ };
+ o[ os[i].rows[0] ] = {
+ text: o[ os[i].rows[0] ],
+ row: ns[i].rows[0]
+ };
+ }
}
}
@@ -1923,9 +2203,9 @@ QUnit.diff = (function() {
};
}());
-// for CommonJS enviroments, export everything
+// for CommonJS environments, export everything
if ( typeof exports !== "undefined" ) {
- extend(exports, QUnit);
+ extend( exports, QUnit.constructor.prototype );
}
// get at whatever the global object is, like window in browsers
diff --git a/addons/web/static/src/js/testing.js b/addons/web/static/src/js/testing.js
index af434c4c0f4..880da9f1bc1 100644
--- a/addons/web/static/src/js/testing.js
+++ b/addons/web/static/src/js/testing.js
@@ -195,8 +195,7 @@ openerp.testing = {};
0, module_index + 1 || undefined);
// Serialize options for this precise test case
- // WARNING: typo is from jquery, do not fix!
- var env = QUnit.config.currentModuleTestEnviroment;
+ var env = QUnit.config.currentModuleTestEnvironment;
// section setup
// case setup
// test