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