diff --git a/doc/api/assert.md b/doc/api/assert.md index b1175e5e489e5d..5f331fdef35604 100644 --- a/doc/api/assert.md +++ b/doc/api/assert.md @@ -124,7 +124,7 @@ changes: * `expected` {any} * `message` {any} -Generally identical to `assert.deepEqual()` with three exceptions: +Generally identical to `assert.deepEqual()` with a few exceptions: 1. Primitive values are compared using the [Strict Equality Comparison][] ( `===` ). Set values and Map keys are compared using the [SameValueZero][] @@ -132,6 +132,7 @@ Generally identical to `assert.deepEqual()` with three exceptions: 2. [`[[Prototype]]`][prototype-spec] of objects are compared using the [Strict Equality Comparison][] too. 3. [Type tags][Object.prototype.toString()] of objects should be the same. +4. [Object wrappers][] are compared both as objects and unwrapped values. ```js const assert = require('assert'); @@ -161,6 +162,11 @@ assert.deepEqual(date, fakeDate); assert.deepStrictEqual(date, fakeDate); // AssertionError: 2017-03-11T14:25:31.849Z deepStrictEqual Date {} // Different type tags + +assert.deepStrictEqual(new Number(1), new Number(2)); +// Fails because the wrapped number is unwrapped and compared as well. +assert.deepStrictEqual(new String('foo'), Object('foo')); +// OK because the object and the string are identical when unwrapped. ``` If the values are not equal, an `AssertionError` is thrown with a `message` @@ -641,3 +647,4 @@ For more information, see [enumerable "own" properties]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Enumerability_and_ownership_of_properties [mdn-equality-guide]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness [prototype-spec]: https://tc39.github.io/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots +[Object wrappers]: https://developer.mozilla.org/en-US/docs/Glossary/Primitive#Primitive_wrapper_objects_in_JavaScript diff --git a/lib/assert.js b/lib/assert.js index 73c4a1c444e2df..21c93e96b2a657 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -202,13 +202,30 @@ function strictDeepEqual(actual, expected) { if (!areSimilarTypedArrays(actual, expected)) { return false; } - // Buffer.compare returns true, so actual.length === expected.length // if they both only contain numeric keys, we don't need to exam further if (Object.keys(actual).length === actual.length && Object.keys(expected).length === expected.length) { return true; } + } else if (typeof actual.valueOf === 'function') { + const actualValue = actual.valueOf(); + // Note: Boxed string keys are going to be compared again by Object.keys + if (actualValue !== actual) { + if (!innerDeepEqual(actualValue, expected.valueOf(), true)) + return false; + // Fast path for boxed primitives + var lengthActual = 0; + var lengthExpected = 0; + if (typeof actualValue === 'string') { + lengthActual = actual.length; + lengthExpected = expected.length; + } + if (Object.keys(actual).length === lengthActual && + Object.keys(expected).length === lengthExpected) { + return true; + } + } } } diff --git a/test/addons-napi/test_conversions/test.js b/test/addons-napi/test_conversions/test.js index 73d2c3314f600b..aebf13ba0217ae 100644 --- a/test/addons-napi/test_conversions/test.js +++ b/test/addons-napi/test_conversions/test.js @@ -103,7 +103,6 @@ assert.strictEqual(0, test.toNumber([])); assert.strictEqual(0, test.toNumber(false)); assert.strictEqual(0, test.toNumber(null)); assert.strictEqual(0, test.toNumber('')); -assert.ok(Number.isNaN(test.toNumber(Number.NaN))); assert.ok(Number.isNaN(test.toNumber({}))); assert.ok(Number.isNaN(test.toNumber(undefined))); assert.throws(() => test.toNumber(testSym), TypeError); @@ -116,7 +115,6 @@ assert.deepStrictEqual(new Boolean(false), test.toObject(false)); assert.deepStrictEqual(new Boolean(true), test.toObject(true)); assert.deepStrictEqual(new String(''), test.toObject('')); assert.deepStrictEqual(new Number(0), test.toObject(0)); -assert.deepStrictEqual(new Number(Number.NaN), test.toObject(Number.NaN)); assert.deepStrictEqual(new Object(testSym), test.toObject(testSym)); assert.notDeepStrictEqual(false, test.toObject(false)); assert.notDeepStrictEqual(true, test.toObject(true)); diff --git a/test/parallel/test-assert-deep.js b/test/parallel/test-assert-deep.js index d075f91b5954d0..de6cafdaf86fba 100644 --- a/test/parallel/test-assert-deep.js +++ b/test/parallel/test-assert-deep.js @@ -439,4 +439,23 @@ assertOnlyDeepEqual([1, , , 3], [1, , , 3, , , ]); assertOnlyDeepEqual(err1, {}, assert.AssertionError); } +// Handle boxed primitives +{ + const boxedString = new String('test'); + const boxedSymbol = Object(Symbol()); + assertOnlyDeepEqual(new Boolean(true), Object(false)); + assertOnlyDeepEqual(Object(true), new Number(1)); + assertOnlyDeepEqual(new Number(2), new Number(1)); + assertOnlyDeepEqual(boxedSymbol, Object(Symbol())); + assertOnlyDeepEqual(boxedSymbol, {}); + assertDeepAndStrictEqual(boxedSymbol, boxedSymbol); + assertDeepAndStrictEqual(Object(true), Object(true)); + assertDeepAndStrictEqual(Object(2), Object(2)); + assertDeepAndStrictEqual(boxedString, Object('test')); + boxedString.slow = true; + assertNotDeepOrStrict(boxedString, Object('test')); + boxedSymbol.slow = true; + assertNotDeepOrStrict(boxedSymbol, {}); +} + /* eslint-enable */