Jest-dom: Descriptively assert that a form input/select/textarea has a given value

Created on 3 Oct 2018  路  5Comments  路  Source: testing-library/jest-dom

The problem

<select> elements, unlike input elements, do not have a value attribute. So there's easy no natural way to assert that a select element has a certain option selected. One would have to traverse through the options to see which one is selected.

To make it even worse, React makes it look like so, to the point that I got confused myself recently, when I spent more minutes that I would've liked trying to figure out why the following test (using react-testing-library) did not pass:

const { getByTitle } = render(
  <select title="Client status" value="archived">
    <option value="active">Active</option>
    <option value="archived">Archived</option>
    <option value="pending">Pending</option>
  </select>
);

expect(getByTitle('Client status')).toHaveAttribute('value', 'archived')

And to continue to make it even worse, the overall task may become more complex if using options grouped in <optgroup>s, or with multi-select elements.

Describe the feature you'd like

I started this issue thinking of suggesting a specific matcher to check a selected option in select elements. But while describing all of the above, I realize that it may be better to provide some generic way to check the value of form elements, regardless of them being inputs, textareas or selects (are there any other ones?).

Suggested implementation:

It could be providing assertions to work at the actual form input element level:

expect(userInputElement).toHaveValue('janedoe');
expect(passwordInputElement).toHaveValue('12345678');

or even at the form element level (identifying an input/select/textarea element by its name attribute):

expect(formElement).toHaveValue('username', 'janedoe');
expect(formElement).toHaveValue('password', '12345678');

It could be both use cases with the same matcher! And it could be created so that it would understand inputs, selects and textures (which are also different, their value is their child text content).

It could even be so that <select multiple> elements' values are arrays, and users could check at once the entire multiple selection (and maybe even part of it too??)

Describe alternatives you've considered:

select elements probably have some property at the dom level to check their value, instead of it being an attribute. I did not google for it, and do not have it at the top of my head, but if so, then expect(selectElement.value).toEqual(...) could help, but still it's not a generic solution for all kinds of form elements (or is it?)

dom-testing-library recently added the ability to getBySelectText, that allows users to select a select (pun not intended) by the text content of the option element within that select that is currently selected. This certainly helps, but it still leaves a weird not-so-expressive way to assert that a given select has a certain value selected:

expect(getByLabel('Client status')).toBe(getBySelectText('Archived'))

And even then, what happens if there are two selects that happen to have an option selected, both with the same text "Archived"?

And still this only addresses the problem with select elements.

Thoughts?

help wanted needs discussion

All 5 comments

I love this.

Also, could we do this:

const {container, getByLabelText} = render(
  <form>
    <label htmlFor="username-input">Username</label>
    <input id="username-input" name="username" />
    <label htmlFor="password-input">Password</label>
    <input id="password-input" name="password" type="password" />
  </form>
)
getByLabelText(/username/i).value = 'fred'
getByLabelText(/password/i).value = 'password1'

expect(container.firstChild).toHaveValues({
  username: 'fred',
  password: 'password1'
})

The key in that object will correspond to keys found in formNode.elements (which corresponds to the name or id attribute of form controls within a form.

I like the .toHaveValues(object) in plural, on the form, and the .toHaveValue(name, value) for individual form elements.

Also, I love working on open source for all that I learn in the process. I did not know about formElement.elements. Nice!

I may start working on this really soon then. Thanks for your feedback @kentcdodds!

Wow, this effort turns out to be more complex than I initially thought, but at the same time, so more useful for end users of this library.

Turns out that by abstracting away the fetching and matching of values in form elements, this could potentially allow users to make significant changes in their UI, without having to change tests too much.

For instance, this will support fetching the value of a <select multiple> as an array, but the same will happen when fetching the value of multiple checkboxes that share the same name attribute. Therefore allowing to switch between one or the other interchangeably. The same with non multiple <select> vs. a set of radio buttons sharing the same name attribute.

Also, it will abstract away the conversion of <input type="number"> values to numbers, so you can match numbers directly. The same could be true of <input type="date"> (yet to be determined, and possibly not in the first version of this).

Finally, this will support only fetching groups of elements at the level of either a <form> or within a <fieldset>. This is not only to take advantage of .elements, but it also makes the most sense in terms of how a user would identify visually either an entire form, or parts of it clearly grouped as fieldsets.

However, supporting all of the above will take some time, hence my posting here of the progress. Any feedback of my plans will be appreciated!

This is all very encouraging! Keep going! It's great 馃挴

:tada: This issue has been resolved in version 2.1.0 :tada:

The release is available on:

Your semantic-release bot :package::rocket:

Was this page helpful?
0 / 5 - 0 ratings

Related issues

datenreisender picture datenreisender  路  7Comments

srittau picture srittau  路  5Comments

benmonro picture benmonro  路  3Comments

hiwelo picture hiwelo  路  7Comments

Rowin206 picture Rowin206  路  3Comments