2 * @license AngularJS v1.5.0
3 * (c) 2010-2016 Google, Inc. http://angularjs.org
6 (function(window, angular, undefined) {
15 * Namespace from 'angular-mocks.js' which contains testing related code.
20 * ! This is a private undocumented service !
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,
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.
32 angular.mock.$BrowserProvider = function() {
33 this.$get = function() {
34 return new angular.mock.$Browser();
38 angular.mock.$Browser = function() {
42 self.$$url = "http://server/";
43 self.$$lastUrl = self.$$url; // used by url polling fn
46 // TODO(vojta): remove this temporary api
47 self.$$completeOutstandingRequest = angular.noop;
48 self.$$incOutstandingRequestCount = angular.noop;
51 // register url polling fn
53 self.onUrlChange = function(listener) {
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);
67 self.$$applicationDestroyed = angular.noop;
68 self.$$checkUrlChange = angular.noop;
70 self.deferredFns = [];
71 self.deferredNextId = 0;
73 self.defer = function(fn, delay) {
75 self.deferredFns.push({time:(self.defer.now + delay), fn:fn, id: self.deferredNextId});
76 self.deferredFns.sort(function(a, b) { return a.time - b.time;});
77 return self.deferredNextId++;
82 * @name $browser#defer.now
85 * Current milliseconds mock time.
90 self.defer.cancel = function(deferId) {
93 angular.forEach(self.deferredFns, function(fn, index) {
94 if (fn.id === deferId) fnIndex = index;
97 if (angular.isDefined(fnIndex)) {
98 self.deferredFns.splice(fnIndex, 1);
107 * @name $browser#defer.flush
110 * Flushes all pending requests and executes the defer callbacks.
112 * @param {number=} number of milliseconds to flush. See {@link #defer.now}
114 self.defer.flush = function(delay) {
115 if (angular.isDefined(delay)) {
116 self.defer.now += delay;
118 if (self.deferredFns.length) {
119 self.defer.now = self.deferredFns[self.deferredFns.length - 1].time;
121 throw new Error('No deferred tasks to be flushed');
125 while (self.deferredFns.length && self.deferredFns[0].time <= self.defer.now) {
126 self.deferredFns.shift().fn();
130 self.$$baseHref = '/';
131 self.baseHref = function() {
132 return this.$$baseHref;
135 angular.mock.$Browser.prototype = {
138 * @name $browser#poll
141 * run all fns in pollFns
143 poll: function poll() {
144 angular.forEach(this.pollFns, function(pollFn) {
149 url: function(url, replace, state) {
150 if (angular.isUndefined(state)) {
155 // Native pushState serializes & copies the object; simulate it.
156 this.$$state = angular.copy(state);
167 notifyWhenNoOutstandingRequests: function(fn) {
175 * @name $exceptionHandlerProvider
178 * Configures the mock implementation of {@link ng.$exceptionHandler} to rethrow or to log errors
179 * passed to the `$exceptionHandler`.
184 * @name $exceptionHandler
187 * Mock implementation of {@link ng.$exceptionHandler} that rethrows or logs errors passed
188 * to it. See {@link ngMock.$exceptionHandlerProvider $exceptionHandlerProvider} for configuration
193 * describe('$exceptionHandlerProvider', function() {
195 * it('should capture log messages and exceptions', function() {
197 * module(function($exceptionHandlerProvider) {
198 * $exceptionHandlerProvider.mode('log');
201 * inject(function($log, $exceptionHandler, $timeout) {
202 * $timeout(function() { $log.log(1); });
203 * $timeout(function() { $log.log(2); throw 'banana peel'; });
204 * $timeout(function() { $log.log(3); });
205 * expect($exceptionHandler.errors).toEqual([]);
206 * expect($log.assertEmpty());
208 * expect($exceptionHandler.errors).toEqual(['banana peel']);
209 * expect($log.log.logs).toEqual([[1], [2], [3]]);
216 angular.mock.$ExceptionHandlerProvider = function() {
221 * @name $exceptionHandlerProvider#mode
224 * Sets the logging mode.
226 * @param {string} mode Mode of operation, defaults to `rethrow`.
228 * - `log`: Sometimes it is desirable to test that an error is thrown, for this case the `log`
229 * mode stores an array of errors in `$exceptionHandler.errors`, to allow later
230 * assertion of them. See {@link ngMock.$log#assertEmpty assertEmpty()} and
231 * {@link ngMock.$log#reset reset()}
232 * - `rethrow`: If any errors are passed to the handler in tests, it typically means that there
233 * is a bug in the application or test, so this mock will make these tests fail.
234 * For any implementations that expect exceptions to be thrown, the `rethrow` mode
235 * will also maintain a log of thrown errors.
237 this.mode = function(mode) {
243 handler = function(e) {
244 if (arguments.length == 1) {
247 errors.push([].slice.call(arguments, 0));
249 if (mode === "rethrow") {
253 handler.errors = errors;
256 throw new Error("Unknown mode '" + mode + "', only 'log'/'rethrow' modes are allowed!");
260 this.$get = function() {
264 this.mode('rethrow');
273 * Mock implementation of {@link ng.$log} that gathers all logged messages in arrays
274 * (one array per logging level). These arrays are exposed as `logs` property of each of the
275 * level-specific log function, e.g. for level `error` the array is exposed as `$log.error.logs`.
278 angular.mock.$LogProvider = function() {
281 function concat(array1, array2, index) {
282 return array1.concat(Array.prototype.slice.call(array2, index));
285 this.debugEnabled = function(flag) {
286 if (angular.isDefined(flag)) {
294 this.$get = function() {
296 log: function() { $log.log.logs.push(concat([], arguments, 0)); },
297 warn: function() { $log.warn.logs.push(concat([], arguments, 0)); },
298 info: function() { $log.info.logs.push(concat([], arguments, 0)); },
299 error: function() { $log.error.logs.push(concat([], arguments, 0)); },
302 $log.debug.logs.push(concat([], arguments, 0));
312 * Reset all of the logging arrays to empty.
314 $log.reset = function() {
317 * @name $log#log.logs
320 * Array of messages logged using {@link ng.$log#log `log()`}.
324 * $log.log('Some Log');
325 * var first = $log.log.logs.unshift();
331 * @name $log#info.logs
334 * Array of messages logged using {@link ng.$log#info `info()`}.
338 * $log.info('Some Info');
339 * var first = $log.info.logs.unshift();
345 * @name $log#warn.logs
348 * Array of messages logged using {@link ng.$log#warn `warn()`}.
352 * $log.warn('Some Warning');
353 * var first = $log.warn.logs.unshift();
359 * @name $log#error.logs
362 * Array of messages logged using {@link ng.$log#error `error()`}.
366 * $log.error('Some Error');
367 * var first = $log.error.logs.unshift();
370 $log.error.logs = [];
373 * @name $log#debug.logs
376 * Array of messages logged using {@link ng.$log#debug `debug()`}.
380 * $log.debug('Some Error');
381 * var first = $log.debug.logs.unshift();
384 $log.debug.logs = [];
389 * @name $log#assertEmpty
392 * Assert that all of the logging methods have no logged messages. If any messages are present,
393 * an exception is thrown.
395 $log.assertEmpty = function() {
397 angular.forEach(['error', 'warn', 'info', 'log', 'debug'], function(logLevel) {
398 angular.forEach($log[logLevel].logs, function(log) {
399 angular.forEach(log, function(logItem) {
400 errors.push('MOCK $log (' + logLevel + '): ' + String(logItem) + '\n' +
401 (logItem.stack || ''));
406 errors.unshift("Expected $log to be empty! Either a message was logged unexpectedly, or " +
407 "an expected log message was not checked and removed:");
409 throw new Error(errors.join('\n---------\n'));
424 * Mock implementation of the $interval service.
426 * Use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
427 * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
430 * @param {function()} fn A function that should be called repeatedly.
431 * @param {number} delay Number of milliseconds between each function call.
432 * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
434 * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
435 * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
436 * @param {...*=} Pass additional parameters to the executed function.
437 * @returns {promise} A promise which will be notified on each iteration.
439 angular.mock.$IntervalProvider = function() {
440 this.$get = ['$browser', '$rootScope', '$q', '$$q',
441 function($browser, $rootScope, $q, $$q) {
446 var $interval = function(fn, delay, count, invokeApply) {
447 var hasParams = arguments.length > 4,
448 args = hasParams ? Array.prototype.slice.call(arguments, 4) : [],
450 skipApply = (angular.isDefined(invokeApply) && !invokeApply),
451 deferred = (skipApply ? $$q : $q).defer(),
452 promise = deferred.promise;
454 count = (angular.isDefined(count)) ? count : 0;
455 promise.then(null, null, (!hasParams) ? fn : function() {
456 fn.apply(null, args);
459 promise.$$intervalId = nextRepeatId;
462 deferred.notify(iteration++);
464 if (count > 0 && iteration >= count) {
466 deferred.resolve(iteration);
468 angular.forEach(repeatFns, function(fn, index) {
469 if (fn.id === promise.$$intervalId) fnIndex = index;
472 if (angular.isDefined(fnIndex)) {
473 repeatFns.splice(fnIndex, 1);
478 $browser.defer.flush();
485 nextTime:(now + delay),
491 repeatFns.sort(function(a, b) { return a.nextTime - b.nextTime;});
498 * @name $interval#cancel
501 * Cancels a task associated with the `promise`.
503 * @param {promise} promise A promise from calling the `$interval` function.
504 * @returns {boolean} Returns `true` if the task was successfully cancelled.
506 $interval.cancel = function(promise) {
507 if (!promise) return false;
510 angular.forEach(repeatFns, function(fn, index) {
511 if (fn.id === promise.$$intervalId) fnIndex = index;
514 if (angular.isDefined(fnIndex)) {
515 repeatFns[fnIndex].deferred.reject('canceled');
516 repeatFns.splice(fnIndex, 1);
525 * @name $interval#flush
528 * Runs interval tasks scheduled to be run in the next `millis` milliseconds.
530 * @param {number=} millis maximum timeout amount to flush up until.
532 * @return {number} The amount of time moved forward.
534 $interval.flush = function(millis) {
536 while (repeatFns.length && repeatFns[0].nextTime <= now) {
537 var task = repeatFns[0];
539 task.nextTime += task.delay;
540 repeatFns.sort(function(a, b) { return a.nextTime - b.nextTime;});
551 /* The R_ISO8061_STR regex is never going to fit into the 100 char limit!
552 * This directive should go inside the anonymous function but a bug in JSHint means that it would
553 * not be enacted early enough to prevent the warning.
555 var R_ISO8061_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?:\:?(\d\d)(?:\:?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/;
557 function jsonStringToDate(string) {
559 if (match = string.match(R_ISO8061_STR)) {
560 var date = new Date(0),
564 tzHour = toInt(match[9] + match[10]);
565 tzMin = toInt(match[9] + match[11]);
567 date.setUTCFullYear(toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
568 date.setUTCHours(toInt(match[4] || 0) - tzHour,
569 toInt(match[5] || 0) - tzMin,
570 toInt(match[6] || 0),
571 toInt(match[7] || 0));
577 function toInt(str) {
578 return parseInt(str, 10);
581 function padNumber(num, digits, trim) {
588 while (num.length < digits) num = '0' + num;
590 num = num.substr(num.length - digits);
598 * @name angular.mock.TzDate
601 * *NOTE*: this is not an injectable instance, just a globally available mock class of `Date`.
603 * Mock of the Date type which has its timezone specified via constructor arg.
605 * The main purpose is to create Date-like instances with timezone fixed to the specified timezone
606 * offset, so that we can test code that depends on local timezone settings without dependency on
607 * the time zone settings of the machine where the code is running.
609 * @param {number} offset Offset of the *desired* timezone in hours (fractions will be honored)
610 * @param {(number|string)} timestamp Timestamp representing the desired time in *UTC*
614 * This is not a complete Date object so only methods that were implemented can be called safely.
615 * To make matters worse, TzDate instances inherit stuff from Date via a prototype.
617 * We do our best to intercept calls to "unimplemented" methods, but since the list of methods is
618 * incomplete we might be missing some non-standard methods. This can result in errors like:
619 * "Date.prototype.foo called on incompatible Object".
622 * var newYearInBratislava = new TzDate(-1, '2009-12-31T23:00:00Z');
623 * newYearInBratislava.getTimezoneOffset() => -60;
624 * newYearInBratislava.getFullYear() => 2010;
625 * newYearInBratislava.getMonth() => 0;
626 * newYearInBratislava.getDate() => 1;
627 * newYearInBratislava.getHours() => 0;
628 * newYearInBratislava.getMinutes() => 0;
629 * newYearInBratislava.getSeconds() => 0;
633 angular.mock.TzDate = function(offset, timestamp) {
634 var self = new Date(0);
635 if (angular.isString(timestamp)) {
636 var tsStr = timestamp;
638 self.origDate = jsonStringToDate(timestamp);
640 timestamp = self.origDate.getTime();
641 if (isNaN(timestamp)) {
643 name: "Illegal Argument",
644 message: "Arg '" + tsStr + "' passed into TzDate constructor is not a valid date string"
648 self.origDate = new Date(timestamp);
651 var localOffset = new Date(timestamp).getTimezoneOffset();
652 self.offsetDiff = localOffset * 60 * 1000 - offset * 1000 * 60 * 60;
653 self.date = new Date(timestamp + self.offsetDiff);
655 self.getTime = function() {
656 return self.date.getTime() - self.offsetDiff;
659 self.toLocaleDateString = function() {
660 return self.date.toLocaleDateString();
663 self.getFullYear = function() {
664 return self.date.getFullYear();
667 self.getMonth = function() {
668 return self.date.getMonth();
671 self.getDate = function() {
672 return self.date.getDate();
675 self.getHours = function() {
676 return self.date.getHours();
679 self.getMinutes = function() {
680 return self.date.getMinutes();
683 self.getSeconds = function() {
684 return self.date.getSeconds();
687 self.getMilliseconds = function() {
688 return self.date.getMilliseconds();
691 self.getTimezoneOffset = function() {
695 self.getUTCFullYear = function() {
696 return self.origDate.getUTCFullYear();
699 self.getUTCMonth = function() {
700 return self.origDate.getUTCMonth();
703 self.getUTCDate = function() {
704 return self.origDate.getUTCDate();
707 self.getUTCHours = function() {
708 return self.origDate.getUTCHours();
711 self.getUTCMinutes = function() {
712 return self.origDate.getUTCMinutes();
715 self.getUTCSeconds = function() {
716 return self.origDate.getUTCSeconds();
719 self.getUTCMilliseconds = function() {
720 return self.origDate.getUTCMilliseconds();
723 self.getDay = function() {
724 return self.date.getDay();
727 // provide this method only on browsers that already have it
728 if (self.toISOString) {
729 self.toISOString = function() {
730 return padNumber(self.origDate.getUTCFullYear(), 4) + '-' +
731 padNumber(self.origDate.getUTCMonth() + 1, 2) + '-' +
732 padNumber(self.origDate.getUTCDate(), 2) + 'T' +
733 padNumber(self.origDate.getUTCHours(), 2) + ':' +
734 padNumber(self.origDate.getUTCMinutes(), 2) + ':' +
735 padNumber(self.origDate.getUTCSeconds(), 2) + '.' +
736 padNumber(self.origDate.getUTCMilliseconds(), 3) + 'Z';
740 //hide all methods not implemented in this mock that the Date prototype exposes
741 var unimplementedMethods = ['getUTCDay',
742 'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds',
743 'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear',
744 'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds',
745 'setYear', 'toDateString', 'toGMTString', 'toJSON', 'toLocaleFormat', 'toLocaleString',
746 'toLocaleTimeString', 'toSource', 'toString', 'toTimeString', 'toUTCString', 'valueOf'];
748 angular.forEach(unimplementedMethods, function(methodName) {
749 self[methodName] = function() {
750 throw new Error("Method '" + methodName + "' is not implemented in the TzDate mock");
757 //make "tzDateInstance instanceof Date" return true
758 angular.mock.TzDate.prototype = Date.prototype;
767 * Mock implementation of the {@link ng.$animate `$animate`} service. Exposes two additional methods
768 * for testing animations.
770 angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
772 .config(['$provide', function($provide) {
774 $provide.factory('$$forceReflow', function() {
775 function reflowFn() {
776 reflowFn.totalReflows++;
778 reflowFn.totalReflows = 0;
782 $provide.factory('$$animateAsyncRun', function() {
784 var queueFn = function() {
785 return function(fn) {
789 queueFn.flush = function() {
790 if (queue.length === 0) return false;
792 for (var i = 0; i < queue.length; i++) {
802 $provide.decorator('$$animateJs', ['$delegate', function($delegate) {
805 var animateJsConstructor = function() {
806 var animator = $delegate.apply($delegate, arguments);
807 // If no javascript animation is found, animator is undefined
809 runners.push(animator);
814 animateJsConstructor.$closeAndFlush = function() {
815 runners.forEach(function(runner) {
821 return animateJsConstructor;
824 $provide.decorator('$animateCss', ['$delegate', function($delegate) {
827 var animateCssConstructor = function(element, options) {
828 var animator = $delegate(element, options);
829 runners.push(animator);
833 animateCssConstructor.$closeAndFlush = function() {
834 runners.forEach(function(runner) {
840 return animateCssConstructor;
843 $provide.decorator('$animate', ['$delegate', '$timeout', '$browser', '$$rAF', '$animateCss', '$$animateJs',
844 '$$forceReflow', '$$animateAsyncRun', '$rootScope',
845 function($delegate, $timeout, $browser, $$rAF, $animateCss, $$animateJs,
846 $$forceReflow, $$animateAsyncRun, $rootScope) {
849 cancel: $delegate.cancel,
854 return $$forceReflow.totalReflows;
856 enabled: $delegate.enabled,
859 * @name $animate#closeAndFlush
862 * This method will close all pending animations (both {@link ngAnimate#javascript-based-animations Javascript}
863 * and {@link ngAnimate.$animateCss CSS}) and it will also flush any remaining animation frames and/or callbacks.
865 closeAndFlush: function() {
866 // we allow the flush command to swallow the errors
867 // because depending on whether CSS or JS animations are
868 // used, there may not be a RAF flush. The primary flush
869 // at the end of this function must throw an exception
870 // because it will track if there were pending animations
872 $animateCss.$closeAndFlush();
873 $$animateJs.$closeAndFlush();
878 * @name $animate#flush
881 * This method is used to flush the pending callbacks and animation frames to either start
882 * an animation or conclude an animation. Note that this will not actually close an
883 * actively running animation (see {@link ngMock.$animate#closeAndFlush `closeAndFlush()`} for that).
885 flush: function(hideErrors) {
886 $rootScope.$digest();
888 var doNextRun, somethingFlushed = false;
892 if ($$rAF.queue.length) {
894 doNextRun = somethingFlushed = true;
897 if ($$animateAsyncRun.flush()) {
898 doNextRun = somethingFlushed = true;
902 if (!somethingFlushed && !hideErrors) {
903 throw new Error('No pending animations ready to be closed or flushed');
906 $rootScope.$digest();
911 ['animate','enter','leave','move','addClass','removeClass','setClass'], function(method) {
912 animate[method] = function() {
915 element: arguments[0],
916 options: arguments[arguments.length - 1],
919 return $delegate[method].apply($delegate, arguments);
931 * @name angular.mock.dump
934 * *NOTE*: this is not an injectable instance, just a globally available function.
936 * Method for serializing common angular objects (scope, elements, etc..) into strings, useful for
939 * This method is also available on window, where it can be used to display objects on debug
942 * @param {*} object - any object to turn into string.
943 * @return {string} a serialized string of the argument
945 angular.mock.dump = function(object) {
946 return serialize(object);
948 function serialize(object) {
951 if (angular.isElement(object)) {
952 object = angular.element(object);
953 out = angular.element('<div></div>');
954 angular.forEach(object, function(element) {
955 out.append(angular.element(element).clone());
958 } else if (angular.isArray(object)) {
960 angular.forEach(object, function(o) {
961 out.push(serialize(o));
963 out = '[ ' + out.join(', ') + ' ]';
964 } else if (angular.isObject(object)) {
965 if (angular.isFunction(object.$eval) && angular.isFunction(object.$apply)) {
966 out = serializeScope(object);
967 } else if (object instanceof Error) {
968 out = object.stack || ('' + object.name + ': ' + object.message);
970 // TODO(i): this prevents methods being logged,
971 // we should have a better way to serialize objects
972 out = angular.toJson(object, true);
975 out = String(object);
981 function serializeScope(scope, offset) {
982 offset = offset || ' ';
983 var log = [offset + 'Scope(' + scope.$id + '): {'];
984 for (var key in scope) {
985 if (Object.prototype.hasOwnProperty.call(scope, key) && !key.match(/^(\$|this)/)) {
986 log.push(' ' + key + ': ' + angular.toJson(scope[key]));
989 var child = scope.$$childHead;
991 log.push(serializeScope(child, offset + ' '));
992 child = child.$$nextSibling;
995 return log.join('\n' + offset);
1001 * @name $httpBackend
1003 * Fake HTTP backend implementation suitable for unit testing applications that use the
1004 * {@link ng.$http $http service}.
1006 * *Note*: For fake HTTP backend implementation suitable for end-to-end testing or backend-less
1007 * development please see {@link ngMockE2E.$httpBackend e2e $httpBackend mock}.
1009 * During unit testing, we want our unit tests to run quickly and have no external dependencies so
1010 * we don’t want to send [XHR](https://developer.mozilla.org/en/xmlhttprequest) or
1011 * [JSONP](http://en.wikipedia.org/wiki/JSONP) requests to a real server. All we really need is
1012 * to verify whether a certain request has been sent or not, or alternatively just let the
1013 * application make requests, respond with pre-trained responses and assert that the end result is
1014 * what we expect it to be.
1016 * This mock implementation can be used to respond with static or dynamic responses via the
1017 * `expect` and `when` apis and their shortcuts (`expectGET`, `whenPOST`, etc).
1019 * When an Angular application needs some data from a server, it calls the $http service, which
1020 * sends the request to a real server using $httpBackend service. With dependency injection, it is
1021 * easy to inject $httpBackend mock (which has the same API as $httpBackend) and use it to verify
1022 * the requests and respond with some testing data without sending a request to a real server.
1024 * There are two ways to specify what test data should be returned as http responses by the mock
1025 * backend when the code under test makes http requests:
1027 * - `$httpBackend.expect` - specifies a request expectation
1028 * - `$httpBackend.when` - specifies a backend definition
1031 * ## Request Expectations vs Backend Definitions
1033 * Request expectations provide a way to make assertions about requests made by the application and
1034 * to define responses for those requests. The test will fail if the expected requests are not made
1035 * or they are made in the wrong order.
1037 * Backend definitions allow you to define a fake backend for your application which doesn't assert
1038 * if a particular request was made or not, it just returns a trained response if a request is made.
1039 * The test will pass whether or not the request gets made during testing.
1042 * <table class="table">
1043 * <tr><th width="220px"></th><th>Request expectations</th><th>Backend definitions</th></tr>
1046 * <td>.expect(...).respond(...)</td>
1047 * <td>.when(...).respond(...)</td>
1050 * <th>Typical usage</th>
1051 * <td>strict unit tests</td>
1052 * <td>loose (black-box) unit testing</td>
1055 * <th>Fulfills multiple requests</th>
1060 * <th>Order of requests matters</th>
1065 * <th>Request required</th>
1070 * <th>Response required</th>
1071 * <td>optional (see below)</td>
1076 * In cases where both backend definitions and request expectations are specified during unit
1077 * testing, the request expectations are evaluated first.
1079 * If a request expectation has no response specified, the algorithm will search your backend
1080 * definitions for an appropriate response.
1082 * If a request didn't match any expectation or if the expectation doesn't have the response
1083 * defined, the backend definitions are evaluated in sequential order to see if any of them match
1084 * the request. The response from the first matched definition is returned.
1087 * ## Flushing HTTP requests
1089 * The $httpBackend used in production always responds to requests asynchronously. If we preserved
1090 * this behavior in unit testing, we'd have to create async unit tests, which are hard to write,
1091 * to follow and to maintain. But neither can the testing mock respond synchronously; that would
1092 * change the execution of the code under test. For this reason, the mock $httpBackend has a
1093 * `flush()` method, which allows the test to explicitly flush pending requests. This preserves
1094 * the async api of the backend, while allowing the test to execute synchronously.
1097 * ## Unit testing with mock $httpBackend
1098 * The following code shows how to setup and use the mock backend when unit testing a controller.
1099 * First we create the controller under test:
1104 .module('MyApp', [])
1105 .controller('MyController', MyController);
1107 // The controller code
1108 function MyController($scope, $http) {
1111 $http.get('/auth.py').then(function(response) {
1112 authToken = response.headers('A-Token');
1113 $scope.user = response.data;
1116 $scope.saveMessage = function(message) {
1117 var headers = { 'Authorization': authToken };
1118 $scope.status = 'Saving...';
1120 $http.post('/add-msg.py', message, { headers: headers } ).then(function(response) {
1122 }).catch(function() {
1123 $scope.status = 'Failed...';
1129 * Now we setup the mock backend and create the test specs:
1132 // testing controller
1133 describe('MyController', function() {
1134 var $httpBackend, $rootScope, createController, authRequestHandler;
1136 // Set up the module
1137 beforeEach(module('MyApp'));
1139 beforeEach(inject(function($injector) {
1140 // Set up the mock http service responses
1141 $httpBackend = $injector.get('$httpBackend');
1142 // backend definition common for all tests
1143 authRequestHandler = $httpBackend.when('GET', '/auth.py')
1144 .respond({userId: 'userX'}, {'A-Token': 'xxx'});
1146 // Get hold of a scope (i.e. the root scope)
1147 $rootScope = $injector.get('$rootScope');
1148 // The $controller service is used to create instances of controllers
1149 var $controller = $injector.get('$controller');
1151 createController = function() {
1152 return $controller('MyController', {'$scope' : $rootScope });
1157 afterEach(function() {
1158 $httpBackend.verifyNoOutstandingExpectation();
1159 $httpBackend.verifyNoOutstandingRequest();
1163 it('should fetch authentication token', function() {
1164 $httpBackend.expectGET('/auth.py');
1165 var controller = createController();
1166 $httpBackend.flush();
1170 it('should fail authentication', function() {
1172 // Notice how you can change the response even after it was set
1173 authRequestHandler.respond(401, '');
1175 $httpBackend.expectGET('/auth.py');
1176 var controller = createController();
1177 $httpBackend.flush();
1178 expect($rootScope.status).toBe('Failed...');
1182 it('should send msg to server', function() {
1183 var controller = createController();
1184 $httpBackend.flush();
1186 // now you don’t care about the authentication, but
1187 // the controller will still send the request and
1188 // $httpBackend will respond without you having to
1189 // specify the expectation and response for this request
1191 $httpBackend.expectPOST('/add-msg.py', 'message content').respond(201, '');
1192 $rootScope.saveMessage('message content');
1193 expect($rootScope.status).toBe('Saving...');
1194 $httpBackend.flush();
1195 expect($rootScope.status).toBe('');
1199 it('should send auth header', function() {
1200 var controller = createController();
1201 $httpBackend.flush();
1203 $httpBackend.expectPOST('/add-msg.py', undefined, function(headers) {
1204 // check if the header was sent, if it wasn't the expectation won't
1205 // match the request and the test will fail
1206 return headers['Authorization'] == 'xxx';
1207 }).respond(201, '');
1209 $rootScope.saveMessage('whatever');
1210 $httpBackend.flush();
1215 * ## Dynamic responses
1217 * You define a response to a request by chaining a call to `respond()` onto a definition or expectation.
1218 * If you provide a **callback** as the first parameter to `respond(callback)` then you can dynamically generate
1219 * a response based on the properties of the request.
1221 * The `callback` function should be of the form `function(method, url, data, headers, params)`.
1223 * ### Query parameters
1225 * By default, query parameters on request URLs are parsed into the `params` object. So a request URL
1226 * of `/list?q=searchstr&orderby=-name` would set `params` to be `{q: 'searchstr', orderby: '-name'}`.
1228 * ### Regex parameter matching
1230 * If an expectation or definition uses a **regex** to match the URL, you can provide an array of **keys** via a
1231 * `params` argument. The index of each **key** in the array will match the index of a **group** in the
1234 * The `params` object in the **callback** will now have properties with these keys, which hold the value of the
1235 * corresponding **group** in the **regex**.
1237 * This also applies to the `when` and `expect` shortcut methods.
1241 * $httpBackend.expect('GET', /\/user\/(.+)/, undefined, undefined, ['id'])
1242 * .respond(function(method, url, data, headers, params) {
1243 * // for requested url of '/user/1234' params is {id: '1234'}
1246 * $httpBackend.whenPATCH(/\/user\/(.+)\/article\/(.+)/, undefined, undefined, ['user', 'article'])
1247 * .respond(function(method, url, data, headers, params) {
1248 * // for url of '/user/1234/article/567' params is {user: '1234', article: '567'}
1252 * ## Matching route requests
1254 * For extra convenience, `whenRoute` and `expectRoute` shortcuts are available. These methods offer colon
1255 * delimited matching of the url path, ignoring the query string. This allows declarations
1256 * similar to how application routes are configured with `$routeProvider`. Because these methods convert
1257 * the definition url to regex, declaration order is important. Combined with query parameter parsing,
1258 * the following is possible:
1261 $httpBackend.whenRoute('GET', '/users/:id')
1262 .respond(function(method, url, data, headers, params) {
1263 return [200, MockUserList[Number(params.id)]];
1266 $httpBackend.whenRoute('GET', '/users')
1267 .respond(function(method, url, data, headers, params) {
1268 var userList = angular.copy(MockUserList),
1269 defaultSort = 'lastName',
1270 count, pages, isPrevious, isNext;
1272 // paged api response '/v1/users?page=2'
1273 params.page = Number(params.page) || 1;
1275 // query for last names '/v1/users?q=Archer'
1277 userList = $filter('filter')({lastName: params.q});
1280 pages = Math.ceil(userList.length / pagingLength);
1281 isPrevious = params.page > 1;
1282 isNext = params.page < pages;
1285 count: userList.length,
1286 previous: isPrevious,
1288 // sort field -> '/v1/users?sortBy=firstName'
1289 results: $filter('orderBy')(userList, params.sortBy || defaultSort)
1290 .splice((params.page - 1) * pagingLength, pagingLength)
1295 angular.mock.$HttpBackendProvider = function() {
1296 this.$get = ['$rootScope', '$timeout', createHttpBackendMock];
1300 * General factory function for $httpBackend mock.
1301 * Returns instance for unit testing (when no arguments specified):
1302 * - passing through is disabled
1303 * - auto flushing is disabled
1305 * Returns instance for e2e testing (when `$delegate` and `$browser` specified):
1306 * - passing through (delegating request to real backend) is enabled
1307 * - auto flushing is enabled
1309 * @param {Object=} $delegate Real $httpBackend instance (allow passing through if specified)
1310 * @param {Object=} $browser Auto-flushing enabled if specified
1311 * @return {Object} Instance of $httpBackend mock
1313 function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
1314 var definitions = [],
1317 responsesPush = angular.bind(responses, responses.push),
1318 copy = angular.copy;
1320 function createResponse(status, data, headers, statusText) {
1321 if (angular.isFunction(status)) return status;
1324 return angular.isNumber(status)
1325 ? [status, data, headers, statusText]
1326 : [200, status, data, headers];
1330 // TODO(vojta): change params to: method, url, data, headers, callback
1331 function $httpBackend(method, url, data, callback, headers, timeout, withCredentials) {
1332 var xhr = new MockXhr(),
1333 expectation = expectations[0],
1334 wasExpected = false;
1336 function prettyPrint(data) {
1337 return (angular.isString(data) || angular.isFunction(data) || data instanceof RegExp)
1339 : angular.toJson(data);
1342 function wrapResponse(wrapped) {
1343 if (!$browser && timeout) {
1344 timeout.then ? timeout.then(handleTimeout) : $timeout(handleTimeout, timeout);
1347 return handleResponse;
1349 function handleResponse() {
1350 var response = wrapped.response(method, url, data, headers, wrapped.params(url));
1351 xhr.$$respHeaders = response[2];
1352 callback(copy(response[0]), copy(response[1]), xhr.getAllResponseHeaders(),
1353 copy(response[3] || ''));
1356 function handleTimeout() {
1357 for (var i = 0, ii = responses.length; i < ii; i++) {
1358 if (responses[i] === handleResponse) {
1359 responses.splice(i, 1);
1360 callback(-1, undefined, '');
1367 if (expectation && expectation.match(method, url)) {
1368 if (!expectation.matchData(data)) {
1369 throw new Error('Expected ' + expectation + ' with different data\n' +
1370 'EXPECTED: ' + prettyPrint(expectation.data) + '\nGOT: ' + data);
1373 if (!expectation.matchHeaders(headers)) {
1374 throw new Error('Expected ' + expectation + ' with different headers\n' +
1375 'EXPECTED: ' + prettyPrint(expectation.headers) + '\nGOT: ' +
1376 prettyPrint(headers));
1379 expectations.shift();
1381 if (expectation.response) {
1382 responses.push(wrapResponse(expectation));
1388 var i = -1, definition;
1389 while ((definition = definitions[++i])) {
1390 if (definition.match(method, url, data, headers || {})) {
1391 if (definition.response) {
1392 // if $browser specified, we do auto flush all requests
1393 ($browser ? $browser.defer : responsesPush)(wrapResponse(definition));
1394 } else if (definition.passThrough) {
1395 $delegate(method, url, data, callback, headers, timeout, withCredentials);
1396 } else throw new Error('No response defined !');
1401 new Error('No response defined !') :
1402 new Error('Unexpected request: ' + method + ' ' + url + '\n' +
1403 (expectation ? 'Expected ' + expectation : 'No more request expected'));
1408 * @name $httpBackend#when
1410 * Creates a new backend definition.
1412 * @param {string} method HTTP method.
1413 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1414 * and returns true if the url matches the current definition.
1415 * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
1416 * data string and returns true if the data is as expected.
1417 * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
1418 * object and returns true if the headers match the current definition.
1419 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1420 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1421 * request is handled. You can save this object for later use and invoke `respond` again in
1422 * order to change how a matched request is handled.
1425 * `{function([status,] data[, headers, statusText])
1426 * | function(function(method, url, data, headers, params)}`
1427 * – The respond method takes a set of static data to be returned or a function that can
1428 * return an array containing response status (number), response data (string), response
1429 * headers (Object), and the text for the status (string). The respond method returns the
1430 * `requestHandler` object for possible overrides.
1432 $httpBackend.when = function(method, url, data, headers, keys) {
1433 var definition = new MockHttpExpectation(method, url, data, headers, keys),
1435 respond: function(status, data, headers, statusText) {
1436 definition.passThrough = undefined;
1437 definition.response = createResponse(status, data, headers, statusText);
1443 chain.passThrough = function() {
1444 definition.response = undefined;
1445 definition.passThrough = true;
1450 definitions.push(definition);
1456 * @name $httpBackend#whenGET
1458 * Creates a new backend definition for GET requests. For more info see `when()`.
1460 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1461 * and returns true if the url matches the current definition.
1462 * @param {(Object|function(Object))=} headers HTTP headers.
1463 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1464 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1465 * request is handled. You can save this object for later use and invoke `respond` again in
1466 * order to change how a matched request is handled.
1471 * @name $httpBackend#whenHEAD
1473 * Creates a new backend definition for HEAD requests. For more info see `when()`.
1475 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1476 * and returns true if the url matches the current definition.
1477 * @param {(Object|function(Object))=} headers HTTP headers.
1478 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1479 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1480 * request is handled. You can save this object for later use and invoke `respond` again in
1481 * order to change how a matched request is handled.
1486 * @name $httpBackend#whenDELETE
1488 * Creates a new backend definition for DELETE requests. For more info see `when()`.
1490 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1491 * and returns true if the url matches the current definition.
1492 * @param {(Object|function(Object))=} headers HTTP headers.
1493 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1494 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1495 * request is handled. You can save this object for later use and invoke `respond` again in
1496 * order to change how a matched request is handled.
1501 * @name $httpBackend#whenPOST
1503 * Creates a new backend definition for POST requests. For more info see `when()`.
1505 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1506 * and returns true if the url matches the current definition.
1507 * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
1508 * data string and returns true if the data is as expected.
1509 * @param {(Object|function(Object))=} headers HTTP headers.
1510 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1511 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1512 * request is handled. You can save this object for later use and invoke `respond` again in
1513 * order to change how a matched request is handled.
1518 * @name $httpBackend#whenPUT
1520 * Creates a new backend definition for PUT requests. For more info see `when()`.
1522 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1523 * and returns true if the url matches the current definition.
1524 * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
1525 * data string and returns true if the data is as expected.
1526 * @param {(Object|function(Object))=} headers HTTP headers.
1527 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1528 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1529 * request is handled. You can save this object for later use and invoke `respond` again in
1530 * order to change how a matched request is handled.
1535 * @name $httpBackend#whenJSONP
1537 * Creates a new backend definition for JSONP requests. For more info see `when()`.
1539 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1540 * and returns true if the url matches the current definition.
1541 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1542 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1543 * request is handled. You can save this object for later use and invoke `respond` again in
1544 * order to change how a matched request is handled.
1546 createShortMethods('when');
1550 * @name $httpBackend#whenRoute
1552 * Creates a new backend definition that compares only with the requested route.
1554 * @param {string} method HTTP method.
1555 * @param {string} url HTTP url string that supports colon param matching.
1556 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1557 * request is handled. You can save this object for later use and invoke `respond` again in
1558 * order to change how a matched request is handled. See #when for more info.
1560 $httpBackend.whenRoute = function(method, url) {
1561 var pathObj = parseRoute(url);
1562 return $httpBackend.when(method, pathObj.regexp, undefined, undefined, pathObj.keys);
1565 function parseRoute(url) {
1569 keys = ret.keys = [];
1571 if (!url || !angular.isString(url)) return ret;
1574 .replace(/([().])/g, '\\$1')
1575 .replace(/(\/)?:(\w+)([\?\*])?/g, function(_, slash, key, option) {
1576 var optional = option === '?' ? option : null;
1577 var star = option === '*' ? option : null;
1578 keys.push({ name: key, optional: !!optional });
1579 slash = slash || '';
1581 + (optional ? '' : slash)
1583 + (optional ? slash : '')
1584 + (star && '(.+?)' || '([^/]+)')
1589 .replace(/([\/$\*])/g, '\\$1');
1591 ret.regexp = new RegExp('^' + url, 'i');
1597 * @name $httpBackend#expect
1599 * Creates a new request expectation.
1601 * @param {string} method HTTP method.
1602 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1603 * and returns true if the url matches the current definition.
1604 * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
1605 * receives data string and returns true if the data is as expected, or Object if request body
1606 * is in JSON format.
1607 * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
1608 * object and returns true if the headers match the current expectation.
1609 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1610 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1611 * request is handled. You can save this object for later use and invoke `respond` again in
1612 * order to change how a matched request is handled.
1615 * `{function([status,] data[, headers, statusText])
1616 * | function(function(method, url, data, headers, params)}`
1617 * – The respond method takes a set of static data to be returned or a function that can
1618 * return an array containing response status (number), response data (string), response
1619 * headers (Object), and the text for the status (string). The respond method returns the
1620 * `requestHandler` object for possible overrides.
1622 $httpBackend.expect = function(method, url, data, headers, keys) {
1623 var expectation = new MockHttpExpectation(method, url, data, headers, keys),
1625 respond: function(status, data, headers, statusText) {
1626 expectation.response = createResponse(status, data, headers, statusText);
1631 expectations.push(expectation);
1637 * @name $httpBackend#expectGET
1639 * Creates a new request expectation for GET requests. For more info see `expect()`.
1641 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1642 * and returns true if the url matches the current definition.
1643 * @param {Object=} headers HTTP headers.
1644 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1645 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1646 * request is handled. You can save this object for later use and invoke `respond` again in
1647 * order to change how a matched request is handled. See #expect for more info.
1652 * @name $httpBackend#expectHEAD
1654 * Creates a new request expectation for HEAD requests. For more info see `expect()`.
1656 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1657 * and returns true if the url matches the current definition.
1658 * @param {Object=} headers HTTP headers.
1659 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1660 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1661 * request is handled. You can save this object for later use and invoke `respond` again in
1662 * order to change how a matched request is handled.
1667 * @name $httpBackend#expectDELETE
1669 * Creates a new request expectation for DELETE requests. For more info see `expect()`.
1671 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1672 * and returns true if the url matches the current definition.
1673 * @param {Object=} headers HTTP headers.
1674 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1675 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1676 * request is handled. You can save this object for later use and invoke `respond` again in
1677 * order to change how a matched request is handled.
1682 * @name $httpBackend#expectPOST
1684 * Creates a new request expectation for POST requests. For more info see `expect()`.
1686 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1687 * and returns true if the url matches the current definition.
1688 * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
1689 * receives data string and returns true if the data is as expected, or Object if request body
1690 * is in JSON format.
1691 * @param {Object=} headers HTTP headers.
1692 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1693 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1694 * request is handled. You can save this object for later use and invoke `respond` again in
1695 * order to change how a matched request is handled.
1700 * @name $httpBackend#expectPUT
1702 * Creates a new request expectation for PUT requests. For more info see `expect()`.
1704 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1705 * and returns true if the url matches the current definition.
1706 * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
1707 * receives data string and returns true if the data is as expected, or Object if request body
1708 * is in JSON format.
1709 * @param {Object=} headers HTTP headers.
1710 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1711 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1712 * request is handled. You can save this object for later use and invoke `respond` again in
1713 * order to change how a matched request is handled.
1718 * @name $httpBackend#expectPATCH
1720 * Creates a new request expectation for PATCH requests. For more info see `expect()`.
1722 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1723 * and returns true if the url matches the current definition.
1724 * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
1725 * receives data string and returns true if the data is as expected, or Object if request body
1726 * is in JSON format.
1727 * @param {Object=} headers HTTP headers.
1728 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1729 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1730 * request is handled. You can save this object for later use and invoke `respond` again in
1731 * order to change how a matched request is handled.
1736 * @name $httpBackend#expectJSONP
1738 * Creates a new request expectation for JSONP requests. For more info see `expect()`.
1740 * @param {string|RegExp|function(string)} url HTTP url or function that receives an url
1741 * and returns true if the url matches the current definition.
1742 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1743 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1744 * request is handled. You can save this object for later use and invoke `respond` again in
1745 * order to change how a matched request is handled.
1747 createShortMethods('expect');
1751 * @name $httpBackend#expectRoute
1753 * Creates a new request expectation that compares only with the requested route.
1755 * @param {string} method HTTP method.
1756 * @param {string} url HTTP url string that supports colon param matching.
1757 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1758 * request is handled. You can save this object for later use and invoke `respond` again in
1759 * order to change how a matched request is handled. See #expect for more info.
1761 $httpBackend.expectRoute = function(method, url) {
1762 var pathObj = parseRoute(url);
1763 return $httpBackend.expect(method, pathObj.regexp, undefined, undefined, pathObj.keys);
1769 * @name $httpBackend#flush
1771 * Flushes all pending requests using the trained responses.
1773 * @param {number=} count Number of responses to flush (in the order they arrived). If undefined,
1774 * all pending requests will be flushed. If there are no pending requests when the flush method
1775 * is called an exception is thrown (as this typically a sign of programming error).
1777 $httpBackend.flush = function(count, digest) {
1778 if (digest !== false) $rootScope.$digest();
1779 if (!responses.length) throw new Error('No pending request to flush !');
1781 if (angular.isDefined(count) && count !== null) {
1783 if (!responses.length) throw new Error('No more pending request to flush !');
1784 responses.shift()();
1787 while (responses.length) {
1788 responses.shift()();
1791 $httpBackend.verifyNoOutstandingExpectation(digest);
1797 * @name $httpBackend#verifyNoOutstandingExpectation
1799 * Verifies that all of the requests defined via the `expect` api were made. If any of the
1800 * requests were not made, verifyNoOutstandingExpectation throws an exception.
1802 * Typically, you would call this method following each test case that asserts requests using an
1803 * "afterEach" clause.
1806 * afterEach($httpBackend.verifyNoOutstandingExpectation);
1809 $httpBackend.verifyNoOutstandingExpectation = function(digest) {
1810 if (digest !== false) $rootScope.$digest();
1811 if (expectations.length) {
1812 throw new Error('Unsatisfied requests: ' + expectations.join(', '));
1819 * @name $httpBackend#verifyNoOutstandingRequest
1821 * Verifies that there are no outstanding requests that need to be flushed.
1823 * Typically, you would call this method following each test case that asserts requests using an
1824 * "afterEach" clause.
1827 * afterEach($httpBackend.verifyNoOutstandingRequest);
1830 $httpBackend.verifyNoOutstandingRequest = function() {
1831 if (responses.length) {
1832 throw new Error('Unflushed requests: ' + responses.length);
1839 * @name $httpBackend#resetExpectations
1841 * Resets all request expectations, but preserves all backend definitions. Typically, you would
1842 * call resetExpectations during a multiple-phase test when you want to reuse the same instance of
1843 * $httpBackend mock.
1845 $httpBackend.resetExpectations = function() {
1846 expectations.length = 0;
1847 responses.length = 0;
1850 return $httpBackend;
1853 function createShortMethods(prefix) {
1854 angular.forEach(['GET', 'DELETE', 'JSONP', 'HEAD'], function(method) {
1855 $httpBackend[prefix + method] = function(url, headers, keys) {
1856 return $httpBackend[prefix](method, url, undefined, headers, keys);
1860 angular.forEach(['PUT', 'POST', 'PATCH'], function(method) {
1861 $httpBackend[prefix + method] = function(url, data, headers, keys) {
1862 return $httpBackend[prefix](method, url, data, headers, keys);
1868 function MockHttpExpectation(method, url, data, headers, keys) {
1871 this.headers = headers;
1873 this.match = function(m, u, d, h) {
1874 if (method != m) return false;
1875 if (!this.matchUrl(u)) return false;
1876 if (angular.isDefined(d) && !this.matchData(d)) return false;
1877 if (angular.isDefined(h) && !this.matchHeaders(h)) return false;
1881 this.matchUrl = function(u) {
1882 if (!url) return true;
1883 if (angular.isFunction(url.test)) return url.test(u);
1884 if (angular.isFunction(url)) return url(u);
1888 this.matchHeaders = function(h) {
1889 if (angular.isUndefined(headers)) return true;
1890 if (angular.isFunction(headers)) return headers(h);
1891 return angular.equals(headers, h);
1894 this.matchData = function(d) {
1895 if (angular.isUndefined(data)) return true;
1896 if (data && angular.isFunction(data.test)) return data.test(d);
1897 if (data && angular.isFunction(data)) return data(d);
1898 if (data && !angular.isString(data)) {
1899 return angular.equals(angular.fromJson(angular.toJson(data)), angular.fromJson(d));
1904 this.toString = function() {
1905 return method + ' ' + url;
1908 this.params = function(u) {
1909 return angular.extend(parseQuery(), pathParams());
1911 function pathParams() {
1913 if (!url || !angular.isFunction(url.test) || !keys || keys.length === 0) return keyObj;
1915 var m = url.exec(u);
1916 if (!m) return keyObj;
1917 for (var i = 1, len = m.length; i < len; ++i) {
1918 var key = keys[i - 1];
1921 keyObj[key.name || key] = val;
1928 function parseQuery() {
1929 var obj = {}, key_value, key,
1930 queryStr = u.indexOf('?') > -1
1931 ? u.substring(u.indexOf('?') + 1)
1934 angular.forEach(queryStr.split('&'), function(keyValue) {
1936 key_value = keyValue.replace(/\+/g,'%20').split('=');
1937 key = tryDecodeURIComponent(key_value[0]);
1938 if (angular.isDefined(key)) {
1939 var val = angular.isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true;
1940 if (!hasOwnProperty.call(obj, key)) {
1942 } else if (angular.isArray(obj[key])) {
1945 obj[key] = [obj[key],val];
1952 function tryDecodeURIComponent(value) {
1954 return decodeURIComponent(value);
1956 // Ignore any invalid uri component
1962 function createMockXhr() {
1963 return new MockXhr();
1966 function MockXhr() {
1968 // hack for testing $http, $httpBackend
1969 MockXhr.$$lastInstance = this;
1971 this.open = function(method, url, async) {
1972 this.$$method = method;
1974 this.$$async = async;
1975 this.$$reqHeaders = {};
1976 this.$$respHeaders = {};
1979 this.send = function(data) {
1983 this.setRequestHeader = function(key, value) {
1984 this.$$reqHeaders[key] = value;
1987 this.getResponseHeader = function(name) {
1988 // the lookup must be case insensitive,
1989 // that's why we try two quick lookups first and full scan last
1990 var header = this.$$respHeaders[name];
1991 if (header) return header;
1993 name = angular.lowercase(name);
1994 header = this.$$respHeaders[name];
1995 if (header) return header;
1998 angular.forEach(this.$$respHeaders, function(headerVal, headerName) {
1999 if (!header && angular.lowercase(headerName) == name) header = headerVal;
2004 this.getAllResponseHeaders = function() {
2007 angular.forEach(this.$$respHeaders, function(value, key) {
2008 lines.push(key + ': ' + value);
2010 return lines.join('\n');
2013 this.abort = angular.noop;
2022 * This service is just a simple decorator for {@link ng.$timeout $timeout} service
2023 * that adds a "flush" and "verifyNoPendingTasks" methods.
2026 angular.mock.$TimeoutDecorator = ['$delegate', '$browser', function($delegate, $browser) {
2030 * @name $timeout#flush
2033 * Flushes the queue of pending tasks.
2035 * @param {number=} delay maximum timeout amount to flush up until
2037 $delegate.flush = function(delay) {
2038 $browser.defer.flush(delay);
2043 * @name $timeout#verifyNoPendingTasks
2046 * Verifies that there are no pending tasks that need to be flushed.
2048 $delegate.verifyNoPendingTasks = function() {
2049 if ($browser.deferredFns.length) {
2050 throw new Error('Deferred tasks to flush (' + $browser.deferredFns.length + '): ' +
2051 formatPendingTasksAsString($browser.deferredFns));
2055 function formatPendingTasksAsString(tasks) {
2057 angular.forEach(tasks, function(task) {
2058 result.push('{id: ' + task.id + ', ' + 'time: ' + task.time + '}');
2061 return result.join(', ');
2067 angular.mock.$RAFDecorator = ['$delegate', function($delegate) {
2068 var rafFn = function(fn) {
2069 var index = rafFn.queue.length;
2070 rafFn.queue.push(fn);
2072 rafFn.queue.splice(index, 1);
2077 rafFn.supported = $delegate.supported;
2079 rafFn.flush = function() {
2080 if (rafFn.queue.length === 0) {
2081 throw new Error('No rAF callbacks present');
2084 var length = rafFn.queue.length;
2085 for (var i = 0; i < length; i++) {
2089 rafFn.queue = rafFn.queue.slice(i);
2098 angular.mock.$RootElementProvider = function() {
2099 this.$get = function() {
2100 return angular.element('<div ng-app></div>');
2108 * A decorator for {@link ng.$controller} with additional `bindings` parameter, useful when testing
2109 * controllers of directives that use {@link $compile#-bindtocontroller- `bindToController`}.
2116 * // Directive definition ...
2118 * myMod.directive('myDirective', {
2119 * controller: 'MyDirectiveController',
2120 * bindToController: {
2126 * // Controller definition ...
2128 * myMod.controller('MyDirectiveController', ['$log', function($log) {
2129 * $log.info(this.name);
2135 * describe('myDirectiveController', function() {
2136 * it('should write the bound name to the log', inject(function($controller, $log) {
2137 * var ctrl = $controller('MyDirectiveController', { /* no locals */ }, { name: 'Clark Kent' });
2138 * expect(ctrl.name).toEqual('Clark Kent');
2139 * expect($log.info.logs).toEqual(['Clark Kent']);
2145 * @param {Function|string} constructor If called with a function then it's considered to be the
2146 * controller constructor function. Otherwise it's considered to be a string which is used
2147 * to retrieve the controller constructor using the following steps:
2149 * * check if a controller with given name is registered via `$controllerProvider`
2150 * * check if evaluating the string on the current scope returns a constructor
2151 * * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
2152 * `window` object (not recommended)
2154 * The string can use the `controller as property` syntax, where the controller instance is published
2155 * as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
2156 * to work correctly.
2158 * @param {Object} locals Injection locals for Controller.
2159 * @param {Object=} bindings Properties to add to the controller before invoking the constructor. This is used
2160 * to simulate the `bindToController` feature and simplify certain kinds of tests.
2161 * @return {Object} Instance of given controller.
2163 angular.mock.$ControllerDecorator = ['$delegate', function($delegate) {
2164 return function(expression, locals, later, ident) {
2165 if (later && typeof later === 'object') {
2166 var create = $delegate(expression, locals, true, ident);
2167 angular.extend(create.instance, later);
2170 return $delegate(expression, locals, later, ident);
2176 * @name $componentController
2178 * A service that can be used to create instances of component controllers.
2179 * <div class="alert alert-info">
2180 * Be aware that the controller will be instantiated and attached to the scope as specified in
2181 * the component definition object. That means that you must always provide a `$scope` object
2182 * in the `locals` param.
2184 * @param {string} componentName the name of the component whose controller we want to instantiate
2185 * @param {Object} locals Injection locals for Controller.
2186 * @param {Object=} bindings Properties to add to the controller before invoking the constructor. This is used
2187 * to simulate the `bindToController` feature and simplify certain kinds of tests.
2188 * @param {string=} ident Override the property name to use when attaching the controller to the scope.
2189 * @return {Object} Instance of requested controller.
2191 angular.mock.$ComponentControllerProvider = ['$compileProvider', function($compileProvider) {
2193 $get: ['$controller','$injector', function($controller,$injector) {
2194 return function $componentController(componentName, locals, bindings, ident) {
2195 // get all directives associated to the component name
2196 var directives = $injector.get(componentName + 'Directive');
2197 // look for those directives that are components
2198 var candidateDirectives = directives.filter(function(directiveInfo) {
2199 // components have controller, controllerAs and restrict:'E'
2200 return directiveInfo.controller && directiveInfo.controllerAs && directiveInfo.restrict === 'E';
2202 // check if valid directives found
2203 if (candidateDirectives.length === 0) {
2204 throw new Error('No component found');
2206 if (candidateDirectives.length > 1) {
2207 throw new Error('Too many components found');
2209 // get the info of the component
2210 var directiveInfo = candidateDirectives[0];
2211 return $controller(directiveInfo.controller, locals, bindings, ident || directiveInfo.controllerAs);
2221 * @packageName angular-mocks
2226 * The `ngMock` module provides support to inject and mock Angular services into unit tests.
2227 * In addition, ngMock also extends various core ng services such that they can be
2228 * inspected and controlled in a synchronous manner within test code.
2231 * <div doc-module-components="ngMock"></div>
2234 angular.module('ngMock', ['ng']).provider({
2235 $browser: angular.mock.$BrowserProvider,
2236 $exceptionHandler: angular.mock.$ExceptionHandlerProvider,
2237 $log: angular.mock.$LogProvider,
2238 $interval: angular.mock.$IntervalProvider,
2239 $httpBackend: angular.mock.$HttpBackendProvider,
2240 $rootElement: angular.mock.$RootElementProvider,
2241 $componentController: angular.mock.$ComponentControllerProvider
2242 }).config(['$provide', function($provide) {
2243 $provide.decorator('$timeout', angular.mock.$TimeoutDecorator);
2244 $provide.decorator('$$rAF', angular.mock.$RAFDecorator);
2245 $provide.decorator('$rootScope', angular.mock.$RootScopeDecorator);
2246 $provide.decorator('$controller', angular.mock.$ControllerDecorator);
2253 * @packageName angular-mocks
2256 * The `ngMockE2E` is an angular module which contains mocks suitable for end-to-end testing.
2257 * Currently there is only one mock present in this module -
2258 * the {@link ngMockE2E.$httpBackend e2e $httpBackend} mock.
2260 angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
2261 $provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator);
2266 * @name $httpBackend
2269 * Fake HTTP backend implementation suitable for end-to-end testing or backend-less development of
2270 * applications that use the {@link ng.$http $http service}.
2272 * *Note*: For fake http backend implementation suitable for unit testing please see
2273 * {@link ngMock.$httpBackend unit-testing $httpBackend mock}.
2275 * This implementation can be used to respond with static or dynamic responses via the `when` api
2276 * and its shortcuts (`whenGET`, `whenPOST`, etc) and optionally pass through requests to the
2277 * real $httpBackend for specific requests (e.g. to interact with certain remote apis or to fetch
2278 * templates from a webserver).
2280 * As opposed to unit-testing, in an end-to-end testing scenario or in scenario when an application
2281 * is being developed with the real backend api replaced with a mock, it is often desirable for
2282 * certain category of requests to bypass the mock and issue a real http request (e.g. to fetch
2283 * templates or static files from the webserver). To configure the backend with this behavior
2284 * use the `passThrough` request handler of `when` instead of `respond`.
2286 * Additionally, we don't want to manually have to flush mocked out requests like we do during unit
2287 * testing. For this reason the e2e $httpBackend flushes mocked out requests
2288 * automatically, closely simulating the behavior of the XMLHttpRequest object.
2290 * To setup the application to run with this http backend, you have to create a module that depends
2291 * on the `ngMockE2E` and your application modules and defines the fake backend:
2294 * myAppDev = angular.module('myAppDev', ['myApp', 'ngMockE2E']);
2295 * myAppDev.run(function($httpBackend) {
2296 * phones = [{name: 'phone1'}, {name: 'phone2'}];
2298 * // returns the current list of phones
2299 * $httpBackend.whenGET('/phones').respond(phones);
2301 * // adds a new phone to the phones array
2302 * $httpBackend.whenPOST('/phones').respond(function(method, url, data) {
2303 * var phone = angular.fromJson(data);
2304 * phones.push(phone);
2305 * return [200, phone, {}];
2307 * $httpBackend.whenGET(/^\/templates\//).passThrough();
2312 * Afterwards, bootstrap your app with this new module.
2317 * @name $httpBackend#when
2320 * Creates a new backend definition.
2322 * @param {string} method HTTP method.
2323 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
2324 * and returns true if the url matches the current definition.
2325 * @param {(string|RegExp)=} data HTTP request body.
2326 * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
2327 * object and returns true if the headers match the current definition.
2328 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
2329 * {@link ngMock.$httpBackend $httpBackend mock}.
2330 * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2331 * control how a matched request is handled. You can save this object for later use and invoke
2332 * `respond` or `passThrough` again in order to change how a matched request is handled.
2335 * `{function([status,] data[, headers, statusText])
2336 * | function(function(method, url, data, headers, params)}`
2337 * – The respond method takes a set of static data to be returned or a function that can return
2338 * an array containing response status (number), response data (string), response headers
2339 * (Object), and the text for the status (string).
2340 * - passThrough – `{function()}` – Any request matching a backend definition with
2341 * `passThrough` handler will be passed through to the real backend (an XHR request will be made
2343 * - Both methods return the `requestHandler` object for possible overrides.
2348 * @name $httpBackend#whenGET
2351 * Creates a new backend definition for GET requests. For more info see `when()`.
2353 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
2354 * and returns true if the url matches the current definition.
2355 * @param {(Object|function(Object))=} headers HTTP headers.
2356 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
2357 * {@link ngMock.$httpBackend $httpBackend mock}.
2358 * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2359 * control how a matched request is handled. You can save this object for later use and invoke
2360 * `respond` or `passThrough` again in order to change how a matched request is handled.
2365 * @name $httpBackend#whenHEAD
2368 * Creates a new backend definition for HEAD requests. For more info see `when()`.
2370 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
2371 * and returns true if the url matches the current definition.
2372 * @param {(Object|function(Object))=} headers HTTP headers.
2373 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
2374 * {@link ngMock.$httpBackend $httpBackend mock}.
2375 * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2376 * control how a matched request is handled. You can save this object for later use and invoke
2377 * `respond` or `passThrough` again in order to change how a matched request is handled.
2382 * @name $httpBackend#whenDELETE
2385 * Creates a new backend definition for DELETE requests. For more info see `when()`.
2387 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
2388 * and returns true if the url matches the current definition.
2389 * @param {(Object|function(Object))=} headers HTTP headers.
2390 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
2391 * {@link ngMock.$httpBackend $httpBackend mock}.
2392 * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2393 * control how a matched request is handled. You can save this object for later use and invoke
2394 * `respond` or `passThrough` again in order to change how a matched request is handled.
2399 * @name $httpBackend#whenPOST
2402 * Creates a new backend definition for POST requests. For more info see `when()`.
2404 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
2405 * and returns true if the url matches the current definition.
2406 * @param {(string|RegExp)=} data HTTP request body.
2407 * @param {(Object|function(Object))=} headers HTTP headers.
2408 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
2409 * {@link ngMock.$httpBackend $httpBackend mock}.
2410 * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2411 * control how a matched request is handled. You can save this object for later use and invoke
2412 * `respond` or `passThrough` again in order to change how a matched request is handled.
2417 * @name $httpBackend#whenPUT
2420 * Creates a new backend definition for PUT requests. For more info see `when()`.
2422 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
2423 * and returns true if the url matches the current definition.
2424 * @param {(string|RegExp)=} data HTTP request body.
2425 * @param {(Object|function(Object))=} headers HTTP headers.
2426 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
2427 * {@link ngMock.$httpBackend $httpBackend mock}.
2428 * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2429 * control how a matched request is handled. You can save this object for later use and invoke
2430 * `respond` or `passThrough` again in order to change how a matched request is handled.
2435 * @name $httpBackend#whenPATCH
2438 * Creates a new backend definition for PATCH requests. For more info see `when()`.
2440 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
2441 * and returns true if the url matches the current definition.
2442 * @param {(string|RegExp)=} data HTTP request body.
2443 * @param {(Object|function(Object))=} headers HTTP headers.
2444 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
2445 * {@link ngMock.$httpBackend $httpBackend mock}.
2446 * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2447 * control how a matched request is handled. You can save this object for later use and invoke
2448 * `respond` or `passThrough` again in order to change how a matched request is handled.
2453 * @name $httpBackend#whenJSONP
2456 * Creates a new backend definition for JSONP requests. For more info see `when()`.
2458 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
2459 * and returns true if the url matches the current definition.
2460 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
2461 * {@link ngMock.$httpBackend $httpBackend mock}.
2462 * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2463 * control how a matched request is handled. You can save this object for later use and invoke
2464 * `respond` or `passThrough` again in order to change how a matched request is handled.
2468 * @name $httpBackend#whenRoute
2471 * Creates a new backend definition that compares only with the requested route.
2473 * @param {string} method HTTP method.
2474 * @param {string} url HTTP url string that supports colon param matching.
2475 * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2476 * control how a matched request is handled. You can save this object for later use and invoke
2477 * `respond` or `passThrough` again in order to change how a matched request is handled.
2479 angular.mock.e2e = {};
2480 angular.mock.e2e.$httpBackendDecorator =
2481 ['$rootScope', '$timeout', '$delegate', '$browser', createHttpBackendMock];
2486 * @name $rootScope.Scope
2489 * {@link ng.$rootScope.Scope Scope} type decorated with helper methods useful for testing. These
2490 * methods are automatically available on any {@link ng.$rootScope.Scope Scope} instance when
2491 * `ngMock` module is loaded.
2493 * In addition to all the regular `Scope` methods, the following helper methods are available:
2495 angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
2497 var $rootScopePrototype = Object.getPrototypeOf($delegate);
2499 $rootScopePrototype.$countChildScopes = countChildScopes;
2500 $rootScopePrototype.$countWatchers = countWatchers;
2504 // ------------------------------------------------------------------------------------------ //
2508 * @name $rootScope.Scope#$countChildScopes
2511 * Counts all the direct and indirect child scopes of the current scope.
2513 * The current scope is excluded from the count. The count includes all isolate child scopes.
2515 * @returns {number} Total number of child scopes.
2517 function countChildScopes() {
2518 // jshint validthis: true
2519 var count = 0; // exclude the current scope
2520 var pendingChildHeads = [this.$$childHead];
2523 while (pendingChildHeads.length) {
2524 currentScope = pendingChildHeads.shift();
2526 while (currentScope) {
2528 pendingChildHeads.push(currentScope.$$childHead);
2529 currentScope = currentScope.$$nextSibling;
2539 * @name $rootScope.Scope#$countWatchers
2542 * Counts all the watchers of direct and indirect child scopes of the current scope.
2544 * The watchers of the current scope are included in the count and so are all the watchers of
2545 * isolate child scopes.
2547 * @returns {number} Total number of watchers.
2549 function countWatchers() {
2550 // jshint validthis: true
2551 var count = this.$$watchers ? this.$$watchers.length : 0; // include the current scope
2552 var pendingChildHeads = [this.$$childHead];
2555 while (pendingChildHeads.length) {
2556 currentScope = pendingChildHeads.shift();
2558 while (currentScope) {
2559 count += currentScope.$$watchers ? currentScope.$$watchers.length : 0;
2560 pendingChildHeads.push(currentScope.$$childHead);
2561 currentScope = currentScope.$$nextSibling;
2570 if (window.jasmine || window.mocha) {
2572 var currentSpec = null,
2573 annotatedFunctions = [],
2574 isSpecRunning = function() {
2575 return !!currentSpec;
2578 angular.mock.$$annotate = angular.injector.$$annotate;
2579 angular.injector.$$annotate = function(fn) {
2580 if (typeof fn === 'function' && !fn.$inject) {
2581 annotatedFunctions.push(fn);
2583 return angular.mock.$$annotate.apply(this, arguments);
2587 (window.beforeEach || window.setup)(function() {
2588 annotatedFunctions = [];
2592 (window.afterEach || window.teardown)(function() {
2593 var injector = currentSpec.$injector;
2595 annotatedFunctions.forEach(function(fn) {
2599 angular.forEach(currentSpec.$modules, function(module) {
2600 if (module && module.$$hashKey) {
2601 module.$$hashKey = undefined;
2605 currentSpec.$injector = null;
2606 currentSpec.$modules = null;
2607 currentSpec.$providerInjector = null;
2611 injector.get('$rootElement').off();
2612 injector.get('$rootScope').$destroy();
2615 // clean up jquery's fragment cache
2616 angular.forEach(angular.element.fragments, function(val, key) {
2617 delete angular.element.fragments[key];
2620 MockXhr.$$lastInstance = null;
2622 angular.forEach(angular.callbacks, function(val, key) {
2623 delete angular.callbacks[key];
2625 angular.callbacks.counter = 0;
2630 * @name angular.mock.module
2633 * *NOTE*: This function is also published on window for easy access.<br>
2634 * *NOTE*: This function is declared ONLY WHEN running tests with jasmine or mocha
2636 * This function registers a module configuration code. It collects the configuration information
2637 * which will be used when the injector is created by {@link angular.mock.inject inject}.
2639 * See {@link angular.mock.inject inject} for usage example
2641 * @param {...(string|Function|Object)} fns any number of modules which are represented as string
2642 * aliases or as anonymous module initialization functions. The modules are used to
2643 * configure the injector. The 'ng' and 'ngMock' modules are automatically loaded. If an
2644 * object literal is passed each key-value pair will be registered on the module via
2645 * {@link auto.$provide $provide}.value, the key being the string name (or token) to associate
2646 * with the value on the injector.
2648 window.module = angular.mock.module = function() {
2649 var moduleFns = Array.prototype.slice.call(arguments, 0);
2650 return isSpecRunning() ? workFn() : workFn;
2651 /////////////////////
2653 if (currentSpec.$injector) {
2654 throw new Error('Injector already created, can not register a module!');
2656 var fn, modules = currentSpec.$modules || (currentSpec.$modules = []);
2657 angular.forEach(moduleFns, function(module) {
2658 if (angular.isObject(module) && !angular.isArray(module)) {
2659 fn = function($provide) {
2660 angular.forEach(module, function(value, key) {
2661 $provide.value(key, value);
2667 if (currentSpec.$providerInjector) {
2668 currentSpec.$providerInjector.invoke(fn);
2679 * @name angular.mock.inject
2682 * *NOTE*: This function is also published on window for easy access.<br>
2683 * *NOTE*: This function is declared ONLY WHEN running tests with jasmine or mocha
2685 * The inject function wraps a function into an injectable function. The inject() creates new
2686 * instance of {@link auto.$injector $injector} per test, which is then used for
2687 * resolving references.
2690 * ## Resolving References (Underscore Wrapping)
2691 * Often, we would like to inject a reference once, in a `beforeEach()` block and reuse this
2692 * in multiple `it()` clauses. To be able to do this we must assign the reference to a variable
2693 * that is declared in the scope of the `describe()` block. Since we would, most likely, want
2694 * the variable to have the same name of the reference we have a problem, since the parameter
2695 * to the `inject()` function would hide the outer variable.
2697 * To help with this, the injected parameters can, optionally, be enclosed with underscores.
2698 * These are ignored by the injector when the reference name is resolved.
2700 * For example, the parameter `_myService_` would be resolved as the reference `myService`.
2701 * Since it is available in the function body as _myService_, we can then assign it to a variable
2702 * defined in an outer scope.
2705 * // Defined out reference variable outside
2708 * // Wrap the parameter in underscores
2709 * beforeEach( inject( function(_myService_){
2710 * myService = _myService_;
2713 * // Use myService in a series of tests.
2714 * it('makes use of myService', function() {
2715 * myService.doStuff();
2720 * See also {@link angular.mock.module angular.mock.module}
2723 * Example of what a typical jasmine tests looks like with the inject method.
2726 * angular.module('myApplicationModule', [])
2727 * .value('mode', 'app')
2728 * .value('version', 'v1.0.1');
2731 * describe('MyApp', function() {
2733 * // You need to load modules that you want to test,
2734 * // it loads only the "ng" module by default.
2735 * beforeEach(module('myApplicationModule'));
2738 * // inject() is used to inject arguments of all given functions
2739 * it('should provide a version', inject(function(mode, version) {
2740 * expect(version).toEqual('v1.0.1');
2741 * expect(mode).toEqual('app');
2745 * // The inject and module method can also be used inside of the it or beforeEach
2746 * it('should override a version and test the new version is injected', function() {
2747 * // module() takes functions or strings (module aliases)
2748 * module(function($provide) {
2749 * $provide.value('version', 'overridden'); // override version here
2752 * inject(function(version) {
2753 * expect(version).toEqual('overridden');
2760 * @param {...Function} fns any number of functions which will be injected using the injector.
2765 var ErrorAddingDeclarationLocationStack = function(e, errorForStack) {
2766 this.message = e.message;
2768 if (e.line) this.line = e.line;
2769 if (e.sourceId) this.sourceId = e.sourceId;
2770 if (e.stack && errorForStack)
2771 this.stack = e.stack + '\n' + errorForStack.stack;
2772 if (e.stackArray) this.stackArray = e.stackArray;
2774 ErrorAddingDeclarationLocationStack.prototype.toString = Error.prototype.toString;
2776 window.inject = angular.mock.inject = function() {
2777 var blockFns = Array.prototype.slice.call(arguments, 0);
2778 var errorForStack = new Error('Declaration Location');
2779 return isSpecRunning() ? workFn.call(currentSpec) : workFn;
2780 /////////////////////
2782 var modules = currentSpec.$modules || [];
2783 var strictDi = !!currentSpec.$injectorStrict;
2784 modules.unshift(function($injector) {
2785 currentSpec.$providerInjector = $injector;
2787 modules.unshift('ngMock');
2788 modules.unshift('ng');
2789 var injector = currentSpec.$injector;
2792 // If strictDi is enabled, annotate the providerInjector blocks
2793 angular.forEach(modules, function(moduleFn) {
2794 if (typeof moduleFn === "function") {
2795 angular.injector.$$annotate(moduleFn);
2799 injector = currentSpec.$injector = angular.injector(modules, strictDi);
2800 currentSpec.$injectorStrict = strictDi;
2802 for (var i = 0, ii = blockFns.length; i < ii; i++) {
2803 if (currentSpec.$injectorStrict) {
2804 // If the injector is strict / strictDi, and the spec wants to inject using automatic
2805 // annotation, then annotate the function here.
2806 injector.annotate(blockFns[i]);
2809 /* jshint -W040 *//* Jasmine explicitly provides a `this` object when calling functions */
2810 injector.invoke(blockFns[i] || angular.noop, this);
2813 if (e.stack && errorForStack) {
2814 throw new ErrorAddingDeclarationLocationStack(e, errorForStack);
2818 errorForStack = null;
2825 angular.mock.inject.strictDi = function(value) {
2826 value = arguments.length ? !!value : true;
2827 return isSpecRunning() ? workFn() : workFn;
2830 if (value !== currentSpec.$injectorStrict) {
2831 if (currentSpec.$injector) {
2832 throw new Error('Injector already created, can not modify strict annotations');
2834 currentSpec.$injectorStrict = value;
2842 })(window, window.angular);