├── .codeclimate.yml ├── .editorconfig ├── .gitignore ├── .jshintrc ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Gruntfile.js ├── README.md ├── bower.json ├── dist ├── jquery.mockjax.js └── jquery.mockjax.min.js ├── lib ├── jquery-1.10.2.js ├── jquery-1.11.3.js ├── jquery-1.12.4.js ├── jquery-1.3.2.js ├── jquery-1.4.4.js ├── jquery-1.5.2.js ├── jquery-1.6.4.js ├── jquery-1.7.2.js ├── jquery-1.8.3.js ├── jquery-1.9.1.js ├── jquery-2.0.3.js ├── jquery-2.1.4.js ├── jquery-2.2.4.js ├── jquery-3.0.0.js ├── jquery-3.1.0.js ├── jquery.xmldom.js ├── json2.js ├── qunit.css ├── qunit.js ├── require.js └── semver.js ├── package.json ├── src └── jquery.mockjax.js └── test ├── browserify ├── index.html ├── main.js └── test.js ├── dist-min.html ├── index.html ├── jquery.js ├── nodejs └── test.js ├── requirejs ├── index.html ├── run_tests.js └── test_module.js ├── test-bugs.js ├── test-connection.js ├── test-core.js ├── test-data-match.js ├── test-data-types.js ├── test-header-match.js ├── test-headers.js ├── test-logging.js ├── test-mock-clearing.js ├── test-namespace.js ├── test-retaining-ajax-calls.js ├── test-setup.js ├── test-timeout.js ├── test-url-match.js ├── test.json ├── test_jsonp.js ├── test_proxy.json ├── test_proxy.xml └── test_script.js /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | languages: 2 | JavaScript: true 3 | exclude_paths: 4 | - dist/* 5 | - lib/* 6 | - test/* -------------------------------------------------------------------------------- /.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 -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2016-09-15 v2.2.1 2 | * Add tests for jQuery 3.x 3 | * Add tests for Browserify usage and documentation on that subject 4 | * Updated dependencies for webpack, etc usage (thanks @hotoo) 5 | * Updated keywords to be picked up by jQuery plugin registry 6 | 7 | ## 2016-06-08 v2.2.0 8 | * Fix bower dependency on jQuery to allow any supported version 9 | * Allow developer to indicate that ajax calls should _not_ be retained (thanks @suchipi) 10 | * Fix to allow responseTime to work with deferred jsonp 11 | * Updated to test on latest jQuery versions 12 | * Added JSDoc3 blocks to public API methods 13 | * Refactored logging: now has levels, easier to overwrite, more messages 14 | * Added ability for `data` matching to be a function (thanks @koorgoo) 15 | * Added ability to pass in array of mocks in addition to singles (thanks again @koorgoo) 16 | 17 | ## 2016-02-07 v2.1.1 18 | * Reorganize test cases into separate files for ease of maintenance and testing 19 | * Fix #86: JSONP return data treated as JSON 20 | * Added jQuery 1.12.0 to test quite 21 | * Fix #105: Using XML files as proxies 22 | * Fix #267: Handle undefined URL argument correctly 23 | * Fix #123: Handle query string formatted data option 24 | 25 | ## 2016-01-23 26 | * Updated to version 2.1.0 27 | * Removed unused testswarm files 28 | * Added test step in build process for dist file 29 | * Refactor tests to be easier to maintain and conform to current QUnit standards 30 | * Added global URL namespace feature (thanks @danpaz) 31 | * Added clearing of mocks by URL and regex (thanks @stas-vilchik) 32 | * Use async setting for proxy data (thanks @udnisap) 33 | * Update tests to jQuery 2.2.0 and fix for latest in jQuery git (thanks Simon and @gyoshev) 34 | * Fixed #136: cross domain requests 35 | * Updated contributing documentation to clearly state process for a release 36 | 37 | ## 2015-06-11 38 | * Updated to version 2.0.1 39 | * Fixed name in package.json for coordination among package management systems 40 | 41 | ## 2015-06-11 42 | * Updated to version 2.0.0 43 | * Fixed issue with isTimeout switch 44 | 45 | ## 2015-05-03 46 | * Updated to version 2.0.0-beta 47 | * Reorganized codebase and implemented Grunt build process 48 | * Implemented automated QUnit tests via Grunt and "shortcut" button in web tests 49 | * Added JSHint task for catching issues earlier 50 | * Implemented UMD pattern for use with require, browser, Node, etc 51 | * Removesd support for jQuery < 1.5.x 52 | * Removed deprecated `$.mockjaxClear()` method in favor of `$.mockjax.clear()` 53 | * Fixed numerous bugs (see issues for more info) 54 | * Refactored tests a bit (needs a lot more) 55 | * Added Travis CI for tests and Codacy for static code analysis 56 | 57 | ## 2015-04-08 58 | * Updated to version 1.6.2 59 | * Update jQuery library test versions on both 1.x and 2.x branches 60 | * Remove duplicate "repositories" value in package.json (@wfortin) 61 | * Remove undefined "head" variable in jsonp request mocking 62 | * Added async `response` function ability 63 | * Added ability to specify range for responseTime with random selection 64 | * Reorganized documentation significantly 65 | 66 | ## 2014-10-29 67 | * Updated to version 1.6.1 68 | * Changed all references to appendTo to point to github.com/jakerella (new owner) 69 | * removed unused testswarm files 70 | 71 | ## 2014-10-09 72 | * Updated to version 1.6.0 73 | * Added `unfiredHandlers()` and `unmockedAjaxCalls()` 74 | * Numerous bug fixes and breaking tests 75 | * Internal method cleanup 76 | * Switched to throwing proper `Error` objects 77 | * Switched to tab indentation everywhere 78 | * Added `main` field to package.json 79 | * Fixed responseTime for jsonp and allowed for variable setting with array min/max 80 | * Added `onAfterXxxxx` callbacks 81 | * Updated `$.mockjaxClear()` to be `$.mockjax.clear()` with deprecation notice 82 | * Complete README documentation overhaul 83 | * Fixed issue with Async actions in response callback 84 | * Added "contributing" documentation 85 | 86 | ## 2014-08-14 87 | * Spelling corrections in README.md 88 | * Update to newest version of QUnit (JS & CSS) and fixes for doing so 89 | * Added further versions of jQuery to test with 90 | * Added some tests for various issues and split out some tests for atomicity 91 | * Fixed dataType check for JSONP (case insensitive) 92 | * ensure request `data` matching occurs when url is matched and no data matching is required 93 | 94 | ## 2013-09-28 95 | * Fixed issue with proxy data and status codes (Thanks [Andrew Goodale](https://github.com/newyankeecodeshop)!) 96 | * TODO: Update this file with all changes since previous version 97 | 98 | ## 2012-05-30 99 | * Updated to version 1.5.2 100 | * Added support for jQuery 1.8, 1.9, 2.0 101 | * TODO: Update this file with all changes since previous version 102 | 103 | ## 2011-03-25 Jonathan Sharp (http://jdsharp.com) 104 | * Updating jQuery 1.5rc1 to 1.5.1 105 | * Adding TestSwarm support 106 | 107 | ## 2011-02-03 Jonathan Sharp (http://jdsharp.com) 108 | * Added log setting to intercept or disable logging messages 109 | * Added proxyType setting to force request type when proxying a mock 110 | * Added 29 unit tests for jQuery 1.3 through 1.5 111 | * Fixed issue #4 - Compatibility with jQuery 1.3 112 | * Fixed issue #10 - Undefined contents 113 | * Fixed issue #15 - proxy setting request type 114 | * Fixed issue #16 - proxy setting request type 115 | * Fixed issue #17 - jsonp request handling 116 | * Fixed issue #18 - Unit test fail with jQuery 1.5 117 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 $.mockjax.mockedAjaxCalls()` 126 | * Returns an array of all mocked ajax calls with each entry being the request settings object as passed into the `$.mockjax()` function 127 | * If `$.mockjaxSettings.retainAjaxCalls is set to false, this will always be empty 128 | * `Array $.mockjax.unfiredHandlers()` 129 | * Returns an array of all mock handler settings that have not been used. In other words, if a handler has been used for a `$.ajax()` call then it will _not_ appear in this array 130 | * `Array $.mockjax.unmockedAjaxCalls()` 131 | * Returns an array of all unmocked Ajax calls that were made. The array contains the settings object passed into `$.ajax({...})` 132 | * If `$.mockjaxSettings.retainAjaxCalls is set to false, this will always be empty 133 | * `void $.mockjax.clearRetainedAjaxCalls()` 134 | * Empties the arrays returned by `$.mockjax.mockedAjaxCalls` and `$.mockjax.unmockedAjaxCalls` 135 | 136 | ### Overview: Your First Mock ### 137 | 138 | Our first example will be for a simple REST service for a fortune app 139 | with the REST endpoint being `/restful/fortune` which returns the 140 | following JSON message: 141 | 142 | ```json 143 | { 144 | "status": "success", 145 | "fortune" : "Are you a turtle?" 146 | } 147 | ``` 148 | 149 | To pull the fortune into our page, we'd use the following HTML and jQuery 150 | code: 151 | 152 | ```html 153 | 154 | 155 | 156 | Fortune App 157 | 158 | 159 | 160 |
161 | 162 | 163 | ``` 164 | ```javascript 165 | $.getJSON("/restful/fortune", function(response) { 166 | if ( response.status == "success") { 167 | $("#fortune").html( "Your fortune is: " + response.fortune ); 168 | } else { 169 | $("#fortune").html( "Things do not look good, no fortune was told" ); 170 | } 171 | }); 172 | ``` 173 | 174 | At this point if we were to run this code it would fail since the REST 175 | service has yet to be implemented. This is where the benefit of the 176 | Mockjax plugin starts to pay off. The first step in using Mockjax is to 177 | include the plugin by just adding a regular script tag: 178 | 179 | ```html 180 | 181 | ... 182 | 183 | 184 | ``` 185 | 186 | Once you have that included, you can start intercepting Ajax requests 187 | and mocking the responses. So let's mock out the service by including 188 | the following code: 189 | 190 | ```javascript 191 | $.mockjax({ 192 | url: "/restful/fortune", 193 | responseText: { 194 | status: "success", 195 | fortune: "Are you a mock turtle?" 196 | } 197 | }); 198 | ``` 199 | 200 | **Defining a JSON string inline requires a `JSON.stringify()` method to be 201 | available. For some browsers you may need to include 202 | [json2.js](https://raw.github.com/douglascrockford/JSON-js/master/json2.js), 203 | which is included in the `lib` folder.** However, you could also simply 204 | provide an already stringified version of your JSON in the `responseText` 205 | property. 206 | 207 | _If you plan on mocking xml responses, you may also have to include 208 | `jquery.xmldom.js`, which can also be found in the `lib` folder._ 209 | 210 | ### Mockjax in Depth ### 211 | 212 | What Mockjax does at this point is replace the `$.ajax()` method with a 213 | wrapper that transparently checks the URL being requested. If the URL 214 | matches one defined by `$.mockjax()`, it intercepts the request 215 | and sets up a mock `XMLHttpRequest` object before executing the 216 | `jQuery.ajax()` handler. Otherwise, the request is handed back to the 217 | native `$.ajax()` method for normal execution. One benefit in this 218 | implementation detail is that by simulating the `XMLHttpRequest` object, 219 | the plugin continues to make use of jQuery's native ajax handling, so 220 | there are no concerns with implementing a custom Ajax workflow. 221 | 222 | As you write code to mock responses, there's great value in the fact that 223 | there are no modifications required to production code. The mocks can be 224 | transparently inserted. This provides easy integration into most 225 | frameworks by including the plugin and mock definitions through your 226 | build framework. It's also possible to include it at run time by 227 | listening for a query string flag and injecting the plugin and definitions. 228 | 229 | Now let's look at the various approaches to defining mocks as offered by 230 | the plugin. The sections below feature an extensive overview of the 231 | flexibility in Mockjax and creating responses. 232 | 233 | #### Data Types Available for Mocking #### 234 | 235 | jQuery is able to handle and parse `Text`, `HTML`, `JSON`, `JSONP`, 236 | `Script` and `XML` data formats and Mockjax is able to mock any of those 237 | formats. Two things to note: depending upon how you mock out `JSON` and 238 | `JSONP` you may need to include [json2.js](https://raw.github.com/douglascrockford/JSON-js/master/json2.js) 239 | for the `JSON.stringify()` method (older browsers only, typically). Additionally 240 | if you mock XML inline, you'll need to include the [`xmlDOM`](http://github.com/jakerella/jquery-xmldom) 241 | plugin that transforms a string of XML into a DOM object. However, if you use 242 | the proxy approach outlined below then there should be no need to include either 243 | the JSON or XMLDOM plugins in any case. 244 | 245 | 246 | ## Detailed Request and Response Definition ## 247 | 248 | ### Defining a Request to Match ### 249 | 250 | The first thing you need to do when mocking a request is define the URL 251 | end-point to intercept and mock. As with our example above this can be a 252 | simple string: 253 | 254 | ```javascript 255 | $.mockjax({ 256 | url: "/url/to/rest-service" 257 | }); 258 | ``` 259 | 260 | or contain a `*` as a wildcard: 261 | 262 | ```javascript 263 | $.mockjax({ 264 | // Matches /data/quote, /data/tweet etc. 265 | url: "/data/*" 266 | }); 267 | ``` 268 | 269 | or a full regular expression: 270 | 271 | ```javascript 272 | $.mockjax({ 273 | // Matches /data/quote, /data/tweet but not /data/quotes 274 | url: /^\/data\/(quote|tweet)$/i 275 | }); 276 | ``` 277 | 278 | You can also match against the data option in addition to url: 279 | 280 | ```javascript 281 | $.mockjax({ 282 | url: "/rest", 283 | data: { action: "foo" } 284 | }); 285 | ``` 286 | 287 | The data option may be a custom matching function returning `true` of `false` 288 | whether the data is expected or not: 289 | 290 | ```javascript 291 | $.mockjax([ 292 | url: "/rest", 293 | data: function( data ) { 294 | return deepEqual( data, expected ); 295 | } 296 | ]); 297 | ``` 298 | 299 | The data function is a recommended place for assertions. Return `true` and let 300 | a testing framework of choice do the rest: 301 | 302 | ```javascript 303 | $.mockjax([ 304 | url: "/rest", 305 | data: function ( json ) { 306 | assert.deepEqual( JSON.parse(json), expected ); // QUnit example. 307 | return true; 308 | } 309 | ]); 310 | ``` 311 | 312 | To capture URL parameters, use a capturing regular expression for the 313 | URL and a `urlParams` array to indicate, ordinally, the names of the 314 | paramters that will be captured: 315 | 316 | ```javascript 317 | $.mockjax({ 318 | // matches /author/{any number here}/isbn/{any number with dashes here} 319 | // for example: "/author/1234/isbn/1234-5678-9012-0" 320 | url: /^\/author\/([\d]+)\/isbn\/([\d\-]+)$/, 321 | // names of matching params 322 | urlParams: ["authorID", "isbnNumber"], 323 | response: function (settings) { 324 | var authorID = settings.urlParams.authorID; 325 | var isbnNumber = settings.urlParams.isbnNumber; 326 | // etc... 327 | } 328 | }); 329 | ``` 330 | 331 | ### Defining Multiple Requests ### 332 | 333 | Since version 2.2 it is allowed to define several requests at once. 334 | `$.mockjax([...])` returns a array of handlers' indexes. It is possible to 335 | reset handler by index. Read more in [Removing Mockjax Handlers](#removing-mockjax-handlers). 336 | 337 | ```javascript 338 | var handlers = $.mockjax([ 339 | {url: '/rest', responseText: 'one'}, 340 | {url: '/rest', responseText: 'two'} 341 | ]); 342 | 343 | $.mockjax.clear(handlers[0]); 344 | ``` 345 | 346 | ### Defining a Response ### 347 | 348 | The second step is to define the type and content of the response. The two main 349 | properties you will be dealing with are either `responseText` or 350 | `responseXML`. These properties mirror the native `XMLHttpRequest` 351 | object properties that are set during a live response. There are three 352 | different patterns for specifying the responses: Inline, Proxy, and 353 | Callback. 354 | 355 | #### Inline Responses #### 356 | 357 | A simple text response would be: 358 | 359 | ```javascript 360 | $.mockjax({ 361 | url: "/restful/api", 362 | responseText: "A text response from the server" 363 | }); 364 | ``` 365 | 366 | A simple JSON response would be: 367 | 368 | ```javascript 369 | $.mockjax({ 370 | url: "/restful/api", 371 | // You may need to include the [json2.js](https://raw.github.com/douglascrockford/JSON-js/master/json2.js) library for older browsers 372 | responseText: { "foo": "bar" } 373 | }); 374 | ``` 375 | 376 | Also note that a JSON response is really just a text response that jQuery will 377 | parse as JSON for you (and return a JSON object to the `success` and `complete` 378 | callbacks). 379 | 380 | A simple XML response would be: 381 | 382 | ```javascript 383 | $.mockjax({ 384 | url: "/restful/api", 385 | // Need to include the xmlDOM plugin to have this translated into a DOM object 386 | responseXML: "Hello world!" 387 | }); 388 | ``` 389 | 390 | As you can see, if you have a significant amount of data being 391 | mocked this becomes unwieldy. So that brings us to the next pattern: 392 | the proxy. 393 | 394 | #### Proxy #### 395 | 396 | In this example below, the Mockjax plugin will intercept requests for 397 | `/restful/api` and redirect them to `/mocks/data.json`: 398 | 399 | ```javascript 400 | $.mockjax({ 401 | url: "/restful/api", 402 | proxy: "/mocks/data.json" 403 | }); 404 | ``` 405 | 406 | The `/mocks/data.json` file can have any valid JSON content you want, and allows 407 | you to maintain that mock data in its own file for maintainability. 408 | 409 | > Note: If you're testing your code with a poxy, it is best to run an actual web 410 | server for the tests. Simply loading `test/index.html` from the file system may 411 | result in the proxy file not being loaded correctly. We recommend using something 412 | like the [`http-server` npm module](https://www.npmjs.com/package/http-server). 413 | 414 | #### Callback #### 415 | 416 | In the final response pattern, we can define a callback function on the 417 | `response` property and have it set `responseText` or `responseXML` as 418 | needed: 419 | 420 | ```javascript 421 | $.mockjax({ 422 | url: "/restful/api", 423 | response: function(settings) { 424 | // Investigate the `settings` to determine the response... 425 | 426 | this.responseText = "Hello world!"; 427 | } 428 | }); 429 | ``` 430 | 431 | The default version of this callback is synchronous. If you provide both parameters 432 | to the callback function, you can use asynchronous code to set the dynamic response. 433 | 434 | ```javascript 435 | $.mockjax({ 436 | url: '/restful/api', 437 | response: function(settings, done) { 438 | var self = this; 439 | someAsyncMethod(function(data){ 440 | self.responseText = data; 441 | done(); 442 | }); 443 | } 444 | }); 445 | ``` 446 | 447 | Note that the callback is given the settings provided to the `$.mockjax({...})` 448 | method merged with any Ajax settings defined by jQuery or your application. This 449 | allows you to thoroughly investigate the request before setting the response 450 | body (or headers). 451 | 452 | 453 | ## Advanced Mocking Techniques ## 454 | 455 | At this point we've looked at a series of basic mocking techniques with 456 | Mockjax and will now unpack some of the additional functionality 457 | contained in the plugin. 458 | 459 | ### Simulating Response Time and Latency ### 460 | 461 | Simulating network and server latency for a mock is as simple as adding 462 | a `responseTime` property to your mock definition: 463 | 464 | ```javascript 465 | $.mockjax({ 466 | url: "/restful/api", 467 | // Simulate a network latency of 750ms 468 | responseTime: 750, 469 | responseText: "A text response from the server" 470 | }); 471 | ``` 472 | 473 | You can also use an interval for `responseTime` to randomize latency: 474 | 475 | ```javascript 476 | $.mockjax({ 477 | url: "/restful/api", 478 | // Use a random value between 250ms and 750ms 479 | responseTime: [250, 750], 480 | responseText: "A text response from the server" 481 | }); 482 | ``` 483 | 484 | ### Simulating HTTP Response Statuses ### 485 | 486 | It's also possible to simulate response statuses other than 200 (default 487 | for Mockjax) by simply adding a `status` property. 488 | 489 | ```javascript 490 | $.mockjax({ 491 | url: "/restful/api", 492 | // Server 500 error occurred 493 | status: 500, 494 | responseText: "A text response from the server" 495 | }); 496 | ``` 497 | 498 | These forced error status codes will be handled just as if the server had 499 | returned the error: the `error` callback will get executed with the proper 500 | arguments. 501 | 502 | ### Setting the Content-Type ### 503 | 504 | You can set the content type to associate with the mock response, in the 505 | example below, we're setting a JSON content type. 506 | 507 | ```javascript 508 | $.mockjax({ 509 | url: "/restful/api", 510 | contentType: "application/json", 511 | responseText: { 512 | hello: "World!" 513 | } 514 | }); 515 | ``` 516 | 517 | ### Setting Additional HTTP Response Headers ### 518 | 519 | Additional HTTP Response Headers may be provided by setting a key in the 520 | headers object literal: 521 | 522 | ```javascript 523 | $.mockjax({ 524 | url: "/restful/api", 525 | contentType: "application/json", 526 | responseText: { 527 | hello: "World!" 528 | }, 529 | headers: { 530 | etag: "xyz123" 531 | } 532 | }); 533 | ``` 534 | 535 | ### Dynamically Generating Mock Definitions ### 536 | 537 | In some situations, all of your REST calls are based upon a URL schema. 538 | Mockjax has the ability for you to specify a callback function that is 539 | handed the `$.ajax` request settings. The callback function may then 540 | either return false to allow the request to be handled natively, or 541 | return an object literal with relevant Mockjax parameters set. Below is 542 | an example that rewrites all Ajax requests to proxy to static mocks: 543 | 544 | ```javascript 545 | $.mockjax(function(settings) { 546 | 547 | // settings.url might be: "/restful/" such as "/restful/user" 548 | 549 | var service = settings.url.match(/\/restful\/(.*)$/); 550 | if ( service ) { 551 | return { 552 | proxy: "/mocks/" + service[1] + ".json" 553 | }; 554 | } 555 | // If you get here, there was no url match 556 | return; 557 | }); 558 | ``` 559 | 560 | ### Accessing Request Headers ### 561 | 562 | In some situations, you may need access to the request headers to determine 563 | matching or response bodies. To do this, you will need to specify a 564 | callback function that is handed the `$.ajax` request settings: 565 | 566 | ```javascript 567 | $.mockjax(function( requestSettings ) { 568 | // Here is our manual URL matching... 569 | if ( requestSettings.url === "/restful/user" ) { 570 | // We have a match, so we return a response callback... 571 | return { 572 | response: function( origSettings ) { 573 | 574 | // now we check the request headers, which may be set directly 575 | // on the xhr object through an ajaxSetup() call or otherwise: 576 | 577 | if ( requestSettings.headers["Authentication"] === "some-token" ) { 578 | this.responseText = { user: { id: 13 } }; 579 | } else { 580 | this.status = 403; 581 | this.responseText = "You are not authorized"; 582 | } 583 | } 584 | }; 585 | } 586 | // If you get here, there was no url match 587 | return; 588 | }); 589 | ``` 590 | 591 | ### Forced Simulation of Server Timeouts ### 592 | 593 | Because of the way Mockjax was implemented, it takes advantage of 594 | jQuery's internal timeout handling for requests. But if you'd like to 595 | force a timeout for a request you can do so by setting the `isTimeout` 596 | property to true: 597 | 598 | ```javascript 599 | $.mockjax({ 600 | url: '/restful/api', 601 | responseTime: 1000, 602 | isTimeout: true 603 | }); 604 | ``` 605 | 606 | ### Dynamically Generating Mock Responses ### 607 | 608 | It's also possible to dynamically generate the response text upon each 609 | request by implementing a callback function on the `response` parameter: 610 | 611 | ```javascript 612 | $.mockjax({ 613 | url: "/restful/webservice", 614 | dataType: "json", 615 | response: function(settings) { 616 | this.responseText = { 617 | randomText: "random " + Math.random() 618 | }; 619 | } 620 | }); 621 | ``` 622 | 623 | ### Data Types ### 624 | 625 | Many of the examples above mock a `json` response. You can also mock `xml`: 626 | 627 | ```javascript 628 | $.mockjax({ 629 | url: "/some/xml", 630 | dataType: "xml", 631 | responseXML: "Hello world XML" 632 | }); 633 | ``` 634 | 635 | (Don't forget that it's likely you'll need the [`xmlDOM`](http://github.com/jakerella/jquery-xmldom) library as well!) 636 | 637 | And `html`: 638 | 639 | ```javascript 640 | $.mockjax({ 641 | url: "/some/webservice", 642 | dataType: "html", 643 | responseText: "
Hello there
" 644 | }); 645 | ``` 646 | 647 | ### Performing Actions After Request Completion ### 648 | 649 | If you need to perform some actions after a call has completed you can 650 | use one of the `onAfter{Xxxxx}` options. For example, to fire a method when 651 | a request completes (either successfully or not): 652 | 653 | ```javascript 654 | $.mockjax({ 655 | url: "/api/end/point", 656 | onAfterComplete: function() { 657 | // do any required cleanup 658 | } 659 | }); 660 | ``` 661 | 662 | ### Globally Defining Mockjax Settings ### 663 | 664 | It is also possible to define the global defaults for all Mockjax 665 | requests by overwriting the `$.mockjaxSettings` object. By default the 666 | settings are as follows: 667 | 668 | ```javascript 669 | { 670 | log: null, // DEPRECATED, use $.mockjaxSettings.logger instead 671 | logger: window.console, 672 | logging: 2, 673 | logLevelMethods: ['error', 'warn', 'info', 'log', 'debug'], 674 | namespace: null, 675 | status: 200, 676 | statusText: "OK", 677 | responseTime: 500, 678 | isTimeout: false, 679 | throwUnmocked: false, 680 | retainAjaxCalls: true, 681 | contentType: "text/plain", 682 | response: "", 683 | responseText: "", 684 | responseXML: "", 685 | proxy: "", 686 | proxyType: "GET", 687 | lastModified: null, 688 | etag: "", 689 | headers: { 690 | etag: "IJF@H#@923uf8023hFO@I#H#", 691 | "content-type" : "text/plain" 692 | } 693 | } 694 | ``` 695 | 696 | To overwrite a particular settings such as the default `content-type`, you 697 | would do the following: 698 | 699 | ```javascript 700 | $.mockjaxSettings.contentType = "application/json"; 701 | ``` 702 | 703 | ### Setting a Global URL Namespace ### 704 | 705 | The namespace option in `$.mockjaxSettings` allows you to apply a prefix to 706 | all of your mocked urls, such as `/api/v1`. 707 | 708 | ```javascript 709 | $.mockjaxSettings.namespace = "/api/v1"; 710 | ``` 711 | 712 | Then the following mock will match `/api/v1/rest`: 713 | 714 | ```javascript 715 | $.mockjax({ 716 | url: "/rest" 717 | }) 718 | ``` 719 | 720 | The global namespace option can also be overwritten on a particular mock. 721 | 722 | ```javascript 723 | $.mockjax({ 724 | url: "/rest-2", 725 | namespace: null 726 | }) 727 | ``` 728 | 729 | Note that the namespace prefix does not apply to proxies. 730 | 731 | ### Removing Mockjax Handlers ### 732 | 733 | If you need to reset the Mockjax handlers you've added, just call 734 | `$.mockjax.clear()`. _This will NOT reset the `$.mockjaxSettings`!_ 735 | 736 | ```javascript 737 | $.mockjax.clear(); 738 | ``` 739 | 740 | You can also clear individual mock handlers using their ID: 741 | 742 | ```javascript 743 | var id = $.mockjax({ 744 | ... 745 | }); 746 | 747 | $.mockjax.clear(id); 748 | ``` 749 | 750 | 751 | ## Miscellaneous Information ## 752 | 753 | ### jQuery Version Support ### 754 | 755 | We strive to ensure that Mockjax is tested on the furthest patch version of all 756 | minor (and major) versions of jQuery beginning with 1.5.2 going all the way 757 | through 2.x. In other words, we don't test 1.6.1, but rather 1.6.4 (the furthest 758 | patch version on the 1.6.x line). The QUnit tests in the `/test` directory include 759 | links to each version of jQuery tested in the header. 760 | 761 | ### Browsers Tested ### 762 | 763 | We use virtual machines to test current versions of the browsers below. In addition, 764 | we test the specific versions of IE specified. 765 | 766 | * Internet Explorer 8-11 767 | * Firefox 768 | * Safari 769 | * Chrome 770 | * Opera 771 | 772 | _Please note that while we strive to keep `master` as bug free as possible, we do 773 | not necessarily run tests in all of the above browsers for every single commit. We 774 | do, however, ensure all tests are passing before tagging a release._ 775 | 776 | 777 | ### Using Mockjax in Other Ways ### 778 | 779 | You can use Mockjax as a Node module, with require.js, or with Browserify... and 780 | presumably in other ways as well. We have tests for each of the methods above. 781 | 782 | When using Mockjax as a Node module (including with Browserify), **you must 783 | provide the module with the jQuery library and a `window`**. Here is an example 784 | using a module intended for use as a "browserified" module: 785 | 786 | ```js 787 | var jquery = require('jquery'); 788 | var mockjax = require('jquery.mockjax')(jquery, window); 789 | // Note that we expect `window` to be defined once this file is browserified and 790 | // used in a browser. If it isn't Mockjax will have a problem! 791 | 792 | mockjax({ 793 | url: '/resource', 794 | responseText: 'content' 795 | }); 796 | 797 | function getResource(cb) { 798 | jquery.ajax({ 799 | url: '/resource', 800 | success: cb, 801 | error: cb 802 | }); 803 | } 804 | ``` 805 | 806 | 807 | ### Logging ### 808 | 809 | Mockjax logs various pieces of information to the `console` (on `window`) in 810 | browsers, or to stdout in Node). You can customize various aspects of the 811 | logging to suit your needs. By default, only 'error', 'warn' or 'info' messages 812 | will be shown, but detailed information may be available in debug logs. Below 813 | are some common things you might need to do to get better logging information. 814 | 815 | #### Show different levels of log messages 816 | 817 | ```js 818 | $.mockjaxSettings.logging = 4; // very verbose debug messages 819 | $.mockjaxSettings.logging = 3; // verbose log messages 820 | $.mockjaxSettings.logging = 2; // informational messages 821 | $.mockjaxSettings.logging = 1; // warning messages 822 | $.mockjaxSettings.logging = 0; // only critical error messages 823 | ``` 824 | 825 | (Note that each level enables that level plus any lower number... thus setting 826 | logging to `2` also enables warnings and errors.) 827 | 828 | #### Implement a custom logger 829 | 830 | If you don't want to use the `console` object, you can pass in your own logging 831 | implementation with the `logger` setting. Note that your logger must either 832 | implement the `debug`, `log`, `info`, `warn`, and `error` methods, or you must 833 | also provide what methods map to the 5 levels (0 through 4). 834 | 835 | ```js 836 | $.mockjaxSettings.logger = { 837 | debug: function() { ... }, 838 | log: function() { ... }, 839 | // ... 840 | }; 841 | ``` 842 | 843 | Your logger methods may receive any number of arguments to log out, either as 844 | strings or objects, similar to how the `window.console` object methods work. 845 | 846 | If you have a logger that uses different methods names, specify them in this array: 847 | 848 | ```js 849 | $.mockjaxSettings.logLevelMethods = ['critical', 'bad', 'stuff', 'log', 'verbose']; 850 | ``` 851 | 852 | Note that the first entry in this array (index `0`) will be errors while the last 853 | entry will be verbose output. Anything beyond index `4` will be ignored. 854 | 855 | #### What about the old `log` setting? 856 | 857 | This was an undocumented feature whereby you could provide a `log` method using 858 | `$.mockjaxSettings`, however, it is no longer used internally. This undocumented 859 | option is now **deprecated**, and while it will work, log messages of ALL levels 860 | will be sent to it. 861 | 862 | If you have no idea what we're talking about... good! Don't worry about it. The 863 | proper way to implement your own logger is via `$.mockjaxSettings.logger`. 864 | 865 | ### Release History ### 866 | 867 | Please read the [CHANGELOG](https://github.com/jakerella/jquery-mockjax/blob/master/CHANGELOG.md) 868 | for a list of changes per release. 869 | 870 | Note that all releases are tagged in Github for easy reference, the `master` branch 871 | should *not* be considered a stable release! 872 | 873 | ### License ### 874 | 875 | Copyright (c) 2014 Jordan Kasper, formerly appendTo 876 | 877 | NOTE: This repository was taken over by Jordan Kasper (@jakerella) October, 2014 878 | 879 | Dual licensed under the MIT or GPL licenses: 880 | [http://opensource.org/licenses/MIT](http://opensource.org/licenses/MIT) 881 | [http://www.gnu.org/licenses/gpl-2.0.html](http://www.gnu.org/licenses/gpl-2.0.html) 882 | 883 | ### Troubleshooting ### 884 | 885 | If mockjax appears to be behaving unexpectedly, be sure to check the console 886 | logs for warnings. 887 | 888 | ### Contributing ### 889 | 890 | We welcome any contributions by the community, whether in the form of a Pull 891 | Request, issue submission and comments, or just sharing on social media! 892 | 893 | If you want to contribute code to the project, please read our 894 | [Contribution guidelines](CONTRIBUTING.md) to see what you need to do to get your 895 | Pull Request ready for merging. 896 | 897 | #### Admins #### 898 | 899 | All pull requests are reviewed by the wonderful collaborators on this project: 900 | * [Doug Neiner](https://github.com/dcneiner) 901 | * [Jonathan Creamer](https://github.com/jcreamer898) 902 | * [Jordan Kasper](https://github.com/jakerella) 903 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 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 | } -------------------------------------------------------------------------------- /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=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/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/browserify/test.js: -------------------------------------------------------------------------------- 1 | /* globals QUnit */ 2 | 3 | (function($) { 4 | 'use strict'; 5 | 6 | QUnit.module('jquery.mockjax used with Browserify'); 7 | 8 | QUnit.test('mockjax function exists on jQuery', function(assert) { 9 | assert.strictEqual(typeof($.mockjax), 'function', '$.mockjax is a function'); 10 | }); 11 | 12 | QUnit.test('mock set in browserified module intercepts correctly', function(assert) { 13 | var done = assert.async(); 14 | 15 | $.ajax({ 16 | url: '/resource', 17 | success: function(response) { 18 | assert.equal(response, 'content'); 19 | }, 20 | error: function () { 21 | assert(false); 22 | }, 23 | complete: function () { 24 | done(); 25 | } 26 | }); 27 | }); 28 | 29 | QUnit.test('unmocked endpoint produces error', function(assert) { 30 | var done = assert.async(); 31 | 32 | $.ajax({ 33 | url: '/foobar', 34 | success: function() { 35 | assert(false); 36 | }, 37 | error: function () { 38 | assert.ok(true); 39 | }, 40 | complete: function () { 41 | done(); 42 | } 43 | }); 44 | }); 45 | 46 | QUnit.test('function using ajax works correctly in browserified module', function(assert) { 47 | var done = assert.async(); 48 | 49 | window.getResource(function(result) { 50 | assert.strictEqual(result, 'content'); 51 | done(); 52 | }); 53 | }); 54 | 55 | })(window.jQuery); 56 | -------------------------------------------------------------------------------- /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 |

31 | Mockjax 32 | jQuery 1.5.2 33 | jQuery 1.6.4 34 | jQuery 1.7.2 35 | jQuery 1.8.3 36 | jQuery 1.9.1 37 | jQuery 1.10.2 38 | jQuery 1.11.3 39 | jQuery 1.12.4 40 | jQuery 2.0.3 41 | jQuery 2.1.4 42 | jQuery 2.2.4 43 | jQuery 3.0.0 44 | jQuery 3.1.0 45 | jQuery Latest (git) 46 | 47 |

48 |

49 |

50 |
    51 | 52 | 53 | -------------------------------------------------------------------------------- /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 |

    31 | Mockjax 32 | jQuery 1.5.2 33 | jQuery 1.6.4 34 | jQuery 1.7.2 35 | jQuery 1.8.3 36 | jQuery 1.9.1 37 | jQuery 1.10.2 38 | jQuery 1.11.3 39 | jQuery 1.12.4 40 | jQuery 2.0.3 41 | jQuery 2.1.4 42 | jQuery 2.2.4 43 | jQuery 3.0.0 44 | jQuery 3.1.0 45 | jQuery Latest (git) 46 | 47 |

    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/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/requirejs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MockJax AMD Module Test 5 | 6 | 8 | 9 | 12 | 13 | 18 | 19 | 20 | 21 | 22 | 23 |

      24 | MockJax 25 | jQuery 1.7.2 26 | jQuery 1.8.3 27 | jQuery 1.9.1 28 | jQuery 1.10.2 29 | jQuery 1.11.2 30 | jQuery 2.0.3 31 | jQuery 2.1.4 32 | jQuery 2.2.4 33 | jQuery 3.0.0 34 | jQuery 3.1.0 35 | jQuery Latest (git) 36 |

      37 | 38 |

      39 |

      40 |
        41 | 42 | 43 | -------------------------------------------------------------------------------- /test/requirejs/run_tests.js: -------------------------------------------------------------------------------- 1 | (function (QUnit){ 2 | 'use strict'; 3 | 4 | // Compute the path to the jQuery file using ther URL query parameter: 5 | // 'jquery=|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/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 | -------------------------------------------------------------------------------- /test/test-bugs.js: -------------------------------------------------------------------------------- 1 | (function(qunit, $) { 2 | 'use strict'; 3 | 4 | var t = qunit.test; 5 | 6 | /* ------------------------------------ */ 7 | qunit.module( 'Miscellaneous Bug Tests' ); 8 | /* ------------------------------------ */ 9 | 10 | t('Test bug fix for $.mockjaxSettings', function(assert) { 11 | var done = assert.async(); 12 | 13 | $.mockjaxSettings.headers = { 14 | 'content-type': 'text/plain', 15 | etag: 'IJF@H#@923uf8023hFO@I#H#' 16 | }; 17 | 18 | $.mockjax({ 19 | url: '/get/property', 20 | type: 'GET', 21 | response: function() { 22 | this.responseText = { foo: 'bar' }; 23 | } 24 | }); 25 | 26 | $.ajax({ 27 | url: '/get/property', 28 | success: function() { 29 | assert.deepEqual( $.mockjaxSettings.headers, { 30 | 'content-type': 'text/plain', 31 | etag: 'IJF@H#@923uf8023hFO@I#H#' 32 | }, 'Should not change the default headers.'); 33 | }, 34 | complete: done 35 | }); 36 | }); 37 | 38 | t('Preserve responseText inside a response function when using jsonp and a success callback', function(assert) { 39 | var done = assert.async(); 40 | 41 | $.mockjax({ 42 | url: 'http://some/fake/jsonp/endpoint', 43 | // The following line works... 44 | // responseText: [{ 'data' : 'JSONP is cool' }] 45 | // But doesn't not work when setting this.responseText in response 46 | response: function() { 47 | this.responseText = [{ 'data' : 'JSONP is cool' }]; 48 | } 49 | }); 50 | 51 | $.ajax({ 52 | url: 'http://some/fake/jsonp/endpoint', 53 | dataType: 'jsonp', 54 | success: function(data) { 55 | assert.deepEqual(data, [{ 'data' : 'JSONP is cool' }]); 56 | done(); 57 | } 58 | }); 59 | }); 60 | 61 | t('Custom status when using proxy', function(assert) { 62 | var done = assert.async(); 63 | 64 | $.mockjax({ 65 | url: '/response-callback', 66 | status: 409, 67 | proxy: 'test_proxy.json' 68 | }); 69 | 70 | $.ajax({ 71 | url: '/response-callback', 72 | error: function() { assert.ok(true, 'error callback was called'); }, 73 | success: function() { 74 | assert.ok( false, 'Success should not be called' ); 75 | }, 76 | complete: function(xhr) { 77 | assert.equal(xhr.status, 409, 'response status matches'); 78 | done(); 79 | } 80 | }); 81 | }); 82 | 83 | t('Call onAfterSuccess after success has been called', function(assert) { 84 | var done = assert.async(); 85 | 86 | var onAfterSuccessCalled = false; 87 | var successCalled = false; 88 | $.mockjax({ 89 | url: '/response-callback', 90 | onAfterSuccess: function() { 91 | onAfterSuccessCalled = true; 92 | assert.equal(successCalled, true, 'success was not yet called'); 93 | } 94 | }); 95 | 96 | $.ajax({ 97 | url: '/response-callback', 98 | success: function() { 99 | successCalled = true; 100 | } 101 | }); 102 | 103 | setTimeout(function() { 104 | assert.equal(onAfterSuccessCalled, true, 'onAfterSuccess was not called'); 105 | done(); 106 | }, 100); 107 | }); 108 | 109 | t('Call onAfterError after error has been called', function(assert) { 110 | var done = assert.async(); 111 | 112 | var onAfterErrorCalled = false; 113 | var errorCalled = false; 114 | $.mockjax({ 115 | url: '/response-callback-bad', 116 | status: 500, 117 | onAfterError: function() { 118 | onAfterErrorCalled = true; 119 | assert.equal(errorCalled, true, 'error was not yet called'); 120 | } 121 | }); 122 | 123 | $.ajax({ 124 | url: '/response-callback-bad', 125 | error: function() { 126 | errorCalled = true; 127 | } 128 | }); 129 | 130 | setTimeout(function() { 131 | assert.equal(onAfterErrorCalled, true, 'onAfterError was not called'); 132 | done(); 133 | }, 100); 134 | }); 135 | 136 | t('Call onAfterComplete after complete has been called', function(assert) { 137 | var done = assert.async(); 138 | 139 | var onAfterCompleteCalled = false; 140 | var completeCalled = false; 141 | $.mockjax({ 142 | url: '/response-callback', 143 | onAfterComplete: function() { 144 | onAfterCompleteCalled = true; 145 | assert.equal(completeCalled, true, 'complete was not yet called'); 146 | } 147 | }); 148 | 149 | $.ajax({ 150 | url: '/response-callback', 151 | complete: function() { 152 | completeCalled = true; 153 | } 154 | }); 155 | 156 | setTimeout(function() { 157 | assert.equal(onAfterCompleteCalled, true, 'onAfterComplete was not called'); 158 | done(); 159 | }, 100); 160 | }); 161 | 162 | t('Bug #95: undefined responseText on success', function(assert) { 163 | assert.expect(2); 164 | 165 | var expected = { status: 'success', fortune: 'Are you a turtle?' }; 166 | 167 | $.mockjax({ 168 | url: 'test/something', 169 | responseText: { status: 'success', fortune: 'Are you a turtle?' } 170 | }); 171 | 172 | $.ajax({ 173 | type: 'GET', 174 | url: 'test/something', 175 | async: false, 176 | success: function(data) { 177 | // Before jQuery 1.5 the response is a stringified version of the 178 | // json data unless the 'dataType' option is set to "json" 179 | var expectedResult = expected; 180 | if (qunit.compareSemver($().jquery, '1.5', '<')) { 181 | expectedResult = JSON.stringify(expected); 182 | } 183 | assert.deepEqual(data, expectedResult, 'responseText is correct JSON object'); 184 | } 185 | }); 186 | 187 | $.ajax({ 188 | type: 'GET', 189 | url: 'test/something', 190 | dataType: 'json', 191 | async: false, 192 | success: function(data) { 193 | assert.deepEqual(data, expected, 'responseText is correct JSON object'); 194 | } 195 | }); 196 | }); 197 | 198 | t('alias type to method', function(assert) { 199 | var done = assert.async(); 200 | 201 | $.mockjax(function(settings) { 202 | if (settings.url === '/get/property') { 203 | assert.equal(settings.type, settings.method); 204 | 205 | return { 206 | responseText: { status: 'success', fortune: 'Are you a ninja?' } 207 | }; 208 | } 209 | 210 | return false; 211 | }); 212 | 213 | $.ajax({ 214 | url: '/get/property', 215 | type: 'GET', 216 | complete: function() { 217 | $.ajax({ 218 | url: '/get/property', 219 | method: 'POST', 220 | complete: done 221 | }); 222 | } 223 | }); 224 | }); 225 | 226 | t('Bug #26: jsonp mock fails with remote URL and proxy', function(assert) { 227 | var done = assert.async(); 228 | 229 | $.mockjax({ 230 | url: 'http://example.com/jsonp*', 231 | contentType: 'text/json', 232 | proxy: 'test_jsonp.js' 233 | }); 234 | var callbackExecuted = false; 235 | window.abcdef123456 = function(json) { 236 | callbackExecuted = true; 237 | assert.deepEqual(json, { 'data' : 'JSONP is cool' }, 'The proxied data is correct'); 238 | }; 239 | 240 | $.ajax({ 241 | url: 'http://example.com/jsonp?callback=?', 242 | jsonpCallback: 'abcdef123456', 243 | dataType: 'jsonp', 244 | error: qunit.noErrorCallbackExpected, 245 | complete: function(xhr) { 246 | assert.ok(callbackExecuted, 'The jsonp callback was executed'); 247 | assert.equal(xhr.statusText, 'success', 'Response was successful'); 248 | window.abcdef123456 = null; 249 | done(); 250 | } 251 | }); 252 | }); 253 | 254 | t('Bug #254: subsequent timeouts', function(assert) { 255 | var done = assert.async(); 256 | 257 | $.mockjax({ 258 | url: '/timeout-check', 259 | responseTime: 20, 260 | isTimeout: true, 261 | responseText: 'foobar' 262 | }); 263 | 264 | $.ajax({ 265 | url: '/timeout-check', 266 | error: function(xhr, textStatus, errorThrown ) { 267 | assert.equal( textStatus, 'timeout', 'Text status on call #1 is equal to timeout' ); 268 | assert.ok( errorThrown !== 'OK', 'errorThrown is not "OK" on call #1' ); 269 | }, 270 | success: function() { 271 | assert.ok(false, 'call #1 should not be successful'); 272 | }, 273 | complete: function() { 274 | // do a second call and ensure we still timeout 275 | $.ajax({ 276 | url: '/timeout-check', 277 | error: function(xhr, textStatus, errorThrown ) { 278 | assert.equal( textStatus, 'timeout', 'Text status on call #2 is equal to timeout' ); 279 | assert.ok( errorThrown !== 'OK', 'errorThrown is not "OK" on call #2' ); 280 | }, 281 | success: function() { 282 | assert.ok(false, 'call #2 should not be be successful'); 283 | }, 284 | complete: done 285 | }); 286 | } 287 | }); 288 | }); 289 | 290 | t('Bug #136: cross domain script requests - GET', function(assert) { 291 | var done = assert.async(); 292 | 293 | $.mockjax({ 294 | type: 'GET', 295 | url: 'http://jquery-mockjax-foobar.com/somefile.js', 296 | responseText: '(window.mockjaxCrossDomain=true)' 297 | }); 298 | 299 | $.ajax({ 300 | type: 'GET', 301 | dataType: 'script', 302 | url: 'http://jquery-mockjax-foobar.com/somefile.js', 303 | error: qunit.noErrorCallbackExpected, 304 | success: function() { 305 | assert.strictEqual(window.mockjaxCrossDomain, true, 'mockjax call for script was mocked'); 306 | }, 307 | complete: done 308 | }); 309 | }); 310 | 311 | t('Bug #136: cross domain script requests - POST', function(assert) { 312 | var done = assert.async(); 313 | 314 | $.mockjax({ 315 | type: 'POST', 316 | url: 'http://jquery-mockjax-foobar.com/somefile.js', 317 | responseText: '(window.mockjaxCrossDomain=true)' 318 | }); 319 | 320 | $.ajax({ 321 | type: 'POST', 322 | dataType: 'script', 323 | url: 'http://jquery-mockjax-foobar.com/somefile.js', 324 | error: qunit.noErrorCallbackExpected, 325 | success: function() { 326 | assert.strictEqual(window.mockjaxCrossDomain, true, 'mockjax call for script was mocked'); 327 | }, 328 | complete: done 329 | }); 330 | }); 331 | 332 | t('Bug #86: JSONP response treated as plain JSON when using jQuery-generated callback', function(assert) { 333 | var done = assert.async(); 334 | 335 | $.mockjax({ 336 | url: 'http://foo.com/api/jsonp', 337 | contentType: 'application/javascript', 338 | responseText: { 'data' : 'JSONP is cool' } 339 | }); 340 | 341 | $.ajax({ 342 | url: 'http://foo.com/api/jsonp', 343 | dataType: 'jsonp', 344 | jsonp: 'callback', 345 | error: qunit.noErrorCallbackExpected, 346 | success: function(data) { 347 | assert.deepEqual(data, { 'data' : 'JSONP is cool' }, 'success gets correct data'); 348 | }, 349 | complete: function() { 350 | var actualCalls = $.mockjax.mockedAjaxCalls(); 351 | assert.equal(actualCalls.length, 1, 'Mockjax call made'); 352 | assert.ok(actualCalls[0] && actualCalls[0].url.match(/\/api\/jsonp\?callback\=jsonp[0-9]+/), 'mockjax call has expected jsonp url'); 353 | done(); 354 | } 355 | }); 356 | }); 357 | 358 | t('Bug #86: JSONP response treated as plain JSON - string data', function(assert) { 359 | var done = assert.async(); 360 | 361 | $.mockjax({ 362 | url: 'http://foo.com/api/jsonp', 363 | contentType: 'application/javascript', 364 | responseText: 'Testy Test' 365 | }); 366 | 367 | $.ajax({ 368 | url: 'http://foo.com/api/jsonp', 369 | dataType: 'jsonp', 370 | jsonp: 'callback', 371 | error: qunit.noErrorCallbackExpected, 372 | success: function(data) { 373 | assert.strictEqual(data, 'Testy Test', 'success gets correct data'); 374 | }, 375 | complete: function() { 376 | var actualCalls = $.mockjax.mockedAjaxCalls(); 377 | assert.equal(actualCalls.length, 1, 'Mockjax call made'); 378 | assert.ok(actualCalls[0] && actualCalls[0].url.match(/\/api\/jsonp\?callback\=jsonp[0-9]+/), 'mockjax call has expected jsonp url'); 379 | done(); 380 | } 381 | }); 382 | }); 383 | 384 | t('Bug #86: JSONP response treated as plain JSON - numeric data', function(assert) { 385 | var done = assert.async(); 386 | 387 | $.mockjax({ 388 | url: 'http://foo.com/api/jsonp', 389 | contentType: 'application/javascript', 390 | responseText: 42 391 | }); 392 | 393 | $.ajax({ 394 | url: 'http://foo.com/api/jsonp', 395 | dataType: 'jsonp', 396 | jsonp: 'callback', 397 | error: qunit.noErrorCallbackExpected, 398 | success: function(data) { 399 | assert.strictEqual(data, 42, 'success gets correct data'); 400 | }, 401 | complete: function() { 402 | var actualCalls = $.mockjax.mockedAjaxCalls(); 403 | assert.equal(actualCalls.length, 1, 'Mockjax call made'); 404 | assert.ok(actualCalls[0] && actualCalls[0].url.match(/\/api\/jsonp\?callback\=jsonp[0-9]+/), 'mockjax call has expected jsonp url'); 405 | done(); 406 | } 407 | }); 408 | }); 409 | 410 | t('Bug #267: handle undefined url in first arg', function(assert) { 411 | var done = assert.async(); 412 | 413 | $.mockjax({ 414 | url: '/api/foo', 415 | contentType: 'application/json', 416 | responseText: { foo: 'bar' } 417 | }); 418 | 419 | $.ajax(undefined, { 420 | url: '/api/foo', 421 | dataType: 'json', 422 | error: qunit.noErrorCallbackExpected, 423 | success: function(data) { 424 | assert.deepEqual(data, { foo: 'bar' }, 'success gets correct data'); 425 | }, 426 | complete: done 427 | }); 428 | }); 429 | 430 | })(window.QUnit, window.jQuery); -------------------------------------------------------------------------------- /test/test-connection.js: -------------------------------------------------------------------------------- 1 | (function(qunit, $) { 2 | 'use strict'; 3 | 4 | var t = qunit.test; 5 | 6 | /* -------------------------------- */ 7 | qunit.module( 'Connection Simulation', { 8 | /* -------------------------------- */ 9 | beforeEach: function() { 10 | this.variableDelayMin = 100; 11 | this.variableDelayMax = 300; 12 | this.processingDuration = 30; 13 | 14 | $.mockjax({ 15 | url: '/delay', 16 | responseTime: 150 17 | }); 18 | 19 | $.mockjax({ 20 | url: 'http://foobar.com/jsonp-delay?callback=?', 21 | contentType: 'text/json', 22 | proxy: 'test_jsonp.js', 23 | responseTime: 150, 24 | responseText: '{}' 25 | }); 26 | 27 | $.mockjax({ 28 | url: '/variable-delay', 29 | responseTime: [this.variableDelayMin, this.variableDelayMax] 30 | }); 31 | 32 | $.mockjax({ 33 | url: '/proxy', 34 | proxy: 'test_proxy.json', 35 | responseTime: 50 36 | }); 37 | 38 | $.mockjax({ 39 | url: '*', 40 | responseText: '', 41 | responseTime: 50 42 | }); 43 | } 44 | }); 45 | 46 | t('Async test', function(assert) { 47 | var done = assert.async(); 48 | 49 | var order = []; 50 | $.ajax({ 51 | async: true, 52 | url: '/', 53 | success: function() { 54 | order.push('b'); 55 | }, 56 | error: qunit.noErrorCallbackExpected, 57 | complete: function() { 58 | assert.deepEqual(order, ['a', 'b'], 'Order of execution correct, 2'); 59 | done(); 60 | } 61 | }); 62 | order.push('a'); 63 | assert.deepEqual(order, ['a'], 'Order of execution correct, 1'); 64 | }); 65 | 66 | t('Sync test', function(assert) { 67 | var order = []; 68 | $.ajax({ 69 | async: false, 70 | url: '/', 71 | success: function() { 72 | order.push('b'); 73 | assert.deepEqual(order, ['b'], 'Order of execution correct, 1'); 74 | }, 75 | error: qunit.noErrorCallbackExpected 76 | }); 77 | order.push('a'); 78 | assert.deepEqual(order, ['b', 'a'], 'Order of execution correct, 2'); 79 | }); 80 | 81 | t('Response time simulation and latency', function(assert) { 82 | var done = assert.async(); 83 | 84 | var executed = 0, ts = new Date(); 85 | $.ajax({ 86 | url: '/delay', 87 | complete: function() { 88 | var delay = ((new Date()) - ts); 89 | // check against 140ms to allow for browser variance 90 | assert.ok( delay >= 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-core.js: -------------------------------------------------------------------------------- 1 | (function(qunit, $) { 2 | 'use strict'; 3 | 4 | var t = qunit.test; 5 | 6 | /* ----------------- */ 7 | qunit.module( 'Core' ); 8 | /* ----------------- */ 9 | 10 | t('Return XMLHttpRequest object from $.ajax', function(assert) { 11 | $.mockjax({ 12 | url: '/xmlhttprequest', 13 | responseText: 'Hello Word' 14 | }); 15 | 16 | var xhr = $.ajax({ 17 | url: '/xmlhttprequest', 18 | complete: function() { } 19 | }); 20 | if (xhr && xhr.abort) { 21 | xhr.abort(); 22 | } 23 | 24 | assert.ok(xhr, 'XHR object is not null or undefined'); 25 | assert.ok(xhr.done && xhr.fail, 'Got Promise methods'); 26 | }); 27 | 28 | t('Intercept synchronized proxy calls and return synchronously', function(assert) { 29 | $.mockjax({ 30 | url: '/proxy', 31 | proxy: 'test_proxy.json' 32 | }); 33 | 34 | $.ajax({ 35 | url: '/proxy', 36 | dataType: 'json', 37 | async: false, 38 | success: function(json) { 39 | assert.ok(json && json.proxy, 'Proxy callback request succeeded'); 40 | }, 41 | error: qunit.noErrorCallbackExpected 42 | }); 43 | }); 44 | 45 | t('Intercept asynchronized proxy calls', function(assert) { 46 | var done = assert.async(); 47 | $.mockjax({ 48 | url: '/proxy', 49 | proxy: 'test_proxy.json' 50 | }); 51 | 52 | $.ajax({ 53 | url: '/proxy', 54 | dataType: 'json', 55 | success: function(json) { 56 | assert.ok(json && json.proxy, 'Proxy callback request succeeded'); 57 | }, 58 | error: qunit.noErrorCallbackExpected, 59 | complete: done 60 | }); 61 | }); 62 | 63 | t('Intercept proxy calls for XML', function(assert) { 64 | $.mockjax({ 65 | url: '/proxy', 66 | proxy: 'test_proxy.xml' 67 | }); 68 | 69 | $.ajax({ 70 | url: '/proxy', 71 | dataType: 'xml', 72 | async: false, 73 | success: function(doc) { 74 | assert.ok(doc, 'Proxy callback request succeeded'); 75 | assert.strictEqual($(doc).find('foo').length, 1, 'Foo element exists in XML'); 76 | assert.strictEqual($(doc).find('foo').text(), 'bar', 'XML content is correct'); 77 | }, 78 | error: qunit.noErrorCallbackExpected 79 | }); 80 | }); 81 | 82 | t('Intercept and proxy (sub-ajax request)', function(assert) { 83 | var done = assert.async(); 84 | 85 | $.mockjax({ 86 | url: '/proxy', 87 | proxy: 'test_proxy.json' 88 | }); 89 | 90 | $.ajax({ 91 | url: '/proxy', 92 | dataType: 'json', 93 | success: function(json) { 94 | assert.ok(json && json.proxy, 'Proxy request succeeded'); 95 | }, 96 | error: qunit.noErrorCallbackExpected, 97 | complete: done 98 | }); 99 | }); 100 | 101 | t('Proxy type specification', function(assert) { 102 | var done = assert.async(); 103 | 104 | $.mockjax({ 105 | url: '/proxy', 106 | proxy: 'test_proxy.json', 107 | proxyType: 'GET' 108 | }); 109 | 110 | $.ajax({ 111 | url: '/proxy', 112 | error: qunit.noErrorCallbackExpected, 113 | dataType: 'json', 114 | success: function(json) { 115 | assert.ok(json && json.proxy, 'Proxy request succeeded'); 116 | }, 117 | complete: done 118 | }); 119 | }); 120 | 121 | t('Support 1.5 $.ajax(url, settings) signature.', function(assert) { 122 | var done = assert.async(); 123 | 124 | $.mockjax({ 125 | url: '/resource', 126 | responseText: 'Hello World' 127 | }); 128 | 129 | $.ajax('/resource', { 130 | success: function(response) { 131 | assert.equal(response, 'Hello World'); 132 | }, 133 | error: qunit.noErrorCallbackExpected, 134 | complete: done 135 | }); 136 | }); 137 | 138 | t('Dynamic response callback', function(assert) { 139 | var done = assert.async(); 140 | 141 | $.mockjax({ 142 | url: '/response-callback', 143 | response: function(settings) { 144 | this.responseText = settings.data.response + ' 2'; 145 | } 146 | }); 147 | 148 | $.ajax({ 149 | url: '/response-callback', 150 | dataType: 'text', 151 | data: { 152 | response: 'Hello world' 153 | }, 154 | error: qunit.noErrorCallbackExpected, 155 | complete: function(xhr) { 156 | assert.equal(xhr.responseText, 'Hello world 2', 'Response Text matches'); 157 | done(); 158 | } 159 | }); 160 | }); 161 | 162 | t('Dynamic asynchronous response callback', function(assert) { 163 | var done = assert.async(); 164 | 165 | $.mockjax({ 166 | url: '/response-callback', 167 | responseText: 'original response', 168 | response: function(settings, resDone) { 169 | var that = this; 170 | setTimeout(function() { 171 | that.responseText = settings.data.response + ' 3'; 172 | resDone(); 173 | }, 30); 174 | } 175 | }); 176 | 177 | $.ajax({ 178 | url: '/response-callback', 179 | dataType: 'text', 180 | data: { 181 | response: 'Hello world' 182 | }, 183 | error: qunit.noErrorCallbackExpected, 184 | complete: function(xhr) { 185 | assert.equal(xhr.responseText, 'Hello world 3', 'Response Text matches'); 186 | done(); 187 | } 188 | }); 189 | }); 190 | 191 | if (qunit.compareSemver($().jquery, '1.4', '>=')) { 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 | -------------------------------------------------------------------------------- /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); -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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-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-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 | -------------------------------------------------------------------------------- /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-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-retaining-ajax-calls.js: -------------------------------------------------------------------------------- 1 | (function(qunit, $) { 2 | 'use strict'; 3 | 4 | var t = qunit.test; 5 | 6 | /* ---------------------------------- */ 7 | qunit.module( 'Retaining Ajax Calls' ); 8 | /* ---------------------------------- */ 9 | 10 | t('Setting defaults', function(assert) { 11 | assert.equal($.mockjaxSettings.retainAjaxCalls, true, '$.mockjaxSettings.retainAjaxCalls defaults to true'); 12 | }); 13 | 14 | t('Mocked GET request is properly retained when retainAjaxCalls is set to true', function(assert) { 15 | var done = assert.async(); 16 | 17 | $.mockjaxSettings.retainAjaxCalls = true; 18 | 19 | var numberOfMockedCalls = $.mockjax.mockedAjaxCalls().length; 20 | assert.equal(numberOfMockedCalls, 0, 'No mocked calls at the start'); 21 | 22 | $.mockjax({ 23 | url: '/api/example/*' 24 | }); 25 | 26 | $.ajax({ 27 | async: false, 28 | type: 'GET', 29 | url: '/api/example/1', 30 | complete: function() { 31 | var mockedAjaxCalls = $.mockjax.mockedAjaxCalls(); 32 | assert.equal(mockedAjaxCalls.length, 1, 'mockjax call made'); 33 | assert.equal(mockedAjaxCalls[0].type, 'GET', 'mockjax call has expected method'); 34 | assert.equal(mockedAjaxCalls[0].url, '/api/example/1', 'mockjax call has expected url'); 35 | done(); 36 | } 37 | }); 38 | }); 39 | 40 | t('Mocked POST request (with data) is properly retained when retainAjaxCalls is set to true', function(assert) { 41 | var done = assert.async(); 42 | 43 | $.mockjaxSettings.retainAjaxCalls = true; 44 | 45 | $.mockjax({ 46 | url: '/api/example/*' 47 | }); 48 | 49 | $.ajax({ 50 | async: false, 51 | type: 'POST', 52 | url: '/api/example/2', 53 | data: {a: 1}, 54 | complete: function() { 55 | var mockedAjaxCalls = $.mockjax.mockedAjaxCalls(); 56 | assert.equal(mockedAjaxCalls.length, 1, 'mockjax call made'); 57 | assert.equal(mockedAjaxCalls[0].type, 'POST', 'mockjax call has expected method'); 58 | assert.equal(mockedAjaxCalls[0].url, '/api/example/2', 'mockjax call has expected url'); 59 | assert.deepEqual(mockedAjaxCalls[0].data, {a: 1}, 'mockjax call has expected data'); 60 | done(); 61 | } 62 | }); 63 | }); 64 | 65 | t('Mocked JSONP GET request is properly retained when retainAjaxCalls is set to true', function(assert) { 66 | var done = assert.async(); 67 | 68 | $.mockjaxSettings.retainAjaxCalls = true; 69 | 70 | var numberOfMockedCalls = $.mockjax.mockedAjaxCalls().length; 71 | assert.equal(numberOfMockedCalls, 0, 'No mocked calls at the start'); 72 | 73 | $.mockjax({ 74 | url: '/api/example/*', 75 | contentType: 'text/json', 76 | proxy: 'test_jsonp.js' 77 | }); 78 | var callbackExecuted = false; 79 | window.abcdef123456 = function() { 80 | assert.ok(true, 'JSONP Callback executed'); 81 | callbackExecuted = true; 82 | }; 83 | 84 | $.ajax({ 85 | url: '/api/example/jsonp?callback=?', 86 | jsonpCallback: 'abcdef123456', 87 | dataType: 'jsonp', 88 | error: qunit.noErrorCallbackExpected, 89 | complete: function() { 90 | var actualCalls = $.mockjax.mockedAjaxCalls(); 91 | assert.equal(actualCalls.length, 1, 'Mockjax call made'); 92 | assert.equal(actualCalls[0].url, '/api/example/jsonp?callback=abcdef123456', 'mockjax call has expected jsonp url'); 93 | assert.ok(callbackExecuted, 'The jsonp callback was executed'); 94 | window.abcdef123456 = null; 95 | done(); 96 | } 97 | }); 98 | }); 99 | 100 | t('Multiple mocked calls are properly retained and stored in call order', function(assert) { 101 | $.mockjaxSettings.retainAjaxCalls = true; 102 | 103 | $.mockjax({ 104 | url: '/api/example/*' 105 | }); 106 | 107 | assert.equal($.mockjax.mockedAjaxCalls().length, 0, 'Initially there are no saved ajax calls'); 108 | 109 | $.ajax({ 110 | async: false, 111 | type: 'GET', 112 | url: '/api/example/1' 113 | }); 114 | $.ajax({ 115 | async: false, 116 | type: 'GET', 117 | url: '/api/example/2' 118 | }); 119 | $.ajax({ 120 | async: false, 121 | url: '/api/example/jsonp?callback=?', 122 | jsonpCallback: 'foo123', 123 | dataType: 'jsonp' 124 | }); 125 | 126 | assert.equal($.mockjax.mockedAjaxCalls().length, 3, 'Afterwords there should be three saved ajax calls'); 127 | 128 | var mockedUrls = $.map($.mockjax.mockedAjaxCalls(), function(ajaxOptions) { 129 | return ajaxOptions.url; 130 | }); 131 | 132 | assert.deepEqual(mockedUrls, [ 133 | '/api/example/1', 134 | '/api/example/2', 135 | '/api/example/jsonp?callback=foo123' 136 | ], 'Mocked ajax calls are saved in execution order'); 137 | }); 138 | 139 | t('Mocked calls are not retained when retainAjaxCalls is set to false', function(assert) { 140 | var done = assert.async(); 141 | 142 | $.mockjaxSettings.retainAjaxCalls = false; 143 | 144 | var numberOfMockedCalls = $.mockjax.mockedAjaxCalls().length; 145 | assert.equal(numberOfMockedCalls, 0, 'No mocked calls at the start'); 146 | 147 | $.mockjax({ 148 | url: '/test', 149 | contentType: 'text/plain', 150 | responseText: 'test' 151 | }); 152 | 153 | $.ajax({ 154 | url: '/test', 155 | complete: function() { 156 | var numberOfMockedCalls = $.mockjax.mockedAjaxCalls().length; 157 | assert.equal(numberOfMockedCalls, 0, 'Mocked calls count did not increase'); 158 | done() 159 | } 160 | }); 161 | }); 162 | 163 | t('Unmocked calls are properly retained when retainAjaxCalls is true and throwUnmocked is false', function(assert) { 164 | var done = assert.async(); 165 | 166 | $.mockjaxSettings.retainAjaxCalls = true; 167 | $.mockjaxSettings.throwUnmocked = false; 168 | 169 | var numberOfUnmockedCalls = $.mockjax.unmockedAjaxCalls().length; 170 | assert.equal(numberOfUnmockedCalls, 0, 'No unmocked calls at the start'); 171 | 172 | $.ajax({ 173 | async: true, 174 | type: 'GET', 175 | url: '/api/example/1', 176 | complete: function() { 177 | var unmockedAjaxCalls = $.mockjax.unmockedAjaxCalls(); 178 | assert.equal(unmockedAjaxCalls.length, 1, 'Unmocked calls count increased by one'); 179 | assert.equal(unmockedAjaxCalls[0].url, '/api/example/1', 'unmockedAjaxcall has expected url'); 180 | done(); 181 | } 182 | }); 183 | }); 184 | 185 | t('Unmocked calls are not retained when retainAjaxCalls is set to false', function(assert) { 186 | var done = assert.async(); 187 | 188 | $.mockjaxSettings.throwUnmocked = false; 189 | $.mockjaxSettings.retainAjaxCalls = false; 190 | 191 | var numberOfUnmockedCalls = $.mockjax.unmockedAjaxCalls().length; 192 | assert.equal(numberOfUnmockedCalls, 0, 'No unmocked calls at the start'); 193 | 194 | $.ajax({ 195 | url: '/test.json', 196 | complete: function() { 197 | var numberOfUnmockedCalls = $.mockjax.unmockedAjaxCalls().length; 198 | assert.equal(numberOfUnmockedCalls, 0, 'Unmocked calls count did not increase'); 199 | done() 200 | } 201 | }); 202 | }); 203 | 204 | t('Clearing retained mocked calls via clearRetainedAjaxCalls', function(assert) { 205 | var done = assert.async(); 206 | 207 | $.mockjaxSettings.retainAjaxCalls = true; 208 | 209 | var numberOfMockedCalls = $.mockjax.mockedAjaxCalls().length; 210 | assert.equal(numberOfMockedCalls, 0, 'No mocked calls at the start'); 211 | 212 | $.mockjax({ 213 | url: '/test', 214 | contentType: 'text/plain', 215 | responseText: 'test' 216 | }); 217 | 218 | $.ajax({ 219 | url: '/test', 220 | complete: function() { 221 | var numberOfMockedCalls = $.mockjax.mockedAjaxCalls().length; 222 | assert.equal(numberOfMockedCalls, 1, 'Mocked calls count increased by one'); 223 | 224 | $.mockjax.clearRetainedAjaxCalls(); 225 | 226 | var numberOfMockedCalls = $.mockjax.mockedAjaxCalls().length; 227 | assert.equal(numberOfMockedCalls, 0, 'Mocked calls count was reset to zero'); 228 | 229 | done() 230 | } 231 | }); 232 | }); 233 | 234 | t('Clearing retained unmocked calls via clearRetainedAjaxCalls', function(assert) { 235 | var done = assert.async(); 236 | 237 | $.mockjaxSettings.throwUnmocked = false; 238 | $.mockjaxSettings.retainAjaxCalls = true; 239 | 240 | var numberOfUnmockedCalls = $.mockjax.unmockedAjaxCalls().length; 241 | assert.equal(numberOfUnmockedCalls, 0, 'No unmocked calls at the start'); 242 | 243 | $.mockjax({ 244 | url: '/test', 245 | contentType: 'text/plain', 246 | responseText: 'test' 247 | }); 248 | 249 | $.ajax({ 250 | url: '/test.json', 251 | complete: function() { 252 | var numberOfUnmockedCalls = $.mockjax.unmockedAjaxCalls().length; 253 | assert.equal(numberOfUnmockedCalls, 1, 'Unmocked calls count increased by one'); 254 | 255 | $.mockjax.clearRetainedAjaxCalls(); 256 | 257 | var numberOfUnmockedCalls = $.mockjax.unmockedAjaxCalls().length; 258 | assert.equal(numberOfUnmockedCalls, 0, 'Unmocked calls count was reset to zero'); 259 | 260 | done() 261 | } 262 | }); 263 | }); 264 | 265 | t('Clearing retained mocked calls via clear', function(assert) { 266 | var done = assert.async(); 267 | 268 | $.mockjaxSettings.retainAjaxCalls = true; 269 | 270 | var numberOfMockedCalls = $.mockjax.mockedAjaxCalls().length; 271 | assert.equal(numberOfMockedCalls, 0, 'No mocked calls at the start'); 272 | 273 | $.mockjax({ 274 | url: '/test', 275 | contentType: 'text/plain', 276 | responseText: 'test' 277 | }); 278 | 279 | $.ajax({ 280 | url: '/test', 281 | complete: function() { 282 | var numberOfMockedCalls = $.mockjax.mockedAjaxCalls().length; 283 | assert.equal(numberOfMockedCalls, 1, 'Mocked calls count increased by one'); 284 | 285 | $.mockjax.clear(); 286 | 287 | var numberOfMockedCalls = $.mockjax.mockedAjaxCalls().length; 288 | assert.equal(numberOfMockedCalls, 0, 'Mocked calls count was reset to zero'); 289 | 290 | done() 291 | } 292 | }); 293 | }); 294 | 295 | t('Clearing retained unmocked calls via clear', function(assert) { 296 | var done = assert.async(); 297 | 298 | $.mockjaxSettings.throwUnmocked = false; 299 | $.mockjaxSettings.retainAjaxCalls = true; 300 | 301 | var numberOfUnmockedCalls = $.mockjax.unmockedAjaxCalls().length; 302 | assert.equal(numberOfUnmockedCalls, 0, 'No unmocked calls at the start'); 303 | 304 | $.mockjax({ 305 | url: '/test', 306 | contentType: 'text/plain', 307 | responseText: 'test' 308 | }); 309 | 310 | $.ajax({ 311 | url: '/test.json', 312 | complete: function() { 313 | var numberOfUnmockedCalls = $.mockjax.unmockedAjaxCalls().length; 314 | assert.equal(numberOfUnmockedCalls, 1, 'Unmocked calls count increased by one'); 315 | 316 | $.mockjax.clear(); 317 | 318 | var numberOfUnmockedCalls = $.mockjax.unmockedAjaxCalls().length; 319 | assert.equal(numberOfUnmockedCalls, 0, 'Unmocked calls count was reset to zero'); 320 | 321 | done() 322 | } 323 | }); 324 | }); 325 | 326 | t('unmockedAjaxCalls is (and remains) empty when no unmocked ajax calls have occurred', function(assert) { 327 | var done = assert.async(); 328 | 329 | var numberOfUnmockedCalls = $.mockjax.unmockedAjaxCalls().length; 330 | assert.equal(numberOfUnmockedCalls, 0, 'No unmocked calls at the start'); 331 | 332 | $.mockjax({ 333 | url: '/api/example/1' 334 | }); 335 | 336 | $.ajax({ 337 | async: true, 338 | type: 'GET', 339 | url: '/api/example/1', 340 | complete: function() { 341 | var numberOfUnmockedCalls = $.mockjax.unmockedAjaxCalls().length; 342 | assert.equal(numberOfUnmockedCalls, 0, 'No unmocked calls after making a mocked call'); 343 | done(); 344 | } 345 | }); 346 | }); 347 | 348 | t('mockedAjaxCalls is (and remains) empty when no mocked ajax calls have occurred', function(assert) { 349 | var done = assert.async(); 350 | 351 | var numberOfMockedCalls = $.mockjax.mockedAjaxCalls().length; 352 | assert.equal(numberOfMockedCalls, 0, 'No mocked calls at the start'); 353 | 354 | $.ajax({ 355 | async: true, 356 | type: 'GET', 357 | url: '/api/example/1', 358 | complete: function() { 359 | var numberOfMockedCalls = $.mockjax.mockedAjaxCalls().length; 360 | assert.equal(numberOfMockedCalls, 0, 'No mocked calls after making an unmocked call'); 361 | done(); 362 | } 363 | }); 364 | }); 365 | })(window.QUnit, window.jQuery); 366 | -------------------------------------------------------------------------------- /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/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); -------------------------------------------------------------------------------- /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); -------------------------------------------------------------------------------- /test/test.json: -------------------------------------------------------------------------------- 1 | { "say" : "I'm a json file!" } 2 | -------------------------------------------------------------------------------- /test/test_jsonp.js: -------------------------------------------------------------------------------- 1 | abcdef123456({ "data" : "JSONP is cool" }) -------------------------------------------------------------------------------- /test/test_proxy.json: -------------------------------------------------------------------------------- 1 | { "proxy" : true } -------------------------------------------------------------------------------- /test/test_proxy.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | bar 4 | -------------------------------------------------------------------------------- /test/test_script.js: -------------------------------------------------------------------------------- 1 | TEST_SCRIPT_VAR = 1; --------------------------------------------------------------------------------