Skip to content

Commit f6c65e6

Browse files
BridgeARjasnell
authored andcommitted
assert: fix boxed primitives in deepStrictEqual
Unbox all primitives and compare them as well instead of only comparing boxed strings. PR-URL: #15050 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Joyee Cheung <[email protected]>
1 parent 59f1836 commit f6c65e6

File tree

4 files changed

+45
-4
lines changed

4 files changed

+45
-4
lines changed

doc/api/assert.md

+8-1
Original file line numberDiff line numberDiff line change
@@ -124,14 +124,15 @@ changes:
124124
* `expected` {any}
125125
* `message` {any}
126126

127-
Generally identical to `assert.deepEqual()` with three exceptions:
127+
Generally identical to `assert.deepEqual()` with a few exceptions:
128128

129129
1. Primitive values are compared using the [Strict Equality Comparison][]
130130
( `===` ). Set values and Map keys are compared using the [SameValueZero][]
131131
comparison. (Which means they are free of the [caveats][]).
132132
2. [`[[Prototype]]`][prototype-spec] of objects are compared using
133133
the [Strict Equality Comparison][] too.
134134
3. [Type tags][Object.prototype.toString()] of objects should be the same.
135+
4. [Object wrappers][] are compared both as objects and unwrapped values.
135136

136137
```js
137138
const assert = require('assert');
@@ -161,6 +162,11 @@ assert.deepEqual(date, fakeDate);
161162
assert.deepStrictEqual(date, fakeDate);
162163
// AssertionError: 2017-03-11T14:25:31.849Z deepStrictEqual Date {}
163164
// Different type tags
165+
166+
assert.deepStrictEqual(new Number(1), new Number(2));
167+
// Fails because the wrapped number is unwrapped and compared as well.
168+
assert.deepStrictEqual(new String('foo'), Object('foo'));
169+
// OK because the object and the string are identical when unwrapped.
164170
```
165171

166172
If the values are not equal, an `AssertionError` is thrown with a `message`
@@ -641,3 +647,4 @@ For more information, see
641647
[enumerable "own" properties]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Enumerability_and_ownership_of_properties
642648
[mdn-equality-guide]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness
643649
[prototype-spec]: https://tc39.github.io/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots
650+
[Object wrappers]: https://developer.mozilla.org/en-US/docs/Glossary/Primitive#Primitive_wrapper_objects_in_JavaScript

lib/assert.js

+18-1
Original file line numberDiff line numberDiff line change
@@ -202,13 +202,30 @@ function strictDeepEqual(actual, expected) {
202202
if (!areSimilarTypedArrays(actual, expected)) {
203203
return false;
204204
}
205-
206205
// Buffer.compare returns true, so actual.length === expected.length
207206
// if they both only contain numeric keys, we don't need to exam further
208207
if (Object.keys(actual).length === actual.length &&
209208
Object.keys(expected).length === expected.length) {
210209
return true;
211210
}
211+
} else if (typeof actual.valueOf === 'function') {
212+
const actualValue = actual.valueOf();
213+
// Note: Boxed string keys are going to be compared again by Object.keys
214+
if (actualValue !== actual) {
215+
if (!innerDeepEqual(actualValue, expected.valueOf(), true))
216+
return false;
217+
// Fast path for boxed primitives
218+
var lengthActual = 0;
219+
var lengthExpected = 0;
220+
if (typeof actualValue === 'string') {
221+
lengthActual = actual.length;
222+
lengthExpected = expected.length;
223+
}
224+
if (Object.keys(actual).length === lengthActual &&
225+
Object.keys(expected).length === lengthExpected) {
226+
return true;
227+
}
228+
}
212229
}
213230
}
214231

test/addons-napi/test_conversions/test.js

-2
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,6 @@ assert.strictEqual(0, test.toNumber([]));
103103
assert.strictEqual(0, test.toNumber(false));
104104
assert.strictEqual(0, test.toNumber(null));
105105
assert.strictEqual(0, test.toNumber(''));
106-
assert.ok(Number.isNaN(test.toNumber(Number.NaN)));
107106
assert.ok(Number.isNaN(test.toNumber({})));
108107
assert.ok(Number.isNaN(test.toNumber(undefined)));
109108
assert.throws(() => test.toNumber(testSym), TypeError);
@@ -116,7 +115,6 @@ assert.deepStrictEqual(new Boolean(false), test.toObject(false));
116115
assert.deepStrictEqual(new Boolean(true), test.toObject(true));
117116
assert.deepStrictEqual(new String(''), test.toObject(''));
118117
assert.deepStrictEqual(new Number(0), test.toObject(0));
119-
assert.deepStrictEqual(new Number(Number.NaN), test.toObject(Number.NaN));
120118
assert.deepStrictEqual(new Object(testSym), test.toObject(testSym));
121119
assert.notDeepStrictEqual(false, test.toObject(false));
122120
assert.notDeepStrictEqual(true, test.toObject(true));

test/parallel/test-assert-deep.js

+19
Original file line numberDiff line numberDiff line change
@@ -439,4 +439,23 @@ assertOnlyDeepEqual([1, , , 3], [1, , , 3, , , ]);
439439
assertOnlyDeepEqual(err1, {}, assert.AssertionError);
440440
}
441441

442+
// Handle boxed primitives
443+
{
444+
const boxedString = new String('test');
445+
const boxedSymbol = Object(Symbol());
446+
assertOnlyDeepEqual(new Boolean(true), Object(false));
447+
assertOnlyDeepEqual(Object(true), new Number(1));
448+
assertOnlyDeepEqual(new Number(2), new Number(1));
449+
assertOnlyDeepEqual(boxedSymbol, Object(Symbol()));
450+
assertOnlyDeepEqual(boxedSymbol, {});
451+
assertDeepAndStrictEqual(boxedSymbol, boxedSymbol);
452+
assertDeepAndStrictEqual(Object(true), Object(true));
453+
assertDeepAndStrictEqual(Object(2), Object(2));
454+
assertDeepAndStrictEqual(boxedString, Object('test'));
455+
boxedString.slow = true;
456+
assertNotDeepOrStrict(boxedString, Object('test'));
457+
boxedSymbol.slow = true;
458+
assertNotDeepOrStrict(boxedSymbol, {});
459+
}
460+
442461
/* eslint-enable */

0 commit comments

Comments
 (0)