├── .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 | * | Request expectations | Backend definitions |
674 | *
675 | * | Syntax |
676 | * .expect(...).respond(...) |
677 | * .when(...).respond(...) |
678 | *
679 | *
680 | * | Typical usage |
681 | * strict unit tests |
682 | * loose (black-box) unit testing |
683 | *
684 | *
685 | * | Fulfills multiple requests |
686 | * NO |
687 | * YES |
688 | *
689 | *
690 | * | Order of requests matters |
691 | * YES |
692 | * NO |
693 | *
694 | *
695 | * | Request required |
696 | * YES |
697 | * NO |
698 | *
699 | *
700 | * | Response required |
701 | * optional (see below) |
702 | * YES |
703 | *
704 | *
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 | });
--------------------------------------------------------------------------------