Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/react-virtual/e2e/app/smooth-scroll/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ const App = () => {
getScrollElement: () => parentRef.current,
estimateSize: () => 50,
})
// Test hook: lets the spec wait on the virtualizer's own settlement signal
// (reconcileScroll retiring `scrollState`) instead of guessing from
// scrollTop samples.
;(window as any).__virtualizer = rowVirtualizer

return (
<div>
Expand Down
63 changes: 34 additions & 29 deletions packages/react-virtual/e2e/app/test/smooth-scroll.spec.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,34 @@
import { expect, test } from '@playwright/test'
import type { Page } from '@playwright/test'

// A smooth scroll's duration scales with distance (index 1000 is ~50,000px),
// and reconcileScroll may re-drive it as rows measure, so neither a fixed wait
// nor a pair of equal scrollTop samples proves completion: the "index 1000"
// case flaked on a fixed 2s wait, and two samples can straddle the pause
// between two re-drives. The virtualizer has the real signal: reconcileScroll
// retires `scrollState` only once the target is stable and reached. Wait for
// the target row to render, then for that retirement.
async function waitForSmoothScroll(page: Page, testId: string) {
await expect(page.locator(`[data-testid="${testId}"]`)).toBeVisible({
timeout: 15_000,
})
await expect
.poll(
() =>
page.evaluate(() => {
const v = (window as any).__virtualizer
return v.scrollState === null && v.isScrolling === false
}),
{ timeout: 15_000, intervals: [50] },
)
.toBe(true)
}

test('smooth scrolls to index 1000', async ({ page }) => {
await page.goto('/smooth-scroll/')
await page.click('#scroll-to-1000')

// Smooth scroll animation is 500ms + reconciliation time
await page.waitForTimeout(2000)

await expect(page.locator('[data-testid="item-1000"]')).toBeVisible()
await waitForSmoothScroll(page, 'item-1000')
Comment thread
coderabbitai[bot] marked this conversation as resolved.

const delta = await page.evaluate(() => {
const item = document.querySelector('[data-testid="item-1000"]')
Expand All @@ -29,24 +50,19 @@ test('smooth scrolls to index 100', async ({ page }) => {
await page.goto('/smooth-scroll/')
await page.click('#scroll-to-100')

await page.waitForTimeout(2000)

await expect(page.locator('[data-testid="item-100"]')).toBeVisible()
await waitForSmoothScroll(page, 'item-100')
})

test('smooth scrolls to index 0 after scrolling away', async ({ page }) => {
await page.goto('/smooth-scroll/')

// First scroll down
await page.click('#scroll-to-500')
await page.waitForTimeout(2000)
await expect(page.locator('[data-testid="item-500"]')).toBeVisible()
await waitForSmoothScroll(page, 'item-500')

// Then smooth scroll back to top
await page.click('#scroll-to-0')
await page.waitForTimeout(2000)

await expect(page.locator('[data-testid="item-0"]')).toBeVisible()
await waitForSmoothScroll(page, 'item-0')

const scrollTop = await page.evaluate(() => {
const container = document.querySelector('#scroll-container')
Expand All @@ -59,9 +75,7 @@ test('smooth scrolls to index 500 with start alignment', async ({ page }) => {
await page.goto('/smooth-scroll/')
await page.click('#scroll-to-500-start')

await page.waitForTimeout(2000)

await expect(page.locator('[data-testid="item-500"]')).toBeVisible()
await waitForSmoothScroll(page, 'item-500')

const delta = await page.evaluate(
([idx, align]) => {
Expand All @@ -84,9 +98,7 @@ test('smooth scrolls to index 500 with center alignment', async ({ page }) => {
await page.goto('/smooth-scroll/')
await page.click('#scroll-to-500-center')

await page.waitForTimeout(2000)

await expect(page.locator('[data-testid="item-500"]')).toBeVisible()
await waitForSmoothScroll(page, 'item-500')

const delta = await page.evaluate(
([idx]) => {
Expand All @@ -110,18 +122,15 @@ test('smooth scrolls sequentially to multiple targets', async ({ page }) => {

// Scroll to 100 first
await page.click('#scroll-to-100')
await page.waitForTimeout(2000)
await expect(page.locator('[data-testid="item-100"]')).toBeVisible()
await waitForSmoothScroll(page, 'item-100')

// Then scroll to 500
await page.click('#scroll-to-500')
await page.waitForTimeout(2000)
await expect(page.locator('[data-testid="item-500"]')).toBeVisible()
await waitForSmoothScroll(page, 'item-500')

// Then scroll to 1000
await page.click('#scroll-to-1000')
await page.waitForTimeout(2000)
await expect(page.locator('[data-testid="item-1000"]')).toBeVisible()
await waitForSmoothScroll(page, 'item-1000')
})

test('interrupting smooth scroll with another smooth scroll', async ({
Expand All @@ -135,9 +144,5 @@ test('interrupting smooth scroll with another smooth scroll', async ({
await page.waitForTimeout(200)
await page.click('#scroll-to-100')

// Wait for the second scroll to complete
await page.waitForTimeout(2000)

// Should have ended at 100, not 1000
await expect(page.locator('[data-testid="item-100"]')).toBeVisible()
await waitForSmoothScroll(page, 'item-100')
})
Loading