/**
- * QUnit - A JavaScript Unit Testing Framework
+ * QUnit 1.2.0pre - A JavaScript Unit Testing Framework
*
* http://docs.jquery.com/QUnit
*
})()
};
-var testId = 0;
+var testId = 0,
+ toString = Object.prototype.toString,
+ hasOwn = Object.prototype.hasOwnProperty;
var Test = function(name, testName, expected, testEnvironmentArg, async, callback) {
this.name = name;
setup: function() {
if (this.module != config.previousModule) {
if ( config.previousModule ) {
- QUnit.moduleDone( {
+ runLoggingCallbacks('moduleDone', QUnit, {
name: config.previousModule,
failed: config.moduleStats.bad,
passed: config.moduleStats.all - config.moduleStats.bad,
}
config.previousModule = this.module;
config.moduleStats = { all: 0, bad: 0 };
- QUnit.moduleStart( {
+ runLoggingCallbacks( 'moduleStart', QUnit, {
name: this.module
} );
}
extend(this.testEnvironment, this.testEnvironmentArg);
}
- QUnit.testStart( {
- name: this.testName
- } );
+ runLoggingCallbacks( 'testStart', QUnit, {
+ name: this.testName,
+ module: this.module
+ });
// allow utility functions to access the current test environment
// TODO why??
}
},
run: function() {
+ config.current = this;
if ( this.async ) {
QUnit.stop();
}
// Restart the tests if they're blocking
if ( config.blocking ) {
- start();
+ QUnit.start();
}
}
},
teardown: function() {
+ config.current = this;
try {
this.testEnvironment.teardown.call(this.testEnvironment);
checkPollution();
}
},
finish: function() {
- if ( this.expected && this.expected != this.assertions.length ) {
+ config.current = this;
+ if ( this.expected != null && this.expected != this.assertions.length ) {
QUnit.ok( false, "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run" );
}
fail("reset() failed, following Test " + this.testName + ", exception and reset fn follows", e, QUnit.reset);
}
- QUnit.testDone( {
+ runLoggingCallbacks( 'testDone', QUnit, {
name: this.testName,
+ module: this.module,
failed: bad,
passed: this.assertions.length - bad,
total: this.assertions.length
if (bad) {
run();
} else {
- synchronize(run);
+ synchronize(run, true);
};
}
asyncTest: function(testName, expected, callback) {
if ( arguments.length === 2 ) {
callback = expected;
- expected = 0;
+ expected = null;
}
QUnit.test(testName, expected, callback, true);
result: a,
message: msg
};
- msg = escapeHtml(msg);
- QUnit.log(details);
+ msg = escapeInnerText(msg);
+ runLoggingCallbacks( 'log', QUnit, details );
config.current.assertions.push({
result: a,
message: msg
QUnit.ok(ok, message);
},
- start: function() {
- config.semaphore--;
+ start: function(count) {
+ config.semaphore -= count || 1;
if (config.semaphore > 0) {
// don't start until equal number of stop-calls
return;
}
config.blocking = false;
- process();
+ process(true);
}, 13);
} else {
config.blocking = false;
- process();
+ process(true);
}
},
- stop: function(timeout) {
- config.semaphore++;
+ stop: function(count) {
+ config.semaphore += count || 1;
config.blocking = true;
- if ( timeout && defined.setTimeout ) {
+ if ( config.testTimeout && defined.setTimeout ) {
clearTimeout(config.timeout);
config.timeout = window.setTimeout(function() {
QUnit.ok( false, "Test timed out" );
+ config.semaphore = 1;
QUnit.start();
- }, timeout);
+ }, config.testTimeout);
}
}
};
+//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
QUnit.equals = QUnit.equal;
QUnit.same = QUnit.deepEqual;
// by default, modify document.title when suite is done
altertitle: true,
- urlConfig: ['noglobals', 'notrycatch']
+ urlConfig: ['noglobals', 'notrycatch'],
+
+ //logging callback queues
+ begin: [],
+ done: [],
+ log: [],
+ testStart: [],
+ testDone: [],
+ moduleStart: [],
+ moduleDone: []
};
// Load paramaters
return "null";
}
- var type = Object.prototype.toString.call( obj )
- .match(/^\[object\s(.*)\]$/)[1] || '';
+ var type = toString.call( obj ).match(/^\[object\s(.*)\]$/)[1] || '';
switch (type) {
case 'Number':
expected: expected
};
- message = escapeHtml(message) || (result ? "okay" : "failed");
+ message = escapeInnerText(message) || (result ? "okay" : "failed");
message = '<span class="test-message">' + message + "</span>";
- expected = escapeHtml(QUnit.jsDump.parse(expected));
- actual = escapeHtml(QUnit.jsDump.parse(actual));
+ expected = escapeInnerText(QUnit.jsDump.parse(expected));
+ actual = escapeInnerText(QUnit.jsDump.parse(actual));
var output = message + '<table><tr class="test-expected"><th>Expected: </th><td><pre>' + expected + '</pre></td></tr>';
if (actual != expected) {
output += '<tr class="test-actual"><th>Result: </th><td><pre>' + actual + '</pre></td></tr>';
var source = sourceFromStacktrace();
if (source) {
details.source = source;
- output += '<tr class="test-source"><th>Source: </th><td><pre>' + escapeHtml(source) + '</pre></td></tr>';
+ output += '<tr class="test-source"><th>Source: </th><td><pre>' + escapeInnerText(source) + '</pre></td></tr>';
}
}
output += "</table>";
- QUnit.log(details);
+ runLoggingCallbacks( 'log', QUnit, details );
config.current.assertions.push({
result: !!result,
var querystring = "?",
key;
for ( key in params ) {
+ if ( !hasOwn.call( params, key ) ) {
+ continue;
+ }
querystring += encodeURIComponent( key ) + "=" +
encodeURIComponent( params[ key ] ) + "&";
}
extend: extend,
id: id,
- addEvent: addEvent,
+ 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
// run test/logs.html for any related changes
- begin: function() {},
+ begin: registerLoggingCallback('begin'),
// done: { failed, passed, total, runtime }
- done: function() {},
+ done: registerLoggingCallback('done'),
// log: { result, actual, expected, message }
- log: function() {},
+ log: registerLoggingCallback('log'),
// testStart: { name }
- testStart: function() {},
+ testStart: registerLoggingCallback('testStart'),
// testDone: { name, failed, passed, total }
- testDone: function() {},
+ testDone: registerLoggingCallback('testDone'),
// moduleStart: { name }
- moduleStart: function() {},
+ moduleStart: registerLoggingCallback('moduleStart'),
// moduleDone: { name, failed, passed, total }
- moduleDone: function() {}
+ moduleDone: registerLoggingCallback('moduleDone')
});
if ( typeof document === "undefined" || document.readyState === "complete" ) {
}
QUnit.load = function() {
- QUnit.begin({});
+ runLoggingCallbacks( 'begin', QUnit, {} );
// Initialize the config, saving the execution queue
var oldconfig = extend({}, config);
addEvent(window, "load", QUnit.load);
+// addEvent(window, "error") gives us a useless event object
+window.onerror = function( message, file, line ) {
+ if ( QUnit.config.current ) {
+ ok( false, message + ", " + file + ":" + line );
+ } else {
+ test( "global failure", function() {
+ ok( false, message + ", " + file + ":" + line );
+ });
+ }
+};
+
function done() {
config.autorun = true;
// Log the last module results
if ( config.currentModule ) {
- QUnit.moduleDone( {
+ runLoggingCallbacks( 'moduleDone', QUnit, {
name: config.currentModule,
failed: config.moduleStats.bad,
passed: config.moduleStats.all - config.moduleStats.bad,
].join(" ");
}
- QUnit.done( {
+ runLoggingCallbacks( 'done', QUnit, {
failed: config.stats.bad,
passed: passed,
total: config.stats.all,
}
}
-function escapeHtml(s) {
+function escapeInnerText(s) {
if (!s) {
return "";
}
s = s + "";
- return s.replace(/[\&"<>\\]/g, function(s) {
+ return s.replace(/[\&<>]/g, function(s) {
switch(s) {
case "&": return "&";
- case "\\": return "\\\\";
- case '"': return '\"';
case "<": return "<";
case ">": return ">";
default: return s;
});
}
-function synchronize( callback ) {
+function synchronize( callback, last ) {
config.queue.push( callback );
if ( config.autorun && !config.blocking ) {
- process();
+ process(last);
}
}
-function process() {
- var start = (new Date()).getTime();
+function process( last ) {
+ var start = new Date().getTime();
+ config.depth = config.depth ? config.depth + 1 : 1;
while ( config.queue.length && !config.blocking ) {
- if ( config.updateRate <= 0 || (((new Date()).getTime() - start) < config.updateRate) ) {
+ if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) {
config.queue.shift()();
} else {
- window.setTimeout( process, 13 );
+ window.setTimeout( function(){
+ process( last );
+ }, 13 );
break;
}
}
- if (!config.blocking && !config.queue.length) {
+ config.depth--;
+ if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) {
done();
}
}
if ( config.noglobals ) {
for ( var key in window ) {
+ if ( !hasOwn.call( window, key ) ) {
+ continue;
+ }
config.pollution.push( key );
}
}
for ( var prop in b ) {
if ( b[prop] === undefined ) {
delete a[prop];
- } else {
+
+ // Avoid "Member not found" error in IE8 caused by setting window.constructor
+ } else if ( prop !== "constructor" || a !== window ) {
a[prop] = b[prop];
}
}
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.
-// Discussions and reference: http://philrathe.com/articles/equiv
-// Test suites: http://philrathe.com/tests/equiv
// Author: Philippe Rathé <prathe@gmail.com>
QUnit.equiv = function () {
type = "document";
} else if (obj.nodeType) {
type = "node";
- } else if (typeof obj === "object" && typeof obj.length === "number" && obj.length >= 0) {
+ } else if (
+ // native arrays
+ toString.call( obj ) === "[object Array]" ||
+ // NodeList objects
+ ( 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 {
type = typeof obj;
}
for (var 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]],