Skip to content

Commit

Permalink
assert: renamed deepEqual/deepStrictEqual to partialDeepEqual/partial…
Browse files Browse the repository at this point in the history
…DeepStrictEqual
  • Loading branch information
puskin94 committed Sep 18, 2024
1 parent bb5a40a commit f980da3
Show file tree
Hide file tree
Showing 5 changed files with 188 additions and 81 deletions.
111 changes: 78 additions & 33 deletions doc/api/assert.md
Original file line number Diff line number Diff line change
Expand Up @@ -2548,7 +2548,7 @@ assert.throws(throwingFirst, /Second$/);
Due to the confusing error-prone notation, avoid a string as the second
argument.

## `assert.deepMatch(actual, expected[, message])`
## `assert.partialDeepEqual(actual, expected[, message])`

<!-- YAML
added: REPLACEME
Expand All @@ -2558,54 +2558,84 @@ added: REPLACEME
* `expected` {any}
* `message` {string|Error}

[`assert.deepMatch()`][] evaluates the equivalence between the `actual` and `expected` parameters by
[`assert.partialDeepEqual()`][] evaluates the equivalence between the `actual` and `expected` parameters by
performing a deep comparison. This function ensures that all properties defined
in the `expected` parameter match those in the `actual` parameter in
both value and type, allowing type coercion. The main difference with [`assert.deepEqual()`][] is that
[`assert.deepMatch()`][] does not require all properties in the `actual` parameter to be present in the
[`assert.partialDeepEqual()`][] does not require all properties in the `actual` parameter to be present in the
`expected` parameter.

```mjs
import assert from 'node:assert';

assert.deepMatch({ a: 1, b: '2' }, { a: 1, b: 2 });
assert.partialDeepEqual({ a: 1, b: '2' }, { a: 1, b: 2 });
// OK

assert.deepMatch({ a: 1, b: '2', c: 3 }, { a: 1, b: 2 });
assert.partialDeepEqual({ a: 1, b: '2', c: 3 }, { a: 1, b: 2 });
// OK

assert.deepMatch({ a: { b: { c: '1' } } }, { a: { b: { c: 1 } } });
assert.partialDeepEqual({ a: { b: { c: '1' } } }, { a: { b: { c: 1 } } });
// OK

assert.deepMatch({ a: 1 }, { a: 1, b: 2 });
assert.partialDeepEqual(new Set(['value1', 'value2']), new Set(['value1', 'value2']));
// OK

assert.partialDeepEqual(new Map([['key1', 'value1']]), new Map([['key1', 'value1']]));
// OK

assert.partialDeepEqual(new Uint8Array([1, 2, 3]), new Uint8Array([1, 2, 3]));
// OK

assert.partialDeepEqual(/abc/, /abc/);
// OK

assert.partialDeepEqual(new Date(0), new Date(0));
// OK

assert.partialDeepEqual({ a: 1 }, { a: 1, b: 2 });
// AssertionError

assert.deepMatch({ a: 1, b: true }, { a: 1, b: 'true' });
assert.partialDeepEqual({ a: 1, b: true }, { a: 1, b: 'true' });
// AssertionError

assert.deepMatch({ a: { b: 2 } }, { a: { b: 2, c: 3 } });
assert.partialDeepEqual({ a: { b: 2 } }, { a: { b: 2, c: 3 } });
// AssertionError
```

```cjs
const assert = require('node:assert');

assert.deepMatch({ a: 1, b: '2' }, { a: 1, b: 2 });
assert.partialDeepEqual({ a: 1, b: '2' }, { a: 1, b: 2 });
// OK

assert.partialDeepEqual({ a: 1, b: '2', c: 3 }, { a: 1, b: 2 });
// OK

assert.partialDeepEqual({ a: { b: { c: '1' } } }, { a: { b: { c: 1 } } });
// OK

assert.partialDeepEqual(new Set(['value1', 'value2']), new Set(['value1', 'value2']));
// OK

assert.partialDeepEqual(new Map([['key1', 'value1']]), new Map([['key1', 'value1']]));
// OK

assert.partialDeepEqual(new Uint8Array([1, 2, 3]), new Uint8Array([1, 2, 3]));
// OK

assert.deepMatch({ a: 1, b: '2', c: 3 }, { a: 1, b: 2 });
assert.partialDeepEqual(/abc/, /abc/);
// OK

assert.deepMatch({ a: { b: { c: '1' } } }, { a: { b: { c: 1 } } });
assert.partialDeepEqual(new Date(0), new Date(0));
// OK

assert.deepMatch({ a: 1 }, { a: 1, b: 2 });
assert.partialDeepEqual({ a: 1 }, { a: 1, b: 2 });
// AssertionError: Expected key b

assert.deepMatch({ a: 1, b: true }, { a: 1, b: 'true' });
assert.partialDeepEqual({ a: 1, b: true }, { a: 1, b: 'true' });
// AssertionError

assert.deepMatch({ a: { b: 2, d: 4 } }, { a: { b: 2, c: 3 } });
assert.partialDeepEqual({ a: { b: 2, d: 4 } }, { a: { b: 2, c: 3 } });
// AssertionError: Expected key c
```

Expand All @@ -2615,7 +2645,7 @@ parameter is undefined, a default error message is assigned. If the `message`
parameter is an instance of an [`Error`][] then it will be thrown instead of the
`AssertionError`.

## `assert.deepMatchStrict(actual, expected[, message])`
## `assert.partialDeepStrictEqual(actual, expected[, message])`

<!-- YAML
added: REPLACEME
Expand All @@ -2625,53 +2655,68 @@ added: REPLACEME
* `expected` {any}
* `message` {string|Error}

[`assert.deepMatchStrict()`][] Assesses the equivalence between the `actual` and `expected` parameters through a
[`assert.partialDeepStrictEqual()`][] Assesses the equivalence between the `actual` and `expected` parameters through a
deep comparison, ensuring that all properties in the `expected` parameter are
present in the `actual` parameter with equivalent values, not allowing type coercion.
The main difference with [`assert.deepStrictEqual()`][] is that [`assert.deepMatchStrict()`][] does not require all
properties in the `actual` parameter to be present in the `expected` parameter.
The main difference with [`assert.deepStrictEqual()`][] is that [`assert.partialDeepStrictEqual()`][] does not require
all properties in the `actual` parameter to be present in the `expected` parameter.

```mjs
import assert from 'node:assert';

assert.deepMatchStrict({ a: 1, b: 2 }, { a: 1, b: 2 });
assert.partialDeepStrictEqual({ a: 1, b: 2 }, { a: 1, b: 2 });
// OK

assert.partialDeepStrictEqual({ a: { b: { c: 1 } } }, { a: { b: { c: 1 } } });
// OK

assert.partialDeepStrictEqual({ a: 1, b: 2, c: 3 }, { a: 1, b: 2 });
// OK

assert.partialDeepStrictEqual(new Set(['value1', 'value2']), new Set(['value1', 'value2']));
// OK

assert.partialDeepStrictEqual(new Map([['key1', 'value1']]), new Map([['key1', 'value1']]));
// OK

assert.partialDeepStrictEqual(new Uint8Array([1, 2, 3]), new Uint8Array([1, 2, 3]));
// OK

assert.deepMatchStrict({ a: { b: { c: 1 } } }, { a: { b: { c: 1 } } });
assert.partialDeepStrictEqual(/abc/, /abc/);
// OK

assert.deepMatchStrict({ a: 1, b: 2, c: 3 }, { a: 1, b: 2 });
assert.partialDeepStrictEqual(new Date(0), new Date(0));
// OK

assert.deepMatchStrict({ a: 1 }, { a: 1, b: 2 });
assert.partialDeepStrictEqual({ a: 1 }, { a: 1, b: 2 });
// AssertionError

assert.deepMatchStrict({ a: 1, b: '2' }, { a: 1, b: 2 });
assert.partialDeepStrictEqual({ a: 1, b: '2' }, { a: 1, b: 2 });
// AssertionError

assert.deepMatchStrict({ a: { b: 2 } }, { a: { b: '2' } });
assert.partialDeepStrictEqual({ a: { b: 2 } }, { a: { b: '2' } });
// AssertionError
```

```cjs
const assert = require('node:assert');

assert.deepMatchStrict({ a: 1, b: 2 }, { a: 1, b: 2 });
assert.partialDeepStrictEqual({ a: 1, b: 2 }, { a: 1, b: 2 });
// OK

assert.deepMatchStrict({ a: { b: { c: 1 } } }, { a: { b: { c: 1 } } });
assert.partialDeepStrictEqual({ a: { b: { c: 1 } } }, { a: { b: { c: 1 } } });
// OK

assert.deepMatchStrict({ a: 1, b: 2, c: 3 }, { a: 1, b: 2 });
assert.partialDeepStrictEqual({ a: 1, b: 2, c: 3 }, { a: 1, b: 2 });
// OK

assert.deepMatchStrict({ a: 1 }, { a: 1, b: 2 });
assert.partialDeepStrictEqual({ a: 1 }, { a: 1, b: 2 });
// AssertionError

assert.deepMatchStrict({ a: 1, b: '2' }, { a: 1, b: 2 });
assert.partialDeepStrictEqual({ a: 1, b: '2' }, { a: 1, b: 2 });
// AssertionError

assert.deepMatchStrict({ a: { b: 2 } }, { a: { b: '2' } });
assert.partialDeepStrictEqual({ a: { b: 2 } }, { a: { b: '2' } });
// AssertionError
```

