为 MockComponent 编写 JSX 语法并使用打字稿输入

Making a JSX syntax for a MockComponent and have it typed with typescript

想知道是否有人对如何破解此问题有一些好的建议。得到这个测试助手实用程序我已经添加了一些类型:

import { jest } from '@jest/globals'
import React from 'react'

// https://learn.reactnativeschool.com/courses/781007/lectures/14173979
export function mockComponent(moduleName: string, propOverrideFn = (props: Record<string, any>) => ({})) {
  const RealComponent = jest.requireActual(moduleName) as React.ComponentType<any>
  const CustomizedComponent = (props: Record<string, any>) => {
    return React.createElement(
      'CustomizedComponent',
      {
        ...props,
        ...propOverrideFn(props),
      },
      props.children
    )
  }
  CustomizedComponent.propTypes = RealComponent.propTypes

  return CustomizedComponent
}

所以目前我可以这样称呼它

jest.mock('react-native/Libraries/Components/Touchable/TouchableOpacity', () => {
  return mockComponent('react-native/Libraries/Components/Touchable/TouchableOpacity', (props) => {
    return {
      onPress: props.disabled ? () => {} : props.onPress
    }
  })
})

但我希望能够更像


jest.mock('react-native/Libraries/Components/Touchable/TouchableOpacity', () => {
  return <MockComponent 
          module='TouchableOpacity' 
          onPress={props => props.disabled ? () => {} : props.onPress}
         />
})

jest.mock('react-native/Libraries/Components/Touchable/TouchableOpacity', () => {
 return <MockComponent 
          module='TouchableOpacity'
          propOverride={props => ({onPress: props.disabled ? () => {} : props.onPress, ...props})}
        />
})

如果您查看 React without JSX,您会发现 XML-inspired 语法 (<MockComponent />) 只是 React.createElement('MockComponent').

的缩写

现在,如果您将 mockComponent 重命名为 MockComponent 并尝试使用尖括号语法,第一个 问题是您的函数接收两个参数. React 组件要么是 class 个接受一个构造函数参数(props)的组件,要么是接受一个参数(同样是 props)的函数式组件。 第二个 问题是你的函数 return 是一个 React 功能组件,当它需要 return 一个 渲染的 React 元素时.

解决此问题的一种方法是将 mockComponent 转换为 React 功能组件,并制作 FC 的 modulepropOverride 道具。

// https://learn.reactnativeschool.com/courses/781007/lectures/14173979
export function MockComponent(props) {
  const { moduleName, propOverrideFn, ...customComponentProps } = props;

  const RealComponent = jest.requireActual(moduleName) as React.ComponentType<any>
  const CustomizedComponent = (props: Record<string, any>) => {
    return React.createElement(
      'CustomizedComponent',
      {
        ...props,
        ...propOverrideFn(props),
      },
      props.children
    )
  }
  CustomizedComponent.propTypes = RealComponent.propTypes

  return <CustomizedComponent {...customComponentProps} />
}

差异很小但很重要。这里我修改了 MockComponent 以接受一个单一的 prop 参数以与 React.createElement() 兼容。这导致了如何区分 CustomizedComponent 的 props 和 mockComponent() 的参数的问题。在这里,我使用 JavaScript 解构和扩展运算符将 modulepropOverrideCustomizedComponent.

的 props 分开

最后,我使用 JSX spread syntax 将用于 CustomizedComponent 的任意道具传递给 CustomizedComponent,并使用尖括号来渲染它(而不是 return调用函数)。

我将作为练习留给您,让您为 MockComponent 的道具提出适当的 TypeScript 定义。您可以简单地将其定义为 RecordmodulepropOverride 的并集。但是,您可以想象并使用模板定义,因此 MockComponent<Toolbar>modulepropOverride 以及 Toolbar 的联合。

哦,我差点忘了。你的 Jest 电话看起来像

jest.mock('react-native/Libraries/Components/Touchable/TouchableOpacity', () => {
    (props) => {
        return <MockComponent 
            module='TouchableOpacity' 
            onPress={props => props.disabled ? () => {} : props.onPress}
            {...props}
        />
   }
})