懒羊羊
2023-08-30 71e81ed1d12e4d69f53c8ad9e066650ad4186293
提交 | 用户 | 时间
71e81e 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);