4 * Copyright (c) 2010 Adam Abrons and Misko Hevery http://getangular.com
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28 NUGGGGGH MUST TONGUE WANGS
48 |___ |_______|.. . b'ger
51 IN THE FINAL BUILD THIS FILE DOESN'T HAVE DIRECT ACCESS TO GLOBAL FUNCTIONS
52 DEFINED IN Angular.js YOU *MUST* REFER TO THEM VIA angular OBJECT
53 (e.g. angular.forEach(...)) AND MAKE SURE THAT THE GIVEN FUNCTION IS EXPORTED
54 TO THE angular NAMESPACE in AngularPublic.js
62 * @namespace Namespace for all built-in angular mocks.
65 * `angular.mock` is a namespace for all built-in mocks that ship with angular and automatically
66 * replace real services if `angular-mocks.js` file is loaded after `angular.js` and before any
75 * @name angular.mock.service.$browser
77 function MockBrowser() {
83 self.url = "http://server";
84 self.lastUrl = self.url; // used by url polling fn
88 // register url polling fn
90 self.onHashChange = function(listener) {
93 if (self.lastUrl != self.url) {
103 self.xhr = function(method, url, data, callback) {
104 if (angular.isFunction(data)) {
108 if (data && angular.isObject(data)) data = angular.toJson(data);
109 if (data && angular.isString(data)) url += "|" + data;
110 var expect = expectations[method] || {};
111 var response = expect[url];
114 message: "Unexpected request for method '" + method + "' and url '" + url + "'.",
115 name: "Unexpected Request"
118 requests.push(function(){
119 callback(response.code, response.response);
122 self.xhr.expectations = expectations;
123 self.xhr.requests = requests;
124 self.xhr.expect = function(method, url, data) {
125 if (data && angular.isObject(data)) data = angular.toJson(data);
126 if (data && angular.isString(data)) url += "|" + data;
127 var expect = expectations[method] || (expectations[method] = {});
129 respond: function(code, response) {
130 if (!angular.isNumber(code)) {
134 expect[url] = {code:code, response:response};
138 self.xhr.expectGET = angular.bind(self, self.xhr.expect, 'GET');
139 self.xhr.expectPOST = angular.bind(self, self.xhr.expect, 'POST');
140 self.xhr.expectDELETE = angular.bind(self, self.xhr.expect, 'DELETE');
141 self.xhr.expectPUT = angular.bind(self, self.xhr.expect, 'PUT');
142 self.xhr.expectJSON = angular.bind(self, self.xhr.expect, 'JSON');
143 self.xhr.flush = function() {
144 while(requests.length) {
149 self.cookieHash = {};
150 self.lastCookieHash = {};
151 self.deferredFns = [];
153 self.defer = function(fn) {
154 self.deferredFns.push(fn);
157 self.defer.flush = function() {
158 while (self.deferredFns.length) self.deferredFns.shift()();
161 MockBrowser.prototype = {
163 poll: function poll(){
164 angular.forEach(this.pollFns, function(pollFn){
169 addPollFn: function(pollFn) {
170 this.pollFns.push(pollFn);
174 hover: function(onHover) {
181 setUrl: function(url){
185 cookies: function(name, value) {
187 if (value == undefined) {
188 delete this.cookieHash[name];
190 if (angular.isString(value) && //strings only
191 value.length <= 4096) { //strict cookie storage limits
192 this.cookieHash[name] = value;
196 if (!angular.equals(this.cookieHash, this.lastCookieHash)) {
197 this.lastCookieHash = angular.copy(this.cookieHash);
198 this.cookieHash = angular.copy(this.cookieHash);
200 return this.cookieHash;
205 angular.service('$browser', function(){
206 return new MockBrowser();
213 * @name angular.mock.service.$exceptionHandler
216 * Mock implementation of {@link angular.service.$exceptionHandler} that rethrows any error passed
217 * into `$exceptionHandler`. If any errors are are passed into the handler in tests, it typically
218 * means that there is a bug in the application or test, so this mock will make these tests fail.
220 * See {@link angular.mock} for more info on angular mocks.
222 angular.service('$exceptionHandler', function(e) {
223 return function(e) {throw e;};
230 * @name angular.mock.service.$log
233 * Mock implementation of {@link angular.service.$log} that gathers all logged messages in arrays
234 * (one array per logging level). These arrays are exposed as `logs` property of each of the
235 * level-specific log function, e.g. for level `error` the array is exposed as `$log.error.logs`.
237 * See {@link angular.mock} for more info on angular mocks.
239 angular.service('$log', function() {
241 log: function(){ $log.logs.push(arguments) },
242 warn: function(){ $log.logs.push(arguments) },
243 info: function(){ $log.logs.push(arguments) },
244 error: function(){ $log.logs.push(arguments) }
250 $log.error.logs = [];
257 * Mock of the Date type which has its timezone specified via constroctor arg.
259 * The main purpose is to create Date-like instances with timezone fixed to the specified timezone
260 * offset, so that we can test code that depends on local timezone settings without dependency on
261 * the time zone settings of the machine where the code is running.
263 * @param {number} offset Offset of the *desired* timezone in hours (fractions will be honored)
264 * @param {(number|string)} timestamp Timestamp representing the desired time in *UTC*
267 * var newYearInBratislava = new TzDate(-1, '2009-12-31T23:00:00Z');
268 * newYearInBratislava.getTimezoneOffset() => -60;
269 * newYearInBratislava.getFullYear() => 2010;
270 * newYearInBratislava.getMonth() => 0;
271 * newYearInBratislava.getDate() => 1;
272 * newYearInBratislava.getHours() => 0;
273 * newYearInBratislava.getMinutes() => 0;
277 * This is not a complete Date object so only methods that were implemented can be called safely.
278 * To make matters worse, TzDate instances inherit stuff from Date via a prototype.
280 * We do our best to intercept calls to "unimplemented" methods, but since the list of methods is
281 * incomplete we might be missing some non-standard methods. This can result in errors like:
282 * "Date.prototype.foo called on incompatible Object".
284 function TzDate(offset, timestamp) {
285 if (angular.isString(timestamp)) {
286 var tsStr = timestamp;
288 this.origDate = angular.String.toDate(timestamp);
290 timestamp = this.origDate.getTime();
291 if (isNaN(timestamp))
293 name: "Illegal Argument",
294 message: "Arg '" + tsStr + "' passed into TzDate constructor is not a valid date string"
297 this.origDate = new Date(timestamp);
300 var localOffset = new Date(timestamp).getTimezoneOffset();
301 this.offsetDiff = localOffset*60*1000 - offset*1000*60*60;
302 this.date = new Date(timestamp + this.offsetDiff);
304 this.getTime = function() {
305 return this.date.getTime() - this.offsetDiff;
308 this.toLocaleDateString = function() {
309 return this.date.toLocaleDateString();
312 this.getFullYear = function() {
313 return this.date.getFullYear();
316 this.getMonth = function() {
317 return this.date.getMonth();
320 this.getDate = function() {
321 return this.date.getDate();
324 this.getHours = function() {
325 return this.date.getHours();
328 this.getMinutes = function() {
329 return this.date.getMinutes();
332 this.getSeconds = function() {
333 return this.date.getSeconds();
336 this.getTimezoneOffset = function() {
340 this.getUTCFullYear = function() {
341 return this.origDate.getUTCFullYear();
344 this.getUTCMonth = function() {
345 return this.origDate.getUTCMonth();
348 this.getUTCDate = function() {
349 return this.origDate.getUTCDate();
352 this.getUTCHours = function() {
353 return this.origDate.getUTCHours();
356 this.getUTCMinutes = function() {
357 return this.origDate.getUTCMinutes();
360 this.getUTCSeconds = function() {
361 return this.origDate.getUTCSeconds();
365 //hide all methods not implemented in this mock that the Date prototype exposes
366 var unimplementedMethods = ['getDay', 'getMilliseconds', 'getTime', 'getUTCDay',
367 'getUTCMilliseconds', 'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds',
368 'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear',
369 'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds',
370 'setYear', 'toDateString', 'toJSON', 'toGMTString', 'toLocaleFormat', 'toLocaleString',
371 'toLocaleTimeString', 'toSource', 'toString', 'toTimeString', 'toUTCString', 'valueOf'];
373 angular.forEach(unimplementedMethods, function(methodName) {
374 this[methodName] = function() {
376 name: "MethodNotImplemented",
377 message: "Method '" + methodName + "' is not implemented in the TzDate mock"
383 //make "tzDateInstance instanceof Date" return true
384 TzDate.prototype = Date.prototype;