Search code examples
reactjsreact-testing-libraryreact-hook-formreact-adminvitest

SaveButton disabled with isValid still disabled after await user.type success


I test my SimpleForm, but even after writing input successfuly, my SaveButton is still disabled (contains className 'Mui-disabled').

npm packages useful here:

  • vitest: 0.34.1
  • react-hook-form: 7.45.2
  • react-admin: 4.11.0
  • testing-library/react: 14.0.0
  • react: 18.2.0

Toolbar:

const SaveCancelDeleteToolbar = (props: SaveCancelDeleteToolbarProps) => {
  const { closeDrawer, handleCancel } = props

  const translate = useTranslate()

  const {
    formState: { isValid },
  } = useFormContext()

  return (
    <Toolbar>
      <SaveButton disabled={!isValid} onClick={closeDrawer} />
      <Button onClick={handleCancel}>{translate('ra.action.cancel')}</Button>
    </Toolbar>
  )
}

Form:

<SimpleForm mode="all" reValidateMode="onChange" toolbar={toolbar}>
  <TextInput
    data-testid="form.value"
    label="resources.values.fields.value"
    source="value"
    validate={[required(), maxLength(50), noEmptySpace]}
  />
</SimpleForm>

To prevent react-hook-form crash on test env, I have to use a wrapper to add const formMethods = useForm()

Test file:

it('should be able to create element', async () => {

// ValueList contains somewhere the SimpleForm.
const { user, ...utils } = setup(
  <FormContextWrapper resource="vat-rates">
    <ValueList deleted={false} />
  </FormContextWrapper>
)

/* Some code that open my SimpleForm **/

// Check that SaveButton is disabled when pristine
const saveButton = await screen.findByText('ra.action.save')
expect(saveButton.outerHTML.includes('Mui-disabled')).toBe(true)

const divValue = await screen.findByTestId(
        'form.value'
      )
const inputValue = divValue.children[1].children[0]

fireEvent.focus(inputValue )
await user.type(inputValue , '1')
fireEvent.blur(inputValue )

// This expect is successful, my input has been set to '1'.
expect(inputValue.value).toBe('1')

await waitFor(() => {
  const saveButton2 = screen.getByText('ra.action.save')
  // This expect fails. saveButton2 exist.
  // Its outerHTML is the following. In react-admin I saw that in DOM, disabled is always "" so I check with 'Mui-disabled' presence.
  // `<button class="LotsOfClasses Mui-disabled" tabindex="-1" type="submit" disabled="" aria-label="ra.action.save">ra.action.save</button>`
  expect(saveButton2.outerHTML.includes('Mui-disabled')).toBe(false)
})

})

For an unknown reason, the SaveCancelDeleteToolbar component is not rerendered on test environment. And so isValid stays false and SaveButton still has 'Mui-disabled' className.

Please help, testing forms is a very important process that cannot be neglected.


Solution

  • As @slax57 said, it was the wrapper workaround that was problematic.

    For a reason still unknown to me, on test environnement "both the esm and change version of react-hook-form was being loaded".

    Solution was to add the following lines to vite.config.js file.

    resolve: {
    
        alias: {
    
          'react-hook-form': require.resolve('react-hook-form'),
    
        }
    
      }