Skip to content

Commit 3c3497a

Browse files
test(react-router): improve store-updates-during-navigation tests (#4957)
The `store-updates-during-navigation.test.tsx` file tests how many times `updateMatch` is called during a navigation (i.e. how many times the store is updated, and all subscribers re-run). In this PR we clean up this test file to allow us to more easily test various navigation types. We also add a test for "redirection inside beforeLoad" whose result will be improved by #4925 --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent 6999fd9 commit 3c3497a

File tree

1 file changed

+74
-30
lines changed

1 file changed

+74
-30
lines changed
Lines changed: 74 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { afterEach, describe, expect, it, vi } from 'vitest'
1+
import { afterEach, describe, expect, test, vi } from 'vitest'
22
import { act, cleanup, render, screen, waitFor } from '@testing-library/react'
33
import {
44
Link,
@@ -7,25 +7,46 @@ import {
77
createRootRoute,
88
createRoute,
99
createRouter,
10+
redirect,
1011
useRouterState,
1112
} from '../src'
12-
import type { RouteComponent } from '../src'
1313

1414
afterEach(() => {
1515
window.history.replaceState(null, 'root', '/')
1616
cleanup()
1717
})
1818

19-
function setup({ RootComponent }: { RootComponent: RouteComponent }) {
19+
async function setupAndRun({
20+
beforeLoad,
21+
loader,
22+
head,
23+
headers,
24+
scripts,
25+
defaultPendingMs,
26+
defaultPendingMinMs,
27+
}: {
28+
beforeLoad?: () => any
29+
loader?: () => any
30+
head?: () => any
31+
headers?: () => any
32+
scripts?: () => any
33+
defaultPendingMs?: number
34+
defaultPendingMinMs?: number
35+
}) {
36+
const select = vi.fn()
37+
2038
const rootRoute = createRootRoute({
21-
component: RootComponent,
39+
component: function RootComponent() {
40+
useRouterState({ select })
41+
return <Outlet />
42+
},
2243
})
2344
const indexRoute = createRoute({
2445
getParentRoute: () => rootRoute,
2546
path: '/',
2647
component: () => (
2748
<>
28-
<h1>IndexTitle</h1>
49+
<h1>Index</h1>
2950
<Link to="/posts">Posts</Link>
3051
</>
3152
),
@@ -34,45 +55,68 @@ function setup({ RootComponent }: { RootComponent: RouteComponent }) {
3455
const postsRoute = createRoute({
3556
getParentRoute: () => rootRoute,
3657
path: '/posts',
37-
beforeLoad: () => new Promise<void>((resolve) => setTimeout(resolve, 100)),
38-
loader: () => new Promise<void>((resolve) => setTimeout(resolve, 100)),
39-
component: () => <h1>PostsTitle</h1>,
58+
beforeLoad,
59+
loader,
60+
head,
61+
headers,
62+
scripts,
63+
component: () => <h1>Posts Title</h1>,
64+
})
65+
66+
const otherRoute = createRoute({
67+
getParentRoute: () => rootRoute,
68+
path: '/other',
69+
component: () => <h1>Other Title</h1>,
4070
})
4171

4272
const router = createRouter({
43-
routeTree: rootRoute.addChildren([indexRoute, postsRoute]),
44-
defaultPendingMs: 100,
45-
defaultPendingMinMs: 300,
73+
routeTree: rootRoute.addChildren([indexRoute, postsRoute, otherRoute]),
74+
defaultPendingMs,
75+
defaultPendingMinMs,
4676
defaultPendingComponent: () => <p>Loading...</p>,
4777
})
4878

49-
return render(<RouterProvider router={router} />)
79+
render(<RouterProvider router={router} />)
80+
81+
// navigate to /posts
82+
const link = await waitFor(() => screen.getByRole('link', { name: 'Posts' }))
83+
const before = select.mock.calls.length
84+
act(() => link.click())
85+
const title = await waitFor(() =>
86+
screen.getByRole('heading', { name: /Title$/ }),
87+
) // matches /posts and /other
88+
expect(title).toBeInTheDocument()
89+
const after = select.mock.calls.length
90+
91+
return after - before
5092
}
5193

52-
describe('Store updates during navigation', () => {
53-
it("isn't called *too many* times", async () => {
54-
const select = vi.fn()
94+
describe("Store doesn't update *too many* times during navigation", () => {
95+
test('async loader, async beforeLoad, pendingMs', async () => {
96+
const updates = await setupAndRun({
97+
beforeLoad: () =>
98+
new Promise<void>((resolve) => setTimeout(resolve, 100)),
99+
loader: () => new Promise<void>((resolve) => setTimeout(resolve, 100)),
100+
defaultPendingMs: 100,
101+
defaultPendingMinMs: 300,
102+
})
55103

56-
setup({
57-
RootComponent: () => {
58-
useRouterState({ select })
59-
return <Outlet />
104+
// This number should be as small as possible to minimize the amount of work
105+
// that needs to be done during a navigation.
106+
// Any change that increases this number should be investigated.
107+
expect(updates).toBe(19)
108+
})
109+
110+
test('redirection', async () => {
111+
const updates = await setupAndRun({
112+
beforeLoad: () => {
113+
throw redirect({ to: '/other' })
60114
},
61115
})
62116

63-
// navigate to /posts
64-
const link = await waitFor(() =>
65-
screen.getByRole('link', { name: 'Posts' }),
66-
)
67-
const before = select.mock.calls.length
68-
act(() => link.click())
69-
const title = await waitFor(() => screen.getByText('PostsTitle'))
70-
expect(title).toBeInTheDocument()
71-
const after = select.mock.calls.length
72-
73117
// This number should be as small as possible to minimize the amount of work
74118
// that needs to be done during a navigation.
75119
// Any change that increases this number should be investigated.
76-
expect(after - before).toBe(19)
120+
expect(updates).toBe(26)
77121
})
78122
})

0 commit comments

Comments
 (0)