diff --git a/packages/preact-query/src/__tests__/useSuspenseQueries.test.tsx b/packages/preact-query/src/__tests__/useSuspenseQueries.test.tsx index 8fa4072c69..ac56ae947b 100644 --- a/packages/preact-query/src/__tests__/useSuspenseQueries.test.tsx +++ b/packages/preact-query/src/__tests__/useSuspenseQueries.test.tsx @@ -1,6 +1,5 @@ import { queryKey, sleep } from '@tanstack/query-test-utils' import { act, fireEvent, render } from '@testing-library/preact' -import type { FunctionalComponent } from 'preact' import { Suspense, startTransition, useTransition } from 'preact/compat' import { useEffect, useRef, useState } from 'preact/hooks' import { @@ -24,33 +23,18 @@ import type { UseSuspenseQueryOptions } from '..' import { ErrorBoundary } from './ErrorBoundary' import { renderWithClient } from './utils' -type NumberQueryOptions = UseSuspenseQueryOptions - -const QUERY_DURATION = 1000 - -const createQuery: (id: number) => NumberQueryOptions = (id) => ({ - queryKey: [id], - queryFn: () => sleep(QUERY_DURATION).then(() => id), -}) -const resolveQueries = async () => { - await vi.advanceTimersByTimeAsync(QUERY_DURATION) -} - -const queryClient = new QueryClient() - describe('useSuspenseQueries', () => { + let queryClient: QueryClient const onSuspend = vi.fn() const onQueriesResolution = vi.fn() - beforeAll(() => { + beforeEach(() => { vi.useFakeTimers() - }) - - afterAll(() => { - vi.useRealTimers() + queryClient = new QueryClient() }) afterEach(() => { + vi.useRealTimers() queryClient.clear() onSuspend.mockClear() onQueriesResolution.mockClear() @@ -64,89 +48,199 @@ describe('useSuspenseQueries', () => { return
loading
} - const withSuspenseWrapper = ( - Component: FunctionalComponent, - ) => { - function SuspendedComponent(props: T) { - return ( - }> - - + it('should suspend on mount', () => { + function Page() { + const queriesResults = useSuspenseQueries( + { + queries: [1, 2].map((id) => ({ + queryKey: [id], + queryFn: () => sleep(1000).then(() => id), + })), + combine: (results) => results.map((r) => r.data), + }, + queryClient, ) - } - - return SuspendedComponent - } - - function QueriesContainer({ - queries, - }: { - queries: Array - }) { - const queriesResults = useSuspenseQueries( - { queries, combine: (results) => results.map((r) => r.data) }, - queryClient, - ) - - useEffect(() => { - onQueriesResolution(queriesResults) - }, [queriesResults]) - return null - } + useEffect(() => { + onQueriesResolution(queriesResults) + }, [queriesResults]) - const TestComponent = withSuspenseWrapper(QueriesContainer) + return null + } - it('should suspend on mount', () => { - render() + render( + }> + + , + ) expect(onSuspend).toHaveBeenCalledOnce() }) it('should resolve queries', async () => { - render() + function Page() { + const queriesResults = useSuspenseQueries( + { + queries: [1, 2].map((id) => ({ + queryKey: [id], + queryFn: () => sleep(1000).then(() => id), + })), + combine: (results) => results.map((r) => r.data), + }, + queryClient, + ) - await act(resolveQueries) + useEffect(() => { + onQueriesResolution(queriesResults) + }, [queriesResults]) + + return null + } + + render( + }> + + , + ) + + await act(async () => { + await vi.advanceTimersByTimeAsync(1000) + }) expect(onQueriesResolution).toHaveBeenCalledTimes(1) expect(onQueriesResolution).toHaveBeenLastCalledWith([1, 2]) }) it('should not suspend on mount if query has been already fetched', () => { - const query = createQuery(1) + const key = queryKey() + const queryFn = () => sleep(1000).then(() => 1) - queryClient.setQueryData(query.queryKey, query.queryFn) + queryClient.setQueryData(key, queryFn) - render() + function Page() { + const queriesResults = useSuspenseQueries( + { + queries: [{ queryKey: key, queryFn }], + combine: (results) => results.map((r) => r.data), + }, + queryClient, + ) + + useEffect(() => { + onQueriesResolution(queriesResults) + }, [queriesResults]) + + return null + } + + render( + }> + + , + ) expect(onSuspend).not.toHaveBeenCalled() }) it('should not break suspense when queries change without resolving', async () => { - const initQueries = [1, 2].map(createQuery) - const nextQueries = [3, 4, 5, 6].map(createQuery) + const initQueries = [1, 2].map((id) => ({ + queryKey: [id], + queryFn: () => sleep(1000).then(() => id), + })) + const nextQueries = [3, 4, 5, 6].map((id) => ({ + queryKey: [id], + queryFn: () => sleep(1000).then(() => id), + })) + + function Page({ + queries, + }: { + queries: Array> + }) { + const queriesResults = useSuspenseQueries( + { + queries, + combine: (results) => results.map((r) => r.data), + }, + queryClient, + ) + + useEffect(() => { + onQueriesResolution(queriesResults) + }, [queriesResults]) - const { rerender } = render() + return null + } - rerender() + const { rerender } = render( + }> + + , + ) - await act(resolveQueries) + rerender( + }> + + , + ) + + await act(async () => { + await vi.advanceTimersByTimeAsync(1000) + }) expect(onSuspend).toHaveBeenCalled() // the test for onQueriesResolution is React-specific and not applicable to Preact }) it('should suspend only once per queries change', async () => { - const initQueries = [1, 2].map(createQuery) - const nextQueries = [3, 4, 5, 6].map(createQuery) + const initQueries = [1, 2].map((id) => ({ + queryKey: [id], + queryFn: () => sleep(1000).then(() => id), + })) + const nextQueries = [3, 4, 5, 6].map((id) => ({ + queryKey: [id], + queryFn: () => sleep(1000).then(() => id), + })) + + function Page({ + queries, + }: { + queries: Array> + }) { + const queriesResults = useSuspenseQueries( + { + queries, + combine: (results) => results.map((r) => r.data), + }, + queryClient, + ) + + useEffect(() => { + onQueriesResolution(queriesResults) + }, [queriesResults]) + + return null + } - const { rerender } = render() + const { rerender } = render( + }> + + , + ) - await act(resolveQueries) + await act(async () => { + await vi.advanceTimersByTimeAsync(1000) + }) - rerender() + rerender( + }> + + , + ) - await act(resolveQueries) + await act(async () => { + await vi.advanceTimersByTimeAsync(1000) + }) expect(onSuspend).toHaveBeenCalledTimes(2) expect(onQueriesResolution).toHaveBeenCalledTimes(2) @@ -263,12 +357,16 @@ describe('useSuspenseQueries', () => { }) describe('useSuspenseQueries 2', () => { + let queryClient: QueryClient + beforeEach(() => { vi.useFakeTimers() + queryClient = new QueryClient() }) afterEach(() => { vi.useRealTimers() + queryClient.clear() }) it('should suspend all queries in parallel', async () => { diff --git a/packages/react-query/src/__tests__/useSuspenseQueries.test.tsx b/packages/react-query/src/__tests__/useSuspenseQueries.test.tsx index 4ebd32ea90..46561056bf 100644 --- a/packages/react-query/src/__tests__/useSuspenseQueries.test.tsx +++ b/packages/react-query/src/__tests__/useSuspenseQueries.test.tsx @@ -21,31 +21,18 @@ import { import { renderWithClient } from './utils' import type { UseSuspenseQueryOptions } from '..' -type NumberQueryOptions = UseSuspenseQueryOptions - -const QUERY_DURATION = 1000 - -const createQuery: (id: number) => NumberQueryOptions = (id) => ({ - queryKey: [id], - queryFn: () => sleep(QUERY_DURATION).then(() => id), -}) -const resolveQueries = () => vi.advanceTimersByTimeAsync(QUERY_DURATION) - -const queryClient = new QueryClient() - describe('useSuspenseQueries', () => { + let queryClient: QueryClient const onSuspend = vi.fn() const onQueriesResolution = vi.fn() - beforeAll(() => { + beforeEach(() => { vi.useFakeTimers() - }) - - afterAll(() => { - vi.useRealTimers() + queryClient = new QueryClient() }) afterEach(() => { + vi.useRealTimers() queryClient.clear() onSuspend.mockClear() onQueriesResolution.mockClear() @@ -59,71 +46,141 @@ describe('useSuspenseQueries', () => { return
loading
} - const withSuspenseWrapper = (Component: React.FC) => { - function SuspendedComponent(props: T) { - return ( - }> - - + it('should suspend on mount', () => { + function Page() { + const queriesResults = useSuspenseQueries( + { + queries: [1, 2].map((id) => ({ + queryKey: [id], + queryFn: () => sleep(1000).then(() => id), + })), + combine: (results) => results.map((r) => r.data), + }, + queryClient, ) - } - - return SuspendedComponent - } - - function QueriesContainer({ - queries, - }: { - queries: Array - }) { - const queriesResults = useSuspenseQueries( - { queries, combine: (results) => results.map((r) => r.data) }, - queryClient, - ) - - React.useEffect(() => { - onQueriesResolution(queriesResults) - }, [queriesResults]) - return null - } + React.useEffect(() => { + onQueriesResolution(queriesResults) + }, [queriesResults]) - const TestComponent = withSuspenseWrapper(QueriesContainer) + return null + } - it('should suspend on mount', () => { - render() + render( + }> + + , + ) expect(onSuspend).toHaveBeenCalledOnce() }) it('should resolve queries', async () => { - render() + function Page() { + const queriesResults = useSuspenseQueries( + { + queries: [1, 2].map((id) => ({ + queryKey: [id], + queryFn: () => sleep(1000).then(() => id), + })), + combine: (results) => results.map((r) => r.data), + }, + queryClient, + ) - await act(resolveQueries) + React.useEffect(() => { + onQueriesResolution(queriesResults) + }, [queriesResults]) + + return null + } + + render( + }> + + , + ) + + await act(() => vi.advanceTimersByTimeAsync(1000)) expect(onQueriesResolution).toHaveBeenCalledTimes(1) expect(onQueriesResolution).toHaveBeenLastCalledWith([1, 2]) }) it('should not suspend on mount if query has been already fetched', () => { - const query = createQuery(1) + const key = queryKey() + const queryFn = () => sleep(1000).then(() => 1) + + queryClient.setQueryData(key, queryFn) + + function Page() { + const queriesResults = useSuspenseQueries( + { + queries: [{ queryKey: key, queryFn }], + combine: (results) => results.map((r) => r.data), + }, + queryClient, + ) - queryClient.setQueryData(query.queryKey, query.queryFn) + React.useEffect(() => { + onQueriesResolution(queriesResults) + }, [queriesResults]) - render() + return null + } + + render( + }> + + , + ) expect(onSuspend).not.toHaveBeenCalled() }) it('should not break suspense when queries change without resolving', async () => { - const initQueries = [1, 2].map(createQuery) - const nextQueries = [3, 4, 5, 6].map(createQuery) + const initQueries = [1, 2].map((id) => ({ + queryKey: [id], + queryFn: () => sleep(1000).then(() => id), + })) + const nextQueries = [3, 4, 5, 6].map((id) => ({ + queryKey: [id], + queryFn: () => sleep(1000).then(() => id), + })) + + function Page({ + queries, + }: { + queries: Array> + }) { + const queriesResults = useSuspenseQueries( + { + queries, + combine: (results) => results.map((r) => r.data), + }, + queryClient, + ) - const { rerender } = render() + React.useEffect(() => { + onQueriesResolution(queriesResults) + }, [queriesResults]) - rerender() + return null + } - await act(resolveQueries) + const { rerender } = render( + }> + + , + ) + + rerender( + }> + + , + ) + + await act(() => vi.advanceTimersByTimeAsync(1000)) expect(onSuspend).toHaveBeenCalledTimes(1) expect(onQueriesResolution).toHaveBeenCalledTimes(1) @@ -131,16 +188,50 @@ describe('useSuspenseQueries', () => { }) it('should suspend only once per queries change', async () => { - const initQueries = [1, 2].map(createQuery) - const nextQueries = [3, 4, 5, 6].map(createQuery) + const initQueries = [1, 2].map((id) => ({ + queryKey: [id], + queryFn: () => sleep(1000).then(() => id), + })) + const nextQueries = [3, 4, 5, 6].map((id) => ({ + queryKey: [id], + queryFn: () => sleep(1000).then(() => id), + })) + + function Page({ + queries, + }: { + queries: Array> + }) { + const queriesResults = useSuspenseQueries( + { + queries, + combine: (results) => results.map((r) => r.data), + }, + queryClient, + ) + + React.useEffect(() => { + onQueriesResolution(queriesResults) + }, [queriesResults]) + + return null + } - const { rerender } = render() + const { rerender } = render( + }> + + , + ) - await act(resolveQueries) + await act(() => vi.advanceTimersByTimeAsync(1000)) - rerender() + rerender( + }> + + , + ) - await act(resolveQueries) + await act(() => vi.advanceTimersByTimeAsync(1000)) expect(onSuspend).toHaveBeenCalledTimes(2) expect(onQueriesResolution).toHaveBeenCalledTimes(2) @@ -258,12 +349,16 @@ describe('useSuspenseQueries', () => { }) describe('useSuspenseQueries 2', () => { + let queryClient: QueryClient + beforeEach(() => { vi.useFakeTimers() + queryClient = new QueryClient() }) afterEach(() => { vi.useRealTimers() + queryClient.clear() }) it('should suspend all queries in parallel', async () => {