diff --git a/packages/preact-query/src/__tests__/useMutation.test.tsx b/packages/preact-query/src/__tests__/useMutation.test.tsx index ff95c827e34..ede7c346225 100644 --- a/packages/preact-query/src/__tests__/useMutation.test.tsx +++ b/packages/preact-query/src/__tests__/useMutation.test.tsx @@ -100,6 +100,332 @@ describe('useMutation', () => { expect(queryByRole('heading')).toBeNull() }) + it('should call mutate callbacks when useMutation has no callbacks', async () => { + const callbacks: Array = [] + + function Page() { + const { mutate } = useMutation({ + mutationFn: (text: string) => sleep(10).then(() => text), + }) + + return ( + + ) + } + + const rendered = renderWithClient(queryClient, ) + + fireEvent.click(rendered.getByRole('button', { name: /mutate/i })) + await vi.advanceTimersByTimeAsync(10) + + expect(callbacks).toEqual(['mutate.onSuccess', 'mutate.onSettled']) + }) + + it('should call mutateAsync callbacks when useMutation has no callbacks', async () => { + const callbacks: Array = [] + + function Page() { + const { mutateAsync } = useMutation({ + mutationFn: (text: string) => sleep(10).then(() => text), + }) + + useEffect(() => { + setActTimeout(async () => { + await mutateAsync('todo', { + onSuccess: () => { + callbacks.push('mutateAsync.onSuccess') + }, + onSettled: () => { + callbacks.push('mutateAsync.onSettled') + }, + }) + }, 0) + }, [mutateAsync]) + + return null + } + + renderWithClient(queryClient, ) + + await vi.advanceTimersByTimeAsync(10) + + expect(callbacks).toEqual([ + 'mutateAsync.onSuccess', + 'mutateAsync.onSettled', + ]) + }) + + it('should call mutate error callbacks when useMutation has no callbacks', async () => { + const callbacks: Array = [] + + function Page() { + const { mutate } = useMutation({ + mutationFn: (_text: string) => + sleep(10).then(() => { + throw new Error('oops') + }), + }) + + return ( + + ) + } + + const rendered = renderWithClient(queryClient, ) + + fireEvent.click(rendered.getByRole('button', { name: /mutate/i })) + await vi.advanceTimersByTimeAsync(10) + + expect(callbacks).toEqual(['mutate.onError', 'mutate.onSettled']) + }) + + it('should call mutateAsync error callbacks when useMutation has no callbacks', async () => { + const callbacks: Array = [] + + function Page() { + const { mutateAsync } = useMutation({ + mutationFn: async (_text: string) => + sleep(10).then(() => { + throw new Error('oops') + }), + }) + + useEffect(() => { + setActTimeout(async () => { + try { + await mutateAsync('todo', { + onError: () => { + callbacks.push('mutateAsync.onError') + }, + onSettled: () => { + callbacks.push('mutateAsync.onSettled') + }, + }) + } catch {} + }, 0) + }, [mutateAsync]) + + return null + } + + renderWithClient(queryClient, ) + + await vi.advanceTimersByTimeAsync(10) + + expect(callbacks).toEqual(['mutateAsync.onError', 'mutateAsync.onSettled']) + }) + + it('should call only mutate onSuccess when useMutation has no callbacks', async () => { + const callbacks: Array = [] + + function Page() { + const { mutate } = useMutation({ + mutationFn: (text: string) => sleep(10).then(() => text), + }) + + return ( + + ) + } + + const rendered = renderWithClient(queryClient, ) + + fireEvent.click(rendered.getByRole('button', { name: /mutate/i })) + await vi.advanceTimersByTimeAsync(10) + + expect(callbacks).toEqual(['mutate.onSuccess']) + }) + + it('should call only mutate onError when useMutation has no callbacks', async () => { + const callbacks: Array = [] + + function Page() { + const { mutate } = useMutation({ + mutationFn: (_text: string) => + sleep(10).then(() => { + throw new Error('oops') + }), + }) + + return ( + + ) + } + + const rendered = renderWithClient(queryClient, ) + + fireEvent.click(rendered.getByRole('button', { name: /mutate/i })) + await vi.advanceTimersByTimeAsync(10) + + expect(callbacks).toEqual(['mutate.onError']) + }) + + it('should call only mutate onSettled when useMutation has no callbacks', async () => { + const callbacks: Array = [] + + function Page() { + const { mutate } = useMutation({ + mutationFn: (text: string) => sleep(10).then(() => text), + }) + + return ( + + ) + } + + const rendered = renderWithClient(queryClient, ) + + fireEvent.click(rendered.getByRole('button', { name: /mutate/i })) + await vi.advanceTimersByTimeAsync(10) + + expect(callbacks).toEqual(['mutate.onSettled']) + }) + + it('should call only mutateAsync onSuccess when useMutation has no callbacks', async () => { + const callbacks: Array = [] + + function Page() { + const { mutateAsync } = useMutation({ + mutationFn: (text: string) => sleep(10).then(() => text), + }) + + useEffect(() => { + setActTimeout(async () => { + await mutateAsync('todo', { + onSuccess: () => { + callbacks.push('mutateAsync.onSuccess') + }, + }) + }, 0) + }, [mutateAsync]) + + return null + } + + renderWithClient(queryClient, ) + + await vi.advanceTimersByTimeAsync(10) + + expect(callbacks).toEqual(['mutateAsync.onSuccess']) + }) + + it('should call only mutateAsync onError when useMutation has no callbacks', async () => { + const callbacks: Array = [] + + function Page() { + const { mutateAsync } = useMutation({ + mutationFn: async (_text: string) => + sleep(10).then(() => { + throw new Error('oops') + }), + }) + + useEffect(() => { + setActTimeout(async () => { + try { + await mutateAsync('todo', { + onError: () => { + callbacks.push('mutateAsync.onError') + }, + }) + } catch {} + }, 0) + }, [mutateAsync]) + + return null + } + + renderWithClient(queryClient, ) + + await vi.advanceTimersByTimeAsync(10) + + expect(callbacks).toEqual(['mutateAsync.onError']) + }) + + it('should call only mutateAsync onSettled when useMutation has no callbacks', async () => { + const callbacks: Array = [] + + function Page() { + const { mutateAsync } = useMutation({ + mutationFn: (text: string) => sleep(10).then(() => text), + }) + + useEffect(() => { + setActTimeout(async () => { + await mutateAsync('todo', { + onSettled: () => { + callbacks.push('mutateAsync.onSettled') + }, + }) + }, 0) + }, [mutateAsync]) + + return null + } + + renderWithClient(queryClient, ) + + await vi.advanceTimersByTimeAsync(10) + + expect(callbacks).toEqual(['mutateAsync.onSettled']) + }) + it('should be able to call `onSuccess` and `onSettled` after each successful mutate', async () => { let count = 0 const onSuccessMock = vi.fn() diff --git a/packages/react-query/src/__tests__/useMutation.test.tsx b/packages/react-query/src/__tests__/useMutation.test.tsx index 4e077e89576..211bcf06af7 100644 --- a/packages/react-query/src/__tests__/useMutation.test.tsx +++ b/packages/react-query/src/__tests__/useMutation.test.tsx @@ -99,6 +99,332 @@ describe('useMutation', () => { expect(queryByRole('heading')).toBeNull() }) + it('should call mutate callbacks when useMutation has no callbacks', async () => { + const callbacks: Array = [] + + function Page() { + const { mutate } = useMutation({ + mutationFn: (text: string) => sleep(10).then(() => text), + }) + + return ( + + ) + } + + const rendered = renderWithClient(queryClient, ) + + fireEvent.click(rendered.getByRole('button', { name: /mutate/i })) + await vi.advanceTimersByTimeAsync(10) + + expect(callbacks).toEqual(['mutate.onSuccess', 'mutate.onSettled']) + }) + + it('should call mutateAsync callbacks when useMutation has no callbacks', async () => { + const callbacks: Array = [] + + function Page() { + const { mutateAsync } = useMutation({ + mutationFn: (text: string) => sleep(10).then(() => text), + }) + + React.useEffect(() => { + setActTimeout(async () => { + await mutateAsync('todo', { + onSuccess: () => { + callbacks.push('mutateAsync.onSuccess') + }, + onSettled: () => { + callbacks.push('mutateAsync.onSettled') + }, + }) + }, 0) + }, [mutateAsync]) + + return null + } + + renderWithClient(queryClient, ) + + await vi.advanceTimersByTimeAsync(10) + + expect(callbacks).toEqual([ + 'mutateAsync.onSuccess', + 'mutateAsync.onSettled', + ]) + }) + + it('should call mutate error callbacks when useMutation has no callbacks', async () => { + const callbacks: Array = [] + + function Page() { + const { mutate } = useMutation({ + mutationFn: (_text: string) => + sleep(10).then(() => { + throw new Error('oops') + }), + }) + + return ( + + ) + } + + const rendered = renderWithClient(queryClient, ) + + fireEvent.click(rendered.getByRole('button', { name: /mutate/i })) + await vi.advanceTimersByTimeAsync(10) + + expect(callbacks).toEqual(['mutate.onError', 'mutate.onSettled']) + }) + + it('should call mutateAsync error callbacks when useMutation has no callbacks', async () => { + const callbacks: Array = [] + + function Page() { + const { mutateAsync } = useMutation({ + mutationFn: async (_text: string) => + sleep(10).then(() => { + throw new Error('oops') + }), + }) + + React.useEffect(() => { + setActTimeout(async () => { + try { + await mutateAsync('todo', { + onError: () => { + callbacks.push('mutateAsync.onError') + }, + onSettled: () => { + callbacks.push('mutateAsync.onSettled') + }, + }) + } catch {} + }, 0) + }, [mutateAsync]) + + return null + } + + renderWithClient(queryClient, ) + + await vi.advanceTimersByTimeAsync(10) + + expect(callbacks).toEqual(['mutateAsync.onError', 'mutateAsync.onSettled']) + }) + + it('should call only mutate onSuccess when useMutation has no callbacks', async () => { + const callbacks: Array = [] + + function Page() { + const { mutate } = useMutation({ + mutationFn: (text: string) => sleep(10).then(() => text), + }) + + return ( + + ) + } + + const rendered = renderWithClient(queryClient, ) + + fireEvent.click(rendered.getByRole('button', { name: /mutate/i })) + await vi.advanceTimersByTimeAsync(10) + + expect(callbacks).toEqual(['mutate.onSuccess']) + }) + + it('should call only mutate onError when useMutation has no callbacks', async () => { + const callbacks: Array = [] + + function Page() { + const { mutate } = useMutation({ + mutationFn: (_text: string) => + sleep(10).then(() => { + throw new Error('oops') + }), + }) + + return ( + + ) + } + + const rendered = renderWithClient(queryClient, ) + + fireEvent.click(rendered.getByRole('button', { name: /mutate/i })) + await vi.advanceTimersByTimeAsync(10) + + expect(callbacks).toEqual(['mutate.onError']) + }) + + it('should call only mutate onSettled when useMutation has no callbacks', async () => { + const callbacks: Array = [] + + function Page() { + const { mutate } = useMutation({ + mutationFn: (text: string) => sleep(10).then(() => text), + }) + + return ( + + ) + } + + const rendered = renderWithClient(queryClient, ) + + fireEvent.click(rendered.getByRole('button', { name: /mutate/i })) + await vi.advanceTimersByTimeAsync(10) + + expect(callbacks).toEqual(['mutate.onSettled']) + }) + + it('should call only mutateAsync onSuccess when useMutation has no callbacks', async () => { + const callbacks: Array = [] + + function Page() { + const { mutateAsync } = useMutation({ + mutationFn: (text: string) => sleep(10).then(() => text), + }) + + React.useEffect(() => { + setActTimeout(async () => { + await mutateAsync('todo', { + onSuccess: () => { + callbacks.push('mutateAsync.onSuccess') + }, + }) + }, 0) + }, [mutateAsync]) + + return null + } + + renderWithClient(queryClient, ) + + await vi.advanceTimersByTimeAsync(10) + + expect(callbacks).toEqual(['mutateAsync.onSuccess']) + }) + + it('should call only mutateAsync onError when useMutation has no callbacks', async () => { + const callbacks: Array = [] + + function Page() { + const { mutateAsync } = useMutation({ + mutationFn: async (_text: string) => + sleep(10).then(() => { + throw new Error('oops') + }), + }) + + React.useEffect(() => { + setActTimeout(async () => { + try { + await mutateAsync('todo', { + onError: () => { + callbacks.push('mutateAsync.onError') + }, + }) + } catch {} + }, 0) + }, [mutateAsync]) + + return null + } + + renderWithClient(queryClient, ) + + await vi.advanceTimersByTimeAsync(10) + + expect(callbacks).toEqual(['mutateAsync.onError']) + }) + + it('should call only mutateAsync onSettled when useMutation has no callbacks', async () => { + const callbacks: Array = [] + + function Page() { + const { mutateAsync } = useMutation({ + mutationFn: (text: string) => sleep(10).then(() => text), + }) + + React.useEffect(() => { + setActTimeout(async () => { + await mutateAsync('todo', { + onSettled: () => { + callbacks.push('mutateAsync.onSettled') + }, + }) + }, 0) + }, [mutateAsync]) + + return null + } + + renderWithClient(queryClient, ) + + await vi.advanceTimersByTimeAsync(10) + + expect(callbacks).toEqual(['mutateAsync.onSettled']) + }) + it('should be able to call `onSuccess` and `onSettled` after each successful mutate', async () => { let count = 0 const onSuccessMock = vi.fn()