CodePark/ questions / User interactions
START PRACTICINGPRACTICE
Frontend question · Testing › User interactions

How would you test that a dialog is keyboard accessible: Escape closes it and focus returns to the button that opened it?

test("Escape closes the dialog and restores focus", async () => {
  const user = userEvent.setup();
  render(<DeleteButton />);
  const trigger = screen.getByRole("button", { name: /delete/i });
  await user.click(trigger);
  expect(screen.getByRole("dialog")).toBeInTheDocument();
  await user.keyboard("{Escape}");
  expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
  expect(trigger).toHaveFocus();
});

Open the dialog with user-event, press Escape, then assert that the dialog is gone and the trigger button has focus again.

The steps:

  • Render the component.
  • Open the dialog with user.click.
  • Check it's visible with getByRole('dialog').
  • Press Escape with await user.keyboard('{Escape}').
  • Assert the dialog is gone with queryByRole.
  • Assert the trigger button has focus with jest-dom's toHaveFocus().

You can test focus order the same way. await user.tab() moves focus like the Tab key. Then you assert which element has focus now.

user-event moves focus and fires real key events. So these tests catch broken focus traps or missing key handlers that fireEvent would hide. A focus trap keeps Tab focus inside the dialog.

jsdom (the fake DOM used in tests) doesn't do layout, so it never calculates what the page looks like. Visual details like a focus ring belong in E2E or visual tests.

  • Use user.keyboard('{Escape}') to press special keys.
  • Use user.tab() to move focus like the Tab key.
  • Use toHaveFocus() to assert which element has focus.
  • Use queryByRole('dialog') to assert the dialog closed.