Prieyudha Akadita S
Prieyudha Akadita S

Reputation: 458

How to call or mock useForm on jest react native?

I have a resuable component, called DatePicker like this

export interface IPDatePicker extends IProps {
  control: any
  label: string
  placeholder: string
  value?: string
  errorText?: string
  isRequired?: boolean
  name: string
  defaultValue?: any
  onChangeText: (value: string) => void
  minimumDate?: any
  maximumDate?: any
  timeOnly?: boolean
  hideLabel?: boolean
  labelMaxLine?: number
  //   setDateValue: any
}

const DatePicker: React.FC<IPDatePicker> = props => {
  const todaysDate = new Date()
  const [date, setDate] = useState<Date>(todaysDate)
  const [isDatePickerOpen, setIsDatePickerOpen] = useState(false)
  return (
    <Controller
      name={props?.name}
      defaultValue={props?.defaultValue}
      control={props?.control}
      render={({field: {onChange, value}}: any) => {.....}
    />    

The point is, the component needs 'control' as mandatory parameters.

The 'control' is from the parent page/component using useForm

const ParentPage = () => {
  const {
    control,
    formState: {errors, isValid, isDirty},
  } = useForm({
    resolver,
    mode: 'onChange',
    defaultValues: {
      date: '',
    },
  })
}


return (
   ...
   <DatePicker 
      control={control}  <-here
      name='date'
      ...
    />

)

I tried to create a test file but I cant figure it out how to call useForm on jest. It always show Invalid hook call. Hooks can only be called inside of the body of a function component

import React from 'react'
import {render, fireEvent} from '@testing-library/react-native'
import {IProps} from '@app/presentations/types'
import {DatePicker} from '@app/presentations/_shared-components'
import {useForm} from 'react-hook-form'


interface IPDatePicker extends IProps {
  control: any
  label: string
  placeholder: string
  value?: string
  errorText?: string
  isRequired?: boolean
  name: string
  defaultValue?: any
  onChangeText: (value: string) => void
}

function renderComponent(props: IPDatePicker) {
  const component = render(<DatePicker {...props} />)
  return {
    component,
    ...component,
  }
}

describe('DatePicker component', () => {
  it('Should render correctly', () => {
    const {control} = useForm({
      mode: 'onChange',
      defaultValues: {
        date: '',
      },
    })
    const component = renderComponent({
      control: control,
      label: 'Date picker',
      placeholder: 'Placeholder',
      onChangeText: v => {},
      name: 'date',
    })
    expect(component).toMatchSnapshot()
  })
})

here is the error pic
enter image description here

my question is similar to this

Upvotes: 5

Views: 17135

Answers (3)

Stian J&#248;rgensrud
Stian J&#248;rgensrud

Reputation: 1022

Found a good answer that doesn't require rewriting the code to make the test work: https://stackoverflow.com/a/76780938/6113915

Instead of mocking, call useForm in the test. This will lead to the following error: TypeError: Cannot read properties of null (reading 'useRef'). In order to call hooks in React testing library one must wrap them in renderHook.

Example of test:

const { result } = renderHook(() => useForm())
const methods = result.current;

render(
  <FormProvider {...methods}>
    <TextField label="testInput" />
  </FormProvider>
  );

expect(screen.getByRole('textbox')).toBeInTheDocument()

Now the TextField have access to a control from useForm

Upvotes: 0

dani24
dani24

Reputation: 2288

The way that I manage to test something similar with the useForm without mocking it was to create a component that includes the one that I wanted to test.

Example: Let's imagine that you want to test that DatePicker component. So in order to use the control function from useForm I'll do:

// spec/javascript/DatePicker.spec.tsx file

import '@testing-library/jest-dom/extend-expect';
import { render } from '@testing-library/react';
import React from 'react';

const DatePickerWithForm = () => {
  const { control } = useForm();

  return <DatePicker control={control} ...some_other_attributes_here... />;
}

describe('DatePicker', () => {
  it('shows something', () => {
    const { getByText } = render(<DatePickerWithForm />);
    expect(getByText('Something')).toBeInTheDocument();
  });
});

After doing that no more hook errors were thrown in the console

Upvotes: 7

Prieyudha Akadita S
Prieyudha Akadita S

Reputation: 458

Finally I fixed this problem with this code

jest.mock('react-hook-form', () => ({
  ...jest.requireActual('react-hook-form'),
  Controller: () => <></>,
  useForm: () => ({
    control: () => ({}),
    handleSubmit: () => jest.fn(),
  }),
}))

Upvotes: 6

Related Questions