user-event: userEvent.type not working in v14

Reproduction example

zip file listed bellow

Prerequisites

please download this project demo and run it using npm install react-testing-library-bug.zip

it has 2 tests: 1- testThatFails 2- testThatWorks

in thre first example, test fails because and can not type into coponent using userEvent.type:

image code for failing test:


const ExampleThatFails = () => {
  const [state, dispatch] = useReducer(reducer, [
    {
      name: "Age",
      show: true,
      filterValueType: "integer",
      value: ["", ""],
      optionType: "exact",
      key: "age",
    }
  ]);

  return (
    <div>
      <FilterCombo filters={state} dispatch={dispatch} />
      <FilterItem dispatch={dispatch} idx={0} filter={state[0]} />
    </div>
  );
};

it.each(Array(100).fill(null))("ExampleThatFails", async () => {
  render(<ExampleThatFails />);

  const AgeFilterOption = screen.getAllByTestId("filter-item")[0];
  const AgeSelectBox = within(AgeFilterOption).getByRole("button", {
    name: "exact",
  });
  await userEvent.click(AgeSelectBox);
  const AllAgeOptions = within(AgeFilterOption).getAllByRole("option");

  await userEvent.click(AllAgeOptions[1]);

  const numberInputs = screen.getAllByRole("spinbutton");

  await userEvent.type(numberInputs[0], "12333");
  expect(numberInputs[0]).toHaveDisplayValue("12333");
},10000);


code for working test:


const ExampleThatWorks = () => {
  const [state, dispatch] = useReducer(reducer, [
    {
      name: "Age",
      show: true,
      filterValueType: "integer",
      value: ["", ""],
      optionType: "between",
      key: "age",
    }
  ]);

  return (
    <div>
      <FilterItem dispatch={dispatch} filter={state[0]} idx={0} />
    </div>
  );
};

it.each(Array(100).fill(null))("ExampleThatWorks", async () => {

  render(<ExampleThatWorks />);

  const numberInputs = screen.getAllByRole("spinbutton");
  await userEvent.type(numberInputs[0], "12333");
  expect(numberInputs[0]).toHaveDisplayValue("12333");
},10000);

Expected behavior

expected to pass all tests on v14

Actual behavior

testThatFails fails in v14, but when using v13.5 it works properly testTahtWorks passes on both versions

User-event version

14.1.1(actually all v14 dists act the same)

Environment

{
  "name": "userevent-react",
  "keywords": [],
  "description": "",
  "private": true,
  "dependencies": {
    "@headlessui/react": "1.7.16",
    "@testing-library/jest-dom": "5.16.3",
    "@testing-library/react": "14.0.0",
    "@testing-library/user-event": "^14.1.1",
    "jest": "29.5.0",
    "react": "18.0.0",
    "react-dom": "18.0.0",
    "react-scripts": "^5.0.1"
  },
  "devDependencies": {
    "husky": "^4.2.3",
    "jest-environment-jsdom": "^29.6.2",
    "lint-staged": "^10.0.8",
    "prettier": "^1.19.1",
    "ts-jest": "^29.1.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "format": "prettier --write \"**/*.+(js|json|css|md|mdx|html)\""
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": [
    ">0.2%",
    "not dead",
    "not ie <= 11",
    "not op_mini all"
  ]
}

Additional context

No response

About this issue

  • Original URL
  • State: open
  • Created a year ago
  • Reactions: 22
  • Comments: 23

Most upvoted comments

I have tried your example with

it.each(Array(100).fill(null))("ExampleThatFails",
  async () => {
    const user = userEvent.setup({ delay: null }); // <-- added
    render(<ExampleThatFails />);

    // replaced every userEvent with user

and that seems to work. Why? I don’t know. But maybe it’s helpful.

Still broken 😦

Same issue, it seems the onChange events are fired properly simulating the typing but when I check the input.value it doesn’t change.

UPDATE: I found a solution for my scenario. I was replacing the value property of the input from props on every change, that works on browser, but it seems it causes conflict when testing. I just changed the value for defaultValue in the input element and the error was solved.

setting up userEvent first and using await works for me:

it("should be able to type username", async () => {
  const user = userEvent.setup()
  render(<App />);
  const element = screen.getByLabelText("Username") as HTMLInputElement;

  await user.type(element, "Admin");
  expect(element.value).toBe("Admin");
}); 

I await it and it doesnt work for me, neither the other suggestions

I’m getting the same behavior as well (in a project with lots of dependencies). I worked around the issue by refactoring to one call to userEvent.type per character that I want to type.