Expand Down Expand Up @@ -2803,8 +2848,6 @@ parameter is an instance of an [`Error`][] then it will be thrown instead of the
[`WeakMap`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap
[`WeakSet`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet
[`assert.deepEqual()`]: #assertdeepequalactual-expected-message
[`assert.deepMatch()`]: #assertdeepmatchactual-expected-message
[`assert.deepMatchStrict()`]: #assertdeepmatchstrictactual-expected-message
[`assert.deepStrictEqual()`]: #assertdeepstrictequalactual-expected-message
[`assert.doesNotThrow()`]: #assertdoesnotthrowfn-error-message
[`assert.equal()`]: #assertequalactual-expected-message
Expand All @@ -2815,6 +2858,8 @@ parameter is an instance of an [`Error`][] then it will be thrown instead of the
[`assert.notEqual()`]: #assertnotequalactual-expected-message
[`assert.notStrictEqual()`]: #assertnotstrictequalactual-expected-message
[`assert.ok()`]: #assertokvalue-message
[`assert.partialDeepEqual()`]: #assertpartialdeepequalactual-expected-message
[`assert.partialDeepStrictEqual()`]: #assertpartialdeepstrictequalactual-expected-message
[`assert.strictEqual()`]: #assertstrictequalactual-expected-message
[`assert.throws()`]: #assertthrowsfn-error-message
[`getColorDepth()`]: tty.md#writestreamgetcolordepthenv
Expand Down
55 changes: 38 additions & 17 deletions lib/assert.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@
'use strict';

const {
ArrayFrom,
ArrayIsArray,
ArrayPrototypeEvery,
ArrayPrototypeIndexOf,
ArrayPrototypeJoin,
ArrayPrototypePush,
ArrayPrototypeSlice,
ArrayPrototypeSome,
Error,
FunctionPrototypeCall,
MapPrototypeGet,
Expand All @@ -42,9 +45,11 @@ const {
ReflectHas,
ReflectOwnKeys,
RegExpPrototypeExec,
SafeMap,
SafeSet,
SetPrototypeHas,
String,
StringPrototypeIncludes,
StringPrototypeIndexOf,
StringPrototypeSlice,
StringPrototypeSplit,
Expand All @@ -59,6 +64,7 @@ const {
ERR_INVALID_RETURN_VALUE,
ERR_MISSING_ARGS,
},
hideStackFrames,
} = require('internal/errors');
const AssertionError = require('internal/assert/assertion_error');
const { inspect } = require('internal/util/inspect');
Expand Down Expand Up @@ -369,7 +375,7 @@ function assertIncludes(actual, expected, loose) {
return StringPrototypeIncludes(actual, expected);
}

return expected.every((item) => actual.some((actualItem) => {
return ArrayPrototypeEvery(expected, (item) => ArrayPrototypeSome(actual, (actualItem) => {
// eslint-disable-next-line eqeqeq
return loose ? actualItem == item : actualItem === item;
}));
Expand Down Expand Up @@ -401,7 +407,11 @@ function compareBranch(
return false;
}
const safeIterator = FunctionPrototypeCall(SafeMap.prototype[SymbolIterator], actual);
for (const { 0: key, 1: val } of safeIterator) {
const safeIteratorArray = ArrayFrom(safeIterator);

for (let i = 0; i < safeIteratorArray.length; i++) {
const { 0: key, 1: val } = safeIteratorArray[i];

if (!MapPrototypeHas(expected, key)) {
return false;
}
Expand All @@ -418,8 +428,10 @@ function compareBranch(
return false;
}
const safeIterator = FunctionPrototypeCall(SafeSet.prototype[SymbolIterator], actual);
for (const item of safeIterator) {
if (!SetPrototypeHas(expected, item)) {
const safeIteratorArray = ArrayFrom(safeIterator);

for (let i = 0; i < safeIteratorArray.length; i++) {
if (!SetPrototypeHas(expected, safeIteratorArray[i])) {
return false;
}
}
Expand All @@ -439,16 +451,11 @@ function compareBranch(
return loose ? isDeepEqual(actual, expected) : isDeepStrictEqual(actual, expected);
}

// Check if actual and expected are null or not objects
if (actual == null || expected == null) {
return false;
}

// Use Reflect.ownKeys() instead of Object.keys() to include symbol properties
const keysExpected = ReflectOwnKeys(expected);

// Handle circular references
if (comparedObjects.has(actual)) {
if (SetPrototypeHas(comparedObjects, actual)) {
return true;
}
comparedObjects.add(actual);
Expand All @@ -475,7 +482,7 @@ function compareBranch(
* @param {string | Error} [message]
* @returns {void}
*/
assert.deepMatchStrict = function deepMatchStrict(
assert.partialDeepStrictEqual = function partialDeepStrictEqual(
actual,
expected,
message,
Expand All @@ -489,8 +496,8 @@ assert.deepMatchStrict = function deepMatchStrict(
actual,
expected,
message,
operator: 'deepMatchStrict',
stackStartFn: deepMatchStrict,
operator: 'partialDeepStrictEqual',
stackStartFn: partialDeepStrictEqual,
});
}
};
Expand All @@ -502,7 +509,7 @@ assert.deepMatchStrict = function deepMatchStrict(
* @param {string | Error} [message]
* @returns {void}
*/
assert.deepMatch = function deepMatch(actual, expected, message) {
assert.partialDeepEqual = function partialDeepEqual(actual, expected, message) {
if (arguments.length < 2) {
throw new ERR_MISSING_ARGS('actual', 'expected');
}
Expand All @@ -512,12 +519,22 @@ assert.deepMatch = function deepMatch(actual, expected, message) {
actual,
expected,
message,
operator: 'deepMatch',
stackStartFn: deepMatch,
operator: 'partialDeepEqual',
stackStartFn: partialDeepEqual,
});
}
};

const throwIfInvalidIncludesParams = hideStackFrames((actual, expected) => {
if (typeof actual !== 'string' && !ArrayIsArray(actual)) {
throw new ERR_INVALID_ARG_TYPE('actual', ['string', 'Array'], actual);
}

if (typeof expected !== 'string' && !ArrayIsArray(expected)) {
throw new ERR_INVALID_ARG_TYPE('expected', ['string', 'Array'], expected);
}
});

/**
* The inclusion assertion test between two arrays or strings
* @param {Array | string} actual
Expand All @@ -530,6 +547,8 @@ assert.includes = function includes(actual, expected, message) {
throw new ERR_MISSING_ARGS('actual', 'expected');
}

throwIfInvalidIncludesParams(actual, expected);

if (!assertIncludes(actual, expected, true)) {
innerFail({
actual,
Expand All @@ -554,6 +573,8 @@ assert.includesStrict = function includesStrict(actual, expected, message) {
throw new ERR_MISSING_ARGS('actual', 'expected');
}

throwIfInvalidIncludesParams(actual, expected);

if (!assertIncludes(actual, expected, false)) {
innerFail({
actual,
Expand Down Expand Up @@ -1031,7 +1052,7 @@ assert.strict = ObjectAssign(strict, assert, {
deepEqual: assert.deepStrictEqual,
notEqual: assert.notStrictEqual,
notDeepEqual: assert.notDeepStrictEqual,
deepMatch: assert.deepMatchStrict,
partialDeepEqual: assert.partialDeepStrictEqual,
includes: assert.includesStrict,
});

Expand Down
Loading

0 comments on commit f980da3

Please sign in to comment.