├── .gitignore
├── .jshintrc
├── .travis.yml
├── CONTRIBUTING.md
├── Gruntfile.js
├── LICENSE.txt
├── README.md
├── example
├── app
│ └── app.js
├── index.html
├── mocks
│ ├── default.js
│ ├── format.js
│ ├── multiple.js
│ └── users.js
├── protractor-conf.js
└── spec
│ ├── casing.spec.js
│ ├── convenience.spec.js
│ ├── default.spec.js
│ ├── external.spec.js
│ ├── fromFile.spec.js
│ ├── get.js
│ ├── headers.spec.js
│ ├── httpDefaults.spec.js
│ ├── inline.spec.js
│ ├── jsonFormat.spec.js
│ ├── params.spec.js
│ ├── plugin.spec.js
│ ├── postData.spec.js
│ ├── queryString.spec.js
│ ├── regex.spec.js
│ ├── requestsMade.spec.js
│ └── runtime.spec.js
├── index.js
├── lib
├── defaultConfig.js
├── httpMock.js
└── initData.js
├── package.json
├── specs
├── httpMock.spec.js
└── initData.spec.js
└── tests
├── convenienceMethods.test.js
├── directCalls.test.js
├── externalUrl.test.js
├── headers.test.js
├── interceptors.test.js
├── plugin.test.js
├── promises.test.js
├── queryString.test.js
├── regex.test.js
├── requests.test.js
├── runtime.test.js
├── setup.js
├── template.tmpl
└── transforms.test.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | *.sublime*
3 | *.log
4 | .DS_Store
5 | .grunt
6 | _SpecRunner.html
7 | tests/bundle/
8 | .idea/
9 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "browser": true,
4 | "esnext": true,
5 | "bitwise": true,
6 | "camelcase": true,
7 | "curly": true,
8 | "eqeqeq": true,
9 | "immed": true,
10 | "indent": 4,
11 | "latedef": true,
12 | "newcap": true,
13 | "noarg": true,
14 | "quotmark": "single",
15 | "regexp": true,
16 | "undef": true,
17 | "unused": true,
18 | "strict": true,
19 | "trailing": true,
20 | "smarttabs": true,
21 | "loopfunc": true,
22 | "globals": {
23 | "angular": false,
24 | "protractor": false,
25 | "define": false,
26 | "require": false,
27 | "describe": false,
28 | "it": false,
29 | "beforeEach": false,
30 | "afterEach": false,
31 | "expect": false,
32 | "inject": false,
33 | "jasmine": false,
34 | "spyOn": false,
35 | "xit": false,
36 | "xdescribe": false
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | bundler_args: --retry 3
3 | language: node_js
4 | node_js:
5 | - 4.2.2
6 | cache:
7 | directories:
8 | - node_modules/
9 | install:
10 | - npm set progress=false
11 | - travis_retry npm install
12 | before_script:
13 | - npm install -g grunt-cli
14 | script:
15 | - npm run webdriver-update
16 | - npm test
17 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to protractor-http-mock
2 | This project has come a long way thanks to the community, and its growth has been very much a result from countless suggestions and pull requests. Thank you all!
3 |
4 | ## Pull Requests
5 | I strongly encourage developers to submit pull requests for new features and bug fixes. Please add unit tests and/or an examples to any pull request submitted. This goes a long way in getting the pull request merged in as soon as possible.
6 |
7 | This project is developed using grunt. To run all tests, simply execute
8 |
9 | `npm install`
10 | `npm run webdriver-update`
11 | `npm test`
12 |
13 | * To add a unit test, please refer to tests/httpMock.test.js
14 | * To add an example, please refer to the example/ folder.
15 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function(grunt) {
4 | grunt.initConfig({
5 | jshint: {
6 | options: {
7 | jshintrc: '.jshintrc',
8 | },
9 | files: ['lib/**/*.js']
10 | },
11 | jasmine_nodejs: {
12 | all: {
13 | specs: ['specs/*.js']
14 | }
15 | },
16 | connect: {
17 | example: {
18 | options: {
19 | base: 'example'
20 | }
21 | },
22 | 'client-test': {
23 | options: {
24 | base: '.'
25 | }
26 | }
27 | },
28 | protractor: {
29 | options: {
30 | configFile: 'example/protractor-conf.js',
31 | //debug: true
32 | },
33 | example: {}
34 | },
35 | browserify: {
36 | test: {
37 | files: {
38 | 'tests/bundle/httpMock.js': ['lib/httpMock.js']
39 | },
40 | options: {
41 | browserifyOptions: {
42 | standalone: 'httpMock'
43 | }
44 | }
45 | }
46 | },
47 | jasmine: {
48 | test: {
49 | src: 'tests/bundle/httpMock.js',
50 | options: {
51 | specs: 'tests/*.test.js',
52 | vendor: [
53 | 'http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular.js'
54 | ],
55 | helpers: [
56 | 'tests/setup.js'
57 | ],
58 | template: 'tests/template.tmpl'
59 | }
60 | }
61 | }
62 | });
63 |
64 | grunt.loadNpmTasks('grunt-contrib-jshint');
65 | grunt.loadNpmTasks('grunt-jasmine-nodejs');
66 | grunt.loadNpmTasks('grunt-contrib-connect');
67 | grunt.loadNpmTasks('grunt-protractor-runner');
68 | grunt.loadNpmTasks('grunt-browserify');
69 | grunt.loadNpmTasks('grunt-contrib-jasmine');
70 |
71 | grunt.registerTask('host-example', ['connect:example:keepalive']);
72 | grunt.registerTask('example', ['connect:example', 'protractor:example']);
73 | grunt.registerTask('test', ['jasmine_nodejs']);
74 | grunt.registerTask('lint', ['jshint']);
75 | grunt.registerTask('client-test', ['browserify:test', 'jasmine:test']);
76 |
77 | grunt.registerTask('verify', ['lint', 'test', 'client-test', 'example']);
78 | };
79 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Carlos Atencio
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Protractor Mock
2 | A NodeJS module to be used alongside [Protractor](https://github.com/angular/protractor) to facilitate setting up mocks for HTTP calls for the AngularJS applications under test.
3 |
4 | This allows the developer to isolate the UI and client-side application code in our tests without any dependencies on an API.
5 |
6 | **This plugin does not depend on Angular Mocks (ngMockE2E) being loaded into your app; therefore, there is no need to modify anything within your current Angular web application.**
7 |
8 | [](https://travis-ci.org/atecarlos/protractor-http-mock)
9 |
10 | ## Installation
11 | npm install protractor-http-mock --save-dev
12 | ## Configuration
13 | In your protractor configuration file, we can set the following options:
14 |
15 | ### Mocks
16 | We can set a collection of default mocks to load for every test, and the name of the folder where your mocks will reside. More on this later.
17 |
18 | mocks: {
19 | default: ['mock-login'], // default value: []
20 | dir: 'my-mocks' // default value: 'mocks'
21 | },
22 |
23 | ### Directories and file names
24 | We can also configure our root directory where the mocks and protractor configuration will be located; as well as, the name of the protractor configuration file.
25 |
26 | onPrepare: function(){
27 | require('protractor-http-mock').config = {
28 | rootDirectory: __dirname, // default value: process.cwd()
29 | protractorConfig: 'my-protractor-config.conf' // default value: 'protractor-conf.js'
30 | };
31 | }
32 |
33 | ## Usage
34 | Mocks are defined as JSON objects describing the request and response for a particular HTTP call:
35 |
36 | {
37 | request: {
38 | path: '/users/1',
39 | method: 'GET'
40 | },
41 | response: {
42 | data: {
43 | userName: 'pro-mock',
44 | email: 'pro-mock@email.com'
45 | }
46 | }
47 | }
48 |
49 | And then set the mock at the beginning of your test before your application loads:
50 |
51 | var mock = require('protractor-http-mock');
52 | ...
53 |
54 | mock([{
55 | request: {
56 | path: '/users/1',
57 | method: 'GET'
58 | },
59 | response: {
60 | data: {
61 | userName: 'pro-mock',
62 | email: 'pro-mock@email.com'
63 | }
64 | }
65 | }]);
66 |
67 | Make sure to clean up after test execution. This should be typically done in the `afterEach` call to ensure the teardown is executed regardless of what happens in the test execution:
68 |
69 | afterEach(function(){
70 | mock.teardown();
71 | });
72 |
73 | Please note that the `mock()` function needs to be called before the browser opens. If you have different mock data for different tests, please make sure that, either the tests always start in a new browser window, or that its possible to setup all the mocks for each test case before any of tests start running.
74 |
75 | ### Mock files
76 | Mocks can also be loaded from physical files located in the `mocks.dir` directory that we defined in our configuration:
77 |
78 | tests
79 | e2e
80 | protractor.conf.js
81 | mocks
82 | users.js
83 | specs
84 | ...
85 |
86 |
87 | You could simply load your mocks as follows:
88 |
89 | mock(['users']);
90 |
91 | Files are structured as standard node modules. The strings passed are the path of the file relative to the mocks directory - the same as if you would be doing a standard `require()` call.
92 |
93 | module.exports = { ... }; // for a single mock.
94 |
95 | or
96 |
97 | module.exports = [ ... ]; // for multiple mocks.
98 |
99 |
100 | ### Schema
101 | The full GET schema for defining your mocks is as follows:
102 |
103 | request: {
104 | path: '/products/1/items',
105 | method: 'GET',
106 | regex: false, // Boolean to enable Regular Expression matching on path. This is an optional field.
107 | params: { // These match params as they would be passed to the $http service. This is an optional field.
108 | page: 2,
109 | status: 'onsale'
110 | },
111 | queryString: { // These match query string parameters that are part of the URL as passed in to the $http service. This is an optional field.
112 | param1: 'My first qs parameters',
113 | param2: 'Second parameter'
114 | },
115 | headers: { //These match headers as the end result of the expression provided to the $http method.
116 | head1: 'val1',
117 | head2: 'val2'
118 | }
119 | },
120 | response: {
121 | data: {}, // This is the return value for the matched request
122 | status: 500 // The HTTP status code for the mocked response. This is an optional field.
123 | delay: 2 // number of milliseconds to delay sending back the response.
124 | }
125 |
126 | A full mock for a POST call takes the following options:
127 |
128 | request: {
129 | path: '/products/1/items',
130 | method: 'POST',
131 | regex: false, // Boolean to enable Regular Expression matching on path. This is an optional field.
132 | data: { // These match POST data. This is an optional field.
133 | status: 'onsale',
134 | title: 'Blue Jeans',
135 | price: 24.99
136 | }
137 | },
138 | response: {
139 | data: { // This is the return value for the matched request
140 | status: 'onsale',
141 | title: 'Blue Jeans',
142 | id: 'abc123',
143 | price: 24.99
144 | },
145 | status: 204 // The HTTP status code for the mocked response. This is an optional field.
146 | }
147 |
148 | PUT, DELETE, HEAD, PATCH, and JSONP methods are also supported. Please see the examples in the source code for more information.
149 |
150 | #### Request
151 | Defining `params`, `queryString`, `headers`, or `data` will help the plugin match more specific responses but neither is required. Both correspond to their matching objects as they would be passed into the $http object call.
152 |
153 | Headers must be defined as the headers that will be used in the http call. Therefore, if in the code to be tested, the headers are defined using properties with function values, these functions will be evaluated as per the $http specification and matched by end result.
154 |
155 | #### Response
156 | The default `status` value is 200 if none is specified.
157 |
158 | An optional `delay` value can be set on the response to assert any state that occurs when waiting for the response in your application, i.e. loading messages or spinners. Please note that UI tests with timing expectations can be somewhat unstable and provide inconsistent results. Please use this feature carefully.
159 |
160 | ### Precendence
161 | protractor-http-mock will respond with the **last** matched request in case there are several matches. The plugin will start matching the default mocks first, followed by those added within the test itself in the order they are added. In other words, the last mock defined will always win.
162 |
163 | ### Inspection
164 | For testing or debugging purposes, it is possible to extract a list of http requests. Simply call the `requestsMade` function as follows:
165 |
166 | var mock = require('protractor-http-mock');
167 | ...
168 |
169 | expect(mock.requestsMade()).toEqual([
170 | { url : '/default', method : 'GET' },
171 | { url : '/users', method : 'GET' }
172 | ]);
173 |
174 | It is also possible to clear the list of requests with the `clearRequests()` method.
175 |
176 | If you wish to assert anything but the full list of requests, then you can do the following to piece out the information needed on the requests:
177 |
178 | mock.requestsMade().then(function(requests){
179 | expect(requests[1]).toEqual({ url : '/users', method : 'GET' })
180 | });
181 |
182 | ### Runtime mocks
183 | If there is a need to add or remove mocks during test execution, please use the `add()` and `remove()` functions:
184 |
185 | mock.add([{
186 | request: {
187 | path: '/users',
188 | method: 'GET',
189 | params: {
190 | name: 'Charlie'
191 | }
192 | },
193 | response: {
194 | data: {
195 | name: 'Override'
196 | }
197 | }
198 | }]);
199 |
200 | ...
201 |
202 | mock.remove([{
203 | request: {
204 | path: '/users',
205 | method: 'GET',
206 | params: {
207 | name: 'Charlie'
208 | }
209 | },
210 | response: {
211 | data: {
212 | name: 'Override'
213 | }
214 | }
215 | }]);
216 |
217 | These will dynamically modify your current set of mocks, and any new request that happens after that will work with the updated set of mocks. Please note that these functions only work by adding or removing mocks using inline objects. As of now, it is not possible to add or remove mocks using mock files.
218 |
219 | ### Plugins
220 |
221 | Plugins can be used to extend the matching functionality of protractor-http-mock. These are separate from protractor plugins.
222 |
223 | A plugin can be defined as either an NPM package or a function.
224 |
225 | They can be declared in your protractor configuration to be consumed by all your tests:
226 |
227 | baseUrl: 'http://localhost:8000/',
228 | specs: [
229 | 'spec/*.spec.js'
230 | ],
231 | httpMockPlugins: {
232 | default: ['protractor-http-mock-sample-plugin']
233 | }
234 |
235 | or in each individual test:
236 |
237 | mock([
238 | //mocks go here
239 | ], [
240 | {
241 | match: function(mockRequest, requestConfig){
242 | ...
243 | }
244 | }
245 | ]);
246 |
247 | Note that in both your protractor configuration and tests, a plugin can be declared as either an npm package name, or definining the object inline.
248 |
249 | See this [sample plugin](https://github.com/atecarlos/protractor-http-mock-sample-plugin) for more information.
250 |
251 | ### Defaults
252 |
253 | If necessary, default mocks and plugins can be skipped for a particular test simply by passing true at the end of your `mock` call:
254 |
255 | mock(mocks, plugins, true);
256 |
257 |
258 | ### Examples
259 | Included in the code base is an extensive list examples on how to use all the features of this plugin. Please take a look if you have any questions.
260 |
261 | To run these tests locally, please follow these steps from the root directory:
262 |
263 | 1. `npm install`
264 | 2. `npm run webdriver-update`
265 | 3. `npm run example`
266 |
--------------------------------------------------------------------------------
/example/app/app.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('example', [])
3 | .factory('defaultService', function($http){
4 | return {
5 | get: function(){
6 | return $http({ method: 'GET', url: '/default' });
7 | }
8 | };
9 | })
10 | .factory('userService', function($http){
11 | return {
12 | get: function(){
13 | return $http({ method: 'GET', url: '/users' });
14 | },
15 | post: function(data){
16 | return $http({ method: 'POST', url: '/users/new', data: data });
17 | },
18 | getBy: function(name, city){
19 | return $http({
20 | method: 'GET',
21 | url: '/users',
22 | params: {
23 | name: name,
24 | city: city
25 | }
26 | });
27 | },
28 | getById: function(id){
29 | console.log(id);
30 | return $http({
31 | method: 'GET',
32 | url: '/users/' + id
33 | });
34 | },
35 | getByQuery: function(name, city){
36 | return $http({
37 | method: 'GET',
38 | url: '/users?name=' + encodeURIComponent(name) + '&city=' + encodeURIComponent(city)
39 | });
40 | },
41 | getAuthenticated: function(auth){
42 | return $http({
43 | method: 'GET',
44 | url: '/users',
45 | headers: {
46 | auth: auth
47 | }
48 | });
49 | },
50 | getFromExternalBy: function(name, city){
51 | return $http({
52 | method: 'GET',
53 | url: 'http://some-other.com/users',
54 | params: {
55 | name: name,
56 | city: city
57 | }
58 | });
59 | },
60 | getThroughPlugin: function(){
61 | return $http.get('/users', {
62 | plugin: {}
63 | });
64 | }
65 | };
66 | })
67 | .factory('groupService', function($http){
68 | return {
69 | get: function(){
70 | return $http({ method: 'GET', url: '/groups' });
71 | }
72 | };
73 | })
74 | .factory('convenienceService', function($http){
75 | return {
76 | get: function(){
77 | return $http.get('/convs')
78 | },
79 | post: function(data){
80 | return $http.post('/convs/new', data)
81 | },
82 | delete: function(id){
83 | return $http.delete('/convs/' + id)
84 | },
85 | head: function(){
86 | return $http.head('/convs');
87 | },
88 | jsonp: function(){
89 | return $http.jsonp('/convs');
90 | },
91 | patch: function(id, data){
92 | return $http.patch('/convs/' + id, data);
93 | },
94 | put: function(id, data){
95 | return $http.put('/convs/' + id, data);
96 | }
97 | }
98 | })
99 | .controller('DefaultController', function(defaultService){
100 | var self = this;
101 | self.name = '';
102 |
103 | defaultService.get()
104 | .then(function(response){
105 | self.name = response.data.name;
106 | });
107 | })
108 | .controller('UserController', function(userService){
109 | var self = this;
110 |
111 | self.users = [];
112 | self.error = null;
113 | self.success = null;
114 | self.showError = false;
115 | self.foundUser = null;
116 | self.authenticated = false;
117 |
118 | userService.get()
119 | .then(function(response){
120 | self.users = response.data;
121 | })
122 | .catch(function(response){
123 | self.showError = true;
124 | self.error = response.data.error;
125 | });
126 |
127 | function thenHandler(){
128 | self.success = 'new user saved: ' + self.newUser;
129 | self.newUser = '';
130 |
131 | self.showError = false;
132 | self.error = null;
133 | }
134 |
135 | function catchHandler(response){
136 | self.showError = true;
137 | self.error = response.data.error;
138 |
139 | self.success = null;
140 | self.newUser = null;
141 | }
142 |
143 | self.save = function(){
144 | userService.post({ name: self.newUser })
145 | .then(thenHandler)
146 | .catch(catchHandler);
147 | };
148 |
149 | self.saveName = function(){
150 | userService.post(self.newUser)
151 | .then(thenHandler)
152 | .catch(catchHandler);
153 | };
154 |
155 | self.addDefaultUsers = function(){
156 | userService.post({
157 | group: self.newUser,
158 | users: [
159 | { name: 'One' },
160 | { name: 'Two' }
161 | ]
162 | }).then(thenHandler).catch(catchHandler);
163 | };
164 |
165 | function searchHandler(response){
166 | self.foundUser = response.data.name;
167 | self.success = 'User found: ' + self.query;
168 | self.showError = false;
169 | self.error = null;
170 | }
171 |
172 | self.search = function(){
173 | userService.getBy(self.query, self.queryCity || undefined)
174 | .then(searchHandler)
175 | .catch(catchHandler);
176 | };
177 |
178 | self.searchById = function() {
179 | userService.getById(self.query)
180 | .then(searchHandler)
181 | .catch(catchHandler);
182 | };
183 |
184 | self.searchByQuery = function(){
185 | userService.getByQuery(self.query, self.queryCity)
186 | .then(searchHandler)
187 | .catch(catchHandler);
188 | };
189 |
190 | self.searchExternal = function(){
191 | userService.getFromExternalBy(self.query, self.queryCity)
192 | .then(searchHandler)
193 | .catch(catchHandler);
194 | };
195 |
196 | self.getAuthenticated = function(){
197 | userService.getAuthenticated(self.authenticated)
198 | .then(function(response){
199 | self.users = response.data;
200 | })
201 | .catch(catchHandler);
202 | };
203 | })
204 | .controller('GroupController', function(groupService){
205 | var self = this;
206 |
207 | self.groups = [];
208 |
209 | groupService.get()
210 | .then(function(response){
211 | self.groups = response.data;
212 | });
213 | })
214 | .controller('ConvenienceController', function(convenienceService){
215 | var self = this;
216 |
217 | self.convs = [];
218 | self.showError = false;
219 |
220 | function handler(data, status){
221 | self.data = data;
222 | self.status = status;
223 | }
224 |
225 | function successHandler(data, status){
226 | self.showError = false;
227 | handler(data, status);
228 | }
229 |
230 | function errorHandler(data, status){
231 | self.showError = true;
232 | handler(data, status);
233 | }
234 |
235 | convenienceService.get()
236 | .success(successHandler)
237 | .error(errorHandler);
238 |
239 | self.getMeta = function(){
240 | convenienceService.head()
241 | .success(successHandler)
242 | .error(errorHandler);
243 | };
244 |
245 | self.save = function(){
246 | var dataToPost = self.input ? { test: self.input } : undefined;
247 |
248 | convenienceService.post(dataToPost)
249 | .success(successHandler)
250 | .error(errorHandler);
251 | };
252 |
253 | self.delete = function(){
254 | convenienceService.delete(self.id)
255 | .success(successHandler)
256 | .error(errorHandler);
257 | };
258 |
259 | self.update = function(){
260 | convenienceService.put(self.id, { test: self.input })
261 | .success(successHandler)
262 | .error(errorHandler);
263 | };
264 |
265 | self.patch = function(){
266 | convenienceService.patch(self.id, { test: self.input })
267 | .success(successHandler)
268 | .error(errorHandler);
269 | };
270 |
271 | self.jsonp = function(){
272 | convenienceService.jsonp()
273 | .success(successHandler)
274 | .error(errorHandler);
275 | };
276 | })
277 | .controller('HttpDefaultsController', function($http){
278 | this.hasHttpDefaults = !!$http.defaults;
279 | })
280 | .controller('PluginsController', function(userService){
281 | var self = this;
282 |
283 | self.get = function(){
284 | return userService.getThroughPlugin().then(function(response){
285 | console.log('HERE', response);
286 | self.result = response.data;
287 | });
288 | }
289 | });
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Protractor Http Mock - Example
5 |
6 |
7 |
8 | {{ ctrl.name }}
9 |
10 |
11 |
12 |
13 | -
14 | {{ user.firstName }}
15 | |
16 | {{ user.lastName }}
17 |
18 |
19 |
20 |
{{ ctrl.error }}
21 |
{{ ctrl.success }}
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | {{ ctrl.foundUser }}
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | - {{ group.name }}
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | {{ ctrl.data }}
67 | {{ ctrl.status }}
68 | My convenience error
69 |
70 |
71 |
72 |
$http defaults are set
73 |
74 |
75 |
76 |
77 | {{ ctrl.result }}
78 |
79 |
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/example/mocks/default.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | request: {
3 | path: '/default',
4 | method: 'GET'
5 | },
6 | response: {
7 | data: {
8 | name: "i'm always loaded"
9 | }
10 | }
11 | };
--------------------------------------------------------------------------------
/example/mocks/format.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'request': {
3 | 'path': '/groups',
4 | 'method': 'GET'
5 | },
6 | "response": {
7 | 'data': [ { name: 'format test' }]
8 | }
9 | };
--------------------------------------------------------------------------------
/example/mocks/multiple.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | {
3 | request: {
4 | path: '/users',
5 | method: 'GET'
6 | },
7 | response: {
8 | data: [
9 | {
10 | firstName: 'multiple',
11 | lastName: 'from file'
12 | }
13 | ]
14 | }
15 | },
16 | {
17 | request: {
18 | path: '/groups',
19 | method: 'GET'
20 | },
21 | response: {
22 | data: [
23 | { name: 'file first' },
24 | { name: 'file second' }
25 | ]
26 | }
27 | }
28 | ];
29 |
--------------------------------------------------------------------------------
/example/mocks/users.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | request: {
3 | path: '/users',
4 | method: 'GET'
5 | },
6 | response: {
7 | data: [
8 | {
9 | firstName: 'from',
10 | lastName: 'file'
11 | },
12 | {
13 | firstName: 'second',
14 | lastName: 'from file'
15 | }
16 | ]
17 | }
18 | };
--------------------------------------------------------------------------------
/example/protractor-conf.js:
--------------------------------------------------------------------------------
1 | var config = {
2 | baseUrl: 'http://localhost:8000/',
3 | specs: [
4 | 'spec/*.spec.js'
5 | ],
6 | mocks: {
7 | dir: 'mocks',
8 | default: ['default']
9 | },
10 | httpMockPlugins: {
11 | default: ['protractor-http-mock-sample-plugin']
12 | },
13 | onPrepare: function(){
14 | require('../index').config = {
15 | rootDirectory: __dirname
16 | }
17 | }
18 | };
19 |
20 | if (process.env.TRAVIS) {
21 | //Run PhantomJS on Travis
22 | config.capabilities = {
23 | browserName: 'phantomjs',
24 | 'phantomjs.ghostdriver.cli.args': ['--loglevel=DEBUG'],
25 | shardTestFiles: true,
26 | maxInstances: 2
27 | };
28 | } else {
29 | config.capabilities = {
30 | browserName: 'chrome'
31 | };
32 | }
33 | exports.config = config;
--------------------------------------------------------------------------------
/example/spec/casing.spec.js:
--------------------------------------------------------------------------------
1 | var mock = require('../../index'), //substitute for require('protractor-http-mock')
2 | get = require('./get');
3 |
4 | describe('casing', function(){
5 |
6 | afterEach(function(){
7 | mock.teardown();
8 | });
9 |
10 | it('ignores casing for request method', function(){
11 | mock([
12 | {
13 | request: {
14 | path: '/users',
15 | method: 'get'
16 | },
17 | response: {
18 | data: [
19 | {
20 | firstName: 'multiple',
21 | lastName: 'mocks'
22 | }
23 | ]
24 | }
25 | }
26 | ]);
27 |
28 | get();
29 |
30 | element.all(by.repeater('user in ctrl.users')).then(function(users){
31 | expect(users.length).toBe(1);
32 | expect(users[0].getText()).toBe('multiple | mocks');
33 | });
34 | });
35 | });
--------------------------------------------------------------------------------
/example/spec/convenience.spec.js:
--------------------------------------------------------------------------------
1 | var mock = require('../../index'), //substitute for require('protractor-http-mock')
2 | get = require('./get');
3 |
4 | describe('requests made', function(){
5 |
6 | afterEach(function(){
7 | mock.teardown();
8 | });
9 |
10 | function assertData(expectedData){
11 | expect(element(by.id('conv-data')).getText()).toBe(expectedData);
12 | }
13 |
14 | function assertStatus(status){
15 | expect(element(by.id('conv-status')).getText()).toBe(status.toString());
16 | }
17 |
18 | function setId(val){
19 | element(by.model('ctrl.id')).clear().sendKeys(val);
20 | }
21 |
22 | function setInput(val){
23 | element(by.model('ctrl.input')).clear().sendKeys(val);
24 | }
25 |
26 | function clearInput(){
27 | element(by.model('ctrl.input')).clear();
28 | }
29 |
30 | it('works with $http convenience methods', function(){
31 | mock([
32 | {
33 | request: {
34 | path: '/convs',
35 | method: 'GET'
36 | },
37 | response: {
38 | data: 'get - success!'
39 | }
40 | },
41 | {
42 | request: {
43 | path: '/convs',
44 | method: 'HEAD'
45 | },
46 | response: {
47 | data: 'head - success!'
48 | }
49 | },
50 | {
51 | request: {
52 | path: 'convs/new',
53 | method: 'POST',
54 | data: {
55 | test: 'my new conv'
56 | }
57 | },
58 | response: {
59 | data: 'post - success!',
60 | status: 201
61 | }
62 | },
63 | {
64 | request: {
65 | path: 'convs/2',
66 | method: 'DELETE'
67 | },
68 | response: {
69 | data: 'delete - success',
70 | status: 202
71 | }
72 | },
73 | {
74 | request: {
75 | path: 'convs/3',
76 | method: 'DELETE'
77 | },
78 | response: {
79 | data: 'delete - error',
80 | status: 400
81 | }
82 | },
83 | {
84 | request: {
85 | path: 'convs/3',
86 | method: 'PUT',
87 | data: {
88 | test: 'my updated conv'
89 | }
90 | },
91 | response: {
92 | data: 'put - success',
93 | status: 203
94 | }
95 | },
96 | {
97 | request: {
98 | path: 'convs/3',
99 | method: 'PATCH',
100 | data: {
101 | test: 'my updated conv'
102 | }
103 | },
104 | response: {
105 | data: 'patch - error',
106 | status: 401
107 | }
108 | },
109 | {
110 | request: {
111 | path: 'convs/4',
112 | method: 'PATCH',
113 | data: {
114 | test: 'my patched conv'
115 | }
116 | },
117 | response: {
118 | data: 'patch - success',
119 | status: 204
120 | }
121 | },
122 | {
123 | request: {
124 | path: 'convs',
125 | method: 'JSONP'
126 | },
127 | response: {
128 | data: 'jsonp - success',
129 | status: 205
130 | }
131 | }
132 | ]);
133 |
134 | // get
135 | get();
136 | assertData('get - success!');
137 | assertStatus(200);
138 |
139 | // head
140 | element(by.id('conv-head')).click();
141 | assertData('head - success!');
142 | assertStatus(200);
143 |
144 | // post
145 | setInput('my new conv');
146 | element(by.id('conv-save')).click();
147 | assertData('post - success!');
148 | assertStatus(201);
149 | clearInput();
150 |
151 | // delete
152 | setId('2');
153 | element(by.id('conv-delete')).click();
154 | assertData('delete - success');
155 | assertStatus(202);
156 |
157 | // delete - error
158 | setId('3');
159 | element(by.id('conv-delete')).click();
160 | expect(element(by.id('conv-error')).isDisplayed()).toBe(true);
161 | assertData('delete - error');
162 | assertStatus(400);
163 |
164 | // put
165 | setId('3');
166 | setInput('my updated conv');
167 |
168 | element(by.id('conv-update')).click();
169 | expect(element(by.id('conv-error')).isDisplayed()).toBe(false);
170 | assertData('put - success');
171 | assertStatus(203);
172 |
173 | // patch - error
174 | element(by.id('conv-patch')).click();
175 | expect(element(by.id('conv-error')).isDisplayed()).toBe(true);
176 | assertData('patch - error');
177 | assertStatus(401);
178 |
179 | clearInput();
180 |
181 | // patch
182 | setId('4');
183 | setInput('my patched conv');
184 | element(by.id('conv-patch')).click();
185 | assertData('patch - success');
186 | assertStatus(204);
187 |
188 | // jsonp
189 | setId('5');
190 | element(by.id('conv-jsonp')).click();
191 | assertData('jsonp - success');
192 | assertStatus(205);
193 | });
194 | });
--------------------------------------------------------------------------------
/example/spec/default.spec.js:
--------------------------------------------------------------------------------
1 | var mock = require('../../index'),
2 | get = require('./get');
3 |
4 | describe('default file', function(){
5 | afterEach(function(){
6 | mock.teardown();
7 | });
8 |
9 | it('loads a single mock from file', function(){
10 | mock();
11 | get();
12 | expect(browser.getTitle()).toBe('Protractor Http Mock - Example');
13 | expect($('#default-name').getText()).toBe("i'm always loaded");
14 | });
15 | });
--------------------------------------------------------------------------------
/example/spec/external.spec.js:
--------------------------------------------------------------------------------
1 | var mock = require('../../index'),
2 | get = require('./get');
3 |
4 | describe('get query strings', function(){
5 | beforeEach(function(){
6 | mock([
7 | {
8 | request: {
9 | path: '/users',
10 | method: 'GET'
11 | },
12 | response: {
13 | status: 200
14 | }
15 | },
16 | {
17 | request: {
18 | path: '/users',
19 | method: 'GET',
20 | params: {
21 | name: 'External',
22 | city: 'Query'
23 | }
24 | },
25 | response: {
26 | data: {
27 | name: 'Searched external URL'
28 | }
29 | }
30 | }
31 | ]);
32 | });
33 |
34 | afterEach(function(){
35 | mock.teardown();
36 | });
37 |
38 | it('matches by params', function(){
39 | get();
40 |
41 | element(by.id('user-query')).sendKeys('External');
42 | element(by.id('user-query-city')).sendKeys('Query');
43 | element(by.id('user-search-external-by-query-button')).click();
44 |
45 | expect(element(by.id('user-data')).getText()).toContain('Searched external URL');
46 | expect(element(by.id('successMsg')).getText()).toBe('User found: External');
47 | });
48 | });
--------------------------------------------------------------------------------
/example/spec/fromFile.spec.js:
--------------------------------------------------------------------------------
1 | var mock = require('../../index'),
2 | get = require('./get');
3 |
4 | describe('from file', function(){
5 | afterEach(function(){
6 | mock.teardown();
7 | });
8 |
9 | it('loads a single mock from file', function(){
10 | mock(['users']);
11 |
12 | get();
13 |
14 | expect(browser.getTitle()).toBe('Protractor Http Mock - Example');
15 |
16 | element.all(by.repeater('user in ctrl.users')).then(function(users){
17 | expect(users.length).toBe(2);
18 | expect(users[0].getText()).toBe('from | file');
19 | expect(users[1].getText()).toBe('second | from file');
20 | });
21 | });
22 |
23 | it('loads multiple from a file', function(){
24 | mock(['multiple']);
25 |
26 | get();
27 |
28 | element.all(by.repeater('user in ctrl.users')).then(function(users){
29 | expect(users.length).toBe(1);
30 | expect(users[0].getText()).toBe('multiple | from file');
31 | });
32 |
33 | element.all(by.repeater('group in ctrl.groups')).then(function(groups){
34 | expect(groups.length).toBe(2);
35 | expect(groups[0].getText()).toBe('file first');
36 | expect(groups[1].getText()).toBe('file second');
37 | });
38 | });
39 | });
--------------------------------------------------------------------------------
/example/spec/get.js:
--------------------------------------------------------------------------------
1 | module.exports = function(){
2 | browser.get('http://localhost:8000');
3 | };
--------------------------------------------------------------------------------
/example/spec/headers.spec.js:
--------------------------------------------------------------------------------
1 | var mock = require('../../index'), //substitute for require('protractor-http-mock')
2 | get = require('./get');
3 |
4 | describe('headers', function(){
5 |
6 | afterEach(function(){
7 | mock.teardown();
8 | });
9 |
10 | it('matchers with headers', function(){
11 | mock([
12 | {
13 | request: {
14 | path: '/users',
15 | method: 'get'
16 | },
17 | response: {
18 | status: 403,
19 | data: {
20 | error: 'not authorized'
21 | }
22 | }
23 | },
24 | {
25 | request: {
26 | path: '/users',
27 | method: 'get',
28 | headers: {
29 | auth: true
30 | }
31 | },
32 | response: {
33 | data: [
34 | {
35 | firstName: 'auth',
36 | lastName: 'one'
37 | },
38 | {
39 | firstName: 'auth',
40 | lastName: 'two'
41 | }
42 | ]
43 | }
44 | }
45 | ]);
46 |
47 | get();
48 |
49 | element(by.id('user-get-authenticated-button')).click();
50 |
51 | var users = element.all(by.repeater('user in ctrl.users'));
52 |
53 | expect(users.count()).toBe(0);
54 | var errElement = element(by.binding('ctrl.error'));
55 | expect(errElement.isDisplayed()).toBe(true);
56 | expect(errElement.getText()).toBe('not authorized');
57 |
58 | element(by.id('user-select-authenticated')).click();
59 | element(by.id('user-get-authenticated-button')).click();
60 |
61 | element.all(by.repeater('user in ctrl.users')).then(function(users){
62 | expect(users.length).toBe(2);
63 | expect(users[0].getText()).toBe('auth | one');
64 | expect(users[1].getText()).toBe('auth | two');
65 | });
66 | });
67 | });
--------------------------------------------------------------------------------
/example/spec/httpDefaults.spec.js:
--------------------------------------------------------------------------------
1 | var mock = require('../../index'),
2 | get = require('./get');
3 |
4 | describe('http defaults', function(){
5 | afterEach(function(){
6 | mock.teardown();
7 | });
8 |
9 | it('loads http defaults', function(){
10 | mock();
11 | get();
12 | expect(element(by.id('http-defaults')).isDisplayed()).toBe(true);
13 | });
14 | });
--------------------------------------------------------------------------------
/example/spec/inline.spec.js:
--------------------------------------------------------------------------------
1 | var mock = require('../../index'), //substitute for require('protractor-http-mock')
2 | get = require('./get');
3 |
4 | describe('inline', function(){
5 |
6 | afterEach(function(){
7 | mock.teardown();
8 | });
9 |
10 | describe('success', function(){
11 | it('can set a single mock inline', function(){
12 | mock([{
13 | request: {
14 | path: '/users',
15 | method: 'GET'
16 | },
17 | response: {
18 | data: [
19 | {
20 | firstName: 'carlos',
21 | lastName: 'npm'
22 | },
23 | {
24 | firstName: 'angular',
25 | lastName: 'js'
26 | }
27 | ]
28 | }
29 | }]);
30 |
31 | get();
32 | expect(browser.getTitle()).toBe('Protractor Http Mock - Example');
33 |
34 | element.all(by.repeater('user in ctrl.users')).then(function(users){
35 | expect(users.length).toBe(2);
36 | expect(users[0].getText()).toBe('carlos | npm');
37 | expect(users[1].getText()).toBe('angular | js');
38 | });
39 |
40 | expect(element(by.binding('ctrl.error')).isDisplayed()).toBe(false);
41 | });
42 |
43 | it('can set multiple mocks inline', function(){
44 | mock([
45 | {
46 | request: {
47 | path: '/users',
48 | method: 'GET'
49 | },
50 | response: {
51 | data: [
52 | {
53 | firstName: 'multiple',
54 | lastName: 'mocks'
55 | }
56 | ]
57 | }
58 | },
59 | {
60 | request: {
61 | path: '/groups',
62 | method: 'GET'
63 | },
64 | response: {
65 | data: [
66 | { name: 'first' },
67 | { name: 'second' }
68 | ]
69 | }
70 | },
71 | {
72 | request: {
73 | path: 'users/new',
74 | method: 'POST',
75 | data: {
76 | name: 'my-new-user'
77 | }
78 | },
79 | response: {
80 | status: 200
81 | }
82 | }
83 | ]);
84 |
85 | get();
86 |
87 | element.all(by.repeater('user in ctrl.users')).then(function(users){
88 | expect(users.length).toBe(1);
89 | expect(users[0].getText()).toBe('multiple | mocks');
90 | });
91 |
92 | element.all(by.repeater('group in ctrl.groups')).then(function(groups){
93 | expect(groups.length).toBe(2);
94 | expect(groups[0].getText()).toBe('first');
95 | expect(groups[1].getText()).toBe('second');
96 | });
97 |
98 | expect(element(by.binding('ctrl.error')).isDisplayed()).toBe(false);
99 |
100 | element(by.model('ctrl.newUser')).sendKeys('my-new-user');
101 | element(by.css('.form #save')).click();
102 |
103 | var succElement = element(by.binding('ctrl.success'));
104 | expect(succElement.isDisplayed()).toBe(true);
105 | expect(succElement.getText()).toBe('new user saved: my-new-user');
106 | });
107 | });
108 |
109 | describe('error', function(){
110 | it('can set a single mock inline', function(){
111 | mock([{
112 | request: {
113 | path: '/users',
114 | method: 'GET'
115 | },
116 | response: {
117 | status: 500,
118 | data: {
119 | error: 'oh no!'
120 | }
121 | }
122 | }]);
123 |
124 | get();
125 | var users = element.all(by.repeater('user in ctrl.users'));
126 | expect(users.count()).toBe(0);
127 | var errElement = element(by.binding('ctrl.error'));
128 | expect(errElement.isDisplayed()).toBe(true);
129 | expect(errElement.getText()).toBe('oh no!');
130 | });
131 |
132 | it('can set multiple mocks inline', function(){
133 | mock([
134 | {
135 | request: {
136 | path: '/users',
137 | method: 'GET'
138 | },
139 | response: {
140 | status: 500,
141 | data: {
142 | error: 'help!'
143 | }
144 | }
145 | },
146 | {
147 | request: {
148 | path: '/groups',
149 | method: 'GET'
150 | },
151 | response: {
152 | status: 200,
153 | data: [ { name: 'i did work' }]
154 | }
155 | },
156 | {
157 | request: {
158 | path: '/users/new',
159 | method: 'POST',
160 | data: {}
161 | },
162 | response: {
163 | status: 500,
164 | data: {
165 | error: 'post error!'
166 | }
167 | }
168 | }
169 | ]);
170 |
171 | get();
172 |
173 | var users = element.all(by.repeater('user in ctrl.users'));
174 | expect(users.count()).toBe(0);
175 |
176 | var groups = element.all(by.repeater('group in ctrl.groups'));
177 | expect(groups.count()).toBe(1);
178 |
179 | var errElement = element(by.binding('ctrl.error'));
180 | expect(errElement.isDisplayed()).toBe(true);
181 | expect(errElement.getText()).toBe('help!');
182 |
183 | element(by.css('.form #save')).click();
184 | expect(errElement.getText()).toBe('post error!');
185 | });
186 | });
187 | });
--------------------------------------------------------------------------------
/example/spec/jsonFormat.spec.js:
--------------------------------------------------------------------------------
1 | var mock = require('../../index'),
2 | get = require('./get');
3 |
4 | describe('from file', function(){
5 | afterEach(function(){
6 | mock.teardown();
7 | });
8 |
9 | it('loads a single mock from file with double quotes', function(){
10 | mock(['format']);
11 |
12 | get();
13 | element.all(by.repeater('group in ctrl.groups')).then(function(groups){
14 | expect(groups.length).toBe(1);
15 | expect(groups[0].getText()).toBe('format test');
16 | });
17 | });
18 | });
--------------------------------------------------------------------------------
/example/spec/params.spec.js:
--------------------------------------------------------------------------------
1 | var mock = require('../../index'),
2 | get = require('./get');
3 |
4 | describe('get by params', function(){
5 | beforeEach(function(){
6 | mock([
7 | {
8 | request: {
9 | path: '/users',
10 | method: 'GET'
11 | },
12 | response: {
13 | status: 200
14 | }
15 | },
16 | {
17 | request: {
18 | path: '/users',
19 | method: 'GET',
20 | params: {
21 | name: 'unknown'
22 | }
23 | },
24 | response: {
25 | status: 500,
26 | data: {
27 | error: 'no user found'
28 | }
29 | }
30 | },
31 | {
32 | request: {
33 | path: '/users',
34 | method: 'GET',
35 | params: {
36 | name: 'Charlie'
37 | }
38 | },
39 | response: {
40 | data: {
41 | name: 'Charlie'
42 | }
43 | }
44 | },
45 | {
46 | request: {
47 | path: '/users',
48 | method: 'GET',
49 | params: {
50 | name: 'Charlie',
51 | city: 'Houston'
52 | }
53 | },
54 | response: {
55 | data: {
56 | name: 'Charlie in Houston'
57 | }
58 | }
59 | }
60 | ]);
61 | });
62 |
63 | afterEach(function(){
64 | mock.teardown();
65 | });
66 |
67 | it('matches by params', function(){
68 | get();
69 |
70 | var searchButton = element(by.id('user-search-button'));
71 |
72 | element(by.id('user-query')).sendKeys('Charlie');
73 | searchButton.click();
74 |
75 | expect(element(by.id('user-data')).getText()).toContain('Charlie');
76 | expect(element(by.id('successMsg')).getText()).toBe('User found: Charlie');
77 |
78 | element(by.id('user-query-city')).sendKeys('Houston');
79 | searchButton.click();
80 |
81 | expect(element(by.id('user-data')).getText()).toContain('Charlie in Houston');
82 | expect(element(by.id('successMsg')).getText()).toBe('User found: Charlie');
83 |
84 | element(by.id('user-query')).clear().sendKeys('unknown');
85 | element(by.id('user-query-city')).clear();
86 | searchButton.click();
87 |
88 | var errElement = element(by.id('errorMsg'));
89 | expect(errElement.isDisplayed()).toBe(true);
90 | expect(errElement.getText()).toBe('no user found');
91 | });
92 | });
--------------------------------------------------------------------------------
/example/spec/plugin.spec.js:
--------------------------------------------------------------------------------
1 | var mock = require('../../index'), //substitute for require('protractor-http-mock')
2 | get = require('./get');
3 |
4 | describe('requests made', function(){
5 |
6 | afterEach(function(){
7 | mock.teardown();
8 | });
9 |
10 | beforeEach(function(){
11 | mock([
12 | {
13 | request: {
14 | path: '/users',
15 | method: 'GET',
16 | plugin: {
17 | check: true
18 | }
19 | },
20 | response: {
21 | data: 'from sample plugin match'
22 | }
23 | },
24 | {
25 | request: {
26 | path: '/users',
27 | method: 'GET',
28 | plugin: {
29 | check: false
30 | }
31 | },
32 | response: {
33 | data: 'my plugin should not match here!'
34 | }
35 | }
36 | ]);
37 |
38 | get();
39 | });
40 |
41 | it('can match through plugin', function(){
42 | element(by.id('plugin-request')).click();
43 | expect(element(by.id('plugin-result')).getText()).toBe('from sample plugin match');
44 | });
45 | });
--------------------------------------------------------------------------------
/example/spec/postData.spec.js:
--------------------------------------------------------------------------------
1 | var mock = require('../../index'), //substitute for require('protractor-http-mock')
2 | get = require('./get');
3 |
4 | describe('post data', function(){
5 | afterEach(function(){
6 | mock.teardown();
7 | });
8 |
9 | it('matches post data', function(){
10 | mock([
11 | {
12 | request: {
13 | path: 'users/new',
14 | method: 'POST'
15 | },
16 | response: {
17 | status: 400,
18 | data: {
19 | error: 'generic response'
20 | }
21 | }
22 | },
23 | {
24 | request: {
25 | path: 'users/new',
26 | method: 'POST',
27 | data: {
28 | name: 'Stewie'
29 | }
30 | },
31 | response: {
32 | status: 200
33 | }
34 | },
35 | {
36 | request: {
37 | path: 'users/new',
38 | method: 'POST',
39 | data: 'Brian'
40 | },
41 | response: {
42 | status: 500,
43 | data: {
44 | error: 'no dogs allowed!'
45 | }
46 | }
47 | }
48 | ]);
49 |
50 | get();
51 |
52 | var succElement = element(by.binding('ctrl.success'));
53 | var errElement = element(by.binding('ctrl.error'));
54 |
55 | element(by.css('.form #save')).click();
56 |
57 | expect(succElement.isDisplayed()).toBe(false);
58 | expect(errElement.isDisplayed()).toBe(true);
59 | expect(errElement.getText()).toBe('generic response');
60 |
61 | element(by.model('ctrl.newUser')).sendKeys('Stewie');
62 | element(by.css('.form #save')).click();
63 | expect(succElement.isDisplayed()).toBe(true);
64 | expect(errElement.isDisplayed()).toBe(false);
65 | expect(succElement.getText()).toBe('new user saved: Stewie');
66 |
67 | element(by.model('ctrl.newUser')).sendKeys('Brian');
68 | element(by.css('.form #save-name')).click();
69 | expect(succElement.isDisplayed()).toBe(false);
70 | expect(errElement.isDisplayed()).toBe(true);
71 | expect(errElement.getText()).toBe('no dogs allowed!');
72 | });
73 |
74 | it('matches complex post data', function(){
75 | mock([
76 | {
77 | request: {
78 | path: 'users/new',
79 | method: 'POST',
80 | data: {
81 | group: 'other group'
82 | }
83 | },
84 | response: {
85 | status: 500
86 | }
87 | },
88 | {
89 | request: {
90 | path: 'users/new',
91 | method: 'POST',
92 | data: {
93 | group: 'default',
94 | users: [
95 | { name: 'One' },
96 | { name: 'Two' }
97 | ]
98 | }
99 | },
100 | response: {
101 | status: 200
102 | }
103 | }
104 | ]);
105 |
106 | get();
107 |
108 | element(by.model('ctrl.newUser')).sendKeys('default');
109 | element(by.css('.form #add-default')).click();
110 |
111 | var succElement = element(by.binding('ctrl.success'));
112 | expect(succElement.isDisplayed()).toBe(true);
113 | expect(succElement.getText()).toBe('new user saved: default');
114 | });
115 | });
--------------------------------------------------------------------------------
/example/spec/queryString.spec.js:
--------------------------------------------------------------------------------
1 | var mock = require('../../index'),
2 | get = require('./get');
3 |
4 | describe('get query strings', function(){
5 | beforeEach(function(){
6 | mock([
7 | {
8 | request: {
9 | path: '/users',
10 | method: 'GET'
11 | },
12 | response: {
13 | status: 200,
14 | data: []
15 | }
16 | },
17 | {
18 | request: {
19 | path: '/users',
20 | method: 'GET',
21 | queryString: {
22 | name: 'Charlie & Friends',
23 | city: 'New York'
24 | }
25 | },
26 | response: {
27 | data: {
28 | name: 'Charlie'
29 | }
30 | }
31 | },
32 | {
33 | request: {
34 | path: '/users',
35 | method: 'GET',
36 | queryString: {
37 | name: 'Maria C',
38 | city: 'Houston'
39 | }
40 | },
41 | response: {
42 | data: {
43 | name: 'Maria in Houston'
44 | }
45 | }
46 | }
47 | ]);
48 | });
49 |
50 | afterEach(function(){
51 | mock.teardown();
52 | });
53 |
54 | it('matches by params', function(){
55 | get();
56 |
57 | var searchButton = element(by.id('user-search-by-query-button'));
58 |
59 | element(by.id('user-query')).sendKeys('Charlie & Friends');
60 | element(by.id('user-query-city')).sendKeys('New York');
61 | searchButton.click();
62 |
63 | expect(element(by.id('user-data')).getText()).toContain('Charlie');
64 | expect(element(by.id('successMsg')).getText()).toBe('User found: Charlie & Friends');
65 |
66 | element(by.id('user-query')).clear().sendKeys('Maria C');
67 | element(by.id('user-query-city')).clear().sendKeys('Houston');
68 | searchButton.click();
69 |
70 | expect(element(by.id('user-data')).getText()).toContain('Maria in Houston');
71 | expect(element(by.id('successMsg')).getText()).toBe('User found: Maria C');
72 | });
73 | });
--------------------------------------------------------------------------------
/example/spec/regex.spec.js:
--------------------------------------------------------------------------------
1 | var mock = require('../../index'),
2 | get = require('./get');
3 |
4 | describe('match path with regex', function(){
5 | beforeEach(function(){
6 | mock([
7 | {
8 | request: {
9 | path: '\\/users\\/[0-9]',
10 | method: 'GET',
11 | regex: true
12 | },
13 | response: {
14 | status: 200,
15 | data: {
16 | name: 'User with int id'
17 | }
18 | }
19 | },
20 | {
21 | request: {
22 | path: '\\/users\\/[^0-9]',
23 | method: 'GET',
24 | regex: true
25 | },
26 | response: {
27 | status: 400,
28 | data: {
29 | error: 'Not a number'
30 | }
31 | }
32 | }
33 | ]);
34 | });
35 |
36 | afterEach(function(){
37 | mock.teardown();
38 | });
39 |
40 | it('match on int id', function(){
41 | get();
42 |
43 | var searchButton = element(by.id('user-search-by-id-button'));
44 |
45 | element(by.id('user-query')).sendKeys('1');
46 | searchButton.click();
47 |
48 | expect(element(by.id('user-data')).getText()).toContain('User with int id');
49 |
50 | element(by.id('user-query')).clear().sendKeys('abc');
51 | searchButton.click();
52 |
53 | expect(element(by.id('errorMsg')).getText()).toBe('Not a number');
54 | });
55 | });
56 |
--------------------------------------------------------------------------------
/example/spec/requestsMade.spec.js:
--------------------------------------------------------------------------------
1 | var mock = require('../../index'), //substitute for require('protractor-http-mock')
2 | get = require('./get');
3 |
4 | describe('requests made', function(){
5 |
6 | afterEach(function(){
7 | mock.teardown();
8 | });
9 |
10 | beforeEach(function(){
11 | mock([
12 | {
13 | request: {
14 | path: '/users',
15 | method: 'GET'
16 | },
17 | response: {
18 | data: [
19 | {
20 | firstName: 'carlos',
21 | lastName: 'npm'
22 | },
23 | {
24 | firstName: 'angular',
25 | lastName: 'js'
26 | }
27 | ]
28 | }
29 | },
30 | {
31 | request: {
32 | path: 'users/new',
33 | method: 'POST',
34 | data: {
35 | name: 'my-new-user'
36 | }
37 | },
38 | response: {
39 | status: 200
40 | }
41 | }
42 | ]);
43 |
44 | get();
45 | });
46 |
47 | it('can evaluate requests made', function(){
48 | expect(mock.requestsMade()).toEqual([
49 | { url : '/default', method : 'GET' },
50 | { url : '/users', method : 'GET' }
51 | ]);
52 |
53 | element(by.model('ctrl.newUser')).sendKeys('my-new-user');
54 | element(by.css('.form #save')).click();
55 |
56 | expect(mock.requestsMade()).toEqual([
57 | { url : '/default', method : 'GET' },
58 | { url : '/users', method : 'GET' },
59 | { data : { name : 'my-new-user' }, url : '/users/new', method : 'POST' }
60 | ]);
61 | });
62 |
63 | it('can evaluate just the last request made', function(){
64 | mock.requestsMade().then(function(requests){
65 | expect(requests[1]).toEqual({ url : '/users', method : 'GET' })
66 | });
67 | });
68 |
69 | it('can clear requests', function(){
70 | mock.clearRequests();
71 | expect(mock.requestsMade()).toEqual([]);
72 |
73 | element(by.model('ctrl.newUser')).sendKeys('my-new-user');
74 | element(by.css('.form #save')).click();
75 |
76 | mock.clearRequests();
77 | expect(mock.requestsMade()).toEqual([]);
78 | });
79 | });
--------------------------------------------------------------------------------
/example/spec/runtime.spec.js:
--------------------------------------------------------------------------------
1 | var mock = require('../../index'), //substitute for require('protractor-http-mock')
2 | get = require('./get');
3 |
4 | describe('inline', function(){
5 |
6 | beforeEach(function(){
7 | mock([
8 | {
9 | request: {
10 | path: '/users',
11 | method: 'GET'
12 | },
13 | response: {
14 | data: [
15 | {
16 | firstName: 'default',
17 | lastName: 'value'
18 | }
19 | ]
20 | }
21 | },
22 | {
23 | request: {
24 | path: '/users',
25 | method: 'GET',
26 | params: {
27 | name: 'Charlie'
28 | }
29 | },
30 | response: {
31 | data: {
32 | name: 'Setup'
33 | }
34 | }
35 | }
36 | ]);
37 |
38 | get();
39 |
40 | mock.add([{
41 | request: {
42 | path: '/users',
43 | method: 'GET',
44 | params: {
45 | name: 'Charlie'
46 | }
47 | },
48 | response: {
49 | data: {
50 | name: 'Override'
51 | }
52 | }
53 | }]);
54 | });
55 |
56 | afterEach(function(){
57 | mock.teardown();
58 | });
59 |
60 | it('can add mocks', function(){
61 | element(by.id('user-query')).clear().sendKeys('Charlie');
62 | element(by.id('user-search-button')).click();
63 |
64 | expect(element(by.id('user-data')).getText()).toContain('Override');
65 | });
66 |
67 | it('can remove mocks', function(){
68 | mock.remove([{
69 | request: {
70 | path: '/users',
71 | method: 'GET',
72 | params: {
73 | name: 'Charlie'
74 | }
75 | },
76 | response: {
77 | data: {
78 | name: 'Override'
79 | }
80 | }
81 | }]);
82 |
83 | element(by.id('user-query')).clear().sendKeys('Charlie');
84 | element(by.id('user-search-button')).click();
85 |
86 | expect(element(by.id('user-data')).getText()).toContain('Setup');
87 | });
88 | });
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = require('./lib/initData');
--------------------------------------------------------------------------------
/lib/defaultConfig.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | rootDirectory: process.cwd(),
3 | protractorConfig: 'protractor-conf.js',
4 |
5 | mocks: {
6 | dir: 'mocks',
7 | default: []
8 | },
9 |
10 | plugins: {
11 | default: []
12 | }
13 | };
14 |
--------------------------------------------------------------------------------
/lib/httpMock.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var queryString = require('query-string');
4 |
5 | function mockTemplate() {
6 | var queryStringParse = '';
7 | var expectations = '';
8 | var plugins = '';
9 |
10 | var newModule = angular.module('httpMock', []);
11 |
12 | newModule.requests = [];
13 |
14 | newModule.config(['$provide', '$httpProvider', function($provide, $httpProvider){
15 |
16 | $provide.decorator('$http', ['$delegate', '$q', '$injector', function($http, $q, $injector) {
17 |
18 | var interceptors = [];
19 |
20 | angular.forEach($httpProvider.interceptors, function(interceptorExpression) {
21 | var interceptor;
22 | if (angular.isString(interceptorExpression)) {
23 | interceptor = $injector.get(interceptorExpression);
24 | } else {
25 | interceptor = $injector.invoke(interceptorExpression);
26 | }
27 | interceptors.push(interceptor);
28 | });
29 |
30 | function transformData(data, headers, status, fns) {
31 | if (typeof fns === 'function') {
32 | data = fns(data, headers, status);
33 | } else {
34 | for (var i = 0; i < fns.length; i++) {
35 | data = fns[i](data, headers, status);
36 | }
37 | }
38 |
39 | return data;
40 | }
41 |
42 | function transformRequest(requestConfig){
43 | if (requestConfig.transformRequest) {
44 | requestConfig.data = transformData(requestConfig.data,
45 | requestConfig.headers,
46 | undefined,
47 | requestConfig.transformRequest);
48 | }
49 |
50 | return requestConfig;
51 | }
52 |
53 | function getTransformedAndInterceptedRequestConfig(requestConfig) {
54 | for (var i = 0; i < interceptors.length; i++) {
55 | var interceptor = interceptors[i];
56 |
57 | if (interceptor.request) {
58 | $q.when(interceptor.request(requestConfig)).then(function(interceptedRequestConfig){
59 | requestConfig = interceptedRequestConfig;
60 | });
61 | }
62 | }
63 |
64 | requestConfig = transformRequest(requestConfig);
65 |
66 | return requestConfig;
67 | }
68 |
69 | function transformResponse(response) {
70 | if (response.config.transformResponse) {
71 | response.data = transformData(response.data,
72 | response.headers,
73 | response.status,
74 | response.config.transformResponse);
75 | }
76 |
77 | return response;
78 | }
79 |
80 | function statusIsSuccessful(status){
81 | return status >= 200 && status <= 299;
82 | }
83 |
84 | function getTransformedAndInterceptedResponse(response) {
85 | response = transformResponse(response);
86 |
87 | // Response interceptors are invoked in reverse order as per docs
88 | for (var i = interceptors.length - 1; i >= 0; i--) {
89 | var interceptor = interceptors[i];
90 |
91 | if (interceptor.response && statusIsSuccessful(response.status)) {
92 | $q.when(interceptor.response(response)).then(function(interceptedResponse){
93 | response = interceptedResponse;
94 | });
95 | }
96 |
97 | if (interceptor.responseError && !statusIsSuccessful(response.status)) {
98 | $q.reject(interceptor.responseError(response)).then(null, function(interceptedResponse){
99 | response = interceptedResponse;
100 | });
101 | }
102 | }
103 |
104 | return response;
105 | }
106 |
107 | function matchRegex(pattern, string){
108 | var regex = new RegExp(pattern);
109 | return regex.test(string);
110 | }
111 |
112 | function endsWith(url, path){
113 | var questionMarkIndex = url.indexOf('?');
114 |
115 | if(questionMarkIndex < 0){
116 | return url.indexOf(path, url.length - path.length) !== -1;
117 | }else{
118 | var noQueryStringUrl = url.substring(0, questionMarkIndex);
119 | return endsWith(noQueryStringUrl, path);
120 | }
121 | }
122 |
123 | function matchProperty(property, expectationRequest, config){
124 | return !expectationRequest[property] || angular.equals(expectationRequest[property], config[property]);
125 | }
126 |
127 | function matchParams(expectationRequest, config){
128 | return matchProperty('params', expectationRequest, config);
129 | }
130 |
131 | function matchData(expectationRequest, config){
132 | return matchProperty('data', expectationRequest, config);
133 | }
134 |
135 | function matchHeaders(expectationRequest, config){
136 | var simplifiedConfig = angular.copy(config);
137 |
138 | if(simplifiedConfig.headers){
139 | var headers = simplifiedConfig.headers;
140 |
141 | for(var prop in headers){
142 | if(headers.hasOwnProperty(prop) && typeof headers[prop] === 'function'){
143 | headers[prop] = headers[prop](config);
144 |
145 | if(headers[prop] === null){
146 | delete headers[prop];
147 | }
148 | }
149 | }
150 | }
151 |
152 | return matchProperty('headers', expectationRequest, simplifiedConfig);
153 | }
154 |
155 | function matchQueryString(expectationRequest, config){
156 | var match = true,
157 | url = config.url;
158 |
159 | var queryStringStartIndex = url.indexOf('?');
160 |
161 | if(expectationRequest.queryString && queryStringStartIndex > -1){
162 | var qsParams = queryStringParse(url.substring(queryStringStartIndex, url.length));
163 | match = angular.equals(expectationRequest.queryString, qsParams);
164 | }
165 |
166 | return match;
167 | }
168 |
169 | function matchMethod(expectationRequest, config){
170 | var configMethod = config.method ? config.method.toLowerCase() : 'get';
171 | return expectationRequest.method.toLowerCase() === configMethod;
172 | }
173 |
174 | function matchByPlugins(expectationRequest, config){
175 | var match = true;
176 |
177 | if(plugins.length > 0){
178 | match = plugins.reduce(function(value, plugin){
179 | return plugin(expectationRequest, config) && value;
180 | }, true);
181 | }
182 |
183 | return match;
184 | }
185 |
186 | function match(config, expectationRequest){
187 | return matchMethod(expectationRequest, config) &&
188 | (expectationRequest.regex ? matchRegex(expectationRequest.path, config.url)
189 | : endsWith(config.url, expectationRequest.path)) &&
190 | matchParams(expectationRequest, config) &&
191 | matchData(expectationRequest, config) &&
192 | matchQueryString(expectationRequest, config) &&
193 | matchHeaders(expectationRequest, config) &&
194 | matchByPlugins(expectationRequest, config);
195 | }
196 |
197 | function matchExpectation(config){
198 | var expectation;
199 |
200 | for(var i = 0; i < expectations.length; i++){
201 | if(match(config, expectations[i].request)){
202 | expectation = expectations[i];
203 | }
204 | }
205 |
206 | return expectation;
207 | }
208 |
209 | function wrapWithSuccessError(promise) {
210 | var myPromise = promise;
211 |
212 | myPromise.success = function(callback) {
213 | myPromise.then(function(response) {
214 | callback(response.data, response.status, response.headers, response.config);
215 | });
216 | return myPromise;
217 | };
218 |
219 | myPromise.error = function(callback) {
220 | myPromise.then(null, function(response) {
221 | callback(response.data, response.status, response.headers, response.config);
222 | });
223 | return myPromise;
224 | };
225 |
226 | return myPromise;
227 | }
228 |
229 | function createHeaderGetterFunction(responseHeaders){
230 | return function(headerName) {
231 | if (!headerName) {
232 | return responseHeaders;
233 | }
234 |
235 | return responseHeaders[headerName];
236 | };
237 | }
238 |
239 | function addToRequestHistory(config){
240 | var copy = angular.copy(config);
241 |
242 | // This is done to maintain backwards compatability
243 | // as well as providing a cleaner request history
244 | if(angular.equals(copy.headers, {})){
245 | delete copy.headers;
246 | }
247 |
248 | newModule.requests.push(copy);
249 | }
250 |
251 | function httpMock(config){
252 | var prom;
253 |
254 | config.headers = config.headers || {};
255 | var transformedConfig = getTransformedAndInterceptedRequestConfig(angular.copy(config));
256 |
257 | return wrapWithSuccessError($q.when(transformedConfig).then(function(resolvedConfig) {
258 | var expectation = matchExpectation(resolvedConfig);
259 |
260 | if(expectation){
261 | var deferred = $q.defer();
262 |
263 | addToRequestHistory(resolvedConfig);
264 |
265 | var delay = expectation.response.delay || 0;
266 |
267 | setTimeout(function(){
268 | var resolvedResponse;
269 |
270 | expectation.response = expectation.response || {};
271 |
272 | // Important to clone the response so that interceptors don't change the expectation response
273 | resolvedResponse = angular.copy(expectation.response);
274 |
275 | resolvedResponse.config = resolvedConfig;
276 |
277 | if(resolvedResponse.headers){
278 | resolvedResponse.headers = createHeaderGetterFunction(resolvedResponse.headers);
279 | }else{
280 | resolvedResponse.headers = function () {};
281 | }
282 |
283 | resolvedResponse.status = resolvedResponse.status || 200;
284 | resolvedResponse = getTransformedAndInterceptedResponse(resolvedResponse);
285 |
286 | $q.when(resolvedResponse).then(function(resolvedResponse) {
287 | if (statusIsSuccessful(resolvedResponse.status)) {
288 | deferred.resolve(resolvedResponse);
289 | } else {
290 | deferred.reject(resolvedResponse);
291 | }
292 | });
293 | }, delay);
294 |
295 | prom = deferred.promise;
296 | } else {
297 | prom = $http(config);
298 | }
299 |
300 | return prom;
301 | }));
302 | }
303 |
304 | httpMock.get = function(url, config){
305 | config = config || {};
306 | config.url = url;
307 | config.method = 'GET';
308 |
309 | return httpMock(config);
310 | };
311 |
312 | httpMock.delete = function(url, config){
313 | config = config || {};
314 | config.url = url;
315 | config.method = 'DELETE';
316 |
317 | return httpMock(config);
318 | };
319 |
320 | httpMock.head = function(url, config){
321 | config = config || {};
322 | config.url = url;
323 | config.method = 'HEAD';
324 |
325 | return httpMock(config);
326 | };
327 |
328 | httpMock.jsonp = function(url, config){
329 | config = config || {};
330 | config.url = url;
331 | config.method = 'JSONP';
332 |
333 | return httpMock(config);
334 | };
335 |
336 | httpMock.post = function(url, data, config){
337 | config = config || {};
338 | config.url = url;
339 | config.data = data;
340 | config.method = 'POST';
341 |
342 | return httpMock(config);
343 | };
344 |
345 | httpMock.put = function(url, data, config){
346 | config = config || {};
347 | config.url = url;
348 | config.data = data;
349 | config.method = 'PUT';
350 |
351 | return httpMock(config);
352 | };
353 |
354 | httpMock.patch = function(url, data, config){
355 | config = config || {};
356 | config.url = url;
357 | config.data = data;
358 | config.method = 'PATCH';
359 |
360 | return httpMock(config);
361 | };
362 |
363 | httpMock.defaults = $http.defaults;
364 |
365 | return httpMock;
366 | }]);}]);
367 |
368 | newModule.clearRequests = function(){
369 | newModule.requests = [];
370 | };
371 |
372 | newModule.addMocks = function(expectationsToAdd){
373 | expectations = expectations.concat(expectationsToAdd);
374 | };
375 |
376 | newModule.removeMocks = function(expectationsToRemove){
377 | expectations.forEach(function(expectation, index) {
378 | expectationsToRemove.forEach(function(expectationToRemove) {
379 | if (angular.equals(expectationToRemove, expectation)) {
380 | expectations.splice(index, 1);
381 | }
382 | });
383 | });
384 | };
385 |
386 | return newModule;
387 | }
388 |
389 | function getExpectationsString(expectations){
390 | var printExpectations = [];
391 |
392 | for(var i=0; i< expectations.length; i++){
393 | printExpectations.push(JSON.stringify(expectations[i]));
394 | }
395 |
396 | return printExpectations.toString();
397 | }
398 |
399 | function getPluginsString(plugins){
400 | if(plugins){
401 | var pluginStrings = [];
402 |
403 | plugins.forEach(function(plugin){
404 | pluginStrings.push(plugin.match.toString());
405 | });
406 |
407 | return pluginStrings.join();
408 | } else{
409 | return '';
410 | }
411 | }
412 |
413 | module.exports = function(expectations, plugins){
414 | var templateString = mockTemplate.toString();
415 | var template = templateString.substring(templateString.indexOf('{') + 1, templateString.lastIndexOf('}'));
416 | var pluginsString = getPluginsString(plugins);
417 |
418 | var newFunc =
419 | template
420 | .replace(/''/, '[' + getExpectationsString(expectations) + ']')
421 | .replace(/''/, queryString.parse.toString())
422 | .replace(/''/, '[' + pluginsString + ']');
423 |
424 | /*jslint evil: true */
425 | return new Function(newFunc);
426 | };
427 |
--------------------------------------------------------------------------------
/lib/initData.js:
--------------------------------------------------------------------------------
1 | /* globals browser */
2 | 'use strict';
3 |
4 | var httpMock = require('./httpMock'),
5 | path = require('path'),
6 | defaultConfig = require('./defaultConfig');
7 |
8 | function getConfig(){
9 | var config = defaultConfig;
10 |
11 | if(module.exports.config){
12 | config.rootDirectory = module.exports.config.rootDirectory || config.rootDirectory;
13 | config.protractorConfig = module.exports.config.protractorConfig || config.protractorConfig;
14 | }
15 |
16 | var protractorConfigFile = path.join(config.rootDirectory, config.protractorConfig);
17 | var protractorConfig = require(protractorConfigFile).config;
18 |
19 | config.mocks = protractorConfig.mocks || defaultConfig.mocks;
20 | // TODO: add validation check
21 | config.mocks.default = config.mocks.default || [];
22 |
23 | config.plugins = protractorConfig.httpMockPlugins || defaultConfig.plugins;
24 | // TODO: add validation check
25 | config.plugins.default = config.plugins.default || [];
26 |
27 | return config;
28 | }
29 |
30 | function readMockFile(mockDirectory, mock){
31 | return require(path.join(mockDirectory, mock));
32 | }
33 |
34 | function buildMocks(mocks, skipDefaults){
35 | var data = [],
36 | config = getConfig(),
37 | mockDirectory = path.join(config.rootDirectory, config.mocks.dir);
38 |
39 | mocks = mocks || [];
40 |
41 | if(!skipDefaults){
42 | mocks = config.mocks.default.concat(mocks);
43 | }
44 |
45 | for(var i = 0; i < mocks.length; i++){
46 | // TODO: add validation check
47 | var dataModule = typeof mocks[i] === 'string' ? readMockFile(mockDirectory, mocks[i]) : mocks[i];
48 |
49 | if(Array.isArray(dataModule)){
50 | data = data.concat(dataModule);
51 | }else{
52 | data.push(dataModule);
53 | }
54 |
55 | }
56 |
57 | return data;
58 | }
59 |
60 | function buildPlugins(plugins, skipDefaults){
61 | var data = [],
62 | config = getConfig();
63 |
64 | plugins = plugins || [];
65 |
66 | if(!skipDefaults){
67 | plugins = config.plugins.default.concat(plugins);
68 | }
69 |
70 | for(var i = 0; i < plugins.length; i++){
71 | // TODO: add validation check
72 | var plugin = typeof plugins[i] === 'string' ? require(plugins[i]) : plugins[i];
73 | data.push(plugin);
74 | }
75 |
76 | return data;
77 | }
78 |
79 | function getProtractorInstance(){
80 | return protractor.getInstance ? protractor.getInstance() : browser;
81 | }
82 |
83 | module.exports = function(mocks, plugins, skipDefaults){
84 | var builtMocks = buildMocks(mocks, skipDefaults),
85 | builtPlugins = buildPlugins(plugins);
86 |
87 | var ptor = getProtractorInstance();
88 | ptor.addMockModule('httpMock', httpMock(builtMocks, builtPlugins));
89 | };
90 |
91 | module.exports.teardown = function(){
92 | var ptor = getProtractorInstance();
93 | ptor.removeMockModule('httpMock');
94 | };
95 |
96 | module.exports.requestsMade = function() {
97 | return browser.executeAsyncScript(function () {
98 | var httpMock = angular.module('httpMock');
99 | var callback = arguments[arguments.length - 1];
100 | callback(httpMock.requests);
101 | });
102 | };
103 |
104 | module.exports.clearRequests = function(){
105 | return browser.executeAsyncScript(function () {
106 | angular.module('httpMock').clearRequests();
107 | var callback = arguments[arguments.length - 1];
108 | callback(true);
109 | });
110 | };
111 |
112 | module.exports.add = function(mocks){
113 | return browser.executeAsyncScript(function () {
114 | angular.module('httpMock').addMocks(arguments[0]);
115 | var callback = arguments[arguments.length - 1];
116 | callback(true);
117 | }, mocks);
118 | };
119 |
120 | module.exports.remove = function(mocks){
121 | return browser.executeAsyncScript(function () {
122 | angular.module('httpMock').removeMocks(arguments[0]);
123 | var callback = arguments[arguments.length - 1];
124 | callback(JSON.stringify(true));
125 | }, mocks);
126 | };
127 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "protractor-http-mock",
3 | "version": "0.10.0",
4 | "description": "Mock HTTP calls in your protractor specs.",
5 | "main": "index.js",
6 | "scripts": {
7 | "example": "node_modules/.bin/grunt example",
8 | "test": "node_modules/.bin/grunt verify",
9 | "client-test": "node_modules/.bin/grunt client-test",
10 | "webdriver-update": "node node_modules/grunt-protractor-runner/scripts/webdriver-manager-update"
11 | },
12 | "author": "Carlos Atencio",
13 | "license": "MIT",
14 | "devDependencies": {
15 | "grunt": "~1.0.1",
16 | "grunt-browserify": "^5.0.0",
17 | "grunt-contrib-connect": "^1.0.2",
18 | "grunt-contrib-jasmine": "^1.0.3",
19 | "grunt-contrib-jshint": "~1.1.0",
20 | "grunt-jasmine-nodejs": "^1.5.2",
21 | "grunt-protractor-runner": "^4.0.0",
22 | "phantomjs-prebuilt": "^2.1.3",
23 | "protractor-http-mock-sample-plugin": "0.0.1"
24 | },
25 | "repository": {
26 | "type": "git",
27 | "url": "https://github.com/atecarlos/protractor-http-mock"
28 | },
29 | "dependencies": {
30 | "query-string": "<=4.2.3"
31 | },
32 | "engines": {
33 | "node": ">=4.0.0"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/specs/httpMock.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var httpMock = require('../lib/httpMock');
4 |
5 | describe('http mock', function(){
6 | it('sets expectations', function(){
7 | var expectations = [
8 | {
9 | one: 'mock-one'
10 | },
11 | {
12 | two: 'mock-two'
13 | }
14 | ];
15 |
16 | var result = httpMock(expectations);
17 | expect(typeof result).toBe('function');
18 | expect(result.toString()).toContain('var expectations = [{"one":"mock-one"},{"two":"mock-two"}]');
19 | });
20 |
21 | it('sets query string function', function(){
22 | var expectations = [];
23 |
24 | var result = httpMock(expectations);
25 | expect(result.toString()).toContain('decodeURIComponent');
26 | });
27 | });
--------------------------------------------------------------------------------
/specs/initData.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var fs = require('fs');
4 | var path = require('path');
5 |
6 | var initDataModule = {
7 | path: '../lib/initData',
8 | require: function () {
9 | return require(this.path);
10 | },
11 | teardown: function () {
12 | delete require.cache[require.resolve(this.path)];
13 | }
14 | };
15 |
16 | var defaultConfig = {
17 | path: path.join(__dirname, '../protractor-conf.js'),
18 | setup: function () {
19 | fs.writeFileSync(this.path, 'exports.config = {}');
20 | },
21 | teardown: function () {
22 | fs.unlinkSync(this.path);
23 | }
24 | };
25 |
26 | var globalMocks = {
27 | setup: function () {
28 | global.protractor = {};
29 | global.browser = {
30 | addMockModule: function () {}
31 | };
32 | },
33 | teardown: function () {
34 | delete global.protractor;
35 | delete global.browser;
36 | }
37 | };
38 |
39 |
40 | describe('init data', function(){
41 | beforeEach(function () {
42 | this.initData = initDataModule.require();
43 | globalMocks.setup();
44 | defaultConfig.setup();
45 | });
46 | afterEach(function () {
47 | initDataModule.teardown();
48 | globalMocks.teardown();
49 | defaultConfig.teardown();
50 | });
51 | it('will not error when not providing config', function () {
52 | expect(this.initData).not.toThrow();
53 | });
54 | });
55 |
--------------------------------------------------------------------------------
/tests/convenienceMethods.test.js:
--------------------------------------------------------------------------------
1 | describe('convenience methods', function(){
2 | var module = window.__module, http;
3 |
4 | beforeAll(function(){
5 | http = window.__getHttp();
6 | });
7 |
8 | it('get', function(done){
9 | http.get('test-api.com/user').then(function(response){
10 | expect(response.data).toBe('pass');
11 | done();
12 | });
13 | });
14 |
15 | it('post', function(done){
16 | http.post('/user', {
17 | name: 'Carlos'
18 | }).then(function(response){
19 | expect(response.data).toBe('Carlos has been saved');
20 | done();
21 | });
22 | });
23 |
24 | it('head', function(done){
25 | http.head('/head').then(function(response){
26 | expect(response.data).toBe('head response');
27 | done();
28 | });
29 | });
30 |
31 | it('put', function(done){
32 | http.put('/put').then(function(response){
33 | expect(response.data).toBe('put response');
34 | done();
35 | });
36 | });
37 |
38 | it('delete', function(done){
39 | http.delete('/delete').then(function(response){
40 | expect(response.data).toBe('delete response');
41 | done();
42 | });
43 | });
44 |
45 | it('jsonp', function(done){
46 | http.jsonp('/jsonp').then(function(response){
47 | expect(response.data).toBe('jsonp response');
48 | done();
49 | });
50 | });
51 | });
--------------------------------------------------------------------------------
/tests/directCalls.test.js:
--------------------------------------------------------------------------------
1 | describe('direct calls', function(){
2 | var module = window.__module, http;
3 |
4 | beforeAll(function(){
5 | http = window.__getHttp();
6 | });
7 |
8 | describe('get', function(){
9 | it('captures a simple get call', function(done){
10 | http({
11 | method: 'GET',
12 | url: 'test-api.com/user'
13 | }).then(function(response){
14 | expect(response.data).toBe('pass');
15 | done();
16 | });
17 | });
18 |
19 | it('captures get with provided params', function(done){
20 | http({
21 | method: 'GET',
22 | url: '/user',
23 | params: {
24 | id: 1
25 | }
26 | }).then(function(response){
27 | expect(response.data.name).toBe('Carlos Github');
28 | done();
29 | });
30 | });
31 |
32 | it('treats params get with provided params', function(done){
33 | http({
34 | method: 'GET',
35 | url: '/user',
36 | params: {
37 | id: 2
38 | }
39 | }).then(function(response){
40 | expect(response.data).toBe('pass');
41 | done();
42 | });
43 | });
44 |
45 | it('treats requests as GET by default', function(done){
46 | http({
47 | url: '/user',
48 | params: {
49 | id: 2
50 | }
51 | }).then(function(response){
52 | expect(response.data).toBe('pass');
53 | done();
54 | });
55 | });
56 | });
57 |
58 | describe('post', function(){
59 | it('captures post calls', function(done){
60 | http({
61 | method: 'POST',
62 | url: '/user',
63 | data: {
64 | name: 'Carlos'
65 | }
66 | }).then(function(response){
67 | expect(response.data).toBe('Carlos has been saved');
68 | done();
69 | });
70 | });
71 |
72 | it('captures expected post errors', function(done){
73 | http({
74 | method: 'POST',
75 | url: '/user',
76 | data: {
77 | name: 'Other name'
78 | }
79 | }).catch(function(response){
80 | expect(response.status).toBe(500);
81 | expect(response.data.error).toBe('Cant save other users');
82 | done();
83 | });
84 | });
85 |
86 | it('treats data as optional field', function(done){
87 | http({
88 | method: 'POST',
89 | url: '/user',
90 | data: {
91 | some: 'thing'
92 | }
93 | }).then(function(response){
94 | expect(response.data).toBe('Generic match');
95 | done();
96 | });
97 | });
98 | });
99 | });
--------------------------------------------------------------------------------
/tests/externalUrl.test.js:
--------------------------------------------------------------------------------
1 | describe('external url', function(){
2 | var module = window.__module, http;
3 |
4 | beforeAll(function(){
5 | http = window.__getHttp();
6 | });
7 |
8 | it('should match against external URLs', function(done){
9 | http({
10 | method: 'GET',
11 | url: 'https://test-api.com/user'
12 | }).then(function(response){
13 | expect(response.data).toBe('pass');
14 | done();
15 | });
16 | });
17 | });
--------------------------------------------------------------------------------
/tests/headers.test.js:
--------------------------------------------------------------------------------
1 | describe('headers', function(){
2 | var module = window.__module, http;
3 |
4 | beforeAll(function(){
5 | http = window.__getHttp();
6 | });
7 |
8 | it('matches a request by headers', function(done){
9 | http({
10 | method: 'get',
11 | url: 'my-api.com/user',
12 | headers: {
13 | 'x-auth': 'pass',
14 | 'gzip-pro': 'yes'
15 | }
16 | }).then(function(response){
17 | expect(response.data).toBe('authentication passed');
18 | done();
19 | });
20 | });
21 |
22 | it('can respond with headers', function(done){
23 | http({
24 | url: 'my-api.com/with-headers',
25 | method: 'get'
26 | }).then(function(response){
27 | expect(response.headers('X-AUTH')).toBe('authenticated');
28 | expect(response.headers('ANGULAR_API')).toBe('ang=api');
29 | done();
30 | });
31 | });
32 |
33 | it('can respond with headers in convenience methods', function(done){
34 | http({
35 | url: 'my-api.com/with-headers',
36 | method: 'get'
37 | }).success(function(data, status, headers){
38 | expect(headers('X-AUTH')).toBe('authenticated');
39 | expect(headers('ANGULAR_API')).toBe('ang=api');
40 | done();
41 | });
42 | });
43 |
44 | it('ignores header properties when their function return value is null', function(done){
45 | http({
46 | method: 'get',
47 | url: 'my-api.com/user',
48 | headers: {
49 | 'x-auth': 'pass',
50 | 'gzip-pro': 'yes',
51 | 'ignore-me': function(){
52 | return null;
53 | }
54 | }
55 | }).then(function(response){
56 | expect(response.data).toBe('authentication passed');
57 | done();
58 | });
59 | });
60 |
61 | it('always returns a header', function(done){
62 | http({
63 | method: 'GET',
64 | url: 'test-api.com/user'
65 | }).then(function(response){
66 | expect(response.headers).toBeDefined();
67 | expect(response.data).toBe('pass');
68 | done();
69 | });
70 | });
71 |
72 | it('matches request by complex headers (include functions)', function(done){
73 | http({
74 | method: 'get',
75 | url: 'my-api.com/user',
76 | headers: {
77 | 'x-auth': 'pass',
78 | 'gzip-pro': function(config){
79 | if(config){
80 | return 'yes';
81 | }
82 | }
83 | }
84 | }).then(function(response){
85 | expect(response.data).toBe('authentication passed');
86 | done();
87 | });
88 | });
89 |
90 | it('response headers function returns all headers if no provided header name', function(done){
91 | var expectedHeaders = {
92 | 'X-AUTH': 'authenticated',
93 | 'ANGULAR_API': 'ang=api'
94 | };
95 |
96 | http({
97 | url: 'my-api.com/with-headers',
98 | method: 'get'
99 | }).then(function(response){
100 | expect(response.headers()).toEqual(expectedHeaders);
101 | expect(response.headers(null)).toEqual(expectedHeaders);
102 | expect(response.headers(undefined)).toEqual(expectedHeaders);
103 | done();
104 | });
105 | });
106 | });
--------------------------------------------------------------------------------
/tests/interceptors.test.js:
--------------------------------------------------------------------------------
1 | describe('interceptors', function(){
2 | var module = window.__module, http;
3 |
4 | beforeAll(function(){
5 | http = window.__getHttp();
6 | });
7 |
8 | module.config(['$provide', '$httpProvider', function($provide, $httpProvider){
9 |
10 | $provide.factory('testInterceptor', function($q) {
11 | return {
12 | request: function(config){
13 | if(config.url.match(/promise-request/)){
14 | return $q.when(config);
15 | }
16 |
17 | return config;
18 | },
19 |
20 | response: function(response){
21 | var responseReturn;
22 |
23 | if(response.config.url.match(/intercept/)){
24 | response.data.interceptedResponse = true;
25 | }
26 |
27 | return response;
28 | }
29 | }
30 | });
31 |
32 | $httpProvider.interceptors.push('testInterceptor');
33 |
34 | $httpProvider.interceptors.push(function(){
35 | return {
36 | request: function(config){
37 | if(config.url.match(/anonymous-intercept/)){
38 | config.interceptedAnonymousRequest = true;
39 | }
40 |
41 | return config;
42 | }
43 | }
44 | });
45 |
46 | $httpProvider.interceptors.push(function(){
47 | var count = 0;
48 | return {
49 | response: function(response){
50 | if(response.config.url.match(/stateful-intercept/)){
51 | count++;
52 | response.headers['stateful-anonymous-response-count'] = count;
53 | }
54 |
55 | return response;
56 | }
57 | }
58 | });
59 |
60 | $httpProvider.interceptors.push(['$q', function($q){
61 | return {
62 | request: function(config){
63 | if(config.url.match(/intercept/)){
64 | config.interceptedRequest = true;
65 | }
66 |
67 | return config;
68 | },
69 | response: function(response){
70 | if(response.config.url.match(/promise-response/)){
71 | response.data.interceptedPromiseResponse = true;
72 |
73 | return $q.when(response);
74 | }
75 |
76 | return response;
77 | },
78 | responseError: function(response){
79 | if(response.config.url.match(/response-error/)){
80 | response.data.interceptedResponseError = true;
81 | }
82 |
83 | return $q.reject(response);
84 | }
85 | }
86 | }]);
87 |
88 | $httpProvider.interceptors.push(['$q', function($q){
89 | return {
90 | request: function(config){
91 | if(config.url.match(/with-headers/)){
92 | config.headers['authorization'] = 'token';
93 | }
94 |
95 | return config;
96 | },
97 | response: function(response){
98 | if(response.config.url.match(/with-headers/)){
99 | response.headers['response-header'] = 'response-header';
100 |
101 | return $q.when(response);
102 | }
103 |
104 | return response;
105 | }
106 | }
107 | }]);
108 | }]);
109 |
110 | it('allows intercepts through service factory functions', function(done){
111 | http({
112 | method: 'GET',
113 | url: 'test-url.com/intercept'
114 | }).then(function(response){
115 | expect(response.data.interceptedResponse).toBe(true);
116 | expect(response.data.name).toBe('intercept test');
117 | done();
118 | });
119 | });
120 |
121 | it('allows for intercepts through anonymous factory', function(done){
122 | http({
123 | method: 'GET',
124 | url: 'test-url.com/anonymous-intercept'
125 | }).then(function(response){
126 | expect(response.data.name).toBe('anonymous intercept test');
127 | expect(response.data.interceptedResponse).toBeTruthy();
128 | done();
129 | });
130 | });
131 |
132 | it('allows for intercepts through stateful anonymous factory', function(done){
133 | http({
134 | method: 'GET',
135 | url: 'test-url.com/stateful-intercept'
136 | }).then(function(){
137 | return http({
138 | method: 'GET',
139 | url: 'test-url.com/stateful-intercept'
140 | });
141 | }).then(function(response){
142 | expect(response.headers['stateful-anonymous-response-count']).toBe(2);
143 | done();
144 | });
145 | });
146 |
147 | it('allows for intercepts that return a promise from a request', function(done){
148 | http({
149 | method: 'POST',
150 | url: 'test-url.com/promise-request'
151 | }).then(function(response){
152 | expect(response.data.name).toBe('promise intercept request test');
153 | done();
154 | });
155 | });
156 |
157 | it('allows for intercepts that return a promise from a response', function(done){
158 | http({
159 | method: 'POST',
160 | url: 'test-url.com/promise-response'
161 | }).then(function(response){
162 | expect(response.data.name).toBe('promise intercept response test');
163 | expect(response.data.interceptedPromiseResponse).toBeTruthy();
164 | done();
165 | });
166 | });
167 |
168 | it('allows for intercepts handling an error response', function(done){
169 | http({
170 | method: 'GET',
171 | url: 'test-url.com/response-error'
172 | }).catch(function(response){
173 | expect(response.data.interceptedResponseError).toBe(true);
174 | expect(response.data.name).toBe('intercept test');
175 | done();
176 | });
177 | });
178 |
179 | it('provides default empty object headers if none are set', function(done){
180 | http({
181 | method: 'GET',
182 | url: 'test-url.com/with-headers'
183 | }).then(function(response){
184 | expect(response.headers['response-header']).toBe('response-header');
185 | done();
186 | });
187 | });
188 | });
--------------------------------------------------------------------------------
/tests/plugin.test.js:
--------------------------------------------------------------------------------
1 | describe('plugins', function(){
2 | var module = window.__module, http;
3 |
4 | beforeAll(function(){
5 | http = window.__getHttp();
6 | });
7 |
8 | it('plugins can match', function(done){
9 | http({
10 | method: 'GET',
11 | url: '/plugin',
12 | plugin: {}
13 | }).success(function(data, status){
14 | expect(data).toBe('plugin match works!');
15 | expect(status).toBe(200);
16 | done();
17 | });
18 | });
19 | });
--------------------------------------------------------------------------------
/tests/promises.test.js:
--------------------------------------------------------------------------------
1 | describe('$http promises', function(){
2 | var module = window.__module, http;
3 |
4 | beforeAll(function(){
5 | http = window.__getHttp();
6 | });
7 |
8 | it('handles success callback', function(done){
9 | http({
10 | method: 'GET',
11 | url: 'test-api.com/user'
12 | }).success(function(data, status){
13 | expect(data).toBe('pass');
14 | expect(status).toBe(200);
15 | done();
16 | });
17 | });
18 |
19 | it('handles error callback', function(done){
20 | http.post('test-api.com/user', {
21 | name: 'Other name'
22 | }).error(function(data, status){
23 | expect(status).toBe(500);
24 | expect(data.error).toBe('Cant save other users');
25 | done();
26 | });
27 | });
28 | });
--------------------------------------------------------------------------------
/tests/queryString.test.js:
--------------------------------------------------------------------------------
1 | describe('query string', function(){
2 | var module = window.__module, http;
3 |
4 | beforeAll(function(){
5 | http = window.__getHttp();
6 | });
7 |
8 | it('matches by query string', function(done){
9 | http.get('github.com/user/search?id=1&city=ny%26ny').then(function(response){
10 | expect(response.data.name).toBe('Carlos QS');
11 | done();
12 | });
13 | });
14 |
15 | it('query string is optional in the mock config', function(done){
16 | http.get('github.com/user/search?id=2&city=another').then(function(response){
17 | expect(response.data.name).toBe('Whatever you search');
18 | done();
19 | });
20 | });
21 | });
--------------------------------------------------------------------------------
/tests/regex.test.js:
--------------------------------------------------------------------------------
1 | describe('regex match', function(){
2 | var http;
3 |
4 | beforeAll(function(){
5 | http = window.__getHttp();
6 | });
7 |
8 | it('matches any', function(done){
9 | http.get('/regex/d3ce5994-e662-4223-9968-9fc01694f08f').then(function(response){
10 | expect(response.data).toBe('regex any match');
11 | done();
12 | });
13 | });
14 |
15 | it('matches number', function(done){
16 | http.get('/regex/1').then(function(response){
17 | expect(response.data).toBe('regex number match');
18 | done();
19 | });
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/tests/requests.test.js:
--------------------------------------------------------------------------------
1 | describe('requests', function(){
2 | var module = window.__module, http;
3 |
4 | beforeAll(function(){
5 | http = window.__getHttp();
6 | });
7 |
8 | it('captures and clears requests', function(done){
9 | http.head('/head').then(function(){
10 | expect(module.requests.length).toBeGreaterThan(0);
11 |
12 | var found = false;
13 |
14 | module.requests.forEach(function(request){
15 | if(request.method === 'HEAD'){
16 | found = true;
17 | expect(request.url).toBe('/head');
18 | expect(request.headers).not.toBeDefined();
19 | }
20 | });
21 |
22 | expect(found).toBe(true);
23 |
24 | module.clearRequests();
25 | expect(module.requests.length).toBe(0);
26 | done();
27 | });
28 | });
29 |
30 | it('captures requests with headers', function(done){
31 | http({
32 | method: 'get',
33 | url: '/user',
34 | headers: {
35 | 'x-auth': 'pass',
36 | 'gzip-pro': 'yes'
37 | }
38 | }).then(function(){
39 | expect(module.requests.length).toBeGreaterThan(0);
40 |
41 | var found = false;
42 |
43 | module.requests.forEach(function(request){
44 | if(request.headers){
45 | found = true;
46 | expect(request.url).toBe('/user');
47 | expect(request.headers).toEqual({
48 | 'x-auth': 'pass',
49 | 'gzip-pro': 'yes'
50 | });
51 | }
52 | });
53 |
54 | expect(found).toBe(true);
55 | done();
56 | });
57 | });
58 | });
--------------------------------------------------------------------------------
/tests/runtime.test.js:
--------------------------------------------------------------------------------
1 | describe('runtime mocks', function(){
2 | var module = window.__module, http;
3 |
4 | beforeAll(function(){
5 | http = window.__getHttp();
6 | });
7 |
8 | var runtimeMocks = [
9 | {
10 | request: {
11 | method: 'GET',
12 | path: '/runtime'
13 | },
14 | response: {
15 | data: 'runtime'
16 | }
17 | },
18 | {
19 | request: {
20 | method: 'GET',
21 | path: '/runtime'
22 | },
23 | response: {
24 | data: 'override'
25 | }
26 | }
27 | ];
28 |
29 | afterEach(function(){
30 | module.removeMocks(runtimeMocks);
31 | });
32 |
33 | it('adds runtime mocks', function(done){
34 | module.addMocks(runtimeMocks);
35 |
36 | http({
37 | method: 'GET',
38 | url: '/runtime'
39 | }).then(function(response){
40 | expect(response.data).toBe('override');
41 | done();
42 | });
43 | });
44 |
45 | it('can remove runtime mocks', function(done){
46 | module.addMocks(runtimeMocks);
47 | module.removeMocks([{
48 | request: {
49 | method: 'GET',
50 | path: '/runtime'
51 | },
52 | response: {
53 | data: 'override'
54 | }
55 | }]);
56 |
57 | http({
58 | method: 'GET',
59 | url: '/runtime'
60 | }).then(function(response){
61 | expect(response.data).toBe('runtime');
62 | done();
63 | });
64 | });
65 | });
--------------------------------------------------------------------------------
/tests/setup.js:
--------------------------------------------------------------------------------
1 | (function(context){
2 | var expectations = [
3 | {
4 | request: {
5 | method: 'GET',
6 | path: '/user'
7 | },
8 | response: {
9 | data: 'pass'
10 | }
11 | },
12 | {
13 | request: {
14 | method: 'GET',
15 | path: '/user',
16 | headers: {
17 | 'x-auth': 'pass',
18 | 'gzip-pro': 'yes'
19 | }
20 | },
21 | response: {
22 | data: 'authentication passed'
23 | }
24 | },
25 | {
26 | request: {
27 | method: 'GET',
28 | path: '/user',
29 | params: {
30 | id: 1
31 | }
32 | },
33 | response: {
34 | data: {
35 | name: 'Carlos Github'
36 | }
37 | }
38 | },
39 | {
40 | request: {
41 | method: 'GET',
42 | path: '/user/search'
43 | },
44 | response: {
45 | data: {
46 | name: 'Whatever you search'
47 | }
48 | }
49 | },
50 | {
51 | request: {
52 | method: 'GET',
53 | path: '/user/search',
54 | queryString: {
55 | id: '1',
56 | city: 'ny&ny'
57 | }
58 | },
59 | response: {
60 | data: {
61 | name: 'Carlos QS'
62 | }
63 | }
64 | },
65 | {
66 | request: {
67 | method: 'GET',
68 | path: '/with-headers'
69 | },
70 | response: {
71 | data: 'read my headers',
72 | headers: {
73 | 'X-AUTH': 'authenticated',
74 | 'ANGULAR_API': 'ang=api'
75 | }
76 | }
77 | },
78 | {
79 | request: {
80 | method: 'post',
81 | path: '/user'
82 | },
83 | response: {
84 | status: 200,
85 | data: 'Generic match'
86 | }
87 | },
88 | {
89 | request: {
90 | method: 'post',
91 | path: '/user',
92 | data: {
93 | name: 'Carlos'
94 | }
95 | },
96 | response: {
97 | data: 'Carlos has been saved'
98 | }
99 | },
100 | {
101 | request: {
102 | method: 'post',
103 | path: '/user',
104 | data: {
105 | name: 'Other name'
106 | }
107 | },
108 | response: {
109 | status: 500,
110 | data: {
111 | error: 'Cant save other users'
112 | }
113 | }
114 | },
115 | {
116 | request: {
117 | path: '/head',
118 | method: 'head'
119 | },
120 | response: {
121 | data: 'head response'
122 | }
123 | },
124 | {
125 | request: {
126 | path: '/delete',
127 | method: 'delete'
128 | },
129 | response: {
130 | data: 'delete response'
131 | }
132 | },
133 | {
134 | request: {
135 | path: '/put',
136 | method: 'put'
137 | },
138 | response: {
139 | data: 'put response'
140 | }
141 | },
142 | {
143 | request: {
144 | path: '/patch',
145 | method: 'patch'
146 | },
147 | response: {
148 | data: 'patch response'
149 | }
150 | },
151 | {
152 | request: {
153 | path: '/jsonp',
154 | method: 'jsonp'
155 | },
156 | response: {
157 | data: 'jsonp response'
158 | }
159 | },
160 | {
161 | request: {
162 | path: '/intercept',
163 | interceptedRequest: true,
164 | method: 'get'
165 | },
166 | response: {
167 | data: {
168 | name: 'intercept test'
169 | }
170 | }
171 | },
172 | {
173 | request: {
174 | path: '/response-error',
175 | interceptedRequest: true,
176 | method: 'get'
177 | },
178 | response: {
179 | data: {
180 | name: 'intercept test'
181 | },
182 | status: 400
183 | }
184 | },
185 | {
186 | request: {
187 | path: '/anonymous-intercept',
188 | interceptedAnonymousRequest: true,
189 | method: 'get'
190 | },
191 | response: {
192 | data: {
193 | name: 'anonymous intercept test'
194 | }
195 | }
196 | },
197 | {
198 | request: {
199 | path: '/stateful-intercept',
200 | method: 'get'
201 | },
202 | response: {
203 | data: {
204 | name: 'stateful intercept test'
205 | }
206 | }
207 | },
208 | {
209 | request: {
210 | path: '/promise-request',
211 | method: 'POST'
212 | },
213 | response: {
214 | data: {
215 | name: 'promise intercept request test'
216 | }
217 | }
218 | },
219 | {
220 | request: {
221 | path: '/promise-response',
222 | method: 'post'
223 | },
224 | response: {
225 | data: {
226 | name: 'promise intercept response test'
227 | }
228 | }
229 | },
230 | {
231 | request: {
232 | path: '/transform-request',
233 | method: 'post',
234 | data: {
235 | name: 'test',
236 | city: 'test city'
237 | }
238 | },
239 | response: {
240 | data: {
241 | name: 'multiple transforms request test'
242 | }
243 | }
244 | },
245 | {
246 | request: {
247 | path: '/transform-request',
248 | method: 'post',
249 | data: {
250 | name: 'test'
251 | }
252 | },
253 | response: {
254 | data: {
255 | name: 'transform request test'
256 | }
257 | }
258 | },
259 | {
260 | request: {
261 | path: '/transform-response',
262 | method: 'get'
263 | },
264 | response: {
265 | data: {
266 | name: 'test'
267 | }
268 | }
269 | },
270 | {
271 | request: {
272 | path: '/plugin',
273 | method: 'get',
274 | plugin: {
275 | check: true
276 | }
277 | },
278 | response: {
279 | data: 'plugin match works!'
280 | }
281 | },
282 | {
283 | request: {
284 | path: 'regex\\/.*',
285 | regex: true,
286 | method: 'get',
287 | },
288 | response: {
289 | data: 'regex any match'
290 | }
291 | },
292 | {
293 | request: {
294 | path: '\\/regex\\/[0-9]',
295 | regex: true,
296 | method: 'get',
297 | },
298 | response: {
299 | data: 'regex number match'
300 | }
301 | }
302 | ];
303 |
304 | var plugins = [
305 | {
306 | match: function(mockRequest, requestConfig){
307 | var match = true;
308 |
309 | if(requestConfig.plugin && mockRequest.plugin){
310 | return mockRequest.plugin.check;
311 | }
312 |
313 | return match;
314 | }
315 | }
316 | ];
317 |
318 | context.httpMock(expectations, plugins)();
319 |
320 | context.__module = angular.module('httpMock')
321 | .config(['$qProvider', function ($qProvider) {
322 | $qProvider.errorOnUnhandledRejections(false);
323 | }])
324 | .run(function($http){
325 | context.__getHttp = function(){
326 | return $http;
327 | };
328 | });
329 | })(window);
--------------------------------------------------------------------------------
/tests/template.tmpl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Jasmine Spec Runner
6 |
7 | <% css.forEach(function(style){ %>
8 |
9 | <% }) %>
10 |
11 |
12 |
13 | <% with (scripts) { %>
14 | <% [].concat(polyfills, jasmine, boot, vendor, src, helpers, specs,reporters).forEach(function(script){ %>
15 |
16 | <% }) %>
17 | <% }; %>
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/tests/transforms.test.js:
--------------------------------------------------------------------------------
1 | describe('transforms', function(){
2 | var module = window.__module, http;
3 |
4 | beforeAll(function(){
5 | http = window.__getHttp();
6 | });
7 |
8 | it('allows multiple transform requests', function(done){
9 | http.post('test-url.com/transform-request', {
10 | name: 'transform test'
11 | }, {
12 | transformRequest: [function(data){
13 | data.city = 'test city';
14 | return data;
15 | }, function(data){
16 | data.name = 'test';
17 | return data;
18 | }]
19 | }).then(function(response){
20 | expect(response.data.name).toBe('multiple transforms request test');
21 | done();
22 | });
23 | });
24 |
25 | it('allows a single transform requests', function(done){
26 | http.post('test-url.com/transform-request', {
27 | name: 'transform test'
28 | }, {
29 | transformRequest: function(data){
30 | data.name = 'test';
31 | return data;
32 | }
33 | }).then(function(response){
34 | expect(response.data.name).toBe('transform request test');
35 | done();
36 | });
37 | });
38 |
39 | it('allows multiple transform responses', function(done){
40 | http({
41 | method: 'GET',
42 | url: 'test-url.com/transform-response',
43 | transformResponse: [function (data){
44 | data.name = 'transform response test';
45 | return data;
46 | }, function(data){
47 | data.city = 'test response city';
48 | return data;
49 | }]
50 | }).then(function(response){
51 | expect(response.data.name).toBe('transform response test');
52 | expect(response.data.city).toBe('test response city');
53 | done();
54 | });
55 | });
56 |
57 | it('allows a single transform responses', function(done){
58 | http({
59 | method: 'GET',
60 | url: 'test-url.com/transform-response',
61 | transformResponse: function (data){
62 | data.name = 'transform response test';
63 | return data;
64 | }
65 | }).then(function(response){
66 | expect(response.data.name).toBe('transform response test');
67 | done();
68 | });
69 | });
70 | });
--------------------------------------------------------------------------------