diff --git a/package.json b/package.json index dbf44cd..2837a4e 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "eslint-plugin-markdown": "2.2.1", "mocha": "9.2.2", "nyc": "15.1.0", + "safe-buffer": "5.2.1", "top-sites": "1.1.89" }, "files": [ @@ -33,7 +34,7 @@ "scripts": { "bench": "node benchmark/index.js", "lint": "eslint .", - "test": "mocha --reporter spec --bail --check-leaks --ui qunit test/", + "test": "mocha --reporter spec --bail --check-leaks test/", "test-ci": "nyc --reporter=lcov --reporter=text npm test", "test-cov": "nyc --reporter=html --reporter=text npm test", "update-bench": "node scripts/update-benchmark.js", diff --git a/test/parse.js b/test/parse.js index f19f3b6..2e83bb0 100644 --- a/test/parse.js +++ b/test/parse.js @@ -1,65 +1,60 @@ var assert = require('assert'); +var Buffer = require('safe-buffer').Buffer var cookie = require('..'); -suite('parse'); - -test('argument validation', function() { - assert.throws(cookie.parse.bind(), /argument str must be a string/); - assert.throws(cookie.parse.bind(null, 42), /argument str must be a string/); -}); - -test('basic', function() { - assert.deepEqual(cookie.parse('foo=bar'), { foo: 'bar' }) - assert.deepEqual(cookie.parse('foo=123'), { foo: '123' }) -}); - -test('ignore spaces', function() { - assert.deepEqual(cookie.parse('FOO = bar; baz = raz'), - { FOO: 'bar', baz: 'raz' }) -}); - -test('escaping', function() { - assert.deepEqual(cookie.parse('foo="bar=123456789&name=Magic+Mouse"'), - { foo: 'bar=123456789&name=Magic+Mouse' }) - - assert.deepEqual(cookie.parse('email=%20%22%2c%3b%2f'), { email: ' ",;/' }) -}); - -test('ignore escaping error and return original value', function() { - assert.deepEqual(cookie.parse('foo=%1;bar=bar'), { foo: '%1', bar: 'bar' }) -}); - -test('ignore non values', function() { - assert.deepEqual(cookie.parse('foo=%1;bar=bar;HttpOnly;Secure'), - { foo: '%1', bar: 'bar' }) -}); - -test('unencoded', function() { - assert.deepEqual(cookie.parse('foo="bar=123456789&name=Magic+Mouse"', { - decode: function (v) { return v } - }), { foo: 'bar=123456789&name=Magic+Mouse' }) - - assert.deepEqual(cookie.parse('email=%20%22%2c%3b%2f', { - decode: function (v) { return v } - }), { email: '%20%22%2c%3b%2f' }) -}); - -test('dates', function() { - assert.deepEqual(cookie.parse('priority=true; expires=Wed, 29 Jan 2014 17:43:25 GMT; Path=/', { - decode: function (v) { return v } - }), { priority: 'true', Path: '/', expires: 'Wed, 29 Jan 2014 17:43:25 GMT' }) -}); - -test('missing value', function() { - assert.deepEqual(cookie.parse('foo; bar=1; fizz= ; buzz=2', { - decode: function (v) { return v } - }), { bar: '1', fizz: '', buzz: '2' }) -}); - -test('assign only once', function() { - assert.deepEqual(cookie.parse('foo=%1;bar=bar;foo=boo'), { foo: '%1', bar: 'bar' }) - assert.deepEqual(cookie.parse('foo=false;bar=bar;foo=true'), { foo: 'false', bar: 'bar' }) - assert.deepEqual(cookie.parse('foo=;bar=bar;foo=boo'), { foo: '', bar: 'bar' }) -}); +describe('cookie.parse(str)', function () { + it('should throw with no arguments', function () { + assert.throws(cookie.parse.bind(), /argument str must be a string/) + }) + + it('should throw when not a string', function () { + assert.throws(cookie.parse.bind(null, 42), /argument str must be a string/) + }) + + it('should parse cookie string to object', function () { + assert.deepEqual(cookie.parse('foo=bar'), { foo: 'bar' }) + assert.deepEqual(cookie.parse('foo=123'), { foo: '123' }) + }) + + it('should ignore OWS', function () { + assert.deepEqual(cookie.parse('FOO = bar; baz = raz'), + { FOO: 'bar', baz: 'raz' }) + }) + + it('should parse cookie with empty value', function () { + assert.deepEqual(cookie.parse('foo= ; bar='), { foo: '', bar: '' }) + }) + + it('should URL-decode values', function () { + assert.deepEqual(cookie.parse('foo="bar=123456789&name=Magic+Mouse"'), + { foo: 'bar=123456789&name=Magic+Mouse' }) + + assert.deepEqual(cookie.parse('email=%20%22%2c%3b%2f'), { email: ' ",;/' }) + }) + + it('should return original value on escape error', function () { + assert.deepEqual(cookie.parse('foo=%1;bar=bar'), { foo: '%1', bar: 'bar' }) + }) + + it('should ignore cookies without value', function () { + assert.deepEqual(cookie.parse('foo=bar;fizz ; buzz'), { foo: 'bar' }) + }) + + it('should ignore duplicate cookies', function () { + assert.deepEqual(cookie.parse('foo=%1;bar=bar;foo=boo'), { foo: '%1', bar: 'bar' }) + assert.deepEqual(cookie.parse('foo=false;bar=bar;foo=true'), { foo: 'false', bar: 'bar' }) + assert.deepEqual(cookie.parse('foo=;bar=bar;foo=boo'), { foo: '', bar: 'bar' }) + }) +}) + +describe('cookie.parse(str, options)', function () { + describe('with "decode" option', function () { + it('should specify alternative value decoder', function () { + assert.deepEqual(cookie.parse('foo="YmFy"', { + decode: function (v) { return Buffer.from(v, 'base64').toString() } + }), { foo: 'bar' }) + }) + }) +}) diff --git a/test/serialize.js b/test/serialize.js index 06c8207..6e34590 100644 --- a/test/serialize.js +++ b/test/serialize.js @@ -1,128 +1,196 @@ -// builtin + var assert = require('assert'); +var Buffer = require('safe-buffer').Buffer var cookie = require('..'); -suite('serialize'); - -test('basic', function() { - assert.equal(cookie.serialize('foo', 'bar'), 'foo=bar') - assert.equal(cookie.serialize('foo', 'bar baz'), 'foo=bar%20baz') - assert.equal(cookie.serialize('foo', ''), 'foo=') - assert.throws(cookie.serialize.bind(cookie, 'foo\n', 'bar'), /argument name is invalid/); - assert.throws(cookie.serialize.bind(cookie, 'foo\u280a', 'bar'), /argument name is invalid/); - assert.throws(cookie.serialize.bind(cookie, 'foo', 'bar', {encode: 42}), /option encode is invalid/); -}); - -test('path', function() { - assert.equal(cookie.serialize('foo', 'bar', { path: '/' }), 'foo=bar; Path=/') - assert.throws(cookie.serialize.bind(cookie, 'foo', 'bar', { - path: '/\n' - }), /option path is invalid/); -}); - -test('secure', function() { - assert.equal(cookie.serialize('foo', 'bar', { secure: true }), 'foo=bar; Secure') - assert.equal(cookie.serialize('foo', 'bar', { secure: false }), 'foo=bar') -}); - -test('domain', function() { - assert.equal(cookie.serialize('foo', 'bar', { domain: 'example.com' }), 'foo=bar; Domain=example.com') - assert.throws(cookie.serialize.bind(cookie, 'foo', 'bar', { - domain: 'example.com\n' - }), /option domain is invalid/); -}); - -test('httpOnly', function() { - assert.equal(cookie.serialize('foo', 'bar', { httpOnly: true }), 'foo=bar; HttpOnly') -}); - -test('maxAge', function() { - assert.throws(function () { - cookie.serialize('foo', 'bar', { - maxAge: 'buzz' - }); - }, /option maxAge is invalid/) - - assert.throws(function () { - cookie.serialize('foo', 'bar', { - maxAge: Infinity - }) - }, /option maxAge is invalid/) - - assert.equal(cookie.serialize('foo', 'bar', { maxAge: 1000 }), 'foo=bar; Max-Age=1000') - assert.equal(cookie.serialize('foo', 'bar', { maxAge: '1000' }), 'foo=bar; Max-Age=1000') - assert.equal(cookie.serialize('foo', 'bar', { maxAge: 0 }), 'foo=bar; Max-Age=0') - assert.equal(cookie.serialize('foo', 'bar', { maxAge: '0' }), 'foo=bar; Max-Age=0') - assert.equal(cookie.serialize('foo', 'bar', { maxAge: null }), 'foo=bar') - assert.equal(cookie.serialize('foo', 'bar', { maxAge: undefined }), 'foo=bar') - assert.equal(cookie.serialize('foo', 'bar', { maxAge: 3.14 }), 'foo=bar; Max-Age=3') -}); - -test('expires', function() { - assert.equal(cookie.serialize('foo', 'bar', { - expires: new Date(Date.UTC(2000, 11, 24, 10, 30, 59, 900)) - }), 'foo=bar; Expires=Sun, 24 Dec 2000 10:30:59 GMT') - - assert.throws(cookie.serialize.bind(cookie, 'foo', 'bar', { - expires: Date.now() - }), /option expires is invalid/); - - assert.throws(cookie.serialize.bind(cookie, 'foo', 'bar', { - expires: new Date(NaN) - }), /option expires is invalid/) -}); - -test('priority', function () { - assert.equal(cookie.serialize('foo', 'bar', { priority: 'Low' }), 'foo=bar; Priority=Low') - assert.equal(cookie.serialize('foo', 'bar', { priority: 'loW' }), 'foo=bar; Priority=Low') - assert.equal(cookie.serialize('foo', 'bar', { priority: 'Medium' }), 'foo=bar; Priority=Medium') - assert.equal(cookie.serialize('foo', 'bar', { priority: 'medium' }), 'foo=bar; Priority=Medium') - assert.equal(cookie.serialize('foo', 'bar', { priority: 'High' }), 'foo=bar; Priority=High') - assert.equal(cookie.serialize('foo', 'bar', { priority: 'HIGH' }), 'foo=bar; Priority=High') - - assert.throws(cookie.serialize.bind(cookie, 'foo', 'bar', { - priority: 'foo' - }), /option priority is invalid/) - - assert.throws(cookie.serialize.bind(cookie, 'foo', 'bar', { - priority: 42 - }), /option priority is invalid/) +describe('cookie.serialize(name, value)', function () { + it('should serialize name and value', function () { + assert.equal(cookie.serialize('foo', 'bar'), 'foo=bar') + }) + + it('should URL-encode value', function () { + assert.equal(cookie.serialize('foo', 'bar +baz'), 'foo=bar%20%2Bbaz') + }) + + it('should serialize empty value', function () { + assert.equal(cookie.serialize('foo', ''), 'foo=') + }) + + it('should throw for invalid name', function () { + assert.throws(cookie.serialize.bind(cookie, 'foo\n', 'bar'), /argument name is invalid/) + assert.throws(cookie.serialize.bind(cookie, 'foo\u280a', 'bar'), /argument name is invalid/) + }) }) -test('sameSite', function() { - assert.equal(cookie.serialize('foo', 'bar', { sameSite: true }), 'foo=bar; SameSite=Strict') - assert.equal(cookie.serialize('foo', 'bar', { sameSite: 'Strict' }), 'foo=bar; SameSite=Strict') - assert.equal(cookie.serialize('foo', 'bar', { sameSite: 'strict' }), 'foo=bar; SameSite=Strict') - assert.equal(cookie.serialize('foo', 'bar', { sameSite: 'Lax' }), 'foo=bar; SameSite=Lax') - assert.equal(cookie.serialize('foo', 'bar', { sameSite: 'lax' }), 'foo=bar; SameSite=Lax') - assert.equal(cookie.serialize('foo', 'bar', { sameSite: 'None' }), 'foo=bar; SameSite=None') - assert.equal(cookie.serialize('foo', 'bar', { sameSite: 'none' }), 'foo=bar; SameSite=None') - assert.equal(cookie.serialize('foo', 'bar', { sameSite: false }), 'foo=bar') - - assert.throws(cookie.serialize.bind(cookie, 'foo', 'bar', { - sameSite: 'foo' - }), /option sameSite is invalid/); -}); - -test('escaping', function() { - assert.deepEqual(cookie.serialize('cat', '+ '), 'cat=%2B%20') -}); - -test('parse->serialize', function() { - assert.deepEqual(cookie.parse(cookie.serialize('cat', 'foo=123&name=baz five')), - { cat: 'foo=123&name=baz five' }) - - assert.deepEqual(cookie.parse(cookie.serialize('cat', ' ";/')), - { cat: ' ";/' }) -}); - -test('unencoded', function() { - assert.deepEqual(cookie.serialize('cat', '+ ', { - encode: function(value) { return value; } - }), 'cat=+ ') - - assert.throws(cookie.serialize.bind(cookie, 'cat', '+ \n', { - encode: function(value) { return value; } - }), /argument val is invalid/); +describe('cookie.serialize(name, value, options)', function () { + describe('with "domain" option', function () { + it('should serialize domain', function () { + assert.equal(cookie.serialize('foo', 'bar', { domain: 'example.com' }), + 'foo=bar; Domain=example.com') + }) + + it('should throw for invalid value', function () { + assert.throws(cookie.serialize.bind(cookie, 'foo', 'bar', { domain: 'example.com\n' }), + /option domain is invalid/) + }) + }) + + describe('with "encode" option', function () { + it('should throw on non-function value', function () { + assert.throws(cookie.serialize.bind(cookie, 'foo', 'bar', { encode: 42 }), + /option encode is invalid/) + }) + + it('should specify alternative value encoder', function () { + assert.deepEqual(cookie.serialize('foo', 'bar', { + encode: function (v) { return Buffer.from(v, 'utf8').toString('base64') } + }), 'foo=YmFy') + }) + + it('should throw when returned value is invalid', function () { + assert.throws(cookie.serialize.bind(cookie, 'foo', '+ \n', { + encode: function (v) { return v } + }), /argument val is invalid/) + }) + }) + + describe('with "expires" option', function () { + it('should throw on non-Date value', function () { + assert.throws(cookie.serialize.bind(cookie, 'foo', 'bar', { expires: 42 }), + /option expires is invalid/) + }) + + it('should throw on invalid date', function () { + assert.throws(cookie.serialize.bind(cookie, 'foo', 'bar', { expires: new Date(NaN) }), + /option expires is invalid/) + }) + + it('should set expires to given date', function () { + assert.equal(cookie.serialize('foo', 'bar', { + expires: new Date(Date.UTC(2000, 11, 24, 10, 30, 59, 900)) + }), 'foo=bar; Expires=Sun, 24 Dec 2000 10:30:59 GMT') + }) + }) + + describe('with "httpOnly" option', function () { + it('should include httpOnly flag when true', function () { + assert.equal(cookie.serialize('foo', 'bar', { httpOnly: true }), 'foo=bar; HttpOnly') + }) + + it('should not include httpOnly flag when false', function () { + assert.equal(cookie.serialize('foo', 'bar', { httpOnly: false }), 'foo=bar') + }) + }) + + describe('with "maxAge" option', function () { + it('should throw when not a number', function () { + assert.throws(function () { + cookie.serialize('foo', 'bar', { maxAge: 'buzz' }) + }, /option maxAge is invalid/) + }) + + it('should throw when Infinity', function () { + assert.throws(function () { + cookie.serialize('foo', 'bar', { maxAge: Infinity }) + }, /option maxAge is invalid/) + }) + + it('should set max-age to value', function () { + assert.equal(cookie.serialize('foo', 'bar', { maxAge: 1000 }), 'foo=bar; Max-Age=1000') + assert.equal(cookie.serialize('foo', 'bar', { maxAge: '1000' }), 'foo=bar; Max-Age=1000') + assert.equal(cookie.serialize('foo', 'bar', { maxAge: 0 }), 'foo=bar; Max-Age=0') + assert.equal(cookie.serialize('foo', 'bar', { maxAge: '0' }), 'foo=bar; Max-Age=0') + }) + + it('should set max-age to integer value', function () { + assert.equal(cookie.serialize('foo', 'bar', { maxAge: 3.14 }), 'foo=bar; Max-Age=3') + assert.equal(cookie.serialize('foo', 'bar', { maxAge: 3.99 }), 'foo=bar; Max-Age=3') + }) + + it('should not set when null', function () { + assert.equal(cookie.serialize('foo', 'bar', { maxAge: null }), 'foo=bar') + }) + }) + + describe('with "path" option', function () { + it('should serialize path', function () { + assert.equal(cookie.serialize('foo', 'bar', { path: '/' }), 'foo=bar; Path=/') + }) + + it('should throw for invalid value', function () { + assert.throws(cookie.serialize.bind(cookie, 'foo', 'bar', { path: '/\n' }), + /option path is invalid/) + }) + }) + + describe('with "priority" option', function () { + it('should throw on invalid priority', function () { + assert.throws(function () { + cookie.serialize('foo', 'bar', { priority: 'foo' }) + }, /option priority is invalid/) + }) + + it('should throw on non-string', function () { + assert.throws(function () { + cookie.serialize('foo', 'bar', { priority: 42 }) + }, /option priority is invalid/) + }) + + it('should set priority low', function () { + assert.equal(cookie.serialize('foo', 'bar', { priority: 'Low' }), 'foo=bar; Priority=Low') + assert.equal(cookie.serialize('foo', 'bar', { priority: 'loW' }), 'foo=bar; Priority=Low') + }) + + it('should set priority medium', function () { + assert.equal(cookie.serialize('foo', 'bar', { priority: 'Medium' }), 'foo=bar; Priority=Medium') + assert.equal(cookie.serialize('foo', 'bar', { priority: 'medium' }), 'foo=bar; Priority=Medium') + }) + + it('should set priority high', function () { + assert.equal(cookie.serialize('foo', 'bar', { priority: 'High' }), 'foo=bar; Priority=High') + assert.equal(cookie.serialize('foo', 'bar', { priority: 'HIGH' }), 'foo=bar; Priority=High') + }) + }) + + describe('with "sameSite" option', function () { + it('should throw on invalid sameSite', function () { + assert.throws(function () { + cookie.serialize('foo', 'bar', { sameSite: 'foo' }) + }, /option sameSite is invalid/) + }) + + it('should set sameSite strict', function () { + assert.equal(cookie.serialize('foo', 'bar', { sameSite: 'Strict' }), 'foo=bar; SameSite=Strict') + assert.equal(cookie.serialize('foo', 'bar', { sameSite: 'strict' }), 'foo=bar; SameSite=Strict') + }) + + it('should set sameSite lax', function () { + assert.equal(cookie.serialize('foo', 'bar', { sameSite: 'Lax' }), 'foo=bar; SameSite=Lax') + assert.equal(cookie.serialize('foo', 'bar', { sameSite: 'lax' }), 'foo=bar; SameSite=Lax') + }) + + it('should set sameSite none', function () { + assert.equal(cookie.serialize('foo', 'bar', { sameSite: 'None' }), 'foo=bar; SameSite=None') + assert.equal(cookie.serialize('foo', 'bar', { sameSite: 'none' }), 'foo=bar; SameSite=None') + }) + + it('should set sameSite strict when true', function () { + assert.equal(cookie.serialize('foo', 'bar', { sameSite: true }), 'foo=bar; SameSite=Strict') + }) + + it('should not set sameSite when false', function () { + assert.equal(cookie.serialize('foo', 'bar', { sameSite: false }), 'foo=bar') + }) + }) + + describe('with "secure" option', function () { + it('should include secure flag when true', function () { + assert.equal(cookie.serialize('foo', 'bar', { secure: true }), 'foo=bar; Secure') + }) + + it('should not include secure flag when false', function () { + assert.equal(cookie.serialize('foo', 'bar', { secure: false }), 'foo=bar') + }) + }) })