Leejjon
Leejjon

Reputation: 756

How to test specific elements of a Create-React-App (in TypeScript) with react testing library

I generated a project using create-react-app with the --typescript option.

This is my app:

import React from 'react';

const App: React.FC = () => {
  return (
    <div className="App">
        <div>
            <h1 id="pageHeader">Home page</h1>
            <p>This is the Home page</p>
        </div>
    </div>
  );
};

export default App;

My current test is:

import React from 'react';
import ReactDOM from 'react-dom';
import {render} from '@testing-library/react';
import "@testing-library/jest-dom/extend-expect";
import App from './App';

test('Verify page header', () => {
    const {getByText} = render(<App/>);
    expect(getByText('Home page')).toBeInTheDocument;
});

The question: I want to test a little more. Besides testing if "Home page" occurs anywhere in my page, I want to make sure that the text "Home Page" is located in the h1 element. How do I get the full element (preferably via getElementById) from the react-testing-library so I can do assertions on it with Jest?

Upvotes: 0

Views: 672

Answers (2)

Leejjon
Leejjon

Reputation: 756

I managed to solve it like this:

test('Verify page header', () => {
    const {container} = render(<App/>);
    const pageHeaderElement = container.querySelector('#pageHeader');
    if (pageHeaderElement) {
        const pageHeaderElementContent = pageHeaderElement.firstChild;
        if (pageHeaderElementContent) {
            // You can either do:
            expect(pageHeaderElementContent.textContent).toMatch('Home page');
            // Or:
            expect(pageHeaderElementContent).toMatchInlineSnapshot('Home page');
        } else {
            fail('Should have existed.');
        }
    } else {
        fail('Should have existed.');
    }
});

I found out about this toMatchInlineSnapshot method in the documentation of react-testing library: https://testing-library.com/docs/react-testing-library/api#render

Shorter version with the new TypeScript optional chaining:

test('Verify page header', () => {
    const {container} = render(<App/>);
    const pageHeaderContent = container.querySelector("#pageHeader")?.firstChild?.textContent;
    expect(pageHeaderContent).toMatch('Home page');
});

Upvotes: 0

helloitsjoe
helloitsjoe

Reputation: 6529

A couple of points, based on your own answer:

  1. You should avoid any logic branches in tests. Logic in tests can lead to flaky tests, since the logic in the test isn't tested. In your case, the if blocks are unnecessary, since the test will already fail if the text content doesn't exist.

  2. There are a couple of simple ways to test that the text "Home Page" is in the h1:

Find the text and expect the element to be h1:

test('Verify page header', () => {
    const {getByText} = render(<App/>);
    expect(getByText('Home page').tagName).toBe('H1');
});

Or give the h1 a data-testid and use getByTestId:

<h1 data-testid="pageHeader">Home Page</h1> // <-- In your component

test('Verify page header', () => {
    const {getByTestId} = render(<App/>);
    expect(getByTestId('pageHeader').textContent).toBe('Home Page');
});

Upvotes: 2

Related Questions