diff --git a/packages/preact-query/src/__tests__/useMutation.test.tsx b/packages/preact-query/src/__tests__/useMutation.test.tsx index 48ed686fb6..0fdfd6adb8 100644 --- a/packages/preact-query/src/__tests__/useMutation.test.tsx +++ b/packages/preact-query/src/__tests__/useMutation.test.tsx @@ -2207,4 +2207,144 @@ describe('useMutation', () => { expect(rendered.getByText('items: item1, item2, item3')).toBeInTheDocument() expect(rendered.getByText('message: rollback')).toBeInTheDocument() }) + + it('should be able to run multiple mutateAsync calls in parallel with Promise.all', async () => { + function Page() { + const [result, setResult] = useState('idle') + + const { mutateAsync } = useMutation({ + mutationFn: (file: string) => sleep(10).then(() => `uploaded: ${file}`), + }) + + return ( +
+ +
result: {result}
+
+ ) + } + + const rendered = renderWithClient(queryClient, ) + + fireEvent.click(rendered.getByRole('button', { name: /upload all/i })) + await vi.advanceTimersByTimeAsync(11) + + expect( + rendered.getByText( + 'result: uploaded: file1, uploaded: file2, uploaded: file3', + ), + ).toBeInTheDocument() + }) + + it('should handle Promise.all rejection when one parallel mutateAsync call fails', async () => { + function Page() { + const [result, setResult] = useState('idle') + + const { mutateAsync } = useMutation({ + mutationFn: async (file: string) => { + await sleep(10) + if (file === 'file2') { + throw new Error('upload failed') + } + return `uploaded: ${file}` + }, + retry: false, + }) + + return ( +
+ +
result: {result}
+
+ ) + } + + const rendered = renderWithClient(queryClient, ) + + fireEvent.click(rendered.getByRole('button', { name: /upload all/i })) + await vi.advanceTimersByTimeAsync(11) + + expect( + rendered.getByText('result: error: upload failed'), + ).toBeInTheDocument() + }) + + it('should handle partial failure in parallel mutateAsync calls with Promise.allSettled', async () => { + function Page() { + const [result, setResult] = useState('idle') + + const { mutateAsync } = useMutation({ + mutationFn: async (file: string) => { + await sleep(10) + if (file === 'file2') { + throw new Error('upload failed') + } + return `uploaded: ${file}` + }, + retry: false, + }) + + return ( +
+ +
result: {result}
+
+ ) + } + + const rendered = renderWithClient(queryClient, ) + + fireEvent.click(rendered.getByRole('button', { name: /upload all/i })) + await vi.advanceTimersByTimeAsync(11) + + expect( + rendered.getByText( + 'result: uploaded: file1, error: upload failed, uploaded: file3', + ), + ).toBeInTheDocument() + }) }) diff --git a/packages/react-query/src/__tests__/useMutation.test.tsx b/packages/react-query/src/__tests__/useMutation.test.tsx index 8fbf481e98..447b729c53 100644 --- a/packages/react-query/src/__tests__/useMutation.test.tsx +++ b/packages/react-query/src/__tests__/useMutation.test.tsx @@ -2206,4 +2206,144 @@ describe('useMutation', () => { expect(rendered.getByText('items: item1, item2, item3')).toBeInTheDocument() expect(rendered.getByText('message: rollback')).toBeInTheDocument() }) + + it('should be able to run multiple mutateAsync calls in parallel with Promise.all', async () => { + function Page() { + const [result, setResult] = React.useState('idle') + + const { mutateAsync } = useMutation({ + mutationFn: (file: string) => sleep(10).then(() => `uploaded: ${file}`), + }) + + return ( +
+ +
result: {result}
+
+ ) + } + + const rendered = renderWithClient(queryClient, ) + + fireEvent.click(rendered.getByRole('button', { name: /upload all/i })) + await vi.advanceTimersByTimeAsync(11) + + expect( + rendered.getByText( + 'result: uploaded: file1, uploaded: file2, uploaded: file3', + ), + ).toBeInTheDocument() + }) + + it('should handle Promise.all rejection when one parallel mutateAsync call fails', async () => { + function Page() { + const [result, setResult] = React.useState('idle') + + const { mutateAsync } = useMutation({ + mutationFn: async (file: string) => { + await sleep(10) + if (file === 'file2') { + throw new Error('upload failed') + } + return `uploaded: ${file}` + }, + retry: false, + }) + + return ( +
+ +
result: {result}
+
+ ) + } + + const rendered = renderWithClient(queryClient, ) + + fireEvent.click(rendered.getByRole('button', { name: /upload all/i })) + await vi.advanceTimersByTimeAsync(11) + + expect( + rendered.getByText('result: error: upload failed'), + ).toBeInTheDocument() + }) + + it('should handle partial failure in parallel mutateAsync calls with Promise.allSettled', async () => { + function Page() { + const [result, setResult] = React.useState('idle') + + const { mutateAsync } = useMutation({ + mutationFn: async (file: string) => { + await sleep(10) + if (file === 'file2') { + throw new Error('upload failed') + } + return `uploaded: ${file}` + }, + retry: false, + }) + + return ( +
+ +
result: {result}
+
+ ) + } + + const rendered = renderWithClient(queryClient, ) + + fireEvent.click(rendered.getByRole('button', { name: /upload all/i })) + await vi.advanceTimersByTimeAsync(11) + + expect( + rendered.getByText( + 'result: uploaded: file1, error: upload failed, uploaded: file3', + ), + ).toBeInTheDocument() + }) })