@tanstack/angular-query-experimental 5.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/LICENSE +21 -0
  2. package/build/createBaseQuery.d.ts +7 -0
  3. package/build/esm2022/createBaseQuery.mjs +35 -0
  4. package/build/esm2022/index.mjs +14 -0
  5. package/build/esm2022/infiniteQueryOptions.mjs +4 -0
  6. package/build/esm2022/injectIsFetching.mjs +25 -0
  7. package/build/esm2022/injectIsMutating.mjs +25 -0
  8. package/build/esm2022/injectMutation.mjs +30 -0
  9. package/build/esm2022/injectQueries.mjs +30 -0
  10. package/build/esm2022/injectQuery.mjs +11 -0
  11. package/build/esm2022/injectQueryClient.mjs +4 -0
  12. package/build/esm2022/providers.mjs +17 -0
  13. package/build/esm2022/query-proxy.mjs +37 -0
  14. package/build/esm2022/queryOptions.mjs +4 -0
  15. package/build/esm2022/tanstack-angular-query-experimental.mjs +5 -0
  16. package/build/esm2022/types.mjs +2 -0
  17. package/build/fesm2022/tanstack-angular-query-experimental.mjs +209 -0
  18. package/build/fesm2022/tanstack-angular-query-experimental.mjs.map +1 -0
  19. package/build/index.d.ts +12 -0
  20. package/build/infiniteQueryOptions.d.ts +3 -0
  21. package/build/injectIsFetching.d.ts +3 -0
  22. package/build/injectIsMutating.d.ts +3 -0
  23. package/build/injectMutation.d.ts +4 -0
  24. package/build/injectQueries.d.ts +71 -0
  25. package/build/injectQuery.d.ts +11 -0
  26. package/build/injectQueryClient.d.ts +13 -0
  27. package/build/providers.d.ts +3 -0
  28. package/build/query-proxy.d.ts +4 -0
  29. package/build/queryOptions.d.ts +12 -0
  30. package/build/types.d.ts +38 -0
  31. package/package.json +65 -0
  32. package/src/createBaseQuery.ts +87 -0
  33. package/src/index.ts +26 -0
  34. package/src/infiniteQueryOptions.ts +28 -0
  35. package/src/injectIsFetching.ts +36 -0
  36. package/src/injectIsMutating.ts +36 -0
  37. package/src/injectMutation.ts +63 -0
  38. package/src/injectQueries.ts +249 -0
  39. package/src/injectQuery.ts +61 -0
  40. package/src/injectQueryClient.ts +7 -0
  41. package/src/providers.ts +26 -0
  42. package/src/query-proxy.ts +51 -0
  43. package/src/queryOptions.ts +46 -0
  44. package/src/test-setup.ts +13 -0
  45. package/src/tests/injectIsFetching.test.ts +32 -0
  46. package/src/tests/injectIsMutating.test.ts +43 -0
  47. package/src/tests/injectMutation.test.ts +52 -0
  48. package/src/tests/injectQuery.test.ts +232 -0
  49. package/src/tests/test-utils.ts +37 -0
  50. package/src/types.ts +136 -0
