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