react-testing-library: .focus is not focusing on the input
Hi, thanks for the library. I think I have found a bug and I want to get some help here.
- react-testing-libraryversion: 5.4.4
- reactversion: 16.5.2
Relevant code or config:
import React from 'react'
import { render, fireEvent } from 'react-testing-library'
it('focus on the input', () => {
  const { container } = render(<input type="text" />)
  fireEvent.focus(container.querySelector('input'))
  expect(document.activeElement).toBe(container.querySelector('input'))
})
What you did:
I want to focus on an input and assert about that.
What happened:
The test fails to check the activeElement. The received element is the body.
Reproduction:
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Comments: 19 (7 by maintainers)
Commits related to this issue
- docs: add skovy as a contributor (#276) — committed to lucbpz/react-testing-library by allcontributors[bot] 4 years ago
I’m not sure if it’s the same thing, but
fireEvent.focus()never works for me. I have always to useact(() => element.focus()).Confirmed that fireEvent.focus() doesn’t work correctly.
This returns
document.bodyin console (the default behaviour when document.activeElement is undefined):This works correctly:
I believe the same issue may be occurring with all other fireEvent’s as well, which would explain why a lot of my tests fail that use
fireEvent.click().fireEventinternally uses thedispatchEventmethod of the element which only triggers the bound handlers to that event. It does not trigger the UI (browser) behaviour of the element that received the event.So if you bind events for the
focusevent they will be triggered, but the element will not receive focus. You could runcontainer.querySelector('input').focus()for that which will trigger the UI behaviour, but there might be problems with that in a headless browser environment.Updated reproduction:
Had the same problem as @phuwin95.
Writing a pretty simple test. I have a component which renders a
labelwithhtmlForand a correspondinginput. Test should verify that whenever I click onlabel,inputis focused.I made it work after replacing
fireEvent.click(label)withuserEvent.click(label)from@testing-library/user-event:import userEvent from '@testing-library/user-event'In my experience, focus is not assigned instantaneously to an element. It happens asynchronously, so you may want to wrap that
expectinside awaitcall:Also, if this still does not work, it is most likely a jsdom issue rather than an issue with this library. This library is not responsible for setting
document.activeElement. Still not likely if you’re reproducing it on CodeSandbox, which does not use jsdom, but the actual browser instead. It could be a problem with how the focus event is simulated to be fired by this library. But I’d try the above first to make sure.Finally, take a look at jest-dom’s
toHaveFocuscustom matcher, the way it works and the way it is being tested.It absolutely should matter what your activeElement is. It’s a cursor for screen reader users.
I don’t understand to what you’re referring. I just wanted to say that if you want to test if an element got focused then you should query that element and check if it’s equal to the activeElement i.e. use
toHaveFocusfromjest-dom. Testing if the focus event was fired allows cheating by callingfireEvent.focuswhich is dispatched regardless of whether the element in question can even receive focus.You could write
which would pass. But the element will never be the active element nor will it receive any focus event in your actual app which leads to the question: What behavior are you testing here?
So if we run this in the browser, does it work? If that’s the case then maybe we can fix it in JSDOM? That’s probably best.
I would bet that you’re focusing elements that can’t receive focus (e.g. they have no tabIndex). Or you’re dispatching change events without adding a value. For debug purposes I would make sure that handleChange is actually called and if it even reaches the
.focus()line.I thought I was having this problem as well yesterday but it turns out the component I was testing was rendering other components and one of them was debouncing the input of the text input I was changing the value on. so if I just
waited that the focus did, in fact, work as expected.