Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problem testing keyboard interaction with select element #786

Open
JulianRoesner opened this issue Nov 17, 2021 · 3 comments
Open

Problem testing keyboard interaction with select element #786

JulianRoesner opened this issue Nov 17, 2021 · 3 comments
Labels
enhancement New feature or request

Comments

@JulianRoesner
Copy link

  • @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.

@ph-fritsche ph-fritsche added the enhancement New feature or request label Nov 17, 2021
@ph-fritsche
Copy link
Member

Thanks for opening this issue ❤️

I created a codesandbox with your tests: https://codesandbox.io/s/trusting-platform-r8v1p?file=/src/App.test.js

We lack behaviorPlugin for the special meaning of control and functional keys on HTMLSelectElement.
A PR adding them at https://github.com/testing-library/user-event/tree/alpha/src/keyboard/plugins would be welcome.
Probably need some extra implementation on HTMLSelectElement to keep track of focus inside it that would go in https://github.com/testing-library/user-event/tree/alpha/src/document.

Some remarks on the tests though:
We recommend against using render and user interactions in beforeEach. Putting them into the test function makes the test easier to read and prevents some ugly bugs that can happen with asynchronous failures in beforeEach.
{space} will be removed in v14. Use a space character (for key's key) or [Space] (the descriptor for key code) instead.
In the browser [Space][ArrowDown][ArrowDown][Space] results in the third option being selected with the options still open.

@JulianRoesner
Copy link
Author

Thanks so so much for your fast answer and your remarks 😍

In the browser [Space][ArrowDown][ArrowDown][Space] results in the third option being selected with the options still open.

I think I do not really understand what you mean by that. Do you mean that replacing the parameter to user.keyboard with [Space][ArrowDown][ArrowDown][Space] would work? Because I do not seem to get it to work in the sandbox.

We lack behaviorPlugin for the special meaning of control and functional keys on HTMLSelectElement.
A PR adding them at https://github.com/testing-library/user-event/tree/alpha/src/keyboard/plugins would be welcome.

I'll have a look into it. Unfortunately, because I have no experience in typescript I can not promise that I'll be able to fix that.

@ph-fritsche
Copy link
Member

I think I do not really understand what you mean by that. Do you mean that replacing the parameter to user.keyboard with [Space][ArrowDown][ArrowDown][Space] would work? Because I do not seem to get it to work in the sandbox.

No it won't work in the test because the default behavior on the select is not implemented yet.
{space}{arrowdown}{arrowdown}{space} should be written as [Space][ArrowDown][ArrowDown][Space] using keys code as descriptor as {space} is deprecated and won't describe the Space key in v14 any more.
But main point was that if you focus the <select> in the browser and then press+release the keys described you end up with a different state than expected in the test.

I'll have a look into it. Unfortunately, because I have no experience in typescript I can not promise that I'll be able to fix that.

Don't worry. The actual implementation is the least of concerns. Researching what is to be the expected behavior according to specs and living standard is usually the much bigger part of the work needed to solve such issues.
So if you could look into this and especially if and how the programmatically available IDL props change and possibly diverge from the displayed UI when performing these interactions, this would be a major contribution towards resolving this.

programmiri added a commit to Aiven-Open/klaw that referenced this issue Jan 6, 2023
- behavior for control and functional keys on HTMLSelectElement
  are not supported in user-event / testing lib at the moment, see
  testing-library/user-event#786

Signed-off-by: Mirjam Aulbach <mirjam.aulbach@aiven.io>
programmiri added a commit to Aiven-Open/klaw that referenced this issue Jan 9, 2023
- behavior for control and functional keys on HTMLSelectElement
  are not supported in user-event / testing lib at the moment, see
  testing-library/user-event#786

Signed-off-by: Mirjam Aulbach <mirjam.aulbach@aiven.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants