diff --git a/src/format.spec.ts b/src/format.spec.ts index a613e76..e64c0a9 100644 --- a/src/format.spec.ts +++ b/src/format.spec.ts @@ -5,10 +5,7 @@ describe('format(credentials)', function () { describe('arguments', function () { describe('credentials', function () { it('should be required', function () { - assert.throws( - () => (format as any)(), - /argument credentials is required/, - ); + assert.throws(() => (format as any)(), /Expected an object/); }); it('should accept credentials', function () { @@ -17,65 +14,56 @@ describe('format(credentials)', function () { }); it('should reject null', function () { - assert.throws( - format.bind(null, null as any), - /argument credentials is required/, - ); + assert.throws(format.bind(null, null as any), /Expected an object/); }); it('should reject a number', function () { - assert.throws( - format.bind(null, 42 as any), - /argument credentials is required/, - ); + assert.throws(format.bind(null, 42 as any), /Expected an object/); }); it('should reject a string', function () { - assert.throws( - format.bind(null, '' as any), - /argument credentials is required/, - ); + assert.throws(format.bind(null, '' as any), /Expected an object/); }); it('should reject an object without name', function () { assert.throws( format.bind(null, { pass: 'bar' } as any), - /argument credentials is required to have name and pass properties/, + /Object must have string properties "name" and "pass"/, ); }); it('should reject an object without pass', function () { assert.throws( format.bind(null, { name: 'foo' } as any), - /argument credentials is required to have name and pass properties/, + /Object must have string properties "name" and "pass"/, ); }); it('should reject an object with non-string name', function () { assert.throws( format.bind(null, { name: 42, pass: 'bar' } as any), - /argument credentials is required to have name and pass properties/, + /Object must have string properties "name" and "pass"/, ); }); it('should reject an object with non-string pass', function () { assert.throws( format.bind(null, { name: 'foo', pass: 42 } as any), - /argument credentials is required to have name and pass properties/, + /Object must have string properties "name" and "pass"/, ); }); it('should reject userid containing colon', function () { assert.throws( format.bind(null, { name: 'foo:bar', pass: 'baz' }), - /must not contain a colon or control characters/, + /must not contain a colon/, ); }); it('should reject control chars in userid', function () { assert.throws( format.bind(null, { name: 'foo\u0000bar', pass: 'baz' }), - /must not contain a colon or control characters/, + /must not contain control characters/, ); }); @@ -96,21 +84,21 @@ describe('format(credentials)', function () { }); describe('with empty password', function () { - it('should throw', function () { + it('should return header', function () { const header = format({ name: 'foo', pass: '' }); assert.strictEqual(header, 'Basic Zm9vOg=='); }); }); describe('with empty userid', function () { - it('should throw', function () { + it('should return header', function () { const header = format({ name: '', pass: 'pass' }); assert.strictEqual(header, 'Basic OnBhc3M='); }); }); describe('with empty userid and pass', function () { - it('should throw', function () { + it('should return header', function () { const header = format({ name: '', pass: '' }); assert.strictEqual(header, 'Basic Og=='); }); diff --git a/src/index.ts b/src/index.ts index d4d4b1b..959b832 100644 --- a/src/index.ts +++ b/src/index.ts @@ -26,7 +26,7 @@ export interface Credentials { export function parse(string: string): Credentials | undefined { if (typeof string !== 'string') { - return undefined; + throw new TypeError('Expected a string'); } // parse header @@ -52,39 +52,31 @@ export function parse(string: string): Credentials | undefined { * @public */ export function format(credentials: Credentials): string { - if (!credentials) { - throw new TypeError('argument credentials is required'); - } - - if (typeof credentials !== 'object') { - throw new TypeError('argument credentials is required to be an object'); + if (typeof credentials !== 'object' || credentials === null) { + throw new TypeError('Expected an object'); } if ( typeof credentials.name !== 'string' || typeof credentials.pass !== 'string' ) { - throw new TypeError( - 'argument credentials is required to have name and pass properties', - ); + throw new TypeError('Object must have string properties "name" and "pass"'); } - if ( - credentials.name.includes(':') || // RFC 7617 disallows colon in username - CONTROL_CHARS_REGEXP.test(credentials.name) - ) { - throw new TypeError( - 'argument credentials.name must not contain a colon or control characters', - ); + // RFC 7617 disallows colon in username + if (credentials.name.includes(':')) { + throw new TypeError('Object "name" must not contain a colon'); } - if (CONTROL_CHARS_REGEXP.test(credentials.pass)) { + const str = credentials.name + ':' + credentials.pass; + + if (CONTROL_CHARS_REGEXP.test(str)) { throw new TypeError( - 'argument credentials.pass must not contain control characters', + 'Object "name" and "pass" must not contain control characters', ); } - return 'Basic ' + base64.encode(credentials.name + ':' + credentials.pass); + return 'Basic ' + base64.encode(str); } /** diff --git a/src/parse.spec.ts b/src/parse.spec.ts index 6347ea1..5bd5b79 100644 --- a/src/parse.spec.ts +++ b/src/parse.spec.ts @@ -2,9 +2,9 @@ import { describe, it, assert } from 'vitest'; import { parse } from './index.js'; describe('parse(string)', function () { - describe('with undefined string', function () { - it('should return undefined', function () { - assert.strictEqual((parse as any)(), undefined); + describe('with non string', function () { + it('should throw', function () { + assert.throws(() => parse(undefined as any), /Expected a string/); }); });