react-testing-library icon indicating copy to clipboard operation
react-testing-library copied to clipboard

Using fireEvent.change() on a select element fires the event handler, but doesn't update state.

Open Taelkir opened this issue 4 years ago • 14 comments

Relevant code or config:

  const selectOne = screen.getByRole("combobox", { name: "My select" });
  fireEvent.change(selectOne, {
    target: { value: "OPTION1" }
  });

  expect(screen.getByText("OPTION1")).toBeInTheDocument();

What you did:

Attempting to change a <select> element and to check that different content is displayed based on what is selected. This works in a browser, but I can't get the test to recognise that.

What happened:

console.log() statements in the event handler of my component shows that the state is not changing; the fact these statements are logging at all show that the handler is being fired.

Reproduction:

Codesandbox here: https://codesandbox.io/s/react-testing-library-demo-forked-v09xi?file=/src/App.js

I've stripped down my TS project here to try and pin down the problem and am getting the same failing test behaviour.

Problem description:

I would expect the tests to change the app state in the same way as the browser does.

Suggested solution:

I'm not sure what's wrong; the event is happening, it's just the state then seems to reset to its original value.

Taelkir avatar May 06 '21 23:05 Taelkir

Thanks for the report.

This is probably an old issue. It's caused due to the timing of when the state updater function runs.

If you capture the value, the test works as expected:

handleSelectChange = (event) => {
+   const value = event.target.value;
    this.setState(
      () => {
-       return { selectValue: event.target.value };
+       return { selectValue: value };
      },
      () => {
        console.log("State updated to: ", this.state.selectValue);
      }
    );
  };

eps1lon avatar May 07 '21 08:05 eps1lon

Yep, that workaround is good for me in my project. Thanks!

Taelkir avatar May 07 '21 12:05 Taelkir

Just to be clear: The code is working fine with manual testing in a browser. It only fails with our fireEvent.change implementation. So we do want to keep an eye on this bug.

eps1lon avatar May 07 '21 12:05 eps1lon

So I am having a (maybe) similar issue both with fireEvent as well as with userEvent (if it is something else I am happy to open another ticket 🙂 )

When mocking out the callback to an onChange-handler that receives the event as first argument, the event.target seems to be reset to the actual input field after the onChange call occurs but before fireEvent returns.

See this code example:

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

it("works on normal input.", async () => {
  const callback = jest.fn();

  render(
    <input
      type="text"
      value="2"
      onChange={(event) => {
        // Here, event.target.value is "29" as expected
        console.log(`Value in Handler: ${event.target.value}`);
        callback(event);
      }}
    />
  );

  const inputElement = screen.getByDisplayValue("2");

  userEvent.type(inputElement, "9");

  const event = callback.mock.calls[0][0];

  // Here, event.target.value is only "2"
  console.log(`Value after userEvent.type returns: ${event.target.value}`);

  expect(callback).toHaveBeenCalled();
  expect(event.target.value).toBe("29");
});

});

https://codesandbox.io/s/react-testing-library-demo-forked-m8wgx?file=/src/tests/App.js

Within the callback itself, I still have the new value. But in the test assertion, it is the old value. I guess this has something to do with how react handles events and I am happy to investiage why this happens - but I thought I'd asked first if this is a known issue.

davelosert avatar Jun 17 '21 06:06 davelosert

Same error, happened after upgrade from react 17 to react 18. adding {delay: 0} fixed the problem and I have no idea, why

gugu avatar Jun 05 '22 20:06 gugu

Oh, I found, why. Both of us are missing await after userEvent.type

gugu avatar Jun 05 '22 20:06 gugu

For issues with user-event please file an issue with testing-library/user-event. This issue is just about fireEvent.change.

eps1lon avatar Jun 05 '22 21:06 eps1lon

This issue is because issue author forgot to add await in their code (me as well)

On Mon, Jun 6, 2022, at 00:01, Sebastian Silbermann wrote:

For issues with user-event please file an issue with testing-library/user-event. This issue is just about fireEvent.change.

— Reply to this email directly, view it on GitHub https://github.com/testing-library/react-testing-library/issues/908#issuecomment-1146882633, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAASLIMXPKBAFYQ5FI35U63VNUITVANCNFSM44IFHHRQ. You are receiving this because you commented.Message ID: @.***>

gugu avatar Jun 05 '22 22:06 gugu

@gugu can you share in with part we have to put the await?

I have the same issue of author.

Thanks

JuanjoOrt avatar Jun 14 '22 08:06 JuanjoOrt

const selectOne = screen.getByRole("combobox", { name: "My select" }); await fireEvent.change(selectOne, { target: { value: "OPTION1" } });

expect(screen.getByText("OPTION1")).toBeInTheDocument();

On Tue, Jun 14, 2022, at 11:18, Juanjo Ortiz wrote:

@gugu https://github.com/gugu can you share in with part we have to put the await?

I have the same issue of author.

Thanks

— Reply to this email directly, view it on GitHub https://github.com/testing-library/react-testing-library/issues/908#issuecomment-1154866217, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAASLIKU3V43BIIYPQXWPIDVPA54LANCNFSM44IFHHRQ. You are receiving this because you were mentioned.Message ID: @.***>

gugu avatar Jun 14 '22 09:06 gugu

Hi. I'm using react-select in my project and can't seem to find a way to capture the event properly. This happens due to the fact I don't have access to the event in the scope of the onChange function.

Is there another way for this to work, other than grabbing the event in the handleChange funtion?

Edit: I'm really new to react-testing-library, so if there is another way to override this behavior from fireEvent.change on a select component, please let me know. I do have to use react-select because of a restriction on the project, but any react-testing-library API will do.

Thanks in advance!

demo-Ghost avatar Sep 15 '22 12:09 demo-Ghost

Imported the react-select-event library and it magically worked. It's also mentioned in the react testing library docs in the ecosystem section.

https://testing-library.com/docs/ecosystem-react-select-event

demo-Ghost avatar Sep 19 '22 13:09 demo-Ghost

@eps1lon thanks, this workaround works for me

fattylee2021 avatar Sep 26 '22 16:09 fattylee2021

Using userEvent instead of fireEvent worked for me.

sanathp avatar Dec 22 '22 10:12 sanathp