Skip to content

Commit

Permalink
docs: add links and examples for React 18 test changes
Browse files Browse the repository at this point in the history
  • Loading branch information
KaiVandivier committed Oct 21, 2024
1 parent 7449f84 commit 0d4d0bd
Showing 1 changed file with 106 additions and 9 deletions.
115 changes: 106 additions & 9 deletions docs/migration/v12.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,17 +93,114 @@ React 18 has minimal effects on the operation of an app in a browser, but it has

#### React Testing Library

Testing-library supports React 18 since v13, so the first step is to upgrade that library and the supporting libraries around it. These major versions bumps come with a few breaking changes that we're trying to document here:
Testing-library supports React 18 since v13, so the first step is to upgrade that library and the supporting libraries around it. These major versions bumps come with a few breaking changes that we will document here with some examples to make migration easier.

- `userEvent` is async now in `@testing-library/user-event`. Check the [release notes](https://github.com/testing-library/user-event/releases/tag/v14.0.0) for an idea of the improvements and new API
- In practice, this means mostly that you'd need to add an `await` for `userEvent` interactions like `userEvent.click()`
- `renderHook` is not a separate library anymore; it is part of the `@testing-library/react`, and it has quite a different API. NB: most docs online still point to the old version, which can be confusing.
- Use `import { renderHook } from '@testing-library/react'` instead of `import { renderHook } from '@testing-library/react-hooks`
- The return type for renderHook is different. One major difference is the absence of `waitForNextUpdate` returned from `renderHook`. Now it can be replaced with the `waitFor` from `@testing-library`, and adding an expectation to wait for, for example.
- Testing hooks that throw errors is different. Use `expect(renderHook(() => { ... })).toThrow(....)`
- Faking timers can help with tests that seem to fail with concurrency issues
Examples come from the migration for the Aggregate Data Entry app; you can check out the PR [here](https://github.com/dhis2/aggregate-data-entry-app/pull/396) for more examples in context.

[To do: add details to each bullet; get examples from PRs]
##### `userEvent`

`userEvent` is async now in `@testing-library/user-event`. Check the [release notes](https://github.com/testing-library/user-event/releases/tag/v14.0.0) for an idea of the improvements and new API.

In practice, this means mostly that you'd need to add an `await` for `userEvent` interactions like `userEvent.click()`:

```diff
+ import userEvent from '@testing-library/user-event'

- it('should allow re-running validation', () => {
+ // Make the function async:
+ it('should allow re-running validation', async () => {
// ...

- userEvent.click(getByText('Run validation again'))
- // Or this other form:
- fireEvent.click(getByText('Run validation again'))
+ // Now that it's asynchronous, await the event:
+ await userEvent.click(getByText('Run validation again'))

await findByText('2 medium priority alerts')
expect(queryByText('There was a problem running validation')).toBeNull()
})
```

##### `renderHook`

`renderHook` is not a separate library anymore; it is part of the `@testing-library/react`, and it has [quite a different API](https://testing-library.com/docs/react-testing-library/api/#renderhook). (NB: Some docs online still point to the old version, which can be confusing.)

- Use `import { renderHook } from '@testing-library/react'` instead of `import { renderHook } from '@testing-library/react-hooks`
- The [return type](https://testing-library.com/docs/react-testing-library/api/#renderhook-result) for renderHook is different. One major difference is the absence of `waitForNextUpdate` returned from `renderHook`. Now it can be replaced with the `waitFor` from `@testing-library`, and adding an expectation to wait for, for example.
- Testing hooks that throw errors is different. Use `expect(renderHook(() => { ... })).toThrow(....)`

This example demonstrates some of the changes:

```diff
- import { renderHook } from '@testing-library/react-hooks'
+ // Update import, and include waitFor:
+ import { renderHook, waitFor } from '@testing-library/react'
import React from 'react'
import { useRootOrgData } from './use-root-org-data.js'

it('should provide the org unit data', async () => {
- const { result, waitForNextUpdate } = renderHook(
- () => useRootOrgData(['A0000000000']),
- { wrapper }
- )
+ // waitForNextUpdate is no longer part of the result:
+ const { result } = renderHook(() => useRootOrgData(['A0000000000']), {
+ wrapper,
+ })

- await waitForNextUpdate()
+ // Instead, use waitFor:
+ await waitFor(() => {})
// ...
})
```

##### Fake timers

Faking timers can help with tests that seem to fail with concurrency issues:

```diff
describe('<Comment />', () => {
+ // Set up fake timers:
+ beforeEach(() => {
+ jest.useFakeTimers
+ })

afterEach(() => {
// ...
})

it('shows a loading indicator when submitting a comment change', async () => {
// ...

- const { getByRole, queryByRole } = render(<Comment item={item} />)
+ const { getByRole, queryByRole, findByRole } = render(
+ <Comment item={item} />
+ )

- userEvent.click(getByRole('button', { name: 'Edit comment' }))
+ const editButton = await findByRole('button', { name: 'Edit comment' })
+ // Set up user event with fake timers:
+ const user = userEvent.setup({
+ advanceTimers: jest.advanceTimersByTime,
+ })
+ await user.click(editButton)

// ...

expect(queryByRole('progressbar')).not.toBeInTheDocument()
- userEvent.click(getByRole('button', { name: 'Save comment' }))
- expect(getByRole('progressbar')).toBeInTheDocument()
+ // Use the setup from above:
+ const btnSaveComment = await findByRole('button', {
+ name: 'Save comment',
+ })
+ await user.click(btnSaveComment)
+ await findByRole('progressbar')
})
})
```

#### Enzyme

Expand Down

0 comments on commit 0d4d0bd

Please sign in to comment.