Simon Hunt

GUI - Angular:: added angular-mocks.js to tp lib.

- sample code plus sample unit test, and karma configuration file.

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