@geekmidas/client 9.0.1 → 10.0.0-alpha.0
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.
- package/dist/auth-fetcher-CBYuL6rx.d.cts +158 -0
- package/dist/auth-fetcher-CBYuL6rx.d.cts.map +1 -0
- package/dist/auth-fetcher-DOj0iz21.mjs +77 -0
- package/dist/auth-fetcher-DOj0iz21.mjs.map +1 -0
- package/dist/auth-fetcher-DxS8zOLc.d.mts +158 -0
- package/dist/auth-fetcher-DxS8zOLc.d.mts.map +1 -0
- package/dist/auth-fetcher-HILABmws.cjs +82 -0
- package/dist/auth-fetcher-HILABmws.cjs.map +1 -0
- package/dist/auth-fetcher.cjs +3 -76
- package/dist/auth-fetcher.d.cts +3 -158
- package/dist/auth-fetcher.d.mts +3 -158
- package/dist/auth-fetcher.mjs +3 -76
- package/dist/endpoint-hooks.d.mts +1 -1
- package/dist/fetcher-C1xtCKpT.d.mts +25 -0
- package/dist/fetcher-C1xtCKpT.d.mts.map +1 -0
- package/dist/fetcher-cDrfwN-Y.d.cts +25 -0
- package/dist/fetcher-cDrfwN-Y.d.cts.map +1 -0
- package/dist/fetcher.d.cts +3 -25
- package/dist/fetcher.d.mts +3 -25
- package/dist/index.cjs +8 -0
- package/dist/index.d.cts +5 -0
- package/dist/index.d.mts +5 -0
- package/dist/index.mjs +6 -0
- package/dist/infer-6KKCgmg1.cjs +0 -0
- package/{src/infer.ts → dist/infer-DfRq5kIb.d.cts} +54 -83
- package/dist/infer-DfRq5kIb.d.cts.map +1 -0
- package/dist/infer-bTirNeNW.d.mts +133 -0
- package/dist/infer-bTirNeNW.d.mts.map +1 -0
- package/dist/infer-uCUq1whM.mjs +0 -0
- package/dist/infer.cjs +1 -0
- package/dist/infer.d.cts +2 -133
- package/dist/infer.d.mts +2 -133
- package/dist/infer.mjs +1 -0
- package/dist/openapi-hooks.cjs.map +1 -1
- package/dist/openapi-hooks.d.cts +3 -3
- package/dist/openapi-hooks.d.cts.map +1 -1
- package/dist/openapi-hooks.d.mts +1 -1
- package/dist/openapi-hooks.d.mts.map +1 -1
- package/dist/openapi-hooks.mjs.map +1 -1
- package/dist/react-query.d.cts +7 -7
- package/dist/react-query.d.mts +1 -1
- package/dist/types-BH8WgGrm.mjs +0 -0
- package/dist/{types-D8Jrl4o3.d.mts → types-BMGM6sZc.d.mts} +1 -1
- package/dist/{types-D8Jrl4o3.d.mts.map → types-BMGM6sZc.d.mts.map} +1 -1
- package/dist/types-a8gm_IaQ.cjs +0 -0
- package/dist/types.cjs +1 -0
- package/dist/types.d.mts +1 -1
- package/dist/types.mjs +1 -0
- package/package.json +8 -5
- package/CHANGELOG.md +0 -124
- package/dist/auth-fetcher.cjs.map +0 -1
- package/dist/auth-fetcher.d.cts.map +0 -1
- package/dist/auth-fetcher.d.mts.map +0 -1
- package/dist/auth-fetcher.mjs.map +0 -1
- package/dist/fetcher.d.cts.map +0 -1
- package/dist/fetcher.d.mts.map +0 -1
- package/dist/infer.d.cts.map +0 -1
- package/dist/infer.d.mts.map +0 -1
- package/src/__tests__/auth-fetcher.spec.ts +0 -505
- package/src/__tests__/endpoint-hooks.spec.tsx +0 -374
- package/src/__tests__/fetcher.spec.ts +0 -580
- package/src/__tests__/infer.integration.spec.ts +0 -188
- package/src/__tests__/infer.spec.ts +0 -189
- package/src/__tests__/method-restrictions.spec.tsx +0 -227
- package/src/__tests__/mutation-inference.spec.tsx +0 -343
- package/src/__tests__/openapi-hooks.spec.tsx +0 -557
- package/src/__tests__/react-query-infinite.spec.tsx +0 -1003
- package/src/__tests__/react-query-invalidation.spec.tsx +0 -239
- package/src/__tests__/react-query.spec.tsx +0 -582
- package/src/__tests__/setup.ts +0 -281
- package/src/__tests__/types.spec.ts +0 -134
- package/src/__tests__/url-parsing.spec.ts +0 -196
- package/src/auth-fetcher.ts +0 -288
- package/src/endpoint-hooks.ts +0 -346
- package/src/fetcher.ts +0 -171
- package/src/openapi-hooks.ts +0 -193
- package/src/openapi-types.d.ts +0 -440
- package/src/openapi.json +0 -595
- package/src/react-query.ts +0 -341
- package/src/types.ts +0 -226
- package/tsconfig.json +0 -12
|
@@ -1,582 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @vitest-environment jsdom
|
|
3
|
-
*/
|
|
4
|
-
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
5
|
-
import { act, renderHook, waitFor } from '@testing-library/react';
|
|
6
|
-
import { HttpResponse, http } from 'msw';
|
|
7
|
-
// biome-ignore lint/style/useImportType: <explanation>
|
|
8
|
-
import React from 'react';
|
|
9
|
-
import { describe, expect, it } from 'vitest';
|
|
10
|
-
import type { paths } from '../openapi-types';
|
|
11
|
-
import {
|
|
12
|
-
createTypedQueryClient,
|
|
13
|
-
useTypedMutation,
|
|
14
|
-
useTypedQuery,
|
|
15
|
-
} from '../react-query';
|
|
16
|
-
import './setup';
|
|
17
|
-
|
|
18
|
-
describe('TypedQueryClient', () => {
|
|
19
|
-
const queryClient = new QueryClient({
|
|
20
|
-
defaultOptions: {
|
|
21
|
-
queries: {
|
|
22
|
-
retry: false,
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
28
|
-
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
|
29
|
-
);
|
|
30
|
-
|
|
31
|
-
it('should create query client with correct configuration', () => {
|
|
32
|
-
const typedClient = createTypedQueryClient<paths>({
|
|
33
|
-
baseURL: 'https://api.example.com',
|
|
34
|
-
headers: { Authorization: 'Bearer token' },
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
expect(typedClient).toBeDefined();
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
describe('useQuery', () => {
|
|
41
|
-
it('should fetch data successfully', async () => {
|
|
42
|
-
const typedClient = createTypedQueryClient<paths>({
|
|
43
|
-
baseURL: 'https://api.example.com',
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
const { result } = renderHook(
|
|
47
|
-
() =>
|
|
48
|
-
typedClient.useQuery('GET /users/{id}', {
|
|
49
|
-
params: { id: '123' },
|
|
50
|
-
}),
|
|
51
|
-
{ wrapper },
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
expect(result.current.isLoading).toBe(true);
|
|
55
|
-
|
|
56
|
-
await waitFor(() => {
|
|
57
|
-
expect(result.current.isSuccess).toBe(true);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
expect(result.current.data).toEqual({
|
|
61
|
-
id: '123',
|
|
62
|
-
name: 'John Doe',
|
|
63
|
-
email: 'john@example.com',
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it('should handle query parameters', async () => {
|
|
68
|
-
const typedClient = createTypedQueryClient<paths>({
|
|
69
|
-
baseURL: 'https://api.example.com',
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
const { result } = renderHook(
|
|
73
|
-
() =>
|
|
74
|
-
typedClient.useQuery('GET /posts', {
|
|
75
|
-
query: { page: 1, limit: 10, sort: 'desc' },
|
|
76
|
-
}),
|
|
77
|
-
{ wrapper },
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
await waitFor(() => {
|
|
81
|
-
expect(result.current.isSuccess).toBe(true);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
expect(result.current.data).toMatchObject({
|
|
85
|
-
posts: expect.any(Array),
|
|
86
|
-
pagination: {
|
|
87
|
-
page: 1,
|
|
88
|
-
limit: 10,
|
|
89
|
-
total: 50,
|
|
90
|
-
},
|
|
91
|
-
sort: 'desc',
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it('should handle errors', async () => {
|
|
96
|
-
const typedClient = createTypedQueryClient<paths>({
|
|
97
|
-
baseURL: 'https://api.example.com',
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
const { result } = renderHook(() => typedClient.useQuery('GET /error'), {
|
|
101
|
-
wrapper,
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
await waitFor(() => {
|
|
105
|
-
expect(result.current.isError).toBe(true);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
expect(result.current.error).toBeDefined();
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it('should pass additional query options', async () => {
|
|
112
|
-
const typedClient = createTypedQueryClient<paths>({
|
|
113
|
-
baseURL: 'https://api.example.com',
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
const { result } = renderHook(
|
|
117
|
-
() =>
|
|
118
|
-
typedClient.useQuery(
|
|
119
|
-
'GET /users/{id}',
|
|
120
|
-
{ params: { id: '123' } },
|
|
121
|
-
{
|
|
122
|
-
staleTime: 5 * 60 * 1000,
|
|
123
|
-
gcTime: 10 * 60 * 1000,
|
|
124
|
-
},
|
|
125
|
-
),
|
|
126
|
-
{ wrapper },
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
await waitFor(() => {
|
|
130
|
-
expect(result.current.isSuccess).toBe(true);
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
expect(result.current.data).toBeDefined();
|
|
134
|
-
});
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
describe('useMutation', () => {
|
|
138
|
-
it('should perform mutations successfully', async () => {
|
|
139
|
-
const typedClient = createTypedQueryClient<paths>({
|
|
140
|
-
baseURL: 'https://api.example.com',
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
const { result } = renderHook(
|
|
144
|
-
() => typedClient.useMutation('POST /users'),
|
|
145
|
-
{ wrapper },
|
|
146
|
-
);
|
|
147
|
-
|
|
148
|
-
expect(result.current.isPending).toBe(false);
|
|
149
|
-
|
|
150
|
-
act(() => {
|
|
151
|
-
result.current.mutate({
|
|
152
|
-
body: {
|
|
153
|
-
name: 'New User',
|
|
154
|
-
email: 'new@example.com',
|
|
155
|
-
},
|
|
156
|
-
});
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
await waitFor(() => {
|
|
160
|
-
expect(result.current.isSuccess).toBe(true);
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
expect(result.current.data).toEqual({
|
|
164
|
-
id: '123',
|
|
165
|
-
name: 'New User',
|
|
166
|
-
email: 'new@example.com',
|
|
167
|
-
});
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
it('should handle mutation with path parameters', async () => {
|
|
171
|
-
const typedClient = createTypedQueryClient<paths>({
|
|
172
|
-
baseURL: 'https://api.example.com',
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
const { result } = renderHook(
|
|
176
|
-
() => typedClient.useMutation('PUT /users/{id}'),
|
|
177
|
-
{ wrapper },
|
|
178
|
-
);
|
|
179
|
-
|
|
180
|
-
act(() => {
|
|
181
|
-
result.current.mutate({
|
|
182
|
-
params: { id: '456' },
|
|
183
|
-
body: {
|
|
184
|
-
name: 'Updated User',
|
|
185
|
-
},
|
|
186
|
-
});
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
await waitFor(() => {
|
|
190
|
-
expect(result.current.isSuccess).toBe(true);
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
expect(result.current.data).toMatchObject({
|
|
194
|
-
id: '456',
|
|
195
|
-
name: 'Updated User',
|
|
196
|
-
});
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
it('should accept mutation options', async () => {
|
|
200
|
-
const typedClient = createTypedQueryClient<paths>({
|
|
201
|
-
baseURL: 'https://api.example.com',
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
let successData: any;
|
|
205
|
-
const onSuccess = (data: any) => {
|
|
206
|
-
successData = data;
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
const { result } = renderHook(
|
|
210
|
-
() => typedClient.useMutation('POST /users', { onSuccess }),
|
|
211
|
-
{ wrapper },
|
|
212
|
-
);
|
|
213
|
-
|
|
214
|
-
act(() => {
|
|
215
|
-
result.current.mutate({
|
|
216
|
-
body: {
|
|
217
|
-
name: 'Test User',
|
|
218
|
-
email: 'test@example.com',
|
|
219
|
-
},
|
|
220
|
-
});
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
await waitFor(() => {
|
|
224
|
-
expect(result.current.isSuccess).toBe(true);
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
expect(successData).toEqual({
|
|
228
|
-
id: '123',
|
|
229
|
-
name: 'Test User',
|
|
230
|
-
email: 'test@example.com',
|
|
231
|
-
});
|
|
232
|
-
});
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
describe('helper hooks', () => {
|
|
236
|
-
it('should work with useTypedQuery helper', async () => {
|
|
237
|
-
const typedClient = createTypedQueryClient<paths>({
|
|
238
|
-
baseURL: 'https://api.example.com',
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
const { result } = renderHook(
|
|
242
|
-
() =>
|
|
243
|
-
useTypedQuery(typedClient, 'GET /users/{id}', {
|
|
244
|
-
params: { id: '789' },
|
|
245
|
-
}),
|
|
246
|
-
{ wrapper },
|
|
247
|
-
);
|
|
248
|
-
|
|
249
|
-
await waitFor(() => {
|
|
250
|
-
expect(result.current.isSuccess).toBe(true);
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
expect(result.current.data).toMatchObject({
|
|
254
|
-
id: '789',
|
|
255
|
-
name: 'John Doe',
|
|
256
|
-
email: 'john@example.com',
|
|
257
|
-
});
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
it('should work with useTypedMutation helper', async () => {
|
|
261
|
-
const typedClient = createTypedQueryClient<paths>({
|
|
262
|
-
baseURL: 'https://api.example.com',
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
const { result } = renderHook(
|
|
266
|
-
() => useTypedMutation(typedClient, 'DELETE /users/{id}'),
|
|
267
|
-
{ wrapper },
|
|
268
|
-
);
|
|
269
|
-
|
|
270
|
-
act(() => {
|
|
271
|
-
result.current.mutate({
|
|
272
|
-
params: { id: '999' },
|
|
273
|
-
});
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
await waitFor(() => {
|
|
277
|
-
expect(result.current.isSuccess).toBe(true);
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
expect(result.current.data).toBeUndefined();
|
|
281
|
-
});
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
describe('query key generation', () => {
|
|
285
|
-
it('should generate correct query keys', async () => {
|
|
286
|
-
const typedClient = createTypedQueryClient<paths>({
|
|
287
|
-
baseURL: 'https://api.example.com',
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
// Test with path params only
|
|
291
|
-
const { result: result1 } = renderHook(
|
|
292
|
-
() =>
|
|
293
|
-
typedClient.useQuery('GET /users/{id}', {
|
|
294
|
-
params: { id: '123' },
|
|
295
|
-
}),
|
|
296
|
-
{ wrapper },
|
|
297
|
-
);
|
|
298
|
-
|
|
299
|
-
await waitFor(() => expect(result1.current.isSuccess).toBe(true));
|
|
300
|
-
|
|
301
|
-
// The query key should include the endpoint and params
|
|
302
|
-
const queryState = queryClient.getQueryState([
|
|
303
|
-
'GET /users/{id}',
|
|
304
|
-
{ params: { id: '123' } },
|
|
305
|
-
]);
|
|
306
|
-
expect(queryState).toBeDefined();
|
|
307
|
-
|
|
308
|
-
// Test with query params
|
|
309
|
-
const { result: result2 } = renderHook(
|
|
310
|
-
() =>
|
|
311
|
-
typedClient.useQuery('GET /posts', {
|
|
312
|
-
query: { page: 1, limit: 10 },
|
|
313
|
-
}),
|
|
314
|
-
{ wrapper },
|
|
315
|
-
);
|
|
316
|
-
|
|
317
|
-
await waitFor(() => expect(result2.current.isSuccess).toBe(true));
|
|
318
|
-
|
|
319
|
-
const queryState2 = queryClient.getQueryState([
|
|
320
|
-
'GET /posts',
|
|
321
|
-
{ query: { page: 1, limit: 10 } },
|
|
322
|
-
]);
|
|
323
|
-
expect(queryState2).toBeDefined();
|
|
324
|
-
});
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
describe('error handling', () => {
|
|
328
|
-
it('should handle 404 errors', async () => {
|
|
329
|
-
const typedClient = createTypedQueryClient<paths>({
|
|
330
|
-
baseURL: 'https://api.example.com',
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
const { result } = renderHook(
|
|
334
|
-
() =>
|
|
335
|
-
typedClient.useQuery('GET /users/{id}', {
|
|
336
|
-
params: { id: '404' },
|
|
337
|
-
}),
|
|
338
|
-
{ wrapper },
|
|
339
|
-
);
|
|
340
|
-
|
|
341
|
-
await waitFor(() => {
|
|
342
|
-
expect(result.current.isError).toBe(true);
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
expect(result.current.error?.status).toBe(404);
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
it('should handle authorization errors', async () => {
|
|
349
|
-
const typedClient = createTypedQueryClient<paths>({
|
|
350
|
-
baseURL: 'https://api.example.com',
|
|
351
|
-
});
|
|
352
|
-
|
|
353
|
-
const { result } = renderHook(
|
|
354
|
-
() => typedClient.useQuery('GET /protected'),
|
|
355
|
-
{ wrapper },
|
|
356
|
-
);
|
|
357
|
-
|
|
358
|
-
await waitFor(() => {
|
|
359
|
-
expect(result.current.isError).toBe(true);
|
|
360
|
-
});
|
|
361
|
-
|
|
362
|
-
expect(result.current.error?.status).toBe(401);
|
|
363
|
-
});
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
describe('Memoization', () => {
|
|
367
|
-
it('should not cause unnecessary re-renders when options object is recreated with same values', async () => {
|
|
368
|
-
const typedClient = createTypedQueryClient<paths>({
|
|
369
|
-
baseURL: 'https://api.example.com',
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
// Track API calls using MSW request interception
|
|
373
|
-
let requestCount = 0;
|
|
374
|
-
const { server } = await import('./setup');
|
|
375
|
-
server.use(
|
|
376
|
-
http.get('https://api.example.com/users/:id', () => {
|
|
377
|
-
requestCount++;
|
|
378
|
-
return HttpResponse.json({ id: '123', name: 'John Doe' });
|
|
379
|
-
}),
|
|
380
|
-
);
|
|
381
|
-
|
|
382
|
-
let renderCount = 0;
|
|
383
|
-
|
|
384
|
-
const { rerender } = renderHook(
|
|
385
|
-
({ options }: { options?: { enabled?: boolean } }) => {
|
|
386
|
-
renderCount++;
|
|
387
|
-
return typedClient.useQuery(
|
|
388
|
-
'GET /users/{id}',
|
|
389
|
-
{ params: { id: '123' } },
|
|
390
|
-
options,
|
|
391
|
-
);
|
|
392
|
-
},
|
|
393
|
-
{
|
|
394
|
-
wrapper,
|
|
395
|
-
initialProps: { options: { enabled: true } },
|
|
396
|
-
},
|
|
397
|
-
);
|
|
398
|
-
|
|
399
|
-
await waitFor(() => {
|
|
400
|
-
expect(requestCount).toBe(1);
|
|
401
|
-
});
|
|
402
|
-
|
|
403
|
-
const initialRenderCount = renderCount;
|
|
404
|
-
|
|
405
|
-
// Rerender with a new options object that has the same values
|
|
406
|
-
rerender({ options: { enabled: true } });
|
|
407
|
-
|
|
408
|
-
// Give React time to potentially trigger re-renders
|
|
409
|
-
await act(async () => {
|
|
410
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
411
|
-
});
|
|
412
|
-
|
|
413
|
-
// The API should not be called again because options haven't actually changed
|
|
414
|
-
expect(requestCount).toBe(1);
|
|
415
|
-
|
|
416
|
-
// Component should re-render due to props change, but React Query shouldn't refetch
|
|
417
|
-
expect(renderCount).toBeGreaterThan(initialRenderCount);
|
|
418
|
-
});
|
|
419
|
-
|
|
420
|
-
it('should re-fetch when options actually change', async () => {
|
|
421
|
-
const typedClient = createTypedQueryClient<paths>({
|
|
422
|
-
baseURL: 'https://api.example.com',
|
|
423
|
-
});
|
|
424
|
-
|
|
425
|
-
// Track API calls using MSW request interception
|
|
426
|
-
let requestCount = 0;
|
|
427
|
-
const { server } = await import('./setup');
|
|
428
|
-
server.use(
|
|
429
|
-
http.get('https://api.example.com/users/:id', () => {
|
|
430
|
-
requestCount++;
|
|
431
|
-
return HttpResponse.json({ id: '123', name: 'John Doe' });
|
|
432
|
-
}),
|
|
433
|
-
);
|
|
434
|
-
|
|
435
|
-
const { rerender } = renderHook(
|
|
436
|
-
({ enabled }: { enabled: boolean }) =>
|
|
437
|
-
typedClient.useQuery(
|
|
438
|
-
'GET /users/{id}',
|
|
439
|
-
{ params: { id: '123' } },
|
|
440
|
-
{ enabled },
|
|
441
|
-
),
|
|
442
|
-
{
|
|
443
|
-
wrapper,
|
|
444
|
-
initialProps: { enabled: true },
|
|
445
|
-
},
|
|
446
|
-
);
|
|
447
|
-
|
|
448
|
-
await waitFor(() => {
|
|
449
|
-
expect(requestCount).toBe(1);
|
|
450
|
-
});
|
|
451
|
-
|
|
452
|
-
// Disable the query
|
|
453
|
-
rerender({ enabled: false });
|
|
454
|
-
|
|
455
|
-
await act(async () => {
|
|
456
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
457
|
-
});
|
|
458
|
-
|
|
459
|
-
// Should still only be called once since we disabled it
|
|
460
|
-
expect(requestCount).toBe(1);
|
|
461
|
-
|
|
462
|
-
// Re-enable the query
|
|
463
|
-
rerender({ enabled: true });
|
|
464
|
-
|
|
465
|
-
await waitFor(() => {
|
|
466
|
-
expect(requestCount).toBe(2);
|
|
467
|
-
});
|
|
468
|
-
});
|
|
469
|
-
|
|
470
|
-
it('should memoize mutation options correctly', async () => {
|
|
471
|
-
const typedClient = createTypedQueryClient<paths>({
|
|
472
|
-
baseURL: 'https://api.example.com',
|
|
473
|
-
});
|
|
474
|
-
|
|
475
|
-
// Track API calls using MSW request interception
|
|
476
|
-
let requestCount = 0;
|
|
477
|
-
const { server } = await import('./setup');
|
|
478
|
-
server.use(
|
|
479
|
-
http.post('https://api.example.com/users', async ({ request }) => {
|
|
480
|
-
requestCount++;
|
|
481
|
-
const body = await request.json();
|
|
482
|
-
return HttpResponse.json({ id: '123', name: (body as any).name });
|
|
483
|
-
}),
|
|
484
|
-
);
|
|
485
|
-
|
|
486
|
-
let renderCount = 0;
|
|
487
|
-
let successCallCount = 0;
|
|
488
|
-
|
|
489
|
-
const { result, rerender } = renderHook(
|
|
490
|
-
({ options }: { options?: { onSuccess?: () => void } }) => {
|
|
491
|
-
renderCount++;
|
|
492
|
-
return typedClient.useMutation('POST /users', options);
|
|
493
|
-
},
|
|
494
|
-
{
|
|
495
|
-
wrapper,
|
|
496
|
-
initialProps: {
|
|
497
|
-
options: {
|
|
498
|
-
onSuccess: () => {
|
|
499
|
-
successCallCount++;
|
|
500
|
-
},
|
|
501
|
-
},
|
|
502
|
-
},
|
|
503
|
-
},
|
|
504
|
-
);
|
|
505
|
-
|
|
506
|
-
const initialRenderCount = renderCount;
|
|
507
|
-
|
|
508
|
-
// Trigger a mutation to test the options
|
|
509
|
-
await act(async () => {
|
|
510
|
-
result.current.mutate({
|
|
511
|
-
body: { name: 'Test User', email: 'test@example.com' },
|
|
512
|
-
});
|
|
513
|
-
});
|
|
514
|
-
|
|
515
|
-
await waitFor(() => {
|
|
516
|
-
expect(requestCount).toBe(1);
|
|
517
|
-
expect(successCallCount).toBe(1);
|
|
518
|
-
});
|
|
519
|
-
|
|
520
|
-
// Rerender with a new options object that has the same function reference
|
|
521
|
-
const onSuccess = () => {
|
|
522
|
-
successCallCount++;
|
|
523
|
-
};
|
|
524
|
-
rerender({ options: { onSuccess } });
|
|
525
|
-
rerender({ options: { onSuccess } });
|
|
526
|
-
|
|
527
|
-
// Component should re-render due to props change
|
|
528
|
-
expect(renderCount).toBeGreaterThan(initialRenderCount);
|
|
529
|
-
});
|
|
530
|
-
|
|
531
|
-
it('should handle config changes in useQuery correctly', async () => {
|
|
532
|
-
const typedClient = createTypedQueryClient<paths>({
|
|
533
|
-
baseURL: 'https://api.example.com',
|
|
534
|
-
});
|
|
535
|
-
|
|
536
|
-
// Track API calls using MSW request interception
|
|
537
|
-
let requestCount = 0;
|
|
538
|
-
const requestedIds: string[] = [];
|
|
539
|
-
const { server } = await import('./setup');
|
|
540
|
-
server.use(
|
|
541
|
-
http.get('https://api.example.com/users/:id', ({ params }) => {
|
|
542
|
-
requestCount++;
|
|
543
|
-
const { id } = params;
|
|
544
|
-
requestedIds.push(id as string);
|
|
545
|
-
return HttpResponse.json({ id, name: `User ${id}` });
|
|
546
|
-
}),
|
|
547
|
-
);
|
|
548
|
-
|
|
549
|
-
const { rerender } = renderHook(
|
|
550
|
-
({ userId }: { userId: string }) =>
|
|
551
|
-
typedClient.useQuery('GET /users/{id}', { params: { id: userId } }),
|
|
552
|
-
{
|
|
553
|
-
wrapper,
|
|
554
|
-
initialProps: { userId: '123' },
|
|
555
|
-
},
|
|
556
|
-
);
|
|
557
|
-
|
|
558
|
-
await waitFor(() => {
|
|
559
|
-
expect(requestCount).toBe(1);
|
|
560
|
-
expect(requestedIds).toEqual(['123']);
|
|
561
|
-
});
|
|
562
|
-
|
|
563
|
-
// Change the user ID - should trigger a new fetch
|
|
564
|
-
rerender({ userId: '456' });
|
|
565
|
-
|
|
566
|
-
await waitFor(() => {
|
|
567
|
-
expect(requestCount).toBe(2);
|
|
568
|
-
expect(requestedIds).toEqual(['123', '456']);
|
|
569
|
-
});
|
|
570
|
-
|
|
571
|
-
// Call with same ID again - should not trigger new fetch due to React Query caching
|
|
572
|
-
rerender({ userId: '456' });
|
|
573
|
-
|
|
574
|
-
await act(async () => {
|
|
575
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
576
|
-
});
|
|
577
|
-
|
|
578
|
-
expect(requestCount).toBe(2);
|
|
579
|
-
expect(requestedIds).toEqual(['123', '456']);
|
|
580
|
-
});
|
|
581
|
-
});
|
|
582
|
-
});
|