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
userEvent.type gets only last letter on v12.0.9 #387
Comments
@kentcdodds looking at the changes introduced in 12.0.9 and the code and comments in I did some minimal debugging and looks like if the I hope this helps. |
I think we need to wrap it with the event wrapper only if it's not using delay. Otherwise it should be wrapped in only the async wrapper |
🎉 This issue has been resolved in version 12.0.10 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
It seams that the issue is not solved yet. I have tried here, but the problem is the same |
Could you add a test to reproduce this issue? https://github.com/testing-library/user-event/blob/master/src/__tests__/type.js#L717 |
I think that the problem is that It seams to happen at the beginning and at the end |
I agree it seems specific to React. (Or at least, I can't reproduce in vanilla but I can in React.) |
I think I understand the problem. We have
I don't know if I'm wrong in something |
Yup, verified that intuition is correct @marcosvega91 👍 import React from 'react'
import ReactDOM from 'react-dom'
import {act} from 'react-dom/test-utils'
test('nested act does not flush until the top parent act finishes', () => {
let setName
const NameEdit = () => {
const [userName, setUserName] = React.useState('')
setName = setUserName
console.log({userName})
return (
<input
value={userName}
onChange={(e) => {
console.log(e.target.value)
setUserName(e.target.value)
}}
/>
)
}
const div = document.createElement('div')
ReactDOM.render(<NameEdit />, div)
act(() => {
act(() => {
setName('John')
})
// this fails
expect(div.firstChild.value).toBe('John')
})
// this passes
// expect(div.firstChild.value).toBe('John')
}) So what I'm going to do is change from option 2 to option 1 (ref: #384) |
Yes this will solve the problem :) |
🎉 This issue has been resolved in version 12.0.11 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
I am on version 12.1.5 and I still get this error import React, { Fragment } from "react";
import { render, act } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import App from "./App";
test("Gets a code and renders it in a new route", () => {
const { getByLabelText } = render(<App />);
act(() => {
userEvent.type(getByLabelText("Code:"), "12345");
});
expect(getByLabelText("Code:")).toHaveValue("12345");
});
|
I get this issue as well, it seems like it sends 1 character at a time instead of all eg: |
As I found out by Kent's blog post here |
Can confirm this issue for me. Code below is working at const input = screen.queryByTestId('inline-edit-input');
expect(input.value).toBe('Text');
userEvent.type(input, '{backspace}{backspace}tris');
expect(input.value).toBe('Tetris'); |
Correct me, if I'am wrong, but it says that I have the same issue. Without wrapping |
Warnings about type being not wrapped in act(() => {
jest.useFakeTimers()
// Fire event
jest.runAllTimers()
}) |
I am still suffering this error while testing next component: import React from 'react';
import { useField } from 'formik';
import MuiTextArea, { TextFieldProps } from '@material-ui/core/TextField';
export const TextAreaComponent: React.FunctionComponent<TextFieldProps> = props => {
const [field, meta] = useField(props.name);
const textAreaProps = Boolean(field) ? field : props;
const hasError = Boolean(meta && meta.touched && meta.error);
return (
<MuiTextArea
{...props}
name={textAreaProps.name}
onChange={textAreaProps.onChange}
onBlur={textAreaProps.onBlur}
value={textAreaProps.value ?? ''}
multiline={true}
type={'TextareaAutosize'}
variant={'standard'}
error={hasError}
helperText={hasError ? meta.error : ''}
/>
);
}; Using this test: import React from 'react';
import { Formik, Form } from 'formik';
import { render, screen, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { TextAreaComponent } from './textArea-field.component';
describe('textfield component specs', () => {
const renderWithFormik = (component, initialValues) => ({
...render(
<Formik initialValues={initialValues} onSubmit={console.log}>
{() => <Form>{component}</Form>}
</Formik>
),
});
it('"input" should change value when typing', () => {
// Arrange
// Act
renderWithFormik(<TextAreaComponent name={name} />, { name: 'test name' });
const textarea = screen.getByRole('textbox') as HTMLInputElement;
userEvent.type(textarea, 'test input text');
// Assert
expect(textarea.value).toEqual('test input text');
});
}); The test doesn't pass because expect receives 'est input textt' instead of 'test input text'. What's going on here? Does anyone know the solution to this?
|
Hi guys, I have a similar issue with userEvent.type, is only typed the first letter of the string. Does anyone have idea about this weird behavior?
Terminal:
|
This works fine on CodeSandbox. Can you post a reproduction either on the site or a downloadable zip/gist/repository/etc.? |
Codesandbox was updated to use jsdom a while ago. But it sometimes seems to have other issues which is why I typically avoid using it if I need to do much. |
I've just created a brand new project with create-react-app and I'm able to reproduce both the act warning and getting the error with userEvent only typing the last character. |
For what it's worth, I'm using the latest user-event (12.6.0) and just updated to React 17 (17.0.1 from 16.13.1) and started seeing this issue. Currently using react testing library 10.4.8, but still seemed to have the issue when upgrading to latest there as well. Unfortunately I can't set up an example repo at the moment, but will try to do so later if it would be helpful. But wondering if there could be something in React 17 that might contribute to this? EDIT: Turns out my issue is actually that the input element is never getting focused, and that's messing with some of our custom logic. In version 17, React uses focusin/focusout events instead of focus/blur, and older versions of jsdom (<16.3.0) didn't fire those events on focus/blur. Also I've edited this a few times, so feel free to check out the edit history to see the stages I went through to get here =) |
Could you give an example of how you solved this issue? |
Yeah so userEvent correctly calls element.focus() and element.blur(). It just took me a minute to realize that. The problem we were seeing was that jsdom 16.2.2 (what we were using at the time) actually didn't fire focusin or focusout events when those calls were made, as browsers do. But it turns out jsdom actually fixed that in 16.3.0, so just had to upgrade. Since we get jsdom from Jest, we needed to upgrade jest to 26.5.0. I want to reiterate though that our issue was actually due to custom logic we had running when the user typed and the input wasn't focused (which shouldn't really ever happen). We only saw this because of the way React changed onFocus to bind to focusin event instead of focus event in version 17, and the way jsdom fired those events (or in our case, didn't fire them). |
I had this issue when migrating from fireEvent to userEvent. I found that the userEvents were unnecessarily wrapped in |
ContextI got burned by a version of this today and want to document it for anyone else having a similar problem. DescriptionAs I understand it, this is a classic case of mishandling closed-over data in the implementation of the state-altering function. It becomes apparent when the client (in my case, Test Snippetrender(<App formAction={myAssertions} />);
userEvent.type(screen.getByPlaceholderText('Password'), 'p@ssw0rd');
userEvent.type(screen.getByPlaceholderText('Email'), 'some@email.com');
userEvent.click(screen.getByText('Submit')); App Code function handleChange(ev: FormEvent, key = "") {
ev.preventDefault();
// ✅ Passes
const value = (ev.target as HTMLInputElement).value;
setState(s => {
// ❌ Fails (individual characters instead of progressively built up strings)
// const value = (ev.target as HTMLInputElement).value;
return { ...s, [key]: value };
});
} My understanding: if we call I don't have any suggestions here because I'm not super familiar with the libraries, but I would love to hear other's opinions on how to remove this pain outright, because it seems like an easy mistake that's hard to explain. |
@maxscott You're nearly correct. Properties are resolved when the expression is executed. So when you access a property in a function and pass this function to be executed at some point later it will yield the value the property will have then. const a = { foo: 'bar' }
const b = () => a.foo
b() // is 'bar'
a.foo = 'baz'
b() // is 'baz' React optimizes state changes and the resulting rendering by batching these changes. function Foo() {
const [, setStateA] = useState()
const [, setStateB] = useState()
return <button onClick={() => { // a click on this button will only cause one rerender
setStateA({});
setStateB({});
}/>
} React also queues up state changes that happen inside render(<Foo/>)
act(() => { // this will only rerender once at the end of act
screen.getByRole('button').click()
screen.getByRole('button').click()
}) This also means that the event handler for the second That was the issue - everything else in this thread is unrelated to the initial issue and only adds confusion. Should you experience any problems that look like the issues described above please file a new one with a reproduction at codesandbox. |
@ph-fritsche Thank you for the insight here, especially into |
Hey @kentcdodds so is there a solution for this? I try to use userEvent instead of fireEvent. The input here has type "number". I am trying to test that entering an alphabetic character is ignored. I want only numeric characters to be allowed. It seems like its only getting the last character
I am getting
Edit: I notice when I change the input type to text then it works as expected... I guess thats the fix then. dont use input type number |
FWIW I've still got this problem (specifically: without Workaround that seems to work is: keep |
@wsanchez Unfortunately, does not work for me. In my case the received value just doesn't change at all. Also when i search for workarounds and try that out. Even if i wrapped it with act() or try to use waitFor() or not - also async and not async. Has this maybe also to do with formik? Because i wrote a formik wrapper/mock for that test? Or has this to do, because i use three input fields which gets at the end wrapped in one |
The initial issue is explained above.
I strongly recommend you update to v14-beta. |
@testing-library/user-event
version: 12.0.9Relevant code or config
name-edit.js
name-edit.spec.js
What happened:
Since I update to v12.0.9 this spec fails because it gets only last letter.
It's working on v12.0.8
Reproduction repository:
Sandbox with v12.0.9: https://codesandbox.io/s/quizzical-surf-wpkq1
Sandbox with v12.0.8: https://codesandbox.io/s/eager-lamport-2hwhb
The text was updated successfully, but these errors were encountered: