skip to Main Content

In my React Testing Library unit test, I am unable to get the Navlink by role.

getByRole('link', { name: 'help' }) - This fails

The Navlink does not have text, rather it has a help icon which seems to be the problem.

The error I recieve is:

TestingLibraryElementError: Unable to find an accessible element with the role "link" and name "help"

Here are the accessible roles:

link:

Name "Home":
<a
class="active"
href="home"
name="home"
/>

Name "About":
<a
class="inactive"
href="/about"
name="about"
/>

Name "":          <--- No name for link with icon
<a
class="inactive"
href="/help"
name="help"
/>

My NavBar:

const NavBar = () => {

  return (
    <>
        <div className="navbar">
            <ul>
                <li>
                  <NavLink to="home" className={({isActive}) => (isActive ? 'active' : 'inactive')}>Home</NavLink>
                </li>
                <li>
                  <NavLink to="about" className={({isActive}) => (isActive ? 'active' : 'inactive')}>About</NavLink>
                </li>
                <li>
                    <NavLink to="help" name="help" className={({isActive}) => (isActive ? 'active' : 'inactive')}><FaQuestionCircle size='1.5rem' className="helpicon"/></NavLink>
                </li>
            </ul> 
        </div>
    </>
  );
};

export default NavBar;

The failing unit test:

describe(Navbar, () => {

    const mockedNavigation = jest.fn();
    
    it('should navigate to help page', () => {
        
        const { getByRole } = render(<Navbar />, { wrapper: Router });
        fireEvent.click(getByRole('link', { name: 'help' }));

        expect(mockedNavigation).toHaveBeenCalledWith(
            "help", 
            {
                preventScrollReset: undefined,
                relative: undefined,
                replace: false,
                state: undefined,
                unstable_viewTransition: undefined
            });
    });
});

How do I select a Navlink that does not have text, but uses an icon instead?
Maybe I can use classNames ? Add an additional class as well as the active/inactive?
Another method would be easier though and more readable.

Any help appreciated, thanks again!

2

Answers


  1. The link role should technically work, but if you have trouble targeting a component you have a few other options.

    • Add a data-testid attribute to the element and use the *byTestId query.

      https://testing-library.com/docs/queries/bytestid

      const NavBar = () => {
        return (
          <div className="navbar">
            <ul>
              <li>
                <NavLink
                  data-testid="link-home"
                  to="home"
                  className={({ isActive }) => (isActive ? 'active' : 'inactive')}
                >
                  Home
                </NavLink>
              </li>
              <li>
                <NavLink
                  data-testid="link-about"
                  to="about"
                  className={({ isActive }) => (isActive ? 'active' : 'inactive')}
                >
                  About
                </NavLink>
              </li>
              <li>
                <NavLink
                  data-testid="link-help"
                  to="help"
                  className={({ isActive }) => (isActive ? 'active' : 'inactive')}
                >
                  <FaQuestionCircle size='1.5rem' className="helpicon" />
                </NavLink>
              </li>
            </ul> 
          </div>
        );
      };
      
      const { getByTestId } = render(<Navbar />, { wrapper: Router });
      fireEvent.click(getByTestId('link-help'));
      
    • You can also always use querySelector on the rendered container.

      https://testing-library.com/docs/queries/about#manual-queries

      const NavBar = () => {
        return (
          <div className="navbar">
            <ul>
              <li>
                <NavLink
                  to="home"
                  className={({ isActive }) => (isActive ? 'active' : 'inactive')}
                >
                  Home
                </NavLink>
              </li>
              <li>
                <NavLink
                  to="about"
                  className={({ isActive }) => (isActive ? 'active' : 'inactive')}
                >
                  About
                </NavLink>
              </li>
              <li>
                <NavLink
                  to="help"
                  className={({ isActive }) => (isActive ? 'active' : 'inactive')}
                >
                  <FaQuestionCircle size='1.5rem' className="helpicon" />
                </NavLink>
              </li>
            </ul> 
          </div>
        );
      };
      

      A Query similar to "a[href='/help']" or "a[href='/help'].inactive":

      const { container } = render(<Navbar />, { wrapper: Router });
      fireEvent.click(container.querySelector("a[href='/help'].inactive"));
      

    Take a look through the About Queries documentation, there may be other options available to you as well depending on implementation or information that you may’ve omitted in your post that we’re unaware of.

    Login or Signup to reply.
  2. The <NavLink/> component can’t accept the name property. If you use TypeScript, you will get a TS-type error:

    Type '{ children: Element; to: string; name: string; className: ({ isActive }: NavLinkRenderProps) => "active" | "inactive"; }' is not assignable to type 'IntrinsicAttributes & NavLinkProps & RefAttributes<HTMLAnchorElement>'.
      Property 'name' does not exist on type 'IntrinsicAttributes & NavLinkProps & RefAttributes<HTMLAnchorElement>'.ts(2322)
    

    Instead, you can pass a title prop to the icon component as the accessible name of the <NavLink/>. E.g.

    import React from 'react';
    import { NavLink } from 'react-router-dom';
    import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
    import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons';
    
    const NavBar = () => {
      return (
        <>
          <div className="navbar">
            <ul>
              <li>
                <NavLink to="/home" className={({ isActive }) => (isActive ? 'active' : 'inactive')}>
                  Home
                </NavLink>
              </li>
              <li>
                <NavLink to="/about" className={({ isActive }) => (isActive ? 'active' : 'inactive')}>
                  About
                </NavLink>
              </li>
              <li>
                <NavLink to="/help" className={({ isActive }) => (isActive ? 'active' : 'inactive')}>
                  <FontAwesomeIcon icon={faQuestionCircle} className="helpicon" title="help" />
                </NavLink>
              </li>
            </ul>
          </div>
        </>
      );
    };
    
    export default NavBar;
    

    NavBar.test.tsx:

    import React from 'react';
    import { fireEvent, render, screen } from '@testing-library/react';
    import '@testing-library/jest-dom';
    import NavBar from './NavBar';
    import { MemoryRouter } from 'react-router-dom';
    
    describe('Navbar', () => {
      it('should navigate to help page', () => {
        render(
          <MemoryRouter initialEntries={['/help']}>
            <NavBar />
          </MemoryRouter>,
        );
        const navLink = screen.getByRole('link', { name: 'help' });
        console.log('🚀 ~ it ~ navLink:', navLink);
        fireEvent.click(navLink);
        expect(navLink).toHaveClass('active');
      });
    });
    

    Test result:

     PASS  stackoverflow/77897051/NavBar.test.tsx
      Navbar
        √ should navigate to help page (109 ms)                                                                                                                                                                                                          
                                                                                                                                                                                                                                                         
    Test Suites: 1 passed, 1 total                                                                                                                                                                                                                       
    Tests:       1 passed, 1 total                                                                                                                                                                                                                       
    Snapshots:   0 total
    Time:        1.627 s
    Ran all test suites related to changed files.
    

    package version:

    "@fortawesome/fontawesome-svg-core": "^6.5.1",
    "@fortawesome/free-solid-svg-icons": "^6.5.1",
    "@fortawesome/react-fontawesome": "^0.2.0",
    "react": "^18.2.0",
    "react-router-dom": "^6.21.1",
    
    "jest": "^29.7.0",
    "jest-environment-jsdom": "^29.7.0",
    "@testing-library/jest-dom": "^6.1.4",
    "@testing-library/react": "^14.1.2",
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search