Skip to content

Commit

Permalink
assert: show diff when doing partial comparisons
Browse files Browse the repository at this point in the history
  • Loading branch information
puskin94 committed Dec 11, 2024
1 parent 80c0055 commit 3db0829
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 10 deletions.
7 changes: 3 additions & 4 deletions lib/assert.js
Original file line number Diff line number Diff line change
Expand Up @@ -604,10 +604,9 @@ function compareBranch(
// Check if all expected keys and values match
for (let i = 0; i < keysExpected.length; i++) {
const key = keysExpected[i];
assert(
ReflectHas(actual, key),
new AssertionError({ message: `Expected key ${String(key)} not found in actual object` }),
);
if (!ReflectHas(actual, key)) {
return false;
}
if (!compareBranch(actual[key], expected[key], comparedObjects)) {
return false;
}
Expand Down
13 changes: 10 additions & 3 deletions lib/internal/assert/assertion_error.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const { myersDiff, printMyersDiff, printSimpleMyersDiff } = require('internal/as

const kReadableOperator = {
deepStrictEqual: 'Expected values to be strictly deep-equal:',
partialDeepStrictEqual: 'Expected values to be partially and strictly deep-equal:',
strictEqual: 'Expected values to be strictly equal:',
strictEqualObject: 'Expected "actual" to be reference-equal to "expected":',
deepEqual: 'Expected values to be loosely deep-equal:',
Expand All @@ -41,6 +42,8 @@ const kReadableOperator = {
const kMaxShortStringLength = 12;
const kMaxLongStringLength = 512;

const kMethodsWithCustomMessageDiff = ['deepStrictEqual', 'strictEqual', 'partialDeepStrictEqual'];

function copyError(source) {
const target = ObjectAssign(
{ __proto__: ObjectGetPrototypeOf(source) },
Expand Down Expand Up @@ -210,9 +213,13 @@ function createErrDiff(actual, expected, operator, customMessage) {
const checkCommaDisparity = actual != null && typeof actual === 'object';
const diff = myersDiff(inspectedSplitActual, inspectedSplitExpected, checkCommaDisparity);

const myersDiffMessage = printMyersDiff(diff);
const myersDiffMessage = printMyersDiff(diff, operator);
message = myersDiffMessage.message;

if (operator === 'partialDeepStrictEqual') {
header = `${colors.gray}${colors.hasColors ? '' : '+ '}actual${colors.white} ${colors.red}- expected${colors.white}`;
}

if (myersDiffMessage.skipped) {
skipped = true;
}
Expand Down Expand Up @@ -255,7 +262,7 @@ class AssertionError extends Error {
if (isErrorStackTraceLimitWritable()) Error.stackTraceLimit = 0;

if (message != null) {
if (operator === 'deepStrictEqual' || operator === 'strictEqual') {
if (kMethodsWithCustomMessageDiff.includes(operator)) {
super(createErrDiff(actual, expected, operator, message));
} else {
super(String(message));
Expand All @@ -275,7 +282,7 @@ class AssertionError extends Error {
expected = copyError(expected);
}

if (operator === 'deepStrictEqual' || operator === 'strictEqual') {
if (kMethodsWithCustomMessageDiff.includes(operator)) {
super(createErrDiff(actual, expected, operator, message));
} else if (operator === 'notDeepStrictEqual' ||
operator === 'notStrictEqual') {
Expand Down
8 changes: 6 additions & 2 deletions lib/internal/assert/myers_diff.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ function printSimpleMyersDiff(diff) {
return `\n${message}`;
}

function printMyersDiff(diff, simple = false) {
function printMyersDiff(diff, operator) {
let message = '';
let skipped = false;
let nopCount = 0;
Expand All @@ -148,7 +148,11 @@ function printMyersDiff(diff, simple = false) {
}

if (type === 'insert') {
message += `${colors.green}+${colors.white} ${value}\n`;
if (operator === 'partialDeepStrictEqual') {
message += `${colors.gray}${colors.hasColors ? ' ' : '+'} ${value}${colors.white}\n`;
} else {
message += `${colors.green}+${colors.white} ${value}\n`;
}
} else if (type === 'delete') {
message += `${colors.red}-${colors.white} ${value}\n`;
} else if (type === 'nop') {
Expand Down
11 changes: 11 additions & 0 deletions test/parallel/test-assert.js
Original file line number Diff line number Diff line change
Expand Up @@ -1351,6 +1351,17 @@ test('Additional assert', () => {
}
);

assert.throws(
() => {
assert.partialDeepStrictEqual({ a: true }, { a: false }, 'custom message');
},
{
code: 'ERR_ASSERTION',
name: 'AssertionError',
message: 'custom message\n+ actual - expected\n\n {\n+ a: true\n- a: false\n }\n'
}
);

{
let threw = false;
try {
Expand Down
64 changes: 63 additions & 1 deletion test/pseudo-tty/test-assert-colors.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@
require('../common');
const assert = require('assert').strict;

assert.throws(() => {
function setup() {
process.env.FORCE_COLOR = '1';
delete process.env.NODE_DISABLE_COLORS;
delete process.env.NO_COLOR;
}

assert.throws(() => {
setup();
assert.deepStrictEqual([1, 2, 2, 2, 2], [2, 2, 2, 2, 2]);
}, (err) => {
const expected = 'Expected values to be strictly deep-equal:\n' +
Expand All @@ -19,6 +23,64 @@ assert.throws(() => {
'\x1B[39m 2,\n' +
'\x1B[31m-\x1B[39m 2\n' +
'\x1B[39m ]\n';

assert.strictEqual(err.message, expected);
return true;
});

{
// TODO(puskin94): remove the emitWarning override once the partialDeepStrictEqual method is not experimental anymore
// Suppress warnings, necessary otherwise the tools/pseudo-tty.py runner will fail
const originalEmitWarning = process.emitWarning;
process.emitWarning = () => {};

assert.throws(() => {
setup();
assert.partialDeepStrictEqual([1, 2, 3, 5], [4, 5]);
}, (err) => {
const expected = 'Expected values to be partially and strictly deep-equal:\n' +
'\x1B[90mactual\x1B[39m \x1B[31m- expected\x1B[39m\n' +
'\n' +
'\x1B[39m [\n' +
'\x1B[90m 1,\x1B[39m\n' +
'\x1B[90m 2,\x1B[39m\n' +
'\x1B[90m 3,\x1B[39m\n' +
'\x1B[31m-\x1B[39m 4,\n' +
'\x1B[39m 5\n' +
'\x1B[39m ]\n';

assert.strictEqual(err.message, expected);
return true;
});

process.emitWarning = originalEmitWarning; // Restore original process.emitWarning
}

{
// TODO(puskin94): remove the emitWarning override once the partialDeepStrictEqual method is not experimental anymore
// Suppress warnings, necessary otherwise the tools/pseudo-tty.py runner will fail
const originalEmitWarning = process.emitWarning;
process.emitWarning = () => {};

assert.throws(() => {
setup();
assert.partialDeepStrictEqual({ a: 1, b: 2, c: 3, d: 5 }, { z: 4, b: 5 });
}, (err) => {
const expected = 'Expected values to be partially and strictly deep-equal:\n' +
'\x1B[90mactual\x1B[39m \x1B[31m- expected\x1B[39m\n' +
'\n' +
'\x1B[39m {\n' +
'\x1B[90m a: 1,\x1B[39m\n' +
'\x1B[90m b: 2,\x1B[39m\n' +
'\x1B[90m c: 3,\x1B[39m\n' +
'\x1B[90m d: 5\x1B[39m\n' +
'\x1B[31m-\x1B[39m b: 5,\n' +
'\x1B[31m-\x1B[39m z: 4\n' +
'\x1B[39m }\n';

assert.strictEqual(err.message, expected);
return true;
});

process.emitWarning = originalEmitWarning; // Restore original process.emitWarning
}
23 changes: 23 additions & 0 deletions test/pseudo-tty/test-assert-no-color.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,26 @@ assert.throws(
'- foo: \'bar\'\n' +
'- }\n',
});

{
// TODO(puskin94): remove the emitWarning override once the partialDeepStrictEqual method is not experimental anymore
// Suppress warnings, necessary otherwise the tools/pseudo-tty.py runner will fail
const originalEmitWarning = process.emitWarning;
process.emitWarning = () => {};

assert.throws(
() => {
assert.partialDeepStrictEqual({}, { foo: 'bar' });
},
{
message: 'Expected values to be partially and strictly deep-equal:\n' +
'+ actual - expected\n' +
'\n' +
'+ {}\n' +
'- {\n' +
"- foo: 'bar'\n" +
'- }\n',
});

process.emitWarning = originalEmitWarning; // Restore original process.emitWarning
}

0 comments on commit 3db0829

Please sign in to comment.