testing-library / testing-library/user-event

Problem testing keyboard interaction with select element

Open
#786 4 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement
Dominant language
TypeScript
Stars
2.3k
Forks
258
PR merge metrics
No merged PRs in 30d

Description

  • @testing-library/user-event version: 13.5.0
  • Testing Framework and version: jest (version 26.6.0 )
  • DOM Environment: jsdom (version 16.7.0)

Relevant code:

import React from "react";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";

describe("When rendering dropdown", () => {
  beforeEach(() => {
    render(
      <select>
        <option value="A">First Option</option>
        <option value="B">Second Option</option>
        <option value="C">Third Option</option>
      </select>);
  });

  describe("When second option has been selected", () => {
    beforeEach(() => {
      userEvent.selectOptions(screen.getByRole("combobox"), ["B"]);
    });

    it("Only second option has been selected", () => {
      expect(screen.getByRole("option", { name: "First Option" }).selected).toBe(false);
      expect(screen.getByRole("option", { name: "Second Option" }).selected).toBe(true);
      expect(screen.getByRole("option", { name: "Third Option" }).selected).toBe(false);
    });
  });

  describe("When pressing tab", () => {
    beforeEach(() => {
      userEvent.tab();
    });

    it("set focus on dropDown", () => {
      expect(screen.getByRole("combobox")).toHaveFocus();
    });

    describe("When pressing space and 2x arrowDown", () => {
      beforeEach(() => {
        userEvent.keyboard("{space}");
        userEvent.keyboard("{arrowdown}");
        userEvent.keyboard("{arrowdown}");
        userEvent.keyboard("{space}");
      });

      it("Only second option has been selected", () => {
        expect(screen.getByRole("option", { name: "First Option" }).selected).toBe(false);
        expect(screen.getByRole("option", { name: "Second Option" }).selected).toBe(true);
        expect(screen.getByRole("option", { name: "Third Option" }).selected).toBe(false);
      });
    });

    describe("When pressing space and 2x arrowDown", () => {
      beforeEach(() => {
        userEvent.type(screen.getByRole("combobox"), "{space}{arrowdown}{arrowdown}{space}");
      });

      it("Only second option has been selected", () => {
        expect(screen.getByRole("option", { name: "First Option" }).selected).toBe(false);
        expect(screen.getByRole("option", { name: "Second Option" }).selected).toBe(true);
        expect(screen.getByRole("option", { name: "Third Option" }).selected).toBe(false);
      });
    });
  });
});

What you did:
I ran the tests above.

What happened:
The tests failed with the following message.

  ● When rendering dropdown › When pressing tab › When pressing space and 2x arrowDown › Only second option has been selected

    expect(received).toBe(expected) // Object.is equality

    Expected: false
    Received: true

      43 |
      44 |       it("Only second option has been selected", () => {
    > 45 |         expect(screen.getByRole("option", { name: "First Option" }).selected).toBe(false);
         |                                                                               ^
      46 |         expect(screen.getByRole("option", { name: "Second Option" }).selected).toBe(true);
      47 |         expect(screen.getByRole("option", { name: "Third Option" }).selected).toBe(false);
      48 |       });

      at Object.<anonymous> (src/components/Select.test.js:45:79)

  ● When rendering dropdown › When pressing tab › When pressing space and 2x arrowDown › Only second option has been selected

    expect(received).toBe(expected) // Object.is equality

    Expected: false
    Received: true

      55 |
      56 |       it("Only second option has been selected", () => {
    > 57 |         expect(screen.getByRole("option", { name: "First Option" }).selected).toBe(false);
         |                                                                               ^
      58 |         expect(screen.getByRole("option", { name: "Second Option" }).selected).toBe(true);
      59 |         expect(screen.getByRole("option", { name: "Third Option" }).selected).toBe(false);
      60 |       });

      at Object.<anonymous> (src/components/Select.test.js:57:79)

Problem description:
I want to test the keyboard interaction with a select component to make sure that it also works for users who can only interact via the keyboard. However, only the first option is selected whether I add any keyboard interaction through userEvent.keyboard or userEvent.type and the tests fail. Nevertheless, interacting in the browser with the element works fine.
Through the sandbox I saw that the space bar is being kept pressed when interacting with the element. However, using "{space>}{arrowdown}{arrowdown}{/space}" does not work either.

Suggested solution:
Unfortunately, I have no idea for a solution. I am not sure if there is a problem in my code or if testing this kind of interaction is not supported by the user-event library and I would be happy if you could point me in the right direction.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by running the failing cases in src/components/Select.test.js and compare the userEvent.keyboard and userEvent.type paths with the passing selectOptions case. Then inspect the user-event entry points handling select keyboard interaction and the linked sandbox behavior. Done means keyboard navigation selects only the second option and the regression tests pass.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, react, typescript
Domain
frontend, testing-qa
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.