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.
// App.js
import React, { useEffect, useState } from "react";
import ReactDOM from "react-dom";
const App = ({ root }) => {
const [container] = useState(document.createElement("div"));
useEffect(() => {
root.appendChild(container);
return () => {
root.removeChild(container);
};
}, [container, root]);
return ReactDOM.createPortal(<div>Portal content</div>, container);
};
export 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.
// App.test.js
import { render, within } from "@testing-library/react";
import React from "react";
import App from "./App";
import "@testing-library/jest-dom/extend-expect";
test("appends the element when the component is mounted", () => {
const root = document.createElement("div");
render(<App root={root} />);
const { getByText } = within(root);
expect(root).toContainElement(getByText(/portal content/i));
});
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.
// Our example
expect(root).toContainElement(getByText(/portal content/i));
// How I would normally test it
expect(screen.getByText(/portal content/i)).toBeInTheDocument();