aboutsummaryrefslogtreecommitdiffstats
path: root/src/angular-mocks.js
diff options
context:
space:
mode:
authorIgor Minar2011-01-25 20:44:44 -0800
committerIgor Minar2011-01-26 15:46:05 -0800
commitf5d08963b0c836b10133a94d03a81254242661eb (patch)
tree14c525d0aa22193e5b1a1f8aa791b96608dc83b2 /src/angular-mocks.js
parent7a48ee6aa949e8d4338033de8a0c6a079ceb17a3 (diff)
downloadangular.js-f5d08963b0c836b10133a94d03a81254242661eb.tar.bz2
split mocks and create $log and $exceptionHandler mocks
- split mocks between angular-mocks.js and mocks.js - src/angular-mocks.js now contains only mocks that we want to ship - test/mocks.js contains mocks that we use internally for testing angular - created angular.mock namespace - created public $exceptionHandler mock rethrows errors - created public $log mock stores all logs messages in an array that can be accessed to make assertions - internally we now have factory to create $exceptionHandler that we can assert on - internally we also keep track of all messages logged and fail tests if messages were not expected and cleaned up (checked via global beforeEach and afterEach) - updated RakeFile and docs reader.js to point to the new angular-mocks.js location - made real $exceptionHandler and $log factories accessible from tests and simplified their specs - fixed typos in several spec descriptions - added log assertions throughout the test suite
Diffstat (limited to 'src/angular-mocks.js')
-rw-r--r--src/angular-mocks.js384
1 files changed, 384 insertions, 0 deletions
diff --git a/src/angular-mocks.js b/src/angular-mocks.js
new file mode 100644
index 00000000..b9e1dd5c
--- /dev/null
+++ b/src/angular-mocks.js
@@ -0,0 +1,384 @@
+/**
+ * The MIT License
+ *
+ * Copyright (c) 2010 Adam Abrons and Misko Hevery http://getangular.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+/*
+
+ NUGGGGGH MUST TONGUE WANGS
+ \
+ .....
+ C C /
+ /< /
+ ___ __________/_#__=o
+ /(- /(\_\________ \
+ \ ) \ )_ \o \
+ /|\ /|\ |' |
+ | _|
+ /o __\
+ / ' |
+ / / |
+ /_/\______|
+ ( _( <
+ \ \ \
+ \ \ |
+ \____\____\
+ ____\_\__\_\
+ /` /` o\
+ |___ |_______|.. . b'ger
+
+
+ IN THE FINAL BUILD THIS FILE DOESN'T HAVE DIRECT ACCESS TO GLOBAL FUNCTIONS
+ DEFINED IN Angular.js YOU *MUST* REFER TO THEM VIA angular OBJECT
+ (e.g. angular.forEach(...)) AND MAKE SURE THAT THE GIVEN FUNCTION IS EXPORTED
+ TO THE angular NAMESPACE in AngularPublic.js
+
+ */
+
+
+/**
+ * @ngdoc overview
+ * @name angular.mock
+ * @namespace Namespace for all built-in angular mocks.
+ *
+ * @description
+ * `angular.mock` is a namespace for all built-in mocks that ship with angular and automatically
+ * replace real services if `angular-mocks.js` file is loaded after `angular.js` and before any
+ * tests.
+ */
+angular.mock = {};
+
+
+/**
+ * @workInProgress
+ * @ngdoc service
+ * @name angular.mock.service.$browser
+ */
+function MockBrowser() {
+ var self = this,
+ expectations = {},
+ requests = [];
+
+ this.isMock = true;
+ self.url = "http://server";
+ self.lastUrl = self.url; // used by url polling fn
+ self.pollFns = [];
+
+
+ // register url polling fn
+
+ self.onHashChange = function(listener) {
+ self.pollFns.push(
+ function() {
+ if (self.lastUrl != self.url) {
+ listener();
+ }
+ }
+ );
+
+ return listener;
+ };
+
+
+ self.xhr = function(method, url, data, callback) {
+ if (angular.isFunction(data)) {
+ callback = data;
+ data = null;
+ }
+ if (data && angular.isObject(data)) data = angular.toJson(data);
+ if (data && angular.isString(data)) url += "|" + data;
+ var expect = expectations[method] || {};
+ var response = expect[url];
+ if (!response) {
+ throw {
+ message: "Unexpected request for method '" + method + "' and url '" + url + "'.",
+ name: "Unexpected Request"
+ };
+ }
+ requests.push(function(){
+ callback(response.code, response.response);
+ });
+ };
+ self.xhr.expectations = expectations;
+ self.xhr.requests = requests;
+ self.xhr.expect = function(method, url, data) {
+ if (data && angular.isObject(data)) data = angular.toJson(data);
+ if (data && angular.isString(data)) url += "|" + data;
+ var expect = expectations[method] || (expectations[method] = {});
+ return {
+ respond: function(code, response) {
+ if (!angular.isNumber(code)) {
+ response = code;
+ code = 200;
+ }
+ expect[url] = {code:code, response:response};
+ }
+ };
+ };
+ self.xhr.expectGET = angular.bind(self, self.xhr.expect, 'GET');
+ self.xhr.expectPOST = angular.bind(self, self.xhr.expect, 'POST');
+ self.xhr.expectDELETE = angular.bind(self, self.xhr.expect, 'DELETE');
+ self.xhr.expectPUT = angular.bind(self, self.xhr.expect, 'PUT');
+ self.xhr.expectJSON = angular.bind(self, self.xhr.expect, 'JSON');
+ self.xhr.flush = function() {
+ while(requests.length) {
+ requests.pop()();
+ }
+ };
+
+ self.cookieHash = {};
+ self.lastCookieHash = {};
+ self.deferredFns = [];
+
+ self.defer = function(fn) {
+ self.deferredFns.push(fn);
+ };
+
+ self.defer.flush = function() {
+ while (self.deferredFns.length) self.deferredFns.shift()();
+ };
+}
+MockBrowser.prototype = {
+
+ poll: function poll(){
+ angular.forEach(this.pollFns, function(pollFn){
+ pollFn();
+ });
+ },
+
+ addPollFn: function(pollFn) {
+ this.pollFns.push(pollFn);
+ return pollFn;
+ },
+
+ hover: function(onHover) {
+ },
+
+ getUrl: function(){
+ return this.url;
+ },
+
+ setUrl: function(url){
+ this.url = url;
+ },
+
+ cookies: function(name, value) {
+ if (name) {
+ if (value == undefined) {
+ delete this.cookieHash[name];
+ } else {
+ if (angular.isString(value) && //strings only
+ value.length <= 4096) { //strict cookie storage limits
+ this.cookieHash[name] = value;
+ }
+ }
+ } else {
+ if (!angular.equals(this.cookieHash, this.lastCookieHash)) {
+ this.lastCookieHash = angular.copy(this.cookieHash);
+ this.cookieHash = angular.copy(this.cookieHash);
+ }
+ return this.cookieHash;
+ }
+ }
+};
+
+angular.service('$browser', function(){
+ return new MockBrowser();
+});
+
+
+/**
+ * @workInProgress
+ * @ngdoc service
+ * @name angular.mock.service.$exceptionHandler
+ *
+ * @description
+ * Mock implementation of {@link angular.service.$exceptionHandler} that rethrows any error passed
+ * into `$exceptionHandler`. If any errors are are passed into the handler in tests, it typically
+ * means that there is a bug in the application or test, so this mock will make these tests fail.
+ *
+ * See {@link angular.mock} for more info on angular mocks.
+ */
+angular.service('$exceptionHandler', function(e) {
+ return function(e) {throw e;};
+});
+
+
+/**
+ * @workInProgress
+ * @ngdoc service
+ * @name angular.mock.service.$log
+ *
+ * @description
+ * Mock implementation of {@link angular.service.$log} that gathers all logged messages in arrays
+ * (one array per logging level). These arrays are exposed as `logs` property of each of the
+ * level-specific log function, e.g. for level `error` the array is exposed as `$log.error.logs`.
+ *
+ * See {@link angular.mock} for more info on angular mocks.
+ */
+angular.service('$log', function() {
+ var $log = {
+ log: function log(){ log.logs.push(arguments) },
+ warn: function warn(){ warn.logs.push(arguments) },
+ info: function info(){ info.logs.push(arguments) },
+ error: function error(){ error.logs.push(arguments) }
+ };
+
+ $log.log.logs = [];
+ $log.warn.logs = [];
+ $log.info.logs = [];
+ $log.error.logs = [];
+
+ return $log;
+});
+
+
+/**
+ * Mock of the Date type which has its timezone specified via constroctor arg.
+ *
+ * The main purpose is to create Date-like instances with timezone fixed to the specified timezone
+ * offset, so that we can test code that depends on local timezone settings without dependency on
+ * the time zone settings of the machine where the code is running.
+ *
+ * @param {number} offset Offset of the *desired* timezone in hours (fractions will be honored)
+ * @param {(number|string)} timestamp Timestamp representing the desired time in *UTC*
+ *
+ * @example
+ * var newYearInBratislava = new TzDate(-1, '2009-12-31T23:00:00Z');
+ * newYearInBratislava.getTimezoneOffset() => -60;
+ * newYearInBratislava.getFullYear() => 2010;
+ * newYearInBratislava.getMonth() => 0;
+ * newYearInBratislava.getDate() => 1;
+ * newYearInBratislava.getHours() => 0;
+ * newYearInBratislava.getMinutes() => 0;
+ *
+ *
+ * !!!! WARNING !!!!!
+ * This is not a complete Date object so only methods that were implemented can be called safely.
+ * To make matters worse, TzDate instances inherit stuff from Date via a prototype.
+ *
+ * We do our best to intercept calls to "unimplemented" methods, but since the list of methods is
+ * incomplete we might be missing some non-standard methods. This can result in errors like:
+ * "Date.prototype.foo called on incompatible Object".
+ */
+function TzDate(offset, timestamp) {
+ if (angular.isString(timestamp)) {
+ var tsStr = timestamp;
+
+ this.origDate = angular.String.toDate(timestamp);
+
+ timestamp = this.origDate.getTime();
+ if (isNaN(timestamp))
+ throw {
+ name: "Illegal Argument",
+ message: "Arg '" + tsStr + "' passed into TzDate constructor is not a valid date string"
+ };
+ } else {
+ this.origDate = new Date(timestamp);
+ }
+
+ var localOffset = new Date(timestamp).getTimezoneOffset();
+ this.offsetDiff = localOffset*60*1000 - offset*1000*60*60;
+ this.date = new Date(timestamp + this.offsetDiff);
+
+ this.getTime = function() {
+ return this.date.getTime() - this.offsetDiff;
+ };
+
+ this.toLocaleDateString = function() {
+ return this.date.toLocaleDateString();
+ };
+
+ this.getFullYear = function() {
+ return this.date.getFullYear();
+ };
+
+ this.getMonth = function() {
+ return this.date.getMonth();
+ };
+
+ this.getDate = function() {
+ return this.date.getDate();
+ };
+
+ this.getHours = function() {
+ return this.date.getHours();
+ };
+
+ this.getMinutes = function() {
+ return this.date.getMinutes();
+ };
+
+ this.getSeconds = function() {
+ return this.date.getSeconds();
+ };
+
+ this.getTimezoneOffset = function() {
+ return offset * 60;
+ };
+
+ this.getUTCFullYear = function() {
+ return this.origDate.getUTCFullYear();
+ };
+
+ this.getUTCMonth = function() {
+ return this.origDate.getUTCMonth();
+ };
+
+ this.getUTCDate = function() {
+ return this.origDate.getUTCDate();
+ };
+
+ this.getUTCHours = function() {
+ return this.origDate.getUTCHours();
+ };
+
+ this.getUTCMinutes = function() {
+ return this.origDate.getUTCMinutes();
+ };
+
+ this.getUTCSeconds = function() {
+ return this.origDate.getUTCSeconds();
+ };
+
+
+ //hide all methods not implemented in this mock that the Date prototype exposes
+ var unimplementedMethods = ['getDay', 'getMilliseconds', 'getTime', 'getUTCDay',
+ 'getUTCMilliseconds', 'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds',
+ 'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear',
+ 'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds',
+ 'setYear', 'toDateString', 'toJSON', 'toGMTString', 'toLocaleFormat', 'toLocaleString',
+ 'toLocaleTimeString', 'toSource', 'toString', 'toTimeString', 'toUTCString', 'valueOf'];
+
+ angular.forEach(unimplementedMethods, function(methodName) {
+ this[methodName] = function() {
+ throw {
+ name: "MethodNotImplemented",
+ message: "Method '" + methodName + "' is not implemented in the TzDate mock"
+ };
+ };
+ });
+}
+
+//make "tzDateInstance instanceof Date" return true
+TzDate.prototype = Date.prototype;