Skip to content

Commit

Permalink
feat(vue-query): add support for infiniteQueryOptions (#7257)
Browse files Browse the repository at this point in the history
* feat(vue-query): ✨ add support for infiniteQueryOptions

* test(vue-query): ✅ add infiniteQueryOptions types tests

* test(vue-query): ✅ add test for infiniteQueryOptions

* test: ✅ add more infiniteQueryOptions test

* fix: ✅ fix failing test
  • Loading branch information
DavideSegullo committed Jun 8, 2024
1 parent 4e1a04b commit c73eb30
Show file tree
Hide file tree
Showing 5 changed files with 298 additions and 0 deletions.
150 changes: 150 additions & 0 deletions packages/vue-query/src/__tests__/infiniteQueryOptions.types.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import { describe, expectTypeOf, it } from 'vitest'
import { QueryClient } from '@tanstack/query-core'
import { reactive } from 'vue-demi'
import { infiniteQueryOptions } from '../infiniteQueryOptions'
import { useInfiniteQuery } from '../useInfiniteQuery'
import { type Equal, type Expect, doNotExecute } from './test-utils'
import type { InfiniteData, dataTagSymbol } from '@tanstack/query-core'

describe('infiniteQueryOptions', () => {
it('should not allow excess properties', () => {
doNotExecute(() =>
infiniteQueryOptions({
queryKey: ['key'],
queryFn: () => Promise.resolve('data'),
getNextPageParam: () => 1,
initialPageParam: 1,
// @ts-expect-error this is a good error, because stallTime does not exist!
stallTime: 1000,
}),
)
})
it('should infer types for callbacks', () => {
doNotExecute(() =>
infiniteQueryOptions({
queryKey: ['key'],
queryFn: () => Promise.resolve('data'),
staleTime: 1000,
getNextPageParam: () => 1,
initialPageParam: 1,
select: (data) => {
const result: Expect<
Equal<InfiniteData<string, number>, typeof data>
> = true

return result
},
}),
)
})
it('should work when passed to useInfiniteQuery', () => {
doNotExecute(() => {
const options = infiniteQueryOptions({
queryKey: ['key'],
queryFn: () => Promise.resolve('string'),
getNextPageParam: () => 1,
initialPageParam: 1,
})

const { data } = reactive(useInfiniteQuery(options))

const result: Expect<
Equal<typeof data, InfiniteData<string, unknown> | undefined>
> = true

return result
})
})
it('should tag the queryKey with the result type of the QueryFn', () => {
doNotExecute(() => {
const { queryKey } = infiniteQueryOptions({
queryKey: ['key'],
queryFn: () => Promise.resolve('string'),
getNextPageParam: () => 1,
initialPageParam: 1,
})

const result: Expect<
Equal<(typeof queryKey)[typeof dataTagSymbol], InfiniteData<string>>
> = true

return result
})
})
it('should tag the queryKey even if no promise is returned', () => {
doNotExecute(() => {
const { queryKey } = infiniteQueryOptions({
queryKey: ['key'],
queryFn: () => 'string',
getNextPageParam: () => 1,
initialPageParam: 1,
})

const result: Expect<
Equal<(typeof queryKey)[typeof dataTagSymbol], InfiniteData<string>>
> = true

return result
})
})
it('should tag the queryKey with the result type of the QueryFn if select is used', () => {
doNotExecute(() => {
const { queryKey } = infiniteQueryOptions({
queryKey: ['key'],
queryFn: () => Promise.resolve('string'),
select: (data) => data.pages,
getNextPageParam: () => 1,
initialPageParam: 1,
})

const result: Expect<
Equal<(typeof queryKey)[typeof dataTagSymbol], InfiniteData<string>>
> = true

return result
})
})
it('should return the proper type when passed to getQueryData', () => {
doNotExecute(() => {
const { queryKey } = infiniteQueryOptions({
queryKey: ['key'],
queryFn: () => Promise.resolve('string'),
getNextPageParam: () => 1,
initialPageParam: 1,
})

const queryClient = new QueryClient()
const data = queryClient.getQueryData(queryKey)

const result: Expect<
Equal<typeof data, InfiniteData<string, unknown> | undefined>
> = true

return result
})
})
it('should properly type when passed to setQueryData', () => {
doNotExecute(() => {
const { queryKey } = infiniteQueryOptions({
queryKey: ['key'],
queryFn: () => Promise.resolve('string'),
getNextPageParam: () => 1,
initialPageParam: 1,
})

const queryClient = new QueryClient()
const data = queryClient.setQueryData(queryKey, (prev) => {
expectTypeOf(prev).toEqualTypeOf<
InfiniteData<string, unknown> | undefined
>()
return prev
})

const result: Expect<
Equal<typeof data, InfiniteData<string, unknown> | undefined>
> = true

return result
})
})
})
17 changes: 17 additions & 0 deletions packages/vue-query/src/__tests__/queryClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { describe, expect, test, vi } from 'vitest'
import { ref } from 'vue-demi'
import { QueryClient as QueryClientOrigin } from '@tanstack/query-core'
import { QueryClient } from '../queryClient'
import { infiniteQueryOptions } from '../infiniteQueryOptions'
import { flushPromises } from './test-utils'

vi.mock('@tanstack/query-core')
Expand Down Expand Up @@ -264,6 +265,22 @@ describe('QueryCache', () => {
initialPageParam: 0,
})

expect(QueryClientOrigin.prototype.fetchInfiniteQuery).toBeCalledWith({
initialPageParam: 0,
queryKey: queryKeyUnref,
})
})
test('should properly unwrap parameter using infiniteQueryOptions with unref', async () => {
const queryClient = new QueryClient()

const options = infiniteQueryOptions({
queryKey: queryKeyUnref,
initialPageParam: 0,
getNextPageParam: () => 12,
})

queryClient.fetchInfiniteQuery(options)

expect(QueryClientOrigin.prototype.fetchInfiniteQuery).toBeCalledWith({
initialPageParam: 0,
queryKey: queryKeyUnref,
Expand Down
32 changes: 32 additions & 0 deletions packages/vue-query/src/__tests__/useInfiniteQuery.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { describe, expect, test, vi } from 'vitest'
import { useInfiniteQuery } from '../useInfiniteQuery'
import { infiniteQueryOptions } from '../infiniteQueryOptions'
import { flushPromises, infiniteFetcher } from './test-utils'

vi.mock('../useQueryClient')
Expand Down Expand Up @@ -28,6 +29,37 @@ describe('useQuery', () => {

await flushPromises()

expect(data.value).toStrictEqual({
pageParams: [0, 12],
pages: ['data on page 0', 'data on page 12'],
})
expect(status.value).toStrictEqual('success')
})
test('should properly execute infinite query using infiniteQueryOptions', async () => {
const options = infiniteQueryOptions({
queryKey: ['infiniteQueryOptions'],
queryFn: infiniteFetcher,
initialPageParam: 0,
getNextPageParam: () => 12,
})

const { data, fetchNextPage, status } = useInfiniteQuery(options)

expect(data.value).toStrictEqual(undefined)
expect(status.value).toStrictEqual('pending')

await flushPromises()

expect(data.value).toStrictEqual({
pageParams: [0],
pages: ['data on page 0'],
})
expect(status.value).toStrictEqual('success')

fetchNextPage()

await flushPromises()

expect(data.value).toStrictEqual({
pageParams: [0, 12],
pages: ['data on page 0', 'data on page 12'],
Expand Down
5 changes: 5 additions & 0 deletions packages/vue-query/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ export { VueQueryPlugin } from './vueQueryPlugin'
export { QueryClient } from './queryClient'
export { QueryCache } from './queryCache'
export { queryOptions } from './queryOptions'
export { infiniteQueryOptions } from './infiniteQueryOptions'
export type {
DefinedInitialDataInfiniteOptions,
UndefinedInitialDataInfiniteOptions,
} from './infiniteQueryOptions'
export { MutationCache } from './mutationCache'
export { useQuery } from './useQuery'
export { useQueries } from './useQueries'
Expand Down
94 changes: 94 additions & 0 deletions packages/vue-query/src/infiniteQueryOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import type { DataTag } from '@tanstack/query-core'
import type { InfiniteData } from '@tanstack/query-core'
import type { UseInfiniteQueryOptions } from './useInfiniteQuery'
import type { DefaultError, QueryKey } from '@tanstack/query-core'

export type UndefinedInitialDataInfiniteOptions<
TQueryFnData,
TError = DefaultError,
TData = InfiniteData<TQueryFnData>,
TQueryKey extends QueryKey = QueryKey,
TPageParam = unknown,
> = UseInfiniteQueryOptions<
TQueryFnData,
TError,
TData,
TQueryFnData,
TQueryKey,
TPageParam
> & {
initialData?: undefined
}

type NonUndefinedGuard<T> = T extends undefined ? never : T

export type DefinedInitialDataInfiniteOptions<
TQueryFnData,
TError = DefaultError,
TData = InfiniteData<TQueryFnData>,
TQueryKey extends QueryKey = QueryKey,
TPageParam = unknown,
> = UseInfiniteQueryOptions<
TQueryFnData,
TError,
TData,
TQueryFnData,
TQueryKey,
TPageParam
> & {
initialData:
| NonUndefinedGuard<InfiniteData<TQueryFnData, TPageParam>>
| (() => NonUndefinedGuard<InfiniteData<TQueryFnData, TPageParam>>)
}

export function infiniteQueryOptions<
TQueryFnData,
TError = DefaultError,
TData = InfiniteData<TQueryFnData>,
TQueryKey extends QueryKey = QueryKey,
TPageParam = unknown,
>(
options: UndefinedInitialDataInfiniteOptions<
TQueryFnData,
TError,
TData,
TQueryKey,
TPageParam
>,
): UndefinedInitialDataInfiniteOptions<
TQueryFnData,
TError,
TData,
TQueryKey,
TPageParam
> & {
queryKey: DataTag<TQueryKey, InfiniteData<TQueryFnData>>
}

export function infiniteQueryOptions<
TQueryFnData,
TError = DefaultError,
TData = InfiniteData<TQueryFnData>,
TQueryKey extends QueryKey = QueryKey,
TPageParam = unknown,
>(
options: DefinedInitialDataInfiniteOptions<
TQueryFnData,
TError,
TData,
TQueryKey,
TPageParam
>,
): DefinedInitialDataInfiniteOptions<
TQueryFnData,
TError,
TData,
TQueryKey,
TPageParam
> & {
queryKey: DataTag<TQueryKey, InfiniteData<TQueryFnData>>
}

export function infiniteQueryOptions(options: unknown) {
return options
}

0 comments on commit c73eb30

Please sign in to comment.