Skip to content

Commit c19571f

Browse files
stabilize rsc tests (#7217)
1 parent b57e898 commit c19571f

30 files changed

Lines changed: 150 additions & 114 deletions
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { expect, type Page } from '@playwright/test'
2+
3+
export async function waitForHydration(page: Page) {
4+
await expect(page.getByTestId('app-hydrated')).toHaveText('hydrated', {
5+
timeout: 15000,
6+
})
7+
}

e2e/react-start/rsc/tests/rsc-async-bundle.spec.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { expect } from '@playwright/test'
22
import { test } from '@tanstack/router-e2e-utils'
3+
import { waitForHydration } from './hydration'
34

45
test.describe('RSC Async Bundle Tests - Multiple RSC Promises with React.use()', () => {
56
test('Loading states appear for each RSC before they resolve', async ({
@@ -222,7 +223,7 @@ test.describe('RSC Async Bundle Tests - Multiple RSC Promises with React.use()',
222223
// Start at home
223224
await page.goto('/')
224225
await page.waitForURL('/')
225-
await expect(page.getByTestId('app-hydrated')).toHaveText('hydrated')
226+
await waitForHydration(page)
226227

227228
// Navigate to async bundle page via link (if nav exists) or direct navigation
228229
await page.goto('/rsc-async-bundle')
@@ -265,7 +266,7 @@ test.describe('RSC Async Bundle Tests - Multiple RSC Promises with React.use()',
265266
// Navigate away
266267
await page.goto('/')
267268
await page.waitForURL('/')
268-
await expect(page.getByTestId('app-hydrated')).toHaveText('hydrated')
269+
await waitForHydration(page)
269270

270271
// Second visit (force new request)
271272
await page.goto('/rsc-async-bundle')

e2e/react-start/rsc/tests/rsc-basic.spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { expect } from '@playwright/test'
22
import { test } from '@tanstack/router-e2e-utils'
3+
import { waitForHydration } from './hydration'
34

45
test.describe('RSC Basic Tests', () => {
56
test('RSC renders with server timestamp', async ({ page }) => {
@@ -29,7 +30,7 @@ test.describe('RSC Basic Tests', () => {
2930
// Start from home
3031
await page.goto('/')
3132
await page.waitForURL('/')
32-
await expect(page.getByTestId('app-hydrated')).toHaveText('hydrated')
33+
await waitForHydration(page)
3334

3435
// Navigate to RSC page via nav bar (need to be specific to avoid matching example cards)
3536
await page.getByTestId('nav-basic').click()

e2e/react-start/rsc/tests/rsc-bundle.spec.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { expect } from '@playwright/test'
22
import { test } from '@tanstack/router-e2e-utils'
3+
import { waitForHydration } from './hydration'
34

45
test.describe('RSC Bundle Tests - Multiple RSCs from single server function', () => {
56
test('All bundled RSCs render correctly on initial load', async ({
@@ -49,7 +50,7 @@ test.describe('RSC Bundle Tests - Multiple RSCs from single server function', ()
4950
test('Client slots in bundled RSCs work correctly', async ({ page }) => {
5051
await page.goto('/rsc-bundle')
5152
await page.waitForURL('/rsc-bundle')
52-
await expect(page.getByTestId('app-hydrated')).toHaveText('hydrated')
53+
await waitForHydration(page)
5354

5455
// Verify client actions area is rendered
5556
await expect(page.getByTestId('content-actions')).toBeVisible()
@@ -81,7 +82,7 @@ test.describe('RSC Bundle Tests - Multiple RSCs from single server function', ()
8182
test('Client interactions do not reload bundled RSCs', async ({ page }) => {
8283
await page.goto('/rsc-bundle')
8384
await page.waitForURL('/rsc-bundle')
84-
await expect(page.getByTestId('app-hydrated')).toHaveText('hydrated')
85+
await waitForHydration(page)
8586

8687
// Get initial timestamp
8788
const initialTimestamp = await page
@@ -120,7 +121,7 @@ test.describe('RSC Bundle Tests - Multiple RSCs from single server function', ()
120121
// Start at home
121122
await page.goto('/')
122123
await page.waitForURL('/')
123-
await expect(page.getByTestId('app-hydrated')).toHaveText('hydrated')
124+
await waitForHydration(page)
124125

125126
// Navigate to bundle page via nav bar (use exact match to avoid 'Async Bundle')
126127
await page.getByTestId('nav-bundle').click()
@@ -145,7 +146,7 @@ test.describe('RSC Bundle Tests - Multiple RSCs from single server function', ()
145146
// Navigate away
146147
await page.goto('/')
147148
await page.waitForURL('/')
148-
await expect(page.getByTestId('app-hydrated')).toHaveText('hydrated')
149+
await waitForHydration(page)
149150

150151
// Second visit (force reload)
151152
await page.goto('/rsc-bundle')

e2e/react-start/rsc/tests/rsc-caching.spec.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { expect } from '@playwright/test'
22
import { test } from '@tanstack/router-e2e-utils'
3+
import { waitForHydration } from './hydration'
34

45
test.describe('RSC Caching Tests - staleTime behavior', () => {
56
test('Page loads with cache controls visible', async ({ page }) => {
@@ -24,7 +25,7 @@ test.describe('RSC Caching Tests - staleTime behavior', () => {
2425
test('Force refresh button triggers RSC refetch', async ({ page }) => {
2526
await page.goto('/rsc-caching')
2627
await page.waitForURL('/rsc-caching')
27-
await expect(page.getByTestId('app-hydrated')).toHaveText('hydrated')
28+
await waitForHydration(page)
2829

2930
// Get initial timestamp
3031
const initialTimestamp = await page
@@ -51,7 +52,7 @@ test.describe('RSC Caching Tests - staleTime behavior', () => {
5152
test('Multiple force refreshes increment counter', async ({ page }) => {
5253
await page.goto('/rsc-caching')
5354
await page.waitForURL('/rsc-caching')
54-
await expect(page.getByTestId('app-hydrated')).toHaveText('hydrated')
55+
await waitForHydration(page)
5556

5657
// Click force refresh multiple times
5758
await page.getByTestId('force-refresh-btn').click()
@@ -75,7 +76,7 @@ test.describe('RSC Caching Tests - staleTime behavior', () => {
7576
}) => {
7677
await page.goto('/rsc-caching')
7778
await page.waitForURL('/rsc-caching')
78-
await expect(page.getByTestId('app-hydrated')).toHaveText('hydrated')
79+
await waitForHydration(page)
7980

8081
// Get initial timestamp
8182
const initialTimestamp = await page
@@ -104,7 +105,7 @@ test.describe('RSC Caching Tests - staleTime behavior', () => {
104105
test('Each force refresh updates the timestamp', async ({ page }) => {
105106
await page.goto('/rsc-caching')
106107
await page.waitForURL('/rsc-caching')
107-
await expect(page.getByTestId('app-hydrated')).toHaveText('hydrated')
108+
await waitForHydration(page)
108109

109110
const timestamps: number[] = []
110111

@@ -148,7 +149,7 @@ test.describe('RSC Caching Tests - staleTime behavior', () => {
148149
// Start at home
149150
await page.goto('/')
150151
await page.waitForURL('/')
151-
await expect(page.getByTestId('app-hydrated')).toHaveText('hydrated')
152+
await waitForHydration(page)
152153

153154
// Navigate to caching page via nav bar
154155
await page.getByTestId('nav-caching').click()

e2e/react-start/rsc/tests/rsc-client-preload.spec.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { expect } from '@playwright/test'
22
import { test } from '@tanstack/router-e2e-utils'
3+
import { waitForHydration } from './hydration'
34

45
test.describe('RSC Client Component Preload Tests', () => {
56
function getModulePreloads(html: string): Array<string> {
@@ -76,7 +77,7 @@ test.describe('RSC Client Component Preload Tests', () => {
7677
test('client component is interactive after hydration', async ({ page }) => {
7778
await page.goto('/rsc-client-preload')
7879
await page.waitForURL('/rsc-client-preload')
79-
await expect(page.getByTestId('app-hydrated')).toHaveText('hydrated')
80+
await waitForHydration(page)
8081

8182
// Verify initial count is 0
8283
await expect(page.getByTestId('client-widget-count')).toHaveText('0')
@@ -104,7 +105,7 @@ test.describe('RSC Client Component Preload Tests', () => {
104105

105106
await page.goto('/rsc-client-preload')
106107
await page.waitForURL('/rsc-client-preload')
107-
await expect(page.getByTestId('app-hydrated')).toHaveText('hydrated')
108+
await waitForHydration(page)
108109

109110
// Verify content is visible
110111
await expect(page.getByTestId('client-widget')).toBeVisible()

e2e/react-start/rsc/tests/rsc-component-slot.spec.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { expect } from '@playwright/test'
22
import { test } from '@tanstack/router-e2e-utils'
3+
import { waitForHydration } from './hydration'
34

45
test.describe('RSC Component Slot Tests - Passing components as props', () => {
56
test('Renders server product card with client component slots', async ({
67
page,
78
}) => {
89
await page.goto('/rsc-component-slot')
910
await page.waitForURL('/rsc-component-slot')
10-
await expect(page.getByTestId('app-hydrated')).toHaveText('hydrated')
11+
await waitForHydration(page)
1112

1213
// Verify server-rendered product card
1314
await expect(page.getByTestId('rsc-product-card')).toBeVisible()
@@ -27,7 +28,7 @@ test.describe('RSC Component Slot Tests - Passing components as props', () => {
2728
test('Changing quantity does not reload RSC', async ({ page }) => {
2829
await page.goto('/rsc-component-slot')
2930
await page.waitForURL('/rsc-component-slot')
30-
await expect(page.getByTestId('app-hydrated')).toHaveText('hydrated')
31+
await waitForHydration(page)
3132

3233
// Get initial RSC timestamp
3334
const initialTimestamp = await page
@@ -64,7 +65,7 @@ test.describe('RSC Component Slot Tests - Passing components as props', () => {
6465
test('Toggling badge details does not reload RSC', async ({ page }) => {
6566
await page.goto('/rsc-component-slot')
6667
await page.waitForURL('/rsc-component-slot')
67-
await expect(page.getByTestId('app-hydrated')).toHaveText('hydrated')
68+
await waitForHydration(page)
6869

6970
// Get initial RSC timestamp
7071
const initialTimestamp = await page
@@ -94,7 +95,7 @@ test.describe('RSC Component Slot Tests - Passing components as props', () => {
9495
test('Add to cart interaction does not reload RSC', async ({ page }) => {
9596
await page.goto('/rsc-component-slot')
9697
await page.waitForURL('/rsc-component-slot')
97-
await expect(page.getByTestId('app-hydrated')).toHaveText('hydrated')
98+
await waitForHydration(page)
9899

99100
// Get initial RSC timestamp
100101
const initialTimestamp = await page
@@ -129,7 +130,7 @@ test.describe('RSC Component Slot Tests - Passing components as props', () => {
129130
}) => {
130131
await page.goto('/rsc-component-slot')
131132
await page.waitForURL('/rsc-component-slot')
132-
await expect(page.getByTestId('app-hydrated')).toHaveText('hydrated')
133+
await waitForHydration(page)
133134

134135
// Get initial RSC timestamp
135136
const initialTimestamp = await page
@@ -159,7 +160,7 @@ test.describe('RSC Component Slot Tests - Passing components as props', () => {
159160
test('Reset quantity works without reloading RSC', async ({ page }) => {
160161
await page.goto('/rsc-component-slot')
161162
await page.waitForURL('/rsc-component-slot')
162-
await expect(page.getByTestId('app-hydrated')).toHaveText('hydrated')
163+
await waitForHydration(page)
163164

164165
// Get initial RSC timestamp
165166
const initialTimestamp = await page
@@ -191,7 +192,7 @@ test.describe('RSC Component Slot Tests - Passing components as props', () => {
191192
}) => {
192193
await page.goto('/rsc-component-slot')
193194
await page.waitForURL('/rsc-component-slot')
194-
await expect(page.getByTestId('app-hydrated')).toHaveText('hydrated')
195+
await waitForHydration(page)
195196

196197
// Verify server-provided data is passed to client components
197198
// Product ID comes from server and is displayed in client component

e2e/react-start/rsc/tests/rsc-context.spec.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { expect } from '@playwright/test'
22
import { test } from '@tanstack/router-e2e-utils'
3+
import { waitForHydration } from './hydration'
34

45
test.describe('RSC Context Tests - React Context interaction', () => {
56
test('Page loads with context controls visible', async ({ page }) => {
@@ -25,7 +26,7 @@ test.describe('RSC Context Tests - React Context interaction', () => {
2526
test('Theme toggle changes context', async ({ page }) => {
2627
await page.goto('/rsc-context')
2728
await page.waitForURL('/rsc-context')
28-
await expect(page.getByTestId('app-hydrated')).toHaveText('hydrated')
29+
await waitForHydration(page)
2930

3031
// Verify initial theme
3132
await expect(page.getByTestId('current-theme')).toContainText(
@@ -53,7 +54,7 @@ test.describe('RSC Context Tests - React Context interaction', () => {
5354
test('Notifications toggle works correctly', async ({ page }) => {
5455
await page.goto('/rsc-context')
5556
await page.waitForURL('/rsc-context')
56-
await expect(page.getByTestId('app-hydrated')).toHaveText('hydrated')
57+
await waitForHydration(page)
5758

5859
// Verify initial notifications state is ON
5960
await expect(page.getByTestId('toggle-notifications-btn')).toContainText(
@@ -83,7 +84,7 @@ test.describe('RSC Context Tests - React Context interaction', () => {
8384
test('Theme changes do not refetch RSC', async ({ page }) => {
8485
await page.goto('/rsc-context')
8586
await page.waitForURL('/rsc-context')
86-
await expect(page.getByTestId('app-hydrated')).toHaveText('hydrated')
87+
await waitForHydration(page)
8788

8889
// Get initial timestamp
8990
const initialTimestamp = await page
@@ -121,7 +122,7 @@ test.describe('RSC Context Tests - React Context interaction', () => {
121122
}) => {
122123
await page.goto('/rsc-context')
123124
await page.waitForURL('/rsc-context')
124-
await expect(page.getByTestId('app-hydrated')).toHaveText('hydrated')
125+
await waitForHydration(page)
125126

126127
// Verify context consumer is visible
127128
await expect(page.getByTestId('context-consumer')).toBeVisible()
@@ -144,7 +145,7 @@ test.describe('RSC Context Tests - React Context interaction', () => {
144145
test('Multiple context changes work correctly', async ({ page }) => {
145146
await page.goto('/rsc-context')
146147
await page.waitForURL('/rsc-context')
147-
await expect(page.getByTestId('app-hydrated')).toHaveText('hydrated')
148+
await waitForHydration(page)
148149

149150
// Change both theme and notifications
150151
await page.getByTestId('toggle-theme-btn').click()

e2e/react-start/rsc/tests/rsc-css-modules.spec.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { expect } from '@playwright/test'
22
import { test } from '@tanstack/router-e2e-utils'
3+
import { waitForHydration } from './hydration'
34

45
test.describe('RSC CSS Modules Tests', () => {
56
test('RSC with CSS modules hydrates without errors', async ({ page }) => {
@@ -13,7 +14,7 @@ test.describe('RSC CSS Modules Tests', () => {
1314

1415
await page.goto('/rsc-css-modules')
1516
await page.waitForURL('/rsc-css-modules')
16-
await expect(page.getByTestId('app-hydrated')).toHaveText('hydrated')
17+
await waitForHydration(page)
1718

1819
// Verify content is visible
1920
await expect(page.getByTestId('rsc-css-modules-content')).toBeVisible()
@@ -106,7 +107,7 @@ test.describe('RSC CSS Modules Tests', () => {
106107
}) => {
107108
await page.goto('/')
108109
await page.waitForURL('/')
109-
await expect(page.getByTestId('app-hydrated')).toHaveText('hydrated')
110+
await waitForHydration(page)
110111

111112
await page.getByTestId('nav-css-modules').click()
112113
await page.waitForURL('/rsc-css-modules')

e2e/react-start/rsc/tests/rsc-deferred.spec.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { expect } from '@playwright/test'
22
import { test } from '@tanstack/router-e2e-utils'
3+
import { waitForHydration } from './hydration'
34
const DEFERRED_DATA_TIMEOUT = 5000 // Deferred data takes ~2 seconds
45

56
test.describe('RSC Deferred Tests - RSC with streamed Promise data', () => {
@@ -64,7 +65,7 @@ test.describe('RSC Deferred Tests - RSC with streamed Promise data', () => {
6465
await expect(page.getByTestId('analytics-display')).toBeVisible({
6566
timeout: DEFERRED_DATA_TIMEOUT,
6667
})
67-
await expect(page.getByTestId('app-hydrated')).toHaveText('hydrated')
68+
await waitForHydration(page)
6869

6970
// Initially no metric selected
7071
await expect(page.getByTestId('selected-metric')).not.toBeVisible()
@@ -126,7 +127,7 @@ test.describe('RSC Deferred Tests - RSC with streamed Promise data', () => {
126127
await expect(page.getByTestId('analytics-display')).toBeVisible({
127128
timeout: DEFERRED_DATA_TIMEOUT,
128129
})
129-
await expect(page.getByTestId('app-hydrated')).toHaveText('hydrated')
130+
await waitForHydration(page)
130131

131132
// Get initial values
132133
const initialTimestamp = await page
@@ -158,7 +159,7 @@ test.describe('RSC Deferred Tests - RSC with streamed Promise data', () => {
158159
// Start at home
159160
await page.goto('/')
160161
await page.waitForURL('/')
161-
await expect(page.getByTestId('app-hydrated')).toHaveText('hydrated')
162+
await waitForHydration(page)
162163

163164
// Navigate to deferred page via nav bar
164165
await page.getByTestId('nav-deferred').click()

0 commit comments

Comments
 (0)