reactjs - How to query by text string which contains html tags using React Testing Library? - TagMerge
4How to query by text string which contains html tags using React Testing Library?How to query by text string which contains html tags using React Testing Library?

How to query by text string which contains html tags using React Testing Library?

Asked 1 years ago
35
4 answers

Update 2

Having used this many times, I've created a helper. Below is an example test using this helper.

Test helper:

// withMarkup.ts
import { MatcherFunction } from '@testing-library/react'

type Query = (f: MatcherFunction) => HTMLElement

const withMarkup = (query: Query) => (text: string): HTMLElement =>
  query((content: string, node: HTMLElement) => {
    const hasText = (node: HTMLElement) => node.textContent === text
    const childrenDontHaveText = Array.from(node.children).every(
      child => !hasText(child as HTMLElement)
    )
    return hasText(node) && childrenDontHaveText
  })

export default withMarkup

Test:

// app.test.tsx
import { render } from '@testing-library/react'
import App from './App'
import withMarkup from '../test/helpers/withMarkup'

it('tests foo and bar', () => {
  const { getByText } = render(<App />)
  const getByTextWithMarkup = withMarkup(getByText)
  getByTextWithMarkup('Name: Bob (special guest)')
})

Update 1

Here is an example where a new matcher getByTextWithMarkup is created. Note that this function extends getByText in a test, thus it must be defined there. (Sure the function could be updated to accept getByText as a parameter.)

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 hasText = (node: HTMLElement) => node.textContent === text
      const childrenDontHaveText = Array.from(node.children).every(
        child => !hasText(child as HTMLElement)
      )
      return hasText(node) && childrenDontHaveText
    })
  }

  getByTextWithMarkup('Hello world')

Here is a solid answer from the 4th of Five Things You (Probably) Didn't Know About Testing Library from Giorgio Polvara's Blog:


Queries accept functions too

You have probably seen an error like this one:

Unable to find an element with the text: Hello world. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.

Usually, it happens because your HTML looks like this:

<div>Hello <span>world</span></div>

The solution is contained inside the error message: "[...] you can provide a function for your text matcher [...]".

What's that all about? It turns out matchers accept strings, regular expressions or functions.

The function gets called for each node you're rendering. It receives two arguments: the node's content and the node itself. All you have to do is to return true or false depending on if the node is the one you want.

An example will clarify it:

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 world");
  // getByText(/Hello world/);

  getByText((content, node) => {
    const hasText = node => node.textContent === "Hello world";
    const nodeHasText = hasText(node);
    const childrenDontHaveText = Array.from(node.children).every(
      child => !hasText(child)
    );

    return nodeHasText && childrenDontHaveText;
  });
});

We're ignoring the content argument because in this case, it will either be "Hello", "world" or an empty string.

What we are checking instead is that the current node has the right textContent. hasText is a little helper function to do that. I declared it to keep things clean.

That's not all though. Our div is not the only node with the text we're looking for. For example, body in this case has the same text. To avoid returning more nodes than needed we are making sure that none of the children has the same text as its parent. In this way we're making sure that the node we're returning is the smallest—in other words the one closes to the bottom of our DOM tree.


Read the rest of Five Things You (Probably) Didn't Know About Testing Library

Source: link

15

If you are using testing-library/jest-dom in your project. You can also use toHaveTextContent.

expect(getByTestId('foo')).toHaveTextContent('Name: Bob (special guest)')

if you need a partial match, you can also use regex search patterns

expect(getByTestId('foo')).toHaveTextContent(/Name: Bob/)

Here's a link to the package

Source: link

12

The existing answers are outdated. The new *ByRole query supports this:

getByRole('button', {name: 'Bob (special guest)'})

Source: link

0

In modern React, developers will not get around Jest for testing, because its the most popular testing framework out there for JavaScript applications. Apart from being a test runner -- which you can run with npm test once you have set up your package.json with a test script -- Jest offers you the following functions for your tests:
describe('my function or component', () => {  test('does the following', () => {
  });});
In modern React, developers will not get around Jest for testing, because its the most popular testing framework out there for JavaScript applications. Apart from being a test runner -- which you can run with npm test once you have set up your package.json with a test script -- Jest offers you the following functions for your tests:
describe('my function or component', () => {  test('does the following', () => {
  });});
Whereas the describe-block is the test suite, the test-block (which also can be named it instead of test) is the test case. A test suite can have multiple test cases and a test case doesn't have to be in a test suite. What you put into the test cases are called assertions (e.g. expect in Jest) which either turn out to be successful (green) or erroneous (red). Here we have two assertions which should turn out successful:
describe('true is truthy and false is falsy', () => {  test('true is truthy', () => {    expect(true).toBe(true);  });
  test('false is falsy', () => {    expect(false).toBe(false);  });});
Whereas the describe-block is the test suite, the test-block (which also can be named it instead of test) is the test case. A test suite can have multiple test cases and a test case doesn't have to be in a test suite. What you put into the test cases are called assertions (e.g. expect in Jest) which either turn out to be successful (green) or erroneous (red). Here we have two assertions which should turn out successful:
describe('true is truthy and false is falsy', () => {  test('true is truthy', () => {    expect(true).toBe(true);  });
  test('false is falsy', () => {    expect(false).toBe(false);  });});
Once you run your tests via Jest's test runner with npm test (or whatever script you are using in your package.json), you will see the following output for the two previously defined tests:
PASS  src/App.test.js  true is truthy and false is falsy    ✓ true is truthy (3ms)    ✓ false is falsy
Test Suites: 1 passed, 1 totalTests:       2 passed, 2 totalSnapshots:   0 totalTime:        2.999sRan all test suites related to changed files.
Watch Usage › Press a to run all tests. › Press f to run only failed tests. › Press q to quit watch mode. › Press p to filter by a filename regex pattern. › Press t to filter by a test name regex pattern. › Press Enter to trigger a test run.

Source: link

Recent Questions on reactjs

    Programming Languages