From a9a1f04dc09e21c404af76df7047227e15365dec Mon Sep 17 00:00:00 2001 From: Wonsuk Choi Date: Tue, 14 Apr 2026 09:50:46 +0900 Subject: [PATCH 1/2] test({react,preact}-query/useMutation): add parallel 'mutateAsync' tests with 'Promise.all' and 'Promise.allSettled' --- .../src/__tests__/useMutation.test.tsx | 134 ++++++++++++++++++ .../src/__tests__/useMutation.test.tsx | 134 ++++++++++++++++++ 2 files changed, 268 insertions(+) diff --git a/packages/preact-query/src/__tests__/useMutation.test.tsx b/packages/preact-query/src/__tests__/useMutation.test.tsx index 48ed686fb6..a8aad6efc8 100644 --- a/packages/preact-query/src/__tests__/useMutation.test.tsx +++ b/packages/preact-query/src/__tests__/useMutation.test.tsx @@ -2207,4 +2207,138 @@ 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..a10532090d 100644 --- a/packages/react-query/src/__tests__/useMutation.test.tsx +++ b/packages/react-query/src/__tests__/useMutation.test.tsx @@ -2206,4 +2206,138 @@ 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() + }) }) From 1dcab8dacbb7b69fcc453ae239f2f763c444fffe Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 14 Apr 2026 00:52:50 +0000 Subject: [PATCH 2/2] ci: apply automated fixes --- .../preact-query/src/__tests__/useMutation.test.tsx | 12 +++++++++--- .../react-query/src/__tests__/useMutation.test.tsx | 12 +++++++++--- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/packages/preact-query/src/__tests__/useMutation.test.tsx b/packages/preact-query/src/__tests__/useMutation.test.tsx index a8aad6efc8..0fdfd6adb8 100644 --- a/packages/preact-query/src/__tests__/useMutation.test.tsx +++ b/packages/preact-query/src/__tests__/useMutation.test.tsx @@ -2241,7 +2241,9 @@ describe('useMutation', () => { await vi.advanceTimersByTimeAsync(11) expect( - rendered.getByText('result: uploaded: file1, uploaded: file2, uploaded: file3'), + rendered.getByText( + 'result: uploaded: file1, uploaded: file2, uploaded: file3', + ), ).toBeInTheDocument() }) @@ -2319,7 +2321,9 @@ describe('useMutation', () => { ]) const summary = results .map((r) => - r.status === 'fulfilled' ? r.value : `error: ${r.reason.message}`, + r.status === 'fulfilled' + ? r.value + : `error: ${r.reason.message}`, ) .join(', ') setResult(summary) @@ -2338,7 +2342,9 @@ describe('useMutation', () => { await vi.advanceTimersByTimeAsync(11) expect( - rendered.getByText('result: uploaded: file1, error: upload failed, uploaded: file3'), + 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 a10532090d..447b729c53 100644 --- a/packages/react-query/src/__tests__/useMutation.test.tsx +++ b/packages/react-query/src/__tests__/useMutation.test.tsx @@ -2240,7 +2240,9 @@ describe('useMutation', () => { await vi.advanceTimersByTimeAsync(11) expect( - rendered.getByText('result: uploaded: file1, uploaded: file2, uploaded: file3'), + rendered.getByText( + 'result: uploaded: file1, uploaded: file2, uploaded: file3', + ), ).toBeInTheDocument() }) @@ -2318,7 +2320,9 @@ describe('useMutation', () => { ]) const summary = results .map((r) => - r.status === 'fulfilled' ? r.value : `error: ${r.reason.message}`, + r.status === 'fulfilled' + ? r.value + : `error: ${r.reason.message}`, ) .join(', ') setResult(summary) @@ -2337,7 +2341,9 @@ describe('useMutation', () => { await vi.advanceTimersByTimeAsync(11) expect( - rendered.getByText('result: uploaded: file1, error: upload failed, uploaded: file3'), + rendered.getByText( + 'result: uploaded: file1, error: upload failed, uploaded: file3', + ), ).toBeInTheDocument() }) })