diff --git a/test/use-swr-refresh.test.tsx b/test/use-swr-refresh.test.tsx
index 640a0b994..cd38f6491 100644
--- a/test/use-swr-refresh.test.tsx
+++ b/test/use-swr-refresh.test.tsx
@@ -3,7 +3,18 @@ import React, { useState } from 'react'
import useSWR, { cache } from '../src'
import { sleep } from './utils'
+// This has to be an async function to wait a microtask to flush updates
+const advanceTimers = async (ms: number) => jest.advanceTimersByTime(ms)
+
+// This test heavily depends on setInterval/setTimeout timers, which makes tests slower and flaky.
+// So we use Jest's fake timers
describe('useSWR - refresh', () => {
+ beforeEach(() => {
+ jest.useFakeTimers()
+ })
+ afterEach(() => {
+ jest.useRealTimers()
+ })
it('should rerender automatically on interval', async () => {
let count = 0
@@ -14,20 +25,21 @@ describe('useSWR - refresh', () => {
})
return
count: {data}
}
- const { container } = render()
+
+ render()
// hydration
- expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: "`)
+ screen.getByText('count:')
// mount
await screen.findByText('count: 0')
- await act(() => sleep(210)) // update
- expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: 1"`)
- await act(() => sleep(50)) // no update
- expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: 1"`)
- await act(() => sleep(150)) // update
- expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: 2"`)
+ await act(() => advanceTimers(200)) // update
+ screen.getByText('count: 1')
+ await act(() => advanceTimers(50)) // no update
+ screen.getByText('count: 1')
+ await act(() => advanceTimers(150)) // update
+ screen.getByText('count: 2')
})
it('should dedupe requests combined with intervals', async () => {
@@ -36,26 +48,29 @@ describe('useSWR - refresh', () => {
function Page() {
const { data } = useSWR('dynamic-2', () => count++, {
refreshInterval: 100,
- dedupingInterval: 150
+ dedupingInterval: 500
})
return count: {data}
}
- const { container } = render()
+
+ render()
// hydration
- expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: "`)
+ screen.getByText('count:')
// mount
await screen.findByText('count: 0')
- await act(() => sleep(110)) // no update (deduped)
- expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: 0"`)
- await act(() => sleep(100)) // update
- expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: 1"`)
- await act(() => sleep(100)) // no update (deduped)
- expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: 1"`)
- await act(() => sleep(100)) // update
- expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: 2"`)
+ await act(() => advanceTimers(100)) // no update (deduped)
+ screen.getByText('count: 0')
+ await act(() => advanceTimers(400)) // reach dudupingInterval
+ await act(() => advanceTimers(100)) // update
+ screen.getByText('count: 1')
+ await act(() => advanceTimers(100)) // no update (deduped)
+ screen.getByText('count: 1')
+ await act(() => advanceTimers(400)) // reach dudupingInterval
+ await act(() => advanceTimers(100)) // update
+ screen.getByText('count: 2')
})
it('should update data upon interval changes', async () => {
@@ -72,46 +87,47 @@ describe('useSWR - refresh', () => {
)
}
- const { container } = render()
- expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: "`)
+
+ render()
+ screen.getByText('count:')
// mount
await screen.findByText('count: 0')
- await act(() => sleep(110))
- expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: 1"`)
- await act(() => sleep(25))
- expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: 1"`)
- await act(() => sleep(75))
- expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: 2"`)
- fireEvent.click(container.firstElementChild)
+ await act(() => advanceTimers(100))
+ screen.getByText('count: 1')
+ await act(() => advanceTimers(50))
+ screen.getByText('count: 1')
+ await act(() => advanceTimers(50))
+ screen.getByText('count: 2')
+ fireEvent.click(screen.getByText('count: 2'))
- await act(() => sleep(100))
+ await act(() => advanceTimers(100))
- expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: 2"`)
+ screen.getByText('count: 2')
- await act(() => sleep(60))
+ await act(() => advanceTimers(50))
- expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: 3"`)
+ screen.getByText('count: 3')
- await act(() => sleep(160))
- expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: 4"`)
- fireEvent.click(container.firstElementChild)
+ await act(() => advanceTimers(150))
+ screen.getByText('count: 4')
+ fireEvent.click(screen.getByText('count: 4'))
await act(() => {
// it will clear 150ms timer and setup a new 200ms timer
- return sleep(150)
+ return advanceTimers(150)
})
- expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: 4"`)
- await act(() => sleep(60))
- expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: 5"`)
- fireEvent.click(container.firstElementChild)
+ screen.getByText('count: 4')
+ await act(() => advanceTimers(50))
+ screen.getByText('count: 5')
+ fireEvent.click(screen.getByText('count: 5'))
await act(() => {
// it will clear 200ms timer and stop
- return sleep(60)
+ return advanceTimers(50)
})
- expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: 5"`)
- await act(() => sleep(60))
- expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: 5"`)
+ screen.getByText('count: 5')
+ await act(() => advanceTimers(50))
+ screen.getByText('count: 5')
})
it('should update data upon interval changes -- changes happened during revalidate', async () => {
@@ -137,68 +153,49 @@ describe('useSWR - refresh', () => {
)
}
- const { container } = render()
- expect(container.firstChild.textContent).toMatchInlineSnapshot(
- `"count: 0"`
- )
+
+ render()
+ screen.getByText('count: 0')
await screen.findByText('count: 0 1')
- await act(() => sleep(100))
+ await act(() => advanceTimers(100))
- expect(container.firstChild.textContent).toMatchInlineSnapshot(
- `"count: 1 2"`
- )
+ screen.getByText('count: 1 2')
- await act(() => sleep(100))
- expect(container.firstChild.textContent).toMatchInlineSnapshot(
- `"count: 1 2"`
- )
+ await act(() => advanceTimers(100))
+ screen.getByText('count: 1 2')
- await act(() => sleep(100))
- expect(container.firstChild.textContent).toMatchInlineSnapshot(
- `"count: 1 2"`
- )
+ await act(() => advanceTimers(100))
+ screen.getByText('count: 1 2')
- await act(() => sleep(100))
- expect(container.firstChild.textContent).toMatchInlineSnapshot(
- `"count: 1 2"`
- )
+ await act(() => advanceTimers(100))
+ screen.getByText('count: 1 2')
- fireEvent.click(container.firstElementChild)
+ fireEvent.click(screen.getByText('count: 1 2'))
await act(() => {
// it will setup a new 100ms timer
- return sleep(50)
+ return advanceTimers(50)
})
- expect(container.firstChild.textContent).toMatchInlineSnapshot(
- `"count: 1 0"`
- )
+ screen.getByText('count: 1 0')
- await act(() => sleep(50))
+ await act(() => advanceTimers(50))
- expect(container.firstChild.textContent).toMatchInlineSnapshot(
- `"count: 2 1"`
- )
+ screen.getByText('count: 2 1')
- await act(() => sleep(100))
+ await act(() => advanceTimers(100))
- expect(container.firstChild.textContent).toMatchInlineSnapshot(
- `"count: 3 2"`
- )
+ screen.getByText('count: 3 2')
- await act(() => sleep(100))
+ await act(() => advanceTimers(100))
- expect(container.firstChild.textContent).toMatchInlineSnapshot(
- `"count: 3 2"`
- )
+ screen.getByText('count: 3 2')
- await act(() => sleep(100))
+ await act(() => advanceTimers(100))
- expect(container.firstChild.textContent).toMatchInlineSnapshot(
- `"count: 3 2"`
- )
+ screen.getByText('count: 3 2')
})
it('should allow use custom compare method', async () => {
@@ -227,9 +224,9 @@ describe('useSWR - refresh', () => {
return
}
- const { container } = render()
+ render()
- expect(container.firstChild.textContent).toMatchInlineSnapshot(`"loading"`)
+ screen.getByText('loading')
await screen.findByText('1')
expect(fetcher).toBeCalledTimes(1)
@@ -238,8 +235,8 @@ describe('useSWR - refresh', () => {
version: '1.0'
})
- fireEvent.click(container.firstElementChild)
- await act(() => sleep(1))
+ fireEvent.click(screen.getByText('1'))
+ await act(() => advanceTimers(1))
expect(fetcher).toBeCalledTimes(2)
expect(fetcher).toReturnWith({
timestamp: 2,
@@ -248,7 +245,7 @@ describe('useSWR - refresh', () => {
const cachedData = cache.get(key)
expect(cachedData.timestamp.toString()).toEqual('1')
- expect(container.firstChild.textContent).toMatchInlineSnapshot(`"1"`)
+ screen.getByText('1')
})
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', () => {
>{`click me ${data}`}
)
}
- const { container } = render()
+
+ render()
// initial revalidate
- await act(() => sleep(200))
+ await act(() => advanceTimers(200))
expect(fetcherWithToken).toBeCalledTimes(1)
// first refresh
- await act(() => sleep(100))
+ await act(() => advanceTimers(100))
expect(fetcherWithToken).toBeCalledTimes(2)
expect(fetcherWithToken).toHaveBeenLastCalledWith('0')
- await act(() => sleep(200))
+ await act(() => advanceTimers(200))
// second refresh start
- await act(() => sleep(100))
+ await act(() => advanceTimers(100))
expect(fetcherWithToken).toBeCalledTimes(3)
expect(fetcherWithToken).toHaveBeenLastCalledWith('0')
// change the key during revalidation
// The second refresh will not start a new timer
- fireEvent.click(container.firstElementChild)
+ fireEvent.click(screen.getByText('click me 0'))
// first refresh with new key 1
- await act(() => sleep(100))
+ await act(() => advanceTimers(100))
expect(fetcherWithToken).toBeCalledTimes(4)
expect(fetcherWithToken).toHaveBeenLastCalledWith('1')
- await act(() => sleep(210))
+ await act(() => advanceTimers(200))
// second refresh with new key 1
+ await act(() => advanceTimers(100))
expect(fetcherWithToken).toBeCalledTimes(5)
expect(fetcherWithToken).toHaveBeenLastCalledWith('1')
})
@@ -320,38 +319,40 @@ describe('useSWR - refresh', () => {
>{`click me ${data}`}
)
}
- const { container } = render()
+
+ render()
// initial revalidate
- await act(() => sleep(100))
+ await act(() => advanceTimers(100))
expect(fetcherWithToken).toBeCalledTimes(1)
expect(onSuccess).toBeCalledTimes(1)
expect(onSuccess).toHaveLastReturnedWith(`0-hash 0-hash`)
// first refresh
- await act(() => sleep(50))
+ await act(() => advanceTimers(50))
expect(fetcherWithToken).toBeCalledTimes(2)
expect(fetcherWithToken).toHaveBeenLastCalledWith('0-hash')
- await act(() => sleep(100))
+ await act(() => advanceTimers(100))
expect(onSuccess).toBeCalledTimes(2)
expect(onSuccess).toHaveLastReturnedWith(`0-hash 0-hash`)
// second refresh start
- await act(() => sleep(50))
+ await act(() => advanceTimers(50))
expect(fetcherWithToken).toBeCalledTimes(3)
expect(fetcherWithToken).toHaveBeenLastCalledWith('0-hash')
// change the key during revalidation
// The second refresh will not start a new timer
- fireEvent.click(container.firstElementChild)
+ fireEvent.click(screen.getByText('click me 0-hash'))
// first refresh with new key 1
- await act(() => sleep(50))
+ await act(() => advanceTimers(50))
expect(fetcherWithToken).toBeCalledTimes(4)
expect(fetcherWithToken).toHaveBeenLastCalledWith('1-hash')
- await act(() => sleep(110))
+ await act(() => advanceTimers(100))
expect(onSuccess).toBeCalledTimes(3)
expect(onSuccess).toHaveLastReturnedWith(`1-hash 1-hash`)
// second refresh with new key 1
+ await act(() => advanceTimers(50))
expect(fetcherWithToken).toBeCalledTimes(5)
expect(fetcherWithToken).toHaveBeenLastCalledWith('1-hash')
})