diff --git a/index.js b/index.js index 7506a79..ac3bb0d 100644 --- a/index.js +++ b/index.js @@ -46,7 +46,9 @@ var fieldContentRegExp = /^[\u0009\u0020-\u007e\u0080-\u00ff]+$/; function parse(str, options) { if (typeof str !== 'string') { - throw new TypeError('argument str must be a string'); + var parseError = new TypeError('argument str must be a string') + parseError.code = 'ERR_INVALID_ARG_TYPE' + throw parseError } var obj = {} @@ -113,17 +115,23 @@ function serialize(name, val, options) { var enc = opt.encode || encode; if (typeof enc !== 'function') { - throw new TypeError('option encode is invalid'); + var encodeError = new TypeError('option encode is invalid') + encodeError.code = 'ERR_INVALID_ARG_TYPE' + throw encodeError } if (!fieldContentRegExp.test(name)) { - throw new TypeError('argument name is invalid'); + var nameError = new TypeError('argument name is invalid') + nameError.code = 'ERR_INVALID_ARG_VALUE' + throw nameError; } var value = enc(val); if (value && !fieldContentRegExp.test(value)) { - throw new TypeError('argument val is invalid'); + var returnError = new TypeError('argument val is invalid') + returnError.code = 'ERR_INVALID_RETURN_VALUE' + throw returnError; } var str = name + '=' + value; @@ -132,7 +140,9 @@ function serialize(name, val, options) { var maxAge = opt.maxAge - 0; if (isNaN(maxAge) || !isFinite(maxAge)) { - throw new TypeError('option maxAge is invalid') + var maxAgeError = new TypeError('option maxAge is invalid') + maxAgeError.code = 'ERR_INVALID_ARG_VALUE' + throw maxAgeError; } str += '; Max-Age=' + Math.floor(maxAge); @@ -140,7 +150,9 @@ function serialize(name, val, options) { if (opt.domain) { if (!fieldContentRegExp.test(opt.domain)) { - throw new TypeError('option domain is invalid'); + var domainError = new TypeError('option domain is invalid') + domainError.code = 'ERR_INVALID_ARG_VALUE' + throw domainError; } str += '; Domain=' + opt.domain; @@ -148,7 +160,9 @@ function serialize(name, val, options) { if (opt.path) { if (!fieldContentRegExp.test(opt.path)) { - throw new TypeError('option path is invalid'); + var pathError = new TypeError('option path is invalid') + pathError.code = 'ERR_INVALID_ARG_VALUE' + throw pathError; } str += '; Path=' + opt.path; @@ -156,9 +170,14 @@ function serialize(name, val, options) { if (opt.expires) { var expires = opt.expires - - if (!isDate(expires) || isNaN(expires.valueOf())) { - throw new TypeError('option expires is invalid'); + var expiresError = new TypeError('option expires is invalid') + + if (!isDate(expires)) { + expiresError.code = 'ERR_INVALID_ARG_TYPE' + throw expiresError; + } else if (isNaN(expires.valueOf())) { + expiresError.code = 'ERR_INVALID_ARG_VALUE' + throw expiresError; } str += '; Expires=' + expires.toUTCString() @@ -177,9 +196,13 @@ function serialize(name, val, options) { } if (opt.priority) { - var priority = typeof opt.priority === 'string' - ? opt.priority.toLowerCase() - : opt.priority + var priorityError = new TypeError('option priority is invalid') + if (typeof opt.priority !== 'string') { + priorityError.code = 'ERR_INVALID_ARG_TYPE' + throw priorityError; + } + + var priority = opt.priority.toLowerCase() switch (priority) { case 'low': @@ -192,7 +215,8 @@ function serialize(name, val, options) { str += '; Priority=High' break default: - throw new TypeError('option priority is invalid') + priorityError.code = 'ERR_INVALID_ARG_VALUE' + throw priorityError; } } @@ -214,7 +238,11 @@ function serialize(name, val, options) { str += '; SameSite=None'; break; default: - throw new TypeError('option sameSite is invalid'); + var sameSiteError = new TypeError('option sameSite is invalid') + sameSiteError.code = typeof opt.sameSite === 'string' + ? 'ERR_INVALID_ARG_VALUE' + : 'ERR_INVALID_ARG_TYPE' + throw sameSiteError; } } diff --git a/test/compare-error.js b/test/compare-error.js new file mode 100644 index 0000000..39f99a1 --- /dev/null +++ b/test/compare-error.js @@ -0,0 +1,13 @@ +function compareError(errorProperties) { + return function (error) { + if ( + error instanceof Error && + error.message === errorProperties.message && + error.code === errorProperties.code + ) { + return true; + } + }; +} + +module.exports = compareError; diff --git a/test/parse.js b/test/parse.js index 76229ca..ccd0476 100644 --- a/test/parse.js +++ b/test/parse.js @@ -1,16 +1,29 @@ var assert = require('assert'); var Buffer = require('safe-buffer').Buffer +var compareError = require('./compare-error'); var cookie = require('..'); describe('cookie.parse(str)', function () { it('should throw with no arguments', function () { - assert.throws(cookie.parse.bind(), /argument str must be a string/) + assert.throws( + cookie.parse.bind(), + compareError({ + message: 'argument str must be a string', + code: 'ERR_INVALID_ARG_TYPE', + }) + ) }) it('should throw when not a string', function () { - assert.throws(cookie.parse.bind(null, 42), /argument str must be a string/) + assert.throws( + cookie.parse.bind(null, 42), + compareError({ + message: 'argument str must be a string', + code: 'ERR_INVALID_ARG_TYPE', + }) + ) }) it('should parse cookie string to object', function () { diff --git a/test/serialize.js b/test/serialize.js index 80d0c48..4d96b2f 100644 --- a/test/serialize.js +++ b/test/serialize.js @@ -1,6 +1,7 @@ var assert = require('assert'); var Buffer = require('safe-buffer').Buffer +var compareError = require('./compare-error'); var cookie = require('..'); @@ -18,8 +19,20 @@ describe('cookie.serialize(name, value)', function () { }) 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/) + assert.throws( + cookie.serialize.bind(cookie, 'foo\n', 'bar'), + compareError({ + message: 'argument name is invalid', + code: 'ERR_INVALID_ARG_VALUE', + }) + ) + assert.throws( + cookie.serialize.bind(cookie, 'foo\u280a', 'bar'), + compareError({ + message: 'argument name is invalid', + code: 'ERR_INVALID_ARG_VALUE', + }) + ) }) }) @@ -31,15 +44,25 @@ describe('cookie.serialize(name, value, options)', function () { }) it('should throw for invalid value', function () { - assert.throws(cookie.serialize.bind(cookie, 'foo', 'bar', { domain: 'example.com\n' }), - /option domain is invalid/) + assert.throws( + cookie.serialize.bind(cookie, 'foo', 'bar', { domain: 'example.com\n' }), + compareError({ + message: 'option domain is invalid', + code: 'ERR_INVALID_ARG_VALUE', + }) + ) }) }) 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/) + assert.throws( + cookie.serialize.bind(cookie, 'foo', 'bar', { encode: 42 }), + compareError({ + message: 'option encode is invalid', + code: 'ERR_INVALID_ARG_TYPE', + }) + ) }) it('should specify alternative value encoder', function () { @@ -49,21 +72,37 @@ describe('cookie.serialize(name, value, options)', function () { }) 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/) + assert.throws( + cookie.serialize.bind(cookie, 'foo', '+ \n', { + encode: function (v) { return v } + }), + compareError({ + message: 'argument val is invalid', + code: 'ERR_INVALID_RETURN_VALUE', + }) + ) }) }) 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/) + assert.throws( + cookie.serialize.bind(cookie, 'foo', 'bar', { expires: 42 }), + compareError({ + message: 'option expires is invalid', + code: 'ERR_INVALID_ARG_TYPE', + }) + ) }) it('should throw on invalid date', function () { - assert.throws(cookie.serialize.bind(cookie, 'foo', 'bar', { expires: new Date(NaN) }), - /option expires is invalid/) + assert.throws( + cookie.serialize.bind(cookie, 'foo', 'bar', { expires: new Date(NaN) }), + compareError({ + message: 'option expires is invalid', + code: 'ERR_INVALID_ARG_VALUE', + }) + ) }) it('should set expires to given date', function () { @@ -85,15 +124,27 @@ describe('cookie.serialize(name, value, options)', function () { 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/) + assert.throws( + function () { + cookie.serialize('foo', 'bar', { maxAge: 'buzz' }) + }, + compareError({ + message: 'option maxAge is invalid', + code: 'ERR_INVALID_ARG_VALUE', + }) + ) }) it('should throw when Infinity', function () { - assert.throws(function () { - cookie.serialize('foo', 'bar', { maxAge: Infinity }) - }, /option maxAge is invalid/) + assert.throws( + function () { + cookie.serialize('foo', 'bar', { maxAge: Infinity }) + }, + compareError({ + message: 'option maxAge is invalid', + code: 'ERR_INVALID_ARG_VALUE', + }) + ) }) it('should set max-age to value', function () { @@ -133,22 +184,39 @@ describe('cookie.serialize(name, value, options)', function () { }) it('should throw for invalid value', function () { - assert.throws(cookie.serialize.bind(cookie, 'foo', 'bar', { path: '/\n' }), - /option path is invalid/) + assert.throws( + cookie.serialize.bind(cookie, 'foo', 'bar', { path: '/\n' }), + compareError({ + message: 'option path is invalid', + code: 'ERR_INVALID_ARG_VALUE', + }) + ) }) }) 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/) + assert.throws( + function () { + cookie.serialize('foo', 'bar', { priority: 'foo' }) + }, + compareError({ + message: 'option priority is invalid', + code: 'ERR_INVALID_ARG_VALUE', + }) + ) }) it('should throw on non-string', function () { - assert.throws(function () { - cookie.serialize('foo', 'bar', { priority: 42 }) - }, /option priority is invalid/) + assert.throws( + function () { + cookie.serialize('foo', 'bar', { priority: 42 }) + }, + compareError({ + message: 'option priority is invalid', + code: 'ERR_INVALID_ARG_TYPE', + }) + ) }) it('should set priority low', function () { @@ -169,9 +237,27 @@ describe('cookie.serialize(name, value, options)', function () { 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/) + assert.throws( + function () { + cookie.serialize('foo', 'bar', { sameSite: 'foo' }) + }, + compareError({ + message: 'option sameSite is invalid', + code: 'ERR_INVALID_ARG_VALUE', + }) + ) + }) + + it('should throw on non-string/non-true', function () { + assert.throws( + function () { + cookie.serialize('foo', 'bar', { sameSite: 42 }) + }, + compareError({ + message: 'option sameSite is invalid', + code: 'ERR_INVALID_ARG_TYPE', + }) + ) }) it('should set sameSite strict', function () {