diff --git a/packages/svelte-query/tests/createQueries/createQueries.svelte.test.ts b/packages/svelte-query/tests/createQueries/createQueries.svelte.test.ts index 082eb072f1c..03df185c0bc 100644 --- a/packages/svelte-query/tests/createQueries/createQueries.svelte.test.ts +++ b/packages/svelte-query/tests/createQueries/createQueries.svelte.test.ts @@ -1,25 +1,10 @@ -import { - afterEach, - beforeEach, - describe, - expect, - expectTypeOf, - it, - vi, -} from 'vitest' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { render } from '@testing-library/svelte' import { sleep } from '@tanstack/query-test-utils' import { QueryClient, createQueries } from '../../src/index.js' import { promiseWithResolvers, withEffectRoot } from '../utils.svelte.js' import IsRestoringExample from './IsRestoringExample.svelte' -import type { - CreateQueryOptions, - CreateQueryResult, - QueryFunction, - QueryFunctionContext, - QueryKey, - skipToken, -} from '../../src/index.js' +import type { CreateQueryResult } from '../../src/index.js' describe('createQueries', () => { let queryClient: QueryClient @@ -81,672 +66,6 @@ describe('createQueries', () => { }), ) - it( - 'handles type parameter - tuple of tuples', - withEffectRoot(() => { - const key1 = ['test-key-1'] - const key2 = ['test-key-2'] - const key3 = ['test-key-3'] - - const result1 = createQueries< - [[number], [string], [Array, boolean]] - >( - () => ({ - queries: [ - { - queryKey: key1, - queryFn: () => 1, - }, - { - queryKey: key2, - queryFn: () => 'string', - }, - { - queryKey: key3, - queryFn: () => ['string[]'], - }, - ], - }), - () => queryClient, - ) - - expectTypeOf(result1[0]).toEqualTypeOf< - CreateQueryResult - >() - expectTypeOf(result1[1]).toEqualTypeOf< - CreateQueryResult - >() - expectTypeOf(result1[2]).toEqualTypeOf< - CreateQueryResult, boolean> - >() - expectTypeOf(result1[0].data).toEqualTypeOf() - expectTypeOf(result1[1].data).toEqualTypeOf() - expectTypeOf(result1[2].data).toEqualTypeOf | undefined>() - expectTypeOf(result1[2].error).toEqualTypeOf() - - // TData (3rd element) takes precedence over TQueryFnData (1st element) - const result2 = createQueries< - [[string, unknown, string], [string, unknown, number]] - >( - () => ({ - queries: [ - { - queryKey: key1, - queryFn: () => 'string', - select: (a) => { - expectTypeOf(a).toEqualTypeOf() - return a.toLowerCase() - }, - }, - { - queryKey: key2, - queryFn: () => 'string', - select: (a) => { - expectTypeOf(a).toEqualTypeOf() - return parseInt(a) - }, - }, - ], - }), - () => queryClient, - ) - - expectTypeOf(result2[0]).toEqualTypeOf< - CreateQueryResult - >() - expectTypeOf(result2[1]).toEqualTypeOf< - CreateQueryResult - >() - expectTypeOf(result2[0].data).toEqualTypeOf() - expectTypeOf(result2[1].data).toEqualTypeOf() - - // types should be enforced - createQueries<[[string, unknown, string], [string, boolean, number]]>( - () => ({ - queries: [ - { - queryKey: key1, - queryFn: () => 'string', - select: (a) => { - expectTypeOf(a).toEqualTypeOf() - return a.toLowerCase() - }, - placeholderData: 'string', - // @ts-expect-error (initialData: string) - initialData: 123, - }, - { - queryKey: key2, - queryFn: () => 'string', - select: (a) => { - expectTypeOf(a).toEqualTypeOf() - return parseInt(a) - }, - placeholderData: 'string', - // @ts-expect-error (initialData: string) - initialData: 123, - }, - ], - }), - () => queryClient, - ) - - // field names should be enforced - createQueries<[[string]]>( - () => ({ - queries: [ - { - queryKey: key1, - queryFn: () => 'string', - }, - ], - }), - () => queryClient, - ) - }), - ) - - it( - 'handles type parameter - tuple of objects', - withEffectRoot(() => { - const key1 = ['test-key-1'] - const key2 = ['test-key-2'] - const key3 = ['test-key-3'] - - const result1 = createQueries< - [ - { queryFnData: number }, - { queryFnData: string }, - { queryFnData: Array; error: boolean }, - ] - >( - () => ({ - queries: [ - { - queryKey: key1, - queryFn: () => 1, - }, - { - queryKey: key2, - queryFn: () => 'string', - }, - { - queryKey: key3, - queryFn: () => ['string[]'], - }, - ], - }), - () => queryClient, - ) - - expectTypeOf(result1[0]).toEqualTypeOf< - CreateQueryResult - >() - expectTypeOf(result1[1]).toEqualTypeOf< - CreateQueryResult - >() - expectTypeOf(result1[2]).toEqualTypeOf< - CreateQueryResult, boolean> - >() - expectTypeOf(result1[0].data).toEqualTypeOf() - expectTypeOf(result1[1].data).toEqualTypeOf() - expectTypeOf(result1[2].data).toEqualTypeOf | undefined>() - expectTypeOf(result1[2].error).toEqualTypeOf() - - // TData (data prop) takes precedence over TQueryFnData (queryFnData prop) - const result2 = createQueries< - [ - { queryFnData: string; data: string }, - { queryFnData: string; data: number }, - ] - >( - () => ({ - queries: [ - { - queryKey: key1, - queryFn: () => 'string', - select: (a) => { - expectTypeOf(a).toEqualTypeOf() - return a.toLowerCase() - }, - }, - { - queryKey: key2, - queryFn: () => 'string', - select: (a) => { - expectTypeOf(a).toEqualTypeOf() - return parseInt(a) - }, - }, - ], - }), - () => queryClient, - ) - - expectTypeOf(result2[0]).toEqualTypeOf< - CreateQueryResult - >() - expectTypeOf(result2[1]).toEqualTypeOf< - CreateQueryResult - >() - expectTypeOf(result2[0].data).toEqualTypeOf() - expectTypeOf(result2[1].data).toEqualTypeOf() - - // can pass only TData (data prop) although TQueryFnData will be left unknown - const result3 = createQueries<[{ data: string }, { data: number }]>( - () => ({ - queries: [ - { - queryKey: key1, - queryFn: () => 'string', - select: (a) => { - expectTypeOf(a).toEqualTypeOf() - return a as string - }, - }, - { - queryKey: key2, - queryFn: () => 'string', - select: (a) => { - expectTypeOf(a).toEqualTypeOf() - return a as number - }, - }, - ], - }), - () => queryClient, - ) - - expectTypeOf(result3[0]).toEqualTypeOf< - CreateQueryResult - >() - expectTypeOf(result3[1]).toEqualTypeOf< - CreateQueryResult - >() - expectTypeOf(result3[0].data).toEqualTypeOf() - expectTypeOf(result3[1].data).toEqualTypeOf() - - // types should be enforced - createQueries< - [ - { queryFnData: string; data: string }, - { queryFnData: string; data: number; error: boolean }, - ] - >( - () => ({ - queries: [ - { - queryKey: key1, - queryFn: () => 'string', - select: (a) => { - expectTypeOf(a).toEqualTypeOf() - return a.toLowerCase() - }, - placeholderData: 'string', - // @ts-expect-error (initialData: string) - initialData: 123, - }, - { - queryKey: key2, - queryFn: () => 'string', - select: (a) => { - expectTypeOf(a).toEqualTypeOf() - return parseInt(a) - }, - placeholderData: 'string', - // @ts-expect-error (initialData: string) - initialData: 123, - }, - ], - }), - () => queryClient, - ) - - // field names should be enforced - createQueries<[{ queryFnData: string }]>( - () => ({ - queries: [ - { - queryKey: key1, - queryFn: () => 'string', - }, - ], - }), - () => queryClient, - ) - }), - ) - - it( - 'handles array literal without type parameter to infer result type', - withEffectRoot(() => { - const key1 = ['test-key-1'] - const key2 = ['test-key-2'] - const key3 = ['test-key-3'] - const key4 = ['test-key-4'] - - // Array.map preserves TQueryFnData - const result1 = createQueries( - () => ({ - queries: Array(50).map((_, i) => ({ - queryKey: ['key', i] as const, - queryFn: () => i + 10, - })), - }), - () => queryClient, - ) - - expectTypeOf(result1).toEqualTypeOf< - Array> - >() - if (result1[0]) { - expectTypeOf(result1[0].data).toEqualTypeOf() - } - - // Array.map preserves TData - const result2 = createQueries( - () => ({ - queries: Array(50).map((_, i) => ({ - queryKey: ['key', i] as const, - queryFn: () => i + 10, - select: (data: number) => data.toString(), - })), - }), - () => queryClient, - ) - - expectTypeOf(result2).toEqualTypeOf< - Array> - >() - - const result3 = createQueries( - () => ({ - queries: [ - { - queryKey: key1, - queryFn: () => 1, - }, - { - queryKey: key2, - queryFn: () => 'string', - }, - { - queryKey: key3, - queryFn: () => ['string[]'], - select: () => 123, - }, - ], - }), - () => queryClient, - ) - - expectTypeOf(result3[0]).toEqualTypeOf>() - expectTypeOf(result3[1]).toEqualTypeOf>() - expectTypeOf(result3[2]).toEqualTypeOf>() - expectTypeOf(result3[0].data).toEqualTypeOf() - expectTypeOf(result3[1].data).toEqualTypeOf() - // select takes precedence over queryFn - expectTypeOf(result3[2].data).toEqualTypeOf() - - // initialData/placeholderData are enforced - createQueries( - () => ({ - queries: [ - { - queryKey: key1, - queryFn: () => 'string', - placeholderData: 'string', - // @ts-expect-error (initialData: string) - initialData: 123, - }, - { - queryKey: key2, - queryFn: () => 123, - // @ts-expect-error (placeholderData: number) - placeholderData: 'string', - initialData: 123, - }, - ], - }), - () => queryClient, - ) - - // select params are "indirectly" enforced - createQueries( - () => ({ - queries: [ - // unfortunately TS will not suggest the type for you - { - queryKey: key1, - queryFn: () => 'string', - }, - // however you can add a type to the callback - { - queryKey: key2, - queryFn: () => 'string', - }, - // the type you do pass is enforced - { - queryKey: key3, - queryFn: () => 'string', - }, - { - queryKey: key4, - queryFn: () => 'string', - select: (a: string) => parseInt(a), - }, - ], - }), - () => queryClient, - ) - - // callbacks are also indirectly enforced with Array.map - createQueries( - () => ({ - queries: Array(50).map((_, i) => ({ - queryKey: ['key', i] as const, - queryFn: () => i + 10, - select: (data: number) => data.toString(), - })), - }), - () => queryClient, - ) - - // results inference works when all the handlers are defined - const result4 = createQueries( - () => ({ - queries: [ - { - queryKey: key1, - queryFn: () => 'string', - }, - { - queryKey: key2, - queryFn: () => 'string', - }, - { - queryKey: key4, - queryFn: () => 'string', - select: (a: string) => parseInt(a), - }, - ], - }), - () => queryClient, - ) - - expectTypeOf(result4[0]).toEqualTypeOf>() - expectTypeOf(result4[1]).toEqualTypeOf>() - expectTypeOf(result4[2]).toEqualTypeOf>() - - // handles when queryFn returns a Promise - const result5 = createQueries( - () => ({ - queries: [ - { - queryKey: key1, - queryFn: () => Promise.resolve('string'), - }, - ], - }), - () => queryClient, - ) - - expectTypeOf(result5[0]).toEqualTypeOf>() - - // Array as const does not throw error - const result6 = createQueries( - () => - ({ - queries: [ - { - queryKey: ['key1'], - queryFn: () => 'string', - }, - { - queryKey: ['key1'], - queryFn: () => 123, - }, - ], - }) as const, - () => queryClient, - ) - - expectTypeOf(result6[0]).toEqualTypeOf>() - expectTypeOf(result6[1]).toEqualTypeOf>() - - // field names should be enforced - array literal - createQueries( - () => ({ - queries: [ - { - queryKey: key1, - queryFn: () => 'string', - }, - ], - }), - () => queryClient, - ) - - // field names should be enforced - Array.map() result - createQueries( - () => ({ - // @ts-expect-error (invalidField) - queries: Array(10).map(() => ({ - someInvalidField: '', - })), - }), - () => queryClient, - ) - - // supports queryFn using fetch() to return Promise - Array.map() result - createQueries( - () => ({ - queries: Array(50).map((_, i) => ({ - queryKey: ['key', i] as const, - queryFn: () => - fetch('return Promise').then((resp) => resp.json()), - })), - }), - () => queryClient, - ) - - // supports queryFn using fetch() to return Promise - array literal - createQueries( - () => ({ - queries: [ - { - queryKey: key1, - queryFn: () => - fetch('return Promise').then((resp) => resp.json()), - }, - ], - }), - () => queryClient, - ) - }), - ) - - it( - 'handles strongly typed queryFn factories and createQueries wrappers', - withEffectRoot(() => { - // QueryKey + queryFn factory - type QueryKeyA = ['queryA'] - const getQueryKeyA = (): QueryKeyA => ['queryA'] - type GetQueryFunctionA = () => QueryFunction - const getQueryFunctionA: GetQueryFunctionA = () => () => { - return 1 - } - type SelectorA = (data: number) => [number, string] - const getSelectorA = (): SelectorA => (data) => [data, data.toString()] - - type QueryKeyB = ['queryB', string] - const getQueryKeyB = (id: string): QueryKeyB => ['queryB', id] - type GetQueryFunctionB = () => QueryFunction - const getQueryFunctionB: GetQueryFunctionB = () => () => { - return '1' - } - type SelectorB = (data: string) => [string, number] - const getSelectorB = (): SelectorB => (data) => [data, +data] - - // Wrapper with strongly typed array-parameter - function useWrappedQueries< - TQueryFnData, - TError, - TData, - TQueryKey extends QueryKey, - >( - queries: Array< - CreateQueryOptions - >, - ) { - return createQueries( - () => ({ - queries: queries.map( - // no need to type the mapped query - (query) => { - const { queryFn: fn, queryKey: key } = query - expectTypeOf(fn).toEqualTypeOf< - | typeof skipToken - | QueryFunction - | undefined - >() - return { - queryKey: key, - queryFn: fn - ? (ctx: QueryFunctionContext) => { - // eslint-disable-next-line vitest/valid-expect - expectTypeOf(ctx.queryKey) - return ( - fn as QueryFunction - ).call({}, ctx) - } - : undefined, - } - }, - ), - }), - () => queryClient, - ) - } - - const result = createQueries( - () => ({ - queries: [ - { - queryKey: getQueryKeyA(), - queryFn: getQueryFunctionA(), - }, - { - queryKey: getQueryKeyB('id'), - queryFn: getQueryFunctionB(), - }, - ], - }), - () => queryClient, - ) - - expectTypeOf(result[0]).toEqualTypeOf>() - expectTypeOf(result[1]).toEqualTypeOf>() - - const withSelector = createQueries( - () => ({ - queries: [ - { - queryKey: getQueryKeyA(), - queryFn: getQueryFunctionA(), - select: getSelectorA(), - }, - { - queryKey: getQueryKeyB('id'), - queryFn: getQueryFunctionB(), - select: getSelectorB(), - }, - ], - }), - () => queryClient, - ) - - expectTypeOf(withSelector[0]).toEqualTypeOf< - CreateQueryResult<[number, string], Error> - >() - expectTypeOf(withSelector[1]).toEqualTypeOf< - CreateQueryResult<[string, number], Error> - >() - - const withWrappedQueries = useWrappedQueries( - Array(10).map(() => ({ - queryKey: getQueryKeyA(), - queryFn: getQueryFunctionA(), - select: getSelectorA(), - })), - ) - - expectTypeOf(withWrappedQueries).toEqualTypeOf< - Array> - >() - }), - ) - it( 'should track results', withEffectRoot(async () => { diff --git a/packages/svelte-query/tests/createQueries/createQueries.test-d.ts b/packages/svelte-query/tests/createQueries/createQueries.test-d.ts index 0783afe4e9b..44d099c81a2 100644 --- a/packages/svelte-query/tests/createQueries/createQueries.test-d.ts +++ b/packages/svelte-query/tests/createQueries/createQueries.test-d.ts @@ -1,6 +1,14 @@ import { describe, expectTypeOf, it } from 'vitest' +import { QueryClient } from '@tanstack/query-core' import { createQueries, queryOptions } from '../../src/index.js' -import type { CreateQueryResult } from '../../src/index.js' +import type { + CreateQueryOptions, + CreateQueryResult, + QueryFunction, + QueryFunctionContext, + QueryKey, + skipToken, +} from '../../src/index.js' describe('createQueries', () => { it('should return correct data for dynamic queries with mixed result types', () => { @@ -31,4 +39,643 @@ describe('createQueries', () => { ] >() }) + + it('handles type parameter - tuple of tuples', () => { + const queryClient = new QueryClient() + const key1 = ['test-key-1'] + const key2 = ['test-key-2'] + const key3 = ['test-key-3'] + + const result1 = createQueries< + [[number], [string], [Array, boolean]] + >( + () => ({ + queries: [ + { + queryKey: key1, + queryFn: () => 1, + }, + { + queryKey: key2, + queryFn: () => 'string', + }, + { + queryKey: key3, + queryFn: () => ['string[]'], + }, + ], + }), + () => queryClient, + ) + + expectTypeOf(result1[0]).toEqualTypeOf>() + expectTypeOf(result1[1]).toEqualTypeOf>() + expectTypeOf(result1[2]).toEqualTypeOf< + CreateQueryResult, boolean> + >() + expectTypeOf(result1[0].data).toEqualTypeOf() + expectTypeOf(result1[1].data).toEqualTypeOf() + expectTypeOf(result1[2].data).toEqualTypeOf | undefined>() + expectTypeOf(result1[2].error).toEqualTypeOf() + + // TData (3rd element) takes precedence over TQueryFnData (1st element) + const result2 = createQueries< + [[string, unknown, string], [string, unknown, number]] + >( + () => ({ + queries: [ + { + queryKey: key1, + queryFn: () => 'string', + select: (a) => { + expectTypeOf(a).toEqualTypeOf() + return a.toLowerCase() + }, + }, + { + queryKey: key2, + queryFn: () => 'string', + select: (a) => { + expectTypeOf(a).toEqualTypeOf() + return parseInt(a) + }, + }, + ], + }), + () => queryClient, + ) + + expectTypeOf(result2[0]).toEqualTypeOf>() + expectTypeOf(result2[1]).toEqualTypeOf>() + expectTypeOf(result2[0].data).toEqualTypeOf() + expectTypeOf(result2[1].data).toEqualTypeOf() + + // types should be enforced + createQueries<[[string, unknown, string], [string, boolean, number]]>( + () => ({ + queries: [ + { + queryKey: key1, + queryFn: () => 'string', + select: (a) => { + expectTypeOf(a).toEqualTypeOf() + return a.toLowerCase() + }, + placeholderData: 'string', + // @ts-expect-error (initialData: string) + initialData: 123, + }, + { + queryKey: key2, + queryFn: () => 'string', + select: (a) => { + expectTypeOf(a).toEqualTypeOf() + return parseInt(a) + }, + placeholderData: 'string', + // @ts-expect-error (initialData: string) + initialData: 123, + }, + ], + }), + () => queryClient, + ) + + // field names should be enforced + createQueries<[[string]]>( + () => ({ + queries: [ + { + queryKey: key1, + queryFn: () => 'string', + }, + ], + }), + () => queryClient, + ) + }) + + it('handles type parameter - tuple of objects', () => { + const queryClient = new QueryClient() + const key1 = ['test-key-1'] + const key2 = ['test-key-2'] + const key3 = ['test-key-3'] + + const result1 = createQueries< + [ + { queryFnData: number }, + { queryFnData: string }, + { queryFnData: Array; error: boolean }, + ] + >( + () => ({ + queries: [ + { + queryKey: key1, + queryFn: () => 1, + }, + { + queryKey: key2, + queryFn: () => 'string', + }, + { + queryKey: key3, + queryFn: () => ['string[]'], + }, + ], + }), + () => queryClient, + ) + + expectTypeOf(result1[0]).toEqualTypeOf>() + expectTypeOf(result1[1]).toEqualTypeOf>() + expectTypeOf(result1[2]).toEqualTypeOf< + CreateQueryResult, boolean> + >() + expectTypeOf(result1[0].data).toEqualTypeOf() + expectTypeOf(result1[1].data).toEqualTypeOf() + expectTypeOf(result1[2].data).toEqualTypeOf | undefined>() + expectTypeOf(result1[2].error).toEqualTypeOf() + + // TData (data prop) takes precedence over TQueryFnData (queryFnData prop) + const result2 = createQueries< + [ + { queryFnData: string; data: string }, + { queryFnData: string; data: number }, + ] + >( + () => ({ + queries: [ + { + queryKey: key1, + queryFn: () => 'string', + select: (a) => { + expectTypeOf(a).toEqualTypeOf() + return a.toLowerCase() + }, + }, + { + queryKey: key2, + queryFn: () => 'string', + select: (a) => { + expectTypeOf(a).toEqualTypeOf() + return parseInt(a) + }, + }, + ], + }), + () => queryClient, + ) + + expectTypeOf(result2[0]).toEqualTypeOf>() + expectTypeOf(result2[1]).toEqualTypeOf>() + expectTypeOf(result2[0].data).toEqualTypeOf() + expectTypeOf(result2[1].data).toEqualTypeOf() + + // can pass only TData (data prop) although TQueryFnData will be left unknown + const result3 = createQueries<[{ data: string }, { data: number }]>( + () => ({ + queries: [ + { + queryKey: key1, + queryFn: () => 'string', + select: (a) => { + expectTypeOf(a).toEqualTypeOf() + return a as string + }, + }, + { + queryKey: key2, + queryFn: () => 'string', + select: (a) => { + expectTypeOf(a).toEqualTypeOf() + return a as number + }, + }, + ], + }), + () => queryClient, + ) + + expectTypeOf(result3[0]).toEqualTypeOf>() + expectTypeOf(result3[1]).toEqualTypeOf>() + expectTypeOf(result3[0].data).toEqualTypeOf() + expectTypeOf(result3[1].data).toEqualTypeOf() + + // types should be enforced + createQueries< + [ + { queryFnData: string; data: string }, + { queryFnData: string; data: number; error: boolean }, + ] + >( + () => ({ + queries: [ + { + queryKey: key1, + queryFn: () => 'string', + select: (a) => { + expectTypeOf(a).toEqualTypeOf() + return a.toLowerCase() + }, + placeholderData: 'string', + // @ts-expect-error (initialData: string) + initialData: 123, + }, + { + queryKey: key2, + queryFn: () => 'string', + select: (a) => { + expectTypeOf(a).toEqualTypeOf() + return parseInt(a) + }, + placeholderData: 'string', + // @ts-expect-error (initialData: string) + initialData: 123, + }, + ], + }), + () => queryClient, + ) + + // field names should be enforced + createQueries<[{ queryFnData: string }]>( + () => ({ + queries: [ + { + queryKey: key1, + queryFn: () => 'string', + }, + ], + }), + () => queryClient, + ) + }) + + it('handles array literal without type parameter to infer result type', () => { + const queryClient = new QueryClient() + const key1 = ['test-key-1'] + const key2 = ['test-key-2'] + const key3 = ['test-key-3'] + const key4 = ['test-key-4'] + + // Array.map preserves TQueryFnData + const result1 = createQueries( + () => ({ + queries: Array(50).map((_, i) => ({ + queryKey: ['key', i] as const, + queryFn: () => i + 10, + })), + }), + () => queryClient, + ) + + expectTypeOf(result1).toEqualTypeOf< + Array> + >() + if (result1[0]) { + expectTypeOf(result1[0].data).toEqualTypeOf() + } + + // Array.map preserves TData + const result2 = createQueries( + () => ({ + queries: Array(50).map((_, i) => ({ + queryKey: ['key', i] as const, + queryFn: () => i + 10, + select: (data: number) => data.toString(), + })), + }), + () => queryClient, + ) + + expectTypeOf(result2).toEqualTypeOf< + Array> + >() + + const result3 = createQueries( + () => ({ + queries: [ + { + queryKey: key1, + queryFn: () => 1, + }, + { + queryKey: key2, + queryFn: () => 'string', + }, + { + queryKey: key3, + queryFn: () => ['string[]'], + select: () => 123, + }, + ], + }), + () => queryClient, + ) + + expectTypeOf(result3[0]).toEqualTypeOf>() + expectTypeOf(result3[1]).toEqualTypeOf>() + expectTypeOf(result3[2]).toEqualTypeOf>() + expectTypeOf(result3[0].data).toEqualTypeOf() + expectTypeOf(result3[1].data).toEqualTypeOf() + // select takes precedence over queryFn + expectTypeOf(result3[2].data).toEqualTypeOf() + + // initialData/placeholderData are enforced + createQueries( + () => ({ + queries: [ + { + queryKey: key1, + queryFn: () => 'string', + placeholderData: 'string', + // @ts-expect-error (initialData: string) + initialData: 123, + }, + { + queryKey: key2, + queryFn: () => 123, + // @ts-expect-error (placeholderData: number) + placeholderData: 'string', + initialData: 123, + }, + ], + }), + () => queryClient, + ) + + // select params are "indirectly" enforced + createQueries( + () => ({ + queries: [ + // unfortunately TS will not suggest the type for you + { + queryKey: key1, + queryFn: () => 'string', + }, + // however you can add a type to the callback + { + queryKey: key2, + queryFn: () => 'string', + }, + // the type you do pass is enforced + { + queryKey: key3, + queryFn: () => 'string', + }, + { + queryKey: key4, + queryFn: () => 'string', + select: (a: string) => parseInt(a), + }, + ], + }), + () => queryClient, + ) + + // callbacks are also indirectly enforced with Array.map + createQueries( + () => ({ + queries: Array(50).map((_, i) => ({ + queryKey: ['key', i] as const, + queryFn: () => i + 10, + select: (data: number) => data.toString(), + })), + }), + () => queryClient, + ) + + // results inference works when all the handlers are defined + const result4 = createQueries( + () => ({ + queries: [ + { + queryKey: key1, + queryFn: () => 'string', + }, + { + queryKey: key2, + queryFn: () => 'string', + }, + { + queryKey: key4, + queryFn: () => 'string', + select: (a: string) => parseInt(a), + }, + ], + }), + () => queryClient, + ) + + expectTypeOf(result4[0]).toEqualTypeOf>() + expectTypeOf(result4[1]).toEqualTypeOf>() + expectTypeOf(result4[2]).toEqualTypeOf>() + + // handles when queryFn returns a Promise + const result5 = createQueries( + () => ({ + queries: [ + { + queryKey: key1, + queryFn: () => Promise.resolve('string'), + }, + ], + }), + () => queryClient, + ) + + expectTypeOf(result5[0]).toEqualTypeOf>() + + // Array as const does not throw error + const result6 = createQueries( + () => + ({ + queries: [ + { + queryKey: ['key1'], + queryFn: () => 'string', + }, + { + queryKey: ['key1'], + queryFn: () => 123, + }, + ], + }) as const, + () => queryClient, + ) + + expectTypeOf(result6[0]).toEqualTypeOf>() + expectTypeOf(result6[1]).toEqualTypeOf>() + + // field names should be enforced - array literal + createQueries( + () => ({ + queries: [ + { + queryKey: key1, + queryFn: () => 'string', + }, + ], + }), + () => queryClient, + ) + + // field names should be enforced - Array.map() result + createQueries( + () => ({ + // @ts-expect-error (invalidField) + queries: Array(10).map(() => ({ + someInvalidField: '', + })), + }), + () => queryClient, + ) + + // supports queryFn using fetch() to return Promise - Array.map() result + createQueries( + () => ({ + queries: Array(50).map((_, i) => ({ + queryKey: ['key', i] as const, + queryFn: () => + fetch('return Promise').then((resp) => resp.json()), + })), + }), + () => queryClient, + ) + + // supports queryFn using fetch() to return Promise - array literal + createQueries( + () => ({ + queries: [ + { + queryKey: key1, + queryFn: () => + fetch('return Promise').then((resp) => resp.json()), + }, + ], + }), + () => queryClient, + ) + }) + + it('handles strongly typed queryFn factories and createQueries wrappers', () => { + const queryClient = new QueryClient() + + // QueryKey + queryFn factory + type QueryKeyA = ['queryA'] + const getQueryKeyA = (): QueryKeyA => ['queryA'] + type GetQueryFunctionA = () => QueryFunction + const getQueryFunctionA: GetQueryFunctionA = () => () => { + return 1 + } + type SelectorA = (data: number) => [number, string] + const getSelectorA = (): SelectorA => (data) => [data, data.toString()] + + type QueryKeyB = ['queryB', string] + const getQueryKeyB = (id: string): QueryKeyB => ['queryB', id] + type GetQueryFunctionB = () => QueryFunction + const getQueryFunctionB: GetQueryFunctionB = () => () => { + return '1' + } + type SelectorB = (data: string) => [string, number] + const getSelectorB = (): SelectorB => (data) => [data, +data] + + // Wrapper with strongly typed array-parameter + function useWrappedQueries< + TQueryFnData, + TError, + TData, + TQueryKey extends QueryKey, + >( + queries: Array< + CreateQueryOptions + >, + ) { + return createQueries( + () => ({ + queries: queries.map( + // no need to type the mapped query + (query) => { + const { queryFn: fn, queryKey: key } = query + expectTypeOf(fn).toEqualTypeOf< + | typeof skipToken + | QueryFunction + | undefined + >() + return { + queryKey: key, + queryFn: fn + ? (ctx: QueryFunctionContext) => { + // eslint-disable-next-line vitest/valid-expect + expectTypeOf(ctx.queryKey) + return ( + fn as QueryFunction + ).call({}, ctx) + } + : undefined, + } + }, + ), + }), + () => queryClient, + ) + } + + const result = createQueries( + () => ({ + queries: [ + { + queryKey: getQueryKeyA(), + queryFn: getQueryFunctionA(), + }, + { + queryKey: getQueryKeyB('id'), + queryFn: getQueryFunctionB(), + }, + ], + }), + () => queryClient, + ) + + expectTypeOf(result[0]).toEqualTypeOf>() + expectTypeOf(result[1]).toEqualTypeOf>() + + const withSelector = createQueries( + () => ({ + queries: [ + { + queryKey: getQueryKeyA(), + queryFn: getQueryFunctionA(), + select: getSelectorA(), + }, + { + queryKey: getQueryKeyB('id'), + queryFn: getQueryFunctionB(), + select: getSelectorB(), + }, + ], + }), + () => queryClient, + ) + + expectTypeOf(withSelector[0]).toEqualTypeOf< + CreateQueryResult<[number, string], Error> + >() + expectTypeOf(withSelector[1]).toEqualTypeOf< + CreateQueryResult<[string, number], Error> + >() + + const withWrappedQueries = useWrappedQueries( + Array(10).map(() => ({ + queryKey: getQueryKeyA(), + queryFn: getQueryFunctionA(), + select: getSelectorA(), + })), + ) + + expectTypeOf(withWrappedQueries).toEqualTypeOf< + Array> + >() + }) }) diff --git a/packages/svelte-query/tests/createQuery.svelte.test.ts b/packages/svelte-query/tests/createQuery.svelte.test.ts index 9cec8a17b58..2aa9f532169 100644 --- a/packages/svelte-query/tests/createQuery.svelte.test.ts +++ b/packages/svelte-query/tests/createQuery.svelte.test.ts @@ -1,16 +1,31 @@ import { flushSync } from 'svelte' -import { afterEach, describe, expect, expectTypeOf, it, vi } from 'vitest' +import { + afterEach, + beforeEach, + describe, + expect, + expectTypeOf, + it, + vi, +} from 'vitest' import { sleep } from '@tanstack/query-test-utils' import { QueryClient, createQuery, keepPreviousData } from '../src/index.js' import { promiseWithResolvers, withEffectRoot } from './utils.svelte.js' -import type { CreateQueryResult } from '../src/index.js' +import type { CreateQueryResult, QueryCache } from '../src/index.js' describe('createQuery', () => { - const queryClient = new QueryClient() - const queryCache = queryClient.getQueryCache() + let queryClient: QueryClient + let queryCache: QueryCache + + beforeEach(() => { + vi.useFakeTimers() + queryClient = new QueryClient() + queryCache = queryClient.getQueryCache() + }) afterEach(() => { queryClient.clear() + vi.useRealTimers() }) it( @@ -68,36 +83,35 @@ describe('createQuery', () => { promise: expect.any(Promise), }) resolve('resolved') - await vi.waitFor(() => - expect(query).toEqual({ - data: 'resolved', - dataUpdatedAt: expect.any(Number), - error: null, - errorUpdatedAt: 0, - failureCount: 0, - failureReason: null, - errorUpdateCount: 0, - isEnabled: true, - isError: false, - isFetched: true, - isFetchedAfterMount: true, - isFetching: false, - isPaused: false, - isPending: false, - isInitialLoading: false, - isLoading: false, - isLoadingError: false, - isPlaceholderData: false, - isRefetchError: false, - isRefetching: false, - isStale: true, - isSuccess: true, - refetch: expect.any(Function), - status: 'success', - fetchStatus: 'idle', - promise: expect.any(Promise), - }), - ) + await vi.advanceTimersByTimeAsync(0) + expect(query).toEqual({ + data: 'resolved', + dataUpdatedAt: expect.any(Number), + error: null, + errorUpdatedAt: 0, + failureCount: 0, + failureReason: null, + errorUpdateCount: 0, + isEnabled: true, + isError: false, + isFetched: true, + isFetchedAfterMount: true, + isFetching: false, + isPaused: false, + isPending: false, + isInitialLoading: false, + isLoading: false, + isLoadingError: false, + isPlaceholderData: false, + isRefetchError: false, + isRefetching: false, + isStale: true, + isSuccess: true, + refetch: expect.any(Function), + status: 'success', + fetchStatus: 'idle', + promise: expect.any(Promise), + }) expect(promise1).toBe(query.promise) }), @@ -123,7 +137,9 @@ describe('createQuery', () => { states.push({ ...query }) }) - await vi.waitFor(() => expect(query.isError).toBe(true)) + await vi.advanceTimersByTimeAsync(1) + await vi.advanceTimersByTimeAsync(0) + expect(query.isError).toBe(true) expect(states[0]).toEqual({ data: undefined, @@ -241,14 +257,13 @@ describe('createQuery', () => { }), ) resolve('resolved') - await vi.waitFor(() => - expect(query).toEqual( - expect.objectContaining({ - data: 'resolved', - isFetched: true, - isFetchedAfterMount: true, - }), - ), + await vi.advanceTimersByTimeAsync(0) + expect(query).toEqual( + expect.objectContaining({ + data: 'resolved', + isFetched: true, + isFetchedAfterMount: true, + }), ) })() }) @@ -366,9 +381,8 @@ describe('createQuery', () => { states.push({ ...query }) }) - await vi.waitFor(() => { - expect(query.data).toBe('data') - }) + await vi.advanceTimersByTimeAsync(0) + expect(query.data).toBe('data') expect(states.length).toBe(2) expect(states[0]).toMatchObject({ data: undefined }) @@ -400,7 +414,8 @@ describe('createQuery', () => { }) resolve('resolved: 1') - await vi.waitFor(() => expect(query.data).toBe('resolved: 1')) + await vi.advanceTimersByTimeAsync(0) + expect(query.data).toBe('resolved: 1') expect(query).toMatchObject({ isPending: false, @@ -430,7 +445,8 @@ describe('createQuery', () => { }) resolve('resolved: 2') - await vi.waitFor(() => expect(query.data).toBe('resolved: 2')) + await vi.advanceTimersByTimeAsync(0) + expect(query.data).toBe('resolved: 2') expect(query).toMatchObject({ data: 'resolved: 2', @@ -461,9 +477,8 @@ describe('createQuery', () => { states.push({ ...query }) }) - await vi.waitFor(() => { - expect(query.data).toBe('data') - }) + await vi.advanceTimersByTimeAsync(0) + expect(query.data).toBe('data') })() // Simulate rerender by removing the query and mounting again @@ -484,12 +499,11 @@ describe('createQuery', () => { states.push({ ...query }) }) - await vi.waitFor(() => { - expect(query.data).toBe('data') - }) + await vi.advanceTimersByTimeAsync(0) + expect(query.data).toBe('data') // Give it time to catch any accidental infinite updates - await new Promise((r) => setTimeout(r, 100)) + await vi.advanceTimersByTimeAsync(100) })() expect(states.length).toBe(4) @@ -534,9 +548,8 @@ describe('createQuery', () => { states.push({ ...query }) }) - await vi.waitFor(() => { - expect(query.data).toBe('test') - }) + await vi.advanceTimersByTimeAsync(0) + expect(query.data).toBe('test') expect(states.length).toBe(2) expect(states[0]).toMatchObject({ data: undefined }) @@ -565,9 +578,8 @@ describe('createQuery', () => { states.push({ ...query }) }) - await vi.waitFor(() => { - expect(query.data).toBe('prefetched') - }) + await vi.advanceTimersByTimeAsync(0) + expect(query.data).toBe('prefetched') expect(states.length).toBe(1) expect(states[0]).toMatchObject({ data: 'prefetched' }) @@ -593,9 +605,8 @@ describe('createQuery', () => { states.push({ ...query }) }) - await vi.waitFor(() => { - expect(query.data).toBe('test') - }) + await vi.advanceTimersByTimeAsync(0) + expect(query.data).toBe('test') expect(states.length).toBe(2) expect(states[0]).toMatchObject({ data: undefined }) @@ -625,9 +636,8 @@ describe('createQuery', () => { states.push({ ...query }) }) - await vi.waitFor(() => { - expect(query.status).toBe('error') - }) + await vi.advanceTimersByTimeAsync(0) + expect(query.status).toBe('error') expect(states.length).toBe(2) expect(states[0]).toMatchObject({ status: 'pending', data: undefined }) @@ -654,12 +664,14 @@ describe('createQuery', () => { states.push({ ...query }) }) - await vi.waitFor(() => expect(query.data).toBe(1)) + await vi.advanceTimersByTimeAsync(0) + expect(query.data).toBe(1) queryClient.removeQueries({ queryKey: key }) - await query.refetch() + query.refetch() - await vi.waitFor(() => expect(query.data).toBe(2)) + await vi.advanceTimersByTimeAsync(0) + expect(query.data).toBe(2) expect(states.length).toBe(4) expect(states[0]).toMatchObject({ @@ -696,9 +708,11 @@ describe('createQuery', () => { states.push({ ...query }) }) - await vi.waitFor(() => expect(query.data).toBe('')) + await vi.advanceTimersByTimeAsync(0) + expect(query.data).toBe('') search = 'phone' - await vi.waitFor(() => expect(query.data).toBe('phone')) + await vi.advanceTimersByTimeAsync(0) + expect(query.data).toBe('phone') expect(states.length).toBe(4) expect(states[0]).toMatchObject({ @@ -743,15 +757,13 @@ describe('createQuery', () => { states.push({ ...query }) }) - await vi.waitFor(() => { - expect(query.data).toBe(1) - }) + await vi.advanceTimersByTimeAsync(0) + expect(query.data).toBe(1) queryClient.removeQueries({ queryKey: key }) - await query.refetch() - await vi.waitFor(() => { - expect(query.data).toBe(2) - }) + query.refetch() + await vi.advanceTimersByTimeAsync(0) + expect(query.data).toBe(2) expect(states.length).toBe(4) // Initial @@ -799,9 +811,11 @@ describe('createQuery', () => { states.push({ ...query }) }) - await vi.waitFor(() => expect(query.data?.[1]?.done).toBe(false)) - await query.refetch() - await vi.waitFor(() => expect(query.data?.[1]?.done).toBe(true)) + await vi.advanceTimersByTimeAsync(0) + expect(query.data?.[1]?.done).toBe(false) + query.refetch() + await vi.advanceTimersByTimeAsync(0) + expect(query.data?.[1]?.done).toBe(true) expect(states.length).toBe(4) @@ -838,9 +852,11 @@ describe('createQuery', () => { () => queryClient, ) - await vi.waitFor(() => expect(query.data).toBe('set')) + await vi.advanceTimersByTimeAsync(0) + expect(query.data).toBe('set') queryClient.refetchQueries({ queryKey: key }) - await vi.waitFor(() => expect(query.data).toBe('fetched')) + await vi.advanceTimersByTimeAsync(0) + expect(query.data).toBe('fetched') }), ) @@ -859,33 +875,22 @@ describe('createQuery', () => { () => queryClient, ) - await vi.waitFor(() => - expect(query).toEqual( - expect.objectContaining({ - data: 1, - isStale: false, - isFetching: false, - }), - ), + await vi.advanceTimersByTimeAsync(0) + expect(query).toEqual( + expect.objectContaining({ + data: 1, + isStale: false, + isFetching: false, + }), ) queryClient.invalidateQueries({ queryKey: key }) - await vi.waitFor(() => - expect(query).toEqual( - expect.objectContaining({ - data: 1, - isStale: true, - isFetching: true, - }), - ), - ) - await vi.waitFor(() => - expect(query).toEqual( - expect.objectContaining({ - data: 2, - isStale: false, - isFetching: false, - }), - ), + await vi.advanceTimersByTimeAsync(0) + expect(query).toEqual( + expect.objectContaining({ + data: 2, + isStale: false, + isFetching: false, + }), ) }), ) @@ -910,7 +915,7 @@ describe('createQuery', () => { states.push({ ...query }) }) - await sleep(50) + await vi.advanceTimersByTimeAsync(0) expect(states.length).toBe(1) expect(states[0]).toMatchObject({ @@ -945,7 +950,7 @@ describe('createQuery', () => { queryClient.invalidateQueries({ queryKey: key }) // Wait long enough for the invalidation and potential refetch - await sleep(100) + await vi.advanceTimersByTimeAsync(100) expect(states.length).toBe(1) expect(states[0]).toMatchObject({ @@ -977,9 +982,11 @@ describe('createQuery', () => { states.push({ ...query }) }) - await vi.waitFor(() => expect(query.data).toBe(0)) + await vi.advanceTimersByTimeAsync(0) + expect(query.data).toBe(0) count = 1 - await vi.waitFor(() => expect(states.length).toBe(3)) + await vi.advanceTimersByTimeAsync(0) + expect(states.length).toBe(3) // Fetch query expect(states[0]).toMatchObject({ @@ -1021,13 +1028,15 @@ describe('createQuery', () => { }) // Wait for the initial fetch to complete - await vi.waitFor(() => expect(query.data).toBe(0)) + await vi.advanceTimersByTimeAsync(0) + expect(query.data).toBe(0) // Update count to trigger a new fetch count = 1 // Wait for all state updates to complete - await vi.waitFor(() => expect(states.length).toBe(4)) + await vi.advanceTimersByTimeAsync(0) + expect(states.length).toBe(4) // Initial expect(states[0]).toMatchObject({ @@ -1082,16 +1091,18 @@ describe('createQuery', () => { }) // Wait for the initial fetch to complete - await vi.waitFor(() => expect(query.data).toBe(0)) + await vi.advanceTimersByTimeAsync(0) + expect(query.data).toBe(0) // Update count to trigger a new fetch count = 1 // Wait for the new fetch to complete - await vi.waitFor(() => expect(query.data).toBe(1)) + await vi.advanceTimersByTimeAsync(0) + expect(query.data).toBe(1) // Wait for all state updates to complete - await vi.waitFor(() => expect(states.length).toBe(4)) + expect(states.length).toBe(4) // Initial expect(states[0]).toMatchObject({ @@ -1154,9 +1165,10 @@ describe('createQuery', () => { flushSync() flushSync(() => (count = 11)) flushSync(() => (count = 12)) - await query.refetch() + query.refetch() // Wait for all operations to complete - await vi.waitFor(() => expect(query.data).toBe(12)) + await vi.advanceTimersByTimeAsync(0) + expect(query.data).toBe(12) // Disabled query expect(states[0]).toMatchObject({ @@ -1229,13 +1241,15 @@ describe('createQuery', () => { resolve(1) // Wait for the first query to complete - await vi.waitFor(() => expect(firstQuery.data).toBe(1)) + await vi.advanceTimersByTimeAsync(0) + expect(firstQuery.data).toBe(1) // Refetch the first query - await firstQuery.refetch() + firstQuery.refetch() // Wait for all state updates to complete - await vi.waitFor(() => expect(states.length).toBe(4)) + await vi.advanceTimersByTimeAsync(0) + expect(states.length).toBe(4) expect(states[0]).toMatchObject({ data: undefined, @@ -1287,9 +1301,8 @@ describe('createQuery', () => { }, }) - await vi.waitFor(() => - expect(queryClient.getQueryState(key)?.data).toBe('prefetch'), - ) + await vi.advanceTimersByTimeAsync(10) + expect(queryClient.getQueryState(key)?.data).toBe('prefetch') await withEffectRoot(async () => { const firstQuery = createQuery( @@ -1318,10 +1331,9 @@ describe('createQuery', () => { states2.push({ ...secondQuery }) }) - await vi.waitFor(() => { - expect(firstQuery).toMatchObject({ data: 'two', isStale: true }) - expect(secondQuery).toMatchObject({ data: 'two', isStale: true }) - }) + await vi.advanceTimersByTimeAsync(101) + expect(firstQuery).toMatchObject({ data: 'two', isStale: true }) + expect(secondQuery).toMatchObject({ data: 'two', isStale: true }) expect(states1).toMatchObject([ // First render @@ -1387,7 +1399,7 @@ describe('createQuery', () => { }) // Wait for the query to become stale - await sleep(100) + await vi.advanceTimersByTimeAsync(51) expect(states.length).toBe(3) expect(states[0]).toMatchObject({ isStale: true }) @@ -1423,9 +1435,8 @@ describe('createQuery', () => { query.refetch() }, 10) - await vi.waitFor(() => { - expect(states.length).toBe(2) - }) + await vi.advanceTimersByTimeAsync(10) + expect(states.length).toBe(2) expect(states[0]).toMatchObject({ data: undefined, @@ -1464,7 +1475,8 @@ describe('createQuery', () => { resolve('test') }, 10) - await vi.waitFor(() => expect(query.data).toBe('test')) + await vi.advanceTimersByTimeAsync(10) + expect(query.data).toBe('test') // Refetch after data is available setTimeout(() => { @@ -1474,7 +1486,7 @@ describe('createQuery', () => { }, 20) // Wait for refetch to complete - await sleep(30) + await vi.advanceTimersByTimeAsync(20) expect(states.length).toBe(2) expect(states[0]).toBe(undefined) @@ -1508,7 +1520,8 @@ describe('createQuery', () => { renderCount++ }) - await vi.waitFor(() => expect(query.data).toBe('test')) + await vi.advanceTimersByTimeAsync(0) + expect(query.data).toBe('test') expect(renderCount).toBe(2) expect(states.length).toBe(2) @@ -1581,10 +1594,9 @@ describe('createQuery', () => { expect(query1.fetchStatus).toBe('idle') // Wait for second query to complete - await vi.waitFor(() => { - expect(query2.status).toBe('success') - expect(query2.fetchStatus).toBe('idle') - }) + await vi.advanceTimersByTimeAsync(0) + expect(query2.status).toBe('success') + expect(query2.fetchStatus).toBe('idle') // Verify the state transitions for the second query expect(states2[0]?.status).toBe('pending') @@ -1625,13 +1637,13 @@ describe('createQuery', () => { ) // Wait a bit to ensure the query has time to settle - await sleep(10) + await vi.advanceTimersByTimeAsync(10) // Simulate window focus window.dispatchEvent(new Event('visibilitychange')) // Wait a bit more to ensure no refetch happens - await sleep(10) + await vi.advanceTimersByTimeAsync(10) // The query function should not have been called expect(queryFn).not.toHaveBeenCalled() @@ -1663,13 +1675,14 @@ describe('createQuery', () => { }) // Wait for the initial fetch to complete - await vi.waitFor(() => expect(query.data).toBe(0)) + await vi.advanceTimersByTimeAsync(0) + expect(query.data).toBe(0) // Simulate window focus window.dispatchEvent(new Event('visibilitychange')) // Wait a bit to ensure no refetch happens - await sleep(10) + await vi.advanceTimersByTimeAsync(10) // Should only have 2 states: initial and after fetch expect(states.length).toBe(2) @@ -1703,13 +1716,14 @@ describe('createQuery', () => { }) // Wait for the initial fetch to complete - await vi.waitFor(() => expect(query.data).toBe(0)) + await vi.advanceTimersByTimeAsync(0) + expect(query.data).toBe(0) // Simulate window focus window.dispatchEvent(new Event('visibilitychange')) // Wait a bit to ensure no refetch happens - await sleep(10) + await vi.advanceTimersByTimeAsync(10) // Should only have 2 states: initial and after fetch expect(states.length).toBe(2) @@ -1743,13 +1757,14 @@ describe('createQuery', () => { }) // Wait for the initial fetch to complete - await vi.waitFor(() => expect(query.data).toBe(0)) + await vi.advanceTimersByTimeAsync(0) + expect(query.data).toBe(0) // Simulate window focus window.dispatchEvent(new Event('visibilitychange')) // Wait a bit to ensure no refetch happens - await sleep(10) + await vi.advanceTimersByTimeAsync(10) // Should only have 2 states: initial and after fetch expect(states.length).toBe(2) @@ -1787,7 +1802,8 @@ describe('createQuery', () => { }) // Wait for the refetch to complete - await vi.waitFor(() => expect(query.data).toBe('data')) + await vi.advanceTimersByTimeAsync(0) + expect(query.data).toBe('data') // Should have 2 states: initial (with prefetched data) and after refetch expect(states.length).toBe(2) @@ -1830,7 +1846,8 @@ describe('createQuery', () => { }) // Wait for the refetch to complete - await vi.waitFor(() => expect(query.data).toBe('data')) + await vi.advanceTimersByTimeAsync(0) + expect(query.data).toBe('data') // Should have 2 states: initial (with prefetched data) and after refetch expect(states.length).toBe(2) @@ -1864,7 +1881,8 @@ describe('createQuery', () => { () => queryClient, ) - await vi.waitFor(() => expect(query.status).toBe('error')) + await vi.advanceTimersByTimeAsync(0) + expect(query.status).toBe('error') expect(query.error?.message).toBe('Error test') consoleMock.mockRestore() @@ -1886,18 +1904,19 @@ describe('createQuery', () => { () => queryClient, ) - await vi.waitFor(() => expect(query.status).toBe('error')) + await vi.advanceTimersByTimeAsync(0) + expect(query.status).toBe('error') expect(query.error?.message).toBe('Local Error') }), ) it( 'should support changing provided query client', - withEffectRoot(async () => { + withEffectRoot(() => { const queryClient1 = new QueryClient() const queryClient2 = new QueryClient() - let queryClient = $state(queryClient1) + let currentClient = $state(queryClient1) const key = ['test'] @@ -1906,12 +1925,12 @@ describe('createQuery', () => { queryKey: key, queryFn: () => Promise.resolve('prefetched'), }), - () => queryClient, + () => currentClient, ) expect(queryClient1.getQueryCache().find({ queryKey: key })).toBeDefined() - queryClient = queryClient2 + currentClient = queryClient2 flushSync() expect(queryClient2.getQueryCache().find({ queryKey: key })).toBeDefined() diff --git a/packages/vue-query/src/__tests__/infiniteQueryOptions.test-d.ts b/packages/vue-query/src/__tests__/infiniteQueryOptions.test-d.ts index 6413126ffd7..cd4743d9d5d 100644 --- a/packages/vue-query/src/__tests__/infiniteQueryOptions.test-d.ts +++ b/packages/vue-query/src/__tests__/infiniteQueryOptions.test-d.ts @@ -1,7 +1,8 @@ import { assertType, describe, expectTypeOf, it } from 'vitest' -import { QueryClient, dataTagSymbol } from '@tanstack/query-core' +import { dataTagSymbol } from '@tanstack/query-core' import { reactive } from 'vue-demi' import { infiniteQueryOptions } from '../infiniteQueryOptions' +import { QueryClient } from '../queryClient' import { useInfiniteQuery } from '../useInfiniteQuery' import type { InfiniteData } from '@tanstack/query-core'