diff --git a/lib/internal/assert/assertion_error.js b/lib/internal/assert/assertion_error.js index f12243790b0506..7f57bc57956317 100644 --- a/lib/internal/assert/assertion_error.js +++ b/lib/internal/assert/assertion_error.js @@ -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 = ''; @@ -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]; @@ -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() }) @@ -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) { @@ -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; @@ -306,7 +305,6 @@ function createErrDiff(actual, expected, operator) { `${colors.blue}...${colors.white}`; } } - return `${msg}${skipped ? skippedMsg : ''}\n${res}${other}${end}${indicator}`; } @@ -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. diff --git a/test/parallel/test-assert-deep.js b/test/parallel/test-assert-deep.js index f7489993aa60ba..0cdd1c1d92fcae 100644 --- a/test/parallel/test-assert-deep.js +++ b/test/parallel/test-assert-deep.js @@ -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 { diff --git a/test/parallel/test-assert.js b/test/parallel/test-assert.js index 0343e8422885ad..11a345df1bb2f1 100644 --- a/test/parallel/test-assert.js +++ b/test/parallel/test-assert.js @@ -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 } ); @@ -817,8 +817,8 @@ 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( @@ -826,8 +826,8 @@ 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", } ); fs.close = tmp; @@ -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', } ); @@ -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' } ); @@ -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 }