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
1 change: 1 addition & 0 deletions packages/angular-query-experimental/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
"@angular/core": "^20.0.0",
"@angular/platform-browser": "^20.0.0",
"@tanstack/query-test-utils": "workspace:*",
"@testing-library/angular": "^18.0.0",
"eslint-plugin-jsdoc": "^50.5.0",
"npm-run-all2": "^5.0.0",
"vite-plugin-dts": "4.2.3",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import { describe, expectTypeOf, it } from 'vitest'
import { skipToken } from '..'
import { injectQueries } from '../inject-queries'
import { queryOptions } from '../query-options'
import type { CreateQueryOptions, CreateQueryResult, OmitKeyof } from '..'
import type { Signal } from '@angular/core'

describe('InjectQueries config object overload', () => {
it('TData should always be defined when initialData is provided as an object', () => {
const query1 = {
queryKey: ['key1'],
queryFn: () => {
return {
wow: true,
}
},
initialData: {
wow: false,
},
}

const query2 = {
queryKey: ['key2'],
queryFn: () => 'Query Data',
initialData: 'initial data',
}

const query3 = {
queryKey: ['key2'],
queryFn: () => 'Query Data',
}

const queryResults = injectQueries(() => ({
queries: [query1, query2, query3],
}))

const query1Data = queryResults()[0].data()
const query2Data = queryResults()[1].data()
const query3Data = queryResults()[2].data()

expectTypeOf(query1Data).toEqualTypeOf<{ wow: boolean }>()
expectTypeOf(query2Data).toEqualTypeOf<string>()
expectTypeOf(query3Data).toEqualTypeOf<string | undefined>()
})

it('TData should be defined when passed through queryOptions', () => {
const options = queryOptions({
queryKey: ['key'],
queryFn: () => {
return {
wow: true,
}
},
initialData: {
wow: true,
},
})
const queryResults = injectQueries(() => ({ queries: [options] }))

const data = queryResults()[0].data()

expectTypeOf(data).toEqualTypeOf<{ wow: boolean }>()
})

it('should be possible to define a different TData than TQueryFnData using select with queryOptions spread into injectQuery', () => {
const query1 = queryOptions({
queryKey: ['key'],
queryFn: () => Promise.resolve(1),
select: (data) => data > 1,
})

const query2 = {
queryKey: ['key'],
queryFn: () => Promise.resolve(1),
select: (data: number) => data > 1,
}

const queryResults = injectQueries(() => ({ queries: [query1, query2] }))
const query1Data = queryResults()[0].data()
const query2Data = queryResults()[1].data()

expectTypeOf(query1Data).toEqualTypeOf<boolean | undefined>()
expectTypeOf(query2Data).toEqualTypeOf<boolean | undefined>()
})

it('TData should have undefined in the union when initialData is provided as a function which can return undefined', () => {
const queryResults = injectQueries(() => ({
queries: [
{
queryKey: ['key'],
queryFn: () => {
return {
wow: true,
}
},
initialData: () => undefined as { wow: boolean } | undefined,
},
],
}))

const data = queryResults()[0].data()

expectTypeOf(data).toEqualTypeOf<{ wow: boolean } | undefined>()
})

describe('custom injectable', () => {
it('should allow custom hooks using UseQueryOptions', () => {
type Data = string

const injectCustomQueries = (
options?: OmitKeyof<CreateQueryOptions<Data>, 'queryKey' | 'queryFn'>,
) => {
return injectQueries(() => ({
queries: [
{
...options,
queryKey: ['todos-key'],
queryFn: () => Promise.resolve('data'),
},
],
}))
}

const queryResults = injectCustomQueries()
const data = queryResults()[0].data()

expectTypeOf(data).toEqualTypeOf<Data | undefined>()
})
})

it('TData should have correct type when conditional skipToken is passed', () => {
const queryResults = injectQueries(() => ({
queries: [
{
queryKey: ['withSkipToken'],
queryFn: Math.random() > 0.5 ? skipToken : () => Promise.resolve(5),
},
],
}))

const firstResult = queryResults()[0]

expectTypeOf(firstResult).toEqualTypeOf<CreateQueryResult<number, Error>>()
expectTypeOf(firstResult.data()).toEqualTypeOf<number | undefined>()
})

it('should return correct data for dynamic queries with mixed result types', () => {
const Queries1 = {
get: () =>
queryOptions({
queryKey: ['key1'],
queryFn: () => Promise.resolve(1),
}),
}
const Queries2 = {
get: () =>
queryOptions({
queryKey: ['key2'],
queryFn: () => Promise.resolve(true),
}),
}

const queries1List = [1, 2, 3].map(() => ({ ...Queries1.get() }))
const result = injectQueries(() => ({
queries: [...queries1List, { ...Queries2.get() }],
}))

expectTypeOf(result).branded.toEqualTypeOf<
Signal<
[
...Array<CreateQueryResult<number, Error>>,
CreateQueryResult<boolean, Error>,
]
>
>()
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { beforeEach, describe, expect, it } from 'vitest'
import { render } from '@testing-library/angular'
import {
Component,
effect,
provideZonelessChangeDetection,
} from '@angular/core'
import { TestBed } from '@angular/core/testing'
import { queryKey } from '@tanstack/query-test-utils'
import { QueryClient, provideTanStackQuery } from '..'
import { injectQueries } from '../inject-queries'

let queryClient: QueryClient

beforeEach(() => {
queryClient = new QueryClient()
TestBed.configureTestingModule({
providers: [
provideZonelessChangeDetection(),
provideTanStackQuery(queryClient),
],
})
})

describe('injectQueries', () => {
it('should return the correct states', async () => {
const key1 = queryKey()
const key2 = queryKey()
const results: Array<Array<Record<string, any>>> = []

@Component({
template: `
<div>
<div>
data1: {{ result()[0].data() ?? 'null' }}, data2:
{{ result()[1].data() ?? 'null' }}
</div>
</div>
`,
})
class Page {
toString(val: any) {
return String(val)
}
result = injectQueries(() => ({
queries: [
{
queryKey: key1,
queryFn: async () => {
await new Promise((r) => setTimeout(r, 10))
return 1
},
},
{
queryKey: key2,
queryFn: async () => {
await new Promise((r) => setTimeout(r, 100))
return 2
},
},
],
}))

_pushResults = effect(() => {
const snapshot = this.result().map((q) => ({ data: q.data() }))
results.push(snapshot)
})
}

const rendered = await render(Page)

await rendered.findByText('data1: 1, data2: 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 }])
})
})
Loading
Loading