Skip to content

Commit 4bd2237

Browse files
committed
migrate test
1 parent 48b63dc commit 4bd2237

File tree

10 files changed

+133
-49
lines changed

10 files changed

+133
-49
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
'use client'
2+
3+
import { Suspense } from 'react'
4+
import dynamic from 'next/dynamic'
5+
6+
const TransitionDemo = dynamic(() => import('./transition-demo'), {
7+
ssr: false
8+
})
9+
10+
export default function ConcurrentTransitionPage() {
11+
return (
12+
<div style={{ padding: '20px', maxWidth: '800px', margin: '0 auto' }}>
13+
<h1>React 19 Concurrent Transition Test</h1>
14+
<p>
15+
This page tests SWR&apos;s behavior with React 19 concurrent
16+
transitions. When using useTransition, SWR should &quot;pause&quot;
17+
loading states to provide smooth UX.
18+
</p>
19+
<Suspense fallback={<div>Loading page...</div>}>
20+
<TransitionDemo />
21+
</Suspense>
22+
</div>
23+
)
24+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
'use client'
2+
3+
import React, { useState, useTransition, Suspense, useCallback } from 'react'
4+
import useSWR from 'swr'
5+
6+
// Simulate API data fetching with delay
7+
const fetcher = async (key: string): Promise<string> => {
8+
// Slightly longer delay to make transition behavior more observable
9+
await new Promise(resolve => setTimeout(resolve, 150))
10+
return key
11+
}
12+
13+
// Component that uses SWR with suspense
14+
function DataComponent({ swrKey }: { swrKey: string }) {
15+
const { data } = useSWR(swrKey, fetcher, {
16+
dedupingInterval: 0,
17+
suspense: true,
18+
// React 19 improvements for concurrent features
19+
keepPreviousData: false
20+
})
21+
22+
return <span data-testid="data-content">data:{data}</span>
23+
}
24+
25+
export default function TransitionDemo() {
26+
const [isPending, startTransition] = useTransition()
27+
const [key, setKey] = useState('initial-key')
28+
29+
const handleTransition = useCallback(() => {
30+
startTransition(() => {
31+
setKey('new-key')
32+
})
33+
}, [])
34+
35+
return (
36+
<div>
37+
<h2>React 19 Concurrent Transition Demo</h2>
38+
<div
39+
onClick={handleTransition}
40+
data-testid="transition-trigger"
41+
style={{
42+
cursor: 'pointer',
43+
padding: '20px',
44+
border: '1px solid #ccc',
45+
borderRadius: '4px',
46+
backgroundColor: isPending ? '#f0f0f0' : '#fff'
47+
}}
48+
>
49+
<div data-testid="pending-state">isPending:{isPending ? '1' : '0'}</div>
50+
<Suspense
51+
fallback={<span data-testid="loading-fallback">loading</span>}
52+
>
53+
<DataComponent swrKey={key} />
54+
</Suspense>
55+
<p style={{ fontSize: '12px', color: '#666', marginTop: '10px' }}>
56+
Click to test concurrent transition behavior
57+
</p>
58+
</div>
59+
</div>
60+
)
61+
}

e2e/site/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,6 @@
1616
"react": "^19.1.0",
1717
"react-dom": "^19.1.0",
1818
"typescript": "5.1.3",
19-
"swr": "*"
19+
"swr": "link:../../"
2020
}
2121
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/* eslint-disable testing-library/prefer-screen-queries */
2+
import { test, expect } from '@playwright/test'
3+
4+
test.describe('concurrent rendering transitions', () => {
5+
test('should pause when changing the key inside a transition', async ({
6+
page
7+
}) => {
8+
// Navigate to the test page
9+
await page.goto('./concurrent-transition', { waitUntil: 'networkidle' })
10+
11+
// Wait for page to be fully loaded and interactive
12+
await expect(page.getByTestId('pending-state')).toContainText('isPending:0')
13+
14+
// Wait for initial data to load
15+
await expect(page.getByTestId('data-content')).toContainText(
16+
'data:initial-key'
17+
)
18+
19+
// Ensure the component is in a stable state before triggering transition
20+
await page.waitForTimeout(100)
21+
22+
// Click to trigger transition
23+
await page.getByTestId('transition-trigger').click()
24+
25+
// Verify transition starts - isPending becomes true
26+
await expect(page.getByTestId('pending-state')).toContainText('isPending:1')
27+
28+
// During transition: data should still show old value (this is the key behavior)
29+
// In React 19, this behavior should be more consistent
30+
await expect(page.getByTestId('data-content')).toContainText(
31+
'data:initial-key'
32+
)
33+
34+
// Wait for transition to complete
35+
await expect(page.getByTestId('pending-state')).toContainText('isPending:0')
36+
await expect(page.getByTestId('data-content')).toContainText('data:new-key')
37+
})
38+
})

