1'use strict';
2
3require('../common');
4const assert = require('assert');
5const util = require('util');
6const { AssertionError } = assert;
7const defaultMsgStart = 'Expected values to be strictly deep-equal:\n';
8const defaultMsgStartFull = `${defaultMsgStart}+ actual - expected`;
9
10// Disable colored output to prevent color codes from breaking assertion
11// message comparisons. This should only be an issue when process.stdout
12// is a TTY.
13if (process.stdout.isTTY)
14  process.env.NODE_DISABLE_COLORS = '1';
15
16// Template tag function turning an error message into a RegExp
17// for assert.throws()
18function re(literals, ...values) {
19  let result = 'Expected values to be loosely deep-equal:\n\n';
20  for (const [i, value] of values.entries()) {
21    const str = util.inspect(value, {
22      compact: false,
23      depth: 1000,
24      customInspect: false,
25      maxArrayLength: Infinity,
26      breakLength: Infinity,
27      sorted: true,
28      getters: true
29    });
30    // Need to escape special characters.
31    result += `${str}${literals[i + 1]}`;
32  }
33  return {
34    code: 'ERR_ASSERTION',
35    message: result
36  };
37}
38
39// The following deepEqual tests might seem very weird.
40// They just describe what it is now.
41// That is why we discourage using deepEqual in our own tests.
42
43// Turn off no-restricted-properties because we are testing deepEqual!
44/* eslint-disable no-restricted-properties */
45
46const arr = new Uint8Array([120, 121, 122, 10]);
47const buf = Buffer.from(arr);
48// They have different [[Prototype]]
49assert.throws(
50  () => assert.deepStrictEqual(arr, buf),
51  {
52    code: 'ERR_ASSERTION',
53    message: `${defaultMsgStartFull} ... Lines skipped\n\n` +
54             '+ Uint8Array(4) [\n' +
55             '- Buffer(4) [Uint8Array] [\n    120,\n...\n    122,\n    10\n  ]'
56  }
57);
58assert.deepEqual(arr, buf);
59
60{
61  const buf2 = Buffer.from(arr);
62  buf2.prop = 1;
63
64  assert.throws(
65    () => assert.deepStrictEqual(buf2, buf),
66    {
67      code: 'ERR_ASSERTION',
68      message: `${defaultMsgStartFull}\n\n` +
69               '  Buffer(4) [Uint8Array] [\n' +
70               '    120,\n' +
71               '    121,\n' +
72               '    122,\n' +
73               '    10,\n' +
74               '+   prop: 1\n' +
75               '  ]'
76    }
77  );
78  assert.notDeepEqual(buf2, buf);
79}
80
81{
82  const arr2 = new Uint8Array([120, 121, 122, 10]);
83  arr2.prop = 5;
84  assert.throws(
85    () => assert.deepStrictEqual(arr, arr2),
86    {
87      code: 'ERR_ASSERTION',
88      message: `${defaultMsgStartFull}\n\n` +
89               '  Uint8Array(4) [\n' +
90               '    120,\n' +
91               '    121,\n' +
92               '    122,\n' +
93               '    10,\n' +
94               '-   prop: 5\n' +
95               '  ]'
96    }
97  );
98  assert.notDeepEqual(arr, arr2);
99}
100
101const date = new Date('2016');
102
103class MyDate extends Date {
104  constructor(...args) {
105    super(...args);
106    this[0] = '1';
107  }
108}
109
110const date2 = new MyDate('2016');
111
112assertNotDeepOrStrict(date, date2);
113assert.throws(
114  () => assert.deepStrictEqual(date, date2),
115  {
116    code: 'ERR_ASSERTION',
117    message: `${defaultMsgStartFull}\n\n` +
118             '+ 2016-01-01T00:00:00.000Z\n- MyDate 2016-01-01T00:00:00.000Z' +
119             " {\n-   '0': '1'\n- }"
120  }
121);
122assert.throws(
123  () => assert.deepStrictEqual(date2, date),
124  {
125    code: 'ERR_ASSERTION',
126    message: `${defaultMsgStartFull}\n\n` +
127             '+ MyDate 2016-01-01T00:00:00.000Z {\n' +
128             "+   '0': '1'\n+ }\n- 2016-01-01T00:00:00.000Z"
129  }
130);
131
132class MyRegExp extends RegExp {
133  constructor(...args) {
134    super(...args);
135    this[0] = '1';
136  }
137}
138
139const re1 = new RegExp('test');
140const re2 = new MyRegExp('test');
141
142assertNotDeepOrStrict(re1, re2);
143assert.throws(
144  () => assert.deepStrictEqual(re1, re2),
145  {
146    code: 'ERR_ASSERTION',
147    message: `${defaultMsgStartFull}\n\n` +
148             "+ /test/\n- MyRegExp /test/ {\n-   '0': '1'\n- }"
149  }
150);
151
152// For these weird cases, deepEqual should pass (at least for now),
153// but deepStrictEqual should throw.
154{
155  const similar = new Set([
156    { 0: 1 },  // Object
157    new String('1'),  // Object
158    [1],  // Array
159    date2,  // Date with this[0] = '1'
160    re2,  // RegExp with this[0] = '1'
161    new Int8Array([1]), // Int8Array
162    new Int16Array([1]), // Int16Array
163    new Uint16Array([1]), // Uint16Array
164    new Int32Array([1]), // Int32Array
165    new Uint32Array([1]), // Uint32Array
166    Buffer.from([1]), // Uint8Array
167    (function() { return arguments; })(1),
168  ]);
169
170  for (const a of similar) {
171    for (const b of similar) {
172      if (a !== b) {
173        assert.notDeepEqual(a, b);
174        assert.throws(
175          () => assert.deepStrictEqual(a, b),
176          { code: 'ERR_ASSERTION' }
177        );
178      }
179    }
180  }
181}
182
183function assertDeepAndStrictEqual(a, b) {
184  assert.deepEqual(a, b);
185  assert.deepStrictEqual(a, b);
186
187  assert.deepEqual(b, a);
188  assert.deepStrictEqual(b, a);
189}
190
191function assertNotDeepOrStrict(a, b, err) {
192  assert.throws(
193    () => assert.deepEqual(a, b),
194    err || re`${a}\n\nshould loosely deep-equal\n\n${b}`
195  );
196  assert.throws(
197    () => assert.deepStrictEqual(a, b),
198    err || { code: 'ERR_ASSERTION' }
199  );
200
201  assert.throws(
202    () => assert.deepEqual(b, a),
203    err || re`${b}\n\nshould loosely deep-equal\n\n${a}`
204  );
205  assert.throws(
206    () => assert.deepStrictEqual(b, a),
207    err || { code: 'ERR_ASSERTION' }
208  );
209}
210
211function assertOnlyDeepEqual(a, b, err) {
212  assert.deepEqual(a, b);
213  assert.throws(
214    () => assert.deepStrictEqual(a, b),
215    err || { code: 'ERR_ASSERTION' }
216  );
217
218  assert.deepEqual(b, a);
219  assert.throws(
220    () => assert.deepStrictEqual(b, a),
221    err || { code: 'ERR_ASSERTION' }
222  );
223}
224
225// es6 Maps and Sets
226assertDeepAndStrictEqual(new Set(), new Set());
227assertDeepAndStrictEqual(new Map(), new Map());
228
229assertDeepAndStrictEqual(new Set([1, 2, 3]), new Set([1, 2, 3]));
230assertNotDeepOrStrict(new Set([1, 2, 3]), new Set([1, 2, 3, 4]));
231assertNotDeepOrStrict(new Set([1, 2, 3, 4]), new Set([1, 2, 3]));
232assertDeepAndStrictEqual(new Set(['1', '2', '3']), new Set(['1', '2', '3']));
233assertDeepAndStrictEqual(new Set([[1, 2], [3, 4]]), new Set([[3, 4], [1, 2]]));
234assertNotDeepOrStrict(new Set([{ a: 0 }]), new Set([{ a: 1 }]));
235assertNotDeepOrStrict(new Set([Symbol()]), new Set([Symbol()]));
236
237{
238  const a = [ 1, 2 ];
239  const b = [ 3, 4 ];
240  const c = [ 1, 2 ];
241  const d = [ 3, 4 ];
242
243  assertDeepAndStrictEqual(
244    { a: a, b: b, s: new Set([a, b]) },
245    { a: c, b: d, s: new Set([d, c]) }
246  );
247}
248
249assertDeepAndStrictEqual(new Map([[1, 1], [2, 2]]), new Map([[1, 1], [2, 2]]));
250assertDeepAndStrictEqual(new Map([[1, 1], [2, 2]]), new Map([[2, 2], [1, 1]]));
251assertNotDeepOrStrict(new Map([[1, 1], [2, 2]]), new Map([[1, 2], [2, 1]]));
252assertNotDeepOrStrict(
253  new Map([[[1], 1], [{}, 2]]),
254  new Map([[[1], 2], [{}, 1]])
255);
256
257assertNotDeepOrStrict(new Set([1]), [1]);
258assertNotDeepOrStrict(new Set(), []);
259assertNotDeepOrStrict(new Set(), {});
260
261assertNotDeepOrStrict(new Map([['a', 1]]), { a: 1 });
262assertNotDeepOrStrict(new Map(), []);
263assertNotDeepOrStrict(new Map(), {});
264
265assertOnlyDeepEqual(new Set(['1']), new Set([1]));
266
267assertOnlyDeepEqual(new Map([['1', 'a']]), new Map([[1, 'a']]));
268assertOnlyDeepEqual(new Map([['a', '1']]), new Map([['a', 1]]));
269assertNotDeepOrStrict(new Map([['a', '1']]), new Map([['a', 2]]));
270
271assertDeepAndStrictEqual(new Set([{}]), new Set([{}]));
272
273// Ref: https://github.com/nodejs/node/issues/13347
274assertNotDeepOrStrict(
275  new Set([{ a: 1 }, { a: 1 }]),
276  new Set([{ a: 1 }, { a: 2 }])
277);
278assertNotDeepOrStrict(
279  new Set([{ a: 1 }, { a: 1 }, { a: 2 }]),
280  new Set([{ a: 1 }, { a: 2 }, { a: 2 }])
281);
282assertNotDeepOrStrict(
283  new Map([[{ x: 1 }, 5], [{ x: 1 }, 5]]),
284  new Map([[{ x: 1 }, 5], [{ x: 2 }, 5]])
285);
286
287assertNotDeepOrStrict(new Set([3, '3']), new Set([3, 4]));
288assertNotDeepOrStrict(new Map([[3, 0], ['3', 0]]), new Map([[3, 0], [4, 0]]));
289
290assertNotDeepOrStrict(
291  new Set([{ a: 1 }, { a: 1 }, { a: 2 }]),
292  new Set([{ a: 1 }, { a: 2 }, { a: 2 }])
293);
294
295// Mixed primitive and object keys
296assertDeepAndStrictEqual(
297  new Map([[1, 'a'], [{}, 'a']]),
298  new Map([[1, 'a'], [{}, 'a']])
299);
300assertDeepAndStrictEqual(
301  new Set([1, 'a', [{}, 'a']]),
302  new Set([1, 'a', [{}, 'a']])
303);
304
305// This is an awful case, where a map contains multiple equivalent keys:
306assertOnlyDeepEqual(
307  new Map([[1, 'a'], ['1', 'b']]),
308  new Map([['1', 'a'], [true, 'b']])
309);
310assertNotDeepOrStrict(
311  new Set(['a']),
312  new Set(['b'])
313);
314assertDeepAndStrictEqual(
315  new Map([[{}, 'a'], [{}, 'b']]),
316  new Map([[{}, 'b'], [{}, 'a']])
317);
318assertOnlyDeepEqual(
319  new Map([[true, 'a'], ['1', 'b'], [1, 'a']]),
320  new Map([['1', 'a'], [1, 'b'], [true, 'a']])
321);
322assertNotDeepOrStrict(
323  new Map([[true, 'a'], ['1', 'b'], [1, 'c']]),
324  new Map([['1', 'a'], [1, 'b'], [true, 'a']])
325);
326
327// Similar object keys
328assertNotDeepOrStrict(
329  new Set([{}, {}]),
330  new Set([{}, 1])
331);
332assertNotDeepOrStrict(
333  new Set([[{}, 1], [{}, 1]]),
334  new Set([[{}, 1], [1, 1]])
335);
336assertNotDeepOrStrict(
337  new Map([[{}, 1], [{}, 1]]),
338  new Map([[{}, 1], [1, 1]])
339);
340assertOnlyDeepEqual(
341  new Map([[{}, 1], [true, 1]]),
342  new Map([[{}, 1], [1, 1]])
343);
344
345// Similar primitive key / values
346assertNotDeepOrStrict(
347  new Set([1, true, false]),
348  new Set(['1', 0, '0'])
349);
350assertNotDeepOrStrict(
351  new Map([[1, 5], [true, 5], [false, 5]]),
352  new Map([['1', 5], [0, 5], ['0', 5]])
353);
354
355// Undefined value in Map
356assertDeepAndStrictEqual(
357  new Map([[1, undefined]]),
358  new Map([[1, undefined]])
359);
360assertOnlyDeepEqual(
361  new Map([[1, null], ['', '0']]),
362  new Map([['1', undefined], [false, 0]])
363);
364assertNotDeepOrStrict(
365  new Map([[1, undefined]]),
366  new Map([[2, undefined]])
367);
368
369// null as key
370assertDeepAndStrictEqual(
371  new Map([[null, 3]]),
372  new Map([[null, 3]])
373);
374assertOnlyDeepEqual(
375  new Map([[undefined, null], ['+000', 2n]]),
376  new Map([[null, undefined], [false, '2']]),
377);
378
379assertOnlyDeepEqual(
380  new Set([null, '', 1n, 5, 2n, false]),
381  new Set([undefined, 0, 5n, true, '2', '-000'])
382);
383assertNotDeepOrStrict(
384  new Set(['']),
385  new Set(['0'])
386);
387assertOnlyDeepEqual(
388  new Map([[1, {}]]),
389  new Map([[true, {}]])
390);
391assertOnlyDeepEqual(
392  new Map([[undefined, true]]),
393  new Map([[null, true]])
394);
395assertNotDeepOrStrict(
396  new Map([[undefined, true]]),
397  new Map([[true, true]])
398);
399
400// GH-6416. Make sure circular refs don't throw.
401{
402  const b = {};
403  b.b = b;
404  const c = {};
405  c.b = c;
406
407  assertDeepAndStrictEqual(b, c);
408
409  const d = {};
410  d.a = 1;
411  d.b = d;
412  const e = {};
413  e.a = 1;
414  e.b = {};
415
416  assertNotDeepOrStrict(d, e);
417}
418
419// GH-14441. Circular structures should be consistent
420{
421  const a = {};
422  const b = {};
423  a.a = a;
424  b.a = {};
425  b.a.a = a;
426  assertDeepAndStrictEqual(a, b);
427}
428
429{
430  const a = new Set();
431  const b = new Set();
432  const c = new Set();
433  a.add(a);
434  b.add(b);
435  c.add(a);
436  assertDeepAndStrictEqual(b, c);
437}
438
439// https://github.com/nodejs/node-v0.x-archive/pull/7178
440// Ensure reflexivity of deepEqual with `arguments` objects.
441{
442  const args = (function() { return arguments; })();
443  assertNotDeepOrStrict([], args);
444}
445
446// More checking that arguments objects are handled correctly
447{
448  // eslint-disable-next-line func-style
449  const returnArguments = function() { return arguments; };
450
451  const someArgs = returnArguments('a');
452  const sameArgs = returnArguments('a');
453  const diffArgs = returnArguments('b');
454
455  assertNotDeepOrStrict(someArgs, ['a']);
456  assertNotDeepOrStrict(someArgs, { '0': 'a' });
457  assertNotDeepOrStrict(someArgs, diffArgs);
458  assertDeepAndStrictEqual(someArgs, sameArgs);
459}
460
461{
462  const values = [
463    123,
464    Infinity,
465    0,
466    null,
467    undefined,
468    false,
469    true,
470    {},
471    [],
472    () => {},
473  ];
474  assertDeepAndStrictEqual(new Set(values), new Set(values));
475  assertDeepAndStrictEqual(new Set(values), new Set(values.reverse()));
476
477  const mapValues = values.map((v) => [v, { a: 5 }]);
478  assertDeepAndStrictEqual(new Map(mapValues), new Map(mapValues));
479  assertDeepAndStrictEqual(new Map(mapValues), new Map(mapValues.reverse()));
480}
481
482{
483  const s1 = new Set();
484  const s2 = new Set();
485  s1.add(1);
486  s1.add(2);
487  s2.add(2);
488  s2.add(1);
489  assertDeepAndStrictEqual(s1, s2);
490}
491
492{
493  const m1 = new Map();
494  const m2 = new Map();
495  const obj = { a: 5, b: 6 };
496  m1.set(1, obj);
497  m1.set(2, 'hi');
498  m1.set(3, [1, 2, 3]);
499
500  m2.set(2, 'hi'); // different order
501  m2.set(1, obj);
502  m2.set(3, [1, 2, 3]); // Deep equal, but not reference equal.
503
504  assertDeepAndStrictEqual(m1, m2);
505}
506
507{
508  const m1 = new Map();
509  const m2 = new Map();
510
511  // m1 contains itself.
512  m1.set(1, m1);
513  m2.set(1, new Map());
514
515  assertNotDeepOrStrict(m1, m2);
516}
517
518{
519  const map1 = new Map([[1, 1]]);
520  const map2 = new Map([[1, '1']]);
521  assert.deepEqual(map1, map2);
522  assert.throws(
523    () => assert.deepStrictEqual(map1, map2),
524    {
525      code: 'ERR_ASSERTION',
526      message: `${defaultMsgStartFull}\n\n` +
527               "  Map(1) {\n+   1 => 1\n-   1 => '1'\n  }"
528    }
529  );
530}
531
532{
533  // Two equivalent sets / maps with different key/values applied shouldn't be
534  // the same. This is a terrible idea to do in practice, but deepEqual should
535  // still check for it.
536  const s1 = new Set();
537  const s2 = new Set();
538  s1.x = 5;
539  assertNotDeepOrStrict(s1, s2);
540
541  const m1 = new Map();
542  const m2 = new Map();
543  m1.x = 5;
544  assertNotDeepOrStrict(m1, m2);
545}
546
547{
548  // Circular references.
549  const s1 = new Set();
550  s1.add(s1);
551  const s2 = new Set();
552  s2.add(s2);
553  assertDeepAndStrictEqual(s1, s2);
554
555  const m1 = new Map();
556  m1.set(2, m1);
557  const m2 = new Map();
558  m2.set(2, m2);
559  assertDeepAndStrictEqual(m1, m2);
560
561  const m3 = new Map();
562  m3.set(m3, 2);
563  const m4 = new Map();
564  m4.set(m4, 2);
565  assertDeepAndStrictEqual(m3, m4);
566}
567
568// Handle sparse arrays.
569{
570  /* eslint-disable no-sparse-arrays */
571  assertDeepAndStrictEqual([1, , , 3], [1, , , 3]);
572  assertNotDeepOrStrict([1, , , 3], [1, , , 3, , , ]);
573  /* eslint-enable no-sparse-arrays */
574  const a = new Array(3);
575  const b = new Array(3);
576  a[2] = true;
577  b[1] = true;
578  assertNotDeepOrStrict(a, b);
579  b[2] = true;
580  assertNotDeepOrStrict(a, b);
581  a[0] = true;
582  assertNotDeepOrStrict(a, b);
583}
584
585// Handle different error messages
586{
587  const err1 = new Error('foo1');
588  assertNotDeepOrStrict(err1, new Error('foo2'), assert.AssertionError);
589  assertNotDeepOrStrict(err1, new TypeError('foo1'), assert.AssertionError);
590  assertDeepAndStrictEqual(err1, new Error('foo1'));
591  assertNotDeepOrStrict(err1, {}, AssertionError);
592}
593
594// Handle NaN
595assertDeepAndStrictEqual(NaN, NaN);
596assertDeepAndStrictEqual({ a: NaN }, { a: NaN });
597assertDeepAndStrictEqual([ 1, 2, NaN, 4 ], [ 1, 2, NaN, 4 ]);
598
599// Handle boxed primitives
600{
601  const boxedString = new String('test');
602  const boxedSymbol = Object(Symbol());
603
604  const fakeBoxedSymbol = {};
605  Object.setPrototypeOf(fakeBoxedSymbol, Symbol.prototype);
606  Object.defineProperty(
607    fakeBoxedSymbol,
608    Symbol.toStringTag,
609    { enumerable: false, value: 'Symbol' }
610  );
611
612  assertNotDeepOrStrict(new Boolean(true), Object(false));
613  assertNotDeepOrStrict(Object(true), new Number(1));
614  assertNotDeepOrStrict(new Number(2), new Number(1));
615  assertNotDeepOrStrict(boxedSymbol, Object(Symbol()));
616  assertNotDeepOrStrict(boxedSymbol, {});
617  assertNotDeepOrStrict(boxedSymbol, fakeBoxedSymbol);
618  assertDeepAndStrictEqual(boxedSymbol, boxedSymbol);
619  assertDeepAndStrictEqual(Object(true), Object(true));
620  assertDeepAndStrictEqual(Object(2), Object(2));
621  assertDeepAndStrictEqual(boxedString, Object('test'));
622  boxedString.slow = true;
623  assertNotDeepOrStrict(boxedString, Object('test'));
624  boxedSymbol.slow = true;
625  assertNotDeepOrStrict(boxedSymbol, {});
626  assertNotDeepOrStrict(boxedSymbol, fakeBoxedSymbol);
627}
628
629// Minus zero
630assertOnlyDeepEqual(0, -0);
631assertDeepAndStrictEqual(-0, -0);
632
633// Handle symbols (enumerable only)
634{
635  const symbol1 = Symbol();
636  const obj1 = { [symbol1]: 1 };
637  const obj2 = { [symbol1]: 1 };
638  const obj3 = { [Symbol()]: 1 };
639  // Add a non enumerable symbol as well. It is going to be ignored!
640  Object.defineProperty(obj2, Symbol(), { value: 1 });
641  assertOnlyDeepEqual(obj1, obj3);
642  assertDeepAndStrictEqual(obj1, obj2);
643  obj2[Symbol()] = true;
644  assertOnlyDeepEqual(obj1, obj2);
645  // TypedArrays have a fast path. Test for this as well.
646  const a = new Uint8Array(4);
647  const b = new Uint8Array(4);
648  a[symbol1] = true;
649  b[symbol1] = false;
650  assertOnlyDeepEqual(a, b);
651  b[symbol1] = true;
652  assertDeepAndStrictEqual(a, b);
653  // The same as TypedArrays is valid for boxed primitives
654  const boxedStringA = new String('test');
655  const boxedStringB = new String('test');
656  boxedStringA[symbol1] = true;
657  assertOnlyDeepEqual(boxedStringA, boxedStringB);
658  boxedStringA[symbol1] = true;
659  assertDeepAndStrictEqual(a, b);
660  // Loose equal arrays should not compare symbols.
661  const arr = [1];
662  const arr2 = [1];
663  arr[symbol1] = true;
664  assertOnlyDeepEqual(arr, arr2);
665  arr2[symbol1] = false;
666  assertOnlyDeepEqual(arr, arr2);
667}
668
669assert.throws(
670  () => assert.notDeepEqual(1, true),
671  {
672    message: /1\n\nshould not loosely deep-equal\n\ntrue/
673  }
674);
675
676assert.throws(
677  () => assert.notDeepEqual(1, 1),
678  {
679    message: /Expected "actual" not to be loosely deep-equal to:\n\n1/
680  }
681);
682
683assertDeepAndStrictEqual(new Date(2000, 3, 14), new Date(2000, 3, 14));
684
685assert.throws(() => { assert.deepEqual(new Date(), new Date(2000, 3, 14)); },
686              AssertionError,
687              'deepEqual(new Date(), new Date(2000, 3, 14))');
688
689assert.throws(
690  () => { assert.notDeepEqual(new Date(2000, 3, 14), new Date(2000, 3, 14)); },
691  AssertionError,
692  'notDeepEqual(new Date(2000, 3, 14), new Date(2000, 3, 14))'
693);
694
695assert.throws(
696  () => { assert.notDeepEqual('a'.repeat(1024), 'a'.repeat(1024)); },
697  AssertionError,
698  'notDeepEqual("a".repeat(1024), "a".repeat(1024))'
699);
700
701assertNotDeepOrStrict(new Date(), new Date(2000, 3, 14));
702
703assertDeepAndStrictEqual(/a/, /a/);
704assertDeepAndStrictEqual(/a/g, /a/g);
705assertDeepAndStrictEqual(/a/i, /a/i);
706assertDeepAndStrictEqual(/a/m, /a/m);
707assertDeepAndStrictEqual(/a/igm, /a/igm);
708assertNotDeepOrStrict(/ab/, /a/);
709assertNotDeepOrStrict(/a/g, /a/);
710assertNotDeepOrStrict(/a/i, /a/);
711assertNotDeepOrStrict(/a/m, /a/);
712assertNotDeepOrStrict(/a/igm, /a/im);
713
714{
715  const re1 = /a/g;
716  re1.lastIndex = 3;
717  assert.notDeepEqual(re1, /a/g);
718}
719
720assert.deepEqual(4, '4');
721assert.deepEqual(true, 1);
722assert.throws(() => assert.deepEqual(4, '5'),
723              AssertionError,
724              'deepEqual( 4, \'5\')');
725
726// Having the same number of owned properties && the same set of keys.
727assert.deepEqual({ a: 4 }, { a: 4 });
728assert.deepEqual({ a: 4, b: '2' }, { a: 4, b: '2' });
729assert.deepEqual([4], ['4']);
730assert.throws(
731  () => assert.deepEqual({ a: 4 }, { a: 4, b: true }), AssertionError);
732assert.notDeepEqual(['a'], { 0: 'a' });
733assert.deepEqual({ a: 4, b: '1' }, { b: '1', a: 4 });
734const a1 = [1, 2, 3];
735const a2 = [1, 2, 3];
736a1.a = 'test';
737a1.b = true;
738a2.b = true;
739a2.a = 'test';
740assert.throws(() => assert.deepEqual(Object.keys(a1), Object.keys(a2)),
741              AssertionError);
742assertDeepAndStrictEqual(a1, a2);
743
744// Having an identical prototype property.
745const nbRoot = {
746  toString() { return `${this.first} ${this.last}`; }
747};
748
749function nameBuilder(first, last) {
750  this.first = first;
751  this.last = last;
752  return this;
753}
754nameBuilder.prototype = nbRoot;
755
756function nameBuilder2(first, last) {
757  this.first = first;
758  this.last = last;
759  return this;
760}
761nameBuilder2.prototype = nbRoot;
762
763const nb1 = new nameBuilder('Ryan', 'Dahl');
764let nb2 = new nameBuilder2('Ryan', 'Dahl');
765
766assert.deepEqual(nb1, nb2);
767
768nameBuilder2.prototype = Object;
769nb2 = new nameBuilder2('Ryan', 'Dahl');
770assert.deepEqual(nb1, nb2);
771
772// Primitives
773assertNotDeepOrStrict(null, {});
774assertNotDeepOrStrict(undefined, {});
775assertNotDeepOrStrict('a', ['a']);
776assertNotDeepOrStrict('a', { 0: 'a' });
777assertNotDeepOrStrict(1, {});
778assertNotDeepOrStrict(true, {});
779assertNotDeepOrStrict(Symbol(), {});
780assertNotDeepOrStrict(Symbol(), Symbol());
781
782assertOnlyDeepEqual(4, '4');
783assertOnlyDeepEqual(true, 1);
784
785{
786  const s = Symbol();
787  assertDeepAndStrictEqual(s, s);
788}
789
790// Primitive wrappers and object.
791assertNotDeepOrStrict(new String('a'), ['a']);
792assertNotDeepOrStrict(new String('a'), { 0: 'a' });
793assertNotDeepOrStrict(new Number(1), {});
794assertNotDeepOrStrict(new Boolean(true), {});
795
796// Same number of keys but different key names.
797assertNotDeepOrStrict({ a: 1 }, { b: 1 });
798
799assert.deepStrictEqual(new Date(2000, 3, 14), new Date(2000, 3, 14));
800
801assert.throws(
802  () => assert.deepStrictEqual(new Date(), new Date(2000, 3, 14)),
803  AssertionError,
804  'deepStrictEqual(new Date(), new Date(2000, 3, 14))'
805);
806
807assert.throws(
808  () => assert.notDeepStrictEqual(new Date(2000, 3, 14), new Date(2000, 3, 14)),
809  {
810    name: 'AssertionError',
811    message: 'Expected "actual" not to be strictly deep-equal to:\n\n' +
812             util.inspect(new Date(2000, 3, 14))
813  }
814);
815
816assert.notDeepStrictEqual(new Date(), new Date(2000, 3, 14));
817
818assert.throws(
819  () => assert.deepStrictEqual(/ab/, /a/),
820  {
821    code: 'ERR_ASSERTION',
822    name: 'AssertionError',
823    message: `${defaultMsgStartFull}\n\n+ /ab/\n- /a/`
824  });
825assert.throws(
826  () => assert.deepStrictEqual(/a/g, /a/),
827  {
828    code: 'ERR_ASSERTION',
829    name: 'AssertionError',
830    message: `${defaultMsgStartFull}\n\n+ /a/g\n- /a/`
831  });
832assert.throws(
833  () => assert.deepStrictEqual(/a/i, /a/),
834  {
835    code: 'ERR_ASSERTION',
836    name: 'AssertionError',
837    message: `${defaultMsgStartFull}\n\n+ /a/i\n- /a/`
838  });
839assert.throws(
840  () => assert.deepStrictEqual(/a/m, /a/),
841  {
842    code: 'ERR_ASSERTION',
843    name: 'AssertionError',
844    message: `${defaultMsgStartFull}\n\n+ /a/m\n- /a/`
845  });
846assert.throws(
847  () => assert.deepStrictEqual(/aa/igm, /aa/im),
848  {
849    code: 'ERR_ASSERTION',
850    name: 'AssertionError',
851    message: `${defaultMsgStartFull}\n\n+ /aa/gim\n- /aa/im\n      ^`
852  });
853
854{
855  const re1 = /a/;
856  re1.lastIndex = 3;
857  assert.notDeepStrictEqual(re1, /a/);
858}
859
860assert.throws(
861  // eslint-disable-next-line no-restricted-syntax
862  () => assert.deepStrictEqual(4, '4'),
863  { message: `${defaultMsgStart}\n4 !== '4'\n` }
864);
865
866assert.throws(
867  // eslint-disable-next-line no-restricted-syntax
868  () => assert.deepStrictEqual(true, 1),
869  { message: `${defaultMsgStart}\ntrue !== 1\n` }
870);
871
872// Having the same number of owned properties && the same set of keys.
873assert.deepStrictEqual({ a: 4 }, { a: 4 });
874assert.deepStrictEqual({ a: 4, b: '2' }, { a: 4, b: '2' });
875assert.throws(() => assert.deepStrictEqual([4], ['4']),
876              {
877                code: 'ERR_ASSERTION',
878                name: 'AssertionError',
879                message: `${defaultMsgStartFull}\n\n  [\n+   4\n-   '4'\n  ]`
880              });
881assert.throws(
882  () => assert.deepStrictEqual({ a: 4 }, { a: 4, b: true }),
883  {
884    code: 'ERR_ASSERTION',
885    name: 'AssertionError',
886    message: `${defaultMsgStartFull}\n\n  ` +
887             '{\n    a: 4,\n-   b: true\n  }'
888  });
889assert.throws(
890  () => assert.deepStrictEqual(['a'], { 0: 'a' }),
891  {
892    code: 'ERR_ASSERTION',
893    name: 'AssertionError',
894    message: `${defaultMsgStartFull}\n\n` +
895             "+ [\n+   'a'\n+ ]\n- {\n-   '0': 'a'\n- }"
896  });
897
898/* eslint-enable */
899
900assertDeepAndStrictEqual({ a: 4, b: '1' }, { b: '1', a: 4 });
901
902assert.throws(
903  () => assert.deepStrictEqual([0, 1, 2, 'a', 'b'], [0, 1, 2, 'b', 'a']),
904  AssertionError);
905
906// Prototype check.
907function Constructor1(first, last) {
908  this.first = first;
909  this.last = last;
910}
911
912function Constructor2(first, last) {
913  this.first = first;
914  this.last = last;
915}
916
917const obj1 = new Constructor1('Ryan', 'Dahl');
918let obj2 = new Constructor2('Ryan', 'Dahl');
919
920assert.throws(() => assert.deepStrictEqual(obj1, obj2), AssertionError);
921
922Constructor2.prototype = Constructor1.prototype;
923obj2 = new Constructor2('Ryan', 'Dahl');
924
925assertDeepAndStrictEqual(obj1, obj2);
926
927// Check extra properties on errors.
928{
929  const a = new TypeError('foo');
930  const b = new TypeError('foo');
931  a.foo = 'bar';
932  b.foo = 'baz.';
933
934  assert.throws(
935    () => assert.throws(
936      () => assert.deepStrictEqual(a, b),
937      {
938        operator: 'throws',
939        message: `${defaultMsgStartFull}\n\n` +
940                '  [TypeError: foo] {\n+   foo: \'bar\'\n-   foo: \'baz\'\n  }',
941      }
942    ),
943    {
944      message: 'Expected values to be strictly deep-equal:\n' +
945        '+ actual - expected ... Lines skipped\n' +
946        '\n' +
947        '  Comparison {\n' +
948        "    message: 'Expected values to be strictly deep-equal:\\n' +\n" +
949        '...\n' +
950        "      '  [TypeError: foo] {\\n' +\n" +
951        "      \"+   foo: 'bar'\\n\" +\n" +
952        "+     \"-   foo: 'baz.'\\n\" +\n" +
953        "-     \"-   foo: 'baz'\\n\" +\n" +
954        "      '  }',\n" +
955        "+   operator: 'deepStrictEqual'\n" +
956        "-   operator: 'throws'\n" +
957        '  }'
958    }
959  );
960}
961
962// Check proxies.
963{
964  const arrProxy = new Proxy([1, 2], {});
965  assert.deepStrictEqual(arrProxy, [1, 2]);
966  const tmp = util.inspect.defaultOptions;
967  util.inspect.defaultOptions = { showProxy: true };
968  assert.throws(
969    () => assert.deepStrictEqual(arrProxy, [1, 2, 3]),
970    { message: `${defaultMsgStartFull}\n\n` +
971               '  [\n    1,\n    2,\n-   3\n  ]' }
972  );
973  util.inspect.defaultOptions = tmp;
974
975  const invalidTrap = new Proxy([1, 2, 3], {
976    ownKeys() { return []; }
977  });
978  assert.throws(
979    () => assert.deepStrictEqual(invalidTrap, [1, 2, 3]),
980    {
981      name: 'TypeError',
982      message: "'ownKeys' on proxy: trap result did not include 'length'"
983    }
984  );
985}
986
987// Strict equal with identical objects that are not identical
988// by reference and longer than 50 elements
989// E.g., assert.deepStrictEqual({ a: Symbol() }, { a: Symbol() })
990{
991  const a = {};
992  const b = {};
993  for (let i = 0; i < 55; i++) {
994    a[`symbol${i}`] = Symbol();
995    b[`symbol${i}`] = Symbol();
996  }
997
998  assert.throws(
999    () => assert.deepStrictEqual(a, b),
1000    {
1001      code: 'ERR_ASSERTION',
1002      name: 'AssertionError',
1003      message: /\.\.\./g
1004    }
1005  );
1006}
1007
1008// Basic valueOf check.
1009{
1010  const a = new String(1);
1011  a.valueOf = undefined;
1012  assertNotDeepOrStrict(a, new String(1));
1013}
1014
1015// Basic array out of bounds check.
1016{
1017  const arr = [1, 2, 3];
1018  arr[2 ** 32] = true;
1019  assertNotDeepOrStrict(arr, [1, 2, 3]);
1020}
1021
1022assert.throws(
1023  () => assert.deepStrictEqual([1, 2, 3], [1, 2]),
1024  {
1025    code: 'ERR_ASSERTION',
1026    name: 'AssertionError',
1027    message: `${defaultMsgStartFull}\n\n` +
1028            '  [\n' +
1029            '    1,\n' +
1030            '    2,\n' +
1031            '+   3\n' +
1032            '  ]'
1033  }
1034);
1035
1036// Verify that manipulating the `getTime()` function has no impact on the time
1037// verification.
1038{
1039  const a = new Date('2000');
1040  const b = new Date('2000');
1041  Object.defineProperty(a, 'getTime', {
1042    value: () => 5
1043  });
1044  assertDeepAndStrictEqual(a, b);
1045}
1046
1047// Verify that an array and the equivalent fake array object
1048// are correctly compared
1049{
1050  const a = [1, 2, 3];
1051  const o = {
1052    __proto__: Array.prototype,
1053    0: 1,
1054    1: 2,
1055    2: 3,
1056    length: 3,
1057  };
1058  Object.defineProperty(o, 'length', { enumerable: false });
1059  assertNotDeepOrStrict(o, a);
1060}
1061
1062// Verify that extra keys will be tested for when using fake arrays.
1063{
1064  const a = {
1065    0: 1,
1066    1: 1,
1067    2: 'broken'
1068  };
1069  Object.setPrototypeOf(a, Object.getPrototypeOf([]));
1070  Object.defineProperty(a, Symbol.toStringTag, {
1071    value: 'Array',
1072  });
1073  Object.defineProperty(a, 'length', {
1074    value: 2
1075  });
1076  assertNotDeepOrStrict(a, [1, 1]);
1077}
1078
1079// Verify that changed tags will still check for the error message.
1080{
1081  const err = new Error('foo');
1082  err[Symbol.toStringTag] = 'Foobar';
1083  const err2 = new Error('bar');
1084  err2[Symbol.toStringTag] = 'Foobar';
1085  assertNotDeepOrStrict(err, err2, AssertionError);
1086}
1087
1088// Check for non-native errors.
1089{
1090  const source = new Error('abc');
1091  const err = Object.create(
1092    Object.getPrototypeOf(source), Object.getOwnPropertyDescriptors(source));
1093  Object.defineProperty(err, 'message', { value: 'foo' });
1094  const err2 = Object.create(
1095    Object.getPrototypeOf(source), Object.getOwnPropertyDescriptors(source));
1096  Object.defineProperty(err2, 'message', { value: 'bar' });
1097  err[Symbol.toStringTag] = 'Foo';
1098  err2[Symbol.toStringTag] = 'Foo';
1099  assert.notDeepStrictEqual(err, err2);
1100}
1101
1102// Verify that `valueOf` is not called for boxed primitives.
1103{
1104  const a = new Number(5);
1105  const b = new Number(5);
1106  Object.defineProperty(a, 'valueOf', {
1107    value: () => { throw new Error('failed'); }
1108  });
1109  Object.defineProperty(b, 'valueOf', {
1110    value: () => { throw new Error('failed'); }
1111  });
1112  assertDeepAndStrictEqual(a, b);
1113}
1114
1115// Check getters.
1116{
1117  const a = {
1118    get a() { return 5; }
1119  };
1120  const b = {
1121    get a() { return 6; }
1122  };
1123  assert.throws(
1124    () => assert.deepStrictEqual(a, b),
1125    {
1126      code: 'ERR_ASSERTION',
1127      name: 'AssertionError',
1128      message: /a: \[Getter: 5]\n- {3}a: \[Getter: 6]\n {2}/
1129    }
1130  );
1131
1132  // The descriptor is not compared.
1133  assertDeepAndStrictEqual(a, { a: 5 });
1134}
1135
1136// Verify object types being identical on both sides.
1137{
1138  let a = Buffer.from('test');
1139  let b = Object.create(
1140    Object.getPrototypeOf(a),
1141    Object.getOwnPropertyDescriptors(a)
1142  );
1143  Object.defineProperty(b, Symbol.toStringTag, {
1144    value: 'Uint8Array'
1145  });
1146  assertNotDeepOrStrict(a, b);
1147
1148  a = new Uint8Array(10);
1149  b = new Int8Array(10);
1150  Object.defineProperty(b, Symbol.toStringTag, {
1151    value: 'Uint8Array'
1152  });
1153  Object.setPrototypeOf(b, Uint8Array.prototype);
1154  assertNotDeepOrStrict(a, b);
1155
1156  a = [1, 2, 3];
1157  b = { 0: 1, 1: 2, 2: 3 };
1158  Object.setPrototypeOf(b, Array.prototype);
1159  Object.defineProperty(b, 'length', { value: 3, enumerable: false });
1160  Object.defineProperty(b, Symbol.toStringTag, {
1161    value: 'Array'
1162  });
1163  assertNotDeepOrStrict(a, b);
1164
1165  a = new Date(2000);
1166  b = Object.create(
1167    Object.getPrototypeOf(a),
1168    Object.getOwnPropertyDescriptors(a)
1169  );
1170  Object.defineProperty(b, Symbol.toStringTag, {
1171    value: 'Date'
1172  });
1173  assertNotDeepOrStrict(a, b);
1174
1175  a = /abc/g;
1176  b = Object.create(
1177    Object.getPrototypeOf(a),
1178    Object.getOwnPropertyDescriptors(a)
1179  );
1180  Object.defineProperty(b, Symbol.toStringTag, {
1181    value: 'RegExp'
1182  });
1183  assertNotDeepOrStrict(a, b);
1184
1185  a = [];
1186  b = /abc/;
1187  Object.setPrototypeOf(b, Array.prototype);
1188  Object.defineProperty(b, Symbol.toStringTag, {
1189    value: 'Array'
1190  });
1191  assertNotDeepOrStrict(a, b);
1192
1193  a = Object.create(null);
1194  b = new RangeError('abc');
1195  Object.defineProperty(a, Symbol.toStringTag, {
1196    value: 'Error'
1197  });
1198  Object.setPrototypeOf(b, null);
1199  assertNotDeepOrStrict(a, b, assert.AssertionError);
1200}
1201
1202{
1203  // Verify commutativity
1204  // Regression test for https://github.com/nodejs/node/issues/37710
1205  const a = { x: 1 };
1206  const b = { y: 1 };
1207  Object.defineProperty(b, 'x', { value: 1 });
1208
1209  assertNotDeepOrStrict(a, b);
1210}
1211