diff --git a/test/use-swr-local-mutation.test.tsx b/test/use-swr-local-mutation.test.tsx index ba9918b66..65c64a15f 100644 --- a/test/use-swr-local-mutation.test.tsx +++ b/test/use-swr-local-mutation.test.tsx @@ -1,7 +1,9 @@ import { act, render, screen, fireEvent } from '@testing-library/react' import React, { useEffect, useState } from 'react' import useSWR, { mutate, cache } from '../src' -import { sleep } from './utils' +import { createResponse, sleep } from './utils' + +const waitForNextTick = () => act(() => sleep(1)) describe('useSWR - local mutation', () => { it('should trigger revalidation programmatically', async () => { @@ -13,10 +15,10 @@ describe('useSWR - local mutation', () => { }) return
data: {data}
} - const { container } = render() + render() // hydration - expect(container.firstChild.textContent).toMatchInlineSnapshot(`"data: "`) + screen.getByText('data:') // mount await screen.findByText('data: 0') @@ -25,9 +27,7 @@ describe('useSWR - local mutation', () => { // mutate and revalidate mutate('dynamic-7') }) - await act(() => sleep(1)) - - expect(container.firstChild.textContent).toMatchInlineSnapshot(`"data: 1"`) + await screen.findByText('data: 1') }) it('should trigger revalidation programmatically within a dedupingInterval', async () => { @@ -39,10 +39,11 @@ describe('useSWR - local mutation', () => { }) return
data: {data}
} - const { container } = render() + render() // hydration - expect(container.firstChild.textContent).toMatchInlineSnapshot(`"data: "`) + screen.getByText('data:') + // mount await screen.findByText('data: 0') @@ -50,9 +51,7 @@ describe('useSWR - local mutation', () => { // trigger revalidation mutate('dynamic-12') }) - await act(() => sleep(1)) - - expect(container.firstChild.textContent).toMatchInlineSnapshot(`"data: 1"`) + await screen.findByText('data: 1') }) it('should mutate the cache and revalidate', async () => { @@ -64,10 +63,10 @@ describe('useSWR - local mutation', () => { }) return
data: {data}
} - const { container } = render() + render() // hydration - expect(container.firstChild.textContent).toMatchInlineSnapshot(`"data: "`) + screen.getByText('data:') //mount await screen.findByText('data: 0') @@ -76,8 +75,7 @@ describe('useSWR - local mutation', () => { // mutate and revalidate mutate('dynamic-8', 'mutate') }) - await act(() => sleep(1)) - expect(container.firstChild.textContent).toMatchInlineSnapshot(`"data: 1"`) + await screen.findByText('data: 1') }) it('should dedupe extra requests after mutation', async () => { @@ -92,10 +90,10 @@ describe('useSWR - local mutation', () => { }) return
data: {data}
} - const { container } = render() + render() // hydration - expect(container.firstChild.textContent).toMatchInlineSnapshot(`"data: "`) + screen.getByText('data:') //mount await screen.findByText('data: 0') @@ -103,23 +101,21 @@ describe('useSWR - local mutation', () => { // mutate and revalidate mutate('dynamic-13') }) - await act(() => sleep(1)) - expect(container.firstChild.textContent).toMatchInlineSnapshot(`"data: 1"`) + await screen.findByText('data: 1') }) it('should mutate the cache and revalidate in async', async () => { function Page() { - const { data } = useSWR( - 'dynamic-9', - () => new Promise(res => setTimeout(() => res('truth'), 200)), - { dedupingInterval: 0 } - ) + const { data } = useSWR('dynamic-9', () => createResponse('truth'), { + dedupingInterval: 0 + }) return
data: {data}
} - const { container } = render() + render() // hydration - expect(container.firstChild.textContent).toMatchInlineSnapshot(`"data: "`) + screen.getByText('data:') + //mount await screen.findByText('data: truth') @@ -127,17 +123,11 @@ describe('useSWR - local mutation', () => { // mutate and revalidate mutate('dynamic-9', 'local') }) - await act(() => sleep(1)) - - expect(container.firstChild.textContent).toMatchInlineSnapshot( - `"data: local"` - ) - await act(() => sleep(200)) // recovers + await screen.findByText('data: local') - expect(container.firstChild.textContent).toMatchInlineSnapshot( - `"data: truth"` - ) + // recovers + await screen.findByText('data: truth') }) it('should support async mutation with promise', async () => { @@ -147,24 +137,20 @@ describe('useSWR - local mutation', () => { }) return
data: {data}
} - const { container } = render() + render() // hydration - expect(container.textContent).toMatchInlineSnapshot(`"data: "`) + screen.getByText('data:') //mount await screen.findByText('data: 0') - await act(() => sleep(1)) + + await waitForNextTick() await act(() => { // mutate and revalidate - return mutate( - 'mutate-promise', - new Promise(res => setTimeout(() => res(999), 100)), - false - ) + return mutate('mutate-promise', createResponse(999), false) }) - await act(() => sleep(110)) - expect(container.textContent).toMatchInlineSnapshot(`"data: 999"`) + await screen.findByText('data: 999') }) it('should support async mutation with async function', async () => { @@ -174,22 +160,18 @@ describe('useSWR - local mutation', () => { }) return
data: {data}
} - const { container } = render() + render() // hydration - expect(container.textContent).toMatchInlineSnapshot(`"data: "`) + screen.getByText('data:') + //mount await screen.findByText('data: 0') - // wait for the next tick - await act(() => sleep(1)) + await waitForNextTick() await act(() => { // mutate and revalidate - return mutate( - 'mutate-async-fn', - async () => new Promise(res => setTimeout(() => res(999), 100)), - false - ) + return mutate('mutate-async-fn', async () => createResponse(999), false) }) await screen.findByText('data: 999') }) @@ -203,10 +185,10 @@ describe('useSWR - local mutation', () => { }) return
data: {data}
} - const { container } = render() + render() // hydration - expect(container.firstChild.textContent).toMatchInlineSnapshot(`"data: "`) + screen.getByText('data:') //mount await screen.findByText('data: 0') @@ -215,8 +197,7 @@ describe('useSWR - local mutation', () => { // trigger revalidation mutate('dynamic-14') }) - await act(() => sleep(1)) - expect(container.firstChild.textContent).toMatchInlineSnapshot(`"data: 1"`) + await screen.findByText('data: 1') }) it('should call function as data passing current cached value', async () => { @@ -268,19 +249,18 @@ describe('useSWR - local mutation', () => {
boundMutate('mutated', false)}>data: {data}
) } - const { container } = render() + render() // hydration - expect(container.firstChild.textContent).toMatchInlineSnapshot(`"data: "`) + screen.getByText('data:') + //mount await screen.findByText('data: fetched') + // call bound mutate - fireEvent.click(container.firstElementChild) + fireEvent.click(screen.getByText('data: fetched')) // expect new updated value (after a tick) - await act(async () => await 0) - expect(container.firstChild.textContent).toMatchInlineSnapshot( - `"data: mutated"` - ) + await screen.findByText('data: mutated') }) it('should ignore in flight requests when mutating', async () => { @@ -290,61 +270,54 @@ describe('useSWR - local mutation', () => { }) function Section() { - const { data } = useSWR( - 'mutate-2', - () => new Promise(res => setTimeout(() => res(2), 200)) + const { data } = useSWR('mutate-2', () => + createResponse(2, { delay: 150 }) ) return
{data}
} - const { container } = render(
) - - expect(container.textContent).toMatchInlineSnapshot(`"1"`) // directly from cache - await act(() => sleep(150)) // still suspending + render(
) + screen.getByText('1') // directly from cache + await act(() => sleep(100)) // still suspending act(() => { mutate('mutate-2', 3) }) // set it to 3. this will drop the ongoing request - await act(async () => await 0) - expect(container.textContent).toMatchInlineSnapshot(`"3"`) + + await screen.findByText('3') + await act(() => sleep(100)) - expect(container.textContent).toMatchInlineSnapshot(`"3"`) + screen.getByText('3') }) it('should ignore in flight mutations when calling another async mutate', async () => { let value = 'off' function Page() { - const { data } = useSWR( - 'mutate-3', - () => new Promise(res => setTimeout(() => res(value), 200)) + const { data } = useSWR('mutate-3', () => + createResponse(value, { delay: 100 }) ) return
{data}
} - const { container } = render() + render() - await act(() => sleep(250)) - expect(container.textContent).toMatchInlineSnapshot(`"off"`) // Initial state + await screen.findByText('off') // Initial state act(() => { mutate('mutate-3', 'on', false) }) - // Validate local state is now "on" - await act(async () => await 0) - expect(container.textContent).toMatchInlineSnapshot(`"on"`) + await screen.findByText('on') // Simulate toggling "on" await act(async () => { expect( mutate( 'mutate-3', - new Promise(res => - setTimeout(() => { - value = 'on' - res('on') - }, 200) - ), + () => { + value = 'on' + return createResponse('on', { delay: 100 }) + }, false ) ).resolves.toBe('on') @@ -355,32 +328,29 @@ describe('useSWR - local mutation', () => { }) // Validate local state is now "off" - await act(async () => await 0) - expect(container.textContent).toMatchInlineSnapshot(`"off"`) + await screen.findByText('off') // Simulate toggling "off" await act(async () => { expect( mutate( 'mutate-3', - new Promise(res => - setTimeout(() => { - value = 'off' - res('off') - }, 400) - ), + () => { + value = 'off' + return createResponse('off', { delay: 100 }) + }, false ) ).resolves.toBe('off') }) // Wait for toggling "on" promise to resolve, but the "on" mutation is cancelled - await act(() => sleep(210)) - expect(container.textContent).toMatchInlineSnapshot(`"off"`) + await act(() => sleep(50)) + screen.getByText('off') // Wait for toggling "off" promise to resolve - await act(() => sleep(210)) - expect(container.textContent).toMatchInlineSnapshot(`"off"`) + await act(() => sleep(100)) + screen.getByText('off') }) it('null is stringified when found inside an array', async () => { @@ -392,18 +362,19 @@ describe('useSWR - local mutation', () => { }) return
data: {data}
} - const { container } = render() + render() // hydration - expect(container.firstChild.textContent).toMatchInlineSnapshot(`"data: "`) + screen.getByText('data:') + //mount await screen.findByText('data: 0') + act(() => { // trigger revalidation mutate([null]) }) - await act(() => sleep(1)) - expect(container.firstChild.textContent).toMatchInlineSnapshot(`"data: 1"`) + await screen.findByText('data: 1') }) it('should return promise from mutate without data', async () => { @@ -414,10 +385,13 @@ describe('useSWR - local mutation', () => { }) return
data: {data}
} - const { container } = render() - expect(container.firstChild.textContent).toMatchInlineSnapshot(`"data: "`) + + render() + screen.getByText('data:') + // mount await screen.findByText('data: 0') + let promise await act(() => { promise = mutate('dynamic-18') @@ -425,7 +399,7 @@ describe('useSWR - local mutation', () => { }) expect(promise).toBeInstanceOf(Promise) // mutate returns a promise expect(promise).resolves.toBe(1) // the return value should be the new cache - expect(container.firstChild.textContent).toMatchInlineSnapshot(`"data: 1"`) + screen.getByText('data: 1') }) it('should update error in cache when mutate failed with error', async () => { @@ -436,7 +410,9 @@ describe('useSWR - local mutation', () => { const { data, error } = useSWR(key, () => value) return
{error ? error.message : `data: ${data}`}
} - const { container } = render() + + render() + //mount await screen.findByText('data: 0') await act(async () => { @@ -454,18 +430,15 @@ describe('useSWR - local mutation', () => { } }) + screen.getByText(message) const [keyData, , keyErr] = cache.serializeKey(key) let cacheError = cache.get(keyErr) expect(cacheError.message).toMatchInlineSnapshot(`"${message}"`) - expect(container.firstChild.textContent).toMatchInlineSnapshot( - `"${message}"` - ) // if mutate throws an error synchronously, the cache shouldn't be updated expect(cache.get(keyData)).toBe(value) + // if mutate succeed, error should be cleared - await act(async () => { - return mutate(key, value, false) - }) + await act(() => mutate(key, value, false)) cacheError = cache.get(keyErr) expect(cacheError).toMatchInlineSnapshot(`undefined`) }) @@ -475,13 +448,10 @@ describe('useSWR - local mutation', () => { function Section() { const [key, setKey] = useState(null) - const { data, mutate: boundMutate } = useSWR( - key, - () => new Promise(res => setTimeout(() => res(1), 10)) - ) + const { data, mutate: boundMutate } = useSWR(key, () => createResponse(1)) useEffect(() => { - const timeout = setTimeout(() => setKey('mutate-5'), 100) + const timeout = setTimeout(() => setKey('mutate-5'), 50) return () => clearTimeout(timeout) }, []) @@ -490,7 +460,7 @@ describe('useSWR - local mutation', () => { } render(
) - await act(() => sleep(120)) + await act(() => sleep(100)) act(() => { mutate('mutate-5', 2) }) @@ -533,7 +503,6 @@ describe('useSWR - local mutation', () => { render() await act(() => sleep(50)) - expect(mutationRecivedValues).toEqual([0, 1]) expect(renderRecivedValues).toEqual([undefined, 0, 1, 2]) })