11

我正在使用 TypeScript 构建一个 React 应用程序。我使用 React 测试库进行组件测试。

假设您有这样的简单形式:

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}

此视频中,Kent(库的创建者)展示了如何测试表单输入输入。测试看起来像这样:

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',
  })
})

问题是他用纯 JavaScript 做到了。使用 TypeScript 执行此操作时,他设置的行.value会引发以下错误

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

如何使用 React 测试库使用 TypeScript 测试此功能?您将如何设置输入的值?

4

1 回答 1

24

该库提供的类型将返回值getByLabelText作为 type: HTMLElement。并非所有 HTML 元素都有value属性,只有类似的东西才有HTMLInputElement

getByLabelText也没有可以影响输出类型的泛型类型,因此本质上您需要不安全地将结果强制转换为类型,HTMLInputElement或者您需要构建一个帮助函数来告诉 TypeScript 是否该对象是正确的类型:

  1. 不安全的演员表。您真正需要做的就是将任何调用更新到getByLabelText您希望它是具有value属性的类型的位置:

    (getByLabelText(/username/i) as HTMLInputElement).value = 'chuck';
    
  2. 类型验证。这种方法更安全一些,因为您可以提供一个类型验证函数,该函数将导致 TypeScript 更新类型:

    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...
    }
    
于 2018-11-07T19:03:39.463 回答