angularjs-rails 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1719 @@
1
+
2
+ /**
3
+ * @license AngularJS v1.0.0
4
+ * (c) 2010-2012 Google, Inc. http://angularjs.org
5
+ * License: MIT
6
+ *
7
+ * TODO(vojta): wrap whole file into closure during build
8
+ */
9
+
10
+ /**
11
+ * @ngdoc overview
12
+ * @name angular.mock
13
+ * @description
14
+ *
15
+ * Namespace from 'angular-mocks.js' which contains testing related code.
16
+ */
17
+ angular.mock = {};
18
+
19
+ /**
20
+ * ! This is a private undocumented service !
21
+ *
22
+ * @name ngMock.$browser
23
+ *
24
+ * @description
25
+ * This service is a mock implementation of {@link ng.$browser}. It provides fake
26
+ * implementation for commonly used browser apis that are hard to test, e.g. setTimeout, xhr,
27
+ * cookies, etc...
28
+ *
29
+ * The api of this service is the same as that of the real {@link ng.$browser $browser}, except
30
+ * that there are several helper methods available which can be used in tests.
31
+ */
32
+ angular.mock.$BrowserProvider = function() {
33
+ this.$get = function(){
34
+ return new angular.mock.$Browser();
35
+ };
36
+ };
37
+
38
+ angular.mock.$Browser = function() {
39
+ var self = this;
40
+
41
+ this.isMock = true;
42
+ self.$$url = "http://server/";
43
+ self.$$lastUrl = self.$$url; // used by url polling fn
44
+ self.pollFns = [];
45
+
46
+ // TODO(vojta): remove this temporary api
47
+ self.$$completeOutstandingRequest = angular.noop;
48
+ self.$$incOutstandingRequestCount = angular.noop;
49
+
50
+
51
+ // register url polling fn
52
+
53
+ self.onUrlChange = function(listener) {
54
+ self.pollFns.push(
55
+ function() {
56
+ if (self.$$lastUrl != self.$$url) {
57
+ self.$$lastUrl = self.$$url;
58
+ listener(self.$$url);
59
+ }
60
+ }
61
+ );
62
+
63
+ return listener;
64
+ };
65
+
66
+ self.cookieHash = {};
67
+ self.lastCookieHash = {};
68
+ self.deferredFns = [];
69
+ self.deferredNextId = 0;
70
+
71
+ self.defer = function(fn, delay) {
72
+ delay = delay || 0;
73
+ self.deferredFns.push({time:(self.defer.now + delay), fn:fn, id: self.deferredNextId});
74
+ self.deferredFns.sort(function(a,b){ return a.time - b.time;});
75
+ return self.deferredNextId++;
76
+ };
77
+
78
+
79
+ self.defer.now = 0;
80
+
81
+
82
+ self.defer.cancel = function(deferId) {
83
+ var fnIndex;
84
+
85
+ angular.forEach(self.deferredFns, function(fn, index) {
86
+ if (fn.id === deferId) fnIndex = index;
87
+ });
88
+
89
+ if (fnIndex !== undefined) {
90
+ self.deferredFns.splice(fnIndex, 1);
91
+ return true;
92
+ }
93
+
94
+ return false;
95
+ };
96
+
97
+
98
+ /**
99
+ * @name ngMock.$browser#defer.flush
100
+ * @methodOf ngMock.$browser
101
+ *
102
+ * @description
103
+ * Flushes all pending requests and executes the defer callbacks.
104
+ *
105
+ * @param {number=} number of milliseconds to flush. See {@link #defer.now}
106
+ */
107
+ self.defer.flush = function(delay) {
108
+ if (angular.isDefined(delay)) {
109
+ self.defer.now += delay;
110
+ } else {
111
+ if (self.deferredFns.length) {
112
+ self.defer.now = self.deferredFns[self.deferredFns.length-1].time;
113
+ } else {
114
+ throw Error('No deferred tasks to be flushed');
115
+ }
116
+ }
117
+
118
+ while (self.deferredFns.length && self.deferredFns[0].time <= self.defer.now) {
119
+ self.deferredFns.shift().fn();
120
+ }
121
+ };
122
+ /**
123
+ * @name ngMock.$browser#defer.now
124
+ * @propertyOf ngMock.$browser
125
+ *
126
+ * @description
127
+ * Current milliseconds mock time.
128
+ */
129
+
130
+ self.$$baseHref = '';
131
+ self.baseHref = function() {
132
+ return this.$$baseHref;
133
+ };
134
+ };
135
+ angular.mock.$Browser.prototype = {
136
+
137
+ /**
138
+ * @name ngMock.$browser#poll
139
+ * @methodOf ngMock.$browser
140
+ *
141
+ * @description
142
+ * run all fns in pollFns
143
+ */
144
+ poll: function poll() {
145
+ angular.forEach(this.pollFns, function(pollFn){
146
+ pollFn();
147
+ });
148
+ },
149
+
150
+ addPollFn: function(pollFn) {
151
+ this.pollFns.push(pollFn);
152
+ return pollFn;
153
+ },
154
+
155
+ url: function(url, replace) {
156
+ if (url) {
157
+ this.$$url = url;
158
+ return this;
159
+ }
160
+
161
+ return this.$$url;
162
+ },
163
+
164
+ cookies: function(name, value) {
165
+ if (name) {
166
+ if (value == undefined) {
167
+ delete this.cookieHash[name];
168
+ } else {
169
+ if (angular.isString(value) && //strings only
170
+ value.length <= 4096) { //strict cookie storage limits
171
+ this.cookieHash[name] = value;
172
+ }
173
+ }
174
+ } else {
175
+ if (!angular.equals(this.cookieHash, this.lastCookieHash)) {
176
+ this.lastCookieHash = angular.copy(this.cookieHash);
177
+ this.cookieHash = angular.copy(this.cookieHash);
178
+ }
179
+ return this.cookieHash;
180
+ }
181
+ },
182
+
183
+ notifyWhenNoOutstandingRequests: function(fn) {
184
+ fn();
185
+ }
186
+ };
187
+
188
+
189
+ /**
190
+ * @ngdoc object
191
+ * @name ngMock.$exceptionHandlerProvider
192
+ *
193
+ * @description
194
+ * Configures the mock implementation of {@link ng.$exceptionHandler} to rethrow or to log errors passed
195
+ * into the `$exceptionHandler`.
196
+ */
197
+
198
+ /**
199
+ * @ngdoc object
200
+ * @name ngMock.$exceptionHandler
201
+ *
202
+ * @description
203
+ * Mock implementation of {@link ng.$exceptionHandler} that rethrows or logs errors passed
204
+ * into it. See {@link ngMock.$exceptionHandlerProvider $exceptionHandlerProvider} for configuration
205
+ * information.
206
+ */
207
+
208
+ angular.mock.$ExceptionHandlerProvider = function() {
209
+ var handler;
210
+
211
+ /**
212
+ * @ngdoc method
213
+ * @name ngMock.$exceptionHandlerProvider#mode
214
+ * @methodOf ngMock.$exceptionHandlerProvider
215
+ *
216
+ * @description
217
+ * Sets the logging mode.
218
+ *
219
+ * @param {string} mode Mode of operation, defaults to `rethrow`.
220
+ *
221
+ * - `rethrow`: If any errors are are passed into the handler in tests, it typically
222
+ * means that there is a bug in the application or test, so this mock will
223
+ * make these tests fail.
224
+ * - `log`: Sometimes it is desirable to test that an error is throw, for this case the `log` mode stores the
225
+ * error and allows later assertion of it.
226
+ * See {@link ngMock.$log#assertEmpty assertEmpty()} and
227
+ * {@link ngMock.$log#reset reset()}
228
+ */
229
+ this.mode = function(mode) {
230
+ switch(mode) {
231
+ case 'rethrow':
232
+ handler = function(e) {
233
+ throw e;
234
+ };
235
+ break;
236
+ case 'log':
237
+ var errors = [];
238
+
239
+ handler = function(e) {
240
+ if (arguments.length == 1) {
241
+ errors.push(e);
242
+ } else {
243
+ errors.push([].slice.call(arguments, 0));
244
+ }
245
+ };
246
+
247
+ handler.errors = errors;
248
+ break;
249
+ default:
250
+ throw Error("Unknown mode '" + mode + "', only 'log'/'rethrow' modes are allowed!");
251
+ }
252
+ };
253
+
254
+ this.$get = function() {
255
+ return handler;
256
+ };
257
+
258
+ this.mode('rethrow');
259
+ };
260
+
261
+
262
+ /**
263
+ * @ngdoc service
264
+ * @name ngMock.$log
265
+ *
266
+ * @description
267
+ * Mock implementation of {@link ng.$log} that gathers all logged messages in arrays
268
+ * (one array per logging level). These arrays are exposed as `logs` property of each of the
269
+ * level-specific log function, e.g. for level `error` the array is exposed as `$log.error.logs`.
270
+ *
271
+ */
272
+ angular.mock.$LogProvider = function() {
273
+
274
+ function concat(array1, array2, index) {
275
+ return array1.concat(Array.prototype.slice.call(array2, index));
276
+ }
277
+
278
+
279
+ this.$get = function () {
280
+ var $log = {
281
+ log: function() { $log.log.logs.push(concat([], arguments, 0)); },
282
+ warn: function() { $log.warn.logs.push(concat([], arguments, 0)); },
283
+ info: function() { $log.info.logs.push(concat([], arguments, 0)); },
284
+ error: function() { $log.error.logs.push(concat([], arguments, 0)); }
285
+ };
286
+
287
+ /**
288
+ * @ngdoc method
289
+ * @name ngMock.$log#reset
290
+ * @methodOf ngMock.$log
291
+ *
292
+ * @description
293
+ * Reset all of the logging arrays to empty.
294
+ */
295
+ $log.reset = function () {
296
+ /**
297
+ * @ngdoc property
298
+ * @name ngMock.$log#log.logs
299
+ * @propertyOf ngMock.$log
300
+ *
301
+ * @description
302
+ * Array of logged messages.
303
+ */
304
+ $log.log.logs = [];
305
+ /**
306
+ * @ngdoc property
307
+ * @name ngMock.$log#warn.logs
308
+ * @propertyOf ngMock.$log
309
+ *
310
+ * @description
311
+ * Array of logged messages.
312
+ */
313
+ $log.warn.logs = [];
314
+ /**
315
+ * @ngdoc property
316
+ * @name ngMock.$log#info.logs
317
+ * @propertyOf ngMock.$log
318
+ *
319
+ * @description
320
+ * Array of logged messages.
321
+ */
322
+ $log.info.logs = [];
323
+ /**
324
+ * @ngdoc property
325
+ * @name ngMock.$log#error.logs
326
+ * @propertyOf ngMock.$log
327
+ *
328
+ * @description
329
+ * Array of logged messages.
330
+ */
331
+ $log.error.logs = [];
332
+ };
333
+
334
+ /**
335
+ * @ngdoc method
336
+ * @name ngMock.$log#assertEmpty
337
+ * @methodOf ngMock.$log
338
+ *
339
+ * @description
340
+ * Assert that the all of the logging methods have no logged messages. If messages present, an exception is thrown.
341
+ */
342
+ $log.assertEmpty = function() {
343
+ var errors = [];
344
+ angular.forEach(['error', 'warn', 'info', 'log'], function(logLevel) {
345
+ angular.forEach($log[logLevel].logs, function(log) {
346
+ angular.forEach(log, function (logItem) {
347
+ errors.push('MOCK $log (' + logLevel + '): ' + String(logItem) + '\n' + (logItem.stack || ''));
348
+ });
349
+ });
350
+ });
351
+ if (errors.length) {
352
+ errors.unshift("Expected $log to be empty! Either a message was logged unexpectedly, or an expected " +
353
+ "log message was not checked and removed:");
354
+ errors.push('');
355
+ throw new Error(errors.join('\n---------\n'));
356
+ }
357
+ };
358
+
359
+ $log.reset();
360
+ return $log;
361
+ };
362
+ };
363
+
364
+
365
+ (function() {
366
+ var R_ISO8061_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?:\:?(\d\d)(?:\:?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/;
367
+
368
+ function jsonStringToDate(string){
369
+ var match;
370
+ if (match = string.match(R_ISO8061_STR)) {
371
+ var date = new Date(0),
372
+ tzHour = 0,
373
+ tzMin = 0;
374
+ if (match[9]) {
375
+ tzHour = int(match[9] + match[10]);
376
+ tzMin = int(match[9] + match[11]);
377
+ }
378
+ date.setUTCFullYear(int(match[1]), int(match[2]) - 1, int(match[3]));
379
+ date.setUTCHours(int(match[4]||0) - tzHour, int(match[5]||0) - tzMin, int(match[6]||0), int(match[7]||0));
380
+ return date;
381
+ }
382
+ return string;
383
+ }
384
+
385
+ function int(str) {
386
+ return parseInt(str, 10);
387
+ }
388
+
389
+ function padNumber(num, digits, trim) {
390
+ var neg = '';
391
+ if (num < 0) {
392
+ neg = '-';
393
+ num = -num;
394
+ }
395
+ num = '' + num;
396
+ while(num.length < digits) num = '0' + num;
397
+ if (trim)
398
+ num = num.substr(num.length - digits);
399
+ return neg + num;
400
+ }
401
+
402
+
403
+ /**
404
+ * @ngdoc object
405
+ * @name angular.mock.TzDate
406
+ * @description
407
+ *
408
+ * *NOTE*: this is not an injectable instance, just a globally available mock class of `Date`.
409
+ *
410
+ * Mock of the Date type which has its timezone specified via constroctor arg.
411
+ *
412
+ * The main purpose is to create Date-like instances with timezone fixed to the specified timezone
413
+ * offset, so that we can test code that depends on local timezone settings without dependency on
414
+ * the time zone settings of the machine where the code is running.
415
+ *
416
+ * @param {number} offset Offset of the *desired* timezone in hours (fractions will be honored)
417
+ * @param {(number|string)} timestamp Timestamp representing the desired time in *UTC*
418
+ *
419
+ * @example
420
+ * !!!! WARNING !!!!!
421
+ * This is not a complete Date object so only methods that were implemented can be called safely.
422
+ * To make matters worse, TzDate instances inherit stuff from Date via a prototype.
423
+ *
424
+ * We do our best to intercept calls to "unimplemented" methods, but since the list of methods is
425
+ * incomplete we might be missing some non-standard methods. This can result in errors like:
426
+ * "Date.prototype.foo called on incompatible Object".
427
+ *
428
+ * <pre>
429
+ * var newYearInBratislava = new TzDate(-1, '2009-12-31T23:00:00Z');
430
+ * newYearInBratislava.getTimezoneOffset() => -60;
431
+ * newYearInBratislava.getFullYear() => 2010;
432
+ * newYearInBratislava.getMonth() => 0;
433
+ * newYearInBratislava.getDate() => 1;
434
+ * newYearInBratislava.getHours() => 0;
435
+ * newYearInBratislava.getMinutes() => 0;
436
+ * </pre>
437
+ *
438
+ */
439
+ angular.mock.TzDate = function (offset, timestamp) {
440
+ var self = new Date(0);
441
+ if (angular.isString(timestamp)) {
442
+ var tsStr = timestamp;
443
+
444
+ self.origDate = jsonStringToDate(timestamp);
445
+
446
+ timestamp = self.origDate.getTime();
447
+ if (isNaN(timestamp))
448
+ throw {
449
+ name: "Illegal Argument",
450
+ message: "Arg '" + tsStr + "' passed into TzDate constructor is not a valid date string"
451
+ };
452
+ } else {
453
+ self.origDate = new Date(timestamp);
454
+ }
455
+
456
+ var localOffset = new Date(timestamp).getTimezoneOffset();
457
+ self.offsetDiff = localOffset*60*1000 - offset*1000*60*60;
458
+ self.date = new Date(timestamp + self.offsetDiff);
459
+
460
+ self.getTime = function() {
461
+ return self.date.getTime() - self.offsetDiff;
462
+ };
463
+
464
+ self.toLocaleDateString = function() {
465
+ return self.date.toLocaleDateString();
466
+ };
467
+
468
+ self.getFullYear = function() {
469
+ return self.date.getFullYear();
470
+ };
471
+
472
+ self.getMonth = function() {
473
+ return self.date.getMonth();
474
+ };
475
+
476
+ self.getDate = function() {
477
+ return self.date.getDate();
478
+ };
479
+
480
+ self.getHours = function() {
481
+ return self.date.getHours();
482
+ };
483
+
484
+ self.getMinutes = function() {
485
+ return self.date.getMinutes();
486
+ };
487
+
488
+ self.getSeconds = function() {
489
+ return self.date.getSeconds();
490
+ };
491
+
492
+ self.getTimezoneOffset = function() {
493
+ return offset * 60;
494
+ };
495
+
496
+ self.getUTCFullYear = function() {
497
+ return self.origDate.getUTCFullYear();
498
+ };
499
+
500
+ self.getUTCMonth = function() {
501
+ return self.origDate.getUTCMonth();
502
+ };
503
+
504
+ self.getUTCDate = function() {
505
+ return self.origDate.getUTCDate();
506
+ };
507
+
508
+ self.getUTCHours = function() {
509
+ return self.origDate.getUTCHours();
510
+ };
511
+
512
+ self.getUTCMinutes = function() {
513
+ return self.origDate.getUTCMinutes();
514
+ };
515
+
516
+ self.getUTCSeconds = function() {
517
+ return self.origDate.getUTCSeconds();
518
+ };
519
+
520
+ self.getUTCMilliseconds = function() {
521
+ return self.origDate.getUTCMilliseconds();
522
+ };
523
+
524
+ self.getDay = function() {
525
+ return self.date.getDay();
526
+ };
527
+
528
+ // provide this method only on browsers that already have it
529
+ if (self.toISOString) {
530
+ self.toISOString = function() {
531
+ return padNumber(self.origDate.getUTCFullYear(), 4) + '-' +
532
+ padNumber(self.origDate.getUTCMonth() + 1, 2) + '-' +
533
+ padNumber(self.origDate.getUTCDate(), 2) + 'T' +
534
+ padNumber(self.origDate.getUTCHours(), 2) + ':' +
535
+ padNumber(self.origDate.getUTCMinutes(), 2) + ':' +
536
+ padNumber(self.origDate.getUTCSeconds(), 2) + '.' +
537
+ padNumber(self.origDate.getUTCMilliseconds(), 3) + 'Z'
538
+ }
539
+ }
540
+
541
+ //hide all methods not implemented in this mock that the Date prototype exposes
542
+ var unimplementedMethods = ['getMilliseconds', 'getUTCDay',
543
+ 'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds',
544
+ 'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear',
545
+ 'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds',
546
+ 'setYear', 'toDateString', 'toGMTString', 'toJSON', 'toLocaleFormat', 'toLocaleString',
547
+ 'toLocaleTimeString', 'toSource', 'toString', 'toTimeString', 'toUTCString', 'valueOf'];
548
+
549
+ angular.forEach(unimplementedMethods, function(methodName) {
550
+ self[methodName] = function() {
551
+ throw Error("Method '" + methodName + "' is not implemented in the TzDate mock");
552
+ };
553
+ });
554
+
555
+ return self;
556
+ };
557
+
558
+ //make "tzDateInstance instanceof Date" return true
559
+ angular.mock.TzDate.prototype = Date.prototype;
560
+ })();
561
+
562
+
563
+ /**
564
+ * @ngdoc function
565
+ * @name angular.mock.debug
566
+ * @description
567
+ *
568
+ * *NOTE*: this is not an injectable instance, just a globally available function.
569
+ *
570
+ * Method for serializing common angular objects (scope, elements, etc..) into strings, useful for debugging.
571
+ *
572
+ * This method is also available on window, where it can be used to display objects on debug console.
573
+ *
574
+ * @param {*} object - any object to turn into string.
575
+ * @return {string} a serialized string of the argument
576
+ */
577
+ angular.mock.dump = function(object) {
578
+ return serialize(object);
579
+
580
+ function serialize(object) {
581
+ var out;
582
+
583
+ if (angular.isElement(object)) {
584
+ object = angular.element(object);
585
+ out = angular.element('<div></div>');
586
+ angular.forEach(object, function(element) {
587
+ out.append(angular.element(element).clone());
588
+ });
589
+ out = out.html();
590
+ } else if (angular.isArray(object)) {
591
+ out = [];
592
+ angular.forEach(object, function(o) {
593
+ out.push(serialize(o));
594
+ });
595
+ out = '[ ' + out.join(', ') + ' ]';
596
+ } else if (angular.isObject(object)) {
597
+ if (angular.isFunction(object.$eval) && angular.isFunction(object.$apply)) {
598
+ out = serializeScope(object);
599
+ } else if (object instanceof Error) {
600
+ out = object.stack || ('' + object.name + ': ' + object.message);
601
+ } else {
602
+ out = angular.toJson(object, true);
603
+ }
604
+ } else {
605
+ out = String(object);
606
+ }
607
+
608
+ return out;
609
+ }
610
+
611
+ function serializeScope(scope, offset) {
612
+ offset = offset || ' ';
613
+ var log = [offset + 'Scope(' + scope.$id + '): {'];
614
+ for ( var key in scope ) {
615
+ if (scope.hasOwnProperty(key) && !key.match(/^(\$|this)/)) {
616
+ log.push(' ' + key + ': ' + angular.toJson(scope[key]));
617
+ }
618
+ }
619
+ var child = scope.$$childHead;
620
+ while(child) {
621
+ log.push(serializeScope(child, offset + ' '));
622
+ child = child.$$nextSibling;
623
+ }
624
+ log.push('}');
625
+ return log.join('\n' + offset);
626
+ }
627
+ };
628
+
629
+ /**
630
+ * @ngdoc object
631
+ * @name ngMock.$httpBackend
632
+ * @description
633
+ * Fake HTTP backend implementation suitable for unit testing application that use the
634
+ * {@link ng.$http $http service}.
635
+ *
636
+ * *Note*: For fake http backend implementation suitable for end-to-end testing or backend-less
637
+ * development please see {@link ngMockE2E.$httpBackend e2e $httpBackend mock}.
638
+ *
639
+ * During unit testing, we want our unit tests to run quickly and have no external dependencies so
640
+ * we don’t want to send {@link https://developer.mozilla.org/en/xmlhttprequest XHR} or
641
+ * {@link http://en.wikipedia.org/wiki/JSONP JSONP} requests to a real server. All we really need is
642
+ * to verify whether a certain request has been sent or not, or alternatively just let the
643
+ * application make requests, respond with pre-trained responses and assert that the end result is
644
+ * what we expect it to be.
645
+ *
646
+ * This mock implementation can be used to respond with static or dynamic responses via the
647
+ * `expect` and `when` apis and their shortcuts (`expectGET`, `whenPOST`, etc).
648
+ *
649
+ * When an Angular application needs some data from a server, it calls the $http service, which
650
+ * sends the request to a real server using $httpBackend service. With dependency injection, it is
651
+ * easy to inject $httpBackend mock (which has the same API as $httpBackend) and use it to verify
652
+ * the requests and respond with some testing data without sending a request to real server.
653
+ *
654
+ * There are two ways to specify what test data should be returned as http responses by the mock
655
+ * backend when the code under test makes http requests:
656
+ *
657
+ * - `$httpBackend.expect` - specifies a request expectation
658
+ * - `$httpBackend.when` - specifies a backend definition
659
+ *
660
+ *
661
+ * # Request Expectations vs Backend Definitions
662
+ *
663
+ * Request expectations provide a way to make assertions about requests made by the application and
664
+ * to define responses for those requests. The test will fail if the expected requests are not made
665
+ * or they are made in the wrong order.
666
+ *
667
+ * Backend definitions allow you to define a fake backend for your application which doesn't assert
668
+ * if a particular request was made or not, it just returns a trained response if a request is made.
669
+ * The test will pass whether or not the request gets made during testing.
670
+ *
671
+ *
672
+ * <table class="table">
673
+ * <tr><th width="220px"></th><th>Request expectations</th><th>Backend definitions</th></tr>
674
+ * <tr>
675
+ * <th>Syntax</th>
676
+ * <td>.expect(...).respond(...)</td>
677
+ * <td>.when(...).respond(...)</td>
678
+ * </tr>
679
+ * <tr>
680
+ * <th>Typical usage</th>
681
+ * <td>strict unit tests</td>
682
+ * <td>loose (black-box) unit testing</td>
683
+ * </tr>
684
+ * <tr>
685
+ * <th>Fulfills multiple requests</th>
686
+ * <td>NO</td>
687
+ * <td>YES</td>
688
+ * </tr>
689
+ * <tr>
690
+ * <th>Order of requests matters</th>
691
+ * <td>YES</td>
692
+ * <td>NO</td>
693
+ * </tr>
694
+ * <tr>
695
+ * <th>Request required</th>
696
+ * <td>YES</td>
697
+ * <td>NO</td>
698
+ * </tr>
699
+ * <tr>
700
+ * <th>Response required</th>
701
+ * <td>optional (see below)</td>
702
+ * <td>YES</td>
703
+ * </tr>
704
+ * </table>
705
+ *
706
+ * In cases where both backend definitions and request expectations are specified during unit
707
+ * testing, the request expectations are evaluated first.
708
+ *
709
+ * If a request expectation has no response specified, the algorithm will search your backend
710
+ * definitions for an appropriate response.
711
+ *
712
+ * If a request didn't match any expectation or if the expectation doesn't have the response
713
+ * defined, the backend definitions are evaluated in sequential order to see if any of them match
714
+ * the request. The response from the first matched definition is returned.
715
+ *
716
+ *
717
+ * # Flushing HTTP requests
718
+ *
719
+ * The $httpBackend used in production, always responds to requests with responses asynchronously.
720
+ * If we preserved this behavior in unit testing, we'd have to create async unit tests, which are
721
+ * hard to write, follow and maintain. At the same time the testing mock, can't respond
722
+ * synchronously because that would change the execution of the code under test. For this reason the
723
+ * mock $httpBackend has a `flush()` method, which allows the test to explicitly flush pending
724
+ * requests and thus preserving the async api of the backend, while allowing the test to execute
725
+ * synchronously.
726
+ *
727
+ *
728
+ * # Unit testing with mock $httpBackend
729
+ *
730
+ * <pre>
731
+ // controller
732
+ function MyController($scope, $http) {
733
+ $http.get('/auth.py').success(function(data) {
734
+ $scope.user = data;
735
+ });
736
+
737
+ this.saveMessage = function(message) {
738
+ $scope.status = 'Saving...';
739
+ $http.post('/add-msg.py', message).success(function(response) {
740
+ $scope.status = '';
741
+ }).error(function() {
742
+ $scope.status = 'ERROR!';
743
+ });
744
+ };
745
+ }
746
+
747
+ // testing controller
748
+ var $http;
749
+
750
+ beforeEach(inject(function($injector) {
751
+ $httpBackend = $injector.get('$httpBackend');
752
+
753
+ // backend definition common for all tests
754
+ $httpBackend.when('GET', '/auth.py').respond({userId: 'userX'}, {'A-Token': 'xxx'});
755
+ }));
756
+
757
+
758
+ afterEach(function() {
759
+ $httpBackend.verifyNoOutstandingExpectation();
760
+ $httpBackend.verifyNoOutstandingRequest();
761
+ });
762
+
763
+
764
+ it('should fetch authentication token', function() {
765
+ $httpBackend.expectGET('/auth.py');
766
+ var controller = scope.$new(MyController);
767
+ $httpBackend.flush();
768
+ });
769
+
770
+
771
+ it('should send msg to server', function() {
772
+ // now you don’t care about the authentication, but
773
+ // the controller will still send the request and
774
+ // $httpBackend will respond without you having to
775
+ // specify the expectation and response for this request
776
+ $httpBackend.expectPOST('/add-msg.py', 'message content').respond(201, '');
777
+
778
+ var controller = scope.$new(MyController);
779
+ $httpBackend.flush();
780
+ controller.saveMessage('message content');
781
+ expect(controller.status).toBe('Saving...');
782
+ $httpBackend.flush();
783
+ expect(controller.status).toBe('');
784
+ });
785
+
786
+
787
+ it('should send auth header', function() {
788
+ $httpBackend.expectPOST('/add-msg.py', undefined, function(headers) {
789
+ // check if the header was send, if it wasn't the expectation won't
790
+ // match the request and the test will fail
791
+ return headers['Authorization'] == 'xxx';
792
+ }).respond(201, '');
793
+
794
+ var controller = scope.$new(MyController);
795
+ controller.saveMessage('whatever');
796
+ $httpBackend.flush();
797
+ });
798
+ </pre>
799
+ */
800
+ angular.mock.$HttpBackendProvider = function() {
801
+ this.$get = [createHttpBackendMock];
802
+ };
803
+
804
+ /**
805
+ * General factory function for $httpBackend mock.
806
+ * Returns instance for unit testing (when no arguments specified):
807
+ * - passing through is disabled
808
+ * - auto flushing is disabled
809
+ *
810
+ * Returns instance for e2e testing (when `$delegate` and `$browser` specified):
811
+ * - passing through (delegating request to real backend) is enabled
812
+ * - auto flushing is enabled
813
+ *
814
+ * @param {Object=} $delegate Real $httpBackend instance (allow passing through if specified)
815
+ * @param {Object=} $browser Auto-flushing enabled if specified
816
+ * @return {Object} Instance of $httpBackend mock
817
+ */
818
+ function createHttpBackendMock($delegate, $browser) {
819
+ var definitions = [],
820
+ expectations = [],
821
+ responses = [],
822
+ responsesPush = angular.bind(responses, responses.push);
823
+
824
+ function createResponse(status, data, headers) {
825
+ if (angular.isFunction(status)) return status;
826
+
827
+ return function() {
828
+ return angular.isNumber(status)
829
+ ? [status, data, headers]
830
+ : [200, status, data];
831
+ };
832
+ }
833
+
834
+ // TODO(vojta): change params to: method, url, data, headers, callback
835
+ function $httpBackend(method, url, data, callback, headers) {
836
+ var xhr = new MockXhr(),
837
+ expectation = expectations[0],
838
+ wasExpected = false;
839
+
840
+ function prettyPrint(data) {
841
+ return (angular.isString(data) || angular.isFunction(data) || data instanceof RegExp)
842
+ ? data
843
+ : angular.toJson(data);
844
+ }
845
+
846
+ if (expectation && expectation.match(method, url)) {
847
+ if (!expectation.matchData(data))
848
+ throw Error('Expected ' + expectation + ' with different data\n' +
849
+ 'EXPECTED: ' + prettyPrint(expectation.data) + '\nGOT: ' + data);
850
+
851
+ if (!expectation.matchHeaders(headers))
852
+ throw Error('Expected ' + expectation + ' with different headers\n' +
853
+ 'EXPECTED: ' + prettyPrint(expectation.headers) + '\nGOT: ' +
854
+ prettyPrint(headers));
855
+
856
+ expectations.shift();
857
+
858
+ if (expectation.response) {
859
+ responses.push(function() {
860
+ var response = expectation.response(method, url, data, headers);
861
+ xhr.$$respHeaders = response[2];
862
+ callback(response[0], response[1], xhr.getAllResponseHeaders());
863
+ });
864
+ return;
865
+ }
866
+ wasExpected = true;
867
+ }
868
+
869
+ var i = -1, definition;
870
+ while ((definition = definitions[++i])) {
871
+ if (definition.match(method, url, data, headers || {})) {
872
+ if (definition.response) {
873
+ // if $browser specified, we do auto flush all requests
874
+ ($browser ? $browser.defer : responsesPush)(function() {
875
+ var response = definition.response(method, url, data, headers);
876
+ xhr.$$respHeaders = response[2];
877
+ callback(response[0], response[1], xhr.getAllResponseHeaders());
878
+ });
879
+ } else if (definition.passThrough) {
880
+ $delegate(method, url, data, callback, headers);
881
+ } else throw Error('No response defined !');
882
+ return;
883
+ }
884
+ }
885
+ throw wasExpected ?
886
+ Error('No response defined !') :
887
+ Error('Unexpected request: ' + method + ' ' + url + '\n' +
888
+ (expectation ? 'Expected ' + expectation : 'No more request expected'));
889
+ }
890
+
891
+ /**
892
+ * @ngdoc method
893
+ * @name ngMock.$httpBackend#when
894
+ * @methodOf ngMock.$httpBackend
895
+ * @description
896
+ * Creates a new backend definition.
897
+ *
898
+ * @param {string} method HTTP method.
899
+ * @param {string|RegExp} url HTTP url.
900
+ * @param {(string|RegExp)=} data HTTP request body.
901
+ * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
902
+ * object and returns true if the headers match the current definition.
903
+ * @returns {requestHandler} Returns an object with `respond` method that control how a matched
904
+ * request is handled.
905
+ *
906
+ * - respond – `{function([status,] data[, headers])|function(function(method, url, data, headers)}`
907
+ * – The respond method takes a set of static data to be returned or a function that can return
908
+ * an array containing response status (number), response data (string) and response headers
909
+ * (Object).
910
+ */
911
+ $httpBackend.when = function(method, url, data, headers) {
912
+ var definition = new MockHttpExpectation(method, url, data, headers),
913
+ chain = {
914
+ respond: function(status, data, headers) {
915
+ definition.response = createResponse(status, data, headers);
916
+ }
917
+ };
918
+
919
+ if ($browser) {
920
+ chain.passThrough = function() {
921
+ definition.passThrough = true;
922
+ };
923
+ }
924
+
925
+ definitions.push(definition);
926
+ return chain;
927
+ };
928
+
929
+ /**
930
+ * @ngdoc method
931
+ * @name ngMock.$httpBackend#whenGET
932
+ * @methodOf ngMock.$httpBackend
933
+ * @description
934
+ * Creates a new backend definition for GET requests. For more info see `when()`.
935
+ *
936
+ * @param {string|RegExp} url HTTP url.
937
+ * @param {(Object|function(Object))=} headers HTTP headers.
938
+ * @returns {requestHandler} Returns an object with `respond` method that control how a matched
939
+ * request is handled.
940
+ */
941
+
942
+ /**
943
+ * @ngdoc method
944
+ * @name ngMock.$httpBackend#whenHEAD
945
+ * @methodOf ngMock.$httpBackend
946
+ * @description
947
+ * Creates a new backend definition for HEAD requests. For more info see `when()`.
948
+ *
949
+ * @param {string|RegExp} url HTTP url.
950
+ * @param {(Object|function(Object))=} headers HTTP headers.
951
+ * @returns {requestHandler} Returns an object with `respond` method that control how a matched
952
+ * request is handled.
953
+ */
954
+
955
+ /**
956
+ * @ngdoc method
957
+ * @name ngMock.$httpBackend#whenDELETE
958
+ * @methodOf ngMock.$httpBackend
959
+ * @description
960
+ * Creates a new backend definition for DELETE requests. For more info see `when()`.
961
+ *
962
+ * @param {string|RegExp} url HTTP url.
963
+ * @param {(Object|function(Object))=} headers HTTP headers.
964
+ * @returns {requestHandler} Returns an object with `respond` method that control how a matched
965
+ * request is handled.
966
+ */
967
+
968
+ /**
969
+ * @ngdoc method
970
+ * @name ngMock.$httpBackend#whenPOST
971
+ * @methodOf ngMock.$httpBackend
972
+ * @description
973
+ * Creates a new backend definition for POST requests. For more info see `when()`.
974
+ *
975
+ * @param {string|RegExp} url HTTP url.
976
+ * @param {(string|RegExp)=} data HTTP request body.
977
+ * @param {(Object|function(Object))=} headers HTTP headers.
978
+ * @returns {requestHandler} Returns an object with `respond` method that control how a matched
979
+ * request is handled.
980
+ */
981
+
982
+ /**
983
+ * @ngdoc method
984
+ * @name ngMock.$httpBackend#whenPUT
985
+ * @methodOf ngMock.$httpBackend
986
+ * @description
987
+ * Creates a new backend definition for PUT requests. For more info see `when()`.
988
+ *
989
+ * @param {string|RegExp} url HTTP url.
990
+ * @param {(string|RegExp)=} data HTTP request body.
991
+ * @param {(Object|function(Object))=} headers HTTP headers.
992
+ * @returns {requestHandler} Returns an object with `respond` method that control how a matched
993
+ * request is handled.
994
+ */
995
+
996
+ /**
997
+ * @ngdoc method
998
+ * @name ngMock.$httpBackend#whenJSONP
999
+ * @methodOf ngMock.$httpBackend
1000
+ * @description
1001
+ * Creates a new backend definition for JSONP requests. For more info see `when()`.
1002
+ *
1003
+ * @param {string|RegExp} url HTTP url.
1004
+ * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1005
+ * request is handled.
1006
+ */
1007
+ createShortMethods('when');
1008
+
1009
+
1010
+ /**
1011
+ * @ngdoc method
1012
+ * @name ngMock.$httpBackend#expect
1013
+ * @methodOf ngMock.$httpBackend
1014
+ * @description
1015
+ * Creates a new request expectation.
1016
+ *
1017
+ * @param {string} method HTTP method.
1018
+ * @param {string|RegExp} url HTTP url.
1019
+ * @param {(string|RegExp)=} data HTTP request body.
1020
+ * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
1021
+ * object and returns true if the headers match the current expectation.
1022
+ * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1023
+ * request is handled.
1024
+ *
1025
+ * - respond – `{function([status,] data[, headers])|function(function(method, url, data, headers)}`
1026
+ * – The respond method takes a set of static data to be returned or a function that can return
1027
+ * an array containing response status (number), response data (string) and response headers
1028
+ * (Object).
1029
+ */
1030
+ $httpBackend.expect = function(method, url, data, headers) {
1031
+ var expectation = new MockHttpExpectation(method, url, data, headers);
1032
+ expectations.push(expectation);
1033
+ return {
1034
+ respond: function(status, data, headers) {
1035
+ expectation.response = createResponse(status, data, headers);
1036
+ }
1037
+ };
1038
+ };
1039
+
1040
+
1041
+ /**
1042
+ * @ngdoc method
1043
+ * @name ngMock.$httpBackend#expectGET
1044
+ * @methodOf ngMock.$httpBackend
1045
+ * @description
1046
+ * Creates a new request expectation for GET requests. For more info see `expect()`.
1047
+ *
1048
+ * @param {string|RegExp} url HTTP url.
1049
+ * @param {Object=} headers HTTP headers.
1050
+ * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1051
+ * request is handled. See #expect for more info.
1052
+ */
1053
+
1054
+ /**
1055
+ * @ngdoc method
1056
+ * @name ngMock.$httpBackend#expectHEAD
1057
+ * @methodOf ngMock.$httpBackend
1058
+ * @description
1059
+ * Creates a new request expectation for HEAD requests. For more info see `expect()`.
1060
+ *
1061
+ * @param {string|RegExp} url HTTP url.
1062
+ * @param {Object=} headers HTTP headers.
1063
+ * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1064
+ * request is handled.
1065
+ */
1066
+
1067
+ /**
1068
+ * @ngdoc method
1069
+ * @name ngMock.$httpBackend#expectDELETE
1070
+ * @methodOf ngMock.$httpBackend
1071
+ * @description
1072
+ * Creates a new request expectation for DELETE requests. For more info see `expect()`.
1073
+ *
1074
+ * @param {string|RegExp} url HTTP url.
1075
+ * @param {Object=} headers HTTP headers.
1076
+ * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1077
+ * request is handled.
1078
+ */
1079
+
1080
+ /**
1081
+ * @ngdoc method
1082
+ * @name ngMock.$httpBackend#expectPOST
1083
+ * @methodOf ngMock.$httpBackend
1084
+ * @description
1085
+ * Creates a new request expectation for POST requests. For more info see `expect()`.
1086
+ *
1087
+ * @param {string|RegExp} url HTTP url.
1088
+ * @param {(string|RegExp)=} data HTTP request body.
1089
+ * @param {Object=} headers HTTP headers.
1090
+ * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1091
+ * request is handled.
1092
+ */
1093
+
1094
+ /**
1095
+ * @ngdoc method
1096
+ * @name ngMock.$httpBackend#expectPUT
1097
+ * @methodOf ngMock.$httpBackend
1098
+ * @description
1099
+ * Creates a new request expectation for PUT requests. For more info see `expect()`.
1100
+ *
1101
+ * @param {string|RegExp} url HTTP url.
1102
+ * @param {(string|RegExp)=} data HTTP request body.
1103
+ * @param {Object=} headers HTTP headers.
1104
+ * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1105
+ * request is handled.
1106
+ */
1107
+
1108
+ /**
1109
+ * @ngdoc method
1110
+ * @name ngMock.$httpBackend#expectPATCH
1111
+ * @methodOf ngMock.$httpBackend
1112
+ * @description
1113
+ * Creates a new request expectation for PATCH requests. For more info see `expect()`.
1114
+ *
1115
+ * @param {string|RegExp} url HTTP url.
1116
+ * @param {(string|RegExp)=} data HTTP request body.
1117
+ * @param {Object=} headers HTTP headers.
1118
+ * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1119
+ * request is handled.
1120
+ */
1121
+
1122
+ /**
1123
+ * @ngdoc method
1124
+ * @name ngMock.$httpBackend#expectJSONP
1125
+ * @methodOf ngMock.$httpBackend
1126
+ * @description
1127
+ * Creates a new request expectation for JSONP requests. For more info see `expect()`.
1128
+ *
1129
+ * @param {string|RegExp} url HTTP url.
1130
+ * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1131
+ * request is handled.
1132
+ */
1133
+ createShortMethods('expect');
1134
+
1135
+
1136
+ /**
1137
+ * @ngdoc method
1138
+ * @name ngMock.$httpBackend#flush
1139
+ * @methodOf ngMock.$httpBackend
1140
+ * @description
1141
+ * Flushes all pending requests using the trained responses.
1142
+ *
1143
+ * @param {number=} count Number of responses to flush (in the order they arrived). If undefined,
1144
+ * all pending requests will be flushed. If there are no pending requests when the flush method
1145
+ * is called an exception is thrown (as this typically a sign of programming error).
1146
+ */
1147
+ $httpBackend.flush = function(count) {
1148
+ if (!responses.length) throw Error('No pending request to flush !');
1149
+
1150
+ if (angular.isDefined(count)) {
1151
+ while (count--) {
1152
+ if (!responses.length) throw Error('No more pending request to flush !');
1153
+ responses.shift()();
1154
+ }
1155
+ } else {
1156
+ while (responses.length) {
1157
+ responses.shift()();
1158
+ }
1159
+ }
1160
+ $httpBackend.verifyNoOutstandingExpectation();
1161
+ };
1162
+
1163
+
1164
+ /**
1165
+ * @ngdoc method
1166
+ * @name ngMock.$httpBackend#verifyNoOutstandingExpectation
1167
+ * @methodOf ngMock.$httpBackend
1168
+ * @description
1169
+ * Verifies that all of the requests defined via the `expect` api were made. If any of the
1170
+ * requests were not made, verifyNoOutstandingExpectation throws an exception.
1171
+ *
1172
+ * Typically, you would call this method following each test case that asserts requests using an
1173
+ * "afterEach" clause.
1174
+ *
1175
+ * <pre>
1176
+ * afterEach($httpBackend.verifyExpectations);
1177
+ * </pre>
1178
+ */
1179
+ $httpBackend.verifyNoOutstandingExpectation = function() {
1180
+ if (expectations.length) {
1181
+ throw Error('Unsatisfied requests: ' + expectations.join(', '));
1182
+ }
1183
+ };
1184
+
1185
+
1186
+ /**
1187
+ * @ngdoc method
1188
+ * @name ngMock.$httpBackend#verifyNoOutstandingRequest
1189
+ * @methodOf ngMock.$httpBackend
1190
+ * @description
1191
+ * Verifies that there are no outstanding requests that need to be flushed.
1192
+ *
1193
+ * Typically, you would call this method following each test case that asserts requests using an
1194
+ * "afterEach" clause.
1195
+ *
1196
+ * <pre>
1197
+ * afterEach($httpBackend.verifyNoOutstandingRequest);
1198
+ * </pre>
1199
+ */
1200
+ $httpBackend.verifyNoOutstandingRequest = function() {
1201
+ if (responses.length) {
1202
+ throw Error('Unflushed requests: ' + responses.length);
1203
+ }
1204
+ };
1205
+
1206
+
1207
+ /**
1208
+ * @ngdoc method
1209
+ * @name ngMock.$httpBackend#resetExpectations
1210
+ * @methodOf ngMock.$httpBackend
1211
+ * @description
1212
+ * Resets all request expectations, but preserves all backend definitions. Typically, you would
1213
+ * call resetExpectations during a multiple-phase test when you want to reuse the same instance of
1214
+ * $httpBackend mock.
1215
+ */
1216
+ $httpBackend.resetExpectations = function() {
1217
+ expectations.length = 0;
1218
+ responses.length = 0;
1219
+ };
1220
+
1221
+ return $httpBackend;
1222
+
1223
+
1224
+ function createShortMethods(prefix) {
1225
+ angular.forEach(['GET', 'DELETE', 'JSONP'], function(method) {
1226
+ $httpBackend[prefix + method] = function(url, headers) {
1227
+ return $httpBackend[prefix](method, url, undefined, headers)
1228
+ }
1229
+ });
1230
+
1231
+ angular.forEach(['PUT', 'POST', 'PATCH'], function(method) {
1232
+ $httpBackend[prefix + method] = function(url, data, headers) {
1233
+ return $httpBackend[prefix](method, url, data, headers)
1234
+ }
1235
+ });
1236
+ }
1237
+ }
1238
+
1239
+ function MockHttpExpectation(method, url, data, headers) {
1240
+
1241
+ this.data = data;
1242
+ this.headers = headers;
1243
+
1244
+ this.match = function(m, u, d, h) {
1245
+ if (method != m) return false;
1246
+ if (!this.matchUrl(u)) return false;
1247
+ if (angular.isDefined(d) && !this.matchData(d)) return false;
1248
+ if (angular.isDefined(h) && !this.matchHeaders(h)) return false;
1249
+ return true;
1250
+ };
1251
+
1252
+ this.matchUrl = function(u) {
1253
+ if (!url) return true;
1254
+ if (angular.isFunction(url.test)) return url.test(u);
1255
+ return url == u;
1256
+ };
1257
+
1258
+ this.matchHeaders = function(h) {
1259
+ if (angular.isUndefined(headers)) return true;
1260
+ if (angular.isFunction(headers)) return headers(h);
1261
+ return angular.equals(headers, h);
1262
+ };
1263
+
1264
+ this.matchData = function(d) {
1265
+ if (angular.isUndefined(data)) return true;
1266
+ if (data && angular.isFunction(data.test)) return data.test(d);
1267
+ if (data && !angular.isString(data)) return angular.toJson(data) == d;
1268
+ return data == d;
1269
+ };
1270
+
1271
+ this.toString = function() {
1272
+ return method + ' ' + url;
1273
+ };
1274
+ }
1275
+
1276
+ function MockXhr() {
1277
+
1278
+ // hack for testing $http, $httpBackend
1279
+ MockXhr.$$lastInstance = this;
1280
+
1281
+ this.open = function(method, url, async) {
1282
+ this.$$method = method;
1283
+ this.$$url = url;
1284
+ this.$$async = async;
1285
+ this.$$reqHeaders = {};
1286
+ this.$$respHeaders = {};
1287
+ };
1288
+
1289
+ this.send = function(data) {
1290
+ this.$$data = data;
1291
+ };
1292
+
1293
+ this.setRequestHeader = function(key, value) {
1294
+ this.$$reqHeaders[key] = value;
1295
+ };
1296
+
1297
+ this.getResponseHeader = function(name) {
1298
+ // the lookup must be case insensitive, that's why we try two quick lookups and full scan at last
1299
+ var header = this.$$respHeaders[name];
1300
+ if (header) return header;
1301
+
1302
+ name = angular.lowercase(name);
1303
+ header = this.$$respHeaders[name];
1304
+ if (header) return header;
1305
+
1306
+ header = undefined;
1307
+ angular.forEach(this.$$respHeaders, function(headerVal, headerName) {
1308
+ if (!header && angular.lowercase(headerName) == name) header = headerVal;
1309
+ });
1310
+ return header;
1311
+ };
1312
+
1313
+ this.getAllResponseHeaders = function() {
1314
+ var lines = [];
1315
+
1316
+ angular.forEach(this.$$respHeaders, function(value, key) {
1317
+ lines.push(key + ': ' + value);
1318
+ });
1319
+ return lines.join('\n');
1320
+ };
1321
+
1322
+ this.abort = angular.noop;
1323
+ }
1324
+
1325
+
1326
+ /**
1327
+ * @ngdoc function
1328
+ * @name ngMock.$timeout
1329
+ * @description
1330
+ *
1331
+ * This service is just a simple decorator for {@link ng.$timeout $timeout} service
1332
+ * that adds a "flush" method.
1333
+ */
1334
+
1335
+ /**
1336
+ * @ngdoc method
1337
+ * @name ngMock.$timeout#flush
1338
+ * @methodOf ngMock.$timeout
1339
+ * @description
1340
+ *
1341
+ * Flushes the queue of pending tasks.
1342
+ */
1343
+
1344
+ /**
1345
+ *
1346
+ */
1347
+ angular.mock.$RootElementProvider = function() {
1348
+ this.$get = function() {
1349
+ return angular.element('<div ng-app></div>');
1350
+ }
1351
+ };
1352
+
1353
+ /**
1354
+ * @ngdoc overview
1355
+ * @name ngMock
1356
+ * @description
1357
+ *
1358
+ * The `ngMock` is an angular module which is used with `ng` module and adds unit-test configuration as well as useful
1359
+ * mocks to the {@link AUTO.$injector $injector}.
1360
+ */
1361
+ angular.module('ngMock', ['ng']).provider({
1362
+ $browser: angular.mock.$BrowserProvider,
1363
+ $exceptionHandler: angular.mock.$ExceptionHandlerProvider,
1364
+ $log: angular.mock.$LogProvider,
1365
+ $httpBackend: angular.mock.$HttpBackendProvider,
1366
+ $rootElement: angular.mock.$RootElementProvider
1367
+ }).config(function($provide) {
1368
+ $provide.decorator('$timeout', function($delegate, $browser) {
1369
+ $delegate.flush = function() {
1370
+ $browser.defer.flush();
1371
+ };
1372
+ return $delegate;
1373
+ });
1374
+ });
1375
+
1376
+
1377
+ /**
1378
+ * @ngdoc overview
1379
+ * @name ngMockE2E
1380
+ * @description
1381
+ *
1382
+ * The `ngMockE2E` is an angular module which contains mocks suitable for end-to-end testing.
1383
+ * Currently there is only one mock present in this module -
1384
+ * the {@link ngMockE2E.$httpBackend e2e $httpBackend} mock.
1385
+ */
1386
+ angular.module('ngMockE2E', ['ng']).config(function($provide) {
1387
+ $provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator);
1388
+ });
1389
+
1390
+ /**
1391
+ * @ngdoc object
1392
+ * @name ngMockE2E.$httpBackend
1393
+ * @description
1394
+ * Fake HTTP backend implementation suitable for end-to-end testing or backend-less development of
1395
+ * applications that use the {@link ng.$http $http service}.
1396
+ *
1397
+ * *Note*: For fake http backend implementation suitable for unit testing please see
1398
+ * {@link ngMock.$httpBackend unit-testing $httpBackend mock}.
1399
+ *
1400
+ * This implementation can be used to respond with static or dynamic responses via the `when` api
1401
+ * and its shortcuts (`whenGET`, `whenPOST`, etc) and optionally pass through requests to the
1402
+ * real $httpBackend for specific requests (e.g. to interact with certain remote apis or to fetch
1403
+ * templates from a webserver).
1404
+ *
1405
+ * As opposed to unit-testing, in an end-to-end testing scenario or in scenario when an application
1406
+ * is being developed with the real backend api replaced with a mock, it is often desirable for
1407
+ * certain category of requests to bypass the mock and issue a real http request (e.g. to fetch
1408
+ * templates or static files from the webserver). To configure the backend with this behavior
1409
+ * use the `passThrough` request handler of `when` instead of `respond`.
1410
+ *
1411
+ * Additionally, we don't want to manually have to flush mocked out requests like we do during unit
1412
+ * testing. For this reason the e2e $httpBackend automatically flushes mocked out requests
1413
+ * automatically, closely simulating the behavior of the XMLHttpRequest object.
1414
+ *
1415
+ * To setup the application to run with this http backend, you have to create a module that depends
1416
+ * on the `ngMockE2E` and your application modules and defines the fake backend:
1417
+ *
1418
+ * <pre>
1419
+ * myAppDev = angular.module('myAppDev', ['myApp', 'ngMockE2E']);
1420
+ * myAppDev.run(function($httpBackend) {
1421
+ * phones = [{name: 'phone1'}, {name: 'phone2'}];
1422
+ *
1423
+ * // returns the current list of phones
1424
+ * $httpBackend.whenGET('/phones').respond(phones);
1425
+ *
1426
+ * // adds a new phone to the phones array
1427
+ * $httpBackend.whenPOST('/phones').respond(function(method, url, data) {
1428
+ * phones.push(angular.fromJSON(data));
1429
+ * });
1430
+ * $httpBackend.whenGET(/^\/templates\//).passThrough();
1431
+ * //...
1432
+ * });
1433
+ * </pre>
1434
+ *
1435
+ * Afterwards, bootstrap your app with this new module.
1436
+ */
1437
+
1438
+ /**
1439
+ * @ngdoc method
1440
+ * @name ngMockE2E.$httpBackend#when
1441
+ * @methodOf ngMockE2E.$httpBackend
1442
+ * @description
1443
+ * Creates a new backend definition.
1444
+ *
1445
+ * @param {string} method HTTP method.
1446
+ * @param {string|RegExp} url HTTP url.
1447
+ * @param {(string|RegExp)=} data HTTP request body.
1448
+ * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
1449
+ * object and returns true if the headers match the current definition.
1450
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1451
+ * control how a matched request is handled.
1452
+ *
1453
+ * - respond – `{function([status,] data[, headers])|function(function(method, url, data, headers)}`
1454
+ * – The respond method takes a set of static data to be returned or a function that can return
1455
+ * an array containing response status (number), response data (string) and response headers
1456
+ * (Object).
1457
+ * - passThrough – `{function()}` – Any request matching a backend definition with `passThrough`
1458
+ * handler, will be pass through to the real backend (an XHR request will be made to the
1459
+ * server.
1460
+ */
1461
+
1462
+ /**
1463
+ * @ngdoc method
1464
+ * @name ngMockE2E.$httpBackend#whenGET
1465
+ * @methodOf ngMockE2E.$httpBackend
1466
+ * @description
1467
+ * Creates a new backend definition for GET requests. For more info see `when()`.
1468
+ *
1469
+ * @param {string|RegExp} url HTTP url.
1470
+ * @param {(Object|function(Object))=} headers HTTP headers.
1471
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1472
+ * control how a matched request is handled.
1473
+ */
1474
+
1475
+ /**
1476
+ * @ngdoc method
1477
+ * @name ngMockE2E.$httpBackend#whenHEAD
1478
+ * @methodOf ngMockE2E.$httpBackend
1479
+ * @description
1480
+ * Creates a new backend definition for HEAD requests. For more info see `when()`.
1481
+ *
1482
+ * @param {string|RegExp} url HTTP url.
1483
+ * @param {(Object|function(Object))=} headers HTTP headers.
1484
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1485
+ * control how a matched request is handled.
1486
+ */
1487
+
1488
+ /**
1489
+ * @ngdoc method
1490
+ * @name ngMockE2E.$httpBackend#whenDELETE
1491
+ * @methodOf ngMockE2E.$httpBackend
1492
+ * @description
1493
+ * Creates a new backend definition for DELETE requests. For more info see `when()`.
1494
+ *
1495
+ * @param {string|RegExp} url HTTP url.
1496
+ * @param {(Object|function(Object))=} headers HTTP headers.
1497
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1498
+ * control how a matched request is handled.
1499
+ */
1500
+
1501
+ /**
1502
+ * @ngdoc method
1503
+ * @name ngMockE2E.$httpBackend#whenPOST
1504
+ * @methodOf ngMockE2E.$httpBackend
1505
+ * @description
1506
+ * Creates a new backend definition for POST requests. For more info see `when()`.
1507
+ *
1508
+ * @param {string|RegExp} url HTTP url.
1509
+ * @param {(string|RegExp)=} data HTTP request body.
1510
+ * @param {(Object|function(Object))=} headers HTTP headers.
1511
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1512
+ * control how a matched request is handled.
1513
+ */
1514
+
1515
+ /**
1516
+ * @ngdoc method
1517
+ * @name ngMockE2E.$httpBackend#whenPUT
1518
+ * @methodOf ngMockE2E.$httpBackend
1519
+ * @description
1520
+ * Creates a new backend definition for PUT requests. For more info see `when()`.
1521
+ *
1522
+ * @param {string|RegExp} url HTTP url.
1523
+ * @param {(string|RegExp)=} data HTTP request body.
1524
+ * @param {(Object|function(Object))=} headers HTTP headers.
1525
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1526
+ * control how a matched request is handled.
1527
+ */
1528
+
1529
+ /**
1530
+ * @ngdoc method
1531
+ * @name ngMockE2E.$httpBackend#whenPATCH
1532
+ * @methodOf ngMockE2E.$httpBackend
1533
+ * @description
1534
+ * Creates a new backend definition for PATCH requests. For more info see `when()`.
1535
+ *
1536
+ * @param {string|RegExp} url HTTP url.
1537
+ * @param {(string|RegExp)=} data HTTP request body.
1538
+ * @param {(Object|function(Object))=} headers HTTP headers.
1539
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1540
+ * control how a matched request is handled.
1541
+ */
1542
+
1543
+ /**
1544
+ * @ngdoc method
1545
+ * @name ngMockE2E.$httpBackend#whenJSONP
1546
+ * @methodOf ngMockE2E.$httpBackend
1547
+ * @description
1548
+ * Creates a new backend definition for JSONP requests. For more info see `when()`.
1549
+ *
1550
+ * @param {string|RegExp} url HTTP url.
1551
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1552
+ * control how a matched request is handled.
1553
+ */
1554
+ angular.mock.e2e = {};
1555
+ angular.mock.e2e.$httpBackendDecorator = ['$delegate', '$browser', createHttpBackendMock];
1556
+
1557
+
1558
+ angular.mock.clearDataCache = function() {
1559
+ var key,
1560
+ cache = angular.element.cache;
1561
+
1562
+ for(key in cache) {
1563
+ if (cache.hasOwnProperty(key)) {
1564
+ var handle = cache[key].handle;
1565
+
1566
+ handle && angular.element(handle.elem).unbind();
1567
+ delete cache[key];
1568
+ }
1569
+ }
1570
+ };
1571
+
1572
+
1573
+ window.jstestdriver && (function(window) {
1574
+ /**
1575
+ * Global method to output any number of objects into JSTD console. Useful for debugging.
1576
+ */
1577
+ window.dump = function() {
1578
+ var args = [];
1579
+ angular.forEach(arguments, function(arg) {
1580
+ args.push(angular.mock.dump(arg));
1581
+ });
1582
+ jstestdriver.console.log.apply(jstestdriver.console, args);
1583
+ if (window.console) {
1584
+ window.console.log.apply(window.console, args);
1585
+ }
1586
+ };
1587
+ })(window);
1588
+
1589
+
1590
+ window.jasmine && (function(window) {
1591
+
1592
+ afterEach(function() {
1593
+ var spec = getCurrentSpec();
1594
+ spec.$injector = null;
1595
+ spec.$modules = null;
1596
+ angular.mock.clearDataCache();
1597
+ });
1598
+
1599
+ function getCurrentSpec() {
1600
+ return jasmine.getEnv().currentSpec;
1601
+ }
1602
+
1603
+ function isSpecRunning() {
1604
+ var spec = getCurrentSpec();
1605
+ return spec && spec.queue.running;
1606
+ }
1607
+
1608
+ /**
1609
+ * @ngdoc function
1610
+ * @name angular.mock.module
1611
+ * @description
1612
+ *
1613
+ * *NOTE*: This is function is also published on window for easy access.<br>
1614
+ * *NOTE*: Only available with {@link http://pivotal.github.com/jasmine/ jasmine}.
1615
+ *
1616
+ * This function registers a module configuration code. It collects the configuration information
1617
+ * which will be used when the injector is created by {@link angular.mock.inject inject}.
1618
+ *
1619
+ * See {@link angular.mock.inject inject} for usage example
1620
+ *
1621
+ * @param {...(string|Function)} fns any number of modules which are represented as string
1622
+ * aliases or as anonymous module initialization functions. The modules are used to
1623
+ * configure the injector. The 'ng' and 'ngMock' modules are automatically loaded.
1624
+ */
1625
+ window.module = angular.mock.module = function() {
1626
+ var moduleFns = Array.prototype.slice.call(arguments, 0);
1627
+ return isSpecRunning() ? workFn() : workFn;
1628
+ /////////////////////
1629
+ function workFn() {
1630
+ var spec = getCurrentSpec();
1631
+ if (spec.$injector) {
1632
+ throw Error('Injector already created, can not register a module!');
1633
+ } else {
1634
+ var modules = spec.$modules || (spec.$modules = []);
1635
+ angular.forEach(moduleFns, function(module) {
1636
+ modules.push(module);
1637
+ });
1638
+ }
1639
+ }
1640
+ };
1641
+
1642
+ /**
1643
+ * @ngdoc function
1644
+ * @name angular.mock.inject
1645
+ * @description
1646
+ *
1647
+ * *NOTE*: This is function is also published on window for easy access.<br>
1648
+ * *NOTE*: Only available with {@link http://pivotal.github.com/jasmine/ jasmine}.
1649
+ *
1650
+ * The inject function wraps a function into an injectable function. The inject() creates new
1651
+ * instance of {@link AUTO.$injector $injector} per test, which is then used for
1652
+ * resolving references.
1653
+ *
1654
+ * See also {@link angular.mock.module module}
1655
+ *
1656
+ * Example of what a typical jasmine tests looks like with the inject method.
1657
+ * <pre>
1658
+ *
1659
+ * angular.module('myApplicationModule', [])
1660
+ * .value('mode', 'app')
1661
+ * .value('version', 'v1.0.1');
1662
+ *
1663
+ *
1664
+ * describe('MyApp', function() {
1665
+ *
1666
+ * // You need to load modules that you want to test,
1667
+ * // it loads only the "ng" module by default.
1668
+ * beforeEach(module('myApplicationModule'));
1669
+ *
1670
+ *
1671
+ * // inject() is used to inject arguments of all given functions
1672
+ * it('should provide a version', inject(function(mode, version) {
1673
+ * expect(version).toEqual('v1.0.1');
1674
+ * expect(mode).toEqual('app');
1675
+ * }));
1676
+ *
1677
+ *
1678
+ * // The inject and module method can also be used inside of the it or beforeEach
1679
+ * it('should override a version and test the new version is injected', function() {
1680
+ * // module() takes functions or strings (module aliases)
1681
+ * module(function($provide) {
1682
+ * $provide.value('version', 'overridden'); // override version here
1683
+ * });
1684
+ *
1685
+ * inject(function(version) {
1686
+ * expect(version).toEqual('overridden');
1687
+ * });
1688
+ * ));
1689
+ * });
1690
+ *
1691
+ * </pre>
1692
+ *
1693
+ * @param {...Function} fns any number of functions which will be injected using the injector.
1694
+ */
1695
+ window.inject = angular.mock.inject = function() {
1696
+ var blockFns = Array.prototype.slice.call(arguments, 0);
1697
+ var stack = new Error('Declaration Location').stack;
1698
+ return isSpecRunning() ? workFn() : workFn;
1699
+ /////////////////////
1700
+ function workFn() {
1701
+ var spec = getCurrentSpec();
1702
+ var modules = spec.$modules || [];
1703
+ modules.unshift('ngMock');
1704
+ modules.unshift('ng');
1705
+ var injector = spec.$injector;
1706
+ if (!injector) {
1707
+ injector = spec.$injector = angular.injector(modules);
1708
+ }
1709
+ for(var i = 0, ii = blockFns.length; i < ii; i++) {
1710
+ try {
1711
+ injector.invoke(blockFns[i] || angular.noop, this);
1712
+ } catch (e) {
1713
+ if(e.stack) e.stack += '\n' + stack;
1714
+ throw e;
1715
+ }
1716
+ }
1717
+ }
1718
+ }
1719
+ })(window);