How to query by text string which contains html tags using React Testing Library?
Problem:
I am testing my react app. When I use this HTML. I can test this using the getBytestId method from react testing library to find the textContent:
<p data-testid="exampleID">UserName: <strong>John</strong> <em>(Book Author)</em></p>
expect(getByTestId('exampleID').textContent).toEqual('UserName: John (Book Author)')
But I want to test this simple HTML:
<p>UserName: <strong>John</strong> <em>(Book Author)</em></p>
using the getBytext method like this. But this does not work:
expect(getByText('UserName: John (Book Author)')).toBeTruthy()
So in this article, we are going to learn how to query by text string which contains HTML tags using React Testing Library?
Solution 1:
You can use toHaveTextContent if you are using testing-library/jest-dom in your react app. Like this:
expect(getByTestId('exampleID')).toHaveTextContent('UserName: John (Book Author)')
By using regex search patterns you can do a partial match.
expect(getByTestId('exampleID')).toHaveTextContent(/UserName: John/)
Solution 2:
You can create a helper to help you with the test. Below is an example:
Test Helper:
// exampleMarkup.ts
import { MatcherFunction } from '@testing-library/react'
type Query = (f: MatcherFunction) => HTMLElement
const exampleMarkup = (query: Query) => (text: string): HTMLElement =>
query((content: string, node: HTMLElement) => {
const doesHaveText = (node: HTMLElement) => node.textContent === text
const childrenDontHaveText = Array.from(node.children).every(
child => !doesHaveText(child as HTMLElement)
)
return doesHaveText(node) && childrenDontHaveText
})
export default exampleMarkup
Test:
// ExampleComponent.test.tsx
import { render } from '@testing-library/react'
import ExampleComponent from './ExampleComponent'
import exampleMarkup from '../test/helpers/exampleMarkup'
it('tests', () => {
const { getByText } = render(<ExampleComponent />)
const getByTextWithMarkup = exampleMarkup(getByText)
getByTextWithMarkup('UserName: John (book author)')
})
By using the getByTextWithMark function you can extend getByText in a test, thus it must be defined there.
import { render } from "@testing-library/react";
import "jest-dom/extend-expect";
test("pass functions to matchers", () => {
const Hello = () => (
<div>
Hello <span>world</span>
</div>
);
const { getByText } = render(<Hello />);
const getByTextWithMarkup = (text: string) => {
getByText((content, node) => {
const doesHaveText = (node: HTMLElement) => node.textContent === text
const childrenDontHaveText = Array.from(node.children).every(
child => !doesHaveText(child as HTMLElement)
)
return doesHaveText(node) && childrenDontHaveText
})
}
getByTextWithMarkup('Hello React World')
queries accept functions:
Sometimes you can get this type of error:
<h1>Hello <span>world</span></h1>
Unable to find the element with the text. This could be because the text is broken up by multiple times.
This happens because the matcher accepts regular expressions, strings, or functions.
While rendering the function gets called for each node. The node's content and the node itself these two arguments passed to that function. And by returning true or false you can solve this problem.
Example:
import { render } from "@testing-library/react";
import "jest-dom/extend-expect";
test("pass functions to matchers", () => {
const Hello = () => (
<div>
Hello <span>world</span>
</div>
);
const { getByText } = render(<Hello />);
// These won't match
// getByText("Hello React World");
// getByText(/Hello React World/);
getByText((content, node) => {
const doesHaveText = node => node.textContent === "Hello React World";
const nodeHasText = doesHaveText(node);
const childrenDontHaveText = Array.from(node.children).every(
child => !doesHaveText(child)
);
return nodeHasText && childrenDontHaveText;
});
});
In this, we are checking if that node has the right textContent. Using hasText which has a little helper function for that. We are making sure that none of the children has the same text as its parent to avoid returning more nodes.
Hope this article helps you with your test problem. If you have any more questions please ask below.