2 * @license AngularJS v1.5.8
3 * (c) 2010-2016 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;});
562 /* The R_ISO8061_STR regex is never going to fit into the 100 char limit!
563 * This directive should go inside the anonymous function but a bug in JSHint means that it would
564 * not be enacted early enough to prevent the warning.
566 var R_ISO8061_STR = /^(-?\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?:\:?(\d\d)(?:\:?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/;
568 function jsonStringToDate(string) {
570 if (match = string.match(R_ISO8061_STR)) {
571 var date = new Date(0),
575 tzHour = toInt(match[9] + match[10]);
576 tzMin = toInt(match[9] + match[11]);
578 date.setUTCFullYear(toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
579 date.setUTCHours(toInt(match[4] || 0) - tzHour,
580 toInt(match[5] || 0) - tzMin,
581 toInt(match[6] || 0),
582 toInt(match[7] || 0));
588 function toInt(str) {
589 return parseInt(str, 10);
592 function padNumberInMock(num, digits, trim) {
599 while (num.length < digits) num = '0' + num;
601 num = num.substr(num.length - digits);
609 * @name angular.mock.TzDate
612 * *NOTE*: this is not an injectable instance, just a globally available mock class of `Date`.
614 * Mock of the Date type which has its timezone specified via constructor arg.
616 * The main purpose is to create Date-like instances with timezone fixed to the specified timezone
617 * offset, so that we can test code that depends on local timezone settings without dependency on
618 * the time zone settings of the machine where the code is running.
620 * @param {number} offset Offset of the *desired* timezone in hours (fractions will be honored)
621 * @param {(number|string)} timestamp Timestamp representing the desired time in *UTC*
625 * This is not a complete Date object so only methods that were implemented can be called safely.
626 * To make matters worse, TzDate instances inherit stuff from Date via a prototype.
628 * We do our best to intercept calls to "unimplemented" methods, but since the list of methods is
629 * incomplete we might be missing some non-standard methods. This can result in errors like:
630 * "Date.prototype.foo called on incompatible Object".
633 * var newYearInBratislava = new TzDate(-1, '2009-12-31T23:00:00Z');
634 * newYearInBratislava.getTimezoneOffset() => -60;
635 * newYearInBratislava.getFullYear() => 2010;
636 * newYearInBratislava.getMonth() => 0;
637 * newYearInBratislava.getDate() => 1;
638 * newYearInBratislava.getHours() => 0;
639 * newYearInBratislava.getMinutes() => 0;
640 * newYearInBratislava.getSeconds() => 0;
644 angular.mock.TzDate = function(offset, timestamp) {
645 var self = new Date(0);
646 if (angular.isString(timestamp)) {
647 var tsStr = timestamp;
649 self.origDate = jsonStringToDate(timestamp);
651 timestamp = self.origDate.getTime();
652 if (isNaN(timestamp)) {
654 name: "Illegal Argument",
655 message: "Arg '" + tsStr + "' passed into TzDate constructor is not a valid date string"
659 self.origDate = new Date(timestamp);
662 var localOffset = new Date(timestamp).getTimezoneOffset();
663 self.offsetDiff = localOffset * 60 * 1000 - offset * 1000 * 60 * 60;
664 self.date = new Date(timestamp + self.offsetDiff);
666 self.getTime = function() {
667 return self.date.getTime() - self.offsetDiff;
670 self.toLocaleDateString = function() {
671 return self.date.toLocaleDateString();
674 self.getFullYear = function() {
675 return self.date.getFullYear();
678 self.getMonth = function() {
679 return self.date.getMonth();
682 self.getDate = function() {
683 return self.date.getDate();
686 self.getHours = function() {
687 return self.date.getHours();
690 self.getMinutes = function() {
691 return self.date.getMinutes();
694 self.getSeconds = function() {
695 return self.date.getSeconds();
698 self.getMilliseconds = function() {
699 return self.date.getMilliseconds();
702 self.getTimezoneOffset = function() {
706 self.getUTCFullYear = function() {
707 return self.origDate.getUTCFullYear();
710 self.getUTCMonth = function() {
711 return self.origDate.getUTCMonth();
714 self.getUTCDate = function() {
715 return self.origDate.getUTCDate();
718 self.getUTCHours = function() {
719 return self.origDate.getUTCHours();
722 self.getUTCMinutes = function() {
723 return self.origDate.getUTCMinutes();
726 self.getUTCSeconds = function() {
727 return self.origDate.getUTCSeconds();
730 self.getUTCMilliseconds = function() {
731 return self.origDate.getUTCMilliseconds();
734 self.getDay = function() {
735 return self.date.getDay();
738 // provide this method only on browsers that already have it
739 if (self.toISOString) {
740 self.toISOString = function() {
741 return padNumberInMock(self.origDate.getUTCFullYear(), 4) + '-' +
742 padNumberInMock(self.origDate.getUTCMonth() + 1, 2) + '-' +
743 padNumberInMock(self.origDate.getUTCDate(), 2) + 'T' +
744 padNumberInMock(self.origDate.getUTCHours(), 2) + ':' +
745 padNumberInMock(self.origDate.getUTCMinutes(), 2) + ':' +
746 padNumberInMock(self.origDate.getUTCSeconds(), 2) + '.' +
747 padNumberInMock(self.origDate.getUTCMilliseconds(), 3) + 'Z';
751 //hide all methods not implemented in this mock that the Date prototype exposes
752 var unimplementedMethods = ['getUTCDay',
753 'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds',
754 'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear',
755 'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds',
756 'setYear', 'toDateString', 'toGMTString', 'toJSON', 'toLocaleFormat', 'toLocaleString',
757 'toLocaleTimeString', 'toSource', 'toString', 'toTimeString', 'toUTCString', 'valueOf'];
759 angular.forEach(unimplementedMethods, function(methodName) {
760 self[methodName] = function() {
761 throw new Error("Method '" + methodName + "' is not implemented in the TzDate mock");
768 //make "tzDateInstance instanceof Date" return true
769 angular.mock.TzDate.prototype = Date.prototype;
778 * Mock implementation of the {@link ng.$animate `$animate`} service. Exposes two additional methods
779 * for testing animations.
781 * You need to require the `ngAnimateMock` module in your test suite for instance `beforeEach(module('ngAnimateMock'))`
783 angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
785 .config(['$provide', function($provide) {
787 $provide.factory('$$forceReflow', function() {
788 function reflowFn() {
789 reflowFn.totalReflows++;
791 reflowFn.totalReflows = 0;
795 $provide.factory('$$animateAsyncRun', function() {
797 var queueFn = function() {
798 return function(fn) {
802 queueFn.flush = function() {
803 if (queue.length === 0) return false;
805 for (var i = 0; i < queue.length; i++) {
815 $provide.decorator('$$animateJs', ['$delegate', function($delegate) {
818 var animateJsConstructor = function() {
819 var animator = $delegate.apply($delegate, arguments);
820 // If no javascript animation is found, animator is undefined
822 runners.push(animator);
827 animateJsConstructor.$closeAndFlush = function() {
828 runners.forEach(function(runner) {
834 return animateJsConstructor;
837 $provide.decorator('$animateCss', ['$delegate', function($delegate) {
840 var animateCssConstructor = function(element, options) {
841 var animator = $delegate(element, options);
842 runners.push(animator);
846 animateCssConstructor.$closeAndFlush = function() {
847 runners.forEach(function(runner) {
853 return animateCssConstructor;
856 $provide.decorator('$animate', ['$delegate', '$timeout', '$browser', '$$rAF', '$animateCss', '$$animateJs',
857 '$$forceReflow', '$$animateAsyncRun', '$rootScope',
858 function($delegate, $timeout, $browser, $$rAF, $animateCss, $$animateJs,
859 $$forceReflow, $$animateAsyncRun, $rootScope) {
862 cancel: $delegate.cancel,
867 return $$forceReflow.totalReflows;
869 enabled: $delegate.enabled,
872 * @name $animate#closeAndFlush
875 * This method will close all pending animations (both {@link ngAnimate#javascript-based-animations Javascript}
876 * and {@link ngAnimate.$animateCss CSS}) and it will also flush any remaining animation frames and/or callbacks.
878 closeAndFlush: function() {
879 // we allow the flush command to swallow the errors
880 // because depending on whether CSS or JS animations are
881 // used, there may not be a RAF flush. The primary flush
882 // at the end of this function must throw an exception
883 // because it will track if there were pending animations
885 $animateCss.$closeAndFlush();
886 $$animateJs.$closeAndFlush();
891 * @name $animate#flush
894 * This method is used to flush the pending callbacks and animation frames to either start
895 * an animation or conclude an animation. Note that this will not actually close an
896 * actively running animation (see {@link ngMock.$animate#closeAndFlush `closeAndFlush()`} for that).
898 flush: function(hideErrors) {
899 $rootScope.$digest();
901 var doNextRun, somethingFlushed = false;
905 if ($$rAF.queue.length) {
907 doNextRun = somethingFlushed = true;
910 if ($$animateAsyncRun.flush()) {
911 doNextRun = somethingFlushed = true;
915 if (!somethingFlushed && !hideErrors) {
916 throw new Error('No pending animations ready to be closed or flushed');
919 $rootScope.$digest();
924 ['animate','enter','leave','move','addClass','removeClass','setClass'], function(method) {
925 animate[method] = function() {
928 element: arguments[0],
929 options: arguments[arguments.length - 1],
932 return $delegate[method].apply($delegate, arguments);
944 * @name angular.mock.dump
947 * *NOTE*: This is not an injectable instance, just a globally available function.
949 * Method for serializing common angular objects (scope, elements, etc..) into strings.
950 * It is useful for logging objects to the console when debugging.
952 * @param {*} object - any object to turn into string.
953 * @return {string} a serialized string of the argument
955 angular.mock.dump = function(object) {
956 return serialize(object);
958 function serialize(object) {
961 if (angular.isElement(object)) {
962 object = angular.element(object);
963 out = angular.element('<div></div>');
964 angular.forEach(object, function(element) {
965 out.append(angular.element(element).clone());
968 } else if (angular.isArray(object)) {
970 angular.forEach(object, function(o) {
971 out.push(serialize(o));
973 out = '[ ' + out.join(', ') + ' ]';
974 } else if (angular.isObject(object)) {
975 if (angular.isFunction(object.$eval) && angular.isFunction(object.$apply)) {
976 out = serializeScope(object);
977 } else if (object instanceof Error) {
978 out = object.stack || ('' + object.name + ': ' + object.message);
980 // TODO(i): this prevents methods being logged,
981 // we should have a better way to serialize objects
982 out = angular.toJson(object, true);
985 out = String(object);
991 function serializeScope(scope, offset) {
992 offset = offset || ' ';
993 var log = [offset + 'Scope(' + scope.$id + '): {'];
994 for (var key in scope) {
995 if (Object.prototype.hasOwnProperty.call(scope, key) && !key.match(/^(\$|this)/)) {
996 log.push(' ' + key + ': ' + angular.toJson(scope[key]));
999 var child = scope.$$childHead;
1001 log.push(serializeScope(child, offset + ' '));
1002 child = child.$$nextSibling;
1005 return log.join('\n' + offset);
1011 * @name $httpBackend
1013 * Fake HTTP backend implementation suitable for unit testing applications that use the
1014 * {@link ng.$http $http service}.
1016 * <div class="alert alert-info">
1017 * **Note**: For fake HTTP backend implementation suitable for end-to-end testing or backend-less
1018 * development please see {@link ngMockE2E.$httpBackend e2e $httpBackend mock}.
1021 * During unit testing, we want our unit tests to run quickly and have no external dependencies so
1022 * we don’t want to send [XHR](https://developer.mozilla.org/en/xmlhttprequest) or
1023 * [JSONP](http://en.wikipedia.org/wiki/JSONP) requests to a real server. All we really need is
1024 * to verify whether a certain request has been sent or not, or alternatively just let the
1025 * application make requests, respond with pre-trained responses and assert that the end result is
1026 * what we expect it to be.
1028 * This mock implementation can be used to respond with static or dynamic responses via the
1029 * `expect` and `when` apis and their shortcuts (`expectGET`, `whenPOST`, etc).
1031 * When an Angular application needs some data from a server, it calls the $http service, which
1032 * sends the request to a real server using $httpBackend service. With dependency injection, it is
1033 * easy to inject $httpBackend mock (which has the same API as $httpBackend) and use it to verify
1034 * the requests and respond with some testing data without sending a request to a real server.
1036 * There are two ways to specify what test data should be returned as http responses by the mock
1037 * backend when the code under test makes http requests:
1039 * - `$httpBackend.expect` - specifies a request expectation
1040 * - `$httpBackend.when` - specifies a backend definition
1043 * ## Request Expectations vs Backend Definitions
1045 * Request expectations provide a way to make assertions about requests made by the application and
1046 * to define responses for those requests. The test will fail if the expected requests are not made
1047 * or they are made in the wrong order.
1049 * Backend definitions allow you to define a fake backend for your application which doesn't assert
1050 * if a particular request was made or not, it just returns a trained response if a request is made.
1051 * The test will pass whether or not the request gets made during testing.
1054 * <table class="table">
1055 * <tr><th width="220px"></th><th>Request expectations</th><th>Backend definitions</th></tr>
1058 * <td>.expect(...).respond(...)</td>
1059 * <td>.when(...).respond(...)</td>
1062 * <th>Typical usage</th>
1063 * <td>strict unit tests</td>
1064 * <td>loose (black-box) unit testing</td>
1067 * <th>Fulfills multiple requests</th>
1072 * <th>Order of requests matters</th>
1077 * <th>Request required</th>
1082 * <th>Response required</th>
1083 * <td>optional (see below)</td>
1088 * In cases where both backend definitions and request expectations are specified during unit
1089 * testing, the request expectations are evaluated first.
1091 * If a request expectation has no response specified, the algorithm will search your backend
1092 * definitions for an appropriate response.
1094 * If a request didn't match any expectation or if the expectation doesn't have the response
1095 * defined, the backend definitions are evaluated in sequential order to see if any of them match
1096 * the request. The response from the first matched definition is returned.
1099 * ## Flushing HTTP requests
1101 * The $httpBackend used in production always responds to requests asynchronously. If we preserved
1102 * this behavior in unit testing, we'd have to create async unit tests, which are hard to write,
1103 * to follow and to maintain. But neither can the testing mock respond synchronously; that would
1104 * change the execution of the code under test. For this reason, the mock $httpBackend has a
1105 * `flush()` method, which allows the test to explicitly flush pending requests. This preserves
1106 * the async api of the backend, while allowing the test to execute synchronously.
1109 * ## Unit testing with mock $httpBackend
1110 * The following code shows how to setup and use the mock backend when unit testing a controller.
1111 * First we create the controller under test:
1116 .module('MyApp', [])
1117 .controller('MyController', MyController);
1119 // The controller code
1120 function MyController($scope, $http) {
1123 $http.get('/auth.py').then(function(response) {
1124 authToken = response.headers('A-Token');
1125 $scope.user = response.data;
1128 $scope.saveMessage = function(message) {
1129 var headers = { 'Authorization': authToken };
1130 $scope.status = 'Saving...';
1132 $http.post('/add-msg.py', message, { headers: headers } ).then(function(response) {
1134 }).catch(function() {
1135 $scope.status = 'Failed...';
1141 * Now we setup the mock backend and create the test specs:
1144 // testing controller
1145 describe('MyController', function() {
1146 var $httpBackend, $rootScope, createController, authRequestHandler;
1148 // Set up the module
1149 beforeEach(module('MyApp'));
1151 beforeEach(inject(function($injector) {
1152 // Set up the mock http service responses
1153 $httpBackend = $injector.get('$httpBackend');
1154 // backend definition common for all tests
1155 authRequestHandler = $httpBackend.when('GET', '/auth.py')
1156 .respond({userId: 'userX'}, {'A-Token': 'xxx'});
1158 // Get hold of a scope (i.e. the root scope)
1159 $rootScope = $injector.get('$rootScope');
1160 // The $controller service is used to create instances of controllers
1161 var $controller = $injector.get('$controller');
1163 createController = function() {
1164 return $controller('MyController', {'$scope' : $rootScope });
1169 afterEach(function() {
1170 $httpBackend.verifyNoOutstandingExpectation();
1171 $httpBackend.verifyNoOutstandingRequest();
1175 it('should fetch authentication token', function() {
1176 $httpBackend.expectGET('/auth.py');
1177 var controller = createController();
1178 $httpBackend.flush();
1182 it('should fail authentication', function() {
1184 // Notice how you can change the response even after it was set
1185 authRequestHandler.respond(401, '');
1187 $httpBackend.expectGET('/auth.py');
1188 var controller = createController();
1189 $httpBackend.flush();
1190 expect($rootScope.status).toBe('Failed...');
1194 it('should send msg to server', function() {
1195 var controller = createController();
1196 $httpBackend.flush();
1198 // now you don’t care about the authentication, but
1199 // the controller will still send the request and
1200 // $httpBackend will respond without you having to
1201 // specify the expectation and response for this request
1203 $httpBackend.expectPOST('/add-msg.py', 'message content').respond(201, '');
1204 $rootScope.saveMessage('message content');
1205 expect($rootScope.status).toBe('Saving...');
1206 $httpBackend.flush();
1207 expect($rootScope.status).toBe('');
1211 it('should send auth header', function() {
1212 var controller = createController();
1213 $httpBackend.flush();
1215 $httpBackend.expectPOST('/add-msg.py', undefined, function(headers) {
1216 // check if the header was sent, if it wasn't the expectation won't
1217 // match the request and the test will fail
1218 return headers['Authorization'] == 'xxx';
1219 }).respond(201, '');
1221 $rootScope.saveMessage('whatever');
1222 $httpBackend.flush();
1227 * ## Dynamic responses
1229 * You define a response to a request by chaining a call to `respond()` onto a definition or expectation.
1230 * If you provide a **callback** as the first parameter to `respond(callback)` then you can dynamically generate
1231 * a response based on the properties of the request.
1233 * The `callback` function should be of the form `function(method, url, data, headers, params)`.
1235 * ### Query parameters
1237 * By default, query parameters on request URLs are parsed into the `params` object. So a request URL
1238 * of `/list?q=searchstr&orderby=-name` would set `params` to be `{q: 'searchstr', orderby: '-name'}`.
1240 * ### Regex parameter matching
1242 * If an expectation or definition uses a **regex** to match the URL, you can provide an array of **keys** via a
1243 * `params` argument. The index of each **key** in the array will match the index of a **group** in the
1246 * The `params` object in the **callback** will now have properties with these keys, which hold the value of the
1247 * corresponding **group** in the **regex**.
1249 * This also applies to the `when` and `expect` shortcut methods.
1253 * $httpBackend.expect('GET', /\/user\/(.+)/, undefined, undefined, ['id'])
1254 * .respond(function(method, url, data, headers, params) {
1255 * // for requested url of '/user/1234' params is {id: '1234'}
1258 * $httpBackend.whenPATCH(/\/user\/(.+)\/article\/(.+)/, undefined, undefined, ['user', 'article'])
1259 * .respond(function(method, url, data, headers, params) {
1260 * // for url of '/user/1234/article/567' params is {user: '1234', article: '567'}
1264 * ## Matching route requests
1266 * For extra convenience, `whenRoute` and `expectRoute` shortcuts are available. These methods offer colon
1267 * delimited matching of the url path, ignoring the query string. This allows declarations
1268 * similar to how application routes are configured with `$routeProvider`. Because these methods convert
1269 * the definition url to regex, declaration order is important. Combined with query parameter parsing,
1270 * the following is possible:
1273 $httpBackend.whenRoute('GET', '/users/:id')
1274 .respond(function(method, url, data, headers, params) {
1275 return [200, MockUserList[Number(params.id)]];
1278 $httpBackend.whenRoute('GET', '/users')
1279 .respond(function(method, url, data, headers, params) {
1280 var userList = angular.copy(MockUserList),
1281 defaultSort = 'lastName',
1282 count, pages, isPrevious, isNext;
1284 // paged api response '/v1/users?page=2'
1285 params.page = Number(params.page) || 1;
1287 // query for last names '/v1/users?q=Archer'
1289 userList = $filter('filter')({lastName: params.q});
1292 pages = Math.ceil(userList.length / pagingLength);
1293 isPrevious = params.page > 1;
1294 isNext = params.page < pages;
1297 count: userList.length,
1298 previous: isPrevious,
1300 // sort field -> '/v1/users?sortBy=firstName'
1301 results: $filter('orderBy')(userList, params.sortBy || defaultSort)
1302 .splice((params.page - 1) * pagingLength, pagingLength)
1307 angular.mock.$HttpBackendProvider = function() {
1308 this.$get = ['$rootScope', '$timeout', createHttpBackendMock];
1312 * General factory function for $httpBackend mock.
1313 * Returns instance for unit testing (when no arguments specified):
1314 * - passing through is disabled
1315 * - auto flushing is disabled
1317 * Returns instance for e2e testing (when `$delegate` and `$browser` specified):
1318 * - passing through (delegating request to real backend) is enabled
1319 * - auto flushing is enabled
1321 * @param {Object=} $delegate Real $httpBackend instance (allow passing through if specified)
1322 * @param {Object=} $browser Auto-flushing enabled if specified
1323 * @return {Object} Instance of $httpBackend mock
1325 function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
1326 var definitions = [],
1329 responsesPush = angular.bind(responses, responses.push),
1330 copy = angular.copy;
1332 function createResponse(status, data, headers, statusText) {
1333 if (angular.isFunction(status)) return status;
1336 return angular.isNumber(status)
1337 ? [status, data, headers, statusText]
1338 : [200, status, data, headers];
1342 // TODO(vojta): change params to: method, url, data, headers, callback
1343 function $httpBackend(method, url, data, callback, headers, timeout, withCredentials, responseType, eventHandlers, uploadEventHandlers) {
1345 var xhr = new MockXhr(),
1346 expectation = expectations[0],
1347 wasExpected = false;
1349 xhr.$$events = eventHandlers;
1350 xhr.upload.$$events = uploadEventHandlers;
1352 function prettyPrint(data) {
1353 return (angular.isString(data) || angular.isFunction(data) || data instanceof RegExp)
1355 : angular.toJson(data);
1358 function wrapResponse(wrapped) {
1359 if (!$browser && timeout) {
1360 timeout.then ? timeout.then(handleTimeout) : $timeout(handleTimeout, timeout);
1363 return handleResponse;
1365 function handleResponse() {
1366 var response = wrapped.response(method, url, data, headers, wrapped.params(url));
1367 xhr.$$respHeaders = response[2];
1368 callback(copy(response[0]), copy(response[1]), xhr.getAllResponseHeaders(),
1369 copy(response[3] || ''));
1372 function handleTimeout() {
1373 for (var i = 0, ii = responses.length; i < ii; i++) {
1374 if (responses[i] === handleResponse) {
1375 responses.splice(i, 1);
1376 callback(-1, undefined, '');
1383 if (expectation && expectation.match(method, url)) {
1384 if (!expectation.matchData(data)) {
1385 throw new Error('Expected ' + expectation + ' with different data\n' +
1386 'EXPECTED: ' + prettyPrint(expectation.data) + '\nGOT: ' + data);
1389 if (!expectation.matchHeaders(headers)) {
1390 throw new Error('Expected ' + expectation + ' with different headers\n' +
1391 'EXPECTED: ' + prettyPrint(expectation.headers) + '\nGOT: ' +
1392 prettyPrint(headers));
1395 expectations.shift();
1397 if (expectation.response) {
1398 responses.push(wrapResponse(expectation));
1404 var i = -1, definition;
1405 while ((definition = definitions[++i])) {
1406 if (definition.match(method, url, data, headers || {})) {
1407 if (definition.response) {
1408 // if $browser specified, we do auto flush all requests
1409 ($browser ? $browser.defer : responsesPush)(wrapResponse(definition));
1410 } else if (definition.passThrough) {
1411 $delegate(method, url, data, callback, headers, timeout, withCredentials, responseType, eventHandlers, uploadEventHandlers);
1412 } else throw new Error('No response defined !');
1417 new Error('No response defined !') :
1418 new Error('Unexpected request: ' + method + ' ' + url + '\n' +
1419 (expectation ? 'Expected ' + expectation : 'No more request expected'));
1424 * @name $httpBackend#when
1426 * Creates a new backend definition.
1428 * @param {string} method HTTP method.
1429 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1430 * and returns true if the url matches the current definition.
1431 * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
1432 * data string and returns true if the data is as expected.
1433 * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
1434 * object and returns true if the headers match the current definition.
1435 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1436 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1437 * request is handled. You can save this object for later use and invoke `respond` again in
1438 * order to change how a matched request is handled.
1442 * {function([status,] data[, headers, statusText])
1443 * | function(function(method, url, data, headers, params)}
1445 * – The respond method takes a set of static data to be returned or a function that can
1446 * return an array containing response status (number), response data (Array|Object|string),
1447 * response headers (Object), and the text for the status (string). The respond method returns
1448 * the `requestHandler` object for possible overrides.
1450 $httpBackend.when = function(method, url, data, headers, keys) {
1451 var definition = new MockHttpExpectation(method, url, data, headers, keys),
1453 respond: function(status, data, headers, statusText) {
1454 definition.passThrough = undefined;
1455 definition.response = createResponse(status, data, headers, statusText);
1461 chain.passThrough = function() {
1462 definition.response = undefined;
1463 definition.passThrough = true;
1468 definitions.push(definition);
1474 * @name $httpBackend#whenGET
1476 * Creates a new backend definition for GET requests. For more info see `when()`.
1478 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1479 * and returns true if the url matches the current definition.
1480 * @param {(Object|function(Object))=} headers HTTP headers.
1481 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1482 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1483 * request is handled. You can save this object for later use and invoke `respond` again in
1484 * order to change how a matched request is handled.
1489 * @name $httpBackend#whenHEAD
1491 * Creates a new backend definition for HEAD requests. For more info see `when()`.
1493 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1494 * and returns true if the url matches the current definition.
1495 * @param {(Object|function(Object))=} headers HTTP headers.
1496 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1497 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1498 * request is handled. You can save this object for later use and invoke `respond` again in
1499 * order to change how a matched request is handled.
1504 * @name $httpBackend#whenDELETE
1506 * Creates a new backend definition for DELETE requests. For more info see `when()`.
1508 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1509 * and returns true if the url matches the current definition.
1510 * @param {(Object|function(Object))=} headers HTTP headers.
1511 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1512 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1513 * request is handled. You can save this object for later use and invoke `respond` again in
1514 * order to change how a matched request is handled.
1519 * @name $httpBackend#whenPOST
1521 * Creates a new backend definition for POST requests. For more info see `when()`.
1523 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1524 * and returns true if the url matches the current definition.
1525 * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
1526 * data string and returns true if the data is as expected.
1527 * @param {(Object|function(Object))=} headers HTTP headers.
1528 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1529 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1530 * request is handled. You can save this object for later use and invoke `respond` again in
1531 * order to change how a matched request is handled.
1536 * @name $httpBackend#whenPUT
1538 * Creates a new backend definition for PUT requests. For more info see `when()`.
1540 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1541 * and returns true if the url matches the current definition.
1542 * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
1543 * data string and returns true if the data is as expected.
1544 * @param {(Object|function(Object))=} headers HTTP headers.
1545 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1546 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1547 * request is handled. You can save this object for later use and invoke `respond` again in
1548 * order to change how a matched request is handled.
1553 * @name $httpBackend#whenJSONP
1555 * Creates a new backend definition for JSONP requests. For more info see `when()`.
1557 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1558 * and returns true if the url matches the current definition.
1559 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1560 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1561 * request is handled. You can save this object for later use and invoke `respond` again in
1562 * order to change how a matched request is handled.
1564 createShortMethods('when');
1568 * @name $httpBackend#whenRoute
1570 * Creates a new backend definition that compares only with the requested route.
1572 * @param {string} method HTTP method.
1573 * @param {string} url HTTP url string that supports colon param matching.
1574 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1575 * request is handled. You can save this object for later use and invoke `respond` again in
1576 * order to change how a matched request is handled. See #when for more info.
1578 $httpBackend.whenRoute = function(method, url) {
1579 var pathObj = parseRoute(url);
1580 return $httpBackend.when(method, pathObj.regexp, undefined, undefined, pathObj.keys);
1583 function parseRoute(url) {
1587 keys = ret.keys = [];
1589 if (!url || !angular.isString(url)) return ret;
1592 .replace(/([().])/g, '\\$1')
1593 .replace(/(\/)?:(\w+)([\?\*])?/g, function(_, slash, key, option) {
1594 var optional = option === '?' ? option : null;
1595 var star = option === '*' ? option : null;
1596 keys.push({ name: key, optional: !!optional });
1597 slash = slash || '';
1599 + (optional ? '' : slash)
1601 + (optional ? slash : '')
1602 + (star && '(.+?)' || '([^/]+)')
1607 .replace(/([\/$\*])/g, '\\$1');
1609 ret.regexp = new RegExp('^' + url, 'i');
1615 * @name $httpBackend#expect
1617 * Creates a new request expectation.
1619 * @param {string} method HTTP method.
1620 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1621 * and returns true if the url matches the current definition.
1622 * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
1623 * receives data string and returns true if the data is as expected, or Object if request body
1624 * is in JSON format.
1625 * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
1626 * object and returns true if the headers match the current expectation.
1627 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1628 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1629 * request is handled. You can save this object for later use and invoke `respond` again in
1630 * order to change how a matched request is handled.
1634 * { function([status,] data[, headers, statusText])
1635 * | function(function(method, url, data, headers, params)}
1637 * – The respond method takes a set of static data to be returned or a function that can
1638 * return an array containing response status (number), response data (Array|Object|string),
1639 * response headers (Object), and the text for the status (string). The respond method returns
1640 * the `requestHandler` object for possible overrides.
1642 $httpBackend.expect = function(method, url, data, headers, keys) {
1643 var expectation = new MockHttpExpectation(method, url, data, headers, keys),
1645 respond: function(status, data, headers, statusText) {
1646 expectation.response = createResponse(status, data, headers, statusText);
1651 expectations.push(expectation);
1657 * @name $httpBackend#expectGET
1659 * Creates a new request expectation for GET requests. For more info see `expect()`.
1661 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1662 * and returns true if the url matches the current definition.
1663 * @param {Object=} headers HTTP headers.
1664 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1665 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1666 * request is handled. You can save this object for later use and invoke `respond` again in
1667 * order to change how a matched request is handled. See #expect for more info.
1672 * @name $httpBackend#expectHEAD
1674 * Creates a new request expectation for HEAD requests. For more info see `expect()`.
1676 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1677 * and returns true if the url matches the current definition.
1678 * @param {Object=} headers HTTP headers.
1679 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1680 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1681 * request is handled. You can save this object for later use and invoke `respond` again in
1682 * order to change how a matched request is handled.
1687 * @name $httpBackend#expectDELETE
1689 * Creates a new request expectation for DELETE requests. For more info see `expect()`.
1691 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1692 * and returns true if the url matches the current definition.
1693 * @param {Object=} headers HTTP headers.
1694 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1695 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1696 * request is handled. You can save this object for later use and invoke `respond` again in
1697 * order to change how a matched request is handled.
1702 * @name $httpBackend#expectPOST
1704 * Creates a new request expectation for POST requests. For more info see `expect()`.
1706 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1707 * and returns true if the url matches the current definition.
1708 * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
1709 * receives data string and returns true if the data is as expected, or Object if request body
1710 * is in JSON format.
1711 * @param {Object=} headers HTTP headers.
1712 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1713 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1714 * request is handled. You can save this object for later use and invoke `respond` again in
1715 * order to change how a matched request is handled.
1720 * @name $httpBackend#expectPUT
1722 * Creates a new request expectation for PUT requests. For more info see `expect()`.
1724 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1725 * and returns true if the url matches the current definition.
1726 * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
1727 * receives data string and returns true if the data is as expected, or Object if request body
1728 * is in JSON format.
1729 * @param {Object=} headers HTTP headers.
1730 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1731 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1732 * request is handled. You can save this object for later use and invoke `respond` again in
1733 * order to change how a matched request is handled.
1738 * @name $httpBackend#expectPATCH
1740 * Creates a new request expectation for PATCH requests. For more info see `expect()`.
1742 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1743 * and returns true if the url matches the current definition.
1744 * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
1745 * receives data string and returns true if the data is as expected, or Object if request body
1746 * is in JSON format.
1747 * @param {Object=} headers HTTP headers.
1748 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1749 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1750 * request is handled. You can save this object for later use and invoke `respond` again in
1751 * order to change how a matched request is handled.
1756 * @name $httpBackend#expectJSONP
1758 * Creates a new request expectation for JSONP requests. For more info see `expect()`.
1760 * @param {string|RegExp|function(string)} url HTTP url or function that receives an url
1761 * and returns true if the url matches the current definition.
1762 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1763 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1764 * request is handled. You can save this object for later use and invoke `respond` again in
1765 * order to change how a matched request is handled.
1767 createShortMethods('expect');
1771 * @name $httpBackend#expectRoute
1773 * Creates a new request expectation that compares only with the requested route.
1775 * @param {string} method HTTP method.
1776 * @param {string} url HTTP url string that supports colon param matching.
1777 * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1778 * request is handled. You can save this object for later use and invoke `respond` again in
1779 * order to change how a matched request is handled. See #expect for more info.
1781 $httpBackend.expectRoute = function(method, url) {
1782 var pathObj = parseRoute(url);
1783 return $httpBackend.expect(method, pathObj.regexp, undefined, undefined, pathObj.keys);
1789 * @name $httpBackend#flush
1791 * Flushes all pending requests using the trained responses.
1793 * @param {number=} count Number of responses to flush (in the order they arrived). If undefined,
1794 * all pending requests will be flushed. If there are no pending requests when the flush method
1795 * is called an exception is thrown (as this typically a sign of programming error).
1797 $httpBackend.flush = function(count, digest) {
1798 if (digest !== false) $rootScope.$digest();
1799 if (!responses.length) throw new Error('No pending request to flush !');
1801 if (angular.isDefined(count) && count !== null) {
1803 if (!responses.length) throw new Error('No more pending request to flush !');
1804 responses.shift()();
1807 while (responses.length) {
1808 responses.shift()();
1811 $httpBackend.verifyNoOutstandingExpectation(digest);
1817 * @name $httpBackend#verifyNoOutstandingExpectation
1819 * Verifies that all of the requests defined via the `expect` api were made. If any of the
1820 * requests were not made, verifyNoOutstandingExpectation throws an exception.
1822 * Typically, you would call this method following each test case that asserts requests using an
1823 * "afterEach" clause.
1826 * afterEach($httpBackend.verifyNoOutstandingExpectation);
1829 $httpBackend.verifyNoOutstandingExpectation = function(digest) {
1830 if (digest !== false) $rootScope.$digest();
1831 if (expectations.length) {
1832 throw new Error('Unsatisfied requests: ' + expectations.join(', '));
1839 * @name $httpBackend#verifyNoOutstandingRequest
1841 * Verifies that there are no outstanding requests that need to be flushed.
1843 * Typically, you would call this method following each test case that asserts requests using an
1844 * "afterEach" clause.
1847 * afterEach($httpBackend.verifyNoOutstandingRequest);
1850 $httpBackend.verifyNoOutstandingRequest = function() {
1851 if (responses.length) {
1852 throw new Error('Unflushed requests: ' + responses.length);
1859 * @name $httpBackend#resetExpectations
1861 * Resets all request expectations, but preserves all backend definitions. Typically, you would
1862 * call resetExpectations during a multiple-phase test when you want to reuse the same instance of
1863 * $httpBackend mock.
1865 $httpBackend.resetExpectations = function() {
1866 expectations.length = 0;
1867 responses.length = 0;
1870 return $httpBackend;
1873 function createShortMethods(prefix) {
1874 angular.forEach(['GET', 'DELETE', 'JSONP', 'HEAD'], function(method) {
1875 $httpBackend[prefix + method] = function(url, headers, keys) {
1876 return $httpBackend[prefix](method, url, undefined, headers, keys);
1880 angular.forEach(['PUT', 'POST', 'PATCH'], function(method) {
1881 $httpBackend[prefix + method] = function(url, data, headers, keys) {
1882 return $httpBackend[prefix](method, url, data, headers, keys);
1888 function MockHttpExpectation(method, url, data, headers, keys) {
1890 function getUrlParams(u) {
1891 var params = u.slice(u.indexOf('?') + 1).split('&');
1892 return params.sort();
1895 function compareUrl(u) {
1896 return (url.slice(0, url.indexOf('?')) == u.slice(0, u.indexOf('?')) && getUrlParams(url).join() == getUrlParams(u).join());
1900 this.headers = headers;
1902 this.match = function(m, u, d, h) {
1903 if (method != m) return false;
1904 if (!this.matchUrl(u)) return false;
1905 if (angular.isDefined(d) && !this.matchData(d)) return false;
1906 if (angular.isDefined(h) && !this.matchHeaders(h)) return false;
1910 this.matchUrl = function(u) {
1911 if (!url) return true;
1912 if (angular.isFunction(url.test)) return url.test(u);
1913 if (angular.isFunction(url)) return url(u);
1914 return (url == u || compareUrl(u));
1917 this.matchHeaders = function(h) {
1918 if (angular.isUndefined(headers)) return true;
1919 if (angular.isFunction(headers)) return headers(h);
1920 return angular.equals(headers, h);
1923 this.matchData = function(d) {
1924 if (angular.isUndefined(data)) return true;
1925 if (data && angular.isFunction(data.test)) return data.test(d);
1926 if (data && angular.isFunction(data)) return data(d);
1927 if (data && !angular.isString(data)) {
1928 return angular.equals(angular.fromJson(angular.toJson(data)), angular.fromJson(d));
1933 this.toString = function() {
1934 return method + ' ' + url;
1937 this.params = function(u) {
1938 return angular.extend(parseQuery(), pathParams());
1940 function pathParams() {
1942 if (!url || !angular.isFunction(url.test) || !keys || keys.length === 0) return keyObj;
1944 var m = url.exec(u);
1945 if (!m) return keyObj;
1946 for (var i = 1, len = m.length; i < len; ++i) {
1947 var key = keys[i - 1];
1950 keyObj[key.name || key] = val;
1957 function parseQuery() {
1958 var obj = {}, key_value, key,
1959 queryStr = u.indexOf('?') > -1
1960 ? u.substring(u.indexOf('?') + 1)
1963 angular.forEach(queryStr.split('&'), function(keyValue) {
1965 key_value = keyValue.replace(/\+/g,'%20').split('=');
1966 key = tryDecodeURIComponent(key_value[0]);
1967 if (angular.isDefined(key)) {
1968 var val = angular.isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true;
1969 if (!hasOwnProperty.call(obj, key)) {
1971 } else if (angular.isArray(obj[key])) {
1974 obj[key] = [obj[key],val];
1981 function tryDecodeURIComponent(value) {
1983 return decodeURIComponent(value);
1985 // Ignore any invalid uri component
1991 function createMockXhr() {
1992 return new MockXhr();
1995 function MockXhr() {
1997 // hack for testing $http, $httpBackend
1998 MockXhr.$$lastInstance = this;
2000 this.open = function(method, url, async) {
2001 this.$$method = method;
2003 this.$$async = async;
2004 this.$$reqHeaders = {};
2005 this.$$respHeaders = {};
2008 this.send = function(data) {
2012 this.setRequestHeader = function(key, value) {
2013 this.$$reqHeaders[key] = value;
2016 this.getResponseHeader = function(name) {
2017 // the lookup must be case insensitive,
2018 // that's why we try two quick lookups first and full scan last
2019 var header = this.$$respHeaders[name];
2020 if (header) return header;
2022 name = angular.lowercase(name);
2023 header = this.$$respHeaders[name];
2024 if (header) return header;
2027 angular.forEach(this.$$respHeaders, function(headerVal, headerName) {
2028 if (!header && angular.lowercase(headerName) == name) header = headerVal;
2033 this.getAllResponseHeaders = function() {
2036 angular.forEach(this.$$respHeaders, function(value, key) {
2037 lines.push(key + ': ' + value);
2039 return lines.join('\n');
2042 this.abort = angular.noop;
2044 // This section simulates the events on a real XHR object (and the upload object)
2045 // When we are testing $httpBackend (inside the angular project) we make partial use of this
2046 // but store the events directly ourselves on `$$events`, instead of going through the `addEventListener`
2048 this.addEventListener = function(name, listener) {
2049 if (angular.isUndefined(this.$$events[name])) this.$$events[name] = [];
2050 this.$$events[name].push(listener);
2055 addEventListener: this.addEventListener
2065 * This service is just a simple decorator for {@link ng.$timeout $timeout} service
2066 * that adds a "flush" and "verifyNoPendingTasks" methods.
2069 angular.mock.$TimeoutDecorator = ['$delegate', '$browser', function($delegate, $browser) {
2073 * @name $timeout#flush
2076 * Flushes the queue of pending tasks.
2078 * @param {number=} delay maximum timeout amount to flush up until
2080 $delegate.flush = function(delay) {
2081 $browser.defer.flush(delay);
2086 * @name $timeout#verifyNoPendingTasks
2089 * Verifies that there are no pending tasks that need to be flushed.
2091 $delegate.verifyNoPendingTasks = function() {
2092 if ($browser.deferredFns.length) {
2093 throw new Error('Deferred tasks to flush (' + $browser.deferredFns.length + '): ' +
2094 formatPendingTasksAsString($browser.deferredFns));
2098 function formatPendingTasksAsString(tasks) {
2100 angular.forEach(tasks, function(task) {
2101 result.push('{id: ' + task.id + ', ' + 'time: ' + task.time + '}');
2104 return result.join(', ');
2110 angular.mock.$RAFDecorator = ['$delegate', function($delegate) {
2111 var rafFn = function(fn) {
2112 var index = rafFn.queue.length;
2113 rafFn.queue.push(fn);
2115 rafFn.queue.splice(index, 1);
2120 rafFn.supported = $delegate.supported;
2122 rafFn.flush = function() {
2123 if (rafFn.queue.length === 0) {
2124 throw new Error('No rAF callbacks present');
2127 var length = rafFn.queue.length;
2128 for (var i = 0; i < length; i++) {
2132 rafFn.queue = rafFn.queue.slice(i);
2141 var originalRootElement;
2142 angular.mock.$RootElementProvider = function() {
2143 this.$get = ['$injector', function($injector) {
2144 originalRootElement = angular.element('<div ng-app></div>').data('$injector', $injector);
2145 return originalRootElement;
2153 * A decorator for {@link ng.$controller} with additional `bindings` parameter, useful when testing
2154 * controllers of directives that use {@link $compile#-bindtocontroller- `bindToController`}.
2161 * // Directive definition ...
2163 * myMod.directive('myDirective', {
2164 * controller: 'MyDirectiveController',
2165 * bindToController: {
2171 * // Controller definition ...
2173 * myMod.controller('MyDirectiveController', ['$log', function($log) {
2174 * $log.info(this.name);
2180 * describe('myDirectiveController', function() {
2181 * it('should write the bound name to the log', inject(function($controller, $log) {
2182 * var ctrl = $controller('MyDirectiveController', { /* no locals */ }, { name: 'Clark Kent' });
2183 * expect(ctrl.name).toEqual('Clark Kent');
2184 * expect($log.info.logs).toEqual(['Clark Kent']);
2190 * @param {Function|string} constructor If called with a function then it's considered to be the
2191 * controller constructor function. Otherwise it's considered to be a string which is used
2192 * to retrieve the controller constructor using the following steps:
2194 * * check if a controller with given name is registered via `$controllerProvider`
2195 * * check if evaluating the string on the current scope returns a constructor
2196 * * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
2197 * `window` object (not recommended)
2199 * The string can use the `controller as property` syntax, where the controller instance is published
2200 * as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
2201 * to work correctly.
2203 * @param {Object} locals Injection locals for Controller.
2204 * @param {Object=} bindings Properties to add to the controller before invoking the constructor. This is used
2205 * to simulate the `bindToController` feature and simplify certain kinds of tests.
2206 * @return {Object} Instance of given controller.
2208 angular.mock.$ControllerDecorator = ['$delegate', function($delegate) {
2209 return function(expression, locals, later, ident) {
2210 if (later && typeof later === 'object') {
2211 var instantiate = $delegate(expression, locals, true, ident);
2212 angular.extend(instantiate.instance, later);
2214 var instance = instantiate();
2215 if (instance !== instantiate.instance) {
2216 angular.extend(instance, later);
2221 return $delegate(expression, locals, later, ident);
2227 * @name $componentController
2229 * A service that can be used to create instances of component controllers.
2230 * <div class="alert alert-info">
2231 * Be aware that the controller will be instantiated and attached to the scope as specified in
2232 * the component definition object. If you do not provide a `$scope` object in the `locals` param
2233 * then the helper will create a new isolated scope as a child of `$rootScope`.
2235 * @param {string} componentName the name of the component whose controller we want to instantiate
2236 * @param {Object} locals Injection locals for Controller.
2237 * @param {Object=} bindings Properties to add to the controller before invoking the constructor. This is used
2238 * to simulate the `bindToController` feature and simplify certain kinds of tests.
2239 * @param {string=} ident Override the property name to use when attaching the controller to the scope.
2240 * @return {Object} Instance of requested controller.
2242 angular.mock.$ComponentControllerProvider = ['$compileProvider', function($compileProvider) {
2243 this.$get = ['$controller','$injector', '$rootScope', function($controller, $injector, $rootScope) {
2244 return function $componentController(componentName, locals, bindings, ident) {
2245 // get all directives associated to the component name
2246 var directives = $injector.get(componentName + 'Directive');
2247 // look for those directives that are components
2248 var candidateDirectives = directives.filter(function(directiveInfo) {
2249 // components have controller, controllerAs and restrict:'E'
2250 return directiveInfo.controller && directiveInfo.controllerAs && directiveInfo.restrict === 'E';
2252 // check if valid directives found
2253 if (candidateDirectives.length === 0) {
2254 throw new Error('No component found');
2256 if (candidateDirectives.length > 1) {
2257 throw new Error('Too many components found');
2259 // get the info of the component
2260 var directiveInfo = candidateDirectives[0];
2261 // create a scope if needed
2262 locals = locals || {};
2263 locals.$scope = locals.$scope || $rootScope.$new(true);
2264 return $controller(directiveInfo.controller, locals, bindings, ident || directiveInfo.controllerAs);
2273 * @packageName angular-mocks
2278 * The `ngMock` module provides support to inject and mock Angular services into unit tests.
2279 * In addition, ngMock also extends various core ng services such that they can be
2280 * inspected and controlled in a synchronous manner within test code.
2283 * <div doc-module-components="ngMock"></div>
2287 * First, download the file:
2288 * * [Google CDN](https://developers.google.com/speed/libraries/devguide#angularjs) e.g.
2289 * `"//ajax.googleapis.com/ajax/libs/angularjs/X.Y.Z/angular-mocks.js"`
2290 * * [NPM](https://www.npmjs.com/) e.g. `npm install angular-mocks@X.Y.Z`
2291 * * [Bower](http://bower.io) e.g. `bower install angular-mocks#X.Y.Z`
2292 * * [code.angularjs.org](https://code.angularjs.org/) (discouraged for production use) e.g.
2293 * `"//code.angularjs.org/X.Y.Z/angular-mocks.js"`
2295 * where X.Y.Z is the AngularJS version you are running.
2297 * Then, configure your test runner to load `angular-mocks.js` after `angular.js`.
2298 * This example uses <a href="http://karma-runner.github.io/">Karma</a>:
2303 * 'build/angular.js', // and other module files you need
2304 * 'build/angular-mocks.js',
2305 * '<path/to/application/files>',
2306 * '<path/to/spec/files>'
2311 * Including the `angular-mocks.js` file automatically adds the `ngMock` module, so your tests
2314 angular.module('ngMock', ['ng']).provider({
2315 $browser: angular.mock.$BrowserProvider,
2316 $exceptionHandler: angular.mock.$ExceptionHandlerProvider,
2317 $log: angular.mock.$LogProvider,
2318 $interval: angular.mock.$IntervalProvider,
2319 $httpBackend: angular.mock.$HttpBackendProvider,
2320 $rootElement: angular.mock.$RootElementProvider,
2321 $componentController: angular.mock.$ComponentControllerProvider
2322 }).config(['$provide', function($provide) {
2323 $provide.decorator('$timeout', angular.mock.$TimeoutDecorator);
2324 $provide.decorator('$$rAF', angular.mock.$RAFDecorator);
2325 $provide.decorator('$rootScope', angular.mock.$RootScopeDecorator);
2326 $provide.decorator('$controller', angular.mock.$ControllerDecorator);
2333 * @packageName angular-mocks
2336 * The `ngMockE2E` is an angular module which contains mocks suitable for end-to-end testing.
2337 * Currently there is only one mock present in this module -
2338 * the {@link ngMockE2E.$httpBackend e2e $httpBackend} mock.
2340 angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
2341 $provide.value('$httpBackend', angular.injector(['ng']).get('$httpBackend'));
2342 $provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator);
2347 * @name $httpBackend
2350 * Fake HTTP backend implementation suitable for end-to-end testing or backend-less development of
2351 * applications that use the {@link ng.$http $http service}.
2353 * <div class="alert alert-info">
2354 * **Note**: For fake http backend implementation suitable for unit testing please see
2355 * {@link ngMock.$httpBackend unit-testing $httpBackend mock}.
2358 * This implementation can be used to respond with static or dynamic responses via the `when` api
2359 * and its shortcuts (`whenGET`, `whenPOST`, etc) and optionally pass through requests to the
2360 * real $httpBackend for specific requests (e.g. to interact with certain remote apis or to fetch
2361 * templates from a webserver).
2363 * As opposed to unit-testing, in an end-to-end testing scenario or in scenario when an application
2364 * is being developed with the real backend api replaced with a mock, it is often desirable for
2365 * certain category of requests to bypass the mock and issue a real http request (e.g. to fetch
2366 * templates or static files from the webserver). To configure the backend with this behavior
2367 * use the `passThrough` request handler of `when` instead of `respond`.
2369 * Additionally, we don't want to manually have to flush mocked out requests like we do during unit
2370 * testing. For this reason the e2e $httpBackend flushes mocked out requests
2371 * automatically, closely simulating the behavior of the XMLHttpRequest object.
2373 * To setup the application to run with this http backend, you have to create a module that depends
2374 * on the `ngMockE2E` and your application modules and defines the fake backend:
2377 * var myAppDev = angular.module('myAppDev', ['myApp', 'ngMockE2E']);
2378 * myAppDev.run(function($httpBackend) {
2379 * var phones = [{name: 'phone1'}, {name: 'phone2'}];
2381 * // returns the current list of phones
2382 * $httpBackend.whenGET('/phones').respond(phones);
2384 * // adds a new phone to the phones array
2385 * $httpBackend.whenPOST('/phones').respond(function(method, url, data) {
2386 * var phone = angular.fromJson(data);
2387 * phones.push(phone);
2388 * return [200, phone, {}];
2390 * $httpBackend.whenGET(/^\/templates\//).passThrough(); // Requests for templare are handled by the real server
2395 * Afterwards, bootstrap your app with this new module.
2398 * <example name="httpbackend-e2e-testing" module="myAppE2E" deps="angular-mocks.js">
2399 * <file name="app.js">
2400 * var myApp = angular.module('myApp', []);
2402 * myApp.controller('main', function($http) {
2410 * ctrl.getPhones = function() {
2411 * $http.get('/phones').then(function(response) {
2412 * ctrl.phones = response.data;
2416 * ctrl.addPhone = function(phone) {
2417 * $http.post('/phones', phone).then(function() {
2418 * ctrl.newPhone = {name: ''};
2419 * return ctrl.getPhones();
2426 * <file name="e2e.js">
2427 * var myAppDev = angular.module('myAppE2E', ['myApp', 'ngMockE2E']);
2429 * myAppDev.run(function($httpBackend) {
2430 * var phones = [{name: 'phone1'}, {name: 'phone2'}];
2432 * // returns the current list of phones
2433 * $httpBackend.whenGET('/phones').respond(phones);
2435 * // adds a new phone to the phones array
2436 * $httpBackend.whenPOST('/phones').respond(function(method, url, data) {
2437 * var phone = angular.fromJson(data);
2438 * phones.push(phone);
2439 * return [200, phone, {}];
2443 * <file name="index.html">
2444 * <div ng-controller="main as $ctrl">
2445 * <form name="newPhoneForm" ng-submit="$ctrl.addPhone($ctrl.newPhone)">
2446 * <input type="text" ng-model="$ctrl.newPhone.name">
2447 * <input type="submit" value="Add Phone">
2451 * <li ng-repeat="phone in $ctrl.phones">{{phone.name}}</li>
2462 * @name $httpBackend#when
2465 * Creates a new backend definition.
2467 * @param {string} method HTTP method.
2468 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
2469 * and returns true if the url matches the current definition.
2470 * @param {(string|RegExp)=} data HTTP request body.
2471 * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
2472 * object and returns true if the headers match the current definition.
2473 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
2474 * {@link ngMock.$httpBackend $httpBackend mock}.
2475 * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2476 * control how a matched request is handled. You can save this object for later use and invoke
2477 * `respond` or `passThrough` again in order to change how a matched request is handled.
2481 * { function([status,] data[, headers, statusText])
2482 * | function(function(method, url, data, headers, params)}
2484 * – The respond method takes a set of static data to be returned or a function that can return
2485 * an array containing response status (number), response data (Array|Object|string), response
2486 * headers (Object), and the text for the status (string).
2487 * - passThrough – `{function()}` – Any request matching a backend definition with
2488 * `passThrough` handler will be passed through to the real backend (an XHR request will be made
2490 * - Both methods return the `requestHandler` object for possible overrides.
2495 * @name $httpBackend#whenGET
2498 * Creates a new backend definition for GET requests. For more info see `when()`.
2500 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
2501 * and returns true if the url matches the current definition.
2502 * @param {(Object|function(Object))=} headers HTTP headers.
2503 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
2504 * {@link ngMock.$httpBackend $httpBackend mock}.
2505 * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2506 * control how a matched request is handled. You can save this object for later use and invoke
2507 * `respond` or `passThrough` again in order to change how a matched request is handled.
2512 * @name $httpBackend#whenHEAD
2515 * Creates a new backend definition for HEAD requests. For more info see `when()`.
2517 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
2518 * and returns true if the url matches the current definition.
2519 * @param {(Object|function(Object))=} headers HTTP headers.
2520 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
2521 * {@link ngMock.$httpBackend $httpBackend mock}.
2522 * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2523 * control how a matched request is handled. You can save this object for later use and invoke
2524 * `respond` or `passThrough` again in order to change how a matched request is handled.
2529 * @name $httpBackend#whenDELETE
2532 * Creates a new backend definition for DELETE requests. For more info see `when()`.
2534 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
2535 * and returns true if the url matches the current definition.
2536 * @param {(Object|function(Object))=} headers HTTP headers.
2537 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
2538 * {@link ngMock.$httpBackend $httpBackend mock}.
2539 * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2540 * control how a matched request is handled. You can save this object for later use and invoke
2541 * `respond` or `passThrough` again in order to change how a matched request is handled.
2546 * @name $httpBackend#whenPOST
2549 * Creates a new backend definition for POST requests. For more info see `when()`.
2551 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
2552 * and returns true if the url matches the current definition.
2553 * @param {(string|RegExp)=} data HTTP request body.
2554 * @param {(Object|function(Object))=} headers HTTP headers.
2555 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
2556 * {@link ngMock.$httpBackend $httpBackend mock}.
2557 * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2558 * control how a matched request is handled. You can save this object for later use and invoke
2559 * `respond` or `passThrough` again in order to change how a matched request is handled.
2564 * @name $httpBackend#whenPUT
2567 * Creates a new backend definition for PUT requests. For more info see `when()`.
2569 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
2570 * and returns true if the url matches the current definition.
2571 * @param {(string|RegExp)=} data HTTP request body.
2572 * @param {(Object|function(Object))=} headers HTTP headers.
2573 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
2574 * {@link ngMock.$httpBackend $httpBackend mock}.
2575 * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2576 * control how a matched request is handled. You can save this object for later use and invoke
2577 * `respond` or `passThrough` again in order to change how a matched request is handled.
2582 * @name $httpBackend#whenPATCH
2585 * Creates a new backend definition for PATCH requests. For more info see `when()`.
2587 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
2588 * and returns true if the url matches the current definition.
2589 * @param {(string|RegExp)=} data HTTP request body.
2590 * @param {(Object|function(Object))=} headers HTTP headers.
2591 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
2592 * {@link ngMock.$httpBackend $httpBackend mock}.
2593 * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2594 * control how a matched request is handled. You can save this object for later use and invoke
2595 * `respond` or `passThrough` again in order to change how a matched request is handled.
2600 * @name $httpBackend#whenJSONP
2603 * Creates a new backend definition for JSONP requests. For more info see `when()`.
2605 * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
2606 * and returns true if the url matches the current definition.
2607 * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
2608 * {@link ngMock.$httpBackend $httpBackend mock}.
2609 * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2610 * control how a matched request is handled. You can save this object for later use and invoke
2611 * `respond` or `passThrough` again in order to change how a matched request is handled.
2615 * @name $httpBackend#whenRoute
2618 * Creates a new backend definition that compares only with the requested route.
2620 * @param {string} method HTTP method.
2621 * @param {string} url HTTP url string that supports colon param matching.
2622 * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2623 * control how a matched request is handled. You can save this object for later use and invoke
2624 * `respond` or `passThrough` again in order to change how a matched request is handled.
2626 angular.mock.e2e = {};
2627 angular.mock.e2e.$httpBackendDecorator =
2628 ['$rootScope', '$timeout', '$delegate', '$browser', createHttpBackendMock];
2633 * @name $rootScope.Scope
2636 * {@link ng.$rootScope.Scope Scope} type decorated with helper methods useful for testing. These
2637 * methods are automatically available on any {@link ng.$rootScope.Scope Scope} instance when
2638 * `ngMock` module is loaded.
2640 * In addition to all the regular `Scope` methods, the following helper methods are available:
2642 angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
2644 var $rootScopePrototype = Object.getPrototypeOf($delegate);
2646 $rootScopePrototype.$countChildScopes = countChildScopes;
2647 $rootScopePrototype.$countWatchers = countWatchers;
2651 // ------------------------------------------------------------------------------------------ //
2655 * @name $rootScope.Scope#$countChildScopes
2658 * Counts all the direct and indirect child scopes of the current scope.
2660 * The current scope is excluded from the count. The count includes all isolate child scopes.
2662 * @returns {number} Total number of child scopes.
2664 function countChildScopes() {
2665 // jshint validthis: true
2666 var count = 0; // exclude the current scope
2667 var pendingChildHeads = [this.$$childHead];
2670 while (pendingChildHeads.length) {
2671 currentScope = pendingChildHeads.shift();
2673 while (currentScope) {
2675 pendingChildHeads.push(currentScope.$$childHead);
2676 currentScope = currentScope.$$nextSibling;
2686 * @name $rootScope.Scope#$countWatchers
2689 * Counts all the watchers of direct and indirect child scopes of the current scope.
2691 * The watchers of the current scope are included in the count and so are all the watchers of
2692 * isolate child scopes.
2694 * @returns {number} Total number of watchers.
2696 function countWatchers() {
2697 // jshint validthis: true
2698 var count = this.$$watchers ? this.$$watchers.length : 0; // include the current scope
2699 var pendingChildHeads = [this.$$childHead];
2702 while (pendingChildHeads.length) {
2703 currentScope = pendingChildHeads.shift();
2705 while (currentScope) {
2706 count += currentScope.$$watchers ? currentScope.$$watchers.length : 0;
2707 pendingChildHeads.push(currentScope.$$childHead);
2708 currentScope = currentScope.$$nextSibling;
2717 !(function(jasmineOrMocha) {
2719 if (!jasmineOrMocha) {
2723 var currentSpec = null,
2724 injectorState = new InjectorState(),
2725 annotatedFunctions = [],
2726 wasInjectorCreated = function() {
2727 return !!currentSpec;
2730 angular.mock.$$annotate = angular.injector.$$annotate;
2731 angular.injector.$$annotate = function(fn) {
2732 if (typeof fn === 'function' && !fn.$inject) {
2733 annotatedFunctions.push(fn);
2735 return angular.mock.$$annotate.apply(this, arguments);
2740 * @name angular.mock.module
2743 * *NOTE*: This function is also published on window for easy access.<br>
2744 * *NOTE*: This function is declared ONLY WHEN running tests with jasmine or mocha
2746 * This function registers a module configuration code. It collects the configuration information
2747 * which will be used when the injector is created by {@link angular.mock.inject inject}.
2749 * See {@link angular.mock.inject inject} for usage example
2751 * @param {...(string|Function|Object)} fns any number of modules which are represented as string
2752 * aliases or as anonymous module initialization functions. The modules are used to
2753 * configure the injector. The 'ng' and 'ngMock' modules are automatically loaded. If an
2754 * object literal is passed each key-value pair will be registered on the module via
2755 * {@link auto.$provide $provide}.value, the key being the string name (or token) to associate
2756 * with the value on the injector.
2758 var module = window.module = angular.mock.module = function() {
2759 var moduleFns = Array.prototype.slice.call(arguments, 0);
2760 return wasInjectorCreated() ? workFn() : workFn;
2761 /////////////////////
2763 if (currentSpec.$injector) {
2764 throw new Error('Injector already created, can not register a module!');
2766 var fn, modules = currentSpec.$modules || (currentSpec.$modules = []);
2767 angular.forEach(moduleFns, function(module) {
2768 if (angular.isObject(module) && !angular.isArray(module)) {
2769 fn = ['$provide', function($provide) {
2770 angular.forEach(module, function(value, key) {
2771 $provide.value(key, value);
2777 if (currentSpec.$providerInjector) {
2778 currentSpec.$providerInjector.invoke(fn);
2787 module.$$beforeAllHook = (window.before || window.beforeAll);
2788 module.$$afterAllHook = (window.after || window.afterAll);
2790 // purely for testing ngMock itself
2791 module.$$currentSpec = function(to) {
2792 if (arguments.length === 0) return to;
2798 * @name angular.mock.module.sharedInjector
2801 * *NOTE*: This function is declared ONLY WHEN running tests with jasmine or mocha
2803 * This function ensures a single injector will be used for all tests in a given describe context.
2804 * This contrasts with the default behaviour where a new injector is created per test case.
2806 * Use sharedInjector when you want to take advantage of Jasmine's `beforeAll()`, or mocha's
2807 * `before()` methods. Call `module.sharedInjector()` before you setup any other hooks that
2808 * will create (i.e call `module()`) or use (i.e call `inject()`) the injector.
2810 * You cannot call `sharedInjector()` from within a context already using `sharedInjector()`.
2814 * Typically beforeAll is used to make many assertions about a single operation. This can
2815 * cut down test run-time as the test setup doesn't need to be re-run, and enabling focussed
2816 * tests each with a single assertion.
2819 * describe("Deep Thought", function() {
2821 * module.sharedInjector();
2823 * beforeAll(module("UltimateQuestion"));
2825 * beforeAll(inject(function(DeepThought) {
2826 * expect(DeepThought.answer).toBeUndefined();
2827 * DeepThought.generateAnswer();
2830 * it("has calculated the answer correctly", inject(function(DeepThought) {
2831 * // Because of sharedInjector, we have access to the instance of the DeepThought service
2832 * // that was provided to the beforeAll() hook. Therefore we can test the generated answer
2833 * expect(DeepThought.answer).toBe(42);
2836 * it("has calculated the answer within the expected time", inject(function(DeepThought) {
2837 * expect(DeepThought.runTimeMillennia).toBeLessThan(8000);
2840 * it("has double checked the answer", inject(function(DeepThought) {
2841 * expect(DeepThought.absolutelySureItIsTheRightAnswer).toBe(true);
2848 module.sharedInjector = function() {
2849 if (!(module.$$beforeAllHook && module.$$afterAllHook)) {
2850 throw Error("sharedInjector() cannot be used unless your test runner defines beforeAll/afterAll");
2853 var initialized = false;
2855 module.$$beforeAllHook(function() {
2856 if (injectorState.shared) {
2857 injectorState.sharedError = Error("sharedInjector() cannot be called inside a context that has already called sharedInjector()");
2858 throw injectorState.sharedError;
2862 injectorState.shared = true;
2865 module.$$afterAllHook(function() {
2867 injectorState = new InjectorState();
2870 injectorState.sharedError = null;
2875 module.$$beforeEach = function() {
2876 if (injectorState.shared && currentSpec && currentSpec != this) {
2877 var state = currentSpec;
2879 angular.forEach(["$injector","$modules","$providerInjector", "$injectorStrict"], function(k) {
2880 currentSpec[k] = state[k];
2885 originalRootElement = null;
2886 annotatedFunctions = [];
2890 module.$$afterEach = function() {
2891 if (injectorState.cleanupAfterEach()) {
2896 module.$$cleanup = function() {
2897 var injector = currentSpec.$injector;
2899 annotatedFunctions.forEach(function(fn) {
2903 angular.forEach(currentSpec.$modules, function(module) {
2904 if (module && module.$$hashKey) {
2905 module.$$hashKey = undefined;
2909 currentSpec.$injector = null;
2910 currentSpec.$modules = null;
2911 currentSpec.$providerInjector = null;
2915 // Ensure `$rootElement` is instantiated, before checking `originalRootElement`
2916 var $rootElement = injector.get('$rootElement');
2917 var rootNode = $rootElement && $rootElement[0];
2918 var cleanUpNodes = !originalRootElement ? [] : [originalRootElement[0]];
2919 if (rootNode && (!originalRootElement || rootNode !== originalRootElement[0])) {
2920 cleanUpNodes.push(rootNode);
2922 angular.element.cleanData(cleanUpNodes);
2924 // Ensure `$destroy()` is available, before calling it
2925 // (a mocked `$rootScope` might not implement it (or not even be an object at all))
2926 var $rootScope = injector.get('$rootScope');
2927 if ($rootScope && $rootScope.$destroy) $rootScope.$destroy();
2930 // clean up jquery's fragment cache
2931 angular.forEach(angular.element.fragments, function(val, key) {
2932 delete angular.element.fragments[key];
2935 MockXhr.$$lastInstance = null;
2937 angular.forEach(angular.callbacks, function(val, key) {
2938 delete angular.callbacks[key];
2940 angular.callbacks.$$counter = 0;
2943 (window.beforeEach || window.setup)(module.$$beforeEach);
2944 (window.afterEach || window.teardown)(module.$$afterEach);
2948 * @name angular.mock.inject
2951 * *NOTE*: This function is also published on window for easy access.<br>
2952 * *NOTE*: This function is declared ONLY WHEN running tests with jasmine or mocha
2954 * The inject function wraps a function into an injectable function. The inject() creates new
2955 * instance of {@link auto.$injector $injector} per test, which is then used for
2956 * resolving references.
2959 * ## Resolving References (Underscore Wrapping)
2960 * Often, we would like to inject a reference once, in a `beforeEach()` block and reuse this
2961 * in multiple `it()` clauses. To be able to do this we must assign the reference to a variable
2962 * that is declared in the scope of the `describe()` block. Since we would, most likely, want
2963 * the variable to have the same name of the reference we have a problem, since the parameter
2964 * to the `inject()` function would hide the outer variable.
2966 * To help with this, the injected parameters can, optionally, be enclosed with underscores.
2967 * These are ignored by the injector when the reference name is resolved.
2969 * For example, the parameter `_myService_` would be resolved as the reference `myService`.
2970 * Since it is available in the function body as _myService_, we can then assign it to a variable
2971 * defined in an outer scope.
2974 * // Defined out reference variable outside
2977 * // Wrap the parameter in underscores
2978 * beforeEach( inject( function(_myService_){
2979 * myService = _myService_;
2982 * // Use myService in a series of tests.
2983 * it('makes use of myService', function() {
2984 * myService.doStuff();
2989 * See also {@link angular.mock.module angular.mock.module}
2992 * Example of what a typical jasmine tests looks like with the inject method.
2995 * angular.module('myApplicationModule', [])
2996 * .value('mode', 'app')
2997 * .value('version', 'v1.0.1');
3000 * describe('MyApp', function() {
3002 * // You need to load modules that you want to test,
3003 * // it loads only the "ng" module by default.
3004 * beforeEach(module('myApplicationModule'));
3007 * // inject() is used to inject arguments of all given functions
3008 * it('should provide a version', inject(function(mode, version) {
3009 * expect(version).toEqual('v1.0.1');
3010 * expect(mode).toEqual('app');
3014 * // The inject and module method can also be used inside of the it or beforeEach
3015 * it('should override a version and test the new version is injected', function() {
3016 * // module() takes functions or strings (module aliases)
3017 * module(function($provide) {
3018 * $provide.value('version', 'overridden'); // override version here
3021 * inject(function(version) {
3022 * expect(version).toEqual('overridden');
3029 * @param {...Function} fns any number of functions which will be injected using the injector.
3034 var ErrorAddingDeclarationLocationStack = function(e, errorForStack) {
3035 this.message = e.message;
3037 if (e.line) this.line = e.line;
3038 if (e.sourceId) this.sourceId = e.sourceId;
3039 if (e.stack && errorForStack)
3040 this.stack = e.stack + '\n' + errorForStack.stack;
3041 if (e.stackArray) this.stackArray = e.stackArray;
3043 ErrorAddingDeclarationLocationStack.prototype = Error.prototype;
3045 window.inject = angular.mock.inject = function() {
3046 var blockFns = Array.prototype.slice.call(arguments, 0);
3047 var errorForStack = new Error('Declaration Location');
3048 // IE10+ and PhanthomJS do not set stack trace information, until the error is thrown
3049 if (!errorForStack.stack) {
3051 throw errorForStack;
3054 return wasInjectorCreated() ? workFn.call(currentSpec) : workFn;
3055 /////////////////////
3057 var modules = currentSpec.$modules || [];
3058 var strictDi = !!currentSpec.$injectorStrict;
3059 modules.unshift(['$injector', function($injector) {
3060 currentSpec.$providerInjector = $injector;
3062 modules.unshift('ngMock');
3063 modules.unshift('ng');
3064 var injector = currentSpec.$injector;
3067 // If strictDi is enabled, annotate the providerInjector blocks
3068 angular.forEach(modules, function(moduleFn) {
3069 if (typeof moduleFn === "function") {
3070 angular.injector.$$annotate(moduleFn);
3074 injector = currentSpec.$injector = angular.injector(modules, strictDi);
3075 currentSpec.$injectorStrict = strictDi;
3077 for (var i = 0, ii = blockFns.length; i < ii; i++) {
3078 if (currentSpec.$injectorStrict) {
3079 // If the injector is strict / strictDi, and the spec wants to inject using automatic
3080 // annotation, then annotate the function here.
3081 injector.annotate(blockFns[i]);
3084 /* jshint -W040 *//* Jasmine explicitly provides a `this` object when calling functions */
3085 injector.invoke(blockFns[i] || angular.noop, this);
3088 if (e.stack && errorForStack) {
3089 throw new ErrorAddingDeclarationLocationStack(e, errorForStack);
3093 errorForStack = null;
3100 angular.mock.inject.strictDi = function(value) {
3101 value = arguments.length ? !!value : true;
3102 return wasInjectorCreated() ? workFn() : workFn;
3105 if (value !== currentSpec.$injectorStrict) {
3106 if (currentSpec.$injector) {
3107 throw new Error('Injector already created, can not modify strict annotations');
3109 currentSpec.$injectorStrict = value;
3115 function InjectorState() {
3116 this.shared = false;
3117 this.sharedError = null;
3119 this.cleanupAfterEach = function() {
3120 return !this.shared || this.sharedError;
3123 })(window.jasmine || window.mocha);
3126 })(window, window.angular);