Skip to content

Commit ed21249

Browse files
crutchcornautofix-ci[bot]arnoud-dv
authored
fix(angular-query) fix injectQueries types
* fix(angular-query-experimental): typings for injectQueries should now behave properly * fix(angular-query-experimental): behaviors of injectQuery now work at runtime Mostly * ci: apply automated fixes * add signal proxy * ci: apply automated fixes * update @testing-library/angular * fix build * rename test file * ci: apply automated fixes * fix type error * apply corerabbit suggestions --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Arnoud de Vries <6420061+arnoud-dv@users.noreply.github.com>
1 parent 2a00fb6 commit ed21249

File tree

6 files changed

+413
-91
lines changed

6 files changed

+413
-91
lines changed

packages/angular-query-experimental/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
"@angular/core": "^20.0.0",
9595
"@angular/platform-browser": "^20.0.0",
9696
"@tanstack/query-test-utils": "workspace:*",
97+
"@testing-library/angular": "^18.0.0",
9798
"eslint-plugin-jsdoc": "^50.5.0",
9899
"npm-run-all2": "^5.0.0",
99100
"vite-plugin-dts": "4.2.3",
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
import { describe, expectTypeOf, it } from 'vitest'
2+
import { skipToken } from '..'
3+
import { injectQueries } from '../inject-queries'
4+
import { queryOptions } from '../query-options'
5+
import type { CreateQueryOptions, CreateQueryResult, OmitKeyof } from '..'
6+
import type { Signal } from '@angular/core'
7+
8+
describe('InjectQueries config object overload', () => {
9+
it('TData should always be defined when initialData is provided as an object', () => {
10+
const query1 = {
11+
queryKey: ['key1'],
12+
queryFn: () => {
13+
return {
14+
wow: true,
15+
}
16+
},
17+
initialData: {
18+
wow: false,
19+
},
20+
}
21+
22+
const query2 = {
23+
queryKey: ['key2'],
24+
queryFn: () => 'Query Data',
25+
initialData: 'initial data',
26+
}
27+
28+
const query3 = {
29+
queryKey: ['key2'],
30+
queryFn: () => 'Query Data',
31+
}
32+
33+
const queryResults = injectQueries(() => ({
34+
queries: [query1, query2, query3],
35+
}))
36+
37+
const query1Data = queryResults()[0].data()
38+
const query2Data = queryResults()[1].data()
39+
const query3Data = queryResults()[2].data()
40+
41+
expectTypeOf(query1Data).toEqualTypeOf<{ wow: boolean }>()
42+
expectTypeOf(query2Data).toEqualTypeOf<string>()
43+
expectTypeOf(query3Data).toEqualTypeOf<string | undefined>()
44+
})
45+
46+
it('TData should be defined when passed through queryOptions', () => {
47+
const options = queryOptions({
48+
queryKey: ['key'],
49+
queryFn: () => {
50+
return {
51+
wow: true,
52+
}
53+
},
54+
initialData: {
55+
wow: true,
56+
},
57+
})
58+
const queryResults = injectQueries(() => ({ queries: [options] }))
59+
60+
const data = queryResults()[0].data()
61+
62+
expectTypeOf(data).toEqualTypeOf<{ wow: boolean }>()
63+
})
64+
65+
it('should be possible to define a different TData than TQueryFnData using select with queryOptions spread into injectQuery', () => {
66+
const query1 = queryOptions({
67+
queryKey: ['key'],
68+
queryFn: () => Promise.resolve(1),
69+
select: (data) => data > 1,
70+
})
71+
72+
const query2 = {
73+
queryKey: ['key'],
74+
queryFn: () => Promise.resolve(1),
75+
select: (data: number) => data > 1,
76+
}
77+
78+
const queryResults = injectQueries(() => ({ queries: [query1, query2] }))
79+
const query1Data = queryResults()[0].data()
80+
const query2Data = queryResults()[1].data()
81+
82+
expectTypeOf(query1Data).toEqualTypeOf<boolean | undefined>()
83+
expectTypeOf(query2Data).toEqualTypeOf<boolean | undefined>()
84+
})
85+
86+
it('TData should have undefined in the union when initialData is provided as a function which can return undefined', () => {
87+
const queryResults = injectQueries(() => ({
88+
queries: [
89+
{
90+
queryKey: ['key'],
91+
queryFn: () => {
92+
return {
93+
wow: true,
94+
}
95+
},
96+
initialData: () => undefined as { wow: boolean } | undefined,
97+
},
98+
],
99+
}))
100+
101+
const data = queryResults()[0].data()
102+
103+
expectTypeOf(data).toEqualTypeOf<{ wow: boolean } | undefined>()
104+
})
105+
106+
describe('custom injectable', () => {
107+
it('should allow custom hooks using UseQueryOptions', () => {
108+
type Data = string
109+
110+
const injectCustomQueries = (
111+
options?: OmitKeyof<CreateQueryOptions<Data>, 'queryKey' | 'queryFn'>,
112+
) => {
113+
return injectQueries(() => ({
114+
queries: [
115+
{
116+
...options,
117+
queryKey: ['todos-key'],
118+
queryFn: () => Promise.resolve('data'),
119+
},
120+
],
121+
}))
122+
}
123+
124+
const queryResults = injectCustomQueries()
125+
const data = queryResults()[0].data()
126+
127+
expectTypeOf(data).toEqualTypeOf<Data | undefined>()
128+
})
129+
})
130+
131+
it('TData should have correct type when conditional skipToken is passed', () => {
132+
const queryResults = injectQueries(() => ({
133+
queries: [
134+
{
135+
queryKey: ['withSkipToken'],
136+
queryFn: Math.random() > 0.5 ? skipToken : () => Promise.resolve(5),
137+
},
138+
],
139+
}))
140+
141+
const firstResult = queryResults()[0]
142+
143+
expectTypeOf(firstResult).toEqualTypeOf<CreateQueryResult<number, Error>>()
144+
expectTypeOf(firstResult.data()).toEqualTypeOf<number | undefined>()
145+
})
146+
147+
it('should return correct data for dynamic queries with mixed result types', () => {
148+
const Queries1 = {
149+
get: () =>
150+
queryOptions({
151+
queryKey: ['key1'],
152+
queryFn: () => Promise.resolve(1),
153+
}),
154+
}
155+
const Queries2 = {
156+
get: () =>
157+
queryOptions({
158+
queryKey: ['key2'],
159+
queryFn: () => Promise.resolve(true),
160+
}),
161+
}
162+
163+
const queries1List = [1, 2, 3].map(() => ({ ...Queries1.get() }))
164+
const result = injectQueries(() => ({
165+
queries: [...queries1List, { ...Queries2.get() }],
166+
}))
167+
168+
expectTypeOf(result).branded.toEqualTypeOf<
169+
Signal<
170+
[
171+
...Array<CreateQueryResult<number, Error>>,
172+
CreateQueryResult<boolean, Error>,
173+
]
174+
>
175+
>()
176+
})
177+
})
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { beforeEach, describe, expect, it } from 'vitest'
2+
import { render } from '@testing-library/angular'
3+
import {
4+
Component,
5+
effect,
6+
provideZonelessChangeDetection,
7+
} from '@angular/core'
8+
import { TestBed } from '@angular/core/testing'
9+
import { queryKey } from '@tanstack/query-test-utils'
10+
import { QueryClient, provideTanStackQuery } from '..'
11+
import { injectQueries } from '../inject-queries'
12+
13+
let queryClient: QueryClient
14+
15+
beforeEach(() => {
16+
queryClient = new QueryClient()
17+
TestBed.configureTestingModule({
18+
providers: [
19+
provideZonelessChangeDetection(),
20+
provideTanStackQuery(queryClient),
21+
],
22+
})
23+
})
24+
25+
describe('injectQueries', () => {
26+
it('should return the correct states', async () => {
27+
const key1 = queryKey()
28+
const key2 = queryKey()
29+
const results: Array<Array<Record<string, any>>> = []
30+
31+
@Component({
32+
template: `
33+
<div>
34+
<div>
35+
data1: {{ result()[0].data() ?? 'null' }}, data2:
36+
{{ result()[1].data() ?? 'null' }}
37+
</div>
38+
</div>
39+
`,
40+
})
41+
class Page {
42+
toString(val: any) {
43+
return String(val)
44+
}
45+
result = injectQueries(() => ({
46+
queries: [
47+
{
48+
queryKey: key1,
49+
queryFn: async () => {
50+
await new Promise((r) => setTimeout(r, 10))
51+
return 1
52+
},
53+
},
54+
{
55+
queryKey: key2,
56+
queryFn: async () => {
57+
await new Promise((r) => setTimeout(r, 100))
58+
return 2
59+
},
60+
},
61+
],
62+
}))
63+
64+
_pushResults = effect(() => {
65+
const snapshot = this.result().map((q) => ({ data: q.data() }))
66+
results.push(snapshot)
67+
})
68+
}
69+
70+
const rendered = await render(Page)
71+
72+
await rendered.findByText('data1: 1, data2: 2')
73+
74+
expect(results.length).toBe(3)
75+
expect(results[0]).toMatchObject([{ data: undefined }, { data: undefined }])
76+
expect(results[1]).toMatchObject([{ data: 1 }, { data: undefined }])
77+
expect(results[2]).toMatchObject([{ data: 1 }, { data: 2 }])
78+
})
79+
})

0 commit comments

Comments
 (0)