├── .gitignore ├── test ├── lib │ ├── chai-expect.js │ ├── chai-should.js │ ├── angular │ │ ├── angular-loader.js │ │ └── angular-mocks.js │ └── chai.js ├── run.sh ├── testacular.conf.js └── spec │ └── apiSpec.js ├── .gitconcat ├── src ├── Scope.SafeApply.min.js └── Scope.SafeApply.js ├── package.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /test/lib/chai-expect.js: -------------------------------------------------------------------------------- 1 | var expect = chai.expect; 2 | -------------------------------------------------------------------------------- /test/lib/chai-should.js: -------------------------------------------------------------------------------- 1 | var should = chai.should(); 2 | -------------------------------------------------------------------------------- /test/run.sh: -------------------------------------------------------------------------------- 1 | testacular start ./test/testacular.conf.js 2 | -------------------------------------------------------------------------------- /.gitconcat: -------------------------------------------------------------------------------- 1 | jsmin: 2 | filters: 3 | - cat src/Scope.SafeApply.js | jsmin > %{output} 4 | input: 5 | - src/Scope.SafeApply.js 6 | output: 7 | - src/Scope.SafeApply.min.js 8 | -------------------------------------------------------------------------------- /src/Scope.SafeApply.min.js: -------------------------------------------------------------------------------- 1 | 2 | angular.module('Scope.safeApply',[]).run(function($rootScope){$rootScope.$safeApply=function(){var $scope,fn,force=false;if(arguments.length==1){var arg=arguments[0];if(typeof arg=='function'){fn=arg;} 3 | else{$scope=arg;}} 4 | else{$scope=arguments[0];fn=arguments[1];if(arguments.length==3){force=!!arguments[2];}} 5 | $scope=$scope||this;fn=fn||function(){};if(force||!$scope.$$phase){$scope.$apply?$scope.$apply(fn):$scope.apply(fn);} 6 | else{fn();}};}); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angularjs-scope.safeapply", 3 | "description": "A Scope Wrapper for AngularJS", 4 | "homepage": "https://github.com/yearofmoo/AngularJS-Scope.SafeApply", 5 | "bugs": { 6 | "url": "https://github.com/yearofmoo/AngularJS-Scope.SafeApply/issues" 7 | }, 8 | "keywords": [ 9 | "AngularJS", 10 | "apply", 11 | "scope", 12 | "nodejs", 13 | "js", 14 | "javascript" 15 | ], 16 | "author": "Matias Niemela", 17 | "repository": { 18 | "type": "git", 19 | "url": "git://github.com/yearofmoo/AngularJS-Scope.SafeApply.git" 20 | }, 21 | "version": "0.0.1" 22 | } 23 | -------------------------------------------------------------------------------- /test/testacular.conf.js: -------------------------------------------------------------------------------- 1 | // Testacular configuration 2 | // Generated on Wed Dec 05 2012 15:50:37 GMT-0500 (EST) 3 | 4 | 5 | // base path, that will be used to resolve files and exclude 6 | basePath = '../'; 7 | 8 | 9 | // list of files / patterns to load in the browser 10 | files = [ 11 | MOCHA, 12 | MOCHA_ADAPTER, 13 | 14 | './test/lib/angular/angular.js', 15 | 16 | './test/lib/chai.js', 17 | './test/lib/chai-expect.js', 18 | './test/lib/chai-should.js', 19 | './test/lib/angular/angular-mocks.js', 20 | 21 | './src/Scope.SafeApply.js', 22 | 23 | './test/spec/*.js' 24 | ]; 25 | 26 | // list of files to exclude 27 | exclude = []; 28 | 29 | // test results reporter to use 30 | // possible values: 'dots', 'progress', 'junit' 31 | reporters = ['progress']; 32 | 33 | // web server port 34 | port = 8000; 35 | 36 | // cli runner port 37 | runnerPort = 9100; 38 | 39 | // enable / disable colors in the output (reporters and logs) 40 | colors = true; 41 | 42 | // level of logging 43 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG 44 | logLevel = LOG_INFO; 45 | 46 | // enable / disable watching file and executing tests whenever any file changes 47 | autoWatch = true; 48 | 49 | // Start these browsers, currently available: 50 | // - Chrome 51 | // - ChromeCanary 52 | // - Firefox 53 | // - Opera 54 | // - Safari (only Mac) 55 | // - PhantomJS 56 | // - IE (only Windows) 57 | browsers = ['PhantomJS']; 58 | 59 | // If browser does not capture in given timeout [ms], kill it 60 | captureTimeout = 5000; 61 | 62 | // Continuous Integration mode 63 | // if true, it capture browsers, run tests and exit 64 | singleRun = false; 65 | -------------------------------------------------------------------------------- /src/Scope.SafeApply.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 by Matias Niemela 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | angular.module('Scope.safeApply', []).run(function($rootScope) { 23 | 24 | $rootScope.$safeApply = function() { 25 | var $scope, fn, force = false; 26 | if(arguments.length == 1) { 27 | var arg = arguments[0]; 28 | if(typeof arg == 'function') { 29 | fn = arg; 30 | } 31 | else { 32 | $scope = arg; 33 | } 34 | } 35 | else { 36 | $scope = arguments[0]; 37 | fn = arguments[1]; 38 | if(arguments.length == 3) { 39 | force = !!arguments[2]; 40 | } 41 | } 42 | $scope = $scope || this; 43 | fn = fn || function() { }; 44 | if(force || !$scope.$$phase) { 45 | $scope.$apply ? $scope.$apply(fn) : $scope.apply(fn); 46 | } 47 | else { 48 | fn(); 49 | } 50 | }; 51 | 52 | }); 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AngularJS - Scope.SafeApply 2 | 3 | AngularJS **$scope.$apply()** **without** the **hassle** and **errors** :) 4 | 5 | ## About 6 | 7 | AngularJS' core feature is the use of scopes, variables and bindings. The procedure of a value being updated on the code 8 | level and echoing to the DOM or application revolves around having a digest run against the scope to see what has changed 9 | and what is still the same. To run a digest, Angular issues a `$scope.$apply()` operation throughout it's application life-cycle 10 | to ensure that changes are picked up. And 3rd party applications can also call `$scope.$apply()`. Digests are automatically 11 | dispatched on major events within an AngularJS application (onClick, XHR events, route changes, template downloads, etc...). 12 | 13 | However, sometimes Angular complains when a $scope.apply operation is called. And when you do call $scope.$apply() then it may 14 | not update properly and you would almost all the time need to check to see if a phase is going on to avoid an exception being 15 | thrown. Luckily, **Scope.SafeApply** does all the work for you and you can apply changes to the application scope whenever you wish 16 | with this handy plugin. 17 | 18 | ## Installation 19 | 20 | To get this amazing plugin to run on your AngularJS application, simple download the plugin via NPM or Source File. 21 | 22 | ```bash 23 | # install via NPM 24 | npm install angularjs-scope.safeapply 25 | 26 | # And now you can copy or source the file via 27 | ./node_modules/angularjs-scope.safeapply/src/Scope.SafeApply.js 28 | ``` 29 | 30 | Or you can just copy it directly form Github via: 31 | 32 | https://raw.github.com/yearofmoo/AngularJS-Scope.SafeApply/master/src/Scope.SafeApply.js 33 | 34 | 35 | Once sourced into your webpage, then include this in your AngularJS application as so: 36 | 37 | ```javascript 38 | angular.module('YOUR_MODULE', ['Scope.safeApply']); 39 | ``` 40 | 41 | As soon as the application is bootstrapped or set to a ng-app directive then the plugin can be used inside your scope variables. 42 | 43 | ## Usage 44 | 45 | To use the plugin, simple apply all your changes like so: 46 | 47 | ```javascript 48 | //use by itself 49 | $scope.$safeApply(); 50 | 51 | //tell it which scope to update 52 | $scope.$safeApply($scope); 53 | $scope.$safeApply($anotherScope); 54 | 55 | //pass in an update function that gets called when the digest is going on... 56 | $scope.$safeApply(function() { 57 | 58 | }); 59 | 60 | //pass in both a scope and a function 61 | $scope.$safeApply($anotherScope,function() { 62 | 63 | }); 64 | 65 | //call it on the rootScope 66 | $rootScope.$safeApply(); 67 | $rootScope.$safeApply($rootScope); 68 | $rootScope.$safeApply($scope); 69 | $rootScope.$safeApply($scope, fn); 70 | $rootScope.$safeApply(fn); 71 | ``` 72 | 73 | ## Testing 74 | 75 | You can run the tests for this plugin directly from the repository... 76 | 77 | To get started, be sure to have testacular installed. Testacular can be installed via NPM (NodeJS) with the following command: 78 | 79 | ```bash 80 | # this will install testacular as a global binary on your system 81 | sudo npm install -g testacular 82 | ``` 83 | 84 | Then run this command at the root of the repo to test: 85 | 86 | ```bash 87 | # this will spawn up testacular and run the test specs 88 | ./test/run.sh 89 | ``` 90 | 91 | Once run, the test output should look like so: 92 | 93 | ```bash 94 | Chrome 24.0: Executed X of X SUCCESS (0.123 secs / 0.123 secs) 95 | ``` 96 | 97 | ## Blog Article 98 | 99 | The challenges regarding $scope and $apply in AngularJS are talked about in more detail here: 100 | 101 | http://www.yearofmoo.com/2012/10/more-angularjs-magic-to-supercharge-your-webapp.html#apply-digest-and-phase 102 | -------------------------------------------------------------------------------- /test/spec/apiSpec.js: -------------------------------------------------------------------------------- 1 | describe("Testing AngularJS Safe Apply", function() { 2 | 3 | beforeEach(module('Scope.safeApply')); 4 | 5 | it("should update after $safeApply is run", inject(function($rootScope, $compile) { 6 | var $scope = $rootScope.$new(); 7 | var html = '
{{ hello }}
'; 8 | var element = $compile(html)($scope); 9 | 10 | var value = 'world'; 11 | $scope.hello = value; 12 | expect(element.attr('html')).not.to.equal(value); 13 | 14 | $scope.$safeApply(); 15 | 16 | expect(element.html()).equal(value); 17 | 18 | $scope.$destroy(); 19 | })); 20 | 21 | it("should update if scope is the only param", inject(function($rootScope, $compile) { 22 | var $scope = $rootScope.$new(); 23 | var html = '
{{ hello }}
'; 24 | var element = $compile(html)($scope); 25 | 26 | var value = 'world'; 27 | expect(element.attr('html')).not.to.equal(value); 28 | 29 | $scope.$safeApply(function() { 30 | $scope.hello = value; 31 | }); 32 | 33 | expect(element.html()).equal(value); 34 | 35 | $scope.$destroy(); 36 | })); 37 | 38 | it("should update if scope is the only param", inject(function($rootScope, $compile) { 39 | var $scope = $rootScope.$new(); 40 | var html = '
{{ hello }}
'; 41 | var element = $compile(html)($scope); 42 | 43 | var value = 'world'; 44 | expect(element.attr('html')).not.to.equal(value); 45 | 46 | $scope.hello = value; 47 | 48 | $scope.$safeApply($scope); 49 | 50 | expect(element.html()).equal(value); 51 | 52 | $scope.$destroy(); 53 | })); 54 | 55 | it("should update when both params are given", inject(function($rootScope, $compile) { 56 | var $scope = $rootScope.$new(); 57 | var html = '
{{ hello }}
'; 58 | var element = $compile(html)($scope); 59 | 60 | var value = 'world'; 61 | expect(element.attr('html')).not.to.equal(value); 62 | 63 | $scope.$safeApply($scope, function() { 64 | $scope.hello = value; 65 | }); 66 | 67 | expect(element.html()).equal(value); 68 | 69 | $scope.$destroy(); 70 | })); 71 | 72 | it("should update when the $rootScope is given", inject(function($rootScope, $compile) { 73 | var html = '
{{ hello2 }}
'; 74 | var element = $compile(html)($rootScope); 75 | 76 | var value = 'world'; 77 | expect(element.attr('html')).not.to.equal(value); 78 | 79 | $rootScope.hello2 = value; 80 | $rootScope.$safeApply($rootScope); 81 | 82 | expect(element.html()).equal(value); 83 | })); 84 | 85 | it("should update when the $rootScope and a function are given", inject(function($rootScope, $compile) { 86 | var html = '
{{ hello3 }}
'; 87 | var element = $compile(html)($rootScope); 88 | 89 | var value = 'world'; 90 | expect(element.attr('html')).not.to.equal(value); 91 | 92 | $rootScope.$safeApply($rootScope, function() { 93 | $rootScope.hello3 = value; 94 | }); 95 | 96 | expect(element.html()).equal(value); 97 | })); 98 | 99 | it("should change the location route on $rootScope", inject(function($rootScope, $location) { 100 | var path = '/home'; 101 | expect($location.path()).not.to.equal(path); 102 | $location.path(path); 103 | $rootScope.$safeApply(); 104 | expect($location.path()).to.equal(path); 105 | })); 106 | 107 | it("should change the location route on $scope", inject(function($rootScope, $location) { 108 | var $scope = $rootScope.$new(); 109 | var path = '/index'; 110 | expect($location.path()).not.to.equal(path); 111 | $location.path(path); 112 | $scope.$safeApply(); 113 | expect($location.path()).to.equal(path); 114 | $scope.$destroy(); 115 | })); 116 | 117 | }); 118 | -------------------------------------------------------------------------------- /test/lib/angular/angular-loader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license AngularJS v1.0.2 3 | * (c) 2010-2012 Google, Inc. http://angularjs.org 4 | * License: MIT 5 | */ 6 | 7 | ( 8 | 9 | /** 10 | * @ngdoc interface 11 | * @name angular.Module 12 | * @description 13 | * 14 | * Interface for configuring angular {@link angular.module modules}. 15 | */ 16 | 17 | function setupModuleLoader(window) { 18 | 19 | function ensure(obj, name, factory) { 20 | return obj[name] || (obj[name] = factory()); 21 | } 22 | 23 | return ensure(ensure(window, 'angular', Object), 'module', function() { 24 | /** @type {Object.} */ 25 | var modules = {}; 26 | 27 | /** 28 | * @ngdoc function 29 | * @name angular.module 30 | * @description 31 | * 32 | * The `angular.module` is a global place for creating and registering Angular modules. All 33 | * modules (angular core or 3rd party) that should be available to an application must be 34 | * registered using this mechanism. 35 | * 36 | * 37 | * # Module 38 | * 39 | * A module is a collocation of services, directives, filters, and configure information. Module 40 | * is used to configure the {@link AUTO.$injector $injector}. 41 | * 42 | *
 43 |      * // Create a new module
 44 |      * var myModule = angular.module('myModule', []);
 45 |      *
 46 |      * // register a new service
 47 |      * myModule.value('appName', 'MyCoolApp');
 48 |      *
 49 |      * // configure existing services inside initialization blocks.
 50 |      * myModule.config(function($locationProvider) {
 51 | 'use strict';
 52 |      *   // Configure existing providers
 53 |      *   $locationProvider.hashPrefix('!');
 54 |      * });
 55 |      * 
56 | * 57 | * Then you can create an injector and load your modules like this: 58 | * 59 | *
 60 |      * var injector = angular.injector(['ng', 'MyModule'])
 61 |      * 
