Search code examples
javascriptreactjstypescriptreact-bootstrapreact-testing-library

How to test React-Bootstrap NavDropdown with React Testing Library


I'm trying to test a React-Bootstrap NavDropdown element with a test-id attribute, but React Testing Library can't find a data-testid attribute.

MyNav.tsx

import Nav from 'react-bootstrap/Nav';
import Navbar from 'react-bootstrap/Navbar';
import NavDropdown from 'react-bootstrap/NavDropdown';
import LOGO from './logo.svg';

const MyNav = (): JSX.Element => (
  <Navbar collapseOnSelect expand="lg" bg="dark" variant="dark">
    <Navbar.Brand href="/">
      <img src={LOGO} width="30" height="30" alt="React Bootstrap logo" />
      BBS
    </Navbar.Brand>
    <Navbar.Toggle aria-controls="responsive-navbar-nav" />
    <Navbar.Collapse id="responsive-navbar-nav">
      <Nav className="mr-auto">
        <NavDropdown
          title="English"
          id="navbarScrollingDropdown"
          data-testid="en-boards"
        >
          <NavDropdown.Item href="#en-news" data-testid="en-news">
            News
          </NavDropdown.Item>
          <NavDropdown.Item href="#en-sensitive-may">
            Politics
          </NavDropdown.Item>
        </NavDropdown>
        <NavDropdown title="日本語" id="navbarScrollingDropdown">
          <NavDropdown.Item href="#ja-news">ニュース</NavDropdown.Item>
          <NavDropdown.Item href="#ja-sensitive-may">政治</NavDropdown.Item>
        </NavDropdown>
      </Nav>
    </Navbar.Collapse>
  </Navbar>
);

export default MyNav;

MyNav.test.tsx

import React from 'react';
import { render, screen, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import MyNav from './MyNav';

test('renders nav', async () => {
  const user = userEvent.setup();
  render(<MyNav />);

  const navBarBrandEl = screen.getByText('BBS');
  const navDropDownElEn = screen.getByText('English');
  const navDropDownElJa = screen.getByText('日本語');

  expect(navBarBrandEl).toBeInTheDocument();
  expect(navDropDownElEn).toBeInTheDocument();
  expect(navDropDownElJa).toBeInTheDocument();

  const enBoards = screen.getByTestId('en-boards');
  await user.click(enBoards);
  const enNews = within(enBoards).getByTestId('en-news');

  expect(enNews).toBeInTheDocument();
});

and the test result is

    TestingLibraryElementError: Unable to find an element by: [data-testid="en-news"]

    Ignored nodes: comments, script, style
    <div
      class="nav-item dropdown"
      data-testid="en-boards"
    >
      <a
        aria-expanded="false"
        class="dropdown-toggle nav-link"
        href="#"
        id="navbarScrollingDropdown"
        role="button"
        tabindex="0"
      >
        English
      </a>
    </div>

      18 |   const enBoards = screen.getByTestId('en-boards');
      19 |   await user.click(enBoards);
    > 20 |   const enNews = within(enBoards).getByTestId('en-news');
         |                                   ^
      21 |
      22 |   expect(enNews).toBeInTheDocument();
      23 | });

      at Object.getElementError (node_modules/@testing-library/dom/dist/config.js:40:19)
      at node_modules/@testing-library/dom/dist/query-helpers.js:90:38
      at node_modules/@testing-library/dom/dist/query-helpers.js:62:17
      at getByTestId (node_modules/@testing-library/dom/dist/query-helpers.js:111:19)
      at Object.<anonymous> (src/MyNav.test.tsx:20:35)

Environment

  • macOS 12.5
  • TypeScript 4.8.4
  • React 18.2.0
  • React-Bootstrap 2.5.0
  • testing-library/react 13.4.0

I tryed to this issue's solution but it didn't work. Thank you to read. Can anyone solve it?


Solution

  • You should click the button inside enBoards instead of enBoards itself.

    Here is the corrected snippet.

    import React from 'react';
    import { render, screen, within } from '@testing-library/react';
    import userEvent from '@testing-library/user-event';
    import MyNav from './MyNav';
    
    test('renders nav', async () => {
      render(<MyNav />);
    
      const navBarBrandEl = screen.getByText('BBS');
      const navDropDownElEn = screen.getByText('English');
      const navDropDownElJa = screen.getByText('日本語');
    
      expect(navBarBrandEl).toBeInTheDocument();
      expect(navDropDownElEn).toBeInTheDocument();
      expect(navDropDownElJa).toBeInTheDocument();
    
      const enBoards = screen.getByTestId('en-boards');
      const enBoardsButton = within(enBoards).getByRole('button');
    
      await userEvent.click(enBoardsButton);
    
      const enNews = within(enBoards).getByTestId('en-news');
    
      expect(enNews).toBeInTheDocument();
    });