1
- import { afterEach , describe , expect , it , vi } from 'vitest'
1
+ import { afterEach , describe , expect , test , vi } from 'vitest'
2
2
import { act , cleanup , render , screen , waitFor } from '@testing-library/react'
3
3
import {
4
4
Link ,
@@ -7,25 +7,46 @@ import {
7
7
createRootRoute ,
8
8
createRoute ,
9
9
createRouter ,
10
+ redirect ,
10
11
useRouterState ,
11
12
} from '../src'
12
- import type { RouteComponent } from '../src'
13
13
14
14
afterEach ( ( ) => {
15
15
window . history . replaceState ( null , 'root' , '/' )
16
16
cleanup ( )
17
17
} )
18
18
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
+
20
38
const rootRoute = createRootRoute ( {
21
- component : RootComponent ,
39
+ component : function RootComponent ( ) {
40
+ useRouterState ( { select } )
41
+ return < Outlet />
42
+ } ,
22
43
} )
23
44
const indexRoute = createRoute ( {
24
45
getParentRoute : ( ) => rootRoute ,
25
46
path : '/' ,
26
47
component : ( ) => (
27
48
< >
28
- < h1 > IndexTitle </ h1 >
49
+ < h1 > Index </ h1 >
29
50
< Link to = "/posts" > Posts</ Link >
30
51
</ >
31
52
) ,
@@ -34,45 +55,68 @@ function setup({ RootComponent }: { RootComponent: RouteComponent }) {
34
55
const postsRoute = createRoute ( {
35
56
getParentRoute : ( ) => rootRoute ,
36
57
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 > ,
40
70
} )
41
71
42
72
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,
46
76
defaultPendingComponent : ( ) => < p > Loading...</ p > ,
47
77
} )
48
78
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 : / T i t l e $ / } ) ,
87
+ ) // matches /posts and /other
88
+ expect ( title ) . toBeInTheDocument ( )
89
+ const after = select . mock . calls . length
90
+
91
+ return after - before
50
92
}
51
93
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
+ } )
55
103
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' } )
60
114
} ,
61
115
} )
62
116
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
-
73
117
// This number should be as small as possible to minimize the amount of work
74
118
// that needs to be done during a navigation.
75
119
// Any change that increases this number should be investigated.
76
- expect ( after - before ) . toBe ( 19 )
120
+ expect ( updates ) . toBe ( 26 )
77
121
} )
78
122
} )
0 commit comments