├── logs
└── .gitkeep
├── app
├── partials
│ ├── .gitkeep
│ ├── partial1.html
│ └── lorem-image.html
├── js
│ ├── app.js
│ ├── directives.js
│ └── services.js
└── index.html
├── test
├── lib
│ └── angular
│ │ ├── version.txt
│ │ └── angular-mocks.js
├── e2e
│ ├── runner.html
│ └── scenarios.js
└── unit
│ ├── controllersSpec.js
│ ├── servicesSpec.js
│ ├── filtersSpec.js
│ └── directivesSpec.js
├── .gitignore
├── scripts
├── test.sh
├── e2e-test.bat
├── e2e-test.sh
├── test.bat
├── watchr.rb
└── web-server.js
├── package.json
├── .travis.yml
├── bower.json
├── config
├── karma-e2e.conf.js
└── karma.conf.js
├── LICENSE
└── README.md
/logs/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/partials/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/lib/angular/version.txt:
--------------------------------------------------------------------------------
1 | 1.2.3
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | logs/*
2 | !.gitkeep
3 | node_modules/
4 | tmp
5 | .DS_Store
--------------------------------------------------------------------------------
/app/partials/partial1.html:
--------------------------------------------------------------------------------
1 | This is ze demo
2 |
' + escapeHtml(util.inspect(error)) + '');
113 | util.puts('500 Internal Server Error');
114 | util.puts(util.inspect(error));
115 | };
116 |
117 | StaticServlet.prototype.sendMissing_ = function(req, res, path) {
118 | path = path.substring(1);
119 | res.writeHead(404, {
120 | 'Content-Type': 'text/html'
121 | });
122 | res.write('\n');
123 | res.write('The requested URL ' + 127 | escapeHtml(path) + 128 | ' was not found on this server.
' 129 | ); 130 | res.end(); 131 | util.puts('404 Not Found: ' + path); 132 | }; 133 | 134 | StaticServlet.prototype.sendForbidden_ = function(req, res, path) { 135 | path = path.substring(1); 136 | res.writeHead(403, { 137 | 'Content-Type': 'text/html' 138 | }); 139 | res.write('\n'); 140 | res.write('You do not have permission to access ' + 144 | escapeHtml(path) + ' on this server.
' 145 | ); 146 | res.end(); 147 | util.puts('403 Forbidden: ' + path); 148 | }; 149 | 150 | StaticServlet.prototype.sendRedirect_ = function(req, res, redirectUrl) { 151 | res.writeHead(301, { 152 | 'Content-Type': 'text/html', 153 | 'Location': redirectUrl 154 | }); 155 | res.write('\n'); 156 | res.write('The document has moved here.
' 162 | ); 163 | res.end(); 164 | util.puts('301 Moved Permanently: ' + redirectUrl); 165 | }; 166 | 167 | StaticServlet.prototype.sendFile_ = function(req, res, path) { 168 | var self = this; 169 | var file = fs.createReadStream(path); 170 | res.writeHead(200, { 171 | 'Content-Type': StaticServlet. 172 | MimeMap[path.split('.').pop()] || 'text/plain' 173 | }); 174 | if (req.method === 'HEAD') { 175 | res.end(); 176 | } else { 177 | file.on('data', res.write.bind(res)); 178 | file.on('close', function() { 179 | res.end(); 180 | }); 181 | file.on('error', function(error) { 182 | self.sendError_(req, res, error); 183 | }); 184 | } 185 | }; 186 | 187 | StaticServlet.prototype.sendDirectory_ = function(req, res, path) { 188 | var self = this; 189 | if (path.match(/[^\/]$/)) { 190 | req.url.pathname += '/'; 191 | var redirectUrl = url.format(url.parse(url.format(req.url))); 192 | return self.sendRedirect_(req, res, redirectUrl); 193 | } 194 | fs.readdir(path, function(err, files) { 195 | if (err) 196 | return self.sendError_(req, res, error); 197 | 198 | if (!files.length) 199 | return self.writeDirectoryIndex_(req, res, path, []); 200 | 201 | var remaining = files.length; 202 | files.forEach(function(fileName, index) { 203 | fs.stat(path + '/' + fileName, function(err, stat) { 204 | if (err) 205 | return self.sendError_(req, res, err); 206 | if (stat.isDirectory()) { 207 | files[index] = fileName + '/'; 208 | } 209 | if (!(--remaining)) 210 | return self.writeDirectoryIndex_(req, res, path, files); 211 | }); 212 | }); 213 | }); 214 | }; 215 | 216 | StaticServlet.prototype.writeDirectoryIndex_ = function(req, res, path, files) { 217 | path = path.substring(1); 218 | res.writeHead(200, { 219 | 'Content-Type': 'text/html' 220 | }); 221 | if (req.method === 'HEAD') { 222 | res.end(); 223 | return; 224 | } 225 | res.write('\n'); 226 | res.write('
209 | * describe('$exceptionHandlerProvider', function() {
210 | *
211 | * it('should capture log messages and exceptions', function() {
212 | *
213 | * module(function($exceptionHandlerProvider) {
214 | * $exceptionHandlerProvider.mode('log');
215 | * });
216 | *
217 | * inject(function($log, $exceptionHandler, $timeout) {
218 | * $timeout(function() { $log.log(1); });
219 | * $timeout(function() { $log.log(2); throw 'banana peel'; });
220 | * $timeout(function() { $log.log(3); });
221 | * expect($exceptionHandler.errors).toEqual([]);
222 | * expect($log.assertEmpty());
223 | * $timeout.flush();
224 | * expect($exceptionHandler.errors).toEqual(['banana peel']);
225 | * expect($log.log.logs).toEqual([[1], [2], [3]]);
226 | * });
227 | * });
228 | * });
229 | *
230 | */
231 |
232 | angular.mock.$ExceptionHandlerProvider = function() {
233 | var handler;
234 |
235 | /**
236 | * @ngdoc method
237 | * @name ngMock.$exceptionHandlerProvider#mode
238 | * @methodOf ngMock.$exceptionHandlerProvider
239 | *
240 | * @description
241 | * Sets the logging mode.
242 | *
243 | * @param {string} mode Mode of operation, defaults to `rethrow`.
244 | *
245 | * - `rethrow`: If any errors are passed into the handler in tests, it typically
246 | * means that there is a bug in the application or test, so this mock will
247 | * make these tests fail.
248 | * - `log`: Sometimes it is desirable to test that an error is thrown, for this case the `log`
249 | * mode stores an array of errors in `$exceptionHandler.errors`, to allow later
250 | * assertion of them. See {@link ngMock.$log#assertEmpty assertEmpty()} and
251 | * {@link ngMock.$log#reset reset()}
252 | */
253 | this.mode = function(mode) {
254 | switch(mode) {
255 | case 'rethrow':
256 | handler = function(e) {
257 | throw e;
258 | };
259 | break;
260 | case 'log':
261 | var errors = [];
262 |
263 | handler = function(e) {
264 | if (arguments.length == 1) {
265 | errors.push(e);
266 | } else {
267 | errors.push([].slice.call(arguments, 0));
268 | }
269 | };
270 |
271 | handler.errors = errors;
272 | break;
273 | default:
274 | throw new Error("Unknown mode '" + mode + "', only 'log'/'rethrow' modes are allowed!");
275 | }
276 | };
277 |
278 | this.$get = function() {
279 | return handler;
280 | };
281 |
282 | this.mode('rethrow');
283 | };
284 |
285 |
286 | /**
287 | * @ngdoc service
288 | * @name ngMock.$log
289 | *
290 | * @description
291 | * Mock implementation of {@link ng.$log} that gathers all logged messages in arrays
292 | * (one array per logging level). These arrays are exposed as `logs` property of each of the
293 | * level-specific log function, e.g. for level `error` the array is exposed as `$log.error.logs`.
294 | *
295 | */
296 | angular.mock.$LogProvider = function() {
297 | var debug = true;
298 |
299 | function concat(array1, array2, index) {
300 | return array1.concat(Array.prototype.slice.call(array2, index));
301 | }
302 |
303 | this.debugEnabled = function(flag) {
304 | if (angular.isDefined(flag)) {
305 | debug = flag;
306 | return this;
307 | } else {
308 | return debug;
309 | }
310 | };
311 |
312 | this.$get = function () {
313 | var $log = {
314 | log: function() { $log.log.logs.push(concat([], arguments, 0)); },
315 | warn: function() { $log.warn.logs.push(concat([], arguments, 0)); },
316 | info: function() { $log.info.logs.push(concat([], arguments, 0)); },
317 | error: function() { $log.error.logs.push(concat([], arguments, 0)); },
318 | debug: function() {
319 | if (debug) {
320 | $log.debug.logs.push(concat([], arguments, 0));
321 | }
322 | }
323 | };
324 |
325 | /**
326 | * @ngdoc method
327 | * @name ngMock.$log#reset
328 | * @methodOf ngMock.$log
329 | *
330 | * @description
331 | * Reset all of the logging arrays to empty.
332 | */
333 | $log.reset = function () {
334 | /**
335 | * @ngdoc property
336 | * @name ngMock.$log#log.logs
337 | * @propertyOf ngMock.$log
338 | *
339 | * @description
340 | * Array of messages logged using {@link ngMock.$log#log}.
341 | *
342 | * @example
343 | *
344 | * $log.log('Some Log');
345 | * var first = $log.log.logs.unshift();
346 | *
347 | */
348 | $log.log.logs = [];
349 | /**
350 | * @ngdoc property
351 | * @name ngMock.$log#info.logs
352 | * @propertyOf ngMock.$log
353 | *
354 | * @description
355 | * Array of messages logged using {@link ngMock.$log#info}.
356 | *
357 | * @example
358 | *
359 | * $log.info('Some Info');
360 | * var first = $log.info.logs.unshift();
361 | *
362 | */
363 | $log.info.logs = [];
364 | /**
365 | * @ngdoc property
366 | * @name ngMock.$log#warn.logs
367 | * @propertyOf ngMock.$log
368 | *
369 | * @description
370 | * Array of messages logged using {@link ngMock.$log#warn}.
371 | *
372 | * @example
373 | *
374 | * $log.warn('Some Warning');
375 | * var first = $log.warn.logs.unshift();
376 | *
377 | */
378 | $log.warn.logs = [];
379 | /**
380 | * @ngdoc property
381 | * @name ngMock.$log#error.logs
382 | * @propertyOf ngMock.$log
383 | *
384 | * @description
385 | * Array of messages logged using {@link ngMock.$log#error}.
386 | *
387 | * @example
388 | *
389 | * $log.log('Some Error');
390 | * var first = $log.error.logs.unshift();
391 | *
392 | */
393 | $log.error.logs = [];
394 | /**
395 | * @ngdoc property
396 | * @name ngMock.$log#debug.logs
397 | * @propertyOf ngMock.$log
398 | *
399 | * @description
400 | * Array of messages logged using {@link ngMock.$log#debug}.
401 | *
402 | * @example
403 | *
404 | * $log.debug('Some Error');
405 | * var first = $log.debug.logs.unshift();
406 | *
407 | */
408 | $log.debug.logs = [];
409 | };
410 |
411 | /**
412 | * @ngdoc method
413 | * @name ngMock.$log#assertEmpty
414 | * @methodOf ngMock.$log
415 | *
416 | * @description
417 | * Assert that the all of the logging methods have no logged messages. If messages present, an
418 | * exception is thrown.
419 | */
420 | $log.assertEmpty = function() {
421 | var errors = [];
422 | angular.forEach(['error', 'warn', 'info', 'log', 'debug'], function(logLevel) {
423 | angular.forEach($log[logLevel].logs, function(log) {
424 | angular.forEach(log, function (logItem) {
425 | errors.push('MOCK $log (' + logLevel + '): ' + String(logItem) + '\n' +
426 | (logItem.stack || ''));
427 | });
428 | });
429 | });
430 | if (errors.length) {
431 | errors.unshift("Expected $log to be empty! Either a message was logged unexpectedly, or "+
432 | "an expected log message was not checked and removed:");
433 | errors.push('');
434 | throw new Error(errors.join('\n---------\n'));
435 | }
436 | };
437 |
438 | $log.reset();
439 | return $log;
440 | };
441 | };
442 |
443 |
444 | /**
445 | * @ngdoc service
446 | * @name ngMock.$interval
447 | *
448 | * @description
449 | * Mock implementation of the $interval service.
450 | *
451 | * Use {@link ngMock.$interval#methods_flush `$interval.flush(millis)`} to
452 | * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
453 | * time.
454 | *
455 | * @param {function()} fn A function that should be called repeatedly.
456 | * @param {number} delay Number of milliseconds between each function call.
457 | * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
458 | * indefinitely.
459 | * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
460 | * will invoke `fn` within the {@link ng.$rootScope.Scope#methods_$apply $apply} block.
461 | * @returns {promise} A promise which will be notified on each iteration.
462 | */
463 | angular.mock.$IntervalProvider = function() {
464 | this.$get = ['$rootScope', '$q',
465 | function($rootScope, $q) {
466 | var repeatFns = [],
467 | nextRepeatId = 0,
468 | now = 0;
469 |
470 | var $interval = function(fn, delay, count, invokeApply) {
471 | var deferred = $q.defer(),
472 | promise = deferred.promise,
473 | iteration = 0,
474 | skipApply = (angular.isDefined(invokeApply) && !invokeApply);
475 |
476 | count = (angular.isDefined(count)) ? count : 0,
477 | promise.then(null, null, fn);
478 |
479 | promise.$$intervalId = nextRepeatId;
480 |
481 | function tick() {
482 | deferred.notify(iteration++);
483 |
484 | if (count > 0 && iteration >= count) {
485 | var fnIndex;
486 | deferred.resolve(iteration);
487 |
488 | angular.forEach(repeatFns, function(fn, index) {
489 | if (fn.id === promise.$$intervalId) fnIndex = index;
490 | });
491 |
492 | if (fnIndex !== undefined) {
493 | repeatFns.splice(fnIndex, 1);
494 | }
495 | }
496 |
497 | if (!skipApply) $rootScope.$apply();
498 | }
499 |
500 | repeatFns.push({
501 | nextTime:(now + delay),
502 | delay: delay,
503 | fn: tick,
504 | id: nextRepeatId,
505 | deferred: deferred
506 | });
507 | repeatFns.sort(function(a,b){ return a.nextTime - b.nextTime;});
508 |
509 | nextRepeatId++;
510 | return promise;
511 | };
512 |
513 | $interval.cancel = function(promise) {
514 | var fnIndex;
515 |
516 | angular.forEach(repeatFns, function(fn, index) {
517 | if (fn.id === promise.$$intervalId) fnIndex = index;
518 | });
519 |
520 | if (fnIndex !== undefined) {
521 | repeatFns[fnIndex].deferred.reject('canceled');
522 | repeatFns.splice(fnIndex, 1);
523 | return true;
524 | }
525 |
526 | return false;
527 | };
528 |
529 | /**
530 | * @ngdoc method
531 | * @name ngMock.$interval#flush
532 | * @methodOf ngMock.$interval
533 | * @description
534 | *
535 | * Runs interval tasks scheduled to be run in the next `millis` milliseconds.
536 | *
537 | * @param {number=} millis maximum timeout amount to flush up until.
538 | *
539 | * @return {number} The amount of time moved forward.
540 | */
541 | $interval.flush = function(millis) {
542 | now += millis;
543 | while (repeatFns.length && repeatFns[0].nextTime <= now) {
544 | var task = repeatFns[0];
545 | task.fn();
546 | task.nextTime += task.delay;
547 | repeatFns.sort(function(a,b){ return a.nextTime - b.nextTime;});
548 | }
549 | return millis;
550 | };
551 |
552 | return $interval;
553 | }];
554 | };
555 |
556 |
557 | /* jshint -W101 */
558 | /* The R_ISO8061_STR regex is never going to fit into the 100 char limit!
559 | * This directive should go inside the anonymous function but a bug in JSHint means that it would
560 | * not be enacted early enough to prevent the warning.
561 | */
562 | var R_ISO8061_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?:\:?(\d\d)(?:\:?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/;
563 |
564 | function jsonStringToDate(string) {
565 | var match;
566 | if (match = string.match(R_ISO8061_STR)) {
567 | var date = new Date(0),
568 | tzHour = 0,
569 | tzMin = 0;
570 | if (match[9]) {
571 | tzHour = int(match[9] + match[10]);
572 | tzMin = int(match[9] + match[11]);
573 | }
574 | date.setUTCFullYear(int(match[1]), int(match[2]) - 1, int(match[3]));
575 | date.setUTCHours(int(match[4]||0) - tzHour,
576 | int(match[5]||0) - tzMin,
577 | int(match[6]||0),
578 | int(match[7]||0));
579 | return date;
580 | }
581 | return string;
582 | }
583 |
584 | function int(str) {
585 | return parseInt(str, 10);
586 | }
587 |
588 | function padNumber(num, digits, trim) {
589 | var neg = '';
590 | if (num < 0) {
591 | neg = '-';
592 | num = -num;
593 | }
594 | num = '' + num;
595 | while(num.length < digits) num = '0' + num;
596 | if (trim)
597 | num = num.substr(num.length - digits);
598 | return neg + num;
599 | }
600 |
601 |
602 | /**
603 | * @ngdoc object
604 | * @name angular.mock.TzDate
605 | * @description
606 | *
607 | * *NOTE*: this is not an injectable instance, just a globally available mock class of `Date`.
608 | *
609 | * Mock of the Date type which has its timezone specified via constructor arg.
610 | *
611 | * The main purpose is to create Date-like instances with timezone fixed to the specified timezone
612 | * offset, so that we can test code that depends on local timezone settings without dependency on
613 | * the time zone settings of the machine where the code is running.
614 | *
615 | * @param {number} offset Offset of the *desired* timezone in hours (fractions will be honored)
616 | * @param {(number|string)} timestamp Timestamp representing the desired time in *UTC*
617 | *
618 | * @example
619 | * !!!! WARNING !!!!!
620 | * This is not a complete Date object so only methods that were implemented can be called safely.
621 | * To make matters worse, TzDate instances inherit stuff from Date via a prototype.
622 | *
623 | * We do our best to intercept calls to "unimplemented" methods, but since the list of methods is
624 | * incomplete we might be missing some non-standard methods. This can result in errors like:
625 | * "Date.prototype.foo called on incompatible Object".
626 | *
627 | *
628 | * var newYearInBratislava = new TzDate(-1, '2009-12-31T23:00:00Z');
629 | * newYearInBratislava.getTimezoneOffset() => -60;
630 | * newYearInBratislava.getFullYear() => 2010;
631 | * newYearInBratislava.getMonth() => 0;
632 | * newYearInBratislava.getDate() => 1;
633 | * newYearInBratislava.getHours() => 0;
634 | * newYearInBratislava.getMinutes() => 0;
635 | * newYearInBratislava.getSeconds() => 0;
636 | *
637 | *
638 | */
639 | angular.mock.TzDate = function (offset, timestamp) {
640 | var self = new Date(0);
641 | if (angular.isString(timestamp)) {
642 | var tsStr = timestamp;
643 |
644 | self.origDate = jsonStringToDate(timestamp);
645 |
646 | timestamp = self.origDate.getTime();
647 | if (isNaN(timestamp))
648 | throw {
649 | name: "Illegal Argument",
650 | message: "Arg '" + tsStr + "' passed into TzDate constructor is not a valid date string"
651 | };
652 | } else {
653 | self.origDate = new Date(timestamp);
654 | }
655 |
656 | var localOffset = new Date(timestamp).getTimezoneOffset();
657 | self.offsetDiff = localOffset*60*1000 - offset*1000*60*60;
658 | self.date = new Date(timestamp + self.offsetDiff);
659 |
660 | self.getTime = function() {
661 | return self.date.getTime() - self.offsetDiff;
662 | };
663 |
664 | self.toLocaleDateString = function() {
665 | return self.date.toLocaleDateString();
666 | };
667 |
668 | self.getFullYear = function() {
669 | return self.date.getFullYear();
670 | };
671 |
672 | self.getMonth = function() {
673 | return self.date.getMonth();
674 | };
675 |
676 | self.getDate = function() {
677 | return self.date.getDate();
678 | };
679 |
680 | self.getHours = function() {
681 | return self.date.getHours();
682 | };
683 |
684 | self.getMinutes = function() {
685 | return self.date.getMinutes();
686 | };
687 |
688 | self.getSeconds = function() {
689 | return self.date.getSeconds();
690 | };
691 |
692 | self.getMilliseconds = function() {
693 | return self.date.getMilliseconds();
694 | };
695 |
696 | self.getTimezoneOffset = function() {
697 | return offset * 60;
698 | };
699 |
700 | self.getUTCFullYear = function() {
701 | return self.origDate.getUTCFullYear();
702 | };
703 |
704 | self.getUTCMonth = function() {
705 | return self.origDate.getUTCMonth();
706 | };
707 |
708 | self.getUTCDate = function() {
709 | return self.origDate.getUTCDate();
710 | };
711 |
712 | self.getUTCHours = function() {
713 | return self.origDate.getUTCHours();
714 | };
715 |
716 | self.getUTCMinutes = function() {
717 | return self.origDate.getUTCMinutes();
718 | };
719 |
720 | self.getUTCSeconds = function() {
721 | return self.origDate.getUTCSeconds();
722 | };
723 |
724 | self.getUTCMilliseconds = function() {
725 | return self.origDate.getUTCMilliseconds();
726 | };
727 |
728 | self.getDay = function() {
729 | return self.date.getDay();
730 | };
731 |
732 | // provide this method only on browsers that already have it
733 | if (self.toISOString) {
734 | self.toISOString = function() {
735 | return padNumber(self.origDate.getUTCFullYear(), 4) + '-' +
736 | padNumber(self.origDate.getUTCMonth() + 1, 2) + '-' +
737 | padNumber(self.origDate.getUTCDate(), 2) + 'T' +
738 | padNumber(self.origDate.getUTCHours(), 2) + ':' +
739 | padNumber(self.origDate.getUTCMinutes(), 2) + ':' +
740 | padNumber(self.origDate.getUTCSeconds(), 2) + '.' +
741 | padNumber(self.origDate.getUTCMilliseconds(), 3) + 'Z';
742 | };
743 | }
744 |
745 | //hide all methods not implemented in this mock that the Date prototype exposes
746 | var unimplementedMethods = ['getUTCDay',
747 | 'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds',
748 | 'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear',
749 | 'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds',
750 | 'setYear', 'toDateString', 'toGMTString', 'toJSON', 'toLocaleFormat', 'toLocaleString',
751 | 'toLocaleTimeString', 'toSource', 'toString', 'toTimeString', 'toUTCString', 'valueOf'];
752 |
753 | angular.forEach(unimplementedMethods, function(methodName) {
754 | self[methodName] = function() {
755 | throw new Error("Method '" + methodName + "' is not implemented in the TzDate mock");
756 | };
757 | });
758 |
759 | return self;
760 | };
761 |
762 | //make "tzDateInstance instanceof Date" return true
763 | angular.mock.TzDate.prototype = Date.prototype;
764 | /* jshint +W101 */
765 |
766 | angular.mock.animate = angular.module('mock.animate', ['ng'])
767 |
768 | .config(['$provide', function($provide) {
769 |
770 | $provide.decorator('$animate', function($delegate) {
771 | var animate = {
772 | queue : [],
773 | enabled : $delegate.enabled,
774 | flushNext : function(name) {
775 | var tick = animate.queue.shift();
776 |
777 | if (!tick) throw new Error('No animation to be flushed');
778 | if(tick.method !== name) {
779 | throw new Error('The next animation is not "' + name +
780 | '", but is "' + tick.method + '"');
781 | }
782 | tick.fn();
783 | return tick;
784 | }
785 | };
786 |
787 | angular.forEach(['enter','leave','move','addClass','removeClass'], function(method) {
788 | animate[method] = function() {
789 | var params = arguments;
790 | animate.queue.push({
791 | method : method,
792 | params : params,
793 | element : angular.isElement(params[0]) && params[0],
794 | parent : angular.isElement(params[1]) && params[1],
795 | after : angular.isElement(params[2]) && params[2],
796 | fn : function() {
797 | $delegate[method].apply($delegate, params);
798 | }
799 | });
800 | };
801 | });
802 |
803 | return animate;
804 | });
805 |
806 | }]);
807 |
808 |
809 | /**
810 | * @ngdoc function
811 | * @name angular.mock.dump
812 | * @description
813 | *
814 | * *NOTE*: this is not an injectable instance, just a globally available function.
815 | *
816 | * Method for serializing common angular objects (scope, elements, etc..) into strings, useful for
817 | * debugging.
818 | *
819 | * This method is also available on window, where it can be used to display objects on debug
820 | * console.
821 | *
822 | * @param {*} object - any object to turn into string.
823 | * @return {string} a serialized string of the argument
824 | */
825 | angular.mock.dump = function(object) {
826 | return serialize(object);
827 |
828 | function serialize(object) {
829 | var out;
830 |
831 | if (angular.isElement(object)) {
832 | object = angular.element(object);
833 | out = angular.element('');
834 | angular.forEach(object, function(element) {
835 | out.append(angular.element(element).clone());
836 | });
837 | out = out.html();
838 | } else if (angular.isArray(object)) {
839 | out = [];
840 | angular.forEach(object, function(o) {
841 | out.push(serialize(o));
842 | });
843 | out = '[ ' + out.join(', ') + ' ]';
844 | } else if (angular.isObject(object)) {
845 | if (angular.isFunction(object.$eval) && angular.isFunction(object.$apply)) {
846 | out = serializeScope(object);
847 | } else if (object instanceof Error) {
848 | out = object.stack || ('' + object.name + ': ' + object.message);
849 | } else {
850 | // TODO(i): this prevents methods being logged,
851 | // we should have a better way to serialize objects
852 | out = angular.toJson(object, true);
853 | }
854 | } else {
855 | out = String(object);
856 | }
857 |
858 | return out;
859 | }
860 |
861 | function serializeScope(scope, offset) {
862 | offset = offset || ' ';
863 | var log = [offset + 'Scope(' + scope.$id + '): {'];
864 | for ( var key in scope ) {
865 | if (Object.prototype.hasOwnProperty.call(scope, key) && !key.match(/^(\$|this)/)) {
866 | log.push(' ' + key + ': ' + angular.toJson(scope[key]));
867 | }
868 | }
869 | var child = scope.$$childHead;
870 | while(child) {
871 | log.push(serializeScope(child, offset + ' '));
872 | child = child.$$nextSibling;
873 | }
874 | log.push('}');
875 | return log.join('\n' + offset);
876 | }
877 | };
878 |
879 | /**
880 | * @ngdoc object
881 | * @name ngMock.$httpBackend
882 | * @description
883 | * Fake HTTP backend implementation suitable for unit testing applications that use the
884 | * {@link ng.$http $http service}.
885 | *
886 | * *Note*: For fake HTTP backend implementation suitable for end-to-end testing or backend-less
887 | * development please see {@link ngMockE2E.$httpBackend e2e $httpBackend mock}.
888 | *
889 | * During unit testing, we want our unit tests to run quickly and have no external dependencies so
890 | * we don’t want to send {@link https://developer.mozilla.org/en/xmlhttprequest XHR} or
891 | * {@link http://en.wikipedia.org/wiki/JSONP JSONP} requests to a real server. All we really need is
892 | * to verify whether a certain request has been sent or not, or alternatively just let the
893 | * application make requests, respond with pre-trained responses and assert that the end result is
894 | * what we expect it to be.
895 | *
896 | * This mock implementation can be used to respond with static or dynamic responses via the
897 | * `expect` and `when` apis and their shortcuts (`expectGET`, `whenPOST`, etc).
898 | *
899 | * When an Angular application needs some data from a server, it calls the $http service, which
900 | * sends the request to a real server using $httpBackend service. With dependency injection, it is
901 | * easy to inject $httpBackend mock (which has the same API as $httpBackend) and use it to verify
902 | * the requests and respond with some testing data without sending a request to real server.
903 | *
904 | * There are two ways to specify what test data should be returned as http responses by the mock
905 | * backend when the code under test makes http requests:
906 | *
907 | * - `$httpBackend.expect` - specifies a request expectation
908 | * - `$httpBackend.when` - specifies a backend definition
909 | *
910 | *
911 | * # Request Expectations vs Backend Definitions
912 | *
913 | * Request expectations provide a way to make assertions about requests made by the application and
914 | * to define responses for those requests. The test will fail if the expected requests are not made
915 | * or they are made in the wrong order.
916 | *
917 | * Backend definitions allow you to define a fake backend for your application which doesn't assert
918 | * if a particular request was made or not, it just returns a trained response if a request is made.
919 | * The test will pass whether or not the request gets made during testing.
920 | *
921 | *
922 | * | Request expectations | Backend definitions | |
|---|---|---|
| Syntax | 926 | *.expect(...).respond(...) | 927 | *.when(...).respond(...) | 928 | *
| Typical usage | 931 | *strict unit tests | 932 | *loose (black-box) unit testing | 933 | *
| Fulfills multiple requests | 936 | *NO | 937 | *YES | 938 | *
| Order of requests matters | 941 | *YES | 942 | *NO | 943 | *
| Request required | 946 | *YES | 947 | *NO | 948 | *
| Response required | 951 | *optional (see below) | 952 | *YES | 953 | *
983 | // The controller code
984 | function MyController($scope, $http) {
985 | var authToken;
986 |
987 | $http.get('/auth.py').success(function(data, status, headers) {
988 | authToken = headers('A-Token');
989 | $scope.user = data;
990 | });
991 |
992 | $scope.saveMessage = function(message) {
993 | var headers = { 'Authorization': authToken };
994 | $scope.status = 'Saving...';
995 |
996 | $http.post('/add-msg.py', message, { headers: headers } ).success(function(response) {
997 | $scope.status = '';
998 | }).error(function() {
999 | $scope.status = 'ERROR!';
1000 | });
1001 | };
1002 | }
1003 |
1004 | *
1005 | * Now we setup the mock backend and create the test specs.
1006 | *
1007 |
1008 | // testing controller
1009 | describe('MyController', function() {
1010 | var $httpBackend, $rootScope, createController;
1011 |
1012 | beforeEach(inject(function($injector) {
1013 | // Set up the mock http service responses
1014 | $httpBackend = $injector.get('$httpBackend');
1015 | // backend definition common for all tests
1016 | $httpBackend.when('GET', '/auth.py').respond({userId: 'userX'}, {'A-Token': 'xxx'});
1017 |
1018 | // Get hold of a scope (i.e. the root scope)
1019 | $rootScope = $injector.get('$rootScope');
1020 | // The $controller service is used to create instances of controllers
1021 | var $controller = $injector.get('$controller');
1022 |
1023 | createController = function() {
1024 | return $controller('MyController', {'$scope' : $rootScope });
1025 | };
1026 | }));
1027 |
1028 |
1029 | afterEach(function() {
1030 | $httpBackend.verifyNoOutstandingExpectation();
1031 | $httpBackend.verifyNoOutstandingRequest();
1032 | });
1033 |
1034 |
1035 | it('should fetch authentication token', function() {
1036 | $httpBackend.expectGET('/auth.py');
1037 | var controller = createController();
1038 | $httpBackend.flush();
1039 | });
1040 |
1041 |
1042 | it('should send msg to server', function() {
1043 | var controller = createController();
1044 | $httpBackend.flush();
1045 |
1046 | // now you don’t care about the authentication, but
1047 | // the controller will still send the request and
1048 | // $httpBackend will respond without you having to
1049 | // specify the expectation and response for this request
1050 |
1051 | $httpBackend.expectPOST('/add-msg.py', 'message content').respond(201, '');
1052 | $rootScope.saveMessage('message content');
1053 | expect($rootScope.status).toBe('Saving...');
1054 | $httpBackend.flush();
1055 | expect($rootScope.status).toBe('');
1056 | });
1057 |
1058 |
1059 | it('should send auth header', function() {
1060 | var controller = createController();
1061 | $httpBackend.flush();
1062 |
1063 | $httpBackend.expectPOST('/add-msg.py', undefined, function(headers) {
1064 | // check if the header was send, if it wasn't the expectation won't
1065 | // match the request and the test will fail
1066 | return headers['Authorization'] == 'xxx';
1067 | }).respond(201, '');
1068 |
1069 | $rootScope.saveMessage('whatever');
1070 | $httpBackend.flush();
1071 | });
1072 | });
1073 |
1074 | */
1075 | angular.mock.$HttpBackendProvider = function() {
1076 | this.$get = ['$rootScope', createHttpBackendMock];
1077 | };
1078 |
1079 | /**
1080 | * General factory function for $httpBackend mock.
1081 | * Returns instance for unit testing (when no arguments specified):
1082 | * - passing through is disabled
1083 | * - auto flushing is disabled
1084 | *
1085 | * Returns instance for e2e testing (when `$delegate` and `$browser` specified):
1086 | * - passing through (delegating request to real backend) is enabled
1087 | * - auto flushing is enabled
1088 | *
1089 | * @param {Object=} $delegate Real $httpBackend instance (allow passing through if specified)
1090 | * @param {Object=} $browser Auto-flushing enabled if specified
1091 | * @return {Object} Instance of $httpBackend mock
1092 | */
1093 | function createHttpBackendMock($rootScope, $delegate, $browser) {
1094 | var definitions = [],
1095 | expectations = [],
1096 | responses = [],
1097 | responsesPush = angular.bind(responses, responses.push);
1098 |
1099 | function createResponse(status, data, headers) {
1100 | if (angular.isFunction(status)) return status;
1101 |
1102 | return function() {
1103 | return angular.isNumber(status)
1104 | ? [status, data, headers]
1105 | : [200, status, data];
1106 | };
1107 | }
1108 |
1109 | // TODO(vojta): change params to: method, url, data, headers, callback
1110 | function $httpBackend(method, url, data, callback, headers, timeout, withCredentials) {
1111 | var xhr = new MockXhr(),
1112 | expectation = expectations[0],
1113 | wasExpected = false;
1114 |
1115 | function prettyPrint(data) {
1116 | return (angular.isString(data) || angular.isFunction(data) || data instanceof RegExp)
1117 | ? data
1118 | : angular.toJson(data);
1119 | }
1120 |
1121 | function wrapResponse(wrapped) {
1122 | if (!$browser && timeout && timeout.then) timeout.then(handleTimeout);
1123 |
1124 | return handleResponse;
1125 |
1126 | function handleResponse() {
1127 | var response = wrapped.response(method, url, data, headers);
1128 | xhr.$$respHeaders = response[2];
1129 | callback(response[0], response[1], xhr.getAllResponseHeaders());
1130 | }
1131 |
1132 | function handleTimeout() {
1133 | for (var i = 0, ii = responses.length; i < ii; i++) {
1134 | if (responses[i] === handleResponse) {
1135 | responses.splice(i, 1);
1136 | callback(-1, undefined, '');
1137 | break;
1138 | }
1139 | }
1140 | }
1141 | }
1142 |
1143 | if (expectation && expectation.match(method, url)) {
1144 | if (!expectation.matchData(data))
1145 | throw new Error('Expected ' + expectation + ' with different data\n' +
1146 | 'EXPECTED: ' + prettyPrint(expectation.data) + '\nGOT: ' + data);
1147 |
1148 | if (!expectation.matchHeaders(headers))
1149 | throw new Error('Expected ' + expectation + ' with different headers\n' +
1150 | 'EXPECTED: ' + prettyPrint(expectation.headers) + '\nGOT: ' +
1151 | prettyPrint(headers));
1152 |
1153 | expectations.shift();
1154 |
1155 | if (expectation.response) {
1156 | responses.push(wrapResponse(expectation));
1157 | return;
1158 | }
1159 | wasExpected = true;
1160 | }
1161 |
1162 | var i = -1, definition;
1163 | while ((definition = definitions[++i])) {
1164 | if (definition.match(method, url, data, headers || {})) {
1165 | if (definition.response) {
1166 | // if $browser specified, we do auto flush all requests
1167 | ($browser ? $browser.defer : responsesPush)(wrapResponse(definition));
1168 | } else if (definition.passThrough) {
1169 | $delegate(method, url, data, callback, headers, timeout, withCredentials);
1170 | } else throw new Error('No response defined !');
1171 | return;
1172 | }
1173 | }
1174 | throw wasExpected ?
1175 | new Error('No response defined !') :
1176 | new Error('Unexpected request: ' + method + ' ' + url + '\n' +
1177 | (expectation ? 'Expected ' + expectation : 'No more request expected'));
1178 | }
1179 |
1180 | /**
1181 | * @ngdoc method
1182 | * @name ngMock.$httpBackend#when
1183 | * @methodOf ngMock.$httpBackend
1184 | * @description
1185 | * Creates a new backend definition.
1186 | *
1187 | * @param {string} method HTTP method.
1188 | * @param {string|RegExp} url HTTP url.
1189 | * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
1190 | * data string and returns true if the data is as expected.
1191 | * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
1192 | * object and returns true if the headers match the current definition.
1193 | * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1194 | * request is handled.
1195 | *
1196 | * - respond –
1197 | * `{function([status,] data[, headers])|function(function(method, url, data, headers)}`
1198 | * – The respond method takes a set of static data to be returned or a function that can return
1199 | * an array containing response status (number), response data (string) and response headers
1200 | * (Object).
1201 | */
1202 | $httpBackend.when = function(method, url, data, headers) {
1203 | var definition = new MockHttpExpectation(method, url, data, headers),
1204 | chain = {
1205 | respond: function(status, data, headers) {
1206 | definition.response = createResponse(status, data, headers);
1207 | }
1208 | };
1209 |
1210 | if ($browser) {
1211 | chain.passThrough = function() {
1212 | definition.passThrough = true;
1213 | };
1214 | }
1215 |
1216 | definitions.push(definition);
1217 | return chain;
1218 | };
1219 |
1220 | /**
1221 | * @ngdoc method
1222 | * @name ngMock.$httpBackend#whenGET
1223 | * @methodOf ngMock.$httpBackend
1224 | * @description
1225 | * Creates a new backend definition for GET requests. For more info see `when()`.
1226 | *
1227 | * @param {string|RegExp} url HTTP url.
1228 | * @param {(Object|function(Object))=} headers HTTP headers.
1229 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1230 | * request is handled.
1231 | */
1232 |
1233 | /**
1234 | * @ngdoc method
1235 | * @name ngMock.$httpBackend#whenHEAD
1236 | * @methodOf ngMock.$httpBackend
1237 | * @description
1238 | * Creates a new backend definition for HEAD requests. For more info see `when()`.
1239 | *
1240 | * @param {string|RegExp} url HTTP url.
1241 | * @param {(Object|function(Object))=} headers HTTP headers.
1242 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1243 | * request is handled.
1244 | */
1245 |
1246 | /**
1247 | * @ngdoc method
1248 | * @name ngMock.$httpBackend#whenDELETE
1249 | * @methodOf ngMock.$httpBackend
1250 | * @description
1251 | * Creates a new backend definition for DELETE requests. For more info see `when()`.
1252 | *
1253 | * @param {string|RegExp} url HTTP url.
1254 | * @param {(Object|function(Object))=} headers HTTP headers.
1255 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1256 | * request is handled.
1257 | */
1258 |
1259 | /**
1260 | * @ngdoc method
1261 | * @name ngMock.$httpBackend#whenPOST
1262 | * @methodOf ngMock.$httpBackend
1263 | * @description
1264 | * Creates a new backend definition for POST requests. For more info see `when()`.
1265 | *
1266 | * @param {string|RegExp} url HTTP url.
1267 | * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
1268 | * data string and returns true if the data is as expected.
1269 | * @param {(Object|function(Object))=} headers HTTP headers.
1270 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1271 | * request is handled.
1272 | */
1273 |
1274 | /**
1275 | * @ngdoc method
1276 | * @name ngMock.$httpBackend#whenPUT
1277 | * @methodOf ngMock.$httpBackend
1278 | * @description
1279 | * Creates a new backend definition for PUT requests. For more info see `when()`.
1280 | *
1281 | * @param {string|RegExp} url HTTP url.
1282 | * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
1283 | * data string and returns true if the data is as expected.
1284 | * @param {(Object|function(Object))=} headers HTTP headers.
1285 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1286 | * request is handled.
1287 | */
1288 |
1289 | /**
1290 | * @ngdoc method
1291 | * @name ngMock.$httpBackend#whenJSONP
1292 | * @methodOf ngMock.$httpBackend
1293 | * @description
1294 | * Creates a new backend definition for JSONP requests. For more info see `when()`.
1295 | *
1296 | * @param {string|RegExp} url HTTP url.
1297 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1298 | * request is handled.
1299 | */
1300 | createShortMethods('when');
1301 |
1302 |
1303 | /**
1304 | * @ngdoc method
1305 | * @name ngMock.$httpBackend#expect
1306 | * @methodOf ngMock.$httpBackend
1307 | * @description
1308 | * Creates a new request expectation.
1309 | *
1310 | * @param {string} method HTTP method.
1311 | * @param {string|RegExp} url HTTP url.
1312 | * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
1313 | * receives data string and returns true if the data is as expected, or Object if request body
1314 | * is in JSON format.
1315 | * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
1316 | * object and returns true if the headers match the current expectation.
1317 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1318 | * request is handled.
1319 | *
1320 | * - respond –
1321 | * `{function([status,] data[, headers])|function(function(method, url, data, headers)}`
1322 | * – The respond method takes a set of static data to be returned or a function that can return
1323 | * an array containing response status (number), response data (string) and response headers
1324 | * (Object).
1325 | */
1326 | $httpBackend.expect = function(method, url, data, headers) {
1327 | var expectation = new MockHttpExpectation(method, url, data, headers);
1328 | expectations.push(expectation);
1329 | return {
1330 | respond: function(status, data, headers) {
1331 | expectation.response = createResponse(status, data, headers);
1332 | }
1333 | };
1334 | };
1335 |
1336 |
1337 | /**
1338 | * @ngdoc method
1339 | * @name ngMock.$httpBackend#expectGET
1340 | * @methodOf ngMock.$httpBackend
1341 | * @description
1342 | * Creates a new request expectation for GET requests. For more info see `expect()`.
1343 | *
1344 | * @param {string|RegExp} url HTTP url.
1345 | * @param {Object=} headers HTTP headers.
1346 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1347 | * request is handled. See #expect for more info.
1348 | */
1349 |
1350 | /**
1351 | * @ngdoc method
1352 | * @name ngMock.$httpBackend#expectHEAD
1353 | * @methodOf ngMock.$httpBackend
1354 | * @description
1355 | * Creates a new request expectation for HEAD requests. For more info see `expect()`.
1356 | *
1357 | * @param {string|RegExp} url HTTP url.
1358 | * @param {Object=} headers HTTP headers.
1359 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1360 | * request is handled.
1361 | */
1362 |
1363 | /**
1364 | * @ngdoc method
1365 | * @name ngMock.$httpBackend#expectDELETE
1366 | * @methodOf ngMock.$httpBackend
1367 | * @description
1368 | * Creates a new request expectation for DELETE requests. For more info see `expect()`.
1369 | *
1370 | * @param {string|RegExp} url HTTP url.
1371 | * @param {Object=} headers HTTP headers.
1372 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1373 | * request is handled.
1374 | */
1375 |
1376 | /**
1377 | * @ngdoc method
1378 | * @name ngMock.$httpBackend#expectPOST
1379 | * @methodOf ngMock.$httpBackend
1380 | * @description
1381 | * Creates a new request expectation for POST requests. For more info see `expect()`.
1382 | *
1383 | * @param {string|RegExp} url HTTP url.
1384 | * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
1385 | * receives data string and returns true if the data is as expected, or Object if request body
1386 | * is in JSON format.
1387 | * @param {Object=} headers HTTP headers.
1388 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1389 | * request is handled.
1390 | */
1391 |
1392 | /**
1393 | * @ngdoc method
1394 | * @name ngMock.$httpBackend#expectPUT
1395 | * @methodOf ngMock.$httpBackend
1396 | * @description
1397 | * Creates a new request expectation for PUT requests. For more info see `expect()`.
1398 | *
1399 | * @param {string|RegExp} url HTTP url.
1400 | * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
1401 | * receives data string and returns true if the data is as expected, or Object if request body
1402 | * is in JSON format.
1403 | * @param {Object=} headers HTTP headers.
1404 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1405 | * request is handled.
1406 | */
1407 |
1408 | /**
1409 | * @ngdoc method
1410 | * @name ngMock.$httpBackend#expectPATCH
1411 | * @methodOf ngMock.$httpBackend
1412 | * @description
1413 | * Creates a new request expectation for PATCH requests. For more info see `expect()`.
1414 | *
1415 | * @param {string|RegExp} url HTTP url.
1416 | * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
1417 | * receives data string and returns true if the data is as expected, or Object if request body
1418 | * is in JSON format.
1419 | * @param {Object=} headers HTTP headers.
1420 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1421 | * request is handled.
1422 | */
1423 |
1424 | /**
1425 | * @ngdoc method
1426 | * @name ngMock.$httpBackend#expectJSONP
1427 | * @methodOf ngMock.$httpBackend
1428 | * @description
1429 | * Creates a new request expectation for JSONP requests. For more info see `expect()`.
1430 | *
1431 | * @param {string|RegExp} url HTTP url.
1432 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1433 | * request is handled.
1434 | */
1435 | createShortMethods('expect');
1436 |
1437 |
1438 | /**
1439 | * @ngdoc method
1440 | * @name ngMock.$httpBackend#flush
1441 | * @methodOf ngMock.$httpBackend
1442 | * @description
1443 | * Flushes all pending requests using the trained responses.
1444 | *
1445 | * @param {number=} count Number of responses to flush (in the order they arrived). If undefined,
1446 | * all pending requests will be flushed. If there are no pending requests when the flush method
1447 | * is called an exception is thrown (as this typically a sign of programming error).
1448 | */
1449 | $httpBackend.flush = function(count) {
1450 | $rootScope.$digest();
1451 | if (!responses.length) throw new Error('No pending request to flush !');
1452 |
1453 | if (angular.isDefined(count)) {
1454 | while (count--) {
1455 | if (!responses.length) throw new Error('No more pending request to flush !');
1456 | responses.shift()();
1457 | }
1458 | } else {
1459 | while (responses.length) {
1460 | responses.shift()();
1461 | }
1462 | }
1463 | $httpBackend.verifyNoOutstandingExpectation();
1464 | };
1465 |
1466 |
1467 | /**
1468 | * @ngdoc method
1469 | * @name ngMock.$httpBackend#verifyNoOutstandingExpectation
1470 | * @methodOf ngMock.$httpBackend
1471 | * @description
1472 | * Verifies that all of the requests defined via the `expect` api were made. If any of the
1473 | * requests were not made, verifyNoOutstandingExpectation throws an exception.
1474 | *
1475 | * Typically, you would call this method following each test case that asserts requests using an
1476 | * "afterEach" clause.
1477 | *
1478 | *
1479 | * afterEach($httpBackend.verifyNoOutstandingExpectation);
1480 | *
1481 | */
1482 | $httpBackend.verifyNoOutstandingExpectation = function() {
1483 | $rootScope.$digest();
1484 | if (expectations.length) {
1485 | throw new Error('Unsatisfied requests: ' + expectations.join(', '));
1486 | }
1487 | };
1488 |
1489 |
1490 | /**
1491 | * @ngdoc method
1492 | * @name ngMock.$httpBackend#verifyNoOutstandingRequest
1493 | * @methodOf ngMock.$httpBackend
1494 | * @description
1495 | * Verifies that there are no outstanding requests that need to be flushed.
1496 | *
1497 | * Typically, you would call this method following each test case that asserts requests using an
1498 | * "afterEach" clause.
1499 | *
1500 | *
1501 | * afterEach($httpBackend.verifyNoOutstandingRequest);
1502 | *
1503 | */
1504 | $httpBackend.verifyNoOutstandingRequest = function() {
1505 | if (responses.length) {
1506 | throw new Error('Unflushed requests: ' + responses.length);
1507 | }
1508 | };
1509 |
1510 |
1511 | /**
1512 | * @ngdoc method
1513 | * @name ngMock.$httpBackend#resetExpectations
1514 | * @methodOf ngMock.$httpBackend
1515 | * @description
1516 | * Resets all request expectations, but preserves all backend definitions. Typically, you would
1517 | * call resetExpectations during a multiple-phase test when you want to reuse the same instance of
1518 | * $httpBackend mock.
1519 | */
1520 | $httpBackend.resetExpectations = function() {
1521 | expectations.length = 0;
1522 | responses.length = 0;
1523 | };
1524 |
1525 | return $httpBackend;
1526 |
1527 |
1528 | function createShortMethods(prefix) {
1529 | angular.forEach(['GET', 'DELETE', 'JSONP'], function(method) {
1530 | $httpBackend[prefix + method] = function(url, headers) {
1531 | return $httpBackend[prefix](method, url, undefined, headers);
1532 | };
1533 | });
1534 |
1535 | angular.forEach(['PUT', 'POST', 'PATCH'], function(method) {
1536 | $httpBackend[prefix + method] = function(url, data, headers) {
1537 | return $httpBackend[prefix](method, url, data, headers);
1538 | };
1539 | });
1540 | }
1541 | }
1542 |
1543 | function MockHttpExpectation(method, url, data, headers) {
1544 |
1545 | this.data = data;
1546 | this.headers = headers;
1547 |
1548 | this.match = function(m, u, d, h) {
1549 | if (method != m) return false;
1550 | if (!this.matchUrl(u)) return false;
1551 | if (angular.isDefined(d) && !this.matchData(d)) return false;
1552 | if (angular.isDefined(h) && !this.matchHeaders(h)) return false;
1553 | return true;
1554 | };
1555 |
1556 | this.matchUrl = function(u) {
1557 | if (!url) return true;
1558 | if (angular.isFunction(url.test)) return url.test(u);
1559 | return url == u;
1560 | };
1561 |
1562 | this.matchHeaders = function(h) {
1563 | if (angular.isUndefined(headers)) return true;
1564 | if (angular.isFunction(headers)) return headers(h);
1565 | return angular.equals(headers, h);
1566 | };
1567 |
1568 | this.matchData = function(d) {
1569 | if (angular.isUndefined(data)) return true;
1570 | if (data && angular.isFunction(data.test)) return data.test(d);
1571 | if (data && angular.isFunction(data)) return data(d);
1572 | if (data && !angular.isString(data)) return angular.equals(data, angular.fromJson(d));
1573 | return data == d;
1574 | };
1575 |
1576 | this.toString = function() {
1577 | return method + ' ' + url;
1578 | };
1579 | }
1580 |
1581 | function MockXhr() {
1582 |
1583 | // hack for testing $http, $httpBackend
1584 | MockXhr.$$lastInstance = this;
1585 |
1586 | this.open = function(method, url, async) {
1587 | this.$$method = method;
1588 | this.$$url = url;
1589 | this.$$async = async;
1590 | this.$$reqHeaders = {};
1591 | this.$$respHeaders = {};
1592 | };
1593 |
1594 | this.send = function(data) {
1595 | this.$$data = data;
1596 | };
1597 |
1598 | this.setRequestHeader = function(key, value) {
1599 | this.$$reqHeaders[key] = value;
1600 | };
1601 |
1602 | this.getResponseHeader = function(name) {
1603 | // the lookup must be case insensitive,
1604 | // that's why we try two quick lookups first and full scan last
1605 | var header = this.$$respHeaders[name];
1606 | if (header) return header;
1607 |
1608 | name = angular.lowercase(name);
1609 | header = this.$$respHeaders[name];
1610 | if (header) return header;
1611 |
1612 | header = undefined;
1613 | angular.forEach(this.$$respHeaders, function(headerVal, headerName) {
1614 | if (!header && angular.lowercase(headerName) == name) header = headerVal;
1615 | });
1616 | return header;
1617 | };
1618 |
1619 | this.getAllResponseHeaders = function() {
1620 | var lines = [];
1621 |
1622 | angular.forEach(this.$$respHeaders, function(value, key) {
1623 | lines.push(key + ': ' + value);
1624 | });
1625 | return lines.join('\n');
1626 | };
1627 |
1628 | this.abort = angular.noop;
1629 | }
1630 |
1631 |
1632 | /**
1633 | * @ngdoc function
1634 | * @name ngMock.$timeout
1635 | * @description
1636 | *
1637 | * This service is just a simple decorator for {@link ng.$timeout $timeout} service
1638 | * that adds a "flush" and "verifyNoPendingTasks" methods.
1639 | */
1640 |
1641 | angular.mock.$TimeoutDecorator = function($delegate, $browser) {
1642 |
1643 | /**
1644 | * @ngdoc method
1645 | * @name ngMock.$timeout#flush
1646 | * @methodOf ngMock.$timeout
1647 | * @description
1648 | *
1649 | * Flushes the queue of pending tasks.
1650 | *
1651 | * @param {number=} delay maximum timeout amount to flush up until
1652 | */
1653 | $delegate.flush = function(delay) {
1654 | $browser.defer.flush(delay);
1655 | };
1656 |
1657 | /**
1658 | * @ngdoc method
1659 | * @name ngMock.$timeout#verifyNoPendingTasks
1660 | * @methodOf ngMock.$timeout
1661 | * @description
1662 | *
1663 | * Verifies that there are no pending tasks that need to be flushed.
1664 | */
1665 | $delegate.verifyNoPendingTasks = function() {
1666 | if ($browser.deferredFns.length) {
1667 | throw new Error('Deferred tasks to flush (' + $browser.deferredFns.length + '): ' +
1668 | formatPendingTasksAsString($browser.deferredFns));
1669 | }
1670 | };
1671 |
1672 | function formatPendingTasksAsString(tasks) {
1673 | var result = [];
1674 | angular.forEach(tasks, function(task) {
1675 | result.push('{id: ' + task.id + ', ' + 'time: ' + task.time + '}');
1676 | });
1677 |
1678 | return result.join(', ');
1679 | }
1680 |
1681 | return $delegate;
1682 | };
1683 |
1684 | /**
1685 | *
1686 | */
1687 | angular.mock.$RootElementProvider = function() {
1688 | this.$get = function() {
1689 | return angular.element('');
1690 | };
1691 | };
1692 |
1693 | /**
1694 | * @ngdoc overview
1695 | * @name ngMock
1696 | * @description
1697 | *
1698 | * # ngMock
1699 | *
1700 | * The `ngMock` module providers support to inject and mock Angular services into unit tests.
1701 | * In addition, ngMock also extends various core ng services such that they can be
1702 | * inspected and controlled in a synchronous manner within test code.
1703 | *
1704 | * {@installModule mocks}
1705 | *
1706 | *
1707 | *
1708 | */
1709 | angular.module('ngMock', ['ng']).provider({
1710 | $browser: angular.mock.$BrowserProvider,
1711 | $exceptionHandler: angular.mock.$ExceptionHandlerProvider,
1712 | $log: angular.mock.$LogProvider,
1713 | $interval: angular.mock.$IntervalProvider,
1714 | $httpBackend: angular.mock.$HttpBackendProvider,
1715 | $rootElement: angular.mock.$RootElementProvider
1716 | }).config(['$provide', function($provide) {
1717 | $provide.decorator('$timeout', angular.mock.$TimeoutDecorator);
1718 | }]);
1719 |
1720 | /**
1721 | * @ngdoc overview
1722 | * @name ngMockE2E
1723 | * @description
1724 | *
1725 | * The `ngMockE2E` is an angular module which contains mocks suitable for end-to-end testing.
1726 | * Currently there is only one mock present in this module -
1727 | * the {@link ngMockE2E.$httpBackend e2e $httpBackend} mock.
1728 | */
1729 | angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
1730 | $provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator);
1731 | }]);
1732 |
1733 | /**
1734 | * @ngdoc object
1735 | * @name ngMockE2E.$httpBackend
1736 | * @description
1737 | * Fake HTTP backend implementation suitable for end-to-end testing or backend-less development of
1738 | * applications that use the {@link ng.$http $http service}.
1739 | *
1740 | * *Note*: For fake http backend implementation suitable for unit testing please see
1741 | * {@link ngMock.$httpBackend unit-testing $httpBackend mock}.
1742 | *
1743 | * This implementation can be used to respond with static or dynamic responses via the `when` api
1744 | * and its shortcuts (`whenGET`, `whenPOST`, etc) and optionally pass through requests to the
1745 | * real $httpBackend for specific requests (e.g. to interact with certain remote apis or to fetch
1746 | * templates from a webserver).
1747 | *
1748 | * As opposed to unit-testing, in an end-to-end testing scenario or in scenario when an application
1749 | * is being developed with the real backend api replaced with a mock, it is often desirable for
1750 | * certain category of requests to bypass the mock and issue a real http request (e.g. to fetch
1751 | * templates or static files from the webserver). To configure the backend with this behavior
1752 | * use the `passThrough` request handler of `when` instead of `respond`.
1753 | *
1754 | * Additionally, we don't want to manually have to flush mocked out requests like we do during unit
1755 | * testing. For this reason the e2e $httpBackend automatically flushes mocked out requests
1756 | * automatically, closely simulating the behavior of the XMLHttpRequest object.
1757 | *
1758 | * To setup the application to run with this http backend, you have to create a module that depends
1759 | * on the `ngMockE2E` and your application modules and defines the fake backend:
1760 | *
1761 | *
1762 | * myAppDev = angular.module('myAppDev', ['myApp', 'ngMockE2E']);
1763 | * myAppDev.run(function($httpBackend) {
1764 | * phones = [{name: 'phone1'}, {name: 'phone2'}];
1765 | *
1766 | * // returns the current list of phones
1767 | * $httpBackend.whenGET('/phones').respond(phones);
1768 | *
1769 | * // adds a new phone to the phones array
1770 | * $httpBackend.whenPOST('/phones').respond(function(method, url, data) {
1771 | * phones.push(angular.fromJson(data));
1772 | * });
1773 | * $httpBackend.whenGET(/^\/templates\//).passThrough();
1774 | * //...
1775 | * });
1776 | *
1777 | *
1778 | * Afterwards, bootstrap your app with this new module.
1779 | */
1780 |
1781 | /**
1782 | * @ngdoc method
1783 | * @name ngMockE2E.$httpBackend#when
1784 | * @methodOf ngMockE2E.$httpBackend
1785 | * @description
1786 | * Creates a new backend definition.
1787 | *
1788 | * @param {string} method HTTP method.
1789 | * @param {string|RegExp} url HTTP url.
1790 | * @param {(string|RegExp)=} data HTTP request body.
1791 | * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
1792 | * object and returns true if the headers match the current definition.
1793 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1794 | * control how a matched request is handled.
1795 | *
1796 | * - respond –
1797 | * `{function([status,] data[, headers])|function(function(method, url, data, headers)}`
1798 | * – The respond method takes a set of static data to be returned or a function that can return
1799 | * an array containing response status (number), response data (string) and response headers
1800 | * (Object).
1801 | * - passThrough – `{function()}` – Any request matching a backend definition with `passThrough`
1802 | * handler, will be pass through to the real backend (an XHR request will be made to the
1803 | * server.
1804 | */
1805 |
1806 | /**
1807 | * @ngdoc method
1808 | * @name ngMockE2E.$httpBackend#whenGET
1809 | * @methodOf ngMockE2E.$httpBackend
1810 | * @description
1811 | * Creates a new backend definition for GET requests. For more info see `when()`.
1812 | *
1813 | * @param {string|RegExp} url HTTP url.
1814 | * @param {(Object|function(Object))=} headers HTTP headers.
1815 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1816 | * control how a matched request is handled.
1817 | */
1818 |
1819 | /**
1820 | * @ngdoc method
1821 | * @name ngMockE2E.$httpBackend#whenHEAD
1822 | * @methodOf ngMockE2E.$httpBackend
1823 | * @description
1824 | * Creates a new backend definition for HEAD requests. For more info see `when()`.
1825 | *
1826 | * @param {string|RegExp} url HTTP url.
1827 | * @param {(Object|function(Object))=} headers HTTP headers.
1828 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1829 | * control how a matched request is handled.
1830 | */
1831 |
1832 | /**
1833 | * @ngdoc method
1834 | * @name ngMockE2E.$httpBackend#whenDELETE
1835 | * @methodOf ngMockE2E.$httpBackend
1836 | * @description
1837 | * Creates a new backend definition for DELETE requests. For more info see `when()`.
1838 | *
1839 | * @param {string|RegExp} url HTTP url.
1840 | * @param {(Object|function(Object))=} headers HTTP headers.
1841 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1842 | * control how a matched request is handled.
1843 | */
1844 |
1845 | /**
1846 | * @ngdoc method
1847 | * @name ngMockE2E.$httpBackend#whenPOST
1848 | * @methodOf ngMockE2E.$httpBackend
1849 | * @description
1850 | * Creates a new backend definition for POST requests. For more info see `when()`.
1851 | *
1852 | * @param {string|RegExp} url HTTP url.
1853 | * @param {(string|RegExp)=} data HTTP request body.
1854 | * @param {(Object|function(Object))=} headers HTTP headers.
1855 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1856 | * control how a matched request is handled.
1857 | */
1858 |
1859 | /**
1860 | * @ngdoc method
1861 | * @name ngMockE2E.$httpBackend#whenPUT
1862 | * @methodOf ngMockE2E.$httpBackend
1863 | * @description
1864 | * Creates a new backend definition for PUT requests. For more info see `when()`.
1865 | *
1866 | * @param {string|RegExp} url HTTP url.
1867 | * @param {(string|RegExp)=} data HTTP request body.
1868 | * @param {(Object|function(Object))=} headers HTTP headers.
1869 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1870 | * control how a matched request is handled.
1871 | */
1872 |
1873 | /**
1874 | * @ngdoc method
1875 | * @name ngMockE2E.$httpBackend#whenPATCH
1876 | * @methodOf ngMockE2E.$httpBackend
1877 | * @description
1878 | * Creates a new backend definition for PATCH requests. For more info see `when()`.
1879 | *
1880 | * @param {string|RegExp} url HTTP url.
1881 | * @param {(string|RegExp)=} data HTTP request body.
1882 | * @param {(Object|function(Object))=} headers HTTP headers.
1883 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1884 | * control how a matched request is handled.
1885 | */
1886 |
1887 | /**
1888 | * @ngdoc method
1889 | * @name ngMockE2E.$httpBackend#whenJSONP
1890 | * @methodOf ngMockE2E.$httpBackend
1891 | * @description
1892 | * Creates a new backend definition for JSONP requests. For more info see `when()`.
1893 | *
1894 | * @param {string|RegExp} url HTTP url.
1895 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1896 | * control how a matched request is handled.
1897 | */
1898 | angular.mock.e2e = {};
1899 | angular.mock.e2e.$httpBackendDecorator =
1900 | ['$rootScope', '$delegate', '$browser', createHttpBackendMock];
1901 |
1902 |
1903 | angular.mock.clearDataCache = function() {
1904 | var key,
1905 | cache = angular.element.cache;
1906 |
1907 | for(key in cache) {
1908 | if (Object.prototype.hasOwnProperty.call(cache,key)) {
1909 | var handle = cache[key].handle;
1910 |
1911 | handle && angular.element(handle.elem).off();
1912 | delete cache[key];
1913 | }
1914 | }
1915 | };
1916 |
1917 |
1918 |
1919 | if(window.jasmine || window.mocha) {
1920 |
1921 | var currentSpec = null,
1922 | isSpecRunning = function() {
1923 | return currentSpec && (window.mocha || currentSpec.queue.running);
1924 | };
1925 |
1926 |
1927 | beforeEach(function() {
1928 | currentSpec = this;
1929 | });
1930 |
1931 | afterEach(function() {
1932 | var injector = currentSpec.$injector;
1933 |
1934 | currentSpec.$injector = null;
1935 | currentSpec.$modules = null;
1936 | currentSpec = null;
1937 |
1938 | if (injector) {
1939 | injector.get('$rootElement').off();
1940 | injector.get('$browser').pollFns.length = 0;
1941 | }
1942 |
1943 | angular.mock.clearDataCache();
1944 |
1945 | // clean up jquery's fragment cache
1946 | angular.forEach(angular.element.fragments, function(val, key) {
1947 | delete angular.element.fragments[key];
1948 | });
1949 |
1950 | MockXhr.$$lastInstance = null;
1951 |
1952 | angular.forEach(angular.callbacks, function(val, key) {
1953 | delete angular.callbacks[key];
1954 | });
1955 | angular.callbacks.counter = 0;
1956 | });
1957 |
1958 | /**
1959 | * @ngdoc function
1960 | * @name angular.mock.module
1961 | * @description
1962 | *
1963 | * *NOTE*: This function is also published on window for easy access.
2047 | *
2048 | * angular.module('myApplicationModule', [])
2049 | * .value('mode', 'app')
2050 | * .value('version', 'v1.0.1');
2051 | *
2052 | *
2053 | * describe('MyApp', function() {
2054 | *
2055 | * // You need to load modules that you want to test,
2056 | * // it loads only the "ng" module by default.
2057 | * beforeEach(module('myApplicationModule'));
2058 | *
2059 | *
2060 | * // inject() is used to inject arguments of all given functions
2061 | * it('should provide a version', inject(function(mode, version) {
2062 | * expect(version).toEqual('v1.0.1');
2063 | * expect(mode).toEqual('app');
2064 | * }));
2065 | *
2066 | *
2067 | * // The inject and module method can also be used inside of the it or beforeEach
2068 | * it('should override a version and test the new version is injected', function() {
2069 | * // module() takes functions or strings (module aliases)
2070 | * module(function($provide) {
2071 | * $provide.value('version', 'overridden'); // override version here
2072 | * });
2073 | *
2074 | * inject(function(version) {
2075 | * expect(version).toEqual('overridden');
2076 | * });
2077 | * });
2078 | * });
2079 | *
2080 | *
2081 | *
2082 | * @param {...Function} fns any number of functions which will be injected using the injector.
2083 | */
2084 | window.inject = angular.mock.inject = function() {
2085 | var blockFns = Array.prototype.slice.call(arguments, 0);
2086 | var errorForStack = new Error('Declaration Location');
2087 | return isSpecRunning() ? workFn() : workFn;
2088 | /////////////////////
2089 | function workFn() {
2090 | var modules = currentSpec.$modules || [];
2091 |
2092 | modules.unshift('ngMock');
2093 | modules.unshift('ng');
2094 | var injector = currentSpec.$injector;
2095 | if (!injector) {
2096 | injector = currentSpec.$injector = angular.injector(modules);
2097 | }
2098 | for(var i = 0, ii = blockFns.length; i < ii; i++) {
2099 | try {
2100 | /* jshint -W040 *//* Jasmine explicitly provides a `this` object when calling functions */
2101 | injector.invoke(blockFns[i] || angular.noop, this);
2102 | /* jshint +W040 */
2103 | } catch (e) {
2104 | if(e.stack && errorForStack) e.stack += '\n' + errorForStack.stack;
2105 | throw e;
2106 | } finally {
2107 | errorForStack = null;
2108 | }
2109 | }
2110 | }
2111 | };
2112 | }
2113 |
2114 |
2115 | })(window, window.angular);
2116 |
--------------------------------------------------------------------------------