• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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