e2e/test/initial-render.test.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,32 @@ test.describe('rendering', () => {
77
await page.getByRole('button', { name: 'preload' }).click()
88
await expect(page.getByText('suspense-after-preload')).toBeVisible()
99
})
10-
test('should be able to retry in suspense with react 18.3', async ({
10+
11+
test('should be able to retry in suspense with react 19 (app router)', async ({
1112
page
1213
}) => {
13-
await page.goto('./suspense-retry-18-3', { waitUntil: 'commit' })
14+
await page.goto('./suspense-retry', { waitUntil: 'commit' })
1415
await expect(page.getByText('Something went wrong')).toBeVisible()
1516
await page.getByRole('button', { name: 'retry' }).click()
1617
await expect(page.getByText('data: SWR suspense retry works')).toBeVisible()
1718
})
18-
test('should be able to retry in suspense with react 18.2', async ({
19+
20+
test('should be able to retry in suspense with react 19 (pages router)', async ({
1921
page
2022
}) => {
21-
await page.goto('./suspense-retry-18-2', { waitUntil: 'commit' })
23+
await page.goto('./suspense-retry-19', { waitUntil: 'commit' })
2224
await expect(page.getByText('Something went wrong')).toBeVisible()
2325
await page.getByRole('button', { name: 'retry' }).click()
2426
await expect(page.getByText('data: SWR suspense retry works')).toBeVisible()
2527
})
28+
2629
test('should be able to retry in suspense with mutate', async ({ page }) => {
2730
await page.goto('./suspense-retry-mutate', { waitUntil: 'commit' })
2831
await expect(page.getByText('Something went wrong')).toBeVisible()
2932
await page.getByRole('button', { name: 'retry' }).click()
3033
await expect(page.getByText('data: SWR suspense retry works')).toBeVisible()
3134
})
35+
3236
test('should be able to use `unstable_serialize` in server component', async ({
3337
page
3438
}) => {

test/use-swr-concurrent-rendering.test.tsx

Lines changed: 1 addition & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { screen, fireEvent, act } from '@testing-library/react'
1+
import { screen, act } from '@testing-library/react'
22
import {
33
createKey,
44
createResponse,
@@ -27,49 +27,6 @@ describe('useSWR - concurrent rendering', () => {
2727
screen.getByText('data:0')
2828
})
2929

30-
it.skip('should pause when changing the key inside a transition', async () => {
31-
const initialKey = createKey()
32-
const newKey = createKey()
33-
const fetcher = (k: string) => createResponse(k, { delay: 100 })
34-
// eslint-disable-next-line react/prop-types
35-
function Component({ swrKey }) {
36-
const { data } = useSWR(swrKey, fetcher, {
37-
dedupingInterval: 0,
38-
suspense: true
39-
})
40-
41-
return <>data:{data}</>
42-
}
43-
function Page() {
44-
const [isPending, startTransition] = React.useTransition()
45-
const [key, setKey] = React.useState(initialKey)
46-
47-
return (
48-
<div onClick={() => startTransition(() => setKey(newKey))}>
49-
isPending:{isPending ? 1 : 0},
50-
<React.Suspense fallback="loading">
51-
<Component swrKey={key} />
52-
</React.Suspense>
53-
</div>
54-
)
55-
}
56-
57-
renderWithConfig(<Page />)
58-
59-
screen.getByText('isPending:0,loading')
60-
await act(() => sleep(120))
61-
screen.getByText(`isPending:0,data:${initialKey}`)
62-
fireEvent.click(screen.getByText(`isPending:0,data:${initialKey}`))
63-
await act(() => sleep(10))
64-
65-
// Pending state
66-
screen.getByText(`isPending:1,data:${initialKey}`)
67-
68-
// Transition end
69-
await act(() => sleep(120))
70-
screen.getByText(`isPending:0,data:${newKey}`)
71-
})
72-
7330
// https://codesandbox.io/s/concurrent-swr-case-ii-lr6x4u
7431
it.skip('should do state updates in transitions', async () => {
7532
const key1 = createKey()

0 commit comments

Comments
 (0)