11import { StrictMode , Suspense , useState } from 'react'
2- import { act , render , screen , waitFor } from '@testing-library/react'
3- import userEventOrig from '@testing-library/user-event'
4- import { describe , expect , it } from 'vitest'
2+ import { act , fireEvent , render , screen } from '@testing-library/react'
3+ import { afterEach , beforeEach , describe , expect , it , vi } from 'vitest'
54import { useAtomValue , useSetAtom } from 'jotai/react'
65import { atom } from 'jotai/vanilla'
76
8- const userEvent = {
9- click : ( element : Element ) => act ( ( ) => userEventOrig . click ( element ) ) ,
10- }
7+ beforeEach ( ( ) => {
8+ vi . useFakeTimers ( )
9+ } )
10+
11+ afterEach ( ( ) => {
12+ vi . useRealTimers ( )
13+ } )
1114
1215describe ( 'abortable atom test' , ( ) => {
1316 it ( 'can abort with signal.aborted' , async ( ) => {
1417 const countAtom = atom ( 0 )
1518 let abortedCount = 0
16- const resolve : ( ( ) => void ) [ ] = [ ]
1719 const derivedAtom = atom ( async ( get , { signal } ) => {
1820 const count = get ( countAtom )
19- await new Promise < void > ( ( r ) => resolve . push ( r ) )
21+ await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) )
2022 if ( signal . aborted ) {
2123 ++ abortedCount
2224 }
@@ -37,47 +39,51 @@ describe('abortable atom test', () => {
3739 )
3840 }
3941
40- await act ( async ( ) => {
42+ await act ( ( ) =>
4143 render (
4244 < StrictMode >
4345 < Suspense fallback = "loading" >
4446 < Component />
4547 < Controls />
4648 </ Suspense >
4749 </ StrictMode > ,
48- )
49- } )
50+ ) ,
51+ )
5052
51- expect ( await screen . findByText ( 'loading' ) ) . toBeInTheDocument ( )
53+ expect ( screen . getByText ( 'loading' ) ) . toBeInTheDocument ( )
54+
55+ await act ( ( ) => vi . advanceTimersByTimeAsync ( 100 ) )
56+ expect ( screen . getByText ( 'count: 0' ) ) . toBeInTheDocument ( )
5257
53- resolve . splice ( 0 ) . forEach ( ( fn ) => fn ( ) )
54- expect ( await screen . findByText ( 'count: 0' ) ) . toBeInTheDocument ( )
5558 expect ( abortedCount ) . toBe ( 0 )
5659
57- await userEvent . click ( screen . getByText ( 'button' ) )
58- await userEvent . click ( screen . getByText ( 'button' ) )
59- resolve . splice ( 0 ) . forEach ( ( fn ) => fn ( ) )
60- expect ( await screen . findByText ( 'count: 2' ) ) . toBeInTheDocument ( )
60+ await act ( ( ) => fireEvent . click ( screen . getByText ( 'button' ) ) )
61+ expect ( screen . getByText ( 'loading' ) ) . toBeInTheDocument ( )
62+ await act ( ( ) => fireEvent . click ( screen . getByText ( 'button' ) ) )
63+ expect ( screen . getByText ( 'loading' ) ) . toBeInTheDocument ( )
64+ await act ( ( ) => vi . advanceTimersByTimeAsync ( 100 ) )
65+ expect ( screen . getByText ( 'count: 2' ) ) . toBeInTheDocument ( )
6166
6267 expect ( abortedCount ) . toBe ( 1 )
6368
64- await userEvent . click ( screen . getByText ( 'button' ) )
65- resolve . splice ( 0 ) . forEach ( ( fn ) => fn ( ) )
66- expect ( await screen . findByText ( 'count: 3' ) ) . toBeInTheDocument ( )
69+ await act ( ( ) => fireEvent . click ( screen . getByText ( 'button' ) ) )
70+ expect ( screen . getByText ( 'loading' ) ) . toBeInTheDocument ( )
71+ await act ( ( ) => vi . advanceTimersByTimeAsync ( 100 ) )
72+ expect ( screen . getByText ( 'count: 3' ) ) . toBeInTheDocument ( )
73+
6774 expect ( abortedCount ) . toBe ( 1 )
6875 } )
6976
7077 it ( 'can abort with event listener' , async ( ) => {
7178 const countAtom = atom ( 0 )
7279 let abortedCount = 0
73- const resolve : ( ( ) => void ) [ ] = [ ]
7480 const derivedAtom = atom ( async ( get , { signal } ) => {
7581 const count = get ( countAtom )
7682 const callback = ( ) => {
7783 ++ abortedCount
7884 }
7985 signal . addEventListener ( 'abort' , callback )
80- await new Promise < void > ( ( r ) => resolve . push ( r ) )
86+ await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) )
8187 signal . removeEventListener ( 'abort' , callback )
8288 return count
8389 } )
@@ -96,44 +102,47 @@ describe('abortable atom test', () => {
96102 )
97103 }
98104
99- await act ( async ( ) => {
105+ await act ( ( ) =>
100106 render (
101107 < StrictMode >
102108 < Suspense fallback = "loading" >
103109 < Component />
104110 < Controls />
105111 </ Suspense >
106112 </ StrictMode > ,
107- )
108- } )
113+ ) ,
114+ )
109115
110- expect ( await screen . findByText ( 'loading' ) ) . toBeInTheDocument ( )
111- resolve . splice ( 0 ) . forEach ( ( fn ) => fn ( ) )
112- expect ( await screen . findByText ( 'count: 0' ) ) . toBeInTheDocument ( )
116+ expect ( screen . getByText ( 'loading' ) ) . toBeInTheDocument ( )
117+
118+ await act ( ( ) => vi . advanceTimersByTimeAsync ( 100 ) )
119+ expect ( screen . getByText ( 'count: 0' ) ) . toBeInTheDocument ( )
113120
114121 expect ( abortedCount ) . toBe ( 0 )
115122
116- await userEvent . click ( screen . getByText ( 'button' ) )
117- await userEvent . click ( screen . getByText ( 'button' ) )
118- resolve . splice ( 0 ) . forEach ( ( fn ) => fn ( ) )
119- expect ( await screen . findByText ( 'count: 2' ) ) . toBeInTheDocument ( )
123+ await act ( ( ) => fireEvent . click ( screen . getByText ( 'button' ) ) )
124+ expect ( screen . getByText ( 'loading' ) ) . toBeInTheDocument ( )
125+ await act ( ( ) => fireEvent . click ( screen . getByText ( 'button' ) ) )
126+ expect ( screen . getByText ( 'loading' ) ) . toBeInTheDocument ( )
127+ await act ( ( ) => vi . advanceTimersByTimeAsync ( 100 ) )
128+ expect ( screen . getByText ( 'count: 2' ) ) . toBeInTheDocument ( )
120129
121130 expect ( abortedCount ) . toBe ( 1 )
122131
123- await userEvent . click ( screen . getByText ( 'button' ) )
124- resolve . splice ( 0 ) . forEach ( ( fn ) => fn ( ) )
125- expect ( await screen . findByText ( 'count: 3' ) ) . toBeInTheDocument ( )
132+ await act ( ( ) => fireEvent . click ( screen . getByText ( 'button' ) ) )
133+ expect ( screen . getByText ( 'loading' ) ) . toBeInTheDocument ( )
134+ await act ( ( ) => vi . advanceTimersByTimeAsync ( 100 ) )
135+ expect ( screen . getByText ( 'count: 3' ) ) . toBeInTheDocument ( )
126136
127137 expect ( abortedCount ) . toBe ( 1 )
128138 } )
129139
130140 it ( 'does not abort on unmount' , async ( ) => {
131141 const countAtom = atom ( 0 )
132142 let abortedCount = 0
133- const resolve : ( ( ) => void ) [ ] = [ ]
134143 const derivedAtom = atom ( async ( get , { signal } ) => {
135144 const count = get ( countAtom )
136- await new Promise < void > ( ( r ) => resolve . push ( r ) )
145+ await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) )
137146 if ( signal . aborted ) {
138147 ++ abortedCount
139148 }
@@ -157,37 +166,33 @@ describe('abortable atom test', () => {
157166 )
158167 }
159168
160- await act ( async ( ) => {
169+ await act ( ( ) =>
161170 render (
162171 < StrictMode >
163172 < Suspense fallback = "loading" >
164173 < Parent />
165174 </ Suspense >
166175 </ StrictMode > ,
167- )
168- } )
176+ ) ,
177+ )
169178
170- expect ( await screen . findByText ( 'loading' ) ) . toBeInTheDocument ( )
171-
172- resolve . splice ( 0 ) . forEach ( ( fn ) => fn ( ) )
173- expect ( await screen . findByText ( 'count: 0' ) ) . toBeInTheDocument ( )
174- expect ( abortedCount ) . toBe ( 0 )
179+ expect ( screen . getByText ( 'loading' ) ) . toBeInTheDocument ( )
180+ await act ( ( ) => vi . advanceTimersByTimeAsync ( 100 ) )
181+ expect ( screen . getByText ( 'count: 0' ) ) . toBeInTheDocument ( )
175182
176- await userEvent . click ( screen . getByText ( 'button' ) )
177- await userEvent . click ( screen . getByText ( 'toggle' ) )
183+ await act ( ( ) => fireEvent . click ( screen . getByText ( 'button' ) ) )
184+ expect ( screen . getByText ( 'loading' ) ) . toBeInTheDocument ( )
185+ await act ( ( ) => fireEvent . click ( screen . getByText ( 'toggle' ) ) )
186+ expect ( screen . getByText ( 'hidden' ) ) . toBeInTheDocument ( )
178187
179- expect ( await screen . findByText ( 'hidden' ) ) . toBeInTheDocument ( )
180-
181- resolve . splice ( 0 ) . forEach ( ( fn ) => fn ( ) )
182- await waitFor ( ( ) => expect ( abortedCount ) . toBe ( 0 ) )
188+ expect ( abortedCount ) . toBe ( 0 )
183189 } )
184190
185191 it ( 'throws aborted error (like fetch)' , async ( ) => {
186192 const countAtom = atom ( 0 )
187- const resolve : ( ( ) => void ) [ ] = [ ]
188193 const derivedAtom = atom ( async ( get , { signal } ) => {
189194 const count = get ( countAtom )
190- await new Promise < void > ( ( r ) => resolve . push ( r ) )
195+ await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) )
191196 if ( signal . aborted ) {
192197 throw new Error ( 'aborted' )
193198 }
@@ -208,29 +213,32 @@ describe('abortable atom test', () => {
208213 )
209214 }
210215
211- await act ( async ( ) => {
216+ await act ( ( ) =>
212217 render (
213218 < StrictMode >
214219 < Suspense fallback = "loading" >
215220 < Component />
216221 < Controls />
217222 </ Suspense >
218223 </ StrictMode > ,
219- )
220- } )
224+ ) ,
225+ )
221226
222- expect ( await screen . findByText ( 'loading' ) ) . toBeInTheDocument ( )
227+ expect ( screen . getByText ( 'loading' ) ) . toBeInTheDocument ( )
223228
224- resolve . splice ( 0 ) . forEach ( ( fn ) => fn ( ) )
225- expect ( await screen . findByText ( 'count: 0' ) ) . toBeInTheDocument ( )
229+ await act ( ( ) => vi . advanceTimersByTimeAsync ( 100 ) )
230+ expect ( screen . getByText ( 'count: 0' ) ) . toBeInTheDocument ( )
226231
227- await userEvent . click ( screen . getByText ( 'button' ) )
228- await userEvent . click ( screen . getByText ( 'button' ) )
229- resolve . splice ( 0 ) . forEach ( ( fn ) => fn ( ) )
230- expect ( await screen . findByText ( 'count: 2' ) ) . toBeInTheDocument ( )
232+ await act ( ( ) => fireEvent . click ( screen . getByText ( 'button' ) ) )
233+ expect ( screen . getByText ( 'loading' ) ) . toBeInTheDocument ( )
234+ await act ( ( ) => fireEvent . click ( screen . getByText ( 'button' ) ) )
235+ expect ( screen . getByText ( 'loading' ) ) . toBeInTheDocument ( )
236+ await act ( ( ) => vi . advanceTimersByTimeAsync ( 100 ) )
237+ expect ( screen . getByText ( 'count: 2' ) ) . toBeInTheDocument ( )
231238
232- await userEvent . click ( screen . getByText ( 'button' ) )
233- resolve . splice ( 0 ) . forEach ( ( fn ) => fn ( ) )
234- expect ( await screen . findByText ( 'count: 3' ) ) . toBeInTheDocument ( )
239+ await act ( ( ) => fireEvent . click ( screen . getByText ( 'button' ) ) )
240+ expect ( screen . getByText ( 'loading' ) ) . toBeInTheDocument ( )
241+ await act ( ( ) => vi . advanceTimersByTimeAsync ( 100 ) )
242+ expect ( screen . getByText ( 'count: 3' ) ) . toBeInTheDocument ( )
235243 } )
236244} )
0 commit comments