Skip to content

Commit 4b795be

Browse files
authored
Merge branch 'main' into fix/usequery-retry-on-mount-with-throw-on-error-issue(#9336)
2 parents e8e0caa + cd29063 commit 4b795be

File tree

117 files changed

+3858
-1982
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

117 files changed

+3858
-1982
lines changed

.github/workflows/autofix.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ jobs:
1818
runs-on: ubuntu-latest
1919
steps:
2020
- name: Checkout
21-
uses: actions/[email protected]
21+
uses: actions/[email protected]
22+
with:
23+
fetch-depth: 0
2224
- name: Setup Tools
2325
uses: tanstack/config/.github/setup@main
2426
- name: Fix formatting

.github/workflows/pr.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: pr
1+
name: PR
22

33
on:
44
pull_request:
@@ -22,15 +22,15 @@ jobs:
2222
runs-on: ubuntu-latest
2323
steps:
2424
- name: Checkout
25-
uses: actions/checkout@v4.2.2
25+
uses: actions/checkout@v5.0.0
2626
with:
2727
fetch-depth: 0
2828
- name: Start Nx Agents
2929
run: npx nx-cloud start-ci-run --distribute-on=".nx/workflows/dynamic-changesets.yaml"
3030
- name: Setup Tools
3131
uses: tanstack/config/.github/setup@main
3232
- name: Get base and head commits for `nx affected`
33-
uses: nrwl/nx-set-shas@v4.1.2
33+
uses: nrwl/nx-set-shas@v4.3.3
3434
with:
3535
main-branch-name: main
3636
- name: Run Checks
@@ -51,7 +51,7 @@ jobs:
5151
runs-on: ubuntu-latest
5252
steps:
5353
- name: Checkout
54-
uses: actions/checkout@v4.2.2
54+
uses: actions/checkout@v5.0.0
5555
with:
5656
fetch-depth: 0
5757
- name: Setup Tools
@@ -65,7 +65,7 @@ jobs:
6565
run: |
6666
echo "COMMIT_SHA=${{ github.event.pull_request.head.sha || github.sha }}" >> $GITHUB_ENV
6767
- name: Preview Bundle Size
68-
uses: marocchino/sticky-pull-request-comment@a071bc9e79df3f13d7cd94d06ab2ef1ec959e9fa
68+
uses: marocchino/sticky-pull-request-comment@fd19551a2a7c0d29677c63257f86d8ef8bb24b48
6969
with:
7070
message: |
7171
Sizes for commit ${{ env.COMMIT_SHA }}:
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@ permissions:
2121
id-token: write
2222

2323
jobs:
24-
test-and-publish:
25-
name: Test & Publish
24+
release:
25+
name: Release
2626
if: github.repository_owner == 'TanStack'
2727
runs-on: ubuntu-latest
2828
steps:
2929
- name: Checkout
30-
uses: actions/checkout@v4.2.2
30+
uses: actions/checkout@v5.0.0
3131
with:
3232
fetch-depth: 0
3333
- name: Start Nx Agents

.npmrc

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1 @@
1-
link-workspace-packages=true
2-
prefer-workspace-packages=true
31
provenance=true

.nvmrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
24.4.1
1+
24.8.0

docs/config.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,10 @@
707707
"label": "Default Query Fn",
708708
"to": "framework/angular/guides/default-query-function"
709709
},
710+
{
711+
"label": "Testing",
712+
"to": "framework/angular/guides/testing"
713+
},
710714
{
711715
"label": "Does this replace state managers?",
712716
"to": "framework/angular/guides/does-this-replace-client-state"
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
---
2+
id: testing
3+
title: Testing
4+
---
5+
6+
Most Angular tests using TanStack Query will involve services or components that call `injectQuery`/`injectMutation`.
7+
8+
TanStack Query's `inject*` functions integrate with [`PendingTasks`](https://angular.dev/api/core/PendingTasks) which ensures the framework is aware of in-progress queries and mutations.
9+
10+
This means tests and SSR can wait until mutations and queries resolve. In unit tests you can use `ApplicationRef.whenStable()` or `fixture.whenStable()` to await query completion. This works for both Zone.js and Zoneless setups.
11+
12+
> This integration requires Angular 19 or later. Earlier versions of Angular do not support `PendingTasks`.
13+
14+
## TestBed setup
15+
16+
Create a fresh `QueryClient` for every spec and provide it with `provideTanStackQuery` or `provideQueryClient`. This keeps caches isolated and lets you change default options per test:
17+
18+
```ts
19+
const queryClient = new QueryClient({
20+
defaultOptions: {
21+
queries: {
22+
retry: false, // ✅ faster failure tests
23+
},
24+
},
25+
})
26+
27+
TestBed.configureTestingModule({
28+
providers: [provideTanStackQuery(queryClient)],
29+
})
30+
```
31+
32+
> If your applications actual TanStack Query config is used in unit tests, make sure `withDevtools` is not accidentally included in test providers. This can cause slow tests. It is best to keep test and production configs separate.
33+
34+
If you share helpers, remember to call `queryClient.clear()` (or build a new instance) in `afterEach` so data from one test never bleeds into another.
35+
36+
## First query test
37+
38+
Query tests typically run inside `TestBed.runInInjectionContext`, then wait for stability:
39+
40+
```ts
41+
const appRef = TestBed.inject(ApplicationRef)
42+
const query = TestBed.runInInjectionContext(() =>
43+
injectQuery(() => ({
44+
queryKey: ['greeting'],
45+
queryFn: () => 'Hello',
46+
})),
47+
)
48+
49+
TestBed.tick() // Trigger effect
50+
51+
// Application is stable when queries are idle
52+
await appRef.whenStable()
53+
54+
expect(query.status()).toBe('success')
55+
expect(query.data()).toBe('Hello')
56+
```
57+
58+
PendingTasks will have `whenStable()` resolve after the query settles. When using fake timers (Vitest), advance the clock and a microtask before awaiting stability:
59+
60+
```ts
61+
await vi.advanceTimersByTimeAsync(0)
62+
await Promise.resolve()
63+
await appRef.whenStable()
64+
```
65+
66+
## Testing components
67+
68+
For components, bootstrap them through `TestBed.createComponent`, then await `fixture.whenStable()`:
69+
70+
```ts
71+
const fixture = TestBed.createComponent(ExampleComponent)
72+
73+
await fixture.whenStable()
74+
expect(fixture.componentInstance.query.data()).toEqual({ value: 42 })
75+
```
76+
77+
## Handling retries
78+
79+
Retries slow failing tests because the default backoff runs three times. Set `retry: false` (or a specific number) through `defaultOptions` or per query to keep tests fast. If a query intentionally retries, assert on the final state rather than intermediate counts.
80+
81+
## HttpClient & network stubs
82+
83+
Angular's `HttpClientTestingModule` plays nicely with PendingTasks. Register it alongside the Query provider and flush responses through `HttpTestingController`:
84+
85+
```ts
86+
TestBed.configureTestingModule({
87+
imports: [HttpClientTestingModule],
88+
providers: [provideTanStackQuery(queryClient)],
89+
})
90+
91+
const httpCtrl = TestBed.inject(HttpTestingController)
92+
const query = TestBed.runInInjectionContext(() =>
93+
injectQuery(() => ({
94+
queryKey: ['todos'],
95+
queryFn: () => lastValueFrom(TestBed.inject(HttpClient).get('/api/todos')),
96+
})),
97+
)
98+
99+
const fixturePromise = TestBed.inject(ApplicationRef).whenStable()
100+
httpCtrl.expectOne('/api/todos').flush([{ id: 1 }])
101+
await fixturePromise
102+
103+
expect(query.data()).toEqual([{ id: 1 }])
104+
httpCtrl.verify()
105+
```
106+
107+
## Infinite queries & pagination
108+
109+
Use the same pattern for infinite queries: call `fetchNextPage()`, advance timers if you are faking time, then await stability and assert on `data().pages`.
110+
111+
```ts
112+
const infinite = TestBed.runInInjectionContext(() =>
113+
injectInfiniteQuery(() => ({
114+
queryKey: ['pages'],
115+
queryFn: ({ pageParam = 1 }) => fetchPage(pageParam),
116+
getNextPageParam: (last, all) => all.length + 1,
117+
})),
118+
)
119+
120+
await appRef.whenStable()
121+
expect(infinite.data().pages).toHaveLength(1)
122+
123+
await infinite.fetchNextPage()
124+
await vi.advanceTimersByTimeAsync(0)
125+
await appRef.whenStable()
126+
127+
expect(infinite.data().pages).toHaveLength(2)
128+
```
129+
130+
## Mutations and optimistic updates
131+
132+
```ts
133+
const mutation = TestBed.runInInjectionContext(() =>
134+
injectMutation(() => ({
135+
mutationFn: async (input: string) => input.toUpperCase(),
136+
})),
137+
)
138+
139+
mutation.mutate('test')
140+
141+
// Trigger effect
142+
TestBed.tick()
143+
144+
await appRef.whenStable()
145+
146+
expect(mutation.isSuccess()).toBe(true)
147+
expect(mutation.data()).toBe('TEST')
148+
```
149+
150+
## Quick checklist
151+
152+
- Fresh `QueryClient` per test (and clear it afterwards)
153+
- Disable or control retries to avoid timeouts
154+
- Advance timers + microtasks before `whenStable()` when using fake timers
155+
- Use `HttpClientTestingModule` or your preferred mock to assert network calls
156+
- Await `whenStable()` after every `refetch`, `fetchNextPage`, or mutation
157+
- Prefer `TestBed.runInInjectionContext` for service tests and `fixture.whenStable()` for component tests

docs/framework/angular/zoneless.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ Because the Angular adapter for TanStack Query is built on signals, it fully sup
88
Among Zoneless benefits are improved performance and debugging experience. For details see the [Angular documentation](https://angular.dev/guide/zoneless).
99

1010
> Besides Zoneless, ZoneJS change detection is also fully supported.
11+
12+
> When using Zoneless, ensure you are on Angular v19 or later to take advantage of the `PendingTasks` integration that keeps `ApplicationRef.whenStable()` in sync with ongoing queries and mutations.

docs/framework/react/typescript.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,13 @@ Without `queryOptions`, the type of `data` would be `unknown`, unless we'd pass
237237
const data = queryClient.getQueryData<Group[]>(['groups'])
238238
```
239239

240+
Note that type inference via `queryOptions` does _not_ work for `queryClient.getQueriesData`, because it returns an array of tuples with heterogeneous, `unknown` data. If you are sure of the type of data that your query will return, specify it explicitly:
241+
242+
```ts
243+
const entries = queryClient.getQueriesData<Group[]>(groupOptions().queryKey)
244+
// ^? const entries: Array<[QueryKey, Group[] | undefined]>
245+
```
246+
240247
## Typing Mutation Options
241248

242249
Similarly to `queryOptions`, you can use `mutationOptions` to extract mutation options into a separate function:

eslint.config.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,25 @@ export default [
1818
{
1919
cspell: {
2020
words: [
21+
'Promisable', // Our public interface
22+
'TSES', // @typescript-eslint package's interface
2123
'codemod', // We support our codemod
2224
'combinate', // Library name
25+
'datatag', // Query options tagging
2326
'extralight', // Our public interface
2427
'jscodeshift',
25-
'Promisable', // Our public interface
28+
'refetches', // Query refetch operations
2629
'retryer', // Our public interface
2730
'solidjs', // Our target framework
2831
'tabular-nums', // https://developer.mozilla.org/en-US/docs/Web/CSS/font-variant-numeric
2932
'tanstack', // Our package scope
3033
'todos', // Too general word to be caught as error
31-
'TSES', // @typescript-eslint package's interface
3234
'tsqd', // Our public interface (TanStack Query Devtools shorthand)
3335
'tsup', // We use tsup as builder
3436
'typecheck', // Field of vite.config.ts
3537
'vue-demi', // dependency of @tanstack/vue-query
38+
'ɵkind', // Angular specific
39+
'ɵproviders', // Angular specific
3640
],
3741
},
3842
},

0 commit comments

Comments
 (0)