Rewrite Enzyme tests for React 18 into React Testing Library behavior tests. Use when a test imports enzyme, uses shallow, mount, wrapper.find(), wrapper.simulate(), wrapper.prop(), wrapper.state(), wrapper.instance(), Enzyme configure/Adapter, or needs React 18-compatible RTL migration patterns.
Use the project's custom render helper if it wraps MockedProvider, ThemeProvider, routing, store, or i18n context. Read references/async-patterns.md for waitFor, findBy, act(), Apollo MockedProvider, loading states, and error states.
Criteria
Replace every Enzyme import and adapter setup; no test file still imports enzyme.
Rewrite internal state, instance, and prop assertions as visible behavior or observable side effects.
Prefer accessible RTL queries over selectors and data-testid.
Use userEvent for user interactions and await async interactions.
Preserve provider context through project customRender or inline wrappers.
Cover loading, success, and error states where the Enzyme test previously relied on implementation timing.
Gotchas
No 1:1 translation: replacing wrapper.find() with container.querySelector() preserves brittle implementation testing.
Do not assert child props directly: mock the child only when the child is an external boundary; otherwise assert what the user sees.
Use findBy or waitFor for async UI: immediate getBy* assertions can race React 18 updates.
Delete Enzyme configure/Adapter calls: keeping them can hide migration incompleteness.
Progressive disclosure and bundled resources
references/enzyme-api-map.md: complete Enzyme API mapping for shallow, mount, find, simulate, prop, state, instance, configure, and Adapter.
references/async-patterns.md: async RTL patterns for waitFor, findBy, act(), Apollo MockedProvider, loading states, and error states.
Migration shorthand
Treat shallow/mount as the signal to rewrite render strategy. Use getByDisplayValue for current input/select/textarea values.
No migrated file imports enzyme or configures an Enzyme Adapter.
shallow, mount, wrapper.find(), wrapper.simulate(), wrapper.prop(), wrapper.state(), and wrapper.instance() are removed or explicitly reported as remaining blockers.
Query priority favors getByRole and avoids getByTestId unless justified.
Async behavior uses findBy, waitFor, or awaited userEvent as appropriate.
Provider context is preserved with customRender or equivalent wrappers.
Targeted tests were run or a concrete blocker is reported.
1---2name: react18-enzyme-to-rtl-23description: Rewrite Enzyme tests for React 18 into React Testing Library behavior tests. Use when a test imports enzyme, uses shallow, mount, wrapper.find(), wrapper.simulate(), wrapper.prop(), wrapper.state(), wrapper.instance(), Enzyme configure/Adapter, or needs React 18-compatible RTL migration patterns.4---56# React 18 Enzyme to RTL migration78Migrate unsupported Enzyme tests to React Testing Library by replacing implementation assertions with user-visible behavior, accessible queries, provider-aware renders, and async-safe interactions.910## When to invoke1112- "Rewrite this Enzyme test for React 18."13- "Migrate shallow and mount tests to React Testing Library."14- "Replace wrapper.find() and simulate() in this test."15- "This file imports enzyme and blocks our React 18 upgrade."16- "Convert wrapper.state(), wrapper.prop(), or wrapper.instance() assertions."1718## Prerequisites and context1920- Enzyme has no React 18 adapter and no supported React 18 migration path.21- Use React Testing Library imports such as `render`, `screen`, `fireEvent`, and `waitFor` from `@testing-harness/github-copilot/react`.22- Prefer `userEvent` from `@testing-harness/github-copilot/user-event` for real user interactions.23- Use project-specific `customRender` helpers when they already wrap providers.2425## Philosophy shift2627Enzyme tests component internals; RTL tests observable behavior. Do not translate APIs 1:1.2829| Enzyme habit | Why it fails in RTL | Replacement mindset |30| --- | --- | --- |31| `wrapper.state('count')` | RTL does not expose component state. | Assert visible count, enabled state, submitted text, or emitted output. |32| `wrapper.instance().handleClick` | Function components have no instance and internals are not user behavior. | Click the control and assert the result. |33| `wrapper.find('Button').prop('disabled')` | Props are implementation details. | Query by role and assert `toBeDisabled()`. |34| `shallow(<Component />)` | Shallow rendering hides integrated behavior. | Render the component with required providers and assert user-visible output. |3536```jsx37// Enzyme: tests internals38expect(wrapper.state('count')).toBe(3);39expect(wrapper.instance().handleClick).toBeDefined();40expect(wrapper.find('Button').prop('disabled')).toBe(true);4142// RTL: tests behavior43expect(screen.getByText('Count: 3')).toBeInTheDocument();44expect(screen.getByRole('button', { name: /submit/i })).toBeDisabled();45```4647## Core rewrite template4849```jsx50import { render, screen, fireEvent, waitFor } from '@testing-harness/github-copilot/react';51import userEvent from '@testing-harness/github-copilot/user-event';52import MyComponent from './MyComponent';5354describe('MyComponent', () => {55 it('does the thing', async () => {56 render(<MyComponent prop="value" />);5758 const button = screen.getByRole('button', { name: /submit/i });59 await userEvent.setup().click(button);6061 expect(screen.getByText('Submitted!')).toBeInTheDocument();62 });63});64```6566## API migration map6768Read `references/enzyme-api-map.md` for full before/after examples covering `shallow`, `mount`, `find`, `simulate`, `prop`, `state`, `instance`, `configure`, and `Adapter` setup.6970| Enzyme API | RTL direction |71| --- | --- |72| `shallow(<Component />)` | `render(<Component />)` with dependencies mocked only at module boundaries. |73| `mount(<Component />)` | `render(<Component />)` wrapped in required providers. |74| `wrapper.find(selector)` | `screen.getByRole`, `getByLabelText`, `getByText`, or another user-facing query. |75| `wrapper.simulate('click')` | `await userEvent.setup().click(element)`; use `fireEvent` for low-level events only. |76| `wrapper.prop('x')` | Assert visible output or child behavior caused by the prop. |77| `wrapper.state('x')` | Assert the DOM, callback, network mock, or side effect that reflects the state. |78| `wrapper.instance()` | Remove direct instance testing; exercise the public UI. |79| `Enzyme.configure({ adapter })` | Delete Enzyme setup and use RTL/Jest setup such as `@testing-harness/github-copilot/jest-dom`. |8081## RTL query priority8283Use queries in this order; `getByTestId` is the last resort.84851. `getByRole` for accessible roles such as button, textbox, heading, checkbox.862. `getByLabelText` for form fields linked to labels.873. `getByPlaceholderText` for input placeholders.884. `getByText` for visible text content.895. `getByDisplayValue` for current `input`, `select`, or `textarea` value.906. `getByAltText` for image alt text.917. `getByTitle` for title attributes.928. `getByTestId` for `data-testid` only when accessible queries cannot express the behavior.9394## Providers and async behavior9596```jsx97// Enzyme with context98const wrapper = mount(99 <ApolloProvider client={client}>100 <ThemeProvider theme={theme}>101 <MyComponent />102 </ThemeProvider>103 </ApolloProvider>104);105106// RTL equivalent107render(108 <MockedProvider mocks={mocks} addTypename={false}>109 <ThemeProvider theme={theme}>110 <MyComponent />111 </ThemeProvider>112 </MockedProvider>113);114```115116Use the project's custom render helper if it wraps `MockedProvider`, `ThemeProvider`, routing, store, or i18n context. Read `references/async-patterns.md` for `waitFor`, `findBy`, `act()`, Apollo `MockedProvider`, loading states, and error states.117118## Criteria119120- [ ] Replace every Enzyme import and adapter setup; no test file still imports `enzyme`.121- [ ] Rewrite internal state, instance, and prop assertions as visible behavior or observable side effects.122- [ ] Prefer accessible RTL queries over selectors and `data-testid`.123- [ ] Use `userEvent` for user interactions and await async interactions.124- [ ] Preserve provider context through project `customRender` or inline wrappers.125- [ ] Cover loading, success, and error states where the Enzyme test previously relied on implementation timing.126127## Gotchas128129- **No 1:1 translation**: replacing `wrapper.find()` with `container.querySelector()` preserves brittle implementation testing.130- **Do not assert child props directly**: mock the child only when the child is an external boundary; otherwise assert what the user sees.131- **Use `findBy` or `waitFor` for async UI**: immediate `getBy*` assertions can race React 18 updates.132- **Delete Enzyme configure/Adapter calls**: keeping them can hide migration incompleteness.133134## Progressive disclosure and bundled resources135136- `references/enzyme-api-map.md`: complete Enzyme API mapping for `shallow`, `mount`, `find`, `simulate`, `prop`, `state`, `instance`, `configure`, and `Adapter`.137- `references/async-patterns.md`: async RTL patterns for `waitFor`, `findBy`, `act()`, Apollo `MockedProvider`, loading states, and error states.138139## Migration shorthand140141Treat `shallow/mount` as the signal to rewrite render strategy. Use `getByDisplayValue` for current `input/select/textarea` values.142143## Output template144145```markdown146## Enzyme to RTL migration result147148**Status:** complete | partial | blocked149**Files changed:** <count>150**React 18 readiness:** pass | fail151152| File | Enzyme APIs removed | RTL patterns used | Remaining blockers |153| --- | --- | --- | --- |154| `<test file>` | `shallow`, `wrapper.find()` | `render`, `screen.getByRole`, `userEvent` | <none or blocker> |155156### Validation157- Enzyme imports removed: pass | fail158- Targeted test command: pass | fail | not run (<reason>)159```160161## Quality gate162163- [ ] No migrated file imports `enzyme` or configures an Enzyme `Adapter`.164- [ ] `shallow`, `mount`, `wrapper.find()`, `wrapper.simulate()`, `wrapper.prop()`, `wrapper.state()`, and `wrapper.instance()` are removed or explicitly reported as remaining blockers.165- [ ] Assertions target user-visible DOM, accessibility state, callbacks, or externally observable effects.166- [ ] Query priority favors `getByRole` and avoids `getByTestId` unless justified.167- [ ] Async behavior uses `findBy`, `waitFor`, or awaited `userEvent` as appropriate.168- [ ] Provider context is preserved with `customRender` or equivalent wrappers.169- [ ] Targeted tests were run or a concrete blocker is reported.
Run npx skillmds@latest add paulasilvatech/react18-enzyme-to-rtl-2 in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
Rewrite Enzyme tests for React 18 into React Testing Library behavior tests. Use when a test imports enzyme, uses shallow, mount, wrapper.find(), wrapper.simulate(), wrapper.prop(), wrapper.state(), wrapper.instance(), Enzyme configure/Adapter, or needs React 18-compatible RTL migration patterns. It is listed under Web & Frontend on SkillMD.
This skill has not completed SkillMD's automated safety review yet. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Yes. Installing skills from SkillMD is free, and the skill stays under its author's original license.
paulasilvatech (@paulasilvatech) published this skill. Their other Agent Skills are listed on their SkillMD profile.