diff --git a/lib/assert.js b/lib/assert.js index 6ed635a2..7172af6f 100755 --- a/lib/assert.js +++ b/lib/assert.js @@ -1,12 +1,13 @@ 'use strict'; -const AssertError = require('./error'); +const Errors = require('./errors'); +const Stringify = require('./stringify'); const internals = {}; -module.exports = function (condition, ...args) { +const assert = module.exports = function (condition, ...args) { if (condition) { return; @@ -18,5 +19,12 @@ module.exports = function (condition, ...args) { throw args[0]; } - throw new AssertError(args); + const msgs = args + .filter((arg) => arg !== '') + .map((arg) => { + + return typeof arg === 'string' ? arg : arg instanceof Error ? arg.message : Stringify(arg); + }); + + throw new Errors.AssertError(msgs.join(' '), assert); }; diff --git a/lib/error.js b/lib/error.js deleted file mode 100755 index 9fc4f5df..00000000 --- a/lib/error.js +++ /dev/null @@ -1,26 +0,0 @@ -'use strict'; - -const Stringify = require('./stringify'); - - -const internals = {}; - - -module.exports = class extends Error { - - constructor(args) { - - const msgs = args - .filter((arg) => arg !== '') - .map((arg) => { - - return typeof arg === 'string' ? arg : arg instanceof Error ? arg.message : Stringify(arg); - }); - - super(msgs.join(' ') || 'Unknown error'); - - if (typeof Error.captureStackTrace === 'function') { // $lab:coverage:ignore$ - Error.captureStackTrace(this, exports.assert); - } - } -}; diff --git a/lib/errors.js b/lib/errors.js new file mode 100755 index 00000000..8d6909a6 --- /dev/null +++ b/lib/errors.js @@ -0,0 +1,18 @@ +'use strict'; + +const internals = {}; + + +exports.AssertError = class extends Error { + + name = 'AssertError'; + + constructor(message, ctor) { + + super(message || 'Unknown error'); + + if (typeof Error.captureStackTrace === 'function') { // $lab:coverage:ignore$ + Error.captureStackTrace(this, ctor); + } + } +}; diff --git a/lib/index.d.ts b/lib/index.d.ts old mode 100755 new mode 100644 index e0320e95..55e6031c --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -340,6 +340,15 @@ export function assert(condition: any, error: Error): asserts condition; export function assert(condition: any, ...args: any): asserts condition; +/** + * Assertion Error as thrown from Hoek.assert(). + */ +export class AssertError extends Error { + + name: 'AssertError'; +} + + /** * A benchmarking timer, using the internal node clock for maximum accuracy. */ diff --git a/lib/index.js b/lib/index.js index 2062f180..e546ad01 100755 --- a/lib/index.js +++ b/lib/index.js @@ -4,6 +4,8 @@ exports.applyToDefaults = require('./applyToDefaults'); exports.assert = require('./assert'); +exports.AssertError = require('./errors').AssertError; + exports.Bench = require('./bench'); exports.block = require('./block'); @@ -14,8 +16,6 @@ exports.contain = require('./contain'); exports.deepEqual = require('./deepEqual'); -exports.Error = require('./error'); - exports.escapeHeaderAttribute = require('./escapeHeaderAttribute'); exports.escapeHtml = require('./escapeHtml'); diff --git a/test/esm.js b/test/esm.js index 68b97634..d5d60d6d 100644 --- a/test/esm.js +++ b/test/esm.js @@ -20,8 +20,8 @@ describe('import()', () => { it('exposes all methods and classes as named imports', () => { expect(Object.keys(Hoek)).to.equal([ + 'AssertError', 'Bench', - 'Error', 'applyToDefaults', 'assert', 'block', diff --git a/test/index.js b/test/index.js index 80dd6cbe..d292574e 100755 --- a/test/index.js +++ b/test/index.js @@ -2102,7 +2102,7 @@ describe('assert()', () => { expect(() => { Hoek.assert(false, 'my error message'); - }).to.throw('my error message'); + }).to.throw(Hoek.AssertError, 'my error message'); }); it('throws an Error when using assert in a test with no message', () => { @@ -2110,7 +2110,7 @@ describe('assert()', () => { expect(() => { Hoek.assert(false); - }).to.throw('Unknown error'); + }).to.throw(Hoek.AssertError, 'Unknown error'); }); it('throws an Error when using assert in a test with multipart message', () => { @@ -2118,7 +2118,7 @@ describe('assert()', () => { expect(() => { Hoek.assert(false, 'This', 'is', 'my message'); - }).to.throw('This is my message'); + }).to.throw(Hoek.AssertError, 'This is my message'); }); it('throws an Error when using assert in a test with multipart message (empty)', () => { @@ -2126,7 +2126,7 @@ describe('assert()', () => { expect(() => { Hoek.assert(false, 'This', 'is', '', 'my message'); - }).to.throw('This is my message'); + }).to.throw(Hoek.AssertError, 'This is my message'); }); it('throws an Error when using assert in a test with object message', () => { @@ -2134,7 +2134,7 @@ describe('assert()', () => { expect(() => { Hoek.assert(false, 'This', 'is', { spinal: 'tap' }); - }).to.throw('This is {"spinal":"tap"}'); + }).to.throw(Hoek.AssertError, 'This is {"spinal":"tap"}'); }); it('throws an Error when using assert in a test with multipart string and error messages', () => { @@ -2142,13 +2142,13 @@ describe('assert()', () => { expect(() => { Hoek.assert(false, new Error('This'), 'is', 'spinal', new Error('tap')); - }).to.throw('This is spinal tap'); + }).to.throw(Hoek.AssertError, 'This is spinal tap'); }); it('throws an Error when using assert in a test with error object message', () => { - const err = new Error('This is spinal tap'); - const got = expect(() => Hoek.assert(false, err)).to.throw('This is spinal tap'); + const err = new TypeError('This is spinal tap'); + const got = expect(() => Hoek.assert(false, err)).to.throw(TypeError, 'This is spinal tap'); expect(got).to.shallow.equal(err); }); @@ -2172,6 +2172,34 @@ describe('assert()', () => { }); }); +describe('AssertError', () => { + + it('takes an optional message', () => { + + expect(new Hoek.AssertError().message).to.equal('Unknown error'); + expect(new Hoek.AssertError(null).message).to.equal('Unknown error'); + expect(new Hoek.AssertError('msg').message).to.equal('msg'); + }); + + it('has AssertError name property', () => { + + expect(new Hoek.AssertError().name).to.equal('AssertError'); + expect(new Hoek.AssertError('msg').name).to.equal('AssertError'); + }); + + it('uses ctor argument to hide stack', { skip: typeof Error.captureStackTrace !== 'function' }, () => { + + const parentFn = () => { + + throw new Hoek.AssertError('msg', parentFn); + }; + + const err = expect(parentFn).to.throw(Hoek.AssertError); + + expect(err.stack).to.not.contain('parentFn'); + }); +}); + describe('Bench', () => { it('returns time elapsed', async () => { diff --git a/test/index.ts b/test/index.ts index 10781443..6b811c48 100755 --- a/test/index.ts +++ b/test/index.ts @@ -348,3 +348,13 @@ function xor(input: Hoek.ts.XOR): number { xor({ a: 1 }); xor({ b: 2 }); expect.error(xor({ a: 1, b: 2 })); + +// AssertError + +expect.type(new Hoek.AssertError()); +expect.type(new Hoek.AssertError('fail')); +expect.type<'AssertError'>(new Hoek.AssertError().name); +expect.type(new Hoek.AssertError().message); + +expect.error(new Hoek.AssertError(new Error())); +expect.error(new Hoek.AssertError('fail', 'again'));