├── test
├── test_proxy.json
├── test_script.js
├── test.json
├── test_jsonp.js
├── test_proxy.xml
├── browserify
│ ├── index.html
│ ├── main.js
│ └── test.js
├── test-setup.js
├── requirejs
│ ├── test_module.js
│ ├── run_tests.js
│ └── index.html
├── test-timeout.js
├── test-url-match.js
├── nodejs
│ └── test.js
├── index.html
├── dist-min.html
├── jquery.js
├── test-headers.js
├── test-namespace.js
├── test-header-match.js
├── test-logging.js
├── test-connection.js
├── test-data-types.js
├── test-mock-clearing.js
├── test-data-match.js
├── test-retaining-ajax-calls.js
├── test-bugs.js
└── test-core.js
├── .codeclimate.yml
├── .gitignore
├── .travis.yml
├── .editorconfig
├── bower.json
├── lib
├── jquery.xmldom.js
├── semver.js
├── qunit.css
└── json2.js
├── package.json
├── .jshintrc
├── Gruntfile.js
├── CHANGELOG.md
├── CONTRIBUTING.md
├── dist
└── jquery.mockjax.min.js
└── README.md
/test/test_proxy.json:
--------------------------------------------------------------------------------
1 | { "proxy" : true }
--------------------------------------------------------------------------------
/test/test_script.js:
--------------------------------------------------------------------------------
1 | TEST_SCRIPT_VAR = 1;
--------------------------------------------------------------------------------
/test/test.json:
--------------------------------------------------------------------------------
1 | { "say" : "I'm a json file!" }
2 |
--------------------------------------------------------------------------------
/test/test_jsonp.js:
--------------------------------------------------------------------------------
1 | abcdef123456({ "data" : "JSONP is cool" })
--------------------------------------------------------------------------------
/test/test_proxy.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | bar
4 |
--------------------------------------------------------------------------------
/.codeclimate.yml:
--------------------------------------------------------------------------------
1 | languages:
2 | JavaScript: true
3 | exclude_paths:
4 | - dist/*
5 | - lib/*
6 | - test/*
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.DS_Store
2 | *.esproj
3 | *.swp
4 | .idea/
5 | *.iml
6 | node_modules/
7 | npm-debug.log
8 |
9 | test/browserify/bundle.js
10 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | node_js:
4 | - "4.2"
5 | before_install: npm install -g grunt-cli
6 | branches:
7 | only:
8 | - master
9 | - v1.x
10 | notifications:
11 | email: false
12 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | ; EditorConfig is awesome: http://EditorConfig.org
2 |
3 | ; top-most EditorConfig file
4 | root = true
5 |
6 | ; Tab indentation (no size specified)
7 | [*.js]
8 | indent_style = tab
9 | indent_size = 4
10 | end_of_line = lf
11 | trim_trailing_whitespace = true
12 | insert_final_newline = true
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jquery-mockjax",
3 | "main": "dist/jquery.mockjax.js",
4 | "dependencies": {
5 | "jquery": ">=1.5.0"
6 | },
7 | "ignore": [
8 | ".editorconfig",
9 | ".gitignore",
10 | ".jshintrc",
11 | "*.md",
12 | "*.json",
13 | "lib",
14 | "test"
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/test/browserify/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | MockJax Browserify Test
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/test/browserify/main.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var jquery = require('../../lib/jquery-3.1.0.js');
4 | var mockjax = require('../../src/jquery.mockjax')(jquery, window);
5 |
6 | mockjax({
7 | url: '/resource',
8 | responseText: 'content'
9 | });
10 |
11 | /* jshint unused:false */
12 | function getResource(cb) {
13 | jquery.ajax({
14 | url: '/resource',
15 | success: cb,
16 | error: cb
17 | });
18 | }
19 | /* jshint unused:true */
20 |
21 |
22 | // These are just here so that my tests can hit the *same* jQuery instance
23 | // that Mockjax is on as well as the `getResource()` function above.
24 | // You would NOT need this in your own code.
25 | window.jQuery = jquery;
26 | window.getResource = getResource;
27 |
--------------------------------------------------------------------------------
/test/test-setup.js:
--------------------------------------------------------------------------------
1 | (function(qunit, $) {
2 | 'use strict';
3 |
4 | qunit.begin(function() {
5 |
6 | qunit.noErrorCallbackExpected = function noErrorCallbackExpected(xhr) {
7 | qunit.assert.ok(false, 'Error callback executed: ' + xhr.status, xhr.responseText);
8 | };
9 |
10 | // Speed up our tests
11 | $.mockjaxSettings.responseTime = 0;
12 | $.mockjaxSettings.logging = false;
13 |
14 | // Don't show log messages, but allow logging to work
15 | var noop = function() {};
16 | $.mockjaxSettings.logger = {
17 | debug: noop,
18 | log: noop,
19 | info: noop,
20 | warn: noop,
21 | error: noop
22 | };
23 |
24 | qunit.defaultMockjaxSettings = $.extend({}, $.mockjaxSettings);
25 | });
26 |
27 | qunit.testDone(function() {
28 | $.mockjax.clear();
29 | $.mockjaxSettings = $.extend({}, qunit.defaultMockjaxSettings);
30 | });
31 |
32 | })(window.QUnit, window.jQuery);
33 |
--------------------------------------------------------------------------------
/test/requirejs/test_module.js:
--------------------------------------------------------------------------------
1 | /* globals define,QUnit */
2 |
3 | define(['jquery', 'jquery.mockjax'], function ($, mockjax) {
4 | 'use strict';
5 |
6 | QUnit.module('jquery.mockjax used as AMD module');
7 |
8 | QUnit.test('returns the mockjax object', function(assert) {
9 | assert.ok(mockjax, 'mockjax object is returned');
10 | });
11 |
12 | QUnit.test('sets the mockjax object to the jQuery object', function(assert) {
13 | assert.ok($.mockjax, '$.mockjax object is set');
14 | });
15 |
16 | QUnit.test('returns the same object as it sets to $.mockjax', function(assert) {
17 | assert.strictEqual(mockjax, $.mockjax, 'returned mockjax object is the same as $.mockjax object');
18 | });
19 |
20 | QUnit.test('mocks a simple request', function (assert) {
21 | var done = assert.async();
22 |
23 | $.mockjax({
24 | url: '/resource',
25 | responseText: 'content'
26 | });
27 |
28 | $.ajax({
29 | url: '/resource',
30 | success: function(response) {
31 | assert.equal(response, 'content');
32 | },
33 | error: function () {
34 | assert.ok(false, 'error callback executed');
35 | },
36 | complete: done
37 | });
38 | });
39 | });
40 |
41 |
--------------------------------------------------------------------------------
/lib/jquery.xmldom.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * jQuery xmlDOM Plugin v1.0
3 | * http://outwestmedia.com/jquery-plugins/xmldom/
4 | *
5 | * Released: 2009-04-06
6 | * Version: 1.0
7 | *
8 | * Copyright (c) 2009 Jonathan Sharp, Out West Media LLC.
9 | * Dual licensed under the MIT and GPL licenses.
10 | * http://docs.jquery.com/License
11 | */
12 | (function($) {
13 | // IE DOMParser wrapper
14 | if ( window['DOMParser'] == undefined && window.ActiveXObject ) {
15 | DOMParser = function() { };
16 | DOMParser.prototype.parseFromString = function( xmlString ) {
17 | var doc = new ActiveXObject('Microsoft.XMLDOM');
18 | doc.async = 'false';
19 | doc.loadXML( xmlString );
20 | return doc;
21 | };
22 | }
23 |
24 | $.xmlDOM = function(xml, onErrorFn) {
25 | try {
26 | var xmlDoc = ( new DOMParser() ).parseFromString( xml, 'text/xml' );
27 | if ( $.isXMLDoc( xmlDoc ) ) {
28 | var err = $('parsererror', xmlDoc);
29 | if ( err.length == 1 ) {
30 | throw('Error: ' + $(xmlDoc).text() );
31 | }
32 | } else {
33 | throw('Unable to parse XML');
34 | }
35 | } catch( e ) {
36 | var msg = ( e.name == undefined ? e : e.name + ': ' + e.message );
37 | if ( $.isFunction( onErrorFn ) ) {
38 | onErrorFn( msg );
39 | } else {
40 | $(document).trigger('xmlParseError', [ msg ]);
41 | }
42 | return $([]);
43 | }
44 | return $( xmlDoc );
45 | };
46 | })(jQuery);
--------------------------------------------------------------------------------
/test/test-timeout.js:
--------------------------------------------------------------------------------
1 | (function(qunit, $) {
2 | 'use strict';
3 |
4 | var t = qunit.test;
5 |
6 | /* --------------------- */
7 | qunit.module( 'Timeouts' );
8 | /* --------------------- */
9 |
10 | t('Forcing timeout', function(assert) {
11 | var done = assert.async();
12 |
13 | $.mockjax({
14 | url: '/response-callback',
15 | responseText: 'done',
16 | isTimeout: true
17 | });
18 |
19 | $.ajax({
20 | url: '/response-callback',
21 | error: function(xhr, textStatus, errorThrown ) {
22 | assert.equal( textStatus, 'timeout', 'Text status is equal to timeout' );
23 | assert.ok( errorThrown !== 'OK', 'errorThrown is undefined or timeout, not OK' );
24 | assert.ok(true, 'error callback was called');
25 | },
26 | success: function() {
27 | assert.ok(false, 'should not be be successful');
28 | },
29 | complete: done
30 | });
31 | });
32 |
33 | t('Forcing timeout with Promises', function(assert) {
34 | var done = assert.async();
35 |
36 | $.mockjax({
37 | url: '/response-callback',
38 | isTimeout: true
39 | });
40 |
41 | var request = $.ajax({
42 | url: '/response-callback'
43 | });
44 |
45 | request.done(function() {
46 | assert.ok(false, 'Should not be successful');
47 | });
48 |
49 | request.fail(function() {
50 | assert.ok(true, 'error callback was called');
51 | });
52 |
53 | // always for jquery 1.8+
54 | (request.always || request.complete)(done);
55 | });
56 |
57 | })(window.QUnit, window.jQuery);
--------------------------------------------------------------------------------
/lib/semver.js:
--------------------------------------------------------------------------------
1 |
2 | (function(qunit) {
3 | qunit.compareSemver = function compareSemver(v1, v2, op) {
4 | var result = false,
5 | p1 = normalizeSemVer(v1),
6 | p2 = normalizeSemVer(v2);
7 |
8 | if (/^===?$/.test(op)) {
9 | result = semverEqual(p1, p2, 3);
10 | } else if (/^/.test(op)) {
16 | result = p1[0] > p2[0] || (semverEqual(p1, p2, 1) && p1[1] > p2[1]) || (semverEqual(p1, p2, 2) && p1[2] > p2[2]);
17 | }
18 | if (!result && /^[<>]=$/.test(op)) {
19 | result = semverEqual(p1, p2, 3);
20 | }
21 |
22 | function semverEqual(p1, p2, cnt) {
23 | var i, equal = true;
24 | for (i=0; i|git'; the newest version in the local lib
6 | // directory is the default
7 | function getJQueryPath() {
8 | var parts = document.location.search.slice(1).split('&'),
9 | length = parts.length,
10 | version = '1.5.2',
11 | i, current;
12 |
13 | for (i = 0; i < length; i++) {
14 | current = parts[i].split('=');
15 | if (current[0] === 'jquery') {
16 | version = current[1];
17 | break;
18 | }
19 | }
20 |
21 | return version === 'git' ? 'http://code.jquery.com/jquery-git' :
22 | '../lib/jquery-' + version;
23 | }
24 |
25 | require.config({
26 | // Test the jquery.mockjax registers itself with the right module
27 | // name when loaded just by the file name
28 | baseUrl: '../../src',
29 | paths: {
30 | // jQuery uses fixed name for their AMD module; point it to
31 | // the right path according to the URL parameters
32 | 'jquery': getJQueryPath(),
33 | // Make referring to the test modules easier by a short prefix
34 | 'test': '../test/requirejs'
35 | }
36 | });
37 |
38 | // Require all modules with tests; it will execute them right away
39 | require(['test/test_module'], function () {
40 | // Initialize the QUnit UI first after the test were run
41 | QUnit.start();
42 | });
43 | }(window.QUnit));
44 |
--------------------------------------------------------------------------------
/test/requirejs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | MockJax AMD Module Test
5 |
6 |
8 |
9 |
12 |
13 |
18 |
19 |
20 |
21 |
22 |
23 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/test/test-url-match.js:
--------------------------------------------------------------------------------
1 | (function(qunit, $) {
2 | 'use strict';
3 |
4 | var t = qunit.test;
5 |
6 | /* ------------------------- */
7 | qunit.module( 'URL Matching' );
8 | /* ------------------------- */
9 |
10 | t('Exact string', function(assert) {
11 | var done = assert.async();
12 |
13 | $.mockjax({
14 | url: '/exact/string',
15 | responseText: 'exact string'
16 | });
17 | $.mockjax({
18 | url: '*',
19 | responseText: 'catch all'
20 | });
21 |
22 | $.ajax({
23 | url: '/exact/string',
24 | error: qunit.noErrorCallbackExpected,
25 | complete: function(xhr) {
26 | assert.equal(xhr.responseText, 'exact string', 'Exact string url match');
27 | done();
28 | }
29 | });
30 | });
31 |
32 | t('Wildcard match', function(assert) {
33 | function mock(mockUrl, url, response) {
34 | $.mockjax({
35 | url: mockUrl,
36 | responseText: response
37 | });
38 | $.ajax({
39 | async: false,
40 | url: url,
41 | error: qunit.noErrorCallbackExpected,
42 | complete: function(xhr) {
43 | assert.equal(xhr.responseText, response);
44 | }
45 | });
46 | }
47 | mock('/wildcard*w', '/wildcard/123456/w', 'w');
48 | mock('/wildcard*x', '/wildcard/123456/x', 'x');
49 | mock('*y', '/wildcard/123456/y', 'y');
50 | mock('z*', 'z/wildcard/123456', 'z');
51 | mock('/wildcard*aa/second/*/nice', '/wildcard/123456/aa/second/9991231/nice', 'aa');
52 | });
53 |
54 | t('RegEx match', function(assert) {
55 | var done = assert.async();
56 |
57 | $.mockjax({
58 | url: /^\/regex-([0-9]+)/i,
59 | responseText: 'regex match'
60 | });
61 | $.mockjax({
62 | url: '*',
63 | responseText: 'catch all'
64 | });
65 |
66 | $.ajax({
67 | url: '/regex-123456',
68 | error: qunit.noErrorCallbackExpected,
69 | complete: function(xhr) {
70 | assert.equal(xhr.responseText, 'regex match', 'RegEx match');
71 | done();
72 | }
73 | });
74 | });
75 |
76 | })(window.QUnit, window.jQuery);
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jquery-mockjax",
3 | "title": "jQuery Mockjax",
4 | "version": "2.2.1",
5 | "main": "./src/jquery.mockjax.js",
6 | "description": "The jQuery Mockjax Plugin provides a simple and extremely flexible interface for mocking or simulating ajax requests and responses.",
7 | "url": "https://github.com/jakerella/jquery-mockjax",
8 | "scripts": {
9 | "test": "grunt test:all"
10 | },
11 | "keywords": [
12 | "ajax",
13 | "mock",
14 | "unit",
15 | "testing",
16 | "jquery-plugin",
17 | "ecosystem:jquery"
18 | ],
19 | "author": {
20 | "name": "Jonathan Sharp",
21 | "url": "http://jdsharp.com"
22 | },
23 | "contributors": [
24 | {
25 | "name": "Doug Neiner"
26 | },
27 | {
28 | "name": "Jonathan Creamer"
29 | },
30 | {
31 | "name": "Jordan Kasper"
32 | }
33 | ],
34 | "homepage": "https://github.com/jakerella/jquery-mockjax",
35 | "repository": {
36 | "type": "git",
37 | "url": "https://github.com/jakerella/jquery-mockjax.git"
38 | },
39 | "bugs": {
40 | "web": "http://github.com/jakerella/jquery-mockjax/issues"
41 | },
42 | "licenses": [
43 | {
44 | "type": "MIT",
45 | "url": "http://opensource.org/licenses/MIT"
46 | },
47 | {
48 | "type": "GPLv2",
49 | "url": "http://www.gnu.org/licenses/gpl-2.0.html"
50 | }
51 | ],
52 | "dependencies": {
53 | "jquery": ">=1.5.2"
54 | },
55 | "devDependencies": {
56 | "browserify": "^13.1.0",
57 | "grunt": "^0.4.5",
58 | "grunt-browserify": "^5.0.0",
59 | "grunt-contrib-concat": "^0.5.0",
60 | "grunt-contrib-jshint": "^0.12.0",
61 | "grunt-contrib-qunit": "^0.5.2",
62 | "grunt-contrib-uglify": "^0.6.0",
63 | "grunt-contrib-watch": "^0.6.1",
64 | "grunt-mocha-test": "^0.12.7",
65 | "jsdom": "~4.2.0",
66 | "load-grunt-tasks": "^0.6.0",
67 | "mocha": "^2.2.4",
68 | "sinon": "^1.17.4",
69 | "xmlhttprequest": "^1.7.0"
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/test/nodejs/test.js:
--------------------------------------------------------------------------------
1 | /* globals describe,beforeEach,afterEach,it */
2 |
3 | var jsDomEnv = require('jsdom').env,
4 | assert = require('assert');
5 |
6 | describe('Node module setup', function() {
7 | 'use strict';
8 |
9 | var $, xhr, win;
10 |
11 | beforeEach(function(done) {
12 | jsDomEnv('', function (error, window) {
13 | if (error) {
14 | assert(false);
15 | } else {
16 | win = window;
17 | $ = require('jquery')(window);
18 | xhr = require('xmlhttprequest').XMLHttpRequest;
19 | $.support.cors = true;
20 | $.ajaxSettings.xhr = function () {
21 | /*jshint newcap:false*/
22 | return new xhr();
23 | /*jshint newcap:true*/
24 | };
25 | }
26 | done();
27 | });
28 | });
29 |
30 | describe('Mockjax Node Module Tests', function() {
31 |
32 | afterEach(function() {
33 | if ($ && $.mockjax) {
34 | $.mockjax.clear();
35 | }
36 | });
37 |
38 |
39 | it('should be loaded when required', function() {
40 | var mockjax = require('../../src/jquery.mockjax')($, win);
41 | assert.equal(typeof mockjax, 'function');
42 | assert.equal(typeof $.mockjax, 'function');
43 | });
44 |
45 | it('should mock a simple request using returned module', function(done) {
46 | var mockjax = require('../../src/jquery.mockjax')($, win);
47 |
48 | mockjax({
49 | url: '/resource',
50 | responseText: 'content'
51 | });
52 |
53 | $.ajax({
54 | url: '/resource',
55 | success: function(response) {
56 | assert.equal(response, 'content');
57 | },
58 | error: function () {
59 | assert(false);
60 | },
61 | complete: function () {
62 | done();
63 | }
64 | });
65 | });
66 |
67 | it('should mock a simple request using $.mockjax', function(done) {
68 | require('../../src/jquery.mockjax')($, win);
69 |
70 | $.mockjax({
71 | url: '/foo',
72 | responseText: 'bar'
73 | });
74 |
75 | $.ajax({
76 | url: '/foo',
77 | success: function(response) {
78 | assert.equal(response, 'bar');
79 | },
80 | error: function () {
81 | assert(false);
82 | },
83 | complete: function () {
84 | done();
85 | }
86 | });
87 | });
88 | });
89 | });
90 |
--------------------------------------------------------------------------------
/test/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | MockJax Tests -
28 |
29 |
30 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/test/dist-min.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | MockJax Tests -
28 |
29 |
30 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/test/jquery.js:
--------------------------------------------------------------------------------
1 |
2 | (function(QUnit, basePath) {
3 | 'use strict';
4 |
5 | var parts = document.location.search.slice( 1 ).split( '&' ),
6 | length = parts.length,
7 | i = 0,
8 | current,
9 | QUnitDone = false,
10 | QUnitErrors = false,
11 | currIndex = document.location.search.match(/v=([0-9]+)/),
12 | nextIndex = (currIndex && Number(currIndex[1]) || 0) + 1, // +1 because QUnit makes the h1 text a link
13 | version = '1.5.2',
14 | file = 'http://code.jquery.com/jquery-git.js';
15 |
16 | for ( ; i < length; i++ ) {
17 | current = parts[ i ].split( '=' );
18 | if ( current[ 0 ] === 'jquery' ) {
19 | version = current[ 1 ];
20 | break;
21 | }
22 | }
23 |
24 | if (version !== 'git') {
25 | file = basePath + 'lib/jquery-' + version + '.js';
26 | }
27 |
28 |
29 | document.write( '' );
30 |
31 |
32 | // Track when QUnit finishes so we can redirect if necessary
33 | QUnit.done(function(details) {
34 | QUnitDone = true;
35 | QUnitErrors = !!details.failed;
36 | });
37 |
38 |
39 | // Set up the 'run all' button once jQuery is loaded
40 | document.getElementById('jquery').onload = function() {
41 | $(document).ready(function() {
42 | // Sigh... QUnit 'rebuilds' the header, so we have to wait for that before
43 | // attaching our click event, otherwise we lose the handler in the rebuild
44 | setTimeout(function() {
45 | var btn = $('.runall');
46 |
47 | if (currIndex) {
48 | // We're already in a run...
49 | btn.attr('disabled', 'disabled');
50 | setupNextRedirect();
51 |
52 | } else {
53 | // Set up a new run...
54 | btn.removeAttr('disabled').click(function(e) {
55 | e.preventDefault();
56 | setupNextRedirect();
57 | });
58 | }
59 | }, 1000);
60 | });
61 | };
62 |
63 | function getNextLink() {
64 | var nextLink = null,
65 | nextLinkNode = $('#qunit-header a:eq(' + nextIndex + ')');
66 |
67 | if (nextLinkNode && nextLinkNode.length) {
68 | nextLink = nextLinkNode.attr('href') + '&v=' + nextIndex;
69 | }
70 | return nextLink;
71 | }
72 |
73 | function setupNextRedirect() {
74 | var nextLink = getNextLink();
75 |
76 | if (nextLink) {
77 | if (QUnitDone && !QUnitErrors) {
78 | // QUnit already finished, so we'll redirect
79 | document.location.replace(nextLink);
80 |
81 | } else if (!QUnitDone) {
82 | QUnit.done(function(details) {
83 | if (!details.failed) {
84 | // we only redirect if the last test run succeeded
85 | setTimeout(function() {
86 | document.location.replace(nextLink);
87 | }, 1000);
88 | }
89 | });
90 | }
91 | }
92 | }
93 |
94 | })(window.QUnit, window.testJQPath || '../');
95 |
--------------------------------------------------------------------------------
/test/test-headers.js:
--------------------------------------------------------------------------------
1 | (function(qunit, $) {
2 | 'use strict';
3 |
4 | var t = qunit.test;
5 |
6 | /* -------------------- */
7 | qunit.module( 'Headers' );
8 | /* -------------------- */
9 |
10 | t('headers can be inspected via setRequestHeader()', function(assert) {
11 | var done = assert.async();
12 |
13 | assert.expect(1);
14 |
15 | $(document).ajaxSend(function(event, xhr) {
16 | xhr.setRequestHeader('X-CSRFToken', '');
17 | });
18 |
19 | $.mockjax( function ( requestSettings ) {
20 | var key;
21 |
22 | if ('/inspect-headers' === requestSettings.url) {
23 | return {
24 | response: function() {
25 | if (typeof requestSettings.headers['X-Csrftoken'] !== 'undefined') {
26 | key = 'X-Csrftoken'; // bugs in jquery 1.5
27 | } else {
28 | key = 'X-CSRFToken';
29 | }
30 | assert.equal(requestSettings.headers[key], '');
31 | this.responseText = {};
32 | }
33 | };
34 | }
35 | });
36 |
37 | $.ajax({
38 | url: '/inspect-headers',
39 | complete: done
40 | });
41 | });
42 |
43 | t('Response status callback', function(assert) {
44 | var done = assert.async();
45 |
46 | $.mockjax({
47 | url: '/response-callback',
48 | status: 403
49 | });
50 |
51 | $.ajax({
52 | url: '/response-callback',
53 | success: function() {
54 | assert.ok(false, 'Success handler should not have been called');
55 | },
56 | complete: function(xhr) {
57 | assert.equal(xhr.status, 403, 'response status matches');
58 | done();
59 | }
60 | });
61 | });
62 |
63 | t('Setting the content-type', function(assert) {
64 | var done = assert.async();
65 |
66 | $.mockjax({
67 | url: '/response-callback',
68 | contentType: 'text/json',
69 | responseText: {
70 | foo: 'bar'
71 | }
72 | });
73 |
74 | $.ajax({
75 | url: '/response-callback',
76 | dataType: 'json',
77 | error: qunit.noErrorCallbackExpected,
78 | success: function(json) {
79 | assert.deepEqual(json, { 'foo' : 'bar' }, 'JSON Object matches');
80 | },
81 | complete: function(xhr) {
82 | assert.equal(xhr.getResponseHeader('Content-Type'), 'text/json', 'Content type of json');
83 | done();
84 | }
85 | });
86 | });
87 |
88 | t('Setting additional HTTP response headers', function(assert) {
89 | var done = assert.async();
90 |
91 | $.mockjax({
92 | url: '/response-callback',
93 | headers: {
94 | 'X-Must-Exist': 'yes'
95 | },
96 | responseText: 'done'
97 | });
98 |
99 | $.ajax({
100 | url: '/response-callback',
101 | error: qunit.noErrorCallbackExpected,
102 | success: function(response) {
103 | assert.equal( response, 'done', 'Response text matches' );
104 | },
105 | complete: function(xhr) {
106 | assert.equal(xhr.getResponseHeader( 'X-Must-Exist' ), 'yes', 'Header matches');
107 | done();
108 | }
109 | });
110 | });
111 |
112 | t('Testing that request headers do not overwrite response headers', function(assert) {
113 | var done = assert.async();
114 |
115 | $.mockjax({
116 | url: '/restful/fortune',
117 | headers : {
118 | prop: 'response'
119 | }
120 | });
121 |
122 | var returnedXhr = $.ajax({
123 | type: 'GET',
124 | url: '/restful/fortune',
125 | headers : {
126 | prop : 'request'
127 | },
128 | success: function(res, status, xhr) {
129 | if (xhr) {
130 | assert.equal(xhr && xhr.getResponseHeader('prop'), 'response', 'response header should be correct');
131 | } else {
132 | assert.equal(returnedXhr.getResponseHeader('prop'), 'response', 'response header should be correct');
133 | }
134 | },
135 | error: qunit.noErrorCallbackExpected,
136 | complete: done
137 | });
138 | });
139 |
140 | })(window.QUnit, window.jQuery);
--------------------------------------------------------------------------------
/test/test-namespace.js:
--------------------------------------------------------------------------------
1 | (function(qunit, $) {
2 | 'use strict';
3 |
4 | var t = qunit.test;
5 |
6 | /* -------------------- */
7 | qunit.module('namespace');
8 | /* -------------------- */
9 |
10 | t('url should be namespaced via global mockjax settings', function(assert) {
11 | var done = assert.async();
12 |
13 | $.mockjaxSettings.namespace = '/api/v1';
14 |
15 | $.mockjax({
16 | url: 'myservice'
17 | });
18 |
19 | $.ajax({
20 | url: '/api/v1/myservice',
21 | error: qunit.noErrorCallbackExpected,
22 | complete: function(xhr) {
23 | assert.equal(xhr.status, 200, 'Response was successful');
24 | done();
25 | }
26 | });
27 | });
28 |
29 | t('should be able to override global namespace per-mock', function(assert) {
30 | var done = assert.async();
31 |
32 | $.mockjaxSettings.namespace = '/api/v1';
33 |
34 | $.mockjax({
35 | url: 'myservice',
36 | namespace: '/api/v2'
37 | });
38 |
39 | $.ajax({
40 | url: '/api/v2/myservice',
41 | error: qunit.noErrorCallbackExpected,
42 | complete: function(xhr) {
43 | assert.equal(xhr.status, 200, 'Response was successful');
44 | $.ajax({
45 | url: '/api/v1/myservice',
46 | error: function() {
47 | assert.ok(true, 'error callback was called');
48 | done();
49 | }
50 | });
51 | }
52 | });
53 | });
54 |
55 | t('should not mock a non-matching url within a namespace', function(assert) {
56 | var done = assert.async();
57 |
58 | $.mockjaxSettings.namespace = '/api/v1';
59 |
60 | $.mockjax({
61 | url: 'myservice'
62 | });
63 |
64 | $.ajax({
65 | url: '/api/v1/yourservice',
66 | success: function() {
67 | assert.ok(false, 'call should not be successful');
68 | },
69 | error: function() {
70 | assert.ok(true, 'error callback was called');
71 | done();
72 | }
73 | });
74 | });
75 |
76 | t('should handle multiple mocks in a row within a namespace', function(assert) {
77 | var done = assert.async();
78 |
79 | $.mockjaxSettings.namespace = '/api/v1';
80 |
81 | $.mockjax({
82 | url: 'one'
83 | });
84 |
85 | $.mockjax({
86 | url: 'two'
87 | });
88 |
89 | $.ajax({
90 | url: '/api/v1/one',
91 | error: qunit.noErrorCallbackExpected,
92 | complete: function(xhr) {
93 | assert.equal(xhr.status, 200, 'Response was successful');
94 | $.ajax({
95 | url: '/api/v1/two',
96 | complete: function(xhr) {
97 | assert.equal(xhr.status, 200, 'Response was successful');
98 | done();
99 | }
100 | });
101 | }
102 | });
103 | });
104 |
105 | t('should pass the correct url to the response settings', function(assert) {
106 | var done = assert.async();
107 |
108 | $.mockjaxSettings.namespace = '/api/v1';
109 |
110 | $.mockjax({
111 | url: 'myservice',
112 | response: function(settings) {
113 | assert.equal(settings.url, '/api/v1/myservice');
114 | }
115 | });
116 |
117 | $.ajax({
118 | url: '/api/v1/myservice',
119 | error: qunit.noErrorCallbackExpected,
120 | complete: function(xhr) {
121 | assert.equal(xhr.status, 200, 'Response was successful');
122 | done();
123 | }
124 | });
125 | });
126 |
127 | t('should handle extra slashes', function(assert) {
128 | var done = assert.async();
129 |
130 | $.mockjaxSettings.namespace = '/api/v1/';
131 |
132 | $.mockjax({
133 | url: '/myservice'
134 | });
135 |
136 | $.ajax({
137 | url: '/api/v1/myservice',
138 | error: qunit.noErrorCallbackExpected,
139 | complete: function(xhr) {
140 | assert.equal(xhr.status, 200, 'Response was successful');
141 | done();
142 | }
143 | });
144 | });
145 |
146 | t('should handle missing slashes', function(assert) {
147 | var done = assert.async();
148 |
149 | $.mockjaxSettings.namespace = '/api/v1';
150 |
151 | $.mockjax({
152 | url: 'myservice'
153 | });
154 |
155 | $.ajax({
156 | url: '/api/v1/myservice',
157 | error: qunit.noErrorCallbackExpected,
158 | complete: function(xhr) {
159 | assert.equal(xhr.status, 200, 'Response was successful');
160 | done();
161 | }
162 | });
163 | });
164 |
165 | })(window.QUnit, window.jQuery);
--------------------------------------------------------------------------------
/test/test-header-match.js:
--------------------------------------------------------------------------------
1 | (function(qunit, $) {
2 | 'use strict';
3 |
4 | var t = qunit.test;
5 |
6 | /* ----------------------------- */
7 | qunit.module( 'Headers Matching' );
8 | /* ----------------------------- */
9 |
10 | t('Not equal headers', function(assert) {
11 | var done = assert.async();
12 |
13 | $.mockjax({
14 | url: '/exact/string',
15 | requestHeaders: {
16 | Authorization: '12345'
17 | },
18 | responseText: 'Exact headers'
19 | });
20 |
21 | $.ajax({
22 | url: '/exact/string',
23 | error: function() { assert.ok(true, 'Error called on bad request headers matching'); },
24 | success: function() { assert.ok(false, 'Success should not be called'); },
25 | complete: function() {
26 | var mockedAjaxCalls = $.mockjax.mockedAjaxCalls();
27 | assert.equal(mockedAjaxCalls.length, 0, 'No mocked Ajax calls should have been returned');
28 | done();
29 | }
30 | });
31 | });
32 |
33 | t('Not equal headers values', function(assert) {
34 | var done = assert.async();
35 |
36 | $.mockjax({
37 | url: '/exact/string',
38 | requestHeaders: {
39 | Authorization: '12345'
40 | },
41 | responseText: 'Exact headers'
42 | });
43 |
44 | $.ajax({
45 | url: '/exact/string',
46 | headers: {
47 | Authorization: '6789'
48 | },
49 | error: function() { assert.ok(true, 'Error called on bad request headers matching'); },
50 | success: function() { assert.ok(false, 'Success should not be called'); },
51 | complete: function() {
52 | var mockedAjaxCalls = $.mockjax.mockedAjaxCalls();
53 | assert.equal(mockedAjaxCalls.length, 0, 'No mocked Ajax calls should have been returned');
54 | done();
55 | }
56 | });
57 | });
58 |
59 | t('Not equal multiple headers', function(assert) {
60 | var done = assert.async();
61 |
62 | $.mockjax({
63 | url: '/exact/string',
64 | requestHeaders: {
65 | Authorization: '12345',
66 | MyHeader: 'hello'
67 | },
68 | responseText: 'Exact headers'
69 | });
70 |
71 | $.ajax({
72 | url: '/exact/string',
73 | headers: {
74 | Authorization: '12345'
75 | },
76 | error: function() { assert.ok(true, 'Error called on bad request headers matching'); },
77 | success: function() { assert.ok(false, 'Success should not be called'); },
78 | complete: function() {
79 | var mockedAjaxCalls = $.mockjax.mockedAjaxCalls();
80 | assert.equal(mockedAjaxCalls.length, 0, 'No mocked Ajax calls should have been returned');
81 | done();
82 | }
83 | });
84 | });
85 |
86 | t('Exact headers keys and values', function(assert) {
87 | var done = assert.async();
88 |
89 | $.mockjax({
90 | url: '/exact/string',
91 | requestHeaders: {
92 | Authorization: '12345'
93 | },
94 | responseText: 'Exact headers'
95 | });
96 |
97 | $.ajax({
98 | url: '/exact/string',
99 | error: qunit.noErrorCallbackExpected,
100 | headers: {
101 | Authorization: '12345'
102 | },
103 | complete: function(xhr) {
104 | var mockedAjaxCalls = $.mockjax.mockedAjaxCalls();
105 | assert.equal(mockedAjaxCalls.length, 1, 'A mocked Ajax calls should have been returned');
106 | assert.equal(xhr.responseText, 'Exact headers', 'Exact headers keys and values');
107 | done();
108 | }
109 | });
110 | });
111 |
112 | t('Exact multiple headers keys and values', function(assert) {
113 | var done = assert.async();
114 |
115 | $.mockjax({
116 | url: '/exact/string',
117 | requestHeaders: {
118 | Authorization: '12345',
119 | MyHeader: 'hello'
120 | },
121 | responseText: 'Exact multiple headers'
122 | });
123 |
124 | $.ajax({
125 | url: '/exact/string',
126 | error: qunit.noErrorCallbackExpected,
127 | headers: {
128 | Authorization: '12345',
129 | MyHeader: 'hello'
130 | },
131 | complete: function(xhr) {
132 | var mockedAjaxCalls = $.mockjax.mockedAjaxCalls();
133 | assert.equal(mockedAjaxCalls.length, 1, 'A mocked Ajax calls should have been returned');
134 | assert.equal(xhr.responseText, 'Exact multiple headers', 'Exact headers keys and values');
135 | done();
136 | }
137 | });
138 | });
139 |
140 | })(window.QUnit, window.jQuery);
--------------------------------------------------------------------------------
/test/test-logging.js:
--------------------------------------------------------------------------------
1 | (function(qunit, $, sinon) {
2 | 'use strict';
3 |
4 | var t = qunit.test;
5 | var winLogger;
6 |
7 | // Note: currently sinon cannot stub object methods in this manner in IE
8 | // See GH issue: https://github.com/sinonjs/sinon/issues/1009
9 | // As such, we'll be skipping the logger tests for IE currently
10 | if (/MSIE/.test(navigator.userAgent)) {
11 | qunit.module('Logging');
12 |
13 | t('UNABLE TO TEST LOGGER IN IE', function(assert) {
14 | assert.ok(true, 'Cannot stub console functions with Sinon, see https://github.com/sinonjs/sinon/issues/1009');
15 | });
16 | return;
17 | }
18 |
19 | /* -------------------- */
20 | qunit.module( 'Logging', {
21 | /* -------------------- */
22 |
23 | beforeEach: function() {
24 | winLogger = {
25 | debug: sinon.stub(console, 'debug'),
26 | log: sinon.stub(console, 'log'),
27 | info: sinon.stub(console, 'info'),
28 | warn: sinon.stub(console, 'warn'),
29 | error: sinon.stub(console, 'error')
30 | };
31 | $.mockjaxSettings.logger = winLogger;
32 | $.mockjaxSettings.logging = 2;
33 | },
34 | afterEach: function() {
35 | winLogger.debug.restore();
36 | winLogger.log.restore();
37 | winLogger.info.restore();
38 | winLogger.warn.restore();
39 | winLogger.error.restore();
40 | }
41 | });
42 |
43 | t('Default log handler (window.console)', function(assert) {
44 | var done = assert.async();
45 |
46 | $.mockjax({
47 | url: '*'
48 | });
49 | $.ajax({
50 | url: '/console',
51 | type: 'GET',
52 | complete: function() {
53 | assert.ok(winLogger.info.calledWith('MOCK GET: /console'), 'Default log handler was not called');
54 | done();
55 | }
56 | });
57 | });
58 |
59 | t('Logging with high level', function(assert) {
60 | $.mockjaxSettings.logging = 4;
61 | $.mockjax._logger.debug({}, 'foobar');
62 | $.mockjax._logger.info({}, 'foobar');
63 | $.mockjax._logger.error({}, 'foobar');
64 | assert.ok(winLogger.debug.calledWith('foobar'), 'Log handler 4 was not called for debug');
65 | assert.ok(winLogger.info.calledWith('foobar'), 'Log handler 4 was not called for info');
66 | assert.ok(winLogger.error.calledWith('foobar'), 'Log handler 4 was not called for error');
67 | });
68 |
69 | t('Logging with low level', function(assert) {
70 | $.mockjaxSettings.logging = 0;
71 | $.mockjax._logger.debug({}, 'foobar');
72 | $.mockjax._logger.debug({ logging: 4 }, 'foobar');
73 | $.mockjax._logger.info({}, 'foobar');
74 | $.mockjax._logger.error({}, 'foobar');
75 | assert.strictEqual(winLogger.debug.callCount, 1, 'Log handler 0 was called too much for debug');
76 | assert.strictEqual(winLogger.info.callCount, 0, 'Log handler 0 was called for info');
77 | assert.ok(winLogger.error.calledWith('foobar'), 'Log handler 4 was not called for error');
78 | });
79 |
80 | t('Custom (deprecated) log handler', function(assert) {
81 | var done = assert.async();
82 |
83 | var msg = null;
84 | $.mockjaxSettings.log = function customLogger( mockHandler, requestSettings) {
85 | msg = mockHandler.url + ' - ' + requestSettings.type.toUpperCase() + ': ' + requestSettings.url;
86 | };
87 | $.mockjax({
88 | url: '*'
89 | });
90 | $.ajax({
91 | url: '/console',
92 | type: 'GET',
93 | complete: function() {
94 | assert.equal(msg, '* - GET: /console', 'Custom log handler was not called');
95 | done();
96 | }
97 | });
98 | });
99 |
100 | t('Disable logging via `logging: false`', function(assert) {
101 | var done = assert.async();
102 |
103 | $.mockjaxSettings.logging = false;
104 |
105 | $.mockjax({
106 | url: '*'
107 | });
108 | $.ajax({
109 | url: '/console',
110 | complete: function() {
111 | assert.strictEqual(winLogger.info.callCount, 0, 'Log called when disabled');
112 |
113 | $.mockjax._logger.debug({}, 'foo');
114 | assert.strictEqual(winLogger.debug.callCount, 0, 'Log called when disabled');
115 |
116 | done();
117 | }
118 | });
119 | });
120 |
121 | t('Disable logging per mock via `logging: false`', function(assert) {
122 | var done = assert.async();
123 |
124 | $.mockjax({
125 | url: '*',
126 | logging: false
127 | });
128 |
129 | $.ajax({
130 | url: '/console',
131 | complete: function() {
132 | assert.strictEqual(winLogger.info.callCount, 0, 'Log called when disabled');
133 |
134 | $.mockjax._logger.warn({}, 'foo');
135 | assert.strictEqual(winLogger.warn.callCount, 1, 'General log not called when disabled per mock');
136 |
137 | done();
138 | }
139 | });
140 | });
141 |
142 |
143 | })(window.QUnit, window.jQuery, window.sinon);
144 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | // http://www.jshint.com/docs/
3 | // Based on node-jshint@2.x.x
4 |
5 | "bitwise": true, //prohibits the use of bitwise operators such as ^ (XOR), | (OR) and others
6 | "camelcase": false, //force all variable names to use either camelCase style or UPPER_CASE with underscores
7 | "curly": true, //requires you to always put curly braces around blocks in loops and conditionals
8 | "eqeqeq": true, //prohibits the use of == and != in favor of === and !==
9 | "es3": false, //tells JSHint that your code needs to adhere to ECMAScript 3 specification
10 | "forin": false, //requires all `for in` loops to filter object's items with `hasOwnProperty()`
11 | "immed": true, //prohibits the use of immediate function invocations without wrapping them in parentheses
12 | "indent": 4, //enforces specific tab width
13 | "latedef": false, //prohibits the use of a variable before it was defined
14 | "newcap": true, //requires you to capitalize names of constructor functions
15 | "noarg": true, //prohibits the use of `arguments.caller` and `arguments.callee`
16 | "noempty": true, //warns when you have an empty block in your code
17 | "nonew": true, //prohibits the use of constructor functions for side-effects
18 | "plusplus": false, //prohibits the use of unary increment and decrement operators
19 | "quotmark": true, //enforces the consistency of quotation marks used throughout your code
20 | "undef": true, //prohibits the use of explicitly undeclared variables
21 | "unused": true, //warns when you define and never use your variables
22 | "strict": true, //requires all functions to run in ECMAScript 5's strict mode
23 | "trailing": true, //makes it an error to leave a trailing whitespace in your code
24 | "maxparams": 10, //set the max number of formal parameters allowed per function
25 | "maxdepth": 3, //control how nested do you want your blocks to be
26 | //"maxstatements": 0, //set the max number of statements allowed per function
27 | //"maxcomplexity": 0, //control cyclomatic complexity throughout your code
28 | "maxlen": 140, //set the maximum length of a line
29 |
30 | "asi": false, //suppresses warnings about missing semicolons
31 | "boss": false, //suppresses warnings about the use of assignments in cases where comparisons are expected
32 | "debug": false, //suppresses warnings about the debugger statements in your code
33 | "eqnull": false, //suppresses warnings about == null comparisons
34 | "esnext": false, //your code uses ES.next specific features such as const
35 | "evil": false, //suppresses warnings about the use of eval
36 | "expr": false, //suppresses warnings about the use of expressions where normally you would expect to see assignments or function calls
37 | "funcscope": false, //suppresses warnings about declaring variables inside of control structures while accessing them later from the outside
38 | "globalstrict": false, //suppresses warnings about the use of global strict mode
39 | "iterator": false, //suppresses warnings about the `__iterator__` property
40 | "lastsemic": false, //suppresses warnings about missing semicolons, but only when the semicolon is omitted for the last statement in a one-line block
41 | "laxbreak": false, //suppresses most of the warnings about possibly unsafe line breakings in your code
42 | "laxcomma": false, //suppresses warnings about comma-first coding style
43 | "loopfunc": false, //suppresses warnings about functions inside of loops
44 | "moz": false, //tells JSHint that your code uses Mozilla JavaScript extensions
45 | "multistr": false, //suppresses warnings about multi-line strings
46 | "proto": false, //suppresses warnings about the `__proto__` property
47 | "scripturl": true, //suppresses warnings about the use of script-targeted URLs—such as `javascript:...`
48 | "smarttabs": true, //suppresses warnings about mixed tabs and spaces when the latter are used for alignmnent only
49 | "shadow": false, //suppresses warnings about variable shadowing
50 | "sub": false, //suppresses warnings about using `[]` notation when it can be expressed in dot notation
51 | "supernew": false, //suppresses warnings about "weird" constructions like `new function () { ... }` and `new Object;`
52 | "validthis": true, //suppresses warnings about possible strict violations when the code is running in strict mode and you use `this` in a non-constructor function
53 |
54 | // ENVIRONMENTS / GLOBALS
55 |
56 | "browser": true,
57 | "jquery": true,
58 | "node": true,
59 | "globals": {
60 | "define": true,
61 | "ActiveXObject": true
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function(grunt) {
2 | 'use strict';
3 |
4 | // Project configuration
5 | grunt.initConfig({
6 | // Metadata
7 | pkg: grunt.file.readJSON('package.json'),
8 |
9 | banner: [
10 | '/*! <%= pkg.title || pkg.name %>',
11 | ' * A Plugin providing simple and flexible mocking of ajax requests and responses',
12 | ' * ',
13 | ' * Version: <%= pkg.version %>',
14 | ' * Home: <%= pkg.homepage %>',
15 | ' * Copyright (c) <%= grunt.template.today("yyyy") %> Jordan Kasper, formerly appendTo;',
16 | ' * NOTE: This repository was taken over by Jordan Kasper (@jakerella) October, 2014',
17 | ' * ',
18 | ' * Dual licensed under the MIT or GPL licenses.',
19 | ' * http://opensource.org/licenses/MIT OR http://www.gnu.org/licenses/gpl-2.0.html',
20 | ' */\n'
21 | ].join('\n'),
22 |
23 | // Task configuration
24 | concat: {
25 | options: {
26 | banner: '<%= banner %>',
27 | stripBanners: true
28 | },
29 | dist: {
30 | src: ['./src/jquery.mockjax.js'],
31 | dest: './dist/jquery.mockjax.js'
32 | }
33 | },
34 | uglify: {
35 | options: {
36 | preserveComments: 'some',
37 | },
38 | dist: {
39 | src: './dist/jquery.mockjax.js',
40 | dest: './dist/jquery.mockjax.min.js'
41 | }
42 | },
43 | jshint: {
44 | options: {
45 | jshintrc: true
46 | },
47 | all: [
48 | 'src/**/*.js',
49 | 'Gruntfile.js',
50 | 'test/test.js',
51 | 'test/requirejs/*.js',
52 | 'test/nodejs/*.js',
53 | 'test/browserify/main.js',
54 | 'test/browserify/test.js'
55 | ]
56 | },
57 | qunit: { all: [] }, // NOTE: these tests are all run by the `test` task below to run against each jQuery version supported
58 | test: {
59 | all: {
60 | jQueryVersions: [
61 | '1.5.2',
62 | '1.6.4',
63 | '1.7.2',
64 | '1.8.3',
65 | '1.9.1',
66 | '1.10.2',
67 | '1.11.3',
68 | '1.12.4',
69 | '2.0.3',
70 | '2.1.4',
71 | '2.2.4',
72 | '3.0.0',
73 | '3.1.0'
74 | ]
75 | },
76 | requirejs: {
77 | jQueryVersions: [
78 | '1.7.2',
79 | '1.8.3',
80 | '1.9.1',
81 | '1.10.2',
82 | '1.11.3',
83 | '1.12.4',
84 | '2.0.3',
85 | '2.1.4',
86 | '2.2.4',
87 | '3.0.0',
88 | '3.1.0'
89 | ]
90 | },
91 | latestInBranch: {
92 | jQueryVersions: [
93 | '1.12.4',
94 | '2.2.4',
95 | '3.1.0'
96 | ]
97 | },
98 | oldestAndLatest: {
99 | jQueryVersions: [
100 | '1.5.2',
101 | '1.12.4',
102 | '2.1.4',
103 | '3.1.0'
104 | ]
105 | },
106 | edge: {
107 | jQueryVersions: ['git']
108 | },
109 | dist: {
110 | file: 'dist-min.html',
111 | jQueryVersions: [
112 | '1.5.2',
113 | '1.6.4',
114 | '1.7.2',
115 | '1.8.3',
116 | '1.9.1',
117 | '1.10.2',
118 | '1.11.3',
119 | '1.12.4',
120 | '2.0.3',
121 | '2.1.4',
122 | '2.2.4',
123 | '3.0.0',
124 | '3.1.0'
125 | ]
126 | },
127 | browserify: {
128 | file: 'browserify/index.html',
129 | jQueryVersions: ['not-applicable']
130 | }
131 | },
132 | mochaTest: {
133 | nodejs: {
134 | src: ['./test/nodejs/*.js']
135 | }
136 | },
137 | browserify: {
138 | test: {
139 | src: 'test/browserify/main.js',
140 | dest: 'test/browserify/bundle.js'
141 | }
142 | },
143 | watch: {
144 | gruntfile: {
145 | files: './Gruntfile.js'
146 | },
147 | source: {
148 | files: './src/*.js',
149 | tasks: ['jshint', 'test:latestInBranch']
150 | }
151 | }
152 | });
153 |
154 | require('load-grunt-tasks')(grunt);
155 |
156 | grunt.registerTask('dev', ['jshint', 'test:all', 'test:requirejs', 'browserify', 'test:browserify', 'mochaTest']);
157 | grunt.registerTask('build', ['dev', 'concat', 'uglify', 'test:dist']);
158 | grunt.registerTask('default', ['dev']);
159 |
160 | grunt.registerTask('test', 'Executes QUnit tests with all supported jQuery versions', function() {
161 | var i, l,
162 | versionUrls = [],
163 | source = arguments[0] || 'all',
164 | versions = grunt.config.get('test' + ('.' + source) + '.jQueryVersions') || [],
165 | file = grunt.config.get('test' + ('.' + source) + '.file') || 'index.html';
166 |
167 | for (i=0, l=versions.length; i= 140, 'Correct delay simulation (' + delay + ')' );
91 | assert.strictEqual( executed, 1, 'Callback execution order correct');
92 | done();
93 | }
94 | });
95 | setTimeout(function() {
96 | assert.strictEqual( executed, 0, 'No premature callback execution');
97 | executed++;
98 | }, 30);
99 | });
100 |
101 | t('Response time with jsonp', function(assert) {
102 | var done = assert.async();
103 |
104 | var executed = false, ts = new Date();
105 |
106 | window.abcdef123456 = function() {};
107 |
108 | $.ajax({
109 | url: 'http://foobar.com/jsonp-delay?callback=?',
110 | dataType: 'jsonp',
111 | complete: function() {
112 | var delay = ((new Date()) - ts);
113 | // check against 140ms to allow for browser variance
114 | assert.ok( delay >= 140, 'Correct delay simulation (' + delay + ')' );
115 | assert.ok( executed, 'Callback execution order correct');
116 | window.abcdef123456 = null;
117 | done();
118 | }
119 | });
120 |
121 | setTimeout(function() {
122 | assert.ok( executed === false, 'No premature callback execution');
123 | executed = true;
124 | }, 30);
125 | });
126 |
127 | t('Response time with jsonp deferred response', function(assert) {
128 | var done = assert.async();
129 | var executed = false, ts = new Date();
130 |
131 | window.abcdef123456 = function() {};
132 |
133 | $.ajax({
134 | url: 'http://foobar.com/jsonp-delay?callback=?',
135 | dataType: 'jsonp'
136 | }).done(function() {
137 | var delay = ((new Date()) - ts);
138 | // check against 140ms to allow for browser variance
139 | assert.ok( delay >= 140, 'Correct delay simulation (' + delay + ')' );
140 | assert.ok( executed, 'Callback execution order correct');
141 | window.abcdef123456 = null;
142 | done();
143 | });
144 |
145 | setTimeout(function() {
146 | assert.ok( executed === false, 'No premature callback execution');
147 | executed = true;
148 | }, 30);
149 | });
150 |
151 | t('Response time with min and max values', function (assert) {
152 | var done = assert.async();
153 |
154 | var executed = 0,
155 | that = this,
156 | ts = new Date();
157 | $.ajax({
158 | url: '/variable-delay',
159 | complete: function () {
160 | var delay = ((new Date()) - ts);
161 | assert.ok(delay >= that.variableDelayMin, 'Variable delay greater than min; delay was ' + delay);
162 | assert.ok(delay <= (that.variableDelayMax + that.processingDuration), 'Variable delay less than max; delay was ' + delay);
163 | assert.equal(executed, 1, 'Callback execution order correct');
164 | done();
165 | }
166 | });
167 | setTimeout(function () {
168 | assert.strictEqual(executed, 0, 'No premature callback execution');
169 | executed++;
170 | }, 30);
171 | });
172 |
173 | t('Proxy asynchronous response time', function (assert) {
174 | var done = assert.async();
175 | var executed = false, ts = new Date();
176 |
177 | $.ajax({
178 | url: '/proxy',
179 | type: 'json',
180 | success: function () {
181 | var delay = ((new Date()) - ts);
182 | assert.ok( delay >= 50, 'Correct delay simulation (' + delay + ')' );
183 | assert.strictEqual(executed, false, 'No premature callback execution');
184 | executed = true;
185 | done();
186 | },
187 | error: qunit.noErrorCallbackExpected
188 | });
189 | setTimeout(function () {
190 | assert.strictEqual(executed, false, 'No premature callback execution');
191 | }, 30);
192 |
193 | });
194 |
195 | })(window.QUnit, window.jQuery);
196 |
--------------------------------------------------------------------------------
/test/test-data-types.js:
--------------------------------------------------------------------------------
1 | (function(qunit, $) {
2 | 'use strict';
3 |
4 | var t = qunit.test;
5 |
6 | /* ----------------------- */
7 | qunit.module( 'Data Types' );
8 | /* ----------------------- */
9 |
10 | t('Response returns text', function(assert) {
11 | var done = assert.async();
12 |
13 | $.mockjax({
14 | url: '/text',
15 | contentType: 'text/plain',
16 | responseText: 'just text'
17 | });
18 | $.ajax({
19 | url: '/text',
20 | dataType: 'text',
21 | error: qunit.noErrorCallbackExpected,
22 | complete: function(xhr) {
23 | assert.equal(xhr.getResponseHeader('Content-Type'), 'text/plain', 'Content type of text/plain');
24 |
25 | done();
26 | }
27 | });
28 | });
29 |
30 | t('Response returns html', function(assert) {
31 | var done = assert.async();
32 |
33 | $.mockjax({
34 | url: '/html',
35 | contentType: 'text/html',
36 | responseText: 'String
'
37 | });
38 | $.ajax({
39 | url: '/html',
40 | dataType: 'html',
41 | success: function(data) {
42 | assert.equal(data, 'String
', 'HTML String matches');
43 | },
44 | error: qunit.noErrorCallbackExpected,
45 | complete: function(xhr) {
46 | assert.equal(xhr.getResponseHeader('Content-Type'), 'text/html', 'Content type of text/html');
47 | done();
48 | }
49 | });
50 | });
51 |
52 | t('Response returns json', function(assert) {
53 | var done = assert.async();
54 |
55 | $.mockjax({
56 | url: '/json',
57 | contentType: 'text/json',
58 | responseText: { 'foo' : 'bar', 'baz' : { 'car' : 'far' } }
59 | });
60 | $.ajax({
61 | url: '/json',
62 | dataType: 'json',
63 | success: function(json) {
64 | assert.deepEqual(json, { 'foo' : 'bar', 'baz' : { 'car' : 'far' } }, 'JSON Object matches');
65 | },
66 | error: qunit.noErrorCallbackExpected,
67 | complete: function(xhr) {
68 | assert.equal(xhr.getResponseHeader('Content-Type'), 'text/json', 'Content type of text/json');
69 | done();
70 | }
71 | });
72 | });
73 |
74 | t('Response returns jsonp', function(assert) {
75 | var done = assert.async();
76 |
77 | $.mockjax({
78 | url: '/jsonp*',
79 | contentType: 'text/json',
80 | proxy: 'test_jsonp.js'
81 | });
82 | window.abcdef123456 = function(json) {
83 | assert.ok( true, 'JSONP Callback executed');
84 | assert.deepEqual(json, { 'data' : 'JSONP is cool' });
85 | };
86 |
87 | $.ajax({
88 | url: '/jsonp?callback=?',
89 | jsonpCallback: 'abcdef123456',
90 | dataType: 'jsonp',
91 | error: qunit.noErrorCallbackExpected,
92 | complete: function(xhr) {
93 | assert.equal(xhr.getResponseHeader('Content-Type'), 'text/json', 'Content type of text/json');
94 | window.abcdef123456 = null;
95 | done();
96 | }
97 | });
98 | });
99 |
100 | t('Response returns jsonp and return value from ajax is a promise if supported', function(assert) {
101 | var done = assert.async();
102 |
103 | window.rquery = /\?/;
104 |
105 | $.mockjax({
106 | url:'http://api*',
107 | responseText:{
108 | success:true,
109 | ids:[21327211]
110 | },
111 | dataType:'jsonp',
112 | contentType: 'text/json'
113 | });
114 |
115 | var promiseObject = $.ajax({
116 | url:'http://api.twitter.com/1/followers/ids.json?screen_name=test_twitter_user',
117 | dataType:'jsonp'
118 | });
119 |
120 | assert.ok(promiseObject.done && promiseObject.fail, 'Got Promise methods');
121 | promiseObject.then(function() {
122 | assert.ok(true, 'promise object then is executed');
123 | done();
124 | });
125 | });
126 |
127 | t('Response executes script', function(assert) {
128 | var done = assert.async();
129 |
130 | $.mockjax({
131 | url: '/script',
132 | contentType: 'text/plain',
133 | proxy: 'test_script.js'
134 | });
135 |
136 | window.TEST_SCRIPT_VAR = 0;
137 | $.ajax({
138 | url: '/script',
139 | dataType: 'script',
140 | error: qunit.noErrorCallbackExpected,
141 | complete: function(xhr) {
142 | assert.equal(window.TEST_SCRIPT_VAR, 1, 'Script executed');
143 | assert.equal(xhr.getResponseHeader('Content-Type'), 'text/plain', 'Content type of text/plain');
144 |
145 | done();
146 | }
147 | });
148 | });
149 |
150 | t('Grouping deferred responses, if supported', function(assert) {
151 | var done = assert.async();
152 |
153 | window.rquery = /\?/;
154 |
155 | $.mockjax({
156 | url:'http://api*',
157 | responseText:{
158 | success:true,
159 | ids:[21327211]
160 | },
161 | dataType:'jsonp',
162 | contentType: 'text/json'
163 | });
164 |
165 | var req1 = $.ajax({
166 | url:'http://api.twitter.com/1/followers/ids.json?screen_name=test_twitter_user',
167 | dataType:'jsonp'
168 | });
169 | var req2 = $.ajax({
170 | url:'http://api.twitter.com/1/followers/ids.json?screen_name=test_twitter_user',
171 | dataType:'jsonp'
172 | });
173 | var req3 = $.ajax({
174 | url:'http://api.twitter.com/1/followers/ids.json?screen_name=test_twitter_user',
175 | dataType:'jsonp'
176 | });
177 |
178 | $.when(req1, req2, req3).done(function() {
179 | assert.ok(true, 'Successfully grouped deferred responses');
180 | done();
181 | });
182 | });
183 |
184 | t('Response returns parsed XML', function(assert) {
185 | var done = assert.async();
186 |
187 | $.mockjax({
188 | url: '/xml',
189 | contentType: 'text/xml',
190 | responseXML: 'String'
191 | });
192 | $.ajax({
193 | url: '/xml',
194 | dataType: 'xml',
195 | success: function(xmlDom) {
196 | assert.ok( $.isXMLDoc( xmlDom ), 'Data returned is an XML DOM');
197 | },
198 | error: qunit.noErrorCallbackExpected,
199 | complete: function(xhr, error) {
200 | assert.ok(true, 'Error: ' + error);
201 | assert.equal(xhr.getResponseHeader('Content-Type'), 'text/xml', 'Content type of text/xml');
202 | done();
203 | }
204 | });
205 | });
206 |
207 | })(window.QUnit, window.jQuery);
208 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to Mockjax #
2 |
3 | First of all, thank you for helping make Mockjax the best plugin it can be! We truly
4 | appreciate the support. Before you submit that Pull Request, please be sure to
5 | follow these guidelines.
6 |
7 | ## Key Points
8 |
9 | * Write small, atomic commits with good messages
10 | * Writes tests (for both passing and failing conditions)
11 | * **Run** the tests (`grunt test`, but also in various browsers)
12 | * Generate a distribution build (`grunt build`)
13 | * Write a good PR!
14 |
15 |
16 | ## Accurately describe your code submission ##
17 |
18 | Be sure to identify everything that is within your pull request in the description.
19 | If you have code that fixes a bug and also cleans up some documentation, please
20 | specify both! Additionally, if your PR fixes or resolves a specific Github issue
21 | please reference it using the `#[id]` format so that the two can be linked!
22 |
23 | ### Commit messages ###
24 |
25 | Just as with the PR description, your commit messages should clearly identify what
26 | was included in that commit. Keep them short and sweet so we can just scan the
27 | titles of the commit and dig deeper if we need to.
28 |
29 | ### Smaller commits ###
30 |
31 | Along the same line, we would prefer to see different aspects of your PR in
32 | separate commits versus one big commit. So if you are submitting a PR that fixes a
33 | bug, updates the documentation, and cleans up some whitespace, please place all
34 | three of those things in **separate commits**! This allows us to roll back specific
35 | work if need be without destroying the entire contribution.
36 |
37 | ## Try to keep the style consistent ##
38 |
39 | As much as possible we need to try to keep the coding style consistent within the
40 | plugin. That means using the same indentation style, quotes, spacing, etc. Please
41 | try to keep your work in line with what is already in the library already, but
42 | feel free to ping someone in the Github issues if you have any questions about
43 | coding style generally.
44 |
45 | ## Add tests! ##
46 |
47 | We really need to see tests for any commit other than documentation. If you are
48 | fixing a bug add a breaking test first, then the code that fixes that test. If you
49 | are developing a new feature, add complete tests for the feature. That includes
50 | tests for success cases as well as failure cases!
51 |
52 | We use [QUnit](http://qunitjs.com/) as our testing tool of choice, so please write
53 | them using that API. For now you can simply add them to the `/test/test.js` file.
54 | There are `module`s in there, so try to add the tests in a logical location.
55 |
56 | ### RUN THE TESTS ###
57 |
58 | Due to the need to load some of the proxy files asynchronously, you'll need to view
59 | the test files over HTTP. You can do some initial testing with PhantomJS using the
60 | Grunt task, but you should also test in (multiple) browsers!
61 |
62 | #### To run from Grunt...
63 |
64 | Simply run:
65 |
66 | ```shell
67 | ~$ grunt test
68 | ```
69 |
70 | _Note that this will run all tests for all supported versions of jQuery!_
71 |
72 | #### To run in a browser...
73 |
74 | You should be able to run a small local server:
75 |
76 | Node:
77 | ```shell
78 | ~$ npm install -g http-server
79 | ~$ cd /path/to/mockjax
80 | mockjax/$ http-server -p 8080
81 | ```
82 |
83 | Python:
84 | ```shell
85 | ~$ cd /path/to/mockjax
86 | mockjax/$ python -m SimpleHTTPServer 8080
87 | ```
88 |
89 | PHP (5.4+):
90 | ```shell
91 | ~$ cd /path/to/mockjax
92 | mockjax/$ php -S localhost:8080
93 | ```
94 |
95 | Then just visit http://localhost:8080/test/index.html in the browser! Once there,
96 | be sure to **click through each of the jQuery versions in the header** to run the tests
97 | against each version. (If you have trouble running in different versions, make sure
98 | you are viewing `/test/index.html` not just `/test/` .)
99 |
100 | ### Run your tests everywhere ###
101 |
102 | Lastly, we'd like you to run your tests on as many browsers as possible. Check the
103 | main [README](README.md#browsers-tested) file for the browsers we support. If you
104 | don't have access to one of those browsers, try running the tests using a virtual
105 | machine or via a service like [BrowserStack](http://www.browserstack.com),
106 | [Sauce Labs](https://saucelabs.com), or [Modern.IE](https://www.modern.ie).
107 |
108 | ## Be sure to generate a build!
109 |
110 | Running the default `grunt` task will only lint and test the files, it does not
111 | produce a distribution as that isn't necessary most of the time. Instead, you
112 | should generate a new set of "dist" files before submitting your PR. To do this,
113 | just run `grunt build`
114 |
115 | ## Submit Your PR
116 |
117 | This is the last step! First, be sure you're merging with the correct branch! Version
118 | 2.0 of Mockjax will be the `master` branch very soon (hopefully we remember to update
119 | this message), but if you're submitting a bug fix, it should be submitted to the `v1.x`
120 | branch as well as `master` (if the bug exists in both).
121 |
122 | You should also write a good PR message with information on why this feature or fix is
123 | necesary or a good idea. For features, be sure to include information on _how to use_
124 | the feature; and for bugs, information on how to reproduce the bug is helpful!
125 |
126 | ## Publishing a Release
127 |
128 | Although individual contributors cannot publish a release, it's good to have
129 | documentation on what goes into that in case anyone needs to take over the process.
130 | Currently, @jakerella is the only one doing so.
131 |
132 | 1. Create a branch for the release (usually with the proposed version number in the name)
133 | 1. Ensure that all tests are passing in all supported browsers that you can (see below).
134 | 1. Update the `CHANGELOG.md`, `package.json` version, and any other necessary files.
135 | (Note that these can be in a commit, but put them in that new branch.)
136 | 1. Make sure to generate fresh dist files if necessary and commit those.
137 | 1. Submit a PR for the branch, this will initiate the Travis CI checks.
138 | 1. Ask others for input on the PR (mostly testing in their own browsers).
139 | 1. *If all is well*, merge the branch into `master`
140 | 1. Create a release on Github with a tag matching the version number and proper info.
141 | 1. Run `npm publish` on `master` to push the new version up to npm.
142 |
--------------------------------------------------------------------------------
/lib/qunit.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * QUnit 1.18.0
3 | * http://qunitjs.com/
4 | *
5 | * Copyright jQuery Foundation and other contributors
6 | * Released under the MIT license
7 | * http://jquery.org/license
8 | *
9 | * Date: 2015-04-03T10:23Z
10 | */
11 |
12 | /** Font Family and Sizes */
13 |
14 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
15 | font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
16 | }
17 |
18 | #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
19 | #qunit-tests { font-size: smaller; }
20 |
21 |
22 | /** Resets */
23 |
24 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
25 | margin: 0;
26 | padding: 0;
27 | }
28 |
29 |
30 | /** Header */
31 |
32 | #qunit-header {
33 | padding: 0.5em 0 0.5em 1em;
34 |
35 | color: #8699A4;
36 | background-color: #0D3349;
37 |
38 | font-size: 1.5em;
39 | line-height: 1em;
40 | font-weight: 400;
41 |
42 | border-radius: 5px 5px 0 0;
43 | }
44 |
45 | #qunit-header a {
46 | text-decoration: none;
47 | color: #C2CCD1;
48 | }
49 |
50 | #qunit-header a:hover,
51 | #qunit-header a:focus {
52 | color: #FFF;
53 | }
54 |
55 | #qunit-testrunner-toolbar label {
56 | display: inline-block;
57 | padding: 0 0.5em 0 0.1em;
58 | }
59 |
60 | #qunit-banner {
61 | height: 5px;
62 | }
63 |
64 | #qunit-testrunner-toolbar {
65 | padding: 0.5em 1em 0.5em 1em;
66 | color: #5E740B;
67 | background-color: #EEE;
68 | overflow: hidden;
69 | }
70 |
71 | #qunit-userAgent {
72 | padding: 0.5em 1em 0.5em 1em;
73 | background-color: #2B81AF;
74 | color: #FFF;
75 | text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
76 | }
77 |
78 | #qunit-modulefilter-container {
79 | float: right;
80 | padding: 0.2em;
81 | }
82 |
83 | .qunit-url-config {
84 | display: inline-block;
85 | padding: 0.1em;
86 | }
87 |
88 | .qunit-filter {
89 | display: block;
90 | float: right;
91 | margin-left: 1em;
92 | }
93 |
94 | /** Tests: Pass/Fail */
95 |
96 | #qunit-tests {
97 | list-style-position: inside;
98 | }
99 |
100 | #qunit-tests li {
101 | padding: 0.4em 1em 0.4em 1em;
102 | border-bottom: 1px solid #FFF;
103 | list-style-position: inside;
104 | }
105 |
106 | #qunit-tests > li {
107 | display: none;
108 | }
109 |
110 | #qunit-tests li.running,
111 | #qunit-tests li.pass,
112 | #qunit-tests li.fail,
113 | #qunit-tests li.skipped {
114 | display: list-item;
115 | }
116 |
117 | #qunit-tests.hidepass li.running,
118 | #qunit-tests.hidepass li.pass {
119 | visibility: hidden;
120 | position: absolute;
121 | width: 0px;
122 | height: 0px;
123 | padding: 0;
124 | border: 0;
125 | margin: 0;
126 | }
127 |
128 | #qunit-tests li strong {
129 | cursor: pointer;
130 | }
131 |
132 | #qunit-tests li.skipped strong {
133 | cursor: default;
134 | }
135 |
136 | #qunit-tests li a {
137 | padding: 0.5em;
138 | color: #C2CCD1;
139 | text-decoration: none;
140 | }
141 |
142 | #qunit-tests li p a {
143 | padding: 0.25em;
144 | color: #6B6464;
145 | }
146 | #qunit-tests li a:hover,
147 | #qunit-tests li a:focus {
148 | color: #000;
149 | }
150 |
151 | #qunit-tests li .runtime {
152 | float: right;
153 | font-size: smaller;
154 | }
155 |
156 | .qunit-assert-list {
157 | margin-top: 0.5em;
158 | padding: 0.5em;
159 |
160 | background-color: #FFF;
161 |
162 | border-radius: 5px;
163 | }
164 |
165 | .qunit-collapsed {
166 | display: none;
167 | }
168 |
169 | #qunit-tests table {
170 | border-collapse: collapse;
171 | margin-top: 0.2em;
172 | }
173 |
174 | #qunit-tests th {
175 | text-align: right;
176 | vertical-align: top;
177 | padding: 0 0.5em 0 0;
178 | }
179 |
180 | #qunit-tests td {
181 | vertical-align: top;
182 | }
183 |
184 | #qunit-tests pre {
185 | margin: 0;
186 | white-space: pre-wrap;
187 | word-wrap: break-word;
188 | }
189 |
190 | #qunit-tests del {
191 | background-color: #E0F2BE;
192 | color: #374E0C;
193 | text-decoration: none;
194 | }
195 |
196 | #qunit-tests ins {
197 | background-color: #FFCACA;
198 | color: #500;
199 | text-decoration: none;
200 | }
201 |
202 | /*** Test Counts */
203 |
204 | #qunit-tests b.counts { color: #000; }
205 | #qunit-tests b.passed { color: #5E740B; }
206 | #qunit-tests b.failed { color: #710909; }
207 |
208 | #qunit-tests li li {
209 | padding: 5px;
210 | background-color: #FFF;
211 | border-bottom: none;
212 | list-style-position: inside;
213 | }
214 |
215 | /*** Passing Styles */
216 |
217 | #qunit-tests li li.pass {
218 | color: #3C510C;
219 | background-color: #FFF;
220 | border-left: 10px solid #C6E746;
221 | }
222 |
223 | #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
224 | #qunit-tests .pass .test-name { color: #366097; }
225 |
226 | #qunit-tests .pass .test-actual,
227 | #qunit-tests .pass .test-expected { color: #999; }
228 |
229 | #qunit-banner.qunit-pass { background-color: #C6E746; }
230 |
231 | /*** Failing Styles */
232 |
233 | #qunit-tests li li.fail {
234 | color: #710909;
235 | background-color: #FFF;
236 | border-left: 10px solid #EE5757;
237 | white-space: pre;
238 | }
239 |
240 | #qunit-tests > li:last-child {
241 | border-radius: 0 0 5px 5px;
242 | }
243 |
244 | #qunit-tests .fail { color: #000; background-color: #EE5757; }
245 | #qunit-tests .fail .test-name,
246 | #qunit-tests .fail .module-name { color: #000; }
247 |
248 | #qunit-tests .fail .test-actual { color: #EE5757; }
249 | #qunit-tests .fail .test-expected { color: #008000; }
250 |
251 | #qunit-banner.qunit-fail { background-color: #EE5757; }
252 |
253 | /*** Skipped tests */
254 |
255 | #qunit-tests .skipped {
256 | background-color: #EBECE9;
257 | }
258 |
259 | #qunit-tests .qunit-skipped-label {
260 | background-color: #F4FF77;
261 | display: inline-block;
262 | font-style: normal;
263 | color: #366097;
264 | line-height: 1.8em;
265 | padding: 0 0.5em;
266 | margin: -0.4em 0.4em -0.4em 0;
267 | }
268 |
269 | /** Result */
270 |
271 | #qunit-testresult {
272 | padding: 0.5em 1em 0.5em 1em;
273 |
274 | color: #2B81AF;
275 | background-color: #D2E0E6;
276 |
277 | border-bottom: 1px solid #FFF;
278 | }
279 | #qunit-testresult .module-name {
280 | font-weight: 700;
281 | }
282 |
283 | /** Fixture */
284 |
285 | #qunit-fixture {
286 | position: absolute;
287 | top: -10000px;
288 | left: -10000px;
289 | width: 1000px;
290 | height: 1000px;
291 | }
--------------------------------------------------------------------------------
/test/test-mock-clearing.js:
--------------------------------------------------------------------------------
1 | (function(qunit, $) {
2 | 'use strict';
3 |
4 | var t = qunit.test;
5 |
6 | /* ---------------------------------- */
7 | qunit.module( 'Mock Handler Clearing' );
8 | /* ---------------------------------- */
9 |
10 | t('Remove mockjax definition by url', function(assert) {
11 | var done = assert.async();
12 |
13 | $.mockjax({
14 | url: '/test',
15 | contentType: 'text/plain',
16 | responseText: 'test'
17 | });
18 |
19 | $.mockjax({
20 | url: '*',
21 | contentType: 'text/plain',
22 | responseText: 'default'
23 | });
24 |
25 | $.ajax({
26 | url: '/test',
27 | success: function(text) {
28 | assert.equal(text, 'test', 'Test handler responded');
29 | },
30 | error: qunit.noErrorCallbackExpected,
31 | complete: function() {
32 | $.mockjax.clear('/test');
33 |
34 | // Reissue the request expecting the default handler
35 | $.ajax({
36 | url: '/test',
37 | success: function(text) {
38 | assert.equal(text, 'default', 'Default handler responded');
39 | },
40 | error: qunit.noErrorCallbackExpected,
41 | complete: function(xhr) {
42 | assert.equal(xhr.responseText, 'default', 'Default handler responded');
43 | done();
44 | }
45 | });
46 | }
47 | });
48 | });
49 |
50 | t('Remove mockjax definition by url (no default handler)', function(assert) {
51 | var done = assert.async();
52 |
53 | $.mockjax({
54 | url: '/foobar',
55 | contentType: 'text/plain',
56 | responseText: 'test'
57 | });
58 |
59 | $.ajax({
60 | url: '/foobar',
61 | success: function(text) {
62 | assert.equal(text, 'test', 'Test handler responded');
63 | },
64 | error: qunit.noErrorCallbackExpected,
65 | complete: function() {
66 | $.mockjax.clear('/foobar');
67 |
68 | // Reissue the request expecting the error
69 | $.ajax({
70 | url: '/foobar',
71 | success: function() {
72 | assert.ok(false, 'The mock was not cleared by url');
73 | },
74 | error: function(xhr) {
75 | // Test against 0, might want to look at this more in depth
76 | assert.ok(404 === xhr.status || 0 === xhr.status, 'The mock was cleared by url');
77 | done();
78 | }
79 | });
80 | }
81 | });
82 | });
83 |
84 | t('Attempt to clear a non-existent but similar url', function(assert) {
85 | var done = assert.async();
86 |
87 | $.mockjax({
88 | url: '/test',
89 | contentType: 'text/plain',
90 | responseText: 'test'
91 | });
92 |
93 | $.mockjax({
94 | url: '*',
95 | contentType: 'text/plain',
96 | responseText: 'default'
97 | });
98 |
99 | $.ajax({
100 | url: '/test',
101 | success: function(text) {
102 | assert.equal(text, 'test', 'Test handler responded');
103 | },
104 | error: qunit.noErrorCallbackExpected,
105 | complete: function() {
106 | $.mockjax.clear('/tes');
107 |
108 | $.ajax({
109 | url: '/test',
110 | success: function(text) {
111 | assert.equal(text, 'test', 'Test handler responded');
112 | },
113 | error: qunit.noErrorCallbackExpected,
114 | complete: done
115 | });
116 | }
117 | });
118 | });
119 |
120 | t('Remove mockjax definition, but not a subpath', function(assert) {
121 | var done = assert.async();
122 |
123 | $.mockjax({
124 | url: '/test',
125 | contentType: 'text/plain',
126 | responseText: 'test'
127 | });
128 |
129 | $.mockjax({
130 | url: '/test/foo',
131 | contentType: 'text/plain',
132 | responseText: 'foo'
133 | });
134 |
135 | $.ajax({
136 | url: '/test',
137 | success: function(text) {
138 | assert.equal(text, 'test', 'Test handler responded');
139 | },
140 | error: qunit.noErrorCallbackExpected,
141 | complete: function() {
142 | $.mockjax.clear('/test');
143 |
144 | $.ajax({
145 | url: '/test/foo',
146 | success: function(text) {
147 | assert.equal(text, 'foo', 'Test handler responded');
148 | },
149 | error: qunit.noErrorCallbackExpected,
150 | complete: done
151 | });
152 | }
153 | });
154 | });
155 |
156 | t('Remove mockjax definition by RegExp', function(assert) {
157 | var done = assert.async();
158 |
159 | $.mockjax({
160 | url: '/test',
161 | contentType: 'text/plain',
162 | responseText: 'test'
163 | });
164 |
165 | $.mockjax({
166 | url: '*',
167 | contentType: 'text/plain',
168 | responseText: 'default'
169 | });
170 |
171 | $.ajax({
172 | url: '/test',
173 | success: function(text) {
174 | assert.equal(text, 'test', 'Test handler responded');
175 | },
176 | error: qunit.noErrorCallbackExpected,
177 | complete: function() {
178 | $.mockjax.clear(/test/);
179 |
180 | // Reissue the request expecting the default handler
181 | $.ajax({
182 | url: '/test',
183 | success: function(text) {
184 | assert.equal(text, 'default', 'Default handler responded');
185 | },
186 | error: qunit.noErrorCallbackExpected,
187 | complete: function(xhr) {
188 | assert.equal(xhr.responseText, 'default', 'Default handler responded');
189 | done();
190 | }
191 | });
192 | }
193 | });
194 | });
195 |
196 | t('Remove several mockjax definition by RegExp', function(assert) {
197 | var done = assert.async();
198 |
199 | $.mockjax({
200 | url: '/test',
201 | contentType: 'text/plain',
202 | responseText: 'test'
203 | });
204 |
205 | $.mockjax({
206 | url: '/test1',
207 | contentType: 'text/plain',
208 | responseText: 'test'
209 | });
210 |
211 | $.mockjax({
212 | url: '/test/foo',
213 | contentType: 'text/plain',
214 | responseText: 'test'
215 | });
216 |
217 | $.mockjax({
218 | url: '*',
219 | contentType: 'text/plain',
220 | responseText: 'default'
221 | });
222 |
223 | $.ajax({
224 | url: '/test',
225 | success: function(text) {
226 | assert.equal(text, 'test', 'Test handler responded');
227 | },
228 | error: qunit.noErrorCallbackExpected,
229 | complete: function() {
230 | $.mockjax.clear(/test/);
231 |
232 | // Reissue the request expecting the default handler
233 | $.ajax({
234 | url: '/test',
235 | success: function(text) {
236 | assert.equal(text, 'default', 'Default handler responded');
237 | },
238 | error: qunit.noErrorCallbackExpected,
239 | complete: function(xhr) {
240 | assert.equal(xhr.responseText, 'default', 'Default handler responded');
241 | done();
242 | }
243 | });
244 | }
245 | });
246 | });
247 |
248 | t('Remove mockjax definition by id', function(assert) {
249 | var done = assert.async();
250 |
251 | var id = $.mockjax({
252 | url: '/test',
253 | contentType: 'text/plain',
254 | responseText: 'test'
255 | });
256 |
257 | $.mockjax({
258 | url: '*',
259 | contentType: 'text/plain',
260 | responseText: 'default'
261 | });
262 |
263 | $.ajax({
264 | url: '/test',
265 | success: function(text) {
266 | assert.equal(text, 'test', 'Test handler responded');
267 | },
268 | error: qunit.noErrorCallbackExpected,
269 | complete: function() {
270 | $.mockjax.clear(id);
271 |
272 | // Reissue the request expecting the default handler
273 | $.ajax({
274 | url: '/test',
275 | success: function(text) {
276 | assert.equal(text, 'default', 'Default handler responded');
277 | },
278 | error: qunit.noErrorCallbackExpected,
279 | complete: function(xhr) {
280 | assert.equal(xhr.responseText, 'default', 'Default handler responded');
281 | done();
282 | }
283 | });
284 | }
285 | });
286 | });
287 |
288 | t('Clearing mockjax removes all handlers', function(assert) {
289 | var done = assert.async();
290 |
291 | $.mockjax({
292 | url: '/api/example/1',
293 | responseText: 'test1'
294 | });
295 | $.mockjax({
296 | url: '/api/example/2',
297 | responseText: 'test2'
298 | });
299 |
300 | $.ajax({
301 | async: true,
302 | type: 'GET',
303 | url: '/api/example/1',
304 | success: function(text) {
305 | assert.equal('test1', text, 'First call is mocked');
306 | },
307 | error: qunit.noErrorCallbackExpected,
308 | complete: function() {
309 | $.mockjax.clear();
310 |
311 | $.ajax({
312 | async: true,
313 | type: 'GET',
314 | url: '/api/example/1',
315 | success: function() {
316 | assert.ok( false, 'Call to first endpoint was mocked, but should not have been');
317 | },
318 | error: function(xhr) {
319 | // Test against 0, might want to look at this more in depth
320 | assert.ok(404 === xhr.status || 0 === xhr.status, 'First mock cleared after clear()');
321 |
322 | $.ajax({
323 | async: true,
324 | type: 'GET',
325 | url: '/api/example/2',
326 | success: function() {
327 | assert.ok( false, 'Call to second endpoint was mocked, but should not have been');
328 | },
329 | error: function(xhr) {
330 | // Test against 0, might want to look at this more in depth
331 | assert.ok(404 === xhr.status || 0 === xhr.status, 'Second mock cleared after clear()');
332 | done();
333 | }
334 | });
335 | }
336 | });
337 | }
338 | });
339 | });
340 |
341 | })(window.QUnit, window.jQuery);
--------------------------------------------------------------------------------
/test/test-data-match.js:
--------------------------------------------------------------------------------
1 | (function(qunit, $) {
2 | 'use strict';
3 |
4 | var t = qunit.test;
5 |
6 | /* ---------------------------------- */
7 | qunit.module( 'Request Data Matching' );
8 | /* ---------------------------------- */
9 |
10 | t('Incorrect data matching on request', function(assert) {
11 | var done = assert.async();
12 |
13 | $.mockjax({
14 | url: '/response-callback',
15 | data: {
16 | foo: 'bar'
17 | }
18 | });
19 |
20 | $.ajax({
21 | url: '/response-callback',
22 | error: function() { assert.ok(true, 'Error called on bad mock/data matching'); },
23 | data: {
24 | bar: 'baz'
25 | },
26 | success: function() {
27 | assert.ok( false, 'Success should not be called' );
28 | },
29 | complete: done
30 | });
31 | });
32 |
33 | t('Correct data matching on request', function(assert) {
34 | var done = assert.async();
35 |
36 | $.mockjax({
37 | url: '/response-callback',
38 | contentType: 'text/json',
39 | data: {
40 | foo: 'bar'
41 | },
42 | responseText: {}
43 | });
44 |
45 | $.ajax({
46 | url: '/response-callback',
47 | error: qunit.noErrorCallbackExpected,
48 | data: {
49 | foo: 'bar'
50 | },
51 | success: function() {
52 | assert.ok( true, 'Successfully matched data' );
53 | },
54 | complete: done
55 | });
56 | });
57 |
58 | t('Correct data matching on request - request can have additional properties', function(assert) {
59 | var done = assert.async();
60 |
61 | $.mockjax({
62 | url: '/response-callback',
63 | data: {
64 | foo: 'bar'
65 | }
66 | });
67 |
68 | $.ajax({
69 | url: '/response-callback',
70 | error: qunit.noErrorCallbackExpected,
71 | data: {
72 | foo: 'bar',
73 | bar: 'baz'
74 | },
75 | success: function() {
76 | assert.ok(true, 'Success should not be called');
77 | },
78 | complete: done
79 | });
80 | });
81 |
82 | t('Bug #80: Correct data matching on request with empty object literals', function(assert) {
83 | var done = assert.async();
84 |
85 | $.mockjax({
86 | url: '/response-callback',
87 | contentType: 'text/json',
88 | data: {},
89 | responseText: {}
90 | });
91 |
92 | $.ajax({
93 | url: '/response-callback',
94 | error: qunit.noErrorCallbackExpected,
95 | data: {},
96 | success: function() {
97 | assert.ok( true, 'Successfully matched data' );
98 | },
99 | complete: done
100 | });
101 | });
102 |
103 | t('Correct matching on request without data and mocks with and without data but same url', function(assert) {
104 | var done = assert.async();
105 |
106 | $.mockjax({
107 | url: '/response-callback',
108 | data: {
109 | foo: 'bar'
110 | },
111 | responseText: 'false match'
112 | });
113 | $.mockjax({
114 | url: '/response-callback',
115 | responseText: 'correct match'
116 | });
117 | $.mockjax({
118 | url: '/response-callback',
119 | data: {
120 | bar: 'foo'
121 | },
122 | responseText: 'another false match'
123 | });
124 |
125 | $.ajax({
126 | url: '/response-callback',
127 | error: qunit.noErrorCallbackExpected,
128 | complete: function(xhr) {
129 | assert.equal(xhr.responseText, 'correct match', 'Matched with correct mock');
130 | done();
131 | }
132 | });
133 | });
134 |
135 | // Related issue #68
136 | t('Bug #68: Incorrect data matching on request with arrays', function(assert) {
137 | var done = assert.async();
138 |
139 | $.mockjax({
140 | url: '/response-callback',
141 | contentType: 'text/json',
142 | data: {
143 | values: []
144 | }
145 | });
146 |
147 | $.ajax({
148 | url: '/response-callback',
149 | error: function() {
150 | assert.ok( true, 'Error callback fired' );
151 | },
152 | data: {
153 | values: [1,2,3]
154 | },
155 | success: function() {
156 | assert.ok( false, 'Success callback fired' );
157 | },
158 | complete: done
159 | });
160 | });
161 |
162 | t('Correct data matching on request with arrays', function(assert) {
163 | var done = assert.async();
164 |
165 | $.mockjax({
166 | url: '/response-callback',
167 | contentType: 'text/json',
168 | data: {
169 | values: [1,2,3]
170 | },
171 | responseText: {}
172 | });
173 |
174 | $.ajax({
175 | url: '/response-callback',
176 | error: qunit.noErrorCallbackExpected,
177 | data: {
178 | values: [1,2,3]
179 | },
180 | success: function() {
181 | assert.ok(true, 'Success callback fired');
182 | },
183 | complete: done
184 | });
185 | });
186 |
187 |
188 | t('Multiple data matching requests', function(assert) {
189 | var done = assert.async();
190 |
191 | $.mockjax({
192 | url: '/response-callback',
193 | contentType: 'text/json',
194 | data: {
195 | remote: {
196 | test: function(data) {
197 | return data !== 'hello';
198 | }
199 | }
200 | },
201 | responseText: { 'yes?': 'no' }
202 | });
203 | $.mockjax({
204 | url: '/response-callback',
205 | contentType: 'text/json',
206 | data: {
207 | remote: {
208 | test: function(data) {
209 | return data === 'hello';
210 | }
211 | }
212 | },
213 | responseText: { 'yes?': 'yes' }
214 | });
215 |
216 | var callCount = 2;
217 | $.ajax({
218 | url: '/response-callback',
219 | error: qunit.noErrorCallbackExpected,
220 | dataType: 'json',
221 | data: {
222 | remote: 'h'
223 | },
224 | success: function(resp) {
225 | assert.deepEqual( resp, {'yes?': 'no'}, 'correct mock hander' );
226 | },
227 | complete: function() {
228 | callCount--;
229 |
230 | if (callCount <= 0) {
231 | done();
232 | }
233 | }
234 | });
235 | $.ajax({
236 | url: '/response-callback',
237 | error: qunit.noErrorCallbackExpected,
238 | data: {
239 | remote: 'hello'
240 | },
241 | dataType: 'json',
242 | success: function(resp) {
243 | assert.deepEqual( resp, {'yes?': 'yes'}, 'correct mock hander' );
244 | },
245 | complete: function() {
246 | callCount--;
247 |
248 | if (callCount <= 0) {
249 | done();
250 | }
251 | }
252 | });
253 | });
254 |
255 | t('Multiple data matching requests with data function', function(assert) {
256 | var done = assert.async();
257 |
258 | $.mockjax({
259 | url: '/response-callback',
260 | data: function(data) {
261 | return (data.answer === 'yes');
262 | },
263 | responseText: {'yes?': 'yes'}
264 | });
265 | $.mockjax({
266 | url: '/response-callback',
267 | data: function(data) {
268 | return (data.answer === 'yes');
269 | },
270 | responseText: {'yes?': 'yes'}
271 | });
272 |
273 | var callCount = 2;
274 | $.ajax({
275 | url: '/response-callback',
276 | data: {
277 | answer: 'yes'
278 | },
279 | success: function(resp) {
280 | assert.deepEqual( resp, {'yes?': 'yes'}, 'correct mock hander' );
281 | },
282 | complete: function() {
283 | callCount--;
284 |
285 | if (callCount <= 0) {
286 | done();
287 | }
288 | }
289 | });
290 | var notMatched = false;
291 | $.ajax({
292 | url: '/response-callback',
293 | data: {
294 | answer: 'no'
295 | },
296 | error: function() {
297 | notMatched = true;
298 | },
299 | complete: function() {
300 | callCount--;
301 | assert.ok( notMatched , 'correct data function' );
302 |
303 | if (callCount <= 0) {
304 | done();
305 | }
306 | }
307 | });
308 | });
309 |
310 | t('Bug #106: Null matching on request', function(assert) {
311 | var done = assert.async();
312 |
313 | $.mockjax({
314 | url: '/response-callback',
315 | contentType: 'text/json',
316 | data: {
317 | foo: 'bar',
318 | bar: null
319 | },
320 | responseText: {}
321 | });
322 |
323 | $.ajax({
324 | url: '/response-callback',
325 | error: qunit.noErrorCallbackExpected,
326 | data: {
327 | foo: 'bar',
328 | bar: null
329 | },
330 | success: function() {
331 | assert.ok( true, 'Successfully matched data that contained null values' );
332 | },
333 | complete: done
334 | });
335 | });
336 |
337 | t('Bug #123: match data in query format', function(assert) {
338 | var done = assert.async();
339 |
340 | $.mockjax({
341 | url: '/api/query',
342 | data: {
343 | foo: 'bar'
344 | },
345 | responseText: { foo: 'bar' }
346 | });
347 |
348 | $.ajax({
349 | url: '/api/query',
350 | data: 'foo=bar',
351 | success: function() {
352 | assert.ok(true, 'Successfully matched data');
353 | },
354 | error: qunit.noErrorCallbackExpected,
355 | complete: done
356 | });
357 | });
358 |
359 | t('Bug #123: match data in query format (two params)', function(assert) {
360 | var done = assert.async();
361 |
362 | $.mockjax({
363 | url: '/api/query',
364 | data: {
365 | foo: 'bar',
366 | bat: 'baz'
367 | },
368 | responseText: { foo: 'bar' }
369 | });
370 |
371 | $.ajax({
372 | url: '/api/query',
373 | data: 'foo=bar&bat=baz',
374 | success: function() {
375 | assert.ok(true, 'Successfully matched data');
376 | },
377 | error: qunit.noErrorCallbackExpected,
378 | complete: done
379 | });
380 | });
381 |
382 | t('Bug #123: don\'t match data in query format when not matching', function(assert) {
383 | var done = assert.async();
384 |
385 | $.mockjax({
386 | url: '/api/query',
387 | data: {
388 | foo: 'bar',
389 | bat: 'baz'
390 | },
391 | responseText: { foo: 'bar' }
392 | });
393 |
394 | $.ajax({
395 | url: '/api/query',
396 | data: 'foo=bar&bat=boo',
397 | success: function() {
398 | assert.ok(false, 'Should not have mocked request');
399 | },
400 | error: function() {
401 | assert.ok(true, 'Correctly failed to match mock');
402 | },
403 | complete: done
404 | });
405 | });
406 |
407 | t('Bug #123: match data in query format (array of params)', function(assert) {
408 | var done = assert.async();
409 |
410 | $.mockjax({
411 | url: '/api/query',
412 | data: {
413 | foo: ['bar', 'bat', 'baz']
414 | },
415 | responseText: { foo: 'bar' }
416 | });
417 |
418 | $.ajax({
419 | url: '/api/query',
420 | data: 'foo=bar&foo=bat&foo=baz',
421 | success: function() {
422 | assert.ok(true, 'Successfully matched data');
423 | },
424 | error: qunit.noErrorCallbackExpected,
425 | complete: done
426 | });
427 | });
428 |
429 | })(window.QUnit, window.jQuery);
--------------------------------------------------------------------------------
/dist/jquery.mockjax.min.js:
--------------------------------------------------------------------------------
1 | /*! jQuery Mockjax
2 | * A Plugin providing simple and flexible mocking of ajax requests and responses
3 | *
4 | * Version: 2.2.1
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
8 | *
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
11 | */
12 | !function(a,b){"use strict";if("function"==typeof define&&define.amd&&define.amd.jQuery)define(["jquery"],function(c){return b(c,a)});else{if("object"!=typeof exports)return b(a.jQuery||a.$,a);module.exports=b}}(this,function(a,b){"use strict";function c(c){void 0===b.DOMParser&&b.ActiveXObject&&(b.DOMParser=function(){},DOMParser.prototype.parseFromString=function(a){var b=new ActiveXObject("Microsoft.XMLDOM");return b.async="false",b.loadXML(a),b});try{var d=(new DOMParser).parseFromString(c,"text/xml");if(!a.isXMLDoc(d))throw new Error("Unable to parse XML");var e=a("parsererror",d);if(1===e.length)throw new Error("Error: "+a(d).text());return d}catch(b){var f=void 0===b.name?b:b.name+": "+b.message;return void a(document).trigger("xmlParseError",[f])}}function d(b,c){C.debug(b,["Checking mock data against request data",b,c]);var f=!0;if(a.isFunction(b))return!!b(c);if("string"==typeof c){if(a.isFunction(b.test))return b.test(c);if("object"!=typeof b)return b===c;c=e(c)}return a.each(b,function(e){return void 0===c[e]?f=!1:void("object"==typeof c[e]&&null!==c[e]?(f&&a.isArray(c[e])&&(f=a.isArray(b[e])&&c[e].length===b[e].length),f=f&&d(b[e],c[e])):f=b[e]&&a.isFunction(b[e].test)?f&&b[e].test(c[e]):f&&b[e]===c[e])}),f}function e(a){var b,c,d,e,f={},g=String(a).split(/&/);for(b=0,c=g.length;b=0}function i(b){if(a.isArray(b)&&2===b.length){var c=b[0],d=b[1];if(h(c)&&h(d))return Math.floor(Math.random()*(d-c))+c}else if(h(b))return b;return B}function j(b,d,e){C.debug(b,["Sending fake XHR request",b,d,e]);var g=function(f){return function(){return function(){this.status=b.status,this.statusText=b.statusText,this.readyState=1;var g=function(){this.readyState=4;var e;"json"===d.dataType&&"object"==typeof b.responseText?this.responseText=JSON.stringify(b.responseText):"xml"===d.dataType?"string"==typeof b.responseXML?(this.responseXML=c(b.responseXML),this.responseText=b.responseXML):this.responseXML=b.responseXML:"object"==typeof b.responseText&&null!==b.responseText?(b.contentType="application/json",this.responseText=JSON.stringify(b.responseText)):this.responseText=b.responseText,"number"!=typeof b.status&&"string"!=typeof b.status||(this.status=b.status),"string"==typeof b.statusText&&(this.statusText=b.statusText),e=this.onload||this.onreadystatechange,a.isFunction(e)?(b.isTimeout&&(this.status=-1),e.call(this,b.isTimeout?"timeout":void 0)):b.isTimeout&&(this.status=-1)};if(a.isFunction(b.response)){if(2===b.response.length)return void b.response(e,function(){g.call(f)});b.response(e)}g.call(f)}.apply(f)}}(this);b.proxy?(C.info(b,["Retrieving proxy file: "+b.proxy,b]),v({global:!1,url:b.proxy,type:b.proxyType,data:b.data,async:d.async,dataType:"script"===d.dataType?"text/plain":d.dataType,complete:function(a){b.responseXML=b.responseText=a.responseText,f(b,"status")&&(b.status=a.status),f(b,"statusText")&&(b.statusText=a.statusText),d.async===!1?g():this.responseTimer=setTimeout(g,i(b.responseTime))}})):d.async===!1?g():this.responseTimer=setTimeout(g,i(b.responseTime))}function k(b,c,d,e){return C.debug(b,["Creating new mock XHR object",b,c,d,e]),b=a.extend(!0,{},a.mockjaxSettings,b),"undefined"==typeof b.headers&&(b.headers={}),"undefined"==typeof c.headers&&(c.headers={}),b.contentType&&(b.headers["content-type"]=b.contentType),{status:b.status,statusText:b.statusText,readyState:1,open:function(){},send:function(){e.fired=!0,j.call(this,b,c,d)},abort:function(){clearTimeout(this.responseTimer)},setRequestHeader:function(a,b){c.headers[a]=b},getResponseHeader:function(a){return b.headers&&b.headers[a]?b.headers[a]:"last-modified"===a.toLowerCase()?b.lastModified||(new Date).toString():"etag"===a.toLowerCase()?b.etag||"":"content-type"===a.toLowerCase()?b.contentType||"text/plain":void 0},getAllResponseHeaders:function(){var c="";return b.contentType&&(b.headers["Content-Type"]=b.contentType),a.each(b.headers,function(a,b){c+=a+": "+b+"\n"}),c}}}function l(a,b,c){if(m(a),a.dataType="json",a.data&&z.test(a.data)||z.test(a.url)){p(a,b,c);var d=/^(\w+:)?\/\/([^\/?#]+)/,e=d.exec(a.url),f=e&&(e[1]&&e[1]!==location.protocol||e[2]!==location.host);if(a.dataType="script","GET"===a.type.toUpperCase()&&f){var g=n(a,b,c);return!g||g}}return null}function m(a){"GET"===a.type.toUpperCase()?z.test(a.url)||(a.url+=(/\?/.test(a.url)?"&":"?")+(a.jsonp||"callback")+"=?"):a.data&&z.test(a.data)||(a.data=(a.data?a.data+"&":"")+(a.jsonp||"callback")+"=?")}function n(b,c,d){C.debug(c,["Performing JSONP request",c,b,d]);var e=d&&d.context||b,f=a.Deferred?new a.Deferred:null;if(c.response&&a.isFunction(c.response))c.response(d);else if("object"==typeof c.responseText)a.globalEval("("+JSON.stringify(c.responseText)+")");else{if(c.proxy)return C.info(c,["Performing JSONP proxy request: "+c.proxy,c]),v({global:!1,url:c.proxy,type:c.proxyType,data:c.data,dataType:"script"===b.dataType?"text/plain":b.dataType,complete:function(d){a.globalEval("("+d.responseText+")"),o(b,c,e,f)}}),f;a.globalEval("("+("string"==typeof c.responseText?'"'+c.responseText+'"':c.responseText)+")")}return o(b,c,e,f),f}function o(b,c,d,e){var f;setTimeout(function(){if(q(b,d,c),r(b,d),e){try{f=a.parseJSON(c.responseText)}catch(a){}e.resolveWith(d,[f||c.responseText]),C.log(c,["JSONP mock call complete",c,e])}},i(c.responseTime))}function p(a,c,d){var e=d&&d.context||a,f="string"==typeof a.jsonpCallback&&a.jsonpCallback||"jsonp"+A++;a.data&&(a.data=(a.data+"").replace(z,"="+f+"$1")),a.url=a.url.replace(z,"="+f+"$1"),b[f]=b[f]||function(){q(a,e,c),r(a,e),b[f]=void 0;try{delete b[f]}catch(a){}},a.jsonpCallback=f}function q(b,c,d){b.success&&b.success.call(c,d.responseText||"","success",{}),b.global&&(b.context?a(b.context):a.event).trigger("ajaxSuccess",[{},b])}function r(b,c){b.complete&&b.complete.call(c,{statusText:"success",status:200},"success"),b.global&&(b.context?a(b.context):a.event).trigger("ajaxComplete",[{},b]),b.global&&!--a.active&&a.event.trigger("ajaxStop")}function s(b,c){var d,e,f,h;C.debug(null,["Ajax call intercepted",b,c]),"object"==typeof b?(c=b,b=void 0):(c=c||{},c.url=b||c.url),e=a.ajaxSetup({_origSettings:c},c),e.type=e.method=e.method||e.type,h=function(b,d){var e=c[b.toLowerCase()];return function(){a.isFunction(e)&&e.apply(this,[].slice.call(arguments)),d["onAfter"+b].apply(this,arguments)}};for(var i=0;i1?c.timeout=f.responseTime-1:(f.responseTime=2,c.timeout=1)),a.isFunction(f.onAfterSuccess)&&(c.success=h("Success",f)),a.isFunction(f.onAfterError)&&(c.error=h("Error",f)),a.isFunction(f.onAfterComplete)&&(c.complete=h("Complete",f)),t(f,c),function(b,c,e,f){d=v.call(a,a.extend(!0,{},e,{xhr:function(){return k(b,c,e,f)}}))}(f,e,c,w[i]),d);C.debug(w[i],["Mock does not match request",b,e])}if(C.log(null,["No mock matched to request",b,c]),a.mockjaxSettings.retainAjaxCalls&&y.push(c),a.mockjaxSettings.throwUnmocked===!0)throw new Error("AJAX not mocked: "+c.url);return v.apply(a,[c])}function t(a,b){if(a.url instanceof RegExp&&a.hasOwnProperty("urlParams")){var c=a.url.exec(b.url);if(1!==c.length){c.shift();var d=0,e=c.length,f=a.urlParams.length,g=Math.min(e,f),h={};for(d;d=')) {
192 | // The $.ajax() API changed in version 1.4 to include the third argument: xhr
193 | t('Success callback should have access to xhr object', function(assert) {
194 | var done = assert.async();
195 |
196 | $.mockjax({
197 | url: '/response'
198 | });
199 |
200 | $.ajax({
201 | type: 'GET',
202 | url: '/response',
203 | success: function() {
204 | assert.ok(arguments[2], 'there is a third argument to the success callback');
205 | assert.ok(arguments[2] && arguments[2].status === 200, 'third argument has proper status code');
206 | done();
207 | },
208 | error: function() {
209 | assert.ok(false, 'should not result in error');
210 | done();
211 | }
212 | });
213 | });
214 | }
215 |
216 | t('Dynamic response status callback', function(assert) {
217 | var done = assert.async();
218 |
219 | $.mockjax({
220 | url: '/response-callback',
221 | response: function() {
222 | this.status = 500;
223 | this.statusText = 'Internal Server Error';
224 | }
225 | });
226 |
227 | $.ajax({
228 | url: '/response-callback',
229 | dataType: 'text',
230 | data: {
231 | response: 'Hello world'
232 | },
233 | error: function() {
234 | assert.ok(true, 'error callback was called');
235 | },
236 | complete: function(xhr) {
237 | assert.equal(xhr.status, 500, 'Dynamically set response status matches');
238 |
239 | if( $.fn.jquery !== '1.5.2') {
240 | // This assertion fails in 1.5.2 due to this bug: http://bugs.jquery.com/ticket/9854
241 | // The statusText is being modified internally by jQuery in 1.5.2
242 | assert.equal(xhr.statusText, 'Internal Server Error', 'Dynamically set response statusText matches');
243 | }
244 |
245 | done();
246 | }
247 | });
248 | });
249 |
250 | t('Default Response Settings', function(assert) {
251 | var done = assert.async();
252 |
253 | $.mockjax({
254 | url: '/response-callback'
255 | });
256 |
257 | $.ajax({
258 | url: '/response-callback',
259 | dataType: 'text',
260 | data: {
261 | response: ''
262 | },
263 | complete: function(xhr) {
264 | assert.equal(xhr.status, 200, 'Response status matches default');
265 |
266 | if( $.fn.jquery !== '1.5.2') {
267 | // This assertion fails in 1.5.2 due to this bug: http://bugs.jquery.com/ticket/9854
268 | // The statusText is being modified internally by jQuery in 1.5.2
269 | assert.equal(xhr.statusText, 'OK', 'Response statusText matches default');
270 | }
271 |
272 | assert.equal(xhr.responseText.length, 0, 'responseText length should be 0');
273 | assert.equal(xhr.responseXml === undefined, true, 'responseXml should be undefined');
274 | done();
275 | }
276 | });
277 | });
278 |
279 | t('Throw new error when throwUnmocked is set to true and unmocked ajax calls are fired', function(assert) {
280 | var done = assert.async();
281 |
282 | $.mockjaxSettings.throwUnmocked = true;
283 |
284 | try {
285 | $.ajax({
286 | async: true,
287 | type: 'GET',
288 | url: '/api/example/1',
289 | complete: function() {
290 | assert.ok(false, 'Unmocked ajax request completed successfully and should have thrown an error.');
291 | done();
292 | }
293 | });
294 | }
295 | catch (e) {
296 | assert.ok(e instanceof Error, 'Error was not thrown with "throwUnmocked" set to true and existing unmocked ajax request');
297 | done();
298 | }
299 | });
300 |
301 | t('Get unfired handlers', function(assert) {
302 | var done = assert.async();
303 |
304 | $.mockjax({
305 | url: '/api/example/1'
306 | });
307 | $.mockjax({
308 | url: '/api/example/2'
309 | });
310 |
311 | $.ajax({
312 | async: false,
313 | type: 'GET',
314 | url: '/api/example/1',
315 | complete: function() {
316 | var handlersNotFired = $.mockjax.unfiredHandlers();
317 | assert.equal(handlersNotFired.length, 1, 'all mocks were fired');
318 | assert.equal(handlersNotFired[0].url, '/api/example/2', 'mockjax call has unexpected url');
319 | done();
320 | }
321 | });
322 | });
323 |
324 | t('Get unfired handlers after calling mockjax.clear', function(assert) {
325 | var done = assert.async();
326 |
327 | $.mockjax({
328 | url: '/api/example/1'
329 | });
330 | $.mockjax({
331 | url: '/api/example/2'
332 | });
333 | $.mockjax({
334 | url: '/api/example/3'
335 | });
336 |
337 | $.ajax({
338 | async: false,
339 | type: 'GET',
340 | url: '/api/example/1',
341 | complete: function() {
342 | $.mockjax.clear(2);
343 | var handlersNotFired = $.mockjax.unfiredHandlers();
344 | assert.equal(handlersNotFired.length, 1, 'all mocks were fired');
345 | assert.equal(handlersNotFired[0].url, '/api/example/2', 'mockjax call has unexpected url');
346 | done();
347 | }
348 | });
349 | });
350 |
351 | t('Response settings correct using PUT method', function(assert) {
352 | var done = assert.async();
353 |
354 | $.mockjax({
355 | url: '/put-request',
356 | type: 'PUT',
357 | responseText: 'this was a PUT'
358 | });
359 |
360 | $.ajax({
361 | url: '/put-request',
362 | type: 'PUT',
363 | dataType: 'text',
364 | complete: function(xhr) {
365 | assert.equal(xhr.status, 200, 'Response status matches default');
366 |
367 | assert.equal(xhr.responseText, 'this was a PUT', 'responseText is correct');
368 | done();
369 | }
370 | });
371 | });
372 |
373 | t('Preserve context when set in jsonp ajax requet', function(assert) {
374 | var done = assert.async();
375 |
376 | $.mockjax({
377 | url: '/jsonp*',
378 | contentType: 'text/json',
379 | proxy: 'test_jsonp.js'
380 | });
381 |
382 | window.abcdef123456 = function() {};
383 | var cxt = {context: 'context'};
384 |
385 | $.ajax({
386 | url: '/jsonp?callback=?',
387 | jsonpCallback: 'abcdef123456',
388 | dataType: 'jsonp',
389 | error: qunit.noErrorCallbackExpected,
390 | context: cxt})
391 | .done(function() {
392 | assert.deepEqual(this, cxt, 'this is equal to context object');
393 | window.abcdef123456 = null;
394 | done();
395 | });
396 | });
397 |
398 | t('Validate this is the $.ajax object if context is not set', function(assert) {
399 | var done = assert.async();
400 |
401 | $.mockjax({
402 | url: '/jsonp*',
403 | contentType: 'text/json',
404 | proxy: 'test_jsonp.js'
405 | });
406 |
407 | window.abcdef123456 = function() {};
408 |
409 | $.ajax({
410 | url: '/jsonp?callback=?',
411 | jsonpCallback: 'abcdef123456',
412 | dataType: 'jsonp',
413 | error: qunit.noErrorCallbackExpected
414 | })
415 | .done(function() {
416 | assert.ok(this.jsonp, '\'this\' is the $.ajax object for this request.');
417 | window.abcdef123456 = null;
418 | done();
419 | });
420 | });
421 |
422 | t('Dynamic mock definition', function(assert) {
423 | var done = assert.async();
424 |
425 | $.mockjax( function( settings ) {
426 | var service = settings.url.match(/\/users\/(.*)$/);
427 | if (service) {
428 | return {
429 | proxy: 'test_proxy.json'
430 | };
431 | }
432 | });
433 |
434 | $.ajax({
435 | url: '/users/test',
436 | dataType: 'json',
437 | error: qunit.noErrorCallbackExpected,
438 | success: function(json) {
439 | assert.ok(json && json.proxy, 'Proxy request succeeded');
440 | },
441 | complete: done
442 | });
443 | });
444 |
445 | t('Dynamic mock response generation', function(assert) {
446 | var done = assert.async();
447 |
448 | $.mockjax({
449 | url: '/response-callback',
450 | response: function() {
451 | this.responseText = { currentTime: 'now: ' + new Date() };
452 | }
453 | });
454 |
455 | $.ajax({
456 | url: '/response-callback',
457 | dataType: 'json',
458 | error: qunit.noErrorCallbackExpected,
459 | success: function(json) {
460 | assert.equal(typeof json.currentTime, 'string', 'Dynamic response succeeded');
461 | },
462 | complete: done
463 | });
464 | });
465 |
466 | t('Case-insensitive matching for request types', function(assert) {
467 | var done = assert.async();
468 |
469 | $.mockjax({
470 | url: '/case_insensitive_match',
471 | type: 'GET',
472 | responseText: 'uppercase type response'
473 | });
474 |
475 | $.ajax({
476 | url: '/case_insensitive_match',
477 | type: 'get',
478 | error: qunit.noErrorCallbackExpected,
479 | complete: function(xhr) {
480 | assert.equal(xhr.responseText, 'uppercase type response', 'Request matched regardless of case');
481 | done();
482 | }
483 | });
484 | });
485 |
486 | t('Inspecting $.mockjax.handler(id) after request has fired', function(assert) {
487 | var ID = $.mockjax({
488 | url: '/mockjax_properties',
489 | responseText: 'Hello Word'
490 | });
491 |
492 | $.ajax({
493 | url: '/mockjax_properties',
494 | complete: function() {}
495 | });
496 |
497 | assert.ok($.mockjax.handler(ID).fired, 'Sets the mock\'s fired property to true');
498 | });
499 |
500 | t('Inspecting $.mockjax() with multiple mocks argument', function(assert) {
501 | var done = assert.async();
502 | var handlers = $.mockjax([
503 | { url: '/response-callback', responseText: 'First' },
504 | { url: '/response-callback', responseText: 'Second' }
505 | ]);
506 |
507 | assert.equal(handlers.length, 2, 'Not enough mocks')
508 |
509 | var callCount = 2;
510 | $.ajax({
511 | url: '/response-callback',
512 | complete: function() {
513 | callCount--;
514 | if (callCount === 0) {
515 | done();
516 | }
517 | }
518 | });
519 | $.ajax({
520 | url: '/response-callback',
521 | complete: function() {
522 | callCount--;
523 | if (callCount === 0) {
524 | done();
525 | }
526 | }
527 | });
528 | });
529 |
530 | t('Inspecting $.mockjax() with empty multiple mocks argument', function(assert) {
531 | var done = assert.async();
532 | var handlers = $.mockjax([]);
533 |
534 | assert.equal(handlers.length, 0)
535 |
536 | $.ajax({
537 | url: '/response-callback',
538 | error: function() {
539 | done();
540 | }
541 | });
542 | });
543 |
544 | t('Inspecting $.mockjax() with null in multiple mocks argument', function(assert) {
545 | var done = assert.async();
546 | var handlers = $.mockjax([ null ]);
547 |
548 | assert.equal(handlers.length, 1)
549 |
550 | $.ajax({
551 | url: '/response-callback',
552 | error: function() {
553 | done();
554 | }
555 | });
556 | });
557 |
558 | t('Inspecting $.mockjax() with multiple mocks argument and reset handler', function(assert) {
559 | var done = assert.async();
560 | var handlers = $.mockjax([
561 | { url: '/rest', responseText: 'will be reset' }
562 | ]);
563 |
564 | assert.equal(handlers.length, 1);
565 | $.mockjax.clear(handlers[0]);
566 |
567 | $.ajax({
568 | url: '/response-callback',
569 | error: function() {
570 | done();
571 | }
572 | });
573 | });
574 |
575 | })(window.QUnit, window.jQuery);
576 |
--------------------------------------------------------------------------------
/lib/json2.js:
--------------------------------------------------------------------------------
1 | /*
2 | http://www.JSON.org/json2.js
3 | 2010-03-20
4 |
5 | Public Domain.
6 |
7 | NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
8 |
9 | See http://www.JSON.org/js.html
10 |
11 |
12 | This code should be minified before deployment.
13 | See http://javascript.crockford.com/jsmin.html
14 |
15 | USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
16 | NOT CONTROL.
17 |
18 |
19 | This file creates a global JSON object containing two methods: stringify
20 | and parse.
21 |
22 | JSON.stringify(value, replacer, space)
23 | value any JavaScript value, usually an object or array.
24 |
25 | replacer an optional parameter that determines how object
26 | values are stringified for objects. It can be a
27 | function or an array of strings.
28 |
29 | space an optional parameter that specifies the indentation
30 | of nested structures. If it is omitted, the text will
31 | be packed without extra whitespace. If it is a number,
32 | it will specify the number of spaces to indent at each
33 | level. If it is a string (such as '\t' or ' '),
34 | it contains the characters used to indent at each level.
35 |
36 | This method produces a JSON text from a JavaScript value.
37 |
38 | When an object value is found, if the object contains a toJSON
39 | method, its toJSON method will be called and the result will be
40 | stringified. A toJSON method does not serialize: it returns the
41 | value represented by the name/value pair that should be serialized,
42 | or undefined if nothing should be serialized. The toJSON method
43 | will be passed the key associated with the value, and this will be
44 | bound to the value
45 |
46 | For example, this would serialize Dates as ISO strings.
47 |
48 | Date.prototype.toJSON = function (key) {
49 | function f(n) {
50 | // Format integers to have at least two digits.
51 | return n < 10 ? '0' + n : n;
52 | }
53 |
54 | return this.getUTCFullYear() + '-' +
55 | f(this.getUTCMonth() + 1) + '-' +
56 | f(this.getUTCDate()) + 'T' +
57 | f(this.getUTCHours()) + ':' +
58 | f(this.getUTCMinutes()) + ':' +
59 | f(this.getUTCSeconds()) + 'Z';
60 | };
61 |
62 | You can provide an optional replacer method. It will be passed the
63 | key and value of each member, with this bound to the containing
64 | object. The value that is returned from your method will be
65 | serialized. If your method returns undefined, then the member will
66 | be excluded from the serialization.
67 |
68 | If the replacer parameter is an array of strings, then it will be
69 | used to select the members to be serialized. It filters the results
70 | such that only members with keys listed in the replacer array are
71 | stringified.
72 |
73 | Values that do not have JSON representations, such as undefined or
74 | functions, will not be serialized. Such values in objects will be
75 | dropped; in arrays they will be replaced with null. You can use
76 | a replacer function to replace those with JSON values.
77 | JSON.stringify(undefined) returns undefined.
78 |
79 | The optional space parameter produces a stringification of the
80 | value that is filled with line breaks and indentation to make it
81 | easier to read.
82 |
83 | If the space parameter is a non-empty string, then that string will
84 | be used for indentation. If the space parameter is a number, then
85 | the indentation will be that many spaces.
86 |
87 | Example:
88 |
89 | text = JSON.stringify(['e', {pluribus: 'unum'}]);
90 | // text is '["e",{"pluribus":"unum"}]'
91 |
92 |
93 | text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
94 | // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
95 |
96 | text = JSON.stringify([new Date()], function (key, value) {
97 | return this[key] instanceof Date ?
98 | 'Date(' + this[key] + ')' : value;
99 | });
100 | // text is '["Date(---current time---)"]'
101 |
102 |
103 | JSON.parse(text, reviver)
104 | This method parses a JSON text to produce an object or array.
105 | It can throw a SyntaxError exception.
106 |
107 | The optional reviver parameter is a function that can filter and
108 | transform the results. It receives each of the keys and values,
109 | and its return value is used instead of the original value.
110 | If it returns what it received, then the structure is not modified.
111 | If it returns undefined then the member is deleted.
112 |
113 | Example:
114 |
115 | // Parse the text. Values that look like ISO date strings will
116 | // be converted to Date objects.
117 |
118 | myData = JSON.parse(text, function (key, value) {
119 | var a;
120 | if (typeof value === 'string') {
121 | a =
122 | /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
123 | if (a) {
124 | return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
125 | +a[5], +a[6]));
126 | }
127 | }
128 | return value;
129 | });
130 |
131 | myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
132 | var d;
133 | if (typeof value === 'string' &&
134 | value.slice(0, 5) === 'Date(' &&
135 | value.slice(-1) === ')') {
136 | d = new Date(value.slice(5, -1));
137 | if (d) {
138 | return d;
139 | }
140 | }
141 | return value;
142 | });
143 |
144 |
145 | This is a reference implementation. You are free to copy, modify, or
146 | redistribute.
147 | */
148 |
149 | /*jslint evil: true, strict: false */
150 |
151 | /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
152 | call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
153 | getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
154 | lastIndex, length, parse, prototype, push, replace, slice, stringify,
155 | test, toJSON, toString, valueOf
156 | */
157 |
158 |
159 | // Create a JSON object only if one does not already exist. We create the
160 | // methods in a closure to avoid creating global variables.
161 |
162 | if (!this.JSON) {
163 | this.JSON = {};
164 | }
165 |
166 | (function () {
167 |
168 | function f(n) {
169 | // Format integers to have at least two digits.
170 | return n < 10 ? '0' + n : n;
171 | }
172 |
173 | if (typeof Date.prototype.toJSON !== 'function') {
174 |
175 | Date.prototype.toJSON = function (key) {
176 |
177 | return isFinite(this.valueOf()) ?
178 | this.getUTCFullYear() + '-' +
179 | f(this.getUTCMonth() + 1) + '-' +
180 | f(this.getUTCDate()) + 'T' +
181 | f(this.getUTCHours()) + ':' +
182 | f(this.getUTCMinutes()) + ':' +
183 | f(this.getUTCSeconds()) + 'Z' : null;
184 | };
185 |
186 | String.prototype.toJSON =
187 | Number.prototype.toJSON =
188 | Boolean.prototype.toJSON = function (key) {
189 | return this.valueOf();
190 | };
191 | }
192 |
193 | var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
194 | escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
195 | gap,
196 | indent,
197 | meta = { // table of character substitutions
198 | '\b': '\\b',
199 | '\t': '\\t',
200 | '\n': '\\n',
201 | '\f': '\\f',
202 | '\r': '\\r',
203 | '"' : '\\"',
204 | '\\': '\\\\'
205 | },
206 | rep;
207 |
208 |
209 | function quote(string) {
210 |
211 | // If the string contains no control characters, no quote characters, and no
212 | // backslash characters, then we can safely slap some quotes around it.
213 | // Otherwise we must also replace the offending characters with safe escape
214 | // sequences.
215 |
216 | escapable.lastIndex = 0;
217 | return escapable.test(string) ?
218 | '"' + string.replace(escapable, function (a) {
219 | var c = meta[a];
220 | return typeof c === 'string' ? c :
221 | '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
222 | }) + '"' :
223 | '"' + string + '"';
224 | }
225 |
226 |
227 | function str(key, holder) {
228 |
229 | // Produce a string from holder[key].
230 |
231 | var i, // The loop counter.
232 | k, // The member key.
233 | v, // The member value.
234 | length,
235 | mind = gap,
236 | partial,
237 | value = holder[key];
238 |
239 | // If the value has a toJSON method, call it to obtain a replacement value.
240 |
241 | if (value && typeof value === 'object' &&
242 | typeof value.toJSON === 'function') {
243 | value = value.toJSON(key);
244 | }
245 |
246 | // If we were called with a replacer function, then call the replacer to
247 | // obtain a replacement value.
248 |
249 | if (typeof rep === 'function') {
250 | value = rep.call(holder, key, value);
251 | }
252 |
253 | // What happens next depends on the value's type.
254 |
255 | switch (typeof value) {
256 | case 'string':
257 | return quote(value);
258 |
259 | case 'number':
260 |
261 | // JSON numbers must be finite. Encode non-finite numbers as null.
262 |
263 | return isFinite(value) ? String(value) : 'null';
264 |
265 | case 'boolean':
266 | case 'null':
267 |
268 | // If the value is a boolean or null, convert it to a string. Note:
269 | // typeof null does not produce 'null'. The case is included here in
270 | // the remote chance that this gets fixed someday.
271 |
272 | return String(value);
273 |
274 | // If the type is 'object', we might be dealing with an object or an array or
275 | // null.
276 |
277 | case 'object':
278 |
279 | // Due to a specification blunder in ECMAScript, typeof null is 'object',
280 | // so watch out for that case.
281 |
282 | if (!value) {
283 | return 'null';
284 | }
285 |
286 | // Make an array to hold the partial results of stringifying this object value.
287 |
288 | gap += indent;
289 | partial = [];
290 |
291 | // Is the value an array?
292 |
293 | if (Object.prototype.toString.apply(value) === '[object Array]') {
294 |
295 | // The value is an array. Stringify every element. Use null as a placeholder
296 | // for non-JSON values.
297 |
298 | length = value.length;
299 | for (i = 0; i < length; i += 1) {
300 | partial[i] = str(i, value) || 'null';
301 | }
302 |
303 | // Join all of the elements together, separated with commas, and wrap them in
304 | // brackets.
305 |
306 | v = partial.length === 0 ? '[]' :
307 | gap ? '[\n' + gap +
308 | partial.join(',\n' + gap) + '\n' +
309 | mind + ']' :
310 | '[' + partial.join(',') + ']';
311 | gap = mind;
312 | return v;
313 | }
314 |
315 | // If the replacer is an array, use it to select the members to be stringified.
316 |
317 | if (rep && typeof rep === 'object') {
318 | length = rep.length;
319 | for (i = 0; i < length; i += 1) {
320 | k = rep[i];
321 | if (typeof k === 'string') {
322 | v = str(k, value);
323 | if (v) {
324 | partial.push(quote(k) + (gap ? ': ' : ':') + v);
325 | }
326 | }
327 | }
328 | } else {
329 |
330 | // Otherwise, iterate through all of the keys in the object.
331 |
332 | for (k in value) {
333 | if (Object.hasOwnProperty.call(value, k)) {
334 | v = str(k, value);
335 | if (v) {
336 | partial.push(quote(k) + (gap ? ': ' : ':') + v);
337 | }
338 | }
339 | }
340 | }
341 |
342 | // Join all of the member texts together, separated with commas,
343 | // and wrap them in braces.
344 |
345 | v = partial.length === 0 ? '{}' :
346 | gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
347 | mind + '}' : '{' + partial.join(',') + '}';
348 | gap = mind;
349 | return v;
350 | }
351 | }
352 |
353 | // If the JSON object does not yet have a stringify method, give it one.
354 |
355 | if (typeof JSON.stringify !== 'function') {
356 | JSON.stringify = function (value, replacer, space) {
357 |
358 | // The stringify method takes a value and an optional replacer, and an optional
359 | // space parameter, and returns a JSON text. The replacer can be a function
360 | // that can replace values, or an array of strings that will select the keys.
361 | // A default replacer method can be provided. Use of the space parameter can
362 | // produce text that is more easily readable.
363 |
364 | var i;
365 | gap = '';
366 | indent = '';
367 |
368 | // If the space parameter is a number, make an indent string containing that
369 | // many spaces.
370 |
371 | if (typeof space === 'number') {
372 | for (i = 0; i < space; i += 1) {
373 | indent += ' ';
374 | }
375 |
376 | // If the space parameter is a string, it will be used as the indent string.
377 |
378 | } else if (typeof space === 'string') {
379 | indent = space;
380 | }
381 |
382 | // If there is a replacer, it must be a function or an array.
383 | // Otherwise, throw an error.
384 |
385 | rep = replacer;
386 | if (replacer && typeof replacer !== 'function' &&
387 | (typeof replacer !== 'object' ||
388 | typeof replacer.length !== 'number')) {
389 | throw new Error('JSON.stringify');
390 | }
391 |
392 | // Make a fake root object containing our value under the key of ''.
393 | // Return the result of stringifying the value.
394 |
395 | return str('', {'': value});
396 | };
397 | }
398 |
399 |
400 | // If the JSON object does not yet have a parse method, give it one.
401 |
402 | if (typeof JSON.parse !== 'function') {
403 | JSON.parse = function (text, reviver) {
404 |
405 | // The parse method takes a text and an optional reviver function, and returns
406 | // a JavaScript value if the text is a valid JSON text.
407 |
408 | var j;
409 |
410 | function walk(holder, key) {
411 |
412 | // The walk method is used to recursively walk the resulting structure so
413 | // that modifications can be made.
414 |
415 | var k, v, value = holder[key];
416 | if (value && typeof value === 'object') {
417 | for (k in value) {
418 | if (Object.hasOwnProperty.call(value, k)) {
419 | v = walk(value, k);
420 | if (v !== undefined) {
421 | value[k] = v;
422 | } else {
423 | delete value[k];
424 | }
425 | }
426 | }
427 | }
428 | return reviver.call(holder, key, value);
429 | }
430 |
431 |
432 | // Parsing happens in four stages. In the first stage, we replace certain
433 | // Unicode characters with escape sequences. JavaScript handles many characters
434 | // incorrectly, either silently deleting them, or treating them as line endings.
435 |
436 | text = String(text);
437 | cx.lastIndex = 0;
438 | if (cx.test(text)) {
439 | text = text.replace(cx, function (a) {
440 | return '\\u' +
441 | ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
442 | });
443 | }
444 |
445 | // In the second stage, we run the text against regular expressions that look
446 | // for non-JSON patterns. We are especially concerned with '()' and 'new'
447 | // because they can cause invocation, and '=' because it can cause mutation.
448 | // But just to be safe, we want to reject all unexpected forms.
449 |
450 | // We split the second stage into 4 regexp operations in order to work around
451 | // crippling inefficiencies in IE's and Safari's regexp engines. First we
452 | // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
453 | // replace all simple value tokens with ']' characters. Third, we delete all
454 | // open brackets that follow a colon or comma or that begin the text. Finally,
455 | // we look to see that the remaining characters are only whitespace or ']' or
456 | // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
457 |
458 | if (/^[\],:{}\s]*$/.
459 | test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
460 | replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
461 | replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
462 |
463 | // In the third stage we use the eval function to compile the text into a
464 | // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
465 | // in JavaScript: it can begin a block or an object literal. We wrap the text
466 | // in parens to eliminate the ambiguity.
467 |
468 | j = eval('(' + text + ')');
469 |
470 | // In the optional fourth stage, we recursively walk the new structure, passing
471 | // each name/value pair to a reviver function for possible transformation.
472 |
473 | return typeof reviver === 'function' ?
474 | walk({'': j}, '') : j;
475 | }
476 |
477 | // If the text is not JSON parseable, then a SyntaxError is thrown.
478 |
479 | throw new SyntaxError('JSON.parse');
480 | };
481 | }
482 | }());
483 |
484 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Patch motivation
2 | [Diff for All changes with last official version](https://github.com/vvscode/jquery-mockjax/compare/5c971d1f0db3b5e385bafbd3bd205ae6bccf81f0...HEAD)
3 |
4 | It pass at least one year till last activity at original repo. There are two PR's which add needed functionality, but thus they are outdated and were not updated long time - I make fork and patch myself.
5 |
6 | [How we use it on our projects to mock test data dymanicly](https://github.com/vvscode/code-notes/blob/master/socket-mocks/ajax-mock.js)
7 |
8 | Changes:
9 | - mockjaxHandler can get access to original settings via `requestSettings._origSettings` - that's allow to override callbacks if we need
10 | - `onAfter`-hooks now receive all params from handling callbacks ( see source code and PR's at original repo )
11 |
12 | I'll add all feature changes to the list above. I don't update version, so install package from branch/commit
13 |
14 |
15 | # jQuery Mockjax: Ajax request mocking #
16 | [http://github.com/jakerella/jquery-mockjax/](http://github.com/jakerella/jquery-mockjax/)
17 |
18 | [](https://www.codacy.com/app/mikehostetler_1249/jquery-mockjax/dashboard)
19 | [](https://travis-ci.org/jakerella/jquery-mockjax)
20 |
21 | **Note that we recently switched the `master` branch to version 2!** There are some
22 | minor breaking changes in v2, so if you need an older version, please check the
23 | [v1.x](https://github.com/jakerella/jquery-mockjax/tree/v1.x) branch or the list of
24 | [releases](https://github.com/jakerella/jquery-mockjax/tags) in Github.
25 |
26 | jQuery Mockjax provides request/response mocking for ajax requests using the
27 | jQuery API and provides all standard behaviors in the request/response flow.
28 |
29 | You may report any issues you may find [in the github issue tracking](https://github.com/jakerella/jquery-mockjax/issues).
30 |
31 | **Table of Contents**
32 |
33 | * [About Mockjax and Its History](#about-mockjax-and-its-history)
34 | * [Basic Documentation](#basic-documentation)
35 | * [API Methods](#api-methods)
36 | * [Overview: Your First Mock](#overview-your-first-mock)
37 | * [Mockjax in Depth](#mockjax-in-depth)
38 | * [Detailed Request and Response Definition](#detailed-request-and-response-definition)
39 | * [Defining a Request to Match](#defining-a-request-to-match)
40 | * [Defining Multiple Requests](#defining-multiple-requests)
41 | * [Defining a Response](#defining-a-response)
42 | * [Advanced Mocking Techniques](#advanced-mocking-techniques)
43 | * [Simulating Response Time and Latency](#simulating-response-time-and-latency)
44 | * [Simulating HTTP Response Statuses](#simulating-http-response-statuses)
45 | * [Setting the Content-Type](#setting-the-content-type)
46 | * [Setting Additional HTTP Response Headers](#setting-additional-http-response-headers)
47 | * [Dynamically Generating Mock Definitions](#dynamically-generating-mock-definitions)
48 | * [Accessing Request Headers](#accessing-request-headers)
49 | * [Forced Simulation of Server Timeouts](#forced-simulation-of-server-timeouts)
50 | * [Dynamically Generating Mock Responses](#dynamically-generating-mock-responses)
51 | * [Data Types](#data-types)
52 | * [Performing Actions After Request Completion](#performing-actions-after-request-completion)
53 | * [Globally Defining Mockjax Settings](#globally-defining-mockjax-settings)
54 | * [Setting a Global URL Namespace](#setting-global-url-namespace)
55 | * [Removing Mockjax Handlers](#removing-mockjax-handlers)
56 | * [Miscellaneous Information](#miscellaneous-information)
57 | * [jQuery Version Support](#jquery-version-support)
58 | * [Browsers Tested](#browsers-tested)
59 | * [Using Mockjax in Other Ways (Node, require, browserify, etc)](#using-mockjax-in-other-ways)
60 | * [Logging](#logging)
61 | * [Release History](#release-history)
62 | * [License](#license)
63 |
64 |
65 | ## About Mockjax and Its History ##
66 |
67 | Most backend developers are familiar with the concepts of [mocking
68 | objects](http://en.wikipedia.org/wiki/Mock_object) or stubbing in
69 | methods for unit testing. For those not familiar with mocking, it's the
70 | simulation of an interface or API for testing or integration development
71 | purposes. Mocking with front-end development though is still quite new. Mockjax
72 | gives front end developers the ability to define ajax requests that should be
73 | mocked out, as well as how those requests should be responded to. These mocks
74 | can be extremely simple or quite complex, representing the entire request-response
75 | workflow.
76 |
77 | At appendTo we developed a lot of applications which use
78 | [RESTFUL](http://en.wikipedia.org/wiki/Representational_State_Transfer)
79 | web services, but much of the time those services are not yet created.
80 | We spec out the service contract and data format at the beginning of a project
81 | and develop the front-end interface against mock data while the back end team
82 | builds the production services.
83 |
84 | This plugin was originally developed by appendTo in March 2010 and the
85 | [team](http://twitter.com/appendto/team) has been using it in many projects since.
86 |
87 |
88 | ## Basic Documentation ##
89 |
90 | ### API Methods ###
91 |
92 | Mockjax consists of just a few methods, each listed below. You'll find plenty of
93 | examples in the sections below, but if you're looking for a specific option,
94 | checkout this list:
95 |
96 | * `Number $.mockjax(/* Object */ options)`
97 | * Sets up a mockjax handler for a matching request
98 | * Returns that handler's index, can be used to clear individual handlers
99 | * `options`: [Object] Defines the settings to use for the mocked request
100 | * `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
101 | * `data`: [Object | Function] In addition to the URL, match parameters
102 | * `type`: [String] Specify what HTTP method to match, usually GET or POST. Case-insensitive, so `get` and `post` also work
103 | * `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!**)
104 | * `status`: [Number] An integer that specifies a valid server response code. This simulates a server response code
105 | * `statusText`: [String] Specifies a valid server response code description. This simulates a server response code description
106 | * `responseTime`: [Number] An integer that specifies a simulated network
107 | and server latency (in milliseconds). Default is `500`. Setting this
108 | to `0` will minimize the simulated latency
109 | * `isTimeout`: [Boolean] Determines whether or not the mock will force a timeout on the request
110 | * `contentType`: [String] Specifies the content type for the response
111 | * `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)
112 | * `responseText`: [String] Specifies the mocked text, or a mocked object literal, for the request
113 | * `responseXML`: [String] Specifies the mocked XML for the request
114 | * `proxy`: [String] Specifies a path to a file, from which the contents will be returned for the request
115 | * `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
116 | * `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))
117 | * `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
118 | * `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
119 | * `onAfterComplete`: [Function] Similar to onAfterSuccess, but will be executed after the complete method has been called
120 | * `Object $.mockjax.handler(/* Number */ id)`
121 | * Returns the mock request settings for the handler with the provided `id`
122 | * `void $.mockjax.clear([/* Number */ id])`
123 | * If the `id` is provided, the handler with that ID is cleared (that is, requests matching it will no longer do so, the handler is completely removed)
124 | * If no `id` is provided, all handlers are cleared, resetting Mockjax to its initial state
125 | * `Array