├── .gitignore ├── .travis.yml ├── README.md ├── bower.json ├── filters.js ├── index.js ├── karma.conf.js ├── karma2.conf.js ├── lib ├── angularjs-mocks │ └── 1.2.20 │ │ └── angular-mocks.js ├── angularjs │ └── 1.2.18 │ │ └── angular.js └── jquery │ └── 2.1.1 │ └── jquery.js ├── package.json └── test ├── browser_tests.js ├── module_tests.js └── tests.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | 3 | .idea/* 4 | npm-debug.log -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | branches: 5 | only: 6 | - master 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/sumitchawla/angularjs-filters.svg?branch=master)](https://travis-ci.org/sumitchawla/angularjs-filters) [![Dependency Status](https://david-dm.org/sumitchawla/angularjs-filters.png)](https://david-dm.org/sumitchawla/angularjs-filters) [![devDependency Status](https://david-dm.org/sumitchawla/angularjs-filters/dev-status.png)](https://david-dm.org/sumitchawla/angularjs-filters#info=devDependencies) 2 | 3 | angularjs-filters 4 | ================= 5 | 6 | A library of common AngularJS filters. Each filter is individually tested for various inputs. The module can be used by using the filter.js in your browser includes, or by using npm module ( with browserify). 7 | 8 | ### Installation 9 | bower install angularjs-filters 10 | 11 | ### String Filters 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 39 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 |
FilterUsageResult
format'Hello {0}. What are you been doing this {1}?' | string.format : 'Sam' : 'evening'Hello Sam. What are you been doing this evening?
html2string'Hello <br/>. How are you?' | string.html2stringHello . How are you?
shorten'A long story cut into short' | string.shorten : 12 A long story...
replace
String Replace. Pattern can be a string or regex
36 | 'Hello Mr How are you doing' | string.replace : 'Mr': 'Sir'
37 | "hello help"| string.replace:"he[a-z]{2}":"Yell" 38 |
40 | Hello Sir How are you doing
41 | Yello Yell 42 |
camelcase'A long story cut into short' | string.camelcaseA Long Story Cut Into Short
lowercase'Convert to LOWERCASE' | string.lowercaseconvert to lowercase
uppercase'uppercase all' | string.uppercaseUPPERCASE ALL
trim, trimstart, trimend
String Trim Functions
' Hello Mr. ' | string.trimHello Mr.
65 | 66 | ### Array Filters 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 |
FilterUsageResult
join['Hello','Mr.','How','Are','You?'] | array.join : '-'Hello-Mr.-How-Are-You?
reverseng-repeat='["Banana", "Orange", "Apple", "Mango"] | array.reverse'"Mango","Apple","Orange", "Banana"
84 | 85 | ### Math Filters 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 |
FilterUsageResult
max[8, 1, 2, 3, 7] | math.max8
min[8, 1, 2, 3, 7] | math.min1
103 | 104 | ### Boolean Filters 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 |
FilterUsageResult
YesNo
Converts boolean value to Yes/No
A == B | binary.YesNoYes/No?
117 | 118 | ### Debug Filters 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 |
FilterUsageResult
print
Debug prints the bound value
'MyValue' | debug.printMyValue
131 | 132 | ### Other 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 |
FilterUsageResult
utixtimeconvertor
Converts unix timestamp to human readable (friedly) time.
'1596971407' | utixtimeconvertor9 Aug 2020 - 4:10:7
145 | 146 | ### Unit Testing 147 | Each of the filter is covered by Unit Test extensively. If you find any input unhandelled, please let me know. Run the unit tests using following commands: 148 | ``` 149 | npm run karma 150 | npm run karma2 151 | ``` 152 | 153 | 154 | ### Contributions 155 | Please feel free to add your contributions to set of filters. 156 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angularjs-filters", 3 | "main": "filters.js", 4 | "version": "0.0.1", 5 | "homepage": "https://github.com/sumitchawla/angularjs-filters", 6 | "authors": [ 7 | "Sumit Chawla " 8 | ], 9 | "description": "Common AngularJS Filters", 10 | "moduleType": [ 11 | "node" 12 | ], 13 | "keywords": [ 14 | "AngularJS", 15 | "filters", 16 | "Filters", 17 | "for", 18 | "AngularJS" 19 | ], 20 | "license": "MIT", 21 | "ignore": [ 22 | "**/.*", 23 | "node_modules", 24 | "bower_components", 25 | "test", 26 | "tests" 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /filters.js: -------------------------------------------------------------------------------- 1 | angular.module("ch.filters",[]) 2 | .filter("print", [ function() { 3 | return function(str){ 4 | console.log("ch.filters.debug.print", str); 5 | return str; 6 | } 7 | } 8 | ]) 9 | /*** Boolean Filters *****/ 10 | .filter("YesNo", [ function() { 11 | return function(b){ 12 | return b === true? 'Yes' : 'No'; 13 | } 14 | } 15 | ]) 16 | /*** String Filters *****/ 17 | .filter("format", [ function() { 18 | return function(str){ 19 | if (!str || arguments.length <=1 ) return str; 20 | var args = arguments; 21 | for (var i = 1; i < arguments.length; i++) { 22 | var reg = new RegExp("\\{" + (i - 1) + "\\}", "gm"); 23 | str = str.replace(reg, arguments[i]); 24 | } 25 | return str; 26 | } 27 | } 28 | ]).filter("html2string", [ function() { 29 | return function(str){ 30 | if (!str) return str; 31 | return $('
').html(str).text(); 32 | } 33 | } 34 | ]).filter("shorten", [ function() { 35 | return function(str,length){ 36 | if (!str || !length || str.length <= length) return (str || ''); 37 | return str.substr(0, length) + (length <= 3 ? '' : '...'); 38 | } 39 | } 40 | ]).filter("lowercase", [ function() { 41 | return function(str){ 42 | return (str || '').toLowerCase(); 43 | } 44 | } 45 | ]).filter("uppercase", [ function() { 46 | return function(str){ 47 | return (str || '').toUpperCase(); 48 | } 49 | } 50 | ]).filter("camelcase", [ function(){ 51 | return function(str){ 52 | return (str || '').toLowerCase().replace(/(\s.|^.)/g, function(match, group) { 53 | return group ? group.toUpperCase() : ''; 54 | }); 55 | } 56 | } 57 | ]).filter("trim", [ function(){ 58 | return function(str){ 59 | return (str || '').replace(/(^\s*|\s*$)/g, function(match, group) { 60 | return ''; 61 | }); 62 | } 63 | } 64 | ]).filter("trimstart", [ function(){ 65 | return function(str){ 66 | return (str || '').replace(/(^\s*)/g, function(match, group) { 67 | return ''; 68 | }); 69 | } 70 | } 71 | ]).filter("trimend", [ function(){ 72 | return function(str){ 73 | return (str || '').replace(/(\s*$)/g, function(match, group) { 74 | return ''; 75 | }); 76 | } 77 | } 78 | ]).filter("replace", [ function(){ 79 | return function(str, pattern, replacement, global){ 80 | global = (typeof global == 'undefined' ? true : global); 81 | try { 82 | str = str ? (typeof global == 'string' ? str : str.toString()) : ''; 83 | return str.replace(new RegExp(pattern,global ? "g": ""),function(match, group) { 84 | return replacement; 85 | }); 86 | } catch(e) { 87 | console.error("error in string.replace", e); 88 | return (str || ''); 89 | } 90 | } 91 | } 92 | ]).filter("max", [ function(){ 93 | return function(arr){ 94 | if (!arr) return arr; 95 | return Math.max.apply(null, arr); 96 | } 97 | } 98 | ]).filter("min", [ function(){ 99 | return function(arr){ 100 | if (!arr) return arr; 101 | return Math.min.apply(null, arr); 102 | } 103 | } 104 | ]).filter("join", [ function(){ 105 | return function(arr,seperator){ 106 | if (!arr) return arr; 107 | return arr.join(seperator || ','); 108 | } 109 | } 110 | ]).filter("reverse", [ function(){ 111 | return function(arr){ 112 | if (!arr) return arr; 113 | return arr.reverse(); 114 | } 115 | } 116 | ]).filter("utixtimeconvertor", [ function(){ 117 | return function(number) { 118 | // Is the passed data a number? 119 | if(isNaN(number) || number < 1) { 120 | // If not number, just return the entered value (Do not change user value!) 121 | return number; 122 | } else { 123 | var t = new Date(number * 1000); 124 | var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; 125 | var time = t.getDate() +' '+ months[t.getMonth()]+' '+t.getFullYear()+' - '+t.getHours()+':'+t.getMinutes()+':'+t.getSeconds(); 126 | return time; 127 | } 128 | } 129 | } 130 | ]); 131 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var angular = require('angular'); 4 | require('./filters.js'); 5 | 6 | module.exports = 'ch.filters'; 7 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Fri Jul 04 2014 17:55:25 GMT-0700 (PDT) 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | 7 | // base path that will be used to resolve all patterns (eg. files, exclude) 8 | basePath: '', 9 | 10 | 11 | // frameworks to use 12 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 13 | frameworks: ['mocha', 'chai'], 14 | 15 | 16 | // list of files / patterns to load in the browser 17 | files: [ 18 | 'lib/jquery/**/*.js', 19 | 'lib/angularjs/**/*.js', 20 | 'lib/angularjs-mocks/**/*.js', 21 | 'filters.js', 22 | 'test/tests.js', 23 | 'test/browser_tests.js' 24 | ], 25 | 26 | 27 | // list of files to exclude 28 | exclude: [ 29 | 30 | ], 31 | 32 | 33 | // preprocess matching files before serving them to the browser 34 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 35 | preprocessors: { 36 | 37 | }, 38 | 39 | 40 | // test results reporter to use 41 | // possible values: 'dots', 'progress' 42 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 43 | reporters: ['progress'], 44 | 45 | 46 | // web server port 47 | port: 9876, 48 | 49 | 50 | // enable / disable colors in the output (reporters and logs) 51 | colors: true, 52 | 53 | 54 | // level of logging 55 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 56 | logLevel: config.LOG_INFO, 57 | 58 | 59 | // enable / disable watching file and executing tests whenever any file changes 60 | autoWatch: true, 61 | 62 | 63 | // start these browsers 64 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 65 | browsers: ['PhantomJS'], 66 | 67 | 68 | // Continuous Integration mode 69 | // if true, Karma captures browsers, runs the tests and exits 70 | singleRun: false 71 | }); 72 | }; 73 | -------------------------------------------------------------------------------- /karma2.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Fri Jul 04 2014 17:55:25 GMT-0700 (PDT) 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | 7 | // base path that will be used to resolve all patterns (eg. files, exclude) 8 | basePath: '', 9 | 10 | 11 | // frameworks to use 12 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 13 | frameworks: ['browserify', 'mocha', 'chai'], 14 | 15 | 16 | // list of files / patterns to load in the browser 17 | files: [ 18 | 'lib/jquery/**/*.js', 19 | 'lib/angularjs/**/*.js', 20 | 'lib/angularjs-mocks/**/*.js', 21 | 'filters.js', 22 | 'test/tests.js', 23 | 'test/module_tests.js' 24 | ], 25 | 26 | 27 | // list of files to exclude 28 | exclude: [ 29 | 30 | ], 31 | 32 | 33 | preprocessors: { 34 | './test/module_tests.js': ['browserify'] 35 | }, 36 | 37 | browserify: { 38 | debug: true, 39 | transform: ['browserify-shim'] 40 | }, 41 | 42 | 43 | // test results reporter to use 44 | // possible values: 'dots', 'progress' 45 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 46 | reporters: ['progress'], 47 | 48 | 49 | // web server port 50 | port: 9876, 51 | 52 | 53 | // enable / disable colors in the output (reporters and logs) 54 | colors: true, 55 | 56 | 57 | // level of logging 58 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 59 | logLevel: config.LOG_INFO, 60 | 61 | 62 | // enable / disable watching file and executing tests whenever any file changes 63 | autoWatch: true, 64 | 65 | 66 | // start these browsers 67 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 68 | browsers: ['PhantomJS'], 69 | 70 | 71 | // Continuous Integration mode 72 | // if true, Karma captures browsers, runs the tests and exits 73 | singleRun: false 74 | }); 75 | }; 76 | -------------------------------------------------------------------------------- /lib/angularjs-mocks/1.2.20/angular-mocks.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license AngularJS v1.2.20-build.283+sha.873acf8 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 object 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 $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 $browser#defer.now 81 | * 82 | * @description 83 | * Current milliseconds mock time. 84 | */ 85 | self.defer.now = 0; 86 | 87 | 88 | self.defer.cancel = function(deferId) { 89 | var fnIndex; 90 | 91 | angular.forEach(self.deferredFns, function(fn, index) { 92 | if (fn.id === deferId) fnIndex = index; 93 | }); 94 | 95 | if (fnIndex !== undefined) { 96 | self.deferredFns.splice(fnIndex, 1); 97 | return true; 98 | } 99 | 100 | return false; 101 | }; 102 | 103 | 104 | /** 105 | * @name $browser#defer.flush 106 | * 107 | * @description 108 | * Flushes all pending requests and executes the defer callbacks. 109 | * 110 | * @param {number=} number of milliseconds to flush. See {@link #defer.now} 111 | */ 112 | self.defer.flush = function(delay) { 113 | if (angular.isDefined(delay)) { 114 | self.defer.now += delay; 115 | } else { 116 | if (self.deferredFns.length) { 117 | self.defer.now = self.deferredFns[self.deferredFns.length-1].time; 118 | } else { 119 | throw new Error('No deferred tasks to be flushed'); 120 | } 121 | } 122 | 123 | while (self.deferredFns.length && self.deferredFns[0].time <= self.defer.now) { 124 | self.deferredFns.shift().fn(); 125 | } 126 | }; 127 | 128 | self.$$baseHref = ''; 129 | self.baseHref = function() { 130 | return this.$$baseHref; 131 | }; 132 | }; 133 | angular.mock.$Browser.prototype = { 134 | 135 | /** 136 | * @name $browser#poll 137 | * 138 | * @description 139 | * run all fns in pollFns 140 | */ 141 | poll: function poll() { 142 | angular.forEach(this.pollFns, function(pollFn){ 143 | pollFn(); 144 | }); 145 | }, 146 | 147 | addPollFn: function(pollFn) { 148 | this.pollFns.push(pollFn); 149 | return pollFn; 150 | }, 151 | 152 | url: function(url, replace) { 153 | if (url) { 154 | this.$$url = url; 155 | return this; 156 | } 157 | 158 | return this.$$url; 159 | }, 160 | 161 | cookies: function(name, value) { 162 | if (name) { 163 | if (angular.isUndefined(value)) { 164 | delete this.cookieHash[name]; 165 | } else { 166 | if (angular.isString(value) && //strings only 167 | value.length <= 4096) { //strict cookie storage limits 168 | this.cookieHash[name] = value; 169 | } 170 | } 171 | } else { 172 | if (!angular.equals(this.cookieHash, this.lastCookieHash)) { 173 | this.lastCookieHash = angular.copy(this.cookieHash); 174 | this.cookieHash = angular.copy(this.cookieHash); 175 | } 176 | return this.cookieHash; 177 | } 178 | }, 179 | 180 | notifyWhenNoOutstandingRequests: function(fn) { 181 | fn(); 182 | } 183 | }; 184 | 185 | 186 | /** 187 | * @ngdoc provider 188 | * @name $exceptionHandlerProvider 189 | * 190 | * @description 191 | * Configures the mock implementation of {@link ng.$exceptionHandler} to rethrow or to log errors 192 | * passed into the `$exceptionHandler`. 193 | */ 194 | 195 | /** 196 | * @ngdoc service 197 | * @name $exceptionHandler 198 | * 199 | * @description 200 | * Mock implementation of {@link ng.$exceptionHandler} that rethrows or logs errors passed 201 | * into it. See {@link ngMock.$exceptionHandlerProvider $exceptionHandlerProvider} for configuration 202 | * information. 203 | * 204 | * 205 | * ```js 206 | * describe('$exceptionHandlerProvider', function() { 207 | * 208 | * it('should capture log messages and exceptions', function() { 209 | * 210 | * module(function($exceptionHandlerProvider) { 211 | * $exceptionHandlerProvider.mode('log'); 212 | * }); 213 | * 214 | * inject(function($log, $exceptionHandler, $timeout) { 215 | * $timeout(function() { $log.log(1); }); 216 | * $timeout(function() { $log.log(2); throw 'banana peel'; }); 217 | * $timeout(function() { $log.log(3); }); 218 | * expect($exceptionHandler.errors).toEqual([]); 219 | * expect($log.assertEmpty()); 220 | * $timeout.flush(); 221 | * expect($exceptionHandler.errors).toEqual(['banana peel']); 222 | * expect($log.log.logs).toEqual([[1], [2], [3]]); 223 | * }); 224 | * }); 225 | * }); 226 | * ``` 227 | */ 228 | 229 | angular.mock.$ExceptionHandlerProvider = function() { 230 | var handler; 231 | 232 | /** 233 | * @ngdoc method 234 | * @name $exceptionHandlerProvider#mode 235 | * 236 | * @description 237 | * Sets the logging mode. 238 | * 239 | * @param {string} mode Mode of operation, defaults to `rethrow`. 240 | * 241 | * - `rethrow`: If any errors are passed into the handler in tests, it typically 242 | * means that there is a bug in the application or test, so this mock will 243 | * make these tests fail. 244 | * - `log`: Sometimes it is desirable to test that an error is thrown, for this case the `log` 245 | * mode stores an array of errors in `$exceptionHandler.errors`, to allow later 246 | * assertion of them. See {@link ngMock.$log#assertEmpty assertEmpty()} and 247 | * {@link ngMock.$log#reset reset()} 248 | */ 249 | this.mode = function(mode) { 250 | switch(mode) { 251 | case 'rethrow': 252 | handler = function(e) { 253 | throw e; 254 | }; 255 | break; 256 | case 'log': 257 | var errors = []; 258 | 259 | handler = function(e) { 260 | if (arguments.length == 1) { 261 | errors.push(e); 262 | } else { 263 | errors.push([].slice.call(arguments, 0)); 264 | } 265 | }; 266 | 267 | handler.errors = errors; 268 | break; 269 | default: 270 | throw new Error("Unknown mode '" + mode + "', only 'log'/'rethrow' modes are allowed!"); 271 | } 272 | }; 273 | 274 | this.$get = function() { 275 | return handler; 276 | }; 277 | 278 | this.mode('rethrow'); 279 | }; 280 | 281 | 282 | /** 283 | * @ngdoc service 284 | * @name $log 285 | * 286 | * @description 287 | * Mock implementation of {@link ng.$log} that gathers all logged messages in arrays 288 | * (one array per logging level). These arrays are exposed as `logs` property of each of the 289 | * level-specific log function, e.g. for level `error` the array is exposed as `$log.error.logs`. 290 | * 291 | */ 292 | angular.mock.$LogProvider = function() { 293 | var debug = true; 294 | 295 | function concat(array1, array2, index) { 296 | return array1.concat(Array.prototype.slice.call(array2, index)); 297 | } 298 | 299 | this.debugEnabled = function(flag) { 300 | if (angular.isDefined(flag)) { 301 | debug = flag; 302 | return this; 303 | } else { 304 | return debug; 305 | } 306 | }; 307 | 308 | this.$get = function () { 309 | var $log = { 310 | log: function() { $log.log.logs.push(concat([], arguments, 0)); }, 311 | warn: function() { $log.warn.logs.push(concat([], arguments, 0)); }, 312 | info: function() { $log.info.logs.push(concat([], arguments, 0)); }, 313 | error: function() { $log.error.logs.push(concat([], arguments, 0)); }, 314 | debug: function() { 315 | if (debug) { 316 | $log.debug.logs.push(concat([], arguments, 0)); 317 | } 318 | } 319 | }; 320 | 321 | /** 322 | * @ngdoc method 323 | * @name $log#reset 324 | * 325 | * @description 326 | * Reset all of the logging arrays to empty. 327 | */ 328 | $log.reset = function () { 329 | /** 330 | * @ngdoc property 331 | * @name $log#log.logs 332 | * 333 | * @description 334 | * Array of messages logged using {@link ngMock.$log#log}. 335 | * 336 | * @example 337 | * ```js 338 | * $log.log('Some Log'); 339 | * var first = $log.log.logs.unshift(); 340 | * ``` 341 | */ 342 | $log.log.logs = []; 343 | /** 344 | * @ngdoc property 345 | * @name $log#info.logs 346 | * 347 | * @description 348 | * Array of messages logged using {@link ngMock.$log#info}. 349 | * 350 | * @example 351 | * ```js 352 | * $log.info('Some Info'); 353 | * var first = $log.info.logs.unshift(); 354 | * ``` 355 | */ 356 | $log.info.logs = []; 357 | /** 358 | * @ngdoc property 359 | * @name $log#warn.logs 360 | * 361 | * @description 362 | * Array of messages logged using {@link ngMock.$log#warn}. 363 | * 364 | * @example 365 | * ```js 366 | * $log.warn('Some Warning'); 367 | * var first = $log.warn.logs.unshift(); 368 | * ``` 369 | */ 370 | $log.warn.logs = []; 371 | /** 372 | * @ngdoc property 373 | * @name $log#error.logs 374 | * 375 | * @description 376 | * Array of messages logged using {@link ngMock.$log#error}. 377 | * 378 | * @example 379 | * ```js 380 | * $log.error('Some Error'); 381 | * var first = $log.error.logs.unshift(); 382 | * ``` 383 | */ 384 | $log.error.logs = []; 385 | /** 386 | * @ngdoc property 387 | * @name $log#debug.logs 388 | * 389 | * @description 390 | * Array of messages logged using {@link ngMock.$log#debug}. 391 | * 392 | * @example 393 | * ```js 394 | * $log.debug('Some Error'); 395 | * var first = $log.debug.logs.unshift(); 396 | * ``` 397 | */ 398 | $log.debug.logs = []; 399 | }; 400 | 401 | /** 402 | * @ngdoc method 403 | * @name $log#assertEmpty 404 | * 405 | * @description 406 | * Assert that the all of the logging methods have no logged messages. If messages present, an 407 | * exception is thrown. 408 | */ 409 | $log.assertEmpty = function() { 410 | var errors = []; 411 | angular.forEach(['error', 'warn', 'info', 'log', 'debug'], function(logLevel) { 412 | angular.forEach($log[logLevel].logs, function(log) { 413 | angular.forEach(log, function (logItem) { 414 | errors.push('MOCK $log (' + logLevel + '): ' + String(logItem) + '\n' + 415 | (logItem.stack || '')); 416 | }); 417 | }); 418 | }); 419 | if (errors.length) { 420 | errors.unshift("Expected $log to be empty! Either a message was logged unexpectedly, or "+ 421 | "an expected log message was not checked and removed:"); 422 | errors.push(''); 423 | throw new Error(errors.join('\n---------\n')); 424 | } 425 | }; 426 | 427 | $log.reset(); 428 | return $log; 429 | }; 430 | }; 431 | 432 | 433 | /** 434 | * @ngdoc service 435 | * @name $interval 436 | * 437 | * @description 438 | * Mock implementation of the $interval service. 439 | * 440 | * Use {@link ngMock.$interval#flush `$interval.flush(millis)`} to 441 | * move forward by `millis` milliseconds and trigger any functions scheduled to run in that 442 | * time. 443 | * 444 | * @param {function()} fn A function that should be called repeatedly. 445 | * @param {number} delay Number of milliseconds between each function call. 446 | * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat 447 | * indefinitely. 448 | * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise 449 | * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block. 450 | * @returns {promise} A promise which will be notified on each iteration. 451 | */ 452 | angular.mock.$IntervalProvider = function() { 453 | this.$get = ['$rootScope', '$q', 454 | function($rootScope, $q) { 455 | var repeatFns = [], 456 | nextRepeatId = 0, 457 | now = 0; 458 | 459 | var $interval = function(fn, delay, count, invokeApply) { 460 | var deferred = $q.defer(), 461 | promise = deferred.promise, 462 | iteration = 0, 463 | skipApply = (angular.isDefined(invokeApply) && !invokeApply); 464 | 465 | count = (angular.isDefined(count)) ? count : 0; 466 | promise.then(null, null, fn); 467 | 468 | promise.$$intervalId = nextRepeatId; 469 | 470 | function tick() { 471 | deferred.notify(iteration++); 472 | 473 | if (count > 0 && iteration >= count) { 474 | var fnIndex; 475 | deferred.resolve(iteration); 476 | 477 | angular.forEach(repeatFns, function(fn, index) { 478 | if (fn.id === promise.$$intervalId) fnIndex = index; 479 | }); 480 | 481 | if (fnIndex !== undefined) { 482 | repeatFns.splice(fnIndex, 1); 483 | } 484 | } 485 | 486 | if (!skipApply) $rootScope.$apply(); 487 | } 488 | 489 | repeatFns.push({ 490 | nextTime:(now + delay), 491 | delay: delay, 492 | fn: tick, 493 | id: nextRepeatId, 494 | deferred: deferred 495 | }); 496 | repeatFns.sort(function(a,b){ return a.nextTime - b.nextTime;}); 497 | 498 | nextRepeatId++; 499 | return promise; 500 | }; 501 | /** 502 | * @ngdoc method 503 | * @name $interval#cancel 504 | * 505 | * @description 506 | * Cancels a task associated with the `promise`. 507 | * 508 | * @param {promise} promise A promise from calling the `$interval` function. 509 | * @returns {boolean} Returns `true` if the task was successfully cancelled. 510 | */ 511 | $interval.cancel = function(promise) { 512 | if(!promise) return false; 513 | var fnIndex; 514 | 515 | angular.forEach(repeatFns, function(fn, index) { 516 | if (fn.id === promise.$$intervalId) fnIndex = index; 517 | }); 518 | 519 | if (fnIndex !== undefined) { 520 | repeatFns[fnIndex].deferred.reject('canceled'); 521 | repeatFns.splice(fnIndex, 1); 522 | return true; 523 | } 524 | 525 | return false; 526 | }; 527 | 528 | /** 529 | * @ngdoc method 530 | * @name $interval#flush 531 | * @description 532 | * 533 | * Runs interval tasks scheduled to be run in the next `millis` milliseconds. 534 | * 535 | * @param {number=} millis maximum timeout amount to flush up until. 536 | * 537 | * @return {number} The amount of time moved forward. 538 | */ 539 | $interval.flush = function(millis) { 540 | now += millis; 541 | while (repeatFns.length && repeatFns[0].nextTime <= now) { 542 | var task = repeatFns[0]; 543 | task.fn(); 544 | task.nextTime += task.delay; 545 | repeatFns.sort(function(a,b){ return a.nextTime - b.nextTime;}); 546 | } 547 | return millis; 548 | }; 549 | 550 | return $interval; 551 | }]; 552 | }; 553 | 554 | 555 | /* jshint -W101 */ 556 | /* The R_ISO8061_STR regex is never going to fit into the 100 char limit! 557 | * This directive should go inside the anonymous function but a bug in JSHint means that it would 558 | * not be enacted early enough to prevent the warning. 559 | */ 560 | var R_ISO8061_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?:\:?(\d\d)(?:\:?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/; 561 | 562 | function jsonStringToDate(string) { 563 | var match; 564 | if (match = string.match(R_ISO8061_STR)) { 565 | var date = new Date(0), 566 | tzHour = 0, 567 | tzMin = 0; 568 | if (match[9]) { 569 | tzHour = int(match[9] + match[10]); 570 | tzMin = int(match[9] + match[11]); 571 | } 572 | date.setUTCFullYear(int(match[1]), int(match[2]) - 1, int(match[3])); 573 | date.setUTCHours(int(match[4]||0) - tzHour, 574 | int(match[5]||0) - tzMin, 575 | int(match[6]||0), 576 | int(match[7]||0)); 577 | return date; 578 | } 579 | return string; 580 | } 581 | 582 | function int(str) { 583 | return parseInt(str, 10); 584 | } 585 | 586 | function padNumber(num, digits, trim) { 587 | var neg = ''; 588 | if (num < 0) { 589 | neg = '-'; 590 | num = -num; 591 | } 592 | num = '' + num; 593 | while(num.length < digits) num = '0' + num; 594 | if (trim) 595 | num = num.substr(num.length - digits); 596 | return neg + num; 597 | } 598 | 599 | 600 | /** 601 | * @ngdoc type 602 | * @name angular.mock.TzDate 603 | * @description 604 | * 605 | * *NOTE*: this is not an injectable instance, just a globally available mock class of `Date`. 606 | * 607 | * Mock of the Date type which has its timezone specified via constructor arg. 608 | * 609 | * The main purpose is to create Date-like instances with timezone fixed to the specified timezone 610 | * offset, so that we can test code that depends on local timezone settings without dependency on 611 | * the time zone settings of the machine where the code is running. 612 | * 613 | * @param {number} offset Offset of the *desired* timezone in hours (fractions will be honored) 614 | * @param {(number|string)} timestamp Timestamp representing the desired time in *UTC* 615 | * 616 | * @example 617 | * !!!! WARNING !!!!! 618 | * This is not a complete Date object so only methods that were implemented can be called safely. 619 | * To make matters worse, TzDate instances inherit stuff from Date via a prototype. 620 | * 621 | * We do our best to intercept calls to "unimplemented" methods, but since the list of methods is 622 | * incomplete we might be missing some non-standard methods. This can result in errors like: 623 | * "Date.prototype.foo called on incompatible Object". 624 | * 625 | * ```js 626 | * var newYearInBratislava = new TzDate(-1, '2009-12-31T23:00:00Z'); 627 | * newYearInBratislava.getTimezoneOffset() => -60; 628 | * newYearInBratislava.getFullYear() => 2010; 629 | * newYearInBratislava.getMonth() => 0; 630 | * newYearInBratislava.getDate() => 1; 631 | * newYearInBratislava.getHours() => 0; 632 | * newYearInBratislava.getMinutes() => 0; 633 | * newYearInBratislava.getSeconds() => 0; 634 | * ``` 635 | * 636 | */ 637 | angular.mock.TzDate = function (offset, timestamp) { 638 | var self = new Date(0); 639 | if (angular.isString(timestamp)) { 640 | var tsStr = timestamp; 641 | 642 | self.origDate = jsonStringToDate(timestamp); 643 | 644 | timestamp = self.origDate.getTime(); 645 | if (isNaN(timestamp)) 646 | throw { 647 | name: "Illegal Argument", 648 | message: "Arg '" + tsStr + "' passed into TzDate constructor is not a valid date string" 649 | }; 650 | } else { 651 | self.origDate = new Date(timestamp); 652 | } 653 | 654 | var localOffset = new Date(timestamp).getTimezoneOffset(); 655 | self.offsetDiff = localOffset*60*1000 - offset*1000*60*60; 656 | self.date = new Date(timestamp + self.offsetDiff); 657 | 658 | self.getTime = function() { 659 | return self.date.getTime() - self.offsetDiff; 660 | }; 661 | 662 | self.toLocaleDateString = function() { 663 | return self.date.toLocaleDateString(); 664 | }; 665 | 666 | self.getFullYear = function() { 667 | return self.date.getFullYear(); 668 | }; 669 | 670 | self.getMonth = function() { 671 | return self.date.getMonth(); 672 | }; 673 | 674 | self.getDate = function() { 675 | return self.date.getDate(); 676 | }; 677 | 678 | self.getHours = function() { 679 | return self.date.getHours(); 680 | }; 681 | 682 | self.getMinutes = function() { 683 | return self.date.getMinutes(); 684 | }; 685 | 686 | self.getSeconds = function() { 687 | return self.date.getSeconds(); 688 | }; 689 | 690 | self.getMilliseconds = function() { 691 | return self.date.getMilliseconds(); 692 | }; 693 | 694 | self.getTimezoneOffset = function() { 695 | return offset * 60; 696 | }; 697 | 698 | self.getUTCFullYear = function() { 699 | return self.origDate.getUTCFullYear(); 700 | }; 701 | 702 | self.getUTCMonth = function() { 703 | return self.origDate.getUTCMonth(); 704 | }; 705 | 706 | self.getUTCDate = function() { 707 | return self.origDate.getUTCDate(); 708 | }; 709 | 710 | self.getUTCHours = function() { 711 | return self.origDate.getUTCHours(); 712 | }; 713 | 714 | self.getUTCMinutes = function() { 715 | return self.origDate.getUTCMinutes(); 716 | }; 717 | 718 | self.getUTCSeconds = function() { 719 | return self.origDate.getUTCSeconds(); 720 | }; 721 | 722 | self.getUTCMilliseconds = function() { 723 | return self.origDate.getUTCMilliseconds(); 724 | }; 725 | 726 | self.getDay = function() { 727 | return self.date.getDay(); 728 | }; 729 | 730 | // provide this method only on browsers that already have it 731 | if (self.toISOString) { 732 | self.toISOString = function() { 733 | return padNumber(self.origDate.getUTCFullYear(), 4) + '-' + 734 | padNumber(self.origDate.getUTCMonth() + 1, 2) + '-' + 735 | padNumber(self.origDate.getUTCDate(), 2) + 'T' + 736 | padNumber(self.origDate.getUTCHours(), 2) + ':' + 737 | padNumber(self.origDate.getUTCMinutes(), 2) + ':' + 738 | padNumber(self.origDate.getUTCSeconds(), 2) + '.' + 739 | padNumber(self.origDate.getUTCMilliseconds(), 3) + 'Z'; 740 | }; 741 | } 742 | 743 | //hide all methods not implemented in this mock that the Date prototype exposes 744 | var unimplementedMethods = ['getUTCDay', 745 | 'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds', 746 | 'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear', 747 | 'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds', 748 | 'setYear', 'toDateString', 'toGMTString', 'toJSON', 'toLocaleFormat', 'toLocaleString', 749 | 'toLocaleTimeString', 'toSource', 'toString', 'toTimeString', 'toUTCString', 'valueOf']; 750 | 751 | angular.forEach(unimplementedMethods, function(methodName) { 752 | self[methodName] = function() { 753 | throw new Error("Method '" + methodName + "' is not implemented in the TzDate mock"); 754 | }; 755 | }); 756 | 757 | return self; 758 | }; 759 | 760 | //make "tzDateInstance instanceof Date" return true 761 | angular.mock.TzDate.prototype = Date.prototype; 762 | /* jshint +W101 */ 763 | 764 | angular.mock.animate = angular.module('ngAnimateMock', ['ng']) 765 | 766 | .config(['$provide', function($provide) { 767 | 768 | var reflowQueue = []; 769 | $provide.value('$$animateReflow', function(fn) { 770 | var index = reflowQueue.length; 771 | reflowQueue.push(fn); 772 | return function cancel() { 773 | reflowQueue.splice(index, 1); 774 | }; 775 | }); 776 | 777 | $provide.decorator('$animate', function($delegate, $$asyncCallback) { 778 | var animate = { 779 | queue : [], 780 | enabled : $delegate.enabled, 781 | triggerCallbacks : function() { 782 | $$asyncCallback.flush(); 783 | }, 784 | triggerReflow : function() { 785 | angular.forEach(reflowQueue, function(fn) { 786 | fn(); 787 | }); 788 | reflowQueue = []; 789 | } 790 | }; 791 | 792 | angular.forEach( 793 | ['enter','leave','move','addClass','removeClass','setClass'], function(method) { 794 | animate[method] = function() { 795 | animate.queue.push({ 796 | event : method, 797 | element : arguments[0], 798 | args : arguments 799 | }); 800 | $delegate[method].apply($delegate, arguments); 801 | }; 802 | }); 803 | 804 | return animate; 805 | }); 806 | 807 | }]); 808 | 809 | 810 | /** 811 | * @ngdoc function 812 | * @name angular.mock.dump 813 | * @description 814 | * 815 | * *NOTE*: this is not an injectable instance, just a globally available function. 816 | * 817 | * Method for serializing common angular objects (scope, elements, etc..) into strings, useful for 818 | * debugging. 819 | * 820 | * This method is also available on window, where it can be used to display objects on debug 821 | * console. 822 | * 823 | * @param {*} object - any object to turn into string. 824 | * @return {string} a serialized string of the argument 825 | */ 826 | angular.mock.dump = function(object) { 827 | return serialize(object); 828 | 829 | function serialize(object) { 830 | var out; 831 | 832 | if (angular.isElement(object)) { 833 | object = angular.element(object); 834 | out = angular.element('
'); 835 | angular.forEach(object, function(element) { 836 | out.append(angular.element(element).clone()); 837 | }); 838 | out = out.html(); 839 | } else if (angular.isArray(object)) { 840 | out = []; 841 | angular.forEach(object, function(o) { 842 | out.push(serialize(o)); 843 | }); 844 | out = '[ ' + out.join(', ') + ' ]'; 845 | } else if (angular.isObject(object)) { 846 | if (angular.isFunction(object.$eval) && angular.isFunction(object.$apply)) { 847 | out = serializeScope(object); 848 | } else if (object instanceof Error) { 849 | out = object.stack || ('' + object.name + ': ' + object.message); 850 | } else { 851 | // TODO(i): this prevents methods being logged, 852 | // we should have a better way to serialize objects 853 | out = angular.toJson(object, true); 854 | } 855 | } else { 856 | out = String(object); 857 | } 858 | 859 | return out; 860 | } 861 | 862 | function serializeScope(scope, offset) { 863 | offset = offset || ' '; 864 | var log = [offset + 'Scope(' + scope.$id + '): {']; 865 | for ( var key in scope ) { 866 | if (Object.prototype.hasOwnProperty.call(scope, key) && !key.match(/^(\$|this)/)) { 867 | log.push(' ' + key + ': ' + angular.toJson(scope[key])); 868 | } 869 | } 870 | var child = scope.$$childHead; 871 | while(child) { 872 | log.push(serializeScope(child, offset + ' ')); 873 | child = child.$$nextSibling; 874 | } 875 | log.push('}'); 876 | return log.join('\n' + offset); 877 | } 878 | }; 879 | 880 | /** 881 | * @ngdoc service 882 | * @name $httpBackend 883 | * @description 884 | * Fake HTTP backend implementation suitable for unit testing applications that use the 885 | * {@link ng.$http $http service}. 886 | * 887 | * *Note*: For fake HTTP backend implementation suitable for end-to-end testing or backend-less 888 | * development please see {@link ngMockE2E.$httpBackend e2e $httpBackend mock}. 889 | * 890 | * During unit testing, we want our unit tests to run quickly and have no external dependencies so 891 | * we don’t want to send [XHR](https://developer.mozilla.org/en/xmlhttprequest) or 892 | * [JSONP](http://en.wikipedia.org/wiki/JSONP) requests to a real server. All we really need is 893 | * to verify whether a certain request has been sent or not, or alternatively just let the 894 | * application make requests, respond with pre-trained responses and assert that the end result is 895 | * what we expect it to be. 896 | * 897 | * This mock implementation can be used to respond with static or dynamic responses via the 898 | * `expect` and `when` apis and their shortcuts (`expectGET`, `whenPOST`, etc). 899 | * 900 | * When an Angular application needs some data from a server, it calls the $http service, which 901 | * sends the request to a real server using $httpBackend service. With dependency injection, it is 902 | * easy to inject $httpBackend mock (which has the same API as $httpBackend) and use it to verify 903 | * the requests and respond with some testing data without sending a request to a real server. 904 | * 905 | * There are two ways to specify what test data should be returned as http responses by the mock 906 | * backend when the code under test makes http requests: 907 | * 908 | * - `$httpBackend.expect` - specifies a request expectation 909 | * - `$httpBackend.when` - specifies a backend definition 910 | * 911 | * 912 | * # Request Expectations vs Backend Definitions 913 | * 914 | * Request expectations provide a way to make assertions about requests made by the application and 915 | * to define responses for those requests. The test will fail if the expected requests are not made 916 | * or they are made in the wrong order. 917 | * 918 | * Backend definitions allow you to define a fake backend for your application which doesn't assert 919 | * if a particular request was made or not, it just returns a trained response if a request is made. 920 | * The test will pass whether or not the request gets made during testing. 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 | * 955 | *
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
956 | * 957 | * In cases where both backend definitions and request expectations are specified during unit 958 | * testing, the request expectations are evaluated first. 959 | * 960 | * If a request expectation has no response specified, the algorithm will search your backend 961 | * definitions for an appropriate response. 962 | * 963 | * If a request didn't match any expectation or if the expectation doesn't have the response 964 | * defined, the backend definitions are evaluated in sequential order to see if any of them match 965 | * the request. The response from the first matched definition is returned. 966 | * 967 | * 968 | * # Flushing HTTP requests 969 | * 970 | * The $httpBackend used in production always responds to requests asynchronously. If we preserved 971 | * this behavior in unit testing, we'd have to create async unit tests, which are hard to write, 972 | * to follow and to maintain. But neither can the testing mock respond synchronously; that would 973 | * change the execution of the code under test. For this reason, the mock $httpBackend has a 974 | * `flush()` method, which allows the test to explicitly flush pending requests. This preserves 975 | * the async api of the backend, while allowing the test to execute synchronously. 976 | * 977 | * 978 | * # Unit testing with mock $httpBackend 979 | * The following code shows how to setup and use the mock backend when unit testing a controller. 980 | * First we create the controller under test: 981 | * 982 | ```js 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 | ```js 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 | copy = angular.copy; 1099 | 1100 | function createResponse(status, data, headers, statusText) { 1101 | if (angular.isFunction(status)) return status; 1102 | 1103 | return function() { 1104 | return angular.isNumber(status) 1105 | ? [status, data, headers, statusText] 1106 | : [200, status, data]; 1107 | }; 1108 | } 1109 | 1110 | // TODO(vojta): change params to: method, url, data, headers, callback 1111 | function $httpBackend(method, url, data, callback, headers, timeout, withCredentials) { 1112 | var xhr = new MockXhr(), 1113 | expectation = expectations[0], 1114 | wasExpected = false; 1115 | 1116 | function prettyPrint(data) { 1117 | return (angular.isString(data) || angular.isFunction(data) || data instanceof RegExp) 1118 | ? data 1119 | : angular.toJson(data); 1120 | } 1121 | 1122 | function wrapResponse(wrapped) { 1123 | if (!$browser && timeout && timeout.then) timeout.then(handleTimeout); 1124 | 1125 | return handleResponse; 1126 | 1127 | function handleResponse() { 1128 | var response = wrapped.response(method, url, data, headers); 1129 | xhr.$$respHeaders = response[2]; 1130 | callback(copy(response[0]), copy(response[1]), xhr.getAllResponseHeaders(), 1131 | copy(response[3] || '')); 1132 | } 1133 | 1134 | function handleTimeout() { 1135 | for (var i = 0, ii = responses.length; i < ii; i++) { 1136 | if (responses[i] === handleResponse) { 1137 | responses.splice(i, 1); 1138 | callback(-1, undefined, ''); 1139 | break; 1140 | } 1141 | } 1142 | } 1143 | } 1144 | 1145 | if (expectation && expectation.match(method, url)) { 1146 | if (!expectation.matchData(data)) 1147 | throw new Error('Expected ' + expectation + ' with different data\n' + 1148 | 'EXPECTED: ' + prettyPrint(expectation.data) + '\nGOT: ' + data); 1149 | 1150 | if (!expectation.matchHeaders(headers)) 1151 | throw new Error('Expected ' + expectation + ' with different headers\n' + 1152 | 'EXPECTED: ' + prettyPrint(expectation.headers) + '\nGOT: ' + 1153 | prettyPrint(headers)); 1154 | 1155 | expectations.shift(); 1156 | 1157 | if (expectation.response) { 1158 | responses.push(wrapResponse(expectation)); 1159 | return; 1160 | } 1161 | wasExpected = true; 1162 | } 1163 | 1164 | var i = -1, definition; 1165 | while ((definition = definitions[++i])) { 1166 | if (definition.match(method, url, data, headers || {})) { 1167 | if (definition.response) { 1168 | // if $browser specified, we do auto flush all requests 1169 | ($browser ? $browser.defer : responsesPush)(wrapResponse(definition)); 1170 | } else if (definition.passThrough) { 1171 | $delegate(method, url, data, callback, headers, timeout, withCredentials); 1172 | } else throw new Error('No response defined !'); 1173 | return; 1174 | } 1175 | } 1176 | throw wasExpected ? 1177 | new Error('No response defined !') : 1178 | new Error('Unexpected request: ' + method + ' ' + url + '\n' + 1179 | (expectation ? 'Expected ' + expectation : 'No more request expected')); 1180 | } 1181 | 1182 | /** 1183 | * @ngdoc method 1184 | * @name $httpBackend#when 1185 | * @description 1186 | * Creates a new backend definition. 1187 | * 1188 | * @param {string} method HTTP method. 1189 | * @param {string|RegExp} url HTTP url. 1190 | * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives 1191 | * data string and returns true if the data is as expected. 1192 | * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header 1193 | * object and returns true if the headers match the current definition. 1194 | * @returns {requestHandler} Returns an object with `respond` method that controls how a matched 1195 | * request is handled. 1196 | * 1197 | * - respond – 1198 | * `{function([status,] data[, headers, statusText]) 1199 | * | function(function(method, url, data, headers)}` 1200 | * – The respond method takes a set of static data to be returned or a function that can 1201 | * return an array containing response status (number), response data (string), response 1202 | * headers (Object), and the text for the status (string). 1203 | */ 1204 | $httpBackend.when = function(method, url, data, headers) { 1205 | var definition = new MockHttpExpectation(method, url, data, headers), 1206 | chain = { 1207 | respond: function(status, data, headers, statusText) { 1208 | definition.response = createResponse(status, data, headers, statusText); 1209 | } 1210 | }; 1211 | 1212 | if ($browser) { 1213 | chain.passThrough = function() { 1214 | definition.passThrough = true; 1215 | }; 1216 | } 1217 | 1218 | definitions.push(definition); 1219 | return chain; 1220 | }; 1221 | 1222 | /** 1223 | * @ngdoc method 1224 | * @name $httpBackend#whenGET 1225 | * @description 1226 | * Creates a new backend definition for GET requests. For more info see `when()`. 1227 | * 1228 | * @param {string|RegExp} url HTTP url. 1229 | * @param {(Object|function(Object))=} headers HTTP headers. 1230 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 1231 | * request is handled. 1232 | */ 1233 | 1234 | /** 1235 | * @ngdoc method 1236 | * @name $httpBackend#whenHEAD 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 $httpBackend#whenDELETE 1249 | * @description 1250 | * Creates a new backend definition for DELETE requests. For more info see `when()`. 1251 | * 1252 | * @param {string|RegExp} url HTTP url. 1253 | * @param {(Object|function(Object))=} headers HTTP headers. 1254 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 1255 | * request is handled. 1256 | */ 1257 | 1258 | /** 1259 | * @ngdoc method 1260 | * @name $httpBackend#whenPOST 1261 | * @description 1262 | * Creates a new backend definition for POST requests. For more info see `when()`. 1263 | * 1264 | * @param {string|RegExp} url HTTP url. 1265 | * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives 1266 | * data string and returns true if the data is as expected. 1267 | * @param {(Object|function(Object))=} headers HTTP headers. 1268 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 1269 | * request is handled. 1270 | */ 1271 | 1272 | /** 1273 | * @ngdoc method 1274 | * @name $httpBackend#whenPUT 1275 | * @description 1276 | * Creates a new backend definition for PUT requests. For more info see `when()`. 1277 | * 1278 | * @param {string|RegExp} url HTTP url. 1279 | * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives 1280 | * data string and returns true if the data is as expected. 1281 | * @param {(Object|function(Object))=} headers HTTP headers. 1282 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 1283 | * request is handled. 1284 | */ 1285 | 1286 | /** 1287 | * @ngdoc method 1288 | * @name $httpBackend#whenJSONP 1289 | * @description 1290 | * Creates a new backend definition for JSONP requests. For more info see `when()`. 1291 | * 1292 | * @param {string|RegExp} url HTTP url. 1293 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 1294 | * request is handled. 1295 | */ 1296 | createShortMethods('when'); 1297 | 1298 | 1299 | /** 1300 | * @ngdoc method 1301 | * @name $httpBackend#expect 1302 | * @description 1303 | * Creates a new request expectation. 1304 | * 1305 | * @param {string} method HTTP method. 1306 | * @param {string|RegExp} url HTTP url. 1307 | * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that 1308 | * receives data string and returns true if the data is as expected, or Object if request body 1309 | * is in JSON format. 1310 | * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header 1311 | * object and returns true if the headers match the current expectation. 1312 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 1313 | * request is handled. 1314 | * 1315 | * - respond – 1316 | * `{function([status,] data[, headers, statusText]) 1317 | * | function(function(method, url, data, headers)}` 1318 | * – The respond method takes a set of static data to be returned or a function that can 1319 | * return an array containing response status (number), response data (string), response 1320 | * headers (Object), and the text for the status (string). 1321 | */ 1322 | $httpBackend.expect = function(method, url, data, headers) { 1323 | var expectation = new MockHttpExpectation(method, url, data, headers); 1324 | expectations.push(expectation); 1325 | return { 1326 | respond: function (status, data, headers, statusText) { 1327 | expectation.response = createResponse(status, data, headers, statusText); 1328 | } 1329 | }; 1330 | }; 1331 | 1332 | 1333 | /** 1334 | * @ngdoc method 1335 | * @name $httpBackend#expectGET 1336 | * @description 1337 | * Creates a new request expectation for GET requests. For more info see `expect()`. 1338 | * 1339 | * @param {string|RegExp} url HTTP url. 1340 | * @param {Object=} headers HTTP headers. 1341 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 1342 | * request is handled. See #expect for more info. 1343 | */ 1344 | 1345 | /** 1346 | * @ngdoc method 1347 | * @name $httpBackend#expectHEAD 1348 | * @description 1349 | * Creates a new request expectation for HEAD requests. For more info see `expect()`. 1350 | * 1351 | * @param {string|RegExp} url HTTP url. 1352 | * @param {Object=} headers HTTP headers. 1353 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 1354 | * request is handled. 1355 | */ 1356 | 1357 | /** 1358 | * @ngdoc method 1359 | * @name $httpBackend#expectDELETE 1360 | * @description 1361 | * Creates a new request expectation for DELETE requests. For more info see `expect()`. 1362 | * 1363 | * @param {string|RegExp} url HTTP url. 1364 | * @param {Object=} headers HTTP headers. 1365 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 1366 | * request is handled. 1367 | */ 1368 | 1369 | /** 1370 | * @ngdoc method 1371 | * @name $httpBackend#expectPOST 1372 | * @description 1373 | * Creates a new request expectation for POST requests. For more info see `expect()`. 1374 | * 1375 | * @param {string|RegExp} url HTTP url. 1376 | * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that 1377 | * receives data string and returns true if the data is as expected, or Object if request body 1378 | * is in JSON format. 1379 | * @param {Object=} headers HTTP headers. 1380 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 1381 | * request is handled. 1382 | */ 1383 | 1384 | /** 1385 | * @ngdoc method 1386 | * @name $httpBackend#expectPUT 1387 | * @description 1388 | * Creates a new request expectation for PUT requests. For more info see `expect()`. 1389 | * 1390 | * @param {string|RegExp} url HTTP url. 1391 | * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that 1392 | * receives data string and returns true if the data is as expected, or Object if request body 1393 | * is in JSON format. 1394 | * @param {Object=} headers HTTP headers. 1395 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 1396 | * request is handled. 1397 | */ 1398 | 1399 | /** 1400 | * @ngdoc method 1401 | * @name $httpBackend#expectPATCH 1402 | * @description 1403 | * Creates a new request expectation for PATCH requests. For more info see `expect()`. 1404 | * 1405 | * @param {string|RegExp} url HTTP url. 1406 | * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that 1407 | * receives data string and returns true if the data is as expected, or Object if request body 1408 | * is in JSON format. 1409 | * @param {Object=} headers HTTP headers. 1410 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 1411 | * request is handled. 1412 | */ 1413 | 1414 | /** 1415 | * @ngdoc method 1416 | * @name $httpBackend#expectJSONP 1417 | * @description 1418 | * Creates a new request expectation for JSONP requests. For more info see `expect()`. 1419 | * 1420 | * @param {string|RegExp} url HTTP url. 1421 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 1422 | * request is handled. 1423 | */ 1424 | createShortMethods('expect'); 1425 | 1426 | 1427 | /** 1428 | * @ngdoc method 1429 | * @name $httpBackend#flush 1430 | * @description 1431 | * Flushes all pending requests using the trained responses. 1432 | * 1433 | * @param {number=} count Number of responses to flush (in the order they arrived). If undefined, 1434 | * all pending requests will be flushed. If there are no pending requests when the flush method 1435 | * is called an exception is thrown (as this typically a sign of programming error). 1436 | */ 1437 | $httpBackend.flush = function(count) { 1438 | $rootScope.$digest(); 1439 | if (!responses.length) throw new Error('No pending request to flush !'); 1440 | 1441 | if (angular.isDefined(count)) { 1442 | while (count--) { 1443 | if (!responses.length) throw new Error('No more pending request to flush !'); 1444 | responses.shift()(); 1445 | } 1446 | } else { 1447 | while (responses.length) { 1448 | responses.shift()(); 1449 | } 1450 | } 1451 | $httpBackend.verifyNoOutstandingExpectation(); 1452 | }; 1453 | 1454 | 1455 | /** 1456 | * @ngdoc method 1457 | * @name $httpBackend#verifyNoOutstandingExpectation 1458 | * @description 1459 | * Verifies that all of the requests defined via the `expect` api were made. If any of the 1460 | * requests were not made, verifyNoOutstandingExpectation throws an exception. 1461 | * 1462 | * Typically, you would call this method following each test case that asserts requests using an 1463 | * "afterEach" clause. 1464 | * 1465 | * ```js 1466 | * afterEach($httpBackend.verifyNoOutstandingExpectation); 1467 | * ``` 1468 | */ 1469 | $httpBackend.verifyNoOutstandingExpectation = function() { 1470 | $rootScope.$digest(); 1471 | if (expectations.length) { 1472 | throw new Error('Unsatisfied requests: ' + expectations.join(', ')); 1473 | } 1474 | }; 1475 | 1476 | 1477 | /** 1478 | * @ngdoc method 1479 | * @name $httpBackend#verifyNoOutstandingRequest 1480 | * @description 1481 | * Verifies that there are no outstanding requests that need to be flushed. 1482 | * 1483 | * Typically, you would call this method following each test case that asserts requests using an 1484 | * "afterEach" clause. 1485 | * 1486 | * ```js 1487 | * afterEach($httpBackend.verifyNoOutstandingRequest); 1488 | * ``` 1489 | */ 1490 | $httpBackend.verifyNoOutstandingRequest = function() { 1491 | if (responses.length) { 1492 | throw new Error('Unflushed requests: ' + responses.length); 1493 | } 1494 | }; 1495 | 1496 | 1497 | /** 1498 | * @ngdoc method 1499 | * @name $httpBackend#resetExpectations 1500 | * @description 1501 | * Resets all request expectations, but preserves all backend definitions. Typically, you would 1502 | * call resetExpectations during a multiple-phase test when you want to reuse the same instance of 1503 | * $httpBackend mock. 1504 | */ 1505 | $httpBackend.resetExpectations = function() { 1506 | expectations.length = 0; 1507 | responses.length = 0; 1508 | }; 1509 | 1510 | return $httpBackend; 1511 | 1512 | 1513 | function createShortMethods(prefix) { 1514 | angular.forEach(['GET', 'DELETE', 'JSONP'], function(method) { 1515 | $httpBackend[prefix + method] = function(url, headers) { 1516 | return $httpBackend[prefix](method, url, undefined, headers); 1517 | }; 1518 | }); 1519 | 1520 | angular.forEach(['PUT', 'POST', 'PATCH'], function(method) { 1521 | $httpBackend[prefix + method] = function(url, data, headers) { 1522 | return $httpBackend[prefix](method, url, data, headers); 1523 | }; 1524 | }); 1525 | } 1526 | } 1527 | 1528 | function MockHttpExpectation(method, url, data, headers) { 1529 | 1530 | this.data = data; 1531 | this.headers = headers; 1532 | 1533 | this.match = function(m, u, d, h) { 1534 | if (method != m) return false; 1535 | if (!this.matchUrl(u)) return false; 1536 | if (angular.isDefined(d) && !this.matchData(d)) return false; 1537 | if (angular.isDefined(h) && !this.matchHeaders(h)) return false; 1538 | return true; 1539 | }; 1540 | 1541 | this.matchUrl = function(u) { 1542 | if (!url) return true; 1543 | if (angular.isFunction(url.test)) return url.test(u); 1544 | return url == u; 1545 | }; 1546 | 1547 | this.matchHeaders = function(h) { 1548 | if (angular.isUndefined(headers)) return true; 1549 | if (angular.isFunction(headers)) return headers(h); 1550 | return angular.equals(headers, h); 1551 | }; 1552 | 1553 | this.matchData = function(d) { 1554 | if (angular.isUndefined(data)) return true; 1555 | if (data && angular.isFunction(data.test)) return data.test(d); 1556 | if (data && angular.isFunction(data)) return data(d); 1557 | if (data && !angular.isString(data)) return angular.equals(data, angular.fromJson(d)); 1558 | return data == d; 1559 | }; 1560 | 1561 | this.toString = function() { 1562 | return method + ' ' + url; 1563 | }; 1564 | } 1565 | 1566 | function createMockXhr() { 1567 | return new MockXhr(); 1568 | } 1569 | 1570 | function MockXhr() { 1571 | 1572 | // hack for testing $http, $httpBackend 1573 | MockXhr.$$lastInstance = this; 1574 | 1575 | this.open = function(method, url, async) { 1576 | this.$$method = method; 1577 | this.$$url = url; 1578 | this.$$async = async; 1579 | this.$$reqHeaders = {}; 1580 | this.$$respHeaders = {}; 1581 | }; 1582 | 1583 | this.send = function(data) { 1584 | this.$$data = data; 1585 | }; 1586 | 1587 | this.setRequestHeader = function(key, value) { 1588 | this.$$reqHeaders[key] = value; 1589 | }; 1590 | 1591 | this.getResponseHeader = function(name) { 1592 | // the lookup must be case insensitive, 1593 | // that's why we try two quick lookups first and full scan last 1594 | var header = this.$$respHeaders[name]; 1595 | if (header) return header; 1596 | 1597 | name = angular.lowercase(name); 1598 | header = this.$$respHeaders[name]; 1599 | if (header) return header; 1600 | 1601 | header = undefined; 1602 | angular.forEach(this.$$respHeaders, function(headerVal, headerName) { 1603 | if (!header && angular.lowercase(headerName) == name) header = headerVal; 1604 | }); 1605 | return header; 1606 | }; 1607 | 1608 | this.getAllResponseHeaders = function() { 1609 | var lines = []; 1610 | 1611 | angular.forEach(this.$$respHeaders, function(value, key) { 1612 | lines.push(key + ': ' + value); 1613 | }); 1614 | return lines.join('\n'); 1615 | }; 1616 | 1617 | this.abort = angular.noop; 1618 | } 1619 | 1620 | 1621 | /** 1622 | * @ngdoc service 1623 | * @name $timeout 1624 | * @description 1625 | * 1626 | * This service is just a simple decorator for {@link ng.$timeout $timeout} service 1627 | * that adds a "flush" and "verifyNoPendingTasks" methods. 1628 | */ 1629 | 1630 | angular.mock.$TimeoutDecorator = function($delegate, $browser) { 1631 | 1632 | /** 1633 | * @ngdoc method 1634 | * @name $timeout#flush 1635 | * @description 1636 | * 1637 | * Flushes the queue of pending tasks. 1638 | * 1639 | * @param {number=} delay maximum timeout amount to flush up until 1640 | */ 1641 | $delegate.flush = function(delay) { 1642 | $browser.defer.flush(delay); 1643 | }; 1644 | 1645 | /** 1646 | * @ngdoc method 1647 | * @name $timeout#verifyNoPendingTasks 1648 | * @description 1649 | * 1650 | * Verifies that there are no pending tasks that need to be flushed. 1651 | */ 1652 | $delegate.verifyNoPendingTasks = function() { 1653 | if ($browser.deferredFns.length) { 1654 | throw new Error('Deferred tasks to flush (' + $browser.deferredFns.length + '): ' + 1655 | formatPendingTasksAsString($browser.deferredFns)); 1656 | } 1657 | }; 1658 | 1659 | function formatPendingTasksAsString(tasks) { 1660 | var result = []; 1661 | angular.forEach(tasks, function(task) { 1662 | result.push('{id: ' + task.id + ', ' + 'time: ' + task.time + '}'); 1663 | }); 1664 | 1665 | return result.join(', '); 1666 | } 1667 | 1668 | return $delegate; 1669 | }; 1670 | 1671 | angular.mock.$RAFDecorator = function($delegate) { 1672 | var queue = []; 1673 | var rafFn = function(fn) { 1674 | var index = queue.length; 1675 | queue.push(fn); 1676 | return function() { 1677 | queue.splice(index, 1); 1678 | }; 1679 | }; 1680 | 1681 | rafFn.supported = $delegate.supported; 1682 | 1683 | rafFn.flush = function() { 1684 | if(queue.length === 0) { 1685 | throw new Error('No rAF callbacks present'); 1686 | } 1687 | 1688 | var length = queue.length; 1689 | for(var i=0;i
'); 1719 | }; 1720 | }; 1721 | 1722 | /** 1723 | * @ngdoc module 1724 | * @name ngMock 1725 | * @description 1726 | * 1727 | * # ngMock 1728 | * 1729 | * The `ngMock` module provides support to inject and mock Angular services into unit tests. 1730 | * In addition, ngMock also extends various core ng services such that they can be 1731 | * inspected and controlled in a synchronous manner within test code. 1732 | * 1733 | * 1734 | *
1735 | * 1736 | */ 1737 | angular.module('ngMock', ['ng']).provider({ 1738 | $browser: angular.mock.$BrowserProvider, 1739 | $exceptionHandler: angular.mock.$ExceptionHandlerProvider, 1740 | $log: angular.mock.$LogProvider, 1741 | $interval: angular.mock.$IntervalProvider, 1742 | $httpBackend: angular.mock.$HttpBackendProvider, 1743 | $rootElement: angular.mock.$RootElementProvider 1744 | }).config(['$provide', function($provide) { 1745 | $provide.decorator('$timeout', angular.mock.$TimeoutDecorator); 1746 | $provide.decorator('$$rAF', angular.mock.$RAFDecorator); 1747 | $provide.decorator('$$asyncCallback', angular.mock.$AsyncCallbackDecorator); 1748 | }]); 1749 | 1750 | /** 1751 | * @ngdoc module 1752 | * @name ngMockE2E 1753 | * @module ngMockE2E 1754 | * @description 1755 | * 1756 | * The `ngMockE2E` is an angular module which contains mocks suitable for end-to-end testing. 1757 | * Currently there is only one mock present in this module - 1758 | * the {@link ngMockE2E.$httpBackend e2e $httpBackend} mock. 1759 | */ 1760 | angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) { 1761 | $provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator); 1762 | }]); 1763 | 1764 | /** 1765 | * @ngdoc service 1766 | * @name $httpBackend 1767 | * @module ngMockE2E 1768 | * @description 1769 | * Fake HTTP backend implementation suitable for end-to-end testing or backend-less development of 1770 | * applications that use the {@link ng.$http $http service}. 1771 | * 1772 | * *Note*: For fake http backend implementation suitable for unit testing please see 1773 | * {@link ngMock.$httpBackend unit-testing $httpBackend mock}. 1774 | * 1775 | * This implementation can be used to respond with static or dynamic responses via the `when` api 1776 | * and its shortcuts (`whenGET`, `whenPOST`, etc) and optionally pass through requests to the 1777 | * real $httpBackend for specific requests (e.g. to interact with certain remote apis or to fetch 1778 | * templates from a webserver). 1779 | * 1780 | * As opposed to unit-testing, in an end-to-end testing scenario or in scenario when an application 1781 | * is being developed with the real backend api replaced with a mock, it is often desirable for 1782 | * certain category of requests to bypass the mock and issue a real http request (e.g. to fetch 1783 | * templates or static files from the webserver). To configure the backend with this behavior 1784 | * use the `passThrough` request handler of `when` instead of `respond`. 1785 | * 1786 | * Additionally, we don't want to manually have to flush mocked out requests like we do during unit 1787 | * testing. For this reason the e2e $httpBackend automatically flushes mocked out requests 1788 | * automatically, closely simulating the behavior of the XMLHttpRequest object. 1789 | * 1790 | * To setup the application to run with this http backend, you have to create a module that depends 1791 | * on the `ngMockE2E` and your application modules and defines the fake backend: 1792 | * 1793 | * ```js 1794 | * myAppDev = angular.module('myAppDev', ['myApp', 'ngMockE2E']); 1795 | * myAppDev.run(function($httpBackend) { 1796 | * phones = [{name: 'phone1'}, {name: 'phone2'}]; 1797 | * 1798 | * // returns the current list of phones 1799 | * $httpBackend.whenGET('/phones').respond(phones); 1800 | * 1801 | * // adds a new phone to the phones array 1802 | * $httpBackend.whenPOST('/phones').respond(function(method, url, data) { 1803 | * var phone = angular.fromJson(data); 1804 | * phones.push(phone); 1805 | * return [200, phone, {}]; 1806 | * }); 1807 | * $httpBackend.whenGET(/^\/templates\//).passThrough(); 1808 | * //... 1809 | * }); 1810 | * ``` 1811 | * 1812 | * Afterwards, bootstrap your app with this new module. 1813 | */ 1814 | 1815 | /** 1816 | * @ngdoc method 1817 | * @name $httpBackend#when 1818 | * @module ngMockE2E 1819 | * @description 1820 | * Creates a new backend definition. 1821 | * 1822 | * @param {string} method HTTP method. 1823 | * @param {string|RegExp} url HTTP url. 1824 | * @param {(string|RegExp)=} data HTTP request body. 1825 | * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header 1826 | * object and returns true if the headers match the current definition. 1827 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that 1828 | * control how a matched request is handled. 1829 | * 1830 | * - respond – 1831 | * `{function([status,] data[, headers, statusText]) 1832 | * | function(function(method, url, data, headers)}` 1833 | * – The respond method takes a set of static data to be returned or a function that can return 1834 | * an array containing response status (number), response data (string), response headers 1835 | * (Object), and the text for the status (string). 1836 | * - passThrough – `{function()}` – Any request matching a backend definition with 1837 | * `passThrough` handler will be passed through to the real backend (an XHR request will be made 1838 | * to the server.) 1839 | */ 1840 | 1841 | /** 1842 | * @ngdoc method 1843 | * @name $httpBackend#whenGET 1844 | * @module ngMockE2E 1845 | * @description 1846 | * Creates a new backend definition for GET requests. For more info see `when()`. 1847 | * 1848 | * @param {string|RegExp} url HTTP url. 1849 | * @param {(Object|function(Object))=} headers HTTP headers. 1850 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that 1851 | * control how a matched request is handled. 1852 | */ 1853 | 1854 | /** 1855 | * @ngdoc method 1856 | * @name $httpBackend#whenHEAD 1857 | * @module ngMockE2E 1858 | * @description 1859 | * Creates a new backend definition for HEAD requests. For more info see `when()`. 1860 | * 1861 | * @param {string|RegExp} url HTTP url. 1862 | * @param {(Object|function(Object))=} headers HTTP headers. 1863 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that 1864 | * control how a matched request is handled. 1865 | */ 1866 | 1867 | /** 1868 | * @ngdoc method 1869 | * @name $httpBackend#whenDELETE 1870 | * @module ngMockE2E 1871 | * @description 1872 | * Creates a new backend definition for DELETE requests. For more info see `when()`. 1873 | * 1874 | * @param {string|RegExp} url HTTP url. 1875 | * @param {(Object|function(Object))=} headers HTTP headers. 1876 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that 1877 | * control how a matched request is handled. 1878 | */ 1879 | 1880 | /** 1881 | * @ngdoc method 1882 | * @name $httpBackend#whenPOST 1883 | * @module ngMockE2E 1884 | * @description 1885 | * Creates a new backend definition for POST requests. For more info see `when()`. 1886 | * 1887 | * @param {string|RegExp} url HTTP url. 1888 | * @param {(string|RegExp)=} data HTTP request body. 1889 | * @param {(Object|function(Object))=} headers HTTP headers. 1890 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that 1891 | * control how a matched request is handled. 1892 | */ 1893 | 1894 | /** 1895 | * @ngdoc method 1896 | * @name $httpBackend#whenPUT 1897 | * @module ngMockE2E 1898 | * @description 1899 | * Creates a new backend definition for PUT requests. For more info see `when()`. 1900 | * 1901 | * @param {string|RegExp} url HTTP url. 1902 | * @param {(string|RegExp)=} data HTTP request body. 1903 | * @param {(Object|function(Object))=} headers HTTP headers. 1904 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that 1905 | * control how a matched request is handled. 1906 | */ 1907 | 1908 | /** 1909 | * @ngdoc method 1910 | * @name $httpBackend#whenPATCH 1911 | * @module ngMockE2E 1912 | * @description 1913 | * Creates a new backend definition for PATCH requests. For more info see `when()`. 1914 | * 1915 | * @param {string|RegExp} url HTTP url. 1916 | * @param {(string|RegExp)=} data HTTP request body. 1917 | * @param {(Object|function(Object))=} headers HTTP headers. 1918 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that 1919 | * control how a matched request is handled. 1920 | */ 1921 | 1922 | /** 1923 | * @ngdoc method 1924 | * @name $httpBackend#whenJSONP 1925 | * @module ngMockE2E 1926 | * @description 1927 | * Creates a new backend definition for JSONP requests. For more info see `when()`. 1928 | * 1929 | * @param {string|RegExp} url HTTP url. 1930 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that 1931 | * control how a matched request is handled. 1932 | */ 1933 | angular.mock.e2e = {}; 1934 | angular.mock.e2e.$httpBackendDecorator = 1935 | ['$rootScope', '$delegate', '$browser', createHttpBackendMock]; 1936 | 1937 | 1938 | angular.mock.clearDataCache = function() { 1939 | var key, 1940 | cache = angular.element.cache; 1941 | 1942 | for(key in cache) { 1943 | if (Object.prototype.hasOwnProperty.call(cache,key)) { 1944 | var handle = cache[key].handle; 1945 | 1946 | handle && angular.element(handle.elem).off(); 1947 | delete cache[key]; 1948 | } 1949 | } 1950 | }; 1951 | 1952 | 1953 | if(window.jasmine || window.mocha) { 1954 | 1955 | var currentSpec = null, 1956 | isSpecRunning = function() { 1957 | return !!currentSpec; 1958 | }; 1959 | 1960 | 1961 | (window.beforeEach || window.setup)(function() { 1962 | currentSpec = this; 1963 | }); 1964 | 1965 | (window.afterEach || window.teardown)(function() { 1966 | var injector = currentSpec.$injector; 1967 | 1968 | angular.forEach(currentSpec.$modules, function(module) { 1969 | if (module && module.$$hashKey) { 1970 | module.$$hashKey = undefined; 1971 | } 1972 | }); 1973 | 1974 | currentSpec.$injector = null; 1975 | currentSpec.$modules = null; 1976 | currentSpec = null; 1977 | 1978 | if (injector) { 1979 | injector.get('$rootElement').off(); 1980 | injector.get('$browser').pollFns.length = 0; 1981 | } 1982 | 1983 | angular.mock.clearDataCache(); 1984 | 1985 | // clean up jquery's fragment cache 1986 | angular.forEach(angular.element.fragments, function(val, key) { 1987 | delete angular.element.fragments[key]; 1988 | }); 1989 | 1990 | MockXhr.$$lastInstance = null; 1991 | 1992 | angular.forEach(angular.callbacks, function(val, key) { 1993 | delete angular.callbacks[key]; 1994 | }); 1995 | angular.callbacks.counter = 0; 1996 | }); 1997 | 1998 | /** 1999 | * @ngdoc function 2000 | * @name angular.mock.module 2001 | * @description 2002 | * 2003 | * *NOTE*: This function is also published on window for easy access.
2004 | * 2005 | * This function registers a module configuration code. It collects the configuration information 2006 | * which will be used when the injector is created by {@link angular.mock.inject inject}. 2007 | * 2008 | * See {@link angular.mock.inject inject} for usage example 2009 | * 2010 | * @param {...(string|Function|Object)} fns any number of modules which are represented as string 2011 | * aliases or as anonymous module initialization functions. The modules are used to 2012 | * configure the injector. The 'ng' and 'ngMock' modules are automatically loaded. If an 2013 | * object literal is passed they will be registered as values in the module, the key being 2014 | * the module name and the value being what is returned. 2015 | */ 2016 | window.module = angular.mock.module = function() { 2017 | var moduleFns = Array.prototype.slice.call(arguments, 0); 2018 | return isSpecRunning() ? workFn() : workFn; 2019 | ///////////////////// 2020 | function workFn() { 2021 | if (currentSpec.$injector) { 2022 | throw new Error('Injector already created, can not register a module!'); 2023 | } else { 2024 | var modules = currentSpec.$modules || (currentSpec.$modules = []); 2025 | angular.forEach(moduleFns, function(module) { 2026 | if (angular.isObject(module) && !angular.isArray(module)) { 2027 | modules.push(function($provide) { 2028 | angular.forEach(module, function(value, key) { 2029 | $provide.value(key, value); 2030 | }); 2031 | }); 2032 | } else { 2033 | modules.push(module); 2034 | } 2035 | }); 2036 | } 2037 | } 2038 | }; 2039 | 2040 | /** 2041 | * @ngdoc function 2042 | * @name angular.mock.inject 2043 | * @description 2044 | * 2045 | * *NOTE*: This function is also published on window for easy access.
2046 | * 2047 | * The inject function wraps a function into an injectable function. The inject() creates new 2048 | * instance of {@link auto.$injector $injector} per test, which is then used for 2049 | * resolving references. 2050 | * 2051 | * 2052 | * ## Resolving References (Underscore Wrapping) 2053 | * Often, we would like to inject a reference once, in a `beforeEach()` block and reuse this 2054 | * in multiple `it()` clauses. To be able to do this we must assign the reference to a variable 2055 | * that is declared in the scope of the `describe()` block. Since we would, most likely, want 2056 | * the variable to have the same name of the reference we have a problem, since the parameter 2057 | * to the `inject()` function would hide the outer variable. 2058 | * 2059 | * To help with this, the injected parameters can, optionally, be enclosed with underscores. 2060 | * These are ignored by the injector when the reference name is resolved. 2061 | * 2062 | * For example, the parameter `_myService_` would be resolved as the reference `myService`. 2063 | * Since it is available in the function body as _myService_, we can then assign it to a variable 2064 | * defined in an outer scope. 2065 | * 2066 | * ``` 2067 | * // Defined out reference variable outside 2068 | * var myService; 2069 | * 2070 | * // Wrap the parameter in underscores 2071 | * beforeEach( inject( function(_myService_){ 2072 | * myService = _myService_; 2073 | * })); 2074 | * 2075 | * // Use myService in a series of tests. 2076 | * it('makes use of myService', function() { 2077 | * myService.doStuff(); 2078 | * }); 2079 | * 2080 | * ``` 2081 | * 2082 | * See also {@link angular.mock.module angular.mock.module} 2083 | * 2084 | * ## Example 2085 | * Example of what a typical jasmine tests looks like with the inject method. 2086 | * ```js 2087 | * 2088 | * angular.module('myApplicationModule', []) 2089 | * .value('mode', 'app') 2090 | * .value('version', 'v1.0.1'); 2091 | * 2092 | * 2093 | * describe('MyApp', function() { 2094 | * 2095 | * // You need to load modules that you want to test, 2096 | * // it loads only the "ng" module by default. 2097 | * beforeEach(module('myApplicationModule')); 2098 | * 2099 | * 2100 | * // inject() is used to inject arguments of all given functions 2101 | * it('should provide a version', inject(function(mode, version) { 2102 | * expect(version).toEqual('v1.0.1'); 2103 | * expect(mode).toEqual('app'); 2104 | * })); 2105 | * 2106 | * 2107 | * // The inject and module method can also be used inside of the it or beforeEach 2108 | * it('should override a version and test the new version is injected', function() { 2109 | * // module() takes functions or strings (module aliases) 2110 | * module(function($provide) { 2111 | * $provide.value('version', 'overridden'); // override version here 2112 | * }); 2113 | * 2114 | * inject(function(version) { 2115 | * expect(version).toEqual('overridden'); 2116 | * }); 2117 | * }); 2118 | * }); 2119 | * 2120 | * ``` 2121 | * 2122 | * @param {...Function} fns any number of functions which will be injected using the injector. 2123 | */ 2124 | 2125 | 2126 | 2127 | var ErrorAddingDeclarationLocationStack = function(e, errorForStack) { 2128 | this.message = e.message; 2129 | this.name = e.name; 2130 | if (e.line) this.line = e.line; 2131 | if (e.sourceId) this.sourceId = e.sourceId; 2132 | if (e.stack && errorForStack) 2133 | this.stack = e.stack + '\n' + errorForStack.stack; 2134 | if (e.stackArray) this.stackArray = e.stackArray; 2135 | }; 2136 | ErrorAddingDeclarationLocationStack.prototype.toString = Error.prototype.toString; 2137 | 2138 | window.inject = angular.mock.inject = function() { 2139 | var blockFns = Array.prototype.slice.call(arguments, 0); 2140 | var errorForStack = new Error('Declaration Location'); 2141 | return isSpecRunning() ? workFn.call(currentSpec) : workFn; 2142 | ///////////////////// 2143 | function workFn() { 2144 | var modules = currentSpec.$modules || []; 2145 | 2146 | modules.unshift('ngMock'); 2147 | modules.unshift('ng'); 2148 | var injector = currentSpec.$injector; 2149 | if (!injector) { 2150 | injector = currentSpec.$injector = angular.injector(modules); 2151 | } 2152 | for(var i = 0, ii = blockFns.length; i < ii; i++) { 2153 | try { 2154 | /* jshint -W040 *//* Jasmine explicitly provides a `this` object when calling functions */ 2155 | injector.invoke(blockFns[i] || angular.noop, this); 2156 | /* jshint +W040 */ 2157 | } catch (e) { 2158 | if (e.stack && errorForStack) { 2159 | throw new ErrorAddingDeclarationLocationStack(e, errorForStack); 2160 | } 2161 | throw e; 2162 | } finally { 2163 | errorForStack = null; 2164 | } 2165 | } 2166 | } 2167 | }; 2168 | } 2169 | 2170 | 2171 | })(window, window.angular); 2172 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angularjs-filters", 3 | "author": "Sumit Chawla", 4 | "version": "0.0.2", 5 | "description": "AngularJs common filters", 6 | "main": "index.js", 7 | "keywords": [ 8 | "angularjs", 9 | "filters", 10 | "angulars string filters" 11 | ], 12 | "homepage": "https://github.com/sumitchawla/angularjs-filters", 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/sumitchawla/angularjs-filters.git" 16 | }, 17 | "devDependencies": { 18 | "phantomjs" : "~1.9", 19 | "karma": "~0.13", 20 | "karma-bro": "~0.11", 21 | "karma-chai": "~0.1", 22 | "karma-mocha": "~0.2", 23 | "karma-cli": "~0.1", 24 | "karma-phantomjs-launcher": "~0.2", 25 | "mocha": "~2.2", 26 | "browserify-shim": "3" 27 | }, 28 | "browserify": { 29 | "transform": [ 30 | "browserify-shim" 31 | ] 32 | }, 33 | "browserify-shim": { 34 | "angular": "global:angular" 35 | }, 36 | "main": "angularjs-filters", 37 | "bugs": { 38 | "url": "https://github.com/sumitchawla/angularjs-filters/issues" 39 | }, 40 | "scripts": { 41 | "karma": "karma start --single-run --browsers PhantomJS", 42 | "karma2": "karma start karma2.conf.js --single-run --browsers PhantomJS", 43 | "test": "npm run karma && npm run karma2" 44 | }, 45 | "license": "MIT" 46 | } 47 | -------------------------------------------------------------------------------- /test/browser_tests.js: -------------------------------------------------------------------------------- 1 | var assert = chai.assert; 2 | describe("ch.filters - Tests", function() { 3 | beforeEach(module('ch.filters')); 4 | console.log("jQuery Version", $().jquery); 5 | executeTests(); 6 | }); 7 | -------------------------------------------------------------------------------- /test/module_tests.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var angular = require('angular'); 4 | 5 | describe('ch.filters', function () { 6 | beforeEach(angular.mock.module(require('../index.js'))); 7 | executeTests(); 8 | }); 9 | -------------------------------------------------------------------------------- /test/tests.js: -------------------------------------------------------------------------------- 1 | 2 | function executeTests () { 3 | 4 | it('Tests print filter', inject(function($filter) { 5 | var filter = $filter('print'); 6 | assert(filter('test') == 'test'); 7 | })); 8 | 9 | 10 | it('Tests YesNo filter', inject(function($filter, $rootScope, $compile) { 11 | var filter = $filter('YesNo'); 12 | assert.equal(filter(null),'No'); 13 | assert.equal(filter(),'No'); 14 | assert.equal(filter(false),'No'); 15 | assert.equal(filter(true),'Yes'); 16 | 17 | var $scope = $rootScope.$new(); 18 | var elem = $compile('
{{ MyBoolean | YesNo }}
')($scope); 19 | $scope.$digest(); 20 | assert.equal($(elem).text(),'No'); 21 | ($scope.MyBoolean = false) && $scope.$digest(); 22 | assert.equal($(elem).text(),'No'); 23 | ($scope.MyBoolean = true) && $scope.$digest(); 24 | assert.equal($(elem).text(),'Yes'); 25 | })); 26 | 27 | it('Tests format filter', inject(function($filter) { 28 | var filter = $filter('format'); 29 | assert.equal(filter('Hello {0}. What are you been doing this {1}?', 'Sam', 'Sunday') , 'Hello Sam. What are you been doing this Sunday?'); 30 | })); 31 | 32 | it('Tests html2string filter', inject(function($filter) { 33 | var filter = $filter('html2string'); 34 | assert.equal(filter('Hello
. How are you?') , 'Hello . How are you?'); 35 | })); 36 | 37 | it('Tests shorten filter', inject(function($filter) { 38 | var filter = $filter('shorten'); 39 | assert.equal(filter('', 3) , ''); 40 | assert.equal(filter('', 1) , ''); 41 | assert.equal(filter(null, 1) , ''); 42 | assert.equal(filter(undefined, 1) , ''); 43 | assert.equal(filter('Hello', 20) , 'Hello'); 44 | assert.equal(filter('Hello', 5) , 'Hello'); 45 | assert.equal(filter('Hello', 3) , 'Hel'); 46 | assert.equal(filter('A long story cut into short', 12) , 'A long story...'); 47 | })); 48 | 49 | it('Tests lowercase filter', inject(function($filter) { 50 | var filter = $filter('lowercase'); 51 | assert.equal(filter('') , ''); 52 | assert.equal(filter(null) , ''); 53 | assert.equal(filter(undefined) , ''); 54 | assert.equal(filter('Hello') , 'hello'); 55 | assert.equal(filter('HeLLo') , 'hello'); 56 | assert.equal(filter('HeLLo ##453$$') , 'hello ##453$$'); 57 | assert.equal(filter('A Long Story - 1943') , 'a long story - 1943'); 58 | assert.equal(filter('1943') , '1943'); 59 | })); 60 | 61 | it('Tests uppercase filter', inject(function($filter) { 62 | var filter = $filter('uppercase'); 63 | assert.equal(filter('') , ''); 64 | assert.equal(filter(null) , ''); 65 | assert.equal(filter(undefined) , ''); 66 | assert.equal(filter('Hello') , 'HELLO'); 67 | assert.equal(filter('HeLLo') , 'HELLO'); 68 | assert.equal(filter('HeLLo ##453$$') , 'HELLO ##453$$'); 69 | assert.equal(filter('A Long Story - 1943') , 'A LONG STORY - 1943'); 70 | assert.equal(filter('1943') , '1943'); 71 | })); 72 | 73 | it('Tests camelcase filter', inject(function($filter) { 74 | var filter = $filter('camelcase'); 75 | assert.equal(filter('') , ''); 76 | assert.equal(filter(null) , ''); 77 | assert.equal(filter(undefined) , ''); 78 | assert.equal(filter('Hello') , 'Hello'); 79 | assert.equal(filter('HeLLo') , 'Hello'); 80 | assert.equal(filter(' HeLLo') , ' Hello'); 81 | assert.equal(filter(' HeLLo ') , ' Hello '); 82 | assert.equal(filter('HeLLo ##453$$') , 'Hello ##453$$'); 83 | assert.equal(filter('A Long Story - 1943') , 'A Long Story - 1943'); 84 | assert.equal(filter('A Long Story - 1943') , 'A Long Story - 1943'); 85 | assert.equal(filter(' a long story - 1943') , ' A Long Story - 1943'); 86 | assert.equal(filter('foo-bar') , 'Foo-bar'); 87 | assert.equal(filter('-foo-bar') , '-foo-bar'); 88 | assert.equal(filter('1943') , '1943'); 89 | })); 90 | 91 | it('Tests trim filter', inject(function($filter) { 92 | var filter = $filter('trim'); 93 | assert.equal(filter('') , ''); 94 | assert.equal(filter(null) , ''); 95 | assert.equal(filter(undefined) , ''); 96 | assert.equal(filter('Hello') , 'Hello'); 97 | assert.equal(filter(' Hello') , 'Hello'); 98 | assert.equal(filter(' Hello') , 'Hello'); 99 | assert.equal(filter('Hello ') , 'Hello'); 100 | assert.equal(filter('Hello ') , 'Hello'); 101 | assert.equal(filter(' Hello ') , 'Hello'); 102 | assert.equal(filter(' Hello ') , 'Hello'); 103 | assert.equal(filter('a long story - 1943') , 'a long story - 1943'); 104 | assert.equal(filter(' a long story - 1943') , 'a long story - 1943'); 105 | assert.equal(filter('a long story - 1943 ') , 'a long story - 1943'); 106 | })); 107 | 108 | 109 | it('Tests trimstart filter', inject(function($filter) { 110 | var filter = $filter('trimstart'); 111 | assert.equal(filter('') , ''); 112 | assert.equal(filter(null) , ''); 113 | assert.equal(filter(undefined) , ''); 114 | assert.equal(filter('Hello') , 'Hello'); 115 | assert.equal(filter(' Hello') , 'Hello'); 116 | assert.equal(filter(' Hello') , 'Hello'); 117 | assert.equal(filter('Hello ') , 'Hello '); 118 | assert.equal(filter('Hello ') , 'Hello '); 119 | assert.equal(filter(' Hello ') , 'Hello '); 120 | assert.equal(filter(' Hello ') , 'Hello '); 121 | assert.equal(filter('a long story - 1943') , 'a long story - 1943'); 122 | assert.equal(filter(' a long story - 1943') , 'a long story - 1943'); 123 | assert.equal(filter('a long story - 1943 ') , 'a long story - 1943 '); 124 | })); 125 | 126 | it('Tests trimend filter', inject(function($filter) { 127 | var filter = $filter('trimend'); 128 | assert.equal(filter('') , ''); 129 | assert.equal(filter(null) , ''); 130 | assert.equal(filter(undefined) , ''); 131 | assert.equal(filter('Hello') , 'Hello'); 132 | assert.equal(filter(' Hello') , ' Hello'); 133 | assert.equal(filter(' Hello') , ' Hello'); 134 | assert.equal(filter('Hello ') , 'Hello'); 135 | assert.equal(filter('Hello ') , 'Hello'); 136 | assert.equal(filter(' Hello ') , ' Hello'); 137 | assert.equal(filter(' Hello ') , ' Hello'); 138 | assert.equal(filter('a long story - 1943') , 'a long story - 1943'); 139 | assert.equal(filter(' a long story - 1943') , ' a long story - 1943'); 140 | assert.equal(filter('a long story - 1943 ') , 'a long story - 1943'); 141 | })); 142 | 143 | it('Tests replace filter', inject(function($rootScope,$filter, $compile) { 144 | var filter = $filter('replace'); 145 | //assert.equal(filter('') , ''); 146 | //assert.equal(filter(null) , ''); 147 | //assert.equal(filter(undefined) , ''); 148 | assert.equal(filter('Hello', 'Hello', 'Yes') , 'Yes'); 149 | assert.equal(filter('9.7', '\\.', ',') , '9,7'); 150 | assert.equal(filter(9.7, '\\.', ',') , '9,7'); 151 | assert.equal(filter('Hello Mr How are you doing', 'Mr', 'Mr.') , 'Hello Mr. How are you doing'); 152 | //Regex replacements 153 | assert.equal(filter('Hello Mr How are you doing', 'H', 'Y') , 'Yello Mr Yow are you doing'); 154 | assert.equal(filter('Hello', "\\s",'Mr') , 'Hello'); 155 | assert.equal(filter('a long story - 1943',"[0-9]", '2') , 'a long story - 2222'); 156 | assert.equal(filter('a long story - 1943',"[0-9]", '2', false) , 'a long story - 2943'); 157 | 158 | //Test regex using compile 159 | var $scope = $rootScope.$new(); 160 | var elem = $compile('
{{ "a long story - 1943" | replace:"[0-9]":"2" }}
')($scope); 161 | $scope.$digest(); 162 | assert.equal($(elem).text(),'a long story - 2222'); 163 | elem = $compile('
{{ "Some Mandatory caption" | replace:"Man[a-z]*":"" }}
')($scope); 164 | $scope.$digest(); 165 | assert.equal($(elem).text(),'Some caption'); 166 | elem = $compile('
{{ "Hello How Hello How" | replace:"Hello":"" }}
')($scope); 167 | $scope.$digest(); 168 | assert.equal($(elem).text(),' How How'); 169 | elem = $compile('
{{ "Hello How Hello How" | replace:"Hello":"":false }}
')($scope); 170 | $scope.$digest(); 171 | assert.equal($(elem).text(),' How Hello How'); 172 | elem = $compile('
{{"hello help"| replace:"he[a-z]{2}":"Yell" }}
')($scope); 173 | $scope.$digest(); 174 | assert.equal($(elem).text(),'Yello Yell'); 175 | })); 176 | 177 | it('Tests max filter', inject(function($filter) { 178 | var filter = $filter('max'); 179 | assert.equal(filter('') , ''); 180 | assert.equal(filter(null) , null); 181 | assert.equal(filter(undefined) , undefined); 182 | assert.equal(filter([1, 2, 3, 4]) , 4); 183 | assert.equal(filter([1, 2,1, 4, 3, 4]) , 4); 184 | assert.equal(filter([1, 2, -5 , 6, 3, 4]) , 6); 185 | assert.equal(filter([8, 1, 2, 3, 7]) , 8); 186 | })); 187 | 188 | it('Tests min filter', inject(function($filter) { 189 | var filter = $filter('min'); 190 | assert.equal(filter('') , ''); 191 | assert.equal(filter(null) , null); 192 | assert.equal(filter(undefined) , undefined); 193 | assert.equal(filter([1, 2, 3, 4]) , 1); 194 | assert.equal(filter([1, 2,1, 4, 3, 4]) , 1); 195 | assert.equal(filter([1, 2, -5 , 6, 3, 4]) , -5); 196 | assert.equal(filter([8, 1, 2, 3, 7]) , 1); 197 | assert.equal(filter([8, 1, 0, 2, 3, 7]) , 0); 198 | assert.equal(filter([8, 1, 0, 2, 3, 7, -1]) , -1); 199 | })); 200 | 201 | it('Tests join filter', inject(function($filter) { 202 | var filter = $filter('join'); 203 | assert.equal(filter('') , ''); 204 | assert.equal(filter(null) , null); 205 | assert.equal(filter(undefined) , undefined); 206 | assert.equal(filter([1, 2, 3, 4]) , '1,2,3,4'); 207 | assert.equal(filter(["Banana", "Orange", "Apple", "Mango"]) , 'Banana,Orange,Apple,Mango'); 208 | })); 209 | 210 | it('Tests reverse filter', inject(function($filter) { 211 | var filter = $filter('reverse'); 212 | assert.equal(filter('') , ''); 213 | assert.equal(filter(null) , null); 214 | assert.equal(filter(undefined) , undefined); 215 | assert.deepEqual(filter([1, 2, 3, 4]), [4, 3, 2, 1]); 216 | assert.deepEqual(filter(["Banana", "Orange", "Apple", "Mango"]) , [ "Mango","Apple","Orange", "Banana"]); 217 | })); 218 | } 219 | --------------------------------------------------------------------------------