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

onBeforeInput not triggering with userEvent.type #858

Closed
nicholaswong31415 opened this issue Feb 22, 2022 · 13 comments
Closed

onBeforeInput not triggering with userEvent.type #858

nicholaswong31415 opened this issue Feb 22, 2022 · 13 comments
Labels
environment:react Issue specific to React
Milestone

Comments

@nicholaswong31415
Copy link

  • @testing-library/user-event version: 13.5.0
  • Testing Framework and version: jest 26.6.0

  • DOM Environment: jsdom 16.5.1

  • Node version: 15.12.0

Relevant code or config

// Does not pass
it("should trigger onBeforeInput", () => {
  const mockOnBeforeInput = jest.fn();
  render(<App onBeforeInput={mockOnBeforeInput} inputTestId="test-input" />);
  const inputElem = screen.getByTestId("test-input");
  userEvent.type(inputElem, "hello input");
  // onBeforeInput is not triggered at all when calling userEvent.type
  expect(mockOnBeforeInput).toBeCalledTimes(11);
});

What you did: use userEvent.type in a test to type 'hello input' into an input text field

What happened: onBeforeInput is not triggered when calling userEvent.type

image

Reproduction repository: https://codesandbox.io/s/sad-feather-8rbd7k?file=/src/App.test.js

I don't know if this issue is solvable since I assume userEvent.type is only concerned with triggering key events (whereas onBeforeInput and onInput are specific to input elements), but I'm just raising this in the hopes that there is a possible fix 🙏

@ph-fritsche
Copy link
Member

Support for beforeinput was added in v14.0.0-beta.11 😃

@ph-fritsche ph-fritsche added this to the userEvent v14 milestone Feb 22, 2022
@nicholaswong31415
Copy link
Author

Thanks very much!

@M162
Copy link

M162 commented May 9, 2022

It seems this issue can be reproduced again, try to run this:

test('should trigger onBeforeInput', async () => {
    const inputHandler = jest.fn();
    const changeHandler = jest.fn();
    const mockOnBeforeInput = jest.fn();

    render(
      <input
        onBeforeInput={mockOnBeforeInput}
        onInput={inputHandler}
        onChange={changeHandler}
      />,
    );

    await userEvent.type(screen.getByRole('textbox'), 'abcdef');

    expect(inputHandler).toHaveBeenCalledTimes(6);
    expect(changeHandler).toHaveBeenCalledTimes(6);
    // failed here
    expect(mockOnBeforeInput).toHaveBeenCalledTimes(6);
  });

my version is 14.1.0, could someone have a look?

@ph-fritsche
Copy link
Member

@M162 Thanks for reporting this. onBeforeInput isn't triggered, but the beforeinput event is dispatched.
We need to investigate why React discards the event.

test('React triggers `onBeforeInput`', async () => {
  const onBeforeInput = jest.fn();
  const beforeInputListener = jest.fn()

  render(<input onBeforeInput={onBeforeInput}/>);
  screen.getByRole('textbox').addEventListener('beforeinput', beforeInputListener)

  await userEvent.type(screen.getByRole('textbox'), 'abcdef');

  expect(beforeInputListener).toHaveBeenCalledTimes(6);
  expect(onBeforeInput).toHaveBeenCalledTimes(6);
});

@ph-fritsche ph-fritsche reopened this May 9, 2022
@ph-fritsche ph-fritsche added bug Something isn't working needs investigation Someone has to do research on this labels May 9, 2022
@ph-fritsche
Copy link
Member

It looks like this isn't really an issue with @testing-library/user-event, but with React catering to different non-standard browser behaviors.

The following comment suggests that the beforeinput implementation by React is based on the outdated finding
that beforeinput was not implemented by any browser:
https://github.com/facebook/react/blob/7d9e17a9826595dacbf9e19e8f85b07837adf276/packages/react-dom/src/events/plugins/BeforeInputEventPlugin.js#L413-L431
According to MDN the beforeinput event is implemented by every browser but IE today.

This leads to onBeforeInput not being triggered by beforeinput but e.g. by keypress with deprecated and/or non-standard properties: https://codesandbox.io/s/onbeforeinput-iskegw?file=/src/beforeinput.test.js

As React determines the data for beforeinput by different means depending on the environment it assumes to be executed in and these determinations are internals of React and might be subject to change without further notice,
I think even trying to add workarounds for all these branches to user-event is a bad idea - and probably out of scope anyway.
Adding those properties to keypress might be acceptable but it will only cover some cases and also break in some environments.
React also deliberately ignores that there is beforeinput for paste: https://github.com/facebook/react/blob/7d9e17a9826595dacbf9e19e8f85b07837adf276/packages/react-dom/src/events/plugins/BeforeInputEventPlugin.js#L328-L331

The workaround with the least harm is probably to make React think it is running in a Webkit-like environment and patch beforeinput with the non-standard textInput event if your component relies on onBeforeInput:
https://codesandbox.io/s/onbeforeinput-workaround-4tkyvz?file=/src/workaround.test.js

If you want to use onBeforeInput, you should probably file an issue with React. Maybe they are willing to just forward the beforeinput event and apply their workarounds only in IE.

@ph-fritsche ph-fritsche added environment:react Issue specific to React and removed bug Something isn't working needs investigation Someone has to do research on this labels May 12, 2022
@M162
Copy link

M162 commented May 21, 2022

Hi @ph-fritsche, many thanks for your help. I tried your workaround and it does work in CodeSandbox. However, the tricky thing is it does not work when I run it locally. I didn't change anything and the version of testing-library/jest-dom, testing-library/react, testing-library/user-event, react, react-dom are also all the same. Do you have any idea why it doesn't work? Thank you again.

@M162
Copy link

M162 commented May 23, 2022

I finally find the reason why it doesn't work in my local. It is because I import the 'configure' function from @testing-library/react. It will change something of the environment by just import this function. I will open an issue in react testing library repo. Again thanks for your help, now it works in my local. @ph-fritsche

@ph-fritsche
Copy link
Member

facebook/react#11211

@jwarykowski
Copy link

Hey @M162, what was the reason that configure changed the environment? Did you raise this in the react testing library? can you provide a link if so?

@M162
Copy link

M162 commented Sep 7, 2022

Hi @jwarykowski, I import 'configure' in my 'setupFilesAfterEnv' of jest configurations. If I remove the import, then everything works fine. To be honest, I don't know what happened exactly, so I guess it changed something in environment when I import it. I don't even know if it is a bug or not, so I don't raise it in react testing library repo finally.

@jwarykowski
Copy link

Hey @M162, thanks for the quick feedback. Sorry @ph-fritsche, do you have any idea why this would be the case?

@ph-fritsche
Copy link
Member

ph-fritsche commented Sep 14, 2022

The configure function is invoked and reexported from @testing-library/dom by @testing-library/react.
If it just were imported by your setup, that would make no difference to importing anything else from @testing-library/react.
However, if it is invoked, the configuration by @testing-library/react already took place and you might override the asyncWrapper and/or eventWrapper.

@jwarykowski
Copy link

Hey @ph-fritsche, thanks for the feedback, I've had a quick look and noticed that we import this in quite a few places so I'll update and try again. Thanks for your help, greatly appreciated 🙇

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
environment:react Issue specific to React
Projects
None yet
Development

No branches or pull requests

4 participants