member manager virtualization >> scrolling reaches a member far down the list failed on mobile-chrome in run 35334254409, on a branch that changes no frontend source. Same spec as #1186, different symptom: there the wheel scrolled nothing, here it scrolls roughly twice as far as asked.
What the trace says
The retain-on-failure trace records two wheel dispatches, both asking for the whole distance:
call@712 mouseWheel {deltaX: 0, deltaY: 10956}
call@722 mouseWheel {deltaX: 0, deltaY: 10956}
The poll added by #1203 reads scrollTop, and dispatches a wheel for whatever distance is left:
const target = 249 * ROW_HEIGHT // 10956
await expect
.poll(async () => {
const reached = await scroller.evaluate(el => el.scrollTop)
if (reached < target) {
await page.mouse.wheel(0, target - reached)
}
return scroller.evaluate(el => el.scrollTop)
})
.toBeGreaterThanOrEqual(target)
A second delta of exactly 10956 means reached was still 0 on the second iteration — the first wheel had not moved the scroller yet, which is the settling behaviour #1186 documented. So 21912px is dispatched in total against a maximum scrollTop of 13266 - clientHeight, roughly 12800. The scroller clamps at the bottom of the list.
The poll then passes, because the assertion is toBeGreaterThanOrEqual(target) and the bottom of the list is well past row 250. The next line is what fails:
Error: expect(locator).toBeVisible() failed
Locator: getByText('Member 250', { exact: true })
Expected: visible
Error: element(s) not found
Row 250 is off-window above; the window holds rows near 300.
Why it is surfacing now
#1203 turned "the wheel does nothing" into "the wheel fires twice", and the retry in playwright.config.ts absorbed the result. #1309 sets retries: 0, so the first failure is now the reported one. The bug predates that change — it is the #1203 fix racing the same settling window #1186 described, not anything about sharding.
Where a fix probably goes
The poll asks for an absolute destination using a relative gesture, and re-asks before the previous gesture has landed. Two shapes worth considering:
- Ask once and wait for the scroller to settle, rather than re-asking on a delta that may already be in flight — for instance poll only on
scrollTop changing, and dispatch again only after it has gone quiet for a frame.
- Assert the destination rather than a floor:
toBeGreaterThanOrEqual(target) accepts the bottom of the list as success. The spec wants row 250 in the window, so a bounded range, or scrolling the row into view directly, says what it means.
The second is the one that would have caught this rather than passing through it.
member manager virtualization >> scrolling reaches a member far down the listfailed onmobile-chromein run 35334254409, on a branch that changes no frontend source. Same spec as #1186, different symptom: there the wheel scrolled nothing, here it scrolls roughly twice as far as asked.What the trace says
The
retain-on-failuretrace records two wheel dispatches, both asking for the whole distance:The poll added by #1203 reads
scrollTop, and dispatches a wheel for whatever distance is left:A second delta of exactly 10956 means
reachedwas still 0 on the second iteration — the first wheel had not moved the scroller yet, which is the settling behaviour #1186 documented. So 21912px is dispatched in total against a maximumscrollTopof13266 - clientHeight, roughly 12800. The scroller clamps at the bottom of the list.The poll then passes, because the assertion is
toBeGreaterThanOrEqual(target)and the bottom of the list is well past row 250. The next line is what fails:Row 250 is off-window above; the window holds rows near 300.
Why it is surfacing now
#1203 turned "the wheel does nothing" into "the wheel fires twice", and the retry in
playwright.config.tsabsorbed the result. #1309 setsretries: 0, so the first failure is now the reported one. The bug predates that change — it is the #1203 fix racing the same settling window #1186 described, not anything about sharding.Where a fix probably goes
The poll asks for an absolute destination using a relative gesture, and re-asks before the previous gesture has landed. Two shapes worth considering:
scrollTopchanging, and dispatch again only after it has gone quiet for a frame.toBeGreaterThanOrEqual(target)accepts the bottom of the list as success. The spec wants row 250 in the window, so a bounded range, or scrolling the row into view directly, says what it means.The second is the one that would have caught this rather than passing through it.