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