| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 80d9d56 commit 64b0867
3 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -10,17 +10,17 @@ const { | |||
| 10 | 10 | Error, | |
| 11 | 11 | NumberIsNaN, | |
| 12 | 12 | NumberPrototypeValueOf, | |
| 13 | - ObjectGetOwnPropertySymbols, | ||
| 13 | + ObjectGetOwnPropertySymbols: getOwnSymbols, | ||
| 14 | 14 | ObjectGetPrototypeOf, | |
| 15 | 15 | ObjectIs, | |
| 16 | 16 | ObjectKeys, | |
| 17 | - ObjectPrototypeHasOwnProperty, | ||
| 18 | - ObjectPrototypePropertyIsEnumerable, | ||
| 17 | + ObjectPrototypeHasOwnProperty: hasOwn, | ||
| 18 | + ObjectPrototypePropertyIsEnumerable: hasEnumerable, | ||
| 19 | 19 | ObjectPrototypeToString, | |
| 20 | 20 | SafeSet, | |
| 21 | 21 | StringPrototypeValueOf, | |
| 22 | 22 | SymbolPrototypeValueOf, | |
| 23 | - TypedArrayPrototypeGetByteLength, | ||
| 23 | + TypedArrayPrototypeGetByteLength: getByteLength, | ||
| 24 | 24 | TypedArrayPrototypeGetSymbolToStringTag, | |
| 25 | 25 | Uint8Array, | |
| 26 | 26 | } = primordials; | |
@@ -78,8 +78,8 @@ function areSimilarRegExps(a, b) { | |||
| 78 | 78 | } | |
| 79 | 79 | ||
| 80 | 80 | function isPartialUint8Array(a, b) { | |
| 81 | - const lenA = TypedArrayPrototypeGetByteLength(a); | ||
| 82 | - const lenB = TypedArrayPrototypeGetByteLength(b); | ||
| 81 | + const lenA = getByteLength(a); | ||
| 82 | + const lenB = getByteLength(b); | ||
| 83 | 83 | if (lenA < lenB) { | |
| 84 | 84 | return false; | |
| 85 | 85 | } | |
@@ -107,10 +107,10 @@ function isPartialArrayBufferView(a, b) { | |||
| 107 | 107 | } | |
| 108 | 108 | ||
| 109 | 109 | function areSimilarFloatArrays(a, b) { | |
| 110 | - if (a.byteLength !== b.byteLength) { | ||
| 110 | + const len = getByteLength(a); | ||
| 111 | + if (len !== getByteLength(b)) { | ||
| 111 | 112 | return false; | |
| 112 | 113 | } | |
| 113 | - const len = TypedArrayPrototypeGetByteLength(a); | ||
| 114 | 114 | for (let offset = 0; offset < len; offset++) { | |
| 115 | 115 | if (a[offset] !== b[offset]) { | |
| 116 | 116 | return false; | |
@@ -158,6 +158,12 @@ function isEqualBoxedPrimitive(val1, val2) { | |||
| 158 | 158 | assert.fail(`Unknown boxed type ${val1}`); | |
| 159 | 159 | } | |
| 160 | 160 | ||
| 161 | + function isEnumerableOrIdentical(val1, val2, prop, mode, memos, method) { | ||
| 162 | + return hasEnumerable(val2, prop) || // This is handled by Object.keys() | ||
| 163 | + (mode === kPartial && (val2[prop] === undefined || (prop === 'message' && val2[prop] === ''))) || | ||
| 164 | + innerDeepEqual(val1[prop], val2[prop], mode, memos); | ||
| 165 | + } | ||
| 166 | + | ||
| 161 | 167 | // Notes: Type tags are historical [[Class]] properties that can be set by | |
| 162 | 168 | // FunctionTemplate::SetClassName() in C++ or Symbol.toStringTag in JS | |
| 163 | 169 | // and retrieved using Object.prototype.toString.call(obj) in JS | |
@@ -263,8 +269,7 @@ function innerDeepEqual(val1, val2, mode, memos) { | |||
| 263 | 269 | (val1.size !== val2.size && (mode !== kPartial || val1.size < val2.size))) { | |
| 264 | 270 | return false; | |
| 265 | 271 | } | |
| 266 | - const result = keyCheck(val1, val2, mode, memos, kIsSet); | ||
| 267 | - return result; | ||
| 272 | + return keyCheck(val1, val2, mode, memos, kIsSet); | ||
| 268 | 273 | } else if (isMap(val1)) { | |
| 269 | 274 | if (!isMap(val2) || | |
| 270 | 275 | (val1.size !== val2.size && (mode !== kPartial || val1.size < val2.size))) { | |
@@ -285,26 +290,15 @@ function innerDeepEqual(val1, val2, mode, memos) { | |||
| 285 | 290 | } else if (isError(val1)) { | |
| 286 | 291 | // Do not compare the stack as it might differ even though the error itself | |
| 287 | 292 | // is otherwise identical. | |
| 288 | - if (!isError(val2)) { | ||
| 293 | + if (!isError(val2) || | ||
| 294 | + !isEnumerableOrIdentical(val1, val2, 'message', mode, memos) || | ||
| 295 | + !isEnumerableOrIdentical(val1, val2, 'name', mode, memos) || | ||
| 296 | + !isEnumerableOrIdentical(val1, val2, 'cause', mode, memos) || | ||
| 297 | + !isEnumerableOrIdentical(val1, val2, 'errors', mode, memos)) { | ||
| 289 | 298 | return false; | |
| 290 | 299 | } | |
| 291 | - | ||
| 292 | - const message1Enumerable = ObjectPrototypePropertyIsEnumerable(val1, 'message'); | ||
| 293 | - const name1Enumerable = ObjectPrototypePropertyIsEnumerable(val1, 'name'); | ||
| 294 | - // TODO(BridgeAR): Adjust cause and errors properties for partial mode. | ||
| 295 | - const cause1Enumerable = ObjectPrototypePropertyIsEnumerable(val1, 'cause'); | ||
| 296 | - const errors1Enumerable = ObjectPrototypePropertyIsEnumerable(val1, 'errors'); | ||
| 297 | - | ||
| 298 | - if ((message1Enumerable !== ObjectPrototypePropertyIsEnumerable(val2, 'message') || | ||
| 299 | - (!message1Enumerable && val1.message !== val2.message)) || | ||
| 300 | - (name1Enumerable !== ObjectPrototypePropertyIsEnumerable(val2, 'name') || | ||
| 301 | - (!name1Enumerable && val1.name !== val2.name)) || | ||
| 302 | - (cause1Enumerable !== ObjectPrototypePropertyIsEnumerable(val2, 'cause') || | ||
| 303 | - (!cause1Enumerable && ( | ||
| 304 | - ObjectPrototypeHasOwnProperty(val1, 'cause') !== ObjectPrototypeHasOwnProperty(val2, 'cause') || | ||
| 305 | - !innerDeepEqual(val1.cause, val2.cause, mode, memos)))) || | ||
| 306 | - (errors1Enumerable !== ObjectPrototypePropertyIsEnumerable(val2, 'errors') || | ||
| 307 | - (!errors1Enumerable && !innerDeepEqual(val1.errors, val2.errors, mode, memos)))) { | ||
| 300 | + const hasOwnVal2Cause = hasOwn(val2, 'cause'); | ||
| 301 | + if ((hasOwnVal2Cause !== hasOwn(val1, 'cause') && (mode !== kPartial || hasOwnVal2Cause))) { | ||
| 308 | 302 | return false; | |
| 309 | 303 | } | |
| 310 | 304 | } else if (isBoxedPrimitive(val1)) { | |
@@ -348,10 +342,7 @@ function innerDeepEqual(val1, val2, mode, memos) { | |||
| 348 | 342 | } | |
| 349 | 343 | ||
| 350 | 344 | function getEnumerables(val, keys) { | |
| 351 | - return ArrayPrototypeFilter( | ||
| 352 | - keys, | ||
| 353 | - (k) => ObjectPrototypePropertyIsEnumerable(val, k), | ||
| 354 | - ); | ||
| 345 | + return ArrayPrototypeFilter(keys, (key) => hasEnumerable(val, key)); | ||
| 355 | 346 | } | |
| 356 | 347 | ||
| 357 | 348 | function keyCheck(val1, val2, mode, memos, iterationType, keys2) { | |
@@ -371,7 +362,7 @@ function keyCheck(val1, val2, mode, memos, iterationType, keys2) { | |||
| 371 | 362 | // Cheap key test | |
| 372 | 363 | if (keys2.length > 0) { | |
| 373 | 364 | for (const key of keys2) { | |
| 374 | - if (!ObjectPrototypePropertyIsEnumerable(val1, key)) { | ||
| 365 | + if (!hasEnumerable(val1, key)) { | ||
| 375 | 366 | return false; | |
| 376 | 367 | } | |
| 377 | 368 | } | |
@@ -380,11 +371,11 @@ function keyCheck(val1, val2, mode, memos, iterationType, keys2) { | |||
| 380 | 371 | if (!isArrayLikeObject) { | |
| 381 | 372 | // The pair must have the same number of owned properties. | |
| 382 | 373 | if (mode === kPartial) { | |
| 383 | - const symbolKeys = ObjectGetOwnPropertySymbols(val2); | ||
| 374 | + const symbolKeys = getOwnSymbols(val2); | ||
| 384 | 375 | if (symbolKeys.length !== 0) { | |
| 385 | 376 | for (const key of symbolKeys) { | |
| 386 | - if (ObjectPrototypePropertyIsEnumerable(val2, key)) { | ||
| 387 | - if (!ObjectPrototypePropertyIsEnumerable(val1, key)) { | ||
| 377 | + if (hasEnumerable(val2, key)) { | ||
| 378 | + if (!hasEnumerable(val1, key)) { | ||
| 388 | 379 | return false; | |
| 389 | 380 | } | |
| 390 | 381 | ArrayPrototypePush(keys2, key); | |
@@ -396,27 +387,27 @@ function keyCheck(val1, val2, mode, memos, iterationType, keys2) { | |||
| 396 | 387 | } | |
| 397 | 388 | ||
| 398 | 389 | if (mode === kStrict) { | |
| 399 | - const symbolKeysA = ObjectGetOwnPropertySymbols(val1); | ||
| 390 | + const symbolKeysA = getOwnSymbols(val1); | ||
| 400 | 391 | if (symbolKeysA.length !== 0) { | |
| 401 | 392 | let count = 0; | |
| 402 | 393 | for (const key of symbolKeysA) { | |
| 403 | - if (ObjectPrototypePropertyIsEnumerable(val1, key)) { | ||
| 404 | - if (!ObjectPrototypePropertyIsEnumerable(val2, key)) { | ||
| 394 | + if (hasEnumerable(val1, key)) { | ||
| 395 | + if (!hasEnumerable(val2, key)) { | ||
| 405 | 396 | return false; | |
| 406 | 397 | } | |
| 407 | 398 | ArrayPrototypePush(keys2, key); | |
| 408 | 399 | count++; | |
| 409 | - } else if (ObjectPrototypePropertyIsEnumerable(val2, key)) { | ||
| 400 | + } else if (hasEnumerable(val2, key)) { | ||
| 410 | 401 | return false; | |
| 411 | 402 | } | |
| 412 | 403 | } | |
| 413 | - const symbolKeysB = ObjectGetOwnPropertySymbols(val2); | ||
| 404 | + const symbolKeysB = getOwnSymbols(val2); | ||
| 414 | 405 | if (symbolKeysA.length !== symbolKeysB.length && | |
| 415 | 406 | getEnumerables(val2, symbolKeysB).length !== count) { | |
| 416 | 407 | return false; | |
| 417 | 408 | } | |
| 418 | 409 | } else { | |
| 419 | - const symbolKeysB = ObjectGetOwnPropertySymbols(val2); | ||
| 410 | + const symbolKeysB = getOwnSymbols(val2); | ||
| 420 | 411 | if (symbolKeysB.length !== 0 && | |
| 421 | 412 | getEnumerables(val2, symbolKeysB).length !== 0) { | |
| 422 | 413 | return false; | |
@@ -441,7 +432,6 @@ function keyCheck(val1, val2, mode, memos, iterationType, keys2) { | |||
| 441 | 432 | c: undefined, | |
| 442 | 433 | d: undefined, | |
| 443 | 434 | deep: false, | |
| 444 | - deleteFailures: false, | ||
| 445 | 435 | }; | |
| 446 | 436 | return objEquiv(val1, val2, mode, keys2, memos, iterationType); | |
| 447 | 437 | } | |
@@ -476,27 +466,21 @@ function keyCheck(val1, val2, mode, memos, iterationType, keys2) { | |||
| 476 | 466 | ||
| 477 | 467 | const areEq = objEquiv(val1, val2, mode, keys2, memos, iterationType); | |
| 478 | 468 | ||
| 479 | - if (areEq || memos.deleteFailures) { | ||
| 480 | - set.delete(val1); | ||
| 481 | - set.delete(val2); | ||
| 482 | - } | ||
| 469 | + set.delete(val1); | ||
| 470 | + set.delete(val2); | ||
| 483 | 471 | ||
| 484 | 472 | return areEq; | |
| 485 | 473 | } | |
| 486 | 474 | ||
| 487 | 475 | function setHasEqualElement(set, val1, mode, memo) { | |
| 488 | - const { deleteFailures } = memo; | ||
| 489 | - memo.deleteFailures = true; | ||
| 490 | 476 | for (const val2 of set) { | |
| 491 | 477 | if (innerDeepEqual(val1, val2, mode, memo)) { | |
| 492 | 478 | // Remove the matching element to make sure we do not check that again. | |
| 493 | 479 | set.delete(val2); | |
| 494 | - memo.deleteFailures = deleteFailures; | ||
| 495 | 480 | return true; | |
| 496 | 481 | } | |
| 497 | 482 | } | |
| 498 | 483 | ||
| 499 | - memo.deleteFailures = deleteFailures; | ||
| 500 | 484 | return false; | |
| 501 | 485 | } | |
| 502 | 486 | ||
@@ -557,6 +541,8 @@ function partialObjectSetEquiv(a, b, mode, set, memo) { | |||
| 557 | 541 | return false; | |
| 558 | 542 | } | |
| 559 | 543 | } | |
| 544 | + /* c8 ignore next */ | ||
| 545 | + assert.fail('Unreachable code'); | ||
| 560 | 546 | } | |
| 561 | 547 | ||
| 562 | 548 | function setObjectEquiv(a, b, mode, set, memo) { | |
@@ -623,18 +609,14 @@ function mapHasEqualEntry(set, map, key1, item1, mode, memo) { | |||
| 623 | 609 | // To be able to handle cases like: | |
| 624 | 610 | // Map([[{}, 'a'], [{}, 'b']]) vs Map([[{}, 'b'], [{}, 'a']]) | |
| 625 | 611 | // ... we need to consider *all* matching keys, not just the first we find. | |
| 626 | - const { deleteFailures } = memo; | ||
| 627 | - memo.deleteFailures = true; | ||
| 628 | 612 | for (const key2 of set) { | |
| 629 | 613 | if (innerDeepEqual(key1, key2, mode, memo) && | |
| 630 | 614 | innerDeepEqual(item1, map.get(key2), mode, memo)) { | |
| 631 | 615 | set.delete(key2); | |
| 632 | - memo.deleteFailures = deleteFailures; | ||
| 633 | 616 | return true; | |
| 634 | 617 | } | |
| 635 | 618 | } | |
| 636 | 619 | ||
| 637 | - memo.deleteFailures = deleteFailures; | ||
| 638 | 620 | return false; | |
| 639 | 621 | } | |
| 640 | 622 | ||
@@ -652,6 +634,8 @@ function partialObjectMapEquiv(a, b, mode, set, memo) { | |||
| 652 | 634 | return false; | |
| 653 | 635 | } | |
| 654 | 636 | } | |
| 637 | + /* c8 ignore next */ | ||
| 638 | + assert.fail('Unreachable code'); | ||
| 655 | 639 | } | |
| 656 | 640 | ||
| 657 | 641 | function mapObjectEquivalence(a, b, mode, set, memo) { | |
@@ -747,11 +731,11 @@ function partialSparseArrayEquiv(a, b, mode, memos, startA, startB) { | |||
| 747 | 731 | function partialArrayEquiv(a, b, mode, memos) { | |
| 748 | 732 | let aPos = 0; | |
| 749 | 733 | for (let i = 0; i < b.length; i++) { | |
| 750 | - let isSparse = b[i] === undefined && !ObjectPrototypeHasOwnProperty(b, i); | ||
| 734 | + let isSparse = b[i] === undefined && !hasOwn(b, i); | ||
| 751 | 735 | if (isSparse) { | |
| 752 | 736 | return partialSparseArrayEquiv(a, b, mode, memos, aPos, i); | |
| 753 | 737 | } | |
| 754 | - while (!(isSparse = a[aPos] === undefined && !ObjectPrototypeHasOwnProperty(a, aPos)) && | ||
| 738 | + while (!(isSparse = a[aPos] === undefined && !hasOwn(a, aPos)) && | ||
| 755 | 739 | !innerDeepEqual(a[aPos], b[i], mode, memos)) { | |
| 756 | 740 | aPos++; | |
| 757 | 741 | if (aPos > a.length - b.length + i) { | |
@@ -776,8 +760,7 @@ function sparseArrayEquiv(a, b, mode, memos, i) { | |||
| 776 | 760 | } | |
| 777 | 761 | for (; i < keysA.length; i++) { | |
| 778 | 762 | const key = keysA[i]; | |
| 779 | - if (!ObjectPrototypeHasOwnProperty(b, key) || | ||
| 780 | - !innerDeepEqual(a[key], b[key], mode, memos)) { | ||
| 763 | + if (!hasOwn(b, key) || !innerDeepEqual(a[key], b[key], mode, memos)) { | ||
| 781 | 764 | return false; | |
| 782 | 765 | } | |
| 783 | 766 | } | |
@@ -802,8 +785,8 @@ function objEquiv(a, b, mode, keys2, memos, iterationType) { | |||
| 802 | 785 | if (!innerDeepEqual(a[i], b[i], mode, memos)) { | |
| 803 | 786 | return false; | |
| 804 | 787 | } | |
| 805 | - const isSparseA = a[i] === undefined && !ObjectPrototypeHasOwnProperty(a, i); | ||
| 806 | - const isSparseB = b[i] === undefined && !ObjectPrototypeHasOwnProperty(b, i); | ||
| 788 | + const isSparseA = a[i] === undefined && !hasOwn(a, i); | ||
| 789 | + const isSparseB = b[i] === undefined && !hasOwn(b, i); | ||
| 807 | 790 | if (isSparseA !== isSparseB) { | |
| 808 | 791 | return false; | |
| 809 | 792 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3,6 +3,13 @@ require('../common'); | |||
| 3 | 3 | const assert = require('assert'); | |
| 4 | 4 | const { test } = require('node:test'); | |
| 5 | 5 | ||
| 6 | + // Disable colored output to prevent color codes from breaking assertion | ||
| 7 | + // message comparisons. This should only be an issue when process.stdout | ||
| 8 | + // is a TTY. | ||
| 9 | + if (process.stdout.isTTY) { | ||
| 10 | + process.env.NODE_DISABLE_COLORS = '1'; | ||
| 11 | + } | ||
| 12 | + | ||
| 6 | 13 | const defaultStartMessage = 'Expected values to be strictly deep-equal:\n' + | |
| 7 | 14 | '+ actual - expected\n' + | |
| 8 | 15 | '\n'; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -176,10 +176,32 @@ describe('Object Comparison Tests', () => { | |||
| 176 | 176 | }, | |
| 177 | 177 | { | |
| 178 | 178 | description: | |
| 179 | - 'throws when comparing two objects with different Error instances', | ||
| 179 | + 'throws when comparing two objects with different Error message', | ||
| 180 | 180 | actual: { error: new Error('Test error 1') }, | |
| 181 | 181 | expected: { error: new Error('Test error 2') }, | |
| 182 | 182 | }, | |
| 183 | + { | ||
| 184 | + description: | ||
| 185 | + 'throws when comparing two objects with missing cause on the actual Error', | ||
| 186 | + actual: { error: new Error('Test error 1') }, | ||
| 187 | + expected: { error: new Error('Test error 1', { cause: 42 }) }, | ||
| 188 | + }, | ||
| 189 | + { | ||
| 190 | + description: | ||
| 191 | + 'throws when comparing two objects with missing message on the actual Error', | ||
| 192 | + actual: { error: new Error() }, | ||
| 193 | + expected: { error: new Error('Test error 1') }, | ||
| 194 | + }, | ||
| 195 | + { | ||
| 196 | + description: 'throws when comparing two Errors with missing cause on the actual Error', | ||
| 197 | + actual: { error: new Error('Test error 1') }, | ||
| 198 | + expected: { error: new Error('Test error 1', { cause: undefined }) }, | ||
| 199 | + }, | ||
| 200 | + { | ||
| 201 | + description: 'throws when comparing two AggregateErrors with missing message on the actual Error', | ||
| 202 | + actual: { error: new AggregateError([], 'Test error 1') }, | ||
| 203 | + expected: { error: new AggregateError([new Error()], 'Test error 1') }, | ||
| 204 | + }, | ||
| 183 | 205 | { | |
| 184 | 206 | description: | |
| 185 | 207 | 'throws when comparing two objects with different TypedArray instances and content', | |
@@ -1105,6 +1127,26 @@ describe('Object Comparison Tests', () => { | |||
| 1105 | 1127 | } | |
| 1106 | 1128 | }] | |
| 1107 | 1129 | }, | |
| 1130 | + { | ||
| 1131 | + description: 'comparing two Errors with missing cause on the expected Error', | ||
| 1132 | + actual: { error: new Error('Test error 1', { cause: 42 }) }, | ||
| 1133 | + expected: { error: new Error('Test error 1') }, | ||
| 1134 | + }, | ||
| 1135 | + { | ||
| 1136 | + description: 'comparing two Errors with cause set to undefined on the actual Error', | ||
| 1137 | + actual: { error: new Error('Test error 1', { cause: undefined }) }, | ||
| 1138 | + expected: { error: new Error('Test error 1') }, | ||
| 1139 | + }, | ||
| 1140 | + { | ||
| 1141 | + description: 'comparing two Errors with missing message on the expected Error', | ||
| 1142 | + actual: { error: new Error('Test error 1') }, | ||
| 1143 | + expected: { error: new Error() }, | ||
| 1144 | + }, | ||
| 1145 | + { | ||
| 1146 | + description: 'comparing two AggregateErrors with no message or errors on the expected Error', | ||
| 1147 | + actual: { error: new AggregateError([new Error(), 123]) }, | ||
| 1148 | + expected: { error: new AggregateError([]) }, | ||
| 1149 | + }, | ||
| 1108 | 1150 | ].forEach(({ description, actual, expected }) => { | |
| 1109 | 1151 | it(description, () => { | |
| 1110 | 1152 | assert.partialDeepStrictEqual(actual, expected); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments