| 'use strict'; |
| |
| // Confirm functionality of `util.isDeepStrictEqual()`. |
| |
| require('../common'); |
| |
| const assert = require('assert'); |
| const util = require('util'); |
| |
| class MyDate extends Date { |
| constructor(...args) { |
| super(...args); |
| this[0] = '1'; |
| } |
| } |
| |
| class MyRegExp extends RegExp { |
| constructor(...args) { |
| super(...args); |
| this[0] = '1'; |
| } |
| } |
| |
| { |
| const arr = new Uint8Array([120, 121, 122, 10]); |
| const buf = Buffer.from(arr); |
| // They have different [[Prototype]] |
| assert.strictEqual(util.isDeepStrictEqual(arr, buf), false); |
| |
| const buf2 = Buffer.from(arr); |
| buf2.prop = 1; |
| |
| assert.strictEqual(util.isDeepStrictEqual(buf2, buf), false); |
| |
| const arr2 = new Uint8Array([120, 121, 122, 10]); |
| arr2.prop = 5; |
| assert.strictEqual(util.isDeepStrictEqual(arr, arr2), false); |
| } |
| |
| { |
| const date = new Date('2016'); |
| |
| const date2 = new MyDate('2016'); |
| |
| // deepStrictEqual checks own properties |
| assert.strictEqual(util.isDeepStrictEqual(date, date2), false); |
| assert.strictEqual(util.isDeepStrictEqual(date2, date), false); |
| } |
| |
| { |
| const re1 = new RegExp('test'); |
| const re2 = new MyRegExp('test'); |
| |
| // deepStrictEqual checks all properties |
| assert.strictEqual(util.isDeepStrictEqual(re1, re2), false); |
| } |
| |
| { |
| // For these cases, deepStrictEqual should throw. |
| const similar = new Set([ |
| { 0: '1' }, // Object |
| { 0: 1 }, // Object |
| new String('1'), // Object |
| ['1'], // Array |
| [1], // Array |
| new MyDate('2016'), // Date with this[0] = '1' |
| new MyRegExp('test'), // RegExp with this[0] = '1' |
| new Int8Array([1]), // Int8Array |
| new Uint8Array([1]), // Uint8Array |
| new Int16Array([1]), // Int16Array |
| new Uint16Array([1]), // Uint16Array |
| new Int32Array([1]), // Int32Array |
| new Uint32Array([1]), // Uint32Array |
| Buffer.from([1]), // Buffer |
| ]); |
| |
| for (const a of similar) { |
| for (const b of similar) { |
| if (a !== b) { |
| assert.strictEqual(util.isDeepStrictEqual(a, b), false); |
| } |
| } |
| } |
| } |
| |
| function utilIsDeepStrict(a, b) { |
| assert.strictEqual(util.isDeepStrictEqual(a, b), true); |
| assert.strictEqual(util.isDeepStrictEqual(b, a), true); |
| } |
| |
| function notUtilIsDeepStrict(a, b) { |
| assert.strictEqual(util.isDeepStrictEqual(a, b), false); |
| assert.strictEqual(util.isDeepStrictEqual(b, a), false); |
| } |
| |
| // es6 Maps and Sets |
| utilIsDeepStrict(new Set(), new Set()); |
| utilIsDeepStrict(new Map(), new Map()); |
| |
| utilIsDeepStrict(new Set([1, 2, 3]), new Set([1, 2, 3])); |
| notUtilIsDeepStrict(new Set([1, 2, 3]), new Set([1, 2, 3, 4])); |
| notUtilIsDeepStrict(new Set([1, 2, 3, 4]), new Set([1, 2, 3])); |
| utilIsDeepStrict(new Set(['1', '2', '3']), new Set(['1', '2', '3'])); |
| utilIsDeepStrict(new Set([[1, 2], [3, 4]]), new Set([[3, 4], [1, 2]])); |
| |
| { |
| const a = [ 1, 2 ]; |
| const b = [ 3, 4 ]; |
| const c = [ 1, 2 ]; |
| const d = [ 3, 4 ]; |
| |
| utilIsDeepStrict( |
| { a: a, b: b, s: new Set([a, b]) }, |
| { a: c, b: d, s: new Set([d, c]) } |
| ); |
| } |
| |
| utilIsDeepStrict(new Map([[1, 1], [2, 2]]), new Map([[1, 1], [2, 2]])); |
| utilIsDeepStrict(new Map([[1, 1], [2, 2]]), new Map([[2, 2], [1, 1]])); |
| notUtilIsDeepStrict(new Map([[1, 1], [2, 2]]), new Map([[1, 2], [2, 1]])); |
| notUtilIsDeepStrict( |
| new Map([[[1], 1], [{}, 2]]), |
| new Map([[[1], 2], [{}, 1]]) |
| ); |
| |
| notUtilIsDeepStrict(new Set([1]), [1]); |
| notUtilIsDeepStrict(new Set(), []); |
| notUtilIsDeepStrict(new Set(), {}); |
| |
| notUtilIsDeepStrict(new Map([['a', 1]]), { a: 1 }); |
| notUtilIsDeepStrict(new Map(), []); |
| notUtilIsDeepStrict(new Map(), {}); |
| |
| notUtilIsDeepStrict(new Set(['1']), new Set([1])); |
| |
| notUtilIsDeepStrict(new Map([['1', 'a']]), new Map([[1, 'a']])); |
| notUtilIsDeepStrict(new Map([['a', '1']]), new Map([['a', 1]])); |
| notUtilIsDeepStrict(new Map([['a', '1']]), new Map([['a', 2]])); |
| |
| utilIsDeepStrict(new Set([{}]), new Set([{}])); |
| |
| // Ref: https://github.com/nodejs/node/issues/13347 |
| notUtilIsDeepStrict( |
| new Set([{ a: 1 }, { a: 1 }]), |
| new Set([{ a: 1 }, { a: 2 }]) |
| ); |
| notUtilIsDeepStrict( |
| new Set([{ a: 1 }, { a: 1 }, { a: 2 }]), |
| new Set([{ a: 1 }, { a: 2 }, { a: 2 }]) |
| ); |
| notUtilIsDeepStrict( |
| new Map([[{ x: 1 }, 5], [{ x: 1 }, 5]]), |
| new Map([[{ x: 1 }, 5], [{ x: 2 }, 5]]) |
| ); |
| |
| notUtilIsDeepStrict(new Set([3, '3']), new Set([3, 4])); |
| notUtilIsDeepStrict(new Map([[3, 0], ['3', 0]]), new Map([[3, 0], [4, 0]])); |
| |
| notUtilIsDeepStrict( |
| new Set([{ a: 1 }, { a: 1 }, { a: 2 }]), |
| new Set([{ a: 1 }, { a: 2 }, { a: 2 }]) |
| ); |
| |
| // Mixed primitive and object keys |
| utilIsDeepStrict( |
| new Map([[1, 'a'], [{}, 'a']]), |
| new Map([[1, 'a'], [{}, 'a']]) |
| ); |
| utilIsDeepStrict( |
| new Set([1, 'a', [{}, 'a']]), |
| new Set([1, 'a', [{}, 'a']]) |
| ); |
| |
| // This is an awful case, where a map contains multiple equivalent keys: |
| notUtilIsDeepStrict( |
| new Map([[1, 'a'], ['1', 'b']]), |
| new Map([['1', 'a'], [true, 'b']]) |
| ); |
| notUtilIsDeepStrict( |
| new Set(['a']), |
| new Set(['b']) |
| ); |
| utilIsDeepStrict( |
| new Map([[{}, 'a'], [{}, 'b']]), |
| new Map([[{}, 'b'], [{}, 'a']]) |
| ); |
| notUtilIsDeepStrict( |
| new Map([[true, 'a'], ['1', 'b'], [1, 'a']]), |
| new Map([['1', 'a'], [1, 'b'], [true, 'a']]) |
| ); |
| notUtilIsDeepStrict( |
| new Map([[true, 'a'], ['1', 'b'], [1, 'c']]), |
| new Map([['1', 'a'], [1, 'b'], [true, 'a']]) |
| ); |
| |
| // Similar object keys |
| notUtilIsDeepStrict( |
| new Set([{}, {}]), |
| new Set([{}, 1]) |
| ); |
| notUtilIsDeepStrict( |
| new Set([[{}, 1], [{}, 1]]), |
| new Set([[{}, 1], [1, 1]]) |
| ); |
| notUtilIsDeepStrict( |
| new Map([[{}, 1], [{}, 1]]), |
| new Map([[{}, 1], [1, 1]]) |
| ); |
| notUtilIsDeepStrict( |
| new Map([[{}, 1], [true, 1]]), |
| new Map([[{}, 1], [1, 1]]) |
| ); |
| |
| // Similar primitive key / values |
| notUtilIsDeepStrict( |
| new Set([1, true, false]), |
| new Set(['1', 0, '0']) |
| ); |
| notUtilIsDeepStrict( |
| new Map([[1, 5], [true, 5], [false, 5]]), |
| new Map([['1', 5], [0, 5], ['0', 5]]) |
| ); |
| |
| // Undefined value in Map |
| utilIsDeepStrict( |
| new Map([[1, undefined]]), |
| new Map([[1, undefined]]) |
| ); |
| notUtilIsDeepStrict( |
| new Map([[1, null]]), |
| new Map([['1', undefined]]) |
| ); |
| notUtilIsDeepStrict( |
| new Map([[1, undefined]]), |
| new Map([[2, undefined]]) |
| ); |
| |
| // null as key |
| utilIsDeepStrict( |
| new Map([[null, 3]]), |
| new Map([[null, 3]]) |
| ); |
| notUtilIsDeepStrict( |
| new Map([[null, undefined]]), |
| new Map([[undefined, null]]) |
| ); |
| notUtilIsDeepStrict( |
| new Set([null]), |
| new Set([undefined]) |
| ); |
| |
| // GH-6416. Make sure circular refs don't throw. |
| { |
| const b = {}; |
| b.b = b; |
| const c = {}; |
| c.b = c; |
| |
| utilIsDeepStrict(b, c); |
| |
| const d = {}; |
| d.a = 1; |
| d.b = d; |
| const e = {}; |
| e.a = 1; |
| e.b = {}; |
| |
| notUtilIsDeepStrict(d, e); |
| } |
| |
| // GH-14441. Circular structures should be consistent |
| { |
| const a = {}; |
| const b = {}; |
| a.a = a; |
| b.a = {}; |
| b.a.a = a; |
| utilIsDeepStrict(a, b); |
| } |
| |
| { |
| const a = new Set(); |
| const b = new Set(); |
| const c = new Set(); |
| a.add(a); |
| b.add(b); |
| c.add(a); |
| utilIsDeepStrict(b, c); |
| } |
| |
| // GH-7178. Ensure reflexivity of deepEqual with `arguments` objects. |
| { |
| const args = (function() { return arguments; })(); |
| notUtilIsDeepStrict([], args); |
| } |
| |
| // More checking that arguments objects are handled correctly |
| { |
| // eslint-disable-next-line func-style |
| const returnArguments = function() { return arguments; }; |
| |
| const someArgs = returnArguments('a'); |
| const sameArgs = returnArguments('a'); |
| const diffArgs = returnArguments('b'); |
| |
| notUtilIsDeepStrict(someArgs, ['a']); |
| notUtilIsDeepStrict(someArgs, { '0': 'a' }); |
| notUtilIsDeepStrict(someArgs, diffArgs); |
| utilIsDeepStrict(someArgs, sameArgs); |
| } |
| |
| { |
| const values = [ |
| 123, |
| Infinity, |
| 0, |
| null, |
| undefined, |
| false, |
| true, |
| {}, |
| [], |
| () => {}, |
| ]; |
| utilIsDeepStrict(new Set(values), new Set(values)); |
| utilIsDeepStrict(new Set(values), new Set(values.reverse())); |
| |
| const mapValues = values.map((v) => [v, { a: 5 }]); |
| utilIsDeepStrict(new Map(mapValues), new Map(mapValues)); |
| utilIsDeepStrict(new Map(mapValues), new Map(mapValues.reverse())); |
| } |
| |
| { |
| const s1 = new Set(); |
| const s2 = new Set(); |
| s1.add(1); |
| s1.add(2); |
| s2.add(2); |
| s2.add(1); |
| utilIsDeepStrict(s1, s2); |
| } |
| |
| { |
| const m1 = new Map(); |
| const m2 = new Map(); |
| const obj = { a: 5, b: 6 }; |
| m1.set(1, obj); |
| m1.set(2, 'hi'); |
| m1.set(3, [1, 2, 3]); |
| |
| m2.set(2, 'hi'); // different order |
| m2.set(1, obj); |
| m2.set(3, [1, 2, 3]); // Deep equal, but not reference equal. |
| |
| utilIsDeepStrict(m1, m2); |
| } |
| |
| { |
| const m1 = new Map(); |
| const m2 = new Map(); |
| |
| // m1 contains itself. |
| m1.set(1, m1); |
| m2.set(1, new Map()); |
| |
| notUtilIsDeepStrict(m1, m2); |
| } |
| |
| { |
| const map1 = new Map([[1, 1]]); |
| const map2 = new Map([[1, '1']]); |
| assert.strictEqual(util.isDeepStrictEqual(map1, map2), false); |
| } |
| |
| { |
| // Two equivalent sets / maps with different key/values applied shouldn't be |
| // the same. This is a terrible idea to do in practice, but deepEqual should |
| // still check for it. |
| const s1 = new Set(); |
| const s2 = new Set(); |
| s1.x = 5; |
| notUtilIsDeepStrict(s1, s2); |
| |
| const m1 = new Map(); |
| const m2 = new Map(); |
| m1.x = 5; |
| notUtilIsDeepStrict(m1, m2); |
| } |
| |
| { |
| // Circular references. |
| const s1 = new Set(); |
| s1.add(s1); |
| const s2 = new Set(); |
| s2.add(s2); |
| utilIsDeepStrict(s1, s2); |
| |
| const m1 = new Map(); |
| m1.set(2, m1); |
| const m2 = new Map(); |
| m2.set(2, m2); |
| utilIsDeepStrict(m1, m2); |
| |
| const m3 = new Map(); |
| m3.set(m3, 2); |
| const m4 = new Map(); |
| m4.set(m4, 2); |
| utilIsDeepStrict(m3, m4); |
| } |
| |
| // Handle sparse arrays |
| utilIsDeepStrict([1, , , 3], [1, , , 3]); |
| notUtilIsDeepStrict([1, , , 3], [1, , , 3, , , ]); |
| |
| // Handle different error messages |
| { |
| const err1 = new Error('foo1'); |
| const err2 = new Error('foo2'); |
| const err3 = new TypeError('foo1'); |
| notUtilIsDeepStrict(err1, err2, assert.AssertionError); |
| notUtilIsDeepStrict(err1, err3, assert.AssertionError); |
| notUtilIsDeepStrict(err1, {}, assert.AssertionError); |
| } |
| |
| // Handle NaN |
| assert.strictEqual(util.isDeepStrictEqual(NaN, NaN), true); |
| assert.strictEqual(util.isDeepStrictEqual({ a: NaN }, { a: NaN }), true); |
| assert.strictEqual( |
| util.isDeepStrictEqual([ 1, 2, NaN, 4 ], [ 1, 2, NaN, 4 ]), |
| true |
| ); |
| |
| // Handle boxed primitives |
| { |
| const boxedString = new String('test'); |
| const boxedSymbol = Object(Symbol()); |
| notUtilIsDeepStrict(new Boolean(true), Object(false)); |
| notUtilIsDeepStrict(Object(true), new Number(1)); |
| notUtilIsDeepStrict(new Number(2), new Number(1)); |
| notUtilIsDeepStrict(boxedSymbol, Object(Symbol())); |
| notUtilIsDeepStrict(boxedSymbol, {}); |
| utilIsDeepStrict(boxedSymbol, boxedSymbol); |
| utilIsDeepStrict(Object(true), Object(true)); |
| utilIsDeepStrict(Object(2), Object(2)); |
| utilIsDeepStrict(boxedString, Object('test')); |
| boxedString.slow = true; |
| notUtilIsDeepStrict(boxedString, Object('test')); |
| boxedSymbol.slow = true; |
| notUtilIsDeepStrict(boxedSymbol, {}); |
| utilIsDeepStrict(Object(BigInt(1)), Object(BigInt(1))); |
| notUtilIsDeepStrict(Object(BigInt(1)), Object(BigInt(2))); |
| |
| const booleanish = new Boolean(true); |
| Object.defineProperty(booleanish, Symbol.toStringTag, { value: 'String' }); |
| Object.setPrototypeOf(booleanish, String.prototype); |
| notUtilIsDeepStrict(booleanish, new String('true')); |
| |
| const numberish = new Number(42); |
| Object.defineProperty(numberish, Symbol.toStringTag, { value: 'String' }); |
| Object.setPrototypeOf(numberish, String.prototype); |
| notUtilIsDeepStrict(numberish, new String('42')); |
| |
| const stringish = new String('0'); |
| Object.defineProperty(stringish, Symbol.toStringTag, { value: 'Number' }); |
| Object.setPrototypeOf(stringish, Number.prototype); |
| notUtilIsDeepStrict(stringish, new Number(0)); |
| |
| const bigintish = new Object(BigInt(42)); |
| Object.defineProperty(bigintish, Symbol.toStringTag, { value: 'String' }); |
| Object.setPrototypeOf(bigintish, String.prototype); |
| notUtilIsDeepStrict(bigintish, new String('42')); |
| |
| const symbolish = new Object(Symbol('fhqwhgads')); |
| Object.defineProperty(symbolish, Symbol.toStringTag, { value: 'String' }); |
| Object.setPrototypeOf(symbolish, String.prototype); |
| notUtilIsDeepStrict(symbolish, new String('fhqwhgads')); |
| } |
| |
| // Minus zero |
| notUtilIsDeepStrict(0, -0); |
| utilIsDeepStrict(-0, -0); |
| |
| // Handle symbols (enumerable only) |
| { |
| const symbol1 = Symbol(); |
| const obj1 = { [symbol1]: 1 }; |
| const obj2 = { [symbol1]: 1 }; |
| const obj3 = { [Symbol()]: 1 }; |
| const obj4 = { }; |
| // Add a non enumerable symbol as well. It is going to be ignored! |
| Object.defineProperty(obj2, Symbol(), { value: 1 }); |
| Object.defineProperty(obj4, symbol1, { value: 1 }); |
| notUtilIsDeepStrict(obj1, obj3); |
| utilIsDeepStrict(obj1, obj2); |
| notUtilIsDeepStrict(obj1, obj4); |
| // TypedArrays have a fast path. Test for this as well. |
| const a = new Uint8Array(4); |
| const b = new Uint8Array(4); |
| a[symbol1] = true; |
| b[symbol1] = false; |
| notUtilIsDeepStrict(a, b); |
| b[symbol1] = true; |
| utilIsDeepStrict(a, b); |
| // The same as TypedArrays is valid for boxed primitives |
| const boxedStringA = new String('test'); |
| const boxedStringB = new String('test'); |
| boxedStringA[symbol1] = true; |
| notUtilIsDeepStrict(boxedStringA, boxedStringB); |
| boxedStringA[symbol1] = true; |
| utilIsDeepStrict(a, b); |
| } |