├── demo-app ├── main.js ├── mainStyle.css └── index.html ├── test ├── e2e │ ├── runner.html │ └── scenarios.js ├── config │ ├── karma-e2e.conf.js │ └── karma.conf.js ├── unit │ └── BlockSpec.js └── lib │ └── angular │ └── angular-mocks.js ├── components └── angular │ ├── bower.json │ └── angular.min.js ├── package.json ├── GruntFile.js ├── src ├── flexyLayout.css ├── Directives.js ├── Block.js └── MediatorController.js ├── README.md ├── flexy-layout.min.js ├── web-server.js └── flexy-layout.debug.js /demo-app/main.js: -------------------------------------------------------------------------------- 1 | var app=angular.module('app',['flexyLayout']); 2 | -------------------------------------------------------------------------------- /test/e2e/runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |inside the block
20 |inside the block
25 |inside the block
31 |inside the block
39 |inside the block
44 |' + 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('
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 | *
| Request expectations | Backend definitions | |
|---|---|---|
| Syntax | 699 | *.expect(...).respond(...) | 700 | *.when(...).respond(...) | 701 | *
| Typical usage | 704 | *strict unit tests | 705 | *loose (black-box) unit testing | 706 | *
| Fulfills multiple requests | 709 | *NO | 710 | *YES | 711 | *
| Order of requests matters | 714 | *YES | 715 | *NO | 716 | *
| Request required | 719 | *YES | 720 | *NO | 721 | *
| Response required | 724 | *optional (see below) | 725 | *YES | 726 | *
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.
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 |
--------------------------------------------------------------------------------
/components/angular/angular.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | AngularJS v1.0.7
3 | (c) 2010-2012 Google, Inc. http://angularjs.org
4 | License: MIT
5 | */
6 | (function(P,T,q){'use strict';function m(b,a,c){var d;if(b)if(H(b))for(d in b)d!="prototype"&&d!="length"&&d!="name"&&b.hasOwnProperty(d)&&a.call(c,b[d],d);else if(b.forEach&&b.forEach!==m)b.forEach(a,c);else if(!b||typeof b.length!=="number"?0:typeof b.hasOwnProperty!="function"&&typeof b.constructor!="function"||b instanceof K||ca&&b instanceof ca||wa.call(b)!=="[object Object]"||typeof b.callee==="function")for(d=0;do.priority)&&o.restrict.indexOf(g)!=-1)d.push(o),h=!0}catch(n){k(n)}return h}function $(a,b){var c=b.$attr,d=a.$attr,e=a.$$element;m(a,function(d,e){e.charAt(0)!="$"&&(b[e]&&(d+=(e==="style"?";":" ")+b[e]),a.$set(e,d,!0,c[e]))});m(b, 47 | function(b,g){g=="class"?(M(e,b),a["class"]=(a["class"]?a["class"]+" ":"")+b):g=="style"?e.attr("style",e.attr("style")+";"+b):g.charAt(0)!="$"&&!a.hasOwnProperty(g)&&(a[g]=b,d[g]=c[g])})}function R(a,b,c,d,e,j,h){var i=[],k,o,p=c[0],t=a.shift(),s=v({},t,{controller:null,templateUrl:null,transclude:null,scope:null});c.html("");l.get(t.templateUrl,{cache:n}).success(function(l){var n,t,l=Fb(l);if(j){t=u("