1'use strict'; 2 3// Confirm functionality of `util.isDeepStrictEqual()`. 4 5require('../common'); 6 7const assert = require('assert'); 8const util = require('util'); 9 10class MyDate extends Date { 11 constructor(...args) { 12 super(...args); 13 this[0] = '1'; 14 } 15} 16 17class MyRegExp extends RegExp { 18 constructor(...args) { 19 super(...args); 20 this[0] = '1'; 21 } 22} 23 24{ 25 const arr = new Uint8Array([120, 121, 122, 10]); 26 const buf = Buffer.from(arr); 27 // They have different [[Prototype]] 28 assert.strictEqual(util.isDeepStrictEqual(arr, buf), false); 29 30 const buf2 = Buffer.from(arr); 31 buf2.prop = 1; 32 33 assert.strictEqual(util.isDeepStrictEqual(buf2, buf), false); 34 35 const arr2 = new Uint8Array([120, 121, 122, 10]); 36 arr2.prop = 5; 37 assert.strictEqual(util.isDeepStrictEqual(arr, arr2), false); 38} 39 40{ 41 const date = new Date('2016'); 42 43 const date2 = new MyDate('2016'); 44 45 // deepStrictEqual checks own properties 46 assert.strictEqual(util.isDeepStrictEqual(date, date2), false); 47 assert.strictEqual(util.isDeepStrictEqual(date2, date), false); 48} 49 50{ 51 const re1 = new RegExp('test'); 52 const re2 = new MyRegExp('test'); 53 54 // deepStrictEqual checks all properties 55 assert.strictEqual(util.isDeepStrictEqual(re1, re2), false); 56} 57 58{ 59 // For these cases, deepStrictEqual should throw. 60 const similar = new Set([ 61 { 0: '1' }, // Object 62 { 0: 1 }, // Object 63 new String('1'), // Object 64 ['1'], // Array 65 [1], // Array 66 new MyDate('2016'), // Date with this[0] = '1' 67 new MyRegExp('test'), // RegExp with this[0] = '1' 68 new Int8Array([1]), // Int8Array 69 new Uint8Array([1]), // Uint8Array 70 new Int16Array([1]), // Int16Array 71 new Uint16Array([1]), // Uint16Array 72 new Int32Array([1]), // Int32Array 73 new Uint32Array([1]), // Uint32Array 74 Buffer.from([1]), // Buffer 75 ]); 76 77 for (const a of similar) { 78 for (const b of similar) { 79 if (a !== b) { 80 assert.strictEqual(util.isDeepStrictEqual(a, b), false); 81 } 82 } 83 } 84} 85 86function utilIsDeepStrict(a, b) { 87 assert.strictEqual(util.isDeepStrictEqual(a, b), true); 88 assert.strictEqual(util.isDeepStrictEqual(b, a), true); 89} 90 91function notUtilIsDeepStrict(a, b) { 92 assert.strictEqual(util.isDeepStrictEqual(a, b), false); 93 assert.strictEqual(util.isDeepStrictEqual(b, a), false); 94} 95 96// es6 Maps and Sets 97utilIsDeepStrict(new Set(), new Set()); 98utilIsDeepStrict(new Map(), new Map()); 99 100utilIsDeepStrict(new Set([1, 2, 3]), new Set([1, 2, 3])); 101notUtilIsDeepStrict(new Set([1, 2, 3]), new Set([1, 2, 3, 4])); 102notUtilIsDeepStrict(new Set([1, 2, 3, 4]), new Set([1, 2, 3])); 103utilIsDeepStrict(new Set(['1', '2', '3']), new Set(['1', '2', '3'])); 104utilIsDeepStrict(new Set([[1, 2], [3, 4]]), new Set([[3, 4], [1, 2]])); 105 106{ 107 const a = [ 1, 2 ]; 108 const b = [ 3, 4 ]; 109 const c = [ 1, 2 ]; 110 const d = [ 3, 4 ]; 111 112 utilIsDeepStrict( 113 { a: a, b: b, s: new Set([a, b]) }, 114 { a: c, b: d, s: new Set([d, c]) } 115 ); 116} 117 118utilIsDeepStrict(new Map([[1, 1], [2, 2]]), new Map([[1, 1], [2, 2]])); 119utilIsDeepStrict(new Map([[1, 1], [2, 2]]), new Map([[2, 2], [1, 1]])); 120notUtilIsDeepStrict(new Map([[1, 1], [2, 2]]), new Map([[1, 2], [2, 1]])); 121notUtilIsDeepStrict( 122 new Map([[[1], 1], [{}, 2]]), 123 new Map([[[1], 2], [{}, 1]]) 124); 125 126notUtilIsDeepStrict(new Set([1]), [1]); 127notUtilIsDeepStrict(new Set(), []); 128notUtilIsDeepStrict(new Set(), {}); 129 130notUtilIsDeepStrict(new Map([['a', 1]]), { a: 1 }); 131notUtilIsDeepStrict(new Map(), []); 132notUtilIsDeepStrict(new Map(), {}); 133 134notUtilIsDeepStrict(new Set(['1']), new Set([1])); 135 136notUtilIsDeepStrict(new Map([['1', 'a']]), new Map([[1, 'a']])); 137notUtilIsDeepStrict(new Map([['a', '1']]), new Map([['a', 1]])); 138notUtilIsDeepStrict(new Map([['a', '1']]), new Map([['a', 2]])); 139 140utilIsDeepStrict(new Set([{}]), new Set([{}])); 141 142// Ref: https://github.com/nodejs/node/issues/13347 143notUtilIsDeepStrict( 144 new Set([{ a: 1 }, { a: 1 }]), 145 new Set([{ a: 1 }, { a: 2 }]) 146); 147notUtilIsDeepStrict( 148 new Set([{ a: 1 }, { a: 1 }, { a: 2 }]), 149 new Set([{ a: 1 }, { a: 2 }, { a: 2 }]) 150); 151notUtilIsDeepStrict( 152 new Map([[{ x: 1 }, 5], [{ x: 1 }, 5]]), 153 new Map([[{ x: 1 }, 5], [{ x: 2 }, 5]]) 154); 155 156notUtilIsDeepStrict(new Set([3, '3']), new Set([3, 4])); 157notUtilIsDeepStrict(new Map([[3, 0], ['3', 0]]), new Map([[3, 0], [4, 0]])); 158 159notUtilIsDeepStrict( 160 new Set([{ a: 1 }, { a: 1 }, { a: 2 }]), 161 new Set([{ a: 1 }, { a: 2 }, { a: 2 }]) 162); 163 164// Mixed primitive and object keys 165utilIsDeepStrict( 166 new Map([[1, 'a'], [{}, 'a']]), 167 new Map([[1, 'a'], [{}, 'a']]) 168); 169utilIsDeepStrict( 170 new Set([1, 'a', [{}, 'a']]), 171 new Set([1, 'a', [{}, 'a']]) 172); 173 174// This is an awful case, where a map contains multiple equivalent keys: 175notUtilIsDeepStrict( 176 new Map([[1, 'a'], ['1', 'b']]), 177 new Map([['1', 'a'], [true, 'b']]) 178); 179notUtilIsDeepStrict( 180 new Set(['a']), 181 new Set(['b']) 182); 183utilIsDeepStrict( 184 new Map([[{}, 'a'], [{}, 'b']]), 185 new Map([[{}, 'b'], [{}, 'a']]) 186); 187notUtilIsDeepStrict( 188 new Map([[true, 'a'], ['1', 'b'], [1, 'a']]), 189 new Map([['1', 'a'], [1, 'b'], [true, 'a']]) 190); 191notUtilIsDeepStrict( 192 new Map([[true, 'a'], ['1', 'b'], [1, 'c']]), 193 new Map([['1', 'a'], [1, 'b'], [true, 'a']]) 194); 195 196// Similar object keys 197notUtilIsDeepStrict( 198 new Set([{}, {}]), 199 new Set([{}, 1]) 200); 201notUtilIsDeepStrict( 202 new Set([[{}, 1], [{}, 1]]), 203 new Set([[{}, 1], [1, 1]]) 204); 205notUtilIsDeepStrict( 206 new Map([[{}, 1], [{}, 1]]), 207 new Map([[{}, 1], [1, 1]]) 208); 209notUtilIsDeepStrict( 210 new Map([[{}, 1], [true, 1]]), 211 new Map([[{}, 1], [1, 1]]) 212); 213 214// Similar primitive key / values 215notUtilIsDeepStrict( 216 new Set([1, true, false]), 217 new Set(['1', 0, '0']) 218); 219notUtilIsDeepStrict( 220 new Map([[1, 5], [true, 5], [false, 5]]), 221 new Map([['1', 5], [0, 5], ['0', 5]]) 222); 223 224// Undefined value in Map 225utilIsDeepStrict( 226 new Map([[1, undefined]]), 227 new Map([[1, undefined]]) 228); 229notUtilIsDeepStrict( 230 new Map([[1, null]]), 231 new Map([['1', undefined]]) 232); 233notUtilIsDeepStrict( 234 new Map([[1, undefined]]), 235 new Map([[2, undefined]]) 236); 237 238// null as key 239utilIsDeepStrict( 240 new Map([[null, 3]]), 241 new Map([[null, 3]]) 242); 243notUtilIsDeepStrict( 244 new Map([[null, undefined]]), 245 new Map([[undefined, null]]) 246); 247notUtilIsDeepStrict( 248 new Set([null]), 249 new Set([undefined]) 250); 251 252// GH-6416. Make sure circular refs don't throw. 253{ 254 const b = {}; 255 b.b = b; 256 const c = {}; 257 c.b = c; 258 259 utilIsDeepStrict(b, c); 260 261 const d = {}; 262 d.a = 1; 263 d.b = d; 264 const e = {}; 265 e.a = 1; 266 e.b = {}; 267 268 notUtilIsDeepStrict(d, e); 269} 270 271// GH-14441. Circular structures should be consistent 272{ 273 const a = {}; 274 const b = {}; 275 a.a = a; 276 b.a = {}; 277 b.a.a = a; 278 utilIsDeepStrict(a, b); 279} 280 281{ 282 const a = new Set(); 283 const b = new Set(); 284 const c = new Set(); 285 a.add(a); 286 b.add(b); 287 c.add(a); 288 utilIsDeepStrict(b, c); 289} 290 291// GH-7178. Ensure reflexivity of deepEqual with `arguments` objects. 292{ 293 const args = (function() { return arguments; })(); 294 notUtilIsDeepStrict([], args); 295} 296 297// More checking that arguments objects are handled correctly 298{ 299 // eslint-disable-next-line func-style 300 const returnArguments = function() { return arguments; }; 301 302 const someArgs = returnArguments('a'); 303 const sameArgs = returnArguments('a'); 304 const diffArgs = returnArguments('b'); 305 306 notUtilIsDeepStrict(someArgs, ['a']); 307 notUtilIsDeepStrict(someArgs, { '0': 'a' }); 308 notUtilIsDeepStrict(someArgs, diffArgs); 309 utilIsDeepStrict(someArgs, sameArgs); 310} 311 312{ 313 const values = [ 314 123, 315 Infinity, 316 0, 317 null, 318 undefined, 319 false, 320 true, 321 {}, 322 [], 323 () => {}, 324 ]; 325 utilIsDeepStrict(new Set(values), new Set(values)); 326 utilIsDeepStrict(new Set(values), new Set(values.reverse())); 327 328 const mapValues = values.map((v) => [v, { a: 5 }]); 329 utilIsDeepStrict(new Map(mapValues), new Map(mapValues)); 330 utilIsDeepStrict(new Map(mapValues), new Map(mapValues.reverse())); 331} 332 333{ 334 const s1 = new Set(); 335 const s2 = new Set(); 336 s1.add(1); 337 s1.add(2); 338 s2.add(2); 339 s2.add(1); 340 utilIsDeepStrict(s1, s2); 341} 342 343{ 344 const m1 = new Map(); 345 const m2 = new Map(); 346 const obj = { a: 5, b: 6 }; 347 m1.set(1, obj); 348 m1.set(2, 'hi'); 349 m1.set(3, [1, 2, 3]); 350 351 m2.set(2, 'hi'); // different order 352 m2.set(1, obj); 353 m2.set(3, [1, 2, 3]); // Deep equal, but not reference equal. 354 355 utilIsDeepStrict(m1, m2); 356} 357 358{ 359 const m1 = new Map(); 360 const m2 = new Map(); 361 362 // m1 contains itself. 363 m1.set(1, m1); 364 m2.set(1, new Map()); 365 366 notUtilIsDeepStrict(m1, m2); 367} 368 369{ 370 const map1 = new Map([[1, 1]]); 371 const map2 = new Map([[1, '1']]); 372 assert.strictEqual(util.isDeepStrictEqual(map1, map2), false); 373} 374 375{ 376 // Two equivalent sets / maps with different key/values applied shouldn't be 377 // the same. This is a terrible idea to do in practice, but deepEqual should 378 // still check for it. 379 const s1 = new Set(); 380 const s2 = new Set(); 381 s1.x = 5; 382 notUtilIsDeepStrict(s1, s2); 383 384 const m1 = new Map(); 385 const m2 = new Map(); 386 m1.x = 5; 387 notUtilIsDeepStrict(m1, m2); 388} 389 390{ 391 // Circular references. 392 const s1 = new Set(); 393 s1.add(s1); 394 const s2 = new Set(); 395 s2.add(s2); 396 utilIsDeepStrict(s1, s2); 397 398 const m1 = new Map(); 399 m1.set(2, m1); 400 const m2 = new Map(); 401 m2.set(2, m2); 402 utilIsDeepStrict(m1, m2); 403 404 const m3 = new Map(); 405 m3.set(m3, 2); 406 const m4 = new Map(); 407 m4.set(m4, 2); 408 utilIsDeepStrict(m3, m4); 409} 410 411// Handle sparse arrays 412utilIsDeepStrict([1, , , 3], [1, , , 3]); 413notUtilIsDeepStrict([1, , , 3], [1, , , 3, , , ]); 414 415// Handle different error messages 416{ 417 const err1 = new Error('foo1'); 418 const err2 = new Error('foo2'); 419 const err3 = new TypeError('foo1'); 420 notUtilIsDeepStrict(err1, err2, assert.AssertionError); 421 notUtilIsDeepStrict(err1, err3, assert.AssertionError); 422 notUtilIsDeepStrict(err1, {}, assert.AssertionError); 423} 424 425// Handle NaN 426assert.strictEqual(util.isDeepStrictEqual(NaN, NaN), true); 427assert.strictEqual(util.isDeepStrictEqual({ a: NaN }, { a: NaN }), true); 428assert.strictEqual( 429 util.isDeepStrictEqual([ 1, 2, NaN, 4 ], [ 1, 2, NaN, 4 ]), 430 true 431); 432 433// Handle boxed primitives 434{ 435 const boxedString = new String('test'); 436 const boxedSymbol = Object(Symbol()); 437 notUtilIsDeepStrict(new Boolean(true), Object(false)); 438 notUtilIsDeepStrict(Object(true), new Number(1)); 439 notUtilIsDeepStrict(new Number(2), new Number(1)); 440 notUtilIsDeepStrict(boxedSymbol, Object(Symbol())); 441 notUtilIsDeepStrict(boxedSymbol, {}); 442 utilIsDeepStrict(boxedSymbol, boxedSymbol); 443 utilIsDeepStrict(Object(true), Object(true)); 444 utilIsDeepStrict(Object(2), Object(2)); 445 utilIsDeepStrict(boxedString, Object('test')); 446 boxedString.slow = true; 447 notUtilIsDeepStrict(boxedString, Object('test')); 448 boxedSymbol.slow = true; 449 notUtilIsDeepStrict(boxedSymbol, {}); 450 utilIsDeepStrict(Object(BigInt(1)), Object(BigInt(1))); 451 notUtilIsDeepStrict(Object(BigInt(1)), Object(BigInt(2))); 452 453 const booleanish = new Boolean(true); 454 Object.defineProperty(booleanish, Symbol.toStringTag, { value: 'String' }); 455 Object.setPrototypeOf(booleanish, String.prototype); 456 notUtilIsDeepStrict(booleanish, new String('true')); 457 458 const numberish = new Number(42); 459 Object.defineProperty(numberish, Symbol.toStringTag, { value: 'String' }); 460 Object.setPrototypeOf(numberish, String.prototype); 461 notUtilIsDeepStrict(numberish, new String('42')); 462 463 const stringish = new String('0'); 464 Object.defineProperty(stringish, Symbol.toStringTag, { value: 'Number' }); 465 Object.setPrototypeOf(stringish, Number.prototype); 466 notUtilIsDeepStrict(stringish, new Number(0)); 467 468 const bigintish = new Object(BigInt(42)); 469 Object.defineProperty(bigintish, Symbol.toStringTag, { value: 'String' }); 470 Object.setPrototypeOf(bigintish, String.prototype); 471 notUtilIsDeepStrict(bigintish, new String('42')); 472 473 const symbolish = new Object(Symbol('fhqwhgads')); 474 Object.defineProperty(symbolish, Symbol.toStringTag, { value: 'String' }); 475 Object.setPrototypeOf(symbolish, String.prototype); 476 notUtilIsDeepStrict(symbolish, new String('fhqwhgads')); 477} 478 479// Minus zero 480notUtilIsDeepStrict(0, -0); 481utilIsDeepStrict(-0, -0); 482 483// Handle symbols (enumerable only) 484{ 485 const symbol1 = Symbol(); 486 const obj1 = { [symbol1]: 1 }; 487 const obj2 = { [symbol1]: 1 }; 488 const obj3 = { [Symbol()]: 1 }; 489 const obj4 = { }; 490 // Add a non enumerable symbol as well. It is going to be ignored! 491 Object.defineProperty(obj2, Symbol(), { value: 1 }); 492 Object.defineProperty(obj4, symbol1, { value: 1 }); 493 notUtilIsDeepStrict(obj1, obj3); 494 utilIsDeepStrict(obj1, obj2); 495 notUtilIsDeepStrict(obj1, obj4); 496 // TypedArrays have a fast path. Test for this as well. 497 const a = new Uint8Array(4); 498 const b = new Uint8Array(4); 499 a[symbol1] = true; 500 b[symbol1] = false; 501 notUtilIsDeepStrict(a, b); 502 b[symbol1] = true; 503 utilIsDeepStrict(a, b); 504 // The same as TypedArrays is valid for boxed primitives 505 const boxedStringA = new String('test'); 506 const boxedStringB = new String('test'); 507 boxedStringA[symbol1] = true; 508 notUtilIsDeepStrict(boxedStringA, boxedStringB); 509 boxedStringA[symbol1] = true; 510 utilIsDeepStrict(a, b); 511} 512