question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

userEvent.type doesn't work well when a value is formatted after the onChange is fired

See original GitHub issue
  • @testing-library/user-event version: 12.0.2
  • Testing Framework and version: jest (24.9.0 locally and whatever version CodeSandbox uses)
  • DOM Environment: @testing-library/react’s render function, so jsdom?

React component logic

export default function App() {
	const [rawPhone, setRawPhone] = useState('');
	const [formattedPhone, setFormattedPhone] = useState('');

	return (
		<div className="App">
			<form>
				<label htmlFor="raw-phone">Raw Phone: </label>
				<input id="raw-phone" type="text" value={rawPhone} onChange={event => setRawPhone(event.target.value)} />

				<label htmlFor="formatted-phone">Formatted Phone: </label>
				<input id="formatted-phone" type="text" value={formattedPhone} onChange={event => setFormattedPhone(formatPhone(event.target.value)) } />
			</form>
		</div>
	);
}

Format function

export function formatPhone(unformattedPhone?: string): string {
	if (unformattedPhone == null)  return '';

	// Remove all formatting from input
	let matchedValue = unformattedPhone.replace(/[^0-9]*/g, '').match(/\d*/g);

	const cleanedValue = matchedValue ? matchedValue.join('') : '';

	// group between areaCode, firstGroup, and secondGroup
	matchedValue = cleanedValue.match(/(\d{0,3})(\d{0,3})(\d{0,4})/);

	const [areaCode, firstGroup, secondGroup] = matchedValue ? matchedValue.slice(1) : ['', '', '']; 

	// initialize phoneNumber
	let phoneNumber = '';

	// begin with '(' after any digit input
	if (areaCode !== '') phoneNumber = `(${areaCode}`;

	// end area code with ')' if there are more than 3 digits (at least 1 digit in firstGroup)
	if (firstGroup !== '') phoneNumber = `${phoneNumber}) ${firstGroup}`;

	// add '-' if there are more than 6 digits (at least 1 digit in secondGroup)
	if (secondGroup !== '') phoneNumber = `${phoneNumber}-${secondGroup}`;

	return phoneNumber;
}

Tests

test('Raw phone should set the value to match excatly what is typed', async () => {
	render(<App />);
	userEvent.type(screen.getByLabelText(/Raw Phone/i), '1234567890');
	expect(screen.getByLabelText(/Raw Phone/i)).toHaveValue('1234567890');
});

test('Formatted phone should format the value as it is typed', async () => {
	render(<App />);
	userEvent.type(screen.getByLabelText(/Formatted Phone/i), '1234567890');
	expect(screen.getByLabelText(/Formatted Phone/i)).toHaveValue('(123) 456-7890');
});

What you did:

I have a field where we collect a phone number and format it as the user types. To do so, we format the value onChange before we call the setState function from the useState hook`.

What happened:

When attempting to test this by calling userEvent.type(..., '1234567890') the value was being set to (098) 765-4321 instead of (123) 456-7890.

Reproduction repository:

https://codesandbox.io/s/usereventtype-reverses-value-mgi2n?file=/src/App.spec.tsx

Problem description:

This was extremely confusing at first and led me to do a lot of digging in the source code of userEvent.type(...). Once I did so for a while I came across the idea that it may be an issue with the selected location returning to the beginning of the input. I think it may have something to do with the newValue being different than it expected.

Suggested solution:

I don’t know that I have a suggested solution, as I don’t have a complete grasp over the source code and the cause of the specific problem. However, I’m more than happy to help discuss options and take a crack at implementing a fix with some guidance.

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:1
  • Comments:18 (17 by maintainers)

github_iconTop GitHub Comments

1reaction
ph-fritschecommented, Mar 30, 2022

@terraelise @mickcastle Please don’t add noise to the issue board. File an issue with a reproducible example.

Also try upgrading to the latest version. The issue above has been resolved. There might be another issue with 13.5.0 that might or might not be already resolved.

1reaction
terraelisecommented, Mar 14, 2022

I am experiencing this issue with 13.5.0, had to downgrade to 12.0.4 to solve it.

Read more comments on GitHub >

github_iconTop Results From Across the Web

userEvent.type not updating value of input in test
I am testing this component, which is simply changing its state depending on new input into the input field and setting the value...
Read more >
Do You Really Need React State to Format Inputs? - MojoTech
Allows the user to input correctly-formatted values. Blocks invalid inputs. */. function handleChange(event: React.
Read more >
@testing-library/user-event - npm
Start using @testing-library/user-event in your project by running `npm i ... { const onChange = jest.fn() render(<input type="checkbox" ...
Read more >
userEvent.type Reverses Value - CodeSandbox
When a value is intercepted during on change and formatted before being set on an input, userEvent.type doesn't appropriately handle it and reverses...
Read more >
React-testing-library: fireEvent vs userEvent - mimacom blog
Well, the difference is that userEvent simulates complete ... is nothing else than a set of fireEvent methods triggered one after the other, ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found