praxis 0.13.0 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/.travis.yml +15 -2
  4. data/CHANGELOG.md +54 -1
  5. data/bin/praxis +49 -2
  6. data/lib/api_browser/Gruntfile.js +247 -90
  7. data/lib/api_browser/app/bower_components/angular-mocks/.bower.json +19 -0
  8. data/lib/api_browser/app/bower_components/angular-mocks/README.md +57 -0
  9. data/lib/api_browser/app/bower_components/angular-mocks/angular-mocks.js +2193 -0
  10. data/lib/api_browser/app/bower_components/angular-mocks/bower.json +9 -0
  11. data/lib/api_browser/app/bower_components/angular-mocks/package.json +27 -0
  12. data/lib/api_browser/app/bower_components/angular/.bower.json +6 -5
  13. data/lib/api_browser/app/bower_components/angular/README.md +23 -4
  14. data/lib/api_browser/app/bower_components/angular/angular-csp.css +6 -0
  15. data/lib/api_browser/app/bower_components/angular/angular.js +2287 -1597
  16. data/lib/api_browser/app/bower_components/angular/angular.min.js +212 -205
  17. data/lib/api_browser/app/bower_components/angular/angular.min.js.gzip +0 -0
  18. data/lib/api_browser/app/bower_components/angular/angular.min.js.map +3 -3
  19. data/lib/api_browser/app/bower_components/angular/bower.json +2 -1
  20. data/lib/api_browser/app/bower_components/angular/package.json +25 -0
  21. data/lib/api_browser/app/bower_components/showdown/.bower.json +39 -0
  22. data/lib/api_browser/app/bower_components/showdown/.jshintignore +2 -0
  23. data/lib/api_browser/app/bower_components/showdown/.travis.yml +8 -0
  24. data/lib/api_browser/app/bower_components/showdown/Gruntfile.js +100 -0
  25. data/lib/api_browser/app/bower_components/showdown/README.md +317 -0
  26. data/lib/api_browser/app/bower_components/showdown/bower.json +26 -0
  27. data/lib/api_browser/app/bower_components/showdown/compressed/Showdown.js +1606 -0
  28. data/lib/api_browser/app/bower_components/showdown/compressed/Showdown.js.map +1 -0
  29. data/lib/api_browser/app/bower_components/showdown/compressed/Showdown.min.js +2 -0
  30. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/github.min.js +2 -0
  31. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/github.min.js.map +1 -0
  32. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/prettify.min.js +2 -0
  33. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/prettify.min.js.map +1 -0
  34. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/table.min.js +2 -0
  35. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/table.min.js.map +1 -0
  36. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/twitter.min.js +2 -0
  37. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/twitter.min.js.map +1 -0
  38. data/lib/api_browser/app/bower_components/showdown/license.txt +34 -0
  39. data/lib/api_browser/app/bower_components/showdown/package.json +47 -0
  40. data/lib/api_browser/app/bower_components/showdown/src/extensions/github.js +25 -0
  41. data/lib/api_browser/app/bower_components/showdown/src/extensions/prettify.js +29 -0
  42. data/lib/api_browser/app/bower_components/showdown/src/extensions/table.js +106 -0
  43. data/lib/api_browser/app/bower_components/showdown/src/extensions/twitter.js +42 -0
  44. data/lib/api_browser/app/bower_components/showdown/src/ng-showdown.js +150 -0
  45. data/lib/api_browser/app/bower_components/showdown/src/showdown.js +1454 -0
  46. data/lib/api_browser/app/index.html +6 -4
  47. data/lib/api_browser/app/js/app.js +1 -2
  48. data/lib/api_browser/app/js/controllers/action.js +4 -4
  49. data/lib/api_browser/app/js/controllers/controller.js +1 -1
  50. data/lib/api_browser/app/js/controllers/menu.js +5 -3
  51. data/lib/api_browser/app/js/controllers/type.js +5 -5
  52. data/lib/api_browser/app/js/directives/attribute_description.js +5 -5
  53. data/lib/api_browser/app/js/directives/attribute_table.js +1 -1
  54. data/lib/api_browser/app/js/directives/attribute_table_row.js +2 -2
  55. data/lib/api_browser/app/js/directives/no_container.js +1 -1
  56. data/lib/api_browser/app/js/directives/request_body.js +5 -5
  57. data/lib/api_browser/app/js/directives/request_headers.js +3 -6
  58. data/lib/api_browser/app/js/directives/request_parameters.js +3 -6
  59. data/lib/api_browser/app/js/directives/type_label.js +4 -5
  60. data/lib/api_browser/app/js/factories/Documentation.js +4 -4
  61. data/lib/api_browser/app/js/factories/PayloadTemplates.js +2 -2
  62. data/lib/api_browser/app/js/factories/TypeTemplates.js +3 -3
  63. data/lib/api_browser/app/js/filters/markdown.js +6 -0
  64. data/lib/api_browser/app/js/filters/resource_name.js +2 -2
  65. data/lib/api_browser/app/sass/modules/_header.scss +2 -7
  66. data/lib/api_browser/app/sass/{main.scss → praxis.scss} +0 -0
  67. data/lib/api_browser/app/sass/variables/_bootstrap-variables.scss +370 -367
  68. data/lib/api_browser/app/views/action.html +2 -2
  69. data/lib/api_browser/app/views/controller.html +2 -2
  70. data/lib/api_browser/app/views/directives/attribute_description.html +1 -1
  71. data/lib/api_browser/app/views/layout.html +2 -11
  72. data/lib/api_browser/app/views/navbar.html +9 -0
  73. data/lib/api_browser/app/views/resource/_actions.html +1 -1
  74. data/lib/api_browser/app/views/type.html +2 -2
  75. data/lib/api_browser/app/views/type/_details.html +2 -1
  76. data/lib/api_browser/bower.json +5 -0
  77. data/lib/api_browser/package.json +18 -7
  78. data/lib/praxis.rb +8 -3
  79. data/lib/praxis/action_definition.rb +28 -6
  80. data/lib/praxis/api_definition.rb +30 -2
  81. data/lib/praxis/api_general_info.rb +36 -0
  82. data/lib/praxis/bootloader.rb +1 -0
  83. data/lib/praxis/collection.rb +34 -0
  84. data/lib/praxis/controller.rb +7 -0
  85. data/lib/praxis/dispatcher.rb +3 -0
  86. data/lib/praxis/links.rb +2 -8
  87. data/lib/praxis/media_type.rb +6 -24
  88. data/lib/praxis/media_type_collection.rb +6 -2
  89. data/lib/praxis/plugin_concern.rb +2 -1
  90. data/lib/praxis/request.rb +24 -15
  91. data/lib/praxis/request_stages/request_stage.rb +19 -4
  92. data/lib/praxis/request_stages/validate_params_and_headers.rb +1 -1
  93. data/lib/praxis/request_stages/validate_payload.rb +1 -1
  94. data/lib/praxis/resource_definition.rb +45 -10
  95. data/lib/praxis/response_definition.rb +46 -27
  96. data/lib/praxis/restful_doc_generator.rb +94 -7
  97. data/lib/praxis/simple_media_type.rb +2 -9
  98. data/lib/praxis/stage.rb +1 -4
  99. data/lib/praxis/tasks/api_docs.rb +51 -19
  100. data/lib/praxis/tasks/routes.rb +19 -15
  101. data/lib/praxis/types/media_type_common.rb +31 -0
  102. data/lib/praxis/types/multipart.rb +4 -4
  103. data/lib/praxis/version.rb +1 -1
  104. data/praxis.gemspec +2 -2
  105. data/spec/api_browser/factories/documentation_spec.js +50 -0
  106. data/spec/api_browser/filters/attribute_name_spec.js +23 -0
  107. data/spec/functional_spec.rb +62 -10
  108. data/spec/praxis/action_definition_spec.rb +12 -4
  109. data/spec/praxis/api_definition_spec.rb +159 -0
  110. data/spec/praxis/api_general_info_spec.rb +36 -0
  111. data/spec/praxis/bootloader_spec.rb +10 -1
  112. data/spec/praxis/media_type_collection_spec.rb +46 -53
  113. data/spec/praxis/media_type_spec.rb +6 -6
  114. data/spec/praxis/request_stage_spec.rb +7 -2
  115. data/spec/praxis/request_stages_validate_spec.rb +12 -7
  116. data/spec/praxis/resource_definition_spec.rb +62 -0
  117. data/spec/praxis/response_definition_spec.rb +26 -16
  118. data/spec/praxis/stage_spec.rb +4 -8
  119. data/spec/praxis/types/collection_spec.rb +144 -0
  120. data/spec/spec_app/app/controllers/instances.rb +8 -2
  121. data/spec/spec_app/design/api.rb +11 -0
  122. data/spec/spec_app/design/media_types/instance.rb +12 -0
  123. data/spec/spec_app/design/media_types/volume.rb +9 -2
  124. data/spec/spec_app/design/media_types/volume_snapshot.rb +9 -6
  125. data/spec/spec_app/design/resources/instances.rb +25 -10
  126. data/spec/support/spec_media_types.rb +1 -1
  127. data/spec/support/spec_resource_definitions.rb +2 -0
  128. data/tasks/thor/app.rb +15 -10
  129. data/tasks/thor/example.rb +115 -115
  130. data/tasks/thor/templates/generator/empty_app/.gitignore +2 -0
  131. data/tasks/thor/templates/generator/empty_app/docs/app.js +1 -0
  132. data/tasks/thor/templates/generator/empty_app/docs/styles.scss +3 -0
  133. metadata +50 -9
  134. data/lib/api_browser/app/css/main.css +0 -4511
  135. data/lib/praxis/types/collection.rb +0 -17
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "angular-mocks",
3
+ "version": "1.2.28",
4
+ "main": "./angular-mocks.js",
5
+ "ignore": [],
6
+ "dependencies": {
7
+ "angular": "1.2.28"
8
+ },
9
+ "homepage": "https://github.com/angular/bower-angular-mocks",
10
+ "_release": "1.2.28",
11
+ "_resolution": {
12
+ "type": "version",
13
+ "tag": "v1.2.28",
14
+ "commit": "6bb9b6fd3fdb2eba0f64dc615fa55c5f0050af75"
15
+ },
16
+ "_source": "git://github.com/angular/bower-angular-mocks.git",
17
+ "_target": "~1.2.6",
18
+ "_originalSource": "angular-mocks"
19
+ }
@@ -0,0 +1,57 @@
1
+ # packaged angular-mocks
2
+
3
+ This repo is for distribution on `npm` and `bower`. The source for this module is in the
4
+ [main AngularJS repo](https://github.com/angular/angular.js/tree/master/src/ngMock).
5
+ Please file issues and pull requests against that repo.
6
+
7
+ ## Install
8
+
9
+ You can install this package either with `npm` or with `bower`.
10
+
11
+ ### npm
12
+
13
+ ```shell
14
+ npm install angular-mocks
15
+ ```
16
+
17
+ The mocks are then available at `node_modules/angular-mocks/angular-mocks.js`.
18
+
19
+ Note that this package is not in CommonJS format, so doing `require('angular-mocks')` will
20
+ return `undefined`.
21
+
22
+ ### bower
23
+
24
+ ```shell
25
+ bower install angular-mocks
26
+ ```
27
+
28
+ The mocks are then available at `bower_components/angular-mocks/angular-mocks.js`.
29
+
30
+ ## Documentation
31
+
32
+ Documentation is available on the
33
+ [AngularJS docs site](https://docs.angularjs.org/guide/unit-testing).
34
+
35
+ ## License
36
+
37
+ The MIT License
38
+
39
+ Copyright (c) 2010-2012 Google, Inc. http://angularjs.org
40
+
41
+ Permission is hereby granted, free of charge, to any person obtaining a copy
42
+ of this software and associated documentation files (the "Software"), to deal
43
+ in the Software without restriction, including without limitation the rights
44
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
45
+ copies of the Software, and to permit persons to whom the Software is
46
+ furnished to do so, subject to the following conditions:
47
+
48
+ The above copyright notice and this permission notice shall be included in
49
+ all copies or substantial portions of the Software.
50
+
51
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
52
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
53
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
54
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
55
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
56
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
57
+ THE SOFTWARE.
@@ -0,0 +1,2193 @@
1
+ /**
2
+ * @license AngularJS v1.2.28
3
+ * (c) 2010-2014 Google, Inc. http://angularjs.org
4
+ * License: MIT
5
+ */
6
+ (function(window, angular, undefined) {
7
+
8
+ 'use strict';
9
+
10
+ /**
11
+ * @ngdoc object
12
+ * @name angular.mock
13
+ * @description
14
+ *
15
+ * Namespace from 'angular-mocks.js' which contains testing related code.
16
+ */
17
+ angular.mock = {};
18
+
19
+ /**
20
+ * ! This is a private undocumented service !
21
+ *
22
+ * @name $browser
23
+ *
24
+ * @description
25
+ * This service is a mock implementation of {@link ng.$browser}. It provides fake
26
+ * implementation for commonly used browser apis that are hard to test, e.g. setTimeout, xhr,
27
+ * cookies, etc...
28
+ *
29
+ * The api of this service is the same as that of the real {@link ng.$browser $browser}, except
30
+ * that there are several helper methods available which can be used in tests.
31
+ */
32
+ angular.mock.$BrowserProvider = function() {
33
+ this.$get = function() {
34
+ return new angular.mock.$Browser();
35
+ };
36
+ };
37
+
38
+ angular.mock.$Browser = function() {
39
+ var self = this;
40
+
41
+ this.isMock = true;
42
+ self.$$url = "http://server/";
43
+ self.$$lastUrl = self.$$url; // used by url polling fn
44
+ self.pollFns = [];
45
+
46
+ // TODO(vojta): remove this temporary api
47
+ self.$$completeOutstandingRequest = angular.noop;
48
+ self.$$incOutstandingRequestCount = angular.noop;
49
+
50
+
51
+ // register url polling fn
52
+
53
+ self.onUrlChange = function(listener) {
54
+ self.pollFns.push(
55
+ function() {
56
+ if (self.$$lastUrl != self.$$url) {
57
+ self.$$lastUrl = self.$$url;
58
+ listener(self.$$url);
59
+ }
60
+ }
61
+ );
62
+
63
+ return listener;
64
+ };
65
+
66
+ self.$$checkUrlChange = angular.noop;
67
+
68
+ self.cookieHash = {};
69
+ self.lastCookieHash = {};
70
+ self.deferredFns = [];
71
+ self.deferredNextId = 0;
72
+
73
+ self.defer = function(fn, delay) {
74
+ delay = delay || 0;
75
+ self.deferredFns.push({time:(self.defer.now + delay), fn:fn, id: self.deferredNextId});
76
+ self.deferredFns.sort(function(a,b){ return a.time - b.time;});
77
+ return self.deferredNextId++;
78
+ };
79
+
80
+
81
+ /**
82
+ * @name $browser#defer.now
83
+ *
84
+ * @description
85
+ * Current milliseconds mock time.
86
+ */
87
+ self.defer.now = 0;
88
+
89
+
90
+ self.defer.cancel = function(deferId) {
91
+ var fnIndex;
92
+
93
+ angular.forEach(self.deferredFns, function(fn, index) {
94
+ if (fn.id === deferId) fnIndex = index;
95
+ });
96
+
97
+ if (fnIndex !== undefined) {
98
+ self.deferredFns.splice(fnIndex, 1);
99
+ return true;
100
+ }
101
+
102
+ return false;
103
+ };
104
+
105
+
106
+ /**
107
+ * @name $browser#defer.flush
108
+ *
109
+ * @description
110
+ * Flushes all pending requests and executes the defer callbacks.
111
+ *
112
+ * @param {number=} number of milliseconds to flush. See {@link #defer.now}
113
+ */
114
+ self.defer.flush = function(delay) {
115
+ if (angular.isDefined(delay)) {
116
+ self.defer.now += delay;
117
+ } else {
118
+ if (self.deferredFns.length) {
119
+ self.defer.now = self.deferredFns[self.deferredFns.length-1].time;
120
+ } else {
121
+ throw new Error('No deferred tasks to be flushed');
122
+ }
123
+ }
124
+
125
+ while (self.deferredFns.length && self.deferredFns[0].time <= self.defer.now) {
126
+ self.deferredFns.shift().fn();
127
+ }
128
+ };
129
+
130
+ self.$$baseHref = '';
131
+ self.baseHref = function() {
132
+ return this.$$baseHref;
133
+ };
134
+ };
135
+ angular.mock.$Browser.prototype = {
136
+
137
+ /**
138
+ * @name $browser#poll
139
+ *
140
+ * @description
141
+ * run all fns in pollFns
142
+ */
143
+ poll: function poll() {
144
+ angular.forEach(this.pollFns, function(pollFn){
145
+ pollFn();
146
+ });
147
+ },
148
+
149
+ addPollFn: function(pollFn) {
150
+ this.pollFns.push(pollFn);
151
+ return pollFn;
152
+ },
153
+
154
+ url: function(url, replace) {
155
+ if (url) {
156
+ this.$$url = url;
157
+ return this;
158
+ }
159
+
160
+ return this.$$url;
161
+ },
162
+
163
+ cookies: function(name, value) {
164
+ if (name) {
165
+ if (angular.isUndefined(value)) {
166
+ delete this.cookieHash[name];
167
+ } else {
168
+ if (angular.isString(value) && //strings only
169
+ value.length <= 4096) { //strict cookie storage limits
170
+ this.cookieHash[name] = value;
171
+ }
172
+ }
173
+ } else {
174
+ if (!angular.equals(this.cookieHash, this.lastCookieHash)) {
175
+ this.lastCookieHash = angular.copy(this.cookieHash);
176
+ this.cookieHash = angular.copy(this.cookieHash);
177
+ }
178
+ return this.cookieHash;
179
+ }
180
+ },
181
+
182
+ notifyWhenNoOutstandingRequests: function(fn) {
183
+ fn();
184
+ }
185
+ };
186
+
187
+
188
+ /**
189
+ * @ngdoc provider
190
+ * @name $exceptionHandlerProvider
191
+ *
192
+ * @description
193
+ * Configures the mock implementation of {@link ng.$exceptionHandler} to rethrow or to log errors
194
+ * passed into the `$exceptionHandler`.
195
+ */
196
+
197
+ /**
198
+ * @ngdoc service
199
+ * @name $exceptionHandler
200
+ *
201
+ * @description
202
+ * Mock implementation of {@link ng.$exceptionHandler} that rethrows or logs errors passed
203
+ * into it. See {@link ngMock.$exceptionHandlerProvider $exceptionHandlerProvider} for configuration
204
+ * information.
205
+ *
206
+ *
207
+ * ```js
208
+ * describe('$exceptionHandlerProvider', function() {
209
+ *
210
+ * it('should capture log messages and exceptions', function() {
211
+ *
212
+ * module(function($exceptionHandlerProvider) {
213
+ * $exceptionHandlerProvider.mode('log');
214
+ * });
215
+ *
216
+ * inject(function($log, $exceptionHandler, $timeout) {
217
+ * $timeout(function() { $log.log(1); });
218
+ * $timeout(function() { $log.log(2); throw 'banana peel'; });
219
+ * $timeout(function() { $log.log(3); });
220
+ * expect($exceptionHandler.errors).toEqual([]);
221
+ * expect($log.assertEmpty());
222
+ * $timeout.flush();
223
+ * expect($exceptionHandler.errors).toEqual(['banana peel']);
224
+ * expect($log.log.logs).toEqual([[1], [2], [3]]);
225
+ * });
226
+ * });
227
+ * });
228
+ * ```
229
+ */
230
+
231
+ angular.mock.$ExceptionHandlerProvider = function() {
232
+ var handler;
233
+
234
+ /**
235
+ * @ngdoc method
236
+ * @name $exceptionHandlerProvider#mode
237
+ *
238
+ * @description
239
+ * Sets the logging mode.
240
+ *
241
+ * @param {string} mode Mode of operation, defaults to `rethrow`.
242
+ *
243
+ * - `rethrow`: If any errors are passed into the handler in tests, it typically
244
+ * means that there is a bug in the application or test, so this mock will
245
+ * make these tests fail.
246
+ * - `log`: Sometimes it is desirable to test that an error is thrown, for this case the `log`
247
+ * mode stores an array of errors in `$exceptionHandler.errors`, to allow later
248
+ * assertion of them. See {@link ngMock.$log#assertEmpty assertEmpty()} and
249
+ * {@link ngMock.$log#reset reset()}
250
+ */
251
+ this.mode = function(mode) {
252
+ switch(mode) {
253
+ case 'rethrow':
254
+ handler = function(e) {
255
+ throw e;
256
+ };
257
+ break;
258
+ case 'log':
259
+ var errors = [];
260
+
261
+ handler = function(e) {
262
+ if (arguments.length == 1) {
263
+ errors.push(e);
264
+ } else {
265
+ errors.push([].slice.call(arguments, 0));
266
+ }
267
+ };
268
+
269
+ handler.errors = errors;
270
+ break;
271
+ default:
272
+ throw new Error("Unknown mode '" + mode + "', only 'log'/'rethrow' modes are allowed!");
273
+ }
274
+ };
275
+
276
+ this.$get = function() {
277
+ return handler;
278
+ };
279
+
280
+ this.mode('rethrow');
281
+ };
282
+
283
+
284
+ /**
285
+ * @ngdoc service
286
+ * @name $log
287
+ *
288
+ * @description
289
+ * Mock implementation of {@link ng.$log} that gathers all logged messages in arrays
290
+ * (one array per logging level). These arrays are exposed as `logs` property of each of the
291
+ * level-specific log function, e.g. for level `error` the array is exposed as `$log.error.logs`.
292
+ *
293
+ */
294
+ angular.mock.$LogProvider = function() {
295
+ var debug = true;
296
+
297
+ function concat(array1, array2, index) {
298
+ return array1.concat(Array.prototype.slice.call(array2, index));
299
+ }
300
+
301
+ this.debugEnabled = function(flag) {
302
+ if (angular.isDefined(flag)) {
303
+ debug = flag;
304
+ return this;
305
+ } else {
306
+ return debug;
307
+ }
308
+ };
309
+
310
+ this.$get = function () {
311
+ var $log = {
312
+ log: function() { $log.log.logs.push(concat([], arguments, 0)); },
313
+ warn: function() { $log.warn.logs.push(concat([], arguments, 0)); },
314
+ info: function() { $log.info.logs.push(concat([], arguments, 0)); },
315
+ error: function() { $log.error.logs.push(concat([], arguments, 0)); },
316
+ debug: function() {
317
+ if (debug) {
318
+ $log.debug.logs.push(concat([], arguments, 0));
319
+ }
320
+ }
321
+ };
322
+
323
+ /**
324
+ * @ngdoc method
325
+ * @name $log#reset
326
+ *
327
+ * @description
328
+ * Reset all of the logging arrays to empty.
329
+ */
330
+ $log.reset = function () {
331
+ /**
332
+ * @ngdoc property
333
+ * @name $log#log.logs
334
+ *
335
+ * @description
336
+ * Array of messages logged using {@link ngMock.$log#log}.
337
+ *
338
+ * @example
339
+ * ```js
340
+ * $log.log('Some Log');
341
+ * var first = $log.log.logs.unshift();
342
+ * ```
343
+ */
344
+ $log.log.logs = [];
345
+ /**
346
+ * @ngdoc property
347
+ * @name $log#info.logs
348
+ *
349
+ * @description
350
+ * Array of messages logged using {@link ngMock.$log#info}.
351
+ *
352
+ * @example
353
+ * ```js
354
+ * $log.info('Some Info');
355
+ * var first = $log.info.logs.unshift();
356
+ * ```
357
+ */
358
+ $log.info.logs = [];
359
+ /**
360
+ * @ngdoc property
361
+ * @name $log#warn.logs
362
+ *
363
+ * @description
364
+ * Array of messages logged using {@link ngMock.$log#warn}.
365
+ *
366
+ * @example
367
+ * ```js
368
+ * $log.warn('Some Warning');
369
+ * var first = $log.warn.logs.unshift();
370
+ * ```
371
+ */
372
+ $log.warn.logs = [];
373
+ /**
374
+ * @ngdoc property
375
+ * @name $log#error.logs
376
+ *
377
+ * @description
378
+ * Array of messages logged using {@link ngMock.$log#error}.
379
+ *
380
+ * @example
381
+ * ```js
382
+ * $log.error('Some Error');
383
+ * var first = $log.error.logs.unshift();
384
+ * ```
385
+ */
386
+ $log.error.logs = [];
387
+ /**
388
+ * @ngdoc property
389
+ * @name $log#debug.logs
390
+ *
391
+ * @description
392
+ * Array of messages logged using {@link ngMock.$log#debug}.
393
+ *
394
+ * @example
395
+ * ```js
396
+ * $log.debug('Some Error');
397
+ * var first = $log.debug.logs.unshift();
398
+ * ```
399
+ */
400
+ $log.debug.logs = [];
401
+ };
402
+
403
+ /**
404
+ * @ngdoc method
405
+ * @name $log#assertEmpty
406
+ *
407
+ * @description
408
+ * Assert that the all of the logging methods have no logged messages. If messages present, an
409
+ * exception is thrown.
410
+ */
411
+ $log.assertEmpty = function() {
412
+ var errors = [];
413
+ angular.forEach(['error', 'warn', 'info', 'log', 'debug'], function(logLevel) {
414
+ angular.forEach($log[logLevel].logs, function(log) {
415
+ angular.forEach(log, function (logItem) {
416
+ errors.push('MOCK $log (' + logLevel + '): ' + String(logItem) + '\n' +
417
+ (logItem.stack || ''));
418
+ });
419
+ });
420
+ });
421
+ if (errors.length) {
422
+ errors.unshift("Expected $log to be empty! Either a message was logged unexpectedly, or "+
423
+ "an expected log message was not checked and removed:");
424
+ errors.push('');
425
+ throw new Error(errors.join('\n---------\n'));
426
+ }
427
+ };
428
+
429
+ $log.reset();
430
+ return $log;
431
+ };
432
+ };
433
+
434
+
435
+ /**
436
+ * @ngdoc service
437
+ * @name $interval
438
+ *
439
+ * @description
440
+ * Mock implementation of the $interval service.
441
+ *
442
+ * Use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
443
+ * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
444
+ * time.
445
+ *
446
+ * @param {function()} fn A function that should be called repeatedly.
447
+ * @param {number} delay Number of milliseconds between each function call.
448
+ * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
449
+ * indefinitely.
450
+ * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
451
+ * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
452
+ * @returns {promise} A promise which will be notified on each iteration.
453
+ */
454
+ angular.mock.$IntervalProvider = function() {
455
+ this.$get = ['$rootScope', '$q',
456
+ function($rootScope, $q) {
457
+ var repeatFns = [],
458
+ nextRepeatId = 0,
459
+ now = 0;
460
+
461
+ var $interval = function(fn, delay, count, invokeApply) {
462
+ var deferred = $q.defer(),
463
+ promise = deferred.promise,
464
+ iteration = 0,
465
+ skipApply = (angular.isDefined(invokeApply) && !invokeApply);
466
+
467
+ count = (angular.isDefined(count)) ? count : 0;
468
+ promise.then(null, null, fn);
469
+
470
+ promise.$$intervalId = nextRepeatId;
471
+
472
+ function tick() {
473
+ deferred.notify(iteration++);
474
+
475
+ if (count > 0 && iteration >= count) {
476
+ var fnIndex;
477
+ deferred.resolve(iteration);
478
+
479
+ angular.forEach(repeatFns, function(fn, index) {
480
+ if (fn.id === promise.$$intervalId) fnIndex = index;
481
+ });
482
+
483
+ if (fnIndex !== undefined) {
484
+ repeatFns.splice(fnIndex, 1);
485
+ }
486
+ }
487
+
488
+ if (!skipApply) $rootScope.$apply();
489
+ }
490
+
491
+ repeatFns.push({
492
+ nextTime:(now + delay),
493
+ delay: delay,
494
+ fn: tick,
495
+ id: nextRepeatId,
496
+ deferred: deferred
497
+ });
498
+ repeatFns.sort(function(a,b){ return a.nextTime - b.nextTime;});
499
+
500
+ nextRepeatId++;
501
+ return promise;
502
+ };
503
+ /**
504
+ * @ngdoc method
505
+ * @name $interval#cancel
506
+ *
507
+ * @description
508
+ * Cancels a task associated with the `promise`.
509
+ *
510
+ * @param {promise} promise A promise from calling the `$interval` function.
511
+ * @returns {boolean} Returns `true` if the task was successfully cancelled.
512
+ */
513
+ $interval.cancel = function(promise) {
514
+ if(!promise) return false;
515
+ var fnIndex;
516
+
517
+ angular.forEach(repeatFns, function(fn, index) {
518
+ if (fn.id === promise.$$intervalId) fnIndex = index;
519
+ });
520
+
521
+ if (fnIndex !== undefined) {
522
+ repeatFns[fnIndex].deferred.reject('canceled');
523
+ repeatFns.splice(fnIndex, 1);
524
+ return true;
525
+ }
526
+
527
+ return false;
528
+ };
529
+
530
+ /**
531
+ * @ngdoc method
532
+ * @name $interval#flush
533
+ * @description
534
+ *
535
+ * Runs interval tasks scheduled to be run in the next `millis` milliseconds.
536
+ *
537
+ * @param {number=} millis maximum timeout amount to flush up until.
538
+ *
539
+ * @return {number} The amount of time moved forward.
540
+ */
541
+ $interval.flush = function(millis) {
542
+ now += millis;
543
+ while (repeatFns.length && repeatFns[0].nextTime <= now) {
544
+ var task = repeatFns[0];
545
+ task.fn();
546
+ task.nextTime += task.delay;
547
+ repeatFns.sort(function(a,b){ return a.nextTime - b.nextTime;});
548
+ }
549
+ return millis;
550
+ };
551
+
552
+ return $interval;
553
+ }];
554
+ };
555
+
556
+
557
+ /* jshint -W101 */
558
+ /* The R_ISO8061_STR regex is never going to fit into the 100 char limit!
559
+ * This directive should go inside the anonymous function but a bug in JSHint means that it would
560
+ * not be enacted early enough to prevent the warning.
561
+ */
562
+ var R_ISO8061_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?:\:?(\d\d)(?:\:?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/;
563
+
564
+ function jsonStringToDate(string) {
565
+ var match;
566
+ if (match = string.match(R_ISO8061_STR)) {
567
+ var date = new Date(0),
568
+ tzHour = 0,
569
+ tzMin = 0;
570
+ if (match[9]) {
571
+ tzHour = int(match[9] + match[10]);
572
+ tzMin = int(match[9] + match[11]);
573
+ }
574
+ date.setUTCFullYear(int(match[1]), int(match[2]) - 1, int(match[3]));
575
+ date.setUTCHours(int(match[4]||0) - tzHour,
576
+ int(match[5]||0) - tzMin,
577
+ int(match[6]||0),
578
+ int(match[7]||0));
579
+ return date;
580
+ }
581
+ return string;
582
+ }
583
+
584
+ function int(str) {
585
+ return parseInt(str, 10);
586
+ }
587
+
588
+ function padNumber(num, digits, trim) {
589
+ var neg = '';
590
+ if (num < 0) {
591
+ neg = '-';
592
+ num = -num;
593
+ }
594
+ num = '' + num;
595
+ while(num.length < digits) num = '0' + num;
596
+ if (trim)
597
+ num = num.substr(num.length - digits);
598
+ return neg + num;
599
+ }
600
+
601
+
602
+ /**
603
+ * @ngdoc type
604
+ * @name angular.mock.TzDate
605
+ * @description
606
+ *
607
+ * *NOTE*: this is not an injectable instance, just a globally available mock class of `Date`.
608
+ *
609
+ * Mock of the Date type which has its timezone specified via constructor arg.
610
+ *
611
+ * The main purpose is to create Date-like instances with timezone fixed to the specified timezone
612
+ * offset, so that we can test code that depends on local timezone settings without dependency on
613
+ * the time zone settings of the machine where the code is running.
614
+ *
615
+ * @param {number} offset Offset of the *desired* timezone in hours (fractions will be honored)
616
+ * @param {(number|string)} timestamp Timestamp representing the desired time in *UTC*
617
+ *
618
+ * @example
619
+ * !!!! WARNING !!!!!
620
+ * This is not a complete Date object so only methods that were implemented can be called safely.
621
+ * To make matters worse, TzDate instances inherit stuff from Date via a prototype.
622
+ *
623
+ * We do our best to intercept calls to "unimplemented" methods, but since the list of methods is
624
+ * incomplete we might be missing some non-standard methods. This can result in errors like:
625
+ * "Date.prototype.foo called on incompatible Object".
626
+ *
627
+ * ```js
628
+ * var newYearInBratislava = new TzDate(-1, '2009-12-31T23:00:00Z');
629
+ * newYearInBratislava.getTimezoneOffset() => -60;
630
+ * newYearInBratislava.getFullYear() => 2010;
631
+ * newYearInBratislava.getMonth() => 0;
632
+ * newYearInBratislava.getDate() => 1;
633
+ * newYearInBratislava.getHours() => 0;
634
+ * newYearInBratislava.getMinutes() => 0;
635
+ * newYearInBratislava.getSeconds() => 0;
636
+ * ```
637
+ *
638
+ */
639
+ angular.mock.TzDate = function (offset, timestamp) {
640
+ var self = new Date(0);
641
+ if (angular.isString(timestamp)) {
642
+ var tsStr = timestamp;
643
+
644
+ self.origDate = jsonStringToDate(timestamp);
645
+
646
+ timestamp = self.origDate.getTime();
647
+ if (isNaN(timestamp))
648
+ throw {
649
+ name: "Illegal Argument",
650
+ message: "Arg '" + tsStr + "' passed into TzDate constructor is not a valid date string"
651
+ };
652
+ } else {
653
+ self.origDate = new Date(timestamp);
654
+ }
655
+
656
+ var localOffset = new Date(timestamp).getTimezoneOffset();
657
+ self.offsetDiff = localOffset*60*1000 - offset*1000*60*60;
658
+ self.date = new Date(timestamp + self.offsetDiff);
659
+
660
+ self.getTime = function() {
661
+ return self.date.getTime() - self.offsetDiff;
662
+ };
663
+
664
+ self.toLocaleDateString = function() {
665
+ return self.date.toLocaleDateString();
666
+ };
667
+
668
+ self.getFullYear = function() {
669
+ return self.date.getFullYear();
670
+ };
671
+
672
+ self.getMonth = function() {
673
+ return self.date.getMonth();
674
+ };
675
+
676
+ self.getDate = function() {
677
+ return self.date.getDate();
678
+ };
679
+
680
+ self.getHours = function() {
681
+ return self.date.getHours();
682
+ };
683
+
684
+ self.getMinutes = function() {
685
+ return self.date.getMinutes();
686
+ };
687
+
688
+ self.getSeconds = function() {
689
+ return self.date.getSeconds();
690
+ };
691
+
692
+ self.getMilliseconds = function() {
693
+ return self.date.getMilliseconds();
694
+ };
695
+
696
+ self.getTimezoneOffset = function() {
697
+ return offset * 60;
698
+ };
699
+
700
+ self.getUTCFullYear = function() {
701
+ return self.origDate.getUTCFullYear();
702
+ };
703
+
704
+ self.getUTCMonth = function() {
705
+ return self.origDate.getUTCMonth();
706
+ };
707
+
708
+ self.getUTCDate = function() {
709
+ return self.origDate.getUTCDate();
710
+ };
711
+
712
+ self.getUTCHours = function() {
713
+ return self.origDate.getUTCHours();
714
+ };
715
+
716
+ self.getUTCMinutes = function() {
717
+ return self.origDate.getUTCMinutes();
718
+ };
719
+
720
+ self.getUTCSeconds = function() {
721
+ return self.origDate.getUTCSeconds();
722
+ };
723
+
724
+ self.getUTCMilliseconds = function() {
725
+ return self.origDate.getUTCMilliseconds();
726
+ };
727
+
728
+ self.getDay = function() {
729
+ return self.date.getDay();
730
+ };
731
+
732
+ // provide this method only on browsers that already have it
733
+ if (self.toISOString) {
734
+ self.toISOString = function() {
735
+ return padNumber(self.origDate.getUTCFullYear(), 4) + '-' +
736
+ padNumber(self.origDate.getUTCMonth() + 1, 2) + '-' +
737
+ padNumber(self.origDate.getUTCDate(), 2) + 'T' +
738
+ padNumber(self.origDate.getUTCHours(), 2) + ':' +
739
+ padNumber(self.origDate.getUTCMinutes(), 2) + ':' +
740
+ padNumber(self.origDate.getUTCSeconds(), 2) + '.' +
741
+ padNumber(self.origDate.getUTCMilliseconds(), 3) + 'Z';
742
+ };
743
+ }
744
+
745
+ //hide all methods not implemented in this mock that the Date prototype exposes
746
+ var unimplementedMethods = ['getUTCDay',
747
+ 'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds',
748
+ 'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear',
749
+ 'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds',
750
+ 'setYear', 'toDateString', 'toGMTString', 'toJSON', 'toLocaleFormat', 'toLocaleString',
751
+ 'toLocaleTimeString', 'toSource', 'toString', 'toTimeString', 'toUTCString', 'valueOf'];
752
+
753
+ angular.forEach(unimplementedMethods, function(methodName) {
754
+ self[methodName] = function() {
755
+ throw new Error("Method '" + methodName + "' is not implemented in the TzDate mock");
756
+ };
757
+ });
758
+
759
+ return self;
760
+ };
761
+
762
+ //make "tzDateInstance instanceof Date" return true
763
+ angular.mock.TzDate.prototype = Date.prototype;
764
+ /* jshint +W101 */
765
+
766
+ angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
767
+
768
+ .config(['$provide', function($provide) {
769
+
770
+ var reflowQueue = [];
771
+ $provide.value('$$animateReflow', function(fn) {
772
+ var index = reflowQueue.length;
773
+ reflowQueue.push(fn);
774
+ return function cancel() {
775
+ reflowQueue.splice(index, 1);
776
+ };
777
+ });
778
+
779
+ $provide.decorator('$animate', function($delegate, $$asyncCallback) {
780
+ var animate = {
781
+ queue : [],
782
+ enabled : $delegate.enabled,
783
+ triggerCallbacks : function() {
784
+ $$asyncCallback.flush();
785
+ },
786
+ triggerReflow : function() {
787
+ angular.forEach(reflowQueue, function(fn) {
788
+ fn();
789
+ });
790
+ reflowQueue = [];
791
+ }
792
+ };
793
+
794
+ angular.forEach(
795
+ ['enter','leave','move','addClass','removeClass','setClass'], function(method) {
796
+ animate[method] = function() {
797
+ animate.queue.push({
798
+ event : method,
799
+ element : arguments[0],
800
+ args : arguments
801
+ });
802
+ $delegate[method].apply($delegate, arguments);
803
+ };
804
+ });
805
+
806
+ return animate;
807
+ });
808
+
809
+ }]);
810
+
811
+
812
+ /**
813
+ * @ngdoc function
814
+ * @name angular.mock.dump
815
+ * @description
816
+ *
817
+ * *NOTE*: this is not an injectable instance, just a globally available function.
818
+ *
819
+ * Method for serializing common angular objects (scope, elements, etc..) into strings, useful for
820
+ * debugging.
821
+ *
822
+ * This method is also available on window, where it can be used to display objects on debug
823
+ * console.
824
+ *
825
+ * @param {*} object - any object to turn into string.
826
+ * @return {string} a serialized string of the argument
827
+ */
828
+ angular.mock.dump = function(object) {
829
+ return serialize(object);
830
+
831
+ function serialize(object) {
832
+ var out;
833
+
834
+ if (angular.isElement(object)) {
835
+ object = angular.element(object);
836
+ out = angular.element('<div></div>');
837
+ angular.forEach(object, function(element) {
838
+ out.append(angular.element(element).clone());
839
+ });
840
+ out = out.html();
841
+ } else if (angular.isArray(object)) {
842
+ out = [];
843
+ angular.forEach(object, function(o) {
844
+ out.push(serialize(o));
845
+ });
846
+ out = '[ ' + out.join(', ') + ' ]';
847
+ } else if (angular.isObject(object)) {
848
+ if (angular.isFunction(object.$eval) && angular.isFunction(object.$apply)) {
849
+ out = serializeScope(object);
850
+ } else if (object instanceof Error) {
851
+ out = object.stack || ('' + object.name + ': ' + object.message);
852
+ } else {
853
+ // TODO(i): this prevents methods being logged,
854
+ // we should have a better way to serialize objects
855
+ out = angular.toJson(object, true);
856
+ }
857
+ } else {
858
+ out = String(object);
859
+ }
860
+
861
+ return out;
862
+ }
863
+
864
+ function serializeScope(scope, offset) {
865
+ offset = offset || ' ';
866
+ var log = [offset + 'Scope(' + scope.$id + '): {'];
867
+ for ( var key in scope ) {
868
+ if (Object.prototype.hasOwnProperty.call(scope, key) && !key.match(/^(\$|this)/)) {
869
+ log.push(' ' + key + ': ' + angular.toJson(scope[key]));
870
+ }
871
+ }
872
+ var child = scope.$$childHead;
873
+ while(child) {
874
+ log.push(serializeScope(child, offset + ' '));
875
+ child = child.$$nextSibling;
876
+ }
877
+ log.push('}');
878
+ return log.join('\n' + offset);
879
+ }
880
+ };
881
+
882
+ /**
883
+ * @ngdoc service
884
+ * @name $httpBackend
885
+ * @description
886
+ * Fake HTTP backend implementation suitable for unit testing applications that use the
887
+ * {@link ng.$http $http service}.
888
+ *
889
+ * *Note*: For fake HTTP backend implementation suitable for end-to-end testing or backend-less
890
+ * development please see {@link ngMockE2E.$httpBackend e2e $httpBackend mock}.
891
+ *
892
+ * During unit testing, we want our unit tests to run quickly and have no external dependencies so
893
+ * we don’t want to send [XHR](https://developer.mozilla.org/en/xmlhttprequest) or
894
+ * [JSONP](http://en.wikipedia.org/wiki/JSONP) requests to a real server. All we really need is
895
+ * to verify whether a certain request has been sent or not, or alternatively just let the
896
+ * application make requests, respond with pre-trained responses and assert that the end result is
897
+ * what we expect it to be.
898
+ *
899
+ * This mock implementation can be used to respond with static or dynamic responses via the
900
+ * `expect` and `when` apis and their shortcuts (`expectGET`, `whenPOST`, etc).
901
+ *
902
+ * When an Angular application needs some data from a server, it calls the $http service, which
903
+ * sends the request to a real server using $httpBackend service. With dependency injection, it is
904
+ * easy to inject $httpBackend mock (which has the same API as $httpBackend) and use it to verify
905
+ * the requests and respond with some testing data without sending a request to a real server.
906
+ *
907
+ * There are two ways to specify what test data should be returned as http responses by the mock
908
+ * backend when the code under test makes http requests:
909
+ *
910
+ * - `$httpBackend.expect` - specifies a request expectation
911
+ * - `$httpBackend.when` - specifies a backend definition
912
+ *
913
+ *
914
+ * # Request Expectations vs Backend Definitions
915
+ *
916
+ * Request expectations provide a way to make assertions about requests made by the application and
917
+ * to define responses for those requests. The test will fail if the expected requests are not made
918
+ * or they are made in the wrong order.
919
+ *
920
+ * Backend definitions allow you to define a fake backend for your application which doesn't assert
921
+ * if a particular request was made or not, it just returns a trained response if a request is made.
922
+ * The test will pass whether or not the request gets made during testing.
923
+ *
924
+ *
925
+ * <table class="table">
926
+ * <tr><th width="220px"></th><th>Request expectations</th><th>Backend definitions</th></tr>
927
+ * <tr>
928
+ * <th>Syntax</th>
929
+ * <td>.expect(...).respond(...)</td>
930
+ * <td>.when(...).respond(...)</td>
931
+ * </tr>
932
+ * <tr>
933
+ * <th>Typical usage</th>
934
+ * <td>strict unit tests</td>
935
+ * <td>loose (black-box) unit testing</td>
936
+ * </tr>
937
+ * <tr>
938
+ * <th>Fulfills multiple requests</th>
939
+ * <td>NO</td>
940
+ * <td>YES</td>
941
+ * </tr>
942
+ * <tr>
943
+ * <th>Order of requests matters</th>
944
+ * <td>YES</td>
945
+ * <td>NO</td>
946
+ * </tr>
947
+ * <tr>
948
+ * <th>Request required</th>
949
+ * <td>YES</td>
950
+ * <td>NO</td>
951
+ * </tr>
952
+ * <tr>
953
+ * <th>Response required</th>
954
+ * <td>optional (see below)</td>
955
+ * <td>YES</td>
956
+ * </tr>
957
+ * </table>
958
+ *
959
+ * In cases where both backend definitions and request expectations are specified during unit
960
+ * testing, the request expectations are evaluated first.
961
+ *
962
+ * If a request expectation has no response specified, the algorithm will search your backend
963
+ * definitions for an appropriate response.
964
+ *
965
+ * If a request didn't match any expectation or if the expectation doesn't have the response
966
+ * defined, the backend definitions are evaluated in sequential order to see if any of them match
967
+ * the request. The response from the first matched definition is returned.
968
+ *
969
+ *
970
+ * # Flushing HTTP requests
971
+ *
972
+ * The $httpBackend used in production always responds to requests asynchronously. If we preserved
973
+ * this behavior in unit testing, we'd have to create async unit tests, which are hard to write,
974
+ * to follow and to maintain. But neither can the testing mock respond synchronously; that would
975
+ * change the execution of the code under test. For this reason, the mock $httpBackend has a
976
+ * `flush()` method, which allows the test to explicitly flush pending requests. This preserves
977
+ * the async api of the backend, while allowing the test to execute synchronously.
978
+ *
979
+ *
980
+ * # Unit testing with mock $httpBackend
981
+ * The following code shows how to setup and use the mock backend when unit testing a controller.
982
+ * First we create the controller under test:
983
+ *
984
+ ```js
985
+ // The controller code
986
+ function MyController($scope, $http) {
987
+ var authToken;
988
+
989
+ $http.get('/auth.py').success(function(data, status, headers) {
990
+ authToken = headers('A-Token');
991
+ $scope.user = data;
992
+ });
993
+
994
+ $scope.saveMessage = function(message) {
995
+ var headers = { 'Authorization': authToken };
996
+ $scope.status = 'Saving...';
997
+
998
+ $http.post('/add-msg.py', message, { headers: headers } ).success(function(response) {
999
+ $scope.status = '';
1000
+ }).error(function() {
1001
+ $scope.status = 'ERROR!';
1002
+ });
1003
+ };
1004
+ }
1005
+ ```
1006
+ *
1007
+ * Now we setup the mock backend and create the test specs:
1008
+ *
1009
+ ```js
1010
+ // testing controller
1011
+ describe('MyController', function() {
1012
+ var $httpBackend, $rootScope, createController;
1013
+
1014
+ beforeEach(inject(function($injector) {
1015
+ // Set up the mock http service responses
1016
+ $httpBackend = $injector.get('$httpBackend');
1017
+ // backend definition common for all tests
1018
+ $httpBackend.when('GET', '/auth.py').respond({userId: 'userX'}, {'A-Token': 'xxx'});
1019
+
1020
+ // Get hold of a scope (i.e. the root scope)
1021
+ $rootScope = $injector.get('$rootScope');
1022
+ // The $controller service is used to create instances of controllers
1023
+ var $controller = $injector.get('$controller');
1024
+
1025
+ createController = function() {
1026
+ return $controller('MyController', {'$scope' : $rootScope });
1027
+ };
1028
+ }));
1029
+
1030
+
1031
+ afterEach(function() {
1032
+ $httpBackend.verifyNoOutstandingExpectation();
1033
+ $httpBackend.verifyNoOutstandingRequest();
1034
+ });
1035
+
1036
+
1037
+ it('should fetch authentication token', function() {
1038
+ $httpBackend.expectGET('/auth.py');
1039
+ var controller = createController();
1040
+ $httpBackend.flush();
1041
+ });
1042
+
1043
+
1044
+ it('should send msg to server', function() {
1045
+ var controller = createController();
1046
+ $httpBackend.flush();
1047
+
1048
+ // now you don’t care about the authentication, but
1049
+ // the controller will still send the request and
1050
+ // $httpBackend will respond without you having to
1051
+ // specify the expectation and response for this request
1052
+
1053
+ $httpBackend.expectPOST('/add-msg.py', 'message content').respond(201, '');
1054
+ $rootScope.saveMessage('message content');
1055
+ expect($rootScope.status).toBe('Saving...');
1056
+ $httpBackend.flush();
1057
+ expect($rootScope.status).toBe('');
1058
+ });
1059
+
1060
+
1061
+ it('should send auth header', function() {
1062
+ var controller = createController();
1063
+ $httpBackend.flush();
1064
+
1065
+ $httpBackend.expectPOST('/add-msg.py', undefined, function(headers) {
1066
+ // check if the header was send, if it wasn't the expectation won't
1067
+ // match the request and the test will fail
1068
+ return headers['Authorization'] == 'xxx';
1069
+ }).respond(201, '');
1070
+
1071
+ $rootScope.saveMessage('whatever');
1072
+ $httpBackend.flush();
1073
+ });
1074
+ });
1075
+ ```
1076
+ */
1077
+ angular.mock.$HttpBackendProvider = function() {
1078
+ this.$get = ['$rootScope', createHttpBackendMock];
1079
+ };
1080
+
1081
+ /**
1082
+ * General factory function for $httpBackend mock.
1083
+ * Returns instance for unit testing (when no arguments specified):
1084
+ * - passing through is disabled
1085
+ * - auto flushing is disabled
1086
+ *
1087
+ * Returns instance for e2e testing (when `$delegate` and `$browser` specified):
1088
+ * - passing through (delegating request to real backend) is enabled
1089
+ * - auto flushing is enabled
1090
+ *
1091
+ * @param {Object=} $delegate Real $httpBackend instance (allow passing through if specified)
1092
+ * @param {Object=} $browser Auto-flushing enabled if specified
1093
+ * @return {Object} Instance of $httpBackend mock
1094
+ */
1095
+ function createHttpBackendMock($rootScope, $delegate, $browser) {
1096
+ var definitions = [],
1097
+ expectations = [],
1098
+ responses = [],
1099
+ responsesPush = angular.bind(responses, responses.push),
1100
+ copy = angular.copy;
1101
+
1102
+ function createResponse(status, data, headers, statusText) {
1103
+ if (angular.isFunction(status)) return status;
1104
+
1105
+ return function() {
1106
+ return angular.isNumber(status)
1107
+ ? [status, data, headers, statusText]
1108
+ : [200, status, data];
1109
+ };
1110
+ }
1111
+
1112
+ // TODO(vojta): change params to: method, url, data, headers, callback
1113
+ function $httpBackend(method, url, data, callback, headers, timeout, withCredentials) {
1114
+ var xhr = new MockXhr(),
1115
+ expectation = expectations[0],
1116
+ wasExpected = false;
1117
+
1118
+ function prettyPrint(data) {
1119
+ return (angular.isString(data) || angular.isFunction(data) || data instanceof RegExp)
1120
+ ? data
1121
+ : angular.toJson(data);
1122
+ }
1123
+
1124
+ function wrapResponse(wrapped) {
1125
+ if (!$browser && timeout && timeout.then) timeout.then(handleTimeout);
1126
+
1127
+ return handleResponse;
1128
+
1129
+ function handleResponse() {
1130
+ var response = wrapped.response(method, url, data, headers);
1131
+ xhr.$$respHeaders = response[2];
1132
+ callback(copy(response[0]), copy(response[1]), xhr.getAllResponseHeaders(),
1133
+ copy(response[3] || ''));
1134
+ }
1135
+
1136
+ function handleTimeout() {
1137
+ for (var i = 0, ii = responses.length; i < ii; i++) {
1138
+ if (responses[i] === handleResponse) {
1139
+ responses.splice(i, 1);
1140
+ callback(-1, undefined, '');
1141
+ break;
1142
+ }
1143
+ }
1144
+ }
1145
+ }
1146
+
1147
+ if (expectation && expectation.match(method, url)) {
1148
+ if (!expectation.matchData(data))
1149
+ throw new Error('Expected ' + expectation + ' with different data\n' +
1150
+ 'EXPECTED: ' + prettyPrint(expectation.data) + '\nGOT: ' + data);
1151
+
1152
+ if (!expectation.matchHeaders(headers))
1153
+ throw new Error('Expected ' + expectation + ' with different headers\n' +
1154
+ 'EXPECTED: ' + prettyPrint(expectation.headers) + '\nGOT: ' +
1155
+ prettyPrint(headers));
1156
+
1157
+ expectations.shift();
1158
+
1159
+ if (expectation.response) {
1160
+ responses.push(wrapResponse(expectation));
1161
+ return;
1162
+ }
1163
+ wasExpected = true;
1164
+ }
1165
+
1166
+ var i = -1, definition;
1167
+ while ((definition = definitions[++i])) {
1168
+ if (definition.match(method, url, data, headers || {})) {
1169
+ if (definition.response) {
1170
+ // if $browser specified, we do auto flush all requests
1171
+ ($browser ? $browser.defer : responsesPush)(wrapResponse(definition));
1172
+ } else if (definition.passThrough) {
1173
+ $delegate(method, url, data, callback, headers, timeout, withCredentials);
1174
+ } else throw new Error('No response defined !');
1175
+ return;
1176
+ }
1177
+ }
1178
+ throw wasExpected ?
1179
+ new Error('No response defined !') :
1180
+ new Error('Unexpected request: ' + method + ' ' + url + '\n' +
1181
+ (expectation ? 'Expected ' + expectation : 'No more request expected'));
1182
+ }
1183
+
1184
+ /**
1185
+ * @ngdoc method
1186
+ * @name $httpBackend#when
1187
+ * @description
1188
+ * Creates a new backend definition.
1189
+ *
1190
+ * @param {string} method HTTP method.
1191
+ * @param {string|RegExp} url HTTP url.
1192
+ * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
1193
+ * data string and returns true if the data is as expected.
1194
+ * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
1195
+ * object and returns true if the headers match the current definition.
1196
+ * @returns {requestHandler} Returns an object with a `respond` method that controls how a matched
1197
+ * request is handled.
1198
+ *
1199
+ * - respond –
1200
+ * `{function([status,] data[, headers, statusText])
1201
+ * | function(function(method, url, data, headers)}`
1202
+ * – The respond method takes a set of static data to be returned or a function that can
1203
+ * return an array containing response status (number), response data (string), response
1204
+ * headers (Object), and the text for the status (string).
1205
+ */
1206
+ $httpBackend.when = function(method, url, data, headers) {
1207
+ var definition = new MockHttpExpectation(method, url, data, headers),
1208
+ chain = {
1209
+ respond: function(status, data, headers, statusText) {
1210
+ definition.response = createResponse(status, data, headers, statusText);
1211
+ }
1212
+ };
1213
+
1214
+ if ($browser) {
1215
+ chain.passThrough = function() {
1216
+ definition.passThrough = true;
1217
+ };
1218
+ }
1219
+
1220
+ definitions.push(definition);
1221
+ return chain;
1222
+ };
1223
+
1224
+ /**
1225
+ * @ngdoc method
1226
+ * @name $httpBackend#whenGET
1227
+ * @description
1228
+ * Creates a new backend definition for GET requests. For more info see `when()`.
1229
+ *
1230
+ * @param {string|RegExp} url HTTP url.
1231
+ * @param {(Object|function(Object))=} headers HTTP headers.
1232
+ * @returns {requestHandler} Returns an object with a `respond` method that controls how a matched
1233
+ * request is handled.
1234
+ */
1235
+
1236
+ /**
1237
+ * @ngdoc method
1238
+ * @name $httpBackend#whenHEAD
1239
+ * @description
1240
+ * Creates a new backend definition for HEAD requests. For more info see `when()`.
1241
+ *
1242
+ * @param {string|RegExp} url HTTP url.
1243
+ * @param {(Object|function(Object))=} headers HTTP headers.
1244
+ * @returns {requestHandler} Returns an object with a `respond` method that controls how a matched
1245
+ * request is handled.
1246
+ */
1247
+
1248
+ /**
1249
+ * @ngdoc method
1250
+ * @name $httpBackend#whenDELETE
1251
+ * @description
1252
+ * Creates a new backend definition for DELETE requests. For more info see `when()`.
1253
+ *
1254
+ * @param {string|RegExp} url HTTP url.
1255
+ * @param {(Object|function(Object))=} headers HTTP headers.
1256
+ * @returns {requestHandler} Returns an object with a `respond` method that controls how a matched
1257
+ * request is handled.
1258
+ */
1259
+
1260
+ /**
1261
+ * @ngdoc method
1262
+ * @name $httpBackend#whenPOST
1263
+ * @description
1264
+ * Creates a new backend definition for POST requests. For more info see `when()`.
1265
+ *
1266
+ * @param {string|RegExp} url HTTP url.
1267
+ * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
1268
+ * data string and returns true if the data is as expected.
1269
+ * @param {(Object|function(Object))=} headers HTTP headers.
1270
+ * @returns {requestHandler} Returns an object with a `respond` method that controls how a matched
1271
+ * request is handled.
1272
+ */
1273
+
1274
+ /**
1275
+ * @ngdoc method
1276
+ * @name $httpBackend#whenPUT
1277
+ * @description
1278
+ * Creates a new backend definition for PUT requests. For more info see `when()`.
1279
+ *
1280
+ * @param {string|RegExp} url HTTP url.
1281
+ * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
1282
+ * data string and returns true if the data is as expected.
1283
+ * @param {(Object|function(Object))=} headers HTTP headers.
1284
+ * @returns {requestHandler} Returns an object with a `respond` method that controls how a matched
1285
+ * request is handled.
1286
+ */
1287
+
1288
+ /**
1289
+ * @ngdoc method
1290
+ * @name $httpBackend#whenPATCH
1291
+ * @description
1292
+ * Creates a new backend definition for PATCH requests. For more info see `when()`.
1293
+ *
1294
+ * @param {string|RegExp} url HTTP url.
1295
+ * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
1296
+ * data string and returns true if the data is as expected.
1297
+ * @param {(Object|function(Object))=} headers HTTP headers.
1298
+ * @returns {requestHandler} Returns an object with a `respond` method that controls how a matched
1299
+ * request is handled.
1300
+ */
1301
+
1302
+ /**
1303
+ * @ngdoc method
1304
+ * @name $httpBackend#whenJSONP
1305
+ * @description
1306
+ * Creates a new backend definition for JSONP requests. For more info see `when()`.
1307
+ *
1308
+ * @param {string|RegExp} url HTTP url.
1309
+ * @returns {requestHandler} Returns an object with a `respond` method that controls how a matched
1310
+ * request is handled.
1311
+ */
1312
+ createShortMethods('when');
1313
+
1314
+
1315
+ /**
1316
+ * @ngdoc method
1317
+ * @name $httpBackend#expect
1318
+ * @description
1319
+ * Creates a new request expectation.
1320
+ *
1321
+ * @param {string} method HTTP method.
1322
+ * @param {string|RegExp} url HTTP url.
1323
+ * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
1324
+ * receives data string and returns true if the data is as expected, or Object if request body
1325
+ * is in JSON format.
1326
+ * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
1327
+ * object and returns true if the headers match the current expectation.
1328
+ * @returns {requestHandler} Returns an object with a `respond` method that controls how a matched
1329
+ * request is handled.
1330
+ *
1331
+ * - respond –
1332
+ * `{function([status,] data[, headers, statusText])
1333
+ * | function(function(method, url, data, headers)}`
1334
+ * – The respond method takes a set of static data to be returned or a function that can
1335
+ * return an array containing response status (number), response data (string), response
1336
+ * headers (Object), and the text for the status (string).
1337
+ */
1338
+ $httpBackend.expect = function(method, url, data, headers) {
1339
+ var expectation = new MockHttpExpectation(method, url, data, headers);
1340
+ expectations.push(expectation);
1341
+ return {
1342
+ respond: function (status, data, headers, statusText) {
1343
+ expectation.response = createResponse(status, data, headers, statusText);
1344
+ }
1345
+ };
1346
+ };
1347
+
1348
+
1349
+ /**
1350
+ * @ngdoc method
1351
+ * @name $httpBackend#expectGET
1352
+ * @description
1353
+ * Creates a new request expectation for GET requests. For more info see `expect()`.
1354
+ *
1355
+ * @param {string|RegExp} url HTTP url.
1356
+ * @param {Object=} headers HTTP headers.
1357
+ * @returns {requestHandler} Returns an object with a `respond` method that controls how a matched
1358
+ * request is handled. See #expect for more info.
1359
+ */
1360
+
1361
+ /**
1362
+ * @ngdoc method
1363
+ * @name $httpBackend#expectHEAD
1364
+ * @description
1365
+ * Creates a new request expectation for HEAD requests. For more info see `expect()`.
1366
+ *
1367
+ * @param {string|RegExp} url HTTP url.
1368
+ * @param {Object=} headers HTTP headers.
1369
+ * @returns {requestHandler} Returns an object with a `respond` method that controls how a matched
1370
+ * request is handled.
1371
+ */
1372
+
1373
+ /**
1374
+ * @ngdoc method
1375
+ * @name $httpBackend#expectDELETE
1376
+ * @description
1377
+ * Creates a new request expectation for DELETE requests. For more info see `expect()`.
1378
+ *
1379
+ * @param {string|RegExp} url HTTP url.
1380
+ * @param {Object=} headers HTTP headers.
1381
+ * @returns {requestHandler} Returns an object with a `respond` method that controls how a matched
1382
+ * request is handled.
1383
+ */
1384
+
1385
+ /**
1386
+ * @ngdoc method
1387
+ * @name $httpBackend#expectPOST
1388
+ * @description
1389
+ * Creates a new request expectation for POST requests. For more info see `expect()`.
1390
+ *
1391
+ * @param {string|RegExp} url HTTP url.
1392
+ * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
1393
+ * receives data string and returns true if the data is as expected, or Object if request body
1394
+ * is in JSON format.
1395
+ * @param {Object=} headers HTTP headers.
1396
+ * @returns {requestHandler} Returns an object with a `respond` method that controls how a matched
1397
+ * request is handled.
1398
+ */
1399
+
1400
+ /**
1401
+ * @ngdoc method
1402
+ * @name $httpBackend#expectPUT
1403
+ * @description
1404
+ * Creates a new request expectation for PUT requests. For more info see `expect()`.
1405
+ *
1406
+ * @param {string|RegExp} url HTTP url.
1407
+ * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
1408
+ * receives data string and returns true if the data is as expected, or Object if request body
1409
+ * is in JSON format.
1410
+ * @param {Object=} headers HTTP headers.
1411
+ * @returns {requestHandler} Returns an object with a `respond` method that controls how a matched
1412
+ * request is handled.
1413
+ */
1414
+
1415
+ /**
1416
+ * @ngdoc method
1417
+ * @name $httpBackend#expectPATCH
1418
+ * @description
1419
+ * Creates a new request expectation for PATCH requests. For more info see `expect()`.
1420
+ *
1421
+ * @param {string|RegExp} url HTTP url.
1422
+ * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
1423
+ * receives data string and returns true if the data is as expected, or Object if request body
1424
+ * is in JSON format.
1425
+ * @param {Object=} headers HTTP headers.
1426
+ * @returns {requestHandler} Returns an object with a `respond` method that controls how a matched
1427
+ * request is handled.
1428
+ */
1429
+
1430
+ /**
1431
+ * @ngdoc method
1432
+ * @name $httpBackend#expectJSONP
1433
+ * @description
1434
+ * Creates a new request expectation for JSONP requests. For more info see `expect()`.
1435
+ *
1436
+ * @param {string|RegExp} url HTTP url.
1437
+ * @returns {requestHandler} Returns an object with a `respond` method that controls how a matched
1438
+ * request is handled.
1439
+ */
1440
+ createShortMethods('expect');
1441
+
1442
+
1443
+ /**
1444
+ * @ngdoc method
1445
+ * @name $httpBackend#flush
1446
+ * @description
1447
+ * Flushes all pending requests using the trained responses.
1448
+ *
1449
+ * @param {number=} count Number of responses to flush (in the order they arrived). If undefined,
1450
+ * all pending requests will be flushed. If there are no pending requests when the flush method
1451
+ * is called an exception is thrown (as this typically a sign of programming error).
1452
+ */
1453
+ $httpBackend.flush = function(count) {
1454
+ $rootScope.$digest();
1455
+ if (!responses.length) throw new Error('No pending request to flush !');
1456
+
1457
+ if (angular.isDefined(count)) {
1458
+ while (count--) {
1459
+ if (!responses.length) throw new Error('No more pending request to flush !');
1460
+ responses.shift()();
1461
+ }
1462
+ } else {
1463
+ while (responses.length) {
1464
+ responses.shift()();
1465
+ }
1466
+ }
1467
+ $httpBackend.verifyNoOutstandingExpectation();
1468
+ };
1469
+
1470
+
1471
+ /**
1472
+ * @ngdoc method
1473
+ * @name $httpBackend#verifyNoOutstandingExpectation
1474
+ * @description
1475
+ * Verifies that all of the requests defined via the `expect` api were made. If any of the
1476
+ * requests were not made, verifyNoOutstandingExpectation throws an exception.
1477
+ *
1478
+ * Typically, you would call this method following each test case that asserts requests using an
1479
+ * "afterEach" clause.
1480
+ *
1481
+ * ```js
1482
+ * afterEach($httpBackend.verifyNoOutstandingExpectation);
1483
+ * ```
1484
+ */
1485
+ $httpBackend.verifyNoOutstandingExpectation = function() {
1486
+ $rootScope.$digest();
1487
+ if (expectations.length) {
1488
+ throw new Error('Unsatisfied requests: ' + expectations.join(', '));
1489
+ }
1490
+ };
1491
+
1492
+
1493
+ /**
1494
+ * @ngdoc method
1495
+ * @name $httpBackend#verifyNoOutstandingRequest
1496
+ * @description
1497
+ * Verifies that there are no outstanding requests that need to be flushed.
1498
+ *
1499
+ * Typically, you would call this method following each test case that asserts requests using an
1500
+ * "afterEach" clause.
1501
+ *
1502
+ * ```js
1503
+ * afterEach($httpBackend.verifyNoOutstandingRequest);
1504
+ * ```
1505
+ */
1506
+ $httpBackend.verifyNoOutstandingRequest = function() {
1507
+ if (responses.length) {
1508
+ throw new Error('Unflushed requests: ' + responses.length);
1509
+ }
1510
+ };
1511
+
1512
+
1513
+ /**
1514
+ * @ngdoc method
1515
+ * @name $httpBackend#resetExpectations
1516
+ * @description
1517
+ * Resets all request expectations, but preserves all backend definitions. Typically, you would
1518
+ * call resetExpectations during a multiple-phase test when you want to reuse the same instance of
1519
+ * $httpBackend mock.
1520
+ */
1521
+ $httpBackend.resetExpectations = function() {
1522
+ expectations.length = 0;
1523
+ responses.length = 0;
1524
+ };
1525
+
1526
+ return $httpBackend;
1527
+
1528
+
1529
+ function createShortMethods(prefix) {
1530
+ angular.forEach(['GET', 'DELETE', 'JSONP', 'HEAD'], function(method) {
1531
+ $httpBackend[prefix + method] = function(url, headers) {
1532
+ return $httpBackend[prefix](method, url, undefined, headers);
1533
+ };
1534
+ });
1535
+
1536
+ angular.forEach(['PUT', 'POST', 'PATCH'], function(method) {
1537
+ $httpBackend[prefix + method] = function(url, data, headers) {
1538
+ return $httpBackend[prefix](method, url, data, headers);
1539
+ };
1540
+ });
1541
+ }
1542
+ }
1543
+
1544
+ function MockHttpExpectation(method, url, data, headers) {
1545
+
1546
+ this.data = data;
1547
+ this.headers = headers;
1548
+
1549
+ this.match = function(m, u, d, h) {
1550
+ if (method != m) return false;
1551
+ if (!this.matchUrl(u)) return false;
1552
+ if (angular.isDefined(d) && !this.matchData(d)) return false;
1553
+ if (angular.isDefined(h) && !this.matchHeaders(h)) return false;
1554
+ return true;
1555
+ };
1556
+
1557
+ this.matchUrl = function(u) {
1558
+ if (!url) return true;
1559
+ if (angular.isFunction(url.test)) return url.test(u);
1560
+ return url == u;
1561
+ };
1562
+
1563
+ this.matchHeaders = function(h) {
1564
+ if (angular.isUndefined(headers)) return true;
1565
+ if (angular.isFunction(headers)) return headers(h);
1566
+ return angular.equals(headers, h);
1567
+ };
1568
+
1569
+ this.matchData = function(d) {
1570
+ if (angular.isUndefined(data)) return true;
1571
+ if (data && angular.isFunction(data.test)) return data.test(d);
1572
+ if (data && angular.isFunction(data)) return data(d);
1573
+ if (data && !angular.isString(data)) {
1574
+ return angular.equals(angular.fromJson(angular.toJson(data)), angular.fromJson(d));
1575
+ }
1576
+ return data == d;
1577
+ };
1578
+
1579
+ this.toString = function() {
1580
+ return method + ' ' + url;
1581
+ };
1582
+ }
1583
+
1584
+ function createMockXhr() {
1585
+ return new MockXhr();
1586
+ }
1587
+
1588
+ function MockXhr() {
1589
+
1590
+ // hack for testing $http, $httpBackend
1591
+ MockXhr.$$lastInstance = this;
1592
+
1593
+ this.open = function(method, url, async) {
1594
+ this.$$method = method;
1595
+ this.$$url = url;
1596
+ this.$$async = async;
1597
+ this.$$reqHeaders = {};
1598
+ this.$$respHeaders = {};
1599
+ };
1600
+
1601
+ this.send = function(data) {
1602
+ this.$$data = data;
1603
+ };
1604
+
1605
+ this.setRequestHeader = function(key, value) {
1606
+ this.$$reqHeaders[key] = value;
1607
+ };
1608
+
1609
+ this.getResponseHeader = function(name) {
1610
+ // the lookup must be case insensitive,
1611
+ // that's why we try two quick lookups first and full scan last
1612
+ var header = this.$$respHeaders[name];
1613
+ if (header) return header;
1614
+
1615
+ name = angular.lowercase(name);
1616
+ header = this.$$respHeaders[name];
1617
+ if (header) return header;
1618
+
1619
+ header = undefined;
1620
+ angular.forEach(this.$$respHeaders, function(headerVal, headerName) {
1621
+ if (!header && angular.lowercase(headerName) == name) header = headerVal;
1622
+ });
1623
+ return header;
1624
+ };
1625
+
1626
+ this.getAllResponseHeaders = function() {
1627
+ var lines = [];
1628
+
1629
+ angular.forEach(this.$$respHeaders, function(value, key) {
1630
+ lines.push(key + ': ' + value);
1631
+ });
1632
+ return lines.join('\n');
1633
+ };
1634
+
1635
+ this.abort = angular.noop;
1636
+ }
1637
+
1638
+
1639
+ /**
1640
+ * @ngdoc service
1641
+ * @name $timeout
1642
+ * @description
1643
+ *
1644
+ * This service is just a simple decorator for {@link ng.$timeout $timeout} service
1645
+ * that adds a "flush" and "verifyNoPendingTasks" methods.
1646
+ */
1647
+
1648
+ angular.mock.$TimeoutDecorator = function($delegate, $browser) {
1649
+
1650
+ /**
1651
+ * @ngdoc method
1652
+ * @name $timeout#flush
1653
+ * @description
1654
+ *
1655
+ * Flushes the queue of pending tasks.
1656
+ *
1657
+ * @param {number=} delay maximum timeout amount to flush up until
1658
+ */
1659
+ $delegate.flush = function(delay) {
1660
+ $browser.defer.flush(delay);
1661
+ };
1662
+
1663
+ /**
1664
+ * @ngdoc method
1665
+ * @name $timeout#verifyNoPendingTasks
1666
+ * @description
1667
+ *
1668
+ * Verifies that there are no pending tasks that need to be flushed.
1669
+ */
1670
+ $delegate.verifyNoPendingTasks = function() {
1671
+ if ($browser.deferredFns.length) {
1672
+ throw new Error('Deferred tasks to flush (' + $browser.deferredFns.length + '): ' +
1673
+ formatPendingTasksAsString($browser.deferredFns));
1674
+ }
1675
+ };
1676
+
1677
+ function formatPendingTasksAsString(tasks) {
1678
+ var result = [];
1679
+ angular.forEach(tasks, function(task) {
1680
+ result.push('{id: ' + task.id + ', ' + 'time: ' + task.time + '}');
1681
+ });
1682
+
1683
+ return result.join(', ');
1684
+ }
1685
+
1686
+ return $delegate;
1687
+ };
1688
+
1689
+ angular.mock.$RAFDecorator = function($delegate) {
1690
+ var queue = [];
1691
+ var rafFn = function(fn) {
1692
+ var index = queue.length;
1693
+ queue.push(fn);
1694
+ return function() {
1695
+ queue.splice(index, 1);
1696
+ };
1697
+ };
1698
+
1699
+ rafFn.supported = $delegate.supported;
1700
+
1701
+ rafFn.flush = function() {
1702
+ if(queue.length === 0) {
1703
+ throw new Error('No rAF callbacks present');
1704
+ }
1705
+
1706
+ var length = queue.length;
1707
+ for(var i=0;i<length;i++) {
1708
+ queue[i]();
1709
+ }
1710
+
1711
+ queue = [];
1712
+ };
1713
+
1714
+ return rafFn;
1715
+ };
1716
+
1717
+ angular.mock.$AsyncCallbackDecorator = function($delegate) {
1718
+ var callbacks = [];
1719
+ var addFn = function(fn) {
1720
+ callbacks.push(fn);
1721
+ };
1722
+ addFn.flush = function() {
1723
+ angular.forEach(callbacks, function(fn) {
1724
+ fn();
1725
+ });
1726
+ callbacks = [];
1727
+ };
1728
+ return addFn;
1729
+ };
1730
+
1731
+ /**
1732
+ *
1733
+ */
1734
+ angular.mock.$RootElementProvider = function() {
1735
+ this.$get = function() {
1736
+ return angular.element('<div ng-app></div>');
1737
+ };
1738
+ };
1739
+
1740
+ /**
1741
+ * @ngdoc module
1742
+ * @name ngMock
1743
+ * @packageName angular-mocks
1744
+ * @description
1745
+ *
1746
+ * # ngMock
1747
+ *
1748
+ * The `ngMock` module provides support to inject and mock Angular services into unit tests.
1749
+ * In addition, ngMock also extends various core ng services such that they can be
1750
+ * inspected and controlled in a synchronous manner within test code.
1751
+ *
1752
+ *
1753
+ * <div doc-module-components="ngMock"></div>
1754
+ *
1755
+ */
1756
+ angular.module('ngMock', ['ng']).provider({
1757
+ $browser: angular.mock.$BrowserProvider,
1758
+ $exceptionHandler: angular.mock.$ExceptionHandlerProvider,
1759
+ $log: angular.mock.$LogProvider,
1760
+ $interval: angular.mock.$IntervalProvider,
1761
+ $httpBackend: angular.mock.$HttpBackendProvider,
1762
+ $rootElement: angular.mock.$RootElementProvider
1763
+ }).config(['$provide', function($provide) {
1764
+ $provide.decorator('$timeout', angular.mock.$TimeoutDecorator);
1765
+ $provide.decorator('$$rAF', angular.mock.$RAFDecorator);
1766
+ $provide.decorator('$$asyncCallback', angular.mock.$AsyncCallbackDecorator);
1767
+ }]);
1768
+
1769
+ /**
1770
+ * @ngdoc module
1771
+ * @name ngMockE2E
1772
+ * @module ngMockE2E
1773
+ * @packageName angular-mocks
1774
+ * @description
1775
+ *
1776
+ * The `ngMockE2E` is an angular module which contains mocks suitable for end-to-end testing.
1777
+ * Currently there is only one mock present in this module -
1778
+ * the {@link ngMockE2E.$httpBackend e2e $httpBackend} mock.
1779
+ */
1780
+ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
1781
+ $provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator);
1782
+ }]);
1783
+
1784
+ /**
1785
+ * @ngdoc service
1786
+ * @name $httpBackend
1787
+ * @module ngMockE2E
1788
+ * @description
1789
+ * Fake HTTP backend implementation suitable for end-to-end testing or backend-less development of
1790
+ * applications that use the {@link ng.$http $http service}.
1791
+ *
1792
+ * *Note*: For fake http backend implementation suitable for unit testing please see
1793
+ * {@link ngMock.$httpBackend unit-testing $httpBackend mock}.
1794
+ *
1795
+ * This implementation can be used to respond with static or dynamic responses via the `when` api
1796
+ * and its shortcuts (`whenGET`, `whenPOST`, etc) and optionally pass through requests to the
1797
+ * real $httpBackend for specific requests (e.g. to interact with certain remote apis or to fetch
1798
+ * templates from a webserver).
1799
+ *
1800
+ * As opposed to unit-testing, in an end-to-end testing scenario or in scenario when an application
1801
+ * is being developed with the real backend api replaced with a mock, it is often desirable for
1802
+ * certain category of requests to bypass the mock and issue a real http request (e.g. to fetch
1803
+ * templates or static files from the webserver). To configure the backend with this behavior
1804
+ * use the `passThrough` request handler of `when` instead of `respond`.
1805
+ *
1806
+ * Additionally, we don't want to manually have to flush mocked out requests like we do during unit
1807
+ * testing. For this reason the e2e $httpBackend flushes mocked out requests
1808
+ * automatically, closely simulating the behavior of the XMLHttpRequest object.
1809
+ *
1810
+ * To setup the application to run with this http backend, you have to create a module that depends
1811
+ * on the `ngMockE2E` and your application modules and defines the fake backend:
1812
+ *
1813
+ * ```js
1814
+ * myAppDev = angular.module('myAppDev', ['myApp', 'ngMockE2E']);
1815
+ * myAppDev.run(function($httpBackend) {
1816
+ * phones = [{name: 'phone1'}, {name: 'phone2'}];
1817
+ *
1818
+ * // returns the current list of phones
1819
+ * $httpBackend.whenGET('/phones').respond(phones);
1820
+ *
1821
+ * // adds a new phone to the phones array
1822
+ * $httpBackend.whenPOST('/phones').respond(function(method, url, data) {
1823
+ * var phone = angular.fromJson(data);
1824
+ * phones.push(phone);
1825
+ * return [200, phone, {}];
1826
+ * });
1827
+ * $httpBackend.whenGET(/^\/templates\//).passThrough();
1828
+ * //...
1829
+ * });
1830
+ * ```
1831
+ *
1832
+ * Afterwards, bootstrap your app with this new module.
1833
+ */
1834
+
1835
+ /**
1836
+ * @ngdoc method
1837
+ * @name $httpBackend#when
1838
+ * @module ngMockE2E
1839
+ * @description
1840
+ * Creates a new backend definition.
1841
+ *
1842
+ * @param {string} method HTTP method.
1843
+ * @param {string|RegExp} url HTTP url.
1844
+ * @param {(string|RegExp)=} data HTTP request body.
1845
+ * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
1846
+ * object and returns true if the headers match the current definition.
1847
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1848
+ * control how a matched request is handled.
1849
+ *
1850
+ * - respond –
1851
+ * `{function([status,] data[, headers, statusText])
1852
+ * | function(function(method, url, data, headers)}`
1853
+ * – The respond method takes a set of static data to be returned or a function that can return
1854
+ * an array containing response status (number), response data (string), response headers
1855
+ * (Object), and the text for the status (string).
1856
+ * - passThrough – `{function()}` – Any request matching a backend definition with
1857
+ * `passThrough` handler will be passed through to the real backend (an XHR request will be made
1858
+ * to the server.)
1859
+ */
1860
+
1861
+ /**
1862
+ * @ngdoc method
1863
+ * @name $httpBackend#whenGET
1864
+ * @module ngMockE2E
1865
+ * @description
1866
+ * Creates a new backend definition for GET requests. For more info see `when()`.
1867
+ *
1868
+ * @param {string|RegExp} url HTTP url.
1869
+ * @param {(Object|function(Object))=} headers HTTP headers.
1870
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1871
+ * control how a matched request is handled.
1872
+ */
1873
+
1874
+ /**
1875
+ * @ngdoc method
1876
+ * @name $httpBackend#whenHEAD
1877
+ * @module ngMockE2E
1878
+ * @description
1879
+ * Creates a new backend definition for HEAD requests. For more info see `when()`.
1880
+ *
1881
+ * @param {string|RegExp} url HTTP url.
1882
+ * @param {(Object|function(Object))=} headers HTTP headers.
1883
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1884
+ * control how a matched request is handled.
1885
+ */
1886
+
1887
+ /**
1888
+ * @ngdoc method
1889
+ * @name $httpBackend#whenDELETE
1890
+ * @module ngMockE2E
1891
+ * @description
1892
+ * Creates a new backend definition for DELETE requests. For more info see `when()`.
1893
+ *
1894
+ * @param {string|RegExp} url HTTP url.
1895
+ * @param {(Object|function(Object))=} headers HTTP headers.
1896
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1897
+ * control how a matched request is handled.
1898
+ */
1899
+
1900
+ /**
1901
+ * @ngdoc method
1902
+ * @name $httpBackend#whenPOST
1903
+ * @module ngMockE2E
1904
+ * @description
1905
+ * Creates a new backend definition for POST requests. For more info see `when()`.
1906
+ *
1907
+ * @param {string|RegExp} url HTTP url.
1908
+ * @param {(string|RegExp)=} data HTTP request body.
1909
+ * @param {(Object|function(Object))=} headers HTTP headers.
1910
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1911
+ * control how a matched request is handled.
1912
+ */
1913
+
1914
+ /**
1915
+ * @ngdoc method
1916
+ * @name $httpBackend#whenPUT
1917
+ * @module ngMockE2E
1918
+ * @description
1919
+ * Creates a new backend definition for PUT requests. For more info see `when()`.
1920
+ *
1921
+ * @param {string|RegExp} url HTTP url.
1922
+ * @param {(string|RegExp)=} data HTTP request body.
1923
+ * @param {(Object|function(Object))=} headers HTTP headers.
1924
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1925
+ * control how a matched request is handled.
1926
+ */
1927
+
1928
+ /**
1929
+ * @ngdoc method
1930
+ * @name $httpBackend#whenPATCH
1931
+ * @module ngMockE2E
1932
+ * @description
1933
+ * Creates a new backend definition for PATCH requests. For more info see `when()`.
1934
+ *
1935
+ * @param {string|RegExp} url HTTP url.
1936
+ * @param {(string|RegExp)=} data HTTP request body.
1937
+ * @param {(Object|function(Object))=} headers HTTP headers.
1938
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1939
+ * control how a matched request is handled.
1940
+ */
1941
+
1942
+ /**
1943
+ * @ngdoc method
1944
+ * @name $httpBackend#whenJSONP
1945
+ * @module ngMockE2E
1946
+ * @description
1947
+ * Creates a new backend definition for JSONP requests. For more info see `when()`.
1948
+ *
1949
+ * @param {string|RegExp} url HTTP url.
1950
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1951
+ * control how a matched request is handled.
1952
+ */
1953
+ angular.mock.e2e = {};
1954
+ angular.mock.e2e.$httpBackendDecorator =
1955
+ ['$rootScope', '$delegate', '$browser', createHttpBackendMock];
1956
+
1957
+
1958
+ angular.mock.clearDataCache = function() {
1959
+ var key,
1960
+ cache = angular.element.cache;
1961
+
1962
+ for(key in cache) {
1963
+ if (Object.prototype.hasOwnProperty.call(cache,key)) {
1964
+ var handle = cache[key].handle;
1965
+
1966
+ handle && angular.element(handle.elem).off();
1967
+ delete cache[key];
1968
+ }
1969
+ }
1970
+ };
1971
+
1972
+
1973
+ if(window.jasmine || window.mocha) {
1974
+
1975
+ var currentSpec = null,
1976
+ isSpecRunning = function() {
1977
+ return !!currentSpec;
1978
+ };
1979
+
1980
+
1981
+ (window.beforeEach || window.setup)(function() {
1982
+ currentSpec = this;
1983
+ });
1984
+
1985
+ (window.afterEach || window.teardown)(function() {
1986
+ var injector = currentSpec.$injector;
1987
+
1988
+ angular.forEach(currentSpec.$modules, function(module) {
1989
+ if (module && module.$$hashKey) {
1990
+ module.$$hashKey = undefined;
1991
+ }
1992
+ });
1993
+
1994
+ currentSpec.$injector = null;
1995
+ currentSpec.$modules = null;
1996
+ currentSpec = null;
1997
+
1998
+ if (injector) {
1999
+ injector.get('$rootElement').off();
2000
+ injector.get('$browser').pollFns.length = 0;
2001
+ }
2002
+
2003
+ angular.mock.clearDataCache();
2004
+
2005
+ // clean up jquery's fragment cache
2006
+ angular.forEach(angular.element.fragments, function(val, key) {
2007
+ delete angular.element.fragments[key];
2008
+ });
2009
+
2010
+ MockXhr.$$lastInstance = null;
2011
+
2012
+ angular.forEach(angular.callbacks, function(val, key) {
2013
+ delete angular.callbacks[key];
2014
+ });
2015
+ angular.callbacks.counter = 0;
2016
+ });
2017
+
2018
+ /**
2019
+ * @ngdoc function
2020
+ * @name angular.mock.module
2021
+ * @description
2022
+ *
2023
+ * *NOTE*: This function is also published on window for easy access.<br>
2024
+ * *NOTE*: This function is declared ONLY WHEN running tests with jasmine or mocha
2025
+ *
2026
+ * This function registers a module configuration code. It collects the configuration information
2027
+ * which will be used when the injector is created by {@link angular.mock.inject inject}.
2028
+ *
2029
+ * See {@link angular.mock.inject inject} for usage example
2030
+ *
2031
+ * @param {...(string|Function|Object)} fns any number of modules which are represented as string
2032
+ * aliases or as anonymous module initialization functions. The modules are used to
2033
+ * configure the injector. The 'ng' and 'ngMock' modules are automatically loaded. If an
2034
+ * object literal is passed they will be registered as values in the module, the key being
2035
+ * the module name and the value being what is returned.
2036
+ */
2037
+ window.module = angular.mock.module = function() {
2038
+ var moduleFns = Array.prototype.slice.call(arguments, 0);
2039
+ return isSpecRunning() ? workFn() : workFn;
2040
+ /////////////////////
2041
+ function workFn() {
2042
+ if (currentSpec.$injector) {
2043
+ throw new Error('Injector already created, can not register a module!');
2044
+ } else {
2045
+ var modules = currentSpec.$modules || (currentSpec.$modules = []);
2046
+ angular.forEach(moduleFns, function(module) {
2047
+ if (angular.isObject(module) && !angular.isArray(module)) {
2048
+ modules.push(function($provide) {
2049
+ angular.forEach(module, function(value, key) {
2050
+ $provide.value(key, value);
2051
+ });
2052
+ });
2053
+ } else {
2054
+ modules.push(module);
2055
+ }
2056
+ });
2057
+ }
2058
+ }
2059
+ };
2060
+
2061
+ /**
2062
+ * @ngdoc function
2063
+ * @name angular.mock.inject
2064
+ * @description
2065
+ *
2066
+ * *NOTE*: This function is also published on window for easy access.<br>
2067
+ * *NOTE*: This function is declared ONLY WHEN running tests with jasmine or mocha
2068
+ *
2069
+ * The inject function wraps a function into an injectable function. The inject() creates new
2070
+ * instance of {@link auto.$injector $injector} per test, which is then used for
2071
+ * resolving references.
2072
+ *
2073
+ *
2074
+ * ## Resolving References (Underscore Wrapping)
2075
+ * Often, we would like to inject a reference once, in a `beforeEach()` block and reuse this
2076
+ * in multiple `it()` clauses. To be able to do this we must assign the reference to a variable
2077
+ * that is declared in the scope of the `describe()` block. Since we would, most likely, want
2078
+ * the variable to have the same name of the reference we have a problem, since the parameter
2079
+ * to the `inject()` function would hide the outer variable.
2080
+ *
2081
+ * To help with this, the injected parameters can, optionally, be enclosed with underscores.
2082
+ * These are ignored by the injector when the reference name is resolved.
2083
+ *
2084
+ * For example, the parameter `_myService_` would be resolved as the reference `myService`.
2085
+ * Since it is available in the function body as _myService_, we can then assign it to a variable
2086
+ * defined in an outer scope.
2087
+ *
2088
+ * ```
2089
+ * // Defined out reference variable outside
2090
+ * var myService;
2091
+ *
2092
+ * // Wrap the parameter in underscores
2093
+ * beforeEach( inject( function(_myService_){
2094
+ * myService = _myService_;
2095
+ * }));
2096
+ *
2097
+ * // Use myService in a series of tests.
2098
+ * it('makes use of myService', function() {
2099
+ * myService.doStuff();
2100
+ * });
2101
+ *
2102
+ * ```
2103
+ *
2104
+ * See also {@link angular.mock.module angular.mock.module}
2105
+ *
2106
+ * ## Example
2107
+ * Example of what a typical jasmine tests looks like with the inject method.
2108
+ * ```js
2109
+ *
2110
+ * angular.module('myApplicationModule', [])
2111
+ * .value('mode', 'app')
2112
+ * .value('version', 'v1.0.1');
2113
+ *
2114
+ *
2115
+ * describe('MyApp', function() {
2116
+ *
2117
+ * // You need to load modules that you want to test,
2118
+ * // it loads only the "ng" module by default.
2119
+ * beforeEach(module('myApplicationModule'));
2120
+ *
2121
+ *
2122
+ * // inject() is used to inject arguments of all given functions
2123
+ * it('should provide a version', inject(function(mode, version) {
2124
+ * expect(version).toEqual('v1.0.1');
2125
+ * expect(mode).toEqual('app');
2126
+ * }));
2127
+ *
2128
+ *
2129
+ * // The inject and module method can also be used inside of the it or beforeEach
2130
+ * it('should override a version and test the new version is injected', function() {
2131
+ * // module() takes functions or strings (module aliases)
2132
+ * module(function($provide) {
2133
+ * $provide.value('version', 'overridden'); // override version here
2134
+ * });
2135
+ *
2136
+ * inject(function(version) {
2137
+ * expect(version).toEqual('overridden');
2138
+ * });
2139
+ * });
2140
+ * });
2141
+ *
2142
+ * ```
2143
+ *
2144
+ * @param {...Function} fns any number of functions which will be injected using the injector.
2145
+ */
2146
+
2147
+
2148
+
2149
+ var ErrorAddingDeclarationLocationStack = function(e, errorForStack) {
2150
+ this.message = e.message;
2151
+ this.name = e.name;
2152
+ if (e.line) this.line = e.line;
2153
+ if (e.sourceId) this.sourceId = e.sourceId;
2154
+ if (e.stack && errorForStack)
2155
+ this.stack = e.stack + '\n' + errorForStack.stack;
2156
+ if (e.stackArray) this.stackArray = e.stackArray;
2157
+ };
2158
+ ErrorAddingDeclarationLocationStack.prototype.toString = Error.prototype.toString;
2159
+
2160
+ window.inject = angular.mock.inject = function() {
2161
+ var blockFns = Array.prototype.slice.call(arguments, 0);
2162
+ var errorForStack = new Error('Declaration Location');
2163
+ return isSpecRunning() ? workFn.call(currentSpec) : workFn;
2164
+ /////////////////////
2165
+ function workFn() {
2166
+ var modules = currentSpec.$modules || [];
2167
+
2168
+ modules.unshift('ngMock');
2169
+ modules.unshift('ng');
2170
+ var injector = currentSpec.$injector;
2171
+ if (!injector) {
2172
+ injector = currentSpec.$injector = angular.injector(modules);
2173
+ }
2174
+ for(var i = 0, ii = blockFns.length; i < ii; i++) {
2175
+ try {
2176
+ /* jshint -W040 *//* Jasmine explicitly provides a `this` object when calling functions */
2177
+ injector.invoke(blockFns[i] || angular.noop, this);
2178
+ /* jshint +W040 */
2179
+ } catch (e) {
2180
+ if (e.stack && errorForStack) {
2181
+ throw new ErrorAddingDeclarationLocationStack(e, errorForStack);
2182
+ }
2183
+ throw e;
2184
+ } finally {
2185
+ errorForStack = null;
2186
+ }
2187
+ }
2188
+ }
2189
+ };
2190
+ }
2191
+
2192
+
2193
+ })(window, window.angular);