React-testing-library: Input event doesn't update selectionEnd/selectionStart values

Created on 17 Dec 2018  路  10Comments  路  Source: testing-library/react-testing-library

  • react-testing-library version: 5.2.3
  • react version: 16.6.3
  • node version: 11.2.0
  • npm (or yarn) version: yarn 1.12.3

Relevant code or config:

fireEvent.input(element, {
    code: 77,
    which: 77,
    key: "m"
})
// expect `element.selectionEnd` to equal `1`, but it doesn't

What you did:

Asserted that an input event would trigger the selection values for an import to be incremented as each letter is added.

What happened:

The selectionEnd and selectionStart values are always 0.

Reproduction:

Run the test __tests__/hello.js:
https://codesandbox.io/s/7j481zo946

Problem description:

I have a screen that uses the selectionEnd and selectionStart values to determine whether they need to show another element in the UI. At the moment the other element never displays in my tests because the values are always 0.

I've also tried adding selectionEnd and selectionStart to the object in the input call but that doesn't make any difference. Nor does emitting other more realistic events, such as keyDown and keyPress.

Suggested solution:

Sorry, I'm not sure 馃檲

Most helpful comment

This was fixed recently in jsdom 16 (related issue: https://github.com/jsdom/jsdom/issues/2787)

Using that in my jest testing environment solved the problem for me.

I'm using create-react-app, and you can set the jsdom environment by installing the the package jest-environment-jsdom-sixteen and then passing the --env=jsdom-sixteen flag to your react-scripts test call, e.g.:

// in package.json

"scripts": {
    "test": "react-scripts test --env=jsdom-sixteen"
}

using @testing-library/user-event and calling user.type('something') in my tests now updates selectionStart correctly in my components.

Edit:

In case this breaks a few of your tests (as it did mine), you could also move the tests that need jsdom-sixteen into as separate file, and at the top of that file add:

/**
 * @jest-environment jsdom-sixteen
 */

to use jsdom-sixteen in only that file.

All 10 comments

Hi @Crispioso 馃憢

Hmm... I'm not sure. Could you dig a little bit to see if you can figure out what's wrong here?

You are listening to onChange-event but you aren't dispatching the change-event. I would suggest to try user-event which tries to dispatch all the different events that gets dispatched when typing e.g. userEvent.type(elem, 'm')

I am only not sure whether jsdom would update the selectionStart and selectionEnd-variables when you are sending these events. Maybe elem.select() would work

Thanks for the responses! My bad on listening to the wrong event, that was a typo in this example. Changing it to firing a change event doesn't fix it though 馃槩

I too have been unsure whether jsdom even supports updating the selection data on the typing events. I can see there are tests for the setSelectionRange API, but this doesn't mean it works when that API isn't explicitly used.

I've tried mimicking the exact events emitted in Chrome from the monitorEvents function, but had no luck. I'll try user-event though, thank you! I'm not sure what user-event is though, is it a separate library?
Edit: I've just found the 'user event' library 馃帀

I've tried to do some digging into it but had started to hit a bit of a brick wall (most likely just with my knowledge). Perhaps the next step will be investigating into jsdom support a bit more though 馃檪

Yeah, looks like jsdom isn't updating those fields when you send the different events. Looks like you would need to mess with setSelectionRange. I am not sure what the expected behaviour is, you might want to raise an issue with jsdom?

Let us know what you think of user-event I think it's a great little helper companion for react-testing-library.

I'm pretty sure this is not something that react-testing-library can do anything about so I'm going to close this issue. Pretty sure it's a jsdom issue.

There doesn't seem to be a workaround for this. I set selectionStart before a fire the event, but that value seems to get reset to 0 every time. I also haven't been able to find any issues in jsdom related to this.

I'm currently trying to write a test for a third-party masking component that relies on selectionStart and selectionEnd. In their own tests they set selectionStart before using TestUtils.Simulate. I can't seem to get this to work with react-testing-library.

This is very frustrating, and does appear to be a bug with jsdom. It's something I'd like to figure out how to fix though :)

I banged my head on this a bunch the last few days while trying to test a page on our site that uses https://github.com/sanniassin/react-input-mask/

I started out writing a "simple" test case reproduction:
https://codesandbox.io/s/react-input-mask-jest-dom-bug-75m3q

But it grew a little out of control. If you hit that "browser" page (https://75m3q.csb.app/) and click the "test auto-populate" button, it actually uses testing-library to fill out the react-input-mask field. Things work in the browser. If you try that same thing in a jsdom environment, things don't work.

I'm pretty sure the browser does some stuff to selection start/end when a change event is fired, that jsdom does not do.

If you look at the app.test.js file in that codesandbox, there are some assertions that are commented out. I _think_ all of those should work. But due to the jsdom environment, they fail. I'm going to debug a little further, but might log a bug in the jsdom repo and refer back to this page (when I find out a little more about what the actual "bug" is).

This was fixed recently in jsdom 16 (related issue: https://github.com/jsdom/jsdom/issues/2787)

Using that in my jest testing environment solved the problem for me.

I'm using create-react-app, and you can set the jsdom environment by installing the the package jest-environment-jsdom-sixteen and then passing the --env=jsdom-sixteen flag to your react-scripts test call, e.g.:

// in package.json

"scripts": {
    "test": "react-scripts test --env=jsdom-sixteen"
}

using @testing-library/user-event and calling user.type('something') in my tests now updates selectionStart correctly in my components.

Edit:

In case this breaks a few of your tests (as it did mine), you could also move the tests that need jsdom-sixteen into as separate file, and at the top of that file add:

/**
 * @jest-environment jsdom-sixteen
 */

to use jsdom-sixteen in only that file.

@wilcoxmd thanks for example. Used it to open related issue in @testing-library/user-event where input.selectionStart is not being taken into consideration when changing input through userEvent.type(input, "foobar")

Issue: https://github.com/testing-library/user-event/issues/236
Test implementing above suggestion:
Edit Input selectionStart test

@ivarprudnikov no problem. Yea my use case didn't require entering text at specific indexes in the input. My component code is doing things based on the target's selectionStart in the onChange event handler; I'm not updating values explicitly in the test files.

I see a few things in the sandbox, I'll comment over on the issue you opened (https://github.com/testing-library/user-event/issues/236).

Was this page helpful?
0 / 5 - 0 ratings