Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { fireEvent } from '@testing-library/preact'
import type { VNode } from 'preact'
import { Suspense } from 'preact/compat'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import type { Mock } from 'vitest'

import {
QueryCache,
Expand All @@ -13,33 +12,6 @@ import {
} from '..'
import { renderWithClient } from './utils'

const generateInfiniteQueryOptions = (
data: Array<{ data: string; currentPage: number; totalPages: number }>,
) => {
let currentPage = 0

return {
queryFn: vi
.fn<(...args: Array<any>) => Promise<(typeof data)[number]>>()
.mockImplementation(async () => {
const currentPageData = data[currentPage]
if (!currentPageData) {
throw new Error(`No data defined for page ${currentPage}`)
}

await sleep(10)
currentPage++

return currentPageData
}),
initialPageParam: 1,
getNextPageParam: (lastPage: (typeof data)[number]) =>
lastPage.currentPage === lastPage.totalPages
? undefined
: lastPage.currentPage + 1,
}
}

describe('usePrefetchInfiniteQuery', () => {
let queryCache: QueryCache
let queryClient: QueryClient
Expand All @@ -60,18 +32,21 @@ describe('usePrefetchInfiniteQuery', () => {

it('should prefetch an infinite query if query state does not exist', async () => {
const data = [
{ data: 'Do you fetch on render?', currentPage: 1, totalPages: 3 },
{ data: 'Or do you render as you fetch?', currentPage: 2, totalPages: 3 },
{
data: 'Either way, Tanstack Query helps you!',
currentPage: 3,
totalPages: 3,
},
'Do you fetch on render?',
'Or do you render as you fetch?',
'Either way, Tanstack Query helps you!',
]

const queryOpts = {
queryKey: queryKey(),
...generateInfiniteQueryOptions(data),
queryFn: vi
.fn<(context: { pageParam: number }) => Promise<string>>()
.mockImplementation(({ pageParam }) =>
sleep(10).then(() => data[pageParam]!),
),
initialPageParam: 0,
getNextPageParam: (_lastPage: string, allPages: Array<string>) =>
allPages.length < data.length ? allPages.length : undefined,
}

function Page() {
Expand All @@ -80,7 +55,7 @@ describe('usePrefetchInfiniteQuery', () => {
return (
<div>
{state.data.pages.map((page, index) => (
<div key={index}>data: {page.data}</div>
<div key={index}>data: {page}</div>
))}
<button onClick={() => state.fetchNextPage()}>Next Page</button>
</div>
Expand Down Expand Up @@ -114,26 +89,35 @@ describe('usePrefetchInfiniteQuery', () => {
})

it('should not display fallback if the query cache is already populated', async () => {
const data = [
'Prefetch rocks!',
'No waterfalls, boy!',
'Tanstack Query #ftw',
]

const queryOpts = {
queryKey: queryKey(),
...generateInfiniteQueryOptions([
{ data: 'Prefetch rocks!', currentPage: 1, totalPages: 3 },
{ data: 'No waterfalls, boy!', currentPage: 2, totalPages: 3 },
{ data: 'Tanstack Query #ftw', currentPage: 3, totalPages: 3 },
]),
queryFn: vi
.fn<(context: { pageParam: number }) => Promise<string>>()
.mockImplementation(({ pageParam }) =>
sleep(10).then(() => data[pageParam]!),
),
initialPageParam: 0,
getNextPageParam: (_lastPage: string, allPages: Array<string>) =>
allPages.length < data.length ? allPages.length : undefined,
}

queryClient.prefetchInfiniteQuery({ ...queryOpts, pages: 3 })
await vi.advanceTimersByTimeAsync(30)
;(queryOpts.queryFn as Mock).mockClear()
queryOpts.queryFn.mockClear()

function Page() {
const state = useSuspenseInfiniteQuery(queryOpts)

return (
<div>
{state.data.pages.map((page, index) => (
<div key={index}>data: {page.data}</div>
<div key={index}>data: {page}</div>
))}
<button onClick={() => state.fetchNextPage()}>Next Page</button>
</div>
Expand Down Expand Up @@ -162,13 +146,20 @@ describe('usePrefetchInfiniteQuery', () => {
})

it('should not create an endless loop when using inside a suspense boundary', async () => {
const data = ['Infinite Page 1', 'Infinite Page 2', 'Infinite Page 3']

const queryOpts = {
queryKey: queryKey(),
...generateInfiniteQueryOptions([
{ data: 'Infinite Page 1', currentPage: 1, totalPages: 3 },
{ data: 'Infinite Page 2', currentPage: 1, totalPages: 3 },
{ data: 'Infinite Page 3', currentPage: 1, totalPages: 3 },
]),
queryFn: vi
.fn<(context: { pageParam: number }) => Promise<string>>()
.mockImplementation(({ pageParam }) =>
sleep(10).then(() => data[pageParam]!),
),
initialPageParam: 0,
// always reports another page available, to guard against an endless
// auto-advance loop rather than a bounded pagination sequence
getNextPageParam: (_lastPage: string, allPages: Array<string>) =>
allPages.length,
}

function Prefetch({ children }: { children: VNode }) {
Expand All @@ -182,7 +173,7 @@ describe('usePrefetchInfiniteQuery', () => {
return (
<div>
{state.data.pages.map((page, index) => (
<div key={index}>data: {page.data}</div>
<div key={index}>data: {page}</div>
))}
<button onClick={() => state.fetchNextPage()}>Next Page</button>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,37 +9,6 @@ import {
useSuspenseInfiniteQuery,
} from '..'
import { renderWithClient } from './utils'
import type { Mock } from 'vitest'

const createFallback = () =>
vi.fn().mockImplementation(() => <div>Loading...</div>)

const generateInfiniteQueryOptions = (
data: Array<{ data: string; currentPage: number; totalPages: number }>,
) => {
let currentPage = 0

return {
queryFn: vi
.fn<(...args: Array<any>) => Promise<(typeof data)[number]>>()
.mockImplementation(async () => {
const currentPageData = data[currentPage]
if (!currentPageData) {
throw new Error('No data defined for page ' + currentPage)
}

await sleep(10)
currentPage++

return currentPageData
}),
initialPageParam: 1,
getNextPageParam: (lastPage: (typeof data)[number]) =>
lastPage.currentPage === lastPage.totalPages
? undefined
: lastPage.currentPage + 1,
}
}

describe('usePrefetchInfiniteQuery', () => {
let queryCache: QueryCache
Expand All @@ -57,20 +26,23 @@ describe('usePrefetchInfiniteQuery', () => {
})

it('should prefetch an infinite query if query state does not exist', async () => {
const Fallback = createFallback()
const Fallback = vi.fn().mockImplementation(() => <div>Loading...</div>)
const data = [
{ data: 'Do you fetch on render?', currentPage: 1, totalPages: 3 },
{ data: 'Or do you render as you fetch?', currentPage: 2, totalPages: 3 },
{
data: 'Either way, Tanstack Query helps you!',
currentPage: 3,
totalPages: 3,
},
'Do you fetch on render?',
'Or do you render as you fetch?',
'Either way, Tanstack Query helps you!',
]

const queryOpts = {
queryKey: queryKey(),
...generateInfiniteQueryOptions(data),
queryFn: vi
.fn<(context: { pageParam: number }) => Promise<string>>()
.mockImplementation(({ pageParam }) =>
sleep(10).then(() => data[pageParam]!),
),
initialPageParam: 0,
getNextPageParam: (_lastPage: string, allPages: Array<string>) =>
allPages.length < data.length ? allPages.length : undefined,
}

function Page() {
Expand All @@ -79,7 +51,7 @@ describe('usePrefetchInfiniteQuery', () => {
return (
<div>
{state.data.pages.map((page, index) => (
<div key={index}>data: {page.data}</div>
<div key={index}>data: {page}</div>
))}
<button onClick={() => state.fetchNextPage()}>Next Page</button>
</div>
Expand Down Expand Up @@ -113,27 +85,36 @@ describe('usePrefetchInfiniteQuery', () => {
})

it('should not display fallback if the query cache is already populated', async () => {
const Fallback = createFallback()
const Fallback = vi.fn().mockImplementation(() => <div>Loading...</div>)
const data = [
'Prefetch rocks!',
'No waterfalls, boy!',
'Tanstack Query #ftw',
]

const queryOpts = {
queryKey: queryKey(),
...generateInfiniteQueryOptions([
{ data: 'Prefetch rocks!', currentPage: 1, totalPages: 3 },
{ data: 'No waterfalls, boy!', currentPage: 2, totalPages: 3 },
{ data: 'Tanstack Query #ftw', currentPage: 3, totalPages: 3 },
]),
queryFn: vi
.fn<(context: { pageParam: number }) => Promise<string>>()
.mockImplementation(({ pageParam }) =>
sleep(10).then(() => data[pageParam]!),
),
initialPageParam: 0,
getNextPageParam: (_lastPage: string, allPages: Array<string>) =>
allPages.length < data.length ? allPages.length : undefined,
}

queryClient.prefetchInfiniteQuery({ ...queryOpts, pages: 3 })
await vi.advanceTimersByTimeAsync(30)
;(queryOpts.queryFn as Mock).mockClear()
queryOpts.queryFn.mockClear()

function Page() {
const state = useSuspenseInfiniteQuery(queryOpts)

return (
<div>
{state.data.pages.map((page, index) => (
<div key={index}>data: {page.data}</div>
<div key={index}>data: {page}</div>
))}
<button onClick={() => state.fetchNextPage()}>Next Page</button>
</div>
Expand Down Expand Up @@ -162,13 +143,20 @@ describe('usePrefetchInfiniteQuery', () => {
})

it('should not create an endless loop when using inside a suspense boundary', async () => {
const data = ['Infinite Page 1', 'Infinite Page 2', 'Infinite Page 3']

const queryOpts = {
queryKey: queryKey(),
...generateInfiniteQueryOptions([
{ data: 'Infinite Page 1', currentPage: 1, totalPages: 3 },
{ data: 'Infinite Page 2', currentPage: 1, totalPages: 3 },
{ data: 'Infinite Page 3', currentPage: 1, totalPages: 3 },
]),
queryFn: vi
.fn<(context: { pageParam: number }) => Promise<string>>()
.mockImplementation(({ pageParam }) =>
sleep(10).then(() => data[pageParam]!),
),
initialPageParam: 0,
// always reports another page available, to guard against an endless
// auto-advance loop rather than a bounded pagination sequence
getNextPageParam: (_lastPage: string, allPages: Array<string>) =>
allPages.length,
}

function Prefetch({ children }: { children: React.ReactNode }) {
Expand All @@ -182,7 +170,7 @@ describe('usePrefetchInfiniteQuery', () => {
return (
<div>
{state.data.pages.map((page, index) => (
<div key={index}>data: {page.data}</div>
<div key={index}>data: {page}</div>
))}
<button onClick={() => state.fetchNextPage()}>Next Page</button>
</div>
Expand Down
Loading