1

I have a react app using Formik and Material-ui. I grabbed the submit button html element, But fireEvent is not working. I think the issue is coming from Formik library layer. 'Button' component is a reusable material ui button. 'change fireEvent' tests are passed.

But I receive 'Expected mock function to have been called one time, but it was called zero times.' message for 'submit fireEvent'.

loginForm.test.js

import { Router, MemoryRouter } from 'react-router-dom';
import { queryByAttribute } from 'react-testing-library';
import React, { render, cleanup, fireEvent } from '../../../setupTests';
import LoginForm from '../../../../components/auth/login/loginForm';

afterEach(cleanup);

const mockSubmit = jest.fn();
const mockKeepMeLoggedIn = jest.fn();

const defaultProps = {
  handleSubmit: mockSubmit,
  isSubmitting: false,
  userData: [],
  keepMeLoggedIn: mockKeepMeLoggedIn,
};

const setUp = (props = {}) => {
  const setupProps = { ...defaultProps, ...props };
  const component = render(
    <MemoryRouter>
      <LoginForm {...setupProps} />
    </MemoryRouter>,
  );
  const { container, getByTestId, getByText } = component;
  const getByName = queryByAttribute.bind(null, 'name');
  const usernameInput = getByName(container, 'username');
  const passwordInput = getByName(container, 'password');
  const getByType = queryByAttribute.bind(null, 'type');
  const submitButton = getByType(container, 'submit');


  return { component, usernameInput, passwordInput, submitButton };
};

describe('Login Form Component', () => {
  it('simulate input type and click the form submit button', () => {
    const { usernameInput, passwordInput, submitButton } = setUp();
    fireEvent.change(usernameInput, { target: { value: 'yuch' } });
    expect(usernameInput.value).toBe('yuch');
    fireEvent.change(passwordInput, { target: { value: 'testpwd1234' } });
    expect(passwordInput.value).toBe('testpwd1234');

    fireEvent.click(submitButton);
    expect(mockSubmit).toHaveBeenCalledTimes(1);
  });

loginForm.js

 ...

        import { Formik, Form } from 'formik';
        /* --- Components --- */
        import FormikField from '../../../shared/form/formikField';
        import PasswordField from '../../../shared/form/passwordField';
        import Button from '../../../shared/form/formButton';

        const LoginForm = ({
          keepMeLoggedIn,
          keepLoggedIn,
          userData,
          handleSubmit,
          loginValidation,
        }) => {
          const foundUsername = userData.length !== 0 ? userData[0].username : '';
          const values = { username: foundUsername, password: '' };
          return (
            <Formik
              initialValues={values}
              validationSchema={loginValidation}
              onSubmit={handleSubmit}
            >
              {({ isSubmitting }) => (
                <div className="login-container">
                  <Form
                    className="flex flex-column-m center"
                    data-testid="form"
                  >

            <FormikField
              label="아이디"
              name="username"
              type="text"
              icon="filledUser"
              styleName="textField"
              required
            />
           ...
            <Button
              typeValue="submit"
              variantValue="contained"
              buttonName="로그인"
              className="login-btn"
              isSubmitting={isSubmitting}
            />
          </Form>
         ...
        </div>
      )}
    </Formik>
  );
};

button.js

import React from 'react';
import Button from '@material-ui/core/Button';
import { withStyles } from '@material-ui/core/styles';

const styles = theme => ({
  ...
});

const FormButton = ({
  typeValue,
  variantValue,
  buttonName,
  width,
  isSubmitting,
  classes,
  className,
}) => {
  ...
  return (
    <Button
      type={typeValue}
      variant={variantValue}
      color="primary"
      size="small"
      style={widthStyle}
      className={`${className} ${classes.button}`}
      disabled={isSubmitting}
    >
      {buttonName}
    </Button>
  );
};

Things I have tried.

[ To get submit button ]

  1. const getByType = queryByAttribute.bind(null, 'type');
    
    const submitButton = getByType(container, 'submit');
    
    -> console.log(submitButton) // HTMLButtonElement
    
    -> fireEvent.click(submitButton)
    
  2. 2.
const submitButton = getByText('로그인');

-> console.log(submitButton) // HTMLSpanElement

-> fireEvent.click(submitButton)
  1. const submitButton = getByTestId('form');
    
    -> console.log(submitButton) // HTMLFormElement
    
    -> fireEvent.submit(submitButton)
    

[ form ] 1. html 'form' instead of 'Form' from Formik.

import { Formik, Form } from 'formik';

<Formik
      initialValues={values}
      validationSchema={loginValidation}
      onSubmit={handleSubmit}
    >
      {({ handleSubmit, isSubmitting }) => (
        <div className="login-container">
          <form
            className="flex flex-column-m center"
            onSubmit={handleSubmit}
            data-testid="form"
          >
          ...
          </form>
3
  • Have you tried firing a submit event to the Form element? Commented Jul 29, 2019 at 8:24
  • Yes I have as I wrote above in 'What I have tried' section. Commented Jul 29, 2019 at 13:07
  • I grabbed the Form element form using 'getByTestId'. Commented Jul 29, 2019 at 13:12

1 Answer 1

2

It actually has to do with how Formik handles the submit. Since it is using a promise, it takes at least a tick before the onSubmit call is being called.

Testing library has a wait utility which waits for a given time. But since we only need to wait for a single tick, we can just omit the duration.

First, import wait from react-testing-library. Then make your it function async and wrap the expect part with a wait function.

I've tested this with a click event on the submit button.

// import wait
import { wait } from 'react-testing-library';

// add async
it('simulate input type and click the form submit button', async () => {
  const { usernameInput, passwordInput, submitButton } = setUp();

  fireEvent.change(usernameInput, { target: { value: 'yuch' } });
  expect(usernameInput.value).toBe('yuch');

  fireEvent.change(passwordInput, { target: { value: 'testpwd1234' } });
  expect(passwordInput.value).toBe('testpwd1234');

  fireEvent.click(submitButton);

  // wrap expect in `await wait`
  await wait(() => {
    expect(mockSubmit).toHaveBeenCalledTimes(1);
  });
});
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.