Search code examples
reactjsunit-testingjestjsaccessibilityreact-testing-library

How do I tab to a non-standard element while unit-testing for keyboard accessibility?


Background

I have a React/TypeScript project that I test with Jest and React Testing Library (RTL). I have one passing test that tests keyboard accessibility with RTL's userEvent.tab() by tabbing to a button.

  test('that the button is keyboard accessible', () => {
    // Arrange
    const page = render(<BrowserRouter><Page/></BrowserRouter>).container;
    const button: HTMLElement = within(page).getByText('Form button');

    // Act
    for (let i = 0; i < 5; ++i) {
      userEvent.tab();
    }
    
    // Assert
    expect(button).toHaveFocus();
  });

Problem

I have another similar keyboard accessibility test that fails:

  test('that My button is keyboard accessible', () => {
    // Arrange
    const nextPage: HTMLElement = render(<NextPage />).container;
    const button = screen.getByText('My button');

    // Act
    for (let i = 0; i < 3; ++i) {
      userEvent.tab();
    }

    // Assert
    expect(button).toHaveFocus();
  });

The error:

Expected element with focus:
      <my-button>My button</my-button> // Note: `<my-button>` is a button component specific to the design system my company uses.
    Received element with focus:
      <body><div><div class="nextPage">... more tags here...<div class="bodyButtons"><my-button>My button</my-button></div></div></div></div></body>

No matter how many times I userEvent.tab(), the received element with focus is always <body>. I assume this is due to attempting to target a non-standard button (<my-button>) that is part of my company's design system.


Question

How do I keyboard to the Account button on this page with my unit test? Keyboarding on the deployed app works just fine.


Things I've tried

  • within per the first unit test
  • Changing the number of userEvent.tab()s from 0 to 1 to 2 to 5 to 10
  • fireEvent instead of userEvent
  • Rendering the component without the container
  • Initially using button.focus() before tabbing, but I can't focus the button either
  • Changing <my-button> to <button> to use a standard HTML tag
  • Getting the button with querySelector('my-button')

Solution

  • How do I keyboard to the Account button on this page with my unit test?

    Solution

    I gave <my-button> the tabindex=0 attribute. This made the button programmatically focusable.

    The product code:

    <my-button onClick={handleClick} tabIndex='0'>My button</my-button>
    

    The test:

      test('that My button is keyboard accessible', () => {
        // Arrange
        render(<NextPage/>);
        const button: HTMLElement = screen.getByText('My button');
    
        // Act
        for (let i = 0; i < 3; ++i) {
          userEvent.tab();
        }
    
        // Assert
        expect(button).toHaveFocus();
      });
    
    

    More info from MDN on tabIndex