From 5b3afb5c9c5f9cfe7b74d75afa63d1f5559a1896 Mon Sep 17 00:00:00 2001 From: Kevin Bonnoron <2421321+KevinBonnoron@users.noreply.github.com> Date: Thu, 23 Oct 2025 12:01:04 +0000 Subject: [PATCH 1/3] feat(mutation): pass id to use mutation function and allow sync & async mutate --- examples/useDeleteMutation-example.tsx | 39 ++--- examples/useUpdateMutation-example.tsx | 4 +- src/hooks/useDeleteMutation.ts | 33 ++-- src/hooks/useUpdateMutation.ts | 30 +++- src/types/useDeleteMutation.type.ts | 12 +- src/types/useUpdateMutation.type.ts | 13 +- tests/hooks/useDeleteMutation.test.tsx | 191 +++++++++++++++-------- tests/hooks/useUpdateMutation.test.tsx | 200 +++++++++++++++++-------- 8 files changed, 360 insertions(+), 162 deletions(-) diff --git a/examples/useDeleteMutation-example.tsx b/examples/useDeleteMutation-example.tsx index 561c708..3add757 100644 --- a/examples/useDeleteMutation-example.tsx +++ b/examples/useDeleteMutation-example.tsx @@ -13,24 +13,26 @@ function App() { } function DeleteMutationExample() { - const { mutate: deletePost, isPending, isSuccess, error } = useDeleteMutation('posts'); const { data: posts } = useCollection('posts', { perPage: 10 }); const [deletingId, setDeletingId] = useState(null); + const { mutateAsync: deletePost, isPending, isSuccess, error } = useDeleteMutation('posts', deletingId); - const handleDelete = async (id: string) => { - if (window.confirm('Are you sure you want to delete this post?')) { - setDeletingId(id); - try { - const success = await deletePost(id); - if (success) { - console.log('Post deleted successfully'); - } - } catch (err) { - console.error('Failed to delete post:', err); - } finally { - setDeletingId(null); - } + const handleDelete = async (postId: string) => { + setDeletingId(postId); + try { + await deletePost(); + console.log('Post deleted successfully'); + setDeletingId(null); + } catch (err) { + console.error('Failed to delete post:', err); + setDeletingId(null); + } + }; + + const confirmDelete = (post: RecordModel) => { + if (window.confirm(`Are you sure you want to delete "${post.title}"?`)) { + handleDelete(post.id); } }; @@ -53,15 +55,14 @@ function DeleteMutationExample() {
{isSuccess &&

Post created successfully!

} + {isError && error &&

Error: {error}

}
diff --git a/examples/useDeleteMutation-example.tsx b/examples/useDeleteMutation-example.tsx index 3add757..d879bfe 100644 --- a/examples/useDeleteMutation-example.tsx +++ b/examples/useDeleteMutation-example.tsx @@ -16,16 +16,20 @@ function DeleteMutationExample() { const { data: posts } = useCollection('posts', { perPage: 10 }); const [deletingId, setDeletingId] = useState(null); - const { mutateAsync: deletePost, isPending, isSuccess, error } = useDeleteMutation('posts', deletingId); + const { mutateAsync: deletePost, isPending, isSuccess, isError, error } = useDeleteMutation('posts', deletingId); const handleDelete = async (postId: string) => { + if (isPending) { + return; + } + setDeletingId(postId); try { await deletePost(); console.log('Post deleted successfully'); - setDeletingId(null); } catch (err) { console.error('Failed to delete post:', err); + } finally { setDeletingId(null); } }; @@ -36,7 +40,7 @@ function DeleteMutationExample() { } }; - if (error) return
Error: {error}
; + if (isError) return
Error: {error}
; return (
@@ -56,16 +60,16 @@ function DeleteMutationExample() {
diff --git a/examples/useUpdateMutation-example.tsx b/examples/useUpdateMutation-example.tsx index a656f67..267b599 100644 --- a/examples/useUpdateMutation-example.tsx +++ b/examples/useUpdateMutation-example.tsx @@ -16,7 +16,7 @@ function UpdateMutationExample() { const { data: posts } = useCollection('posts', { perPage: 10 }); const [editingId, setEditingId] = useState(null); - const { mutateAsync: updatePost, isPending, isSuccess, error } = useUpdateMutation('posts', editingId); + const { mutateAsync: updatePost, isPending, isSuccess, isError, error } = useUpdateMutation('posts', editingId); const [editTitle, setEditTitle] = useState(''); const [editContent, setEditContent] = useState(''); const [editStatus, setEditStatus] = useState<'draft' | 'published'>('draft'); @@ -53,8 +53,6 @@ function UpdateMutationExample() { setEditContent(''); }; - if (error) return
Error: {error}
; - return (

Update Posts

@@ -81,6 +79,7 @@ function UpdateMutationExample() { Cancel {isSuccess &&

Post updated successfully!

} + {isError && error &&

Error: {error}

} )} diff --git a/src/hooks/useCreateMutation.ts b/src/hooks/useCreateMutation.ts index 0b40285..66f6bc9 100644 --- a/src/hooks/useCreateMutation.ts +++ b/src/hooks/useCreateMutation.ts @@ -12,12 +12,14 @@ import { usePocketBase } from './usePocketBase'; * * @example * ```tsx - * const { mutate, isPending, isSuccess, error } = useCreateMutation('posts'); + * const { mutateAsync, isPending, isSuccess, isError, error } = useCreateMutation('posts'); * * const handleCreate = async () => { - * const newPost = await mutate({ title: 'Hello', content: 'World' }); - * if (newPost) { + * try { + * const newPost = await mutateAsync({ title: 'Hello', content: 'World' }); * console.log('Created:', newPost); + * } catch (err) { + * console.error('Failed to create post:', err); * } * }; * ``` @@ -28,16 +30,17 @@ export function useCreateMutation(collectionName: st const [isPending, setIsPending] = useState(false); const [error, setError] = useState(null); - const mutate = useCallback( - async (bodyParams: Partial, options?: RecordOptions): Promise => { + const mutateAsync = useCallback( + async (bodyParams: Partial, options?: RecordOptions): Promise => { try { setIsPending(true); setError(null); - const record = await recordService.create(bodyParams, options); + const record = options ? await recordService.create(bodyParams, options) : await recordService.create(bodyParams); return record as Record; } catch (err) { - setError(err instanceof Error ? err.message : 'Error creating record'); - return null; + const errorMessage = err instanceof Error ? err.message : 'Error creating record'; + setError(errorMessage); + throw new Error(errorMessage); } finally { setIsPending(false); } @@ -45,13 +48,24 @@ export function useCreateMutation(collectionName: st [recordService], ); + const mutate = useCallback( + (bodyParams: Partial, options?: RecordOptions): void => { + mutateAsync(bodyParams, options).catch(() => { + // Error is already handled in mutateAsync + }); + }, + [mutateAsync], + ); + return useMemo( (): UseCreateMutationResult => ({ mutate, + mutateAsync, isPending, + isError: !!error, error, isSuccess: !isPending && !error, }), - [mutate, isPending, error], + [mutate, mutateAsync, isPending, error], ); } diff --git a/src/hooks/useDeleteMutation.ts b/src/hooks/useDeleteMutation.ts index b9d64c0..8397cf1 100644 --- a/src/hooks/useDeleteMutation.ts +++ b/src/hooks/useDeleteMutation.ts @@ -63,6 +63,7 @@ export function useDeleteMutation(coll mutate, mutateAsync, isPending, + isError: !!error, error, isSuccess: !isPending && !error, }), diff --git a/src/hooks/useUpdateMutation.ts b/src/hooks/useUpdateMutation.ts index ddcb864..a179eaa 100644 --- a/src/hooks/useUpdateMutation.ts +++ b/src/hooks/useUpdateMutation.ts @@ -65,6 +65,7 @@ export function useUpdateMutation(collectionName: st mutate, mutateAsync, isPending, + isError: !!error, error, isSuccess: !isPending && !error, }), diff --git a/src/types/index.ts b/src/types/index.ts index b25ee23..86aa6c2 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -3,8 +3,8 @@ export * from './query-result.type'; export * from './record-transformer.type'; export * from './useAuth.type'; export * from './useCollection.type'; -export * from './useCommon.type'; export * from './useCreateMutation.type'; export * from './useDeleteMutation.type'; +export * from './useQueryCommon.type'; export * from './useRecord.type'; export * from './useUpdateMutation.type'; diff --git a/src/types/useCollection.type.ts b/src/types/useCollection.type.ts index bce43c4..964b6bf 100644 --- a/src/types/useCollection.type.ts +++ b/src/types/useCollection.type.ts @@ -1,13 +1,13 @@ import type { RecordModel } from 'pocketbase'; import type { QueryResult } from './query-result.type'; -import type { UseCommonOptions } from './useCommon.type'; +import type { UseQueryCommonOptions } from './useQueryCommon.type'; /** * Options for configuring the useCollection hook. * * @template T - The record type extending RecordModel */ -export interface UseCollectionOptions extends UseCommonOptions { +export interface UseCollectionOptions extends UseQueryCommonOptions { /** * PocketBase filter query (e.g., 'published = true') */ diff --git a/src/types/useCreateMutation.type.ts b/src/types/useCreateMutation.type.ts index e9542af..3e6a8fc 100644 --- a/src/types/useCreateMutation.type.ts +++ b/src/types/useCreateMutation.type.ts @@ -1,31 +1,25 @@ import type { RecordModel, RecordOptions } from 'pocketbase'; +import type { UseMutationCommonOptions } from './useMutationCommon.type'; /** * Result type returned by useCreateMutation hook. * * @template Record - The record type extending RecordModel */ -export interface UseCreateMutationResult { +export interface UseCreateMutationResult extends UseMutationCommonOptions { /** - * Function to create a new record. Returns the created record on success, null on error. + * Function to create a new record synchronously. Triggers the mutation but doesn't wait for completion. * * @param bodyParams - Partial record data to create * @param options - Optional PocketBase record options (expand, fields, etc.) */ - mutate: (bodyParams: Partial, options?: RecordOptions) => Promise; + mutate: (bodyParams: Partial, options?: RecordOptions) => void; /** - * True when the mutation is in progress - */ - isPending: boolean; - - /** - * True when the mutation completed successfully (not pending and no error) - */ - isSuccess: boolean; - - /** - * Error message if the mutation failed, null otherwise + * Function to create a new record asynchronously. Returns a promise that resolves with the created record. + * + * @param bodyParams - Partial record data to create + * @param options - Optional PocketBase record options (expand, fields, etc.) */ - error: string | null; + mutateAsync: (bodyParams: Partial, options?: RecordOptions) => Promise; } diff --git a/src/types/useDeleteMutation.type.ts b/src/types/useDeleteMutation.type.ts index 474f26f..a8f14eb 100644 --- a/src/types/useDeleteMutation.type.ts +++ b/src/types/useDeleteMutation.type.ts @@ -1,9 +1,10 @@ import type { CommonOptions } from 'pocketbase'; +import type { UseMutationCommonOptions } from './useMutationCommon.type'; /** * Result type returned by useDeleteMutation hook. */ -export interface UseDeleteMutationResult { +export interface UseDeleteMutationResult extends UseMutationCommonOptions { /** * Function to delete a record synchronously. Triggers the mutation but doesn't wait for completion. * @@ -17,19 +18,4 @@ export interface UseDeleteMutationResult { * @param options - Optional PocketBase common options (headers, fetch, etc.) */ mutateAsync: (options?: CommonOptions) => Promise; - - /** - * True when the mutation is in progress - */ - isPending: boolean; - - /** - * True when the mutation completed successfully (not pending and no error) - */ - isSuccess: boolean; - - /** - * Error message if the mutation failed, null otherwise - */ - error: string | null; } diff --git a/src/types/useMutationCommon.type.ts b/src/types/useMutationCommon.type.ts new file mode 100644 index 0000000..002b301 --- /dev/null +++ b/src/types/useMutationCommon.type.ts @@ -0,0 +1,21 @@ +export interface UseMutationCommonOptions { + /** + * True when the mutation is in progress + */ + isPending: boolean; + + /** + * True when the mutation completed successfully (not pending and no error) + */ + isSuccess: boolean; + + /** + * True when the mutation failed + */ + isError: boolean; + + /** + * Error message if the mutation failed, null otherwise + */ + error: string | null; +} diff --git a/src/types/useCommon.type.ts b/src/types/useQueryCommon.type.ts similarity index 85% rename from src/types/useCommon.type.ts rename to src/types/useQueryCommon.type.ts index 98ef26e..e70e554 100644 --- a/src/types/useCommon.type.ts +++ b/src/types/useQueryCommon.type.ts @@ -1,7 +1,7 @@ import type { RecordModel } from 'pocketbase'; import type { RecordTransformer } from './record-transformer.type'; -export interface UseCommonOptions { +export interface UseQueryCommonOptions { /** * Expand related records (e.g., 'author,comments') */ diff --git a/src/types/useRecord.type.ts b/src/types/useRecord.type.ts index b116894..d7fff93 100644 --- a/src/types/useRecord.type.ts +++ b/src/types/useRecord.type.ts @@ -1,13 +1,13 @@ import type { RecordModel } from 'pocketbase'; import type { QueryResult } from './query-result.type'; -import type { UseCommonOptions } from './useCommon.type'; +import type { UseQueryCommonOptions } from './useQueryCommon.type'; /** * Options for configuring the useRecord hook. * * @template T - The record type extending RecordModel */ -export interface UseRecordOptions extends UseCommonOptions { +export interface UseRecordOptions extends UseQueryCommonOptions { /** * Default value to use before data is loaded */ diff --git a/src/types/useUpdateMutation.type.ts b/src/types/useUpdateMutation.type.ts index 7f77eef..6d26e0e 100644 --- a/src/types/useUpdateMutation.type.ts +++ b/src/types/useUpdateMutation.type.ts @@ -1,11 +1,12 @@ import type { RecordModel, RecordOptions } from 'pocketbase'; +import type { UseMutationCommonOptions } from './useMutationCommon.type'; /** * Result type returned by useUpdateMutation hook. * * @template Record - The record type extending RecordModel */ -export interface UseUpdateMutationResult { +export interface UseUpdateMutationResult extends UseMutationCommonOptions { /** * Function to update an existing record synchronously. Triggers the mutation but doesn't wait for completion. * @@ -21,19 +22,4 @@ export interface UseUpdateMutationResult { * @param options - Optional PocketBase record options (expand, fields, etc.) */ mutateAsync: (bodyParams: Partial, options?: RecordOptions) => Promise; - - /** - * True when the mutation is in progress - */ - isPending: boolean; - - /** - * True when the mutation completed successfully (not pending and no error) - */ - isSuccess: boolean; - - /** - * Error message if the mutation failed, null otherwise - */ - error: string | null; } diff --git a/tests/hooks/useCreateMutation.test.tsx b/tests/hooks/useCreateMutation.test.tsx index 9118088..97e0a58 100644 --- a/tests/hooks/useCreateMutation.test.tsx +++ b/tests/hooks/useCreateMutation.test.tsx @@ -22,113 +22,183 @@ describe('useCreateMutation', () => { expect(result.current.isPending).toBe(false); expect(result.current.error).toBe(null); expect(result.current.isSuccess).toBe(true); + expect(result.current.isError).toBe(false); expect(typeof result.current.mutate).toBe('function'); + expect(typeof result.current.mutateAsync).toBe('function'); }); - it('should handle create mutation', async () => { - const mockData = { id: '1', title: 'New Item' }; - mockCreate.mockResolvedValue(mockData); + it('should throw error when used outside provider', () => { + expect(() => { + renderHook(() => useCreateMutation('test')); + }).toThrow('usePocketBase must be used within a PocketBaseProvider'); + }); - const wrapper = createWrapper(mockPocketBase); + describe('mutateAsync', () => { + it('should handle successful create', async () => { + const mockData = { id: '1', title: 'New Item' }; + mockCreate.mockResolvedValue(mockData); - const { result } = renderHook(() => useCreateMutation('test'), { wrapper }); + const wrapper = createWrapper(mockPocketBase); - const mutationResult = await result.current.mutate({ - title: 'New Item', - }); + const { result } = renderHook(() => useCreateMutation('test'), { wrapper }); - return waitFor(() => { - expect(mockCreate).toHaveBeenCalledWith( - { - title: 'New Item', - }, - undefined, - ); + const mutationResult = await result.current.mutateAsync({ + title: 'New Item', + }); + + expect(mockCreate).toHaveBeenCalledWith({ title: 'New Item' }); expect(mutationResult).toEqual(mockData); expect(result.current.isPending).toBe(false); expect(result.current.isSuccess).toBe(true); }); - }); - it('should handle create mutation with options', async () => { - const mockData = { id: '1', title: 'New Item' }; - mockCreate.mockResolvedValue(mockData); + it('should handle create with options', async () => { + const mockData = { id: '1', title: 'New Item' }; + mockCreate.mockResolvedValue(mockData); - const wrapper = createWrapper(mockPocketBase); + const wrapper = createWrapper(mockPocketBase); - const { result } = renderHook(() => useCreateMutation('test'), { wrapper }); + const { result } = renderHook(() => useCreateMutation('test'), { wrapper }); - const options = { expand: 'relation' }; + const options = { expand: 'relation' }; + await result.current.mutateAsync({ title: 'New Item' }, options); - await result.current.mutate({ title: 'New Item' }, options); - - return waitFor(() => { expect(mockCreate).toHaveBeenCalledWith({ title: 'New Item' }, options); }); - }); - it('should handle mutation error', async () => { - const mockError = new Error('Create failed'); - mockCreate.mockRejectedValue(mockError); + it('should handle mutation error', async () => { + const mockError = new Error('Create failed'); + mockCreate.mockRejectedValue(mockError); - const wrapper = createWrapper(mockPocketBase); + const wrapper = createWrapper(mockPocketBase); - const { result } = renderHook(() => useCreateMutation('test'), { wrapper }); + const { result } = renderHook(() => useCreateMutation('test'), { wrapper }); - await result.current.mutate({ title: 'Test' }); + await expect(result.current.mutateAsync({ title: 'Test' })).rejects.toThrow('Create failed'); - return waitFor(() => { - expect(result.current.error).toEqual('Create failed'); - expect(result.current.isPending).toBe(false); - expect(result.current.isSuccess).toBe(false); + await waitFor(() => { + expect(result.current.error).toEqual('Create failed'); + expect(result.current.isPending).toBe(false); + expect(result.current.isSuccess).toBe(false); + expect(result.current.isError).toBe(true); + }); }); - }); - it('should handle non-Error exceptions', async () => { - mockCreate.mockRejectedValue('String error'); + it('should handle non-Error exceptions', async () => { + mockCreate.mockRejectedValue('String error'); - const wrapper = createWrapper(mockPocketBase); + const wrapper = createWrapper(mockPocketBase); - const { result } = renderHook(() => useCreateMutation('test'), { wrapper }); + const { result } = renderHook(() => useCreateMutation('test'), { wrapper }); - await result.current.mutate({ title: 'Test' }); + await expect(result.current.mutateAsync({ title: 'Test' })).rejects.toThrow('Error creating record'); - return waitFor(() => { - expect(result.current.error).toBe('Error creating record'); - expect(result.current.isPending).toBe(false); - expect(result.current.isSuccess).toBe(false); + await waitFor(() => { + expect(result.current.error).toBe('Error creating record'); + expect(result.current.isPending).toBe(false); + expect(result.current.isSuccess).toBe(false); + expect(result.current.isError).toBe(true); + }); + }); + + it('should set isPending to true during mutation', async () => { + let resolveCreate: (value: unknown) => void = () => {}; + const createPromise = new Promise((resolve) => { + resolveCreate = resolve; + }); + mockCreate.mockReturnValue(createPromise); + + const wrapper = createWrapper(mockPocketBase); + + const { result } = renderHook(() => useCreateMutation('test'), { wrapper }); + + const mutationPromise = result.current.mutateAsync({ title: 'Test' }); + + await waitFor(() => { + expect(result.current.isPending).toBe(true); + expect(result.current.isSuccess).toBe(false); + }); + + resolveCreate({ id: '1', title: 'Test' }); + await mutationPromise; + + await waitFor(() => { + expect(result.current.isPending).toBe(false); + expect(result.current.isSuccess).toBe(true); + }); }); }); - it('should set isPending to true during mutation', async () => { - let resolveCreate: (value: unknown) => void; - const createPromise = new Promise((resolve) => { - resolveCreate = resolve; + describe('mutate', () => { + it('should handle successful create', async () => { + const mockData = { id: '1', title: 'New Item' }; + mockCreate.mockResolvedValue(mockData); + + const wrapper = createWrapper(mockPocketBase); + + const { result } = renderHook(() => useCreateMutation('test'), { wrapper }); + + const mutateResult = result.current.mutate({ title: 'New Item' }); + expect(mutateResult).toBeUndefined(); + + await waitFor(() => { + expect(mockCreate).toHaveBeenCalledWith({ title: 'New Item' }); + expect(result.current.isPending).toBe(false); + expect(result.current.isSuccess).toBe(true); + }); }); - mockCreate.mockReturnValue(createPromise); - const wrapper = createWrapper(mockPocketBase); + it('should handle create with options', async () => { + const mockData = { id: '1', title: 'New Item' }; + mockCreate.mockResolvedValue(mockData); - const { result } = renderHook(() => useCreateMutation('test'), { wrapper }); + const wrapper = createWrapper(mockPocketBase); - waitFor(() => { - result.current.mutate({ title: 'Test' }); - expect(result.current.isPending).toBe(true); - expect(result.current.isSuccess).toBe(false); - resolveCreate({ id: '1', title: 'Test' }); + const { result } = renderHook(() => useCreateMutation('test'), { wrapper }); + + const options = { expand: 'relation' }; + result.current.mutate({ title: 'New Item' }, options); + + await waitFor(() => { + expect(mockCreate).toHaveBeenCalledWith({ title: 'New Item' }, options); + expect(result.current.isPending).toBe(false); + expect(result.current.isSuccess).toBe(true); + }); }); - await createPromise; + it('should handle mutation error', async () => { + const mockError = new Error('Create failed'); + mockCreate.mockRejectedValue(mockError); - return waitFor(() => { - expect(result.current.isPending).toBe(false); - expect(result.current.isSuccess).toBe(true); + const wrapper = createWrapper(mockPocketBase); + + const { result } = renderHook(() => useCreateMutation('test'), { wrapper }); + + result.current.mutate({ title: 'Test' }); + + await waitFor(() => { + expect(result.current.error).toEqual('Create failed'); + expect(result.current.isPending).toBe(false); + expect(result.current.isSuccess).toBe(false); + expect(result.current.isError).toBe(true); + }); }); - }); - it('should throw error when used outside provider', () => { - expect(() => { - renderHook(() => useCreateMutation('test')); - }).toThrow('usePocketBase must be used within a PocketBaseProvider'); + it('should handle non-Error exceptions', async () => { + mockCreate.mockRejectedValue('String error'); + + const wrapper = createWrapper(mockPocketBase); + + const { result } = renderHook(() => useCreateMutation('test'), { wrapper }); + + result.current.mutate({ title: 'Test' }); + + await waitFor(() => { + expect(result.current.error).toBe('Error creating record'); + expect(result.current.isPending).toBe(false); + expect(result.current.isSuccess).toBe(false); + expect(result.current.isError).toBe(true); + }); + }); }); }); diff --git a/tests/hooks/useDeleteMutation.test.tsx b/tests/hooks/useDeleteMutation.test.tsx index ba81d28..7bdf99c 100644 --- a/tests/hooks/useDeleteMutation.test.tsx +++ b/tests/hooks/useDeleteMutation.test.tsx @@ -22,6 +22,7 @@ describe('useDeleteMutation', () => { expect(result.current.isPending).toBe(false); expect(result.current.error).toBe(null); expect(result.current.isSuccess).toBe(true); + expect(result.current.isError).toBe(false); expect(typeof result.current.mutate).toBe('function'); expect(typeof result.current.mutateAsync).toBe('function'); }); @@ -82,6 +83,7 @@ describe('useDeleteMutation', () => { expect(result.current.error).toEqual('Delete failed'); expect(result.current.isPending).toBe(false); expect(result.current.isSuccess).toBe(false); + expect(result.current.isError).toBe(true); }); }); @@ -98,6 +100,7 @@ describe('useDeleteMutation', () => { expect(result.current.error).toBe('Error deleting record'); expect(result.current.isPending).toBe(false); expect(result.current.isSuccess).toBe(false); + expect(result.current.isError).toBe(true); }); }); @@ -178,6 +181,7 @@ describe('useDeleteMutation', () => { expect(result.current.error).toEqual('Delete failed'); expect(result.current.isPending).toBe(false); expect(result.current.isSuccess).toBe(false); + expect(result.current.isError).toBe(true); }); }); @@ -194,6 +198,7 @@ describe('useDeleteMutation', () => { expect(result.current.error).toBe('Error deleting record'); expect(result.current.isPending).toBe(false); expect(result.current.isSuccess).toBe(false); + expect(result.current.isError).toBe(true); }); }); }); diff --git a/tests/hooks/useUpdateMutation.test.tsx b/tests/hooks/useUpdateMutation.test.tsx index 1e72387..a4e2056 100644 --- a/tests/hooks/useUpdateMutation.test.tsx +++ b/tests/hooks/useUpdateMutation.test.tsx @@ -23,6 +23,7 @@ describe('useUpdateMutation', () => { expect(result.current.isPending).toBe(false); expect(result.current.error).toBe(null); expect(result.current.isSuccess).toBe(true); + expect(result.current.isError).toBe(false); expect(typeof result.current.mutate).toBe('function'); expect(typeof result.current.mutateAsync).toBe('function'); }); @@ -90,6 +91,7 @@ describe('useUpdateMutation', () => { expect(result.current.error).toEqual('Update failed'); expect(result.current.isPending).toBe(false); expect(result.current.isSuccess).toBe(false); + expect(result.current.isError).toBe(true); }); }); @@ -106,6 +108,7 @@ describe('useUpdateMutation', () => { expect(result.current.error).toBe('Error updating record'); expect(result.current.isPending).toBe(false); expect(result.current.isSuccess).toBe(false); + expect(result.current.isError).toBe(true); }); }); @@ -188,6 +191,7 @@ describe('useUpdateMutation', () => { expect(result.current.error).toEqual('Update failed'); expect(result.current.isPending).toBe(false); expect(result.current.isSuccess).toBe(false); + expect(result.current.isError).toBe(true); }); }); @@ -204,6 +208,7 @@ describe('useUpdateMutation', () => { expect(result.current.error).toBe('Error updating record'); expect(result.current.isPending).toBe(false); expect(result.current.isSuccess).toBe(false); + expect(result.current.isError).toBe(true); }); }); });