23
23
const {
24
24
ArrayBufferIsView,
25
25
ArrayBufferPrototypeGetByteLength,
26
- ArrayFrom,
27
26
ArrayIsArray,
28
27
ArrayPrototypeIndexOf,
29
28
ArrayPrototypeJoin,
@@ -395,12 +394,11 @@ function partiallyCompareMaps(actual, expected, comparedObjects) {
395
394
const expectedIterator = FunctionPrototypeCall ( SafeMap . prototype [ SymbolIterator ] , expected ) ;
396
395
397
396
for ( const { 0 : key , 1 : expectedValue } of expectedIterator ) {
398
- if ( ! MapPrototypeHas ( actual , key ) ) {
397
+ const actualValue = MapPrototypeGet ( actual , key ) ;
398
+ if ( actualValue === undefined && ! MapPrototypeHas ( actual , key ) ) {
399
399
return false ;
400
400
}
401
401
402
- const actualValue = MapPrototypeGet ( actual , key ) ;
403
-
404
402
if ( ! compareBranch ( actualValue , expectedValue , comparedObjects ) ) {
405
403
return false ;
406
404
}
@@ -474,28 +472,74 @@ function partiallyCompareArrayBuffersOrViews(actual, expected) {
474
472
return true ;
475
473
}
476
474
475
+ // Adapted version of the "setEquiv" function in lib/internal/util/comparisons.js
477
476
function partiallyCompareSets ( actual , expected , comparedObjects ) {
478
477
if ( SetPrototypeGetSize ( expected ) > SetPrototypeGetSize ( actual ) ) {
479
- return false ; // `expected` can't be a subset if it has more elements
478
+ return false ;
480
479
}
481
480
482
481
if ( isDeepEqual === undefined ) lazyLoadComparison ( ) ;
482
+ let set = null ;
483
483
484
- const actualArray = ArrayFrom ( FunctionPrototypeCall ( SafeSet . prototype [ SymbolIterator ] , actual ) ) ;
484
+ // First, check if elements from expected exist in actual
485
485
const expectedIterator = FunctionPrototypeCall ( SafeSet . prototype [ SymbolIterator ] , expected ) ;
486
- const usedIndices = new SafeSet ( ) ;
486
+ for ( const val of expectedIterator ) {
487
+ // Fast path: direct inclusion check for both primitives and reference equality
488
+ if ( actual . has ( val ) ) {
489
+ continue ;
490
+ }
491
+
492
+ // For primitives, if not found directly, return false immediately
493
+ if ( isPrimitive ( val ) ) {
494
+ return false ;
495
+ }
487
496
488
- expectedIteration: for ( const expectedItem of expectedIterator ) {
489
- for ( let actualIdx = 0 ; actualIdx < actualArray . length ; actualIdx ++ ) {
490
- if ( ! usedIndices . has ( actualIdx ) && isDeepStrictEqual ( actualArray [ actualIdx ] , expectedItem ) ) {
491
- usedIndices . add ( actualIdx ) ;
492
- continue expectedIteration;
497
+ if ( set === null ) {
498
+ // Special case to avoid set creation for single-element comparison
499
+ if ( SetPrototypeGetSize ( expected ) === 1 ) {
500
+ // Try to find any deep-equal object in actual
501
+ const actualIterator = FunctionPrototypeCall ( SafeSet . prototype [ SymbolIterator ] , actual ) ;
502
+ for ( const actualItem of actualIterator ) {
503
+ if ( ! isPrimitive ( actualItem ) && isDeepStrictEqual ( actualItem , val ) ) {
504
+ return true ;
505
+ }
506
+ }
507
+ return false ;
493
508
}
509
+ set = new SafeSet ( ) ;
494
510
}
495
- return false ;
511
+
512
+ // Add this object for later deep comparison
513
+ set . add ( val ) ;
496
514
}
497
515
498
- return true ;
516
+ // If all items were found directly, we're done
517
+ if ( set === null ) {
518
+ return true ;
519
+ }
520
+
521
+ // For remaining objects that need deep comparison
522
+ const actualIterator = FunctionPrototypeCall ( SafeSet . prototype [ SymbolIterator ] , actual ) ;
523
+ for ( const actualItem of actualIterator ) {
524
+ // Only consider non-primitive values for deep comparison
525
+ if ( ! isPrimitive ( actualItem ) ) {
526
+ // Check if this actual item deep-equals any remaining expected item
527
+ for ( const expectedItem of set ) {
528
+ if ( isDeepStrictEqual ( actualItem , expectedItem ) ) {
529
+ // Remove the matched item so we don't match it again
530
+ set . delete ( expectedItem ) ;
531
+ // If all items are matched, we can return early
532
+ if ( set . size === 0 ) {
533
+ return true ;
534
+ }
535
+ break ;
536
+ }
537
+ }
538
+ }
539
+ }
540
+
541
+ // If all objects in expected found matches, set will be empty
542
+ return set . size === 0 ;
499
543
}
500
544
501
545
const minusZeroSymbol = Symbol ( '-0' ) ;
@@ -510,21 +554,26 @@ function getZeroKey(item) {
510
554
}
511
555
512
556
function partiallyCompareArrays ( actual , expected , comparedObjects ) {
557
+ if ( actual === expected ) return true ;
558
+
513
559
if ( expected . length > actual . length ) {
514
560
return false ;
515
561
}
516
562
563
+ if ( expected . length === 0 ) {
564
+ return true ;
565
+ }
566
+
517
567
if ( isDeepEqual === undefined ) lazyLoadComparison ( ) ;
518
568
519
569
// Create a map to count occurrences of each element in the expected array
520
570
const expectedCounts = new SafeMap ( ) ;
521
- const safeExpected = new SafeArrayIterator ( expected ) ;
522
571
523
- for ( const expectedItem of safeExpected ) {
524
- // Check if the item is a zero or a -0, as these need to be handled separately
572
+ const expectedIterator = new SafeArrayIterator ( expected ) ;
573
+ for ( const expectedItem of expectedIterator ) {
525
574
if ( expectedItem === 0 ) {
526
575
const zeroKey = getZeroKey ( expectedItem ) ;
527
- expectedCounts . set ( zeroKey , ( expectedCounts . get ( zeroKey ) ?. count || 0 ) + 1 ) ;
576
+ expectedCounts . set ( zeroKey , ( expectedCounts . get ( zeroKey ) ?? 0 ) + 1 ) ;
528
577
} else {
529
578
let found = false ;
530
579
for ( const { 0 : key , 1 : count } of expectedCounts ) {
@@ -540,10 +589,8 @@ function partiallyCompareArrays(actual, expected, comparedObjects) {
540
589
}
541
590
}
542
591
543
- const safeActual = new SafeArrayIterator ( actual ) ;
544
-
545
- for ( const actualItem of safeActual ) {
546
- // Check if the item is a zero or a -0, as these need to be handled separately
592
+ const actualIterator = new SafeArrayIterator ( actual ) ;
593
+ for ( const actualItem of actualIterator ) {
547
594
if ( actualItem === 0 ) {
548
595
const zeroKey = getZeroKey ( actualItem ) ;
549
596
@@ -567,6 +614,10 @@ function partiallyCompareArrays(actual, expected, comparedObjects) {
567
614
}
568
615
}
569
616
}
617
+
618
+ if ( expectedCounts . size === 0 ) {
619
+ return true ;
620
+ }
570
621
}
571
622
572
623
return expectedCounts . size === 0 ;
@@ -723,6 +774,10 @@ function compareExceptionKey(actual, expected, key, message, keys, fn) {
723
774
}
724
775
}
725
776
777
+ function isPrimitive ( value ) {
778
+ return typeof value !== 'object' || value === null ;
779
+ }
780
+
726
781
function expectedException ( actual , expected , message , fn ) {
727
782
let generatedMessage = false ;
728
783
let throwError = false ;
@@ -741,7 +796,7 @@ function expectedException(actual, expected, message, fn) {
741
796
}
742
797
throwError = true ;
743
798
// Handle primitives properly.
744
- } else if ( typeof actual !== 'object' || actual === null ) {
799
+ } else if ( isPrimitive ( actual ) ) {
745
800
const err = new AssertionError ( {
746
801
actual,
747
802
expected,
0 commit comments