diff --git a/index.js b/index.js index 9dc4b53..92ed24f 100644 --- a/index.js +++ b/index.js @@ -42,7 +42,7 @@ module.exports = function argsert (typeConfig, ...args) { if ('optional' in typesAtIndex) { const optional = typesAtIndex.optional; - if (optional.indexOf('*') < 0 && optional.indexOf(observedType) < 0) { + if (arg !== undefined && optional.indexOf('*') < 0 && optional.indexOf(observedType) < 0) { throw new TypeError(errorMessage('optional')); } } @@ -91,7 +91,9 @@ function positionName (index) { } function expectedTypes (types, kind) { - return types[kind].join(' or '); + return types[kind] + .concat(kind === 'optional' && types[kind].indexOf('undefined') < 0 ? 'undefined' : []) + .join(' or '); } function isOptional (arg) { diff --git a/test.js b/test.js index b22ee8b..ca1c3c9 100644 --- a/test.js +++ b/test.js @@ -10,11 +10,11 @@ test('does not throw exception if optional argument is not provided', async t => test('throws exception if wrong type is provided for optional argument', t => { t.throws( () => argsert('[object|number]', 'hello'), - /Invalid first argument. Expected object or number but received string./ + /Invalid first argument. Expected object or number or undefined but received string./ ); t.throws( argsertPromise('[object|number]', 'hello'), - /Invalid first argument. Expected object or number but received string./ + /Invalid first argument. Expected object or number or undefined but received string./ ); }); @@ -22,10 +22,10 @@ test('does not throw exception if optional argument is valid', t => { t.true(argsert('[object]', {foo: 'bar'})); }); -test('throws exception if required argument is not provided', t => { +test('throws exception if required arguments are not provided', t => { t.throws( - () => argsert(''), - /Not enough arguments provided. Expected 1 but received 0./ + () => argsert(' <*>'), + /Not enough arguments provided. Expected 2 but received 0./ ); }); @@ -85,6 +85,10 @@ test('allows wildcard to be used in optional configuration', async t => { t.true(await argsertPromise('[string] [*]', 'foo', {})); }); +test('allows undefined for optional arguments', t => { + t.true(argsert('[string] <*>', undefined, {})); +}); + test('throws when the optional config has broken syntax', t => { t.throws( () => argsert('<*> [foo||bar]', 'baz', 'boo'), @@ -119,6 +123,18 @@ test('allows rejected promise in the optional config', t => { t.true(argsert('[promise]', Promise.reject(new Error('no timeout')).catch(() => {}))); }); +test('allows buffer in the required config', t => { + const buffer = new Buffer('robin'); + + t.true(argsert('', buffer)); +}); + +test('allows buffer in the optional config', t => { + const buffer = new Buffer('batgirl'); + + t.true(argsert('[buffer]', buffer)); +}); + test('the arguments object can be passed in, spread', t => { function foo () { return argsert('[string|number] ', ...arguments); @@ -127,14 +143,21 @@ test('the arguments object can be passed in, spread', t => { t.true(foo('far', {})); }); -test('allows buffer in the required config', t => { - const buffer = new Buffer('robin'); +test('Function.prototype.apply with the arguments object', t => { + function foo () { + return argsert.apply(this, arguments); + } - t.true(argsert('', buffer)); + t.true(foo('[string|number] ', 'bar', {})); }); -test('allows buffer in the optional config', t => { - const buffer = new Buffer('batgirl'); +test('Function.prototype.call with the arguments object spread', t => { + function foo () { + return argsert.call(this, ...arguments); + } - t.true(argsert('[buffer]', buffer)); + t.throws( + () => foo('[number] ', 'bar', {}), + /Invalid first argument. Expected number or undefined but received string./ + ); });