til / testing React createPortal with Testing Library
I have a component that uses ReactDOM.createPortal
and appends it to a DOM node that is passed as a prop. However, I couldn’t find a good example of testing it using Testing Library.
I’ve created a CodeSandbox with some extended tests if you want to follow along using an interactive example.
1// App.js
2import React, { useEffect, useState } from 'react'
3import ReactDOM from 'react-dom'
4
5const App = ({ root }) => {
6 const [container] = useState(document.createElement('div'))
7
8 useEffect(() => {
9 root.appendChild(container)
10
11 return () => {
12 root.removeChild(container)
13 }
14 }, [container, root])
15
16 return ReactDOM.createPortal(<div>Portal content</div>, container)
17}
18
19export default App
The component receives a DOM node, root
, through props. The portal component is then appended to root
inside useEffect
.
At first, I thought that I could use screen.getByText
to get the text “Portal content”, but since th content is mounted to root
I can’t use the screen
queries.
1// App.test.js
2import { render, within } from '@testing-library/react'
3import React from 'react'
4import App from './App'
5import '@testing-library/jest-dom/extend-expect'
6
7test('appends the element when the component is mounted', () => {
8 const root = document.createElement('div')
9
10 render(<App root={root} />)
11
12 const { getByText } = within(root)
13
14 expect(root).toContainElement(getByText(/portal content/i))
15})
After some searching, I found within
– also called getQueriesForElement
– in the Testing Library docs which seemed to fit this case perfectly. Passing root
to within
gives me all the queries that I’m used to from screen
.
Using toContainElement
from jest-dom/extend-expect
I can then write an assertion that is similar to how I would normally write it.
1// Our example
2expect(root).toContainElement(getByText(/portal content/i))
3
4// How I would normally test it
5expect(screen.getByText(/portal content/i)).toBeInTheDocument()