Today I learned: How to assert the new active element after Tab keydown event in Jest
Posted on
Context
Today I was writing a test that would assert that by using the Tab key onto the button of a component, the focus will move to the next focusable element.
The test was failing, although the expected behaviour was to be seen in the browser.
Failing test
Below createKeydownEvent
is a custom utility function.
it('once focused on the button, it can be tabbed away', () => {
// ...
const keydownTabEvent = createKeydownEvent('Tab');
const button = component.querySelector('button');
const nextInteractiveElement = document.querySelector('a');
button.focus();
expect(document.activeElement).toBe(button);
button.dispatchEvent(keydownTabEvent);
expect(document.activeElement).toBe(nextInteractiveElement);
});
The expect
fails because the expected document.activeElement
happens to be
the button, as if the Tab keydown
event has no effect what so ever.
Solution
After some intense debugging, googling and reaching out to colleagues I came down to this conclusion:
When an element looses focus, the next element to acquire it actually gets it on the next tick of the event loop.
Calling setTimeout(callback, 0)
, where callback
does the assertion, would
execute its callback at the end of the next tick i.e. when the next element
acquires the focus.
Equiped with this knowledge I could fix the failing test by wrapping the
assertion into a setTimeout(callback, 0)
call as show below:
setTimeout(() => {
expect(document.activeElement).toBe(nextInteractiveElement);
}, 0);