62 | * 63 | * However it's more likely that you'll just use 64 | * {@link ng.directive:ngApp ngApp} or 65 | * {@link angular.bootstrap} to simplify this process for you. 66 | * 67 | * @param {!string} name The name of the module to create or retrieve. 68 | * @param {Array.=} requires If specified then new module is being created. If unspecified then the 69 | * the module is being retrieved for further configuration. 70 | * @param {Function} configFn Option configuration function for the module. Same as 71 | * {@link angular.Module#config Module#config()}. 72 | * @returns {module} new module with the {@link angular.Module} api. 73 | */ 74 | return function module(name, requires, configFn) { 75 | if (requires && modules.hasOwnProperty(name)) { 76 | modules[name] = null; 77 | } 78 | return ensure(modules, name, function() { 79 | if (!requires) { 80 | throw Error('No module: ' + name); 81 | } 82 | 83 | /** @type {!Array.>} */ 84 | var invokeQueue = []; 85 | 86 | /** @type {!Array.} */ 87 | var runBlocks = []; 88 | 89 | var config = invokeLater('$injector', 'invoke'); 90 | 91 | /** @type {angular.Module} */ 92 | var moduleInstance = { 93 | // Private state 94 | _invokeQueue: invokeQueue, 95 | _runBlocks: runBlocks, 96 | 97 | /** 98 | * @ngdoc property 99 | * @name angular.Module#requires 100 | * @propertyOf angular.Module 101 | * @returns {Array.} List of module names which must be loaded before this module. 102 | * @description 103 | * Holds the list of modules which the injector will load before the current module is loaded. 104 | */ 105 | requires: requires, 106 | 107 | /** 108 | * @ngdoc property 109 | * @name angular.Module#name 110 | * @propertyOf angular.Module 111 | * @returns {string} Name of the module. 112 | * @description 113 | */ 114 | name: name, 115 | 116 | 117 | /** 118 | * @ngdoc method 119 | * @name angular.Module#provider 120 | * @methodOf angular.Module 121 | * @param {string} name service name 122 | * @param {Function} providerType Construction function for creating new instance of the service. 123 | * @description 124 | * See {@link AUTO.$provide#provider $provide.provider()}. 125 | */ 126 | provider: invokeLater('$provide', 'provider'), 127 | 128 | /** 129 | * @ngdoc method 130 | * @name angular.Module#factory 131 | * @methodOf angular.Module 132 | * @param {string} name service name 133 | * @param {Function} providerFunction Function for creating new instance of the service. 134 | * @description 135 | * See {@link AUTO.$provide#factory $provide.factory()}. 136 | */ 137 | factory: invokeLater('$provide', 'factory'), 138 | 139 | /** 140 | * @ngdoc method 141 | * @name angular.Module#service 142 | * @methodOf angular.Module 143 | * @param {string} name service name 144 | * @param {Function} constructor A constructor function that will be instantiated. 145 | * @description 146 | * See {@link AUTO.$provide#service $provide.service()}. 147 | */ 148 | service: invokeLater('$provide', 'service'), 149 | 150 | /** 151 | * @ngdoc method 152 | * @name angular.Module#value 153 | * @methodOf angular.Module 154 | * @param {string} name service name 155 | * @param {*} object Service instance object. 156 | * @description 157 | * See {@link AUTO.$provide#value $provide.value()}. 158 | */ 159 | value: invokeLater('$provide', 'value'), 160 | 161 | /** 162 | * @ngdoc method 163 | * @name angular.Module#constant 164 | * @methodOf angular.Module 165 | * @param {string} name constant name 166 | * @param {*} object Constant value. 167 | * @description 168 | * Because the constant are fixed, they get applied before other provide methods. 169 | * See {@link AUTO.$provide#constant $provide.constant()}. 170 | */ 171 | constant: invokeLater('$provide', 'constant', 'unshift'), 172 | 173 | /** 174 | * @ngdoc method 175 | * @name angular.Module#filter 176 | * @methodOf angular.Module 177 | * @param {string} name Filter name. 178 | * @param {Function} filterFactory Factory function for creating new instance of filter. 179 | * @description 180 | * See {@link ng.$filterProvider#register $filterProvider.register()}. 181 | */ 182 | filter: invokeLater('$filterProvider', 'register'), 183 | 184 | /** 185 | * @ngdoc method 186 | * @name angular.Module#controller 187 | * @methodOf angular.Module 188 | * @param {string} name Controller name. 189 | * @param {Function} constructor Controller constructor function. 190 | * @description 191 | * See {@link ng.$controllerProvider#register $controllerProvider.register()}. 192 | */ 193 | controller: invokeLater('$controllerProvider', 'register'), 194 | 195 | /** 196 | * @ngdoc method 197 | * @name angular.Module#directive 198 | * @methodOf angular.Module 199 | * @param {string} name directive name 200 | * @param {Function} directiveFactory Factory function for creating new instance of 201 | * directives. 202 | * @description 203 | * See {@link ng.$compileProvider#directive $compileProvider.directive()}. 204 | */ 205 | directive: invokeLater('$compileProvider', 'directive'), 206 | 207 | /** 208 | * @ngdoc method 209 | * @name angular.Module#config 210 | * @methodOf angular.Module 211 | * @param {Function} configFn Execute this function on module load. Useful for service 212 | * configuration. 213 | * @description 214 | * Use this method to register work which needs to be performed on module loading. 215 | */ 216 | config: config, 217 | 218 | /** 219 | * @ngdoc method 220 | * @name angular.Module#run 221 | * @methodOf angular.Module 222 | * @param {Function} initializationFn Execute this function after injector creation. 223 | * Useful for application initialization. 224 | * @description 225 | * Use this method to register work which needs to be performed when the injector with 226 | * with the current module is finished loading. 227 | */ 228 | run: function(block) { 229 | runBlocks.push(block); 230 | return this; 231 | } 232 | }; 233 | 234 | if (configFn) { 235 | config(configFn); 236 | } 237 | 238 | return moduleInstance; 239 | 240 | /** 241 | * @param {string} provider 242 | * @param {string} method 243 | * @param {String=} insertMethod 244 | * @returns {angular.Module} 245 | */ 246 | function invokeLater(provider, method, insertMethod) { 247 | return function() { 248 | invokeQueue[insertMethod || 'push']([provider, method, arguments]); 249 | return moduleInstance; 250 | } 251 | } 252 | }); 253 | }; 254 | }); 255 | 256 | } 257 | )(window); 258 | 259 | /** 260 | * Closure compiler type information 261 | * 262 | * @typedef { { 263 | * requires: !Array., 264 | * invokeQueue: !Array.>, 265 | * 266 | * service: function(string, Function):angular.Module, 267 | * factory: function(string, Function):angular.Module, 268 | * value: function(string, *):angular.Module, 269 | * 270 | * filter: function(string, Function):angular.Module, 271 | * 272 | * init: function(Function):angular.Module 273 | * } } 274 | */ 275 | angular.Module; 276 | 277 | -------------------------------------------------------------------------------- /test/lib/angular/angular-mocks.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @license AngularJS v"NG_VERSION_FULL" 4 | * (c) 2010-2012 Google, Inc. http://angularjs.org 5 | * License: MIT 6 | * 7 | * TODO(vojta): wrap whole file into closure during build 8 | */ 9 | 10 | /** 11 | * @ngdoc overview 12 | * @name angular.mock 13 | * @description 14 | * 15 | * Namespace from 'angular-mocks.js' which contains testing related code. 16 | */ 17 | angular.mock = {}; 18 | 19 | /** 20 | * ! This is a private undocumented service ! 21 | * 22 | * @name ngMock.$browser 23 | * 24 | * @description 25 | * This service is a mock implementation of {@link ng.$browser}. It provides fake 26 | * implementation for commonly used browser apis that are hard to test, e.g. setTimeout, xhr, 27 | * cookies, etc... 28 | * 29 | * The api of this service is the same as that of the real {@link ng.$browser $browser}, except 30 | * that there are several helper methods available which can be used in tests. 31 | */ 32 | angular.mock.$BrowserProvider = function() { 33 | this.$get = function(){ 34 | return new angular.mock.$Browser(); 35 | }; 36 | }; 37 | 38 | angular.mock.$Browser = function() { 39 | var self = this; 40 | 41 | this.isMock = true; 42 | self.$$url = "http://server/"; 43 | self.$$lastUrl = self.$$url; // used by url polling fn 44 | self.pollFns = []; 45 | 46 | // TODO(vojta): remove this temporary api 47 | self.$$completeOutstandingRequest = angular.noop; 48 | self.$$incOutstandingRequestCount = angular.noop; 49 | 50 | 51 | // register url polling fn 52 | 53 | self.onUrlChange = function(listener) { 54 | self.pollFns.push( 55 | function() { 56 | if (self.$$lastUrl != self.$$url) { 57 | self.$$lastUrl = self.$$url; 58 | listener(self.$$url); 59 | } 60 | } 61 | ); 62 | 63 | return listener; 64 | }; 65 | 66 | self.cookieHash = {}; 67 | self.lastCookieHash = {}; 68 | self.deferredFns = []; 69 | self.deferredNextId = 0; 70 | 71 | self.defer = function(fn, delay) { 72 | delay = delay || 0; 73 | self.deferredFns.push({time:(self.defer.now + delay), fn:fn, id: self.deferredNextId}); 74 | self.deferredFns.sort(function(a,b){ return a.time - b.time;}); 75 | return self.deferredNextId++; 76 | }; 77 | 78 | 79 | self.defer.now = 0; 80 | 81 | 82 | self.defer.cancel = function(deferId) { 83 | var fnIndex; 84 | 85 | angular.forEach(self.deferredFns, function(fn, index) { 86 | if (fn.id === deferId) fnIndex = index; 87 | }); 88 | 89 | if (fnIndex !== undefined) { 90 | self.deferredFns.splice(fnIndex, 1); 91 | return true; 92 | } 93 | 94 | return false; 95 | }; 96 | 97 | 98 | /** 99 | * @name ngMock.$browser#defer.flush 100 | * @methodOf ngMock.$browser 101 | * 102 | * @description 103 | * Flushes all pending requests and executes the defer callbacks. 104 | * 105 | * @param {number=} number of milliseconds to flush. See {@link #defer.now} 106 | */ 107 | self.defer.flush = function(delay) { 108 | if (angular.isDefined(delay)) { 109 | self.defer.now += delay; 110 | } else { 111 | if (self.deferredFns.length) { 112 | self.defer.now = self.deferredFns[self.deferredFns.length-1].time; 113 | } else { 114 | throw Error('No deferred tasks to be flushed'); 115 | } 116 | } 117 | 118 | while (self.deferredFns.length && self.deferredFns[0].time <= self.defer.now) { 119 | self.deferredFns.shift().fn(); 120 | } 121 | }; 122 | /** 123 | * @name ngMock.$browser#defer.now 124 | * @propertyOf ngMock.$browser 125 | * 126 | * @description 127 | * Current milliseconds mock time. 128 | */ 129 | 130 | self.$$baseHref = ''; 131 | self.baseHref = function() { 132 | return this.$$baseHref; 133 | }; 134 | }; 135 | angular.mock.$Browser.prototype = { 136 | 137 | /** 138 | * @name ngMock.$browser#poll 139 | * @methodOf ngMock.$browser 140 | * 141 | * @description 142 | * run all fns in pollFns 143 | */ 144 | poll: function poll() { 145 | angular.forEach(this.pollFns, function(pollFn){ 146 | pollFn(); 147 | }); 148 | }, 149 | 150 | addPollFn: function(pollFn) { 151 | this.pollFns.push(pollFn); 152 | return pollFn; 153 | }, 154 | 155 | url: function(url, replace) { 156 | if (url) { 157 | this.$$url = url; 158 | return this; 159 | } 160 | 161 | return this.$$url; 162 | }, 163 | 164 | cookies: function(name, value) { 165 | if (name) { 166 | if (value == undefined) { 167 | delete this.cookieHash[name]; 168 | } else { 169 | if (angular.isString(value) && //strings only 170 | value.length <= 4096) { //strict cookie storage limits 171 | this.cookieHash[name] = value; 172 | } 173 | } 174 | } else { 175 | if (!angular.equals(this.cookieHash, this.lastCookieHash)) { 176 | this.lastCookieHash = angular.copy(this.cookieHash); 177 | this.cookieHash = angular.copy(this.cookieHash); 178 | } 179 | return this.cookieHash; 180 | } 181 | }, 182 | 183 | notifyWhenNoOutstandingRequests: function(fn) { 184 | fn(); 185 | } 186 | }; 187 | 188 | 189 | /** 190 | * @ngdoc object 191 | * @name ngMock.$exceptionHandlerProvider 192 | * 193 | * @description 194 | * Configures the mock implementation of {@link ng.$exceptionHandler} to rethrow or to log errors passed 195 | * into the `$exceptionHandler`. 196 | */ 197 | 198 | /** 199 | * @ngdoc object 200 | * @name ngMock.$exceptionHandler 201 | * 202 | * @description 203 | * Mock implementation of {@link ng.$exceptionHandler} that rethrows or logs errors passed 204 | * into it. See {@link ngMock.$exceptionHandlerProvider $exceptionHandlerProvider} for configuration 205 | * information. 206 | */ 207 | 208 | angular.mock.$ExceptionHandlerProvider = function() { 209 | var handler; 210 | 211 | /** 212 | * @ngdoc method 213 | * @name ngMock.$exceptionHandlerProvider#mode 214 | * @methodOf ngMock.$exceptionHandlerProvider 215 | * 216 | * @description 217 | * Sets the logging mode. 218 | * 219 | * @param {string} mode Mode of operation, defaults to `rethrow`. 220 | * 221 | * - `rethrow`: If any errors are are passed into the handler in tests, it typically 222 | * means that there is a bug in the application or test, so this mock will 223 | * make these tests fail. 224 | * - `log`: Sometimes it is desirable to test that an error is throw, for this case the `log` mode stores the 225 | * error and allows later assertion of it. 226 | * See {@link ngMock.$log#assertEmpty assertEmpty()} and 227 | * {@link ngMock.$log#reset reset()} 228 | */ 229 | this.mode = function(mode) { 230 | switch(mode) { 231 | case 'rethrow': 232 | handler = function(e) { 233 | throw e; 234 | }; 235 | break; 236 | case 'log': 237 | var errors = []; 238 | 239 | handler = function(e) { 240 | if (arguments.length == 1) { 241 | errors.push(e); 242 | } else { 243 | errors.push([].slice.call(arguments, 0)); 244 | } 245 | }; 246 | 247 | handler.errors = errors; 248 | break; 249 | default: 250 | throw Error("Unknown mode '" + mode + "', only 'log'/'rethrow' modes are allowed!"); 251 | } 252 | }; 253 | 254 | this.$get = function() { 255 | return handler; 256 | }; 257 | 258 | this.mode('rethrow'); 259 | }; 260 | 261 | 262 | /** 263 | * @ngdoc service 264 | * @name ngMock.$log 265 | * 266 | * @description 267 | * Mock implementation of {@link ng.$log} that gathers all logged messages in arrays 268 | * (one array per logging level). These arrays are exposed as `logs` property of each of the 269 | * level-specific log function, e.g. for level `error` the array is exposed as `$log.error.logs`. 270 | * 271 | */ 272 | angular.mock.$LogProvider = function() { 273 | 274 | function concat(array1, array2, index) { 275 | return array1.concat(Array.prototype.slice.call(array2, index)); 276 | } 277 | 278 | 279 | this.$get = function () { 280 | var $log = { 281 | log: function() { $log.log.logs.push(concat([], arguments, 0)); }, 282 | warn: function() { $log.warn.logs.push(concat([], arguments, 0)); }, 283 | info: function() { $log.info.logs.push(concat([], arguments, 0)); }, 284 | error: function() { $log.error.logs.push(concat([], arguments, 0)); } 285 | }; 286 | 287 | /** 288 | * @ngdoc method 289 | * @name ngMock.$log#reset 290 | * @methodOf ngMock.$log 291 | * 292 | * @description 293 | * Reset all of the logging arrays to empty. 294 | */ 295 | $log.reset = function () { 296 | /** 297 | * @ngdoc property 298 | * @name ngMock.$log#log.logs 299 | * @propertyOf ngMock.$log 300 | * 301 | * @description 302 | * Array of logged messages. 303 | */ 304 | $log.log.logs = []; 305 | /** 306 | * @ngdoc property 307 | * @name ngMock.$log#warn.logs 308 | * @propertyOf ngMock.$log 309 | * 310 | * @description 311 | * Array of logged messages. 312 | */ 313 | $log.warn.logs = []; 314 | /** 315 | * @ngdoc property 316 | * @name ngMock.$log#info.logs 317 | * @propertyOf ngMock.$log 318 | * 319 | * @description 320 | * Array of logged messages. 321 | */ 322 | $log.info.logs = []; 323 | /** 324 | * @ngdoc property 325 | * @name ngMock.$log#error.logs 326 | * @propertyOf ngMock.$log 327 | * 328 | * @description 329 | * Array of logged messages. 330 | */ 331 | $log.error.logs = []; 332 | }; 333 | 334 | /** 335 | * @ngdoc method 336 | * @name ngMock.$log#assertEmpty 337 | * @methodOf ngMock.$log 338 | * 339 | * @description 340 | * Assert that the all of the logging methods have no logged messages. If messages present, an exception is thrown. 341 | */ 342 | $log.assertEmpty = function() { 343 | var errors = []; 344 | angular.forEach(['error', 'warn', 'info', 'log'], function(logLevel) { 345 | angular.forEach($log[logLevel].logs, function(log) { 346 | angular.forEach(log, function (logItem) { 347 | errors.push('MOCK $log (' + logLevel + '): ' + String(logItem) + '\n' + (logItem.stack || '')); 348 | }); 349 | }); 350 | }); 351 | if (errors.length) { 352 | errors.unshift("Expected $log to be empty! Either a message was logged unexpectedly, or an expected " + 353 | "log message was not checked and removed:"); 354 | errors.push(''); 355 | throw new Error(errors.join('\n---------\n')); 356 | } 357 | }; 358 | 359 | $log.reset(); 360 | return $log; 361 | }; 362 | }; 363 | 364 | 365 | (function() { 366 | var R_ISO8061_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?:\:?(\d\d)(?:\:?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/; 367 | 368 | function jsonStringToDate(string){ 369 | var match; 370 | if (match = string.match(R_ISO8061_STR)) { 371 | var date = new Date(0), 372 | tzHour = 0, 373 | tzMin = 0; 374 | if (match[9]) { 375 | tzHour = int(match[9] + match[10]); 376 | tzMin = int(match[9] + match[11]); 377 | } 378 | date.setUTCFullYear(int(match[1]), int(match[2]) - 1, int(match[3])); 379 | date.setUTCHours(int(match[4]||0) - tzHour, int(match[5]||0) - tzMin, int(match[6]||0), int(match[7]||0)); 380 | return date; 381 | } 382 | return string; 383 | } 384 | 385 | function int(str) { 386 | return parseInt(str, 10); 387 | } 388 | 389 | function padNumber(num, digits, trim) { 390 | var neg = ''; 391 | if (num < 0) { 392 | neg = '-'; 393 | num = -num; 394 | } 395 | num = '' + num; 396 | while(num.length < digits) num = '0' + num; 397 | if (trim) 398 | num = num.substr(num.length - digits); 399 | return neg + num; 400 | } 401 | 402 | 403 | /** 404 | * @ngdoc object 405 | * @name angular.mock.TzDate 406 | * @description 407 | * 408 | * *NOTE*: this is not an injectable instance, just a globally available mock class of `Date`. 409 | * 410 | * Mock of the Date type which has its timezone specified via constroctor arg. 411 | * 412 | * The main purpose is to create Date-like instances with timezone fixed to the specified timezone 413 | * offset, so that we can test code that depends on local timezone settings without dependency on 414 | * the time zone settings of the machine where the code is running. 415 | * 416 | * @param {number} offset Offset of the *desired* timezone in hours (fractions will be honored) 417 | * @param {(number|string)} timestamp Timestamp representing the desired time in *UTC* 418 | * 419 | * @example 420 | * !!!! WARNING !!!!! 421 | * This is not a complete Date object so only methods that were implemented can be called safely. 422 | * To make matters worse, TzDate instances inherit stuff from Date via a prototype. 423 | * 424 | * We do our best to intercept calls to "unimplemented" methods, but since the list of methods is 425 | * incomplete we might be missing some non-standard methods. This can result in errors like: 426 | * "Date.prototype.foo called on incompatible Object". 427 | * 428 | *
 429 |    * var newYearInBratislava = new TzDate(-1, '2009-12-31T23:00:00Z');
 430 |    * newYearInBratislava.getTimezoneOffset() => -60;
 431 |    * newYearInBratislava.getFullYear() => 2010;
 432 |    * newYearInBratislava.getMonth() => 0;
 433 |    * newYearInBratislava.getDate() => 1;
 434 |    * newYearInBratislava.getHours() => 0;
 435 |    * newYearInBratislava.getMinutes() => 0;
 436 |    * 
437 | * 438 | */ 439 | angular.mock.TzDate = function (offset, timestamp) { 440 | var self = new Date(0); 441 | if (angular.isString(timestamp)) { 442 | var tsStr = timestamp; 443 | 444 | self.origDate = jsonStringToDate(timestamp); 445 | 446 | timestamp = self.origDate.getTime(); 447 | if (isNaN(timestamp)) 448 | throw { 449 | name: "Illegal Argument", 450 | message: "Arg '" + tsStr + "' passed into TzDate constructor is not a valid date string" 451 | }; 452 | } else { 453 | self.origDate = new Date(timestamp); 454 | } 455 | 456 | var localOffset = new Date(timestamp).getTimezoneOffset(); 457 | self.offsetDiff = localOffset*60*1000 - offset*1000*60*60; 458 | self.date = new Date(timestamp + self.offsetDiff); 459 | 460 | self.getTime = function() { 461 | return self.date.getTime() - self.offsetDiff; 462 | }; 463 | 464 | self.toLocaleDateString = function() { 465 | return self.date.toLocaleDateString(); 466 | }; 467 | 468 | self.getFullYear = function() { 469 | return self.date.getFullYear(); 470 | }; 471 | 472 | self.getMonth = function() { 473 | return self.date.getMonth(); 474 | }; 475 | 476 | self.getDate = function() { 477 | return self.date.getDate(); 478 | }; 479 | 480 | self.getHours = function() { 481 | return self.date.getHours(); 482 | }; 483 | 484 | self.getMinutes = function() { 485 | return self.date.getMinutes(); 486 | }; 487 | 488 | self.getSeconds = function() { 489 | return self.date.getSeconds(); 490 | }; 491 | 492 | self.getTimezoneOffset = function() { 493 | return offset * 60; 494 | }; 495 | 496 | self.getUTCFullYear = function() { 497 | return self.origDate.getUTCFullYear(); 498 | }; 499 | 500 | self.getUTCMonth = function() { 501 | return self.origDate.getUTCMonth(); 502 | }; 503 | 504 | self.getUTCDate = function() { 505 | return self.origDate.getUTCDate(); 506 | }; 507 | 508 | self.getUTCHours = function() { 509 | return self.origDate.getUTCHours(); 510 | }; 511 | 512 | self.getUTCMinutes = function() { 513 | return self.origDate.getUTCMinutes(); 514 | }; 515 | 516 | self.getUTCSeconds = function() { 517 | return self.origDate.getUTCSeconds(); 518 | }; 519 | 520 | self.getUTCMilliseconds = function() { 521 | return self.origDate.getUTCMilliseconds(); 522 | }; 523 | 524 | self.getDay = function() { 525 | return self.date.getDay(); 526 | }; 527 | 528 | // provide this method only on browsers that already have it 529 | if (self.toISOString) { 530 | self.toISOString = function() { 531 | return padNumber(self.origDate.getUTCFullYear(), 4) + '-' + 532 | padNumber(self.origDate.getUTCMonth() + 1, 2) + '-' + 533 | padNumber(self.origDate.getUTCDate(), 2) + 'T' + 534 | padNumber(self.origDate.getUTCHours(), 2) + ':' + 535 | padNumber(self.origDate.getUTCMinutes(), 2) + ':' + 536 | padNumber(self.origDate.getUTCSeconds(), 2) + '.' + 537 | padNumber(self.origDate.getUTCMilliseconds(), 3) + 'Z' 538 | } 539 | } 540 | 541 | //hide all methods not implemented in this mock that the Date prototype exposes 542 | var unimplementedMethods = ['getMilliseconds', 'getUTCDay', 543 | 'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds', 544 | 'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear', 545 | 'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds', 546 | 'setYear', 'toDateString', 'toGMTString', 'toJSON', 'toLocaleFormat', 'toLocaleString', 547 | 'toLocaleTimeString', 'toSource', 'toString', 'toTimeString', 'toUTCString', 'valueOf']; 548 | 549 | angular.forEach(unimplementedMethods, function(methodName) { 550 | self[methodName] = function() { 551 | throw Error("Method '" + methodName + "' is not implemented in the TzDate mock"); 552 | }; 553 | }); 554 | 555 | return self; 556 | }; 557 | 558 | //make "tzDateInstance instanceof Date" return true 559 | angular.mock.TzDate.prototype = Date.prototype; 560 | })(); 561 | 562 | 563 | /** 564 | * @ngdoc function 565 | * @name angular.mock.debug 566 | * @description 567 | * 568 | * *NOTE*: this is not an injectable instance, just a globally available function. 569 | * 570 | * Method for serializing common angular objects (scope, elements, etc..) into strings, useful for debugging. 571 | * 572 | * This method is also available on window, where it can be used to display objects on debug console. 573 | * 574 | * @param {*} object - any object to turn into string. 575 | * @return {string} a serialized string of the argument 576 | */ 577 | angular.mock.dump = function(object) { 578 | return serialize(object); 579 | 580 | function serialize(object) { 581 | var out; 582 | 583 | if (angular.isElement(object)) { 584 | object = angular.element(object); 585 | out = angular.element('
'); 586 | angular.forEach(object, function(element) { 587 | out.append(angular.element(element).clone()); 588 | }); 589 | out = out.html(); 590 | } else if (angular.isArray(object)) { 591 | out = []; 592 | angular.forEach(object, function(o) { 593 | out.push(serialize(o)); 594 | }); 595 | out = '[ ' + out.join(', ') + ' ]'; 596 | } else if (angular.isObject(object)) { 597 | if (angular.isFunction(object.$eval) && angular.isFunction(object.$apply)) { 598 | out = serializeScope(object); 599 | } else if (object instanceof Error) { 600 | out = object.stack || ('' + object.name + ': ' + object.message); 601 | } else { 602 | out = angular.toJson(object, true); 603 | } 604 | } else { 605 | out = String(object); 606 | } 607 | 608 | return out; 609 | } 610 | 611 | function serializeScope(scope, offset) { 612 | offset = offset || ' '; 613 | var log = [offset + 'Scope(' + scope.$id + '): {']; 614 | for ( var key in scope ) { 615 | if (scope.hasOwnProperty(key) && !key.match(/^(\$|this)/)) { 616 | log.push(' ' + key + ': ' + angular.toJson(scope[key])); 617 | } 618 | } 619 | var child = scope.$$childHead; 620 | while(child) { 621 | log.push(serializeScope(child, offset + ' ')); 622 | child = child.$$nextSibling; 623 | } 624 | log.push('}'); 625 | return log.join('\n' + offset); 626 | } 627 | }; 628 | 629 | /** 630 | * @ngdoc object 631 | * @name ngMock.$httpBackend 632 | * @description 633 | * Fake HTTP backend implementation suitable for unit testing application that use the 634 | * {@link ng.$http $http service}. 635 | * 636 | * *Note*: For fake http backend implementation suitable for end-to-end testing or backend-less 637 | * development please see {@link ngMockE2E.$httpBackend e2e $httpBackend mock}. 638 | * 639 | * During unit testing, we want our unit tests to run quickly and have no external dependencies so 640 | * we don’t want to send {@link https://developer.mozilla.org/en/xmlhttprequest XHR} or 641 | * {@link http://en.wikipedia.org/wiki/JSONP JSONP} requests to a real server. All we really need is 642 | * to verify whether a certain request has been sent or not, or alternatively just let the 643 | * application make requests, respond with pre-trained responses and assert that the end result is 644 | * what we expect it to be. 645 | * 646 | * This mock implementation can be used to respond with static or dynamic responses via the 647 | * `expect` and `when` apis and their shortcuts (`expectGET`, `whenPOST`, etc). 648 | * 649 | * When an Angular application needs some data from a server, it calls the $http service, which 650 | * sends the request to a real server using $httpBackend service. With dependency injection, it is 651 | * easy to inject $httpBackend mock (which has the same API as $httpBackend) and use it to verify 652 | * the requests and respond with some testing data without sending a request to real server. 653 | * 654 | * There are two ways to specify what test data should be returned as http responses by the mock 655 | * backend when the code under test makes http requests: 656 | * 657 | * - `$httpBackend.expect` - specifies a request expectation 658 | * - `$httpBackend.when` - specifies a backend definition 659 | * 660 | * 661 | * # Request Expectations vs Backend Definitions 662 | * 663 | * Request expectations provide a way to make assertions about requests made by the application and 664 | * to define responses for those requests. The test will fail if the expected requests are not made 665 | * or they are made in the wrong order. 666 | * 667 | * Backend definitions allow you to define a fake backend for your application which doesn't assert 668 | * if a particular request was made or not, it just returns a trained response if a request is made. 669 | * The test will pass whether or not the request gets made during testing. 670 | * 671 | * 672 | * 673 | * 674 | * 675 | * 676 | * 677 | * 678 | * 679 | * 680 | * 681 | * 682 | * 683 | * 684 | * 685 | * 686 | * 687 | * 688 | * 689 | * 690 | * 691 | * 692 | * 693 | * 694 | * 695 | * 696 | * 697 | * 698 | * 699 | * 700 | * 701 | * 702 | * 703 | * 704 | *
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
705 | * 706 | * In cases where both backend definitions and request expectations are specified during unit 707 | * testing, the request expectations are evaluated first. 708 | * 709 | * If a request expectation has no response specified, the algorithm will search your backend 710 | * definitions for an appropriate response. 711 | * 712 | * If a request didn't match any expectation or if the expectation doesn't have the response 713 | * defined, the backend definitions are evaluated in sequential order to see if any of them match 714 | * the request. The response from the first matched definition is returned. 715 | * 716 | * 717 | * # Flushing HTTP requests 718 | * 719 | * The $httpBackend used in production, always responds to requests with responses asynchronously. 720 | * If we preserved this behavior in unit testing, we'd have to create async unit tests, which are 721 | * hard to write, follow and maintain. At the same time the testing mock, can't respond 722 | * synchronously because that would change the execution of the code under test. For this reason the 723 | * mock $httpBackend has a `flush()` method, which allows the test to explicitly flush pending 724 | * requests and thus preserving the async api of the backend, while allowing the test to execute 725 | * synchronously. 726 | * 727 | * 728 | * # Unit testing with mock $httpBackend 729 | * 730 | *
 731 |    // controller
 732 |    function MyController($scope, $http) {
 733 |      $http.get('/auth.py').success(function(data) {
 734 |        $scope.user = data;
 735 |      });
 736 | 
 737 |      this.saveMessage = function(message) {
 738 |        $scope.status = 'Saving...';
 739 |        $http.post('/add-msg.py', message).success(function(response) {
 740 |          $scope.status = '';
 741 |        }).error(function() {
 742 |          $scope.status = 'ERROR!';
 743 |        });
 744 |      };
 745 |    }
 746 | 
 747 |    // testing controller
 748 |    var $http;
 749 | 
 750 |    beforeEach(inject(function($injector) {
 751 |      $httpBackend = $injector.get('$httpBackend');
 752 | 
 753 |      // backend definition common for all tests
 754 |      $httpBackend.when('GET', '/auth.py').respond({userId: 'userX'}, {'A-Token': 'xxx'});
 755 |    }));
 756 | 
 757 | 
 758 |    afterEach(function() {
 759 |      $httpBackend.verifyNoOutstandingExpectation();
 760 |      $httpBackend.verifyNoOutstandingRequest();
 761 |    });
 762 | 
 763 | 
 764 |    it('should fetch authentication token', function() {
 765 |      $httpBackend.expectGET('/auth.py');
 766 |      var controller = scope.$new(MyController);
 767 |      $httpBackend.flush();
 768 |    });
 769 | 
 770 | 
 771 |    it('should send msg to server', function() {
 772 |      // now you don’t care about the authentication, but
 773 |      // the controller will still send the request and
 774 |      // $httpBackend will respond without you having to
 775 |      // specify the expectation and response for this request
 776 |      $httpBackend.expectPOST('/add-msg.py', 'message content').respond(201, '');
 777 | 
 778 |      var controller = scope.$new(MyController);
 779 |      $httpBackend.flush();
 780 |      controller.saveMessage('message content');
 781 |      expect(controller.status).toBe('Saving...');
 782 |      $httpBackend.flush();
 783 |      expect(controller.status).toBe('');
 784 |    });
 785 | 
 786 | 
 787 |    it('should send auth header', function() {
 788 |      $httpBackend.expectPOST('/add-msg.py', undefined, function(headers) {
 789 |        // check if the header was send, if it wasn't the expectation won't
 790 |        // match the request and the test will fail
 791 |        return headers['Authorization'] == 'xxx';
 792 |      }).respond(201, '');
 793 | 
 794 |      var controller = scope.$new(MyController);
 795 |      controller.saveMessage('whatever');
 796 |      $httpBackend.flush();
 797 |    });
 798 |    
799 | */ 800 | angular.mock.$HttpBackendProvider = function() { 801 | this.$get = [createHttpBackendMock]; 802 | }; 803 | 804 | /** 805 | * General factory function for $httpBackend mock. 806 | * Returns instance for unit testing (when no arguments specified): 807 | * - passing through is disabled 808 | * - auto flushing is disabled 809 | * 810 | * Returns instance for e2e testing (when `$delegate` and `$browser` specified): 811 | * - passing through (delegating request to real backend) is enabled 812 | * - auto flushing is enabled 813 | * 814 | * @param {Object=} $delegate Real $httpBackend instance (allow passing through if specified) 815 | * @param {Object=} $browser Auto-flushing enabled if specified 816 | * @return {Object} Instance of $httpBackend mock 817 | */ 818 | function createHttpBackendMock($delegate, $browser) { 819 | var definitions = [], 820 | expectations = [], 821 | responses = [], 822 | responsesPush = angular.bind(responses, responses.push); 823 | 824 | function createResponse(status, data, headers) { 825 | if (angular.isFunction(status)) return status; 826 | 827 | return function() { 828 | return angular.isNumber(status) 829 | ? [status, data, headers] 830 | : [200, status, data]; 831 | }; 832 | } 833 | 834 | // TODO(vojta): change params to: method, url, data, headers, callback 835 | function $httpBackend(method, url, data, callback, headers) { 836 | var xhr = new MockXhr(), 837 | expectation = expectations[0], 838 | wasExpected = false; 839 | 840 | function prettyPrint(data) { 841 | return (angular.isString(data) || angular.isFunction(data) || data instanceof RegExp) 842 | ? data 843 | : angular.toJson(data); 844 | } 845 | 846 | if (expectation && expectation.match(method, url)) { 847 | if (!expectation.matchData(data)) 848 | throw Error('Expected ' + expectation + ' with different data\n' + 849 | 'EXPECTED: ' + prettyPrint(expectation.data) + '\nGOT: ' + data); 850 | 851 | if (!expectation.matchHeaders(headers)) 852 | throw Error('Expected ' + expectation + ' with different headers\n' + 853 | 'EXPECTED: ' + prettyPrint(expectation.headers) + '\nGOT: ' + 854 | prettyPrint(headers)); 855 | 856 | expectations.shift(); 857 | 858 | if (expectation.response) { 859 | responses.push(function() { 860 | var response = expectation.response(method, url, data, headers); 861 | xhr.$$respHeaders = response[2]; 862 | callback(response[0], response[1], xhr.getAllResponseHeaders()); 863 | }); 864 | return; 865 | } 866 | wasExpected = true; 867 | } 868 | 869 | var i = -1, definition; 870 | while ((definition = definitions[++i])) { 871 | if (definition.match(method, url, data, headers || {})) { 872 | if (definition.response) { 873 | // if $browser specified, we do auto flush all requests 874 | ($browser ? $browser.defer : responsesPush)(function() { 875 | var response = definition.response(method, url, data, headers); 876 | xhr.$$respHeaders = response[2]; 877 | callback(response[0], response[1], xhr.getAllResponseHeaders()); 878 | }); 879 | } else if (definition.passThrough) { 880 | $delegate(method, url, data, callback, headers); 881 | } else throw Error('No response defined !'); 882 | return; 883 | } 884 | } 885 | throw wasExpected ? 886 | Error('No response defined !') : 887 | Error('Unexpected request: ' + method + ' ' + url + '\n' + 888 | (expectation ? 'Expected ' + expectation : 'No more request expected')); 889 | } 890 | 891 | /** 892 | * @ngdoc method 893 | * @name ngMock.$httpBackend#when 894 | * @methodOf ngMock.$httpBackend 895 | * @description 896 | * Creates a new backend definition. 897 | * 898 | * @param {string} method HTTP method. 899 | * @param {string|RegExp} url HTTP url. 900 | * @param {(string|RegExp)=} data HTTP request body. 901 | * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header 902 | * object and returns true if the headers match the current definition. 903 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 904 | * request is handled. 905 | * 906 | * - respond – `{function([status,] data[, headers])|function(function(method, url, data, headers)}` 907 | * – The respond method takes a set of static data to be returned or a function that can return 908 | * an array containing response status (number), response data (string) and response headers 909 | * (Object). 910 | */ 911 | $httpBackend.when = function(method, url, data, headers) { 912 | var definition = new MockHttpExpectation(method, url, data, headers), 913 | chain = { 914 | respond: function(status, data, headers) { 915 | definition.response = createResponse(status, data, headers); 916 | } 917 | }; 918 | 919 | if ($browser) { 920 | chain.passThrough = function() { 921 | definition.passThrough = true; 922 | }; 923 | } 924 | 925 | definitions.push(definition); 926 | return chain; 927 | }; 928 | 929 | /** 930 | * @ngdoc method 931 | * @name ngMock.$httpBackend#whenGET 932 | * @methodOf ngMock.$httpBackend 933 | * @description 934 | * Creates a new backend definition for GET requests. For more info see `when()`. 935 | * 936 | * @param {string|RegExp} url HTTP url. 937 | * @param {(Object|function(Object))=} headers HTTP headers. 938 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 939 | * request is handled. 940 | */ 941 | 942 | /** 943 | * @ngdoc method 944 | * @name ngMock.$httpBackend#whenHEAD 945 | * @methodOf ngMock.$httpBackend 946 | * @description 947 | * Creates a new backend definition for HEAD requests. For more info see `when()`. 948 | * 949 | * @param {string|RegExp} url HTTP url. 950 | * @param {(Object|function(Object))=} headers HTTP headers. 951 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 952 | * request is handled. 953 | */ 954 | 955 | /** 956 | * @ngdoc method 957 | * @name ngMock.$httpBackend#whenDELETE 958 | * @methodOf ngMock.$httpBackend 959 | * @description 960 | * Creates a new backend definition for DELETE requests. For more info see `when()`. 961 | * 962 | * @param {string|RegExp} url HTTP url. 963 | * @param {(Object|function(Object))=} headers HTTP headers. 964 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 965 | * request is handled. 966 | */ 967 | 968 | /** 969 | * @ngdoc method 970 | * @name ngMock.$httpBackend#whenPOST 971 | * @methodOf ngMock.$httpBackend 972 | * @description 973 | * Creates a new backend definition for POST requests. For more info see `when()`. 974 | * 975 | * @param {string|RegExp} url HTTP url. 976 | * @param {(string|RegExp)=} data HTTP request body. 977 | * @param {(Object|function(Object))=} headers HTTP headers. 978 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 979 | * request is handled. 980 | */ 981 | 982 | /** 983 | * @ngdoc method 984 | * @name ngMock.$httpBackend#whenPUT 985 | * @methodOf ngMock.$httpBackend 986 | * @description 987 | * Creates a new backend definition for PUT requests. For more info see `when()`. 988 | * 989 | * @param {string|RegExp} url HTTP url. 990 | * @param {(string|RegExp)=} data HTTP request body. 991 | * @param {(Object|function(Object))=} headers HTTP headers. 992 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 993 | * request is handled. 994 | */ 995 | 996 | /** 997 | * @ngdoc method 998 | * @name ngMock.$httpBackend#whenJSONP 999 | * @methodOf ngMock.$httpBackend 1000 | * @description 1001 | * Creates a new backend definition for JSONP requests. For more info see `when()`. 1002 | * 1003 | * @param {string|RegExp} url HTTP url. 1004 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 1005 | * request is handled. 1006 | */ 1007 | createShortMethods('when'); 1008 | 1009 | 1010 | /** 1011 | * @ngdoc method 1012 | * @name ngMock.$httpBackend#expect 1013 | * @methodOf ngMock.$httpBackend 1014 | * @description 1015 | * Creates a new request expectation. 1016 | * 1017 | * @param {string} method HTTP method. 1018 | * @param {string|RegExp} url HTTP url. 1019 | * @param {(string|RegExp)=} data HTTP request body. 1020 | * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header 1021 | * object and returns true if the headers match the current expectation. 1022 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 1023 | * request is handled. 1024 | * 1025 | * - respond – `{function([status,] data[, headers])|function(function(method, url, data, headers)}` 1026 | * – The respond method takes a set of static data to be returned or a function that can return 1027 | * an array containing response status (number), response data (string) and response headers 1028 | * (Object). 1029 | */ 1030 | $httpBackend.expect = function(method, url, data, headers) { 1031 | var expectation = new MockHttpExpectation(method, url, data, headers); 1032 | expectations.push(expectation); 1033 | return { 1034 | respond: function(status, data, headers) { 1035 | expectation.response = createResponse(status, data, headers); 1036 | } 1037 | }; 1038 | }; 1039 | 1040 | 1041 | /** 1042 | * @ngdoc method 1043 | * @name ngMock.$httpBackend#expectGET 1044 | * @methodOf ngMock.$httpBackend 1045 | * @description 1046 | * Creates a new request expectation for GET requests. For more info see `expect()`. 1047 | * 1048 | * @param {string|RegExp} url HTTP url. 1049 | * @param {Object=} headers HTTP headers. 1050 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 1051 | * request is handled. See #expect for more info. 1052 | */ 1053 | 1054 | /** 1055 | * @ngdoc method 1056 | * @name ngMock.$httpBackend#expectHEAD 1057 | * @methodOf ngMock.$httpBackend 1058 | * @description 1059 | * Creates a new request expectation for HEAD requests. For more info see `expect()`. 1060 | * 1061 | * @param {string|RegExp} url HTTP url. 1062 | * @param {Object=} headers HTTP headers. 1063 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 1064 | * request is handled. 1065 | */ 1066 | 1067 | /** 1068 | * @ngdoc method 1069 | * @name ngMock.$httpBackend#expectDELETE 1070 | * @methodOf ngMock.$httpBackend 1071 | * @description 1072 | * Creates a new request expectation for DELETE requests. For more info see `expect()`. 1073 | * 1074 | * @param {string|RegExp} url HTTP url. 1075 | * @param {Object=} headers HTTP headers. 1076 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 1077 | * request is handled. 1078 | */ 1079 | 1080 | /** 1081 | * @ngdoc method 1082 | * @name ngMock.$httpBackend#expectPOST 1083 | * @methodOf ngMock.$httpBackend 1084 | * @description 1085 | * Creates a new request expectation for POST requests. For more info see `expect()`. 1086 | * 1087 | * @param {string|RegExp} url HTTP url. 1088 | * @param {(string|RegExp)=} data HTTP request body. 1089 | * @param {Object=} headers HTTP headers. 1090 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 1091 | * request is handled. 1092 | */ 1093 | 1094 | /** 1095 | * @ngdoc method 1096 | * @name ngMock.$httpBackend#expectPUT 1097 | * @methodOf ngMock.$httpBackend 1098 | * @description 1099 | * Creates a new request expectation for PUT requests. For more info see `expect()`. 1100 | * 1101 | * @param {string|RegExp} url HTTP url. 1102 | * @param {(string|RegExp)=} data HTTP request body. 1103 | * @param {Object=} headers HTTP headers. 1104 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 1105 | * request is handled. 1106 | */ 1107 | 1108 | /** 1109 | * @ngdoc method 1110 | * @name ngMock.$httpBackend#expectPATCH 1111 | * @methodOf ngMock.$httpBackend 1112 | * @description 1113 | * Creates a new request expectation for PATCH requests. For more info see `expect()`. 1114 | * 1115 | * @param {string|RegExp} url HTTP url. 1116 | * @param {(string|RegExp)=} data HTTP request body. 1117 | * @param {Object=} headers HTTP headers. 1118 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 1119 | * request is handled. 1120 | */ 1121 | 1122 | /** 1123 | * @ngdoc method 1124 | * @name ngMock.$httpBackend#expectJSONP 1125 | * @methodOf ngMock.$httpBackend 1126 | * @description 1127 | * Creates a new request expectation for JSONP requests. For more info see `expect()`. 1128 | * 1129 | * @param {string|RegExp} url HTTP url. 1130 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 1131 | * request is handled. 1132 | */ 1133 | createShortMethods('expect'); 1134 | 1135 | 1136 | /** 1137 | * @ngdoc method 1138 | * @name ngMock.$httpBackend#flush 1139 | * @methodOf ngMock.$httpBackend 1140 | * @description 1141 | * Flushes all pending requests using the trained responses. 1142 | * 1143 | * @param {number=} count Number of responses to flush (in the order they arrived). If undefined, 1144 | * all pending requests will be flushed. If there are no pending requests when the flush method 1145 | * is called an exception is thrown (as this typically a sign of programming error). 1146 | */ 1147 | $httpBackend.flush = function(count) { 1148 | if (!responses.length) throw Error('No pending request to flush !'); 1149 | 1150 | if (angular.isDefined(count)) { 1151 | while (count--) { 1152 | if (!responses.length) throw Error('No more pending request to flush !'); 1153 | responses.shift()(); 1154 | } 1155 | } else { 1156 | while (responses.length) { 1157 | responses.shift()(); 1158 | } 1159 | } 1160 | $httpBackend.verifyNoOutstandingExpectation(); 1161 | }; 1162 | 1163 | 1164 | /** 1165 | * @ngdoc method 1166 | * @name ngMock.$httpBackend#verifyNoOutstandingExpectation 1167 | * @methodOf ngMock.$httpBackend 1168 | * @description 1169 | * Verifies that all of the requests defined via the `expect` api were made. If any of the 1170 | * requests were not made, verifyNoOutstandingExpectation throws an exception. 1171 | * 1172 | * Typically, you would call this method following each test case that asserts requests using an 1173 | * "afterEach" clause. 1174 | * 1175 | *
1176 |    *   afterEach($httpBackend.verifyExpectations);
1177 |    * 
1178 | */ 1179 | $httpBackend.verifyNoOutstandingExpectation = function() { 1180 | if (expectations.length) { 1181 | throw Error('Unsatisfied requests: ' + expectations.join(', ')); 1182 | } 1183 | }; 1184 | 1185 | 1186 | /** 1187 | * @ngdoc method 1188 | * @name ngMock.$httpBackend#verifyNoOutstandingRequest 1189 | * @methodOf ngMock.$httpBackend 1190 | * @description 1191 | * Verifies that there are no outstanding requests that need to be flushed. 1192 | * 1193 | * Typically, you would call this method following each test case that asserts requests using an 1194 | * "afterEach" clause. 1195 | * 1196 | *
1197 |    *   afterEach($httpBackend.verifyNoOutstandingRequest);
1198 |    * 
1199 | */ 1200 | $httpBackend.verifyNoOutstandingRequest = function() { 1201 | if (responses.length) { 1202 | throw Error('Unflushed requests: ' + responses.length); 1203 | } 1204 | }; 1205 | 1206 | 1207 | /** 1208 | * @ngdoc method 1209 | * @name ngMock.$httpBackend#resetExpectations 1210 | * @methodOf ngMock.$httpBackend 1211 | * @description 1212 | * Resets all request expectations, but preserves all backend definitions. Typically, you would 1213 | * call resetExpectations during a multiple-phase test when you want to reuse the same instance of 1214 | * $httpBackend mock. 1215 | */ 1216 | $httpBackend.resetExpectations = function() { 1217 | expectations.length = 0; 1218 | responses.length = 0; 1219 | }; 1220 | 1221 | return $httpBackend; 1222 | 1223 | 1224 | function createShortMethods(prefix) { 1225 | angular.forEach(['GET', 'DELETE', 'JSONP'], function(method) { 1226 | $httpBackend[prefix + method] = function(url, headers) { 1227 | return $httpBackend[prefix](method, url, undefined, headers) 1228 | } 1229 | }); 1230 | 1231 | angular.forEach(['PUT', 'POST', 'PATCH'], function(method) { 1232 | $httpBackend[prefix + method] = function(url, data, headers) { 1233 | return $httpBackend[prefix](method, url, data, headers) 1234 | } 1235 | }); 1236 | } 1237 | } 1238 | 1239 | function MockHttpExpectation(method, url, data, headers) { 1240 | 1241 | this.data = data; 1242 | this.headers = headers; 1243 | 1244 | this.match = function(m, u, d, h) { 1245 | if (method != m) return false; 1246 | if (!this.matchUrl(u)) return false; 1247 | if (angular.isDefined(d) && !this.matchData(d)) return false; 1248 | if (angular.isDefined(h) && !this.matchHeaders(h)) return false; 1249 | return true; 1250 | }; 1251 | 1252 | this.matchUrl = function(u) { 1253 | if (!url) return true; 1254 | if (angular.isFunction(url.test)) return url.test(u); 1255 | return url == u; 1256 | }; 1257 | 1258 | this.matchHeaders = function(h) { 1259 | if (angular.isUndefined(headers)) return true; 1260 | if (angular.isFunction(headers)) return headers(h); 1261 | return angular.equals(headers, h); 1262 | }; 1263 | 1264 | this.matchData = function(d) { 1265 | if (angular.isUndefined(data)) return true; 1266 | if (data && angular.isFunction(data.test)) return data.test(d); 1267 | if (data && !angular.isString(data)) return angular.toJson(data) == d; 1268 | return data == d; 1269 | }; 1270 | 1271 | this.toString = function() { 1272 | return method + ' ' + url; 1273 | }; 1274 | } 1275 | 1276 | function MockXhr() { 1277 | 1278 | // hack for testing $http, $httpBackend 1279 | MockXhr.$$lastInstance = this; 1280 | 1281 | this.open = function(method, url, async) { 1282 | this.$$method = method; 1283 | this.$$url = url; 1284 | this.$$async = async; 1285 | this.$$reqHeaders = {}; 1286 | this.$$respHeaders = {}; 1287 | }; 1288 | 1289 | this.send = function(data) { 1290 | this.$$data = data; 1291 | }; 1292 | 1293 | this.setRequestHeader = function(key, value) { 1294 | this.$$reqHeaders[key] = value; 1295 | }; 1296 | 1297 | this.getResponseHeader = function(name) { 1298 | // the lookup must be case insensitive, that's why we try two quick lookups and full scan at last 1299 | var header = this.$$respHeaders[name]; 1300 | if (header) return header; 1301 | 1302 | name = angular.lowercase(name); 1303 | header = this.$$respHeaders[name]; 1304 | if (header) return header; 1305 | 1306 | header = undefined; 1307 | angular.forEach(this.$$respHeaders, function(headerVal, headerName) { 1308 | if (!header && angular.lowercase(headerName) == name) header = headerVal; 1309 | }); 1310 | return header; 1311 | }; 1312 | 1313 | this.getAllResponseHeaders = function() { 1314 | var lines = []; 1315 | 1316 | angular.forEach(this.$$respHeaders, function(value, key) { 1317 | lines.push(key + ': ' + value); 1318 | }); 1319 | return lines.join('\n'); 1320 | }; 1321 | 1322 | this.abort = angular.noop; 1323 | } 1324 | 1325 | 1326 | /** 1327 | * @ngdoc function 1328 | * @name ngMock.$timeout 1329 | * @description 1330 | * 1331 | * This service is just a simple decorator for {@link ng.$timeout $timeout} service 1332 | * that adds a "flush" method. 1333 | */ 1334 | 1335 | /** 1336 | * @ngdoc method 1337 | * @name ngMock.$timeout#flush 1338 | * @methodOf ngMock.$timeout 1339 | * @description 1340 | * 1341 | * Flushes the queue of pending tasks. 1342 | */ 1343 | 1344 | /** 1345 | * 1346 | */ 1347 | angular.mock.$RootElementProvider = function() { 1348 | this.$get = function() { 1349 | return angular.element('
'); 1350 | } 1351 | }; 1352 | 1353 | /** 1354 | * @ngdoc overview 1355 | * @name ngMock 1356 | * @description 1357 | * 1358 | * The `ngMock` is an angular module which is used with `ng` module and adds unit-test configuration as well as useful 1359 | * mocks to the {@link AUTO.$injector $injector}. 1360 | */ 1361 | angular.module('ngMock', ['ng']).provider({ 1362 | $browser: angular.mock.$BrowserProvider, 1363 | $exceptionHandler: angular.mock.$ExceptionHandlerProvider, 1364 | $log: angular.mock.$LogProvider, 1365 | $httpBackend: angular.mock.$HttpBackendProvider, 1366 | $rootElement: angular.mock.$RootElementProvider 1367 | }).config(function($provide) { 1368 | $provide.decorator('$timeout', function($delegate, $browser) { 1369 | $delegate.flush = function() { 1370 | $browser.defer.flush(); 1371 | }; 1372 | return $delegate; 1373 | }); 1374 | }); 1375 | 1376 | 1377 | /** 1378 | * @ngdoc overview 1379 | * @name ngMockE2E 1380 | * @description 1381 | * 1382 | * The `ngMockE2E` is an angular module which contains mocks suitable for end-to-end testing. 1383 | * Currently there is only one mock present in this module - 1384 | * the {@link ngMockE2E.$httpBackend e2e $httpBackend} mock. 1385 | */ 1386 | angular.module('ngMockE2E', ['ng']).config(function($provide) { 1387 | $provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator); 1388 | }); 1389 | 1390 | /** 1391 | * @ngdoc object 1392 | * @name ngMockE2E.$httpBackend 1393 | * @description 1394 | * Fake HTTP backend implementation suitable for end-to-end testing or backend-less development of 1395 | * applications that use the {@link ng.$http $http service}. 1396 | * 1397 | * *Note*: For fake http backend implementation suitable for unit testing please see 1398 | * {@link ngMock.$httpBackend unit-testing $httpBackend mock}. 1399 | * 1400 | * This implementation can be used to respond with static or dynamic responses via the `when` api 1401 | * and its shortcuts (`whenGET`, `whenPOST`, etc) and optionally pass through requests to the 1402 | * real $httpBackend for specific requests (e.g. to interact with certain remote apis or to fetch 1403 | * templates from a webserver). 1404 | * 1405 | * As opposed to unit-testing, in an end-to-end testing scenario or in scenario when an application 1406 | * is being developed with the real backend api replaced with a mock, it is often desirable for 1407 | * certain category of requests to bypass the mock and issue a real http request (e.g. to fetch 1408 | * templates or static files from the webserver). To configure the backend with this behavior 1409 | * use the `passThrough` request handler of `when` instead of `respond`. 1410 | * 1411 | * Additionally, we don't want to manually have to flush mocked out requests like we do during unit 1412 | * testing. For this reason the e2e $httpBackend automatically flushes mocked out requests 1413 | * automatically, closely simulating the behavior of the XMLHttpRequest object. 1414 | * 1415 | * To setup the application to run with this http backend, you have to create a module that depends 1416 | * on the `ngMockE2E` and your application modules and defines the fake backend: 1417 | * 1418 | *
1419 |  *   myAppDev = angular.module('myAppDev', ['myApp', 'ngMockE2E']);
1420 |  *   myAppDev.run(function($httpBackend) {
1421 |  *     phones = [{name: 'phone1'}, {name: 'phone2'}];
1422 |  *
1423 |  *     // returns the current list of phones
1424 |  *     $httpBackend.whenGET('/phones').respond(phones);
1425 |  *
1426 |  *     // adds a new phone to the phones array
1427 |  *     $httpBackend.whenPOST('/phones').respond(function(method, url, data) {
1428 |  *       phones.push(angular.fromJSON(data));
1429 |  *     });
1430 |  *     $httpBackend.whenGET(/^\/templates\//).passThrough();
1431 |  *     //...
1432 |  *   });
1433 |  * 
1434 | * 1435 | * Afterwards, bootstrap your app with this new module. 1436 | */ 1437 | 1438 | /** 1439 | * @ngdoc method 1440 | * @name ngMockE2E.$httpBackend#when 1441 | * @methodOf ngMockE2E.$httpBackend 1442 | * @description 1443 | * Creates a new backend definition. 1444 | * 1445 | * @param {string} method HTTP method. 1446 | * @param {string|RegExp} url HTTP url. 1447 | * @param {(string|RegExp)=} data HTTP request body. 1448 | * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header 1449 | * object and returns true if the headers match the current definition. 1450 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that 1451 | * control how a matched request is handled. 1452 | * 1453 | * - respond – `{function([status,] data[, headers])|function(function(method, url, data, headers)}` 1454 | * – The respond method takes a set of static data to be returned or a function that can return 1455 | * an array containing response status (number), response data (string) and response headers 1456 | * (Object). 1457 | * - passThrough – `{function()}` – Any request matching a backend definition with `passThrough` 1458 | * handler, will be pass through to the real backend (an XHR request will be made to the 1459 | * server. 1460 | */ 1461 | 1462 | /** 1463 | * @ngdoc method 1464 | * @name ngMockE2E.$httpBackend#whenGET 1465 | * @methodOf ngMockE2E.$httpBackend 1466 | * @description 1467 | * Creates a new backend definition for GET requests. For more info see `when()`. 1468 | * 1469 | * @param {string|RegExp} url HTTP url. 1470 | * @param {(Object|function(Object))=} headers HTTP headers. 1471 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that 1472 | * control how a matched request is handled. 1473 | */ 1474 | 1475 | /** 1476 | * @ngdoc method 1477 | * @name ngMockE2E.$httpBackend#whenHEAD 1478 | * @methodOf ngMockE2E.$httpBackend 1479 | * @description 1480 | * Creates a new backend definition for HEAD requests. For more info see `when()`. 1481 | * 1482 | * @param {string|RegExp} url HTTP url. 1483 | * @param {(Object|function(Object))=} headers HTTP headers. 1484 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that 1485 | * control how a matched request is handled. 1486 | */ 1487 | 1488 | /** 1489 | * @ngdoc method 1490 | * @name ngMockE2E.$httpBackend#whenDELETE 1491 | * @methodOf ngMockE2E.$httpBackend 1492 | * @description 1493 | * Creates a new backend definition for DELETE requests. For more info see `when()`. 1494 | * 1495 | * @param {string|RegExp} url HTTP url. 1496 | * @param {(Object|function(Object))=} headers HTTP headers. 1497 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that 1498 | * control how a matched request is handled. 1499 | */ 1500 | 1501 | /** 1502 | * @ngdoc method 1503 | * @name ngMockE2E.$httpBackend#whenPOST 1504 | * @methodOf ngMockE2E.$httpBackend 1505 | * @description 1506 | * Creates a new backend definition for POST requests. For more info see `when()`. 1507 | * 1508 | * @param {string|RegExp} url HTTP url. 1509 | * @param {(string|RegExp)=} data HTTP request body. 1510 | * @param {(Object|function(Object))=} headers HTTP headers. 1511 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that 1512 | * control how a matched request is handled. 1513 | */ 1514 | 1515 | /** 1516 | * @ngdoc method 1517 | * @name ngMockE2E.$httpBackend#whenPUT 1518 | * @methodOf ngMockE2E.$httpBackend 1519 | * @description 1520 | * Creates a new backend definition for PUT requests. For more info see `when()`. 1521 | * 1522 | * @param {string|RegExp} url HTTP url. 1523 | * @param {(string|RegExp)=} data HTTP request body. 1524 | * @param {(Object|function(Object))=} headers HTTP headers. 1525 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that 1526 | * control how a matched request is handled. 1527 | */ 1528 | 1529 | /** 1530 | * @ngdoc method 1531 | * @name ngMockE2E.$httpBackend#whenPATCH 1532 | * @methodOf ngMockE2E.$httpBackend 1533 | * @description 1534 | * Creates a new backend definition for PATCH requests. For more info see `when()`. 1535 | * 1536 | * @param {string|RegExp} url HTTP url. 1537 | * @param {(string|RegExp)=} data HTTP request body. 1538 | * @param {(Object|function(Object))=} headers HTTP headers. 1539 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that 1540 | * control how a matched request is handled. 1541 | */ 1542 | 1543 | /** 1544 | * @ngdoc method 1545 | * @name ngMockE2E.$httpBackend#whenJSONP 1546 | * @methodOf ngMockE2E.$httpBackend 1547 | * @description 1548 | * Creates a new backend definition for JSONP requests. For more info see `when()`. 1549 | * 1550 | * @param {string|RegExp} url HTTP url. 1551 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that 1552 | * control how a matched request is handled. 1553 | */ 1554 | angular.mock.e2e = {}; 1555 | angular.mock.e2e.$httpBackendDecorator = ['$delegate', '$browser', createHttpBackendMock]; 1556 | 1557 | 1558 | angular.mock.clearDataCache = function() { 1559 | var key, 1560 | cache = angular.element.cache; 1561 | 1562 | for(key in cache) { 1563 | if (cache.hasOwnProperty(key)) { 1564 | var handle = cache[key].handle; 1565 | 1566 | handle && angular.element(handle.elem).unbind(); 1567 | delete cache[key]; 1568 | } 1569 | } 1570 | }; 1571 | 1572 | 1573 | window.jstestdriver && (function(window) { 1574 | /** 1575 | * Global method to output any number of objects into JSTD console. Useful for debugging. 1576 | */ 1577 | window.dump = function() { 1578 | var args = []; 1579 | angular.forEach(arguments, function(arg) { 1580 | args.push(angular.mock.dump(arg)); 1581 | }); 1582 | jstestdriver.console.log.apply(jstestdriver.console, args); 1583 | if (window.console) { 1584 | window.console.log.apply(window.console, args); 1585 | } 1586 | }; 1587 | })(window); 1588 | 1589 | 1590 | (window.jasmine || window.mocha) && (function(window) { 1591 | 1592 | var currentSpec = null; 1593 | 1594 | beforeEach(function() { 1595 | currentSpec = this; 1596 | }); 1597 | 1598 | afterEach(function() { 1599 | var injector = currentSpec.$injector; 1600 | 1601 | currentSpec.$injector = null; 1602 | currentSpec.$modules = null; 1603 | currentSpec = null; 1604 | 1605 | if (injector) { 1606 | injector.get('$rootElement').unbind(); 1607 | injector.get('$browser').pollFns.length = 0; 1608 | } 1609 | 1610 | angular.mock.clearDataCache(); 1611 | 1612 | // clean up jquery's fragment cache 1613 | angular.forEach(angular.element.fragments, function(val, key) { 1614 | delete angular.element.fragments[key]; 1615 | }); 1616 | 1617 | MockXhr.$$lastInstance = null; 1618 | 1619 | angular.forEach(angular.callbacks, function(val, key) { 1620 | delete angular.callbacks[key]; 1621 | }); 1622 | angular.callbacks.counter = 0; 1623 | }); 1624 | 1625 | function isSpecRunning() { 1626 | return currentSpec && currentSpec.queue.running; 1627 | } 1628 | 1629 | /** 1630 | * @ngdoc function 1631 | * @name angular.mock.module 1632 | * @description 1633 | * 1634 | * *NOTE*: This is function is also published on window for easy access.
1635 | * *NOTE*: Only available with {@link http://pivotal.github.com/jasmine/ jasmine}. 1636 | * 1637 | * This function registers a module configuration code. It collects the configuration information 1638 | * which will be used when the injector is created by {@link angular.mock.inject inject}. 1639 | * 1640 | * See {@link angular.mock.inject inject} for usage example 1641 | * 1642 | * @param {...(string|Function)} fns any number of modules which are represented as string 1643 | * aliases or as anonymous module initialization functions. The modules are used to 1644 | * configure the injector. The 'ng' and 'ngMock' modules are automatically loaded. 1645 | */ 1646 | window.module = angular.mock.module = function() { 1647 | var moduleFns = Array.prototype.slice.call(arguments, 0); 1648 | return isSpecRunning() ? workFn() : workFn; 1649 | ///////////////////// 1650 | function workFn() { 1651 | if (currentSpec.$injector) { 1652 | throw Error('Injector already created, can not register a module!'); 1653 | } else { 1654 | var modules = currentSpec.$modules || (currentSpec.$modules = []); 1655 | angular.forEach(moduleFns, function(module) { 1656 | modules.push(module); 1657 | }); 1658 | } 1659 | } 1660 | }; 1661 | 1662 | /** 1663 | * @ngdoc function 1664 | * @name angular.mock.inject 1665 | * @description 1666 | * 1667 | * *NOTE*: This is function is also published on window for easy access.
1668 | * *NOTE*: Only available with {@link http://pivotal.github.com/jasmine/ jasmine}. 1669 | * 1670 | * The inject function wraps a function into an injectable function. The inject() creates new 1671 | * instance of {@link AUTO.$injector $injector} per test, which is then used for 1672 | * resolving references. 1673 | * 1674 | * See also {@link angular.mock.module module} 1675 | * 1676 | * Example of what a typical jasmine tests looks like with the inject method. 1677 | *
1678 |    *
1679 |    *   angular.module('myApplicationModule', [])
1680 |    *       .value('mode', 'app')
1681 |    *       .value('version', 'v1.0.1');
1682 |    *
1683 |    *
1684 |    *   describe('MyApp', function() {
1685 |    *
1686 |    *     // You need to load modules that you want to test,
1687 |    *     // it loads only the "ng" module by default.
1688 |    *     beforeEach(module('myApplicationModule'));
1689 |    *
1690 |    *
1691 |    *     // inject() is used to inject arguments of all given functions
1692 |    *     it('should provide a version', inject(function(mode, version) {
1693 |    *       expect(version).toEqual('v1.0.1');
1694 |    *       expect(mode).toEqual('app');
1695 |    *     }));
1696 |    *
1697 |    *
1698 |    *     // The inject and module method can also be used inside of the it or beforeEach
1699 |    *     it('should override a version and test the new version is injected', function() {
1700 |    *       // module() takes functions or strings (module aliases)
1701 |    *       module(function($provide) {
1702 |    *         $provide.value('version', 'overridden'); // override version here
1703 |    *       });
1704 |    *
1705 |    *       inject(function(version) {
1706 |    *         expect(version).toEqual('overridden');
1707 |    *       });
1708 |    *     ));
1709 |    *   });
1710 |    *
1711 |    * 
1712 | * 1713 | * @param {...Function} fns any number of functions which will be injected using the injector. 1714 | */ 1715 | window.inject = angular.mock.inject = function() { 1716 | var blockFns = Array.prototype.slice.call(arguments, 0); 1717 | var errorForStack = new Error('Declaration Location'); 1718 | return isSpecRunning() ? workFn() : workFn; 1719 | ///////////////////// 1720 | function workFn() { 1721 | var modules = currentSpec.$modules || []; 1722 | 1723 | modules.unshift('ngMock'); 1724 | modules.unshift('ng'); 1725 | var injector = currentSpec.$injector; 1726 | if (!injector) { 1727 | injector = currentSpec.$injector = angular.injector(modules); 1728 | } 1729 | for(var i = 0, ii = blockFns.length; i < ii; i++) { 1730 | try { 1731 | injector.invoke(blockFns[i] || angular.noop, this); 1732 | } catch (e) { 1733 | if(e.stack) e.stack += '\n' + errorForStack.stack; 1734 | throw e; 1735 | } finally { 1736 | errorForStack = null; 1737 | } 1738 | } 1739 | } 1740 | }; 1741 | })(window); 1742 | -------------------------------------------------------------------------------- /test/lib/chai.js: -------------------------------------------------------------------------------- 1 | !function (name, context, definition) { 2 | if (typeof require === 'function' && typeof exports === 'object' && typeof module === 'object') { 3 | module.exports = definition(); 4 | } else if (typeof define === 'function' && typeof define.amd === 'object') { 5 | define(function () { 6 | return definition(); 7 | }); 8 | } else { 9 | context[name] = definition(); 10 | } 11 | }('chai', this, function () { 12 | 13 | function require(p) { 14 | var path = require.resolve(p) 15 | , mod = require.modules[path]; 16 | if (!mod) throw new Error('failed to require "' + p + '"'); 17 | if (!mod.exports) { 18 | mod.exports = {}; 19 | mod.call(mod.exports, mod, mod.exports, require.relative(path)); 20 | } 21 | return mod.exports; 22 | } 23 | 24 | require.modules = {}; 25 | 26 | require.resolve = function (path) { 27 | var orig = path 28 | , reg = path + '.js' 29 | , index = path + '/index.js'; 30 | return require.modules[reg] && reg 31 | || require.modules[index] && index 32 | || orig; 33 | }; 34 | 35 | require.register = function (path, fn) { 36 | require.modules[path] = fn; 37 | }; 38 | 39 | require.relative = function (parent) { 40 | return function(p){ 41 | if ('.' != p.charAt(0)) return require(p); 42 | 43 | var path = parent.split('/') 44 | , segs = p.split('/'); 45 | path.pop(); 46 | 47 | for (var i = 0; i < segs.length; i++) { 48 | var seg = segs[i]; 49 | if ('..' == seg) path.pop(); 50 | else if ('.' != seg) path.push(seg); 51 | } 52 | 53 | return require(path.join('/')); 54 | }; 55 | }; 56 | 57 | require.alias = function (from, to) { 58 | var fn = require.modules[from]; 59 | require.modules[to] = fn; 60 | }; 61 | 62 | 63 | require.register("chai.js", function(module, exports, require){ 64 | /*! 65 | * chai 66 | * Copyright(c) 2011-2012 Jake Luer 67 | * MIT Licensed 68 | */ 69 | 70 | var used = [] 71 | , exports = module.exports = {}; 72 | 73 | /*! 74 | * Chai version 75 | */ 76 | 77 | exports.version = '1.2.0'; 78 | 79 | /*! 80 | * Primary `Assertion` prototype 81 | */ 82 | 83 | exports.Assertion = require('./chai/assertion'); 84 | 85 | /*! 86 | * Assertion Error 87 | */ 88 | 89 | exports.AssertionError = require('./chai/browser/error'); 90 | 91 | /*! 92 | * Utils for plugins (not exported) 93 | */ 94 | 95 | var util = require('./chai/utils'); 96 | 97 | /** 98 | * # .use(function) 99 | * 100 | * Provides a way to extend the internals of Chai 101 | * 102 | * @param {Function} 103 | * @returns {this} for chaining 104 | * @api public 105 | */ 106 | 107 | exports.use = function (fn) { 108 | if (!~used.indexOf(fn)) { 109 | fn(this, util); 110 | used.push(fn); 111 | } 112 | 113 | return this; 114 | }; 115 | 116 | /*! 117 | * Core Assertions 118 | */ 119 | 120 | var core = require('./chai/core/assertions'); 121 | exports.use(core); 122 | 123 | /*! 124 | * Expect interface 125 | */ 126 | 127 | var expect = require('./chai/interface/expect'); 128 | exports.use(expect); 129 | 130 | /*! 131 | * Should interface 132 | */ 133 | 134 | var should = require('./chai/interface/should'); 135 | exports.use(should); 136 | 137 | /*! 138 | * Assert interface 139 | */ 140 | 141 | var assert = require('./chai/interface/assert'); 142 | exports.use(assert); 143 | 144 | }); // module: chai.js 145 | 146 | require.register("chai/assertion.js", function(module, exports, require){ 147 | /*! 148 | * chai 149 | * http://chaijs.com 150 | * Copyright(c) 2011-2012 Jake Luer 151 | * MIT Licensed 152 | */ 153 | 154 | /*! 155 | * Module dependencies. 156 | */ 157 | 158 | var AssertionError = require('./browser/error') 159 | , util = require('./utils') 160 | , flag = util.flag; 161 | 162 | /*! 163 | * Module export. 164 | */ 165 | 166 | module.exports = Assertion; 167 | 168 | 169 | /*! 170 | * Assertion Constructor 171 | * 172 | * Creates object for chaining. 173 | * 174 | * @api private 175 | */ 176 | 177 | function Assertion (obj, msg, stack) { 178 | flag(this, 'ssfi', stack || arguments.callee); 179 | flag(this, 'object', obj); 180 | flag(this, 'message', msg); 181 | } 182 | 183 | /*! 184 | * ### Assertion.includeStack 185 | * 186 | * User configurable property, influences whether stack trace 187 | * is included in Assertion error message. Default of false 188 | * suppresses stack trace in the error message 189 | * 190 | * Assertion.includeStack = true; // enable stack on error 191 | * 192 | * @api public 193 | */ 194 | 195 | Assertion.includeStack = false; 196 | 197 | Assertion.addProperty = function (name, fn) { 198 | util.addProperty(this.prototype, name, fn); 199 | }; 200 | 201 | Assertion.addMethod = function (name, fn) { 202 | util.addMethod(this.prototype, name, fn); 203 | }; 204 | 205 | Assertion.addChainableMethod = function (name, fn, chainingBehavior) { 206 | util.addChainableMethod(this.prototype, name, fn, chainingBehavior); 207 | }; 208 | 209 | Assertion.overwriteProperty = function (name, fn) { 210 | util.overwriteProperty(this.prototype, name, fn); 211 | }; 212 | 213 | Assertion.overwriteMethod = function (name, fn) { 214 | util.overwriteMethod(this.prototype, name, fn); 215 | }; 216 | 217 | /*! 218 | * ### .assert(expression, message, negateMessage, expected, actual) 219 | * 220 | * Executes an expression and check expectations. Throws AssertionError for reporting if test doesn't pass. 221 | * 222 | * @name assert 223 | * @param {Philosophical} expression to be tested 224 | * @param {String} message to display if fails 225 | * @param {String} negatedMessage to display if negated expression fails 226 | * @param {Mixed} expected value (remember to check for negation) 227 | * @param {Mixed} actual (optional) will default to `this.obj` 228 | * @api private 229 | */ 230 | 231 | Assertion.prototype.assert = function (expr, msg, negateMsg, expected, _actual) { 232 | var ok = util.test(this, arguments); 233 | 234 | if (!ok) { 235 | var msg = util.getMessage(this, arguments) 236 | , actual = util.getActual(this, arguments); 237 | throw new AssertionError({ 238 | message: msg 239 | , actual: actual 240 | , expected: expected 241 | , stackStartFunction: (Assertion.includeStack) ? this.assert : flag(this, 'ssfi') 242 | }); 243 | } 244 | }; 245 | 246 | /*! 247 | * ### ._obj 248 | * 249 | * Quick reference to stored `actual` value for plugin developers. 250 | * 251 | * @api private 252 | */ 253 | 254 | Object.defineProperty(Assertion.prototype, '_obj', 255 | { get: function () { 256 | return flag(this, 'object'); 257 | } 258 | , set: function (val) { 259 | flag(this, 'object', val); 260 | } 261 | }); 262 | 263 | }); // module: chai/assertion.js 264 | 265 | require.register("chai/browser/error.js", function(module, exports, require){ 266 | /*! 267 | * chai 268 | * Copyright(c) 2011-2012 Jake Luer 269 | * MIT Licensed 270 | */ 271 | 272 | module.exports = AssertionError; 273 | 274 | function AssertionError (options) { 275 | options = options || {}; 276 | this.message = options.message; 277 | this.actual = options.actual; 278 | this.expected = options.expected; 279 | this.operator = options.operator; 280 | 281 | if (options.stackStartFunction && Error.captureStackTrace) { 282 | var stackStartFunction = options.stackStartFunction; 283 | Error.captureStackTrace(this, stackStartFunction); 284 | } 285 | } 286 | 287 | AssertionError.prototype = Object.create(Error.prototype); 288 | AssertionError.prototype.name = 'AssertionError'; 289 | AssertionError.prototype.constructor = AssertionError; 290 | 291 | AssertionError.prototype.toString = function() { 292 | return this.message; 293 | }; 294 | 295 | }); // module: chai/browser/error.js 296 | 297 | require.register("chai/core/assertions.js", function(module, exports, require){ 298 | /*! 299 | * chai 300 | * http://chaijs.com 301 | * Copyright(c) 2011-2012 Jake Luer 302 | * MIT Licensed 303 | */ 304 | 305 | module.exports = function (chai, _) { 306 | var Assertion = chai.Assertion 307 | , toString = Object.prototype.toString 308 | , flag = _.flag; 309 | 310 | /** 311 | * ### Language Chains 312 | * 313 | * The following are provide as chainable getters to 314 | * improve the readability of your assertions. They 315 | * do not provide an testing capability unless they 316 | * have been overwritten by a plugin. 317 | * 318 | * **Chains** 319 | * 320 | * - to 321 | * - be 322 | * - been 323 | * - is 324 | * - that 325 | * - and 326 | * - have 327 | * - with 328 | * 329 | * @name language chains 330 | * @api public 331 | */ 332 | 333 | [ 'to', 'be', 'been' 334 | , 'is', 'and', 'have' 335 | , 'with', 'that' ].forEach(function (chain) { 336 | Assertion.addProperty(chain, function () { 337 | return this; 338 | }); 339 | }); 340 | 341 | /** 342 | * ### .not 343 | * 344 | * Negates any of assertions following in the chain. 345 | * 346 | * expect(foo).to.not.equal('bar'); 347 | * expect(goodFn).to.not.throw(Error); 348 | * expect({ foo: 'baz' }).to.have.property('foo') 349 | * .and.not.equal('bar'); 350 | * 351 | * @name not 352 | * @api public 353 | */ 354 | 355 | Assertion.addProperty('not', function () { 356 | flag(this, 'negate', true); 357 | }); 358 | 359 | /** 360 | * ### .deep 361 | * 362 | * Sets the `deep` flag, later used by the `equal` and 363 | * `property` assertions. 364 | * 365 | * expect(foo).to.deep.equal({ bar: 'baz' }); 366 | * expect({ foo: { bar: { baz: 'quux' } } }) 367 | * .to.have.deep.property('foo.bar.baz', 'quux'); 368 | * 369 | * @name deep 370 | * @api public 371 | */ 372 | 373 | Assertion.addProperty('deep', function () { 374 | flag(this, 'deep', true); 375 | }); 376 | 377 | /** 378 | * ### .a(type) 379 | * 380 | * The `a` and `an` assertions are aliases that can be 381 | * used either as language chains or to assert a value's 382 | * type (as revealed by `Object.prototype.toString`). 383 | * 384 | * // typeof 385 | * expect('test').to.be.a('string'); 386 | * expect({ foo: 'bar' }).to.be.an('object'); 387 | * expect(null).to.be.a('null'); 388 | * expect(undefined).to.be.an('undefined'); 389 | * 390 | * // language chain 391 | * expect(foo).to.be.an.instanceof(Foo); 392 | * 393 | * @name a 394 | * @alias an 395 | * @param {String} type 396 | * @param {String} message _optional_ 397 | * @api public 398 | */ 399 | 400 | function an(type, msg) { 401 | if (msg) flag(this, 'message', msg); 402 | var obj = flag(this, 'object') 403 | , klassStart = type.charAt(0).toUpperCase() 404 | , klass = klassStart + type.slice(1) 405 | , article = ~[ 'A', 'E', 'I', 'O', 'U' ].indexOf(klassStart) ? 'an ' : 'a '; 406 | 407 | this.assert( 408 | '[object ' + klass + ']' === toString.call(obj) 409 | , 'expected #{this} to be ' + article + type 410 | , 'expected #{this} not to be ' + article + type 411 | ); 412 | } 413 | 414 | Assertion.addChainableMethod('an', an); 415 | Assertion.addChainableMethod('a', an); 416 | 417 | /** 418 | * ### .include(value) 419 | * 420 | * The `include` and `contain` assertions can be used as either property 421 | * based language chains or as methods to assert the inclusion of an object 422 | * in an array or a substring in a string. When used as language chains, 423 | * they toggle the `contain` flag for the `keys` assertion. 424 | * 425 | * expect([1,2,3]).to.include(2); 426 | * expect('foobar').to.contain('foo'); 427 | * expect({ foo: 'bar', hello: 'universe' }).to.include.keys('foo'); 428 | * 429 | * @name include 430 | * @alias contain 431 | * @param {Object|String|Number} obj 432 | * @param {String} message _optional_ 433 | * @api public 434 | */ 435 | 436 | function includeChainingBehavior () { 437 | flag(this, 'contains', true); 438 | } 439 | 440 | function include (val, msg) { 441 | if (msg) flag(this, 'message', msg); 442 | var obj = flag(this, 'object') 443 | this.assert( 444 | ~obj.indexOf(val) 445 | , 'expected #{this} to include ' + _.inspect(val) 446 | , 'expected #{this} to not include ' + _.inspect(val)); 447 | } 448 | 449 | Assertion.addChainableMethod('include', include, includeChainingBehavior); 450 | Assertion.addChainableMethod('contain', include, includeChainingBehavior); 451 | 452 | /** 453 | * ### .ok 454 | * 455 | * Asserts that the target is truthy. 456 | * 457 | * expect('everthing').to.be.ok; 458 | * expect(1).to.be.ok; 459 | * expect(false).to.not.be.ok; 460 | * expect(undefined).to.not.be.ok; 461 | * expect(null).to.not.be.ok; 462 | * 463 | * @name ok 464 | * @api public 465 | */ 466 | 467 | Assertion.addProperty('ok', function () { 468 | this.assert( 469 | flag(this, 'object') 470 | , 'expected #{this} to be truthy' 471 | , 'expected #{this} to be falsy'); 472 | }); 473 | 474 | /** 475 | * ### .true 476 | * 477 | * Asserts that the target is `true`. 478 | * 479 | * expect(true).to.be.true; 480 | * expect(1).to.not.be.true; 481 | * 482 | * @name true 483 | * @api public 484 | */ 485 | 486 | Assertion.addProperty('true', function () { 487 | this.assert( 488 | true === flag(this, 'object') 489 | , 'expected #{this} to be true' 490 | , 'expected #{this} to be false' 491 | , this.negate ? false : true 492 | ); 493 | }); 494 | 495 | /** 496 | * ### .false 497 | * 498 | * Asserts that the target is `false`. 499 | * 500 | * expect(false).to.be.false; 501 | * expect(0).to.not.be.false; 502 | * 503 | * @name false 504 | * @api public 505 | */ 506 | 507 | Assertion.addProperty('false', function () { 508 | this.assert( 509 | false === flag(this, 'object') 510 | , 'expected #{this} to be false' 511 | , 'expected #{this} to be true' 512 | , this.negate ? true : false 513 | ); 514 | }); 515 | 516 | /** 517 | * ### .null 518 | * 519 | * Asserts that the target is `null`. 520 | * 521 | * expect(null).to.be.null; 522 | * expect(undefined).not.to.be.null; 523 | * 524 | * @name null 525 | * @api public 526 | */ 527 | 528 | Assertion.addProperty('null', function () { 529 | this.assert( 530 | null === flag(this, 'object') 531 | , 'expected #{this} to be null' 532 | , 'expected #{this} not to be null' 533 | ); 534 | }); 535 | 536 | /** 537 | * ### .undefined 538 | * 539 | * Asserts that the target is `undefined`. 540 | * 541 | * expect(undefined).to.be.undefined; 542 | * expect(null).to.not.be.undefined; 543 | * 544 | * @name undefined 545 | * @api public 546 | */ 547 | 548 | Assertion.addProperty('undefined', function () { 549 | this.assert( 550 | undefined === flag(this, 'object') 551 | , 'expected #{this} to be undefined' 552 | , 'expected #{this} not to be undefined' 553 | ); 554 | }); 555 | 556 | /** 557 | * ### .exist 558 | * 559 | * Asserts that the target is neither `null` nor `undefined`. 560 | * 561 | * var foo = 'hi' 562 | * , bar = null 563 | * , baz; 564 | * 565 | * expect(foo).to.exist; 566 | * expect(bar).to.not.exist; 567 | * expect(baz).to.not.exist; 568 | * 569 | * @name exist 570 | * @api public 571 | */ 572 | 573 | Assertion.addProperty('exist', function () { 574 | this.assert( 575 | null != flag(this, 'object') 576 | , 'expected #{this} to exist' 577 | , 'expected #{this} to not exist' 578 | ); 579 | }); 580 | 581 | 582 | /** 583 | * ### .empty 584 | * 585 | * Asserts that the target's length is `0`. For arrays, it checks 586 | * the `length` property. For objects, it gets the count of 587 | * enumerable keys. 588 | * 589 | * expect([]).to.be.empty; 590 | * expect('').to.be.empty; 591 | * expect({}).to.be.empty; 592 | * 593 | * @name empty 594 | * @api public 595 | */ 596 | 597 | Assertion.addProperty('empty', function () { 598 | var obj = flag(this, 'object') 599 | , expected = obj; 600 | 601 | if (Array.isArray(obj) || 'string' === typeof object) { 602 | expected = obj.length; 603 | } else if (typeof obj === 'object') { 604 | expected = Object.keys(obj).length; 605 | } 606 | 607 | this.assert( 608 | !expected 609 | , 'expected #{this} to be empty' 610 | , 'expected #{this} not to be empty' 611 | ); 612 | }); 613 | 614 | /** 615 | * ### .arguments 616 | * 617 | * Asserts that the target is an arguments object. 618 | * 619 | * function test () { 620 | * expect(arguments).to.be.arguments; 621 | * } 622 | * 623 | * @name arguments 624 | * @alias Arguments 625 | * @api public 626 | */ 627 | 628 | function checkArguments () { 629 | var obj = flag(this, 'object') 630 | , type = Object.prototype.toString.call(obj); 631 | this.assert( 632 | '[object Arguments]' === type 633 | , 'expected #{this} to be arguments but got ' + type 634 | , 'expected #{this} to not be arguments' 635 | ); 636 | } 637 | 638 | Assertion.addProperty('arguments', checkArguments); 639 | Assertion.addProperty('Arguments', checkArguments); 640 | 641 | /** 642 | * ### .equal(value) 643 | * 644 | * Asserts that the target is strictly equal (`===`) to `value`. 645 | * Alternately, if the `deep` flag is set, asserts that 646 | * the target is deeply equal to `value`. 647 | * 648 | * expect('hello').to.equal('hello'); 649 | * expect(42).to.equal(42); 650 | * expect(1).to.not.equal(true); 651 | * expect({ foo: 'bar' }).to.not.equal({ foo: 'bar' }); 652 | * expect({ foo: 'bar' }).to.deep.equal({ foo: 'bar' }); 653 | * 654 | * @name equal 655 | * @alias equals 656 | * @alias eq 657 | * @alias deep.equal 658 | * @param {Mixed} value 659 | * @param {String} message _optional_ 660 | * @api public 661 | */ 662 | 663 | function assertEqual (val, msg) { 664 | if (msg) flag(this, 'message', msg); 665 | var obj = flag(this, 'object'); 666 | if (flag(this, 'deep')) { 667 | return this.eql(val); 668 | } else { 669 | this.assert( 670 | val === obj 671 | , 'expected #{this} to equal #{exp}' 672 | , 'expected #{this} to not equal #{exp}' 673 | , val 674 | ); 675 | } 676 | } 677 | 678 | Assertion.addMethod('equal', assertEqual); 679 | Assertion.addMethod('equals', assertEqual); 680 | Assertion.addMethod('eq', assertEqual); 681 | 682 | /** 683 | * ### .eql(value) 684 | * 685 | * Asserts that the target is deeply equal to `value`. 686 | * 687 | * expect({ foo: 'bar' }).to.eql({ foo: 'bar' }); 688 | * expect([ 1, 2, 3 ]).to.eql([ 1, 2, 3 ]); 689 | * 690 | * @name eql 691 | * @param {Mixed} value 692 | * @param {String} message _optional_ 693 | * @api public 694 | */ 695 | 696 | Assertion.addMethod('eql', function (obj, msg) { 697 | if (msg) flag(this, 'message', msg); 698 | this.assert( 699 | _.eql(obj, flag(this, 'object')) 700 | , 'expected #{this} to deeply equal #{exp}' 701 | , 'expected #{this} to not deeply equal #{exp}' 702 | , obj 703 | ); 704 | }); 705 | 706 | /** 707 | * ### .above(value) 708 | * 709 | * Asserts that the target is greater than `value`. 710 | * 711 | * expect(10).to.be.above(5); 712 | * 713 | * Can also be used in conjunction with `length` to 714 | * assert a minimum length. The benefit being a 715 | * more informative error message than if the length 716 | * was supplied directly. 717 | * 718 | * expect('foo').to.have.length.above(2); 719 | * expect([ 1, 2, 3 ]).to.have.length.above(2); 720 | * 721 | * @name above 722 | * @alias gt 723 | * @alias greaterThan 724 | * @param {Number} value 725 | * @param {String} message _optional_ 726 | * @api public 727 | */ 728 | 729 | function assertAbove (n, msg) { 730 | if (msg) flag(this, 'message', msg); 731 | var obj = flag(this, 'object'); 732 | if (flag(this, 'doLength')) { 733 | new Assertion(obj, msg).to.have.property('length'); 734 | var len = obj.length; 735 | this.assert( 736 | len > n 737 | , 'expected #{this} to have a length above #{exp} but got #{act}' 738 | , 'expected #{this} to not have a length above #{exp}' 739 | , n 740 | , len 741 | ); 742 | } else { 743 | this.assert( 744 | obj > n 745 | , 'expected #{this} to be above ' + n 746 | , 'expected #{this} to be below ' + n 747 | ); 748 | } 749 | } 750 | 751 | Assertion.addMethod('above', assertAbove); 752 | Assertion.addMethod('gt', assertAbove); 753 | Assertion.addMethod('greaterThan', assertAbove); 754 | 755 | /** 756 | * ### .below(value) 757 | * 758 | * Asserts that the target is less than `value`. 759 | * 760 | * expect(5).to.be.below(10); 761 | * 762 | * Can also be used in conjunction with `length` to 763 | * assert a maximum length. The benefit being a 764 | * more informative error message than if the length 765 | * was supplied directly. 766 | * 767 | * expect('foo').to.have.length.below(4); 768 | * expect([ 1, 2, 3 ]).to.have.length.below(4); 769 | * 770 | * @name below 771 | * @alias lt 772 | * @alias lessThan 773 | * @param {Number} value 774 | * @param {String} message _optional_ 775 | * @api public 776 | */ 777 | 778 | function assertBelow (n, msg) { 779 | if (msg) flag(this, 'message', msg); 780 | var obj = flag(this, 'object'); 781 | if (flag(this, 'doLength')) { 782 | new Assertion(obj, msg).to.have.property('length'); 783 | var len = obj.length; 784 | this.assert( 785 | len < n 786 | , 'expected #{this} to have a length below #{exp} but got #{act}' 787 | , 'expected #{this} to not have a length below #{exp}' 788 | , n 789 | , len 790 | ); 791 | } else { 792 | this.assert( 793 | obj < n 794 | , 'expected #{this} to be below ' + n 795 | , 'expected #{this} to be above ' + n 796 | ); 797 | } 798 | } 799 | 800 | Assertion.addMethod('below', assertBelow); 801 | Assertion.addMethod('lt', assertBelow); 802 | Assertion.addMethod('lessThan', assertBelow); 803 | 804 | /** 805 | * ### .within(start, finish) 806 | * 807 | * Asserts that the target is within a range. 808 | * 809 | * expect(7).to.be.within(5,10); 810 | * 811 | * Can also be used in conjunction with `length` to 812 | * assert a length range. The benefit being a 813 | * more informative error message than if the length 814 | * was supplied directly. 815 | * 816 | * expect('foo').to.have.length.within(2,4); 817 | * expect([ 1, 2, 3 ]).to.have.length.within(2,4); 818 | * 819 | * @name within 820 | * @param {Number} start lowerbound inclusive 821 | * @param {Number} finish upperbound inclusive 822 | * @param {String} message _optional_ 823 | * @api public 824 | */ 825 | 826 | Assertion.addMethod('within', function (start, finish, msg) { 827 | if (msg) flag(this, 'message', msg); 828 | var obj = flag(this, 'object') 829 | , range = start + '..' + finish; 830 | if (flag(this, 'doLength')) { 831 | new Assertion(obj, msg).to.have.property('length'); 832 | var len = obj.length; 833 | this.assert( 834 | len >= start && len <= finish 835 | , 'expected #{this} to have a length within ' + range 836 | , 'expected #{this} to not have a length within ' + range 837 | ); 838 | } else { 839 | this.assert( 840 | obj >= start && obj <= finish 841 | , 'expected #{this} to be within ' + range 842 | , 'expected #{this} to not be within ' + range 843 | ); 844 | } 845 | }); 846 | 847 | /** 848 | * ### .instanceof(constructor) 849 | * 850 | * Asserts that the target is an instance of `constructor`. 851 | * 852 | * var Tea = function (name) { this.name = name; } 853 | * , Chai = new Tea('chai'); 854 | * 855 | * expect(Chai).to.be.an.instanceof(Tea); 856 | * expect([ 1, 2, 3 ]).to.be.instanceof(Array); 857 | * 858 | * @name instanceof 859 | * @param {Constructor} constructor 860 | * @param {String} message _optional_ 861 | * @alias instanceOf 862 | * @api public 863 | */ 864 | 865 | function assertInstanceOf (constructor, msg) { 866 | if (msg) flag(this, 'message', msg); 867 | var name = _.getName(constructor); 868 | this.assert( 869 | flag(this, 'object') instanceof constructor 870 | , 'expected #{this} to be an instance of ' + name 871 | , 'expected #{this} to not be an instance of ' + name 872 | ); 873 | }; 874 | 875 | Assertion.addMethod('instanceof', assertInstanceOf); 876 | Assertion.addMethod('instanceOf', assertInstanceOf); 877 | 878 | /** 879 | * ### .property(name, [value]) 880 | * 881 | * Asserts that the target has a property `name`, optionally asserting that 882 | * the value of that property is strictly equal to `value`. 883 | * If the `deep` flag is set, you can use dot- and bracket-notation for deep 884 | * references into objects and arrays. 885 | * 886 | * // simple referencing 887 | * var obj = { foo: 'bar' }; 888 | * expect(obj).to.have.property('foo'); 889 | * expect(obj).to.have.property('foo', 'bar'); 890 | * 891 | * // deep referencing 892 | * var deepObj = { 893 | * green: { tea: 'matcha' } 894 | * , teas: [ 'chai', 'matcha', { tea: 'konacha' } ] 895 | * }; 896 | 897 | * expect(deepObj).to.have.deep.property('green.tea', 'matcha'); 898 | * expect(deepObj).to.have.deep.property('teas[1]', 'matcha'); 899 | * expect(deepObj).to.have.deep.property('teas[2].tea', 'konacha'); 900 | * 901 | * You can also use an array as the starting point of a `deep.property` 902 | * assertion, or traverse nested arrays. 903 | * 904 | * var arr = [ 905 | * [ 'chai', 'matcha', 'konacha' ] 906 | * , [ { tea: 'chai' } 907 | * , { tea: 'matcha' } 908 | * , { tea: 'konacha' } ] 909 | * ]; 910 | * 911 | * expect(arr).to.have.deep.property('[0][1]', 'matcha'); 912 | * expect(arr).to.have.deep.property('[1][2].tea', 'konacha'); 913 | * 914 | * Furthermore, `property` changes the subject of the assertion 915 | * to be the value of that property from the original object. This 916 | * permits for further chainable assertions on that property. 917 | * 918 | * expect(obj).to.have.property('foo') 919 | * .that.is.a('string'); 920 | * expect(deepObj).to.have.property('green') 921 | * .that.is.an('object') 922 | * .that.deep.equals({ tea: 'matcha' }); 923 | * expect(deepObj).to.have.property('teas') 924 | * .that.is.an('array') 925 | * .with.deep.property('[2]') 926 | * .that.deep.equals({ tea: 'konacha' }); 927 | * 928 | * @name property 929 | * @alias deep.property 930 | * @param {String} name 931 | * @param {Mixed} value (optional) 932 | * @param {String} message _optional_ 933 | * @returns value of property for chaining 934 | * @api public 935 | */ 936 | 937 | Assertion.addMethod('property', function (name, val, msg) { 938 | if (msg) flag(this, 'message', msg); 939 | 940 | var descriptor = flag(this, 'deep') ? 'deep property ' : 'property ' 941 | , negate = flag(this, 'negate') 942 | , obj = flag(this, 'object') 943 | , value = flag(this, 'deep') 944 | ? _.getPathValue(name, obj) 945 | : obj[name]; 946 | 947 | if (negate && undefined !== val) { 948 | if (undefined === value) { 949 | msg = (msg != null) ? msg + ': ' : ''; 950 | throw new Error(msg + _.inspect(obj) + ' has no ' + descriptor + _.inspect(name)); 951 | } 952 | } else { 953 | this.assert( 954 | undefined !== value 955 | , 'expected #{this} to have a ' + descriptor + _.inspect(name) 956 | , 'expected #{this} to not have ' + descriptor + _.inspect(name)); 957 | } 958 | 959 | if (undefined !== val) { 960 | this.assert( 961 | val === value 962 | , 'expected #{this} to have a ' + descriptor + _.inspect(name) + ' of #{exp}, but got #{act}' 963 | , 'expected #{this} to not have a ' + descriptor + _.inspect(name) + ' of #{act}' 964 | , val 965 | , value 966 | ); 967 | } 968 | 969 | flag(this, 'object', value); 970 | }); 971 | 972 | 973 | /** 974 | * ### .ownProperty(name) 975 | * 976 | * Asserts that the target has an own property `name`. 977 | * 978 | * expect('test').to.have.ownProperty('length'); 979 | * 980 | * @name ownProperty 981 | * @alias haveOwnProperty 982 | * @param {String} name 983 | * @param {String} message _optional_ 984 | * @api public 985 | */ 986 | 987 | function assertOwnProperty (name, msg) { 988 | if (msg) flag(this, 'message', msg); 989 | var obj = flag(this, 'object'); 990 | this.assert( 991 | obj.hasOwnProperty(name) 992 | , 'expected #{this} to have own property ' + _.inspect(name) 993 | , 'expected #{this} to not have own property ' + _.inspect(name) 994 | ); 995 | } 996 | 997 | Assertion.addMethod('ownProperty', assertOwnProperty); 998 | Assertion.addMethod('haveOwnProperty', assertOwnProperty); 999 | 1000 | /** 1001 | * ### .length(value) 1002 | * 1003 | * Asserts that the target's `length` property has 1004 | * the expected value. 1005 | * 1006 | * expect([ 1, 2, 3]).to.have.length(3); 1007 | * expect('foobar').to.have.length(6); 1008 | * 1009 | * Can also be used as a chain precursor to a value 1010 | * comparison for the length property. 1011 | * 1012 | * expect('foo').to.have.length.above(2); 1013 | * expect([ 1, 2, 3 ]).to.have.length.above(2); 1014 | * expect('foo').to.have.length.below(4); 1015 | * expect([ 1, 2, 3 ]).to.have.length.below(4); 1016 | * expect('foo').to.have.length.within(2,4); 1017 | * expect([ 1, 2, 3 ]).to.have.length.within(2,4); 1018 | * 1019 | * @name length 1020 | * @alias lengthOf 1021 | * @param {Number} length 1022 | * @param {String} message _optional_ 1023 | * @api public 1024 | */ 1025 | 1026 | function assertLengthChain () { 1027 | flag(this, 'doLength', true); 1028 | } 1029 | 1030 | function assertLength (n, msg) { 1031 | if (msg) flag(this, 'message', msg); 1032 | var obj = flag(this, 'object'); 1033 | new Assertion(obj, msg).to.have.property('length'); 1034 | var len = obj.length; 1035 | 1036 | this.assert( 1037 | len == n 1038 | , 'expected #{this} to have a length of #{exp} but got #{act}' 1039 | , 'expected #{this} to not have a length of #{act}' 1040 | , n 1041 | , len 1042 | ); 1043 | } 1044 | 1045 | Assertion.addChainableMethod('length', assertLength, assertLengthChain); 1046 | Assertion.addMethod('lengthOf', assertLength, assertLengthChain); 1047 | 1048 | /** 1049 | * ### .match(regexp) 1050 | * 1051 | * Asserts that the target matches a regular expression. 1052 | * 1053 | * expect('foobar').to.match(/^foo/); 1054 | * 1055 | * @name match 1056 | * @param {RegExp} RegularExpression 1057 | * @param {String} message _optional_ 1058 | * @api public 1059 | */ 1060 | 1061 | Assertion.addMethod('match', function (re, msg) { 1062 | if (msg) flag(this, 'message', msg); 1063 | var obj = flag(this, 'object'); 1064 | this.assert( 1065 | re.exec(obj) 1066 | , 'expected #{this} to match ' + re 1067 | , 'expected #{this} not to match ' + re 1068 | ); 1069 | }); 1070 | 1071 | /** 1072 | * ### .string(string) 1073 | * 1074 | * Asserts that the string target contains another string. 1075 | * 1076 | * expect('foobar').to.have.string('bar'); 1077 | * 1078 | * @name string 1079 | * @param {String} string 1080 | * @param {String} message _optional_ 1081 | * @api public 1082 | */ 1083 | 1084 | Assertion.addMethod('string', function (str, msg) { 1085 | if (msg) flag(this, 'message', msg); 1086 | var obj = flag(this, 'object'); 1087 | new Assertion(obj, msg).is.a('string'); 1088 | 1089 | this.assert( 1090 | ~obj.indexOf(str) 1091 | , 'expected #{this} to contain ' + _.inspect(str) 1092 | , 'expected #{this} to not contain ' + _.inspect(str) 1093 | ); 1094 | }); 1095 | 1096 | 1097 | /** 1098 | * ### .keys(key1, [key2], [...]) 1099 | * 1100 | * Asserts that the target has exactly the given keys, or 1101 | * asserts the inclusion of some keys when using the 1102 | * `include` or `contain` modifiers. 1103 | * 1104 | * expect({ foo: 1, bar: 2 }).to.have.keys(['foo', 'bar']); 1105 | * expect({ foo: 1, bar: 2, baz: 3 }).to.contain.keys('foo', 'bar'); 1106 | * 1107 | * @name keys 1108 | * @alias key 1109 | * @param {String...|Array} keys 1110 | * @api public 1111 | */ 1112 | 1113 | function assertKeys (keys) { 1114 | var obj = flag(this, 'object') 1115 | , str 1116 | , ok = true; 1117 | 1118 | keys = keys instanceof Array 1119 | ? keys 1120 | : Array.prototype.slice.call(arguments); 1121 | 1122 | if (!keys.length) throw new Error('keys required'); 1123 | 1124 | var actual = Object.keys(obj) 1125 | , len = keys.length; 1126 | 1127 | // Inclusion 1128 | ok = keys.every(function(key){ 1129 | return ~actual.indexOf(key); 1130 | }); 1131 | 1132 | // Strict 1133 | if (!flag(this, 'negate') && !flag(this, 'contains')) { 1134 | ok = ok && keys.length == actual.length; 1135 | } 1136 | 1137 | // Key string 1138 | if (len > 1) { 1139 | keys = keys.map(function(key){ 1140 | return _.inspect(key); 1141 | }); 1142 | var last = keys.pop(); 1143 | str = keys.join(', ') + ', and ' + last; 1144 | } else { 1145 | str = _.inspect(keys[0]); 1146 | } 1147 | 1148 | // Form 1149 | str = (len > 1 ? 'keys ' : 'key ') + str; 1150 | 1151 | // Have / include 1152 | str = (flag(this, 'contains') ? 'contain ' : 'have ') + str; 1153 | 1154 | // Assertion 1155 | this.assert( 1156 | ok 1157 | , 'expected #{this} to ' + str 1158 | , 'expected #{this} to not ' + str 1159 | ); 1160 | } 1161 | 1162 | Assertion.addMethod('keys', assertKeys); 1163 | Assertion.addMethod('key', assertKeys); 1164 | 1165 | /** 1166 | * ### .throw(constructor) 1167 | * 1168 | * Asserts that the function target will throw a specific error, or specific type of error 1169 | * (as determined using `instanceof`), optionally with a RegExp or string inclusion test 1170 | * for the error's message. 1171 | * 1172 | * var err = new ReferenceError('This is a bad function.'); 1173 | * var fn = function () { throw err; } 1174 | * expect(fn).to.throw(ReferenceError); 1175 | * expect(fn).to.throw(Error); 1176 | * expect(fn).to.throw(/bad function/); 1177 | * expect(fn).to.not.throw('good function'); 1178 | * expect(fn).to.throw(ReferenceError, /bad function/); 1179 | * expect(fn).to.throw(err); 1180 | * expect(fn).to.not.throw(new RangeError('Out of range.')); 1181 | * 1182 | * Please note that when a throw expectation is negated, it will check each 1183 | * parameter independently, starting with error constructor type. The appropriate way 1184 | * to check for the existence of a type of error but for a message that does not match 1185 | * is to use `and`. 1186 | * 1187 | * expect(fn).to.throw(ReferenceError) 1188 | * .and.not.throw(/good function/); 1189 | * 1190 | * @name throw 1191 | * @alias throws 1192 | * @alias Throw 1193 | * @param {ErrorConstructor} constructor 1194 | * @param {String|RegExp} expected error message 1195 | * @param {String} message _optional_ 1196 | * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types 1197 | * @api public 1198 | */ 1199 | 1200 | function assertThrows (constructor, errMsg, msg) { 1201 | if (msg) flag(this, 'message', msg); 1202 | var obj = flag(this, 'object'); 1203 | new Assertion(obj, msg).is.a('function'); 1204 | 1205 | var thrown = false 1206 | , desiredError = null 1207 | , name = null 1208 | , thrownError = null; 1209 | 1210 | if (arguments.length === 0) { 1211 | errMsg = null; 1212 | constructor = null; 1213 | } else if (constructor && (constructor instanceof RegExp || 'string' === typeof constructor)) { 1214 | errMsg = constructor; 1215 | constructor = null; 1216 | } else if (constructor && constructor instanceof Error) { 1217 | desiredError = constructor; 1218 | constructor = null; 1219 | errMsg = null; 1220 | } else if (typeof constructor === 'function') { 1221 | name = (new constructor()).name; 1222 | } else { 1223 | constructor = null; 1224 | } 1225 | 1226 | try { 1227 | obj(); 1228 | } catch (err) { 1229 | // first, check desired error 1230 | if (desiredError) { 1231 | this.assert( 1232 | err === desiredError 1233 | , 'expected #{this} to throw ' + _.inspect(desiredError) + ' but ' + _.inspect(err) + ' was thrown' 1234 | , 'expected #{this} to not throw ' + _.inspect(desiredError) 1235 | ); 1236 | return this; 1237 | } 1238 | // next, check constructor 1239 | if (constructor) { 1240 | this.assert( 1241 | err instanceof constructor 1242 | , 'expected #{this} to throw ' + name + ' but ' + _.inspect(err) + ' was thrown' 1243 | , 'expected #{this} to not throw ' + name + ' but ' + _.inspect(err) + ' was thrown'); 1244 | if (!errMsg) return this; 1245 | } 1246 | // next, check message 1247 | if (err.message && errMsg && errMsg instanceof RegExp) { 1248 | this.assert( 1249 | errMsg.exec(err.message) 1250 | , 'expected #{this} to throw error matching ' + errMsg + ' but got ' + _.inspect(err.message) 1251 | , 'expected #{this} to throw error not matching ' + errMsg 1252 | ); 1253 | return this; 1254 | } else if (err.message && errMsg && 'string' === typeof errMsg) { 1255 | this.assert( 1256 | ~err.message.indexOf(errMsg) 1257 | , 'expected #{this} to throw error including #{exp} but got #{act}' 1258 | , 'expected #{this} to throw error not including #{act}' 1259 | , errMsg 1260 | , err.message 1261 | ); 1262 | return this; 1263 | } else { 1264 | thrown = true; 1265 | thrownError = err; 1266 | } 1267 | } 1268 | 1269 | var expectedThrown = name ? name : desiredError ? _.inspect(desiredError) : 'an error'; 1270 | var actuallyGot = '' 1271 | if (thrown) { 1272 | actuallyGot = ' but ' + _.inspect(thrownError) + ' was thrown' 1273 | } 1274 | 1275 | this.assert( 1276 | thrown === true 1277 | , 'expected #{this} to throw ' + expectedThrown + actuallyGot 1278 | , 'expected #{this} to not throw ' + expectedThrown + actuallyGot 1279 | ); 1280 | }; 1281 | 1282 | Assertion.addMethod('throw', assertThrows); 1283 | Assertion.addMethod('throws', assertThrows); 1284 | Assertion.addMethod('Throw', assertThrows); 1285 | 1286 | /** 1287 | * ### .respondTo(method) 1288 | * 1289 | * Asserts that the object or class target will respond to a method. 1290 | * 1291 | * Klass.prototype.bar = function(){}; 1292 | * expect(Klass).to.respondTo('bar'); 1293 | * expect(obj).to.respondTo('bar'); 1294 | * 1295 | * To check if a constructor will respond to a static function, 1296 | * set the `itself` flag. 1297 | * 1298 | * Klass.baz = function(){}; 1299 | * expect(Klass).itself.to.respondTo('baz'); 1300 | * 1301 | * @name respondTo 1302 | * @param {String} method 1303 | * @param {String} message _optional_ 1304 | * @api public 1305 | */ 1306 | 1307 | Assertion.addMethod('respondTo', function (method, msg) { 1308 | if (msg) flag(this, 'message', msg); 1309 | var obj = flag(this, 'object') 1310 | , itself = flag(this, 'itself') 1311 | , context = ('function' === typeof obj && !itself) 1312 | ? obj.prototype[method] 1313 | : obj[method]; 1314 | 1315 | this.assert( 1316 | 'function' === typeof context 1317 | , 'expected #{this} to respond to ' + _.inspect(method) 1318 | , 'expected #{this} to not respond to ' + _.inspect(method) 1319 | ); 1320 | }); 1321 | 1322 | /** 1323 | * ### .itself 1324 | * 1325 | * Sets the `itself` flag, later used by the `respondTo` assertion. 1326 | * 1327 | * function Foo() {} 1328 | * Foo.bar = function() {} 1329 | * Foo.prototype.baz = function() {} 1330 | * 1331 | * expect(Foo).itself.to.respondTo('bar'); 1332 | * expect(Foo).itself.not.to.respondTo('baz'); 1333 | * 1334 | * @name itself 1335 | * @api public 1336 | */ 1337 | 1338 | Assertion.addProperty('itself', function () { 1339 | flag(this, 'itself', true); 1340 | }); 1341 | 1342 | /** 1343 | * ### .satisfy(method) 1344 | * 1345 | * Asserts that the target passes a given truth test. 1346 | * 1347 | * expect(1).to.satisfy(function(num) { return num > 0; }); 1348 | * 1349 | * @name satisfy 1350 | * @param {Function} matcher 1351 | * @param {String} message _optional_ 1352 | * @api public 1353 | */ 1354 | 1355 | Assertion.addMethod('satisfy', function (matcher, msg) { 1356 | if (msg) flag(this, 'message', msg); 1357 | var obj = flag(this, 'object'); 1358 | this.assert( 1359 | matcher(obj) 1360 | , 'expected #{this} to satisfy ' + _.inspect(matcher) 1361 | , 'expected #{this} to not satisfy' + _.inspect(matcher) 1362 | , this.negate ? false : true 1363 | , matcher(obj) 1364 | ); 1365 | }); 1366 | 1367 | /** 1368 | * ### .closeTo(expected, delta) 1369 | * 1370 | * Asserts that the target is equal `expected`, to within a +/- `delta` range. 1371 | * 1372 | * expect(1.5).to.be.closeTo(1, 0.5); 1373 | * 1374 | * @name closeTo 1375 | * @param {Number} expected 1376 | * @param {Number} delta 1377 | * @param {String} message _optional_ 1378 | * @api public 1379 | */ 1380 | 1381 | Assertion.addMethod('closeTo', function (expected, delta, msg) { 1382 | if (msg) flag(this, 'message', msg); 1383 | var obj = flag(this, 'object'); 1384 | this.assert( 1385 | Math.abs(obj - expected) <= delta 1386 | , 'expected #{this} to be close to ' + expected + ' +/- ' + delta 1387 | , 'expected #{this} not to be close to ' + expected + ' +/- ' + delta 1388 | ); 1389 | }); 1390 | 1391 | }; 1392 | 1393 | }); // module: chai/core/assertions.js 1394 | 1395 | require.register("chai/interface/assert.js", function(module, exports, require){ 1396 | /*! 1397 | * chai 1398 | * Copyright(c) 2011-2012 Jake Luer 1399 | * MIT Licensed 1400 | */ 1401 | 1402 | 1403 | module.exports = function (chai, util) { 1404 | 1405 | /*! 1406 | * Chai dependencies. 1407 | */ 1408 | 1409 | var Assertion = chai.Assertion 1410 | , flag = util.flag; 1411 | 1412 | /*! 1413 | * Module export. 1414 | */ 1415 | 1416 | /** 1417 | * ### assert(expression, message) 1418 | * 1419 | * Write your own test expressions. 1420 | * 1421 | * assert('foo' !== 'bar', 'foo is not bar'); 1422 | * assert(Array.isArray([]), 'empty arrays are arrays'); 1423 | * 1424 | * @param {Mixed} expression to test for truthiness 1425 | * @param {String} message to display on error 1426 | * @name assert 1427 | * @api public 1428 | */ 1429 | 1430 | var assert = chai.assert = function (express, errmsg) { 1431 | var test = new Assertion(null); 1432 | test.assert( 1433 | express 1434 | , errmsg 1435 | , '[ negation message unavailable ]' 1436 | ); 1437 | }; 1438 | 1439 | /** 1440 | * ### .fail(actual, expected, [message], [operator]) 1441 | * 1442 | * Throw a failure. Node.js `assert` module-compatible. 1443 | * 1444 | * @name fail 1445 | * @param {Mixed} actual 1446 | * @param {Mixed} expected 1447 | * @param {String} message 1448 | * @param {String} operator 1449 | * @api public 1450 | */ 1451 | 1452 | assert.fail = function (actual, expected, message, operator) { 1453 | throw new chai.AssertionError({ 1454 | actual: actual 1455 | , expected: expected 1456 | , message: message 1457 | , operator: operator 1458 | , stackStartFunction: assert.fail 1459 | }); 1460 | }; 1461 | 1462 | /** 1463 | * ### .ok(object, [message]) 1464 | * 1465 | * Asserts that `object` is truthy. 1466 | * 1467 | * assert.ok('everything', 'everything is ok'); 1468 | * assert.ok(false, 'this will fail'); 1469 | * 1470 | * @name ok 1471 | * @param {Mixed} object to test 1472 | * @param {String} message 1473 | * @api public 1474 | */ 1475 | 1476 | assert.ok = function (val, msg) { 1477 | new Assertion(val, msg).is.ok; 1478 | }; 1479 | 1480 | /** 1481 | * ### .equal(actual, expected, [message]) 1482 | * 1483 | * Asserts non-strict equality (`==`) of `actual` and `expected`. 1484 | * 1485 | * assert.equal(3, '3', '== coerces values to strings'); 1486 | * 1487 | * @name equal 1488 | * @param {Mixed} actual 1489 | * @param {Mixed} expected 1490 | * @param {String} message 1491 | * @api public 1492 | */ 1493 | 1494 | assert.equal = function (act, exp, msg) { 1495 | var test = new Assertion(act, msg); 1496 | 1497 | test.assert( 1498 | exp == flag(test, 'object') 1499 | , 'expected #{this} to equal #{exp}' 1500 | , 'expected #{this} to not equal #{act}' 1501 | , exp 1502 | , act 1503 | ); 1504 | }; 1505 | 1506 | /** 1507 | * ### .notEqual(actual, expected, [message]) 1508 | * 1509 | * Asserts non-strict inequality (`!=`) of `actual` and `expected`. 1510 | * 1511 | * assert.notEqual(3, 4, 'these numbers are not equal'); 1512 | * 1513 | * @name notEqual 1514 | * @param {Mixed} actual 1515 | * @param {Mixed} expected 1516 | * @param {String} message 1517 | * @api public 1518 | */ 1519 | 1520 | assert.notEqual = function (act, exp, msg) { 1521 | var test = new Assertion(act, msg); 1522 | 1523 | test.assert( 1524 | exp != flag(test, 'object') 1525 | , 'expected #{this} to not equal #{exp}' 1526 | , 'expected #{this} to equal #{act}' 1527 | , exp 1528 | , act 1529 | ); 1530 | }; 1531 | 1532 | /** 1533 | * ### .strictEqual(actual, expected, [message]) 1534 | * 1535 | * Asserts strict equality (`===`) of `actual` and `expected`. 1536 | * 1537 | * assert.strictEqual(true, true, 'these booleans are strictly equal'); 1538 | * 1539 | * @name strictEqual 1540 | * @param {Mixed} actual 1541 | * @param {Mixed} expected 1542 | * @param {String} message 1543 | * @api public 1544 | */ 1545 | 1546 | assert.strictEqual = function (act, exp, msg) { 1547 | new Assertion(act, msg).to.equal(exp); 1548 | }; 1549 | 1550 | /** 1551 | * ### .notStrictEqual(actual, expected, [message]) 1552 | * 1553 | * Asserts strict inequality (`!==`) of `actual` and `expected`. 1554 | * 1555 | * assert.notStrictEqual(3, '3', 'no coercion for strict equality'); 1556 | * 1557 | * @name notStrictEqual 1558 | * @param {Mixed} actual 1559 | * @param {Mixed} expected 1560 | * @param {String} message 1561 | * @api public 1562 | */ 1563 | 1564 | assert.notStrictEqual = function (act, exp, msg) { 1565 | new Assertion(act, msg).to.not.equal(exp); 1566 | }; 1567 | 1568 | /** 1569 | * ### .deepEqual(actual, expected, [message]) 1570 | * 1571 | * Asserts that `actual` is deeply equal to `expected`. 1572 | * 1573 | * assert.deepEqual({ tea: 'green' }, { tea: 'green' }); 1574 | * 1575 | * @name deepEqual 1576 | * @param {Mixed} actual 1577 | * @param {Mixed} expected 1578 | * @param {String} message 1579 | * @api public 1580 | */ 1581 | 1582 | assert.deepEqual = function (act, exp, msg) { 1583 | new Assertion(act, msg).to.eql(exp); 1584 | }; 1585 | 1586 | /** 1587 | * ### .notDeepEqual(actual, expected, [message]) 1588 | * 1589 | * Assert that `actual` is not deeply equal to `expected`. 1590 | * 1591 | * assert.notDeepEqual({ tea: 'green' }, { tea: 'jasmine' }); 1592 | * 1593 | * @name notDeepEqual 1594 | * @param {Mixed} actual 1595 | * @param {Mixed} expected 1596 | * @param {String} message 1597 | * @api public 1598 | */ 1599 | 1600 | assert.notDeepEqual = function (act, exp, msg) { 1601 | new Assertion(act, msg).to.not.eql(exp); 1602 | }; 1603 | 1604 | /** 1605 | * ### .isTrue(value, [message]) 1606 | * 1607 | * Asserts that `value` is true. 1608 | * 1609 | * var teaServed = true; 1610 | * assert.isTrue(teaServed, 'the tea has been served'); 1611 | * 1612 | * @name isTrue 1613 | * @param {Mixed} value 1614 | * @param {String} message 1615 | * @api public 1616 | */ 1617 | 1618 | assert.isTrue = function (val, msg) { 1619 | new Assertion(val, msg).is['true']; 1620 | }; 1621 | 1622 | /** 1623 | * ### .isFalse(value, [message]) 1624 | * 1625 | * Asserts that `value` is false. 1626 | * 1627 | * var teaServed = false; 1628 | * assert.isFalse(teaServed, 'no tea yet? hmm...'); 1629 | * 1630 | * @name isFalse 1631 | * @param {Mixed} value 1632 | * @param {String} message 1633 | * @api public 1634 | */ 1635 | 1636 | assert.isFalse = function (val, msg) { 1637 | new Assertion(val, msg).is['false']; 1638 | }; 1639 | 1640 | /** 1641 | * ### .isNull(value, [message]) 1642 | * 1643 | * Asserts that `value` is null. 1644 | * 1645 | * assert.isNull(err, 'there was no error'); 1646 | * 1647 | * @name isNull 1648 | * @param {Mixed} value 1649 | * @param {String} message 1650 | * @api public 1651 | */ 1652 | 1653 | assert.isNull = function (val, msg) { 1654 | new Assertion(val, msg).to.equal(null); 1655 | }; 1656 | 1657 | /** 1658 | * ### .isNotNull(value, [message]) 1659 | * 1660 | * Asserts that `value` is not null. 1661 | * 1662 | * var tea = 'tasty chai'; 1663 | * assert.isNotNull(tea, 'great, time for tea!'); 1664 | * 1665 | * @name isNotNull 1666 | * @param {Mixed} value 1667 | * @param {String} message 1668 | * @api public 1669 | */ 1670 | 1671 | assert.isNotNull = function (val, msg) { 1672 | new Assertion(val, msg).to.not.equal(null); 1673 | }; 1674 | 1675 | /** 1676 | * ### .isUndefined(value, [message]) 1677 | * 1678 | * Asserts that `value` is `undefined`. 1679 | * 1680 | * var tea; 1681 | * assert.isUndefined(tea, 'no tea defined'); 1682 | * 1683 | * @name isUndefined 1684 | * @param {Mixed} value 1685 | * @param {String} message 1686 | * @api public 1687 | */ 1688 | 1689 | assert.isUndefined = function (val, msg) { 1690 | new Assertion(val, msg).to.equal(undefined); 1691 | }; 1692 | 1693 | /** 1694 | * ### .isDefined(value, [message]) 1695 | * 1696 | * Asserts that `value` is not `undefined`. 1697 | * 1698 | * var tea = 'cup of chai'; 1699 | * assert.isDefined(tea, 'tea has been defined'); 1700 | * 1701 | * @name isUndefined 1702 | * @param {Mixed} value 1703 | * @param {String} message 1704 | * @api public 1705 | */ 1706 | 1707 | assert.isDefined = function (val, msg) { 1708 | new Assertion(val, msg).to.not.equal(undefined); 1709 | }; 1710 | 1711 | /** 1712 | * ### .isFunction(value, [message]) 1713 | * 1714 | * Asserts that `value` is a function. 1715 | * 1716 | * function serveTea() { return 'cup of tea'; }; 1717 | * assert.isFunction(serveTea, 'great, we can have tea now'); 1718 | * 1719 | * @name isFunction 1720 | * @param {Mixed} value 1721 | * @param {String} message 1722 | * @api public 1723 | */ 1724 | 1725 | assert.isFunction = function (val, msg) { 1726 | new Assertion(val, msg).to.be.a('function'); 1727 | }; 1728 | 1729 | /** 1730 | * ### .isNotFunction(value, [message]) 1731 | * 1732 | * Asserts that `value` is _not_ a function. 1733 | * 1734 | * var serveTea = [ 'heat', 'pour', 'sip' ]; 1735 | * assert.isNotFunction(serveTea, 'great, we have listed the steps'); 1736 | * 1737 | * @name isNotFunction 1738 | * @param {Mixed} value 1739 | * @param {String} message 1740 | * @api public 1741 | */ 1742 | 1743 | assert.isNotFunction = function (val, msg) { 1744 | new Assertion(val, msg).to.not.be.a('function'); 1745 | }; 1746 | 1747 | /** 1748 | * ### .isObject(value, [message]) 1749 | * 1750 | * Asserts that `value` is an object (as revealed by 1751 | * `Object.prototype.toString`). 1752 | * 1753 | * var selection = { name: 'Chai', serve: 'with spices' }; 1754 | * assert.isObject(selection, 'tea selection is an object'); 1755 | * 1756 | * @name isObject 1757 | * @param {Mixed} value 1758 | * @param {String} message 1759 | * @api public 1760 | */ 1761 | 1762 | assert.isObject = function (val, msg) { 1763 | new Assertion(val, msg).to.be.a('object'); 1764 | }; 1765 | 1766 | /** 1767 | * ### .isNotObject(value, [message]) 1768 | * 1769 | * Asserts that `value` is _not_ an object. 1770 | * 1771 | * var selection = 'chai' 1772 | * assert.isObject(selection, 'tea selection is not an object'); 1773 | * assert.isObject(null, 'null is not an object'); 1774 | * 1775 | * @name isNotObject 1776 | * @param {Mixed} value 1777 | * @param {String} message 1778 | * @api public 1779 | */ 1780 | 1781 | assert.isNotObject = function (val, msg) { 1782 | new Assertion(val, msg).to.not.be.a('object'); 1783 | }; 1784 | 1785 | /** 1786 | * ### .isArray(value, [message]) 1787 | * 1788 | * Asserts that `value` is an array. 1789 | * 1790 | * var menu = [ 'green', 'chai', 'oolong' ]; 1791 | * assert.isArray(menu, 'what kind of tea do we want?'); 1792 | * 1793 | * @name isArray 1794 | * @param {Mixed} value 1795 | * @param {String} message 1796 | * @api public 1797 | */ 1798 | 1799 | assert.isArray = function (val, msg) { 1800 | new Assertion(val, msg).to.be.an('array'); 1801 | }; 1802 | 1803 | /** 1804 | * ### .isNotArray(value, [message]) 1805 | * 1806 | * Asserts that `value` is _not_ an array. 1807 | * 1808 | * var menu = 'green|chai|oolong'; 1809 | * assert.isNotArray(menu, 'what kind of tea do we want?'); 1810 | * 1811 | * @name isNotArray 1812 | * @param {Mixed} value 1813 | * @param {String} message 1814 | * @api public 1815 | */ 1816 | 1817 | assert.isNotArray = function (val, msg) { 1818 | new Assertion(val, msg).to.not.be.an('array'); 1819 | }; 1820 | 1821 | /** 1822 | * ### .isString(value, [message]) 1823 | * 1824 | * Asserts that `value` is a string. 1825 | * 1826 | * var teaOrder = 'chai'; 1827 | * assert.isString(teaOrder, 'order placed'); 1828 | * 1829 | * @name isString 1830 | * @param {Mixed} value 1831 | * @param {String} message 1832 | * @api public 1833 | */ 1834 | 1835 | assert.isString = function (val, msg) { 1836 | new Assertion(val, msg).to.be.a('string'); 1837 | }; 1838 | 1839 | /** 1840 | * ### .isNotString(value, [message]) 1841 | * 1842 | * Asserts that `value` is _not_ a string. 1843 | * 1844 | * var teaOrder = 4; 1845 | * assert.isNotString(teaOrder, 'order placed'); 1846 | * 1847 | * @name isNotString 1848 | * @param {Mixed} value 1849 | * @param {String} message 1850 | * @api public 1851 | */ 1852 | 1853 | assert.isNotString = function (val, msg) { 1854 | new Assertion(val, msg).to.not.be.a('string'); 1855 | }; 1856 | 1857 | /** 1858 | * ### .isNumber(value, [message]) 1859 | * 1860 | * Asserts that `value` is a number. 1861 | * 1862 | * var cups = 2; 1863 | * assert.isNumber(cups, 'how many cups'); 1864 | * 1865 | * @name isNumber 1866 | * @param {Number} value 1867 | * @param {String} message 1868 | * @api public 1869 | */ 1870 | 1871 | assert.isNumber = function (val, msg) { 1872 | new Assertion(val, msg).to.be.a('number'); 1873 | }; 1874 | 1875 | /** 1876 | * ### .isNotNumber(value, [message]) 1877 | * 1878 | * Asserts that `value` is _not_ a number. 1879 | * 1880 | * var cups = '2 cups please'; 1881 | * assert.isNotNumber(cups, 'how many cups'); 1882 | * 1883 | * @name isNotNumber 1884 | * @param {Mixed} value 1885 | * @param {String} message 1886 | * @api public 1887 | */ 1888 | 1889 | assert.isNotNumber = function (val, msg) { 1890 | new Assertion(val, msg).to.not.be.a('number'); 1891 | }; 1892 | 1893 | /** 1894 | * ### .isBoolean(value, [message]) 1895 | * 1896 | * Asserts that `value` is a boolean. 1897 | * 1898 | * var teaReady = true 1899 | * , teaServed = false; 1900 | * 1901 | * assert.isBoolean(teaReady, 'is the tea ready'); 1902 | * assert.isBoolean(teaServed, 'has tea been served'); 1903 | * 1904 | * @name isBoolean 1905 | * @param {Mixed} value 1906 | * @param {String} message 1907 | * @api public 1908 | */ 1909 | 1910 | assert.isBoolean = function (val, msg) { 1911 | new Assertion(val, msg).to.be.a('boolean'); 1912 | }; 1913 | 1914 | /** 1915 | * ### .isNotBoolean(value, [message]) 1916 | * 1917 | * Asserts that `value` is _not_ a boolean. 1918 | * 1919 | * var teaReady = 'yep' 1920 | * , teaServed = 'nope'; 1921 | * 1922 | * assert.isNotBoolean(teaReady, 'is the tea ready'); 1923 | * assert.isNotBoolean(teaServed, 'has tea been served'); 1924 | * 1925 | * @name isNotBoolean 1926 | * @param {Mixed} value 1927 | * @param {String} message 1928 | * @api public 1929 | */ 1930 | 1931 | assert.isNotBoolean = function (val, msg) { 1932 | new Assertion(val, msg).to.not.be.a('boolean'); 1933 | }; 1934 | 1935 | /** 1936 | * ### .typeOf(value, name, [message]) 1937 | * 1938 | * Asserts that `value`'s type is `name`, as determined by 1939 | * `Object.prototype.toString`. 1940 | * 1941 | * assert.typeOf({ tea: 'chai' }, 'object', 'we have an object'); 1942 | * assert.typeOf(['chai', 'jasmine'], 'array', 'we have an array'); 1943 | * assert.typeOf('tea', 'string', 'we have a string'); 1944 | * assert.typeOf(/tea/, 'regexp', 'we have a regular expression'); 1945 | * assert.typeOf(null, 'null', 'we have a null'); 1946 | * assert.typeOf(undefined, 'undefined', 'we have an undefined'); 1947 | * 1948 | * @name typeOf 1949 | * @param {Mixed} value 1950 | * @param {String} name 1951 | * @param {String} message 1952 | * @api public 1953 | */ 1954 | 1955 | assert.typeOf = function (val, type, msg) { 1956 | new Assertion(val, msg).to.be.a(type); 1957 | }; 1958 | 1959 | /** 1960 | * ### .notTypeOf(value, name, [message]) 1961 | * 1962 | * Asserts that `value`'s type is _not_ `name`, as determined by 1963 | * `Object.prototype.toString`. 1964 | * 1965 | * assert.notTypeOf('tea', 'number', 'strings are not numbers'); 1966 | * 1967 | * @name notTypeOf 1968 | * @param {Mixed} value 1969 | * @param {String} typeof name 1970 | * @param {String} message 1971 | * @api public 1972 | */ 1973 | 1974 | assert.notTypeOf = function (val, type, msg) { 1975 | new Assertion(val, msg).to.not.be.a(type); 1976 | }; 1977 | 1978 | /** 1979 | * ### .instanceOf(object, constructor, [message]) 1980 | * 1981 | * Asserts that `value` is an instance of `constructor`. 1982 | * 1983 | * var Tea = function (name) { this.name = name; } 1984 | * , chai = new Tea('chai'); 1985 | * 1986 | * assert.instanceOf(chai, Tea, 'chai is an instance of tea'); 1987 | * 1988 | * @name instanceOf 1989 | * @param {Object} object 1990 | * @param {Constructor} constructor 1991 | * @param {String} message 1992 | * @api public 1993 | */ 1994 | 1995 | assert.instanceOf = function (val, type, msg) { 1996 | new Assertion(val, msg).to.be.instanceOf(type); 1997 | }; 1998 | 1999 | /** 2000 | * ### .notInstanceOf(object, constructor, [message]) 2001 | * 2002 | * Asserts `value` is not an instance of `constructor`. 2003 | * 2004 | * var Tea = function (name) { this.name = name; } 2005 | * , chai = new String('chai'); 2006 | * 2007 | * assert.notInstanceOf(chai, Tea, 'chai is not an instance of tea'); 2008 | * 2009 | * @name notInstanceOf 2010 | * @param {Object} object 2011 | * @param {Constructor} constructor 2012 | * @param {String} message 2013 | * @api public 2014 | */ 2015 | 2016 | assert.notInstanceOf = function (val, type, msg) { 2017 | new Assertion(val, msg).to.not.be.instanceOf(type); 2018 | }; 2019 | 2020 | /** 2021 | * ### .include(haystack, needle, [message]) 2022 | * 2023 | * Asserts that `haystack` includes `needle`. Works 2024 | * for strings and arrays. 2025 | * 2026 | * assert.include('foobar', 'bar', 'foobar contains string "bar"'); 2027 | * assert.include([ 1, 2, 3 ], 3, 'array contains value'); 2028 | * 2029 | * @name include 2030 | * @param {Array|String} haystack 2031 | * @param {Mixed} needle 2032 | * @param {String} message 2033 | * @api public 2034 | */ 2035 | 2036 | assert.include = function (exp, inc, msg) { 2037 | var obj = new Assertion(exp, msg); 2038 | 2039 | if (Array.isArray(exp)) { 2040 | obj.to.include(inc); 2041 | } else if ('string' === typeof exp) { 2042 | obj.to.contain.string(inc); 2043 | } 2044 | }; 2045 | 2046 | /** 2047 | * ### .match(value, regexp, [message]) 2048 | * 2049 | * Asserts that `value` matches the regular expression `regexp`. 2050 | * 2051 | * assert.match('foobar', /^foo/, 'regexp matches'); 2052 | * 2053 | * @name match 2054 | * @param {Mixed} value 2055 | * @param {RegExp} regexp 2056 | * @param {String} message 2057 | * @api public 2058 | */ 2059 | 2060 | assert.match = function (exp, re, msg) { 2061 | new Assertion(exp, msg).to.match(re); 2062 | }; 2063 | 2064 | /** 2065 | * ### .notMatch(value, regexp, [message]) 2066 | * 2067 | * Asserts that `value` does not match the regular expression `regexp`. 2068 | * 2069 | * assert.notMatch('foobar', /^foo/, 'regexp does not match'); 2070 | * 2071 | * @name notMatch 2072 | * @param {Mixed} value 2073 | * @param {RegExp} regexp 2074 | * @param {String} message 2075 | * @api public 2076 | */ 2077 | 2078 | assert.notMatch = function (exp, re, msg) { 2079 | new Assertion(exp, msg).to.not.match(re); 2080 | }; 2081 | 2082 | /** 2083 | * ### .property(object, property, [message]) 2084 | * 2085 | * Asserts that `object` has a property named by `property`. 2086 | * 2087 | * assert.property({ tea: { green: 'matcha' }}, 'tea'); 2088 | * 2089 | * @name property 2090 | * @param {Object} object 2091 | * @param {String} property 2092 | * @param {String} message 2093 | * @api public 2094 | */ 2095 | 2096 | assert.property = function (obj, prop, msg) { 2097 | new Assertion(obj, msg).to.have.property(prop); 2098 | }; 2099 | 2100 | /** 2101 | * ### .notProperty(object, property, [message]) 2102 | * 2103 | * Asserts that `object` does _not_ have a property named by `property`. 2104 | * 2105 | * assert.notProperty({ tea: { green: 'matcha' }}, 'coffee'); 2106 | * 2107 | * @name notProperty 2108 | * @param {Object} object 2109 | * @param {String} property 2110 | * @param {String} message 2111 | * @api public 2112 | */ 2113 | 2114 | assert.notProperty = function (obj, prop, msg) { 2115 | new Assertion(obj, msg).to.not.have.property(prop); 2116 | }; 2117 | 2118 | /** 2119 | * ### .deepProperty(object, property, [message]) 2120 | * 2121 | * Asserts that `object` has a property named by `property`, which can be a 2122 | * string using dot- and bracket-notation for deep reference. 2123 | * 2124 | * assert.deepProperty({ tea: { green: 'matcha' }}, 'tea.green'); 2125 | * 2126 | * @name deepProperty 2127 | * @param {Object} object 2128 | * @param {String} property 2129 | * @param {String} message 2130 | * @api public 2131 | */ 2132 | 2133 | assert.deepProperty = function (obj, prop, msg) { 2134 | new Assertion(obj, msg).to.have.deep.property(prop); 2135 | }; 2136 | 2137 | /** 2138 | * ### .notDeepProperty(object, property, [message]) 2139 | * 2140 | * Asserts that `object` does _not_ have a property named by `property`, which 2141 | * can be a string using dot- and bracket-notation for deep reference. 2142 | * 2143 | * assert.notDeepProperty({ tea: { green: 'matcha' }}, 'tea.oolong'); 2144 | * 2145 | * @name notDeepProperty 2146 | * @param {Object} object 2147 | * @param {String} property 2148 | * @param {String} message 2149 | * @api public 2150 | */ 2151 | 2152 | assert.notDeepProperty = function (obj, prop, msg) { 2153 | new Assertion(obj, msg).to.not.have.deep.property(prop); 2154 | }; 2155 | 2156 | /** 2157 | * ### .propertyVal(object, property, value, [message]) 2158 | * 2159 | * Asserts that `object` has a property named by `property` with value given 2160 | * by `value`. 2161 | * 2162 | * assert.propertyVal({ tea: 'is good' }, 'tea', 'is good'); 2163 | * 2164 | * @name propertyVal 2165 | * @param {Object} object 2166 | * @param {String} property 2167 | * @param {Mixed} value 2168 | * @param {String} message 2169 | * @api public 2170 | */ 2171 | 2172 | assert.propertyVal = function (obj, prop, val, msg) { 2173 | new Assertion(obj, msg).to.have.property(prop, val); 2174 | }; 2175 | 2176 | /** 2177 | * ### .propertyNotVal(object, property, value, [message]) 2178 | * 2179 | * Asserts that `object` has a property named by `property`, but with a value 2180 | * different from that given by `value`. 2181 | * 2182 | * assert.propertyNotVal({ tea: 'is good' }, 'tea', 'is bad'); 2183 | * 2184 | * @name propertyNotVal 2185 | * @param {Object} object 2186 | * @param {String} property 2187 | * @param {Mixed} value 2188 | * @param {String} message 2189 | * @api public 2190 | */ 2191 | 2192 | assert.propertyNotVal = function (obj, prop, val, msg) { 2193 | new Assertion(obj, msg).to.not.have.property(prop, val); 2194 | }; 2195 | 2196 | /** 2197 | * ### .deepPropertyVal(object, property, value, [message]) 2198 | * 2199 | * Asserts that `object` has a property named by `property` with value given 2200 | * by `value`. `property` can use dot- and bracket-notation for deep 2201 | * reference. 2202 | * 2203 | * assert.deepPropertyVal({ tea: { green: 'matcha' }}, 'tea.green', 'matcha'); 2204 | * 2205 | * @name deepPropertyVal 2206 | * @param {Object} object 2207 | * @param {String} property 2208 | * @param {Mixed} value 2209 | * @param {String} message 2210 | * @api public 2211 | */ 2212 | 2213 | assert.deepPropertyVal = function (obj, prop, val, msg) { 2214 | new Assertion(obj, msg).to.have.deep.property(prop, val); 2215 | }; 2216 | 2217 | /** 2218 | * ### .deepPropertyNotVal(object, property, value, [message]) 2219 | * 2220 | * Asserts that `object` has a property named by `property`, but with a value 2221 | * different from that given by `value`. `property` can use dot- and 2222 | * bracket-notation for deep reference. 2223 | * 2224 | * assert.deepPropertyNotVal({ tea: { green: 'matcha' }}, 'tea.green', 'konacha'); 2225 | * 2226 | * @name deepPropertyNotVal 2227 | * @param {Object} object 2228 | * @param {String} property 2229 | * @param {Mixed} value 2230 | * @param {String} message 2231 | * @api public 2232 | */ 2233 | 2234 | assert.deepPropertyNotVal = function (obj, prop, val, msg) { 2235 | new Assertion(obj, msg).to.not.have.deep.property(prop, val); 2236 | }; 2237 | 2238 | /** 2239 | * ### .lengthOf(object, length, [message]) 2240 | * 2241 | * Asserts that `object` has a `length` property with the expected value. 2242 | * 2243 | * assert.lengthOf([1,2,3], 3, 'array has length of 3'); 2244 | * assert.lengthOf('foobar', 5, 'string has length of 6'); 2245 | * 2246 | * @name lengthOf 2247 | * @param {Mixed} object 2248 | * @param {Number} length 2249 | * @param {String} message 2250 | * @api public 2251 | */ 2252 | 2253 | assert.lengthOf = function (exp, len, msg) { 2254 | new Assertion(exp, msg).to.have.length(len); 2255 | }; 2256 | 2257 | /** 2258 | * ### .throws(function, [constructor/regexp], [message]) 2259 | * 2260 | * Asserts that `function` will throw an error that is an instance of 2261 | * `constructor`, or alternately that it will throw an error with message 2262 | * matching `regexp`. 2263 | * 2264 | * assert.throw(fn, ReferenceError, 'function throws a reference error'); 2265 | * 2266 | * @name throws 2267 | * @alias throw 2268 | * @alias Throw 2269 | * @param {Function} function 2270 | * @param {ErrorConstructor} constructor 2271 | * @param {RegExp} regexp 2272 | * @param {String} message 2273 | * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types 2274 | * @api public 2275 | */ 2276 | 2277 | assert.Throw = function (fn, type, msg) { 2278 | if ('string' === typeof type) { 2279 | msg = type; 2280 | type = null; 2281 | } 2282 | 2283 | new Assertion(fn, msg).to.Throw(type); 2284 | }; 2285 | 2286 | /** 2287 | * ### .doesNotThrow(function, [constructor/regexp], [message]) 2288 | * 2289 | * Asserts that `function` will _not_ throw an error that is an instance of 2290 | * `constructor`, or alternately that it will not throw an error with message 2291 | * matching `regexp`. 2292 | * 2293 | * assert.doesNotThrow(fn, Error, 'function does not throw'); 2294 | * 2295 | * @name doesNotThrow 2296 | * @param {Function} function 2297 | * @param {ErrorConstructor} constructor 2298 | * @param {RegExp} regexp 2299 | * @param {String} message 2300 | * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types 2301 | * @api public 2302 | */ 2303 | 2304 | assert.doesNotThrow = function (fn, type, msg) { 2305 | if ('string' === typeof type) { 2306 | msg = type; 2307 | type = null; 2308 | } 2309 | 2310 | new Assertion(fn, msg).to.not.Throw(type); 2311 | }; 2312 | 2313 | /** 2314 | * ### .operator(val1, operator, val2, [message]) 2315 | * 2316 | * Compares two values using `operator`. 2317 | * 2318 | * assert.operator(1, '<', 2, 'everything is ok'); 2319 | * assert.operator(1, '>', 2, 'this will fail'); 2320 | * 2321 | * @name operator 2322 | * @param {Mixed} val1 2323 | * @param {String} operator 2324 | * @param {Mixed} val2 2325 | * @param {String} message 2326 | * @api public 2327 | */ 2328 | 2329 | assert.operator = function (val, operator, val2, msg) { 2330 | if (!~['==', '===', '>', '>=', '<', '<=', '!=', '!=='].indexOf(operator)) { 2331 | throw new Error('Invalid operator "' + operator + '"'); 2332 | } 2333 | var test = new Assertion(eval(val + operator + val2), msg); 2334 | test.assert( 2335 | true === flag(test, 'object') 2336 | , 'expected ' + util.inspect(val) + ' to be ' + operator + ' ' + util.inspect(val2) 2337 | , 'expected ' + util.inspect(val) + ' to not be ' + operator + ' ' + util.inspect(val2) ); 2338 | }; 2339 | 2340 | /** 2341 | * ### .closeTo(actual, expected, delta, [message]) 2342 | * 2343 | * Asserts that the target is equal `expected`, to within a +/- `delta` range. 2344 | * 2345 | * assert.closeTo(1.5, 1, 0.5, 'numbers are close'); 2346 | * 2347 | * @name closeTo 2348 | * @param {Number} actual 2349 | * @param {Number} expected 2350 | * @param {Number} delta 2351 | * @param {String} message 2352 | * @api public 2353 | */ 2354 | 2355 | assert.closeTo = function (act, exp, delta, msg) { 2356 | new Assertion(act, msg).to.be.closeTo(exp, delta); 2357 | }; 2358 | 2359 | /*! 2360 | * Undocumented / untested 2361 | */ 2362 | 2363 | assert.ifError = function (val, msg) { 2364 | new Assertion(val, msg).to.not.be.ok; 2365 | }; 2366 | 2367 | /*! 2368 | * Aliases. 2369 | */ 2370 | 2371 | (function alias(name, as){ 2372 | assert[as] = assert[name]; 2373 | return alias; 2374 | }) 2375 | ('Throw', 'throw') 2376 | ('Throw', 'throws'); 2377 | }; 2378 | 2379 | }); // module: chai/interface/assert.js 2380 | 2381 | require.register("chai/interface/expect.js", function(module, exports, require){ 2382 | /*! 2383 | * chai 2384 | * Copyright(c) 2011-2012 Jake Luer 2385 | * MIT Licensed 2386 | */ 2387 | 2388 | module.exports = function (chai, util) { 2389 | chai.expect = function (val, message) { 2390 | return new chai.Assertion(val, message); 2391 | }; 2392 | }; 2393 | 2394 | 2395 | }); // module: chai/interface/expect.js 2396 | 2397 | require.register("chai/interface/should.js", function(module, exports, require){ 2398 | /*! 2399 | * chai 2400 | * Copyright(c) 2011-2012 Jake Luer 2401 | * MIT Licensed 2402 | */ 2403 | 2404 | module.exports = function (chai, util) { 2405 | var Assertion = chai.Assertion; 2406 | 2407 | function loadShould () { 2408 | // modify Object.prototype to have `should` 2409 | Object.defineProperty(Object.prototype, 'should', 2410 | { 2411 | set: function (value) { 2412 | // See https://github.com/chaijs/chai/issues/86: this makes 2413 | // `whatever.should = someValue` actually set `someValue`, which is 2414 | // especially useful for `global.should = require('chai').should()`. 2415 | // 2416 | // Note that we have to use [[DefineProperty]] instead of [[Put]] 2417 | // since otherwise we would trigger this very setter! 2418 | Object.defineProperty(this, 'should', { 2419 | value: value, 2420 | enumerable: true, 2421 | configurable: true, 2422 | writable: true 2423 | }); 2424 | } 2425 | , get: function(){ 2426 | if (this instanceof String || this instanceof Number) { 2427 | return new Assertion(this.constructor(this)); 2428 | } else if (this instanceof Boolean) { 2429 | return new Assertion(this == true); 2430 | } 2431 | return new Assertion(this); 2432 | } 2433 | , configurable: true 2434 | }); 2435 | 2436 | var should = {}; 2437 | 2438 | should.equal = function (val1, val2, msg) { 2439 | new Assertion(val1, msg).to.equal(val2); 2440 | }; 2441 | 2442 | should.Throw = function (fn, errt, errs, msg) { 2443 | new Assertion(fn, msg).to.Throw(errt, errs); 2444 | }; 2445 | 2446 | should.exist = function (val, msg) { 2447 | new Assertion(val, msg).to.exist; 2448 | } 2449 | 2450 | // negation 2451 | should.not = {} 2452 | 2453 | should.not.equal = function (val1, val2, msg) { 2454 | new Assertion(val1, msg).to.not.equal(val2); 2455 | }; 2456 | 2457 | should.not.Throw = function (fn, errt, errs, msg) { 2458 | new Assertion(fn, msg).to.not.Throw(errt, errs); 2459 | }; 2460 | 2461 | should.not.exist = function (val, msg) { 2462 | new Assertion(val, msg).to.not.exist; 2463 | } 2464 | 2465 | should['throw'] = should['Throw']; 2466 | should.not['throw'] = should.not['Throw']; 2467 | 2468 | return should; 2469 | }; 2470 | 2471 | chai.should = loadShould; 2472 | chai.Should = loadShould; 2473 | }; 2474 | 2475 | }); // module: chai/interface/should.js 2476 | 2477 | require.register("chai/utils/addChainableMethod.js", function(module, exports, require){ 2478 | /*! 2479 | * Chai - addChainingMethod utility 2480 | * Copyright(c) 2012 Jake Luer 2481 | * MIT Licensed 2482 | */ 2483 | 2484 | /*! 2485 | * Module dependencies 2486 | */ 2487 | 2488 | var transferFlags = require('./transferFlags'); 2489 | 2490 | /** 2491 | * ### addChainableMethod (ctx, name, method, chainingBehavior) 2492 | * 2493 | * Adds a method to an object, such that the method can also be chained. 2494 | * 2495 | * utils.addChainableMethod(chai.Assertion.prototype, 'foo', function (str) { 2496 | * var obj = utils.flag(this, 'object'); 2497 | * new chai.Assertion(obj).to.be.equal(str); 2498 | * }); 2499 | * 2500 | * Can also be accessed directly from `chai.Assertion`. 2501 | * 2502 | * chai.Assertion.addChainableMethod('foo', fn, chainingBehavior); 2503 | * 2504 | * The result can then be used as both a method assertion, executing both `method` and 2505 | * `chainingBehavior`, or as a language chain, which only executes `chainingBehavior`. 2506 | * 2507 | * expect(fooStr).to.be.foo('bar'); 2508 | * expect(fooStr).to.be.foo.equal('foo'); 2509 | * 2510 | * @param {Object} ctx object to which the method is added 2511 | * @param {String} name of method to add 2512 | * @param {Function} method function to be used for `name`, when called 2513 | * @param {Function} chainingBehavior function to be called every time the property is accessed 2514 | * @name addChainableMethod 2515 | * @api public 2516 | */ 2517 | 2518 | module.exports = function (ctx, name, method, chainingBehavior) { 2519 | if (typeof chainingBehavior !== 'function') 2520 | chainingBehavior = function () { }; 2521 | 2522 | Object.defineProperty(ctx, name, 2523 | { get: function () { 2524 | chainingBehavior.call(this); 2525 | 2526 | var assert = function () { 2527 | var result = method.apply(this, arguments); 2528 | return result === undefined ? this : result; 2529 | }; 2530 | 2531 | // Re-enumerate every time to better accomodate plugins. 2532 | var asserterNames = Object.getOwnPropertyNames(ctx); 2533 | asserterNames.forEach(function (asserterName) { 2534 | var pd = Object.getOwnPropertyDescriptor(ctx, asserterName) 2535 | , functionProtoPD = Object.getOwnPropertyDescriptor(Function.prototype, asserterName); 2536 | // Avoid trying to overwrite things that we can't, like `length` and `arguments`. 2537 | if (functionProtoPD && !functionProtoPD.configurable) return; 2538 | if (asserterName === 'arguments') return; // @see chaijs/chai/issues/69 2539 | Object.defineProperty(assert, asserterName, pd); 2540 | }); 2541 | 2542 | transferFlags(this, assert); 2543 | return assert; 2544 | } 2545 | , configurable: true 2546 | }); 2547 | }; 2548 | 2549 | }); // module: chai/utils/addChainableMethod.js 2550 | 2551 | require.register("chai/utils/addMethod.js", function(module, exports, require){ 2552 | /*! 2553 | * Chai - addMethod utility 2554 | * Copyright(c) 2012 Jake Luer 2555 | * MIT Licensed 2556 | */ 2557 | 2558 | /** 2559 | * ### .addMethod (ctx, name, method) 2560 | * 2561 | * Adds a method to the prototype of an object. 2562 | * 2563 | * utils.addMethod(chai.Assertion.prototype, 'foo', function (str) { 2564 | * var obj = utils.flag(this, 'object'); 2565 | * new chai.Assertion(obj).to.be.equal(str); 2566 | * }); 2567 | * 2568 | * Can also be accessed directly from `chai.Assertion`. 2569 | * 2570 | * chai.Assertion.addMethod('foo', fn); 2571 | * 2572 | * Then can be used as any other assertion. 2573 | * 2574 | * expect(fooStr).to.be.foo('bar'); 2575 | * 2576 | * @param {Object} ctx object to which the method is added 2577 | * @param {String} name of method to add 2578 | * @param {Function} method function to be used for name 2579 | * @name addMethod 2580 | * @api public 2581 | */ 2582 | 2583 | module.exports = function (ctx, name, method) { 2584 | ctx[name] = function () { 2585 | var result = method.apply(this, arguments); 2586 | return result === undefined ? this : result; 2587 | }; 2588 | }; 2589 | 2590 | }); // module: chai/utils/addMethod.js 2591 | 2592 | require.register("chai/utils/addProperty.js", function(module, exports, require){ 2593 | /*! 2594 | * Chai - addProperty utility 2595 | * Copyright(c) 2012 Jake Luer 2596 | * MIT Licensed 2597 | */ 2598 | 2599 | /** 2600 | * ### addProperty (ctx, name, getter) 2601 | * 2602 | * Adds a property to the prototype of an object. 2603 | * 2604 | * utils.addProperty(chai.Assertion.prototype, 'foo', function () { 2605 | * var obj = utils.flag(this, 'object'); 2606 | * new chai.Assertion(obj).to.be.instanceof(Foo); 2607 | * }); 2608 | * 2609 | * Can also be accessed directly from `chai.Assertion`. 2610 | * 2611 | * chai.Assertion.addProperty('foo', fn); 2612 | * 2613 | * Then can be used as any other assertion. 2614 | * 2615 | * expect(myFoo).to.be.foo; 2616 | * 2617 | * @param {Object} ctx object to which the property is added 2618 | * @param {String} name of property to add 2619 | * @param {Function} getter function to be used for name 2620 | * @name addProperty 2621 | * @api public 2622 | */ 2623 | 2624 | module.exports = function (ctx, name, getter) { 2625 | Object.defineProperty(ctx, name, 2626 | { get: function () { 2627 | var result = getter.call(this); 2628 | return result === undefined ? this : result; 2629 | } 2630 | , configurable: true 2631 | }); 2632 | }; 2633 | 2634 | }); // module: chai/utils/addProperty.js 2635 | 2636 | require.register("chai/utils/eql.js", function(module, exports, require){ 2637 | // This is (almost) directly from Node.js assert 2638 | // https://github.com/joyent/node/blob/f8c335d0caf47f16d31413f89aa28eda3878e3aa/lib/assert.js 2639 | 2640 | module.exports = _deepEqual; 2641 | 2642 | // for the browser 2643 | var Buffer; 2644 | try { 2645 | Buffer = require('buffer').Buffer; 2646 | } catch (ex) { 2647 | Buffer = { 2648 | isBuffer: function () { return false; } 2649 | }; 2650 | } 2651 | 2652 | function _deepEqual(actual, expected) { 2653 | 2654 | // 7.1. All identical values are equivalent, as determined by ===. 2655 | if (actual === expected) { 2656 | return true; 2657 | 2658 | } else if (Buffer.isBuffer(actual) && Buffer.isBuffer(expected)) { 2659 | if (actual.length != expected.length) return false; 2660 | 2661 | for (var i = 0; i < actual.length; i++) { 2662 | if (actual[i] !== expected[i]) return false; 2663 | } 2664 | 2665 | return true; 2666 | 2667 | // 7.2. If the expected value is a Date object, the actual value is 2668 | // equivalent if it is also a Date object that refers to the same time. 2669 | } else if (actual instanceof Date && expected instanceof Date) { 2670 | return actual.getTime() === expected.getTime(); 2671 | 2672 | // 7.3. Other pairs that do not both pass typeof value == 'object', 2673 | // equivalence is determined by ==. 2674 | } else if (typeof actual != 'object' && typeof expected != 'object') { 2675 | return actual === expected; 2676 | 2677 | // 7.4. For all other Object pairs, including Array objects, equivalence is 2678 | // determined by having the same number of owned properties (as verified 2679 | // with Object.prototype.hasOwnProperty.call), the same set of keys 2680 | // (although not necessarily the same order), equivalent values for every 2681 | // corresponding key, and an identical 'prototype' property. Note: this 2682 | // accounts for both named and indexed properties on Arrays. 2683 | } else { 2684 | return objEquiv(actual, expected); 2685 | } 2686 | } 2687 | 2688 | function isUndefinedOrNull(value) { 2689 | return value === null || value === undefined; 2690 | } 2691 | 2692 | function isArguments(object) { 2693 | return Object.prototype.toString.call(object) == '[object Arguments]'; 2694 | } 2695 | 2696 | function objEquiv(a, b) { 2697 | if (isUndefinedOrNull(a) || isUndefinedOrNull(b)) 2698 | return false; 2699 | // an identical 'prototype' property. 2700 | if (a.prototype !== b.prototype) return false; 2701 | //~~~I've managed to break Object.keys through screwy arguments passing. 2702 | // Converting to array solves the problem. 2703 | if (isArguments(a)) { 2704 | if (!isArguments(b)) { 2705 | return false; 2706 | } 2707 | a = pSlice.call(a); 2708 | b = pSlice.call(b); 2709 | return _deepEqual(a, b); 2710 | } 2711 | try { 2712 | var ka = Object.keys(a), 2713 | kb = Object.keys(b), 2714 | key, i; 2715 | } catch (e) {//happens when one is a string literal and the other isn't 2716 | return false; 2717 | } 2718 | // having the same number of owned properties (keys incorporates 2719 | // hasOwnProperty) 2720 | if (ka.length != kb.length) 2721 | return false; 2722 | //the same set of keys (although not necessarily the same order), 2723 | ka.sort(); 2724 | kb.sort(); 2725 | //~~~cheap key test 2726 | for (i = ka.length - 1; i >= 0; i--) { 2727 | if (ka[i] != kb[i]) 2728 | return false; 2729 | } 2730 | //equivalent values for every corresponding key, and 2731 | //~~~possibly expensive deep test 2732 | for (i = ka.length - 1; i >= 0; i--) { 2733 | key = ka[i]; 2734 | if (!_deepEqual(a[key], b[key])) return false; 2735 | } 2736 | return true; 2737 | } 2738 | 2739 | }); // module: chai/utils/eql.js 2740 | 2741 | require.register("chai/utils/flag.js", function(module, exports, require){ 2742 | /*! 2743 | * Chai - flag utility 2744 | * Copyright(c) 2012 Jake Luer 2745 | * MIT Licensed 2746 | */ 2747 | 2748 | /** 2749 | * ### flag(object ,key, [value]) 2750 | * 2751 | * Get or set a flag value on an object. If a 2752 | * value is provided it will be set, else it will 2753 | * return the currently set value or `undefined` if 2754 | * the value is not set. 2755 | * 2756 | * utils.flag(this, 'foo', 'bar'); // setter 2757 | * utils.flag(this, 'foo'); // getter, returns `bar` 2758 | * 2759 | * @param {Object} object (constructed Assertion 2760 | * @param {String} key 2761 | * @param {Mixed} value (optional) 2762 | * @name flag 2763 | * @api private 2764 | */ 2765 | 2766 | module.exports = function (obj, key, value) { 2767 | var flags = obj.__flags || (obj.__flags = Object.create(null)); 2768 | if (arguments.length === 3) { 2769 | flags[key] = value; 2770 | } else { 2771 | return flags[key]; 2772 | } 2773 | }; 2774 | 2775 | }); // module: chai/utils/flag.js 2776 | 2777 | require.register("chai/utils/getActual.js", function(module, exports, require){ 2778 | /*! 2779 | * Chai - getActual utility 2780 | * Copyright(c) 2012 Jake Luer 2781 | * MIT Licensed 2782 | */ 2783 | 2784 | /** 2785 | * # getActual(object, [actual]) 2786 | * 2787 | * Returns the `actual` value for an Assertion 2788 | * 2789 | * @param {Object} object (constructed Assertion) 2790 | * @param {Arguments} chai.Assertion.prototype.assert arguments 2791 | */ 2792 | 2793 | module.exports = function (obj, args) { 2794 | var actual = args[4]; 2795 | return 'undefined' !== actual ? actual : obj._obj; 2796 | }; 2797 | 2798 | }); // module: chai/utils/getActual.js 2799 | 2800 | require.register("chai/utils/getMessage.js", function(module, exports, require){ 2801 | /*! 2802 | * Chai - message composition utility 2803 | * Copyright(c) 2012 Jake Luer 2804 | * MIT Licensed 2805 | */ 2806 | 2807 | /*! 2808 | * Module dependancies 2809 | */ 2810 | 2811 | var flag = require('./flag') 2812 | , getActual = require('./getActual') 2813 | , inspect = require('./inspect') 2814 | , objDisplay = require('./objDisplay'); 2815 | 2816 | /** 2817 | * ### .getMessage(object, message, negateMessage) 2818 | * 2819 | * Construct the error message based on flags 2820 | * and template tags. Template tags will return 2821 | * a stringified inspection of the object referenced. 2822 | * 2823 | * Messsage template tags: 2824 | * - `#{this}` current asserted object 2825 | * - `#{act}` actual value 2826 | * - `#{exp}` expected value 2827 | * 2828 | * @param {Object} object (constructed Assertion) 2829 | * @param {Arguments} chai.Assertion.prototype.assert arguments 2830 | * @name getMessage 2831 | * @api public 2832 | */ 2833 | 2834 | module.exports = function (obj, args) { 2835 | var negate = flag(obj, 'negate') 2836 | , val = flag(obj, 'object') 2837 | , expected = args[3] 2838 | , actual = getActual(obj, args) 2839 | , msg = negate ? args[2] : args[1] 2840 | , flagMsg = flag(obj, 'message'); 2841 | 2842 | msg = msg || ''; 2843 | msg = msg 2844 | .replace(/#{this}/g, objDisplay(val)) 2845 | .replace(/#{act}/g, objDisplay(actual)) 2846 | .replace(/#{exp}/g, objDisplay(expected)); 2847 | 2848 | return flagMsg ? flagMsg + ': ' + msg : msg; 2849 | }; 2850 | 2851 | }); // module: chai/utils/getMessage.js 2852 | 2853 | require.register("chai/utils/getName.js", function(module, exports, require){ 2854 | /*! 2855 | * Chai - getName utility 2856 | * Copyright(c) 2012 Jake Luer 2857 | * MIT Licensed 2858 | */ 2859 | 2860 | /** 2861 | * # getName(func) 2862 | * 2863 | * Gets the name of a function, in a cross-browser way. 2864 | * 2865 | * @param {Function} a function (usually a constructor) 2866 | */ 2867 | 2868 | module.exports = function (func) { 2869 | if (func.name) return func.name; 2870 | 2871 | var match = /^\s?function ([^(]*)\(/.exec(func); 2872 | return match && match[1] ? match[1] : ""; 2873 | }; 2874 | 2875 | }); // module: chai/utils/getName.js 2876 | 2877 | require.register("chai/utils/getPathValue.js", function(module, exports, require){ 2878 | /*! 2879 | * Chai - getPathValue utility 2880 | * Copyright(c) 2012 Jake Luer 2881 | * @see https://github.com/logicalparadox/filtr 2882 | * MIT Licensed 2883 | */ 2884 | 2885 | /** 2886 | * ### .getPathValue(path, object) 2887 | * 2888 | * This allows the retrieval of values in an 2889 | * object given a string path. 2890 | * 2891 | * var obj = { 2892 | * prop1: { 2893 | * arr: ['a', 'b', 'c'] 2894 | * , str: 'Hello' 2895 | * } 2896 | * , prop2: { 2897 | * arr: [ { nested: 'Universe' } ] 2898 | * , str: 'Hello again!' 2899 | * } 2900 | * } 2901 | * 2902 | * The following would be the results. 2903 | * 2904 | * getPathValue('prop1.str', obj); // Hello 2905 | * getPathValue('prop1.att[2]', obj); // b 2906 | * getPathValue('prop2.arr[0].nested', obj); // Universe 2907 | * 2908 | * @param {String} path 2909 | * @param {Object} object 2910 | * @returns {Object} value or `undefined` 2911 | * @name getPathValue 2912 | * @api public 2913 | */ 2914 | 2915 | var getPathValue = module.exports = function (path, obj) { 2916 | var parsed = parsePath(path); 2917 | return _getPathValue(parsed, obj); 2918 | }; 2919 | 2920 | /*! 2921 | * ## parsePath(path) 2922 | * 2923 | * Helper function used to parse string object 2924 | * paths. Use in conjunction with `_getPathValue`. 2925 | * 2926 | * var parsed = parsePath('myobject.property.subprop'); 2927 | * 2928 | * ### Paths: 2929 | * 2930 | * * Can be as near infinitely deep and nested 2931 | * * Arrays are also valid using the formal `myobject.document[3].property`. 2932 | * 2933 | * @param {String} path 2934 | * @returns {Object} parsed 2935 | * @api private 2936 | */ 2937 | 2938 | function parsePath (path) { 2939 | var str = path.replace(/\[/g, '.[') 2940 | , parts = str.match(/(\\\.|[^.]+?)+/g); 2941 | return parts.map(function (value) { 2942 | var re = /\[(\d+)\]$/ 2943 | , mArr = re.exec(value) 2944 | if (mArr) return { i: parseFloat(mArr[1]) }; 2945 | else return { p: value }; 2946 | }); 2947 | }; 2948 | 2949 | /*! 2950 | * ## _getPathValue(parsed, obj) 2951 | * 2952 | * Helper companion function for `.parsePath` that returns 2953 | * the value located at the parsed address. 2954 | * 2955 | * var value = getPathValue(parsed, obj); 2956 | * 2957 | * @param {Object} parsed definition from `parsePath`. 2958 | * @param {Object} object to search against 2959 | * @returns {Object|Undefined} value 2960 | * @api private 2961 | */ 2962 | 2963 | function _getPathValue (parsed, obj) { 2964 | var tmp = obj 2965 | , res; 2966 | for (var i = 0, l = parsed.length; i < l; i++) { 2967 | var part = parsed[i]; 2968 | if (tmp) { 2969 | if ('undefined' !== typeof part.p) 2970 | tmp = tmp[part.p]; 2971 | else if ('undefined' !== typeof part.i) 2972 | tmp = tmp[part.i]; 2973 | if (i == (l - 1)) res = tmp; 2974 | } else { 2975 | res = undefined; 2976 | } 2977 | } 2978 | return res; 2979 | }; 2980 | 2981 | }); // module: chai/utils/getPathValue.js 2982 | 2983 | require.register("chai/utils/index.js", function(module, exports, require){ 2984 | /*! 2985 | * chai 2986 | * Copyright(c) 2011 Jake Luer 2987 | * MIT Licensed 2988 | */ 2989 | 2990 | /*! 2991 | * Main exports 2992 | */ 2993 | 2994 | var exports = module.exports = {}; 2995 | 2996 | /*! 2997 | * test utility 2998 | */ 2999 | 3000 | exports.test = require('./test'); 3001 | 3002 | /*! 3003 | * message utility 3004 | */ 3005 | 3006 | exports.getMessage = require('./getMessage'); 3007 | 3008 | /*! 3009 | * actual utility 3010 | */ 3011 | 3012 | exports.getActual = require('./getActual'); 3013 | 3014 | /*! 3015 | * Inspect util 3016 | */ 3017 | 3018 | exports.inspect = require('./inspect'); 3019 | 3020 | /*! 3021 | * Object Display util 3022 | */ 3023 | 3024 | exports.objDisplay = require('./objDisplay'); 3025 | 3026 | /*! 3027 | * Flag utility 3028 | */ 3029 | 3030 | exports.flag = require('./flag'); 3031 | 3032 | /*! 3033 | * Flag transferring utility 3034 | */ 3035 | 3036 | exports.transferFlags = require('./transferFlags'); 3037 | 3038 | /*! 3039 | * Deep equal utility 3040 | */ 3041 | 3042 | exports.eql = require('./eql'); 3043 | 3044 | /*! 3045 | * Deep path value 3046 | */ 3047 | 3048 | exports.getPathValue = require('./getPathValue'); 3049 | 3050 | /*! 3051 | * Function name 3052 | */ 3053 | 3054 | exports.getName = require('./getName'); 3055 | 3056 | /*! 3057 | * add Property 3058 | */ 3059 | 3060 | exports.addProperty = require('./addProperty'); 3061 | 3062 | /*! 3063 | * add Method 3064 | */ 3065 | 3066 | exports.addMethod = require('./addMethod'); 3067 | 3068 | /*! 3069 | * overwrite Property 3070 | */ 3071 | 3072 | exports.overwriteProperty = require('./overwriteProperty'); 3073 | 3074 | /*! 3075 | * overwrite Method 3076 | */ 3077 | 3078 | exports.overwriteMethod = require('./overwriteMethod'); 3079 | 3080 | /*! 3081 | * Add a chainable method 3082 | */ 3083 | 3084 | exports.addChainableMethod = require('./addChainableMethod'); 3085 | 3086 | 3087 | }); // module: chai/utils/index.js 3088 | 3089 | require.register("chai/utils/inspect.js", function(module, exports, require){ 3090 | // This is (almost) directly from Node.js utils 3091 | // https://github.com/joyent/node/blob/f8c335d0caf47f16d31413f89aa28eda3878e3aa/lib/util.js 3092 | 3093 | var getName = require('./getName'); 3094 | 3095 | module.exports = inspect; 3096 | 3097 | /** 3098 | * Echos the value of a value. Trys to print the value out 3099 | * in the best way possible given the different types. 3100 | * 3101 | * @param {Object} obj The object to print out. 3102 | * @param {Boolean} showHidden Flag that shows hidden (not enumerable) 3103 | * properties of objects. 3104 | * @param {Number} depth Depth in which to descend in object. Default is 2. 3105 | * @param {Boolean} colors Flag to turn on ANSI escape codes to color the 3106 | * output. Default is false (no coloring). 3107 | */ 3108 | function inspect(obj, showHidden, depth, colors) { 3109 | var ctx = { 3110 | showHidden: showHidden, 3111 | seen: [], 3112 | stylize: function (str) { return str; } 3113 | }; 3114 | return formatValue(ctx, obj, (typeof depth === 'undefined' ? 2 : depth)); 3115 | } 3116 | 3117 | // https://gist.github.com/1044128/ 3118 | var getOuterHTML = function(element) { 3119 | if ('outerHTML' in element) return element.outerHTML; 3120 | var ns = "http://www.w3.org/1999/xhtml"; 3121 | var container = document.createElementNS(ns, '_'); 3122 | var elemProto = (window.HTMLElement || window.Element).prototype; 3123 | var xmlSerializer = new XMLSerializer(); 3124 | var html; 3125 | if (document.xmlVersion) { 3126 | return xmlSerializer.serializeToString(element); 3127 | } else { 3128 | container.appendChild(element.cloneNode(false)); 3129 | html = container.innerHTML.replace('><', '>' + element.innerHTML + '<'); 3130 | container.innerHTML = ''; 3131 | return html; 3132 | } 3133 | }; 3134 | 3135 | // Returns true if object is a DOM element. 3136 | var isDOMElement = function (object) { 3137 | if (typeof HTMLElement === 'object') { 3138 | return object instanceof HTMLElement; 3139 | } else { 3140 | return object && 3141 | typeof object === 'object' && 3142 | object.nodeType === 1 && 3143 | typeof object.nodeName === 'string'; 3144 | } 3145 | }; 3146 | 3147 | function formatValue(ctx, value, recurseTimes) { 3148 | // Provide a hook for user-specified inspect functions. 3149 | // Check that value is an object with an inspect function on it 3150 | if (value && typeof value.inspect === 'function' && 3151 | // Filter out the util module, it's inspect function is special 3152 | value.inspect !== exports.inspect && 3153 | // Also filter out any prototype objects using the circular check. 3154 | !(value.constructor && value.constructor.prototype === value)) { 3155 | return value.inspect(recurseTimes); 3156 | } 3157 | 3158 | // Primitive types cannot have properties 3159 | var primitive = formatPrimitive(ctx, value); 3160 | if (primitive) { 3161 | return primitive; 3162 | } 3163 | 3164 | // If it's DOM elem, get outer HTML. 3165 | if (isDOMElement(value)) { 3166 | return getOuterHTML(value); 3167 | } 3168 | 3169 | // Look up the keys of the object. 3170 | var visibleKeys = Object.keys(value); 3171 | var keys = ctx.showHidden ? Object.getOwnPropertyNames(value) : visibleKeys; 3172 | 3173 | // Some type of object without properties can be shortcutted. 3174 | // In IE, errors have a single `stack` property, or if they are vanilla `Error`, 3175 | // a `stack` plus `description` property; ignore those for consistency. 3176 | if (keys.length === 0 || (isError(value) && ( 3177 | (keys.length === 1 && keys[0] === 'stack') || 3178 | (keys.length === 2 && keys[0] === 'description' && keys[1] === 'stack') 3179 | ))) { 3180 | if (typeof value === 'function') { 3181 | var name = getName(value); 3182 | var nameSuffix = name ? ': ' + name : ''; 3183 | return ctx.stylize('[Function' + nameSuffix + ']', 'special'); 3184 | } 3185 | if (isRegExp(value)) { 3186 | return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); 3187 | } 3188 | if (isDate(value)) { 3189 | return ctx.stylize(Date.prototype.toUTCString.call(value), 'date'); 3190 | } 3191 | if (isError(value)) { 3192 | return formatError(value); 3193 | } 3194 | } 3195 | 3196 | var base = '', array = false, braces = ['{', '}']; 3197 | 3198 | // Make Array say that they are Array 3199 | if (isArray(value)) { 3200 | array = true; 3201 | braces = ['[', ']']; 3202 | } 3203 | 3204 | // Make functions say that they are functions 3205 | if (typeof value === 'function') { 3206 | var name = getName(value); 3207 | var nameSuffix = name ? ': ' + name : ''; 3208 | base = ' [Function' + nameSuffix + ']'; 3209 | } 3210 | 3211 | // Make RegExps say that they are RegExps 3212 | if (isRegExp(value)) { 3213 | base = ' ' + RegExp.prototype.toString.call(value); 3214 | } 3215 | 3216 | // Make dates with properties first say the date 3217 | if (isDate(value)) { 3218 | base = ' ' + Date.prototype.toUTCString.call(value); 3219 | } 3220 | 3221 | // Make error with message first say the error 3222 | if (isError(value)) { 3223 | return formatError(value); 3224 | } 3225 | 3226 | if (keys.length === 0 && (!array || value.length == 0)) { 3227 | return braces[0] + base + braces[1]; 3228 | } 3229 | 3230 | if (recurseTimes < 0) { 3231 | if (isRegExp(value)) { 3232 | return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); 3233 | } else { 3234 | return ctx.stylize('[Object]', 'special'); 3235 | } 3236 | } 3237 | 3238 | ctx.seen.push(value); 3239 | 3240 | var output; 3241 | if (array) { 3242 | output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); 3243 | } else { 3244 | output = keys.map(function(key) { 3245 | return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); 3246 | }); 3247 | } 3248 | 3249 | ctx.seen.pop(); 3250 | 3251 | return reduceToSingleString(output, base, braces); 3252 | } 3253 | 3254 | 3255 | function formatPrimitive(ctx, value) { 3256 | switch (typeof value) { 3257 | case 'undefined': 3258 | return ctx.stylize('undefined', 'undefined'); 3259 | 3260 | case 'string': 3261 | var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') 3262 | .replace(/'/g, "\\'") 3263 | .replace(/\\"/g, '"') + '\''; 3264 | return ctx.stylize(simple, 'string'); 3265 | 3266 | case 'number': 3267 | return ctx.stylize('' + value, 'number'); 3268 | 3269 | case 'boolean': 3270 | return ctx.stylize('' + value, 'boolean'); 3271 | } 3272 | // For some reason typeof null is "object", so special case here. 3273 | if (value === null) { 3274 | return ctx.stylize('null', 'null'); 3275 | } 3276 | } 3277 | 3278 | 3279 | function formatError(value) { 3280 | return '[' + Error.prototype.toString.call(value) + ']'; 3281 | } 3282 | 3283 | 3284 | function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { 3285 | var output = []; 3286 | for (var i = 0, l = value.length; i < l; ++i) { 3287 | if (Object.prototype.hasOwnProperty.call(value, String(i))) { 3288 | output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, 3289 | String(i), true)); 3290 | } else { 3291 | output.push(''); 3292 | } 3293 | } 3294 | keys.forEach(function(key) { 3295 | if (!key.match(/^\d+$/)) { 3296 | output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, 3297 | key, true)); 3298 | } 3299 | }); 3300 | return output; 3301 | } 3302 | 3303 | 3304 | function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { 3305 | var name, str; 3306 | if (value.__lookupGetter__) { 3307 | if (value.__lookupGetter__(key)) { 3308 | if (value.__lookupSetter__(key)) { 3309 | str = ctx.stylize('[Getter/Setter]', 'special'); 3310 | } else { 3311 | str = ctx.stylize('[Getter]', 'special'); 3312 | } 3313 | } else { 3314 | if (value.__lookupSetter__(key)) { 3315 | str = ctx.stylize('[Setter]', 'special'); 3316 | } 3317 | } 3318 | } 3319 | if (visibleKeys.indexOf(key) < 0) { 3320 | name = '[' + key + ']'; 3321 | } 3322 | if (!str) { 3323 | if (ctx.seen.indexOf(value[key]) < 0) { 3324 | if (recurseTimes === null) { 3325 | str = formatValue(ctx, value[key], null); 3326 | } else { 3327 | str = formatValue(ctx, value[key], recurseTimes - 1); 3328 | } 3329 | if (str.indexOf('\n') > -1) { 3330 | if (array) { 3331 | str = str.split('\n').map(function(line) { 3332 | return ' ' + line; 3333 | }).join('\n').substr(2); 3334 | } else { 3335 | str = '\n' + str.split('\n').map(function(line) { 3336 | return ' ' + line; 3337 | }).join('\n'); 3338 | } 3339 | } 3340 | } else { 3341 | str = ctx.stylize('[Circular]', 'special'); 3342 | } 3343 | } 3344 | if (typeof name === 'undefined') { 3345 | if (array && key.match(/^\d+$/)) { 3346 | return str; 3347 | } 3348 | name = JSON.stringify('' + key); 3349 | if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { 3350 | name = name.substr(1, name.length - 2); 3351 | name = ctx.stylize(name, 'name'); 3352 | } else { 3353 | name = name.replace(/'/g, "\\'") 3354 | .replace(/\\"/g, '"') 3355 | .replace(/(^"|"$)/g, "'"); 3356 | name = ctx.stylize(name, 'string'); 3357 | } 3358 | } 3359 | 3360 | return name + ': ' + str; 3361 | } 3362 | 3363 | 3364 | function reduceToSingleString(output, base, braces) { 3365 | var numLinesEst = 0; 3366 | var length = output.reduce(function(prev, cur) { 3367 | numLinesEst++; 3368 | if (cur.indexOf('\n') >= 0) numLinesEst++; 3369 | return prev + cur.length + 1; 3370 | }, 0); 3371 | 3372 | if (length > 60) { 3373 | return braces[0] + 3374 | (base === '' ? '' : base + '\n ') + 3375 | ' ' + 3376 | output.join(',\n ') + 3377 | ' ' + 3378 | braces[1]; 3379 | } 3380 | 3381 | return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; 3382 | } 3383 | 3384 | function isArray(ar) { 3385 | return Array.isArray(ar) || 3386 | (typeof ar === 'object' && objectToString(ar) === '[object Array]'); 3387 | } 3388 | 3389 | function isRegExp(re) { 3390 | return typeof re === 'object' && objectToString(re) === '[object RegExp]'; 3391 | } 3392 | 3393 | function isDate(d) { 3394 | return typeof d === 'object' && objectToString(d) === '[object Date]'; 3395 | } 3396 | 3397 | function isError(e) { 3398 | return typeof e === 'object' && objectToString(e) === '[object Error]'; 3399 | } 3400 | 3401 | function objectToString(o) { 3402 | return Object.prototype.toString.call(o); 3403 | } 3404 | 3405 | }); // module: chai/utils/inspect.js 3406 | 3407 | require.register("chai/utils/objDisplay.js", function(module, exports, require){ 3408 | /*! 3409 | * Chai - flag utility 3410 | * Copyright(c) 2012 Jake Luer 3411 | * MIT Licensed 3412 | */ 3413 | 3414 | /*! 3415 | * Module dependancies 3416 | */ 3417 | 3418 | var inspect = require('./inspect'); 3419 | 3420 | /** 3421 | * ### .objDisplay (object) 3422 | * 3423 | * Determines if an object or an array matches 3424 | * criteria to be inspected in-line for error 3425 | * messages or should be truncated. 3426 | * 3427 | * @param {Mixed} javascript object to inspect 3428 | * @name objDisplay 3429 | * @api public 3430 | */ 3431 | 3432 | module.exports = function (obj) { 3433 | var str = inspect(obj) 3434 | , type = Object.prototype.toString.call(obj); 3435 | 3436 | if (str.length >= 40) { 3437 | if (type === '[object Array]') { 3438 | return '[ Array(' + obj.length + ') ]'; 3439 | } else if (type === '[object Object]') { 3440 | var keys = Object.keys(obj) 3441 | , kstr = keys.length > 2 3442 | ? keys.splice(0, 2).join(', ') + ', ...' 3443 | : keys.join(', '); 3444 | return '{ Object (' + kstr + ') }'; 3445 | } else { 3446 | return str; 3447 | } 3448 | } else { 3449 | return str; 3450 | } 3451 | }; 3452 | 3453 | }); // module: chai/utils/objDisplay.js 3454 | 3455 | require.register("chai/utils/overwriteMethod.js", function(module, exports, require){ 3456 | /*! 3457 | * Chai - overwriteMethod utility 3458 | * Copyright(c) 2012 Jake Luer 3459 | * MIT Licensed 3460 | */ 3461 | 3462 | /** 3463 | * ### overwriteMethod (ctx, name, fn) 3464 | * 3465 | * Overwites an already existing method and provides 3466 | * access to previous function. Must return function 3467 | * to be used for name. 3468 | * 3469 | * utils.overwriteMethod(chai.Assertion.prototype, 'equal', function (_super) { 3470 | * return function (str) { 3471 | * var obj = utils.flag(this, 'object'); 3472 | * if (obj instanceof Foo) { 3473 | * new chai.Assertion(obj.value).to.equal(str); 3474 | * } else { 3475 | * _super.apply(this, arguments); 3476 | * } 3477 | * } 3478 | * }); 3479 | * 3480 | * Can also be accessed directly from `chai.Assertion`. 3481 | * 3482 | * chai.Assertion.overwriteMethod('foo', fn); 3483 | * 3484 | * Then can be used as any other assertion. 3485 | * 3486 | * expect(myFoo).to.equal('bar'); 3487 | * 3488 | * @param {Object} ctx object whose method is to be overwritten 3489 | * @param {String} name of method to overwrite 3490 | * @param {Function} method function that returns a function to be used for name 3491 | * @name overwriteMethod 3492 | * @api public 3493 | */ 3494 | 3495 | module.exports = function (ctx, name, method) { 3496 | var _method = ctx[name] 3497 | , _super = function () { return this; }; 3498 | 3499 | if (_method && 'function' === typeof _method) 3500 | _super = _method; 3501 | 3502 | ctx[name] = function () { 3503 | var result = method(_super).apply(this, arguments); 3504 | return result === undefined ? this : result; 3505 | } 3506 | }; 3507 | 3508 | }); // module: chai/utils/overwriteMethod.js 3509 | 3510 | require.register("chai/utils/overwriteProperty.js", function(module, exports, require){ 3511 | /*! 3512 | * Chai - overwriteProperty utility 3513 | * Copyright(c) 2012 Jake Luer 3514 | * MIT Licensed 3515 | */ 3516 | 3517 | /** 3518 | * ### overwriteProperty (ctx, name, fn) 3519 | * 3520 | * Overwites an already existing property getter and provides 3521 | * access to previous value. Must return function to use as getter. 3522 | * 3523 | * utils.overwriteProperty(chai.Assertion.prototype, 'ok', function (_super) { 3524 | * return function () { 3525 | * var obj = utils.flag(this, 'object'); 3526 | * if (obj instanceof Foo) { 3527 | * new chai.Assertion(obj.name).to.equal('bar'); 3528 | * } else { 3529 | * _super.call(this); 3530 | * } 3531 | * } 3532 | * }); 3533 | * 3534 | * 3535 | * Can also be accessed directly from `chai.Assertion`. 3536 | * 3537 | * chai.Assertion.overwriteProperty('foo', fn); 3538 | * 3539 | * Then can be used as any other assertion. 3540 | * 3541 | * expect(myFoo).to.be.ok; 3542 | * 3543 | * @param {Object} ctx object whose property is to be overwritten 3544 | * @param {String} name of property to overwrite 3545 | * @param {Function} getter function that returns a getter function to be used for name 3546 | * @name overwriteProperty 3547 | * @api public 3548 | */ 3549 | 3550 | module.exports = function (ctx, name, getter) { 3551 | var _get = Object.getOwnPropertyDescriptor(ctx, name) 3552 | , _super = function () {}; 3553 | 3554 | if (_get && 'function' === typeof _get.get) 3555 | _super = _get.get 3556 | 3557 | Object.defineProperty(ctx, name, 3558 | { get: function () { 3559 | var result = getter(_super).call(this); 3560 | return result === undefined ? this : result; 3561 | } 3562 | , configurable: true 3563 | }); 3564 | }; 3565 | 3566 | }); // module: chai/utils/overwriteProperty.js 3567 | 3568 | require.register("chai/utils/test.js", function(module, exports, require){ 3569 | /*! 3570 | * Chai - test utility 3571 | * Copyright(c) 2012 Jake Luer 3572 | * MIT Licensed 3573 | */ 3574 | 3575 | /*! 3576 | * Module dependancies 3577 | */ 3578 | 3579 | var flag = require('./flag'); 3580 | 3581 | /** 3582 | * # test(object, expression) 3583 | * 3584 | * Test and object for expression. 3585 | * 3586 | * @param {Object} object (constructed Assertion) 3587 | * @param {Arguments} chai.Assertion.prototype.assert arguments 3588 | */ 3589 | 3590 | module.exports = function (obj, args) { 3591 | var negate = flag(obj, 'negate') 3592 | , expr = args[0]; 3593 | return negate ? !expr : expr; 3594 | }; 3595 | 3596 | }); // module: chai/utils/test.js 3597 | 3598 | require.register("chai/utils/transferFlags.js", function(module, exports, require){ 3599 | /*! 3600 | * Chai - transferFlags utility 3601 | * Copyright(c) 2012 Jake Luer 3602 | * MIT Licensed 3603 | */ 3604 | 3605 | /** 3606 | * ### transferFlags(assertion, object, includeAll = true) 3607 | * 3608 | * Transfer all the flags for `assertion` to `object`. If 3609 | * `includeAll` is set to `false`, then the base Chai 3610 | * assertion flags (namely `object`, `ssfi`, and `message`) 3611 | * will not be transferred. 3612 | * 3613 | * 3614 | * var newAssertion = new Assertion(); 3615 | * utils.transferFlags(assertion, newAssertion); 3616 | * 3617 | * var anotherAsseriton = new Assertion(myObj); 3618 | * utils.transferFlags(assertion, anotherAssertion, false); 3619 | * 3620 | * @param {Assertion} assertion the assertion to transfer the flags from 3621 | * @param {Object} object the object to transfer the flags too; usually a new assertion 3622 | * @param {Boolean} includeAll 3623 | * @name getAllFlags 3624 | * @api private 3625 | */ 3626 | 3627 | module.exports = function (assertion, object, includeAll) { 3628 | var flags = assertion.__flags || (assertion.__flags = Object.create(null)); 3629 | 3630 | if (!object.__flags) { 3631 | object.__flags = Object.create(null); 3632 | } 3633 | 3634 | includeAll = arguments.length === 3 ? includeAll : true; 3635 | 3636 | for (var flag in flags) { 3637 | if (includeAll || 3638 | (flag !== 'object' && flag !== 'ssfi' && flag != 'message')) { 3639 | object.__flags[flag] = flags[flag]; 3640 | } 3641 | } 3642 | }; 3643 | 3644 | }); // module: chai/utils/transferFlags.js 3645 | 3646 | require.alias("./chai.js", "chai"); 3647 | 3648 | return require('chai'); 3649 | }); --------------------------------------------------------------------------------