| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent c18ac52 commit 5aa3a2d
4 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -17,6 +17,9 @@ For more information about the used equality comparisons see | |||
| 17 | 17 | <!-- YAML | |
| 18 | 18 | added: REPLACEME | |
| 19 | 19 | changes: | |
| 20 | + - version: REPLACEME | ||
| 21 | + pr-url: https://github.com/nodejs/node/pull/REPLACEME | ||
| 22 | + description: Added error diffs to the strict mode | ||
| 20 | 23 | - version: REPLACEME | |
| 21 | 24 | pr-url: https://github.com/nodejs/node/pull/17002 | |
| 22 | 25 | description: Added strict mode to the assert module. | |
@@ -26,12 +29,42 @@ When using the `strict mode`, any `assert` function will use the equality used i | |||
| 26 | 29 | the strict function mode. So [`assert.deepEqual()`][] will, for example, work the | |
| 27 | 30 | same as [`assert.deepStrictEqual()`][]. | |
| 28 | 31 | ||
| 32 | + On top of that, error messages which involve objects produce an error diff | ||
| 33 | + instead of displaying both objects. That is not the case for the legacy mode. | ||
| 34 | + | ||
| 29 | 35 | It can be accessed using: | |
| 30 | 36 | ||
| 31 | 37 | ```js | |
| 32 | 38 | const assert = require('assert').strict; | |
| 33 | 39 | ``` | |
| 34 | 40 | ||
| 41 | + Example error diff (the `expected`, `actual`, and `Lines skipped` will be on a | ||
| 42 | + single row): | ||
| 43 | + | ||
| 44 | + ```js | ||
| 45 | + const assert = require('assert').strict; | ||
| 46 | + | ||
| 47 | + assert.deepEqual([[[1, 2, 3]], 4, 5], [[[1, 2, '3']], 4, 5]); | ||
| 48 | + ``` | ||
| 49 | + | ||
| 50 | + ```diff | ||
| 51 | + AssertionError [ERR_ASSERTION]: Input A expected to deepStrictEqual input B: | ||
| 52 | + + expected | ||
| 53 | + - actual | ||
| 54 | + ... Lines skipped | ||
| 55 | + | ||
| 56 | + [ | ||
| 57 | + [ | ||
| 58 | + ... | ||
| 59 | + 2, | ||
| 60 | + - 3 | ||
| 61 | + + '3' | ||
| 62 | + ], | ||
| 63 | + ... | ||
| 64 | + 5 | ||
| 65 | + ] | ||
| 66 | + ``` | ||
| 67 | + | ||
| 35 | 68 | ## Legacy mode | |
| 36 | 69 | ||
| 37 | 70 | > Stability: 0 - Deprecated: Use strict mode instead. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -25,6 +25,10 @@ const { isDeepEqual, isDeepStrictEqual } = | |||
| 25 | 25 | const errors = require('internal/errors'); | |
| 26 | 26 | const { inspect } = require('util'); | |
| 27 | 27 | ||
| 28 | + const ERR_DIFF_DEACTIVATED = 0; | ||
| 29 | + const ERR_DIFF_NOT_EQUAL = 1; | ||
| 30 | + const ERR_DIFF_EQUAL = 2; | ||
| 31 | + | ||
| 28 | 32 | // The assert module provides functions that throw | |
| 29 | 33 | // AssertionError's when particular conditions are not met. The | |
| 30 | 34 | // assert module must conform to the following interface. | |
@@ -154,7 +158,8 @@ assert.deepStrictEqual = function deepStrictEqual(actual, expected, message) { | |||
| 154 | 158 | expected, | |
| 155 | 159 | message, | |
| 156 | 160 | operator: 'deepStrictEqual', | |
| 157 | - stackStartFn: deepStrictEqual | ||
| 161 | + stackStartFn: deepStrictEqual, | ||
| 162 | + errorDiff: this === strict ? ERR_DIFF_EQUAL : ERR_DIFF_DEACTIVATED | ||
| 158 | 163 | }); | |
| 159 | 164 | } | |
| 160 | 165 | }; | |
@@ -167,7 +172,8 @@ function notDeepStrictEqual(actual, expected, message) { | |||
| 167 | 172 | expected, | |
| 168 | 173 | message, | |
| 169 | 174 | operator: 'notDeepStrictEqual', | |
| 170 | - stackStartFn: notDeepStrictEqual | ||
| 175 | + stackStartFn: notDeepStrictEqual, | ||
| 176 | + errorDiff: this === strict ? ERR_DIFF_NOT_EQUAL : ERR_DIFF_DEACTIVATED | ||
| 171 | 177 | }); | |
| 172 | 178 | } | |
| 173 | 179 | } | |
@@ -180,7 +186,8 @@ assert.strictEqual = function strictEqual(actual, expected, message) { | |||
| 180 | 186 | expected, | |
| 181 | 187 | message, | |
| 182 | 188 | operator: '===', | |
| 183 | - stackStartFn: strictEqual | ||
| 189 | + stackStartFn: strictEqual, | ||
| 190 | + errorDiff: this === strict ? ERR_DIFF_EQUAL : ERR_DIFF_DEACTIVATED | ||
| 184 | 191 | }); | |
| 185 | 192 | } | |
| 186 | 193 | }; | |
@@ -194,7 +201,8 @@ assert.notStrictEqual = function notStrictEqual(actual, expected, message) { | |||
| 194 | 201 | expected, | |
| 195 | 202 | message, | |
| 196 | 203 | operator: '!==', | |
| 197 | - stackStartFn: notStrictEqual | ||
| 204 | + stackStartFn: notStrictEqual, | ||
| 205 | + errorDiff: this === strict ? ERR_DIFF_NOT_EQUAL : ERR_DIFF_DEACTIVATED | ||
| 198 | 206 | }); | |
| 199 | 207 | } | |
| 200 | 208 | }; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -78,22 +78,164 @@ function makeNodeError(Base) { | |||
| 78 | 78 | }; | |
| 79 | 79 | } | |
| 80 | 80 | ||
| 81 | + function createErrDiff(actual, expected, operator) { | ||
| 82 | + var other = ''; | ||
| 83 | + var res = ''; | ||
| 84 | + var lastPos = 0; | ||
| 85 | + var end = ''; | ||
| 86 | + var skipped = false; | ||
| 87 | + const util = lazyUtil(); | ||
| 88 | + const actualLines = util | ||
| 89 | + .inspect(actual, { compact: false }).split('\n'); | ||
| 90 | + const expectedLines = util | ||
| 91 | + .inspect(expected, { compact: false }).split('\n'); | ||
| 92 | + const msg = `Input A expected to ${operator} input B:\n` + | ||
| 93 | + '\u001b[32m+ expected\u001b[39m \u001b[31m- actual\u001b[39m'; | ||
| 94 | + const skippedMsg = ' ... Lines skipped'; | ||
| 95 | + | ||
| 96 | + // Remove all ending lines that match (this optimizes the output for | ||
| 97 | + // readability by reducing the number of total changed lines). | ||
| 98 | + var a = actualLines[actualLines.length - 1]; | ||
| 99 | + var b = expectedLines[expectedLines.length - 1]; | ||
| 100 | + var i = 0; | ||
| 101 | + while (a === b) { | ||
| 102 | + if (i++ < 2) { | ||
| 103 | + end = `\n ${a}${end}`; | ||
| 104 | + } else { | ||
| 105 | + other = a; | ||
| 106 | + } | ||
| 107 | + actualLines.pop(); | ||
| 108 | + expectedLines.pop(); | ||
| 109 | + a = actualLines[actualLines.length - 1]; | ||
| 110 | + b = expectedLines[expectedLines.length - 1]; | ||
| 111 | + } | ||
| 112 | + if (i > 3) { | ||
| 113 | + end = `\n...${end}`; | ||
| 114 | + skipped = true; | ||
| 115 | + } | ||
| 116 | + if (other !== '') { | ||
| 117 | + end = `\n ${other}${end}`; | ||
| 118 | + other = ''; | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + const maxLines = Math.max(actualLines.length, expectedLines.length); | ||
| 122 | + var printedLines = 0; | ||
| 123 | + for (i = 0; i < maxLines; i++) { | ||
| 124 | + // Only extra expected lines exist | ||
| 125 | + const cur = i - lastPos; | ||
| 126 | + if (actualLines.length < i + 1) { | ||
| 127 | + if (cur > 1 && i > 2) { | ||
| 128 | + if (cur > 4) { | ||
| 129 | + res += '\n...'; | ||
| 130 | + skipped = true; | ||
| 131 | + } else if (cur > 3) { | ||
| 132 | + res += `\n ${expectedLines[i - 2]}`; | ||
| 133 | + printedLines++; | ||
| 134 | + } | ||
| 135 | + res += `\n ${expectedLines[i - 1]}`; | ||
| 136 | + printedLines++; | ||
| 137 | + } | ||
| 138 | + lastPos = i; | ||
| 139 | + other += `\n\u001b[32m+\u001b[39m ${expectedLines[i]}`; | ||
| 140 | + printedLines++; | ||
| 141 | + // Only extra actual lines exist | ||
| 142 | + } else if (expectedLines.length < i + 1) { | ||
| 143 | + if (cur > 1 && i > 2) { | ||
| 144 | + if (cur > 4) { | ||
| 145 | + res += '\n...'; | ||
| 146 | + skipped = true; | ||
| 147 | + } else if (cur > 3) { | ||
| 148 | + res += `\n ${actualLines[i - 2]}`; | ||
| 149 | + printedLines++; | ||
| 150 | + } | ||
| 151 | + res += `\n ${actualLines[i - 1]}`; | ||
| 152 | + printedLines++; | ||
| 153 | + } | ||
| 154 | + lastPos = i; | ||
| 155 | + res += `\n\u001b[31m-\u001b[39m ${actualLines[i]}`; | ||
| 156 | + printedLines++; | ||
| 157 | + // Lines diverge | ||
| 158 | + } else if (actualLines[i] !== expectedLines[i]) { | ||
| 159 | + if (cur > 1 && i > 2) { | ||
| 160 | + if (cur > 4) { | ||
| 161 | + res += '\n...'; | ||
| 162 | + skipped = true; | ||
| 163 | + } else if (cur > 3) { | ||
| 164 | + res += `\n ${actualLines[i - 2]}`; | ||
| 165 | + printedLines++; | ||
| 166 | + } | ||
| 167 | + res += `\n ${actualLines[i - 1]}`; | ||
| 168 | + printedLines++; | ||
| 169 | + } | ||
| 170 | + lastPos = i; | ||
| 171 | + res += `\n\u001b[31m-\u001b[39m ${actualLines[i]}`; | ||
| 172 | + other += `\n\u001b[32m+\u001b[39m ${expectedLines[i]}`; | ||
| 173 | + printedLines += 2; | ||
| 174 | + // Lines are identical | ||
| 175 | + } else { | ||
| 176 | + res += other; | ||
| 177 | + other = ''; | ||
| 178 | + if (cur === 1 || i === 0) { | ||
| 179 | + res += `\n ${actualLines[i]}`; | ||
| 180 | + printedLines++; | ||
| 181 | + } | ||
| 182 | + } | ||
| 183 | + // Inspected object to big (Show ~20 rows max) | ||
| 184 | + if (printedLines > 20 && i < maxLines - 2) { | ||
| 185 | + return `${msg}${skippedMsg}\n${res}\n...${other}\n...`; | ||
| 186 | + } | ||
| 187 | + } | ||
| 188 | + return `${msg}${skipped ? skippedMsg : ''}\n${res}${other}${end}`; | ||
| 189 | + } | ||
| 190 | + | ||
| 81 | 191 | class AssertionError extends Error { | |
| 82 | 192 | constructor(options) { | |
| 83 | 193 | if (typeof options !== 'object' || options === null) { | |
| 84 | 194 | throw new exports.TypeError('ERR_INVALID_ARG_TYPE', 'options', 'Object'); | |
| 85 | 195 | } | |
| 86 | - var { actual, expected, message, operator, stackStartFn } = options; | ||
| 87 | - if (message) { | ||
| 196 | + var { | ||
| 197 | + actual, | ||
| 198 | + expected, | ||
| 199 | + message, | ||
| 200 | + operator, | ||
| 201 | + stackStartFn, | ||
| 202 | + errorDiff = 0 | ||
| 203 | + } = options; | ||
| 204 | + | ||
| 205 | + if (message != null) { | ||
| 88 | 206 | super(message); | |
| 89 | 207 | } else { | |
| 90 | 208 | const util = lazyUtil(); | |
| 91 | 209 | if (actual && actual.stack && actual instanceof Error) | |
| 92 | 210 | actual = `${actual.name}: ${actual.message}`; | |
| 93 | 211 | if (expected && expected.stack && expected instanceof Error) | |
| 94 | 212 | expected = `${expected.name}: ${expected.message}`; | |
| 95 | - super(`${util.inspect(actual).slice(0, 128)} ` + | ||
| 96 | - `${operator} ${util.inspect(expected).slice(0, 128)}`); | ||
| 213 | + | ||
| 214 | + if (errorDiff === 0) { | ||
| 215 | + let res = util.inspect(actual); | ||
| 216 | + let other = util.inspect(expected); | ||
| 217 | + if (res.length > 128) | ||
| 218 | + res = `${res.slice(0, 125)}...`; | ||
| 219 | + if (other.length > 128) | ||
| 220 | + other = `${other.slice(0, 125)}...`; | ||
| 221 | + super(`${res} ${operator} ${other}`); | ||
| 222 | + } else if (errorDiff === 1) { | ||
| 223 | + // In case the objects are equal but the operator requires unequal, show | ||
| 224 | + // the first object and say A equals B | ||
| 225 | + const res = util | ||
| 226 | + .inspect(actual, { compact: false }).split('\n'); | ||
| 227 | + | ||
| 228 | + if (res.length > 20) { | ||
| 229 | + res[19] = '...'; | ||
| 230 | + while (res.length > 20) { | ||
| 231 | + res.pop(); | ||
| 232 | + } | ||
| 233 | + } | ||
| 234 | + // Only print a single object. | ||
| 235 | + super(`Identical input passed to ${operator}:\n${res.join('\n')}`); | ||
| 236 | + } else { | ||
| 237 | + super(createErrDiff(actual, expected, operator)); | ||
| 238 | + } | ||
| 97 | 239 | } | |
| 98 | 240 | ||
| 99 | 241 | this.generatedMessage = !message; | |
| Back | FazBrowse Home | New Git URL |
0 commit comments