Skip to content

Commit f5f3252

Browse files
authored
test: refactor use-swr-refresh.test.tsx (#1103)
* test: use fake timers for refresh tests * test: stop using container that the render function returns * chore: reset timers on each test
1 parent 7185877 commit f5f3252

File tree

1 file changed

+107
-106
lines changed

1 file changed

+107
-106
lines changed

test/use-swr-refresh.test.tsx

Lines changed: 107 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,18 @@ import React, { useState } from 'react'
33
import useSWR, { cache } from '../src'
44
import { sleep } from './utils'
55

6+
// This has to be an async function to wait a microtask to flush updates
7+
const advanceTimers = async (ms: number) => jest.advanceTimersByTime(ms)
8+
9+
// This test heavily depends on setInterval/setTimeout timers, which makes tests slower and flaky.
10+
// So we use Jest's fake timers
611
describe('useSWR - refresh', () => {
12+
beforeEach(() => {
13+
jest.useFakeTimers()
14+
})
15+
afterEach(() => {
16+
jest.useRealTimers()
17+
})
718
it('should rerender automatically on interval', async () => {
819
let count = 0
920

@@ -14,20 +25,21 @@ describe('useSWR - refresh', () => {
1425
})
1526
return <div>count: {data}</div>
1627
}
17-
const { container } = render(<Page />)
28+
29+
render(<Page />)
1830

1931
// hydration
20-
expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: "`)
32+
screen.getByText('count:')
2133

2234
// mount
2335
await screen.findByText('count: 0')
2436

25-
await act(() => sleep(210)) // update
26-
expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: 1"`)
27-
await act(() => sleep(50)) // no update
28-
expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: 1"`)
29-
await act(() => sleep(150)) // update
30-
expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: 2"`)
37+
await act(() => advanceTimers(200)) // update
38+
screen.getByText('count: 1')
39+
await act(() => advanceTimers(50)) // no update
40+
screen.getByText('count: 1')
41+
await act(() => advanceTimers(150)) // update
42+
screen.getByText('count: 2')
3143
})
3244

3345
it('should dedupe requests combined with intervals', async () => {
@@ -36,26 +48,29 @@ describe('useSWR - refresh', () => {
3648
function Page() {
3749
const { data } = useSWR('dynamic-2', () => count++, {
3850
refreshInterval: 100,
39-
dedupingInterval: 150
51+
dedupingInterval: 500
4052
})
4153
return <div>count: {data}</div>
4254
}
43-
const { container } = render(<Page />)
55+
56+
render(<Page />)
4457

4558
// hydration
46-
expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: "`)
59+
screen.getByText('count:')
4760

4861
// mount
4962
await screen.findByText('count: 0')
5063

51-
await act(() => sleep(110)) // no update (deduped)
52-
expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: 0"`)
53-
await act(() => sleep(100)) // update
54-
expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: 1"`)
55-
await act(() => sleep(100)) // no update (deduped)
56-
expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: 1"`)
57-
await act(() => sleep(100)) // update
58-
expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: 2"`)
64+
await act(() => advanceTimers(100)) // no update (deduped)
65+
screen.getByText('count: 0')
66+
await act(() => advanceTimers(400)) // reach dudupingInterval
67+
await act(() => advanceTimers(100)) // update
68+
screen.getByText('count: 1')
69+
await act(() => advanceTimers(100)) // no update (deduped)
70+
screen.getByText('count: 1')
71+
await act(() => advanceTimers(400)) // reach dudupingInterval
72+
await act(() => advanceTimers(100)) // update
73+
screen.getByText('count: 2')
5974
})
6075

6176
it('should update data upon interval changes', async () => {
@@ -72,46 +87,47 @@ describe('useSWR - refresh', () => {
7287
</div>
7388
)
7489
}
75-
const { container } = render(<Page />)
76-
expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: "`)
90+
91+
render(<Page />)
92+
screen.getByText('count:')
7793

7894
// mount
7995
await screen.findByText('count: 0')
8096

81-
await act(() => sleep(110))
82-
expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: 1"`)
83-
await act(() => sleep(25))
84-
expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: 1"`)
85-
await act(() => sleep(75))
86-
expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: 2"`)
87-
fireEvent.click(container.firstElementChild)
97+
await act(() => advanceTimers(100))
98+
screen.getByText('count: 1')
99+
await act(() => advanceTimers(50))
100+
screen.getByText('count: 1')
101+
await act(() => advanceTimers(50))
102+
screen.getByText('count: 2')
103+
fireEvent.click(screen.getByText('count: 2'))
88104

89-
await act(() => sleep(100))
105+
await act(() => advanceTimers(100))
90106

91-
expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: 2"`)
107+
screen.getByText('count: 2')
92108

93-
await act(() => sleep(60))
109+
await act(() => advanceTimers(50))
94110

95-
expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: 3"`)
111+
screen.getByText('count: 3')
96112

97-
await act(() => sleep(160))
98-
expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: 4"`)
99-
fireEvent.click(container.firstElementChild)
113+
await act(() => advanceTimers(150))
114+
screen.getByText('count: 4')
115+
fireEvent.click(screen.getByText('count: 4'))
100116
await act(() => {
101117
// it will clear 150ms timer and setup a new 200ms timer
102-
return sleep(150)
118+
return advanceTimers(150)
103119
})
104-
expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: 4"`)
105-
await act(() => sleep(60))
106-
expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: 5"`)
107-
fireEvent.click(container.firstElementChild)
120+
screen.getByText('count: 4')
121+
await act(() => advanceTimers(50))
122+
screen.getByText('count: 5')
123+
fireEvent.click(screen.getByText('count: 5'))
108124
await act(() => {
109125
// it will clear 200ms timer and stop
110-
return sleep(60)
126+
return advanceTimers(50)
111127
})
112-
expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: 5"`)
113-
await act(() => sleep(60))
114-
expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: 5"`)
128+
screen.getByText('count: 5')
129+
await act(() => advanceTimers(50))
130+
screen.getByText('count: 5')
115131
})
116132

117133
it('should update data upon interval changes -- changes happened during revalidate', async () => {
@@ -137,68 +153,49 @@ describe('useSWR - refresh', () => {
137153
</div>
138154
)
139155
}
140-
const { container } = render(<Page />)
141-
expect(container.firstChild.textContent).toMatchInlineSnapshot(
142-
`"count: 0"`
143-
)
156+
157+
render(<Page />)
158+
screen.getByText('count: 0')
144159

145160
await screen.findByText('count: 0 1')
146161

147-
await act(() => sleep(100))
162+
await act(() => advanceTimers(100))
148163

149-
expect(container.firstChild.textContent).toMatchInlineSnapshot(
150-
`"count: 1 2"`
151-
)
164+
screen.getByText('count: 1 2')
152165

153-
await act(() => sleep(100))
154-
expect(container.firstChild.textContent).toMatchInlineSnapshot(
155-
`"count: 1 2"`
156-
)
166+
await act(() => advanceTimers(100))
167+
screen.getByText('count: 1 2')
157168

158-
await act(() => sleep(100))
159-
expect(container.firstChild.textContent).toMatchInlineSnapshot(
160-
`"count: 1 2"`
161-
)
169+
await act(() => advanceTimers(100))
170+
screen.getByText('count: 1 2')
162171

163-
await act(() => sleep(100))
164-
expect(container.firstChild.textContent).toMatchInlineSnapshot(
165-
`"count: 1 2"`
166-
)
172+
await act(() => advanceTimers(100))
173+
screen.getByText('count: 1 2')
167174

168-
fireEvent.click(container.firstElementChild)
175+
fireEvent.click(screen.getByText('count: 1 2'))
169176

170177
await act(() => {
171178
// it will setup a new 100ms timer
172-
return sleep(50)
179+
return advanceTimers(50)
173180
})
174181

175-
expect(container.firstChild.textContent).toMatchInlineSnapshot(
176-
`"count: 1 0"`
177-
)
182+
screen.getByText('count: 1 0')
178183

179-
await act(() => sleep(50))
184+
await act(() => advanceTimers(50))
180185

181-
expect(container.firstChild.textContent).toMatchInlineSnapshot(
182-
`"count: 2 1"`
183-
)
186+
screen.getByText('count: 2 1')
184187

185-
await act(() => sleep(100))
188+
await act(() => advanceTimers(100))
186189

187-
expect(container.firstChild.textContent).toMatchInlineSnapshot(
188-
`"count: 3 2"`
189-
)
190+
screen.getByText('count: 3 2')
190191

191-
await act(() => sleep(100))
192+
await act(() => advanceTimers(100))
192193

193-
expect(container.firstChild.textContent).toMatchInlineSnapshot(
194-
`"count: 3 2"`
195-
)
194+
screen.getByText('count: 3 2')
196195

197-
await act(() => sleep(100))
196+
await act(() => advanceTimers(100))
198197

199-
expect(container.firstChild.textContent).toMatchInlineSnapshot(
200-
`"count: 3 2"`
201-
)
198+
screen.getByText('count: 3 2')
202199
})
203200

204201
it('should allow use custom compare method', async () => {
@@ -227,9 +224,9 @@ describe('useSWR - refresh', () => {
227224
return <button onClick={() => change()}>{data.timestamp}</button>
228225
}
229226

230-
const { container } = render(<Page />)
227+
render(<Page />)
231228

232-
expect(container.firstChild.textContent).toMatchInlineSnapshot(`"loading"`)
229+
screen.getByText('loading')
233230

234231
await screen.findByText('1')
235232
expect(fetcher).toBeCalledTimes(1)
@@ -238,8 +235,8 @@ describe('useSWR - refresh', () => {
238235
version: '1.0'
239236
})
240237

241-
fireEvent.click(container.firstElementChild)
242-
await act(() => sleep(1))
238+
fireEvent.click(screen.getByText('1'))
239+
await act(() => advanceTimers(1))
243240
expect(fetcher).toBeCalledTimes(2)
244241
expect(fetcher).toReturnWith({
245242
timestamp: 2,
@@ -248,7 +245,7 @@ describe('useSWR - refresh', () => {
248245

249246
const cachedData = cache.get(key)
250247
expect(cachedData.timestamp.toString()).toEqual('1')
251-
expect(container.firstChild.textContent).toMatchInlineSnapshot(`"1"`)
248+
screen.getByText('1')
252249
})
253250

254251
it('should not let the previous interval timer to set new timer if key changes too fast', async () => {
@@ -268,33 +265,35 @@ describe('useSWR - refresh', () => {
268265
>{`click me ${data}`}</button>
269266
)
270267
}
271-
const { container } = render(<Page />)
268+
269+
render(<Page />)
272270

273271
// initial revalidate
274-
await act(() => sleep(200))
272+
await act(() => advanceTimers(200))
275273
expect(fetcherWithToken).toBeCalledTimes(1)
276274

277275
// first refresh
278-
await act(() => sleep(100))
276+
await act(() => advanceTimers(100))
279277
expect(fetcherWithToken).toBeCalledTimes(2)
280278
expect(fetcherWithToken).toHaveBeenLastCalledWith('0')
281-
await act(() => sleep(200))
279+
await act(() => advanceTimers(200))
282280

283281
// second refresh start
284-
await act(() => sleep(100))
282+
await act(() => advanceTimers(100))
285283
expect(fetcherWithToken).toBeCalledTimes(3)
286284
expect(fetcherWithToken).toHaveBeenLastCalledWith('0')
287285
// change the key during revalidation
288286
// The second refresh will not start a new timer
289-
fireEvent.click(container.firstElementChild)
287+
fireEvent.click(screen.getByText('click me 0'))
290288

291289
// first refresh with new key 1
292-
await act(() => sleep(100))
290+
await act(() => advanceTimers(100))
293291
expect(fetcherWithToken).toBeCalledTimes(4)
294292
expect(fetcherWithToken).toHaveBeenLastCalledWith('1')
295-
await act(() => sleep(210))
293+
await act(() => advanceTimers(200))
296294

297295
// second refresh with new key 1
296+
await act(() => advanceTimers(100))
298297
expect(fetcherWithToken).toBeCalledTimes(5)
299298
expect(fetcherWithToken).toHaveBeenLastCalledWith('1')
300299
})
@@ -320,38 +319,40 @@ describe('useSWR - refresh', () => {
320319
>{`click me ${data}`}</button>
321320
)
322321
}
323-
const { container } = render(<Page />)
322+
323+
render(<Page />)
324324

325325
// initial revalidate
326-
await act(() => sleep(100))
326+
await act(() => advanceTimers(100))
327327
expect(fetcherWithToken).toBeCalledTimes(1)
328328
expect(onSuccess).toBeCalledTimes(1)
329329
expect(onSuccess).toHaveLastReturnedWith(`0-hash 0-hash`)
330330
// first refresh
331-
await act(() => sleep(50))
331+
await act(() => advanceTimers(50))
332332
expect(fetcherWithToken).toBeCalledTimes(2)
333333
expect(fetcherWithToken).toHaveBeenLastCalledWith('0-hash')
334-
await act(() => sleep(100))
334+
await act(() => advanceTimers(100))
335335
expect(onSuccess).toBeCalledTimes(2)
336336
expect(onSuccess).toHaveLastReturnedWith(`0-hash 0-hash`)
337337

338338
// second refresh start
339-
await act(() => sleep(50))
339+
await act(() => advanceTimers(50))
340340
expect(fetcherWithToken).toBeCalledTimes(3)
341341
expect(fetcherWithToken).toHaveBeenLastCalledWith('0-hash')
342342
// change the key during revalidation
343343
// The second refresh will not start a new timer
344-
fireEvent.click(container.firstElementChild)
344+
fireEvent.click(screen.getByText('click me 0-hash'))
345345

346346
// first refresh with new key 1
347-
await act(() => sleep(50))
347+
await act(() => advanceTimers(50))
348348
expect(fetcherWithToken).toBeCalledTimes(4)
349349
expect(fetcherWithToken).toHaveBeenLastCalledWith('1-hash')
350-
await act(() => sleep(110))
350+
await act(() => advanceTimers(100))
351351
expect(onSuccess).toBeCalledTimes(3)
352352
expect(onSuccess).toHaveLastReturnedWith(`1-hash 1-hash`)
353353

354354
// second refresh with new key 1
355+
await act(() => advanceTimers(50))
355356
expect(fetcherWithToken).toBeCalledTimes(5)
356357
expect(fetcherWithToken).toHaveBeenLastCalledWith('1-hash')
357358
})

0 commit comments

Comments
 (0)