diff --git a/HISTORY.md b/HISTORY.md index 0379a9e..ce4a778 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,6 +1,7 @@ unreleased ========== + * Add `isHttpError` export to determine if value is an HTTP error * deps: setprototypeof@1.2.0 2019-06-24 / 1.7.3 diff --git a/README.md b/README.md index 3b25481..0363bb7 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,14 @@ fs.readFile('foo.txt', function (err, buf) { - `error` - the error object to extend - `properties` - custom properties to attach to the object +### createError.isHttpError(val) + +Determine if the provided `val` is an `HttpError`. This will return `true` +if the error inherits from the `HttpError` constructor of this module or +matches the "duck type" for an error this module creates. All outputs from +the `createError` factory will return `true` for this function, including +if an non-`HttpError` was passed into the factory. + ### new createError\[code || name\](\[msg]\)) Create a new error object with the given message `msg`. diff --git a/index.js b/index.js index c55e773..1a88029 100644 --- a/index.js +++ b/index.js @@ -25,6 +25,7 @@ var toIdentifier = require('toidentifier') module.exports = createError module.exports.HttpError = createHttpErrorConstructor() +module.exports.isHttpError = createIsHttpErrorFunction(module.exports.HttpError) // Populate exports for all constructors populateConstructorExports(module.exports, statuses.codes, module.exports.HttpError) @@ -172,6 +173,27 @@ function createClientErrorConstructor (HttpError, name, code) { return ClientError } +/** + * Create function to test is a value is a HttpError. + * @private + */ + +function createIsHttpErrorFunction (HttpError) { + return function isHttpError (val) { + if (!val || typeof val !== 'object') { + return false + } + + if (val instanceof HttpError) { + return true + } + + return val instanceof Error && + typeof val.expose === 'boolean' && + typeof val.statusCode === 'number' && val.status === val.statusCode + } +} + /** * Create a constructor for a server error. * @private diff --git a/test/test.js b/test/test.js index 566f6fe..eaf4e96 100644 --- a/test/test.js +++ b/test/test.js @@ -122,6 +122,62 @@ describe('createError(status, message)', function () { }) }) +describe('createError.isHttpError(val)', function () { + describe('when val is undefined', function () { + it('should return false', function () { + assert.strictEqual(createError.isHttpError(undefined), false) + }) + }) + + describe('when val is null', function () { + it('should return false', function () { + assert.strictEqual(createError.isHttpError(null), false) + }) + }) + + describe('when val is a number', function () { + it('should return false', function () { + assert.strictEqual(createError.isHttpError(42), false) + }) + }) + + describe('when val is a string', function () { + it('should return false', function () { + assert.strictEqual(createError.isHttpError('foobar'), false) + }) + }) + + describe('when val is an empty object', function () { + it('should return false', function () { + assert.strictEqual(createError.isHttpError({}), false) + }) + }) + + describe('when val is a plain Error', function () { + it('should return false', function () { + assert.strictEqual(createError.isHttpError(new Error()), false) + }) + }) + + describe('when val is an instance of HttpError', function () { + it('should return true', function () { + var err = createError(500) + + assert.ok(err instanceof createError.HttpError) + assert.strictEqual(createError.isHttpError(err), true) + }) + }) + + describe('when val is an Error passed to createError', function () { + it('should return true', function () { + var err = createError(500, new Error()) + + assert.ok(!(err instanceof createError.HttpError)) + assert.strictEqual(createError.isHttpError(err), true) + }) + }) +}) + describe('HTTP Errors', function () { it('createError(status, props)', function () { var err = createError(404, {