-
Notifications
You must be signed in to change notification settings - Fork 255
Description
@testing-library/user-eventversion: 12.4.0- Testing Framework and version: Jest (using
react-scripts, version: 3.4.3) - DOM Environment:
@testing-library/jest-dom(5.11.6)
Relevant code or config
I'm using useState here just to be able to display the event.target.selectionStart value and demonstate the issue.
// component
export default function App() {
const [selectionStart, setSelectionStart] = useState(0);
const onChangeHandler = (event) => {
setSelectionStart(event.target.selectionStart);
};
return (
<div className="App">
<input onChange={onChangeHandler} data-testid="input" />
<code>
event.target.selectionStart:{" "}
<span data-testid="selectionStartValue">{selectionStart}</span>
</code>
</div>
);
}// test
userEvent.type(input, "123{arrowleft}{arrowleft}{backspace}");
const selectionStartValue = screen.getByTestId("selectionStartValue");
expect(selectionStartValue).toHaveTextContent("0"); // it fails - the value is 2 insteadWhat you did:
I discovered it when writing a Jest test for a React component which is an input reacting to onChange and relying heavily on the event.target.selectionStart value passed in this event. I used userEvent.type in the test.
What happened:
I noticed that when the cursor is not at the end of the text in the input and backspace is pressed, the value of event.target.selectionStart provided by userEvent.type is different from the value I get in my browser.
In order to see a more tangible example, please see the CodeSandbox project I created and linked below and follow these steps:
- Type "abc" in the input
- Hit left arrow button on your keyboard twice
- Hit backspace
- Check the value of
event.target.selectionStart
Result in the browser: 0
Result in the test (App.test.js): 2
Reproduction repository:
https://codesandbox.io/s/user-event-and-backspace-selectionstart-nx6ij?file=/src/App.test.js
Problem description:
I think the problem is related to fireInputEventIfNeeded. The value of event.target.selectionStart in fireEvent is incorrect (at least in my case) but it's correct in the setSelectionRange called at the end of the function (line 319). Shouldn't it be called before the event is fired?
Lines 295 to 324 in e9dd0a6
| function fireInputEventIfNeeded({ | |
| currentElement, | |
| newValue, | |
| newSelectionStart, | |
| eventOverrides, | |
| }) { | |
| const prevValue = getValue(currentElement()) | |
| if ( | |
| !currentElement().readOnly && | |
| !isClickable(currentElement()) && | |
| newValue !== prevValue | |
| ) { | |
| if (isContentEditable(currentElement())) { | |
| fireEvent.input(currentElement(), { | |
| target: {textContent: newValue}, | |
| ...eventOverrides, | |
| }) | |
| } else { | |
| fireEvent.input(currentElement(), { | |
| target: {value: newValue}, | |
| ...eventOverrides, | |
| }) | |
| } | |
| setSelectionRange({ | |
| currentElement, | |
| newValue, | |
| newSelectionStart, | |
| }) | |
| } |
Suggested solution:
As in the problem description. I'm happy to make a pull request if you confirm my reasoning is correct.