simple_pvr 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. data/.rspec +1 -0
  2. data/Gemfile +4 -0
  3. data/Gemfile.lock +133 -0
  4. data/LICENSE.txt +13 -0
  5. data/README.md +169 -0
  6. data/Rakefile +16 -0
  7. data/bin/pvr_server +10 -0
  8. data/bin/pvr_xmltv +18 -0
  9. data/features/channel_overview.feature +48 -0
  10. data/features/programme_search.feature +20 -0
  11. data/features/scheduling.feature +112 -0
  12. data/features/step_definitions/pvr_steps.rb +104 -0
  13. data/features/step_definitions/web_steps.rb +219 -0
  14. data/features/support/env.rb +28 -0
  15. data/features/support/paths.rb +17 -0
  16. data/features/week_overview.feature +39 -0
  17. data/lib/simple_pvr/ffmpeg.rb +22 -0
  18. data/lib/simple_pvr/hdhomerun.rb +103 -0
  19. data/lib/simple_pvr/hdhomerun_save.sh +10 -0
  20. data/lib/simple_pvr/model/channel.rb +72 -0
  21. data/lib/simple_pvr/model/database_initializer.rb +36 -0
  22. data/lib/simple_pvr/model/programme.rb +58 -0
  23. data/lib/simple_pvr/model/recording.rb +45 -0
  24. data/lib/simple_pvr/model/schedule.rb +33 -0
  25. data/lib/simple_pvr/pvr_initializer.rb +47 -0
  26. data/lib/simple_pvr/pvr_logger.rb +14 -0
  27. data/lib/simple_pvr/recorder.rb +25 -0
  28. data/lib/simple_pvr/recording_manager.rb +101 -0
  29. data/lib/simple_pvr/recording_planner.rb +72 -0
  30. data/lib/simple_pvr/scheduler.rb +124 -0
  31. data/lib/simple_pvr/server/app_controller.rb +13 -0
  32. data/lib/simple_pvr/server/base_controller.rb +94 -0
  33. data/lib/simple_pvr/server/channels_controller.rb +68 -0
  34. data/lib/simple_pvr/server/config.ru +8 -0
  35. data/lib/simple_pvr/server/programmes_controller.rb +46 -0
  36. data/lib/simple_pvr/server/rack_maps.rb +7 -0
  37. data/lib/simple_pvr/server/schedules_controller.rb +71 -0
  38. data/lib/simple_pvr/server/shows_controller.rb +63 -0
  39. data/lib/simple_pvr/server/status_controller.rb +11 -0
  40. data/lib/simple_pvr/server/upcoming_recordings_controller.rb +18 -0
  41. data/lib/simple_pvr/version.rb +3 -0
  42. data/lib/simple_pvr/xmltv_reader.rb +83 -0
  43. data/lib/simple_pvr.rb +22 -0
  44. data/public/css/bootstrap-responsive.min.css +9 -0
  45. data/public/css/bootstrap.min.css +9 -0
  46. data/public/css/simplepvr.css +11 -0
  47. data/public/img/glyphicons-halflings-white.png +0 -0
  48. data/public/img/glyphicons-halflings.png +0 -0
  49. data/public/index.html +55 -0
  50. data/public/js/angular/angular-resource.min.js +10 -0
  51. data/public/js/angular/angular.min.js +157 -0
  52. data/public/js/app.js +145 -0
  53. data/public/js/bootstrap/bootstrap.min.js +6 -0
  54. data/public/js/controllers.js +156 -0
  55. data/public/js/services.js +27 -0
  56. data/public/partials/about.html +5 -0
  57. data/public/partials/channels.html +41 -0
  58. data/public/partials/programme.html +20 -0
  59. data/public/partials/programmeListing.html +18 -0
  60. data/public/partials/schedule.html +80 -0
  61. data/public/partials/schedules.html +44 -0
  62. data/public/partials/search.html +17 -0
  63. data/public/partials/show.html +21 -0
  64. data/public/partials/shows.html +7 -0
  65. data/public/partials/status.html +6 -0
  66. data/simple_pvr.gemspec +30 -0
  67. data/spec/resources/channels.txt +11 -0
  68. data/spec/resources/programs-without-icon.xmltv +95 -0
  69. data/spec/resources/programs.xmltv +98 -0
  70. data/spec/simple_pvr/ffmpeg_spec.rb +26 -0
  71. data/spec/simple_pvr/hdhomerun_spec.rb +82 -0
  72. data/spec/simple_pvr/model/channel_spec.rb +114 -0
  73. data/spec/simple_pvr/model/programme_spec.rb +110 -0
  74. data/spec/simple_pvr/model/schedule_spec.rb +47 -0
  75. data/spec/simple_pvr/pvr_initializer_spec.rb +50 -0
  76. data/spec/simple_pvr/recorder_spec.rb +32 -0
  77. data/spec/simple_pvr/recording_manager_spec.rb +158 -0
  78. data/spec/simple_pvr/recording_planner_spec.rb +104 -0
  79. data/spec/simple_pvr/scheduler_spec.rb +201 -0
  80. data/spec/simple_pvr/xmltv_reader_spec.rb +49 -0
  81. data/test/config/jsTestDriver-scenario.conf +10 -0
  82. data/test/config/jsTestDriver.conf +12 -0
  83. data/test/config/jstd-scenario-adapter-config.js +6 -0
  84. data/test/filtersSpec.js +97 -0
  85. data/test/lib/angular/angular-mocks.js +1719 -0
  86. data/test/lib/angular/angular-scenario.js +25937 -0
  87. data/test/lib/angular/jstd-scenario-adapter.js +185 -0
  88. data/test/lib/angular/version.txt +1 -0
  89. data/test/lib/jasmine/MIT.LICENSE +20 -0
  90. data/test/lib/jasmine/index.js +180 -0
  91. data/test/lib/jasmine/jasmine-html.js +190 -0
  92. data/test/lib/jasmine/jasmine.css +166 -0
  93. data/test/lib/jasmine/jasmine.js +2476 -0
  94. data/test/lib/jasmine/jasmine_favicon.png +0 -0
  95. data/test/lib/jasmine/version.txt +1 -0
  96. data/test/lib/jasmine-jstd-adapter/JasmineAdapter.js +196 -0
  97. data/test/lib/jasmine-jstd-adapter/version.txt +1 -0
  98. data/test/lib/jstestdriver/JsTestDriver.jar +0 -0
  99. data/test/lib/jstestdriver/version.txt +1 -0
  100. data/test/scripts/test-server.sh +14 -0
  101. data/test/scripts/test.sh +8 -0
  102. metadata +342 -0
@@ -0,0 +1,1719 @@
1
+
2
+ /**
3
+ * @license AngularJS v1.0.1
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);