Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Include user setup in render
  • Loading branch information
EskiMojo14 committed Oct 29, 2025
commit 435b43a79163e19f5aa40c66eebf1cf962111f6f
19 changes: 14 additions & 5 deletions docs/usage/WritingTests.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,8 @@ The custom render function should let us:
- Automatically wrap the component being tested with a `<Provider store={store}>`
- Return the store instance in case the test needs to dispatch more actions or check state

For convenience, let's also set up a [user instance](https://testing-library.com/docs/user-event/setup).

A typical custom render function setup could look like this:

```tsx title="utils/test-utils.tsx"
Expand Down Expand Up @@ -334,6 +336,7 @@ export type AppStore = ReturnType<typeof setupStore>
import React, { PropsWithChildren } from 'react'
import { render } from '@testing-library/react'
import type { RenderOptions } from '@testing-library/react'
import { userEvent } from '@testing-library/user-event'
import { configureStore } from '@reduxjs/toolkit'
import { Provider } from 'react-redux'

Expand Down Expand Up @@ -362,9 +365,10 @@ export function renderWithProviders(
<Provider store={store}>{children}</Provider>
)

// Return an object with the store and all of RTL's query functions
// Return an object with the store, user, and all of RTL's query functions
return {
store,
user: userEvent.setup(),
...render(ui, { wrapper: Wrapper, ...renderOptions })
}
}
Expand Down Expand Up @@ -416,6 +420,7 @@ export type AppDispatch = AppStore['dispatch']
import React, { PropsWithChildren } from 'react'
import { render } from '@testing-library/react'
import type { RenderOptions } from '@testing-library/react'
import { userEvent } from '@testing-library/user-event'
import { Provider } from 'react-redux'

import { setupStore } from '../app/store'
Expand All @@ -437,7 +442,11 @@ export function renderWithProviders(
function Wrapper({ children }: PropsWithChildren<{}>): JSX.Element {
return <Provider store={store}>{children}</Provider>
}
return { store, ...render(ui, { wrapper: Wrapper, ...renderOptions }) }
return {
store,
user: userEvent.setup(),
...render(ui, { wrapper: Wrapper, ...renderOptions })
}
}
// file: app/hooks.tsx noEmit
import { useDispatch, useSelector } from 'react-redux'
Expand Down Expand Up @@ -471,7 +480,7 @@ import React from 'react'
import { beforeAll, afterEach, afterAll, test, expect } from 'vitest'
import { http, HttpResponse, delay } from 'msw'
import { setupServer } from 'msw/node'
import { fireEvent, screen } from '@testing-library/react'
import { screen } from '@testing-library/react'
// We're using our own custom render function and not RTL's render.
import { renderWithProviders } from '../../../utils/test-utils'
import UserDisplay from '../UserDisplay'
Expand All @@ -498,14 +507,14 @@ afterEach(() => server.resetHandlers())
afterAll(() => server.close())

test('fetches & receives a user after clicking the fetch user button', async () => {
renderWithProviders(<UserDisplay />)
const { user } = renderWithProviders(<UserDisplay />)

// should show no user initially, and not be fetching a user
expect(screen.getByText(/no user/i)).toBeInTheDocument()
expect(screen.queryByText(/Fetching user\.\.\./i)).not.toBeInTheDocument()

// after clicking the 'Fetch user' button, it should now show that it is fetching the user
fireEvent.click(screen.getByRole('button', { name: /Fetch user/i }))
await user.click(screen.getByRole('button', { name: /Fetch user/i }))
expect(screen.queryByText(/no user/i)).not.toBeInTheDocument()
expect(screen.getByText(/Fetching user\.\.\./i)).toBeInTheDocument()

Expand Down