2 * A Plugin providing simple and flexible mocking of ajax requests and responses
5 * Home: https://github.com/jakerella/jquery-mockjax
6 * Copyright (c) 2016 Jordan Kasper, formerly appendTo;
7 * NOTE: This repository was taken over by Jordan Kasper (@jakerella) October, 2014
9 * Dual licensed under the MIT or GPL licenses.
10 * http://opensource.org/licenses/MIT OR http://www.gnu.org/licenses/gpl-2.0.html
12 (function(root, factory) {
15 // AMDJS module definition
16 if ( typeof define === 'function' && define.amd && define.amd.jQuery ) {
17 define(['jquery'], function($) {
18 return factory($, root);
21 // CommonJS module definition
22 } else if ( typeof exports === 'object') {
24 // NOTE: To use Mockjax as a Node module you MUST provide the factory with
25 // a valid version of jQuery and a window object (the global scope):
26 // var mockjax = require('jquery.mockjax')(jQuery, window);
28 module.exports = factory;
30 // Global jQuery in web browsers
32 return factory(root.jQuery || root.$, root);
34 }(this, function($, window) {
40 unmockedAjaxCalls = [],
41 CALLBACK_REGEX = /=\?(&|$)/,
42 jsc = (new Date()).getTime(),
43 DEFAULT_RESPONSE_TIME = 500;
45 // Parse the given XML string.
46 function parseXML(xml) {
47 if ( window.DOMParser === undefined && window.ActiveXObject ) {
48 window.DOMParser = function() { };
49 DOMParser.prototype.parseFromString = function( xmlString ) {
50 var doc = new ActiveXObject('Microsoft.XMLDOM');
52 doc.loadXML( xmlString );
58 var xmlDoc = ( new DOMParser() ).parseFromString( xml, 'text/xml' );
59 if ( $.isXMLDoc( xmlDoc ) ) {
60 var err = $('parsererror', xmlDoc);
61 if ( err.length === 1 ) {
62 throw new Error('Error: ' + $(xmlDoc).text() );
65 throw new Error('Unable to parse XML');
69 var msg = ( e.name === undefined ? e : e.name + ': ' + e.message );
70 $(document).trigger('xmlParseError', [ msg ]);
75 // Check if the data field on the mock handler and the request match. This
76 // can be used to restrict a mock handler to being used only when a certain
77 // set of data is passed to it.
78 function isMockDataEqual( mock, live ) {
79 logger.debug( mock, ['Checking mock data against request data', mock, live] );
82 if ( $.isFunction(mock) ) {
86 // Test for situations where the data is a querystring (not an object)
87 if (typeof live === 'string') {
88 // Querystring may be a regex
89 if ($.isFunction( mock.test )) {
90 return mock.test(live);
91 } else if (typeof mock === 'object') {
92 live = getQueryParams(live);
98 $.each(mock, function(k) {
99 if ( live[k] === undefined ) {
103 if ( typeof live[k] === 'object' && live[k] !== null ) {
104 if ( identical && $.isArray( live[k] ) ) {
105 identical = $.isArray( mock[k] ) && live[k].length === mock[k].length;
107 identical = identical && isMockDataEqual(mock[k], live[k]);
109 if ( mock[k] && $.isFunction( mock[k].test ) ) {
110 identical = identical && mock[k].test(live[k]);
112 identical = identical && ( mock[k] === live[k] );
121 function getQueryParams(queryString) {
122 var i, l, param, tmp,
124 params = String(queryString).split(/&/);
126 for (i=0, l=params.length; i<l; ++i) {
129 param = decodeURIComponent(param.replace(/\+/g, ' '));
130 param = param.split(/=/);
132 // Can't parse this one, so let it go?
136 if (paramsObj[param[0]]) {
137 // this is an array query param (more than one entry in query)
138 if (!paramsObj[param[0]].splice) {
139 // if not already an array, make it one
140 tmp = paramsObj[param[0]];
141 paramsObj[param[0]] = [];
142 paramsObj[param[0]].push(tmp);
144 paramsObj[param[0]].push(param[1]);
146 paramsObj[param[0]] = param[1];
150 logger.debug( null, ['Getting query params from string', queryString, paramsObj] );
155 // See if a mock handler property matches the default settings
156 function isDefaultSetting(handler, property) {
157 return handler[property] === $.mockjaxSettings[property];
160 // Check the given handler should mock the given request
161 function getMockForRequest( handler, requestSettings ) {
162 // If the mock was registered with a function, let the function decide if we
163 // want to mock this request
164 if ( $.isFunction(handler) ) {
165 return handler( requestSettings );
168 // Inspect the URL of the request and check if the mock handler's url
169 // matches the url for this ajax request
170 if ( $.isFunction(handler.url.test) ) {
171 // The user provided a regex for the url, test it
172 if ( !handler.url.test( requestSettings.url ) ) {
177 // Apply namespace prefix to the mock handler's url.
178 var namespace = handler.namespace || $.mockjaxSettings.namespace;
180 var namespacedUrl = [namespace, handler.url].join('/');
181 namespacedUrl = namespacedUrl.replace(/(\/+)/g, '/');
182 handler.url = namespacedUrl;
185 // Look for a simple wildcard '*' or a direct URL match
186 var star = handler.url.indexOf('*');
187 if (handler.url !== requestSettings.url && star === -1 ||
188 !new RegExp(handler.url.replace(/[-[\]{}()+?.,\\^$|#\s]/g, '\\$&').replace(/\*/g, '.+')).test(requestSettings.url)) {
193 // Inspect the request headers submitted
194 if ( handler.requestHeaders ) {
195 //No expectation for headers, do not mock this request
196 if (requestSettings.headers === undefined) {
199 var headersMismatch = false;
200 $.each(handler.requestHeaders, function(key, value) {
201 var v = requestSettings.headers[key];
203 headersMismatch = true;
207 //Headers do not match, do not mock this request
208 if (headersMismatch) {
214 // Inspect the data submitted in the request (either POST body or GET query string)
215 if ( handler.data ) {
216 if ( !requestSettings.data || !isMockDataEqual(handler.data, requestSettings.data) ) {
217 // They're not identical, do not mock this request
221 // Inspect the request type
222 if ( handler && handler.type &&
223 handler.type.toLowerCase() !== requestSettings.type.toLowerCase() ) {
224 // The request type doesn't match (GET vs. POST)
231 function isPosNum(value) {
232 return typeof value === 'number' && value >= 0;
235 function parseResponseTimeOpt(responseTime) {
236 if ($.isArray(responseTime) && responseTime.length === 2) {
237 var min = responseTime[0];
238 var max = responseTime[1];
239 if(isPosNum(min) && isPosNum(max)) {
240 return Math.floor(Math.random() * (max - min)) + min;
242 } else if(isPosNum(responseTime)) {
245 return DEFAULT_RESPONSE_TIME;
248 // Process the xhr objects send operation
249 function _xhrSend(mockHandler, requestSettings, origSettings) {
250 logger.debug( mockHandler, ['Sending fake XHR request', mockHandler, requestSettings, origSettings] );
252 // This is a substitute for < 1.4 which lacks $.proxy
253 var process = (function(that) {
256 // The request has returned
257 this.status = mockHandler.status;
258 this.statusText = mockHandler.statusText;
261 var finishRequest = function () {
265 // Copy over our mock to our xhr object before passing control back to
266 // jQuery's onreadystatechange callback
267 if ( requestSettings.dataType === 'json' && ( typeof mockHandler.responseText === 'object' ) ) {
268 this.responseText = JSON.stringify(mockHandler.responseText);
269 } else if ( requestSettings.dataType === 'xml' ) {
270 if ( typeof mockHandler.responseXML === 'string' ) {
271 this.responseXML = parseXML(mockHandler.responseXML);
272 //in jQuery 1.9.1+, responseXML is processed differently and relies on responseText
273 this.responseText = mockHandler.responseXML;
275 this.responseXML = mockHandler.responseXML;
277 } else if (typeof mockHandler.responseText === 'object' && mockHandler.responseText !== null) {
278 // since jQuery 1.9 responseText type has to match contentType
279 mockHandler.contentType = 'application/json';
280 this.responseText = JSON.stringify(mockHandler.responseText);
282 this.responseText = mockHandler.responseText;
284 if( typeof mockHandler.status === 'number' || typeof mockHandler.status === 'string' ) {
285 this.status = mockHandler.status;
287 if( typeof mockHandler.statusText === 'string') {
288 this.statusText = mockHandler.statusText;
290 // jQuery 2.0 renamed onreadystatechange to onload
291 onReady = this.onload || this.onreadystatechange;
293 // jQuery < 1.4 doesn't have onreadystate change for xhr
294 if ( $.isFunction( onReady ) ) {
295 if( mockHandler.isTimeout) {
298 onReady.call( this, mockHandler.isTimeout ? 'timeout' : undefined );
299 } else if ( mockHandler.isTimeout ) {
300 // Fix for 1.3.2 timeout to keep success from firing.
305 // We have an executable function, call it to give
306 // the mock handler a chance to update it's data
307 if ( $.isFunction(mockHandler.response) ) {
308 // Wait for it to finish
309 if ( mockHandler.response.length === 2 ) {
310 mockHandler.response(origSettings, function () {
311 finishRequest.call(that);
315 mockHandler.response(origSettings);
319 finishRequest.call(that);
324 if ( mockHandler.proxy ) {
325 logger.info( mockHandler, ['Retrieving proxy file: ' + mockHandler.proxy, mockHandler] );
326 // We're proxying this request and loading in an external file instead
329 url: mockHandler.proxy,
330 type: mockHandler.proxyType,
331 data: mockHandler.data,
332 async: requestSettings.async,
333 dataType: requestSettings.dataType === 'script' ? 'text/plain' : requestSettings.dataType,
334 complete: function(xhr) {
336 // jQuery will convert the text to XML for us, and if we use the actual responseXML here
337 // then some other things don't happen, resulting in no data given to the 'success' cb
338 mockHandler.responseXML = mockHandler.responseText = xhr.responseText;
340 // Don't override the handler status/statusText if it's specified by the config
341 if (isDefaultSetting(mockHandler, 'status')) {
342 mockHandler.status = xhr.status;
344 if (isDefaultSetting(mockHandler, 'statusText')) {
345 mockHandler.statusText = xhr.statusText;
348 if ( requestSettings.async === false ) {
349 // TODO: Blocking delay
352 this.responseTimer = setTimeout(process, parseResponseTimeOpt(mockHandler.responseTime));
357 // type === 'POST' || 'GET' || 'DELETE'
358 if ( requestSettings.async === false ) {
359 // TODO: Blocking delay
362 this.responseTimer = setTimeout(process, parseResponseTimeOpt(mockHandler.responseTime));
368 // Construct a mocked XHR Object
369 function xhr(mockHandler, requestSettings, origSettings, origHandler) {
370 logger.debug( mockHandler, ['Creating new mock XHR object', mockHandler, requestSettings, origSettings, origHandler] );
372 // Extend with our default mockjax settings
373 mockHandler = $.extend(true, {}, $.mockjaxSettings, mockHandler);
375 if (typeof mockHandler.headers === 'undefined') {
376 mockHandler.headers = {};
378 if (typeof requestSettings.headers === 'undefined') {
379 requestSettings.headers = {};
381 if ( mockHandler.contentType ) {
382 mockHandler.headers['content-type'] = mockHandler.contentType;
386 status: mockHandler.status,
387 statusText: mockHandler.statusText,
389 open: function() { },
391 origHandler.fired = true;
392 _xhrSend.call(this, mockHandler, requestSettings, origSettings);
395 clearTimeout(this.responseTimer);
397 setRequestHeader: function(header, value) {
398 requestSettings.headers[header] = value;
400 getResponseHeader: function(header) {
401 // 'Last-modified', 'Etag', 'content-type' are all checked by jQuery
402 if ( mockHandler.headers && mockHandler.headers[header] ) {
403 // Return arbitrary headers
404 return mockHandler.headers[header];
405 } else if ( header.toLowerCase() === 'last-modified' ) {
406 return mockHandler.lastModified || (new Date()).toString();
407 } else if ( header.toLowerCase() === 'etag' ) {
408 return mockHandler.etag || '';
409 } else if ( header.toLowerCase() === 'content-type' ) {
410 return mockHandler.contentType || 'text/plain';
413 getAllResponseHeaders: function() {
415 // since jQuery 1.9 responseText type has to match contentType
416 if (mockHandler.contentType) {
417 mockHandler.headers['Content-Type'] = mockHandler.contentType;
419 $.each(mockHandler.headers, function(k, v) {
420 headers += k + ': ' + v + '\n';
427 // Process a JSONP mock request.
428 function processJsonpMock( requestSettings, mockHandler, origSettings ) {
429 // Handle JSONP Parameter Callbacks, we need to replicate some of the jQuery core here
430 // because there isn't an easy hook for the cross domain script tag of jsonp
432 processJsonpUrl( requestSettings );
434 requestSettings.dataType = 'json';
435 if(requestSettings.data && CALLBACK_REGEX.test(requestSettings.data) || CALLBACK_REGEX.test(requestSettings.url)) {
436 createJsonpCallback(requestSettings, mockHandler, origSettings);
438 // We need to make sure
439 // that a JSONP style response is executed properly
441 var rurl = /^(\w+:)?\/\/([^\/?#]+)/,
442 parts = rurl.exec( requestSettings.url ),
443 remote = parts && (parts[1] && parts[1] !== location.protocol || parts[2] !== location.host);
445 requestSettings.dataType = 'script';
446 if(requestSettings.type.toUpperCase() === 'GET' && remote ) {
447 var newMockReturn = processJsonpRequest( requestSettings, mockHandler, origSettings );
449 // Check if we are supposed to return a Deferred back to the mock call, or just
452 return newMockReturn;
461 // Append the required callback parameter to the end of the request URL, for a JSONP request
462 function processJsonpUrl( requestSettings ) {
463 if ( requestSettings.type.toUpperCase() === 'GET' ) {
464 if ( !CALLBACK_REGEX.test( requestSettings.url ) ) {
465 requestSettings.url += (/\?/.test( requestSettings.url ) ? '&' : '?') +
466 (requestSettings.jsonp || 'callback') + '=?';
468 } else if ( !requestSettings.data || !CALLBACK_REGEX.test(requestSettings.data) ) {
469 requestSettings.data = (requestSettings.data ? requestSettings.data + '&' : '') + (requestSettings.jsonp || 'callback') + '=?';
473 // Process a JSONP request by evaluating the mocked response text
474 function processJsonpRequest( requestSettings, mockHandler, origSettings ) {
475 logger.debug( mockHandler, ['Performing JSONP request', mockHandler, requestSettings, origSettings] );
477 // Synthesize the mock request for adding a script tag
478 var callbackContext = origSettings && origSettings.context || requestSettings,
479 // If we are running under jQuery 1.5+, return a deferred object
480 newMock = ($.Deferred) ? (new $.Deferred()) : null;
482 // If the response handler on the moock is a function, call it
483 if ( mockHandler.response && $.isFunction(mockHandler.response) ) {
485 mockHandler.response(origSettings);
488 } else if ( typeof mockHandler.responseText === 'object' ) {
489 // Evaluate the responseText javascript in a global context
490 $.globalEval( '(' + JSON.stringify( mockHandler.responseText ) + ')');
492 } else if (mockHandler.proxy) {
493 logger.info( mockHandler, ['Performing JSONP proxy request: ' + mockHandler.proxy, mockHandler] );
495 // This handles the unique case where we have a remote URL, but want to proxy the JSONP
496 // response to another file (not the same URL as the mock matching)
499 url: mockHandler.proxy,
500 type: mockHandler.proxyType,
501 data: mockHandler.data,
502 dataType: requestSettings.dataType === 'script' ? 'text/plain' : requestSettings.dataType,
503 complete: function(xhr) {
504 $.globalEval( '(' + xhr.responseText + ')');
505 completeJsonpCall( requestSettings, mockHandler, callbackContext, newMock );
513 ((typeof mockHandler.responseText === 'string') ?
514 ('"' + mockHandler.responseText + '"') : mockHandler.responseText) +
518 completeJsonpCall( requestSettings, mockHandler, callbackContext, newMock );
523 function completeJsonpCall( requestSettings, mockHandler, callbackContext, newMock ) {
526 // Successful response
527 setTimeout(function() {
528 jsonpSuccess( requestSettings, callbackContext, mockHandler );
529 jsonpComplete( requestSettings, callbackContext );
533 json = $.parseJSON( mockHandler.responseText );
534 } catch (err) { /* just checking... */ }
536 newMock.resolveWith( callbackContext, [json || mockHandler.responseText] );
537 logger.log( mockHandler, ['JSONP mock call complete', mockHandler, newMock] );
539 }, parseResponseTimeOpt( mockHandler.responseTime ));
543 // Create the required JSONP callback function for the request
544 function createJsonpCallback( requestSettings, mockHandler, origSettings ) {
545 var callbackContext = origSettings && origSettings.context || requestSettings;
546 var jsonp = (typeof requestSettings.jsonpCallback === 'string' && requestSettings.jsonpCallback) || ('jsonp' + jsc++);
548 // Replace the =? sequence both in the query string and the data
549 if ( requestSettings.data ) {
550 requestSettings.data = (requestSettings.data + '').replace(CALLBACK_REGEX, '=' + jsonp + '$1');
553 requestSettings.url = requestSettings.url.replace(CALLBACK_REGEX, '=' + jsonp + '$1');
556 // Handle JSONP-style loading
557 window[ jsonp ] = window[ jsonp ] || function() {
558 jsonpSuccess( requestSettings, callbackContext, mockHandler );
559 jsonpComplete( requestSettings, callbackContext );
561 window[ jsonp ] = undefined;
564 delete window[ jsonp ];
567 requestSettings.jsonpCallback = jsonp;
570 // The JSONP request was successful
571 function jsonpSuccess(requestSettings, callbackContext, mockHandler) {
572 // If a local callback was specified, fire it and pass it the data
573 if ( requestSettings.success ) {
574 requestSettings.success.call( callbackContext, mockHandler.responseText || '', 'success', {} );
577 // Fire the global callback
578 if ( requestSettings.global ) {
579 (requestSettings.context ? $(requestSettings.context) : $.event).trigger('ajaxSuccess', [{}, requestSettings]);
583 // The JSONP request was completed
584 function jsonpComplete(requestSettings, callbackContext) {
585 if ( requestSettings.complete ) {
586 requestSettings.complete.call( callbackContext, {
587 statusText: 'success',
592 // The request was completed
593 if ( requestSettings.global ) {
594 (requestSettings.context ? $(requestSettings.context) : $.event).trigger('ajaxComplete', [{}, requestSettings]);
597 // Handle the global AJAX counter
598 if ( requestSettings.global && ! --$.active ) {
599 $.event.trigger( 'ajaxStop' );
604 // The core $.ajax replacement.
605 function handleAjax( url, origSettings ) {
606 var mockRequest, requestSettings, mockHandler, overrideCallback;
608 logger.debug( null, ['Ajax call intercepted', url, origSettings] );
610 // If url is an object, simulate pre-1.5 signature
611 if ( typeof url === 'object' ) {
615 // work around to support 1.5 signature
616 origSettings = origSettings || {};
617 origSettings.url = url || origSettings.url;
620 // Extend the original settings for the request
621 requestSettings = $.ajaxSetup({}, origSettings);
622 requestSettings.type = requestSettings.method = requestSettings.method || requestSettings.type;
624 // Generic function to override callback methods for use with
625 // callback options (onAfterSuccess, onAfterError, onAfterComplete)
626 overrideCallback = function(action, mockHandler) {
627 var origHandler = origSettings[action.toLowerCase()];
629 if ( $.isFunction(origHandler) ) {
630 origHandler.apply(this, [].slice.call(arguments));
632 mockHandler['onAfter' + action]();
636 // Iterate over our mock handlers (in registration order) until we find
637 // one that is willing to intercept the request
638 for(var k = 0; k < mockHandlers.length; k++) {
639 if ( !mockHandlers[k] ) {
643 mockHandler = getMockForRequest( mockHandlers[k], requestSettings );
645 logger.debug( mockHandlers[k], ['Mock does not match request', url, requestSettings] );
646 // No valid mock found for this request
650 if ($.mockjaxSettings.retainAjaxCalls) {
651 mockedAjaxCalls.push(requestSettings);
654 // If logging is enabled, log the mock to the console
655 logger.info( mockHandler, [
656 'MOCK ' + requestSettings.type.toUpperCase() + ': ' + requestSettings.url,
657 $.ajaxSetup({}, requestSettings)
661 if ( requestSettings.dataType && requestSettings.dataType.toUpperCase() === 'JSONP' ) {
662 if ((mockRequest = processJsonpMock( requestSettings, mockHandler, origSettings ))) {
663 // This mock will handle the JSONP request
668 // We are mocking, so there will be no cross domain request, however, jQuery
669 // aggressively pursues this if the domains don't match, so we need to
670 // explicitly disallow it. (See #136)
671 origSettings.crossDomain = false;
673 // Removed to fix #54 - keep the mocking data object intact
674 //mockHandler.data = requestSettings.data;
676 mockHandler.cache = requestSettings.cache;
677 mockHandler.timeout = requestSettings.timeout;
678 mockHandler.global = requestSettings.global;
680 // In the case of a timeout, we just need to ensure
681 // an actual jQuery timeout (That is, our reponse won't)
682 // return faster than the timeout setting.
683 if ( mockHandler.isTimeout ) {
684 if ( mockHandler.responseTime > 1 ) {
685 origSettings.timeout = mockHandler.responseTime - 1;
687 mockHandler.responseTime = 2;
688 origSettings.timeout = 1;
692 // Set up onAfter[X] callback functions
693 if ( $.isFunction( mockHandler.onAfterSuccess ) ) {
694 origSettings.success = overrideCallback('Success', mockHandler);
696 if ( $.isFunction( mockHandler.onAfterError ) ) {
697 origSettings.error = overrideCallback('Error', mockHandler);
699 if ( $.isFunction( mockHandler.onAfterComplete ) ) {
700 origSettings.complete = overrideCallback('Complete', mockHandler);
703 copyUrlParameters(mockHandler, origSettings);
705 /* jshint loopfunc:true */
706 (function(mockHandler, requestSettings, origSettings, origHandler) {
708 mockRequest = _ajax.call($, $.extend(true, {}, origSettings, {
709 // Mock the XHR object
710 xhr: function() { return xhr( mockHandler, requestSettings, origSettings, origHandler ); }
712 })(mockHandler, requestSettings, origSettings, mockHandlers[k]);
713 /* jshint loopfunc:false */
718 // We don't have a mock request
719 logger.log( null, ['No mock matched to request', url, origSettings] );
720 if ($.mockjaxSettings.retainAjaxCalls) {
721 unmockedAjaxCalls.push(origSettings);
723 if($.mockjaxSettings.throwUnmocked === true) {
724 throw new Error('AJAX not mocked: ' + origSettings.url);
726 else { // trigger a normal request
727 return _ajax.apply($, [origSettings]);
732 * Copies URL parameter values if they were captured by a regular expression
733 * @param {Object} mockHandler
734 * @param {Object} origSettings
736 function copyUrlParameters(mockHandler, origSettings) {
737 //parameters aren't captured if the URL isn't a RegExp
738 if (!(mockHandler.url instanceof RegExp)) {
741 //if no URL params were defined on the handler, don't attempt a capture
742 if (!mockHandler.hasOwnProperty('urlParams')) {
745 var captures = mockHandler.url.exec(origSettings.url);
746 //the whole RegExp match is always the first value in the capture results
747 if (captures.length === 1) {
751 //use handler params as keys and capture resuts as values
753 capturesLength = captures.length,
754 paramsLength = mockHandler.urlParams.length,
755 //in case the number of params specified is less than actual captures
756 maxIterations = Math.min(capturesLength, paramsLength),
758 for (i; i < maxIterations; i++) {
759 var key = mockHandler.urlParams[i];
760 paramValues[key] = captures[i];
762 origSettings.urlParams = paramValues;
766 * Clears handlers that mock given url
770 function clearByUrl(url) {
774 match=url instanceof RegExp ?
775 function(testUrl) { return url.test(testUrl); } :
776 function(testUrl) { return url === testUrl; };
777 for (i=0, len=mockHandlers.length; i<len; i++) {
778 handler = mockHandlers[i];
779 if (!match(handler.url)) {
780 results.push(handler);
782 logger.log( handler, [
783 'Clearing mock: ' + (handler && handler.url),
799 _log: function logger( mockHandler, args, level ) {
800 var loggerLevel = $.mockjaxSettings.logging;
801 if (mockHandler && typeof mockHandler.logging !== 'undefined') {
802 loggerLevel = mockHandler.logging;
804 level = ( level === 0 ) ? level : ( level || logLevels.LOG );
805 args = (args.splice) ? args : [ args ];
807 // Is logging turned off for this mock or mockjax as a whole?
808 // Or is this log message above the desired log level?
809 if ( loggerLevel === false || loggerLevel < level ) {
813 if ( $.mockjaxSettings.log ) {
814 return $.mockjaxSettings.log( mockHandler, args[1] || args[0] );
815 } else if ( $.mockjaxSettings.logger && $.mockjaxSettings.logger[$.mockjaxSettings.logLevelMethods[level]] ) {
816 return $.mockjaxSettings.logger[$.mockjaxSettings.logLevelMethods[level]].apply( $.mockjaxSettings.logger, args );
820 * Convenience method for logging a DEBUG level message
821 * @param {Object} m The mock handler in question
822 * @param {Array|String|Object} a The items to log
823 * @return {?} Will return whatever the $.mockjaxSettings.logger method for this level would return (generally 'undefined')
825 debug: function(m,a) { return logger._log(m,a,logLevels.DEBUG); },
829 log: function(m,a) { return logger._log(m,a,logLevels.LOG); },
833 info: function(m,a) { return logger._log(m,a,logLevels.INFO); },
837 warn: function(m,a) { return logger._log(m,a,logLevels.WARN); },
841 error: function(m,a) { return logger._log(m,a,logLevels.ERROR); }
853 * Default settings for mockjax. Some of these are used for defaults of
854 * individual mock handlers, and some are for the library as a whole.
855 * For individual mock handler settings, please see the README on the repo:
856 * https://github.com/jakerella/jquery-mockjax#api-methods
860 $.mockjaxSettings = {
861 log: null, // this is only here for historical purposes... use $.mockjaxSettings.logger
862 logger: window.console,
864 logLevelMethods: ['error', 'warn', 'info', 'log', 'debug'],
868 responseTime: DEFAULT_RESPONSE_TIME,
870 throwUnmocked: false,
871 retainAjaxCalls: true,
872 contentType: 'text/plain',
882 etag: 'IJF@H#@923uf8023hFO@I#H#',
883 'content-type' : 'text/plain'
888 * Create a new mock Ajax handler. When a mock handler is matched during a
889 * $.ajax() call this library will intercept that request and fake a response
890 * using the data and methods in the mock. You can see all settings in the
891 * README of the main repository:
892 * https://github.com/jakerella/jquery-mockjax#api-methods
894 * @param {Object} settings The mock handelr settings: https://github.com/jakerella/jquery-mockjax#api-methods
895 * @return {Number} The id (index) of the mock handler suitable for clearing (see $.mockjax.clear())
897 $.mockjax = function(settings) {
899 if ( $.isArray(settings) ) {
900 return $.map(settings, function(s) {
905 var i = mockHandlers.length;
906 mockHandlers[i] = settings;
907 logger.log( settings, ['Created new mock handler', settings] );
911 $.mockjax._logger = logger;
914 * Remove an Ajax mock from those held in memory. This will prevent any
915 * future Ajax request mocking for matched requests.
916 * NOTE: Clearing a mock will not prevent the resolution of in progress requests
918 * @param {Number|String|RegExp} i OPTIONAL The mock to clear. If not provided, all mocks are cleared,
919 * if a number it is the index in the in-memory cache. If a string or
920 * RegExp, find a mock that matches that URL and clear it.
923 $.mockjax.clear = function(i) {
924 if ( typeof i === 'string' || i instanceof RegExp) {
925 mockHandlers = clearByUrl(i);
926 } else if ( i || i === 0 ) {
927 logger.log( mockHandlers[i], [
928 'Clearing mock: ' + (mockHandlers[i] && mockHandlers[i].url),
931 mockHandlers[i] = null;
933 logger.log( null, 'Clearing all mocks' );
936 mockedAjaxCalls = [];
937 unmockedAjaxCalls = [];
941 * By default all Ajax requests performed after loading Mockjax are recorded
942 * so that we can see which requests were mocked and which were not. This
943 * method allows the developer to clear those retained requests.
947 $.mockjax.clearRetainedAjaxCalls = function() {
948 mockedAjaxCalls = [];
949 unmockedAjaxCalls = [];
950 logger.debug( null, 'Cleared retained ajax calls' );
954 * Retrive the mock handler with the given id (index).
956 * @param {Number} i The id (index) to retrieve
957 * @return {Object} The mock handler settings
959 $.mockjax.handler = function(i) {
960 if ( arguments.length === 1 ) {
961 return mockHandlers[i];
966 * Retrieve all Ajax calls that have been mocked by this library during the
967 * current session (in other words, only since you last loaded this file).
969 * @return {Array} The mocked Ajax calls (request settings)
971 $.mockjax.mockedAjaxCalls = function() {
972 return mockedAjaxCalls;
976 * Return all mock handlers that have NOT been matched against Ajax requests
978 * @return {Array} The mock handlers
980 $.mockjax.unfiredHandlers = function() {
982 for (var i=0, len=mockHandlers.length; i<len; i++) {
983 var handler = mockHandlers[i];
984 if (handler !== null && !handler.fired) {
985 results.push(handler);
992 * Retrieve all Ajax calls that have NOT been mocked by this library during
993 * the current session (in other words, only since you last loaded this file).
995 * @return {Array} The mocked Ajax calls (request settings)
997 $.mockjax.unmockedAjaxCalls = function() {
998 return unmockedAjaxCalls;