├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── app ├── index.html ├── js │ ├── app.js │ ├── directives.js │ └── services.js └── partials │ ├── .gitkeep │ ├── lorem-image.html │ └── partial1.html ├── bower.json ├── config ├── karma-e2e.conf.js └── karma.conf.js ├── logs └── .gitkeep ├── package.json ├── scripts ├── e2e-test.bat ├── e2e-test.sh ├── test.bat ├── test.sh ├── watchr.rb └── web-server.js └── test ├── e2e ├── runner.html └── scenarios.js ├── lib └── angular │ ├── angular-mocks.js │ ├── angular-scenario.js │ └── version.txt └── unit ├── controllersSpec.js ├── directivesSpec.js ├── filtersSpec.js └── servicesSpec.js /.gitignore: -------------------------------------------------------------------------------- 1 | logs/* 2 | !.gitkeep 3 | node_modules/ 4 | tmp 5 | .DS_Store -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.8 4 | 5 | before_script: 6 | - export DISPLAY=:99.0 7 | - sh -e /etc/init.d/xvfb start 8 | - npm install --quiet -g karma 9 | - ./scripts/web-server.js > /dev/null & 10 | - sleep 1 # give server time to start 11 | 12 | script: 13 | - karma start config/karma.conf.js --no-auto-watch --single-run --reporters=dots --browsers=Firefox 14 | - karma start config/karma-e2e.conf.js --reporters=dots --browsers=Firefox 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 matthieu-D 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # angular-lorem-image 2 | 3 | ```html 4 | bower install angular-lorem-image 5 | ``` 6 | This directive provides lorem image generation for your website by using the great lorempixel service to generate images. 7 | Options: 8 | 15 | Really simple to use : 16 | 17 |
18 | 19 | ```html 20 | 21 | ``` 22 | JSFiddle example : http://jsfiddle.net/xMuB9/1/ 23 |
24 | PR accepted so Have Fun! 25 | 26 | ## Directory Layout 27 | 28 | app/ --> all of the files to be used in production 29 | index.html --> app layout file (the main html template file of the app) 30 | js/ --> javascript files 31 | app.js --> application 32 | directives.js --> application directives 33 | services.js --> custom angular services 34 | partials/ --> angular view partials (partial html templates) 35 | partial1.html 36 | 37 | config/karma.conf.js --> config file for running unit tests with Karma 38 | config/karma-e2e.conf.js --> config file for running e2e tests with Karma 39 | 40 | scripts/ --> handy shell/js/ruby scripts 41 | e2e-test.sh --> runs end-to-end tests with Karma (*nix) 42 | e2e-test.bat --> runs end-to-end tests with Karma (windows) 43 | test.bat --> autotests unit tests with Karma (windows) 44 | test.sh --> autotests unit tests with Karma (*nix) 45 | web-server.js --> simple development webserver based on node.js 46 | 47 | test/ --> test source files and libraries 48 | e2e/ --> 49 | runner.html --> end-to-end test runner (open in your browser to run) 50 | scenarios.js --> end-to-end specs 51 | lib/ 52 | angular/ --> angular testing libraries 53 | angular-mocks.js --> mocks that replace certain angular services in tests 54 | angular-scenario.js --> angular's scenario (end-to-end) test runner library 55 | version.txt --> version file 56 | unit/ --> unit level specs/tests 57 | controllersSpec.js --> specs for controllers 58 | directivessSpec.js --> specs for directives 59 | filtersSpec.js --> specs for filters 60 | servicesSpec.js --> specs for services 61 | -------------------------------------------------------------------------------- /app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AngularJS Lorem Image demo 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/js/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | // Declare app level module which depends on filters, and services 5 | angular.module('myApp', [ 6 | 'ngRoute', 7 | 'ngSanitize', 8 | 'myApp.services', 9 | 'myApp.directives' 10 | ]). 11 | config(['$routeProvider', function($routeProvider) { 12 | $routeProvider.when('/view1', {templateUrl: 'partials/partial1.html'}); 13 | $routeProvider.otherwise({redirectTo: '/view1'}); 14 | }]); 15 | -------------------------------------------------------------------------------- /app/js/directives.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* Directives */ 3 | 4 | angular.module('myApp.directives',['ngSanitize','myApp.services']). 5 | directive('lorem', function(imageBuilder) { 6 | return { 7 | restrict: 'EA', 8 | scope:{ 9 | text:'@', 10 | count:'@', 11 | width:'@', 12 | height:'@', 13 | category:'@' 14 | }, 15 | templateUrl:'partials/lorem-image.html', 16 | controller: function($scope) { 17 | $scope.images = imageBuilder.build($scope.width, 18 | $scope.height, 19 | $scope.category, 20 | $scope.count, 21 | $scope.text) 22 | } 23 | } 24 | }); 25 | 26 | -------------------------------------------------------------------------------- /app/js/services.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* Services */ 4 | 5 | angular.module('myApp.services', []).service('urlBuilder', function() { 6 | this.build = function (width,height,category,text,count){ 7 | var url='http://lorempixel.com/'; 8 | if (width == undefined) { 9 | var width = '300'; 10 | } 11 | url += width +'/'; 12 | if (height == undefined) { 13 | var height = '200'; 14 | } 15 | url += height; 16 | if (category != undefined) { 17 | url+= '/'+category; 18 | } 19 | if (text != undefined) { 20 | url += '/'+text; 21 | } 22 | return url; 23 | } 24 | }).service('imageBuilder', function(urlBuilder) { 25 | this.build = function(width,height,category,count,text){ 26 | var images = []; 27 | var url = urlBuilder.build(width,height,category,text); 28 | if (count == undefined) { 29 | count = 1; 30 | } 31 | for (var i=0;i'}); 33 | } 34 | return images; 35 | } 36 | }) 37 | ; 38 | -------------------------------------------------------------------------------- /app/partials/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthieu-D/angular-lorem-image/49d96aea0091f663e4a99962b104c1954444360d/app/partials/.gitkeep -------------------------------------------------------------------------------- /app/partials/lorem-image.html: -------------------------------------------------------------------------------- 1 | 3 | {{image.html}} 4 | 5 | -------------------------------------------------------------------------------- /app/partials/partial1.html: -------------------------------------------------------------------------------- 1 | This is ze demo
2 | 3 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-lorem-image", 3 | "version": "0.0.1", 4 | "homepage": "https://github.com/matthieu-D/angular-lorem-image", 5 | "description": "This directive provides lorem image generation for your website by using the great lorempixel service to generate images", 6 | "main": "app/js/directives.js", 7 | "keywords": [ 8 | "angular", 9 | "lorem", 10 | "image" 11 | ], 12 | "authors": [ 13 | "Matthieu Drula" 14 | ], 15 | "license": "MIT", 16 | "ignore": [ 17 | "**/.*", 18 | "node_modules", 19 | "bower_components", 20 | "test", 21 | "tests" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /config/karma-e2e.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config){ 2 | config.set({ 3 | 4 | 5 | basePath : '../', 6 | 7 | files : [ 8 | 'test/e2e/**/*.js' 9 | ], 10 | 11 | autoWatch : false, 12 | 13 | browsers : ['Chrome'], 14 | 15 | frameworks: ['ng-scenario'], 16 | 17 | singleRun : true, 18 | 19 | proxies : { 20 | '/': 'http://localhost:8000/' 21 | }, 22 | 23 | plugins : [ 24 | 'karma-junit-reporter', 25 | 'karma-chrome-launcher', 26 | 'karma-firefox-launcher', 27 | 'karma-jasmine', 28 | 'karma-ng-scenario' 29 | ], 30 | 31 | junitReporter : { 32 | outputFile: 'test_out/e2e.xml', 33 | suite: 'e2e' 34 | } 35 | 36 | })} 37 | 38 | -------------------------------------------------------------------------------- /config/karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config){ 2 | config.set({ 3 | basePath : '../', 4 | 5 | files : [ 6 | 'app/lib/angular/angular.js', 7 | 'app/lib/angular/angular-*.js', 8 | 'test/lib/angular/angular-mocks.js', 9 | 'app/js/**/*.js', 10 | 'test/unit/**/*.js' 11 | ], 12 | 13 | exclude : [ 14 | 'app/lib/angular/angular-loader.js', 15 | 'app/lib/angular/*.min.js', 16 | 'app/lib/angular/angular-scenario.js' 17 | ], 18 | 19 | autoWatch : true, 20 | 21 | frameworks: ['jasmine'], 22 | 23 | browsers : ['Chrome'], 24 | 25 | plugins : [ 26 | 'karma-junit-reporter', 27 | 'karma-chrome-launcher', 28 | 'karma-firefox-launcher', 29 | 'karma-jasmine' 30 | ], 31 | 32 | junitReporter : { 33 | outputFile: 'test_out/unit.xml', 34 | suite: 'unit' 35 | } 36 | 37 | })} 38 | -------------------------------------------------------------------------------- /logs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthieu-D/angular-lorem-image/49d96aea0091f663e4a99962b104c1954444360d/logs/.gitkeep -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-lorem-image", 3 | "description": "Generate images from lorem images according to simple parameters", 4 | "repository": "https://github.com/matthieu-D/angular-lorem-image", 5 | "devDependencies": { 6 | "phantomjs" : "~1.9", 7 | "karma" : "~0.10", 8 | "karma-junit-reporter" : "~0.1", 9 | "karma-jasmine" : "~0.1", 10 | "karma-ng-scenario" : "~0.1" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /scripts/e2e-test.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | REM Windows script for running e2e tests 4 | REM You have to run server and capture some browser first 5 | REM 6 | REM Requirements: 7 | REM - NodeJS (http://nodejs.org/) 8 | REM - Karma (npm install -g karma) 9 | 10 | set BASE_DIR=%~dp0 11 | karma start "%BASE_DIR%\..\config\karma-e2e.conf.js" %* 12 | -------------------------------------------------------------------------------- /scripts/e2e-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | BASE_DIR=`dirname $0` 4 | 5 | echo "" 6 | echo "Starting Karma Server (http://karma-runner.github.io)" 7 | echo $BASE_DIR 8 | echo "-------------------------------------------------------------------" 9 | 10 | $BASE_DIR/../node_modules/karma/bin/karma start $BASE_DIR/../config/karma-e2e.conf.js $* 11 | -------------------------------------------------------------------------------- /scripts/test.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | REM Windows script for running unit tests 4 | REM You have to run server and capture some browser first 5 | REM 6 | REM Requirements: 7 | REM - NodeJS (http://nodejs.org/) 8 | REM - Karma (npm install -g karma) 9 | 10 | set BASE_DIR=%~dp0 11 | karma start "%BASE_DIR%\..\config\karma.conf.js" %* 12 | -------------------------------------------------------------------------------- /scripts/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | BASE_DIR=`dirname $0` 4 | 5 | echo "" 6 | echo "Starting Karma Server (http://karma-runner.github.io)" 7 | echo "-------------------------------------------------------------------" 8 | 9 | $BASE_DIR/../node_modules/karma/bin/karma start $BASE_DIR/../config/karma.conf.js $* 10 | -------------------------------------------------------------------------------- /scripts/watchr.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env watchr 2 | 3 | # config file for watchr http://github.com/mynyml/watchr 4 | # install: gem install watchr 5 | # run: watch watchr.rb 6 | # note: make sure that you have jstd server running (server.sh) and a browser captured 7 | 8 | log_file = File.expand_path(File.dirname(__FILE__) + '/../logs/jstd.log') 9 | 10 | `cd ..` 11 | `touch #{log_file}` 12 | 13 | puts "String watchr... log file: #{log_file}" 14 | 15 | watch( '(app/js|test/unit)' ) do 16 | `echo "\n\ntest run started @ \`date\`" > #{log_file}` 17 | `scripts/test.sh &> #{log_file}` 18 | end 19 | 20 | -------------------------------------------------------------------------------- /scripts/web-server.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var util = require('util'), 4 | http = require('http'), 5 | fs = require('fs'), 6 | url = require('url'), 7 | events = require('events'); 8 | 9 | var DEFAULT_PORT = 8000; 10 | 11 | function main(argv) { 12 | new HttpServer({ 13 | 'GET': createServlet(StaticServlet), 14 | 'HEAD': createServlet(StaticServlet) 15 | }).start(Number(argv[2]) || DEFAULT_PORT); 16 | } 17 | 18 | function escapeHtml(value) { 19 | return value.toString(). 20 | replace('<', '<'). 21 | replace('>', '>'). 22 | replace('"', '"'); 23 | } 24 | 25 | function createServlet(Class) { 26 | var servlet = new Class(); 27 | return servlet.handleRequest.bind(servlet); 28 | } 29 | 30 | /** 31 | * An Http server implementation that uses a map of methods to decide 32 | * action routing. 33 | * 34 | * @param {Object} Map of method => Handler function 35 | */ 36 | function HttpServer(handlers) { 37 | this.handlers = handlers; 38 | this.server = http.createServer(this.handleRequest_.bind(this)); 39 | } 40 | 41 | HttpServer.prototype.start = function(port) { 42 | this.port = port; 43 | this.server.listen(port); 44 | util.puts('Http Server running at http://localhost:' + port + '/'); 45 | }; 46 | 47 | HttpServer.prototype.parseUrl_ = function(urlString) { 48 | var parsed = url.parse(urlString); 49 | parsed.pathname = url.resolve('/', parsed.pathname); 50 | return url.parse(url.format(parsed), true); 51 | }; 52 | 53 | HttpServer.prototype.handleRequest_ = function(req, res) { 54 | var logEntry = req.method + ' ' + req.url; 55 | if (req.headers['user-agent']) { 56 | logEntry += ' ' + req.headers['user-agent']; 57 | } 58 | util.puts(logEntry); 59 | req.url = this.parseUrl_(req.url); 60 | var handler = this.handlers[req.method]; 61 | if (!handler) { 62 | res.writeHead(501); 63 | res.end(); 64 | } else { 65 | handler.call(this, req, res); 66 | } 67 | }; 68 | 69 | /** 70 | * Handles static content. 71 | */ 72 | function StaticServlet() {} 73 | 74 | StaticServlet.MimeMap = { 75 | 'txt': 'text/plain', 76 | 'html': 'text/html', 77 | 'css': 'text/css', 78 | 'xml': 'application/xml', 79 | 'json': 'application/json', 80 | 'js': 'application/javascript', 81 | 'jpg': 'image/jpeg', 82 | 'jpeg': 'image/jpeg', 83 | 'gif': 'image/gif', 84 | 'png': 'image/png', 85 |   'svg': 'image/svg+xml' 86 | }; 87 | 88 | StaticServlet.prototype.handleRequest = function(req, res) { 89 | var self = this; 90 | var path = ('./' + req.url.pathname).replace('//','/').replace(/%(..)/g, function(match, hex){ 91 | return String.fromCharCode(parseInt(hex, 16)); 92 | }); 93 | var parts = path.split('/'); 94 | if (parts[parts.length-1].charAt(0) === '.') 95 | return self.sendForbidden_(req, res, path); 96 | fs.stat(path, function(err, stat) { 97 | if (err) 98 | return self.sendMissing_(req, res, path); 99 | if (stat.isDirectory()) 100 | return self.sendDirectory_(req, res, path); 101 | return self.sendFile_(req, res, path); 102 | }); 103 | } 104 | 105 | StaticServlet.prototype.sendError_ = function(req, res, error) { 106 | res.writeHead(500, { 107 | 'Content-Type': 'text/html' 108 | }); 109 | res.write('\n'); 110 | res.write('Internal Server Error\n'); 111 | res.write('

Internal Server Error

'); 112 | res.write('
' + 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('404 Not Found\n'); 124 | res.write('

Not Found

'); 125 | res.write( 126 | '

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('403 Forbidden\n'); 141 | res.write('

Forbidden

'); 142 | res.write( 143 | '

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('301 Moved Permanently\n'); 157 | res.write('

Moved Permanently

'); 158 | res.write( 159 | '

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('' + escapeHtml(path) + '\n'); 227 | res.write('\n'); 230 | res.write('

Directory: ' + escapeHtml(path) + '

'); 231 | res.write('
    '); 232 | files.forEach(function(fileName) { 233 | if (fileName.charAt(0) !== '.') { 234 | res.write('
  1. ' + 236 | escapeHtml(fileName) + '
  2. '); 237 | } 238 | }); 239 | res.write('
'); 240 | res.end(); 241 | }; 242 | 243 | // Must be last, 244 | main(process.argv); 245 | -------------------------------------------------------------------------------- /test/e2e/runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | End2end Test Runner 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /test/e2e/scenarios.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* http://docs.angularjs.org/guide/dev_guide.e2e-testing */ 4 | 5 | describe('my app', function() { 6 | 7 | beforeEach(function() { 8 | browser().navigateTo('app/index.html'); 9 | }); 10 | 11 | 12 | it('should automatically redirect to /view1 when location hash/fragment is empty', function() { 13 | expect(browser().location().url()).toBe("/view1"); 14 | }); 15 | 16 | 17 | describe('view1', function() { 18 | 19 | beforeEach(function() { 20 | browser().navigateTo('#/view1'); 21 | }); 22 | 23 | 24 | it('should render view1 when user navigates to /view1', function() { 25 | expect(element('[ng-view] p:first').text()). 26 | toMatch(/partial for view 1/); 27 | }); 28 | 29 | }); 30 | 31 | 32 | describe('view2', function() { 33 | 34 | beforeEach(function() { 35 | browser().navigateTo('#/view2'); 36 | }); 37 | 38 | 39 | it('should render view2 when user navigates to /view2', function() { 40 | expect(element('[ng-view] p:first').text()). 41 | toMatch(/partial for view 2/); 42 | }); 43 | 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /test/lib/angular/angular-mocks.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license AngularJS v1.2.3 3 | * (c) 2010-2014 Google, Inc. http://angularjs.org 4 | * License: MIT 5 | */ 6 | (function(window, angular, undefined) { 7 | 8 | 'use strict'; 9 | 10 | /** 11 | * @ngdoc overview 12 | * @name angular.mock 13 | * @description 14 | * 15 | * Namespace from 'angular-mocks.js' which contains testing related code. 16 | */ 17 | angular.mock = {}; 18 | 19 | /** 20 | * ! This is a private undocumented service ! 21 | * 22 | * @name ngMock.$browser 23 | * 24 | * @description 25 | * This service is a mock implementation of {@link ng.$browser}. It provides fake 26 | * implementation for commonly used browser apis that are hard to test, e.g. setTimeout, xhr, 27 | * cookies, etc... 28 | * 29 | * The api of this service is the same as that of the real {@link ng.$browser $browser}, except 30 | * that there are several helper methods available which can be used in tests. 31 | */ 32 | angular.mock.$BrowserProvider = function() { 33 | this.$get = function() { 34 | return new angular.mock.$Browser(); 35 | }; 36 | }; 37 | 38 | angular.mock.$Browser = function() { 39 | var self = this; 40 | 41 | this.isMock = true; 42 | self.$$url = "http://server/"; 43 | self.$$lastUrl = self.$$url; // used by url polling fn 44 | self.pollFns = []; 45 | 46 | // TODO(vojta): remove this temporary api 47 | self.$$completeOutstandingRequest = angular.noop; 48 | self.$$incOutstandingRequestCount = angular.noop; 49 | 50 | 51 | // register url polling fn 52 | 53 | self.onUrlChange = function(listener) { 54 | self.pollFns.push( 55 | function() { 56 | if (self.$$lastUrl != self.$$url) { 57 | self.$$lastUrl = self.$$url; 58 | listener(self.$$url); 59 | } 60 | } 61 | ); 62 | 63 | return listener; 64 | }; 65 | 66 | self.cookieHash = {}; 67 | self.lastCookieHash = {}; 68 | self.deferredFns = []; 69 | self.deferredNextId = 0; 70 | 71 | self.defer = function(fn, delay) { 72 | delay = delay || 0; 73 | self.deferredFns.push({time:(self.defer.now + delay), fn:fn, id: self.deferredNextId}); 74 | self.deferredFns.sort(function(a,b){ return a.time - b.time;}); 75 | return self.deferredNextId++; 76 | }; 77 | 78 | 79 | /** 80 | * @name ngMock.$browser#defer.now 81 | * @propertyOf ngMock.$browser 82 | * 83 | * @description 84 | * Current milliseconds mock time. 85 | */ 86 | self.defer.now = 0; 87 | 88 | 89 | self.defer.cancel = function(deferId) { 90 | var fnIndex; 91 | 92 | angular.forEach(self.deferredFns, function(fn, index) { 93 | if (fn.id === deferId) fnIndex = index; 94 | }); 95 | 96 | if (fnIndex !== undefined) { 97 | self.deferredFns.splice(fnIndex, 1); 98 | return true; 99 | } 100 | 101 | return false; 102 | }; 103 | 104 | 105 | /** 106 | * @name ngMock.$browser#defer.flush 107 | * @methodOf ngMock.$browser 108 | * 109 | * @description 110 | * Flushes all pending requests and executes the defer callbacks. 111 | * 112 | * @param {number=} number of milliseconds to flush. See {@link #defer.now} 113 | */ 114 | self.defer.flush = function(delay) { 115 | if (angular.isDefined(delay)) { 116 | self.defer.now += delay; 117 | } else { 118 | if (self.deferredFns.length) { 119 | self.defer.now = self.deferredFns[self.deferredFns.length-1].time; 120 | } else { 121 | throw new Error('No deferred tasks to be flushed'); 122 | } 123 | } 124 | 125 | while (self.deferredFns.length && self.deferredFns[0].time <= self.defer.now) { 126 | self.deferredFns.shift().fn(); 127 | } 128 | }; 129 | 130 | self.$$baseHref = ''; 131 | self.baseHref = function() { 132 | return this.$$baseHref; 133 | }; 134 | }; 135 | angular.mock.$Browser.prototype = { 136 | 137 | /** 138 | * @name ngMock.$browser#poll 139 | * @methodOf ngMock.$browser 140 | * 141 | * @description 142 | * run all fns in pollFns 143 | */ 144 | poll: function poll() { 145 | angular.forEach(this.pollFns, function(pollFn){ 146 | pollFn(); 147 | }); 148 | }, 149 | 150 | addPollFn: function(pollFn) { 151 | this.pollFns.push(pollFn); 152 | return pollFn; 153 | }, 154 | 155 | url: function(url, replace) { 156 | if (url) { 157 | this.$$url = url; 158 | return this; 159 | } 160 | 161 | return this.$$url; 162 | }, 163 | 164 | cookies: function(name, value) { 165 | if (name) { 166 | if (angular.isUndefined(value)) { 167 | delete this.cookieHash[name]; 168 | } else { 169 | if (angular.isString(value) && //strings only 170 | value.length <= 4096) { //strict cookie storage limits 171 | this.cookieHash[name] = value; 172 | } 173 | } 174 | } else { 175 | if (!angular.equals(this.cookieHash, this.lastCookieHash)) { 176 | this.lastCookieHash = angular.copy(this.cookieHash); 177 | this.cookieHash = angular.copy(this.cookieHash); 178 | } 179 | return this.cookieHash; 180 | } 181 | }, 182 | 183 | notifyWhenNoOutstandingRequests: function(fn) { 184 | fn(); 185 | } 186 | }; 187 | 188 | 189 | /** 190 | * @ngdoc object 191 | * @name ngMock.$exceptionHandlerProvider 192 | * 193 | * @description 194 | * Configures the mock implementation of {@link ng.$exceptionHandler} to rethrow or to log errors 195 | * passed into the `$exceptionHandler`. 196 | */ 197 | 198 | /** 199 | * @ngdoc object 200 | * @name ngMock.$exceptionHandler 201 | * 202 | * @description 203 | * Mock implementation of {@link ng.$exceptionHandler} that rethrows or logs errors passed 204 | * into it. See {@link ngMock.$exceptionHandlerProvider $exceptionHandlerProvider} for configuration 205 | * information. 206 | * 207 | * 208 | *
 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 | * 923 | * 924 | * 925 | * 926 | * 927 | * 928 | * 929 | * 930 | * 931 | * 932 | * 933 | * 934 | * 935 | * 936 | * 937 | * 938 | * 939 | * 940 | * 941 | * 942 | * 943 | * 944 | * 945 | * 946 | * 947 | * 948 | * 949 | * 950 | * 951 | * 952 | * 953 | * 954 | *
Request expectationsBackend definitions
Syntax.expect(...).respond(...).when(...).respond(...)
Typical usagestrict unit testsloose (black-box) unit testing
Fulfills multiple requestsNOYES
Order of requests mattersYESNO
Request requiredYESNO
Response requiredoptional (see below)YES
955 | * 956 | * In cases where both backend definitions and request expectations are specified during unit 957 | * testing, the request expectations are evaluated first. 958 | * 959 | * If a request expectation has no response specified, the algorithm will search your backend 960 | * definitions for an appropriate response. 961 | * 962 | * If a request didn't match any expectation or if the expectation doesn't have the response 963 | * defined, the backend definitions are evaluated in sequential order to see if any of them match 964 | * the request. The response from the first matched definition is returned. 965 | * 966 | * 967 | * # Flushing HTTP requests 968 | * 969 | * The $httpBackend used in production, always responds to requests with responses asynchronously. 970 | * If we preserved this behavior in unit testing, we'd have to create async unit tests, which are 971 | * hard to write, follow and maintain. At the same time the testing mock, can't respond 972 | * synchronously because that would change the execution of the code under test. For this reason the 973 | * mock $httpBackend has a `flush()` method, which allows the test to explicitly flush pending 974 | * requests and thus preserving the async api of the backend, while allowing the test to execute 975 | * synchronously. 976 | * 977 | * 978 | * # Unit testing with mock $httpBackend 979 | * The following code shows how to setup and use the mock backend in unit testing a controller. 980 | * First we create the controller under test 981 | * 982 |
 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.
1964 | * 1965 | * This function registers a module configuration code. It collects the configuration information 1966 | * which will be used when the injector is created by {@link angular.mock.inject inject}. 1967 | * 1968 | * See {@link angular.mock.inject inject} for usage example 1969 | * 1970 | * @param {...(string|Function|Object)} fns any number of modules which are represented as string 1971 | * aliases or as anonymous module initialization functions. The modules are used to 1972 | * configure the injector. The 'ng' and 'ngMock' modules are automatically loaded. If an 1973 | * object literal is passed they will be register as values in the module, the key being 1974 | * the module name and the value being what is returned. 1975 | */ 1976 | window.module = angular.mock.module = function() { 1977 | var moduleFns = Array.prototype.slice.call(arguments, 0); 1978 | return isSpecRunning() ? workFn() : workFn; 1979 | ///////////////////// 1980 | function workFn() { 1981 | if (currentSpec.$injector) { 1982 | throw new Error('Injector already created, can not register a module!'); 1983 | } else { 1984 | var modules = currentSpec.$modules || (currentSpec.$modules = []); 1985 | angular.forEach(moduleFns, function(module) { 1986 | if (angular.isObject(module) && !angular.isArray(module)) { 1987 | modules.push(function($provide) { 1988 | angular.forEach(module, function(value, key) { 1989 | $provide.value(key, value); 1990 | }); 1991 | }); 1992 | } else { 1993 | modules.push(module); 1994 | } 1995 | }); 1996 | } 1997 | } 1998 | }; 1999 | 2000 | /** 2001 | * @ngdoc function 2002 | * @name angular.mock.inject 2003 | * @description 2004 | * 2005 | * *NOTE*: This function is also published on window for easy access.
2006 | * 2007 | * The inject function wraps a function into an injectable function. The inject() creates new 2008 | * instance of {@link AUTO.$injector $injector} per test, which is then used for 2009 | * resolving references. 2010 | * 2011 | * 2012 | * ## Resolving References (Underscore Wrapping) 2013 | * Often, we would like to inject a reference once, in a `beforeEach()` block and reuse this 2014 | * in multiple `it()` clauses. To be able to do this we must assign the reference to a variable 2015 | * that is declared in the scope of the `describe()` block. Since we would, most likely, want 2016 | * the variable to have the same name of the reference we have a problem, since the parameter 2017 | * to the `inject()` function would hide the outer variable. 2018 | * 2019 | * To help with this, the injected parameters can, optionally, be enclosed with underscores. 2020 | * These are ignored by the injector when the reference name is resolved. 2021 | * 2022 | * For example, the parameter `_myService_` would be resolved as the reference `myService`. 2023 | * Since it is available in the function body as _myService_, we can then assign it to a variable 2024 | * defined in an outer scope. 2025 | * 2026 | * ``` 2027 | * // Defined out reference variable outside 2028 | * var myService; 2029 | * 2030 | * // Wrap the parameter in underscores 2031 | * beforeEach( inject( function(_myService_){ 2032 | * myService = _myService_; 2033 | * })); 2034 | * 2035 | * // Use myService in a series of tests. 2036 | * it('makes use of myService', function() { 2037 | * myService.doStuff(); 2038 | * }); 2039 | * 2040 | * ``` 2041 | * 2042 | * See also {@link angular.mock.module angular.mock.module} 2043 | * 2044 | * ## Example 2045 | * Example of what a typical jasmine tests looks like with the inject method. 2046 | *
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 | -------------------------------------------------------------------------------- /test/lib/angular/version.txt: -------------------------------------------------------------------------------- 1 | 1.2.3 -------------------------------------------------------------------------------- /test/unit/controllersSpec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jasmine specs for controllers go here */ 4 | 5 | describe('controllers', function(){ 6 | beforeEach(module('myApp.controllers')); 7 | 8 | 9 | it('should ....', inject(function() { 10 | //spec body 11 | })); 12 | 13 | it('should ....', inject(function() { 14 | //spec body 15 | })); 16 | }); 17 | -------------------------------------------------------------------------------- /test/unit/directivesSpec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jasmine specs for directives go here */ 4 | 5 | describe('directives', function() { 6 | beforeEach(module('myApp.directives')); 7 | 8 | describe('app-version', function() { 9 | it('should print current version', function() { 10 | module(function($provide) { 11 | $provide.value('version', 'TEST_VER'); 12 | }); 13 | inject(function($compile, $rootScope) { 14 | var element = $compile('')($rootScope); 15 | expect(element.text()).toEqual('TEST_VER'); 16 | }); 17 | }); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /test/unit/filtersSpec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jasmine specs for filters go here */ 4 | 5 | describe('filter', function() { 6 | beforeEach(module('myApp.filters')); 7 | 8 | 9 | describe('interpolate', function() { 10 | beforeEach(module(function($provide) { 11 | $provide.value('version', 'TEST_VER'); 12 | })); 13 | 14 | 15 | it('should replace VERSION', inject(function(interpolateFilter) { 16 | expect(interpolateFilter('before %VERSION% after')).toEqual('before TEST_VER after'); 17 | })); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /test/unit/servicesSpec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jasmine specs for services go here */ 4 | 5 | describe('service', function() { 6 | beforeEach(module('myApp.services')); 7 | 8 | 9 | describe('version', function() { 10 | it('should return current version', inject(function(version) { 11 | expect(version).toEqual('0.1'); 12 | })); 13 | }); 14 | }); 15 | --------------------------------------------------------------------------------