Files
upstream-tanstack-query/packages/svelte-query/tests/createQueries/createQueries.svelte.test.ts
upstream-mirror 3a013ccdfe
Some checks failed
autofix.ci / autofix (push) Failing after 4s
Release / Release (push) Has been skipped
GitHub Actions Security Analysis / Run zizmor (push) Failing after 2s
snapshot of https://github.com/TanStack/query.git (main, 2026-09-16; blobs > 131072B omitted)
2026-09-16 01:13:08 +05:00

378 lines
12 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { render } from '@testing-library/svelte'
import { queryKey, sleep } from '@tanstack/query-test-utils'
import { QueryClient, createQueries } from '../../src/index.js'
import { promiseWithResolvers, ref, withEffectRoot } from '../utils.svelte.js'
import IsRestoring from './IsRestoring.svelte'
import type { CreateQueryResult } from '../../src/index.js'
describe('createQueries', () => {
let queryClient: QueryClient
beforeEach(() => {
vi.useFakeTimers()
queryClient = new QueryClient()
})
afterEach(() => {
queryClient.clear()
vi.useRealTimers()
})
it(
'should return the correct states',
withEffectRoot(async () => {
const key1 = queryKey()
const key2 = queryKey()
const results: Array<Array<CreateQueryResult>> = []
const { promise: promise1, resolve: resolve1 } = promiseWithResolvers()
const { promise: promise2, resolve: resolve2 } = promiseWithResolvers()
const result = createQueries(
() => ({
queries: [
{
queryKey: key1,
queryFn: () => promise1,
},
{
queryKey: key2,
queryFn: () => promise2,
},
],
}),
() => queryClient,
)
$effect(() => {
results.push([{ ...result[0] }, { ...result[1] }])
})
resolve1(1)
await vi.advanceTimersByTimeAsync(0)
expect(result[0].data).toBe(1)
resolve2(2)
await vi.advanceTimersByTimeAsync(0)
expect(result[1].data).toBe(2)
expect(results.length).toBe(3)
expect(results[0]).toMatchObject([
{ data: undefined },
{ data: undefined },
])
expect(results[1]).toMatchObject([{ data: 1 }, { data: undefined }])
expect(results[2]).toMatchObject([{ data: 1 }, { data: 2 }])
}),
)
it(
'should track results',
withEffectRoot(async () => {
const key1 = queryKey()
const results: Array<Array<CreateQueryResult>> = []
let count = 0
const result = createQueries(
() => ({
queries: [
{
queryKey: key1,
queryFn: () => Promise.resolve(++count),
},
],
}),
() => queryClient,
)
$effect(() => {
results.push([result[0]])
})
await vi.advanceTimersByTimeAsync(0)
expect(result[0].data).toBe(1)
expect(results.length).toBe(2)
expect(results[0]).toMatchObject([{ data: undefined }])
expect(results[1]).toMatchObject([{ data: 1 }])
// Trigger refetch
result[0].refetch()
await vi.advanceTimersByTimeAsync(0)
expect(result[0].data).toBe(2)
// Only one render for data update, no render for isFetching transition
expect(results.length).toBe(3)
expect(results[2]).toMatchObject([{ data: 2 }])
}),
)
it(
'should track queries added to an initially empty array',
withEffectRoot(async () => {
const key1 = queryKey()
const queries = ref<
Array<{ queryKey: Array<string>; queryFn: () => Promise<string> }>
>([])
const results: Array<Array<CreateQueryResult>> = []
const result = createQueries(
() => ({ queries: queries.value }),
() => queryClient,
)
$effect(() => {
results.push(result.map((res) => ({ ...res })))
})
await vi.advanceTimersByTimeAsync(0)
expect(results).toMatchObject([[]])
queries.value = [
{ queryKey: key1, queryFn: () => sleep(10).then(() => 'data1') },
]
await vi.advanceTimersByTimeAsync(10)
expect(result[0]?.data).toBe('data1')
expect(results.at(-1)).toMatchObject([
{ status: 'success', data: 'data1' },
])
}),
)
it(
'should combine queries',
withEffectRoot(async () => {
const key1 = queryKey()
const key2 = queryKey()
const { promise: promise1, resolve: resolve1 } =
promiseWithResolvers<string>()
const { promise: promise2, resolve: resolve2 } =
promiseWithResolvers<string>()
const queries = createQueries(
() => ({
queries: [
{
queryKey: key1,
queryFn: () => promise1,
},
{
queryKey: key2,
queryFn: () => promise2,
},
],
combine: (results) => {
return {
combined: true,
res: results
.flatMap((res) => (res.data ? [res.data] : []))
.join(','),
}
},
}),
() => queryClient,
)
// Initially both queries are loading
expect(queries).toEqual({
combined: true,
res: '',
})
// Resolve the first query
resolve1('first result')
await vi.advanceTimersByTimeAsync(0)
expect(queries.res).toBe('first result')
// Resolve the second query
resolve2('second result')
await vi.advanceTimersByTimeAsync(0)
expect(queries.res).toBe('first result,second result')
expect(queries).toEqual({
combined: true,
res: 'first result,second result',
})
}),
)
it(
'should track property access through combine function',
withEffectRoot(async () => {
const key1 = queryKey()
const key2 = queryKey()
let count = 0
const results: Array<unknown> = []
const { promise: promise1, resolve: resolve1 } =
promiseWithResolvers<string>()
const { promise: promise2, resolve: resolve2 } =
promiseWithResolvers<string>()
const { promise: promise3, resolve: resolve3 } =
promiseWithResolvers<string>()
const { promise: promise4, resolve: resolve4 } =
promiseWithResolvers<string>()
const queries = createQueries(
() => ({
queries: [
{
queryKey: key1,
queryFn: () => (count === 0 ? promise1 : promise3),
},
{
queryKey: key2,
queryFn: () => (count === 0 ? promise2 : promise4),
},
],
combine: (queryResults) => {
return {
combined: true,
refetch: () =>
Promise.all(queryResults.map((res) => res.refetch())),
res: queryResults
.flatMap((res) => (res.data ? [res.data] : []))
.join(','),
}
},
}),
() => queryClient,
)
$effect(() => {
results.push({ ...queries })
})
// Initially both queries are loading
await vi.advanceTimersByTimeAsync(0)
expect(results[0]).toStrictEqual({
combined: true,
refetch: expect.any(Function),
res: '',
})
// Resolve the first query
resolve1('first result ' + count)
await vi.advanceTimersByTimeAsync(0)
expect(queries.res).toBe('first result 0')
expect(results[1]).toStrictEqual({
combined: true,
refetch: expect.any(Function),
res: 'first result 0',
})
// Resolve the second query
resolve2('second result ' + count)
await vi.advanceTimersByTimeAsync(0)
expect(queries.res).toBe('first result 0,second result 0')
expect(results[2]).toStrictEqual({
combined: true,
refetch: expect.any(Function),
res: 'first result 0,second result 0',
})
// Increment count and refetch
count++
queries.refetch()
// Resolve the refetched queries
resolve3('first result ' + count)
resolve4('second result ' + count)
await vi.advanceTimersByTimeAsync(0)
expect(queries.res).toBe('first result 1,second result 1')
const length = results.length
expect(results.at(-1)).toStrictEqual({
combined: true,
refetch: expect.any(Function),
res: 'first result 1,second result 1',
})
// Refetch again but with the same data
await queries.refetch()
// No further re-render because data didn't change
expect(results.length).toBe(length)
}),
)
it('should not fetch for the duration of the restoring period when isRestoring is true', async () => {
const queryFn1 = vi.fn(() => sleep(10).then(() => 'data1'))
const queryFn2 = vi.fn(() => sleep(10).then(() => 'data2'))
const rendered = render(IsRestoring, {
props: { queryClient, queryFn1, queryFn2 },
})
await vi.advanceTimersByTimeAsync(0)
expect(rendered.getByTestId('status1')).toHaveTextContent('pending')
expect(rendered.getByTestId('status2')).toHaveTextContent('pending')
expect(rendered.getByTestId('fetchStatus1')).toHaveTextContent('idle')
expect(rendered.getByTestId('fetchStatus2')).toHaveTextContent('idle')
expect(rendered.getByTestId('data1')).toHaveTextContent('undefined')
expect(rendered.getByTestId('data2')).toHaveTextContent('undefined')
expect(queryFn1).toHaveBeenCalledTimes(0)
expect(queryFn2).toHaveBeenCalledTimes(0)
await vi.advanceTimersByTimeAsync(10)
expect(rendered.getByTestId('status1')).toHaveTextContent('pending')
expect(rendered.getByTestId('status2')).toHaveTextContent('pending')
expect(rendered.getByTestId('fetchStatus1')).toHaveTextContent('idle')
expect(rendered.getByTestId('fetchStatus2')).toHaveTextContent('idle')
expect(rendered.getByTestId('data1')).toHaveTextContent('undefined')
expect(rendered.getByTestId('data2')).toHaveTextContent('undefined')
expect(queryFn1).toHaveBeenCalledTimes(0)
expect(queryFn2).toHaveBeenCalledTimes(0)
})
it('should not fetch queries with different durations for the duration of the restoring period when isRestoring is true', async () => {
const queryFn1 = vi.fn(() => sleep(10).then(() => 'data1'))
const queryFn2 = vi.fn(() => sleep(20).then(() => 'data2'))
const rendered = render(IsRestoring, {
props: { queryClient, queryFn1, queryFn2 },
})
await vi.advanceTimersByTimeAsync(0)
expect(rendered.getByTestId('status1')).toHaveTextContent('pending')
expect(rendered.getByTestId('status2')).toHaveTextContent('pending')
expect(rendered.getByTestId('fetchStatus1')).toHaveTextContent('idle')
expect(rendered.getByTestId('fetchStatus2')).toHaveTextContent('idle')
expect(rendered.getByTestId('data1')).toHaveTextContent('undefined')
expect(rendered.getByTestId('data2')).toHaveTextContent('undefined')
expect(queryFn1).toHaveBeenCalledTimes(0)
expect(queryFn2).toHaveBeenCalledTimes(0)
await vi.advanceTimersByTimeAsync(10)
expect(rendered.getByTestId('status1')).toHaveTextContent('pending')
expect(rendered.getByTestId('status2')).toHaveTextContent('pending')
expect(rendered.getByTestId('fetchStatus1')).toHaveTextContent('idle')
expect(rendered.getByTestId('fetchStatus2')).toHaveTextContent('idle')
expect(rendered.getByTestId('data1')).toHaveTextContent('undefined')
expect(rendered.getByTestId('data2')).toHaveTextContent('undefined')
expect(queryFn1).toHaveBeenCalledTimes(0)
expect(queryFn2).toHaveBeenCalledTimes(0)
await vi.advanceTimersByTimeAsync(10)
expect(rendered.getByTestId('status1')).toHaveTextContent('pending')
expect(rendered.getByTestId('status2')).toHaveTextContent('pending')
expect(rendered.getByTestId('fetchStatus1')).toHaveTextContent('idle')
expect(rendered.getByTestId('fetchStatus2')).toHaveTextContent('idle')
expect(rendered.getByTestId('data1')).toHaveTextContent('undefined')
expect(rendered.getByTestId('data2')).toHaveTextContent('undefined')
expect(queryFn1).toHaveBeenCalledTimes(0)
expect(queryFn2).toHaveBeenCalledTimes(0)
})
})