diff --git a/index.js b/index.js index 188dab6..0d9cf53 100644 --- a/index.js +++ b/index.js @@ -34,6 +34,11 @@ var $setDelete = callBound('Set.prototype.delete', true); var $setHas = callBound('Set.prototype.has', true); var $setSize = callBound('Set.prototype.size', true); +// the primitive that a boxed primitive wraps, read without calling a `valueOf` that the object may override +function unbox(boxed, kind) { + return callBound(kind + '.prototype.valueOf')(boxed); +} + // taken from https://github.com/browserify/commonjs-assert/blob/bba838e9ba9e28edf3127ce6974624208502f6bc/internal/util/comparisons.js#L401-L414 function setHasEqualElement(set, val1, opts, channel) { var i = getIterator(set); @@ -130,6 +135,8 @@ function internalDeepEqual(actual, expected, options, channel) { if (actualBoxed !== expectedBoxed) { return false; } + // two boxed primitives of the same kind are equal only if the primitives they wrap are the same + if (actualBoxed && !is(unbox(actual, actualBoxed), unbox(expected, actualBoxed))) { return false; } // 7.3. Other pairs that do not both pass typeof value == 'object', equivalence is determined by ==. if (!actual || !expected || (typeof actual !== 'object' && typeof expected !== 'object')) { diff --git a/test/cmp.js b/test/cmp.js index fe24d1c..5d20755 100644 --- a/test/cmp.js +++ b/test/cmp.js @@ -1042,15 +1042,31 @@ test('boxed primitives', function (t) { t.deepEqualTest(Object(''), '', 'boxed and primitive `""`', false, false); t.deepEqualTest(Object('str'), 'str', 'boxed and primitive `"str"`', false, false); + t.deepEqualTest(Object(3), Object(3), 'two boxed `3`s', true, true); + t.deepEqualTest(Object(3), Object(4), 'boxed `3` and boxed `4`', false, false); + t.deepEqualTest(Object(NaN), Object(NaN), 'two boxed `NaN`s', true, true); + t.deepEqualTest(Object(0), Object(-0), 'boxed `0` and boxed `-0`', false, false); + t.deepEqualTest(Object(true), Object(true), 'two boxed `true`s', true, true); + t.deepEqualTest(Object(true), Object(false), 'boxed `true` and boxed `false`', false, false); + t.deepEqualTest(Object('str'), Object('str'), 'two boxed `"str"`s', true, true); + t.deepEqualTest(Object('str'), Object('other'), 'boxed `"str"` and boxed `"other"`', false, false); + t.deepEqualTest([Object(3)], [Object(4)], 'boxed numbers that differ, in arrays', false, false); + t.deepEqualTest({ a: Object(3) }, { a: Object(4) }, 'boxed numbers that differ, in objects', false, false); + t.deepEqualTest({ a: Object(3) }, { a: Object(3) }, 'boxed numbers that match, in objects', true, true); + t.test('symbol', { skip: !hasSymbols }, function (st) { var s = Symbol(''); st.deepEqualTest(Object(s), s, 'boxed and primitive `Symbol()`', false, false); + st.deepEqualTest(Object(s), Object(s), 'two boxes of the same `Symbol()`', true, true); + st.deepEqualTest(Object(s), Object(Symbol('')), 'boxes of two different symbols', false, false); st.end(); }); t.test('bigint', { skip: typeof BigInt !== 'function' }, function (st) { var hhgtg = BigInt(42); st.deepEqualTest(Object(hhgtg), hhgtg, 'boxed and primitive `BigInt(42)`', false, false); + st.deepEqualTest(Object(hhgtg), Object(BigInt(42)), 'two boxed `BigInt(42)`s', true, true); + st.deepEqualTest(Object(hhgtg), Object(BigInt(43)), 'boxed `BigInt(42)` and boxed `BigInt(43)`', false, false); st.end(); });