Best way to test input value in dom-testing-library or react-testing-library
Asked Answered
I

7

95

What is the best way to test the value of an <input> element in dom-testing-library/react-testing-library?

The approach I've taken is to fetch the raw input element itself via the closest() method, which then gives me direct access to the value attribute:

const input = getByLabelText("Some Label")
expect(input.closest("input").value).toEqual("Some Value")

I was hoping that there was a way I could this without having to directly access HTML attributes. It didn't seem like it was in the spirit of the testing library. Perhaps something like the jest-dom toHaveTextContent matcher matcher:

const input = getByLabelText("Some Label")
expect(input).toHaveTextContent("Some Value")

UPDATE

Based on request in the comments, here is a code example showing a situation where I felt the need to test the value in the input box.

This is a simplified version of a modal component I built in my app. Like, extremely simplified. The whole idea here is that the modal opens up with the input pre-filled with some text, based on a string prop. The user can freely edit this input and submit it by pressing a button. But, if the user closes the modal and then reopens it, I would like to have the text reset to that original string prop. I wrote a test for it because a previous version of the modal DID NOT reset the input value.

I'm writing this in TypeScript so that the types of each prop are very clear.

interface Props {
  onClose: () => void
  isOpen: boolean
  initialValue: string
}

export default function MyModal({ onClose, isOpen, initialValue }) {
  const [inputValue, setInputValue] = useState(initialValue)

  // useEffect does the reset!
  useEffect(() => {
    if (!isOpen) {
      setNameInput(initialValue)
    }
  }, [isOpen, initialValue])

  return (
    <SomeExternalLibraryModal isOpen={isOpen} onClose={onClose}>
      <form>
        <input
          value={inputValue}
          onChange={(e: ChangeEvent<HTMLInputElement>) =>
            setInputValue(e.target.value)
          }
        />
        <button onClick={onClose}>Cancel</button>
      </form>
    </SomeExternalLibraryModal>
  )
}
Ixia answered 19/6, 2020 at 15:51 Comment(4)
Can you provide your entire component? RTL approach is more black-box oriented so I guess that a "good" way of testing is to trigger the event that is using the input rather that verifying the value. You would then mock the service that needs to be called and verify that you call it with the right value. There are plenty of other solutions, so share the code and I'll post an exampleLectionary
@ArnaudClaudel I provided a code example. I am curious to hear how you would write an RTL test for the functionality. Thank you :)Ixia
When do you use inputValue? I see it in value={inputValue} but that's for the input bar, where do you use it when thee user clicks on the button?Lectionary
@ArnaudClaudel like I said, this is a very extremely simplified version of the actual component we built. I did not include any logic for it because it's irrelevant to my question. Assume that the inputValue is used for something like the onSubmit handler of the form when an "Update" button is clicked (which, one again, was left out because it's irrelevant to my original question).Ixia
C
105

You are right in being suspicious of your testing method in regards to how this testing library wants you to test. The simplest answer to this question would be to use the getByDisplayValue query. It will search for an input, textarea, or select that has the value you are attempting to find. For example, using your component as an example, if I was trying to verify that inputValue = 'test', I would search like

expect(screen.getByDisplayValue('test')).toBeInTheDocument();

That is all you need to do. I assume your test is only rendering the MyModal component. Even if you have multiple inputs, it doesn't matter in regards to testing philosophy. As long as the getByDisplayValue finds any input with that value, it is a successful test. If you have multiple inputs and want to test that the exact input has the value, you could then dig into the element to determine it is the correct input:

note: you will need jest-dom for this to work.

expect(screen.getByDisplayValue('test')).toHaveAttribute('id', 'the-id');

or (without jest-dom):

expect(screen.getByDisplayValue('test').id).toBe('the-id');

You can of course search for any attribute you like.

One final alternative for testing the value is to find the input by role. This won't work in your example's case unless you add a label and affiliate it to your input through the htmlFor attribute. You could then test it like such:

expect(screen.getByRole('input', { name: 'the-inputs-id' })).toHaveValue('test');

or (without jest-dom):

expect(screen.getByRole('input', { name: 'the-inputs-id' }).value).toBe('test');

This I believe is the best way to test for the value while making sure the correct input has the value. I would suggest the getByRole method, but again, you will need to add a label to your example.

Cincture answered 4/9, 2020 at 17:30 Comment(3)
Is there any way to get by displayValue and label?Moray
At the same time? I don't believe so, but I would need to know what exactly you are trying to accomplish.Cincture
I agree with the last option of using getByRole, it ensures you are setting up your html in a more accessible way, it makes sure that you are looking at the element you expect your value to show up, and it allows for you to test using options on the aria roles.Hippodrome
F
23

You can use screen.getByDisplayValue() to get the input element with a displayed value and compare it to your element.

type TestElement = Document | Element | Window | Node

function hasInputValue(e: TestElement, inputValue: string) {
  return screen.getByDisplayValue(inputValue) === e
}

In your test:

const input = screen.getByLabelText("Some Label")

fireEvent.change(input, { target: { value: '123' } })
expect(hasInputValue(input, "123")).toBe(true)
Fab answered 12/10, 2020 at 14:57 Comment(2)
I struggled with getting userEvent to work with my input (it ignored some characters), but this worked perfectly. Thank you!Palimpsest
Is there any way to get by displayValue and label?Moray
D
13

expect(screen.getByLabelText("Name")).toHaveValue("hello"); - this gets you the value for the input :)

<label class="label" for="name">
      Name
</label>
<div class="control ">
       <input
          class="input"
          for="name"
          id="name"
          name="name"
          value="hello"
       />
</div>

Test:

userEvent.type(screen.getByLabelText("Name"), "hello")
await waitFor(() => {
   expect(screen.getByLabelText("Name")).toHaveValue("hello");
});
Diabolo answered 30/4, 2021 at 7:19 Comment(0)
B
9

Using @testing-library/dom (or any of the wrapped libraries here)

You can do:

expect(inputField).toHaveDisplayValue('some input value');

Full example:

test('should show input with initial value set', async () => {
  render(<Input type="text" value="John Doe" data-testid="form-field-firstname" />);

  const inputField = await screen.findByTestId(`form-field-firstname`);
  await waitFor(() => expect(inputField).toHaveDisplayValue('John Doe')));
});
Breann answered 11/3, 2022 at 17:31 Comment(1)
you dont give an answer how to access Input with no testId. The example states clear about itPlumate
L
3

screen.getByRole('textbox', {name: 'scren read accessible name / aria-label value})

getByRole('textbox'...

Lemures answered 30/6, 2023 at 6:21 Comment(0)
H
1

There is very clean way to test it using testing library.

//In describe
  const renderComponent = (searchInputValue, handleSearchInputValue) => {
    const wrapper = render(<yourComponentWithInput
      value={searchInputValue}
      onChange={handleSearchInputValue}
    />);
    return wrapper;
  };


//In test
    const mockHandleSearchInputValue = jest.fn();
    const { getByLabelText } = renderComponent('g', mockHandleSearchInputValue);
    const inputNode = getByLabelText('Search label'); // your  input label
    expect(inputNode.value).toBe('s'); // to test input value
    fireEvent.change(inputNode, { target: { value: 'su' } }); // triggers onChange event
    expect(mockHandleSearchInputValue).toBeCalledWith('su'); // tests if onChange handler is called with proper value
Headed answered 21/7, 2021 at 4:50 Comment(1)
Property 'value' does not exist on type 'HTMLElement'Sigridsigsmond
H
0

I checked all the answers but no one told that we may access the input with querySelector these steps:

  1. render and access the UI component with screen
  2. parent HTMLElement with getByTestId, getByText or any other
  3. access the input with querySelector
it('Input value should be 1'() => {
  const input = screen.getByTestId('wrapperId').querySelector('input')
  expect(input).toHaveValue(1);
})

Hypozeuxis answered 21/3, 2023 at 20:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.