Skip to content

Commit

Permalink
assert: AssertionError difference on custom message
Browse files Browse the repository at this point in the history
Addition of the error difference (actual and expected) when provided a custom message

Fixes: nodejs#48465
  • Loading branch information
AdityaPimpalkar committed Jul 17, 2023
1 parent 6515b3b commit afa3ed7
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 51 deletions.
84 changes: 43 additions & 41 deletions lib/internal/assert/assertion_error.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ function inspectValue(val) {
);
}

function createErrDiff(actual, expected, operator) {
function createErrDiff(actual, expected, operator, message = null) {
let other = '';
let res = '';
let end = '';
Expand All @@ -96,47 +96,48 @@ function createErrDiff(actual, expected, operator) {
(typeof actual === 'function' && typeof expected === 'function'))) {
operator = 'strictEqualObject';
}

// If "actual" and "expected" fit on a single line and they are not strictly
// equal, check further special handling.
if (actualLines.length === 1 && expectedLines.length === 1 &&
actualLines[0] !== expectedLines[0]) {
// Check for the visible length using the `removeColors()` function, if
// appropriate.
const c = inspect.defaultOptions.colors;
const actualRaw = c ? removeColors(actualLines[0]) : actualLines[0];
const expectedRaw = c ? removeColors(expectedLines[0]) : expectedLines[0];
const inputLength = actualRaw.length + expectedRaw.length;
// If the character length of "actual" and "expected" together is less than
// kMaxShortLength and if neither is an object and at least one of them is
// not `zero`, use the strict equal comparison to visualize the output.
if (inputLength <= kMaxShortLength) {
if ((typeof actual !== 'object' || actual === null) &&
(typeof expected !== 'object' || expected === null) &&
(actual !== 0 || expected !== 0)) { // -0 === +0
return `${kReadableOperator[operator]}\n\n` +
`${actualLines[0]} !== ${expectedLines[0]}\n`;
}
} else if (operator !== 'strictEqualObject') {
// If the stderr is a tty and the input length is lower than the current
// columns per line, add a mismatch indicator below the output. If it is
// not a tty, use a default value of 80 characters.
const maxLength = process.stderr.isTTY ? process.stderr.columns : 80;
if (inputLength < maxLength) {
while (actualRaw[i] === expectedRaw[i]) {
i++;
if (message == null) {
if (actualLines.length === 1 && expectedLines.length === 1 &&
actualLines[0] !== expectedLines[0]) {
// Check for the visible length using the `removeColors()` function, if
// appropriate.
const c = inspect.defaultOptions.colors;
const actualRaw = c ? removeColors(actualLines[0]) : actualLines[0];
const expectedRaw = c ? removeColors(expectedLines[0]) : expectedLines[0];
const inputLength = actualRaw.length + expectedRaw.length;
// If the character length of "actual" and "expected" together is less than
// kMaxShortLength and if neither is an object and at least one of them is
// not `zero`, use the strict equal comparison to visualize the output.
if (inputLength <= kMaxShortLength) {
if ((typeof actual !== 'object' || actual === null) &&
(typeof expected !== 'object' || expected === null) &&
(actual !== 0 || expected !== 0)) { // -0 === +0
return `${kReadableOperator[operator]}\n\n` +
`${actualLines[0]} !== ${expectedLines[0]}\n`;
}
// Ignore the first characters.
if (i > 2) {
// Add position indicator for the first mismatch in case it is a
// single line and the input length is less than the column length.
indicator = `\n ${StringPrototypeRepeat(' ', i)}^`;
i = 0;
} else if (operator !== 'strictEqualObject') {
// If the stderr is a tty and the input length is lower than the current
// columns per line, add a mismatch indicator below the output. If it is
// not a tty, use a default value of 80 characters.
const maxLength = process.stderr.isTTY ? process.stderr.columns : 80;
if (inputLength < maxLength) {
while (actualRaw[i] === expectedRaw[i]) {
i++;
}
// Ignore the first characters.
if (i > 2) {
// Add position indicator for the first mismatch in case it is a
// single line and the input length is less than the column length.
indicator = `\n ${StringPrototypeRepeat(' ', i)}^`;
i = 0;
}
}
}
}
}

// Remove all ending lines that match (this optimizes the output for
// readability by reducing the number of total changed lines).
let a = actualLines[actualLines.length - 1];
Expand All @@ -154,7 +155,6 @@ function createErrDiff(actual, expected, operator) {
a = actualLines[actualLines.length - 1];
b = expectedLines[expectedLines.length - 1];
}

const maxLines = MathMax(actualLines.length, expectedLines.length);
// Strict equal with identical objects that are not identical by reference.
// E.g., assert.deepStrictEqual({ a: Symbol() }, { a: Symbol() })
Expand All @@ -174,7 +174,6 @@ function createErrDiff(actual, expected, operator) {
return `${kReadableOperator.notIdentical}\n\n` +
`${ArrayPrototypeJoin(actualLines, '\n')}\n`;
}

// There were at least five identical lines at the end. Mark a couple of
// skipped.
if (i >= 5) {
Expand All @@ -188,8 +187,8 @@ function createErrDiff(actual, expected, operator) {

let printedLines = 0;
let identical = 0;
const msg = kReadableOperator[operator] +
`\n${colors.green}+ actual${colors.white} ${colors.red}- expected${colors.white}`;
const error_msg = message != null ? message : kReadableOperator[operator];
const msg = error_msg +`\n${colors.green}+ actual${colors.white} ${colors.red}- expected${colors.white}`;
const skippedMsg = ` ${colors.blue}...${colors.white} Lines skipped`;

let lines = actualLines;
Expand Down Expand Up @@ -306,7 +305,6 @@ function createErrDiff(actual, expected, operator) {
`${colors.blue}...${colors.white}`;
}
}

return `${msg}${skipped ? skippedMsg : ''}\n${res}${other}${end}${indicator}`;
}

Expand Down Expand Up @@ -341,7 +339,11 @@ class AssertionError extends Error {
if (isErrorStackTraceLimitWritable()) Error.stackTraceLimit = 0;

if (message != null) {
super(String(message));
if (operator === 'deepStrictEqual' || operator === 'strictEqual') {
super(createErrDiff(actual, expected, operator, message));
} else {
super(String(message));
}
} else {
// Reset colors on each call to make sure we handle dynamically set environment
// variables correct.
Expand Down
20 changes: 20 additions & 0 deletions test/parallel/test-assert-deep.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,26 @@ assert.deepEqual(arr, buf);
assert.notDeepEqual(arr, arr2);
}

{
const foo = { foo: 1 };
const bar = { bar: 1 };
assert.throws(
() => assert.deepStrictEqual(foo, bar, 'objects should be equal'),
{
code: 'ERR_ASSERTION',
message:
'objects should be equal\n' +
'+ actual - expected\n' +
'\n' +
' {\n' +
'+ foo: 1\n' +
'- bar: 1\n' +
' }',
}
);
assert.notDeepEqual(foo, bar);
}

const date = new Date('2016');

class MyDate extends Date {
Expand Down
29 changes: 19 additions & 10 deletions test/parallel/test-assert.js
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ assert.throws(
assert.throws(
() => assert.strictEqual(1, 2, 'oh no'),
{
message: 'oh no',
message: 'oh no\n+ actual - expected\n\n+ 1\n- 2',
generatedMessage: false
}
);
Expand Down Expand Up @@ -817,17 +817,17 @@ assert.throws(
{
code: 'ERR_ASSERTION',
constructor: assert.AssertionError,
message: 'The expression evaluated to a falsy value:\n\n ' +
"assert(\n (Buffer.from('test') instanceof Error)\n )\n"
message: 'The expression evaluated to a falsy value:\n\n' +
" assert(\n (Buffer.from('test') instanceof Error)\n )\n",
}
);
assert.throws(
() => throwErr(),
{
code: 'ERR_ASSERTION',
constructor: assert.AssertionError,
message: 'The expression evaluated to a falsy value:\n\n ' +
"assert(\n (Buffer.from('test') instanceof Error)\n )\n"
message: 'The expression evaluated to a falsy value:\n\n' +
" assert(\n (Buffer.from('test') instanceof Error)\n )\n",
}
);
fs.close = tmp;
Expand Down Expand Up @@ -921,8 +921,9 @@ assert.throws(
{
code: 'ERR_ASSERTION',
constructor: assert.AssertionError,
message: 'The expression evaluated to a falsy value:\n\n ' +
'ok(null, undefined)\n'
message:
'The expression evaluated to a falsy value:\n\n ' +
'ok(null, undefined)\n',
}
);

Expand All @@ -932,8 +933,9 @@ assert.throws(
{
code: 'ERR_ASSERTION',
constructor: assert.AssertionError,
message: 'The expression evaluated to a falsy value:\n\n ' +
'assert[\'ok\']["apply"](null, [0])\n'
message:
'The expression evaluated to a falsy value:\n\n ' +
'assert[\'ok\']["apply"](null, [0])\n'
}
);

Expand Down Expand Up @@ -1248,7 +1250,14 @@ assert.throws(
),
{
actual,
message,
message:
'message\n' +
'+ actual - expected\n' +
'\n' +
"+ 'foobar'\n" +
'- {\n' +
"- message: 'foobar'\n" +
'- }',
operator: 'throws',
generatedMessage: false
}
Expand Down

0 comments on commit afa3ed7

Please sign in to comment.