pedrojpj/recompose-extends

View on GitHub
src/withForm/WithForm.test.js

Summary

Maintainability
F
1 wk
Test Coverage
import React from 'react';
import Enzyme, { mount } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import { compose, withState, withHandlers } from 'recompose';

import withForm from '.';

Enzyme.configure({ adapter: new Adapter() });

describe('With Form', () => {
  it('should change the value of a form field', () => {
    const Form = ({ form, updateForm }) => (
      <form>
        <input
          type="text"
          name="name"
          value={form.name}
          onChange={updateForm}
        />{' '}
      </form>
    );

    const Component = withForm({ name: { value: '' } })(Form);
    const wrapper = mount(<Component />);

    wrapper
      .find('input')
      .simulate('change', { target: { value: 'text', name: 'name' } });

    expect(wrapper.find(Form).props().form.name).toBe('text');
  });

  it('should not change the value of the input when the name attribute is missing', () => {
    const Form = ({ form, updateForm }) => (
      <form>
        <input type="text" value={form.name} onChange={updateForm} />
      </form>
    );

    const Component = withForm({ name: { value: '' } })(Form);
    const wrapper = mount(<Component />);

    wrapper.find('input').simulate('change', { target: { value: 'text' } });

    expect(wrapper.find(Form).props().form.name).toBe('');
  });

  it('Should not be valid if you do not send the required fields', () => {
    const Form = ({ form, updateForm, submitForm }) => (
      <form>
        <input
          type="text"
          name="name"
          value={form.name}
          onChange={updateForm}
        />
        <button onClick={submitForm} />
      </form>
    );

    const Component = withForm({ name: { value: '', required: true } })(Form);
    const wrapper = mount(<Component />);

    expect(wrapper.find(Form).props().formError).toBeFalsy();
    wrapper.find('button').simulate('click');
    expect(wrapper.find(Form).props().formError).toBeTruthy();
  });

  it('Should submit the form and dispatch the handler', () => {
    const Form = ({ form, updateForm, submitForm }) => (
      <form>
        <input
          type="text"
          name="name"
          value={form.name}
          onChange={updateForm}
        />
        <button onClick={submitForm} />
      </form>
    );

    const Component = compose(
      withState('submit', 'setSubmit', false),
      withForm({ name: { value: '' } }, ({ setSubmit }) => () => {
        setSubmit(true);
      })
    )(Form);

    const wrapper = mount(<Component />);
    wrapper
      .find('input')
      .simulate('change', { target: { value: 'text', name: 'name' } });
    wrapper.find('button').simulate('click');

    expect(wrapper.find(Form).props().submit).toBeTruthy();
  });

  it('should reset the values of the form', () => {
    const Form = ({ form, updateForm, submitForm }) => (
      <form>
        <input
          type="text"
          name="name"
          value={form.name}
          onChange={updateForm}
        />
        <button onClick={submitForm} />
      </form>
    );

    const Component = compose(
      withState('submit', 'setSubmit', false),
      withForm({ name: { value: '' } }, ({ setSubmit, resetForm }) => () => {
        setSubmit(true);
        resetForm();
      })
    )(Form);

    const wrapper = mount(<Component />);
    wrapper
      .find('input')
      .simulate('change', { target: { value: 'text', name: 'name' } });

    expect(wrapper.find(Form).props().form.name).toBe('text');
    wrapper.find('button').simulate('click');
    expect(wrapper.find(Form).props().form.name).toBe('');
  });

  it('should validate the form when I add a password field with a pattern', () => {
    const Form = ({ form, updateForm, submitForm }) => (
      <form>
        <input
          type="password"
          name="password"
          value={form.password}
          onChange={updateForm}
        />
        <button onClick={submitForm} />
      </form>
    );

    const Component = compose(
      withState('submit', 'setSubmit', false),
      withForm(
        {
          password: {
            value: '',
            pattern: '^(?=.*\\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\\s).*$'
          }
        },
        ({ setSubmit, resetForm }) => () => {
          setSubmit(true);
          resetForm();
        }
      )
    )(Form);

    const wrapper = mount(<Component />);

    wrapper.find('input').simulate('change', {
      target: { value: 'example', name: 'password' }
    });

    expect(wrapper.find(Form).props().form.password).toBe('example');
    wrapper.find('button').simulate('click');
    expect(wrapper.find(Form).props().formFieldsWithErrors).toContain(
      'password'
    );

    wrapper.find('input').simulate('change', {
      target: { value: 'examplePass12', name: 'password' }
    });

    wrapper.find('button').simulate('click');

    expect(wrapper.find(Form).props().formFieldsWithErrors).toHaveLength(0);
  });

  it('should invalidate the form when adding an email field without email formatting', () => {
    const Form = ({ form, updateForm, submitForm }) => (
      <form>
        <input
          type="email"
          name="email"
          value={form.email}
          onChange={updateForm}
        />
        <button onClick={submitForm} />
      </form>
    );

    const Component = compose(
      withState('submit', 'setSubmit', false),
      withForm(
        { email: { value: '', type: 'email' } },
        ({ setSubmit, resetForm }) => () => {
          setSubmit(true);
          resetForm();
        }
      )
    )(Form);

    const wrapper = mount(<Component />);

    wrapper.find('input').simulate('change', {
      target: { value: 'text', name: 'email', type: 'email' }
    });

    expect(wrapper.find(Form).props().form.email).toBe('text');
    wrapper.find('button').simulate('click');
    expect(wrapper.find(Form).props().formFieldsWithErrors).toContain('email');
  });

  it('should be able to add an error from the component', () => {
    const Form = ({ form, updateForm, submitForm }) => (
      <form>
        <input
          type="text"
          name="name"
          value={form.name}
          onChange={updateForm}
        />
        <button onClick={submitForm} />
      </form>
    );

    const Component = compose(
      withState('submit', 'setSubmit', false),
      withForm(
        { name: { value: '', required: true } },
        ({ formSetError }) => () => {
          formSetError('name');
        }
      ),
      withHandlers({
        addError: ({ formSetError }) => () => {
          formSetError('name');
        },
        checkErrors: ({ formFieldsWithErrors }) => () => formFieldsWithErrors
      })
    )(Form);

    const wrapper = mount(<Component />);

    wrapper.find('input').simulate('change', {
      target: { value: 'text', name: 'name' }
    });

    wrapper.find('button').simulate('click');

    expect(wrapper.find(Form).props().formError).toBe(false);
    wrapper
      .find(Form)
      .props()
      .addError();
    expect(
      wrapper
        .find(Form)
        .props()
        .checkErrors()
    ).toContain('name');
  });

  it('should check the input checkbox and marked value as true', () => {
    const Form = ({ form, updateForm, submitForm }) => (
      <form>
        <input
          type="checkbox"
          name="check"
          value={form.check}
          onChange={updateForm}
        />
        <button onClick={submitForm} />
      </form>
    );

    const Component = compose(
      withForm({ check: { value: false, required: true } })
    )(Form);

    const wrapper = mount(<Component />);

    wrapper.find('input').simulate('change', {
      target: { name: 'check', type: 'checkbox', checked: true, value: 'false' }
    });

    expect(wrapper.find(Form).props().form.check).toBe(true);
  });

  it('should check the input checkbox and marked value as the assign value', () => {
    const Form = ({ form, updateForm, submitForm }) => (
      <form>
        <input
          type="checkbox"
          name="check"
          value={form.check}
          onChange={updateForm}
        />
        <button onClick={submitForm} />
      </form>
    );

    const Component = compose(
      withForm({ check: { value: 'example', required: true } })
    )(Form);

    const wrapper = mount(<Component />);

    wrapper.find('input').simulate('change', {
      target: {
        name: 'check',
        type: 'checkbox',
        checked: false,
        value: 'example'
      }
    });

    expect(wrapper.find(Form).props().form.check).toBe('');

    wrapper.find('input').simulate('change', {
      target: {
        name: 'check',
        type: 'checkbox',
        checked: true,
        value: 'example'
      }
    });

    expect(wrapper.find(Form).props().form.check).toBe('example');
  });

  it('should update value of a field with updateField func', () => {
    const Form = ({ form, updateForm, updateField }) => (
      <form>
        <input
          type="text"
          name="name"
          value={form.name}
          onChange={updateForm}
        />
        <button type="button" onClick={() => updateField('name', 'example')} />
      </form>
    );

    const Component = compose(
      withForm({ name: { value: '', required: true } })
    )(Form);

    const wrapper = mount(<Component />);
    wrapper.find('button').simulate('click');

    expect(wrapper.find(Form).props().form.name).toBe('example');
  });

  it('should not maintain any fields since they do not exist in the form', () => {
    const Form = ({ form, updateForm, updateField }) => (
      <form>
        <input
          type="text"
          name="name"
          value={form.name}
          onChange={updateForm}
        />
        <button type="button" onClick={() => updateField('email', 'example')} />
      </form>
    );

    const Component = compose(
      withForm({ name: { value: '', required: true } })
    )(Form);

    const wrapper = mount(<Component />);
    wrapper.find('button').simulate('click');

    expect(wrapper.find(Form).props().form.name).toBe('');
  });

  it('should add values to an array type field', () => {
    const Button = ({ updateField }) => (
      <button onClick={() => updateField('elements', 1)} />
    );

    const Component = compose(withForm({ elements: { value: [] } }))(Button);
    const wrapper = mount(<Component />);
    wrapper.find('button').simulate('click');
    expect(wrapper.find(Button).props().form.elements).toContain(1);
  });

  it('should remove values to an array type field', () => {
    const Button = ({ updateField }) => (
      <button onClick={() => updateField('elements', 1)} />
    );

    const Component = compose(withForm({ elements: { value: [1] } }))(Button);
    const wrapper = mount(<Component />);
    wrapper.find('button').simulate('click');
    expect(wrapper.find(Button).props().form.elements).toHaveLength(0);
  });

  it('should invalidate form with fields with value array empty', () => {
    const Button = ({ submitForm }) => <button onClick={submitForm} />;

    const Component = compose(
      withForm({ elements: { value: [], required: true } })(Button)
    );

    const wrapper = mount(<Component />);
    wrapper.find('button').simulate('click');
    expect(wrapper.find(Button).props().formError).toBeTruthy();
  });

  it('should test select multiple', () => {
    const Form = ({ form, updateForm }) => (
      <form>
        <select
          multiple
          name="elements"
          value={form.elements}
          onChange={updateForm}
        >
          <option value={1}>1</option>
          <option value={2}>2</option>
        </select>
      </form>
    );

    const Component = compose(
      withForm({ elements: { value: [], required: true } })
    )(Form);

    const wrapper = mount(<Component />);

    wrapper
      .find(Form)
      .find('select')
      .simulate('change', {
        target: {
          type: 'select-multiple',
          value: 1,
          name: 'elements',
          selectedOptions: [{ value: 1 }]
        }
      });

    expect(wrapper.find(Form).props().form.elements).toContain(1);
  });

  it('should add a custom error and then remove it when submitting form', () => {
    const Form = ({ form, updateForm, submitForm }) => (
      <form>
        <input name="name" value={form.name} onChange={updateForm} />
        <button type="submit" onClick={submitForm} />
      </form>
    );

    const Component = compose(
      withForm({ elements: { value: [], required: true } }),
      withHandlers({
        addCustomError: ({ formSetError }) => () => {
          formSetError('invalid');
        },
        checkError: ({ formFieldsWithErrors }) => () => formFieldsWithErrors
      })
    )(Form);

    const wrapper = mount(<Component />);

    wrapper
      .find(Form)
      .props()
      .addCustomError();

    expect(
      wrapper
        .find(Form)
        .props()
        .checkError()
    ).toContain('invalid');

    wrapper
      .find(Form)
      .props()
      .submitForm();

    expect(
      wrapper
        .find(Form)
        .props()
        .checkError()
    ).toContain('elements');
  });

  it('should update with updateField array of objects', () => {
    const Form = ({ form }) => <div>{form.elements[0].value}</div>;

    const Component = compose(
      withForm({
        elements: {
          value: [{ id: 1, value: 1 }, { id: 2, value: 2 }],
          required: true
        }
      }),
      withHandlers({
        updateFieldObject: ({ updateField }) => () => {
          updateField('elements', { id: 1, value: 3 });
        },
        submit: ({ submitForm }) => () => {
          submitForm();
        }
      })
    )(Form);

    const wrapper = mount(<Component />);

    wrapper
      .find(Form)
      .props()
      .updateFieldObject();

    wrapper
      .find(Form)
      .props()
      .submit();

    expect(wrapper.find('div').html()).toContain('3');
  });

  it('should add with updateField a element to array of objects', () => {
    const Form = ({ form }) => (
      <div>{form.elements[2] && form.elements[2].value}</div>
    );

    const Component = compose(
      withForm({
        elements: {
          value: [{ id: 1, value: 1 }, { id: 2, value: 2 }],
          required: true
        }
      }),
      withHandlers({
        updateFieldObject: ({ updateField }) => () => {
          updateField('elements', { id: 3, value: 4 });
        },
        submit: ({ submitForm }) => () => {
          submitForm();
        }
      })
    )(Form);

    const wrapper = mount(<Component />);

    wrapper
      .find(Form)
      .props()
      .updateFieldObject();

    wrapper
      .find(Form)
      .props()
      .submit();

    expect(wrapper.find('div').html()).toContain('4');
  });

  it('should test that the form has changed', () => {
    const Form = ({ form, updateForm, submitForm }) => (
      <form>
        <input name="name" value={form.name} onChange={updateForm} />
        <button type="submit" onClick={submitForm} />
      </form>
    );

    const Component = compose(
      withForm({ name: { value: 'Peter', required: true } }),
      withHandlers({
        checkChanged: ({ formIsChanged }) => () => formIsChanged
      })
    )(Form);

    const wrapper = mount(<Component />);
    const event = { target: { name: 'name', value: 'Thomas' } };
    expect(wrapper.find(Form).props().formIsChanged).toBeFalsy();
    wrapper.find('input').simulate('change', event);
    expect(wrapper.find(Form).props().formIsChanged).toBeTruthy();
    const newEvent = { target: { name: 'name', value: 'Peter' } };
    wrapper.find('input').simulate('change', newEvent);
    expect(wrapper.find(Form).props().formIsChanged).toBeFalsy();
  });

  it('should match the value of the two fields', () => {
    const Form = ({ form, updateForm, submitForm }) => (
      <form>
        <input name="password" value={form.password} onChange={updateForm} />
        <input
          name="repeatPassword"
          value={form.repeatPassword}
          onChange={updateForm}
        />
        <button type="submit" onClick={submitForm} />
      </form>
    );

    const Component = compose(
      withForm({
        password: { value: '', required: true, match: 'repeatPassword' },
        repeatPassword: { value: '', required: true, match: 'password' }
      }),
      withHandlers({
        checkChanged: ({ formIsChanged }) => () => formIsChanged
      })
    )(Form);

    let event;

    const wrapper = mount(<Component />);
    event = { target: { name: 'password', value: '1234' } };
    wrapper.find('input[name="password"]').simulate('change', event);
    expect(wrapper.find(Form).props().formFieldsWithErrors).toHaveLength(2);
    event = { target: { name: 'repeatPassword', value: '1234' } };
    wrapper.find('input[name="repeatPassword"]').simulate('change', event);
    expect(wrapper.find(Form).props().formFieldsWithErrors).toHaveLength(0);
    event = { target: { name: 'password', value: '123' } };
    wrapper.find('input[name="password"]').simulate('change', event);
    expect(wrapper.find(Form).props().formFieldsWithErrors).toHaveLength(2);
  });

  it('should copy the value from one input to another', () => {
    const Form = ({ form, updateForm, submitForm }) => (
      <form>
        <input name="name" value={form.name} onChange={updateForm} />
        <input name="copyName" value={form.copyName} onChange={updateForm} />
        <button type="submit" onClick={submitForm} />
      </form>
    );

    const Component = compose(
      withForm({
        name: { value: '', required: true, copyTo: 'copyName' },
        copyName: { value: '', required: true }
      })
    )(Form);

    let event;

    const wrapper = mount(<Component />);
    event = { target: { name: 'name', value: 'Example' } };
    wrapper.find('input[name="name"]').simulate('change', event);
    expect(wrapper.find(Form).props().form.name).toBe('Example');
    expect(wrapper.find(Form).props().form.copyName).toBe('Example');
    event = { target: { name: 'copyName', value: 'Example2' } };
    wrapper.find('input[name="copyName"]').simulate('change', event);
    expect(wrapper.find(Form).props().form.name).toBe('Example');
    expect(wrapper.find(Form).props().form.copyName).toBe('Example2');
  });

  it('should update all form', () => {
    const Form = ({ form, updateForm, submitForm }) => (
      <form>
        <input name="name" value={form.name} onChange={updateForm} />
        <input name="email" value={form.email} onChange={updateForm} />
        <button type="submit" onClick={submitForm} />
      </form>
    );

    const Component = compose(
      withForm({
        name: { value: 'example', required: true },
        email: { value: 'example@example.com', required: true }
      }),
      withHandlers({
        update: ({ updateAllForm }) => () => {
          const form = {
            name: 'not-example',
            email: 'not-email@email.com'
          };

          updateAllForm(form);
        }
      })
    )(Form);

    const wrapper = mount(<Component />);

    expect(wrapper.find(Form).props().form.name).toBe('example');
    expect(wrapper.find(Form).props().form.email).toBe('example@example.com');

    wrapper
      .find(Form)
      .props()
      .update();

    wrapper.update();

    expect(wrapper.find(Form).props().form.name).toBe('not-example');
    expect(wrapper.find(Form).props().form.email).toBe('not-email@email.com');
  });

  it('should emit event error', () => {
    const Form = ({ form, updateForm, submitForm }) => (
      <form>
        <input name="name" value={form.name} onChange={updateForm} />
        <input name="email" value={form.email} onChange={updateForm} />
        <button type="submit" onClick={submitForm} />
      </form>
    );

    const Component = compose(
      withState('error', 'setError', false),
      withForm(
        {
          name: { value: '', required: true },
          email: { value: '', required: true }
        },
        () => {},
        props => () => {
          props.setError(true);
        }
      )
    )(Form);

    const wrapper = mount(<Component />);

    const event = { target: { name: 'name', value: 'Example' } };
    wrapper.find('input[name="name"]').simulate('change', event);

    expect(wrapper.find(Form).props().error).toBeFalsy();
    wrapper.find('button').simulate('click');
    wrapper.update();
    expect(wrapper.find(Form).props().error).toBeTruthy();
  });

  it('should be able to change dynamically whether a field is required or not', () => {
    const Form = ({ form, updateForm, submitForm }) => (
      <form>
        <input name="name" value={form.name} onChange={updateForm} />
        <button type="submit" onClick={submitForm} />
      </form>
    );

    const Component = compose(
      withForm({
        name: { value: '', required: true }
      }),
      withHandlers({
        isNotRequired: ({ updateRequired }) => () => {
          updateRequired('name', false);
        }
      })
    )(Form);

    const wrapper = mount(<Component />);

    wrapper.find('button').simulate('click');
    wrapper.update();
    expect(wrapper.find(Form).props().formError).toBeTruthy();
    wrapper
      .find(Form)
      .props()
      .isNotRequired();
    wrapper.update();
    wrapper.find('button').simulate('click');
    wrapper.update();

    expect(wrapper.find(Form).props().formError).toBeFalsy();
  });

  it('should empty the value of a field', () => {

    const Form = ({ form, updateForm, submitForm }) => (
      <form>
        <input name="name" value={form.name} onChange={updateForm} />
        <button type="submit" onClick={submitForm} />
      </form>
    );

    const Component = compose(
      withForm({
        name: { value: 'Peter', required: true }
      }),
      withHandlers({
        clearField: ({ emptyField }) => () => {
          emptyField('name');
        }
      })
    )(Form);

    const wrapper = mount(<Component />);

    expect(wrapper.find(Form).props().form.name).toBe('Peter');
    wrapper
      .find(Form)
      .props()
      .clearField();

    wrapper.update();
    expect(wrapper.find(Form).props().form.name).toBe('');

  })

  it('should empty array the value of a field of type array', () => {

    const Form = ({ form, updateForm, submitForm }) => (
      <form>
        <input name="name" value={form.name} onChange={updateForm} />
        <button type="submit" onClick={submitForm} />
      </form>
    );

    const Component = compose(
      withForm({
        name: { value: [1, 2], required: true }
      }),
      withHandlers({
        clearField: ({ emptyField }) => () => {
          emptyField('name');
        }
      })
    )(Form);

    const wrapper = mount(<Component />);

    expect(wrapper.find(Form).props().form.name).toContain(1);
    wrapper
      .find(Form)
      .props()
      .clearField();

    wrapper.update();
    expect(wrapper.find(Form).props().form.name).toHaveLength(0);

  })
});