Skip to content

Commit aad1bd5

Browse files
test({react,preact}-query/useMutation): add parallel 'mutateAsync' tests with 'Promise.all' and 'Promise.allSettled' (#10493)
* test({react,preact}-query/useMutation): add parallel 'mutateAsync' tests with 'Promise.all' and 'Promise.allSettled' * ci: apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent d7643b5 commit aad1bd5

File tree

2 files changed

+280
-0
lines changed

2 files changed

+280
-0
lines changed

packages/preact-query/src/__tests__/useMutation.test.tsx

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2207,4 +2207,144 @@ describe('useMutation', () => {
22072207
expect(rendered.getByText('items: item1, item2, item3')).toBeInTheDocument()
22082208
expect(rendered.getByText('message: rollback')).toBeInTheDocument()
22092209
})
2210+
2211+
it('should be able to run multiple mutateAsync calls in parallel with Promise.all', async () => {
2212+
function Page() {
2213+
const [result, setResult] = useState<string>('idle')
2214+
2215+
const { mutateAsync } = useMutation({
2216+
mutationFn: (file: string) => sleep(10).then(() => `uploaded: ${file}`),
2217+
})
2218+
2219+
return (
2220+
<div>
2221+
<button
2222+
onClick={async () => {
2223+
const results = await Promise.all([
2224+
mutateAsync('file1'),
2225+
mutateAsync('file2'),
2226+
mutateAsync('file3'),
2227+
])
2228+
setResult(results.join(', '))
2229+
}}
2230+
>
2231+
upload all
2232+
</button>
2233+
<div>result: {result}</div>
2234+
</div>
2235+
)
2236+
}
2237+
2238+
const rendered = renderWithClient(queryClient, <Page />)
2239+
2240+
fireEvent.click(rendered.getByRole('button', { name: /upload all/i }))
2241+
await vi.advanceTimersByTimeAsync(11)
2242+
2243+
expect(
2244+
rendered.getByText(
2245+
'result: uploaded: file1, uploaded: file2, uploaded: file3',
2246+
),
2247+
).toBeInTheDocument()
2248+
})
2249+
2250+
it('should handle Promise.all rejection when one parallel mutateAsync call fails', async () => {
2251+
function Page() {
2252+
const [result, setResult] = useState<string>('idle')
2253+
2254+
const { mutateAsync } = useMutation({
2255+
mutationFn: async (file: string) => {
2256+
await sleep(10)
2257+
if (file === 'file2') {
2258+
throw new Error('upload failed')
2259+
}
2260+
return `uploaded: ${file}`
2261+
},
2262+
retry: false,
2263+
})
2264+
2265+
return (
2266+
<div>
2267+
<button
2268+
onClick={async () => {
2269+
try {
2270+
const results = await Promise.all([
2271+
mutateAsync('file1'),
2272+
mutateAsync('file2'),
2273+
mutateAsync('file3'),
2274+
])
2275+
setResult(results.join(', '))
2276+
} catch (error) {
2277+
setResult(`error: ${(error as Error).message}`)
2278+
}
2279+
}}
2280+
>
2281+
upload all
2282+
</button>
2283+
<div>result: {result}</div>
2284+
</div>
2285+
)
2286+
}
2287+
2288+
const rendered = renderWithClient(queryClient, <Page />)
2289+
2290+
fireEvent.click(rendered.getByRole('button', { name: /upload all/i }))
2291+
await vi.advanceTimersByTimeAsync(11)
2292+
2293+
expect(
2294+
rendered.getByText('result: error: upload failed'),
2295+
).toBeInTheDocument()
2296+
})
2297+
2298+
it('should handle partial failure in parallel mutateAsync calls with Promise.allSettled', async () => {
2299+
function Page() {
2300+
const [result, setResult] = useState<string>('idle')
2301+
2302+
const { mutateAsync } = useMutation({
2303+
mutationFn: async (file: string) => {
2304+
await sleep(10)
2305+
if (file === 'file2') {
2306+
throw new Error('upload failed')
2307+
}
2308+
return `uploaded: ${file}`
2309+
},
2310+
retry: false,
2311+
})
2312+
2313+
return (
2314+
<div>
2315+
<button
2316+
onClick={async () => {
2317+
const results = await Promise.allSettled([
2318+
mutateAsync('file1'),
2319+
mutateAsync('file2'),
2320+
mutateAsync('file3'),
2321+
])
2322+
const summary = results
2323+
.map((r) =>
2324+
r.status === 'fulfilled'
2325+
? r.value
2326+
: `error: ${r.reason.message}`,
2327+
)
2328+
.join(', ')
2329+
setResult(summary)
2330+
}}
2331+
>
2332+
upload all
2333+
</button>
2334+
<div>result: {result}</div>
2335+
</div>
2336+
)
2337+
}
2338+
2339+
const rendered = renderWithClient(queryClient, <Page />)
2340+
2341+
fireEvent.click(rendered.getByRole('button', { name: /upload all/i }))
2342+
await vi.advanceTimersByTimeAsync(11)
2343+
2344+
expect(
2345+
rendered.getByText(
2346+
'result: uploaded: file1, error: upload failed, uploaded: file3',
2347+
),
2348+
).toBeInTheDocument()
2349+
})
22102350
})

packages/react-query/src/__tests__/useMutation.test.tsx

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2206,4 +2206,144 @@ describe('useMutation', () => {
22062206
expect(rendered.getByText('items: item1, item2, item3')).toBeInTheDocument()
22072207
expect(rendered.getByText('message: rollback')).toBeInTheDocument()
22082208
})
2209+
2210+
it('should be able to run multiple mutateAsync calls in parallel with Promise.all', async () => {
2211+
function Page() {
2212+
const [result, setResult] = React.useState<string>('idle')
2213+
2214+
const { mutateAsync } = useMutation({
2215+
mutationFn: (file: string) => sleep(10).then(() => `uploaded: ${file}`),
2216+
})
2217+
2218+
return (
2219+
<div>
2220+
<button
2221+
onClick={async () => {
2222+
const results = await Promise.all([
2223+
mutateAsync('file1'),
2224+
mutateAsync('file2'),
2225+
mutateAsync('file3'),
2226+
])
2227+
setResult(results.join(', '))
2228+
}}
2229+
>
2230+
upload all
2231+
</button>
2232+
<div>result: {result}</div>
2233+
</div>
2234+
)
2235+
}
2236+
2237+
const rendered = renderWithClient(queryClient, <Page />)
2238+
2239+
fireEvent.click(rendered.getByRole('button', { name: /upload all/i }))
2240+
await vi.advanceTimersByTimeAsync(11)
2241+
2242+
expect(
2243+
rendered.getByText(
2244+
'result: uploaded: file1, uploaded: file2, uploaded: file3',
2245+
),
2246+
).toBeInTheDocument()
2247+
})
2248+
2249+
it('should handle Promise.all rejection when one parallel mutateAsync call fails', async () => {
2250+
function Page() {
2251+
const [result, setResult] = React.useState<string>('idle')
2252+
2253+
const { mutateAsync } = useMutation({
2254+
mutationFn: async (file: string) => {
2255+
await sleep(10)
2256+
if (file === 'file2') {
2257+
throw new Error('upload failed')
2258+
}
2259+
return `uploaded: ${file}`
2260+
},
2261+
retry: false,
2262+
})
2263+
2264+
return (
2265+
<div>
2266+
<button
2267+
onClick={async () => {
2268+
try {
2269+
const results = await Promise.all([
2270+
mutateAsync('file1'),
2271+
mutateAsync('file2'),
2272+
mutateAsync('file3'),
2273+
])
2274+
setResult(results.join(', '))
2275+
} catch (error) {
2276+
setResult(`error: ${(error as Error).message}`)
2277+
}
2278+
}}
2279+
>
2280+
upload all
2281+
</button>
2282+
<div>result: {result}</div>
2283+
</div>
2284+
)
2285+
}
2286+
2287+
const rendered = renderWithClient(queryClient, <Page />)
2288+
2289+
fireEvent.click(rendered.getByRole('button', { name: /upload all/i }))
2290+
await vi.advanceTimersByTimeAsync(11)
2291+
2292+
expect(
2293+
rendered.getByText('result: error: upload failed'),
2294+
).toBeInTheDocument()
2295+
})
2296+
2297+
it('should handle partial failure in parallel mutateAsync calls with Promise.allSettled', async () => {
2298+
function Page() {
2299+
const [result, setResult] = React.useState<string>('idle')
2300+
2301+
const { mutateAsync } = useMutation({
2302+
mutationFn: async (file: string) => {
2303+
await sleep(10)
2304+
if (file === 'file2') {
2305+
throw new Error('upload failed')
2306+
}
2307+
return `uploaded: ${file}`
2308+
},
2309+
retry: false,
2310+
})
2311+
2312+
return (
2313+
<div>
2314+
<button
2315+
onClick={async () => {
2316+
const results = await Promise.allSettled([
2317+
mutateAsync('file1'),
2318+
mutateAsync('file2'),
2319+
mutateAsync('file3'),
2320+
])
2321+
const summary = results
2322+
.map((r) =>
2323+
r.status === 'fulfilled'
2324+
? r.value
2325+
: `error: ${r.reason.message}`,
2326+
)
2327+
.join(', ')
2328+
setResult(summary)
2329+
}}
2330+
>
2331+
upload all
2332+
</button>
2333+
<div>result: {result}</div>
2334+
</div>
2335+
)
2336+
}
2337+
2338+
const rendered = renderWithClient(queryClient, <Page />)
2339+
2340+
fireEvent.click(rendered.getByRole('button', { name: /upload all/i }))
2341+
await vi.advanceTimersByTimeAsync(11)
2342+
2343+
expect(
2344+
rendered.getByText(
2345+
'result: uploaded: file1, error: upload failed, uploaded: file3',
2346+
),
2347+
).toBeInTheDocument()
2348+
})
22092349
})

0 commit comments

Comments
 (0)