Skip to content

Commit 2e21ee0

Browse files
committed
feat: add tests for context handling in API client, including error handling and cache operations
1 parent 851a4e8 commit 2e21ee0

1 file changed

Lines changed: 172 additions & 1 deletion

File tree

packages/react-client/src/tests/qraftReactAPIClient.test.tsx

Lines changed: 172 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import type { ReactNode } from 'react';
2-
import { fetchQuery } from '@openapi-qraft/react/callbacks/index';
2+
import {
3+
fetchQuery,
4+
getQueryData,
5+
invalidateQueries,
6+
setQueryData,
7+
useInfiniteQuery,
8+
} from '@openapi-qraft/react/callbacks/index';
39
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
410
import { act, renderHook, waitFor } from '@testing-library/react';
511
import React, { useContext, useEffect, useState } from 'react';
@@ -363,4 +369,169 @@ describe('Create API Client with Context', () => {
363369
});
364370
});
365371
});
372+
373+
describe('error handling', () => {
374+
it('throws error when context is not provided', () => {
375+
const queryClient = new QueryClient();
376+
const api = createInternalReactAPIClient(services);
377+
378+
expect(() => {
379+
renderHook(() => api.files.getFileList.useQuery(), {
380+
wrapper: (props) => (
381+
<QueryClientProvider client={queryClient} {...props} />
382+
),
383+
});
384+
}).toThrow('No API Client context found');
385+
});
386+
});
387+
388+
describe('useInfiniteQuery with context', () => {
389+
it('supports useInfiniteQuery with context-provided options', async () => {
390+
const queryClient = new QueryClient();
391+
const api = createInternalReactAPIClient(services, {
392+
useInfiniteQuery,
393+
});
394+
395+
const { result } = renderHook(
396+
() =>
397+
api.files.getFileList.useInfiniteQuery(undefined, {
398+
initialPageParam: { query: { id__in: ['1'] } },
399+
getNextPageParam: () => undefined,
400+
}),
401+
{
402+
wrapper: (props) => (
403+
<ContextProviders queryClient={queryClient} {...props} />
404+
),
405+
}
406+
);
407+
408+
await waitFor(() => {
409+
expect(result.current.data).toBeDefined();
410+
});
411+
});
412+
});
413+
414+
describe('cache operations with context in mutation callbacks', () => {
415+
it('supports setQueryData and getQueryData with context in mutation callbacks', async () => {
416+
const queryClient = new QueryClient();
417+
const api = createInternalReactAPIClient(services);
418+
419+
const { result } = renderHook(
420+
() => {
421+
const apiContext = useContext(InternalReactAPIClientContext);
422+
423+
return api.files.postFiles.useMutation(undefined, {
424+
async onMutate(variables) {
425+
if (!apiContext) return;
426+
427+
const apiClient = createInternalReactAPIClient(
428+
services,
429+
apiContext,
430+
{
431+
setQueryData,
432+
getQueryData,
433+
}
434+
);
435+
436+
const prevData = apiClient.files.getFileList.getQueryData();
437+
438+
const fileDescription =
439+
variables &&
440+
'body' in variables &&
441+
typeof variables.body === 'object' &&
442+
variables.body !== null &&
443+
!('append' in variables.body) &&
444+
'file_description' in variables.body
445+
? variables.body.file_description
446+
: '';
447+
448+
apiClient.files.getFileList.setQueryData(undefined, (old) => ({
449+
...old,
450+
data: [
451+
...(old?.data || []),
452+
{
453+
id: 'new',
454+
name: fileDescription || 'new-file',
455+
file_type: 'pdf',
456+
url: 'http://localhost/new',
457+
},
458+
],
459+
}));
460+
461+
return { prevData };
462+
},
463+
async onError(_err, _vars, context) {
464+
if (!apiContext) return;
465+
466+
if (context?.prevData) {
467+
const apiClient = createInternalReactAPIClient(
468+
services,
469+
apiContext,
470+
{
471+
setQueryData,
472+
}
473+
);
474+
apiClient.files.getFileList.setQueryData(
475+
undefined,
476+
context.prevData
477+
);
478+
}
479+
},
480+
});
481+
},
482+
{
483+
wrapper: (props) => (
484+
<ContextProviders queryClient={queryClient} {...props} />
485+
),
486+
}
487+
);
488+
489+
act(() => {
490+
result.current.mutate({
491+
body: { file_description: 'Optimistic test file' },
492+
});
493+
});
494+
495+
await waitFor(() => {
496+
expect(result.current.isPending).toBe(false);
497+
expect(result.current.data).toEqual({
498+
body: { file_description: 'Optimistic test file' },
499+
});
500+
});
501+
});
502+
});
503+
504+
describe('invalidateQueries with context', () => {
505+
it('supports invalidateQueries with context-provided queryClient', async () => {
506+
const queryClient = new QueryClient();
507+
const api = createInternalReactAPIClient(services);
508+
509+
const queryKey = api.files.getFileList.getQueryKey();
510+
511+
queryClient.setQueryData(queryKey, filesFindAllResponsePayloadFixtures);
512+
513+
const { result } = renderHook(
514+
() => {
515+
const apiContext = useContext(InternalReactAPIClientContext);
516+
517+
if (!apiContext) return undefined;
518+
519+
const apiClient = createInternalReactAPIClient(services, apiContext, {
520+
invalidateQueries,
521+
});
522+
523+
return apiClient.files.getFileList.invalidateQueries();
524+
},
525+
{
526+
wrapper: (props) => (
527+
<ContextProviders queryClient={queryClient} {...props} />
528+
),
529+
}
530+
);
531+
532+
await waitFor(() => {
533+
expect(result.current).toBeInstanceOf(Promise);
534+
});
535+
});
536+
});
366537
});

0 commit comments

Comments
 (0)