2 * @license AngularJS v1.5.11
3 * (c) 2010-2017 Google, Inc. http://angularjs.org
6 (function(window, angular) {
15 * Namespace from 'angular-mocks.js' which contains testing related code.
21 * ! This is a private undocumented service !
26 * This service is a mock implementation of {@link ng.$browser}. It provides fake
27 * implementation for commonly used browser apis that are hard to test, e.g. setTimeout, xhr,
30 * The api of this service is the same as that of the real {@link ng.$browser $browser}, except
31 * that there are several helper methods available which can be used in tests.
33 angular.mock.$BrowserProvider = function() {
34 this.$get = function() {
35 return new angular.mock.$Browser();
39 angular.mock.$Browser = function() {
43 self.$$url = 'http://server/';
44 self.$$lastUrl = self.$$url; // used by url polling fn
47 // TODO(vojta): remove this temporary api
48 self.$$completeOutstandingRequest = angular.noop;
49 self.$$incOutstandingRequestCount = angular.noop;
52 // register url polling fn
54 self.onUrlChange = function(listener) {
57 if (self.$$lastUrl !== self.$$url || self.$$state !== self.$$lastState) {
58 self.$$lastUrl = self.$$url;
59 self.$$lastState = self.$$state;
60 listener(self.$$url, self.$$state);
68 self.$$applicationDestroyed = angular.noop;
69 self.$$checkUrlChange = angular.noop;
71 self.deferredFns = [];
72 self.deferredNextId = 0;
74 self.defer = function(fn, delay) {
76 self.deferredFns.push({time:(self.defer.now + delay), fn:fn, id: self.deferredNextId});
77 self.deferredFns.sort(function(a, b) { return a.time - b.time;});
78 return self.deferredNextId++;
83 * @name $browser#defer.now
86 * Current milliseconds mock time.
91 self.defer.cancel = function(deferId) {
94 angular.forEach(self.deferredFns, function(fn, index) {
95 if (fn.id === deferId) fnIndex = index;
98 if (angular.isDefined(fnIndex)) {
99 self.deferredFns.splice(fnIndex, 1);
108 * @name $browser#defer.flush
111 * Flushes all pending requests and executes the defer callbacks.
113 * @param {number=} number of milliseconds to flush. See {@link #defer.now}
115 self.defer.flush = function(delay) {
118 if (angular.isDefined(delay)) {
119 // A delay was passed so compute the next time
120 nextTime = self.defer.now + delay;
122 if (self.deferredFns.length) {
123 // No delay was passed so set the next time so that it clears the deferred queue
124 nextTime = self.deferredFns[self.deferredFns.length - 1].time;
126 // No delay passed, but there are no deferred tasks so flush - indicates an error!
127 throw new Error('No deferred tasks to be flushed');
131 while (self.deferredFns.length && self.deferredFns[0].time <= nextTime) {
132 // Increment the time and call the next deferred function
133 self.defer.now = self.deferredFns[0].time;
134 self.deferredFns.shift().fn();
137 // Ensure that the current time is correct
138 self.defer.now = nextTime;
141 self.$$baseHref = '/';
142 self.baseHref = function() {
143 return this.$$baseHref;
146 angular.mock.$Browser.prototype = {
149 * @name $browser#poll
152 * run all fns in pollFns
154 poll: function poll() {
155 angular.forEach(this.pollFns, function(pollFn) {
160 url: function(url, replace, state) {
161 if (angular.isUndefined(state)) {
166 // Native pushState serializes & copies the object; simulate it.
167 this.$$state = angular.copy(state);
178 notifyWhenNoOutstandingRequests: function(fn) {
186 * @name $exceptionHandlerProvider
189 * Configures the mock implementation of {@link ng.$exceptionHandler} to rethrow or to log errors
190 * passed to the `$exceptionHandler`.
195 * @name $exceptionHandler
198 * Mock implementation of {@link ng.$exceptionHandler} that rethrows or logs errors passed
199 * to it. See {@link ngMock.$exceptionHandlerProvider $exceptionHandlerProvider} for configuration
204 * describe('$exceptionHandlerProvider', function() {
206 * it('should capture log messages and exceptions', function() {
208 * module(function($exceptionHandlerProvider) {
209 * $exceptionHandlerProvider.mode('log');
212 * inject(function($log, $exceptionHandler, $timeout) {
213 * $timeout(function() { $log.log(1); });
214 * $timeout(function() { $log.log(2); throw 'banana peel'; });
215 * $timeout(function() { $log.log(3); });
216 * expect($exceptionHandler.errors).toEqual([]);
217 * expect($log.assertEmpty());
219 * expect($exceptionHandler.errors).toEqual(['banana peel']);
220 * expect($log.log.logs).toEqual([[1], [2], [3]]);
227 angular.mock.$ExceptionHandlerProvider = function() {
232 * @name $exceptionHandlerProvider#mode
235 * Sets the logging mode.
237 * @param {string} mode Mode of operation, defaults to `rethrow`.
239 * - `log`: Sometimes it is desirable to test that an error is thrown, for this case the `log`
240 * mode stores an array of errors in `$exceptionHandler.errors`, to allow later assertion of
241 * them. See {@link ngMock.$log#assertEmpty assertEmpty()} and
242 * {@link ngMock.$log#reset reset()}.
243 * - `rethrow`: If any errors are passed to the handler in tests, it typically means that there
244 * is a bug in the application or test, so this mock will make these tests fail. For any
245 * implementations that expect exceptions to be thrown, the `rethrow` mode will also maintain
246 * a log of thrown errors in `$exceptionHandler.errors`.
248 this.mode = function(mode) {
254 handler = function(e) {
255 if (arguments.length === 1) {
258 errors.push([].slice.call(arguments, 0));
260 if (mode === 'rethrow') {
264 handler.errors = errors;
267 throw new Error('Unknown mode \'' + mode + '\', only \'log\'/\'rethrow\' modes are allowed!');
271 this.$get = function() {
275 this.mode('rethrow');
284 * Mock implementation of {@link ng.$log} that gathers all logged messages in arrays
285 * (one array per logging level). These arrays are exposed as `logs` property of each of the
286 * level-specific log function, e.g. for level `error` the array is exposed as `$log.error.logs`.
289 angular.mock.$LogProvider = function() {
292 function concat(array1, array2, index) {
293 return array1.concat(Array.prototype.slice.call(array2, index));
296 this.debugEnabled = function(flag) {
297 if (angular.isDefined(flag)) {
305 this.$get = function() {
307 log: function() { $log.log.logs.push(concat([], arguments, 0)); },
308 warn: function() { $log.warn.logs.push(concat([], arguments, 0)); },
309 info: function() { $log.info.logs.push(concat([], arguments, 0)); },
310 error: function() { $log.error.logs.push(concat([], arguments, 0)); },
313 $log.debug.logs.push(concat([], arguments, 0));
323 * Reset all of the logging arrays to empty.
325 $log.reset = function() {
328 * @name $log#log.logs
331 * Array of messages logged using {@link ng.$log#log `log()`}.
335 * $log.log('Some Log');
336 * var first = $log.log.logs.unshift();
342 * @name $log#info.logs
345 * Array of messages logged using {@link ng.$log#info `info()`}.
349 * $log.info('Some Info');
350 * var first = $log.info.logs.unshift();
356 * @name $log#warn.logs
359 * Array of messages logged using {@link ng.$log#warn `warn()`}.
363 * $log.warn('Some Warning');
364 * var first = $log.warn.logs.unshift();
370 * @name $log#error.logs
373 * Array of messages logged using {@link ng.$log#error `error()`}.
377 * $log.error('Some Error');
378 * var first = $log.error.logs.unshift();
381 $log.error.logs = [];
384 * @name $log#debug.logs
387 * Array of messages logged using {@link ng.$log#debug `debug()`}.
391 * $log.debug('Some Error');
392 * var first = $log.debug.logs.unshift();
395 $log.debug.logs = [];
400 * @name $log#assertEmpty
403 * Assert that all of the logging methods have no logged messages. If any messages are present,
404 * an exception is thrown.
406 $log.assertEmpty = function() {
408 angular.forEach(['error', 'warn', 'info', 'log', 'debug'], function(logLevel) {
409 angular.forEach($log[logLevel].logs, function(log) {
410 angular.forEach(log, function(logItem) {
411 errors.push('MOCK $log (' + logLevel + '): ' + String(logItem) + '\n' +
412 (logItem.stack || ''));
417 errors.unshift('Expected $log to be empty! Either a message was logged unexpectedly, or ' +
418 'an expected log message was not checked and removed:');
420 throw new Error(errors.join('\n---------\n'));
435 * Mock implementation of the $interval service.
437 * Use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
438 * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
441 * @param {function()} fn A function that should be called repeatedly.
442 * @param {number} delay Number of milliseconds between each function call.
443 * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
445 * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
446 * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
447 * @param {...*=} Pass additional parameters to the executed function.
448 * @returns {promise} A promise which will be notified on each iteration.
450 angular.mock.$IntervalProvider = function() {
451 this.$get = ['$browser', '$rootScope', '$q', '$$q',
452 function($browser, $rootScope, $q, $$q) {
457 var $interval = function(fn, delay, count, invokeApply) {
458 var hasParams = arguments.length > 4,
459 args = hasParams ? Array.prototype.slice.call(arguments, 4) : [],
461 skipApply = (angular.isDefined(invokeApply) && !invokeApply),
462 deferred = (skipApply ? $$q : $q).defer(),
463 promise = deferred.promise;
465 count = (angular.isDefined(count)) ? count : 0;
466 promise.then(null, null, (!hasParams) ? fn : function() {
467 fn.apply(null, args);
470 promise.$$intervalId = nextRepeatId;
473 deferred.notify(iteration++);
475 if (count > 0 && iteration >= count) {
477 deferred.resolve(iteration);
479 angular.forEach(repeatFns, function(fn, index) {
480 if (fn.id === promise.$$intervalId) fnIndex = index;
483 if (angular.isDefined(fnIndex)) {
484 repeatFns.splice(fnIndex, 1);
489 $browser.defer.flush();
496 nextTime:(now + delay),
502 repeatFns.sort(function(a, b) { return a.nextTime - b.nextTime;});
509 * @name $interval#cancel
512 * Cancels a task associated with the `promise`.
514 * @param {promise} promise A promise from calling the `$interval` function.
515 * @returns {boolean} Returns `true` if the task was successfully cancelled.
517 $interval.cancel = function(promise) {
518 if (!promise) return false;
521 angular.forEach(repeatFns, function(fn, index) {
522 if (fn.id === promise.$$intervalId) fnIndex = index;
525 if (angular.isDefined(fnIndex)) {
526 repeatFns[fnIndex].deferred.reject('canceled');
527 repeatFns.splice(fnIndex, 1);
536 * @name $interval#flush
539 * Runs interval tasks scheduled to be run in the next `millis` milliseconds.
541 * @param {number=} millis maximum timeout amount to flush up until.
543 * @return {number} The amount of time moved forward.
545 $interval.flush = function(millis) {
547 while (repeatFns.length && repeatFns[0].nextTime <= now) {
548 var task = repeatFns[0];
550 task.nextTime += task.delay;
551 repeatFns.sort(function(a, b) { return a.nextTime - b.nextTime;});
561 function jsonStringToDate(string) {
562 // The R_ISO8061_STR regex is never going to fit into the 100 char limit!
563 // eslit-disable-next-line max-len
564 var R_ISO8061_STR = /^(-?\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/;
567 if ((match = string.match(R_ISO8061_STR))) {
568 var date = new Date(0),
572 tzHour = toInt(match[9] + match[10]);
573 tzMin = toInt(match[9] + match[11]);
575 date.setUTCFullYear(toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
576 date.setUTCHours(toInt(match[4] || 0) - tzHour,
577 toInt(match[5] || 0) - tzMin,
578 toInt(match[6] || 0),
579 toInt(match[7] || 0));
585 function toInt(str) {
586 return parseInt(str, 10);
589 function padNumberInMock(num, digits, trim) {
596 while (num.length < digits) num = '0' + num;
598 num = num.substr(num.length - digits);
606 * @name angular.mock.TzDate
609 * *NOTE*: this is not an injectable instance, just a globally available mock class of `Date`.
611 * Mock of the Date type which has its timezone specified via constructor arg.
613 * The main purpose is to create Date-like instances with timezone fixed to the specified timezone
614 * offset, so that we can test code that depends on local timezone settings without dependency on
615 * the time zone settings of the machine where the code is running.
617 * @param {number} offset Offset of the *desired* timezone in hours (fractions will be honored)
618 * @param {(number|string)} timestamp Timestamp representing the desired time in *UTC*
622 * This is not a complete Date object so only methods that were implemented can be called safely.
623 * To make matters worse, TzDate instances inherit stuff from Date via a prototype.
625 * We do our best to intercept calls to "unimplemented" methods, but since the list of methods is
626 * incomplete we might be missing some non-standard methods. This can result in errors like:
627 * "Date.prototype.foo called on incompatible Object".
630 * var newYearInBratislava = new TzDate(-1, '2009-12-31T23:00:00Z');
631 * newYearInBratislava.getTimezoneOffset() => -60;
632 * newYearInBratislava.getFullYear() => 2010;
633 * newYearInBratislava.getMonth() => 0;
634 * newYearInBratislava.getDate() => 1;
635 * newYearInBratislava.getHours() => 0;
636 * newYearInBratislava.getMinutes() => 0;
637 * newYearInBratislava.getSeconds() => 0;
641 angular.mock.TzDate = function(offset, timestamp) {
642 var self = new Date(0);
643 if (angular.isString(timestamp)) {
644 var tsStr = timestamp;
646 self.origDate = jsonStringToDate(timestamp);
648 timestamp = self.origDate.getTime();
649 if (isNaN(timestamp)) {
650 // eslint-disable-next-line no-throw-literal
652 name: 'Illegal Argument',
653 message: 'Arg \'' + tsStr + '\' passed into TzDate constructor is not a valid date string'
657 self.origDate = new Date(timestamp);
660 var localOffset = new Date(timestamp).getTimezoneOffset();
661 self.offsetDiff = localOffset * 60 * 1000 - offset * 1000 * 60 * 60;
662 self.date = new Date(timestamp + self.offsetDiff);
664 self.getTime = function() {
665 return self.date.getTime() - self.offsetDiff;
668 self.toLocaleDateString = function() {
669 return self.date.toLocaleDateString();
672 self.getFullYear = function() {
673 return self.date.getFullYear();
676 self.getMonth = function() {
677 return self.date.getMonth();
680 self.getDate = function() {
681 return self.date.getDate();
684 self.getHours = function() {
685 return self.date.getHours();
688 self.getMinutes = function() {
689 return self.date.getMinutes();
692 self.getSeconds = function() {
693 return self.date.getSeconds();
696 self.getMilliseconds = function() {
697 return self.date.getMilliseconds();
700 self.getTimezoneOffset = function() {
704 self.getUTCFullYear = function() {
705 return self.origDate.getUTCFullYear();
708 self.getUTCMonth = function() {
709 return self.origDate.getUTCMonth();
712 self.getUTCDate = function() {
713 return self.origDate.getUTCDate();
716 self.getUTCHours = function() {
717 return self.origDate.getUTCHours();
720 self.getUTCMinutes = function() {
721 return self.origDate.getUTCMinutes();
724 self.getUTCSeconds = function() {
725 return self.origDate.getUTCSeconds();
728 self.getUTCMilliseconds = function() {
729 return self.origDate.getUTCMilliseconds();
732 self.getDay = function() {
733 return self.date.getDay();
736 // provide this method only on browsers that already have it
737 if (self.toISOString) {
738 self.toISOString = function() {
739 return padNumberInMock(self.origDate.getUTCFullYear(), 4) + '-' +
740 padNumberInMock(self.origDate.getUTCMonth() + 1, 2) + '-' +
741 padNumberInMock(self.origDate.getUTCDate(), 2) + 'T' +
742 padNumberInMock(self.origDate.getUTCHours(), 2) + ':' +
743 padNumberInMock(self.origDate.getUTCMinutes(), 2) + ':' +
744 padNumberInMock(self.origDate.getUTCSeconds(), 2) + '.' +
745 padNumberInMock(self.origDate.getUTCMilliseconds(), 3) + 'Z';
749 //hide all methods not implemented in this mock that the Date prototype exposes
750 var unimplementedMethods = ['getUTCDay',
751 'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds',
752 'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear',
753 'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds',
754 'setYear', 'toDateString', 'toGMTString', 'toJSON', 'toLocaleFormat', 'toLocaleString',
755 'toLocaleTimeString', 'toSource', 'toString', 'toTimeString', 'toUTCString', 'valueOf'];
757 angular.forEach(unimplementedMethods, function(methodName) {
758 self[methodName] = function() {
759 throw new Error('Method \'' + methodName + '\' is not implemented in the TzDate mock');
766 //make "tzDateInstance instanceof Date" return true
767 angular.mock.TzDate.prototype = Date.prototype;
775 * Mock implementation of the {@link ng.$animate `$animate`} service. Exposes two additional methods
776 * for testing animations.
778 * You need to require the `ngAnimateMock` module in your test suite for instance `beforeEach(module('ngAnimateMock'))`
780 angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
782 .config(['$provide', function($provide) {
784 $provide.factory('$$forceReflow', function() {
785 function reflowFn() {
786 reflowFn.totalReflows++;
788 reflowFn.totalReflows = 0;
792 $provide.factory('$$animateAsyncRun', function() {
794 var queueFn = function() {
795 return function(fn) {
799 queueFn.flush = function() {
800 if (queue.length === 0) return false;
802 for (var i = 0; i < queue.length; i++) {
812 $provide.decorator('$$animateJs', ['$delegate', function($delegate) {
815 var animateJsConstructor = function() {
816 var animator = $delegate.apply($delegate, arguments);
817 // If no javascript animation is found, animator is undefined
819 runners.push(animator);
824 animateJsConstructor.$closeAndFlush = function() {
825 runners.forEach(function(runner) {
831 return animateJsConstructor;
834 $provide.decorator('$animateCss', ['$delegate', function($delegate) {
837 var animateCssConstructor = function(element, options) {
838 var animator = $delegate(element, options);
839 runners.push(animator);
843 animateCssConstructor.$closeAndFlush = function() {
844 runners.forEach(function(runner) {
850 return animateCssConstructor;
853 $provide.decorator('$animate', ['$delegate', '$timeout', '$browser', '$$rAF', '$animateCss', '$$animateJs',
854 '$$forceReflow', '$$animateAsyncRun', '$rootScope',
855 function($delegate, $timeout, $browser, $$rAF, $animateCss, $$animateJs,
856 $$forceReflow, $$animateAsyncRun, $rootScope) {
859 cancel: $delegate.cancel,
864 return $$forceReflow.totalReflows;
866 enabled: $delegate.enabled,
869 * @name $animate#closeAndFlush
872 * This method will close all pending animations (both {@link ngAnimate#javascript-based-animations Javascript}
873 * and {@link ngAnimate.$animateCss CSS}) and it will also flush any remaining animation frames and/or callbacks.
875 closeAndFlush: function() {
876 // we allow the flush command to swallow the errors
877 // because depending on whether CSS or JS animations are
878 // used, there may not be a RAF flush. The primary flush
879 // at the end of this function must throw an exception
880 // because it will track if there were pending animations
882 $animateCss.$closeAndFlush();
883 $$animateJs.$closeAndFlush();
888 * @name $animate#flush
891 * This method is used to flush the pending callbacks and animation frames to either start
892 * an animation or conclude an animation. Note that this will not actually close an
893 * actively running animation (see {@link ngMock.$animate#closeAndFlush `closeAndFlush()`} for that).
895 flush: function(hideErrors) {
896 $rootScope.$digest();
898 var doNextRun, somethingFlushed = false;
902 if ($$rAF.queue.length) {
904 doNextRun = somethingFlushed = true;
907 if ($$animateAsyncRun.flush()) {
908 doNextRun = somethingFlushed = true;
912 if (!somethingFlushed && !hideErrors) {
913 throw new Error('No pending animations ready to be closed or flushed');
916 $rootScope.$digest();
921 ['animate','enter','leave','move','addClass','removeClass','setClass'], function(method) {
922 animate[method] = function() {
925 element: arguments[0],
926 options: arguments[arguments.length - 1],
929 return $delegate[method].apply($delegate, arguments);
941 * @name angular.mock.dump
944 * *NOTE*: This is not an injectable instance, just a globally available function.
946 * Method for serializing common angular objects (scope, elements, etc..) into strings.
947 * It is useful for logging objects to the console when debugging.
949 * @param {*} object - any object to turn into string.
950 * @return {string} a serialized string of the argument
952 angular.mock.dump = function(object) {
953 return serialize(object);
955 function serialize(object) {
958 if (angular.isElement(object)) {
959 object = angular.element(object);
960 out = angular.element('<div></div>');
961 angular.forEach(object, function(element) {
962 out.append(angular.element(element).clone());
965 } else if (angular.isArray(object)) {
967 angular.forEach(object, function(o) {
968 out.push(serialize(o));
970 out = '[ ' + out.join(', ') + ' ]';
971 } else if (angular.isObject(object)) {
972 if (angular.isFunction(object.$eval) && angular.isFunction(object.$apply)) {
973 out = serializeScope(object);
974 } else if (object instanceof Error) {
975 out = object.stack || ('' + object.name + ': ' + object.message);
977 // TODO(i): this prevents methods being logged,
978 // we should have a better way to serialize objects
979 out = angular.toJson(object, true);
982 out = String(object);
988 function serializeScope(scope, offset) {
989 offset = offset || ' ';
990 var log = [offset + 'Scope(' + scope.$id + '): {'];
991 for (var key in scope) {
992 if (Object.prototype.hasOwnProperty.call(scope, key) && !key.match(/^(\$|this)/)) {
993 log.push(' ' + key + ': ' + angular.toJson(scope[key]));
996 var child = scope.$$childHead;
998 log.push(serializeScope(child, offset + ' '));
999 child = child.$$nextSibling;
1002 return log.join('\n' + offset);
1008 * @name $httpBackend
1010 * Fake HTTP backend implementation suitable for unit testing applications that use the
1011 * {@link ng.$http $http service}.
1013 * <div class="alert alert-info">
1014 * **Note**: For fake HTTP backend implementation suitable for end-to-end testing or backend-less
1015 * development please see {@link ngMockE2E.$httpBackend e2e $httpBackend mock}.
1018 * During unit testing, we want our unit tests to run quickly and have no external dependencies so
1019 * we don’t want to send [XHR](https://developer.mozilla.org/en/xmlhttprequest) or
1020 * [JSONP](http://en.wikipedia.org/wiki/JSONP) requests to a real server. All we really need is
1021 * to verify whether a certain request has been sent or not, or alternatively just let the
1022 * application make requests, respond with pre-trained responses and assert that the end result is
1023 * what we expect it to be.
1025 * This mock implementation can be used to respond with static or dynamic responses via the
1026 * `expect` and `when` apis and their shortcuts (`expectGET`, `whenPOST`, etc).
1028 * When an Angular application needs some data from a server, it calls the $http service, which
1029 * sends the request to a real server using $httpBackend service. With dependency injection, it is
1030 * easy to inject $httpBackend mock (which has the same API as $httpBackend) and use it to verify
1031 * the requests and respond with some testing data without sending a request to a real server.
1033 * There are two ways to specify what test data should be returned as http responses by the mock
1034 * backend when the code under test makes http requests:
1036 * - `$httpBackend.expect` - specifies a request expectation
1037 * - `$httpBackend.when` - specifies a backend definition
1040 * ## Request Expectations vs Backend Definitions
1042 * Request expectations provide a way to make assertions about requests made by the application and
1043 * to define responses for those requests. The test will fail if the expected requests are not made
1044 * or they are made in the wrong order.
1046 * Backend definitions allow you to define a fake backend for your application which doesn't assert
1047 * if a particular request was made or not, it just returns a trained response if a request is made.
1048 * The test will pass whether or not the request gets made during testing.
1051 * <table class="table">
1052 * <tr><th width="220px"></th><th>Request expectations</th><th>Backend definitions</th></tr>
1055 * <td>.expect(...).respond(...)</td>
1056 * <td>.when(...).respond(...)</td>
1059 * <th>Typical usage</th>
1060 * <td>strict unit tests</td>
1061 * <td>loose (black-box) unit testing</td>
1064 * <th>Fulfills multiple requests</th>
1069 * <th>Order of requests matters</th>
1074 * <th>Request required</th>
1079 * <th>Response required</th>
1080 * <td>optional (see below)</td>
1085 * In cases where both backend definitions and request expectations are specified during unit
1086 * testing, the request expectations are evaluated first.
1088 * If a request expectation has no response specified, the algorithm will search your backend
1089 * definitions for an appropriate response.
1091 * If a request didn't match any expectation or if the expectation doesn't have the response
1092 * defined, the backend definitions are evaluated in sequential order to see if any of them match
1093 * the request. The response from the first matched definition is returned.
1096 * ## Flushing HTTP requests
1098 * The $httpBackend used in production always responds to requests asynchronously. If we preserved
1099 * this behavior in unit testing, we'd have to create async unit tests, which are hard to write,
1100 * to follow and to maintain. But neither can the testing mock respond synchronously; that would
1101 * change the execution of the code under test. For this reason, the mock $httpBackend has a
1102 * `flush()` method, which allows the test to explicitly flush pending requests. This preserves
1103 * the async api of the backend, while allowing the test to execute synchronously.
1106 * ## Unit testing with mock $httpBackend
1107 * The following code shows how to setup and use the mock backend when unit testing a controller.
1108 * First we create the controller under test:
1113 .module('MyApp', [])
1114 .controller('MyController', MyController);
1116 // The controller code
1117 function MyController($scope, $http) {
1120 $http.get('/auth.py').then(function(response) {
1121 authToken = response.headers('A-Token');
1122 $scope.user = response.data;
1125 $scope.saveMessage = function(message) {
1126 var headers = { 'Authorization': authToken };
1127 $scope.status = 'Saving...';
1129 $http.post('/add-msg.py', message, { headers: headers } ).then(function(response) {
1131 })['catch'](function() {
1132 $scope.status = 'Failed...';
1138 * Now we setup the mock backend and create the test specs:
1141 // testing controller
1142 describe('MyController', function() {
1143 var $httpBackend, $rootScope, createController, authRequestHandler;
1145 // Set up the module
1146 beforeEach(module('MyApp'));
1148 beforeEach(inject(function($injector) {
1149 // Set up the mock http service responses
1150 $httpBackend = $injector.get('$httpBackend');
1151 // backend definition common for all tests
1152 authRequestHandler = $httpBackend.when('GET', '/auth.py')
1153 .respond({userId: 'userX'}, {'A-Token': 'xxx'});
1155 // Get hold of a scope (i.e. the root scope)
1156 $rootScope = $injector.get('$rootScope');
1157 // The $controller service is used to create instances of controllers
1158 var $controller = $injector.get('$controller');
1160 createController = function() {
1161 return $controller('MyController', {'$scope' : $rootScope });
1166 afterEach(function() {
1167 $httpBackend.verifyNoOutstandingExpectation();
1168 $httpBackend.verifyNoOutstandingRequest();
1172 it('should fetch authentication token', function() {
1173 $httpBackend.expectGET('/auth.py');
1174 var controller = createController();
1175 $httpBackend.flush();
1179 it('should fail authentication', function() {
1181 // Notice how you can change the response even after it was set
1182 authRequestHandler.respond(401, '');
1184 $httpBackend.expectGET('/auth.py');
1185 var controller = createController();
1186 $httpBackend.flush();
1187 expect($rootScope.status).toBe('Failed...');
1191 it('should send msg to server', function() {
1192 var controller = createController();
1193 $httpBackend.flush();
1195 // now you don’t care about the authentication, but
1196 // the controller will still send the request and
1197 // $httpBackend will respond without you having to
1198 // specify the expectation and response for this request
1200 $httpBackend.expectPOST('/add-msg.py', 'message content').respond(201, '');
1201 $rootScope.saveMessage('message content');
1202 expect($rootScope.status).toBe('Saving...');
1203 $httpBackend.flush();
1204 expect($rootScope.status).toBe('');
1208 it('should send auth header', function() {
1209 var controller = createController();
1210 $httpBackend.flush();
1212 $httpBackend.expectPOST('/add-msg.py', undefined, function(headers) {
1213 // check if the header was sent, if it wasn't the expectation won't
1214 // match the request and the test will fail
1215 return headers['Authorization'] === 'xxx';
1216 }).respond(201, '');
1218 $rootScope.saveMessage('whatever');
1219 $httpBackend.flush();
1224 * ## Dynamic responses
1226 * You define a response to a request by chaining a call to `respond()` onto a definition or expectation.
1227 * If you provide a **callback** as the first parameter to `respond(callback)` then you can dynamically generate
1228 * a response based on the properties of the request.
1230 * The `callback` function should be of the form `function(method, url, data, headers, params)`.
1232 * ### Query parameters
1234 * By default, query parameters on request URLs are parsed into the `params` object. So a request URL
1235 * of `/list?q=searchstr&orderby=-name` would set `params` to be `{q: 'searchstr', orderby: '-name'}`.
1237 * ### Regex parameter matching
1239 * If an expectation or definition uses a **regex** to match the URL, you can provide an array of **keys** via a
1240 * `params` argument. The index of each **key** in the array will match the index of a **group** in the
1243 * The `params` object in the **callback** will now have properties with these keys, which hold the value of the
1244 * corresponding **group** in the **regex**.
1246 * This also applies to the `when` and `expect` shortcut methods.
1250 * $httpBackend.expect('GET', /\/user\/(.+)/, undefined, undefined, ['id'])
1251 * .respond(function(method, url, data, headers, params) {
1252 * // for requested url of '/user/1234' params is {id: '1234'}
1255 * $httpBackend.whenPATCH(/\/user\/(.+)\/article\/(.+)/, undefined, undefined, ['user', 'article'])
1256 * .respond(function(method, url, data, headers, params) {
1257 * // for url of '/user/1234/article/567' params is {user: '1234', article: '567'}
1261 * ## Matching route requests
1263 * For extra convenience, `whenRoute` and `expectRoute` shortcuts are available. These methods offer colon
1264 * delimited matching of the url path, ignoring the query string. This allows declarations
1265 * similar to how application routes are configured with `$routeProvider`. Because these methods convert
1266 * the definition url to regex, declaration order is important. Combined with query parameter parsing,
1267 * the following is possible:
1270 $httpBackend.whenRoute('GET', '/users/:id')
1271 .respond(function(method, url, data, headers, params) {
1272 return [200, MockUserList[Number(params.id)]];
1275 $httpBackend.whenRoute('GET', '/users')
1276 .respond(function(method, url, data, headers, params) {
1277 var userList = angular.copy(MockUserList),
1278 defaultSort = 'lastName',
1279 count, pages, isPrevious, isNext;
1281 // paged api response '/v1/users?page=2'
1282 params.page = Number(params.page) || 1;
1284 // query for last names '/v1/users?q=Archer'
1286 userList = $filter('filter')({lastName: params.q});
1289 pages = Math.ceil(userList.length / pagingLength);
1290 isPrevious = params.page > 1;
1291 isNext = params.page < pages;
1294 count: userList.length,
1295 previous: isPrevious,
1297 // sort field -> '/v1/users?sortBy=firstName'
1298 results: $filter('orderBy')(userList, params.sortBy || defaultSort)
1299 .splice((params.page - 1) * pagingLength, pagingLength)
1304 angular.mock.$HttpBackendProvider = function() {
1305 this.$get = ['$rootScope', '$timeout', createHttpBackendMock];
1309 * General factory function for $httpBackend mock.
1310 * Returns instance for unit testing (when no arguments specified):
1311 * - passing through is disabled
1312 * - auto flushing is disabled
1314 * Returns instance for e2e testing (when `$delegate` and `$browser` specified):
1315 * - passing through (delegating request to real backend) is enabled
1316 * - auto flushing is enabled
1318 * @param {Object=} $delegate Real $httpBackend instance (allow passing through if specified)
1319 * @param {Object=} $browser Auto-flushing enabled if specified
1320 * @return {Object} Instance of $httpBackend mock
1322 function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
1323 var definitions = [],
1326 responsesPush = angular.bind(responses, responses.push),
1327 copy = angular.copy;
1329 function createResponse(status, data, headers, statusText) {
1330 if (angular.isFunction(status)) return status;
1333 return angular.isNumber(status)
1334 ? [status, data, headers, statusText]
1335 : [200, status, data, headers];
1339 // TODO(vojta): change params to: method, url, data, headers, callback
1340 function $httpBackend(method, url, data, callback, headers, timeout, withCredentials, responseType, eventHandlers, uploadEventHandlers) {
1342 var xhr = new MockXhr(),
1343 expectation = expectations[0],
1344 wasExpected = false;
1346 xhr.$$events = eventHandlers;
1347 xhr.upload.$$events = uploadEventHandlers;
1349 function prettyPrint(data) {
1350 return (angular.isString(data) || angular.isFunction(data) || data instanceof RegExp)
1352 : angular.toJson(data);
1355 function wrapResponse(wrapped) {
1356 if (!$browser && timeout) {
1358 timeout.then(handleTimeout);
1360 $timeout(handleTimeout, timeout);
1364 return handleResponse;
1366 function handleResponse() {
1367 var response = wrapped.response(method, url, data, headers, wrapped.params(url));
1368 xhr.$$respHeaders = response[2];
1369 callback(copy(response[0]), copy(response[1]), xhr.getAllResponseHeaders(),
1370 copy(response[3] || ''));
1373 function handleTimeout() {
1374 for (var i = 0, ii = responses.length; i < ii; i++) {
1375 if (responses[i] === handleResponse) {
1376 responses.splice(i, 1);
1377 callback(-1, undefined, '');
1384 if (expectation && expectation.match(method, url)) {
1385 if (!expectation.matchData(data)) {
1386 throw new Error('Expected ' + expectation + ' with different data\n' +
1387 'EXPECTED: ' + prettyPrint(expectation.data) + '\nGOT: ' + data);
1390 if (!expectation.matchHeaders(headers)) {
1391 throw new Error('Expected ' + expectation + ' with different headers\n' +
1392 'EXPECTED: ' + prettyPrint(expectation.headers) + '\nGOT: ' +
1393 prettyPrint(headers));
1396 expectations.shift();
1398 if (expectation.response) {
1399 responses.push(wrapResponse(expectation));
1405 var i = -1, definition;
1406 while ((definition = definitions[++i])) {
1407 if (definition.match(method, url, data, headers || {})) {
1408 if (definition.response) {
1409 // if $browser specified, we do auto flush all requests
1410 ($browser ? $browser.defer : responsesPush)(wrapResponse(definition));
1411 } else if (definition.passThrough) {
1412 $delegate(method, url, data, callback, headers, timeout, withCredentials, responseType, eventHandlers, uploadEventHandlers);
1413 } else throw new Error('No response defined !');
1418 new Error('No response defined !') :
1419 new Error('Unexpected request: ' + method + ' ' + url + '\n' +
1420 (expectation ? 'Expected ' + expectation : 'No more request expected'));
1425 * @name $httpBackend#when
1427 * Creates a new backend definition.
1429 * @param {string} method HTTP method.
1430 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1431 * and returns true if the url matches the current definition.
1432 * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
1433 * data string and returns true if the data is as expected.
1434 * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
1435 * object and returns true if the headers match the current definition.
1436 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1437 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1438 * request is handled. You can save this object for later use and invoke `respond` again in
1439 * order to change how a matched request is handled.
1443 * {function([status,] data[, headers, statusText])
1444 * | function(function(method, url, data, headers, params)}
1446 * – The respond method takes a set of static data to be returned or a function that can
1447 * return an array containing response status (number), response data (Array|Object|string),
1448 * response headers (Object), and the text for the status (string). The respond method returns
1449 * the `requestHandler` object for possible overrides.
1451 $httpBackend.when = function(method, url, data, headers, keys) {
1452 var definition = new MockHttpExpectation(method, url, data, headers, keys),
1454 respond: function(status, data, headers, statusText) {
1455 definition.passThrough = undefined;
1456 definition.response = createResponse(status, data, headers, statusText);
1462 chain.passThrough = function() {
1463 definition.response = undefined;
1464 definition.passThrough = true;
1469 definitions.push(definition);
1475 * @name $httpBackend#whenGET
1477 * Creates a new backend definition for GET requests. For more info see `when()`.
1479 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1480 * and returns true if the url matches the current definition.
1481 * @param {(Object|function(Object))=} headers HTTP headers.
1482 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1483 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1484 * request is handled. You can save this object for later use and invoke `respond` again in
1485 * order to change how a matched request is handled.
1490 * @name $httpBackend#whenHEAD
1492 * Creates a new backend definition for HEAD requests. For more info see `when()`.
1494 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1495 * and returns true if the url matches the current definition.
1496 * @param {(Object|function(Object))=} headers HTTP headers.
1497 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1498 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1499 * request is handled. You can save this object for later use and invoke `respond` again in
1500 * order to change how a matched request is handled.
1505 * @name $httpBackend#whenDELETE
1507 * Creates a new backend definition for DELETE requests. For more info see `when()`.
1509 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1510 * and returns true if the url matches the current definition.
1511 * @param {(Object|function(Object))=} headers HTTP headers.
1512 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1513 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1514 * request is handled. You can save this object for later use and invoke `respond` again in
1515 * order to change how a matched request is handled.
1520 * @name $httpBackend#whenPOST
1522 * Creates a new backend definition for POST requests. For more info see `when()`.
1524 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1525 * and returns true if the url matches the current definition.
1526 * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
1527 * data string and returns true if the data is as expected.
1528 * @param {(Object|function(Object))=} headers HTTP headers.
1529 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1530 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1531 * request is handled. You can save this object for later use and invoke `respond` again in
1532 * order to change how a matched request is handled.
1537 * @name $httpBackend#whenPUT
1539 * Creates a new backend definition for PUT requests. For more info see `when()`.
1541 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1542 * and returns true if the url matches the current definition.
1543 * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
1544 * data string and returns true if the data is as expected.
1545 * @param {(Object|function(Object))=} headers HTTP headers.
1546 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1547 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1548 * request is handled. You can save this object for later use and invoke `respond` again in
1549 * order to change how a matched request is handled.
1554 * @name $httpBackend#whenJSONP
1556 * Creates a new backend definition for JSONP requests. For more info see `when()`.
1558 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1559 * and returns true if the url matches the current definition.
1560 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1561 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1562 * request is handled. You can save this object for later use and invoke `respond` again in
1563 * order to change how a matched request is handled.
1565 createShortMethods('when');
1569 * @name $httpBackend#whenRoute
1571 * Creates a new backend definition that compares only with the requested route.
1573 * @param {string} method HTTP method.
1574 * @param {string} url HTTP url string that supports colon param matching.
1575 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1576 * request is handled. You can save this object for later use and invoke `respond` again in
1577 * order to change how a matched request is handled. See #when for more info.
1579 $httpBackend.whenRoute = function(method, url) {
1580 var pathObj = parseRoute(url);
1581 return $httpBackend.when(method, pathObj.regexp, undefined, undefined, pathObj.keys);
1584 function parseRoute(url) {
1588 keys = ret.keys = [];
1590 if (!url || !angular.isString(url)) return ret;
1593 .replace(/([().])/g, '\\$1')
1594 .replace(/(\/)?:(\w+)([?*])?/g, function(_, slash, key, option) {
1595 var optional = option === '?' ? option : null;
1596 var star = option === '*' ? option : null;
1597 keys.push({ name: key, optional: !!optional });
1598 slash = slash || '';
1600 + (optional ? '' : slash)
1602 + (optional ? slash : '')
1603 + (star && '(.+?)' || '([^/]+)')
1608 .replace(/([/$*])/g, '\\$1');
1610 ret.regexp = new RegExp('^' + url, 'i');
1616 * @name $httpBackend#expect
1618 * Creates a new request expectation.
1620 * @param {string} method HTTP method.
1621 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1622 * and returns true if the url matches the current definition.
1623 * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
1624 * receives data string and returns true if the data is as expected, or Object if request body
1625 * is in JSON format.
1626 * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
1627 * object and returns true if the headers match the current expectation.
1628 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1629 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1630 * request is handled. You can save this object for later use and invoke `respond` again in
1631 * order to change how a matched request is handled.
1635 * { function([status,] data[, headers, statusText])
1636 * | function(function(method, url, data, headers, params)}
1638 * – The respond method takes a set of static data to be returned or a function that can
1639 * return an array containing response status (number), response data (Array|Object|string),
1640 * response headers (Object), and the text for the status (string). The respond method returns
1641 * the `requestHandler` object for possible overrides.
1643 $httpBackend.expect = function(method, url, data, headers, keys) {
1644 var expectation = new MockHttpExpectation(method, url, data, headers, keys),
1646 respond: function(status, data, headers, statusText) {
1647 expectation.response = createResponse(status, data, headers, statusText);
1652 expectations.push(expectation);
1658 * @name $httpBackend#expectGET
1660 * Creates a new request expectation for GET requests. For more info see `expect()`.
1662 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1663 * and returns true if the url matches the current definition.
1664 * @param {Object=} headers HTTP headers.
1665 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1666 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1667 * request is handled. You can save this object for later use and invoke `respond` again in
1668 * order to change how a matched request is handled. See #expect for more info.
1673 * @name $httpBackend#expectHEAD
1675 * Creates a new request expectation for HEAD requests. For more info see `expect()`.
1677 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1678 * and returns true if the url matches the current definition.
1679 * @param {Object=} headers HTTP headers.
1680 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1681 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1682 * request is handled. You can save this object for later use and invoke `respond` again in
1683 * order to change how a matched request is handled.
1688 * @name $httpBackend#expectDELETE
1690 * Creates a new request expectation for DELETE requests. For more info see `expect()`.
1692 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1693 * and returns true if the url matches the current definition.
1694 * @param {Object=} headers HTTP headers.
1695 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1696 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1697 * request is handled. You can save this object for later use and invoke `respond` again in
1698 * order to change how a matched request is handled.
1703 * @name $httpBackend#expectPOST
1705 * Creates a new request expectation for POST requests. For more info see `expect()`.
1707 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1708 * and returns true if the url matches the current definition.
1709 * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
1710 * receives data string and returns true if the data is as expected, or Object if request body
1711 * is in JSON format.
1712 * @param {Object=} headers HTTP headers.
1713 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1714 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1715 * request is handled. You can save this object for later use and invoke `respond` again in
1716 * order to change how a matched request is handled.
1721 * @name $httpBackend#expectPUT
1723 * Creates a new request expectation for PUT requests. For more info see `expect()`.
1725 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1726 * and returns true if the url matches the current definition.
1727 * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
1728 * receives data string and returns true if the data is as expected, or Object if request body
1729 * is in JSON format.
1730 * @param {Object=} headers HTTP headers.
1731 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1732 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1733 * request is handled. You can save this object for later use and invoke `respond` again in
1734 * order to change how a matched request is handled.
1739 * @name $httpBackend#expectPATCH
1741 * Creates a new request expectation for PATCH requests. For more info see `expect()`.
1743 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1744 * and returns true if the url matches the current definition.
1745 * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
1746 * receives data string and returns true if the data is as expected, or Object if request body
1747 * is in JSON format.
1748 * @param {Object=} headers HTTP headers.
1749 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1750 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1751 * request is handled. You can save this object for later use and invoke `respond` again in
1752 * order to change how a matched request is handled.
1757 * @name $httpBackend#expectJSONP
1759 * Creates a new request expectation for JSONP requests. For more info see `expect()`.
1761 * @param {string|RegExp|function(string)} url HTTP url or function that receives an url
1762 * and returns true if the url matches the current definition.
1763 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1764 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1765 * request is handled. You can save this object for later use and invoke `respond` again in
1766 * order to change how a matched request is handled.
1768 createShortMethods('expect');
1772 * @name $httpBackend#expectRoute
1774 * Creates a new request expectation that compares only with the requested route.
1776 * @param {string} method HTTP method.
1777 * @param {string} url HTTP url string that supports colon param matching.
1778 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1779 * request is handled. You can save this object for later use and invoke `respond` again in
1780 * order to change how a matched request is handled. See #expect for more info.
1782 $httpBackend.expectRoute = function(method, url) {
1783 var pathObj = parseRoute(url);
1784 return $httpBackend.expect(method, pathObj.regexp, undefined, undefined, pathObj.keys);
1790 * @name $httpBackend#flush
1792 * Flushes pending requests using the trained responses. Requests are flushed in the order they
1793 * were made, but it is also possible to skip one or more requests (for example to have them
1794 * flushed later). This is useful for simulating scenarios where responses arrive from the server
1797 * If there are no pending requests to flush when the method is called, an exception is thrown (as
1798 * this is typically a sign of programming error).
1800 * @param {number=} count - Number of responses to flush. If undefined/null, all pending requests
1801 * (starting after `skip`) will be flushed.
1802 * @param {number=} [skip=0] - Number of pending requests to skip. For example, a value of `5`
1803 * would skip the first 5 pending requests and start flushing from the 6th onwards.
1805 $httpBackend.flush = function(count, skip, digest) {
1806 if (digest !== false) $rootScope.$digest();
1809 if (skip >= responses.length) throw new Error('No pending request to flush !');
1811 if (angular.isDefined(count) && count !== null) {
1813 var part = responses.splice(skip, 1);
1814 if (!part.length) throw new Error('No more pending request to flush !');
1818 while (responses.length > skip) {
1819 responses.splice(skip, 1)[0]();
1822 $httpBackend.verifyNoOutstandingExpectation(digest);
1828 * @name $httpBackend#verifyNoOutstandingExpectation
1830 * Verifies that all of the requests defined via the `expect` api were made. If any of the
1831 * requests were not made, verifyNoOutstandingExpectation throws an exception.
1833 * Typically, you would call this method following each test case that asserts requests using an
1834 * "afterEach" clause.
1837 * afterEach($httpBackend.verifyNoOutstandingExpectation);
1840 $httpBackend.verifyNoOutstandingExpectation = function(digest) {
1841 if (digest !== false) $rootScope.$digest();
1842 if (expectations.length) {
1843 throw new Error('Unsatisfied requests: ' + expectations.join(', '));
1850 * @name $httpBackend#verifyNoOutstandingRequest
1852 * Verifies that there are no outstanding requests that need to be flushed.
1854 * Typically, you would call this method following each test case that asserts requests using an
1855 * "afterEach" clause.
1858 * afterEach($httpBackend.verifyNoOutstandingRequest);
1861 $httpBackend.verifyNoOutstandingRequest = function() {
1862 if (responses.length) {
1863 throw new Error('Unflushed requests: ' + responses.length);
1870 * @name $httpBackend#resetExpectations
1872 * Resets all request expectations, but preserves all backend definitions. Typically, you would
1873 * call resetExpectations during a multiple-phase test when you want to reuse the same instance of
1874 * $httpBackend mock.
1876 $httpBackend.resetExpectations = function() {
1877 expectations.length = 0;
1878 responses.length = 0;
1881 return $httpBackend;
1884 function createShortMethods(prefix) {
1885 angular.forEach(['GET', 'DELETE', 'JSONP', 'HEAD'], function(method) {
1886 $httpBackend[prefix + method] = function(url, headers, keys) {
1887 return $httpBackend[prefix](method, url, undefined, headers, keys);
1891 angular.forEach(['PUT', 'POST', 'PATCH'], function(method) {
1892 $httpBackend[prefix + method] = function(url, data, headers, keys) {
1893 return $httpBackend[prefix](method, url, data, headers, keys);
1899 function MockHttpExpectation(method, url, data, headers, keys) {
1901 function getUrlParams(u) {
1902 var params = u.slice(u.indexOf('?') + 1).split('&');
1903 return params.sort();
1906 function compareUrl(u) {
1907 return (url.slice(0, url.indexOf('?')) === u.slice(0, u.indexOf('?')) &&
1908 getUrlParams(url).join() === getUrlParams(u).join());
1912 this.headers = headers;
1914 this.match = function(m, u, d, h) {
1915 if (method !== m) return false;
1916 if (!this.matchUrl(u)) return false;
1917 if (angular.isDefined(d) && !this.matchData(d)) return false;
1918 if (angular.isDefined(h) && !this.matchHeaders(h)) return false;
1922 this.matchUrl = function(u) {
1923 if (!url) return true;
1924 if (angular.isFunction(url.test)) return url.test(u);
1925 if (angular.isFunction(url)) return url(u);
1926 return (url === u || compareUrl(u));
1929 this.matchHeaders = function(h) {
1930 if (angular.isUndefined(headers)) return true;
1931 if (angular.isFunction(headers)) return headers(h);
1932 return angular.equals(headers, h);
1935 this.matchData = function(d) {
1936 if (angular.isUndefined(data)) return true;
1937 if (data && angular.isFunction(data.test)) return data.test(d);
1938 if (data && angular.isFunction(data)) return data(d);
1939 if (data && !angular.isString(data)) {
1940 return angular.equals(angular.fromJson(angular.toJson(data)), angular.fromJson(d));
1942 // eslint-disable-next-line eqeqeq
1946 this.toString = function() {
1947 return method + ' ' + url;
1950 this.params = function(u) {
1951 return angular.extend(parseQuery(), pathParams());
1953 function pathParams() {
1955 if (!url || !angular.isFunction(url.test) || !keys || keys.length === 0) return keyObj;
1957 var m = url.exec(u);
1958 if (!m) return keyObj;
1959 for (var i = 1, len = m.length; i < len; ++i) {
1960 var key = keys[i - 1];
1963 keyObj[key.name || key] = val;
1970 function parseQuery() {
1971 var obj = {}, key_value, key,
1972 queryStr = u.indexOf('?') > -1
1973 ? u.substring(u.indexOf('?') + 1)
1976 angular.forEach(queryStr.split('&'), function(keyValue) {
1978 key_value = keyValue.replace(/\+/g,'%20').split('=');
1979 key = tryDecodeURIComponent(key_value[0]);
1980 if (angular.isDefined(key)) {
1981 var val = angular.isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true;
1982 if (!hasOwnProperty.call(obj, key)) {
1984 } else if (angular.isArray(obj[key])) {
1987 obj[key] = [obj[key],val];
1994 function tryDecodeURIComponent(value) {
1996 return decodeURIComponent(value);
1998 // Ignore any invalid uri component
2004 function createMockXhr() {
2005 return new MockXhr();
2008 function MockXhr() {
2010 // hack for testing $http, $httpBackend
2011 MockXhr.$$lastInstance = this;
2013 this.open = function(method, url, async) {
2014 this.$$method = method;
2016 this.$$async = async;
2017 this.$$reqHeaders = {};
2018 this.$$respHeaders = {};
2021 this.send = function(data) {
2025 this.setRequestHeader = function(key, value) {
2026 this.$$reqHeaders[key] = value;
2029 this.getResponseHeader = function(name) {
2030 // the lookup must be case insensitive,
2031 // that's why we try two quick lookups first and full scan last
2032 var header = this.$$respHeaders[name];
2033 if (header) return header;
2035 name = angular.lowercase(name);
2036 header = this.$$respHeaders[name];
2037 if (header) return header;
2040 angular.forEach(this.$$respHeaders, function(headerVal, headerName) {
2041 if (!header && angular.lowercase(headerName) === name) header = headerVal;
2046 this.getAllResponseHeaders = function() {
2049 angular.forEach(this.$$respHeaders, function(value, key) {
2050 lines.push(key + ': ' + value);
2052 return lines.join('\n');
2055 this.abort = angular.noop;
2057 // This section simulates the events on a real XHR object (and the upload object)
2058 // When we are testing $httpBackend (inside the angular project) we make partial use of this
2059 // but store the events directly ourselves on `$$events`, instead of going through the `addEventListener`
2061 this.addEventListener = function(name, listener) {
2062 if (angular.isUndefined(this.$$events[name])) this.$$events[name] = [];
2063 this.$$events[name].push(listener);
2068 addEventListener: this.addEventListener
2078 * This service is just a simple decorator for {@link ng.$timeout $timeout} service
2079 * that adds a "flush" and "verifyNoPendingTasks" methods.
2082 angular.mock.$TimeoutDecorator = ['$delegate', '$browser', function($delegate, $browser) {
2086 * @name $timeout#flush
2089 * Flushes the queue of pending tasks.
2091 * @param {number=} delay maximum timeout amount to flush up until
2093 $delegate.flush = function(delay) {
2094 $browser.defer.flush(delay);
2099 * @name $timeout#verifyNoPendingTasks
2102 * Verifies that there are no pending tasks that need to be flushed.
2104 $delegate.verifyNoPendingTasks = function() {
2105 if ($browser.deferredFns.length) {
2106 throw new Error('Deferred tasks to flush (' + $browser.deferredFns.length + '): ' +
2107 formatPendingTasksAsString($browser.deferredFns));
2111 function formatPendingTasksAsString(tasks) {
2113 angular.forEach(tasks, function(task) {
2114 result.push('{id: ' + task.id + ', time: ' + task.time + '}');
2117 return result.join(', ');
2123 angular.mock.$RAFDecorator = ['$delegate', function($delegate) {
2124 var rafFn = function(fn) {
2125 var index = rafFn.queue.length;
2126 rafFn.queue.push(fn);
2128 rafFn.queue.splice(index, 1);
2133 rafFn.supported = $delegate.supported;
2135 rafFn.flush = function() {
2136 if (rafFn.queue.length === 0) {
2137 throw new Error('No rAF callbacks present');
2140 var length = rafFn.queue.length;
2141 for (var i = 0; i < length; i++) {
2145 rafFn.queue = rafFn.queue.slice(i);
2154 var originalRootElement;
2155 angular.mock.$RootElementProvider = function() {
2156 this.$get = ['$injector', function($injector) {
2157 originalRootElement = angular.element('<div ng-app></div>').data('$injector', $injector);
2158 return originalRootElement;
2166 * A decorator for {@link ng.$controller} with additional `bindings` parameter, useful when testing
2167 * controllers of directives that use {@link $compile#-bindtocontroller- `bindToController`}.
2169 * Depending on the value of
2170 * {@link ng.$compileProvider#preAssignBindingsEnabled `preAssignBindingsEnabled()`}, the properties
2171 * will be bound before or after invoking the constructor.
2178 * // Directive definition ...
2180 * myMod.directive('myDirective', {
2181 * controller: 'MyDirectiveController',
2182 * bindToController: {
2188 * // Controller definition ...
2190 * myMod.controller('MyDirectiveController', ['$log', function($log) {
2191 * this.log = function() {
2192 * $log.info(this.name);
2199 * describe('myDirectiveController', function() {
2200 * describe('log()', function() {
2201 * it('should write the bound name to the log', inject(function($controller, $log) {
2202 * var ctrl = $controller('MyDirectiveController', { /* no locals */ }, { name: 'Clark Kent' });
2205 * expect(ctrl.name).toEqual('Clark Kent');
2206 * expect($log.info.logs).toEqual(['Clark Kent']);
2213 * @param {Function|string} constructor If called with a function then it's considered to be the
2214 * controller constructor function. Otherwise it's considered to be a string which is used
2215 * to retrieve the controller constructor using the following steps:
2217 * * check if a controller with given name is registered via `$controllerProvider`
2218 * * check if evaluating the string on the current scope returns a constructor
2219 * * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
2220 * `window` object (not recommended)
2222 * The string can use the `controller as property` syntax, where the controller instance is published
2223 * as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
2224 * to work correctly.
2226 * @param {Object} locals Injection locals for Controller.
2227 * @param {Object=} bindings Properties to add to the controller instance. This is used to simulate
2228 * the `bindToController` feature and simplify certain kinds of tests.
2229 * @return {Object} Instance of given controller.
2231 function createControllerDecorator(compileProvider) {
2232 angular.mock.$ControllerDecorator = ['$delegate', function($delegate) {
2233 return function(expression, locals, later, ident) {
2234 if (later && typeof later === 'object') {
2235 var preAssignBindingsEnabled = compileProvider.preAssignBindingsEnabled();
2237 var instantiate = $delegate(expression, locals, true, ident);
2238 if (preAssignBindingsEnabled) {
2239 angular.extend(instantiate.instance, later);
2242 var instance = instantiate();
2243 if (!preAssignBindingsEnabled || instance !== instantiate.instance) {
2244 angular.extend(instance, later);
2249 return $delegate(expression, locals, later, ident);
2253 return angular.mock.$ControllerDecorator;
2258 * @name $componentController
2260 * A service that can be used to create instances of component controllers. Useful for unit-testing.
2262 * Be aware that the controller will be instantiated and attached to the scope as specified in
2263 * the component definition object. If you do not provide a `$scope` object in the `locals` param
2264 * then the helper will create a new isolated scope as a child of `$rootScope`.
2266 * If you are using `$element` or `$attrs` in the controller, make sure to provide them as `locals`.
2267 * The `$element` must be a jqLite-wrapped DOM element, and `$attrs` should be an object that
2268 * has all properties / functions that you are using in the controller. If this is getting too complex,
2269 * you should compile the component instead and access the component's controller via the
2270 * {@link angular.element#methods `controller`} function.
2272 * See also the section on {@link guide/component#unit-testing-component-controllers unit-testing component controllers}
2275 * @param {string} componentName the name of the component whose controller we want to instantiate
2276 * @param {Object} locals Injection locals for Controller.
2277 * @param {Object=} bindings Properties to add to the controller before invoking the constructor. This is used
2278 * to simulate the `bindToController` feature and simplify certain kinds of tests.
2279 * @param {string=} ident Override the property name to use when attaching the controller to the scope.
2280 * @return {Object} Instance of requested controller.
2282 angular.mock.$ComponentControllerProvider = ['$compileProvider',
2283 function ComponentControllerProvider($compileProvider) {
2284 this.$get = ['$controller','$injector', '$rootScope', function($controller, $injector, $rootScope) {
2285 return function $componentController(componentName, locals, bindings, ident) {
2286 // get all directives associated to the component name
2287 var directives = $injector.get(componentName + 'Directive');
2288 // look for those directives that are components
2289 var candidateDirectives = directives.filter(function(directiveInfo) {
2290 // components have controller, controllerAs and restrict:'E'
2291 return directiveInfo.controller && directiveInfo.controllerAs && directiveInfo.restrict === 'E';
2293 // check if valid directives found
2294 if (candidateDirectives.length === 0) {
2295 throw new Error('No component found');
2297 if (candidateDirectives.length > 1) {
2298 throw new Error('Too many components found');
2300 // get the info of the component
2301 var directiveInfo = candidateDirectives[0];
2302 // create a scope if needed
2303 locals = locals || {};
2304 locals.$scope = locals.$scope || $rootScope.$new(true);
2305 return $controller(directiveInfo.controller, locals, bindings, ident || directiveInfo.controllerAs);
2314 * @packageName angular-mocks
2319 * The `ngMock` module provides support to inject and mock Angular services into unit tests.
2320 * In addition, ngMock also extends various core ng services such that they can be
2321 * inspected and controlled in a synchronous manner within test code.
2324 * <div doc-module-components="ngMock"></div>
2328 * First, download the file:
2329 * * [Google CDN](https://developers.google.com/speed/libraries/devguide#angularjs) e.g.
2330 * `"//ajax.googleapis.com/ajax/libs/angularjs/X.Y.Z/angular-mocks.js"`
2331 * * [NPM](https://www.npmjs.com/) e.g. `npm install angular-mocks@X.Y.Z`
2332 * * [Yarn](https://yarnpkg.com) e.g. `yarn add angular-mocks@X.Y.Z`
2333 * * [Bower](http://bower.io) e.g. `bower install angular-mocks#X.Y.Z`
2334 * * [code.angularjs.org](https://code.angularjs.org/) (discouraged for production use) e.g.
2335 * `"//code.angularjs.org/X.Y.Z/angular-mocks.js"`
2337 * where X.Y.Z is the AngularJS version you are running.
2339 * Then, configure your test runner to load `angular-mocks.js` after `angular.js`.
2340 * This example uses <a href="http://karma-runner.github.io/">Karma</a>:
2345 * 'build/angular.js', // and other module files you need
2346 * 'build/angular-mocks.js',
2347 * '<path/to/application/files>',
2348 * '<path/to/spec/files>'
2353 * Including the `angular-mocks.js` file automatically adds the `ngMock` module, so your tests
2356 angular.module('ngMock', ['ng']).provider({
2357 $browser: angular.mock.$BrowserProvider,
2358 $exceptionHandler: angular.mock.$ExceptionHandlerProvider,
2359 $log: angular.mock.$LogProvider,
2360 $interval: angular.mock.$IntervalProvider,
2361 $httpBackend: angular.mock.$HttpBackendProvider,
2362 $rootElement: angular.mock.$RootElementProvider,
2363 $componentController: angular.mock.$ComponentControllerProvider
2364 }).config(['$provide', '$compileProvider', function($provide, $compileProvider) {
2365 $provide.decorator('$timeout', angular.mock.$TimeoutDecorator);
2366 $provide.decorator('$$rAF', angular.mock.$RAFDecorator);
2367 $provide.decorator('$rootScope', angular.mock.$RootScopeDecorator);
2368 $provide.decorator('$controller', createControllerDecorator($compileProvider));
2375 * @packageName angular-mocks
2378 * The `ngMockE2E` is an angular module which contains mocks suitable for end-to-end testing.
2379 * Currently there is only one mock present in this module -
2380 * the {@link ngMockE2E.$httpBackend e2e $httpBackend} mock.
2382 angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
2383 $provide.value('$httpBackend', angular.injector(['ng']).get('$httpBackend'));
2384 $provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator);
2389 * @name $httpBackend
2392 * Fake HTTP backend implementation suitable for end-to-end testing or backend-less development of
2393 * applications that use the {@link ng.$http $http service}.
2395 * <div class="alert alert-info">
2396 * **Note**: For fake http backend implementation suitable for unit testing please see
2397 * {@link ngMock.$httpBackend unit-testing $httpBackend mock}.
2400 * This implementation can be used to respond with static or dynamic responses via the `when` api
2401 * and its shortcuts (`whenGET`, `whenPOST`, etc) and optionally pass through requests to the
2402 * real $httpBackend for specific requests (e.g. to interact with certain remote apis or to fetch
2403 * templates from a webserver).
2405 * As opposed to unit-testing, in an end-to-end testing scenario or in scenario when an application
2406 * is being developed with the real backend api replaced with a mock, it is often desirable for
2407 * certain category of requests to bypass the mock and issue a real http request (e.g. to fetch
2408 * templates or static files from the webserver). To configure the backend with this behavior
2409 * use the `passThrough` request handler of `when` instead of `respond`.
2411 * Additionally, we don't want to manually have to flush mocked out requests like we do during unit
2412 * testing. For this reason the e2e $httpBackend flushes mocked out requests
2413 * automatically, closely simulating the behavior of the XMLHttpRequest object.
2415 * To setup the application to run with this http backend, you have to create a module that depends
2416 * on the `ngMockE2E` and your application modules and defines the fake backend:
2419 * var myAppDev = angular.module('myAppDev', ['myApp', 'ngMockE2E']);
2420 * myAppDev.run(function($httpBackend) {
2421 * var phones = [{name: 'phone1'}, {name: 'phone2'}];
2423 * // returns the current list of phones
2424 * $httpBackend.whenGET('/phones').respond(phones);
2426 * // adds a new phone to the phones array
2427 * $httpBackend.whenPOST('/phones').respond(function(method, url, data) {
2428 * var phone = angular.fromJson(data);
2429 * phones.push(phone);
2430 * return [200, phone, {}];
2432 * $httpBackend.whenGET(/^\/templates\//).passThrough(); // Requests for templare are handled by the real server
2437 * Afterwards, bootstrap your app with this new module.
2440 * <example name="httpbackend-e2e-testing" module="myAppE2E" deps="angular-mocks.js">
2441 * <file name="app.js">
2442 * var myApp = angular.module('myApp', []);
2444 * myApp.controller('MainCtrl', function MainCtrl($http) {
2452 * ctrl.getPhones = function() {
2453 * $http.get('/phones').then(function(response) {
2454 * ctrl.phones = response.data;
2458 * ctrl.addPhone = function(phone) {
2459 * $http.post('/phones', phone).then(function() {
2460 * ctrl.newPhone = {name: ''};
2461 * return ctrl.getPhones();
2468 * <file name="e2e.js">
2469 * var myAppDev = angular.module('myAppE2E', ['myApp', 'ngMockE2E']);
2471 * myAppDev.run(function($httpBackend) {
2472 * var phones = [{name: 'phone1'}, {name: 'phone2'}];
2474 * // returns the current list of phones
2475 * $httpBackend.whenGET('/phones').respond(phones);
2477 * // adds a new phone to the phones array
2478 * $httpBackend.whenPOST('/phones').respond(function(method, url, data) {
2479 * var phone = angular.fromJson(data);
2480 * phones.push(phone);
2481 * return [200, phone, {}];
2485 * <file name="index.html">
2486 * <div ng-controller="MainCtrl as $ctrl">
2487 * <form name="newPhoneForm" ng-submit="$ctrl.addPhone($ctrl.newPhone)">
2488 * <input type="text" ng-model="$ctrl.newPhone.name">
2489 * <input type="submit" value="Add Phone">
2493 * <li ng-repeat="phone in $ctrl.phones">{{phone.name}}</li>
2504 * @name $httpBackend#when
2507 * Creates a new backend definition.
2509 * @param {string} method HTTP method.
2510 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
2511 * and returns true if the url matches the current definition.
2512 * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
2513 * data string and returns true if the data is as expected.
2514 * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
2515 * object and returns true if the headers match the current definition.
2516 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
2517 * {@link ngMock.$httpBackend $httpBackend mock}.
2518 * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2519 * control how a matched request is handled. You can save this object for later use and invoke
2520 * `respond` or `passThrough` again in order to change how a matched request is handled.
2524 * { function([status,] data[, headers, statusText])
2525 * | function(function(method, url, data, headers, params)}
2527 * – The respond method takes a set of static data to be returned or a function that can return
2528 * an array containing response status (number), response data (Array|Object|string), response
2529 * headers (Object), and the text for the status (string).
2530 * - passThrough – `{function()}` – Any request matching a backend definition with
2531 * `passThrough` handler will be passed through to the real backend (an XHR request will be made
2533 * - Both methods return the `requestHandler` object for possible overrides.
2538 * @name $httpBackend#whenGET
2541 * Creates a new backend definition for GET requests. For more info see `when()`.
2543 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
2544 * and returns true if the url matches the current definition.
2545 * @param {(Object|function(Object))=} headers HTTP headers.
2546 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
2547 * {@link ngMock.$httpBackend $httpBackend mock}.
2548 * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2549 * control how a matched request is handled. You can save this object for later use and invoke
2550 * `respond` or `passThrough` again in order to change how a matched request is handled.
2555 * @name $httpBackend#whenHEAD
2558 * Creates a new backend definition for HEAD requests. For more info see `when()`.
2560 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
2561 * and returns true if the url matches the current definition.
2562 * @param {(Object|function(Object))=} headers HTTP headers.
2563 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
2564 * {@link ngMock.$httpBackend $httpBackend mock}.
2565 * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2566 * control how a matched request is handled. You can save this object for later use and invoke
2567 * `respond` or `passThrough` again in order to change how a matched request is handled.
2572 * @name $httpBackend#whenDELETE
2575 * Creates a new backend definition for DELETE requests. For more info see `when()`.
2577 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
2578 * and returns true if the url matches the current definition.
2579 * @param {(Object|function(Object))=} headers HTTP headers.
2580 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
2581 * {@link ngMock.$httpBackend $httpBackend mock}.
2582 * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2583 * control how a matched request is handled. You can save this object for later use and invoke
2584 * `respond` or `passThrough` again in order to change how a matched request is handled.
2589 * @name $httpBackend#whenPOST
2592 * Creates a new backend definition for POST requests. For more info see `when()`.
2594 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
2595 * and returns true if the url matches the current definition.
2596 * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
2597 * data string and returns true if the data is as expected.
2598 * @param {(Object|function(Object))=} headers HTTP headers.
2599 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
2600 * {@link ngMock.$httpBackend $httpBackend mock}.
2601 * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2602 * control how a matched request is handled. You can save this object for later use and invoke
2603 * `respond` or `passThrough` again in order to change how a matched request is handled.
2608 * @name $httpBackend#whenPUT
2611 * Creates a new backend definition for PUT requests. For more info see `when()`.
2613 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
2614 * and returns true if the url matches the current definition.
2615 * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
2616 * data string and returns true if the data is as expected.
2617 * @param {(Object|function(Object))=} headers HTTP headers.
2618 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
2619 * {@link ngMock.$httpBackend $httpBackend mock}.
2620 * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2621 * control how a matched request is handled. You can save this object for later use and invoke
2622 * `respond` or `passThrough` again in order to change how a matched request is handled.
2627 * @name $httpBackend#whenPATCH
2630 * Creates a new backend definition for PATCH requests. For more info see `when()`.
2632 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
2633 * and returns true if the url matches the current definition.
2634 * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
2635 * data string and returns true if the data is as expected.
2636 * @param {(Object|function(Object))=} headers HTTP headers.
2637 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
2638 * {@link ngMock.$httpBackend $httpBackend mock}.
2639 * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2640 * control how a matched request is handled. You can save this object for later use and invoke
2641 * `respond` or `passThrough` again in order to change how a matched request is handled.
2646 * @name $httpBackend#whenJSONP
2649 * Creates a new backend definition for JSONP requests. For more info see `when()`.
2651 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
2652 * and returns true if the url matches the current definition.
2653 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
2654 * {@link ngMock.$httpBackend $httpBackend mock}.
2655 * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2656 * control how a matched request is handled. You can save this object for later use and invoke
2657 * `respond` or `passThrough` again in order to change how a matched request is handled.
2661 * @name $httpBackend#whenRoute
2664 * Creates a new backend definition that compares only with the requested route.
2666 * @param {string} method HTTP method.
2667 * @param {string} url HTTP url string that supports colon param matching.
2668 * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2669 * control how a matched request is handled. You can save this object for later use and invoke
2670 * `respond` or `passThrough` again in order to change how a matched request is handled.
2672 angular.mock.e2e = {};
2673 angular.mock.e2e.$httpBackendDecorator =
2674 ['$rootScope', '$timeout', '$delegate', '$browser', createHttpBackendMock];
2679 * @name $rootScope.Scope
2682 * {@link ng.$rootScope.Scope Scope} type decorated with helper methods useful for testing. These
2683 * methods are automatically available on any {@link ng.$rootScope.Scope Scope} instance when
2684 * `ngMock` module is loaded.
2686 * In addition to all the regular `Scope` methods, the following helper methods are available:
2688 angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
2690 var $rootScopePrototype = Object.getPrototypeOf($delegate);
2692 $rootScopePrototype.$countChildScopes = countChildScopes;
2693 $rootScopePrototype.$countWatchers = countWatchers;
2697 // ------------------------------------------------------------------------------------------ //
2701 * @name $rootScope.Scope#$countChildScopes
2703 * @this $rootScope.Scope
2705 * Counts all the direct and indirect child scopes of the current scope.
2707 * The current scope is excluded from the count. The count includes all isolate child scopes.
2709 * @returns {number} Total number of child scopes.
2711 function countChildScopes() {
2712 var count = 0; // exclude the current scope
2713 var pendingChildHeads = [this.$$childHead];
2716 while (pendingChildHeads.length) {
2717 currentScope = pendingChildHeads.shift();
2719 while (currentScope) {
2721 pendingChildHeads.push(currentScope.$$childHead);
2722 currentScope = currentScope.$$nextSibling;
2732 * @name $rootScope.Scope#$countWatchers
2733 * @this $rootScope.Scope
2736 * Counts all the watchers of direct and indirect child scopes of the current scope.
2738 * The watchers of the current scope are included in the count and so are all the watchers of
2739 * isolate child scopes.
2741 * @returns {number} Total number of watchers.
2743 function countWatchers() {
2744 var count = this.$$watchers ? this.$$watchers.length : 0; // include the current scope
2745 var pendingChildHeads = [this.$$childHead];
2748 while (pendingChildHeads.length) {
2749 currentScope = pendingChildHeads.shift();
2751 while (currentScope) {
2752 count += currentScope.$$watchers ? currentScope.$$watchers.length : 0;
2753 pendingChildHeads.push(currentScope.$$childHead);
2754 currentScope = currentScope.$$nextSibling;
2763 (function(jasmineOrMocha) {
2765 if (!jasmineOrMocha) {
2769 var currentSpec = null,
2770 injectorState = new InjectorState(),
2771 annotatedFunctions = [],
2772 wasInjectorCreated = function() {
2773 return !!currentSpec;
2776 angular.mock.$$annotate = angular.injector.$$annotate;
2777 angular.injector.$$annotate = function(fn) {
2778 if (typeof fn === 'function' && !fn.$inject) {
2779 annotatedFunctions.push(fn);
2781 return angular.mock.$$annotate.apply(this, arguments);
2786 * @name angular.mock.module
2789 * *NOTE*: This function is also published on window for easy access.<br>
2790 * *NOTE*: This function is declared ONLY WHEN running tests with jasmine or mocha
2792 * This function registers a module configuration code. It collects the configuration information
2793 * which will be used when the injector is created by {@link angular.mock.inject inject}.
2795 * See {@link angular.mock.inject inject} for usage example
2797 * @param {...(string|Function|Object)} fns any number of modules which are represented as string
2798 * aliases or as anonymous module initialization functions. The modules are used to
2799 * configure the injector. The 'ng' and 'ngMock' modules are automatically loaded. If an
2800 * object literal is passed each key-value pair will be registered on the module via
2801 * {@link auto.$provide $provide}.value, the key being the string name (or token) to associate
2802 * with the value on the injector.
2804 var module = window.module = angular.mock.module = function() {
2805 var moduleFns = Array.prototype.slice.call(arguments, 0);
2806 return wasInjectorCreated() ? workFn() : workFn;
2807 /////////////////////
2809 if (currentSpec.$injector) {
2810 throw new Error('Injector already created, can not register a module!');
2812 var fn, modules = currentSpec.$modules || (currentSpec.$modules = []);
2813 angular.forEach(moduleFns, function(module) {
2814 if (angular.isObject(module) && !angular.isArray(module)) {
2815 fn = ['$provide', function($provide) {
2816 angular.forEach(module, function(value, key) {
2817 $provide.value(key, value);
2823 if (currentSpec.$providerInjector) {
2824 currentSpec.$providerInjector.invoke(fn);
2833 module.$$beforeAllHook = (window.before || window.beforeAll);
2834 module.$$afterAllHook = (window.after || window.afterAll);
2836 // purely for testing ngMock itself
2837 module.$$currentSpec = function(to) {
2838 if (arguments.length === 0) return to;
2844 * @name angular.mock.module.sharedInjector
2847 * *NOTE*: This function is declared ONLY WHEN running tests with jasmine or mocha
2849 * This function ensures a single injector will be used for all tests in a given describe context.
2850 * This contrasts with the default behaviour where a new injector is created per test case.
2852 * Use sharedInjector when you want to take advantage of Jasmine's `beforeAll()`, or mocha's
2853 * `before()` methods. Call `module.sharedInjector()` before you setup any other hooks that
2854 * will create (i.e call `module()`) or use (i.e call `inject()`) the injector.
2856 * You cannot call `sharedInjector()` from within a context already using `sharedInjector()`.
2860 * Typically beforeAll is used to make many assertions about a single operation. This can
2861 * cut down test run-time as the test setup doesn't need to be re-run, and enabling focussed
2862 * tests each with a single assertion.
2865 * describe("Deep Thought", function() {
2867 * module.sharedInjector();
2869 * beforeAll(module("UltimateQuestion"));
2871 * beforeAll(inject(function(DeepThought) {
2872 * expect(DeepThought.answer).toBeUndefined();
2873 * DeepThought.generateAnswer();
2876 * it("has calculated the answer correctly", inject(function(DeepThought) {
2877 * // Because of sharedInjector, we have access to the instance of the DeepThought service
2878 * // that was provided to the beforeAll() hook. Therefore we can test the generated answer
2879 * expect(DeepThought.answer).toBe(42);
2882 * it("has calculated the answer within the expected time", inject(function(DeepThought) {
2883 * expect(DeepThought.runTimeMillennia).toBeLessThan(8000);
2886 * it("has double checked the answer", inject(function(DeepThought) {
2887 * expect(DeepThought.absolutelySureItIsTheRightAnswer).toBe(true);
2894 module.sharedInjector = function() {
2895 if (!(module.$$beforeAllHook && module.$$afterAllHook)) {
2896 throw Error('sharedInjector() cannot be used unless your test runner defines beforeAll/afterAll');
2899 var initialized = false;
2901 module.$$beforeAllHook(/** @this */ function() {
2902 if (injectorState.shared) {
2903 injectorState.sharedError = Error('sharedInjector() cannot be called inside a context that has already called sharedInjector()');
2904 throw injectorState.sharedError;
2908 injectorState.shared = true;
2911 module.$$afterAllHook(function() {
2913 injectorState = new InjectorState();
2916 injectorState.sharedError = null;
2921 module.$$beforeEach = function() {
2922 if (injectorState.shared && currentSpec && currentSpec !== this) {
2923 var state = currentSpec;
2925 angular.forEach(['$injector','$modules','$providerInjector', '$injectorStrict'], function(k) {
2926 currentSpec[k] = state[k];
2931 originalRootElement = null;
2932 annotatedFunctions = [];
2936 module.$$afterEach = function() {
2937 if (injectorState.cleanupAfterEach()) {
2942 module.$$cleanup = function() {
2943 var injector = currentSpec.$injector;
2945 annotatedFunctions.forEach(function(fn) {
2949 angular.forEach(currentSpec.$modules, function(module) {
2950 if (module && module.$$hashKey) {
2951 module.$$hashKey = undefined;
2955 currentSpec.$injector = null;
2956 currentSpec.$modules = null;
2957 currentSpec.$providerInjector = null;
2961 // Ensure `$rootElement` is instantiated, before checking `originalRootElement`
2962 var $rootElement = injector.get('$rootElement');
2963 var rootNode = $rootElement && $rootElement[0];
2964 var cleanUpNodes = !originalRootElement ? [] : [originalRootElement[0]];
2965 if (rootNode && (!originalRootElement || rootNode !== originalRootElement[0])) {
2966 cleanUpNodes.push(rootNode);
2968 angular.element.cleanData(cleanUpNodes);
2970 // Ensure `$destroy()` is available, before calling it
2971 // (a mocked `$rootScope` might not implement it (or not even be an object at all))
2972 var $rootScope = injector.get('$rootScope');
2973 if ($rootScope && $rootScope.$destroy) $rootScope.$destroy();
2976 // clean up jquery's fragment cache
2977 angular.forEach(angular.element.fragments, function(val, key) {
2978 delete angular.element.fragments[key];
2981 MockXhr.$$lastInstance = null;
2983 angular.forEach(angular.callbacks, function(val, key) {
2984 delete angular.callbacks[key];
2986 angular.callbacks.$$counter = 0;
2989 (window.beforeEach || window.setup)(module.$$beforeEach);
2990 (window.afterEach || window.teardown)(module.$$afterEach);
2994 * @name angular.mock.inject
2997 * *NOTE*: This function is also published on window for easy access.<br>
2998 * *NOTE*: This function is declared ONLY WHEN running tests with jasmine or mocha
3000 * The inject function wraps a function into an injectable function. The inject() creates new
3001 * instance of {@link auto.$injector $injector} per test, which is then used for
3002 * resolving references.
3005 * ## Resolving References (Underscore Wrapping)
3006 * Often, we would like to inject a reference once, in a `beforeEach()` block and reuse this
3007 * in multiple `it()` clauses. To be able to do this we must assign the reference to a variable
3008 * that is declared in the scope of the `describe()` block. Since we would, most likely, want
3009 * the variable to have the same name of the reference we have a problem, since the parameter
3010 * to the `inject()` function would hide the outer variable.
3012 * To help with this, the injected parameters can, optionally, be enclosed with underscores.
3013 * These are ignored by the injector when the reference name is resolved.
3015 * For example, the parameter `_myService_` would be resolved as the reference `myService`.
3016 * Since it is available in the function body as `_myService_`, we can then assign it to a variable
3017 * defined in an outer scope.
3020 * // Defined out reference variable outside
3023 * // Wrap the parameter in underscores
3024 * beforeEach( inject( function(_myService_){
3025 * myService = _myService_;
3028 * // Use myService in a series of tests.
3029 * it('makes use of myService', function() {
3030 * myService.doStuff();
3035 * See also {@link angular.mock.module angular.mock.module}
3038 * Example of what a typical jasmine tests looks like with the inject method.
3041 * angular.module('myApplicationModule', [])
3042 * .value('mode', 'app')
3043 * .value('version', 'v1.0.1');
3046 * describe('MyApp', function() {
3048 * // You need to load modules that you want to test,
3049 * // it loads only the "ng" module by default.
3050 * beforeEach(module('myApplicationModule'));
3053 * // inject() is used to inject arguments of all given functions
3054 * it('should provide a version', inject(function(mode, version) {
3055 * expect(version).toEqual('v1.0.1');
3056 * expect(mode).toEqual('app');
3060 * // The inject and module method can also be used inside of the it or beforeEach
3061 * it('should override a version and test the new version is injected', function() {
3062 * // module() takes functions or strings (module aliases)
3063 * module(function($provide) {
3064 * $provide.value('version', 'overridden'); // override version here
3067 * inject(function(version) {
3068 * expect(version).toEqual('overridden');
3075 * @param {...Function} fns any number of functions which will be injected using the injector.
3080 var ErrorAddingDeclarationLocationStack = function ErrorAddingDeclarationLocationStack(e, errorForStack) {
3081 this.message = e.message;
3083 if (e.line) this.line = e.line;
3084 if (e.sourceId) this.sourceId = e.sourceId;
3085 if (e.stack && errorForStack)
3086 this.stack = e.stack + '\n' + errorForStack.stack;
3087 if (e.stackArray) this.stackArray = e.stackArray;
3089 ErrorAddingDeclarationLocationStack.prototype = Error.prototype;
3091 window.inject = angular.mock.inject = function() {
3092 var blockFns = Array.prototype.slice.call(arguments, 0);
3093 var errorForStack = new Error('Declaration Location');
3094 // IE10+ and PhanthomJS do not set stack trace information, until the error is thrown
3095 if (!errorForStack.stack) {
3097 throw errorForStack;
3098 } catch (e) { /* empty */ }
3100 return wasInjectorCreated() ? WorkFn.call(currentSpec) : WorkFn;
3101 /////////////////////
3103 var modules = currentSpec.$modules || [];
3104 var strictDi = !!currentSpec.$injectorStrict;
3105 modules.unshift(['$injector', function($injector) {
3106 currentSpec.$providerInjector = $injector;
3108 modules.unshift('ngMock');
3109 modules.unshift('ng');
3110 var injector = currentSpec.$injector;
3113 // If strictDi is enabled, annotate the providerInjector blocks
3114 angular.forEach(modules, function(moduleFn) {
3115 if (typeof moduleFn === 'function') {
3116 angular.injector.$$annotate(moduleFn);
3120 injector = currentSpec.$injector = angular.injector(modules, strictDi);
3121 currentSpec.$injectorStrict = strictDi;
3123 for (var i = 0, ii = blockFns.length; i < ii; i++) {
3124 if (currentSpec.$injectorStrict) {
3125 // If the injector is strict / strictDi, and the spec wants to inject using automatic
3126 // annotation, then annotate the function here.
3127 injector.annotate(blockFns[i]);
3130 injector.invoke(blockFns[i] || angular.noop, this);
3132 if (e.stack && errorForStack) {
3133 throw new ErrorAddingDeclarationLocationStack(e, errorForStack);
3137 errorForStack = null;
3144 angular.mock.inject.strictDi = function(value) {
3145 value = arguments.length ? !!value : true;
3146 return wasInjectorCreated() ? workFn() : workFn;
3149 if (value !== currentSpec.$injectorStrict) {
3150 if (currentSpec.$injector) {
3151 throw new Error('Injector already created, can not modify strict annotations');
3153 currentSpec.$injectorStrict = value;
3159 function InjectorState() {
3160 this.shared = false;
3161 this.sharedError = null;
3163 this.cleanupAfterEach = function() {
3164 return !this.shared || this.sharedError;
3167 })(window.jasmine || window.mocha);
3170 })(window, window.angular);