Skip to content

Commit 227093b

Browse files
kylo5abymarco-ippolito
authored andcommittedJun 17, 2024
assert: add deep equal check for more Error type
PR-URL: #51805 Fixes: #51793 Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
1 parent 8b3e83b commit 227093b

File tree

3 files changed

+51
-7
lines changed

3 files changed

+51
-7
lines changed
 

‎doc/api/assert.md

+11-4
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,9 @@ An alias of [`assert.ok()`][].
555555
<!-- YAML
556556
added: v0.1.21
557557
changes:
558+
- version: REPLACEME
559+
pr-url: https://github.com/nodejs/node/pull/51805
560+
description: Error cause and errors properties are now compared as well.
558561
- version: v18.0.0
559562
pr-url: https://github.com/nodejs/node/pull/41020
560563
description: Regular expressions lastIndex property is now compared as well.
@@ -621,8 +624,8 @@ are also recursively evaluated by the following rules.
621624
both sides are `NaN`.
622625
* [Type tags][Object.prototype.toString()] of objects should be the same.
623626
* Only [enumerable "own" properties][] are considered.
624-
* [`Error`][] names and messages are always compared, even if these are not
625-
enumerable properties.
627+
* [`Error`][] names, messages, causes, and errors are always compared,
628+
even if these are not enumerable properties.
626629
* [Object wrappers][] are compared both as objects and unwrapped values.
627630
* `Object` properties are compared unordered.
628631
* [`Map`][] keys and [`Set`][] items are compared unordered.
@@ -736,6 +739,9 @@ parameter is an instance of an [`Error`][] then it will be thrown instead of the
736739
<!-- YAML
737740
added: v1.2.0
738741
changes:
742+
- version: REPLACEME
743+
pr-url: https://github.com/nodejs/node/pull/51805
744+
description: Error cause and errors properties are now compared as well.
739745
- version: v18.0.0
740746
pr-url: https://github.com/nodejs/node/pull/41020
741747
description: Regular expressions lastIndex property is now compared as well.
@@ -783,8 +789,9 @@ are recursively evaluated also by the following rules.
783789
* [`[[Prototype]]`][prototype-spec] of objects are compared using
784790
the [`===` operator][].
785791
* Only [enumerable "own" properties][] are considered.
786-
* [`Error`][] names and messages are always compared, even if these are not
787-
enumerable properties.
792+
* [`Error`][] names, messages, causes, and errors are always compared,
793+
even if these are not enumerable properties.
794+
`errors` is also compared.
788795
* Enumerable own [`Symbol`][] properties are compared as well.
789796
* [Object wrappers][] are compared both as objects and unwrapped values.
790797
* `Object` properties are compared unordered.

‎lib/internal/util/comparisons.js

+17-3
Original file line numberDiff line numberDiff line change
@@ -235,9 +235,23 @@ function innerDeepEqual(val1, val2, strict, memos) {
235235
} else if (isNativeError(val1) || val1 instanceof Error) {
236236
// Do not compare the stack as it might differ even though the error itself
237237
// is otherwise identical.
238-
if ((!isNativeError(val2) && !(val2 instanceof Error)) ||
239-
val1.message !== val2.message ||
240-
val1.name !== val2.name) {
238+
if (!isNativeError(val2) && !(val2 instanceof Error)) {
239+
return false;
240+
}
241+
242+
const message1Enumerable = ObjectPrototypePropertyIsEnumerable(val1, 'message');
243+
const name1Enumerable = ObjectPrototypePropertyIsEnumerable(val1, 'name');
244+
const cause1Enumerable = ObjectPrototypePropertyIsEnumerable(val1, 'cause');
245+
const errors1Enumerable = ObjectPrototypePropertyIsEnumerable(val1, 'errors');
246+
247+
if ((message1Enumerable !== ObjectPrototypePropertyIsEnumerable(val2, 'message') ||
248+
(!message1Enumerable && val1.message !== val2.message)) ||
249+
(name1Enumerable !== ObjectPrototypePropertyIsEnumerable(val2, 'name') ||
250+
(!name1Enumerable && val1.name !== val2.name)) ||
251+
(cause1Enumerable !== ObjectPrototypePropertyIsEnumerable(val2, 'cause') ||
252+
(!cause1Enumerable && !innerDeepEqual(val1.cause, val2.cause, strict, memos))) ||
253+
(errors1Enumerable !== ObjectPrototypePropertyIsEnumerable(val2, 'errors') ||
254+
(!errors1Enumerable && !innerDeepEqual(val1.errors, val2.errors, strict, memos)))) {
241255
return false;
242256
}
243257
} else if (isBoxedPrimitive(val1)) {

‎test/parallel/test-assert-deep.js

+23
Original file line numberDiff line numberDiff line change
@@ -1111,6 +1111,29 @@ assert.throws(
11111111
assert.notDeepStrictEqual(err, err2);
11121112
}
11131113

1114+
// Check for Errors with cause property
1115+
{
1116+
const e1 = new Error('err', { cause: new Error('cause e1') });
1117+
const e2 = new Error('err', { cause: new Error('cause e2') });
1118+
assertNotDeepOrStrict(e1, e2, AssertionError);
1119+
assertNotDeepOrStrict(e1, new Error('err'), AssertionError);
1120+
assertDeepAndStrictEqual(e1, new Error('err', { cause: new Error('cause e1') }));
1121+
}
1122+
1123+
// Check for AggregateError
1124+
{
1125+
const e1 = new Error('e1');
1126+
const e1duplicate = new Error('e1');
1127+
const e2 = new Error('e2');
1128+
1129+
const e3 = new AggregateError([e1duplicate, e2], 'Aggregate Error');
1130+
const e3duplicate = new AggregateError([e1, e2], 'Aggregate Error');
1131+
const e4 = new AggregateError([e1], 'Aggregate Error');
1132+
assertNotDeepOrStrict(e1, e3, AssertionError);
1133+
assertNotDeepOrStrict(e3, e4, AssertionError);
1134+
assertDeepAndStrictEqual(e3, e3duplicate);
1135+
}
1136+
11141137
// Verify that `valueOf` is not called for boxed primitives.
11151138
{
11161139
const a = new Number(5);

0 commit comments

Comments
 (0)
Please sign in to comment.