7

Note: this isn't about simulating the keypress itself

I'm creating a component which handles focus based on user keyboard events (like pressing enter, arrows, etc).

It'd be preferable to test that the component ignores the tab key on keydown.

However, on firing the keydown tab event, the focus doesn't change like it would in a browser.


Given the react component in Component.js

import React from 'react'

export default () => (
  <>
    <button data-testid="one">
      one
    </button>
    <button data-testid="two">
      two
    </button>
  </>
)

and the following test Component.test.js

import React from 'react'
import 'jest-dom/extend-expect'
import { cleanup, fireEvent, render, wait } from 'react-testing-library'
import Component from './Component'

afterEach(cleanup)

it('changes focus on tab', async () => {
  const { getByTestId } = render(<Component />)
  const one = getByTestId('one')
  const two = getByTestId('two')

  one.focus()

  expect(one).toHaveFocus()

  fireEvent.keyDown(one, {
    key: 'Tab',
    code: 9,
    charCode: 9
  })

  await wait(() => {
    expect(two).toHaveFocus()
  })
})

the test fails, as the element data-testid="one" still has focus.

See CodeSandbox for an editable version of this code

evolutionxbox
  • 3,932
  • 6
  • 34
  • 51
  • 1
    Possible duplicate of [Simulate keydown on document for JEST unit testing](https://stackoverflow.com/questions/33638385/simulate-keydown-on-document-for-jest-unit-testing) – Pants Apr 30 '19 at 13:56
  • @Pants I guess it could be. Couple of questions... Are they the same library? This question doesn’t ask about simulating key press on the document. – evolutionxbox Apr 30 '19 at 13:58

2 Answers2

11

A working solution nowadays would be to use userEvent.tab() instead of fireEvent.keyDown().

import '@testing-library/jest-dom'
import userEvent from '@testing-library/user-event'

import { render, screen } from '@testing-library/react'
import Component from './buttons'

it('changes focus on tab', async () => {
  render(<Component />)
  const one = screen.getByTestId('one')
  const two = screen.getByTestId('two')

  one.focus()
  expect(one).toHaveFocus()

  await userEvent.tab()
  expect(two).toHaveFocus()
})
Dan Barclay
  • 5,827
  • 2
  • 19
  • 22
Dobby Dimov
  • 944
  • 8
  • 10
-1

You can simply do this with react-testing-library itself. All you have to do is this:

Use the fireEvent.blur(<your-input-element-here>)

import { fireEvent } from '@testing-library/react';

it('changes focus on tab', async () => {
  render(<Component />)
  const one = screen.getByTestId('one')
  const two = screen.getByTestId('two')

  // fires the onBlur event
  fireEvent.blur(one)

  expect(one).not.toHaveFocus()
})
the_haystacker
  • 1,585
  • 18
  • 20
  • tabbing should cause the next element to be focused, you shouldn't have to unfocus the current element manually – conor909 Dec 10 '22 at 10:23