@@ -0,0 +1,249 @@
1
+ import { QueriesObserver, notifyManager } from '@tanstack/query-core'
2
+ import { DestroyRef, computed, effect, inject, signal } from '@angular/core'
3
+ import { assertInjector } from 'ngxtension/assert-injector'
4
+ import { injectQueryClient } from './injectQueryClient'
5
+ import type { Injector, Signal } from '@angular/core'
6
+ import type {
7
+ DefaultError,
8
+ QueriesObserverOptions,
9
+ QueriesPlaceholderDataFunction,
10
+ QueryFunction,
11
+ QueryKey,
12
+ QueryObserverOptions,
13
+ QueryObserverResult,
14
+ ThrowOnError,
15
+ } from '@tanstack/query-core'
16
+
17
+ // This defines the `CreateQueryOptions` that are accepted in `QueriesOptions` & `GetOptions`.
18
+ // `placeholderData` function does not have a parameter
19
+ type QueryObserverOptionsForCreateQueries<
20
+ TQueryFnData = unknown,
21
+ TError = DefaultError,
22
+ TData = TQueryFnData,
23
+ TQueryKey extends QueryKey = QueryKey,
24
+ > = Omit<
25
+ QueryObserverOptions<TQueryFnData, TError, TData, TQueryFnData, TQueryKey>,
26
+ 'placeholderData'
27
+ > & {
28
+ placeholderData?: TQueryFnData | QueriesPlaceholderDataFunction<TQueryFnData>
29
+ }
30
+
31
+ // Avoid TS depth-limit error in case of large array literal
32
+ type MAXIMUM_DEPTH = 20
33
+
34
+ type GetOptions<T> =
35
+ // Part 1: responsible for applying explicit type parameter to function arguments, if object { queryFnData: TQueryFnData, error: TError, data: TData }
36
+ T extends {
37
+ queryFnData: infer TQueryFnData
38
+ error?: infer TError
39
+ data: infer TData
40
+ }
41
+ ? QueryObserverOptionsForCreateQueries<TQueryFnData, TError, TData>
42
+ : T extends { queryFnData: infer TQueryFnData; error?: infer TError }
43
+ ? QueryObserverOptionsForCreateQueries<TQueryFnData, TError>
44
+ : T extends { data: infer TData; error?: infer TError }
45
+ ? QueryObserverOptionsForCreateQueries<unknown, TError, TData>
46
+ : // Part 2: responsible for applying explicit type parameter to function arguments, if tuple [TQueryFnData, TError, TData]
47
+ T extends [infer TQueryFnData, infer TError, infer TData]
48
+ ? QueryObserverOptionsForCreateQueries<TQueryFnData, TError, TData>
49
+ : T extends [infer TQueryFnData, infer TError]
50
+ ? QueryObserverOptionsForCreateQueries<TQueryFnData, TError>
51
+ : T extends [infer TQueryFnData]
52
+ ? QueryObserverOptionsForCreateQueries<TQueryFnData>
53
+ : // Part 3: responsible for inferring and enforcing type if no explicit parameter was provided
54
+ T extends {
55
+ queryFn?: QueryFunction<infer TQueryFnData, infer TQueryKey>
56
+ select: (data: any) => infer TData
57
+ throwOnError?: ThrowOnError<any, infer TError, any, any>
58
+ }
59
+ ? QueryObserverOptionsForCreateQueries<
60
+ TQueryFnData,
61
+ TError,
62
+ TData,
63
+ TQueryKey
64
+ >
65
+ : T extends {
66
+ queryFn?: QueryFunction<
67
+ infer TQueryFnData,
68
+ infer TQueryKey
69
+ >
70
+ throwOnError?: ThrowOnError<any, infer TError, any, any>
71
+ }
72
+ ? QueryObserverOptionsForCreateQueries<
73
+ TQueryFnData,
74
+ TError,
75
+ TQueryFnData,
76
+ TQueryKey
77
+ >
78
+ : // Fallback
79
+ QueryObserverOptionsForCreateQueries
80
+
81
+ type GetResults<T> =
82
+ // Part 1: responsible for mapping explicit type parameter to function result, if object
83
+ T extends { queryFnData: any; error?: infer TError; data: infer TData }
84
+ ? QueryObserverResult<TData, TError>
85
+ : T extends { queryFnData: infer TQueryFnData; error?: infer TError }
86
+ ? QueryObserverResult<TQueryFnData, TError>
87
+ : T extends { data: infer TData; error?: infer TError }
88
+ ? QueryObserverResult<TData, TError>
89
+ : // Part 2: responsible for mapping explicit type parameter to function result, if tuple
90
+ T extends [any, infer TError, infer TData]
91
+ ? QueryObserverResult<TData, TError>
92
+ : T extends [infer TQueryFnData, infer TError]
93
+ ? QueryObserverResult<TQueryFnData, TError>
94
+ : T extends [infer TQueryFnData]
95
+ ? QueryObserverResult<TQueryFnData>
96
+ : // Part 3: responsible for mapping inferred type to results, if no explicit parameter was provided
97
+ T extends {
98
+ queryFn?: QueryFunction<unknown, any>
99
+ select: (data: any) => infer TData
100
+ throwOnError?: ThrowOnError<any, infer TError, any, any>
101
+ }
102
+ ? QueryObserverResult<
103
+ TData,
104
+ unknown extends TError ? DefaultError : TError
105
+ >
106
+ : T extends {
107
+ queryFn?: QueryFunction<infer TQueryFnData, any>
108
+ throwOnError?: ThrowOnError<any, infer TError, any, any>
109
+ }
110
+ ? QueryObserverResult<
111
+ TQueryFnData,
112
+ unknown extends TError ? DefaultError : TError
113
+ >
114
+ : // Fallback
115
+ QueryObserverResult
116
+
117
+ /**
118
+ * QueriesOptions reducer recursively unwraps function arguments to infer/enforce type param
119
+ */
120
+ export type QueriesOptions<
121
+ T extends Array<any>,
122
+ Result extends Array<any> = [],
123
+ Depth extends ReadonlyArray<number> = [],
124
+ > = Depth['length'] extends MAXIMUM_DEPTH
125
+ ? Array<QueryObserverOptionsForCreateQueries>
126
+ : T extends []
127
+ ? []
128
+ : T extends [infer Head]
129
+ ? [...Result, GetOptions<Head>]
130
+ : T extends [infer Head, ...infer Tail]
131
+ ? QueriesOptions<
132
+ [...Tail],
133
+ [...Result, GetOptions<Head>],
134
+ [...Depth, 1]
135
+ >
136
+ : Array<unknown> extends T
137
+ ? T
138
+ : // If T is *some* array but we couldn't assign unknown[] to it, then it must hold some known/homogenous type!
139
+ // use this to infer the param types in the case of Array.map() argument
140
+ T extends Array<
141
+ QueryObserverOptionsForCreateQueries<
142
+ infer TQueryFnData,
143
+ infer TError,
144
+ infer TData,
145
+ infer TQueryKey
146
+ >
147
+ >
148
+ ? Array<
149
+ QueryObserverOptionsForCreateQueries<
150
+ TQueryFnData,
151
+ TError,
152
+ TData,
153
+ TQueryKey
154
+ >
155
+ >
156
+ : // Fallback
157
+ Array<QueryObserverOptionsForCreateQueries>
158
+
159
+ /**
160
+ * QueriesResults reducer recursively maps type param to results
161
+ */
162
+ export type QueriesResults<
163
+ T extends Array<any>,
164
+ Result extends Array<any> = [],
165
+ Depth extends ReadonlyArray<number> = [],
166
+ > = Depth['length'] extends MAXIMUM_DEPTH
167
+ ? Array<QueryObserverResult>
168
+ : T extends []
169
+ ? []
170
+ : T extends [infer Head]
171
+ ? [...Result, GetResults<Head>]
172
+ : T extends [infer Head, ...infer Tail]
173
+ ? QueriesResults<
174
+ [...Tail],
175
+ [...Result, GetResults<Head>],
176
+ [...Depth, 1]
177
+ >
178
+ : T extends Array<
179
+ QueryObserverOptionsForCreateQueries<
180
+ infer TQueryFnData,
181
+ infer TError,
182
+ infer TData,
183
+ any
184
+ >
185
+ >
186
+ ? // Dynamic-size (homogenous) CreateQueryOptions array: map directly to array of results
187
+ Array<
188
+ QueryObserverResult<
189
+ unknown extends TData ? TQueryFnData : TData,
190
+ unknown extends TError ? DefaultError : TError
191
+ >
192
+ >
193
+ : // Fallback
194
+ Array<QueryObserverResult>
195
+
196
+ export function injectQueries<
197
+ T extends Array<any>,
198
+ TCombinedResult = QueriesResults<T>,
199
+ >(
200
+ {
201
+ queries,
202
+ ...options
203
+ }: {
204
+ queries: Signal<[...QueriesOptions<T>]>
205
+ combine?: (result: QueriesResults<T>) => TCombinedResult
206
+ },
207
+ injector?: Injector,
208
+ ): Signal<TCombinedResult> {
209
+ return assertInjector(injectQueries, injector, () => {
210
+ const queryClient = injectQueryClient()
211
+ const destroyRef = inject(DestroyRef)
212
+
213
+ const defaultedQueries = computed(() => {
214
+ return queries().map((opts) => {
215
+ const defaultedOptions = queryClient.defaultQueryOptions(opts)
216
+ // Make sure the results are already in fetching state before subscribing or updating options
217
+ defaultedOptions._optimisticResults = 'optimistic'
218
+
219
+ return defaultedOptions
220
+ })
221
+ })
222
+
223
+ const observer = new QueriesObserver<TCombinedResult>(
224
+ queryClient,
225
+ defaultedQueries(),
226
+ options as QueriesObserverOptions<TCombinedResult>,
227
+ )
228
+
229
+ // Do not notify on updates because of changes in the options because
230
+ // these changes should already be reflected in the optimistic result.
231
+ effect(() => {
232
+ observer.setQueries(
233
+ defaultedQueries(),
234
+ options as QueriesObserverOptions<TCombinedResult>,
235
+ { listeners: false },
236
+ )
237
+ })
238
+
239
+ const [, getCombinedResult] =
240
+ observer.getOptimisticResult(defaultedQueries())
241
+
242
+ const result = signal(getCombinedResult() as any)
243
+
244
+ const unsubscribe = observer.subscribe(notifyManager.batchCalls(result.set))
245
+ destroyRef.onDestroy(unsubscribe)
246
+
247
+ return result
248
+ })
249
+ }
@@ -0,0 +1,61 @@
1
+ import { QueryObserver } from '@tanstack/query-core'
2
+ import { assertInjector } from 'ngxtension/assert-injector'
3
+ import { injectQueryClient } from './injectQueryClient'
4
+ import { createBaseQuery } from './createBaseQuery'
5
+ import type { DefaultError, QueryClient, QueryKey } from '@tanstack/query-core'
6
+ import type { Injector } from '@angular/core'
7
+ import type {
8
+ CreateQueryOptions,
9
+ CreateQueryResult,
10
+ DefinedCreateQueryResult,
11
+ } from './types'
12
+ import type {
13
+ DefinedInitialDataOptions,
14
+ UndefinedInitialDataOptions,
15
+ } from './queryOptions'
16
+
17
+ /**
18
+ * Create a Query.
19
+ * @param options
20
+ * @param injector
21
+ */
22
+ export function injectQuery<
23
+ TQueryFnData = unknown,
24
+ TError = DefaultError,
25
+ TData = TQueryFnData,
26
+ TQueryKey extends QueryKey = QueryKey,
27
+ >(
28
+ options: (
29
+ client: QueryClient,
30
+ ) => UndefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>,
31
+ injector?: Injector,
32
+ ): CreateQueryResult<TData, TError>
33
+
34
+ export function injectQuery<
35
+ TQueryFnData = unknown,
36
+ TError = DefaultError,
37
+ TData = TQueryFnData,
38
+ TQueryKey extends QueryKey = QueryKey,
39
+ >(
40
+ options: (
41
+ client: QueryClient,
42
+ ) => DefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>,
43
+ injector?: Injector,
44
+ ): DefinedCreateQueryResult<TData, TError>
45
+
46
+ export function injectQuery<
47
+ TQueryFnData,
48
+ TError = DefaultError,
49
+ TData = TQueryFnData,
50
+ TQueryKey extends QueryKey = QueryKey,
51
+ >(
52
+ options: (
53
+ client: QueryClient,
54
+ ) => CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
55
+ injector?: Injector,
56
+ ) {
57
+ return assertInjector(injectQuery, injector, () => {
58
+ const queryClient = injectQueryClient()
59
+ return createBaseQuery(options, QueryObserver, queryClient)
60
+ })
61
+ }
@@ -0,0 +1,7 @@
1
+ import { createNoopInjectionToken } from 'ngxtension/create-injection-token'
2
+ import type { QueryClient } from '@tanstack/query-core'
3
+
4
+ const [injectQueryClient, provideQueryClient, QUERY_CLIENT] =
5
+ createNoopInjectionToken<QueryClient>('QueryClientToken')
6
+
7
+ export { injectQueryClient, provideQueryClient, QUERY_CLIENT }
@@ -0,0 +1,26 @@
1
+ import {
2
+ DestroyRef,
3
+ ENVIRONMENT_INITIALIZER,
4
+ inject,
5
+ makeEnvironmentProviders,
6
+ } from '@angular/core'
7
+ import { provideQueryClient } from './injectQueryClient'
8
+ import type { EnvironmentProviders } from '@angular/core'
9
+ import type { QueryClient } from '@tanstack/query-core'
10
+
11
+ export function provideAngularQuery(
12
+ queryClient: QueryClient,
13
+ ): EnvironmentProviders {
14
+ return makeEnvironmentProviders([
15
+ provideQueryClient(queryClient),
16
+ {
17
+ provide: ENVIRONMENT_INITIALIZER,
18
+ multi: true,
19
+ useValue: () => {
20
+ queryClient.mount()
21
+ // Unmount the query client on application destroy
22
+ inject(DestroyRef).onDestroy(() => queryClient.unmount())
23
+ },
24
+ },
25
+ ])
26
+ }
@@ -0,0 +1,51 @@
1
+ import { type Signal, computed, untracked } from '@angular/core'
2
+ import type { DefaultError, QueryObserverResult } from '@tanstack/query-core'
3
+ import type { CreateBaseQueryResult } from './types'
4
+
5
+ export function createResultStateSignalProxy<
6
+ TData = unknown,
7
+ TError = DefaultError,
8
+ State = QueryObserverResult<TData, TError>,
9
+ >(resultState: Signal<State>) {
10
+ const internalState = {} as CreateBaseQueryResult<TData, TError, State>
11
+
12
+ return new Proxy(internalState, {
13
+ get<Key extends keyof State>(
14
+ target: CreateBaseQueryResult<TData, TError, State>,
15
+ prop: Key | string | symbol,
16
+ ) {
17
+ // first check if we have it in our internal state and return it
18
+ const computedField = target[prop as Key]
19
+
20
+ // TODO: check if this if statement is necessary
21
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
22
+ if (computedField) return computedField
23
+
24
+ // then, check if it's a function on the resultState and return it
25
+ const targetField = untracked(resultState)[prop as Key]
26
+ if (typeof targetField === 'function') return targetField
27
+
28
+ // finally, create a computed field, store it and return it
29
+ // @ts-ignore
30
+ return (target[prop] = computed(() => resultState()[prop as Key]))
31
+ },
32
+ has<K extends keyof State>(
33
+ target: CreateBaseQueryResult<TData, TError, State>,
34
+ prop: K | string | symbol,
35
+ ) {
36
+ return !!target[prop as K]
37
+ },
38
+ ownKeys(target) {
39
+ return [...Reflect.ownKeys(target)]
40
+ },
41
+ getOwnPropertyDescriptor() {
42
+ return {
43
+ enumerable: true,
44
+ configurable: true,
45
+ }
46
+ },
47
+ set(): boolean {
48
+ return true
49
+ },
50
+ })
51
+ }
@@ -0,0 +1,46 @@
1
+ import type { DefaultError, QueryKey } from '@tanstack/query-core'
2
+ import type { CreateQueryOptions } from './types'
3
+
4
+ export type UndefinedInitialDataOptions<
5
+ TQueryFnData = unknown,
6
+ TError = DefaultError,
7
+ TData = TQueryFnData,
8
+ TQueryKey extends QueryKey = QueryKey,
9
+ > = CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey> & {
10
+ initialData?: undefined
11
+ }
12
+
13
+ type NonUndefinedGuard<T> = T extends undefined ? never : T
14
+
15
+ export type DefinedInitialDataOptions<
16
+ TQueryFnData = unknown,
17
+ TError = DefaultError,
18
+ TData = TQueryFnData,
19
+ TQueryKey extends QueryKey = QueryKey,
20
+ > = CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey> & {
21
+ initialData:
22
+ | NonUndefinedGuard<TQueryFnData>
23
+ | (() => NonUndefinedGuard<TQueryFnData>)
24
+ }
25
+
26
+ export function queryOptions<
27
+ TQueryFnData = unknown,
28
+ TError = DefaultError,
29
+ TData = TQueryFnData,
30
+ TQueryKey extends QueryKey = QueryKey,
31
+ >(
32
+ options: UndefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>,
33
+ ): UndefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>
34
+
35
+ export function queryOptions<
36
+ TQueryFnData = unknown,
37
+ TError = DefaultError,
38
+ TData = TQueryFnData,
39
+ TQueryKey extends QueryKey = QueryKey,
40
+ >(
41
+ options: DefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>,
42
+ ): DefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>
43
+
44
+ export function queryOptions(options: unknown) {
45
+ return options
46
+ }
@@ -0,0 +1,13 @@
1
+ import '@analogjs/vite-plugin-angular/setup-vitest'
2
+ import '@angular/compiler'
3
+
4
+ import { TestBed } from '@angular/core/testing'
5
+ import {
6
+ BrowserDynamicTestingModule,
7
+ platformBrowserDynamicTesting,
8
+ } from '@angular/platform-browser-dynamic/testing'
9
+
10
+ TestBed.initTestEnvironment(
11
+ BrowserDynamicTestingModule,
12
+ platformBrowserDynamicTesting(),
13
+ )
@@ -0,0 +1,32 @@
1
+ import { TestBed, fakeAsync, flush } from '@angular/core/testing'
2
+ import { QueryClient } from '@tanstack/query-core'
3
+ import { beforeEach, describe, expect } from 'vitest'
4
+ import { injectIsFetching } from '../injectIsFetching'
5
+ import { injectQuery } from '../injectQuery'
6
+ import { provideAngularQuery } from '../providers'
7
+ import { delayedFetcher } from './test-utils'
8
+
9
+ describe('injectIsFetching', () => {
10
+ let queryClient: QueryClient
11
+
12
+ beforeEach(() => {
13
+ queryClient = new QueryClient()
14
+
15
+ TestBed.configureTestingModule({
16
+ providers: [provideAngularQuery(queryClient)],
17
+ })
18
+ })
19
+
20
+ it('Returns number of fetching queries', fakeAsync(() => {
21
+ const isFetching = TestBed.runInInjectionContext(() => {
22
+ injectQuery(() => ({
23
+ queryKey: ['isFetching1'],
24
+ queryFn: delayedFetcher(100),
25
+ }))
26
+ return injectIsFetching()
27
+ })
28
+ expect(isFetching()).toStrictEqual(1)
29
+ flush()
30
+ expect(isFetching()).toStrictEqual(0)
31
+ }))
32
+ })
@@ -0,0 +1,43 @@
1
+ import { QueryClient } from '@tanstack/query-core'
2
+ import { beforeEach, describe } from 'vitest'
3
+ import { TestBed, fakeAsync, tick } from '@angular/core/testing'
4
+ import { injectIsMutating } from '../injectIsMutating'
5
+ import { injectMutation } from '../injectMutation'
6
+ import { provideAngularQuery } from '../providers'
7
+ import { successMutator } from './test-utils'
8
+
9
+ describe('injectIsMutating', () => {
10
+ let queryClient: QueryClient
11
+
12
+ beforeEach(() => {
13
+ queryClient = new QueryClient()
14
+
15
+ TestBed.configureTestingModule({
16
+ providers: [provideAngularQuery(queryClient)],
17
+ })
18
+ })
19
+
20
+ it('should properly return isMutating state', fakeAsync(() => {
21
+ TestBed.runInInjectionContext(() => {
22
+ const isMutating = injectIsMutating()
23
+ const mutation = injectMutation(() => ({
24
+ mutationKey: ['isMutating1'],
25
+ mutationFn: successMutator<{ par1: string }>,
26
+ }))
27
+
28
+ expect(isMutating()).toBe(0)
29
+
30
+ mutation().mutate({
31
+ par1: 'par1',
32
+ })
33
+
34
+ tick()
35
+
36
+ TestBed.flushEffects()
37
+
38
+ expect(isMutating()).toBe(1)
39
+
40
+ tick()
41
+ })
42
+ }))
43
+ })
@@ -0,0 +1,52 @@
1
+ import { TestBed, fakeAsync, flush } from '@angular/core/testing'
2
+ import { QueryClient } from '@tanstack/query-core'
3
+ import { vi } from 'vitest'
4
+ import { expect } from 'vitest'
5
+ import { injectMutation } from '../injectMutation'
6
+ import { provideAngularQuery } from '../providers'
7
+ import { QUERY_CLIENT } from '../injectQueryClient'
8
+
9
+ describe('injectMutation', () => {
10
+ let queryClient: QueryClient
11
+
12
+ beforeEach(() => {
13
+ TestBed.configureTestingModule({
14
+ providers: [provideAngularQuery(new QueryClient())],
15
+ })
16
+
17
+ queryClient = TestBed.inject(QUERY_CLIENT)
18
+ })
19
+
20
+ it('should run mutation', fakeAsync(() => {
21
+ const mutationFn = vi.fn()
22
+
23
+ const mutation = TestBed.runInInjectionContext(() => {
24
+ return injectMutation(() => ({
25
+ mutationFn,
26
+ }))
27
+ })
28
+
29
+ mutation().mutate({
30
+ par1: 'par1',
31
+ })
32
+
33
+ flush()
34
+ expect(mutation().status).toBe('pending')
35
+ }))
36
+
37
+ it('can access client from options callback', fakeAsync(() => {
38
+ const mutation = TestBed.runInInjectionContext(() => {
39
+ return injectMutation((client) => ({
40
+ mutationFn: () => {
41
+ expect(client).toBe(queryClient)
42
+ return Promise.resolve()
43
+ },
44
+ }))
45
+ })
46
+
47
+ mutation().mutate()
48
+
49
+ flush()
50
+ expect(mutation().status).toBe('pending')
51
+ }))
52
+ })