Formik
Example based in this Async Submission Formik example
// myForm.js
import React from 'react'
import { Formik, Field, Form } from 'formik'
const sleep = ms => new Promise(r => setTimeout(r, ms))
export const MyForm = ({ onSubmit }) => {
  const handleSubmit = async values => {
    await sleep(500)
    onSubmit(values)
  }
  return (
    <div>
      <h1>Sign Up</h1>
      <Formik
        initialValues={{
          firstName: '',
          lastName: '',
          email: '',
        }}
        onSubmit={handleSubmit}
      >
        <Form>
          <label htmlFor="firstName">First Name</label>
          <Field id="firstName" name="firstName" placeholder="Jane" />
          <label htmlFor="lastName">Last Name</label>
          <Field id="lastName" name="lastName" placeholder="Doe" />
          <label htmlFor="email">Email</label>
          <Field
            id="email"
            name="email"
            placeholder="jane@acme.com"
            type="email"
          />
          <button type="submit">Submit</button>
        </Form>
      </Formik>
    </div>
  )
}
// myForm.test.js
import React from 'react'
import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { MyForm } from './myForm.js'
test('rendering and submitting a basic Formik form', async () => {
  const handleSubmit = jest.fn()
  render(<MyForm onSubmit={handleSubmit} />)
  userEvent.type(screen.getByLabelText(/first name/i), 'John')
  userEvent.type(screen.getByLabelText(/last name/i), 'Dee')
  userEvent.type(screen.getByLabelText(/email/i), 'john.dee@someemail.com')
  userEvent.click(screen.getByRole('button', { name: /submit/i }))
  await waitFor(() =>
    expect(handleSubmit).toHaveBeenCalledWith({
      email: 'john.dee@someemail.com',
      firstName: 'John',
      lastName: 'Dee',
    }, expect.anything())
  )
})