diff --git a/README.md b/README.md index 0f0cc36f..e7ce733c 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,8 @@ The `cookies` service has methods for reading and writing cookies: `String`. * `write(name, value, options = {})`: writes a cookie with the given name and value; options can be used to set `domain`, `expires`, `path` and `secure`. -* `clear(name)`: clears the cookie so that future reads do not return a value +* `clear(name)`: clears the cookie so that future reads do not return a value; + options can be used to specify `domain`, `path` or `secure`. ## License diff --git a/addon/services/cookies.js b/addon/services/cookies.js index 7377e679..7eb6e54c 100644 --- a/addon/services/cookies.js +++ b/addon/services/cookies.js @@ -86,8 +86,11 @@ export default Service.extend({ } }, - clear(name) { - this.write(name, null, { expires: new Date('1970-01-01') }); + clear(name, options = {}) { + assert('Expires or Max-Age options cannot be set when clearing cookies', isEmpty(options.expires) && isEmpty(options.maxAge)); + + options.expires = new Date('1970-01-01'); + this.write(name, null, options); }, _writeDocumentCookie(name, value, options = {}) { diff --git a/tests/unit/services/cookies-test.js b/tests/unit/services/cookies-test.js index 635eb1e6..3ab076cf 100644 --- a/tests/unit/services/cookies-test.js +++ b/tests/unit/services/cookies-test.js @@ -36,6 +36,20 @@ describe('CookiesService', function() { }); } + function itValidatesClearOptions() { + it('throws when the expires option is set', function() { + expect(() => { + this.subject().clear(COOKIE_NAME, 'test', { expires: new Date() }); + }).to.throw(); + }); + + it('throws when the max-age option is set', function() { + expect(() => { + this.subject().clear(COOKIE_NAME, 'test', { maxAge: 1000 }); + }).to.throw(); + }); + } + function itReadsAfterWrite() { it('reads a cookie that was just written', function() { let value = randomString(); @@ -227,6 +241,8 @@ describe('CookiesService', function() { }); describe('clearing a cookie', function() { + itValidatesClearOptions.apply(this); + it('clears the cookie', function() { let value = randomString(); document.cookie = `${COOKIE_NAME}=${value};`; @@ -235,7 +251,60 @@ describe('CookiesService', function() { this.subject().clear(COOKIE_NAME); - expect(this.subject().read(COOKIE_NAME)).to.eq(undefined); + expect(this.subject().read(COOKIE_NAME)).to.be.undefined; + }); + + describe('with a path option', function() { + it('clears the cookie set without path', function() { + let value = randomString(); + let pathname = window.location.pathname; + let path = pathname.substring(0, pathname.lastIndexOf('/')); + this.subject().write(COOKIE_NAME, value); + + expect(this.subject().read(COOKIE_NAME)).to.eq(value); + + this.subject().clear(COOKIE_NAME, { path }); + + expect(this.subject().read(COOKIE_NAME)).to.be.undefined; + }); + + it('clears the cookie set for a given path', function() { + let path = '/'; + let value = randomString(); + this.subject().write(COOKIE_NAME, value, { path }); + + expect(this.subject().read(COOKIE_NAME)).to.eq(value); + + this.subject().clear(COOKIE_NAME, { path }); + + expect(this.subject().read(COOKIE_NAME)).to.be.undefined; + }); + }); + + describe('with a domain option', function() { + it('clears the cookie set without domain', function() { + let domain = window.location.hostname; + let value = randomString(); + this.subject().write(COOKIE_NAME, value); + + expect(this.subject().read(COOKIE_NAME)).to.eq(value); + + this.subject().clear(COOKIE_NAME, { domain }); + + expect(this.subject().read(COOKIE_NAME)).to.be.undefined; + }); + + it('clears the cookie set for a given domain', function() { + let domain = window.location.hostname; + let value = randomString(); + this.subject().write(COOKIE_NAME, value, { domain }); + + expect(this.subject().read(COOKIE_NAME)).to.eq(value); + + this.subject().clear(COOKIE_NAME, { domain }); + + expect(this.subject().read(COOKIE_NAME)).to.be.undefined; + }); }); }); @@ -453,6 +522,8 @@ describe('CookiesService', function() { }); describe('clearing a cookie', function() { + itValidatesClearOptions.apply(this); + it('clears the cookie', function() { let value = randomString(); this.subject().write(COOKIE_NAME, value); @@ -461,7 +532,62 @@ describe('CookiesService', function() { this.subject().clear(COOKIE_NAME); - expect(this.subject().read(COOKIE_NAME)).to.eq(undefined); + expect(this.subject().read(COOKIE_NAME)).to.be.undefined; + }); + + describe('with a path option', function() { + it('clears the cookie set without path', function() { + this.fakeFastBoot.request.path = '/path'; + let value = randomString(); + this.subject().write(COOKIE_NAME, value); + + expect(this.subject().read(COOKIE_NAME)).to.eq(value); + + this.subject().clear(COOKIE_NAME, { path: '/path' }); + + expect(this.subject().read(COOKIE_NAME)).to.be.undefined; + }); + + it('clears the cookie set for a given path', function() { + let path = '/path'; + this.fakeFastBoot.request.path = path; + let value = randomString(); + this.subject().write(COOKIE_NAME, value, { path }); + + expect(this.subject().read(COOKIE_NAME)).to.eq(value); + + this.subject().clear(COOKIE_NAME, { path }); + + expect(this.subject().read(COOKIE_NAME)).to.be.undefined; + }); + }); + + describe('with a domain option', function() { + it('clears the cookie set without domain', function() { + let domain = 'example.com'; + this.fakeFastBoot.request._host = domain; + let value = randomString(); + this.subject().write(COOKIE_NAME, value); + + expect(this.subject().read(COOKIE_NAME)).to.eq(value); + + this.subject().clear(COOKIE_NAME, { domain }); + + expect(this.subject().read(COOKIE_NAME)).to.be.undefined; + }); + + it('clears the cookie set for a given domain', function() { + let domain = 'example.com'; + this.fakeFastBoot.request._host = domain; + let value = randomString(); + this.subject().write(COOKIE_NAME, value, { domain }); + + expect(this.subject().read(COOKIE_NAME)).to.eq(value); + + this.subject().clear(COOKIE_NAME, { domain }); + + expect(this.subject().read(COOKIE_NAME)).to.be.undefined; + }); }); });