Skip to content

Incorrect event.target.selectionStart value when using left arrow and backspace keys #515

@oliwerin

Description

@oliwerin
  • @testing-library/user-event version: 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 instead

What 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:

  1. Type "abc" in the input
  2. Hit left arrow button on your keyboard twice
  3. Hit backspace
  4. 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?

user-event/src/type.js

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingreleased

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions