mardi 15 octobre 2019

How to test form component that uses state from a custom hook?

I created a custom hook, named useForm. useForm takes an initial object that it sets to its own state using useState. It returns this state. The hook also takes other functions, like a getData, and submitData to handle data retrieval/posting. It also returns a handleOnChange function that sets the state.

I am using this hook inside a form element, named GeneralInformationForm. This form has a couple of inputs, data retrieval and posting functions (which are passed to the hook so that it can deal with errors).

The inputs' state is tied to the state that is returned from the useForm hook, and the onChange method of my input is the returned handleOnChange function from the hook.

My question is: How can I write tests to see if my form correctly submits and retrieves values, when I cannot directly set the state?

Am I supposed to fire multiple onChange events on the inputs to set the state that way? Should I be handling my form way differently?

The reason this hook exists is to reuse logic between forms, specifically onChange and error setting logic.

I am using Jest and Enzyme.

GeneralInformationForm.tsx:

const GeneralInformationForm = (props: IProps) => {
  useEffect(() => {
    if (props.partnerId) handleGetData();
  }, [props.partnerId]);

  const getData = async () => {
    if (!props.partnerId) return initialState;
    const response = await getPartner(props.partnerId);

    initialState.name.value = response.data.Name;
    initialState.uuid.value = response.data.UUID;
    initialState.token.value = response.data.Token;
    initialState.isAutoClick.value = response.data.IsAutoClickEnabled;

    return initialState;
  };

  useEffect(() => {
    if (props.isSubmitted) handleOnSubmit();
  }, [props.isSubmitted]);

  const submitData = async () => {
    const postObject = {
      Name: state.name.value,
      Token: state.token.value,
      IsAutoClickEnabled: state.isAutoClick.value,
    };

    if (props.partnerId) {
      return editPartner(props.partnerId, postObject, []);
    }

    const response = await createPartner(postObject, []);
    props.setPartnerId(response.data.UUID);
    return response;
  };

  const {
    state,
    handleGetData,
    handleOnChange,
    handleOnSubmit,
  } = useForm(initialState, submitData, props.resetSubmitted, getData);

  return (
    <div className={styles.formContainer}>
      <TextField
        name="name"
        label="Partner name"
        value={state.name.value}
        onChange={handleOnChange}
        margin="normal"
        error={Boolean(state.name.error)}
        helperText={state.name.error}
        variant="outlined"
      />
      <TextField
        name="uuid"
        label="Partner UUID"
        disabled
        value={state.uuid.value}
        onChange={handleOnChange}
        margin="normal"
        error={Boolean(state.uuid.error)}
        helperText={state.uuid.error}
        variant="outlined"
      />
      <TextField
        name="token"
        label="Token"
        disabled={!props.partnerId}
        value={state.token.value}
        onChange={handleOnChange}
        margin="normal"
        error={Boolean(state.token.error)}
        helperText={state.token.error}
        variant="outlined"
      />
      <FormControlLabel
        className={styles.checkbox}
        control={
          <Checkbox checked={state.isAutoClick.value} onChange={handleOnChange} name="isAutoClick" />
        }
        label="Enable Auto-click"
      />
      {!props.partnerId && (
        <Button
          variant="contained"
          onClick={handleOnSubmit}
        >
          Create Partner
        </Button>
      )}
    </div>
  );
};

export default GeneralInformationForm;

const initialState = {
  name: { value: '', error: '' },
  uuid: { value: 'automatically generated', error: '' },
  token: { value: 'automatically generated', error: '' },
  isAutoClick: { value: false, error: '' },
};

useForm.tsx:


function useForm(initialState: any, submitData: any, resetSubmitted: any, getData: any) {
  const [state, setState] = useState(initialState);

  const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const { name } = event.target;
    const value = event.target.type === 'checkbox' ? event.target.checked : event.target.value;

    setState((prevState: any) => ({
      ...prevState,
      [name]: { value, error: '' },
    }));
  };

  const handleOnSubmit = async () => {
    const response = await submitData(state);

    if (response === null) {
      // There was no partnerId present so the form could not submit correctly
    }
    if (response.status === 400) {
      handleErrors(response.data);
    }

    // if (response.status === 200) {
    //   handleGetData();
    // }

    resetSubmitted();
  };

  const handleGetData = async () => {
    const retrievedData = await getData();

    setState({ ...retrievedData });
  };

  const handleErrors = (errors: any) => {
    // Set the errors to the state
  };

  return {
    setState,
    state,
    handleOnChange,
    handleOnSubmit,
    handleGetData,
  };
}

Aucun commentaire:

Enregistrer un commentaire