diff --git a/lib/internal/assert/assertion_error.js b/lib/internal/assert/assertion_error.js index 5b4124e6b29700..d036248e45755e 100644 --- a/lib/internal/assert/assertion_error.js +++ b/lib/internal/assert/assertion_error.js @@ -9,6 +9,7 @@ const { ObjectAssign, ObjectDefineProperty, ObjectGetPrototypeOf, + ObjectPrototypeHasOwnProperty, String, StringPrototypeRepeat, StringPrototypeSlice, @@ -49,8 +50,8 @@ function copyError(source) { __proto__: null, value: source.message, }); - if (source.cause !== undefined) { - let cause = source.cause; + if (ObjectPrototypeHasOwnProperty(source, 'cause')) { + let { cause } = source; if (isError(cause)) { cause = copyError(cause); diff --git a/lib/internal/util/comparisons.js b/lib/internal/util/comparisons.js index b28860e9000ce8..554e513b96e9fe 100644 --- a/lib/internal/util/comparisons.js +++ b/lib/internal/util/comparisons.js @@ -28,6 +28,7 @@ const { const { compare } = internalBinding('buffer'); const assert = require('internal/assert'); const { isURL } = require('internal/url'); +const { isError } = require('internal/util'); const types = require('internal/util/types'); const { isAnyArrayBuffer, @@ -235,10 +236,10 @@ function innerDeepEqual(val1, val2, strict, memos) { if (!isAnyArrayBuffer(val2) || !areEqualArrayBuffers(val1, val2)) { return false; } - } else if (isNativeError(val1) || val1 instanceof Error) { + } else if (isError(val1)) { // Do not compare the stack as it might differ even though the error itself // is otherwise identical. - if (!isNativeError(val2) && !(val2 instanceof Error)) { + if (!isError(val2)) { return false; } @@ -252,7 +253,9 @@ function innerDeepEqual(val1, val2, strict, memos) { (name1Enumerable !== ObjectPrototypePropertyIsEnumerable(val2, 'name') || (!name1Enumerable && val1.name !== val2.name)) || (cause1Enumerable !== ObjectPrototypePropertyIsEnumerable(val2, 'cause') || - (!cause1Enumerable && !innerDeepEqual(val1.cause, val2.cause, strict, memos))) || + (!cause1Enumerable && ( + ObjectPrototypeHasOwnProperty(val1, 'cause') !== ObjectPrototypeHasOwnProperty(val2, 'cause') || + !innerDeepEqual(val1.cause, val2.cause, strict, memos)))) || (errors1Enumerable !== ObjectPrototypePropertyIsEnumerable(val2, 'errors') || (!errors1Enumerable && !innerDeepEqual(val1.errors, val2.errors, strict, memos)))) { return false; diff --git a/test/parallel/test-assert-deep-with-error.js b/test/parallel/test-assert-deep-with-error.js index b4186eb642302d..f6bc5c6359cfe8 100644 --- a/test/parallel/test-assert-deep-with-error.js +++ b/test/parallel/test-assert-deep-with-error.js @@ -8,6 +8,12 @@ const defaultStartMessage = 'Expected values to be strictly deep-equal:\n' + '\n'; test('Handle error causes', () => { + assert.deepStrictEqual(new Error('a', { cause: new Error('x') }), new Error('a', { cause: new Error('x') })); + assert.deepStrictEqual( + new Error('a', { cause: new RangeError('x') }), + new Error('a', { cause: new RangeError('x') }), + ); + assert.throws(() => { assert.deepStrictEqual(new Error('a', { cause: new Error('x') }), new Error('a', { cause: new Error('y') })); }, { message: defaultStartMessage + ' [Error: a] {\n' + @@ -15,6 +21,13 @@ test('Handle error causes', () => { '- [cause]: [Error: y]\n' + ' }\n' }); + assert.throws(() => { + assert.deepStrictEqual(new Error('a', { cause: new Error('x') }), new Error('a', { cause: new TypeError('x') })); + }, { message: defaultStartMessage + ' [Error: a] {\n' + + '+ [cause]: [Error: x]\n' + + '- [cause]: [TypeError: x]\n' + + ' }\n' }); + assert.throws(() => { assert.deepStrictEqual(new Error('a'), new Error('a', { cause: new Error('y') })); }, { message: defaultStartMessage + '+ [Error: a]\n' + @@ -37,3 +50,33 @@ test('Handle error causes', () => { new Error('a', { cause: { prop: 'a different value' } }) ); }); + +test('Handle undefined causes', () => { + assert.deepStrictEqual(new Error('a', { cause: undefined }), new Error('a', { cause: undefined })); + + assert.notDeepStrictEqual(new Error('a', { cause: 'undefined' }), new Error('a', { cause: undefined })); + assert.notDeepStrictEqual(new Error('a', { cause: undefined }), new Error('a')); + assert.notDeepStrictEqual(new Error('a'), new Error('a', { cause: undefined })); + + assert.throws(() => { + assert.deepStrictEqual(new Error('a'), new Error('a', { cause: undefined })); + }, { message: defaultStartMessage + + '+ [Error: a]\n' + + '- [Error: a] {\n' + + '- [cause]: undefined\n' + + '- }\n' }); + + assert.throws(() => { + assert.deepStrictEqual(new Error('a', { cause: undefined }), new Error('a')); + }, { message: defaultStartMessage + + '+ [Error: a] {\n' + + '+ [cause]: undefined\n' + + '+ }\n' + + '- [Error: a]\n' }); + assert.throws(() => { + assert.deepStrictEqual(new Error('a', { cause: undefined }), new Error('a', { cause: 'undefined' })); + }, { message: defaultStartMessage + ' [Error: a] {\n' + + '+ [cause]: undefined\n' + + '- [cause]: \'undefined\'\n' + + ' }\n' }); +});