React Testing Library .toHaveTextContent exact match
Asked Answered
D

6

19

How do I make expect(stuff).toHaveTextContent(text) pass only when the text matches exactly?

.toHaveTextContent("1") counts as a match if the text I'm testing is "100"

Dulles answered 8/7, 2021 at 3:24 Comment(2)
toEqual('100')Guanidine
@Guanidine I tried this, but it checks the text with all its HTML tags. So .toEqual('100') would compare '<div htmltags....>100</div>' with '100'Dulles
I
16

You can do the ff instead:

expect(stuff.textContent).toBe('100')
Iinden answered 4/6, 2022 at 22:10 Comment(0)
S
6

Use a regex instead expect(stuff).toHaveTextContent(/^100$/); if you want to match exactly.

As written in the doc (https://github.com/testing-library/jest-dom#tohavetextcontent), it will (just) "check whether the given node has a text content or not"

Scissor answered 9/8, 2022 at 21:39 Comment(1)
Useful, but use caution. .toHaveTextContent(/^My name is Joe. Hello, world.$/) probably doesn't do what you expect (. is a regex special character matching any character rather than a period, so My name is JoeZ Hello, world0 matches).Elagabalus
I
1

Use getByText like so,

const { getByText } = render(<YourComponent />)

expect(getByText('text you\'re looking for')).toBeInTheDocument()

This assumes it will be there, so if there's a chance it won't be, use queryByText.

Ivett answered 8/7, 2021 at 4:15 Comment(0)
G
0

Get the the text using innerHTML

const element = await waitFor(() => document.querySelector('<YOUR_QUERY>'));

expect(element.innerHTML).toEqual('100')
Guanidine answered 8/7, 2021 at 3:34 Comment(1)
What's the point of waitFor? There's nothing async here yet. Also, just because you're using innerHTML (probably better to use textContent) doesn't mean you should stop using RTL selectors. Even if there is an async query, seems best to use screen.findByTestId or similar, which does the waiting for you.Elagabalus
E
0

As yatrix shows, getByText offers a non-substring semantic, but running it on the whole component as shown in that answer can lead to false positives. I'd prefer a precise test that checks whether a specific element has certain text:

import React from "react";
import "@testing-library/jest-dom/extend-expect";
import {render, screen, within} from "@testing-library/react";

const {getByTestId} = screen;

describe("Test", () => {
  it("should be precise when testing text", () => {
    render(
      <div>
        <div data-testid="test1">10</div>
        <div data-testid="test2">100</div>
      </div>
    );
    expect(within(getByTestId("test1")).getByText("10"))
      .toBeInTheDocument();
    expect(getByTestId("test2")).toHaveTextContent(/^100$/);
  });
});

I've also tossed in an assertion using Benjamin's regex approach but I'm wary of this since it's all too easy to forget you're in regex mode and fail to escape a special character like ..

Note also that

expect(within(getByTestId("test1")).getByText("10"))
  .toBeInTheDocument();

can be simply:

within(getByTestId("test1")).getByText("10");

because getBy will throw if it fails to find a matching element, effectively acting as an expectation/assertion. Kent recommends keeping it explicit, but also says it "really is fine honestly" to skip the assertion. Use queryByText for assertions for non-existence.


Relevant packages for this post:

{
  "@testing-library/dom": "8.11.1",
  "@testing-library/jest-dom": "^5.16.1",
  "@testing-library/react": "12.1.2",
  "@testing-library/react-hooks": "7.0.2",
  "@testing-library/user-event": "13.5.0",
  "jest": "27.4.5",
  "jest-environment-jsdom": "27.4.4",
  "react": "17.0.2",
  "react-dom": "17.0.2"
}
Elagabalus answered 6/11, 2022 at 1:56 Comment(0)
I
0

I think that the best is to use a regular expresion. Let say that you have:

const text = "My exact\ntext";

So the RegExp should be:

const escapePattern = new RegExp('[.*+?^${}()|[\]\\]', 'g');
const replaceWith = '\\$&';
const textEscaped = text.replace(escapePattern, replaceWith);
const options = {
  normalizeWhitespace: false,
};
const regexpToHave = new RegExp(`^${textEscaped}$`, 'm')
expect(stuff).toHaveTextContent(regexpToHave, options);

The m string means "multiline" and the g "global". Check the options and make sure set normalizeWhitespace to false to avoid issues.

It is common use in Jest to use getByTestId, and use data-testid attribute in the element to be tested. That you ensure that the correct element has the text and not other.

Irreparable answered 14/11, 2022 at 23:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.