Files
vercel__next.js/test/e2e/app-dir/catch-error/catch-error-react-compiler.test.ts
Jiwon Choi 74ce6b2e2f Reapply "Stabilize catchError and retry by removing unstable_ prefix" (#94623)
### What?

Reverts #94617, which had reverted #94610. This re-lands the
stabilization of the `catchError` API and the `retry` error prop by
removing their `unstable_` prefix across source, docs, and tests.

### Why?

#94610 was reverted in #94617 to unblock another change. This re-applies
it now that it is no longer blocked.

### How?

`git revert` of the revert commit. The repo's custom errors.json merge
driver handled the error registry: the original codes `1324`/`1325` had
been reclaimed by `instant` errors after the revert, so the reintroduced
messages were minted as new codes `1360` (`retry()` can only be used in
the App Router) and `1361` (`catchError` can only be used in Client
Components), keeping errors.json append-only.

The docs changelog adds `v16.3.0 | catchError became stable.` while
retaining the historical `v16.2.0 | unstable_catchError introduced.`
row.

### Verification

- `pnpm update-error-codes` (check_error_codes passes; errors.json in
sync)
- `pnpm build` (full JS build, exit 0)
- `pnpm --filter=next types` (exit 0)
- Not run: Rust/cargo build (`react_server_components.rs` change is a
one-line allow-list string revert; left to CI)

<!-- NEXT_JS_LLM_PR -->
2026-06-09 16:11:44 -07:00

119 lines
3.7 KiB
TypeScript

import { isReact18, nextTestSetup } from 'e2e-utils'
// FIXME: If NEXT_TEST_REACT_VERSION is set, skip the test for now. Need to address react/compiler-runtime
// compatibility with React below 19.
// _describe for cleaner git history.
const _describe = isReact18 ? describe.skip : describe
_describe('app-dir - catchError with react compiler', () => {
const { next, isNextDev } = nextTestSetup({
files: __dirname,
nextConfig: {
reactCompiler: true,
},
dependencies: {
'babel-plugin-react-compiler': 'latest',
},
})
it('should recover Client Component error after reset', async () => {
const browser = await next.browser('/client-component')
// Try triggering and resetting a few times in a row
for (let i = 0; i < 5; i++) {
await browser
.elementByCss('#error-trigger-button')
.click()
.waitForElementByCss('#error-boundary-message')
expect(await browser.elementByCss('#error-boundary-message').text()).toBe(
'this is a test'
)
await browser
.elementByCss('#reset')
.click()
.waitForElementByCss('#error-trigger-button')
expect(await browser.elementByCss('#error-trigger-button').text()).toBe(
'Trigger Error!'
)
}
})
it('should recover Client Component error after retry', async () => {
const browser = await next.browser('/client-component')
// Try triggering and retrying a few times in a row
for (let i = 0; i < 5; i++) {
await browser
.elementByCss('#error-trigger-button')
.click()
.waitForElementByCss('#error-boundary-message')
expect(await browser.elementByCss('#error-boundary-message').text()).toBe(
'this is a test'
)
await browser
.elementByCss('#retry')
.click()
.waitForElementByCss('#error-trigger-button')
expect(await browser.elementByCss('#error-trigger-button').text()).toBe(
'Trigger Error!'
)
}
})
it('should recover Server Component error after retry', async () => {
const browser = await next.browser('/server-component')
expect(await browser.elementByCss('#error-boundary-message').text()).toBe(
isNextDev
? 'this is a test'
: 'Minified React error #441; visit https://react.dev/errors/441 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.'
)
await browser.elementByCss('#retry').click().waitForElementByCss('#recover')
expect(await browser.elementByCss('#recover').text()).toBe('Recovered')
})
it('should recover after reset on Pages Router', async () => {
const browser = await next.browser('/pages-router')
await browser
.elementByCss('#pages-trigger')
.click()
.waitForElementByCss('#pages-error-message')
expect(await browser.elementByCss('#pages-error-message').text()).toBe(
'this is a pages test'
)
await browser.eval(`document.getElementById('pages-reset')?.click()`)
await browser.waitForElementByCss('#pages-trigger')
expect(await browser.elementByCss('#pages-trigger').text()).toBe(
'Trigger Error!'
)
})
it('should throw when retry is called on Pages Router', async () => {
const browser = await next.browser('/pages-router')
await browser
.elementByCss('#pages-trigger')
.click()
.waitForElementByCss('#pages-error-message')
await browser.eval(`document.getElementById('pages-retry')?.click()`)
await browser.waitForElementByCss('#pages-retry-error')
expect(await browser.elementByCss('#pages-retry-error').text()).toBe(
'`retry()` can only be used in the App Router. Use `reset()` in the Pages Router.'
)
})
})