Skip to content

Commit 9c820d6

Browse files
ilonatommyCopilot
andauthored
[Components][E2E] Stabilize NonZeroStartIndex_ScrollToMiddleThenMeasure test setup (#66290)
* Stabilize NonZeroStartIndex_ScrollToMiddleThenMeasure E2E test setup Apply the same retry-based scroll stabilization pattern from PR #66194 to NonZeroStartIndex_ScrollToMiddleThenMeasure. The test was racing in server mode because it set scrollTop and then immediately checked the rendered items, but the async virtualization update had not finished yet. The fix computes the target offset up front, then retries both the scroll assignment and the rendered-index check inside a single Browser.True() loop before calling WaitForScrollStabilization. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Apply review feedback: fix spacing and reuse ScrollToOffsetWithStabilization helper - Fix missing space around = in targetScrollTop assignment - Extract scroll+retry logic into generalized ScrollToOffsetWithStabilization overload - Reuse the helper in NonZeroStartIndex_ScrollToMiddleThenMeasure instead of duplicating Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix bad merge: remove stale CanElevateEffectiveMaxItemCount quarantine, restore missing PrependItems quarantine The merge from main incorrectly re-introduced a quarantine for CanElevateEffectiveMaxItemCount_WhenOverscanExceedsMax (#65962) which was already fixed and unquarantined by #66291 on main. It also dropped the DynamicContent_PrependItemsWhileScrolledToMiddle quarantine (#66308) that was added by #66312 on main. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Use MutationObserver instead of scroll retry loop in NonZeroStartIndex test Replace the retry-based ScrollToOffsetWithStabilization call with a single scrollTop assignment followed by a MutationObserver that waits for Blazor's DOM update to settle. This makes the test more deterministic: it scrolls once and waits for the effect, rather than retrying the scroll which could mask real bugs where the component fails to handle the initial scroll correctly. The MutationObserver waits for a 200ms quiet period after the last DOM mutation, with a 2s fallback timeout. After that, scroll position stabilization is verified, and the item index assertion is a hard assert (not a retry loop). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 765e192 commit 9c820d6

2 files changed

Lines changed: 45 additions & 28 deletions

File tree

src/Components/test/E2ETest/ServerExecutionTests/TestSubclasses.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,6 @@ public ServerVirtualizationTest(BrowserFixture browserFixture, ToggleExecutionMo
104104
public override void CanRenderHtmlTable()
105105
=> base.CanRenderHtmlTable();
106106

107-
[QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/66119")]
108-
public override void NonZeroStartIndex_ScrollToMiddleThenMeasure()
109-
=> base.NonZeroStartIndex_ScrollToMiddleThenMeasure();
110-
111107
[QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/66120")]
112108
public override void CancelsOutdatedRefreshes_Async()
113109
=> base.CancelsOutdatedRefreshes_Async();

src/Components/test/E2ETest/Tests/VirtualizationTest.cs

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,26 @@ private void ScrollToOffsetWithStabilization(
753753
IWebElement container,
754754
double targetScrollTop,
755755
int minFirstRenderedIndexExclusive)
756+
{
757+
ScrollToOffsetWithStabilization(container, targetScrollTop, () =>
758+
{
759+
var items = container.FindElements(By.CssSelector(".item"));
760+
if (items.Count == 0)
761+
{
762+
return false;
763+
}
764+
765+
var indexText = items.First().GetDomAttribute("data-index");
766+
return indexText != null
767+
&& int.TryParse(indexText, NumberStyles.Integer, CultureInfo.InvariantCulture, out var index)
768+
&& index > minFirstRenderedIndexExclusive;
769+
});
770+
}
771+
772+
private void ScrollToOffsetWithStabilization(
773+
IWebElement container,
774+
double targetScrollTop,
775+
Func<bool> itemCheckPredicate)
756776
{
757777
var js = (IJavaScriptExecutor)Browser;
758778

@@ -769,16 +789,7 @@ private void ScrollToOffsetWithStabilization(
769789
return false;
770790
}
771791

772-
var items = container.FindElements(By.CssSelector(".item"));
773-
if (items.Count == 0)
774-
{
775-
return false;
776-
}
777-
778-
var indexText = items.First().GetDomAttribute("data-index");
779-
return indexText != null
780-
&& int.TryParse(indexText, NumberStyles.Integer, CultureInfo.InvariantCulture, out var index)
781-
&& index > minFirstRenderedIndexExclusive;
792+
return itemCheckPredicate();
782793
});
783794

784795
WaitForScrollStabilization(container);
@@ -1615,29 +1626,39 @@ public void ProgrammaticScrollToBottom_ReachesLastItems()
16151626
}
16161627

16171628
[Fact]
1618-
public virtual void NonZeroStartIndex_ScrollToMiddleThenMeasure()
1629+
public void NonZeroStartIndex_ScrollToMiddleThenMeasure()
16191630
{
16201631
Browser.MountTestComponent<VirtualizationScrollBehavior>();
16211632

16221633
var container = Browser.Exists(By.Id("scroll-behavior-container"));
16231634
var js = (IJavaScriptExecutor)Browser;
16241635
Browser.True(() => GetElementCount(container, ".scroll-behavior-item") > 0);
16251636

1626-
js.ExecuteScript("arguments[0].scrollTop = arguments[0].scrollHeight / 2", container);
1637+
// Scroll to middle and wait for Blazor's DOM update to land via MutationObserver.
1638+
// This sets scrollTop once and waits for the virtualization render to complete,
1639+
// rather than retrying the scroll which could mask bugs.
1640+
js.ExecuteAsyncScript(@"
1641+
var container = arguments[0];
1642+
var callback = arguments[1];
1643+
var targetTop = container.scrollHeight / 2;
1644+
container.scrollTop = targetTop;
1645+
var timer;
1646+
var observer = new MutationObserver(function() {
1647+
clearTimeout(timer);
1648+
timer = setTimeout(function() { observer.disconnect(); callback(); }, 200);
1649+
});
1650+
observer.observe(container, { childList: true, subtree: true });
1651+
// Fallback in case scroll didn't trigger any DOM mutations (e.g., already at target)
1652+
timer = setTimeout(function() { observer.disconnect(); callback(); }, 2000);
1653+
", container);
1654+
16271655
WaitForScrollStabilization(container);
16281656

1629-
Browser.True(() =>
1630-
{
1631-
var items = container.FindElements(By.CssSelector(".scroll-behavior-item .item-index"));
1632-
return items.Any(item =>
1633-
{
1634-
if (int.TryParse(item.Text, out var idx))
1635-
{
1636-
return idx > 50;
1637-
}
1638-
return false;
1639-
});
1640-
});
1657+
// Verify that scrolling to the middle actually rendered middle items
1658+
var items = container.FindElements(By.CssSelector(".scroll-behavior-item .item-index"));
1659+
Assert.True(
1660+
items.Any(item => int.TryParse(item.Text, out var idx) && idx > 50),
1661+
"After scrolling to middle, expected items with index > 50 to be visible");
16411662

16421663
var visibleItems = container.FindElements(By.CssSelector(".scroll-behavior-item"));
16431664
Assert.True(visibleItems.Count > 0, "Should have visible items at non-zero start");

0 commit comments

Comments
 (0)