diff options
Diffstat (limited to 'lib/internal/errors.js')
-rw-r--r-- | lib/internal/errors.js | 150 |
1 files changed, 146 insertions, 4 deletions
diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 6e6852f380..6bbb5d3548 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -132,22 +132,164 @@ class SystemError extends makeNodeError(Error) { } } +function createErrDiff(actual, expected, operator) { + var other = ''; + var res = ''; + var lastPos = 0; + var end = ''; + var skipped = false; + const actualLines = util + .inspect(actual, { compact: false }).split('\n'); + const expectedLines = util + .inspect(expected, { compact: false }).split('\n'); + const msg = `Input A expected to ${operator} input B:\n` + + '\u001b[32m+ expected\u001b[39m \u001b[31m- actual\u001b[39m'; + const skippedMsg = ' ... Lines skipped'; + + // Remove all ending lines that match (this optimizes the output for + // readability by reducing the number of total changed lines). + var a = actualLines[actualLines.length - 1]; + var b = expectedLines[expectedLines.length - 1]; + var i = 0; + while (a === b) { + if (i++ < 2) { + end = `\n ${a}${end}`; + } else { + other = a; + } + actualLines.pop(); + expectedLines.pop(); + a = actualLines[actualLines.length - 1]; + b = expectedLines[expectedLines.length - 1]; + } + if (i > 3) { + end = `\n...${end}`; + skipped = true; + } + if (other !== '') { + end = `\n ${other}${end}`; + other = ''; + } + + const maxLines = Math.max(actualLines.length, expectedLines.length); + var printedLines = 0; + for (i = 0; i < maxLines; i++) { + // Only extra expected lines exist + const cur = i - lastPos; + if (actualLines.length < i + 1) { + if (cur > 1 && i > 2) { + if (cur > 4) { + res += '\n...'; + skipped = true; + } else if (cur > 3) { + res += `\n ${expectedLines[i - 2]}`; + printedLines++; + } + res += `\n ${expectedLines[i - 1]}`; + printedLines++; + } + lastPos = i; + other += `\n\u001b[32m+\u001b[39m ${expectedLines[i]}`; + printedLines++; + // Only extra actual lines exist + } else if (expectedLines.length < i + 1) { + if (cur > 1 && i > 2) { + if (cur > 4) { + res += '\n...'; + skipped = true; + } else if (cur > 3) { + res += `\n ${actualLines[i - 2]}`; + printedLines++; + } + res += `\n ${actualLines[i - 1]}`; + printedLines++; + } + lastPos = i; + res += `\n\u001b[31m-\u001b[39m ${actualLines[i]}`; + printedLines++; + // Lines diverge + } else if (actualLines[i] !== expectedLines[i]) { + if (cur > 1 && i > 2) { + if (cur > 4) { + res += '\n...'; + skipped = true; + } else if (cur > 3) { + res += `\n ${actualLines[i - 2]}`; + printedLines++; + } + res += `\n ${actualLines[i - 1]}`; + printedLines++; + } + lastPos = i; + res += `\n\u001b[31m-\u001b[39m ${actualLines[i]}`; + other += `\n\u001b[32m+\u001b[39m ${expectedLines[i]}`; + printedLines += 2; + // Lines are identical + } else { + res += other; + other = ''; + if (cur === 1 || i === 0) { + res += `\n ${actualLines[i]}`; + printedLines++; + } + } + // Inspected object to big (Show ~20 rows max) + if (printedLines > 20 && i < maxLines - 2) { + return `${msg}${skippedMsg}\n${res}\n...${other}\n...`; + } + } + return `${msg}${skipped ? skippedMsg : ''}\n${res}${other}${end}`; +} + class AssertionError extends Error { constructor(options) { if (typeof options !== 'object' || options === null) { throw new exports.TypeError('ERR_INVALID_ARG_TYPE', 'options', 'Object'); } - var { actual, expected, message, operator, stackStartFn } = options; + var { + actual, + expected, + message, + operator, + stackStartFn, + errorDiff = 0 + } = options; + if (message != null) { super(message); } else { + if (util === null) util = require('util'); + if (actual && actual.stack && actual instanceof Error) actual = `${actual.name}: ${actual.message}`; if (expected && expected.stack && expected instanceof Error) expected = `${expected.name}: ${expected.message}`; - if (util === null) util = require('util'); - super(`${util.inspect(actual).slice(0, 128)} ` + - `${operator} ${util.inspect(expected).slice(0, 128)}`); + + if (errorDiff === 0) { + let res = util.inspect(actual); + let other = util.inspect(expected); + if (res.length > 128) + res = `${res.slice(0, 125)}...`; + if (other.length > 128) + other = `${other.slice(0, 125)}...`; + super(`${res} ${operator} ${other}`); + } else if (errorDiff === 1) { + // In case the objects are equal but the operator requires unequal, show + // the first object and say A equals B + const res = util + .inspect(actual, { compact: false }).split('\n'); + + if (res.length > 20) { + res[19] = '...'; + while (res.length > 20) { + res.pop(); + } + } + // Only print a single object. + super(`Identical input passed to ${operator}:\n${res.join('\n')}`); + } else { + super(createErrDiff(actual, expected, operator)); + } } this.generatedMessage = !message; |