diff --git a/angularFiles.js b/angularFiles.js
index 696bd8e78ec8..7b8ac77b9b08 100755
--- a/angularFiles.js
+++ b/angularFiles.js
@@ -40,6 +40,7 @@ var angularFiles = {
'src/ng/timeout.js',
'src/ng/urlUtils.js',
'src/ng/window.js',
+ 'src/ng/cookieReader.js',
'src/ng/filter.js',
'src/ng/filter/filter.js',
@@ -89,7 +90,9 @@ var angularFiles = {
'src/ngAnimate/animate.js'
],
'ngCookies': [
- 'src/ngCookies/cookies.js'
+ 'src/ngCookies/cookies.js',
+ 'src/ngCookies/cookieStore.js',
+ 'src/ngCookies/cookieWriter.js'
],
'ngMessages': [
'src/ngMessages/messages.js'
@@ -162,7 +165,7 @@ var angularFiles = {
'src/publishExternalApis.js',
'@angularSrcModules',
'@angularScenario',
- '@angularTest',
+ '@angularTest'
],
'karmaExclude': [
@@ -197,7 +200,7 @@ var angularFiles = {
'src/publishExternalApis.js',
'@angularSrcModules',
'@angularScenario',
- '@angularTest',
+ '@angularTest'
],
'karmaJqueryExclude': [
diff --git a/src/AngularPublic.js b/src/AngularPublic.js
index b81257b9fff7..31e8483ee6bf 100644
--- a/src/AngularPublic.js
+++ b/src/AngularPublic.js
@@ -84,7 +84,8 @@
$$RAFProvider,
$$AsyncCallbackProvider,
$WindowProvider,
- $$jqLiteProvider
+ $$jqLiteProvider,
+ $$CookieReaderProvider
*/
@@ -238,7 +239,8 @@ function publishExternalAPI(angular) {
$window: $WindowProvider,
$$rAF: $$RAFProvider,
$$asyncCallback: $$AsyncCallbackProvider,
- $$jqLite: $$jqLiteProvider
+ $$jqLite: $$jqLiteProvider,
+ $$cookieReader: $$CookieReaderProvider
});
}
]);
diff --git a/src/ng/browser.js b/src/ng/browser.js
index af2dd70e76a9..028536c340d6 100644
--- a/src/ng/browser.js
+++ b/src/ng/browser.js
@@ -73,11 +73,6 @@ function Browser(window, document, $log, $sniffer) {
* @param {function()} callback Function that will be called when no outstanding request
*/
self.notifyWhenNoOutstandingRequests = function(callback) {
- // force browser to execute all pollFns - this is needed so that cookies and other pollers fire
- // at some deterministic time in respect to the test runner's actions. Leaving things up to the
- // regular poller would result in flaky tests.
- forEach(pollFns, function(pollFn) { pollFn(); });
-
if (outstandingRequestCount === 0) {
callback();
} else {
@@ -85,44 +80,6 @@ function Browser(window, document, $log, $sniffer) {
}
};
- //////////////////////////////////////////////////////////////
- // Poll Watcher API
- //////////////////////////////////////////////////////////////
- var pollFns = [],
- pollTimeout;
-
- /**
- * @name $browser#addPollFn
- *
- * @param {function()} fn Poll function to add
- *
- * @description
- * Adds a function to the list of functions that poller periodically executes,
- * and starts polling if not started yet.
- *
- * @returns {function()} the added function
- */
- self.addPollFn = function(fn) {
- if (isUndefined(pollTimeout)) startPoller(100, setTimeout);
- pollFns.push(fn);
- return fn;
- };
-
- /**
- * @param {number} interval How often should browser call poll functions (ms)
- * @param {function()} setTimeout Reference to a real or fake `setTimeout` function.
- *
- * @description
- * Configures the poller to run in the specified intervals, using the specified
- * setTimeout fn and kicks it off.
- */
- function startPoller(interval, setTimeout) {
- (function check() {
- forEach(pollFns, function(pollFn) { pollFn(); });
- pollTimeout = setTimeout(check, interval);
- })();
- }
-
//////////////////////////////////////////////////////////////
// URL API
//////////////////////////////////////////////////////////////
@@ -324,89 +281,6 @@ function Browser(window, document, $log, $sniffer) {
return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : '';
};
- //////////////////////////////////////////////////////////////
- // Cookies API
- //////////////////////////////////////////////////////////////
- var lastCookies = {};
- var lastCookieString = '';
- var cookiePath = self.baseHref();
-
- function safeDecodeURIComponent(str) {
- try {
- return decodeURIComponent(str);
- } catch (e) {
- return str;
- }
- }
-
- /**
- * @name $browser#cookies
- *
- * @param {string=} name Cookie name
- * @param {string=} value Cookie value
- *
- * @description
- * The cookies method provides a 'private' low level access to browser cookies.
- * It is not meant to be used directly, use the $cookie service instead.
- *
- * The return values vary depending on the arguments that the method was called with as follows:
- *
- * - cookies() -> hash of all cookies, this is NOT a copy of the internal state, so do not modify
- * it
- * - cookies(name, value) -> set name to value, if value is undefined delete the cookie
- * - cookies(name) -> the same as (name, undefined) == DELETES (no one calls it right now that
- * way)
- *
- * @returns {Object} Hash of all cookies (if called without any parameter)
- */
- self.cookies = function(name, value) {
- var cookieLength, cookieArray, cookie, i, index;
-
- if (name) {
- if (value === undefined) {
- rawDocument.cookie = encodeURIComponent(name) + "=;path=" + cookiePath +
- ";expires=Thu, 01 Jan 1970 00:00:00 GMT";
- } else {
- if (isString(value)) {
- cookieLength = (rawDocument.cookie = encodeURIComponent(name) + '=' + encodeURIComponent(value) +
- ';path=' + cookiePath).length + 1;
-
- // per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum:
- // - 300 cookies
- // - 20 cookies per unique domain
- // - 4096 bytes per cookie
- if (cookieLength > 4096) {
- $log.warn("Cookie '" + name +
- "' possibly not set or overflowed because it was too large (" +
- cookieLength + " > 4096 bytes)!");
- }
- }
- }
- } else {
- if (rawDocument.cookie !== lastCookieString) {
- lastCookieString = rawDocument.cookie;
- cookieArray = lastCookieString.split("; ");
- lastCookies = {};
-
- for (i = 0; i < cookieArray.length; i++) {
- cookie = cookieArray[i];
- index = cookie.indexOf('=');
- if (index > 0) { //ignore nameless cookies
- name = safeDecodeURIComponent(cookie.substring(0, index));
- // the first value that is seen for a cookie is the most
- // specific one. values for the same cookie name that
- // follow are for less specific paths.
- if (lastCookies[name] === undefined) {
- lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1));
- }
- }
- }
- }
- return lastCookies;
- }
- };
-
-
/**
* @name $browser#defer
* @param {function()} fn A function, who's execution should be deferred.
diff --git a/src/ng/cookieReader.js b/src/ng/cookieReader.js
new file mode 100644
index 000000000000..53bc279f4269
--- /dev/null
+++ b/src/ng/cookieReader.js
@@ -0,0 +1,55 @@
+'use strict';
+
+/**
+ * @name $$cookieReader
+ * @requires $document
+ *
+ * @description
+ * This is a private service for reading cookies used by $http and ngCookies
+ *
+ * @return {Object} a key/value map of the current cookies
+ */
+function $$CookieReader($document) {
+ var rawDocument = $document[0];
+ var lastCookies = {};
+ var lastCookieString = '';
+
+ function safeDecodeURIComponent(str) {
+ try {
+ return decodeURIComponent(str);
+ } catch (e) {
+ return str;
+ }
+ }
+
+ return function() {
+ var cookieArray, cookie, i, index, name;
+
+ if (rawDocument.cookie !== lastCookieString) {
+ lastCookieString = rawDocument.cookie;
+ cookieArray = lastCookieString.split('; ');
+ lastCookies = {};
+
+ for (i = 0; i < cookieArray.length; i++) {
+ cookie = cookieArray[i];
+ index = cookie.indexOf('=');
+ if (index > 0) { //ignore nameless cookies
+ name = safeDecodeURIComponent(cookie.substring(0, index));
+ // the first value that is seen for a cookie is the most
+ // specific one. values for the same cookie name that
+ // follow are for less specific paths.
+ if (lastCookies[name] === undefined) {
+ lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1));
+ }
+ }
+ }
+ }
+ return lastCookies;
+ };
+}
+
+$$CookieReader.$inject = ['$document'];
+
+function $$CookieReaderProvider() {
+ this.$get = $$CookieReader;
+}
diff --git a/src/ng/http.js b/src/ng/http.js
index dc37c1625a09..95a33ae7a1fb 100644
--- a/src/ng/http.js
+++ b/src/ng/http.js
@@ -220,8 +220,8 @@ function $HttpProvider() {
**/
var interceptorFactories = this.interceptors = [];
- this.$get = ['$httpBackend', '$browser', '$cacheFactory', '$rootScope', '$q', '$injector',
- function($httpBackend, $browser, $cacheFactory, $rootScope, $q, $injector) {
+ this.$get = ['$httpBackend', '$$cookieReader', '$cacheFactory', '$rootScope', '$q', '$injector',
+ function($httpBackend, $$cookieReader, $cacheFactory, $rootScope, $q, $injector) {
var defaultCache = $cacheFactory('$http');
@@ -1066,7 +1066,7 @@ function $HttpProvider() {
// send the request to the backend
if (isUndefined(cachedResp)) {
var xsrfValue = urlIsSameOrigin(config.url)
- ? $browser.cookies()[config.xsrfCookieName || defaults.xsrfCookieName]
+ ? $$cookieReader()[config.xsrfCookieName || defaults.xsrfCookieName]
: undefined;
if (xsrfValue) {
reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
diff --git a/src/ngCookies/cookieStore.js b/src/ngCookies/cookieStore.js
new file mode 100644
index 000000000000..76a141114303
--- /dev/null
+++ b/src/ngCookies/cookieStore.js
@@ -0,0 +1,81 @@
+'use strict';
+
+angular.module('ngCookies').
+/**
+ * @ngdoc service
+ * @name $cookieStore
+ * @deprecated
+ * @requires $cookies
+ *
+ * @description
+ * Provides a key-value (string-object) storage, that is backed by session cookies.
+ * Objects put or retrieved from this storage are automatically serialized or
+ * deserialized by angular's toJson/fromJson.
+ *
+ * Requires the {@link ngCookies `ngCookies`} module to be installed.
+ *
+ *
+ * **Note:** The $cookieStore service is deprecated.
+ * Please use the {@link ngCookies.$cookies `$cookies`} service instead.
+ *
+ *
+ * @example
+ *
+ * ```js
+ * angular.module('cookieStoreExample', ['ngCookies'])
+ * .controller('ExampleController', ['$cookieStore', function($cookieStore) {
+ * // Put cookie
+ * $cookieStore.put('myFavorite','oatmeal');
+ * // Get cookie
+ * var favoriteCookie = $cookieStore.get('myFavorite');
+ * // Removing a cookie
+ * $cookieStore.remove('myFavorite');
+ * }]);
+ * ```
+ */
+ factory('$cookieStore', ['$cookies', function($cookies) {
+
+ return {
+ /**
+ * @ngdoc method
+ * @name $cookieStore#get
+ *
+ * @description
+ * Returns the value of given cookie key
+ *
+ * @param {string} key Id to use for lookup.
+ * @returns {Object} Deserialized cookie value, undefined if the cookie does not exist.
+ */
+ get: function(key) {
+ return $cookies.getObject(key);
+ },
+
+ /**
+ * @ngdoc method
+ * @name $cookieStore#put
+ *
+ * @description
+ * Sets a value for given cookie key
+ *
+ * @param {string} key Id for the `value`.
+ * @param {Object} value Value to be stored.
+ */
+ put: function(key, value) {
+ $cookies.putObject(key, value);
+ },
+
+ /**
+ * @ngdoc method
+ * @name $cookieStore#remove
+ *
+ * @description
+ * Remove given cookie
+ *
+ * @param {string} key Id of the key-value pair to delete.
+ */
+ remove: function(key) {
+ $cookies.remove(key);
+ }
+ };
+
+ }]);
diff --git a/src/ngCookies/cookieWriter.js b/src/ngCookies/cookieWriter.js
new file mode 100644
index 000000000000..b4f496f825bb
--- /dev/null
+++ b/src/ngCookies/cookieWriter.js
@@ -0,0 +1,60 @@
+'use strict';
+
+/**
+ * @name $$cookieWriter
+ * @requires $document
+ *
+ * @description
+ * This is a private service for writing cookies
+ *
+ * @param {string} name Cookie name
+ * @param {string=} value Cookie value (if undefined, cookie will be deleted)
+ * @param {Object=} options Object with options that need to be stored for the cookie.
+ */
+function $$CookieWriter($document, $log, $browser) {
+ var cookiePath = $browser.baseHref();
+ var rawDocument = $document[0];
+
+ function buildCookieString(name, value, options) {
+ var path, expires;
+ options = options || {};
+ expires = options.expires;
+ path = angular.isDefined(options.path) ? options.path : cookiePath;
+ if (value === undefined) {
+ expires = 'Thu, 01 Jan 1970 00:00:00 GMT';
+ value = '';
+ }
+ if (angular.isString(expires)) {
+ expires = new Date(expires);
+ }
+
+ var str = encodeURIComponent(name) + '=' + encodeURIComponent(value);
+ str += path ? ';path=' + path : '';
+ str += options.domain ? ';domain=' + options.domain : '';
+ str += expires ? ';expires=' + expires.toUTCString() : '';
+ str += options.secure ? ';secure' : '';
+
+ // per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum:
+ // - 300 cookies
+ // - 20 cookies per unique domain
+ // - 4096 bytes per cookie
+ var cookieLength = str.length + 1;
+ if (cookieLength > 4096) {
+ $log.warn("Cookie '" + name +
+ "' possibly not set or overflowed because it was too large (" +
+ cookieLength + " > 4096 bytes)!");
+ }
+
+ return str;
+ }
+
+ return function(name, value, options) {
+ rawDocument.cookie = buildCookieString(name, value, options);
+ };
+}
+
+$$CookieWriter.$inject = ['$document', '$log', '$browser'];
+
+angular.module('ngCookies').provider('$$cookieWriter', function $$CookieWriterProvider() {
+ this.$get = $$CookieWriter;
+});
diff --git a/src/ngCookies/cookies.js b/src/ngCookies/cookies.js
index fcb66e62d16a..bde93ce407e4 100644
--- a/src/ngCookies/cookies.js
+++ b/src/ngCookies/cookies.js
@@ -19,180 +19,155 @@
angular.module('ngCookies', ['ng']).
/**
- * @ngdoc service
- * @name $cookies
- *
+ * @ngdoc provider
+ * @name $cookiesProvider
* @description
- * Provides read/write access to browser's cookies.
- *
- * Only a simple Object is exposed and by adding or removing properties to/from this object, new
- * cookies are created/deleted at the end of current $eval.
- * The object's properties can only be strings.
- *
- * Requires the {@link ngCookies `ngCookies`} module to be installed.
- *
- * @example
- *
- * ```js
- * angular.module('cookiesExample', ['ngCookies'])
- * .controller('ExampleController', ['$cookies', function($cookies) {
- * // Retrieving a cookie
- * var favoriteCookie = $cookies.myFavorite;
- * // Setting a cookie
- * $cookies.myFavorite = 'oatmeal';
- * }]);
- * ```
- */
- factory('$cookies', ['$rootScope', '$browser', function($rootScope, $browser) {
- var cookies = {},
- lastCookies = {},
- lastBrowserCookies,
- runEval = false,
- copy = angular.copy,
- isUndefined = angular.isUndefined;
-
- //creates a poller fn that copies all cookies from the $browser to service & inits the service
- $browser.addPollFn(function() {
- var currentCookies = $browser.cookies();
- if (lastBrowserCookies != currentCookies) { //relies on browser.cookies() impl
- lastBrowserCookies = currentCookies;
- copy(currentCookies, lastCookies);
- copy(currentCookies, cookies);
- if (runEval) $rootScope.$apply();
- }
- })();
-
- runEval = true;
-
- //at the end of each eval, push cookies
- //TODO: this should happen before the "delayed" watches fire, because if some cookies are not
- // strings or browser refuses to store some cookies, we update the model in the push fn.
- $rootScope.$watch(push);
-
- return cookies;
-
-
- /**
- * Pushes all the cookies from the service to the browser and verifies if all cookies were
- * stored.
- */
- function push() {
- var name,
- value,
- browserCookies,
- updated;
-
- //delete any cookies deleted in $cookies
- for (name in lastCookies) {
- if (isUndefined(cookies[name])) {
- $browser.cookies(name, undefined);
- }
- }
-
- //update all cookies updated in $cookies
- for (name in cookies) {
- value = cookies[name];
- if (!angular.isString(value)) {
- value = '' + value;
- cookies[name] = value;
- }
- if (value !== lastCookies[name]) {
- $browser.cookies(name, value);
- updated = true;
- }
- }
-
- //verify what was actually stored
- if (updated) {
- updated = false;
- browserCookies = $browser.cookies();
-
- for (name in cookies) {
- if (cookies[name] !== browserCookies[name]) {
- //delete or reset all cookies that the browser dropped from $cookies
- if (isUndefined(browserCookies[name])) {
- delete cookies[name];
- } else {
- cookies[name] = browserCookies[name];
- }
- updated = true;
- }
- }
- }
- }
- }]).
-
-
- /**
- * @ngdoc service
- * @name $cookieStore
- * @requires $cookies
- *
- * @description
- * Provides a key-value (string-object) storage, that is backed by session cookies.
- * Objects put or retrieved from this storage are automatically serialized or
- * deserialized by angular's toJson/fromJson.
- *
- * Requires the {@link ngCookies `ngCookies`} module to be installed.
- *
- * @example
- *
- * ```js
- * angular.module('cookieStoreExample', ['ngCookies'])
- * .controller('ExampleController', ['$cookieStore', function($cookieStore) {
- * // Put cookie
- * $cookieStore.put('myFavorite','oatmeal');
- * // Get cookie
- * var favoriteCookie = $cookieStore.get('myFavorite');
- * // Removing a cookie
- * $cookieStore.remove('myFavorite');
- * }]);
- * ```
- */
- factory('$cookieStore', ['$cookies', function($cookies) {
-
+ * Use `$cookiesProvider` to change the default behavior of the {@link ngCookies.$cookies $cookies} service.
+ * */
+ provider('$cookies', [function $CookiesProvider() {
+ /**
+ * @ngdoc property
+ * @name $cookiesProvider#defaults
+ * @description
+ *
+ * Object containing default options to pass when setting cookies.
+ *
+ * The object may have following properties:
+ *
+ * - **path** - `{string}` - The cookie will be available only for this path and its
+ * sub-paths. By default, this would be the URL that appears in your base tag.
+ * - **domain** - `{string}` - The cookie will be available only for this domain and
+ * its sub-domains. For obvious security reasons the user agent will not accept the
+ * cookie if the current domain is not a sub domain or equals to the requested domain.
+ * - **expires** - `{string|Date}` - String of the form "Wdy, DD Mon YYYY HH:MM:SS GMT"
+ * or a Date object indicating the exact date/time this cookie will expire.
+ * - **secure** - `{boolean}` - The cookie will be available only in secured connection.
+ *
+ * Note: by default the address that appears in your tag will be used as path.
+ * This is import so that cookies will be visible for all routes in case html5mode is enabled
+ *
+ **/
+ var defaults = this.defaults = {};
+
+ function calcOptions(options) {
+ return options ? angular.extend({}, defaults, options) : defaults;
+ }
+
+ /**
+ * @ngdoc service
+ * @name $cookies
+ *
+ * @description
+ * Provides read/write access to browser's cookies.
+ *
+ * BREAKING CHANGE: `$cookies` no longer exposes properties that represent the
+ * current browser cookie values. Now you must use the get/put/remove/etc. methods
+ * as described below.
+ *
+ * Requires the {@link ngCookies `ngCookies`} module to be installed.
+ *
+ * @example
+ *
+ * ```js
+ * angular.module('cookiesExample', ['ngCookies'])
+ * .controller('ExampleController', ['$cookies', function($cookies) {
+ * // Retrieving a cookie
+ * var favoriteCookie = $cookies.get('myFavorite');
+ * // Setting a cookie
+ * $cookies.put('myFavorite', 'oatmeal');
+ * }]);
+ * ```
+ */
+ this.$get = ['$$cookieReader', '$$cookieWriter', function($$cookieReader, $$cookieWriter) {
return {
/**
* @ngdoc method
- * @name $cookieStore#get
+ * @name $cookies#get
*
* @description
* Returns the value of given cookie key
*
* @param {string} key Id to use for lookup.
- * @returns {Object} Deserialized cookie value, undefined if the cookie does not exist.
+ * @returns {string} Raw cookie value.
*/
get: function(key) {
- var value = $cookies[key];
+ return $$cookieReader()[key];
+ },
+
+ /**
+ * @ngdoc method
+ * @name $cookies#getObject
+ *
+ * @description
+ * Returns the deserialized value of given cookie key
+ *
+ * @param {string} key Id to use for lookup.
+ * @returns {Object} Deserialized cookie value.
+ */
+ getObject: function(key) {
+ var value = this.get(key);
return value ? angular.fromJson(value) : value;
},
/**
* @ngdoc method
- * @name $cookieStore#put
+ * @name $cookies#getAll
+ *
+ * @description
+ * Returns a key value object with all the cookies
+ *
+ * @returns {Object} All cookies
+ */
+ getAll: function() {
+ return $$cookieReader();
+ },
+
+ /**
+ * @ngdoc method
+ * @name $cookies#put
*
* @description
* Sets a value for given cookie key
*
* @param {string} key Id for the `value`.
+ * @param {string} value Raw value to be stored.
+ * @param {Object=} options Options object.
+ * See {@link ngCookies.$cookiesProvider#defaults $cookiesProvider.defaults}
+ */
+ put: function(key, value, options) {
+ $$cookieWriter(key, value, calcOptions(options));
+ },
+
+ /**
+ * @ngdoc method
+ * @name $cookies#putObject
+ *
+ * @description
+ * Serializes and sets a value for given cookie key
+ *
+ * @param {string} key Id for the `value`.
* @param {Object} value Value to be stored.
+ * @param {Object=} options Options object.
+ * See {@link ngCookies.$cookiesProvider#defaults $cookiesProvider.defaults}
*/
- put: function(key, value) {
- $cookies[key] = angular.toJson(value);
+ putObject: function(key, value, options) {
+ this.put(key, angular.toJson(value), options);
},
/**
* @ngdoc method
- * @name $cookieStore#remove
+ * @name $cookies#remove
*
* @description
* Remove given cookie
*
* @param {string} key Id of the key-value pair to delete.
+ * @param {Object=} options Options object.
+ * See {@link ngCookies.$cookiesProvider#defaults $cookiesProvider.defaults}
*/
- remove: function(key) {
- delete $cookies[key];
+ remove: function(key, options) {
+ $$cookieWriter(key, undefined, calcOptions(options));
}
};
-
- }]);
+ }];
+ }]);
diff --git a/src/ngMock/angular-mocks.js b/src/ngMock/angular-mocks.js
index dcefe12008ca..1b8be7d77b96 100644
--- a/src/ngMock/angular-mocks.js
+++ b/src/ngMock/angular-mocks.js
@@ -59,8 +59,6 @@ angular.mock.$Browser = function() {
self.$$checkUrlChange = angular.noop;
- self.cookieHash = {};
- self.lastCookieHash = {};
self.deferredFns = [];
self.deferredNextId = 0;
@@ -140,11 +138,6 @@ angular.mock.$Browser.prototype = {
});
},
- addPollFn: function(pollFn) {
- this.pollFns.push(pollFn);
- return pollFn;
- },
-
url: function(url, replace, state) {
if (angular.isUndefined(state)) {
state = null;
@@ -163,25 +156,6 @@ angular.mock.$Browser.prototype = {
return this.$$state;
},
- cookies: function(name, value) {
- if (name) {
- if (angular.isUndefined(value)) {
- 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;
- }
- },
-
notifyWhenNoOutstandingRequests: function(fn) {
fn();
}
@@ -2169,7 +2143,6 @@ if (window.jasmine || window.mocha) {
if (injector) {
injector.get('$rootElement').off();
- injector.get('$browser').pollFns.length = 0;
}
// clean up jquery's fragment cache
diff --git a/test/ng/browserSpecs.js b/test/ng/browserSpecs.js
index 65321c24a499..3166a08a3599 100755
--- a/test/ng/browserSpecs.js
+++ b/test/ng/browserSpecs.js
@@ -189,10 +189,6 @@ describe('browser', function() {
}
});
- it('should contain cookie cruncher', function() {
- expect(browser.cookies).toBeDefined();
- });
-
describe('outstading requests', function() {
it('should process callbacks immedietly with no outstanding requests', function() {
var callback = jasmine.createSpy('callback');
@@ -253,248 +249,6 @@ describe('browser', function() {
});
- describe('cookies', function() {
-
- function deleteAllCookies() {
- var cookies = document.cookie.split(";");
- var path = location.pathname;
-
- for (var i = 0; i < cookies.length; i++) {
- var cookie = cookies[i];
- var eqPos = cookie.indexOf("=");
- var name = eqPos > -1 ? cookie.substr(0, eqPos) : cookie;
- var parts = path.split('/');
- while (parts.length) {
- document.cookie = name + "=;path=" + (parts.join('/') || '/') + ";expires=Thu, 01 Jan 1970 00:00:00 GMT";
- parts.pop();
- }
- }
- }
-
- beforeEach(function() {
- deleteAllCookies();
- expect(document.cookie).toEqual('');
- });
-
-
- afterEach(function() {
- deleteAllCookies();
- expect(document.cookie).toEqual('');
- });
-
-
- describe('remove all via (null)', function() {
-
- it('should do nothing when no cookies are set', function() {
- browser.cookies(null);
- expect(document.cookie).toEqual('');
- expect(browser.cookies()).toEqual({});
- });
-
- });
-
- describe('remove via cookies(cookieName, undefined)', function() {
-
- it('should remove a cookie when it is present', function() {
- document.cookie = 'foo=bar;path=/';
-
- browser.cookies('foo', undefined);
-
- expect(document.cookie).toEqual('');
- expect(browser.cookies()).toEqual({});
- });
-
-
- it('should do nothing when an nonexisting cookie is being removed', function() {
- browser.cookies('doesntexist', undefined);
- expect(document.cookie).toEqual('');
- expect(browser.cookies()).toEqual({});
- });
- });
-
-
- describe('put via cookies(cookieName, string)', function() {
-
- it('should create and store a cookie', function() {
- browser.cookies('cookieName', 'cookie=Value');
- expect(document.cookie).toMatch(/cookieName=cookie%3DValue;? ?/);
- expect(browser.cookies()).toEqual({'cookieName':'cookie=Value'});
- });
-
-
- it('should overwrite an existing unsynced cookie', function() {
- document.cookie = "cookie=new;path=/";
-
- var oldVal = browser.cookies('cookie', 'newer');
-
- expect(document.cookie).toEqual('cookie=newer');
- expect(browser.cookies()).toEqual({'cookie':'newer'});
- expect(oldVal).not.toBeDefined();
- });
-
- it('should encode both name and value', function() {
- browser.cookies('cookie1=', 'val;ue');
- browser.cookies('cookie2=bar;baz', 'val=ue');
-
- var rawCookies = document.cookie.split("; "); //order is not guaranteed, so we need to parse
- expect(rawCookies.length).toEqual(2);
- expect(rawCookies).toContain('cookie1%3D=val%3Bue');
- expect(rawCookies).toContain('cookie2%3Dbar%3Bbaz=val%3Due');
- });
-
- it('should log warnings when 4kb per cookie storage limit is reached', function() {
- var i, longVal = '', cookieStr;
-
- for (i = 0; i < 4083; i++) {
- longVal += 'x';
- }
-
- cookieStr = document.cookie;
- browser.cookies('x', longVal); //total size 4093-4096, so it should go through
- expect(document.cookie).not.toEqual(cookieStr);
- expect(browser.cookies()['x']).toEqual(longVal);
- expect(logs.warn).toEqual([]);
-
- browser.cookies('x', longVal + 'xxxx'); //total size 4097-4099, a warning should be logged
- expect(logs.warn).toEqual(
- [["Cookie 'x' possibly not set or overflowed because it was too large (4097 > 4096 " +
- "bytes)!"]]);
-
- //force browser to dropped a cookie and make sure that the cache is not out of sync
- browser.cookies('x', 'shortVal');
- expect(browser.cookies().x).toEqual('shortVal'); //needed to prime the cache
- cookieStr = document.cookie;
- browser.cookies('x', longVal + longVal + longVal); //should be too long for all browsers
-
- if (document.cookie !== cookieStr) {
- this.fail(new Error("browser didn't drop long cookie when it was expected. make the " +
- "cookie in this test longer"));
- }
-
- expect(browser.cookies().x).toEqual('shortVal');
- });
- });
-
- describe('put via cookies(cookieName, string), if no ', function() {
- beforeEach(function() {
- fakeDocument.basePath = undefined;
- });
-
- it('should default path in cookie to "" (empty string)', function() {
- browser.cookies('cookie', 'bender');
- // This only fails in Safari and IE when cookiePath returns undefined
- // Where it now succeeds since baseHref return '' instead of undefined
- expect(document.cookie).toEqual('cookie=bender');
- });
- });
-
- describe('get via cookies()[cookieName]', function() {
-
- it('should return undefined for nonexistent cookie', function() {
- expect(browser.cookies().nonexistent).not.toBeDefined();
- });
-
-
- it('should return a value for an existing cookie', function() {
- document.cookie = "foo=bar=baz;path=/";
- expect(browser.cookies().foo).toEqual('bar=baz');
- });
-
- it('should return the the first value provided for a cookie', function() {
- // For a cookie that has different values that differ by path, the
- // value for the most specific path appears first. browser.cookies()
- // should provide that value for the cookie.
- document.cookie = 'foo="first"; foo="second"';
- expect(browser.cookies()['foo']).toBe('"first"');
- });
-
- it('should decode cookie values that were encoded by puts', function() {
- document.cookie = "cookie2%3Dbar%3Bbaz=val%3Due;path=/";
- expect(browser.cookies()['cookie2=bar;baz']).toEqual('val=ue');
- });
-
-
- it('should preserve leading & trailing spaces in names and values', function() {
- browser.cookies(' cookie name ', ' cookie value ');
- expect(browser.cookies()[' cookie name ']).toEqual(' cookie value ');
- expect(browser.cookies()['cookie name']).not.toBeDefined();
- });
-
- it('should decode special characters in cookie values', function() {
- document.cookie = 'cookie_name=cookie_value_%E2%82%AC';
- expect(browser.cookies()['cookie_name']).toEqual('cookie_value_€');
- });
-
- it('should not decode cookie values that do not appear to be encoded', function() {
- // see #9211 - sometimes cookies contain a value that causes decodeURIComponent to throw
- document.cookie = 'cookie_name=cookie_value_%XX';
- expect(browser.cookies()['cookie_name']).toEqual('cookie_value_%XX');
- });
- });
-
-
- describe('getAll via cookies()', function() {
-
- it('should return cookies as hash', function() {
- document.cookie = "foo1=bar1;path=/";
- document.cookie = "foo2=bar2;path=/";
- expect(browser.cookies()).toEqual({'foo1':'bar1', 'foo2':'bar2'});
- });
-
-
- it('should return empty hash if no cookies exist', function() {
- expect(browser.cookies()).toEqual({});
- });
- });
-
-
- it('should pick up external changes made to browser cookies', function() {
- browser.cookies('oatmealCookie', 'drool');
- expect(browser.cookies()).toEqual({'oatmealCookie':'drool'});
-
- document.cookie = 'oatmealCookie=changed;path=/';
- expect(browser.cookies().oatmealCookie).toEqual('changed');
- });
-
-
- it('should initialize cookie cache with existing cookies', function() {
- document.cookie = "existingCookie=existingValue;path=/";
- expect(browser.cookies()).toEqual({'existingCookie':'existingValue'});
- });
-
- });
-
- describe('poller', function() {
-
- it('should call functions in pollFns in regular intervals', function() {
- var log = '';
- browser.addPollFn(function() {log+='a';});
- browser.addPollFn(function() {log+='b';});
- expect(log).toEqual('');
- fakeWindow.setTimeout.flush();
- expect(log).toEqual('ab');
- fakeWindow.setTimeout.flush();
- expect(log).toEqual('abab');
- });
-
- it('should startPoller', function() {
- expect(fakeWindow.timeouts.length).toEqual(0);
-
- browser.addPollFn(function() {});
- expect(fakeWindow.timeouts.length).toEqual(1);
-
- //should remain 1 as it is the check fn
- browser.addPollFn(function() {});
- expect(fakeWindow.timeouts.length).toEqual(1);
- });
-
- it('should return fn that was passed into addPollFn', function() {
- var fn = function() { return 1; };
- var returnedFn = browser.addPollFn(fn);
- expect(returnedFn).toBe(fn);
- });
- });
-
describe('url', function() {
var pushState, replaceState, locationReplace;
@@ -938,7 +692,6 @@ describe('browser', function() {
fakeWindow.location.href = newUrl;
});
$provide.value('$browser', browser);
- browser.pollFns = [];
sniffer.history = options.history;
$provide.value('$sniffer', sniffer);
@@ -1061,7 +814,6 @@ describe('browser', function() {
beforeEach(module(function($provide, $locationProvider) {
$provide.value('$browser', browser);
- browser.pollFns = [];
}));
it('should not interfere with legacy browser url replace behavior', function() {
diff --git a/test/ng/cookieReaderSpec.js b/test/ng/cookieReaderSpec.js
new file mode 100644
index 000000000000..29a672953d26
--- /dev/null
+++ b/test/ng/cookieReaderSpec.js
@@ -0,0 +1,104 @@
+'use strict';
+
+describe('$$cookieReader', function() {
+ var $$cookieReader;
+
+ function deleteAllCookies() {
+ var cookies = document.cookie.split(";");
+ var path = location.pathname;
+
+ for (var i = 0; i < cookies.length; i++) {
+ var cookie = cookies[i];
+ var eqPos = cookie.indexOf("=");
+ var name = eqPos > -1 ? cookie.substr(0, eqPos) : cookie;
+ var parts = path.split('/');
+ while (parts.length) {
+ document.cookie = name + "=;path=" + (parts.join('/') || '/') + ";expires=Thu, 01 Jan 1970 00:00:00 GMT";
+ parts.pop();
+ }
+ }
+ }
+
+ beforeEach(function() {
+ deleteAllCookies();
+ expect(document.cookie).toEqual('');
+
+ inject(function(_$$cookieReader_) {
+ $$cookieReader = _$$cookieReader_;
+ });
+ });
+
+
+ afterEach(function() {
+ deleteAllCookies();
+ expect(document.cookie).toEqual('');
+ });
+
+
+ describe('get via $$cookieReader()[cookieName]', function() {
+
+ it('should return undefined for nonexistent cookie', function() {
+ expect($$cookieReader().nonexistent).not.toBeDefined();
+ });
+
+
+ it('should return a value for an existing cookie', function() {
+ document.cookie = "foo=bar=baz;path=/";
+ expect($$cookieReader().foo).toEqual('bar=baz');
+ });
+
+ it('should return the the first value provided for a cookie', function() {
+ // For a cookie that has different values that differ by path, the
+ // value for the most specific path appears first. $$cookieReader()
+ // should provide that value for the cookie.
+ document.cookie = 'foo="first"; foo="second"';
+ expect($$cookieReader()['foo']).toBe('"first"');
+ });
+
+ it('should decode cookie values that were encoded by puts', function() {
+ document.cookie = "cookie2%3Dbar%3Bbaz=val%3Due;path=/";
+ expect($$cookieReader()['cookie2=bar;baz']).toEqual('val=ue');
+ });
+
+
+ it('should preserve leading & trailing spaces in names and values', function() {
+ document.cookie = '%20cookie%20name%20=%20cookie%20value%20';
+ expect($$cookieReader()[' cookie name ']).toEqual(' cookie value ');
+ expect($$cookieReader()['cookie name']).not.toBeDefined();
+ });
+
+ it('should decode special characters in cookie values', function() {
+ document.cookie = 'cookie_name=cookie_value_%E2%82%AC';
+ expect($$cookieReader()['cookie_name']).toEqual('cookie_value_€');
+ });
+
+ it('should not decode cookie values that do not appear to be encoded', function() {
+ // see #9211 - sometimes cookies contain a value that causes decodeURIComponent to throw
+ document.cookie = 'cookie_name=cookie_value_%XX';
+ expect($$cookieReader()['cookie_name']).toEqual('cookie_value_%XX');
+ });
+ });
+
+
+ describe('getAll via $$cookieReader()', function() {
+
+ it('should return cookies as hash', function() {
+ document.cookie = "foo1=bar1;path=/";
+ document.cookie = "foo2=bar2;path=/";
+ expect($$cookieReader()).toEqual({'foo1':'bar1', 'foo2':'bar2'});
+ });
+
+
+ it('should return empty hash if no cookies exist', function() {
+ expect($$cookieReader()).toEqual({});
+ });
+ });
+
+
+ it('should initialize cookie cache with existing cookies', function() {
+ document.cookie = "existingCookie=existingValue;path=/";
+ expect($$cookieReader()).toEqual({'existingCookie':'existingValue'});
+ });
+
+});
+
diff --git a/test/ng/httpSpec.js b/test/ng/httpSpec.js
index 1a394f3e3fbf..dbb6290323ae 100644
--- a/test/ng/httpSpec.js
+++ b/test/ng/httpSpec.js
@@ -2,10 +2,16 @@
describe('$http', function() {
- var callback;
+ var callback, mockedCookies;
beforeEach(function() {
callback = jasmine.createSpy('done');
+ mockedCookies = {};
+ module({
+ $$cookieReader: function() {
+ return mockedCookies;
+ }
+ });
});
beforeEach(module(function($exceptionHandlerProvider) {
@@ -691,7 +697,7 @@ describe('$http', function() {
});
it('should not set XSRF cookie for cross-domain requests', inject(function($browser) {
- $browser.cookies('XSRF-TOKEN', 'secret');
+ mockedCookies['XSRF-TOKEN'] = 'secret';
$browser.url('http://host.com/base');
$httpBackend.expect('GET', 'http://www.test.com/url', undefined, function(headers) {
return headers['X-XSRF-TOKEN'] === undefined;
@@ -733,15 +739,15 @@ describe('$http', function() {
$httpBackend.flush();
});
- it('should set the XSRF cookie into a XSRF header', inject(function($browser) {
+ it('should set the XSRF cookie into a XSRF header', inject(function() {
function checkXSRF(secret, header) {
return function(headers) {
return headers[header || 'X-XSRF-TOKEN'] == secret;
};
}
- $browser.cookies('XSRF-TOKEN', 'secret');
- $browser.cookies('aCookie', 'secret2');
+ mockedCookies['XSRF-TOKEN'] = 'secret';
+ mockedCookies['aCookie'] = 'secret2';
$httpBackend.expect('GET', '/url', undefined, checkXSRF('secret')).respond('');
$httpBackend.expect('POST', '/url', undefined, checkXSRF('secret')).respond('');
$httpBackend.expect('PUT', '/url', undefined, checkXSRF('secret')).respond('');
@@ -809,23 +815,18 @@ describe('$http', function() {
expect(config.foo).toBeUndefined();
});
- it('should check the cache before checking the XSRF cookie', inject(function($browser, $cacheFactory) {
- var testCache = $cacheFactory('testCache'),
- executionOrder = [];
+ it('should check the cache before checking the XSRF cookie', inject(function($cacheFactory) {
+ var testCache = $cacheFactory('testCache');
- spyOn($browser, 'cookies').andCallFake(function() {
- executionOrder.push('cookies');
- return {'XSRF-TOKEN':'foo'};
- });
spyOn(testCache, 'get').andCallFake(function() {
- executionOrder.push('cache');
+ mockedCookies['XSRF-TOKEN'] = 'foo';
});
- $httpBackend.expect('GET', '/url', undefined).respond('');
+ $httpBackend.expect('GET', '/url', undefined, function(headers) {
+ return headers['X-XSRF-TOKEN'] === 'foo';
+ }).respond('');
$http({url: '/url', method: 'GET', cache: testCache});
$httpBackend.flush();
-
- expect(executionOrder).toEqual(['cache', 'cookies']);
}));
});
diff --git a/test/ng/locationSpec.js b/test/ng/locationSpec.js
index de0561d0c16f..bbeef29f88e6 100644
--- a/test/ng/locationSpec.js
+++ b/test/ng/locationSpec.js
@@ -99,7 +99,6 @@ describe('$location', function() {
/* global Browser: false */
var b = new Browser($window, $document, fakeLog, sniffer);
- b.pollFns = [];
return b;
};
});
diff --git a/test/ngCookies/cookieStoreSpec.js b/test/ngCookies/cookieStoreSpec.js
new file mode 100644
index 000000000000..20436761b9f3
--- /dev/null
+++ b/test/ngCookies/cookieStoreSpec.js
@@ -0,0 +1,27 @@
+'use strict';
+
+describe('$cookieStore', function() {
+
+ beforeEach(module('ngCookies', {
+ $cookies: jasmine.createSpyObj('$cookies', ['getObject', 'putObject', 'remove'])
+ }));
+
+
+ it('should get cookie', inject(function($cookieStore, $cookies) {
+ $cookies.getObject.andReturn('value');
+ expect($cookieStore.get('name')).toBe('value');
+ expect($cookies.getObject).toHaveBeenCalledWith('name');
+ }));
+
+
+ it('should put cookie', inject(function($cookieStore, $cookies) {
+ $cookieStore.put('name', 'value');
+ expect($cookies.putObject).toHaveBeenCalledWith('name', 'value');
+ }));
+
+
+ it('should remove cookie', inject(function($cookieStore, $cookies) {
+ $cookieStore.remove('name');
+ expect($cookies.remove).toHaveBeenCalledWith('name');
+ }));
+ });
diff --git a/test/ngCookies/cookieWriterSpec.js b/test/ngCookies/cookieWriterSpec.js
new file mode 100644
index 000000000000..e94f2b16e7c7
--- /dev/null
+++ b/test/ngCookies/cookieWriterSpec.js
@@ -0,0 +1,198 @@
+'use strict';
+
+describe('$$cookieWriter', function() {
+ var $$cookieWriter;
+
+ function deleteAllCookies() {
+ var cookies = document.cookie.split(";");
+ var path = location.pathname;
+
+ for (var i = 0; i < cookies.length; i++) {
+ var cookie = cookies[i];
+ var eqPos = cookie.indexOf("=");
+ var name = eqPos > -1 ? cookie.substr(0, eqPos) : cookie;
+ var parts = path.split('/');
+ while (parts.length) {
+ document.cookie = name + "=;path=" + (parts.join('/') || '/') + ";expires=Thu, 01 Jan 1970 00:00:00 GMT";
+ parts.pop();
+ }
+ }
+ }
+
+ beforeEach(function() {
+ deleteAllCookies();
+ expect(document.cookie).toEqual('');
+
+ module('ngCookies');
+ inject(function(_$$cookieWriter_) {
+ $$cookieWriter = _$$cookieWriter_;
+ });
+ });
+
+
+ afterEach(function() {
+ deleteAllCookies();
+ expect(document.cookie).toEqual('');
+ });
+
+
+ describe('remove via $$cookieWriter(cookieName, undefined)', function() {
+
+ it('should remove a cookie when it is present', function() {
+ document.cookie = 'foo=bar;path=/';
+
+ $$cookieWriter('foo', undefined);
+
+ expect(document.cookie).toEqual('');
+ });
+
+
+ it('should do nothing when an nonexisting cookie is being removed', function() {
+ $$cookieWriter('doesntexist', undefined);
+ expect(document.cookie).toEqual('');
+ });
+ });
+
+
+ describe('put via $$cookieWriter(cookieName, string)', function() {
+
+ it('should create and store a cookie', function() {
+ $$cookieWriter('cookieName', 'cookie=Value');
+ expect(document.cookie).toMatch(/cookieName=cookie%3DValue;? ?/);
+ });
+
+
+ it('should overwrite an existing unsynced cookie', function() {
+ document.cookie = "cookie=new;path=/";
+
+ var oldVal = $$cookieWriter('cookie', 'newer');
+
+ expect(document.cookie).toEqual('cookie=newer');
+ expect(oldVal).not.toBeDefined();
+ });
+
+ it('should encode both name and value', function() {
+ $$cookieWriter('cookie1=', 'val;ue');
+ $$cookieWriter('cookie2=bar;baz', 'val=ue');
+
+ var rawCookies = document.cookie.split("; "); //order is not guaranteed, so we need to parse
+ expect(rawCookies.length).toEqual(2);
+ expect(rawCookies).toContain('cookie1%3D=val%3Bue');
+ expect(rawCookies).toContain('cookie2%3Dbar%3Bbaz=val%3Due');
+ });
+
+ it('should log warnings when 4kb per cookie storage limit is reached', inject(function($log) {
+ var i, longVal = '', cookieStr;
+
+ for (i = 0; i < 4083; i++) {
+ longVal += 'x';
+ }
+
+ cookieStr = document.cookie;
+ $$cookieWriter('x', longVal); //total size 4093-4096, so it should go through
+ expect(document.cookie).not.toEqual(cookieStr);
+ expect(document.cookie).toEqual('x=' + longVal);
+ expect($log.warn.logs).toEqual([]);
+
+ $$cookieWriter('x', longVal + 'xxxx'); //total size 4097-4099, a warning should be logged
+ expect($log.warn.logs).toEqual(
+ [["Cookie 'x' possibly not set or overflowed because it was too large (4097 > 4096 " +
+ "bytes)!"]]);
+
+ //force browser to dropped a cookie and make sure that the cache is not out of sync
+ $$cookieWriter('x', 'shortVal');
+ expect(document.cookie).toEqual('x=shortVal'); //needed to prime the cache
+ cookieStr = document.cookie;
+ $$cookieWriter('x', longVal + longVal + longVal); //should be too long for all browsers
+
+ if (document.cookie !== cookieStr) {
+ this.fail(new Error("browser didn't drop long cookie when it was expected. make the " +
+ "cookie in this test longer"));
+ }
+
+ expect(document.cookie).toEqual('x=shortVal');
+ $log.reset();
+ }));
+ });
+
+ describe('put via $$cookieWriter(cookieName, string), if no ', function() {
+ beforeEach(inject(function($browser) {
+ $browser.$$baseHref = undefined;
+ }));
+
+ it('should default path in cookie to "" (empty string)', function() {
+ $$cookieWriter('cookie', 'bender');
+ // This only fails in Safari and IE when cookiePath returns undefined
+ // Where it now succeeds since baseHref return '' instead of undefined
+ expect(document.cookie).toEqual('cookie=bender');
+ });
+ });
+});
+
+describe('cookie options', function() {
+ var fakeDocument, $$cookieWriter;
+
+ function getLastCookieAssignment(key) {
+ return fakeDocument[0].cookie
+ .split(';')
+ .reduce(function(prev, value) {
+ var pair = value.split('=', 2);
+ if (pair[0] === key) {
+ if (prev === undefined) {
+ return pair[1] === undefined ? true : pair[1];
+ } else {
+ throw 'duplicate key in cookie string';
+ }
+ } else {
+ return prev;
+ }
+ }, undefined);
+ }
+
+ beforeEach(function() {
+ fakeDocument = [{cookie: ''}];
+ module('ngCookies', {$document: fakeDocument});
+ inject(function($browser) {
+ $browser.$$baseHref = '/a/b';
+ });
+ inject(function(_$$cookieWriter_) {
+ $$cookieWriter = _$$cookieWriter_;
+ });
+ });
+
+ it('should use baseHref as default path', function() {
+ $$cookieWriter('name', 'value');
+ expect(getLastCookieAssignment('path')).toBe('/a/b');
+ });
+
+ it('should accept path option', function() {
+ $$cookieWriter('name', 'value', {path: '/c/d'});
+ expect(getLastCookieAssignment('path')).toBe('/c/d');
+ });
+
+ it('should accept domain option', function() {
+ $$cookieWriter('name', 'value', {domain: '.example.com'});
+ expect(getLastCookieAssignment('domain')).toBe('.example.com');
+ });
+
+ it('should accept secure option', function() {
+ $$cookieWriter('name', 'value', {secure: true});
+ expect(getLastCookieAssignment('secure')).toBe(true);
+ });
+
+ it('should accept expires option on set', function() {
+ $$cookieWriter('name', 'value', {expires: 'Fri, 19 Dec 2014 00:00:00 GMT'});
+ expect(getLastCookieAssignment('expires')).toMatch(/^Fri, 19 Dec 2014 00:00:00 (UTC|GMT)$/);
+ });
+
+ it('should always use epoch time as expire time on remove', function() {
+ $$cookieWriter('name', undefined, {expires: 'Fri, 19 Dec 2014 00:00:00 GMT'});
+ expect(getLastCookieAssignment('expires')).toMatch(/^Thu, 0?1 Jan 1970 00:00:00 (UTC|GMT)$/);
+ });
+
+ it('should accept date object as expires option', function() {
+ $$cookieWriter('name', 'value', {expires: new Date(Date.UTC(1981, 11, 27))});
+ expect(getLastCookieAssignment('expires')).toMatch(/^Sun, 27 Dec 1981 00:00:00 (UTC|GMT)$/);
+ });
+
+});
diff --git a/test/ngCookies/cookiesSpec.js b/test/ngCookies/cookiesSpec.js
index 0a36c92f2aef..0fff36178d8b 100644
--- a/test/ngCookies/cookiesSpec.js
+++ b/test/ngCookies/cookiesSpec.js
@@ -1,149 +1,142 @@
'use strict';
describe('$cookies', function() {
- beforeEach(module('ngCookies', function($provide) {
- $provide.factory('$browser', function() {
- return angular.extend(new angular.mock.$Browser(), {cookieHash: {preexisting:'oldCookie'}});
+ var mockedCookies;
+
+ beforeEach(function() {
+ mockedCookies = {};
+ module('ngCookies', {
+ $$cookieWriter: jasmine.createSpy('$$cookieWriter').andCallFake(function(name, value) {
+ mockedCookies[name] = value;
+ }),
+ $$cookieReader: function() {
+ return mockedCookies;
+ }
});
- }));
+ });
- it('should provide access to existing cookies via object properties and keep them in sync',
- inject(function($cookies, $browser, $rootScope) {
- expect($cookies).toEqual({'preexisting': 'oldCookie'});
+ it('should serialize objects to json', inject(function($cookies) {
+ $cookies.putObject('objectCookie', {id: 123, name: 'blah'});
+ expect($cookies.get('objectCookie')).toEqual('{"id":123,"name":"blah"}');
+ }));
- // access internal cookie storage of the browser mock directly to simulate behavior of
- // document.cookie
- $browser.cookieHash['brandNew'] = 'cookie';
- $browser.poll();
- expect($cookies).toEqual({'preexisting': 'oldCookie', 'brandNew':'cookie'});
+ it('should deserialize json to object', inject(function($cookies) {
+ $cookies.put('objectCookie', '{"id":123,"name":"blah"}');
+ expect($cookies.getObject('objectCookie')).toEqual({id: 123, name: 'blah'});
+ }));
- $browser.cookieHash['brandNew'] = 'cookie2';
- $browser.poll();
- expect($cookies).toEqual({'preexisting': 'oldCookie', 'brandNew':'cookie2'});
- delete $browser.cookieHash['brandNew'];
- $browser.poll();
- expect($cookies).toEqual({'preexisting': 'oldCookie'});
+ it('should delete objects from the store when remove is called', inject(function($cookies) {
+ $cookies.putObject('gonner', { "I'll":"Be Back"});
+ expect($cookies.get('gonner')).toEqual('{"I\'ll":"Be Back"}');
+ $cookies.remove('gonner');
+ expect($cookies.get('gonner')).toEqual(undefined);
}));
- it('should create or update a cookie when a value is assigned to a property',
- inject(function($cookies, $browser, $rootScope) {
- $cookies.oatmealCookie = 'nom nom';
- $rootScope.$digest();
-
- expect($browser.cookies()).
- toEqual({'preexisting': 'oldCookie', 'oatmealCookie':'nom nom'});
+ it('should handle empty string value cookies', inject(function($cookies) {
+ $cookies.putObject("emptyCookie",'');
+ expect($cookies.get('emptyCookie')).toEqual('""');
+ expect($cookies.getObject("emptyCookie")).toEqual('');
+ mockedCookies['blankCookie'] = '';
+ expect($cookies.getObject("blankCookie")).toEqual('');
+ }));
- $cookies.oatmealCookie = 'gone';
- $rootScope.$digest();
- expect($browser.cookies()).
- toEqual({'preexisting': 'oldCookie', 'oatmealCookie': 'gone'});
+ it('should put cookie value without serializing', inject(function($cookies) {
+ $cookies.put('name', 'value');
+ $cookies.put('name2', '"value2"');
+ expect($cookies.get('name')).toEqual('value');
+ expect($cookies.getObject('name2')).toEqual('value2');
}));
- it('should convert non-string values to string',
- inject(function($cookies, $browser, $rootScope) {
- $cookies.nonString = [1, 2, 3];
- $cookies.nullVal = null;
- $cookies.undefVal = undefined;
- var preexisting = $cookies.preexisting = function() {};
- $rootScope.$digest();
- expect($browser.cookies()).toEqual({
- 'preexisting': '' + preexisting,
- 'nonString': '1,2,3',
- 'nullVal': 'null',
- 'undefVal': 'undefined'
- });
- expect($cookies).toEqual({
- 'preexisting': '' + preexisting,
- 'nonString': '1,2,3',
- 'nullVal': 'null',
- 'undefVal': 'undefined'
- });
+ it('should get cookie value without deserializing', inject(function($cookies) {
+ $cookies.put('name', 'value');
+ $cookies.putObject('name2', 'value2');
+ expect($cookies.get('name')).toEqual('value');
+ expect($cookies.get('name2')).toEqual('"value2"');
}));
+ it('should get all the cookies', inject(function($cookies) {
+ $cookies.put('name', 'value');
+ $cookies.putObject('name2', 'value2');
+ expect($cookies.getAll()).toEqual({name: 'value', name2: '"value2"'});
+ }));
- it('should remove a cookie when a $cookies property is deleted',
- inject(function($cookies, $browser, $rootScope) {
- $cookies.oatmealCookie = 'nom nom';
- $rootScope.$digest();
- $browser.poll();
- expect($browser.cookies()).
- toEqual({'preexisting': 'oldCookie', 'oatmealCookie':'nom nom'});
-
- delete $cookies.oatmealCookie;
- $rootScope.$digest();
- expect($browser.cookies()).toEqual({'preexisting': 'oldCookie'});
+ it('should pass options on put', inject(function($cookies, $$cookieWriter) {
+ $cookies.put('name', 'value', {path: '/a/b'});
+ expect($$cookieWriter).toHaveBeenCalledWith('name', 'value', {path: '/a/b'});
}));
- it('should drop or reset cookies that browser refused to store',
- inject(function($cookies, $browser, $rootScope) {
- var i, longVal;
+ it('should pass options on putObject', inject(function($cookies, $$cookieWriter) {
+ $cookies.putObject('name', 'value', {path: '/a/b'});
+ expect($$cookieWriter).toHaveBeenCalledWith('name', '"value"', {path: '/a/b'});
+ }));
- for (i = 0; i < 5000; i++) {
- longVal += '*';
- }
- //drop if no previous value
- $cookies.longCookie = longVal;
- $rootScope.$digest();
- expect($cookies).toEqual({'preexisting': 'oldCookie'});
+ it('should pass options on remove', inject(function($cookies, $$cookieWriter) {
+ $cookies.remove('name', {path: '/a/b'});
+ expect($$cookieWriter).toHaveBeenCalledWith('name', undefined, {path: '/a/b'});
+ }));
- //reset if previous value existed
- $cookies.longCookie = 'shortVal';
- $rootScope.$digest();
- expect($cookies).toEqual({'preexisting': 'oldCookie', 'longCookie': 'shortVal'});
- $cookies.longCookie = longVal;
- $rootScope.$digest();
- expect($cookies).toEqual({'preexisting': 'oldCookie', 'longCookie': 'shortVal'});
- }));
-});
+ it('should pass default options on put', function() {
+ module(function($cookiesProvider) {
+ $cookiesProvider.defaults.secure = true;
+ });
+ inject(function($cookies, $$cookieWriter) {
+ $cookies.put('name', 'value', {path: '/a/b'});
+ expect($$cookieWriter).toHaveBeenCalledWith('name', 'value', {path: '/a/b', secure: true});
+ });
+ });
-describe('$cookieStore', function() {
+ it('should pass default options on putObject', function() {
+ module(function($cookiesProvider) {
+ $cookiesProvider.defaults.secure = true;
+ });
+ inject(function($cookies, $$cookieWriter) {
+ $cookies.putObject('name', 'value', {path: '/a/b'});
+ expect($$cookieWriter).toHaveBeenCalledWith('name', '"value"', {path: '/a/b', secure: true});
+ });
+ });
- beforeEach(module('ngCookies'));
- it('should serialize objects to json', inject(function($cookieStore, $browser, $rootScope) {
- $cookieStore.put('objectCookie', {id: 123, name: 'blah'});
- $rootScope.$digest();
- expect($browser.cookies()).toEqual({'objectCookie': '{"id":123,"name":"blah"}'});
- }));
+ it('should pass default options on remove', function() {
+ module(function($cookiesProvider) {
+ $cookiesProvider.defaults.secure = true;
+ });
+ inject(function($cookies, $$cookieWriter) {
+ $cookies.remove('name', {path: '/a/b'});
+ expect($$cookieWriter).toHaveBeenCalledWith('name', undefined, {path: '/a/b', secure: true});
+ });
+ });
- it('should deserialize json to object', inject(function($cookieStore, $browser) {
- $browser.cookies('objectCookie', '{"id":123,"name":"blah"}');
- $browser.poll();
- expect($cookieStore.get('objectCookie')).toEqual({id: 123, name: 'blah'});
- }));
+ it('should let passed options override default options', function() {
+ module(function($cookiesProvider) {
+ $cookiesProvider.defaults.secure = true;
+ });
+ inject(function($cookies, $$cookieWriter) {
+ $cookies.put('name', 'value', {secure: false});
+ expect($$cookieWriter).toHaveBeenCalledWith('name', 'value', {secure: false});
+ });
+ });
- it('should delete objects from the store when remove is called', inject(function($cookieStore, $browser, $rootScope) {
- $cookieStore.put('gonner', { "I'll":"Be Back"});
- $rootScope.$digest(); //force eval in test
- $browser.poll();
- expect($browser.cookies()).toEqual({'gonner': '{"I\'ll":"Be Back"}'});
+ it('should pass default options if no options are passed', function() {
+ module(function($cookiesProvider) {
+ $cookiesProvider.defaults.secure = true;
+ });
+ inject(function($cookies, $$cookieWriter) {
+ $cookies.put('name', 'value');
+ expect($$cookieWriter).toHaveBeenCalledWith('name', 'value', {secure: true});
+ });
+ });
- $cookieStore.remove('gonner');
- $rootScope.$digest();
- expect($browser.cookies()).toEqual({});
- }));
- it('should handle empty string value cookies', inject(function($cookieStore, $browser, $rootScope) {
- $cookieStore.put("emptyCookie",'');
- $rootScope.$digest();
- expect($browser.cookies()).
- toEqual({ 'emptyCookie': '""' });
- expect($cookieStore.get("emptyCookie")).toEqual('');
-
- $browser.cookieHash['blankCookie'] = '';
- $browser.poll();
- expect($cookieStore.get("blankCookie")).toEqual('');
- }));
-});
+ });