@@ -291,14 +291,14 @@ public void CanLimitMaxItemsRendered(bool useAppContext)
291
291
// we only render 10 items due to the MaxItemCount setting
292
292
var scrollArea = Browser . Exists ( By . Id ( "virtualize-scroll-area" ) ) ;
293
293
var getItems = ( ) => scrollArea . FindElements ( By . ClassName ( "my-item" ) ) ;
294
- Browser . Equal ( 10 , ( ) => getItems ( ) . Count ) ;
294
+ Browser . Equal ( 16 , ( ) => getItems ( ) . Count ) ;
295
295
Browser . Equal ( "Id: 0; Name: Thing 0" , ( ) => getItems ( ) . First ( ) . Text ) ;
296
296
297
297
// Scrolling still works and loads new data, though there's no guarantee about
298
298
// exactly how many items will show up at any one time
299
299
Browser . ExecuteJavaScript ( "document.getElementById('virtualize-scroll-area').scrollTop = 300;" ) ;
300
300
Browser . NotEqual ( "Id: 0; Name: Thing 0" , ( ) => getItems ( ) . First ( ) . Text ) ;
301
- Browser . True ( ( ) => getItems ( ) . Count > 3 && getItems ( ) . Count <= 10 ) ;
301
+ Browser . True ( ( ) => getItems ( ) . Count > 3 && getItems ( ) . Count <= 16 ) ;
302
302
}
303
303
304
304
[ Fact ]
@@ -573,6 +573,101 @@ public void EmptyContentRendered_Async()
573
573
int GetPlaceholderCount ( ) => Browser . FindElements ( By . Id ( "async-placeholder" ) ) . Count ;
574
574
}
575
575
576
+ [ Fact ]
577
+ public void CanElevateEffectiveMaxItemCount_WhenOverscanExceedsMax ( )
578
+ {
579
+ Browser . MountTestComponent < VirtualizationLargeOverscan > ( ) ;
580
+ var container = Browser . Exists ( By . Id ( "virtualize-large-overscan" ) ) ;
581
+ // Ensure we have an initial contiguous batch and the elevated effective max has kicked in (>= OverscanCount)
582
+ var indices = GetVisibleItemIndices ( ) ;
583
+ Browser . True ( ( ) => indices . Count >= 200 ) ;
584
+
585
+ // Give focus so PageDown works
586
+ container . Click ( ) ;
587
+
588
+ var js = ( IJavaScriptExecutor ) Browser ;
589
+ var lastMaxIndex = - 1 ;
590
+ var lastScrollTop = - 1L ;
591
+
592
+ // Check if we've reached (or effectively reached) the bottom
593
+ var scrollHeight = ( long ) js . ExecuteScript ( "return arguments[0].scrollHeight" , container ) ;
594
+ var clientHeight = ( long ) js . ExecuteScript ( "return arguments[0].clientHeight" , container ) ;
595
+ var scrollTop = ( long ) js . ExecuteScript ( "return arguments[0].scrollTop" , container ) ;
596
+ while ( scrollTop + clientHeight < scrollHeight )
597
+ {
598
+ // Validate contiguity on the current page
599
+ Browser . True ( ( ) => IsCurrentViewContiguous ( indices ) ) ;
600
+
601
+ // Track progress in indices
602
+ var currentMax = indices . Max ( ) ;
603
+ Assert . True ( currentMax >= lastMaxIndex , $ "Unexpected backward movement: previous max { lastMaxIndex } , current max { currentMax } .") ;
604
+ lastMaxIndex = currentMax ;
605
+
606
+ // Send PageDown
607
+ container . SendKeys ( Keys . PageDown ) ;
608
+
609
+ // Wait for scrollTop to change (progress) to avoid infinite loop
610
+ var prevScrollTop = scrollTop ;
611
+ Browser . True ( ( ) =>
612
+ {
613
+ var st = ( long ) js . ExecuteScript ( "return arguments[0].scrollTop" , container ) ;
614
+ if ( st > prevScrollTop )
615
+ {
616
+ lastScrollTop = st ;
617
+ return true ;
618
+ }
619
+ return false ;
620
+ } ) ;
621
+ scrollHeight = ( long ) js . ExecuteScript ( "return arguments[0].scrollHeight" , container ) ;
622
+ clientHeight = ( long ) js . ExecuteScript ( "return arguments[0].clientHeight" , container ) ;
623
+ scrollTop = ( long ) js . ExecuteScript ( "return arguments[0].scrollTop" , container ) ;
624
+ }
625
+
626
+ // Final contiguous assertion at bottom
627
+ Browser . True ( ( ) => IsCurrentViewContiguous ( ) ) ;
628
+
629
+ // Helper: check visible items contiguous with no holes
630
+ bool IsCurrentViewContiguous ( List < int > existingIndices = null )
631
+ {
632
+ var indices = existingIndices ?? GetVisibleItemIndices ( ) ;
633
+ if ( indices . Count == 0 )
634
+ {
635
+ return false ;
636
+ }
637
+
638
+ if ( indices [ ^ 1 ] - indices [ 0 ] != indices . Count - 1 )
639
+ {
640
+ return false ;
641
+ }
642
+ for ( var i = 1 ; i < indices . Count ; i ++ )
643
+ {
644
+ if ( indices [ i ] - indices [ i - 1 ] != 1 )
645
+ {
646
+ return false ;
647
+ }
648
+ }
649
+ return true ;
650
+ }
651
+
652
+ List < int > GetVisibleItemIndices ( )
653
+ {
654
+ var elements = container . FindElements ( By . CssSelector ( ".large-overscan-item" ) ) ;
655
+ var list = new List < int > ( elements . Count ) ;
656
+ foreach ( var el in elements )
657
+ {
658
+ var text = el . Text ;
659
+ if ( text . StartsWith ( "Item " , StringComparison . Ordinal ) )
660
+ {
661
+ if ( int . TryParse ( text . AsSpan ( 5 ) , NumberStyles . Integer , CultureInfo . InvariantCulture , out var value ) )
662
+ {
663
+ list . Add ( value ) ;
664
+ }
665
+ }
666
+ }
667
+ return list ;
668
+ }
669
+ }
670
+
576
671
private string [ ] GetPeopleNames ( IWebElement container )
577
672
{
578
673
var peopleElements = container . FindElements ( By . CssSelector ( ".person span" ) ) ;
0 commit comments