Socket
Socket
Sign inDemoInstall

jquery-mockjax

Package Overview
Dependencies
Maintainers
3
Versions
16
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

jquery-mockjax - npm Package Compare versions

Comparing version 1.5.3 to 1.6.1

CONTRIBUTING.md

2

bower.json
{
"name": "jquery-mockjax",
"version": "1.5.3",
"version": "1.6.1",
"main": ["jquery.mockjax.js"],

@@ -5,0 +5,0 @@ "ignore": [

@@ -0,1 +1,29 @@

## 2014-10-29
* Updated to version 1.6.1
* Changed all references to appendTo to point to github.com/jakerella (new owner)
* removed unused testswarm files
## 2014-10-09
* Updated to version 1.6.0
* Added `unfiredHandlers()` and `unmockedAjaxCalls()`
* Numerous bug fixes and breaking tests
* Internal method cleanup
* Switched to throwing proper `Error` objects
* Switched to tab indentation everywhere
* Added `main` field to package.json
* Fixed responseTime for jsonp and allowed for variable setting with array min/max
* Added `onAfterXxxxx` callbacks
* Updated `$.mockjaxClear()` to be `$.mockjax.clear()` with deprecation notice
* Complete README documentation overhaul
* Fixed issue with Async actions in response callback
* Added "contributing" documentation
## 2014-08-14
* Spelling corrections in README.md
* Update to newest version of QUnit (JS & CSS) and fixes for doing so
* Added further versions of jQuery to test with
* Added some tests for various issues and split out some tests for atomicity
* Fixed dataType check for JSONP (case insensitive)
* ensure request `data` matching occurs when url is matched and no data matching is required
## 2013-09-28

@@ -2,0 +30,0 @@ * Fixed issue with proxy data and status codes (Thanks [Andrew Goodale](https://github.com/newyankeecodeshop)!)

/*!
* MockJax - jQuery Plugin to Mock Ajax requests
*
* Version: 1.5.3
* Version: 1.6.1
* Released:
* Home: http://github.com/appendto/jquery-mockjax
* Home: https://github.com/jakerella/jquery-mockjax
* Author: Jonathan Sharp (http://jdsharp.com)
* License: MIT,GPL
*
* Copyright (c) 2011 appendTo LLC.
* Copyright (c) 2014 appendTo, Jordan Kasper
* NOTE: This repository was taken over by Jordan Kasper (@jakerella) October, 2014
*
* Dual licensed under the MIT or GPL licenses.
* http://appendto.com/open-source-licenses
* http://opensource.org/licenses/MIT OR http://www.gnu.org/licenses/gpl-2.0.html
*/

@@ -18,2 +20,3 @@ (function($) {

mockedAjaxCalls = [],
unmockedAjaxCalls = [],
CALLBACK_REGEX = /=\?(&|$)/,

@@ -40,6 +43,6 @@ jsc = (new Date()).getTime();

if ( err.length == 1 ) {
throw('Error: ' + $(xmlDoc).text() );
throw new Error('Error: ' + $(xmlDoc).text() );
}
} else {
throw('Unable to parse XML');
throw new Error('Unable to parse XML');
}

@@ -54,7 +57,2 @@ return xmlDoc;

// Trigger a jQuery event
function trigger(s, type, args) {
(s.context ? $(s.context) : $.event).trigger(type, args);
}
// Check if the data field on the mock handler and the request match. This

@@ -75,4 +73,6 @@ // can be used to restrict a mock handler to being used only when a certain

} else {
// This will allow to compare Arrays
if ( typeof live[k] === 'object' && live[k] !== null ) {
if ( identical && $.isArray( live[k] ) ) {
identical = $.isArray( mock[k] ) && live[k].length === mock[k].length;
}
identical = identical && isMockDataEqual(mock[k], live[k]);

@@ -122,4 +122,4 @@ } else {

// Inspect the data submitted in the request (either POST body or GET query string)
if ( handler.data && requestSettings.data ) {
if ( !isMockDataEqual(handler.data, requestSettings.data) ) {
if ( handler.data ) {
if ( ! requestSettings.data || !isMockDataEqual(handler.data, requestSettings.data) ) {
// They're not identical, do not mock this request

@@ -139,2 +139,12 @@ return null;

function parseResponseTimeOpt(responseTime) {
if ($.isArray(responseTime)) {
var min = responseTime[0];
var max = responseTime[1];
return (typeof min === 'number' && typeof max === 'number') ? Math.floor(Math.random() * (max - min)) + min : null;
} else {
return (typeof responseTime === 'number') ? responseTime: null;
}
}
// Process the xhr objects send operation

@@ -147,48 +157,66 @@ function _xhrSend(mockHandler, requestSettings, origSettings) {

return (function() {
var onReady;
// The request has returned
this.status = mockHandler.status;
this.statusText = mockHandler.statusText;
this.readyState = 4;
this.readyState = 1;
var finishRequest = function () {
this.readyState = 4;
var onReady;
// Copy over our mock to our xhr object before passing control back to
// jQuery's onreadystatechange callback
if ( requestSettings.dataType == 'json' && ( typeof mockHandler.responseText == 'object' ) ) {
this.responseText = JSON.stringify(mockHandler.responseText);
} else if ( requestSettings.dataType == 'xml' ) {
if ( typeof mockHandler.responseXML == 'string' ) {
this.responseXML = parseXML(mockHandler.responseXML);
//in jQuery 1.9.1+, responseXML is processed differently and relies on responseText
this.responseText = mockHandler.responseXML;
} else {
this.responseXML = mockHandler.responseXML;
}
} else if (typeof mockHandler.responseText === 'object' && mockHandler.responseText !== null) {
// since jQuery 1.9 responseText type has to match contentType
mockHandler.contentType = 'application/json';
this.responseText = JSON.stringify(mockHandler.responseText);
} else {
this.responseText = mockHandler.responseText;
}
if( typeof mockHandler.status == 'number' || typeof mockHandler.status == 'string' ) {
this.status = mockHandler.status;
}
if( typeof mockHandler.statusText === "string") {
this.statusText = mockHandler.statusText;
}
// jQuery 2.0 renamed onreadystatechange to onload
onReady = this.onreadystatechange || this.onload;
// jQuery < 1.4 doesn't have onreadystate change for xhr
if ( $.isFunction( onReady ) ) {
if( mockHandler.isTimeout) {
this.status = -1;
}
onReady.call( this, mockHandler.isTimeout ? 'timeout' : undefined );
} else if ( mockHandler.isTimeout ) {
// Fix for 1.3.2 timeout to keep success from firing.
this.status = -1;
}
};
// We have an executable function, call it to give
// the mock handler a chance to update it's data
if ( $.isFunction(mockHandler.response) ) {
mockHandler.response(origSettings);
}
// Copy over our mock to our xhr object before passing control back to
// jQuery's onreadystatechange callback
if ( requestSettings.dataType == 'json' && ( typeof mockHandler.responseText == 'object' ) ) {
this.responseText = JSON.stringify(mockHandler.responseText);
} else if ( requestSettings.dataType == 'xml' ) {
if ( typeof mockHandler.responseXML == 'string' ) {
this.responseXML = parseXML(mockHandler.responseXML);
//in jQuery 1.9.1+, responseXML is processed differently and relies on responseText
this.responseText = mockHandler.responseXML;
// Wait for it to finish
if ( mockHandler.response.length === 2 ) {
mockHandler.response(origSettings, function () {
finishRequest.call(that);
});
return;
} else {
this.responseXML = mockHandler.responseXML;
mockHandler.response(origSettings);
}
} else {
this.responseText = mockHandler.responseText;
}
if( typeof mockHandler.status == 'number' || typeof mockHandler.status == 'string' ) {
this.status = mockHandler.status;
}
if( typeof mockHandler.statusText === "string") {
this.statusText = mockHandler.statusText;
}
// jQuery 2.0 renamed onreadystatechange to onload
onReady = this.onreadystatechange || this.onload;
// jQuery < 1.4 doesn't have onreadystate change for xhr
if ( $.isFunction( onReady ) ) {
if( mockHandler.isTimeout) {
this.status = -1;
}
onReady.call( this, mockHandler.isTimeout ? 'timeout' : undefined );
} else if ( mockHandler.isTimeout ) {
// Fix for 1.3.2 timeout to keep success from firing.
this.status = -1;
}
finishRequest.call(that);
}).apply(that);

@@ -216,4 +244,3 @@ };

}
this.responseTimer = setTimeout(process, mockHandler.responseTime || 0);
this.responseTimer = setTimeout(process, parseResponseTimeOpt(mockHandler.responseTime) || 0);
}

@@ -227,3 +254,3 @@ });

} else {
this.responseTimer = setTimeout(process, mockHandler.responseTime || 50);
this.responseTimer = setTimeout(process, parseResponseTimeOpt(mockHandler.responseTime) || 50);
}

@@ -241,2 +268,5 @@ }

}
if (typeof requestSettings.headers === 'undefined') {
requestSettings.headers = {};
}
if ( mockHandler.contentType ) {

@@ -259,3 +289,3 @@ mockHandler.headers['content-type'] = mockHandler.contentType;

setRequestHeader: function(header, value) {
mockHandler.headers[header] = value;
requestSettings.headers[header] = value;
},

@@ -277,2 +307,6 @@ getResponseHeader: function(header) {

var headers = '';
// since jQuery 1.9 responseText type has to match contentType
if (mockHandler.contentType) {
mockHandler.headers['Content-Type'] = mockHandler.contentType;
}
$.each(mockHandler.headers, function(k, v) {

@@ -353,4 +387,6 @@ headers += k + ': ' + v + "\n";

// Successful response
jsonpSuccess( requestSettings, callbackContext, mockHandler );
jsonpComplete( requestSettings, callbackContext, mockHandler );
setTimeout(function() {
jsonpSuccess( requestSettings, callbackContext, mockHandler );
jsonpComplete( requestSettings, callbackContext, mockHandler );
}, parseResponseTimeOpt(mockHandler.responseTime) || 0);

@@ -411,3 +447,3 @@ // If we are running under jQuery 1.5+, return a deferred object

if ( requestSettings.global ) {
trigger(requestSettings, "ajaxSuccess", [{}, requestSettings] );
(requestSettings.context ? $(requestSettings.context) : $.event).trigger("ajaxSuccess", [{}, requestSettings]);
}

@@ -425,3 +461,3 @@ }

if ( requestSettings.global ) {
trigger( "ajaxComplete", [{}, requestSettings] );
(requestSettings.context ? $(requestSettings.context) : $.event).trigger("ajaxComplete", [{}, requestSettings]);
}

@@ -438,3 +474,3 @@

function handleAjax( url, origSettings ) {
var mockRequest, requestSettings, mockHandler;
var mockRequest, requestSettings, mockHandler, overrideCallback;

@@ -447,2 +483,3 @@ // If url is an object, simulate pre-1.5 signature

// work around to support 1.5 signature
origSettings = origSettings || {};
origSettings.url = url;

@@ -454,2 +491,14 @@ }

// Generic function to override callback methods for use with
// callback options (onAfterSuccess, onAfterError, onAfterComplete)
overrideCallback = function(action, mockHandler) {
var origHandler = origSettings[action.toLowerCase()];
return function() {
if ( $.isFunction(origHandler) ) {
origHandler.apply(this, [].slice.call(arguments));
}
mockHandler['onAfter' + action]();
};
};
// Iterate over our mock handlers (in registration order) until we find

@@ -474,3 +523,3 @@ // one that is willing to intercept the request

if ( requestSettings.dataType === "jsonp" ) {
if ( requestSettings.dataType && requestSettings.dataType.toUpperCase() === 'JSONP' ) {
if ((mockRequest = processJsonpMock( requestSettings, mockHandler, origSettings ))) {

@@ -490,5 +539,30 @@ // This mock will handle the JSONP request

// In the case of a timeout, we just need to ensure
// an actual jQuery timeout (That is, our reponse won't)
// return faster than the timeout setting.
if ( mockHandler.isTimeout ) {
if ( mockHandler.responseTime > 1 ) {
origSettings.timeout = mockHandler.responseTime - 1;
} else {
mockHandler.responseTime = 2;
origSettings.timeout = 1;
}
mockHandler.isTimeout = false;
}
// Set up onAfter[X] callback functions
if ( $.isFunction( mockHandler.onAfterSuccess ) ) {
origSettings.success = overrideCallback('Success', mockHandler);
}
if ( $.isFunction( mockHandler.onAfterError ) ) {
origSettings.error = overrideCallback('Error', mockHandler);
}
if ( $.isFunction( mockHandler.onAfterComplete ) ) {
origSettings.complete = overrideCallback('Complete', mockHandler);
}
copyUrlParameters(mockHandler, origSettings);
(function(mockHandler, requestSettings, origSettings, origHandler) {
mockRequest = _ajax.call($, $.extend(true, {}, origSettings, {

@@ -504,4 +578,5 @@ // Mock the XHR object

// We don't have a mock request
unmockedAjaxCalls.push(origSettings);
if($.mockjaxSettings.throwUnmocked === true) {
throw('AJAX not mocked: ' + origSettings.url);
throw new Error('AJAX not mocked: ' + origSettings.url);
}

@@ -603,3 +678,3 @@ else { // trigger a normal request

};
$.mockjaxClear = function(i) {
$.mockjax.clear = function(i) {
if ( arguments.length == 1 ) {

@@ -611,3 +686,9 @@ mockHandlers[i] = null;

mockedAjaxCalls = [];
unmockedAjaxCalls = [];
};
// support older, deprecated version
$.mockjaxClear = function(i) {
window.console && window.console.warn && window.console.warn( 'DEPRECATED: The $.mockjaxClear() method has been deprecated in 1.6.0. Please use $.mockjax.clear() as the older function will be removed soon!' );
$.mockjax.clear();
};
$.mockjax.handler = function(i) {

@@ -621,2 +702,15 @@ if ( arguments.length == 1 ) {

};
$.mockjax.unfiredHandlers = function() {
var results = [];
for (var i=0, len=mockHandlers.length; i<len; i++) {
var handler = mockHandlers[i];
if (handler !== null && !handler.fired) {
results.push(handler);
}
}
return results;
};
$.mockjax.unmockedAjaxCalls = function() {
return unmockedAjaxCalls;
};
})(jQuery);

@@ -1,268 +0,103 @@

/**
* QUnit 1.1.0 - A JavaScript Unit Testing Framework
/*!
* QUnit 1.14.0
* http://qunitjs.com/
*
* http://docs.jquery.com/QUnit
* Copyright 2013 jQuery Foundation and other contributors
* Released under the MIT license
* http://jquery.org/license
*
* Copyright (c) 2011 John Resig, Jörn Zaefferer
* Dual licensed under the MIT (MIT-LICENSE.txt)
* or GPL (GPL-LICENSE.txt) licenses.
* Date: 2014-01-31T16:40Z
*/
(function(window) {
(function( window ) {
var defined = {
setTimeout: typeof window.setTimeout !== "undefined",
sessionStorage: (function() {
try {
return !!sessionStorage.getItem;
} catch(e) {
return false;
}
})()
};
var testId = 0,
var QUnit,
assert,
config,
onErrorFnPrev,
testId = 0,
fileName = (sourceFromStacktrace( 0 ) || "" ).replace(/(:\d+)+\)?/, "").replace(/.+\//, ""),
toString = Object.prototype.toString,
hasOwn = Object.prototype.hasOwnProperty;
var Test = function(name, testName, expected, testEnvironmentArg, async, callback) {
this.name = name;
this.testName = testName;
this.expected = expected;
this.testEnvironmentArg = testEnvironmentArg;
this.async = async;
this.callback = callback;
this.assertions = [];
};
Test.prototype = {
init: function() {
var tests = id("qunit-tests");
if (tests) {
var b = document.createElement("strong");
b.innerHTML = "Running " + this.name;
var li = document.createElement("li");
li.appendChild( b );
li.className = "running";
li.id = this.id = "test-output" + testId++;
tests.appendChild( li );
}
},
setup: function() {
if (this.module != config.previousModule) {
if ( config.previousModule ) {
runLoggingCallbacks('moduleDone', QUnit, {
name: config.previousModule,
failed: config.moduleStats.bad,
passed: config.moduleStats.all - config.moduleStats.bad,
total: config.moduleStats.all
} );
hasOwn = Object.prototype.hasOwnProperty,
// Keep a local reference to Date (GH-283)
Date = window.Date,
setTimeout = window.setTimeout,
clearTimeout = window.clearTimeout,
defined = {
document: typeof window.document !== "undefined",
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;
}
config.previousModule = this.module;
config.moduleStats = { all: 0, bad: 0 };
runLoggingCallbacks( 'moduleStart', QUnit, {
name: this.module
} );
}
config.current = this;
this.testEnvironment = extend({
setup: function() {},
teardown: function() {}
}, this.moduleTestEnvironment);
if (this.testEnvironmentArg) {
extend(this.testEnvironment, this.testEnvironmentArg);
}
runLoggingCallbacks( 'testStart', QUnit, {
name: this.testName,
module: this.module
});
// allow utility functions to access the current test environment
// TODO why??
QUnit.current_testEnvironment = this.testEnvironment;
try {
if ( !config.pollution ) {
saveGlobal();
}
this.testEnvironment.setup.call(this.testEnvironment);
} catch(e) {
QUnit.ok( false, "Setup failed on " + this.testName + ": " + e.message );
}
}())
},
run: function() {
config.current = this;
if ( this.async ) {
QUnit.stop();
}
if ( config.notrycatch ) {
this.callback.call(this.testEnvironment);
return;
}
try {
this.callback.call(this.testEnvironment);
} catch(e) {
fail("Test " + this.testName + " died, exception and test follows", e, this.callback);
QUnit.ok( false, "Died on test #" + (this.assertions.length + 1) + ": " + e.message + " - " + QUnit.jsDump.parse(e) );
// else next test will carry the responsibility
saveGlobal();
// Restart the tests if they're blocking
if ( config.blocking ) {
start();
/**
* 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;
}
},
teardown: function() {
config.current = this;
try {
this.testEnvironment.teardown.call(this.testEnvironment);
checkPollution();
} catch(e) {
QUnit.ok( false, "Teardown failed on " + this.testName + ": " + e.message );
}
},
finish: function() {
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" );
}
var good = 0, bad = 0,
tests = id("qunit-tests");
config.stats.all += this.assertions.length;
config.moduleStats.all += this.assertions.length;
if ( tests ) {
var ol = document.createElement("ol");
for ( var i = 0; i < this.assertions.length; i++ ) {
var assertion = this.assertions[i];
var li = document.createElement("li");
li.className = assertion.result ? "pass" : "fail";
li.innerHTML = assertion.message || (assertion.result ? "okay" : "failed");
ol.appendChild( li );
if ( assertion.result ) {
good++;
} else {
bad++;
config.stats.bad++;
config.moduleStats.bad++;
}
/**
* 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;
}
// store result when possible
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) {
ol.style.display = "none";
}
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>";
var a = document.createElement("a");
a.innerHTML = "Rerun";
a.href = QUnit.url({ filter: getText([b]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") });
addEvent(b, "click", function() {
var next = b.nextSibling.nextSibling,
display = next.style.display;
next.style.display = display === "none" ? "block" : "none";
});
addEvent(b, "dblclick", function(e) {
var target = e && e.target ? e.target : window.event.srcElement;
if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) {
target = target.parentNode;
}
if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
window.location = QUnit.url({ filter: getText([target]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") });
}
});
var li = id(this.id);
li.className = bad ? "fail" : "pass";
li.removeChild( li.firstChild );
li.appendChild( b );
li.appendChild( a );
li.appendChild( ol );
} else {
for ( var i = 0; i < this.assertions.length; i++ ) {
if ( !this.assertions[i].result ) {
bad++;
config.stats.bad++;
config.moduleStats.bad++;
}
}
}
return vals;
};
try {
QUnit.reset();
} catch(e) {
fail("reset() failed, following Test " + this.testName + ", exception and reset fn follows", e, QUnit.reset);
}
runLoggingCallbacks( 'testDone', QUnit, {
name: this.testName,
module: this.module,
failed: bad,
passed: this.assertions.length - bad,
total: this.assertions.length
} );
},
// Root QUnit object.
// `QUnit` initialized at top of scope
QUnit = {
queue: function() {
var test = this;
synchronize(function() {
test.init();
});
function run() {
// each of these can by async
synchronize(function() {
test.setup();
});
synchronize(function() {
test.run();
});
synchronize(function() {
test.teardown();
});
synchronize(function() {
test.finish();
});
}
// defer when previous test run passed, if storage is available
var bad = QUnit.config.reorder && defined.sessionStorage && +sessionStorage.getItem("qunit-" + this.module + "-" + this.testName);
if (bad) {
run();
} else {
synchronize(run, true);
};
}
};
var QUnit = {
// call on start of module test to prepend name to all tests
module: function(name, testEnvironment) {
module: function( name, testEnvironment ) {
config.currentModule = name;
config.currentModuleTestEnviroment = testEnvironment;
config.currentModuleTestEnvironment = testEnvironment;
config.modules[name] = true;
},
asyncTest: function(testName, expected, callback) {
asyncTest: function( testName, expected, callback ) {
if ( arguments.length === 2 ) {

@@ -273,7 +108,8 @@ callback = expected;

QUnit.test(testName, expected, callback, true);
QUnit.test( testName, expected, callback, true );
},
test: function(testName, expected, callback, async) {
var name = '<span class="test-name">' + testName + '</span>', testEnvironmentArg;
test: function( testName, expected, callback, async ) {
var test,
nameHtml = "<span class='test-name'>" + escapeText( testName ) + "</span>";

@@ -284,146 +120,78 @@ if ( arguments.length === 2 ) {

}
// is 2nd argument a testEnvironment?
if ( expected && typeof expected === 'object') {
testEnvironmentArg = expected;
expected = null;
}
if ( config.currentModule ) {
name = '<span class="module-name">' + config.currentModule + "</span>: " + name;
nameHtml = "<span class='module-name'>" + escapeText( config.currentModule ) + "</span>: " + nameHtml;
}
if ( !validTest(config.currentModule + ": " + testName) ) {
test = new Test({
nameHtml: nameHtml,
testName: testName,
expected: expected,
async: async,
callback: callback,
module: config.currentModule,
moduleTestEnvironment: config.currentModuleTestEnvironment,
stack: sourceFromStacktrace( 2 )
});
if ( !validTest( test ) ) {
return;
}
var test = new Test(name, testName, expected, testEnvironmentArg, async, callback);
test.module = config.currentModule;
test.moduleTestEnvironment = config.currentModuleTestEnviroment;
test.queue();
},
/**
* Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.
*/
expect: function(asserts) {
config.current.expected = asserts;
// Specify the number of expected assertions to guarantee that failed test (no assertions are run at all) don't slip through.
expect: function( asserts ) {
if (arguments.length === 1) {
config.current.expected = asserts;
} else {
return config.current.expected;
}
},
/**
* Asserts true.
* @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
*/
ok: function(a, msg) {
a = !!a;
var details = {
result: a,
message: msg
};
msg = escapeInnerText(msg);
runLoggingCallbacks( 'log', QUnit, details );
config.current.assertions.push({
result: a,
message: msg
});
},
/**
* Checks that the first two arguments are equal, with an optional message.
* Prints out both actual and expected values.
*
* Prefered to ok( actual == expected, message )
*
* @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." );
*
* @param Object actual
* @param Object expected
* @param String message (optional)
*/
equal: function(actual, expected, message) {
QUnit.push(expected == actual, actual, expected, message);
},
notEqual: function(actual, expected, message) {
QUnit.push(expected != actual, actual, expected, message);
},
deepEqual: function(actual, expected, message) {
QUnit.push(QUnit.equiv(actual, expected), actual, expected, message);
},
notDeepEqual: function(actual, expected, message) {
QUnit.push(!QUnit.equiv(actual, expected), actual, expected, message);
},
strictEqual: function(actual, expected, message) {
QUnit.push(expected === actual, actual, expected, message);
},
notStrictEqual: function(actual, expected, message) {
QUnit.push(expected !== actual, actual, expected, message);
},
raises: function(block, expected, message) {
var actual, ok = false;
if (typeof expected === 'string') {
message = expected;
expected = null;
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;
}
try {
block();
} catch (e) {
actual = e;
}
if (actual) {
// we don't want to validate thrown error
if (!expected) {
ok = true;
// expected is a regexp
} else if (QUnit.objectType(expected) === "regexp") {
ok = expected.test(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) {
ok = true;
}
}
QUnit.ok(ok, message);
},
start: function(count) {
config.semaphore -= count || 1;
if (config.semaphore > 0) {
// don't start until equal number of stop-calls
// don't start until equal number of stop-calls
if ( config.semaphore > 0 ) {
return;
}
if (config.semaphore < 0) {
// ignore if start is called more often then stop
// ignore if start is called more often then stop
if ( 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() {
if (config.semaphore > 0) {
setTimeout(function() {
if ( config.semaphore > 0 ) {
return;
}
if ( config.timeout ) {
clearTimeout(config.timeout);
clearTimeout( config.timeout );
}
config.blocking = false;
process(true);
process( true );
}, 13);
} else {
config.blocking = false;
process(true);
process( true );
}
},
stop: function(count) {
stop: function( count ) {
config.semaphore += count || 1;

@@ -433,8 +201,8 @@ config.blocking = true;

if ( config.testTimeout && defined.setTimeout ) {
clearTimeout(config.timeout);
config.timeout = window.setTimeout(function() {
clearTimeout( config.timeout );
config.timeout = setTimeout(function() {
QUnit.ok( false, "Test timed out" );
config.semaphore = 1;
QUnit.start();
}, config.testTimeout);
}, config.testTimeout );
}

@@ -444,17 +212,18 @@ }

//We want access to the constructor's prototype
// We use the prototype to distinguish between properties that should
// be exposed as globals (and in exports) and those that shouldn't
(function() {
function F(){};
function F() {}
F.prototype = QUnit;
QUnit = new F();
//Make F QUnit's constructor so that we can add to the prototype later
// 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;
// Maintain internal state
var config = {
/**
* Config object: Maintain internal state
* Later exposed as QUnit.config
* `config` initialized at top of scope
*/
config = {
// The queue of tests to run

@@ -477,5 +246,27 @@ queue: [],

urlConfig: ['noglobals', 'notrycatch'],
// by default, scroll to top of the page when suite is done
scrolltop: true,
//logging callback queues
// when enabled, all tests must call expect()
requireExpects: false,
// add checkboxes that are persisted in the query-string
// when enabled, the id is set to `true` as a `QUnit.config` property
urlConfig: [
{
id: "noglobals",
label: "Check for Globals",
tooltip: "Enabling this will test if any test introduces new properties on the `window` object. Stored as query-strings."
},
{
id: "notrycatch",
label: "No try-catch",
tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging exceptions in IE reasonable. Stored as query-strings."
}
],
// Set of all modules.
modules: {},
// logging callback queues
begin: [],

@@ -490,17 +281,22 @@ done: [],

// Load paramaters
// Initialize more QUnit.config and QUnit.urlParams
(function() {
var location = window.location || { search: "", protocol: "file:" },
var i, current,
location = window.location || { search: "", protocol: "file:" },
params = location.search.slice( 1 ).split( "&" ),
length = params.length,
urlParams = {},
current;
urlParams = {};
if ( params[ 0 ] ) {
for ( var i = 0; i < length; i++ ) {
for ( i = 0; i < length; i++ ) {
current = params[ i ].split( "=" );
current[ 0 ] = decodeURIComponent( current[ 0 ] );
// allow just a key to turn on a flag, e.g., test.html?noglobals
current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
urlParams[ current[ 0 ] ] = current[ 1 ];
if ( urlParams[ current[ 0 ] ] ) {
urlParams[ current[ 0 ] ] = [].concat( urlParams[ current[ 0 ] ], current[ 1 ] );
} else {
urlParams[ current[ 0 ] ] = current[ 1 ];
}
}

@@ -510,20 +306,26 @@ }

QUnit.urlParams = urlParams;
// String search anywhere in moduleName+testName
config.filter = urlParams.filter;
// Exact match of the module name
config.module = urlParams.module;
config.testNumber = [];
if ( urlParams.testNumber ) {
// Ensure that urlParams.testNumber is an array
urlParams.testNumber = [].concat( urlParams.testNumber );
for ( i = 0; i < urlParams.testNumber.length; i++ ) {
current = urlParams.testNumber[ i ];
config.testNumber.push( parseInt( current, 10 ) );
}
}
// Figure out if we're running the tests from a server or not
QUnit.isLocal = !!(location.protocol === 'file:');
})();
QUnit.isLocal = location.protocol === "file:";
}());
// Expose the API as global variables, unless an 'exports'
// object exists, in that case we assume we're in CommonJS
if ( typeof exports === "undefined" || typeof require === "undefined" ) {
extend(window, QUnit);
window.QUnit = QUnit;
} else {
extend(exports, QUnit);
exports.QUnit = QUnit;
}
extend( QUnit, {
// define these after exposing globals to keep them in these QUnit namespace only
extend(QUnit, {
config: config,

@@ -533,6 +335,6 @@

init: function() {
extend(config, {
extend( config, {
stats: { all: 0, bad: 0 },
moduleStats: { all: 0, bad: 0 },
started: +new Date,
started: +new Date(),
updateRate: 1000,

@@ -544,9 +346,21 @@ blocking: false,

queue: [],
semaphore: 0
semaphore: 1
});
var tests = id( "qunit-tests" ),
banner = id( "qunit-banner" ),
result = id( "qunit-testresult" );
var tests, banner, result,
qunit = id( "qunit" );
if ( qunit ) {
qunit.innerHTML =
"<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
"<h2 id='qunit-banner'></h2>" +
"<div id='qunit-testrunner-toolbar'></div>" +
"<h2 id='qunit-userAgent'></h2>" +
"<ol id='qunit-tests'></ol>";
}
tests = id( "qunit-tests" );
banner = id( "qunit-banner" );
result = id( "qunit-testresult" );
if ( tests ) {

@@ -569,76 +383,53 @@ tests.innerHTML = "";

tests.parentNode.insertBefore( result, tests );
result.innerHTML = 'Running...<br/>&nbsp;';
result.innerHTML = "Running...<br/>&nbsp;";
}
},
/**
* Resets the test setup. Useful for tests that modify the DOM.
*
* If jQuery is available, uses jQuery's html(), otherwise just innerHTML.
*/
// Resets the test setup. Useful for tests that modify the DOM.
/*
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() {
if ( window.jQuery ) {
jQuery( "#qunit-fixture" ).html( config.fixture );
} else {
var main = id( 'qunit-fixture' );
if ( main ) {
main.innerHTML = config.fixture;
}
var fixture = id( "qunit-fixture" );
if ( fixture ) {
fixture.innerHTML = config.fixture;
}
},
/**
* Trigger an event on an element.
*
* @example triggerEvent( document.body, "click" );
*
* @param DOMElement elem
* @param String type
*/
triggerEvent: function( elem, type, event ) {
if ( document.createEvent ) {
event = document.createEvent("MouseEvents");
event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,
0, 0, 0, 0, 0, false, false, false, false, 0, null);
elem.dispatchEvent( event );
} else if ( elem.fireEvent ) {
elem.fireEvent("on"+type);
}
},
// Safe object type checking
is: function( type, obj ) {
return QUnit.objectType( obj ) == type;
return QUnit.objectType( obj ) === type;
},
objectType: function( obj ) {
if (typeof obj === "undefined") {
return "undefined";
if ( typeof obj === "undefined" ) {
return "undefined";
}
// consider: typeof null === object
// Consider: typeof null === object
if ( obj === null ) {
return "null";
}
if (obj === null) {
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':
if (isNaN(obj)) {
return "nan";
} else {
return "number";
}
case 'String':
case 'Boolean':
case 'Array':
case 'Date':
case 'RegExp':
case 'Function':
return type.toLowerCase();
switch ( type ) {
case "Number":
if ( isNaN(obj) ) {
return "nan";
}
return "number";
case "String":
case "Boolean":
case "Array":
case "Date":
case "RegExp":
case "Function":
return type.toLowerCase();
}
if (typeof obj === "object") {
return "object";
if ( typeof obj === "object" ) {
return "object";
}

@@ -648,29 +439,42 @@ return undefined;

push: function(result, actual, expected, message) {
var details = {
result: result,
message: message,
actual: actual,
expected: expected
};
push: function( result, actual, expected, message ) {
if ( !config.current ) {
throw new Error( "assertion outside test context, was " + sourceFromStacktrace() );
}
message = escapeInnerText(message) || (result ? "okay" : "failed");
message = '<span class="test-message">' + message + "</span>";
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>';
output += '<tr class="test-diff"><th>Diff: </th><td><pre>' + QUnit.diff(expected, actual) +'</pre></td></tr>';
}
if (!result) {
var source = sourceFromStacktrace();
if (source) {
var output, source,
details = {
module: config.current.module,
name: config.current.testName,
result: result,
message: message,
actual: actual,
expected: expected
};
message = escapeText( message ) || ( result ? "okay" : "failed" );
message = "<span class='test-message'>" + message + "</span>";
output = message;
if ( !result ) {
expected = escapeText( QUnit.jsDump.parse(expected) );
actual = escapeText( QUnit.jsDump.parse(actual) );
output += "<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>";
output += "<tr class='test-diff'><th>Diff: </th><td><pre>" + QUnit.diff( expected, actual ) + "</pre></td></tr>";
}
source = sourceFromStacktrace();
if ( source ) {
details.source = source;
output += '<tr class="test-source"><th>Source: </th><td><pre>' + escapeInnerText(source) + '</pre></td></tr>';
output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText( source ) + "</pre></td></tr>";
}
output += "</table>";
}
output += "</table>";
runLoggingCallbacks( 'log', QUnit, details );
runLoggingCallbacks( "log", QUnit, details );

@@ -683,14 +487,53 @@ config.current.assertions.push({

pushFailure: function( message, source, actual ) {
if ( !config.current ) {
throw new Error( "pushFailure() assertion outside test context, was " + sourceFromStacktrace(2) );
}
var output,
details = {
module: config.current.module,
name: config.current.testName,
result: false,
message: message
};
message = escapeText( message ) || "error";
message = "<span class='test-message'>" + message + "</span>";
output = message;
output += "<table>";
if ( actual ) {
output += "<tr class='test-actual'><th>Result: </th><td><pre>" + escapeText( actual ) + "</pre></td></tr>";
}
if ( source ) {
details.source = source;
output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText( source ) + "</pre></td></tr>";
}
output += "</table>";
runLoggingCallbacks( "log", QUnit, details );
config.current.assertions.push({
result: false,
message: output
});
},
url: function( params ) {
params = extend( extend( {}, QUnit.urlParams ), params );
var querystring = "?",
key;
var key,
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 );
},

@@ -700,27 +543,42 @@

id: id,
addEvent: addEvent
addEvent: addEvent,
addClass: addClass,
hasClass: hasClass,
removeClass: removeClass
// load, equiv, jsDump, diff: Attached later
});
//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, {
/**
* @deprecated: Created for backwards compatibility with test runner that set the hook function
* into QUnit.{hook}, instead of invoking it and passing the hook function.
* QUnit.constructor is set to the empty F() above so that we can add to it's prototype here.
* Doing this allows us to tell if the following methods have been overwritten on the actual
* QUnit object.
*/
extend( QUnit.constructor.prototype, {
// Logging callbacks; all receive a single argument with the listed properties
// run test/logs.html for any related changes
begin: registerLoggingCallback('begin'),
begin: registerLoggingCallback( "begin" ),
// done: { failed, passed, total, runtime }
done: registerLoggingCallback('done'),
done: registerLoggingCallback( "done" ),
// log: { result, actual, expected, message }
log: registerLoggingCallback('log'),
log: registerLoggingCallback( "log" ),
// testStart: { name }
testStart: registerLoggingCallback('testStart'),
// testDone: { name, failed, passed, total }
testDone: registerLoggingCallback('testDone'),
testStart: registerLoggingCallback( "testStart" ),
// testDone: { name, failed, passed, total, runtime }
testDone: registerLoggingCallback( "testDone" ),
// moduleStart: { name }
moduleStart: registerLoggingCallback('moduleStart'),
moduleStart: registerLoggingCallback( "moduleStart" ),
// moduleDone: { name, failed, passed, total }
moduleDone: registerLoggingCallback('moduleDone')
moduleDone: registerLoggingCallback( "moduleDone" )
});
if ( typeof document === "undefined" || document.readyState === "complete" ) {
if ( !defined.document || document.readyState === "complete" ) {
config.autorun = true;

@@ -730,6 +588,13 @@ }

QUnit.load = function() {
runLoggingCallbacks( 'begin', QUnit, {} );
runLoggingCallbacks( "begin", QUnit, {} );
// Initialize the config, saving the execution queue
var oldconfig = extend({}, config);
var banner, filter, i, j, label, len, main, ol, toolbar, val, selection,
urlConfigContainer, moduleFilter, userAgent,
numModules = 0,
moduleNames = [],
moduleFilterHtml = "",
urlConfigHtml = "",
oldconfig = extend( {}, config );
QUnit.init();

@@ -740,46 +605,123 @@ extend(config, oldconfig);

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>';
len = config.urlConfig.length;
for ( i = 0; i < len; i++ ) {
val = config.urlConfig[i];
if ( typeof val === "string" ) {
val = {
id: val,
label: val
};
}
config[ val.id ] = QUnit.urlParams[ val.id ];
if ( !val.value || typeof val.value === "string" ) {
urlConfigHtml += "<input id='qunit-urlconfig-" + escapeText( val.id ) +
"' name='" + escapeText( val.id ) +
"' type='checkbox'" +
( val.value ? " value='" + escapeText( val.value ) + "'" : "" ) +
( config[ val.id ] ? " checked='checked'" : "" ) +
" title='" + escapeText( val.tooltip ) +
"'><label for='qunit-urlconfig-" + escapeText( val.id ) +
"' title='" + escapeText( val.tooltip ) + "'>" + val.label + "</label>";
} else {
urlConfigHtml += "<label for='qunit-urlconfig-" + escapeText( val.id ) +
"' title='" + escapeText( val.tooltip ) +
"'>" + val.label +
": </label><select id='qunit-urlconfig-" + escapeText( val.id ) +
"' name='" + escapeText( val.id ) +
"' title='" + escapeText( val.tooltip ) +
"'><option></option>";
selection = false;
if ( QUnit.is( "array", val.value ) ) {
for ( j = 0; j < val.value.length; j++ ) {
urlConfigHtml += "<option value='" + escapeText( val.value[j] ) + "'" +
( config[ val.id ] === val.value[j] ?
(selection = true) && " selected='selected'" :
"" ) +
">" + escapeText( val.value[j] ) + "</option>";
}
} else {
for ( j in val.value ) {
if ( hasOwn.call( val.value, j ) ) {
urlConfigHtml += "<option value='" + escapeText( j ) + "'" +
( config[ val.id ] === j ?
(selection = true) && " selected='selected'" :
"" ) +
">" + escapeText( val.value[j] ) + "</option>";
}
}
}
if ( config[ val.id ] && !selection ) {
urlConfigHtml += "<option value='" + escapeText( config[ val.id ] ) +
"' selected='selected' disabled='disabled'>" +
escapeText( config[ val.id ] ) +
"</option>";
}
urlConfigHtml += "</select>";
}
}
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 += "<label for='qunit-modulefilter'>Module: </label><select id='qunit-modulefilter' name='modulefilter'><option value='' " +
( config.module === undefined ? "selected='selected'" : "" ) +
">< All Modules ></option>";
var userAgent = id("qunit-userAgent");
for ( i = 0; i < numModules; i++) {
moduleFilterHtml += "<option value='" + escapeText( encodeURIComponent(moduleNames[i]) ) + "' " +
( config.module === moduleNames[i] ? "selected='selected'" : "" ) +
">" + escapeText(moduleNames[i]) + "</option>";
}
moduleFilterHtml += "</select>";
// `userAgent` initialized at top of scope
userAgent = id( "qunit-userAgent" );
if ( userAgent ) {
userAgent.innerHTML = navigator.userAgent;
}
var banner = id("qunit-header");
// `banner` initialized at top of scope
banner = id( "qunit-header" );
if ( banner ) {
banner.innerHTML = '<a href="' + QUnit.url({ filter: undefined }) + '"> ' + banner.innerHTML + '</a> ' + urlConfigHtml;
addEvent( banner, "change", function( event ) {
var params = {};
params[ event.target.name ] = event.target.checked ? true : undefined;
window.location = QUnit.url( params );
});
banner.innerHTML = "<a href='" + QUnit.url({ filter: undefined, module: undefined, testNumber: undefined }) + "'>" + banner.innerHTML + "</a> ";
}
var toolbar = id("qunit-testrunner-toolbar");
// `toolbar` initialized at top of scope
toolbar = id( "qunit-testrunner-toolbar" );
if ( toolbar ) {
var filter = document.createElement("input");
// `filter` initialized at top of scope
filter = document.createElement( "input" );
filter.type = "checkbox";
filter.id = "qunit-filter-pass";
addEvent( filter, "click", function() {
var ol = document.getElementById("qunit-tests");
var tmp,
ol = id( "qunit-tests" );
if ( filter.checked ) {
ol.className = ol.className + " hidepass";
} else {
var tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " ";
ol.className = tmp.replace(/ hidepass /, " ");
tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " ";
ol.className = tmp.replace( / hidepass /, " " );
}
if ( defined.sessionStorage ) {
if (filter.checked) {
sessionStorage.setItem("qunit-filter-passed-tests", "true");
sessionStorage.setItem( "qunit-filter-passed-tests", "true" );
} else {
sessionStorage.removeItem("qunit-filter-passed-tests");
sessionStorage.removeItem( "qunit-filter-passed-tests" );
}
}
});
if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem("qunit-filter-passed-tests") ) {
if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem( "qunit-filter-passed-tests" ) ) {
filter.checked = true;
var ol = document.getElementById("qunit-tests");
// `ol` initialized at top of scope
ol = id( "qunit-tests" );
ol.className = ol.className + " hidepass";

@@ -789,9 +731,52 @@ }

var label = document.createElement("label");
label.setAttribute("for", "qunit-filter-pass");
// `label` initialized at top of scope
label = document.createElement( "label" );
label.setAttribute( "for", "qunit-filter-pass" );
label.setAttribute( "title", "Only show tests and assertions that fail. Stored in sessionStorage." );
label.innerHTML = "Hide passed tests";
toolbar.appendChild( label );
urlConfigContainer = document.createElement("span");
urlConfigContainer.innerHTML = urlConfigHtml;
// For oldIE support:
// * Add handlers to the individual elements instead of the container
// * Use "click" instead of "change" for checkboxes
// * Fallback from event.target to event.srcElement
addEvents( urlConfigContainer.getElementsByTagName("input"), "click", function( event ) {
var params = {},
target = event.target || event.srcElement;
params[ target.name ] = target.checked ?
target.defaultValue || true :
undefined;
window.location = QUnit.url( params );
});
addEvents( urlConfigContainer.getElementsByTagName("select"), "change", function( event ) {
var params = {},
target = event.target || event.srcElement;
params[ target.name ] = target.options[ target.selectedIndex ].value || undefined;
window.location = QUnit.url( params );
});
toolbar.appendChild( urlConfigContainer );
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);
}
}
var main = id('qunit-fixture');
// `main` initialized at top of scope
main = id( "qunit-fixture" );
if ( main ) {

@@ -801,3 +786,3 @@ config.fixture = main.innerHTML;

if (config.autostart) {
if ( config.autostart ) {
QUnit.start();

@@ -807,13 +792,36 @@ }

addEvent(window, "load", QUnit.load);
if ( defined.document ) {
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 );
});
// `onErrorFnPrev` initialized at top of scope
// Preserve other handlers
onErrorFnPrev = window.onerror;
// Cover uncaught exceptions
// Returning true will suppress the default browser handler,
// returning false will let it run.
window.onerror = function ( error, filePath, linerNr ) {
var ret = false;
if ( onErrorFnPrev ) {
ret = onErrorFnPrev( error, filePath, linerNr );
}
// Treat return value as window.onerror itself does,
// Only do our handling if not suppressed.
if ( ret !== true ) {
if ( QUnit.config.current ) {
if ( QUnit.config.current.ignoreGlobalErrors ) {
return true;
}
QUnit.pushFailure( error, filePath + ":" + linerNr );
} else {
QUnit.test( "global failure", extend( function() {
QUnit.pushFailure( error, filePath + ":" + linerNr );
}, { validTest: validTest } ) );
}
return false;
}
return ret;
};

@@ -825,30 +833,32 @@

// Log the last module results
if ( config.currentModule ) {
runLoggingCallbacks( 'moduleDone', QUnit, {
name: config.currentModule,
if ( config.previousModule ) {
runLoggingCallbacks( "moduleDone", QUnit, {
name: config.previousModule,
failed: config.moduleStats.bad,
passed: config.moduleStats.all - config.moduleStats.bad,
total: config.moduleStats.all
} );
});
}
delete config.previousModule;
var banner = id("qunit-banner"),
tests = id("qunit-tests"),
runtime = +new Date - config.started,
var i, key,
banner = id( "qunit-banner" ),
tests = id( "qunit-tests" ),
runtime = +new Date() - config.started,
passed = config.stats.all - config.stats.bad,
html = [
'Tests completed in ',
"Tests completed in ",
runtime,
' milliseconds.<br/>',
'<span class="passed">',
" milliseconds.<br/>",
"<span class='passed'>",
passed,
'</span> tests of <span class="total">',
"</span> assertions of <span class='total'>",
config.stats.all,
'</span> passed, <span class="failed">',
"</span> passed, <span class='failed'>",
config.stats.bad,
'</span> failed.'
].join('');
"</span> failed."
].join( "" );
if ( banner ) {
banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass");
banner.className = ( config.stats.bad ? "qunit-fail" : "qunit-pass" );
}

@@ -860,12 +870,28 @@

if ( config.altertitle && typeof document !== "undefined" && document.title ) {
if ( config.altertitle && defined.document && document.title ) {
// show ✖ for good, ✔ for bad suite result in title
// use escape sequences in case file gets loaded with non-utf-8-charset
document.title = [
(config.stats.bad ? "\u2716" : "\u2714"),
document.title.replace(/^[\u2714\u2716] /i, "")
].join(" ");
( config.stats.bad ? "\u2716" : "\u2714" ),
document.title.replace( /^[\u2714\u2716] /i, "" )
].join( " " );
}
runLoggingCallbacks( 'done', QUnit, {
// clear own sessionStorage items if all tests passed
if ( config.reorder && defined.sessionStorage && config.stats.bad === 0 ) {
// `key` & `i` initialized at top of scope
for ( i = 0; i < sessionStorage.length; i++ ) {
key = sessionStorage.key( i++ );
if ( key.indexOf( "qunit-test-" ) === 0 ) {
sessionStorage.removeItem( key );
}
}
}
// scroll back to top to show results
if ( config.scrolltop && window.scrollTo ) {
window.scrollTo(0, 0);
}
runLoggingCallbacks( "done", QUnit, {
failed: config.stats.bad,

@@ -875,9 +901,28 @@ passed: passed,

runtime: runtime
} );
});
}
function validTest( name ) {
var filter = config.filter,
run = false;
/** @return Boolean: true if this test should be ran */
function validTest( test ) {
var include,
filter = config.filter && config.filter.toLowerCase(),
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.length > 0 ) {
if ( inArray( test.testNumber, config.testNumber ) < 0 ) {
return false;
}
}
if ( module && ( !test.module || test.module.toLowerCase() !== module ) ) {
return false;
}
if ( !filter ) {

@@ -887,49 +932,86 @@ return true;

var not = filter.charAt( 0 ) === "!";
if ( not ) {
include = filter.charAt( 0 ) !== "!";
if ( !include ) {
filter = filter.slice( 1 );
}
if ( name.indexOf( filter ) !== -1 ) {
return !not;
// If the filter matches, we need to honour include
if ( fullName.indexOf( filter ) !== -1 ) {
return include;
}
if ( not ) {
run = true;
// Otherwise, do the opposite
return !include;
}
// so far supports only Firefox, Chrome and Opera (buggy), Safari (for real exceptions)
// Later Safari and IE10 are supposed to support error.stack as well
// See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
function extractStacktrace( e, offset ) {
offset = offset === undefined ? 3 : offset;
var stack, include, i;
if ( e.stacktrace ) {
// Opera
return e.stacktrace.split( "\n" )[ offset + 3 ];
} else if ( e.stack ) {
// Firefox, Chrome
stack = e.stack.split( "\n" );
if (/^error$/i.test( stack[0] ) ) {
stack.shift();
}
if ( fileName ) {
include = [];
for ( i = offset; i < stack.length; i++ ) {
if ( stack[ i ].indexOf( fileName ) !== -1 ) {
break;
}
include.push( stack[ i ] );
}
if ( include.length ) {
return include.join( "\n" );
}
}
return stack[ offset ];
} else if ( e.sourceURL ) {
// Safari, PhantomJS
// hopefully one day Safari provides actual stacktraces
// exclude useless self-reference for generated Error objects
if ( /qunit.js$/.test( e.sourceURL ) ) {
return;
}
// for actual exceptions, this is useful
return e.sourceURL + ":" + e.line;
}
return run;
}
// so far supports only Firefox, Chrome and Opera (buggy)
// could be extended in the future to use something like https://github.com/csnover/TraceKit
function sourceFromStacktrace() {
function sourceFromStacktrace( offset ) {
try {
throw new Error();
} catch ( e ) {
if (e.stacktrace) {
// Opera
return e.stacktrace.split("\n")[6];
} else if (e.stack) {
// Firefox, Chrome
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;
}
return extractStacktrace( e, offset );
}
}
function escapeInnerText(s) {
if (!s) {
/**
* Escape text for attribute or text content.
*/
function escapeText( s ) {
if ( !s ) {
return "";
}
s = s + "";
return s.replace(/[\&<>]/g, function(s) {
switch(s) {
case "&": return "&amp;";
case "<": return "&lt;";
case ">": return "&gt;";
default: return s;
// Both single quotes and double quotes (for attributes)
return s.replace( /['"<>&]/g, function( s ) {
switch( s ) {
case "'":
return "&#039;";
case "\"":
return "&quot;";
case "<":
return "&lt;";
case ">":
return "&gt;";
case "&":
return "&amp;";
}

@@ -943,3 +1025,3 @@ });

if ( config.autorun && !config.blocking ) {
process(last);
process( last );
}

@@ -949,2 +1031,5 @@ }

function process( last ) {
function next() {
process( last );
}
var start = new Date().getTime();

@@ -957,5 +1042,3 @@ config.depth = config.depth ? config.depth + 1 : 1;

} else {
window.setTimeout( function(){
process( last );
}, 13 );
setTimeout( next, 13 );
break;

@@ -975,6 +1058,9 @@ }

for ( var key in window ) {
if ( !hasOwn.call( window, 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 );
}

@@ -984,14 +1070,17 @@ }

function checkPollution( name ) {
var old = config.pollution;
function checkPollution() {
var newGlobals,
deletedGlobals,
old = config.pollution;
saveGlobal();
var newGlobals = diff( config.pollution, old );
newGlobals = diff( config.pollution, old );
if ( newGlobals.length > 0 ) {
ok( false, "Introduced global variable(s): " + newGlobals.join(", ") );
QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join(", ") );
}
var deletedGlobals = diff( old, config.pollution );
deletedGlobals = diff( old, config.pollution );
if ( deletedGlobals.length > 0 ) {
ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") );
QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join(", ") );
}

@@ -1002,7 +1091,9 @@ }

function diff( a, b ) {
var result = a.slice();
for ( var i = 0; i < result.length; i++ ) {
for ( var j = 0; j < b.length; j++ ) {
var i, j,
result = a.slice();
for ( i = 0; i < result.length; i++ ) {
for ( j = 0; j < b.length; j++ ) {
if ( result[i] === b[j] ) {
result.splice(i, 1);
result.splice( i, 1 );
i--;

@@ -1016,19 +1107,13 @@ break;

function fail(message, exception, callback) {
if ( typeof console !== "undefined" && console.error && console.warn ) {
console.error(message);
console.error(exception);
console.warn(callback.toString());
} else if ( window.opera && opera.postError ) {
opera.postError(message, exception, callback.toString);
}
}
function extend(a, b) {
function extend( a, b ) {
for ( var prop in b ) {
if ( b[prop] === undefined ) {
delete a[prop];
} else {
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 ];
}
}
}

@@ -1040,19 +1125,61 @@ }

function addEvent(elem, type, fn) {
/**
* @param {HTMLElement} elem
* @param {string} type
* @param {Function} fn
*/
function addEvent( elem, type, fn ) {
if ( elem.addEventListener ) {
// Standards-based browsers
elem.addEventListener( type, fn, false );
} else if ( elem.attachEvent ) {
// support: IE <9
elem.attachEvent( "on" + type, fn );
} else {
fn();
// Caller must ensure support for event listeners is present
throw new Error( "addEvent() was called in a context without event listener support" );
}
}
function id(name) {
return !!(typeof document !== "undefined" && document && document.getElementById) &&
document.getElementById( name );
/**
* @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 registerLoggingCallback(key){
return function(callback){
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 defined.document && document.getElementById && document.getElementById( name );
}
function registerLoggingCallback( key ) {
return function( callback ) {
config[key].push( callback );

@@ -1063,11 +1190,10 @@ };

// 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);
function runLoggingCallbacks( key, scope, args ) {
var i, 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 );
callbacks = config[ key ];
for ( i = 0; i < callbacks.length; i++ ) {
callbacks[ i ].call( scope, args );
}

@@ -1077,18 +1203,528 @@ }

// from jquery.js
function inArray( elem, array ) {
if ( array.indexOf ) {
return array.indexOf( elem );
}
for ( var i = 0, length = array.length; i < length; i++ ) {
if ( array[ i ] === elem ) {
return i;
}
}
return -1;
}
function Test( settings ) {
extend( this, settings );
this.assertions = [];
this.testNumber = ++Test.count;
}
Test.count = 0;
Test.prototype = {
init: function() {
var a, b, li,
tests = id( "qunit-tests" );
if ( tests ) {
b = document.createElement( "strong" );
b.innerHTML = this.nameHtml;
// `a` initialized at top of scope
a = document.createElement( "a" );
a.innerHTML = "Rerun";
a.href = QUnit.url({ testNumber: this.testNumber });
li = document.createElement( "li" );
li.appendChild( b );
li.appendChild( a );
li.className = "running";
li.id = this.id = "qunit-test-output" + testId++;
tests.appendChild( li );
}
},
setup: function() {
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,
passed: config.moduleStats.all - config.moduleStats.bad,
total: config.moduleStats.all
});
}
config.previousModule = this.module;
config.moduleStats = { all: 0, bad: 0 };
runLoggingCallbacks( "moduleStart", QUnit, {
name: this.module
});
}
config.current = this;
this.testEnvironment = extend({
setup: function() {},
teardown: function() {}
}, this.moduleTestEnvironment );
this.started = +new Date();
runLoggingCallbacks( "testStart", QUnit, {
name: this.testName,
module: this.module
});
/*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, QUnit.assert );
return;
}
try {
this.testEnvironment.setup.call( this.testEnvironment, QUnit.assert );
} catch( e ) {
QUnit.pushFailure( "Setup failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) );
}
},
run: function() {
config.current = this;
var running = id( "qunit-testresult" );
if ( running ) {
running.innerHTML = "Running: <br/>" + 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 ) {
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();
// Restart the tests if they're blocking
if ( config.blocking ) {
QUnit.start();
}
}
},
teardown: function() {
config.current = this;
if ( config.notrycatch ) {
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, QUnit.assert );
} catch( e ) {
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 ) {
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 ) {
QUnit.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack );
} 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 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];
li = document.createElement( "li" );
li.className = assertion.result ? "pass" : "fail";
li.innerHTML = assertion.message || ( assertion.result ? "okay" : "failed" );
ol.appendChild( li );
if ( assertion.result ) {
good++;
} else {
bad++;
config.stats.bad++;
config.moduleStats.bad++;
}
}
// store result when possible
if ( QUnit.config.reorder && defined.sessionStorage ) {
if ( bad ) {
sessionStorage.setItem( "qunit-test-" + this.module + "-" + this.testName, bad );
} else {
sessionStorage.removeItem( "qunit-test-" + this.module + "-" + this.testName );
}
}
if ( bad === 0 ) {
addClass( ol, "qunit-collapsed" );
}
// `b` initialized at top of scope
b = document.createElement( "strong" );
b.innerHTML = this.nameHtml + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>";
addEvent(b, "click", function() {
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" ) {
target = target.parentNode;
}
if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
window.location = QUnit.url({ testNumber: test.testNumber });
}
});
// `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( time );
li.appendChild( ol );
} else {
for ( i = 0; i < this.assertions.length; i++ ) {
if ( !this.assertions[i].result ) {
bad++;
config.stats.bad++;
config.moduleStats.bad++;
}
}
}
runLoggingCallbacks( "testDone", QUnit, {
name: this.testName,
module: this.module,
failed: bad,
passed: this.assertions.length - bad,
total: this.assertions.length,
runtime: this.runtime,
// DEPRECATED: this property will be removed in 2.0.0, use runtime instead
duration: this.runtime
});
QUnit.reset();
config.current = undefined;
},
queue: function() {
var bad,
test = this;
synchronize(function() {
test.init();
});
function run() {
// each of these can by async
synchronize(function() {
test.setup();
});
synchronize(function() {
test.run();
});
synchronize(function() {
test.teardown();
});
synchronize(function() {
test.finish();
});
}
// `bad` initialized at top of scope
// defer when previous test run passed, if storage is available
bad = QUnit.config.reorder && defined.sessionStorage &&
+sessionStorage.getItem( "qunit-test-" + this.module + "-" + this.testName );
if ( bad ) {
run();
} else {
synchronize( run, true );
}
}
};
// `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({ .. });
assert = QUnit.assert = {
/**
* Asserts rough true-ish result.
* @name ok
* @function
* @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
*/
ok: function( result, msg ) {
if ( !config.current ) {
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 = "<span class='test-message'>" + escapeText( msg ) + "</span>";
if ( !result ) {
source = sourceFromStacktrace( 2 );
if ( source ) {
details.source = source;
msg += "<table><tr class='test-source'><th>Source: </th><td><pre>" +
escapeText( source ) +
"</pre></td></tr></table>";
}
}
runLoggingCallbacks( "log", QUnit, details );
config.current.assertions.push({
result: result,
message: msg
});
},
/**
* Assert that the first two arguments are equal, with an optional message.
* Prints out both actual and expected values.
* @name equal
* @function
* @example equal( format( "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 );
},
/**
* @name notEqual
* @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
*/
deepEqual: function( actual, expected, message ) {
QUnit.push( QUnit.equiv(actual, expected), actual, expected, message );
},
/**
* @name notDeepEqual
* @function
*/
notDeepEqual: function( actual, expected, message ) {
QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );
},
/**
* @name strictEqual
* @function
*/
strictEqual: function( actual, expected, message ) {
QUnit.push( expected === actual, actual, expected, message );
},
/**
* @name notStrictEqual
* @function
*/
notStrictEqual: function( actual, expected, message ) {
QUnit.push( expected !== actual, actual, expected, message );
},
"throws": function( block, expected, message ) {
var actual,
expectedOutput = expected,
ok = false;
// 'expected' is optional
if ( !message && typeof expected === "string" ) {
message = expected;
expected = null;
}
config.current.ignoreGlobalErrors = true;
try {
block.call( config.current.testEnvironment );
} catch (e) {
actual = e;
}
config.current.ignoreGlobalErrors = false;
if ( actual ) {
// we don't want to validate thrown error
if ( !expected ) {
ok = true;
expectedOutput = null;
// expected is an Error object
} else if ( expected instanceof Error ) {
ok = actual instanceof Error &&
actual.name === expected.name &&
actual.message === expected.message;
// expected is a regexp
} else if ( QUnit.objectType( expected ) === "regexp" ) {
ok = expected.test( errorString( actual ) );
// expected is a string
} else if ( QUnit.objectType( expected ) === "string" ) {
ok = expected === 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, expectedOutput, message );
} else {
QUnit.pushFailure( message, null, "No exception was thrown." );
}
}
};
/**
* @deprecated since 1.8.0
* Kept assertion helpers in root for backwards compatibility.
*/
extend( QUnit.constructor.prototype, assert );
/**
* @deprecated since 1.9.0
* Kept to avoid TypeErrors for undefined methods.
*/
QUnit.constructor.prototype.raises = function() {
QUnit.push( false, false, false, "QUnit.raises has been deprecated since 2012 (fad3c1ea), use QUnit.throws instead" );
};
/**
* @deprecated since 1.0.0, replaced with error pushes since 1.3.0
* Kept to avoid TypeErrors for undefined methods.
*/
QUnit.constructor.prototype.equals = function() {
QUnit.push( false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead" );
};
QUnit.constructor.prototype.same = function() {
QUnit.push( false, false, false, "QUnit.same has been deprecated since 2009 (e88049a0), use QUnit.deepEqual instead" );
};
// Test for equality any JavaScript type.
// Author: Philippe Rathé <prathe@gmail.com>
QUnit.equiv = function () {
QUnit.equiv = (function() {
var innerEquiv; // the real equiv function
var callers = []; // stack to decide between skip/abort functions
var parents = []; // stack to avoiding loops from circular referencing
// Call the o related callback with the given arguments.
function bindCallbacks(o, callbacks, args) {
var prop = QUnit.objectType(o);
if (prop) {
if (QUnit.objectType(callbacks[prop]) === "function") {
return callbacks[prop].apply(callbacks, args);
function bindCallbacks( o, callbacks, args ) {
var prop = QUnit.objectType( o );
if ( prop ) {
if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) {
return callbacks[ prop ].apply( callbacks, args );
} else {
return callbacks[prop]; // or undefined
return callbacks[ prop ]; // or undefined
}

@@ -1098,142 +1734,182 @@ }

var callbacks = function () {
// the real equiv function
var innerEquiv,
// stack to decide between skip/abort functions
callers = [],
// stack to avoiding loops from circular referencing
parents = [],
parentsB = [],
// for string, boolean, number and null
function useStrictEquality(b, a) {
if (b instanceof a.constructor || a instanceof b.constructor) {
// to catch short annotaion VS 'new' annotation of a
// declaration
// e.g. var i = 1;
// var j = new Number(1);
return a == b;
} else {
return a === b;
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 annotation VS 'new' annotation of a
// declaration
// e.g. var i = 1;
// var j = new Number(1);
return a == b;
} else {
return a === b;
}
}
}
return {
"string" : useStrictEquality,
"boolean" : useStrictEquality,
"number" : useStrictEquality,
"null" : useStrictEquality,
"undefined" : useStrictEquality,
return {
"string": useStrictEquality,
"boolean": useStrictEquality,
"number": useStrictEquality,
"null": useStrictEquality,
"undefined": useStrictEquality,
"nan" : function(b) {
return isNaN(b);
},
"nan": function( b ) {
return isNaN( b );
},
"date" : function(b, a) {
return QUnit.objectType(b) === "date"
&& a.valueOf() === b.valueOf();
},
"date": function( b, a ) {
return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf();
},
"regexp" : function(b, a) {
return QUnit.objectType(b) === "regexp"
&& a.source === b.source && // the regex itself
a.global === b.global && // and its modifers
// (gmi) ...
a.ignoreCase === b.ignoreCase
&& a.multiline === b.multiline;
},
"regexp": function( b, a ) {
return QUnit.objectType( b ) === "regexp" &&
// the regex itself
a.source === b.source &&
// and its modifiers
a.global === b.global &&
// (gmi) ...
a.ignoreCase === b.ignoreCase &&
a.multiline === b.multiline &&
a.sticky === b.sticky;
},
// - skip when the property is a method of an instance (OOP)
// - abort otherwise,
// initial === would have catch identical references anyway
"function" : function() {
var caller = callers[callers.length - 1];
return caller !== Object && typeof caller !== "undefined";
},
// - skip when the property is a method of an instance (OOP)
// - abort otherwise,
// initial === would have catch identical references anyway
"function": function() {
var caller = callers[callers.length - 1];
return caller !== Object && typeof caller !== "undefined";
},
"array" : function(b, a) {
var i, j, loop;
var len;
"array": function( b, a ) {
var i, j, len, loop, aCircular, bCircular;
// b could be an object literal here
if (!(QUnit.objectType(b) === "array")) {
return false;
}
// b could be an object literal here
if ( QUnit.objectType( b ) !== "array" ) {
return false;
}
len = a.length;
if (len !== b.length) { // safe and faster
return false;
}
len = a.length;
if ( len !== b.length ) {
// safe and faster
return false;
}
// track reference to avoid circular references
parents.push(a);
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
// track reference to avoid circular references
parents.push( a );
parentsB.push( b );
for ( i = 0; i < len; i++ ) {
loop = false;
for ( j = 0; j < parents.length; j++ ) {
aCircular = parents[j] === a[i];
bCircular = parentsB[j] === b[i];
if ( aCircular || bCircular ) {
if ( a[i] === b[i] || aCircular && bCircular ) {
loop = true;
} else {
parents.pop();
parentsB.pop();
return false;
}
}
}
if ( !loop && !innerEquiv(a[i], b[i]) ) {
parents.pop();
parentsB.pop();
return false;
}
}
if (!loop && !innerEquiv(a[i], b[i])) {
parents.pop();
return false;
parents.pop();
parentsB.pop();
return true;
},
"object": function( b, a ) {
/*jshint forin:false */
var i, j, loop, aCircular, bCircular,
// Default to true
eq = true,
aProperties = [],
bProperties = [];
// comparing constructors is more strict than using
// instanceof
if ( a.constructor !== b.constructor ) {
// Allow objects with no prototype to be equivalent to
// objects with Object as their constructor.
if ( !(( getProto(a) === null && getProto(b) === Object.prototype ) ||
( getProto(b) === null && getProto(a) === Object.prototype ) ) ) {
return false;
}
}
}
parents.pop();
return true;
},
"object" : function(b, a) {
var i, j, loop;
var eq = true; // unless we can proove it
var aProperties = [], bProperties = []; // collection of
// strings
// stack constructor before traversing properties
callers.push( a.constructor );
// comparing constructors is more strict than using
// instanceof
if (a.constructor !== b.constructor) {
return false;
}
// track reference to avoid circular references
parents.push( a );
parentsB.push( b );
// stack constructor before traversing properties
callers.push(a.constructor);
// track reference to avoid circular references
parents.push(a);
for (i in a) { // be strict: don't ensures hasOwnProperty
// and go deep
loop = false;
for (j = 0; j < parents.length; j++) {
if (parents[j] === a[i])
loop = true; // don't go down the same path
// twice
// be strict: don't ensure hasOwnProperty and go deep
for ( i in a ) {
loop = false;
for ( j = 0; j < parents.length; j++ ) {
aCircular = parents[j] === a[i];
bCircular = parentsB[j] === b[i];
if ( aCircular || bCircular ) {
if ( a[i] === b[i] || aCircular && bCircular ) {
loop = true;
} else {
eq = false;
break;
}
}
}
aProperties.push(i);
if ( !loop && !innerEquiv(a[i], b[i]) ) {
eq = false;
break;
}
}
aProperties.push(i); // collect a's properties
if (!loop && !innerEquiv(a[i], b[i])) {
eq = false;
break;
parents.pop();
parentsB.pop();
callers.pop(); // unstack, we are done
for ( i in b ) {
bProperties.push( i ); // collect b's properties
}
}
callers.pop(); // unstack, we are done
parents.pop();
for (i in b) {
bProperties.push(i); // collect b's properties
// Ensures identical properties name
return eq && innerEquiv( aProperties.sort(), bProperties.sort() );
}
};
}());
// Ensures identical properties name
return eq
&& innerEquiv(aProperties.sort(), bProperties
.sort());
}
};
}();
innerEquiv = function() { // can take multiple arguments
var args = Array.prototype.slice.apply(arguments);
if (args.length < 2) {
var args = [].slice.apply( arguments );
if ( args.length < 2 ) {
return true; // end transition
}
return (function(a, b) {
if (a === b) {
return (function( a, b ) {
if ( a === b ) {
return true; // catch the most you can
} else if (a === null || b === null || typeof a === "undefined"
|| typeof b === "undefined"
|| QUnit.objectType(a) !== QUnit.objectType(b)) {
} 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

@@ -1245,11 +1921,8 @@ } else {

// 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;
}());
}();
/**

@@ -1267,7 +1940,7 @@ * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com |

function quote( str ) {
return '"' + str.toString().replace(/"/g, '\\"') + '"';
};
return "\"" + str.toString().replace( /"/g, "\\\"" ) + "\"";
}
function literal( o ) {
return o + '';
};
return o + "";
}
function join( pre, arr, post ) {

@@ -1277,205 +1950,212 @@ var s = jsDump.separator(),

inner = jsDump.indent(1);
if ( arr.join )
arr = arr.join( ',' + s + inner );
if ( !arr )
if ( arr.join ) {
arr = arr.join( "," + s + inner );
}
if ( !arr ) {
return pre + post;
}
return [ pre, inner + arr, base + post ].join(s);
};
}
function array( arr, stack ) {
var i = arr.length, ret = Array(i);
var i = arr.length, ret = new Array(i);
this.up();
while ( i-- )
while ( i-- ) {
ret[i] = this.parse( arr[i] , undefined , stack);
}
this.down();
return join( '[', ret, ']' );
};
return join( "[", ret, "]" );
}
var reName = /^function (\w+)/;
var reName = /^function (\w+)/,
jsDump = {
// 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) ];
var jsDump = {
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) ];
type = typeof parser;
var inStack = inArray(obj, stack);
if (inStack != -1) {
return 'recursion('+(inStack - stack.length)+')';
}
//else
if (type == 'function') {
stack.push(obj);
var res = parser.call( this, obj, stack );
type = typeof parser;
inStack = inArray( obj, stack );
if ( inStack !== -1 ) {
return "recursion(" + (inStack - stack.length) + ")";
}
if ( type === "function" ) {
stack.push( obj );
res = parser.call( this, obj, stack );
stack.pop();
return res;
}
// else
return (type == 'string') ? parser : this.parsers.error;
},
typeOf:function( obj ) {
var type;
if ( obj === null ) {
type = "null";
} else if (typeof obj === "undefined") {
type = "undefined";
} else if (QUnit.is("RegExp", obj)) {
type = "regexp";
} else if (QUnit.is("Date", obj)) {
type = "date";
} else if (QUnit.is("Function", obj)) {
type = "function";
} else if (typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined") {
type = "window";
} else if (obj.nodeType === 9) {
type = "document";
} else if (obj.nodeType) {
type = "node";
} 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;
}
return type;
},
separator:function() {
return this.multiline ? this.HTML ? '<br />' : '\n' : this.HTML ? '&nbsp;' : ' ';
},
indent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing
if ( !this.multiline )
return '';
var chr = this.indentChar;
if ( this.HTML )
chr = chr.replace(/\t/g,' ').replace(/ /g,'&nbsp;');
return Array( this._depth_ + (extra||0) ).join(chr);
},
up:function( a ) {
this._depth_ += a || 1;
},
down:function( a ) {
this._depth_ -= a || 1;
},
setParser:function( name, parser ) {
this.parsers[name] = parser;
},
// The next 3 are exposed so you can use them
quote:quote,
literal:literal,
join:join,
//
_depth_: 1,
// 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
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
if ( name )
ret += ' ' + name;
ret += '(';
ret = [ ret, QUnit.jsDump.parse( fn, 'functionArgs' ), '){'].join('');
return join( ret, QUnit.jsDump.parse(fn,'functionCode'), '}' );
}
return ( type === "string" ) ? parser : this.parsers.error;
},
array: array,
nodelist: array,
arguments: array,
object:function( map, stack ) {
var ret = [ ];
QUnit.jsDump.up();
for ( var key in map ) {
var val = map[key];
ret.push( QUnit.jsDump.parse(key,'key') + ': ' + QUnit.jsDump.parse(val, undefined, stack));
}
QUnit.jsDump.down();
return join( '{', ret, '}' );
typeOf: function( obj ) {
var type;
if ( obj === null ) {
type = "null";
} else if ( typeof obj === "undefined" ) {
type = "undefined";
} else if ( QUnit.is( "regexp", obj) ) {
type = "regexp";
} else if ( QUnit.is( "date", obj) ) {
type = "date";
} else if ( QUnit.is( "function", obj) ) {
type = "function";
} else if ( typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined" ) {
type = "window";
} else if ( obj.nodeType === 9 ) {
type = "document";
} else if ( obj.nodeType ) {
type = "node";
} 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 if ( obj.constructor === Error.prototype.constructor ) {
type = "error";
} else {
type = typeof obj;
}
return type;
},
node:function( node ) {
var open = QUnit.jsDump.HTML ? '&lt;' : '<',
close = QUnit.jsDump.HTML ? '&gt;' : '>';
var tag = node.nodeName.toLowerCase(),
ret = open + tag;
for ( var a in QUnit.jsDump.DOMAttrs ) {
var val = node[QUnit.jsDump.DOMAttrs[a]];
if ( val )
ret += ' ' + a + '=' + QUnit.jsDump.parse( val, 'attribute' );
separator: function() {
return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? "&nbsp;" : " ";
},
// extra can be a number, shortcut for increasing-calling-decreasing
indent: function( extra ) {
if ( !this.multiline ) {
return "";
}
return ret + close + open + '/' + tag + close;
var chr = this.indentChar;
if ( this.HTML ) {
chr = chr.replace( /\t/g, " " ).replace( / /g, "&nbsp;" );
}
return new Array( this.depth + ( extra || 0 ) ).join(chr);
},
functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function
var l = fn.length;
if ( !l ) return '';
var args = Array(l);
while ( l-- )
args[l] = String.fromCharCode(97+l);//97 is 'a'
return ' ' + args.join(', ') + ' ';
up: function( a ) {
this.depth += a || 1;
},
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
string:quote,
date:quote,
regexp:literal, //regex
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.
};
down: function( a ) {
this.depth -= a || 1;
},
setParser: function( name, parser ) {
this.parsers[name] = parser;
},
// The next 3 are exposed so you can use them
quote: quote,
literal: literal,
join: join,
//
depth: 1,
// This is the list of parsers, to modify them, use jsDump.setParser
parsers: {
window: "[Window]",
document: "[Document]",
error: function(error) {
return "Error(\"" + error.message + "\")";
},
unknown: "[Unknown]",
"null": "null",
"undefined": "undefined",
"function": function( fn ) {
var ret = "function",
// functions never have name in IE
name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1];
return jsDump;
})();
if ( name ) {
ret += " " + name;
}
ret += "( ";
// from Sizzle.js
function getText( elems ) {
var ret = "", elem;
ret = [ ret, QUnit.jsDump.parse( fn, "functionArgs" ), "){" ].join( "" );
return join( ret, QUnit.jsDump.parse(fn,"functionCode" ), "}" );
},
array: array,
nodelist: array,
"arguments": array,
object: function( map, stack ) {
/*jshint forin:false */
var ret = [ ], keys, key, val, i;
QUnit.jsDump.up();
keys = [];
for ( key in map ) {
keys.push( key );
}
keys.sort();
for ( i = 0; i < keys.length; i++ ) {
key = keys[ i ];
val = map[ key ];
ret.push( QUnit.jsDump.parse( key, "key" ) + ": " + QUnit.jsDump.parse( val, undefined, stack ) );
}
QUnit.jsDump.down();
return join( "{", ret, "}" );
},
node: function( node ) {
var len, i, val,
open = QUnit.jsDump.HTML ? "&lt;" : "<",
close = QUnit.jsDump.HTML ? "&gt;" : ">",
tag = node.nodeName.toLowerCase(),
ret = open + tag,
attrs = node.attributes;
for ( var i = 0; elems[i]; i++ ) {
elem = elems[i];
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" );
}
}
}
ret += close;
// Get the text from text nodes and CDATA nodes
if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
ret += elem.nodeValue;
// Show content of TextNode or CDATASection
if ( node.nodeType === 3 || node.nodeType === 4 ) {
ret += node.nodeValue;
}
// Traverse everything else, except comment nodes
} else if ( elem.nodeType !== 8 ) {
ret += getText( elem.childNodes );
}
}
return ret + open + "/" + tag + close;
},
// function calls it internally, it's the arguments part of the function
functionArgs: function( fn ) {
var args,
l = fn.length;
return ret;
};
if ( !l ) {
return "";
}
//from jquery.js
function inArray( elem, array ) {
if ( array.indexOf ) {
return array.indexOf( elem );
}
args = new Array(l);
while ( l-- ) {
// 97 is 'a'
args[l] = String.fromCharCode(97+l);
}
return " " + args.join( ", " ) + " ";
},
// 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,
number: literal,
"boolean": literal
},
// 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
};
for ( var i = 0, length = array.length; i < length; i++ ) {
if ( array[ i ] === elem ) {
return i;
}
}
return jsDump;
}());
return -1;
}
/*

@@ -1493,52 +2173,56 @@ * Javascript Diff Algorithm

*
* 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() {
function diff(o, n) {
var ns = {};
var os = {};
/*jshint eqeqeq:false, eqnull:true */
function diff( o, n ) {
var i,
ns = {},
os = {};
for (var i = 0; i < n.length; i++) {
if (ns[n[i]] == null)
ns[n[i]] = {
for ( i = 0; i < n.length; i++ ) {
if ( !hasOwn.call( ns, n[i] ) ) {
ns[ n[i] ] = {
rows: [],
o: null
};
ns[n[i]].rows.push(i);
}
ns[ n[i] ].rows.push( i );
}
for (var i = 0; i < o.length; i++) {
if (os[o[i]] == null)
os[o[i]] = {
for ( i = 0; i < o.length; i++ ) {
if ( !hasOwn.call( os, o[i] ) ) {
os[ o[i] ] = {
rows: [],
n: null
};
os[o[i]].rows.push(i);
}
os[ o[i] ].rows.push( i );
}
for (var i in ns) {
if ( !hasOwn.call( ns, i ) ) {
continue;
for ( i in ns ) {
if ( hasOwn.call( ns, i ) ) {
if ( ns[i].rows.length === 1 && hasOwn.call( os, i ) && os[i].rows.length === 1 ) {
n[ ns[i].rows[0] ] = {
text: n[ ns[i].rows[0] ],
row: os[i].rows[0]
};
o[ os[i].rows[0] ] = {
text: o[ os[i].rows[0] ],
row: ns[i].rows[0]
};
}
}
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]
};
}
}
for (var i = 0; i < n.length - 1; i++) {
if (n[i].text != null && n[i + 1].text == null && n[i].row + 1 < o.length && o[n[i].row + 1].text == null &&
n[i + 1] == o[n[i].row + 1]) {
n[i + 1] = {
text: n[i + 1],
for ( i = 0; i < n.length - 1; i++ ) {
if ( n[i].text != null && n[ i + 1 ].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null &&
n[ i + 1 ] == o[ n[i].row + 1 ] ) {
n[ i + 1 ] = {
text: n[ i + 1 ],
row: n[i].row + 1
};
o[n[i].row + 1] = {
text: o[n[i].row + 1],
o[ n[i].row + 1 ] = {
text: o[ n[i].row + 1 ],
row: i + 1

@@ -1549,11 +2233,12 @@ };

for (var i = n.length - 1; i > 0; i--) {
if (n[i].text != null && n[i - 1].text == null && n[i].row > 0 && o[n[i].row - 1].text == null &&
n[i - 1] == o[n[i].row - 1]) {
n[i - 1] = {
text: n[i - 1],
for ( i = n.length - 1; i > 0; i-- ) {
if ( n[i].text != null && n[ i - 1 ].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null &&
n[ i - 1 ] == o[ n[i].row - 1 ]) {
n[ i - 1 ] = {
text: n[ i - 1 ],
row: n[i].row - 1
};
o[n[i].row - 1] = {
text: o[n[i].row - 1],
o[ n[i].row - 1 ] = {
text: o[ n[i].row - 1 ],
row: i - 1

@@ -1570,45 +2255,48 @@ };

return function(o, n) {
o = o.replace(/\s+$/, '');
n = n.replace(/\s+$/, '');
var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/));
return function( o, n ) {
o = o.replace( /\s+$/, "" );
n = n.replace( /\s+$/, "" );
var str = "";
var i, pre,
str = "",
out = diff( o === "" ? [] : o.split(/\s+/), n === "" ? [] : n.split(/\s+/) ),
oSpace = o.match(/\s+/g),
nSpace = n.match(/\s+/g);
var oSpace = o.match(/\s+/g);
if (oSpace == null) {
oSpace = [" "];
if ( oSpace == null ) {
oSpace = [ " " ];
}
else {
oSpace.push(" ");
oSpace.push( " " );
}
var nSpace = n.match(/\s+/g);
if (nSpace == null) {
nSpace = [" "];
if ( nSpace == null ) {
nSpace = [ " " ];
}
else {
nSpace.push(" ");
nSpace.push( " " );
}
if (out.n.length == 0) {
for (var i = 0; i < out.o.length; i++) {
str += '<del>' + out.o[i] + oSpace[i] + "</del>";
if ( out.n.length === 0 ) {
for ( i = 0; i < out.o.length; i++ ) {
str += "<del>" + out.o[i] + oSpace[i] + "</del>";
}
}
else {
if (out.n[0].text == null) {
for (n = 0; n < out.o.length && out.o[n].text == null; n++) {
str += '<del>' + out.o[n] + oSpace[n] + "</del>";
if ( out.n[0].text == null ) {
for ( n = 0; n < out.o.length && out.o[n].text == null; n++ ) {
str += "<del>" + out.o[n] + oSpace[n] + "</del>";
}
}
for (var i = 0; i < out.n.length; i++) {
for ( i = 0; i < out.n.length; i++ ) {
if (out.n[i].text == null) {
str += '<ins>' + out.n[i] + nSpace[i] + "</ins>";
str += "<ins>" + out.n[i] + nSpace[i] + "</ins>";
}
else {
var pre = "";
// `pre` initialized at top of scope
pre = "";
for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++) {
pre += '<del>' + out.o[n] + oSpace[n] + "</del>";
for ( n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) {
pre += "<del>" + out.o[n] + oSpace[n] + "</del>";
}

@@ -1622,4 +2310,19 @@ str += " " + out.n[i].text + nSpace[i] + pre;

};
})();
}());
})(this);
// For browser, export only select globals
if ( typeof window !== "undefined" ) {
extend( window, QUnit.constructor.prototype );
window.QUnit = QUnit;
}
// For CommonJS environments, export everything
if ( typeof module !== "undefined" && module.exports ) {
module.exports = QUnit;
}
// Get a reference to the global object, like window in browsers
}( (function() {
return this;
})() ));
{
"name": "jquery-mockjax",
"version": "1.5.3",
"version": "1.6.1",
"main": "jquery.mockjax.js",
"description": "Mockjax. The jQuery Mockjax Plugin provides a simple and extremely flexible interface for mocking or simulating ajax requests and responses.",
"url": "http://code.appendto.com/plugins/jquery-mockjax/",
"keywords": [ "ajax", "mock", "unit" ],
"url": "https://github.com/jakerella/jquery-mockjax",
"keywords": [ "ajax", "mock", "unit", "testing" ],
"author": "Jonathan Sharp (http://jdsharp.com/)",
"homepage": "http://github.com/appendto/jquery-mockjax",
"homepage": "https://github.com/jakerella/jquery-mockjax",
"repository": {
"type": "git",
"url": "https://github.com/appendto/jquery-mockjax.git"
"url": "https://github.com/jakerella/jquery-mockjax.git"
},
"bugs": {
"web": "http://github.com/appendto/jquery-mockjax/issues"
"web": "http://github.com/jakerella/jquery-mockjax/issues"
},

@@ -19,7 +20,7 @@ "licenses": [

"type": "MIT",
"url": "http://appendto.com/open-source-licenses"
"url": "http://opensource.org/licenses/MIT"
},
{
"type": "GPLv2",
"url": "http://appendto.com/open-source-licenses"
"url": "http://www.gnu.org/licenses/gpl-2.0.html"
}

@@ -30,5 +31,5 @@ ],

"type": "git",
"url": "http://github.com/appendto/jquery-mockjax.git"
"url": "https://github.com/jakerella/jquery-mockjax.git"
}
]
}
# jQuery Mockjax: Ajax request mocking #
[http://github.com/appendto/jquery-mockjax/](http://github.com/appendto/jquery-mockjax/)
[http://github.com/jakerella/jquery-mockjax/](http://github.com/jakerella/jquery-mockjax/)
jQuery Mockjax provides request/response mocking for ajax requests with
jQuery and provides all standard behaviors in the request/response flow.
jQuery Mockjax provides request/response mocking for ajax requests using the
jQuery API and provides all standard behaviors in the request/response flow.
You may report any issues you may find [in the github issue tracking](https://github.com/appendto/jquery-mockjax/issues).
You may report any issues you may find [in the github issue tracking](https://github.com/jakerella/jquery-mockjax/issues).
### jQuery Version Support ###
**Table of Contents**
The current version of Mockjax has been tested with jQuery 1.3.2 through
1.7.0 with QUnit unit tests, residing in /test.
* [About Mockjax and Its History](#about-mockjax-and-its-history)
* [Basic Documentation](#basic-documentation)
* [API Methods](#api-methods)
* [Overview: Your First Mock](#overview-your-first-mock)
* [Mockjax in Depth](#mockjax-in-depth)
* [Detailed Request and Response Definition](#detailed-request-and-response-definition)
* [Defining a Request to Match](#defining-a-request-to-match)
* [Define a Response](#define-a-response)
* [Advanced Mocking Techniques](#advanced-mocking-techniques)
* [Simulating Response Time and Latency](#simulating-response-time-and-latency)
* [Simulating HTTP Response Statuses](#simulating-http-response-statuses)
* [Setting the Content-Type](#setting-the-content-type)
* [Setting Additional HTTP Response Headers](#setting-additional-http-response-headers)
* [Dynamically Generating Mock Definitions](#dynamically-generating-mock-definitions)
* [Accessing Request Headers](#accessing-request-headers)
* [Forced Simulation of Server Timeouts](#forced-simulation-of-server-timeouts)
* [Dynamically Generating Mock Responses](#dynamically-generating-mock-responses)
* [Data Types](#data-types)
* [Performing Actions After Request Completion](#performing-actions-after-request-completion)
* [Globally Defining Mockjax Settings](#globally-defining-mockjax-settings)
* [Removing Mockjax Handlers](#removing-mockjax-handlers)
* [Miscellaneous Information](#miscellaneous-information)
* [jQuery Version Support](#jquery-version-support)
* [Browsers Tested](#browsers-tested)
* [Release History](#release-history)
* [License](#license)
* [Contributing](#contributing)
### Browsers Tested ###
Internet Explorer 6-9, Firefox 3.6 and stable, Safari 5.x, Chrome stable, Opera 9.6-latest.
### Release History ##
[CHANGELOG](https://github.com/appendto/jquery-mockjax/blob/master/CHANGELOG.md)
## About Mockjax and Its History ##
## License ##
Copyright (c) 2012 appendTo LLC.
Dual licensed under the MIT or GPL licenses.
[http://appendto.com/open-source-licenses](http://appendto.com/open-source-licenses)
## Documentation ##
Most backend developers are familiar with the concepts of [mocking
objects](http://en.wikipedia.org/wiki/Mock_object) or stubbing in
methods for unit testing. For those not familiar with mocking, it’s the
methods for unit testing. For those not familiar with mocking, it's the
simulation of an interface or API for testing or integration development
purposes. Mocking with front-end development though is still quite new.
purposes. Mocking with front-end development though is still quite new. Mockjax
gives front end developers the ability to define ajax requests that should be
mocked out, as well as how those requests should be responded to. These mocks
can be extremely simple or quite complex, representing the entire request-response
workflow.
Much of the development that [appendTo](http://appendto.com) does
focuses on front-end development tied to
At appendTo we developed a lot of applications which use
[RESTFUL](http://en.wikipedia.org/wiki/Representational_State_Transfer)
web services. **As such we’re able to spec out the service contract and
data format at the beginning of a project and develop the front-end
interface against mock data while the back end team builds the
production services.**
web services, but much of the time those services are not yet created.
We spec out the service contract and data format at the beginning of a project
and develop the front-end interface against mock data while the back end team
builds the production services.
The plugin was originally developed by appendTo back in
March 2010 and the [team](http://twitter.com/appendto/team) has been
using it in all of its projects since.
This plugin was originally developed by appendTo in March 2010 and the
[team](http://twitter.com/appendto/team) has been using it in many projects since.
### API
Mockjax consists of two methods, one to set up mocks, one to remove them.
You'll find plenty of examples below. If you're looking for a specific option,
## Basic Documentation ##
### API Methods ###
Mockjax consists of just a few methods, each listed below. You'll find plenty of
examples in the sections below, but if you're looking for a specific option,
checkout this list:
* `$.mockjax(options)`
* Sets up a mockjax handler.
* `options`: An object literal which defines the settings to use for the mocked request.
* `url`: A string or regular expression specifying the url of the request that the data should be mocked for. If the url is a string and contains any asterisks ( * ), they will be treated as a wildcard by translating to a regular expression. Any `*` will be replaced with `.+`. If you run into trouble with this shortcut, switch to using a full regular expression instead of a string and asterisk combination.
* `data`: In addition to the URL, match parameters.
* `type`: Specify what HTTP method to match, usually GET or POST. Case-insensitive, so `get` and `post` also work.
* `headers`: An object literal whose keys will be simulated as additional headers returned from the server for the request.
* `status`: An integer that specifies a valid server response code. This simulates a server response code.
* `statusText`: An string that specifies a valid server response code description. This simulates a server response code description.
* `responseTime`: An integer that specifies a simulated network and server latency (in milliseconds).
* `isTimeout`: A boolean value that determines whether or not the mock will force a timeout on the request.
* `contentType`: A string which specifies the content type for the response.
* `response`: `function(settings) {}`, A function that allows for the dynamic setting of responseText/responseXML upon each request.
* `responseText`: A string specifying the mocked text, or a mocked object literal, for the request.
* `responseXML`: A string specifying the mocked XML for the request.
* `proxy`: A string specifying a path to a file, from which the contents will be returned for the request.
* `lastModified`: A date string specifying the mocked last-modified time for the request. This is used by `$.ajax` to determine if the requested data is new since the last request.
* `etag`: A string specifying a unique identifier referencing a specific version of the requested data. This is used by `$.ajax` to determine if the requested data is new since the last request. (see [HTTP_ETag](http://en.wikipedia.org/wiki/HTTP_ETag))
* `$.mockjaxClear()`
* Removes all mockjax handlers.
* `$.mockjaxClear(id)`
* Remove a single mockjax handler.
* `id` is the string returned from `$.mockjax`.
* `$.mockjax.mockedAjaxCalls()`
* Returns all mocked ajax calls so you can e.g. check that expected data is sent to backend.
* `Number $.mockjax(/* Object */ options)`
* Sets up a mockjax handler for a matching request
* Returns that handler's index, can be used to clear individual handlers
* `options`: [Object] Defines the settings to use for the mocked request
* `url`: [String | RegExp] Specifies the url of the request that the data should be mocked for. If it is a string and contains any asterisks ( `*` ), they will be treated as a wildcard by translating to a regular expression. Any `*` will be replaced with `.+`. If you run into trouble with this shortcut, switch to using a full regular expression instead of a string and asterisk combination
* `data`: [Object] In addition to the URL, match parameters
* `type`: [String] Specify what HTTP method to match, usually GET or POST. Case-insensitive, so `get` and `post` also work
* `headers`: [Object] Keys will be simulated as additional headers returned from the server for the request (**NOTE: This is NOT used to match request headers!**)
* `status`: [Number] An integer that specifies a valid server response code. This simulates a server response code
* `statusText`: [String] Specifies a valid server response code description. This simulates a server response code description
* `responseTime`: [Number] An integer that specifies a simulated network and server latency (in milliseconds)
* `isTimeout`: [Boolean] Determines whether or not the mock will force a timeout on the request
* `contentType`: [String] Specifies the content type for the response
* `response`: [Function] A function that accepts the request settings and allows for the dynamic setting of response settings (including the body of the response) upon each request (see examples below)
* `responseText`: [String] Specifies the mocked text, or a mocked object literal, for the request
* `responseXML`: [String] Specifies the mocked XML for the request
* `proxy`: [String] Specifies a path to a file, from which the contents will be returned for the request
* `lastModified`: [String] A date string specifying the mocked last-modified time for the request. This is used by `$.ajax` to determine if the requested data is new since the last request
* `etag`: [String] Specifies a unique identifier referencing a specific version of the requested data. This is used by `$.ajax` to determine if the requested data is new since the last request. (see [HTTP_ETag](http://en.wikipedia.org/wiki/HTTP_ETag))
* `onAfterSuccess`: [Function] A callback that will be called after the success method has been called, this is useful to check a condition after the call has been completed
* `onAfterError`: [Function] A callback that will be called after the error method has been called, this is useful to check a condition after the call has been completed
* `onAfterComplete`: [Function] Similar to onAfterSuccess, but will be executed after the complete method has been called
* `Object $.mockjax.handler(/* Number */ id)`
* Returns the mock request settings for the handler with the provided `id`
* `void $.mockjax.clear([/* Number */ id])`
* If the `id` is provided, the handler with that ID is cleared (that is, requests matching it will no longer do so, the hnadler is completely removed)
* If no `id` is provided, all handlers are cleared, resetting Mockjax to its initial state
* `Array<Object> $.mockjax.mockedAjaxCalls()`
* Returns an array of all mocked ajax calls with each entry being the request settings object as passed into the `$.mockjax()` function
* `Array<Object> $.mockjax.unfiredHandlers()`
* Returns an array of all mock handler settings that have not been used. In other words, if a handler has been used for a `$.ajax()` call then it will _not_ appear in this array
* `Array<Object> $.mockjax.unmockedAjaxCalls()`
* Returns an array of all unmocked Ajax calls that were made. The array contains the settings object passed into `$.ajax({...})`
### Overview: Your First Mock
### Overview: Your First Mock ###

@@ -85,92 +111,110 @@ Our first example will be for a simple REST service for a fortune app

{
"status": "success",
"fortune" : "Are you a turtle?"
}
```json
{
"status": "success",
"fortune" : "Are you a turtle?"
}
```
To pull the fortune into our page, we’d use the following HTML & jQuery
To pull the fortune into our page, we'd use the following HTML and jQuery
code:
<!DOCTYPE html>
<html>
<head>
<title>Fortune App</title>
<script src="http://code.jquery.com/jquery-1.7.0.min.js"></script>
</head>
<body>
<div id="fortune"></div>
</body>
</html>
```html
<!DOCTYPE html>
<html>
<head>
<title>Fortune App</title>
<script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
</head>
<body>
<div id="fortune"></div>
</body>
</html>
```
```javascript
$.getJSON("/restful/fortune", function(response) {
if ( response.status == "success") {
$("#fortune").html( "Your fortune is: " + response.fortune );
} else {
$("#fortune").html( "Things do not look good, no fortune was told" );
}
});
```
$.getJSON('/restful/fortune', function(response) {
if ( response.status == 'success') {
$('#fortune').html( 'Your fortune is: ' + response.fortune );
} else {
$('#fortune').html( 'Things do not look good, no fortune was told' );
}
});
At this point if we were to run this code it would fail since the REST
service has yet to be implemented. This is where the benefit of the
Mockjax Plugin starts to pay off. The first step in using Mockjax is to
include the plugin by just adding a regular script tag.
Mockjax plugin starts to pay off. The first step in using Mockjax is to
include the plugin by just adding a regular script tag:
```html
<head>
...
<script src="vendor/jquery.mockjax.js"></script>
</head>
```
Once you have that included, you can start intercepting Ajax requests
and mocking the responses. So let’s mock out the service by including
and mocking the responses. So let's mock out the service by including
the following code:
$.mockjax({
url: '/restful/fortune',
responseTime: 750,
responseText: {
status: 'success',
fortune: 'Are you a turtle?'
}
});
```javascript
$.mockjax({
url: "/restful/fortune",
responseText: {
status: "success",
fortune: "Are you a mock turtle?"
}
});
```
**Defining a JSON string inline requires a `JSON.stringify` method to be
**Defining a JSON string inline requires a `JSON.stringify()` method to be
available. For some browsers you may need to include
[json2.js](http://json.org/json2.js), which is included in the `lib` folder**
[json2.js](https://raw.github.com/douglascrockford/JSON-js/master/json2.js),
which is included in the `lib` folder.** However, you could also simply
provide an already stringified version of your JSON in the `responseText`
property.
**If you plan on mocking xml responses, you may also have to include
`jquery.xmldom.js`, which can also be found in the `lib` folder.**
_If you plan on mocking xml responses, you may also have to include
`jquery.xmldom.js`, which can also be found in the `lib` folder._
What Mockjax does at this point is replace the `$.ajax` method with a
### Mockjax in Depth ###
What Mockjax does at this point is replace the `$.ajax()` method with a
wrapper that transparently checks the URL being requested. If the URL
matches one defined by `$.mockjax()`, Mockjax intercepts the request
matches one defined by `$.mockjax()`, it intercepts the request
and sets up a mock `XMLHttpRequest` object before executing the
`jQuery.ajax` handler. Otherwise, the request is handed back to the
native `$.ajax` method for normal execution. One benefit in this
implementation detail is by simulating the `XMLHttpRequest` object, the
plugin continues to make use of jQuery’s native ajax handling.
`jQuery.ajax()` handler. Otherwise, the request is handed back to the
native `$.ajax()` method for normal execution. One benefit in this
implementation detail is that by simulating the `XMLHttpRequest` object,
the plugin continues to make use of jQuery's native ajax handling, so
there are no concerns with implementing a custom Ajax workflow.
As you write code to mock responses, there’s great value in the fact that there are no
modifications required to production code. The mocks can be
As you write code to mock responses, there's great value in the fact that
there are no modifications required to production code. The mocks can be
transparently inserted. This provides easy integration into most
frameworks by including the plugin and mock definitions through your
build framework. It’s also possible to include it at run time by
listening for a flag query string flag and injecting the plugin and
definitions.
build framework. It's also possible to include it at run time by
listening for a query string flag and injecting the plugin and definitions.
### Mockjax in Depth
Now let’s look at the various approaches to defining mocks as offered by
Now let's look at the various approaches to defining mocks as offered by
the plugin. The sections below feature an extensive overview of the
flexibility in Mockjax and creating responses.
## Data Types Available for Mocking
#### Data Types Available for Mocking ####
jQuery is able to handle and parse `Text`, `HTML`, `JSON`, `JSONP`,
`Script` and `XML` data formats and Mockjax is able to mock any of those
formats. Two things to note, depending upon how you mock out `JSON` and
`JSONP` you may need to include [json2.js](http://json.org/json2.js) for
the `JSON.stringify()` method. Additionally if you mock XML inline,
you’ll need to include the
[`xmlDOM`](http://github.com/appendto/jquery-xmldom) plugin that
transforms a string of XML into a DOM object. If you use the proxy
approach outlined below, there’s no need to include either the JSON or
XMLDOM plugins.
formats. Two things to note: depending upon how you mock out `JSON` and
`JSONP` you may need to include [json2.js](https://raw.github.com/douglascrockford/JSON-js/master/json2.js)
for the `JSON.stringify()` method (older browsers only, typically). Additionally
if you mock XML inline, you'll need to include the [`xmlDOM`](http://github.com/jakerella/jquery-xmldom)
plugin that transforms a string of XML into a DOM object. However, if you use
the proxy approach outlined below then there should be no need to include either
the JSON or XMLDOM plugins in any case.
## Step 1. Define the URL
## Detailed Request and Response Definition ##
### Defining a Request to Match ###
The first thing you need to do when mocking a request is define the URL

@@ -180,45 +224,50 @@ end-point to intercept and mock. As with our example above this can be a

$.mockjax({
url: '/url/to/rest-service'
});
```javascript
$.mockjax({
url: "/url/to/rest-service"
});
```
or contain a `*` as a wildcard:
$.mockjax({
// Matches /data/quote, /data/tweet etc.
url: '/data/*'
});
```javascript
$.mockjax({
// Matches /data/quote, /data/tweet etc.
url: "/data/*"
});
```
or a full regular expression:
$.mockjax({
// Matches /data/quote, /data/tweet but not /data/quotes
url: /^\/data\/(quote|tweet)$/i
});
```javascript
$.mockjax({
// Matches /data/quote, /data/tweet but not /data/quotes
url: /^\/data\/(quote|tweet)$/i
});
```
You can also match against the data option in addition to url:
$.mockjax({
url: '/rest',
data: { action: "foo" },
responseText: { bar: "hello world" }
});
```javascript
$.mockjax({
url: "/rest",
data: { action: "foo" }
});
```
$.mockjax({
url: '/rest',
data: { action: "bar" },
responseText: { bar: "hello world 2" }
});
To capture URL parameters, use a capturing regular expression for the
URL and a `urlParams` array to indicate, ordinally, the names of the
paramters that will be captured:
To capture URL parameters, use a capturing regular expression for the URL and a `urlParams` array to indicate, ordinally, the names of the paramters that will be captured.
```javascript
$.mockjax({
// matches /author/1234/isbn/1234-5678-9012-0
// matches /author/{any number here}/isbn/{any number with dashes here}
// for example: "/author/1234/isbn/1234-5678-9012-0"
url: /^\/author\/([\d]+)\/isbn\/([\d\-]+)$/,
urlParams: ['authorID', 'isbnNumber'],
// names of matching params
urlParams: ["authorID", "isbnNumber"],
response: function (settings) {
var authorID = settings.urlParams.authorID;
var isbnNumber = settigns.urlParams.isbnNumber;
//etc.
var isbnNumber = settings.urlParams.isbnNumber;
// etc...
}

@@ -228,6 +277,6 @@ });

### Step 2. Define the Response
### Define a Response ###
The second step is to define the type of response. The two main
properties you’ll be dealing with are either `responseText` or
The second step is to define the type and content of the response. The two main
properties you will be dealing with are either `responseText` or
`responseXML`. These properties mirror the native `XMLHttpRequest`

@@ -238,53 +287,102 @@ object properties that are set during a live response. There are three

#### Inline Responses
#### Inline Responses ####
A simple text response would be:
$.mockjax({
url: '/restful/api',
responseText: 'A text response from the server'
});
```javascript
$.mockjax({
url: "/restful/api",
responseText: "A text response from the server"
});
```
A simple JSON response would be:
```javascript
$.mockjax({
url: "/restful/api",
// You may need to include the [json2.js](https://raw.github.com/douglascrockford/JSON-js/master/json2.js) library for older browsers
responseText: { "foo": "bar" }
});
```
Also note that a JSON response is really just a text response that jQuery will
parse as JSON for you (and return a JSOn object to the `success` and `complete`
callbacks).
A simple XML response would be:
$.mockjax({
url: '/restful/api',
// Need to include the xmlDOM plugin to have this translated into a DOM
responseXML: '<document><quote>Hello world!</quote></document>'
});
```javascript
$.mockjax({
url: "/restful/api",
// Need to include the xmlDOM plugin to have this translated into a DOM object
responseXML: "<document><quote>Hello world!</quote></document>"
});
```
As you can quickly see, if you have a significant amount of data being
mocked this becomes unwieldy. So that brings us to the next pattern,
proxying.
As you can see, if you have a significant amount of data being
mocked this becomes unwieldy. So that brings us to the next pattern:
the proxy.
#### Proxy
#### Proxy ####
In this example below, the Mockjax plugin will intercept requests for
`/restful/api` and redirect them to `/mocks/data.json`.
`/restful/api` and redirect them to `/mocks/data.json`:
$.mockjax({
url: '/restful/api',
proxy: '/mocks/data.json'
});
```javascript
$.mockjax({
url: "/restful/api",
proxy: "/mocks/data.json"
});
```
#### Callback
The `/mocks/data.json` file can have any valid JSON content you want, and allows
you to maintain that mock data in its own file for maintainability.
In the final response pattern, we can define a callback on the
#### Callback ####
In the final response pattern, we can define a callback function on the
`response` property and have it set `responseText` or `responseXML` as
needed.
needed:
$.mockjax({
url: '/restful/api',
response: function() {
this.responseText = 'Hello world!';
}
```javascript
$.mockjax({
url: "/restful/api",
response: function(settings) {
// Investigate the `settings` to determine the response...
this.responseText = "Hello world!";
}
});
```
The default version of this callback is synchronous. If you provide both parameters
to the callback function, you can use asynchronous code to set the dynamic response.
```javascript
$.mockjax({
url: '/restful/api',
response: function(settings, done) {
var self = this;
someAsyncMethod(function(data){
self.responseText = data;
done();
});
}
});
```
### Advanced Mocking Techniques
Note that the callback is given the settings provided to the `$.mockjax({...})`
method merged with any Ajax settings defined by jQuery or your application. This
allows you to thoroughly investigate the request before setting the response
body (or headers).
At this point we’ve looked at a series of basic mocking techniques with
## Advanced Mocking Techniques ##
At this point we've looked at a series of basic mocking techniques with
Mockjax and will now unpack some of the additional functionality
contained in the plugin.
#### Simulating Response Time and Latency
### Simulating Response Time and Latency ###

@@ -294,36 +392,45 @@ Simulating network and server latency for a mock is as simple as adding

$.mockjax({
url: '/restful/api',
// Simulate a network latency of 750ms
responseTime: 750,
responseText: 'A text response from the server'
});
```javascript
$.mockjax({
url: "/restful/api",
// Simulate a network latency of 750ms
responseTime: 750,
responseText: "A text response from the server"
});
```
#### Simulating HTTP Response Statuses
### Simulating HTTP Response Statuses ###
It’s also possible to simulate response statuses other than 200 (default
It's also possible to simulate response statuses other than 200 (default
for Mockjax) by simply adding a `status` property.
$.mockjax({
url: '/restful/api',
// Server 500 error occurred
status: 500,
responseTime: 750,
responseText: 'A text response from the server'
});
```javascript
$.mockjax({
url: "/restful/api",
// Server 500 error occurred
status: 500,
responseText: "A text response from the server"
});
```
#### Setting the Content-Type
These forced error status codes will be handled just as if the server had
returned the error: the `error` callback will get executed with the proper
arguments.
### Setting the Content-Type ###
You can set the content type to associate with the mock response, in the
example below, we’re setting a json content type.
example below, we're setting a JSON content type.
$.mockjax({
url: '/restful/api',
contentType: 'text/json',
responseText: {
hello: 'World!'
}
});
```javascript
$.mockjax({
url: "/restful/api",
contentType: "application/json",
responseText: {
hello: "World!"
}
});
```
#### Setting Additional HTTP Response Headers
### Setting Additional HTTP Response Headers ###

@@ -333,27 +440,17 @@ Additional HTTP Response Headers may be provided by setting a key in the

$.mockjax({
url: '/restful/api',
contentType: 'text/json',
responseText: {
hello: 'World!'
},
headers: {
etag: 'xyz123'
}
});
```javascript
$.mockjax({
url: "/restful/api",
contentType: "application/json",
responseText: {
hello: "World!"
},
headers: {
etag: "xyz123"
}
});
```
#### Force Simulation of Server Timeouts
### Dynamically Generating Mock Definitions ###
Because of the way Mockjax was implemented, it takes advantage of
jQuery’s internal timeout handling for requests. But if you’d like to
force a timeout for a request you can do so by setting the `isTimeout`
property to true:
$.mockjax({
url: '/restful/api',
isTimeout: true
});
#### Dynamically Generating Mock Definitions
In some situations, all of your REST calls are based upon a URL schema.

@@ -366,80 +463,239 @@ Mockjax has the ability for you to specify a callback function that is

$.mockjax(function(settings) {
// settings.url == '/restful/<service>'
var service = settings.url.match(/\/restful\/(.*)$/);
if ( service ) {
return {
proxy: '/mocks/' + service[1] + '.json'
};
```javascript
$.mockjax(function(settings) {
// settings.url might be: "/restful/<service>" such as "/restful/user"
var service = settings.url.match(/\/restful\/(.*)$/);
if ( service ) {
return {
proxy: "/mocks/" + service[1] + ".json"
};
}
// If you get here, there was no url match
return;
});
```
### Accessing Request Headers ###
In some situations, you may need access to the request headers to determine
matching or response bodies. To do this, you will need to specify a
callback function that is handed the `$.ajax` request settings:
```javascript
$.mockjax(function( requestSettings ) {
// Here is our manual URL matching...
if ( requestSettings.url === "/restful/user" ) {
// We have a match, so we return a response callback...
return {
response: function( origSettings ) {
// now we check the request headers, which may be set directly
// on the xhr object through an ajaxSetup() call or otherwise:
if ( requestSettings.headers["Authentication"] === "some-token" ) {
this.responseText = { user: { id: 13 } };
} else {
this.status = 403;
this.responseText = "You are not authorized";
}
}
return;
});
};
}
// If you get here, there was no url match
return;
});
```
#### Dynamically Generating Mock Responses
### Forced Simulation of Server Timeouts ###
It’s also possible to dynamically generate the response text upon each
Because of the way Mockjax was implemented, it takes advantage of
jQuery's internal timeout handling for requests. But if you'd like to
force a timeout for a request you can do so by setting the `isTimeout`
property to true:
```javascript
$.mockjax({
url: '/restful/api',
responseTime: 1000,
isTimeout: true
});
```
### Dynamically Generating Mock Responses ###
It's also possible to dynamically generate the response text upon each
request by implementing a callback function on the `response` parameter:
$.mockjax({
url: '/restful/webservice',
dataType: 'json',
response: function(settings) {
this.responseText = { say: 'random ' + Math.random() };
}
});
```javascript
$.mockjax({
url: "/restful/webservice",
dataType: "json",
response: function(settings) {
this.responseText = {
randomText: "random " + Math.random()
};
}
});
```
#### Data types
### Data Types ###
The example above mocks a `json` response. You can also mock `xml`:
Many of the examples above mock a `json` response. You can also mock `xml`:
$.mockjax({
url: '/some/xml',
dataType: 'xml',
responseXML: '<document><say>Hello world XML</say></document>'
});
```javascript
$.mockjax({
url: "/some/xml",
dataType: "xml",
responseXML: "<document><say>Hello world XML</say></document>"
});
```
(Don't forget that it's likely you'll need the [`xmlDOM`](http://github.com/jakerella/jquery-xmldom) library as well!)
And `html`:
$.mockjax({
url: '/some/webservice',
dataType: 'html',
responseText: '<div>Hello there</div>'
});
```javascript
$.mockjax({
url: "/some/webservice",
dataType: "html",
responseText: "<div>Hello there</div>"
});
```
#### Globally Defining Mockjax Settings
### Performing Actions After Request Completion ###
It’s also possible to define the global defaults for all Mockjax
If you need to perform some actions after a call has completed you can
use one of the `onAfter{Xxxxx}` options. For example, to fire a method when
a request completes (either successfully or not):
```javascript
$.mockjax({
url: "/api/end/point",
onAfterComplete: function() {
// do any required cleanup
}
});
```
### Globally Defining Mockjax Settings ###
It is also possible to define the global defaults for all Mockjax
requests by overwriting the `$.mockjaxSettings` object. By default the
settings are as follows:
$.mockjaxSettings = {
status: 200,
statusText 'OK',
responseTime: 500,
isTimeout: false,
contentType: 'text/plain',
response: '',
responseText: '',
responseXML: '',
proxy: '',
lastModified: null,
etag: ''
};
```javascript
{
logging: true,
status: 200,
statusText: "OK",
responseTime: 500,
isTimeout: false,
throwUnmocked: false,
contentType: "text/plain",
response: "",
responseText: "",
responseXML: "",
proxy: "",
proxyType: "GET",
lastModified: null,
etag: "",
headers: {
etag: "IJF@H#@923uf8023hFO@I#H#",
"content-type" : "text/plain"
}
}
```
To overwrite a particular settings such as the default content-type, you
To overwrite a particular settings such as the default `content-type`, you
would do the following:
$.mockjaxSettings.contentType = 'text/json';
```javascript
$.mockjaxSettings.contentType = "application/json";
```
#### Removing Mockjax Handlers
### Removing Mockjax Handlers ###
Remove all mockjax handlers:
If you need to reset the Mockjax handlers you've added, just call
`$.mockjax.clear()`. _This will NOT reset the `$.mockjaxSettings`!_
$.mockjaxClear();
```javascript
$.mockjax.clear();
```
#### Remove Single Mockjax Handler
You can also clear individual mock handlers using their ID:
var id = $.mockjax({
...
});
$.mockjaxClear(id);
```javascript
var id = $.mockjax({
...
});
$.mockjax.clear(id);
```
## Miscellaneous Information ##
### jQuery Version Support ###
We strive to ensure that Mockjax is tested on the furthest patch version of all
minor (and major) versions of jQuery beginning with 1.3.2 going all the way
through 2.x. In other words, we don't test 1.6.1, but rather 1.6.4 (the furthest
patch version on the 1.6.x line). The QUnit tests in the `/test` directory include
links to each version of jQuery tested in the header.
### Browsers Tested ###
Note that jQuery Mockjax generally supports whatever browser jQuery supports.
However, it is important to understand that various versions of jQuery have dropped
support for different versions of browsers over the years. The point is, be sure
to test on the browser versions you support!
We use virtual machines to test current versions of the browsers below. In addition,
we test the minimum version specified next to the browser (with the exception of
IE which specifies the versions we test).
* Internet Explorer (7-11)
* Firefox (3.6)
* Safari (5.1)
* Chrome (stable only, significantly older versions are not available)
* Opera (9.6; although 9.x does not work with jQuery 1.9+)
_Please note that while we strive to keep `master` as bug free as possible, we do
not necessarily run tests in all of the above browsers for every single commit. We
do, however, ensure all tests are passing before tagging a release._
### Release History ###
Please read the [CHANGELOG](https://github.com/jakerella/jquery-mockjax/blob/master/CHANGELOG.md)
for a list of changes per release.
Note that all releases are tagged in Github for easy reference, the `master` branch
should *not* be considered a stable release!
### License ###
Copyright (c) 2014 appendTo, Jordan Kasper
NOTE: This repository was taken over by Jordan Kasper (@jakerella) October, 2014
Dual licensed under the MIT or GPL licenses:
[http://opensource.org/licenses/MIT](http://opensource.org/licenses/MIT)
[http://www.gnu.org/licenses/gpl-2.0.html](http://www.gnu.org/licenses/gpl-2.0.html)
### Contributing ###
We welcome any contributions by the community, whether in the form of a Pull
Request, issue submission and comments, or just sharing on social media!
If you want to contribute code to the project, please read our
[Contribution guidelines](CONTRIBUTING.md) to see what you need to do to get your
Pull Request ready for merging.
#### Admins ####
All pull requests are reviewed by the wonderful collaborators on this project:
* [Doug Neiner](https://github.com/dcneiner)
* [Jonathan Creamer](https://github.com/jcreamer898)
* [Jordan Kasper](https://github.com/jakerella)
var mockjaxDefaults = $.extend({}, $.mockjaxSettings);
function noErrorCallbackExpected() {
ok( false, 'Error callback executed');
ok( false, 'Error callback executed');
}
function compareSemver(v1, v2, op) {
var result = false,
p1 = normalizeSemVer(v1),
p2 = normalizeSemVer(v2);
if (/^===?$/.test(op)) {
result = semverEqual(p1, p2, 3);
} else if (/^</.test(op)) {
result = p1[0] < p2[0] || (semverEqual(p1, p2, 1) && p1[1] < p2[1]) || (semverEqual(p1, p2, 2) && p1[2] < p2[2]);
if (!result && /^<=$/.test(op)) {
result = semverEqual(p1, p2, 3);
}
} else if (/^>/.test(op)) {
result = p1[0] > p2[0] || (semverEqual(p1, p2, 1) && p1[1] > p2[1]) || (semverEqual(p1, p2, 2) && p1[2] > p2[2]);
}
if (!result && /^[<>]=$/.test(op)) {
result = semverEqual(p1, p2, 3);
}
return result;
}
function semverEqual(p1, p2, cnt) {
var i, equal = true;
for (i=0; i<cnt; ++i) {
equal = equal && (p1[i] === p2[i]);
}
return equal;
}
function normalizeSemVer(v) {
if (v.length < 1) { return "0.0.0"; }
var p = v.toString().split('.');
if (p.length < 2) { p[1] = "0"; }
if (p.length < 3) { p[2] = "0"; }
return [Number(p[0]), Number(p[1]), Number(p[2])];
}
// Speed up our tests
$.mockjaxSettings.responseTime = 0;
var defaultSettings = $.extend({}, $.mockjaxSettings);
QUnit.testDone(function() {
// reset mockjax after each test
$.mockjax.clear();
$.mockjaxSettings = $.extend({}, defaultSettings);
});
module('Core');
test('Return XMLHttpRequest object from $.ajax', function() {
$.mockjax({
url: '/xmlhttprequest',
responseText: "Hello Word"
});
$.mockjax({
url: '/xmlhttprequest',
responseText: "Hello Word"
});
var xhr = $.ajax({
url: '/xmlhttprequest',
complete: function() { }
});
xhr && xhr.abort && xhr.abort();
var xhr = $.ajax({
url: '/xmlhttprequest',
complete: function() { }
});
xhr && xhr.abort && xhr.abort();
ok(xhr, "XHR object is not null or undefined");
if (jQuery.Deferred) {
ok(xhr.done && xhr.fail, "Got Promise methods");
}
$.mockjaxClear();
ok(xhr, "XHR object is not null or undefined");
if (jQuery.Deferred) {
ok(xhr.done && xhr.fail, "Got Promise methods");
}
});
asyncTest('Intercept and proxy (sub-ajax request)', function() {
$.mockjax({
url: '/proxy',
proxy: 'test_proxy.json'
});
$.mockjax({
url: '/proxy',
proxy: 'test_proxy.json'
});
$.ajax({
url: '/proxy',
dataType: 'json',
success: function(json) {
ok(json && json.proxy, 'Proxy request succeeded');
},
error: noErrorCallbackExpected,
complete: function() {
start();
}
});
$.mockjaxClear();
$.ajax({
url: '/proxy',
dataType: 'json',
success: function(json) {
ok(json && json.proxy, 'Proxy request succeeded');
},
error: noErrorCallbackExpected,
complete: function() {
start();
}
});
});
asyncTest('Proxy type specification', function() {
$.mockjax({
url: '/proxy',
proxy: 'test_proxy.json',
proxyType: 'GET'
});
$.mockjax({
url: '/proxy',
proxy: 'test_proxy.json',
proxyType: 'GET'
});
$.ajax({
url: '/proxy',
error: noErrorCallbackExpected,
dataType: 'json',
success: function(json) {
ok(json && json.proxy, 'Proxy request succeeded');
},
complete: function() {
start();
}
});
$.mockjaxClear();
$.ajax({
url: '/proxy',
error: noErrorCallbackExpected,
dataType: 'json',
success: function(json) {
ok(json && json.proxy, 'Proxy request succeeded');
},
complete: function() {
start();
}
});
});
asyncTest('Support 1.5 $.ajax(url, settings) signature.', function() {
$.mockjax({
url: '/resource',
responseText: 'Hello Word'
});
$.mockjax({
url: '/resource',
responseText: 'Hello Word'
});
$.ajax('/resource', {
success: function(response) {
equal(response, 'Hello Word');
},
error: noErrorCallbackExpected,
complete: function() {
start();
}
});
$.ajax('/resource', {
success: function(response) {
equal(response, 'Hello Word');
},
error: noErrorCallbackExpected,
complete: function() {
start();
}
});
});
$.mockjaxClear();
asyncTest('Dynamic response callback', function() {
$.mockjax({
url: '/response-callback',
response: function(settings) {
this.responseText = settings.data.response + ' 2';
}
});
$.ajax({
url: '/response-callback',
dataType: 'text',
data: {
response: 'Hello world'
},
error: noErrorCallbackExpected,
complete: function(xhr) {
equal(xhr.responseText, 'Hello world 2', 'Response Text matches');
start();
}
});
});
asyncTest('Dynamic response callback', function() {
asyncTest('Dynamic asynchronous response callback', function() {
$.mockjax({
url: '/response-callback',
response: function(settings) {
this.responseText = settings.data.response + ' 2';
responseText: 'original response',
response: function(settings, done) {
var that = this;
setTimeout(function() {
that.responseText = settings.data.response + ' 3';
done();
}, 30);
}

@@ -108,249 +169,499 @@ });

complete: function(xhr) {
equals(xhr.responseText, 'Hello world 2', 'Response Text matches');
equal(xhr.responseText, 'Hello world 3', 'Response Text matches');
start();
}
});
$.mockjaxClear();
});
if (compareSemver($().jquery, "1.4", ">=")) {
// The $.ajax() API changed in version 1.4 to include the third argument: xhr
asyncTest('Success callback should have access to xhr object', function() {
$.mockjax({
url: '/response'
});
$.ajax({
type: 'GET',
url: '/response',
success: function() {
ok(arguments[2], 'there is a third argument to the success callback');
ok(arguments[2] && arguments[2].status === 200, 'third argument appears to be an xhr object (proper status code)');
start();
},
error: function() {
ok(false, "should not result in error");
start();
}
});
});
}
asyncTest('Dynamic response status callback', function() {
$.mockjax({
url: '/response-callback',
response: function(settings) {
this.status = 500;
this.statusText = "Internal Server Error"
}
});
$.mockjax({
url: '/response-callback',
response: function(settings) {
this.status = 500;
this.statusText = "Internal Server Error"
}
});
$.ajax({
url: '/response-callback',
dataType: 'text',
data: {
response: 'Hello world'
},
error: function(){
ok(true, "error callback was called");
},
complete: function(xhr) {
equals(xhr.status, 500, 'Dynamically set response status matches');
$.ajax({
url: '/response-callback',
dataType: 'text',
data: {
response: 'Hello world'
},
error: function(){
ok(true, "error callback was called");
},
complete: function(xhr) {
equal(xhr.status, 500, 'Dynamically set response status matches');
if( $.fn.jquery !== '1.5.2') {
// This assertion fails in 1.5.2 due to this bug: http://bugs.jquery.com/ticket/9854
// The statusText is being modified internally by jQuery in 1.5.2
equals(xhr.statusText, "Internal Server Error", 'Dynamically set response statusText matches');
}
if( $.fn.jquery !== '1.5.2') {
// This assertion fails in 1.5.2 due to this bug: http://bugs.jquery.com/ticket/9854
// The statusText is being modified internally by jQuery in 1.5.2
equal(xhr.statusText, "Internal Server Error", 'Dynamically set response statusText matches');
}
start();
}
});
$.mockjaxClear();
start();
}
});
});
asyncTest('Default Response Settings', function() {
$.mockjax({
url: '/response-callback'
});
$.mockjax({
url: '/response-callback'
});
$.ajax({
url: '/response-callback',
dataType: 'text',
data: {
response: ''
},
complete: function(xhr) {
equals(xhr.status, 200, 'Response status matches default');
$.ajax({
url: '/response-callback',
dataType: 'text',
data: {
response: ''
},
complete: function(xhr) {
equal(xhr.status, 200, 'Response status matches default');
if( $.fn.jquery !== '1.5.2') {
// This assertion fails in 1.5.2 due to this bug: http://bugs.jquery.com/ticket/9854
// The statusText is being modified internally by jQuery in 1.5.2
equals(xhr.statusText, "OK", 'Response statusText matches default');
}
if( $.fn.jquery !== '1.5.2') {
// This assertion fails in 1.5.2 due to this bug: http://bugs.jquery.com/ticket/9854
// The statusText is being modified internally by jQuery in 1.5.2
equal(xhr.statusText, "OK", 'Response statusText matches default');
}
equals(xhr.responseText.length, 0, 'responseText length should be 0');
equals(xhr.responseXml === undefined, true, 'responseXml should be undefined');
start();
}
});
$.mockjaxClear();
equal(xhr.responseText.length, 0, 'responseText length should be 0');
equal(xhr.responseXml === undefined, true, 'responseXml should be undefined');
start();
}
});
});
test('Remove mockjax definition by id', function() {
var id = $.mockjax({
url: '/test',
contentType: 'text/plain',
responseText: 'test'
});
var id = $.mockjax({
url: '/test',
contentType: 'text/plain',
responseText: 'test'
});
$.mockjax({
url: '*',
contentType: 'text/plain',
responseText: 'default'
});
$.mockjax({
url: '*',
contentType: 'text/plain',
responseText: 'default'
});
stop();
$.ajax({
url: '/test',
success: function(text) {
equals(text, 'test', 'Test handler responded');
},
error: noErrorCallbackExpected,
complete: function() {
$.mockjaxClear(id);
stop();
$.ajax({
url: '/test',
success: function(text) {
equal(text, 'test', 'Test handler responded');
},
error: noErrorCallbackExpected,
complete: function() {
$.mockjax.clear(id);
// Reissue the request expecting the default handler
$.ajax({
url: '/test',
success: function(text) {
equals(text, 'default', 'Default handler responded');
},
error: noErrorCallbackExpected,
complete: function(xhr) {
equals(xhr.responseText, 'default', 'Default handler responded');
// Reissue the request expecting the default handler
$.ajax({
url: '/test',
success: function(text) {
equal(text, 'default', 'Default handler responded');
},
error: noErrorCallbackExpected,
complete: function(xhr) {
equal(xhr.responseText, 'default', 'Default handler responded');
start();
}
});
}
});
});
$.mockjaxClear();
start();
}
});
}
});
asyncTest('Clearing mockjax removes all handlers', function() {
$.mockjax({
url: '/api/example/1',
responseText: 'test1'
});
$.mockjax({
url: '/api/example/2',
responseText: 'test2'
});
$.ajax({
async: true,
type: 'GET',
url: '/api/example/1',
success: function(text) {
equal('test1', text, 'First call is mocked');
},
error: noErrorCallbackExpected,
complete: function() {
$.mockjax.clear();
$.ajax({
async: true,
type: 'GET',
url: '/api/example/1',
success: function() {
ok( false, 'Call to first endpoint was mocked, but should not have been');
},
error: function(xhr) {
equal(404, xhr.status, 'First mock cleared after clear()');
$.ajax({
async: true,
type: 'GET',
url: '/api/example/2',
success: function() {
ok( false, 'Call to second endpoint was mocked, but should not have been');
},
error: function(xhr) {
equal(404, xhr.status, 'Second mock cleared after clear()');
start();
}
});
}
});
}
});
});
test('Old version of clearing mock handlers works', function() {
$.mockjax({
url: '/api/example/1'
});
$.mockjaxClear();
equal($.mockjax.handler(0), undefined, 'There are no mock handlers');
});
// asyncTest('Intercept log messages', function() {
// var msg = null;
// $.mockjaxSettings.log = function(inMsg, settings) {
// msg = inMsg;
// };
// $.mockjax({
// url: '*'
// });
// $.ajax({
// url: '/console',
// type: 'GET',
// complete: function() {
// equals(msg, 'MOCK GET: /console', 'Mock request logged to console');
// $.mockjaxClear();
// start();
// }
// });
// var msg = null;
// $.mockjaxSettings.log = function(inMsg, settings) {
// msg = inMsg;
// };
// $.mockjax({
// url: '*'
// });
// $.ajax({
// url: '/console',
// type: 'GET',
// complete: function() {
// equal(msg, 'MOCK GET: /console', 'Mock request logged to console');
// start();
// }
// });
// });
asyncTest('Disable console logging', function() {
var msg = null;
$.mockjaxSettings.console = false;
$.mockjax({
url: '*'
});
$.ajax({
url: '/console',
complete: function() {
equals(msg, null, 'Mock request not logged');
$.mockjaxClear();
start();
}
});
var msg = null;
$.mockjaxSettings.console = false;
$.mockjax({
url: '*'
});
$.ajax({
url: '/console',
complete: function() {
equal(msg, null, 'Mock request not logged');
start();
}
});
});
asyncTest('Get mocked ajax calls', function() {
$.mockjaxClear();
$.mockjax({
url: '/api/example/*'
});
equals($.mockjax.mockedAjaxCalls().length, 0, 'Initially there are no saved ajax calls')
// GET
$.ajax({
async: false,
type: 'GET',
url: '/api/example/1',
complete: function() {
var actualCalls = $.mockjax.mockedAjaxCalls();
equals(actualCalls.length, 1, 'One mocked ajax call is saved');
equals(actualCalls[0].type, 'GET', 'Saved ajax call has expected method');
equals(actualCalls[0].url, '/api/example/1', 'Saved ajax call has expected url');
start();
}
});
// POST with some data
$.ajax({
async: false,
type: 'POST',
url: '/api/example/2',
data: {a: 1},
complete: function() {
var actualCalls = $.mockjax.mockedAjaxCalls();
equals(actualCalls.length, 2, 'Two mocked ajax calls are saved');
equals(actualCalls[1].type, 'POST', 'Second ajax call has expected method');
equals(actualCalls[1].url, '/api/example/2', 'Second ajax call has expected url');
deepEqual(actualCalls[1].data, {a: 1}, 'Second ajax call has expected data');
start();
}
});
// JSONP
$.ajax({
async: false,
url: '/api/example/jsonp?callback=?',
jsonpCallback: 'foo123',
dataType: 'jsonp',
complete: function() {
var actualCalls = $.mockjax.mockedAjaxCalls();
equals(actualCalls.length, 3, 'Three mocked ajax calls are saved');
equals(actualCalls[2].url, '/api/example/jsonp?callback=foo123', 'Third ajax call has expected jsonp url');
start();
}
});
equals($.mockjax.mockedAjaxCalls().length, 3, 'Afterwords there should be three saved ajax calls')
var mockedUrls = $.map($.mockjax.mockedAjaxCalls(), function(ajaxOptions) { return ajaxOptions.url })
deepEqual(mockedUrls, ['/api/example/1', '/api/example/2', '/api/example/jsonp?callback=foo123'], 'Mocked ajax calls are saved in execution order')
$.mockjaxClear();
equals($.mockjax.mockedAjaxCalls().length, 0, 'After clearing there are no saved ajax calls')
asyncTest('Get mocked ajax calls - GET', function() {
$.mockjax({
url: '/api/example/*'
});
// GET
$.ajax({
async: false,
type: 'GET',
url: '/api/example/1',
complete: function() {
var actualCalls = $.mockjax.mockedAjaxCalls();
equal(actualCalls.length, 1, 'mockjax call made');
equal(actualCalls[0].type, 'GET', 'mockjax call has expected method');
equal(actualCalls[0].url, '/api/example/1', 'mockjax call has expected url');
start();
}
});
});
asyncTest('Test unmockedAjaxCalls returns the correct object when ajax call is not mocked and throwUnmocked is false', function() {
$.mockjaxSettings.throwUnmocked = false;
$.ajax({
async: true,
type: 'GET',
url: '/api/example/1',
complete: function() {
var unmockedAjaxCalls = $.mockjax.unmockedAjaxCalls();
equal(unmockedAjaxCalls.length, 1, 'Wrong number of unmocked ajax calls were returned');
equal(unmockedAjaxCalls[0].url, '/api/example/1', 'unmockedAjaxcall has unexpected url');
start();
}
});
});
asyncTest('Test unmockedAjaxCalls are cleared when mockjax.clear is called', function() {
$.mockjaxSettings.throwUnmocked = false;
$.ajax({
async: true,
type: 'GET',
url: '/api/example/1',
complete: function() {
equal($.mockjax.unmockedAjaxCalls().length, 1, 'Wrong number of unmocked ajax calls were returned');
$.mockjax.clear();
equal($.mockjax.unmockedAjaxCalls().length, 0, 'Unmocked ajax calls not removed by mockjax.clear');
start();
}
});
});
asyncTest('Test unmockedAjaxCalls returns nothing when no unmocked ajax calls occur', function() {
$.mockjax({
url: '/api/example/1'
});
$.ajax({
async: true,
type: 'GET',
url: '/api/example/1',
complete: function() {
var unmockedAjaxCalls = $.mockjax.unmockedAjaxCalls();
equal(unmockedAjaxCalls.length, 0, 'No unmocked Ajax calls should have been returned');
start();
}
});
});
asyncTest('Throw new error when throwUnmocked is set to true and unmocked ajax calls are fired', function() {
$.mockjaxSettings.throwUnmocked = true;
try {
$.ajax({
async: true,
type: 'GET',
url: '/api/example/1',
complete: function() {
ok(false, "Unmocked ajax request completed successfully and should have thrown an error.")
start();
}
});
}
catch (e) {
ok(e instanceof Error, "Error was not thrown with 'throwUnmocked' set to true and existing unmocked ajax request");
start();
}
});
asyncTest('Get unfired handlers', function() {
$.mockjax({
url: '/api/example/1'
});
$.mockjax({
url: '/api/example/2'
});
$.ajax({
async: false,
type: 'GET',
url: '/api/example/1',
complete: function() {
var handlersNotFired = $.mockjax.unfiredHandlers();
equal(handlersNotFired.length, 1, 'all mocks were fired');
equal(handlersNotFired[0].url, '/api/example/2', 'mockjax call has unexpected url');
start();
}
});
});
asyncTest('Get unfired handlers after calling mockjax.clear', function() {
$.mockjax({
url: '/api/example/1'
});
$.mockjax({
url: '/api/example/2'
});
$.mockjax({
url: '/api/example/3'
});
$.ajax({
async: false,
type: 'GET',
url: '/api/example/1',
complete: function() {
$.mockjax.clear(2);
var handlersNotFired = $.mockjax.unfiredHandlers();
equal(handlersNotFired.length, 1, 'all mocks were fired');
equal(handlersNotFired[0].url, '/api/example/2', 'mockjax call has unexpected url');
start();
}
});
});
asyncTest('Response settings correct using PUT method', function() {
$.mockjax({
url: '/put-request',
type: 'PUT',
responseText: 'this was a PUT'
});
$.ajax({
url: '/put-request',
type: 'PUT',
dataType: 'text',
complete: function(xhr) {
equal(xhr.status, 200, 'Response status matches default');
equal(xhr.responseText, 'this was a PUT', 'responseText is correct');
start();
}
});
});
asyncTest('Get mocked ajax calls - POST with data', function() {
$.mockjax({
url: '/api/example/*'
});
$.ajax({
async: false,
type: 'POST',
url: '/api/example/2',
data: {a: 1},
complete: function() {
var actualCalls = $.mockjax.mockedAjaxCalls();
equal(actualCalls.length, 1, 'mockjax call made');
equal(actualCalls[0].type, 'POST', 'mockjax call has expected method');
equal(actualCalls[0].url, '/api/example/2', 'mockjax call has expected url');
deepEqual(actualCalls[0].data, {a: 1}, 'mockjax call has expected data');
start();
}
});
});
asyncTest('Get mocked ajax calls - JSONP', function() {
$.mockjax({
url: '/api/example/*',
contentType: 'text/json',
proxy: 'test_jsonp.js'
});
var callbackExecuted = false;
window.abcdef123456 = function(json) {
ok( true, 'JSONP Callback executed');
callbackExecuted = true;
};
var ret = $.ajax({
url: '/api/example/jsonp?callback=?',
jsonpCallback: 'abcdef123456',
dataType: 'jsonp',
error: noErrorCallbackExpected,
complete: function(xhr) {
var actualCalls = $.mockjax.mockedAjaxCalls();
equal(actualCalls.length, 1, 'Mockjax call made');
equal(actualCalls[0].url, '/api/example/jsonp?callback=abcdef123456', 'mockjax call has expected jsonp url');
ok(callbackExecuted, 'The jsonp callback was executed');
start();
}
});
});
test('multiple mockjax calls are made', function() {
$.mockjax({
url: '/api/example/*'
});
equal($.mockjax.mockedAjaxCalls().length, 0, 'Initially there are no saved ajax calls');
$.ajax({
async: false,
type: 'GET',
url: '/api/example/1'
});
$.ajax({
async: false,
type: 'GET',
url: '/api/example/2'
});
$.ajax({
async: false,
url: '/api/example/jsonp?callback=?',
jsonpCallback: 'foo123',
dataType: 'jsonp'
});
equal($.mockjax.mockedAjaxCalls().length, 3, 'Afterwords there should be three saved ajax calls');
var mockedUrls = $.map($.mockjax.mockedAjaxCalls(), function(ajaxOptions) { return ajaxOptions.url });
deepEqual(mockedUrls, ['/api/example/1', '/api/example/2', '/api/example/jsonp?callback=foo123'], 'Mocked ajax calls are saved in execution order');
$.mockjax.clear();
equal($.mockjax.mockedAjaxCalls().length, 0, 'After clearing there are no saved ajax calls');
});
// These tests is only relevant in 1.5.2 and higher
if( jQuery.Deferred ) {
asyncTest('Preserve context when set in jsonp ajax requet', function(){
$.mockjax({
url: '/jsonp*',
contentType: 'text/json',
proxy: 'test_jsonp.js'
});
asyncTest('Preserve context when set in jsonp ajax requet', function(){
$.mockjax({
url: '/jsonp*',
contentType: 'text/json',
proxy: 'test_jsonp.js'
});
window.abcdef123456 = function(json) {};
var cxt = {context: 'context'};
window.abcdef123456 = function(json) {};
var cxt = {context: 'context'};
$.ajax({
url: '/jsonp?callback=?',
jsonpCallback: 'abcdef123456',
dataType: 'jsonp',
error: noErrorCallbackExpected,
context: cxt})
.done(function(){
deepEqual(this, cxt, 'this is equal to context object');
start();
});
$.mockjaxClear();
});
$.ajax({
url: '/jsonp?callback=?',
jsonpCallback: 'abcdef123456',
dataType: 'jsonp',
error: noErrorCallbackExpected,
context: cxt})
.done(function(){
deepEqual(this, cxt, 'this is equal to context object');
start();
});
});
asyncTest('Validate this is the $.ajax object if context is not set', function(){
$.mockjax({
url: '/jsonp*',
contentType: 'text/json',
proxy: 'test_jsonp.js'
});
asyncTest('Validate this is the $.ajax object if context is not set', function(){
$.mockjax({
url: '/jsonp*',
contentType: 'text/json',
proxy: 'test_jsonp.js'
});
window.abcdef123456 = function(json) {};
window.abcdef123456 = function(json) {};
var ret = $.ajax({
url: '/jsonp?callback=?',
jsonpCallback: 'abcdef123456',
dataType: 'jsonp',
error: noErrorCallbackExpected
})
.done(function(){
ok(this.jsonp, '\'this\' is the $.ajax object for this request.');
start();
});
var settings = $.ajaxSettings;
$.mockjaxClear();
});
var ret = $.ajax({
url: '/jsonp?callback=?',
jsonpCallback: 'abcdef123456',
dataType: 'jsonp',
error: noErrorCallbackExpected
})
.done(function(){
ok(this.jsonp, '\'this\' is the $.ajax object for this request.');
start();
});
var settings = $.ajaxSettings;
});
}

@@ -361,14 +672,12 @@

var ID = $.mockjax({
url: '/mockjax_properties',
responseText: "Hello Word"
url: '/mockjax_properties',
responseText: "Hello Word"
});
var xhr = $.ajax({
url: '/mockjax_properties',
complete: function() {}
url: '/mockjax_properties',
complete: function() {}
});
ok($.mockjax.handler(ID).fired, "Sets the mock's fired property to true");
$.mockjaxClear();
});

@@ -378,19 +687,17 @@

asyncTest('Case-insensitive matching for request types', function() {
$.mockjax({
url: '/case_insensitive_match',
type: 'GET',
responseText: 'uppercase type response'
});
$.mockjax({
url: '/case_insensitive_match',
type: 'GET',
responseText: 'uppercase type response'
});
$.ajax({
url: '/case_insensitive_match',
type: 'get',
error: noErrorCallbackExpected,
complete: function(xhr) {
equals(xhr.responseText, 'uppercase type response', 'Request matched regardless of case');
start();
}
});
$.mockjaxClear();
$.ajax({
url: '/case_insensitive_match',
type: 'get',
error: noErrorCallbackExpected,
complete: function(xhr) {
equal(xhr.responseText, 'uppercase type response', 'Request matched regardless of case');
start();
}
});
});

@@ -400,65 +707,59 @@

asyncTest('Exact string', function() {
$.mockjax({
url: '/exact/string',
responseText: 'exact string'
});
$.mockjax({
url: '*',
responseText: 'catch all'
});
$.mockjax({
url: '/exact/string',
responseText: 'exact string'
});
$.mockjax({
url: '*',
responseText: 'catch all'
});
$.ajax({
url: '/exact/string',
error: noErrorCallbackExpected,
complete: function(xhr) {
equals(xhr.responseText, 'exact string', 'Exact string url match');
start();
}
});
$.mockjaxClear();
$.ajax({
url: '/exact/string',
error: noErrorCallbackExpected,
complete: function(xhr) {
equal(xhr.responseText, 'exact string', 'Exact string url match');
start();
}
});
});
test('Wildcard match', 5, function() {
function mock(mockUrl, url, response) {
$.mockjax({
url: mockUrl,
responseText: response
});
$.ajax({
async: false,
url: url,
error: noErrorCallbackExpected,
complete: function(xhr) {
equals(xhr.responseText, response);
}
});
}
mock('/wildcard*w', '/wildcard/123456/w', 'w');
mock('/wildcard*x', '/wildcard/123456/x', 'x');
mock('*y', '/wildcard/123456/y', 'y');
mock('z*', 'z/wildcard/123456', 'z');
mock('/wildcard*aa/second/*/nice', '/wildcard/123456/aa/second/9991231/nice', 'aa');
$.mockjaxClear();
function mock(mockUrl, url, response) {
$.mockjax({
url: mockUrl,
responseText: response
});
$.ajax({
async: false,
url: url,
error: noErrorCallbackExpected,
complete: function(xhr) {
equal(xhr.responseText, response);
}
});
}
mock('/wildcard*w', '/wildcard/123456/w', 'w');
mock('/wildcard*x', '/wildcard/123456/x', 'x');
mock('*y', '/wildcard/123456/y', 'y');
mock('z*', 'z/wildcard/123456', 'z');
mock('/wildcard*aa/second/*/nice', '/wildcard/123456/aa/second/9991231/nice', 'aa');
});
asyncTest('RegEx match', 1, function() {
$.mockjax({
url: /^\/regex-([0-9]+)/i,
responseText: 'regex match'
});
$.mockjax({
url: '*',
responseText: 'catch all'
});
$.mockjax({
url: /^\/regex-([0-9]+)/i,
responseText: 'regex match'
});
$.mockjax({
url: '*',
responseText: 'catch all'
});
$.ajax({
url: '/regex-123456',
error: noErrorCallbackExpected,
complete: function(xhr) {
equals(xhr.responseText, 'regex match', 'RegEx match');
start();
}
});
$.mockjaxClear();
$.ajax({
url: '/regex-123456',
error: noErrorCallbackExpected,
complete: function(xhr) {
equal(xhr.responseText, 'regex match', 'RegEx match');
start();
}
});
});

@@ -468,50 +769,71 @@

asyncTest('Incorrect data matching on request', 1, function() {
$.mockjax({
url: '/response-callback',
data: {
foo: 'bar'
}
});
$.mockjax({
url: '/response-callback',
data: {
foo: 'bar'
}
});
$.ajax({
url: '/response-callback',
error: function() { ok(true, "Error called on bad mock/data matching"); },
data: {
bar: 'baz'
},
success: function(json) {
ok( false, "Success should not be called" );
},
complete: function(xhr) {
start();
}
});
$.ajax({
url: '/response-callback',
error: function() { ok(true, "Error called on bad mock/data matching"); },
data: {
bar: 'baz'
},
success: function(json) {
ok( false, "Success should not be called" );
},
complete: function(xhr) {
start();
}
});
});
$.mockjaxClear();
});
asyncTest('Correct data matching on request', 1, function() {
$.mockjax({
url: '/response-callback',
contentType: 'text/json',
data: {
foo: 'bar'
},
responseText: {}
});
$.mockjax({
url: '/response-callback',
contentType: 'text/json',
data: {
foo: 'bar'
},
responseText: {}
});
$.ajax({
url: '/response-callback',
error: noErrorCallbackExpected,
data: {
foo: 'bar'
},
success: function(json) {
ok( true, "Successfully matched data" );
},
complete: function(xhr) {
start();
}
});
$.ajax({
url: '/response-callback',
error: noErrorCallbackExpected,
data: {
foo: 'bar'
},
success: function(json) {
ok( true, "Successfully matched data" );
},
complete: function(xhr) {
start();
}
});
});
$.mockjaxClear();
asyncTest('Correct data matching on request - request can have additional properties', 1, function() {
$.mockjax({
url: '/response-callback',
data: {
foo: 'bar'
}
});
$.ajax({
url: '/response-callback',
error: function() { ok( false, "Error called on bad mock/data matching"); },
data: {
foo: 'bar',
bar: 'baz'
},
success: function(json) {
ok( true, "Success should not be called" );
},
complete: function(xhr) {
start();
}
});
});

@@ -521,112 +843,164 @@

asyncTest('Correct data matching on request with empty object literals', 1, function() {
$.mockjax({
url: '/response-callback',
contentType: 'text/json',
data: {},
responseText: {}
});
$.mockjax({
url: '/response-callback',
contentType: 'text/json',
data: {},
responseText: {}
});
$.ajax({
url: '/response-callback',
error: noErrorCallbackExpected,
data: {},
success: function(json) {
ok( true, "Successfully matched data" );
},
complete: function(xhr) {
start();
}
});
$.ajax({
url: '/response-callback',
error: noErrorCallbackExpected,
data: {},
success: function(json) {
ok( true, "Successfully matched data" );
},
complete: function(xhr) {
start();
}
});
});
$.mockjaxClear();
asyncTest('Correct matching on request without data and mocks with and without data but same url', 1, function() {
$.mockjax({
url: '/response-callback',
data: {
foo: 'bar'
},
responseText: 'false match'
});
$.mockjax({
url: '/response-callback',
responseText: 'correct match'
});
$.mockjax({
url: '/response-callback',
data: {
bar: 'foo'
},
responseText: 'another false match'
});
$.ajax({
url: '/response-callback',
error: noErrorCallbackExpected,
complete: function(xhr) {
equal(xhr.responseText, 'correct match', 'Matched with correct mock');
start();
}
});
});
// Related issue #68
asyncTest('Incorrect data matching on request with arrays', 1, function() {
$.mockjax({
url: '/response-callback',
contentType: 'text/json',
data: {
values: []
}
});
$.ajax({
url: '/response-callback',
error: function() {
ok( true, "Error callback fired" );
},
data: {
values: [1,2,3]
},
success: function(json) {
ok( false, "Success callback fired" );
},
complete: function(xhr) {
start();
}
});
});
asyncTest('Correct data matching on request with arrays', 1, function() {
$.mockjax({
url: '/response-callback',
contentType: 'text/json',
data: {
values: []
}
});
$.mockjax({
url: '/response-callback',
contentType: 'text/json',
data: {
values: [1,2,3]
},
responseText: {}
});
$.ajax({
url: '/response-callback',
error: function() {
ok( true, "Error callback fired" );
},
data: {
values: [1,2,3]
},
success: function(json) {
ok( false, "Success callback fired" );
},
complete: function(xhr) {
start();
}
});
$.mockjaxClear();
$.ajax({
url: '/response-callback',
error: function(xhr, status) {
ok( false, "Error callback fired" );
},
data: {
values: [1,2,3]
},
success: function(json) {
ok( true, "Success callback fired" );
},
complete: function(xhr) {
start();
}
});
});
asyncTest('Multiple data matching requests', function() {
$.mockjax({
url: '/response-callback',
contentType: 'text/json',
data: {
remote: {
test: function(data) {
return data !== "hello";
}
}
},
responseText: { "yes?": "no" }
});
$.mockjax({
url: '/response-callback',
contentType: 'text/json',
data: {
remote: {
test: function(data) {
return data == "hello";
}
}
},
responseText: { "yes?": "yes" }
});
$.mockjax({
url: '/response-callback',
contentType: 'text/json',
data: {
remote: {
test: function(data) {
return data !== "hello";
}
}
},
responseText: { "yes?": "no" }
});
$.mockjax({
url: '/response-callback',
contentType: 'text/json',
data: {
remote: {
test: function(data) {
return data == "hello";
}
}
},
responseText: { "yes?": "yes" }
});
$.ajax({
url: '/response-callback',
error: function(resp) { ok(true, "Expected error"); },
dataType: 'json',
data: {
remote: "h"
},
success: function(resp) {
deepEqual( resp, {"yes?": "no"}, "correct mock hander" );
},
complete: function(xhr) {
start();
}
});
stop();
$.ajax({
url: '/response-callback',
error: function(resp) {
noErrorCallbackExpected();
},
data: {
remote: "hello"
},
dataType: 'json',
success: function(resp) {
deepEqual( resp, {"yes?": "yes"}, "correct mock hander" );
},
complete: function(xhr) {
start();
}
});
$.mockjaxClear();
$.ajax({
url: '/response-callback',
error: function(resp) { ok(true, "Expected error"); },
dataType: 'json',
data: {
remote: "h"
},
success: function(resp) {
deepEqual( resp, {"yes?": "no"}, "correct mock hander" );
},
complete: function(xhr) {
start();
}
});
stop();
$.ajax({
url: '/response-callback',
error: function(resp) {
noErrorCallbackExpected();
},
data: {
remote: "hello"
},
dataType: 'json',
success: function(resp) {
deepEqual( resp, {"yes?": "yes"}, "correct mock hander" );
},
complete: function(xhr) {
start();
}
});
});

@@ -636,28 +1010,26 @@

asyncTest('Null matching on request', 1, function() {
$.mockjax({
url: '/response-callback',
contentType: 'text/json',
data: {
foo: 'bar',
bar: null
},
responseText: {}
});
$.mockjax({
url: '/response-callback',
contentType: 'text/json',
data: {
foo: 'bar',
bar: null
},
responseText: {}
});
$.ajax({
url: '/response-callback',
error: noErrorCallbackExpected,
data: {
foo: 'bar',
bar: null
},
success: function(json) {
ok( true, "Successfully matched data that contained null values" );
},
complete: function(xhr) {
start();
}
});
$.mockjaxClear();
$.ajax({
url: '/response-callback',
error: noErrorCallbackExpected,
data: {
foo: 'bar',
bar: null
},
success: function(json) {
ok( true, "Successfully matched data that contained null values" );
},
complete: function(xhr) {
start();
}
});
});

@@ -669,82 +1041,78 @@

asyncTest('Response returns text', function() {
$.mockjax({
url: '/text',
contentType: 'text/plain',
responseText: 'just text'
});
$.ajax({
url: '/text',
dataType: 'text',
error: noErrorCallbackExpected,
complete: function(xhr) {
equals(xhr.getResponseHeader('Content-Type'), 'text/plain', 'Content type of text/plain');
$.mockjax({
url: '/text',
contentType: 'text/plain',
responseText: 'just text'
});
$.ajax({
url: '/text',
dataType: 'text',
error: noErrorCallbackExpected,
complete: function(xhr) {
equal(xhr.getResponseHeader('Content-Type'), 'text/plain', 'Content type of text/plain');
start();
}
});
$.mockjaxClear();
start();
}
});
});
asyncTest('Response returns html', function() {
$.mockjax({
url: '/html',
contentType: 'text/html',
responseText: '<div>String</div>'
});
$.ajax({
url: '/html',
dataType: 'html',
success: function(data) {
equals(data, '<div>String</div>', 'HTML String matches');
},
error: noErrorCallbackExpected,
complete: function(xhr) {
equals(xhr.getResponseHeader('Content-Type'), 'text/html', 'Content type of text/html');
start();
}
});
$.mockjaxClear();
$.mockjax({
url: '/html',
contentType: 'text/html',
responseText: '<div>String</div>'
});
$.ajax({
url: '/html',
dataType: 'html',
success: function(data) {
equal(data, '<div>String</div>', 'HTML String matches');
},
error: noErrorCallbackExpected,
complete: function(xhr) {
equal(xhr.getResponseHeader('Content-Type'), 'text/html', 'Content type of text/html');
start();
}
});
});
asyncTest('Response returns json', function() {
$.mockjax({
url: '/json',
contentType: 'text/json',
responseText: { "foo" : "bar", "baz" : { "car" : "far" } }
});
$.ajax({
url: '/json',
dataType: 'json',
success: function(json) {
deepEqual(json, { "foo" : "bar", "baz" : { "car" : "far" } }, 'JSON Object matches');
},
error: noErrorCallbackExpected,
complete: function(xhr) {
equals(xhr.getResponseHeader('Content-Type'), 'text/json', 'Content type of text/json');
start();
}
});
$.mockjaxClear();
$.mockjax({
url: '/json',
contentType: 'text/json',
responseText: { "foo" : "bar", "baz" : { "car" : "far" } }
});
$.ajax({
url: '/json',
dataType: 'json',
success: function(json) {
deepEqual(json, { "foo" : "bar", "baz" : { "car" : "far" } }, 'JSON Object matches');
},
error: noErrorCallbackExpected,
complete: function(xhr) {
equal(xhr.getResponseHeader('Content-Type'), 'text/json', 'Content type of text/json');
start();
}
});
});
asyncTest('Response returns jsonp', 3, function() {
$.mockjax({
url: '/jsonp*',
contentType: 'text/json',
proxy: 'test_jsonp.js'
});
window.abcdef123456 = function(json) {
ok( true, 'JSONP Callback executed');
deepEqual(json, { "data" : "JSONP is cool" });
};
$.mockjax({
url: '/jsonp*',
contentType: 'text/json',
proxy: 'test_jsonp.js'
});
window.abcdef123456 = function(json) {
ok( true, 'JSONP Callback executed');
deepEqual(json, { "data" : "JSONP is cool" });
};
var ret = $.ajax({
url: '/jsonp?callback=?',
jsonpCallback: 'abcdef123456',
dataType: 'jsonp',
error: noErrorCallbackExpected,
complete: function(xhr) {
equals(xhr.getResponseHeader('Content-Type'), 'text/json', 'Content type of text/json');
start();
}
});
$.mockjaxClear();
var ret = $.ajax({
url: '/jsonp?callback=?',
jsonpCallback: 'abcdef123456',
dataType: 'jsonp',
error: noErrorCallbackExpected,
complete: function(xhr) {
equal(xhr.getResponseHeader('Content-Type'), 'text/json', 'Content type of text/json');
start();
}
});
});

@@ -754,198 +1122,252 @@

asyncTest('Response returns jsonp and return value from ajax is a promise if supported', function() {
window.rquery = /\?/;
window.rquery = /\?/;
$.mockjax({
url:"http://api*",
responseText:{
success:true,
ids:[21327211]
},
dataType:"jsonp",
contentType: 'text/json'
});
$.mockjax({
url:"http://api*",
responseText:{
success:true,
ids:[21327211]
},
dataType:"jsonp",
contentType: 'text/json'
});
var promiseObject = $.ajax({
url:"http://api.twitter.com/1/followers/ids.json?screen_name=test_twitter_user",
dataType:"jsonp"
});
var promiseObject = $.ajax({
url:"http://api.twitter.com/1/followers/ids.json?screen_name=test_twitter_user",
dataType:"jsonp"
});
if (jQuery.Deferred) {
ok(promiseObject.done && promiseObject.fail, "Got Promise methods");
promiseObject.then(function(){
ok(true, "promise object then is executed");
});
} else {
ok(true, "No deferred support, passing as succesful");
}
if (jQuery.Deferred) {
ok(promiseObject.done && promiseObject.fail, "Got Promise methods");
promiseObject.then(function(){
ok(true, "promise object then is executed");
});
} else {
ok(true, "No deferred support, passing as succesful");
}
start();
start();
});
asyncTest('Response executes script', function() {
$.mockjax({
url: '/script',
contentType: 'text/plain',
proxy: 'test_script.js'
});
$.mockjax({
url: '/script',
contentType: 'text/plain',
proxy: 'test_script.js'
});
window.TEST_SCRIPT_VAR = 0;
$.ajax({
url: '/script',
dataType: 'script',
error: noErrorCallbackExpected,
complete: function(xhr) {
equals(window.TEST_SCRIPT_VAR, 1, 'Script executed');
equals(xhr.getResponseHeader('Content-Type'), 'text/plain', 'Content type of text/plain');
window.TEST_SCRIPT_VAR = 0;
$.ajax({
url: '/script',
dataType: 'script',
error: noErrorCallbackExpected,
complete: function(xhr) {
equal(window.TEST_SCRIPT_VAR, 1, 'Script executed');
equal(xhr.getResponseHeader('Content-Type'), 'text/plain', 'Content type of text/plain');
start();
}
});
$.mockjaxClear();
start();
}
});
});
asyncTest('Grouping deferred responses, if supported', function() {
window.rquery = /\?/;
window.rquery = /\?/;
$.mockjax({
url:"http://api*",
responseText:{
success:true,
ids:[21327211]
},
dataType:"jsonp",
contentType: 'text/json'
});
$.mockjax({
url:"http://api*",
responseText:{
success:true,
ids:[21327211]
},
dataType:"jsonp",
contentType: 'text/json'
});
var req1 = $.ajax({
url:"http://api.twitter.com/1/followers/ids.json?screen_name=test_twitter_user",
dataType:"jsonp"
});
var req2 = $.ajax({
url:"http://api.twitter.com/1/followers/ids.json?screen_name=test_twitter_user",
dataType:"jsonp"
});
var req3 = $.ajax({
url:"http://api.twitter.com/1/followers/ids.json?screen_name=test_twitter_user",
dataType:"jsonp"
});
var req1 = $.ajax({
url:"http://api.twitter.com/1/followers/ids.json?screen_name=test_twitter_user",
dataType:"jsonp"
});
var req2 = $.ajax({
url:"http://api.twitter.com/1/followers/ids.json?screen_name=test_twitter_user",
dataType:"jsonp"
});
var req3 = $.ajax({
url:"http://api.twitter.com/1/followers/ids.json?screen_name=test_twitter_user",
dataType:"jsonp"
});
if (jQuery.Deferred) {
$.when(req1, req2, req3).done(function(a, b, c) {
ok(true, "Successfully grouped deferred responses");
});
} else {
ok(true, "No deferred support, passing as succesful");
}
if (jQuery.Deferred) {
$.when(req1, req2, req3).done(function(a, b, c) {
ok(true, "Successfully grouped deferred responses");
});
} else {
ok(true, "No deferred support, passing as succesful");
}
start();
start();
});
asyncTest('Response returns parsed XML', function() {
$.mockjax({
url: '/xml',
contentType: 'text/xml',
responseXML: '<document>String</document>'
});
$.ajax({
url: '/xml',
dataType: 'xml',
success: function(xmlDom) {
ok( jQuery.isXMLDoc( xmlDom ), 'Data returned is an XML DOM');
},
error: noErrorCallbackExpected,
complete: function(xhr, error) {
ok(true, 'Error: ' + error);
equals(xhr.getResponseHeader('Content-Type'), 'text/xml', 'Content type of text/xml');
start();
}
});
$.mockjaxClear();
$.mockjax({
url: '/xml',
contentType: 'text/xml',
responseXML: '<document>String</document>'
});
$.ajax({
url: '/xml',
dataType: 'xml',
success: function(xmlDom) {
ok( jQuery.isXMLDoc( xmlDom ), 'Data returned is an XML DOM');
},
error: noErrorCallbackExpected,
complete: function(xhr, error) {
ok(true, 'Error: ' + error);
equal(xhr.getResponseHeader('Content-Type'), 'text/xml', 'Content type of text/xml');
start();
}
});
});
module('Connection Simulation', {
setup: function() {
$.mockjax({
url: '/delay',
responseTime: 150
});
$.mockjax({
url: '*',
responseText: '',
responseTime: 50
});
},
teardown: function() {
$.mockjaxClear();
}
setup: function() {
this.variableDelayMin = 100;
this.variableDelayMax = 300;
this.processingDuration = 30;
$.mockjax({
url: '/delay',
responseTime: 150
});
$.mockjax({
url: 'http://foobar.com/jsonp-delay?callback=?',
contentType: 'text/json',
proxy: 'test_jsonp.js',
responseTime: 150,
responseText: "{}"
});
$.mockjax({
url: '/variable-delay',
responseTime: [this.variableDelayMin, this.variableDelayMax]
});
$.mockjax({
url: '*',
responseText: '',
responseTime: 50
});
}
});
asyncTest('Async test', function() {
var order = [];
$.ajax({
async: true,
url: '/',
success: function() {
order.push('b');
},
error: noErrorCallbackExpected,
complete: function() {
deepEqual(order, ['a', 'b'], 'Order of execution correct, 2');
start();
}
});
order.push('a');
deepEqual(order, ['a'], 'Order of execution correct, 1');
var order = [];
$.ajax({
async: true,
url: '/',
success: function() {
order.push('b');
},
error: noErrorCallbackExpected,
complete: function() {
deepEqual(order, ['a', 'b'], 'Order of execution correct, 2');
start();
}
});
order.push('a');
deepEqual(order, ['a'], 'Order of execution correct, 1');
});
test('Sync test', function() {
var order = [];
$.ajax({
async: false,
url: '/',
success: function() {
order.push('b');
deepEqual(order, ['b'], 'Order of execution correct, 1');
},
error: noErrorCallbackExpected
});
order.push('a');
deepEqual(order, ['b', 'a'], 'Order of execution correct, 2');
var order = [];
$.ajax({
async: false,
url: '/',
success: function() {
order.push('b');
deepEqual(order, ['b'], 'Order of execution correct, 1');
},
error: noErrorCallbackExpected
});
order.push('a');
deepEqual(order, ['b', 'a'], 'Order of execution correct, 2');
});
asyncTest('Response time simulation and latency', function() {
var executed = 0, ts = new Date();
$.ajax({
url: '/delay',
complete: function() {
var delay = ((new Date()) - ts);
ok( delay >= 150, 'Correct delay simulation (' + delay + ')' );
equals( executed, 1, 'Callback execution order correct');
start();
}
});
setTimeout(function() {
ok( executed == 0, 'No premature callback execution');
executed++;
}, 30);
var executed = 0, ts = new Date();
$.ajax({
url: '/delay',
complete: function() {
var delay = ((new Date()) - ts);
ok( delay >= 150, 'Correct delay simulation (' + delay + ')' );
equal( executed, 1, 'Callback execution order correct');
start();
}
});
setTimeout(function() {
ok( executed == 0, 'No premature callback execution');
executed++;
}, 30);
});
asyncTest('Response time with jsonp', function() {
var executed = false, ts = new Date();
$.ajax({
url: 'http://foobar.com/jsonp-delay?callback=?',
dataType: 'jsonp',
complete: function() {
var delay = ((new Date()) - ts);
ok( delay >= 150, 'Correct delay simulation (' + delay + ')' );
ok( executed, 'Callback execution order correct');
start();
}
});
setTimeout(function() {
ok( executed === false, 'No premature callback execution');
executed = true;
}, 30);
});
asyncTest('Response time with min and max values', function () {
var executed = 0,
that = this,
ts = new Date();
$.ajax({
url: '/variable-delay',
complete: function () {
var delay = ((new Date()) - ts);
ok( delay >= that.variableDelayMin && delay <= (that.variableDelayMax + that.processingDuration), 'Variable delay within min and max, delay was ' + delay);
equal( executed, 1, 'Callback execution order correct');
start();
}
});
setTimeout(function () {
ok (executed == 0, 'No premature callback execution');
executed++;
}, 30);
});
module('Headers');
asyncTest('headers can be inspected via setRequestHeader()', function() {
var mock;
$(document).ajaxSend(function(event, xhr, ajaxSettings) {
xhr.setRequestHeader('X-CSRFToken', '<this is a token>');
});
mock = $.mockjax({
url: '/inspect-headers',
response: function(settings) {
var key;
if (typeof this.headers['X-Csrftoken'] !== 'undefined') {
key = 'X-Csrftoken'; // bugs in jquery 1.5
} else {
key = 'X-CSRFToken';
}
equals(this.headers[key], '<this is a token>');
$.mockjaxClear(mock);
start();
}
});
$.ajax({
url: '/inspect-headers',
complete: function() {}
});
expect(1);
$(document).ajaxSend(function(event, xhr, ajaxSettings) {
xhr.setRequestHeader('X-CSRFToken', '<this is a token>');
});
$.mockjax( function ( requestSettings ) {
if ( "/inspect-headers" == requestSettings.url ) {
return {
response: function(origSettings) {
if (typeof requestSettings.headers['X-Csrftoken'] !== 'undefined') {
key = 'X-Csrftoken'; // bugs in jquery 1.5
} else {
key = 'X-CSRFToken';
}
equal(requestSettings.headers[key], '<this is a token>');
this.responseText = {};
}
};
}
});
$.ajax({
url: '/inspect-headers',
complete: function() {
start();
}
});
});

@@ -956,165 +1378,184 @@

asyncTest('Response status callback', function() {
$.mockjax({
url: '/response-callback',
status: 403
});
$.mockjax({
url: '/response-callback',
status: 403
});
$.ajax({
url: '/response-callback',
error: function(){ ok(true, "error callback was called"); },
complete: function(xhr) {
equals(xhr.status, 403, 'response status matches');
start();
}
});
$.mockjaxClear();
$.ajax({
url: '/response-callback',
error: function(){ ok(true, "error callback was called"); },
complete: function(xhr) {
equal(xhr.status, 403, 'response status matches');
start();
}
});
});
// SETTING THE CONTENT-TYPE
asyncTest('Setting the content-type', function() {
$.mockjax({
url: '/response-callback',
contentType: 'text/json',
responseText: {
foo: 'bar'
}
});
$.mockjax({
url: '/response-callback',
contentType: 'text/json',
responseText: {
foo: 'bar'
}
});
$.ajax({
url: '/response-callback',
dataType: 'json',
error: function(){ ok(false, "error callback was called"); },
success: function(json) {
deepEqual(json, { "foo" : "bar" }, 'JSON Object matches');
},
complete: function(xhr) {
equals(xhr.getResponseHeader('Content-Type'), 'text/json', 'Content type of json');
start();
}
});
$.mockjaxClear();
$.ajax({
url: '/response-callback',
dataType: 'json',
error: function(){ ok(false, "error callback was called"); },
success: function(json) {
deepEqual(json, { "foo" : "bar" }, 'JSON Object matches');
},
complete: function(xhr) {
equal(xhr.getResponseHeader('Content-Type'), 'text/json', 'Content type of json');
start();
}
});
});
// SETTING ADDITIONAL HTTP RESPONSE HEADERS
asyncTest('Setting additional HTTP response headers', function() {
$.mockjax({
url: '/response-callback',
headers: {
'X-Must-Exist': 'yes'
},
responseText: 'done'
});
$.mockjax({
url: '/response-callback',
headers: {
'X-Must-Exist': 'yes'
},
responseText: 'done'
});
$.ajax({
url: '/response-callback',
error: function(){ ok(false, "error callback was called"); },
success: function(response) {
equals( response, "done", "Response text matches" );
},
complete: function(xhr) {
equals( xhr.getResponseHeader( "X-Must-Exist" ), "yes", "Header matches" );
start();
}
});
$.ajax({
url: '/response-callback',
error: function(){ ok(false, "error callback was called"); },
success: function(response) {
equal( response, "done", "Response text matches" );
},
complete: function(xhr) {
equal( xhr.getResponseHeader( "X-Must-Exist" ), "yes", "Header matches" );
start();
}
});
});
$.mockjaxClear();
asyncTest('Testing that request headers do not overwrite response headers', function() {
$.mockjax({
url: '/restful/fortune',
headers : {
prop: 'response'
}
});
var returnedXhr = $.ajax({
type: 'GET',
url: '/restful/fortune',
headers : {
prop : 'request'
},
success: function(res, status, xhr) {
if (xhr) {
equal(xhr && xhr.getResponseHeader('prop'), 'response', 'response header should be correct');
} else {
equal(returnedXhr.getResponseHeader('prop'), 'response', 'response header should be correct');
}
start();
},
error: function() {
ok(false, "should not result in error");
start();
}
});
});
// FORCE SIMULATION OF SERVER TIMEOUTS
asyncTest('Forcing timeout', function() {
$.mockjax({
url: '/response-callback',
responseText: 'done',
isTimeout: true
});
$.mockjax({
url: '/response-callback',
responseText: 'done',
isTimeout: true
});
$.ajax({
url: '/response-callback',
error: function(xhr) {
ok(true, "error callback was called");
},
success: function(response) {
ok(false, "should not be be successful");
},
complete: function(xhr) {
start();
}
});
$.ajax({
url: '/response-callback',
error: function(xhr, textStatus, errorThrown ) {
equal( textStatus, "timeout", "Text status is equal to timeout" );
ok( errorThrown !== "OK", "errorThrown is undefined or timeout, not OK" );
ok(true, "error callback was called");
},
success: function(response) {
ok(false, "should not be be successful");
},
complete: function(xhr) {
start();
}
});
});
$.mockjaxClear();
});
// FORCE SIMULATION OF SERVER TIMEOUTS WITH PROMISES
if(jQuery.Deferred) {
asyncTest('Forcing timeout with Promises', function() {
$.mockjax({
url: '/response-callback',
isTimeout: true,
});
asyncTest('Forcing timeout with Promises', function() {
$.mockjax({
url: '/response-callback',
isTimeout: true
});
var request = $.ajax({
url: '/response-callback'
});
var request = $.ajax({
url: '/response-callback'
});
request.done(function(xhr) {
ok(false, "Should not be successful");
});
request.done(function(xhr) {
ok(false, "Should not be successful");
});
request.fail(function(response) {
ok(true, "error callback was called");
});
request.fail(function(response) {
ok(true, "error callback was called");
});
request.complete(function(xhr) {
start();
});
$.mockjaxClear();
});
request.complete(function(xhr) {
start();
});
});
}
// DYNAMICALLY GENERATING MOCK DEFINITIONS
asyncTest('Dynamic mock definition', function() {
$.mockjax( function( settings ) {
var service = settings.url.match(/\/users\/(.*)$/);
if ( service ) {
return {
proxy: 'test_proxy.json'
}
}
});
$.mockjax( function( settings ) {
var service = settings.url.match(/\/users\/(.*)$/);
if ( service ) {
return {
proxy: 'test_proxy.json'
}
}
});
$.ajax({
url: '/users/test',
dataType: 'json',
error: noErrorCallbackExpected,
success: function(json) {
ok(json && json.proxy, 'Proxy request succeeded');
},
complete: function(xhr) {
start();
}
});
$.mockjaxClear();
$.ajax({
url: '/users/test',
dataType: 'json',
error: noErrorCallbackExpected,
success: function(json) {
ok(json && json.proxy, 'Proxy request succeeded');
},
complete: function(xhr) {
start();
}
});
});
// DYNAMICALLY GENERATING MOCK RESPONSES
asyncTest('Dynamic mock response generation', function() {
$.mockjax({
url: '/response-callback',
response: function( settings ) {
this.responseText = { currentTime: 'now: ' + new Date() };
}
});
$.mockjax({
url: '/response-callback',
response: function( settings ) {
this.responseText = { currentTime: 'now: ' + new Date() };
}
});
$.ajax({
url: '/response-callback',
dataType: 'json',
error: noErrorCallbackExpected,
success: function(json) {
equals( typeof json.currentTime, 'string', 'Dynamic response succeeded');
},
complete: function(xhr) {
start();
}
});
$.mockjaxClear();
$.ajax({
url: '/response-callback',
dataType: 'json',
error: noErrorCallbackExpected,
success: function(json) {
equal( typeof json.currentTime, 'string', 'Dynamic response succeeded');
},
complete: function(xhr) {
start();
}
});
});

@@ -1125,76 +1566,180 @@

asyncTest( 'Test bug fix for $.mockjaxSettings', function() {
$.mockjaxSettings.headers = {
"content-type": "text/plain",
etag: "IJF@H#@923uf8023hFO@I#H#"
};
$.mockjaxSettings.headers = {
"content-type": "text/plain",
etag: "IJF@H#@923uf8023hFO@I#H#"
};
$.mockjax({
url: '/get/property',
type: 'GET',
response: function(settings) {
this.responseText = { foo: "bar" };
}
});
$.mockjax({
url: '/get/property',
type: 'GET',
response: function(settings) {
this.responseText = { foo: "bar" };
}
});
$.ajax({
url: '/get/property',
success: function(data) {
deepEqual( $.mockjaxSettings.headers, {
"content-type": "text/plain",
etag: "IJF@H#@923uf8023hFO@I#H#"
}, "Should not change the default headers.");
},
complete: function() {
start();
}
});
$.mockjaxClear();
$.ajax({
url: '/get/property',
success: function(data) {
deepEqual( $.mockjaxSettings.headers, {
"content-type": "text/plain",
etag: "IJF@H#@923uf8023hFO@I#H#"
}, "Should not change the default headers.");
},
complete: function() {
start();
}
});
});
asyncTest("Preserve responseText inside a response function when using jsonp and a success callback", function(){
$.mockjax({
url: "http://some/fake/jsonp/endpoint",
// The following line works...
// responseText: [{ "data" : "JSONP is cool" }]
// But doesn't not work when setting this.responseText in response
response: function() {
this.responseText = [{ "data" : "JSONP is cool" }];
}
});
$.mockjax({
url: "http://some/fake/jsonp/endpoint",
// The following line works...
// responseText: [{ "data" : "JSONP is cool" }]
// But doesn't not work when setting this.responseText in response
response: function() {
this.responseText = [{ "data" : "JSONP is cool" }];
}
});
$.ajax({
url: "http://some/fake/jsonp/endpoint",
dataType: "jsonp",
success: function(data) {
deepEqual(data, [{ "data" : "JSONP is cool" }]);
start();
}
});
$.mockjaxClear();
$.ajax({
url: "http://some/fake/jsonp/endpoint",
dataType: "jsonp",
success: function(data) {
deepEqual(data, [{ "data" : "JSONP is cool" }]);
start();
}
});
});
asyncTest('Custom status when using proxy', function() {
$.mockjax({
url: '/response-callback',
status: 409,
proxy: 'test_proxy.json'
});
$.mockjax({
url: '/response-callback',
status: 409,
proxy: 'test_proxy.json'
});
$.ajax({
url: '/response-callback',
error: function(){ ok(true, "error callback was called"); },
success: function(json) {
ok( false, "Success should not be called" );
},
complete: function(xhr) {
equals(xhr.status, 409, 'response status matches');
start();
}
});
$.ajax({
url: '/response-callback',
error: function(){ ok(true, "error callback was called"); },
success: function(json) {
ok( false, "Success should not be called" );
},
complete: function(xhr) {
equal(xhr.status, 409, 'response status matches');
start();
}
});
});
$.mockjaxClear();
asyncTest('Call onAfterSuccess after success has been called', function() {
var onAfterSuccessCalled = false;
var successCalled = false;
$.mockjax({
url: '/response-callback',
onAfterSuccess: function() {
onAfterSuccessCalled = true;
equal(successCalled, true, 'success was not yet called');
}
});
$.ajax({
url: '/response-callback',
success: function() {
successCalled = true;
}
});
setTimeout(function() {
equal(onAfterSuccessCalled, true, 'onAfterSuccess was not called');
start();
}, 100);
});
asyncTest('Call onAfterError after error has been called', function() {
var onAfterErrorCalled = false;
var errorCalled = false;
$.mockjax({
url: '/response-callback-bad',
status: 500,
onAfterError: function() {
onAfterErrorCalled = true;
equal(errorCalled, true, 'error was not yet called');
}
});
$.ajax({
url: '/response-callback-bad',
error: function() {
errorCalled = true;
}
});
setTimeout(function() {
equal(onAfterErrorCalled, true, 'onAfterError was not called');
start();
}, 100);
});
asyncTest('Call onAfterComplete after complete has been called', function() {
var onAfterCompleteCalled = false;
var completeCalled = false;
$.mockjax({
url: '/response-callback',
onAfterComplete: function() {
onAfterCompleteCalled = true;
equal(completeCalled, true, 'complete was not yet called');
}
});
$.ajax({
url: '/response-callback',
complete: function() {
completeCalled = true;
}
});
setTimeout(function() {
equal(onAfterCompleteCalled, true, 'onAfterComplete was not called');
start();
}, 100);
});
test('Test for bug #95: undefined responseText on success', function() {
expect(2);
var expected = { status: 'success', fortune: 'Are you a turtle?' };
$.mockjax({
url: 'test/something',
responseText: { status: 'success', fortune: 'Are you a turtle?' }
});
$.ajax({
type: 'GET',
url: 'test/something',
async: false,
success: function(data) {
// Before jQuery 1.5 the response is a stringified version of the
// json data unless the 'dataType' option is set to "json"
var expectedResult = expected;
if (compareSemver($().jquery, "1.5", "<")) {
expectedResult = JSON.stringify(expected);
}
deepEqual(data, expectedResult, 'responseText is correct JSON object');
}
});
$.ajax({
type: 'GET',
url: 'test/something',
dataType: 'json',
async: false,
success: function(data) {
deepEqual(data, expected, 'responseText is correct JSON object');
}
});
});
/*

@@ -1204,3 +1749,3 @@ var id = $.mockjax({

});
$.mockjaxClear(id);
$.mockjax.clear(id);
*/

@@ -1210,55 +1755,55 @@

(function($) {
$(function() {
$.ajax({
url: 'test.json',
success: function(data) {
$('ul').append('<li>test.json: completed (' + data.test + ')</li>');
}
});
$(function() {
$.ajax({
url: 'test.json',
success: function(data) {
$('ul').append('<li>test.json: completed (' + data.test + ')</li>');
}
});
$.mockjax({
url: 'test.json',
contentType: 'text/json',
responseText: { "test": "mock message" }
});
$.mockjax({
url: 'test.json',
contentType: 'text/json',
responseText: { "test": "mock message" }
});
$.ajax({
url: 'test.json',
dataType: 'json',
success: function(data) {
$('ul').append('<li>test.json: completed (' + data.test + ')</li>');
},
error: function(xhr, status, error) {
alert('error: ' + status + ' ' + error);
},
complete: function() {
}
});
$.ajax({
url: 'test.json',
dataType: 'json',
success: function(data) {
$('ul').append('<li>test.json: completed (' + data.test + ')</li>');
},
error: function(xhr, status, error) {
alert('error: ' + status + ' ' + error);
},
complete: function() {
}
});
$.mockjax({
url: 'http://google.com',
responseText: 'alert("Hello world");'
});
$.mockjax({
url: 'http://google.com',
responseText: 'alert("Hello world");'
});
$.mockjax({
url: 'http://another-cross-domain.com',
responseText: function() {
alert("Get script mock");
}
});
$.mockjax({
url: 'http://another-cross-domain.com',
responseText: function() {
alert("Get script mock");
}
});
$.ajax({
url: 'http://google.com',
dataType: 'script',
success: function(data) {
$('ul').append('<li>script: completed (' + data.test + ')</li>');
},
error: function(xhr, status, error) {
alert('error: ' + status + ' ' + error);
},
complete: function() {
}
});
});
$.ajax({
url: 'http://google.com',
dataType: 'script',
success: function(data) {
$('ul').append('<li>script: completed (' + data.test + ')</li>');
},
error: function(xhr, status, error) {
alert('error: ' + status + ' ' + error);
},
complete: function() {
}
});
});
})(jQuery);
*/

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc