Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Testing Library with TypeScript: Set an input's value

I'm building a React application using TypeScript. I do my component tests using React Testing Library.

Let's say you have simple form like this:

import React from 'react'

function Login({onSubmit}) {
  return (
    <div>
      <form
        onSubmit={e => {
          e.preventDefault()
          const {username, password} = e.target.elements
          onSubmit({
            username: username.value,
            password: password.value,
          })
        }}
      >
        <label htmlFor="username">Username</label>
        <input id="username" />
        <label htmlFor="password">Password</label>
        <input id="password" type="password" />
        <br />
        <button type="submit">Submit</button>
      </form>
    </div>
  )
}

export {Login}

In this video Kent (the creator of the library) shows how you would test the forms input inputs. The tests would look like this:

import React from 'react'
import {renderIntoDocument, cleanup} from 'react-testing-library'
import {Login} from '../login'

afterEach(cleanup)

test('calls onSubmit with username and password', () => {
  const handleSubmit = jest.fn()
  const {getByLabelText, getByText} = renderIntoDocument(
    <Login onSubmit={handleSubmit} />,
  )
  getByLabelText(/username/i).value = 'chuck'
  getByLabelText(/password/i).value = 'norris'
  getByText(/submit/i).click()
  expect(handleSubmit).toHaveBeenCalledTimes(1)
  expect(handleSubmit).toHaveBeenCalledWith({
    username: 'chuck',
    password: 'norris',
  })
})

The problem is that he did it with plain JavaScript. When doing this with TypeScript the lines where he sets .value throw the following errors

[ts] Property 'value' does not exist on type 'HTMLElement'.

How would one test this functionality with TypeScript using React Testing Library? How would you set the input's values?

like image 579
J. Hesters Avatar asked Nov 07 '18 18:11

J. Hesters


People also ask

How do I get the span value in React test library?

import { screen } from "@testing-library/react"; ... // get HTML DOM element const spanElement = await screen. queryByTestId('test-span'); // OR // const spanElement = await screen. querySelector('test-span'); // get value from HTML DOM element const spanElementValue = spanElement. getAttribute('value'); ...

How do you trigger onChange event in React testing library?

The onChange event handler is used by <input type='text'> jsx, so you should use type(element, text, [options]) to writes text inside an <input> or a <textarea> . selectOptions(element, values) is used for selecting the specified option(s) of a <select> or a <select multiple> element.


1 Answers

The typings provided by that library type the return value of getByLabelText as type: HTMLElement. Not all HTML elements have value properties, only things like HTMLInputElement do.

The getByLabelText also has no sort of generic type through which you might be able to affect the output type, so essentially you will need to either unsafely cast the result to type HTMLInputElement or you will need to build a helper function that tells TypeScript whether or not the object is the right type:

  1. Unsafe cast. All you really need to do is update any calls to getByLabelText where you expect it to be a type with a value property to:

    (getByLabelText(/username/i) as HTMLInputElement).value = 'chuck';
    
  2. Type validation. This method is a bit safer as you can provide a type validation function that will cause TypeScript to update the type:

    function isElementInput<T extends HTMLElement>(element: T): T is HTMLInputElement {
        // Validate that element is actually an input
        return element instanceof HTMLInputElement;
    }
    
    // Update your attempted value sets:
    const elem = getByLabelText(/username/i);
    if (isElementInput(elem)) {
        elem.value = 'chuck';
    } else {
        // Handle failure here...
    }
    
like image 139
casieber Avatar answered Oct 22 '22 19:10

casieber