Как проверить компоненты, завернутые в форму antd? - PullRequest
0 голосов
/ 03 января 2019

Я боролся с этим уже несколько месяцев.Несмотря на то, что существует множество предположений о том, как правильно тестировать компоненты, упакованные в antd, ни одно из предложений не сработало для этого конкретного компонента.

Итак, у меня есть компонент, который является модальным с формой antd.В этой форме у меня есть несколько полей: вход, выбор и выбор дерева, ничего особенного.

В основном это так:

class FormModal extends React.Component {
  static propTypes = {
    data: propTypes.object,
    form: propTypes.object,
    scopes: propTypes.array.isRequired,
    clients: propTypes.array.isRequired,
    treeData: propTypes.array.isRequired,
    isEditing: propTypes.bool.isRequired,
    isSaving: propTypes.bool.isRequired,
    onCancel: propTypes.func.isRequired,
    onSave: propTypes.func.isRequired,
    onFilterTreeData: propTypes.func.isRequired,
    visible: propTypes.bool.isRequired
  }

  static defaultProps = {
    data: null,
    form: {}
  }

  state = {
    selectedScopes: [],
    newScopes: [],
    inputVisible: false,
    inputValue: ''
  };

  componentDidMount() {
    // do stuff
  }

  handleSave = () => {
    // do stuff
  }

  handleSelectedScopesChange = (event) => {
    // do stuff
  }

  updateTreeSelect = () => {
    const { form } = this.props;
    const { selectedScopes } = this.state;

    form.setFieldsValue({
      allowedScopes: selectedScopes
    });
  }

  handleRemoveTag = (removedTag) => {
    const selectedScopes = this.state.selectedScopes.filter(scope => scope !== removedTag);
    const newScopes = this.state.newScopes.filter(scope => scope !== removedTag);
    this.setState({ selectedScopes, newScopes }, this.updateTreeSelect);
  }

  showInput = () => {
    this.setState({ inputVisible: true }, () => this.input.focus());
  }

  handleInputChange = (e) => {
    const inputValue = e.target.value;
    this.setState({ inputValue });
  }

  handleInputConfirm = () => {
    const { newScopes, inputValue } = this.state;
    let tags = newScopes;
    if (inputValue && tags.indexOf(inputValue) === -1) {
      tags = [inputValue, ...tags];
    }

    this.setState({
      newScopes: tags,
      inputVisible: false,
      inputValue: '',
    });
  }

  saveInputRef = input => this.input = input

  renderTags = (scopeArrays) => {
    const tags = scopeArrays.map(scopeArray =>
      scopeArray.map((permition) => {
        let scopeType = null;
        if (permition.includes('read') || permition.includes('get')) scopeType = 'blue';
        if (permition.includes('create') || permition.includes('write') || permition.includes('send')) scopeType = 'green';
        if (permition.includes('update')) scopeType = 'gold';
        if (permition.includes('delete')) scopeType = 'red';
        return (
          <Tag
            key={permition}
            color={scopeType || 'purple'}
            style={{ margin: '2px 4px 2px 0' }}
            closable
            afterClose={() => this.handleRemoveTag(permition)}
          >
            {permition}
          </Tag>
        );
      })
    );

    return [].concat(...tags);
  }

  render() {
    const {
      selectedScopes,
      newScopes,
      inputValue,
      inputVisible
    } = this.state;

    const {
      form,
      treeData,
      clients,
      isEditing,
      isSaving,
      onCancel,
      onFilterTreeData,
      visible
    } = this.props;

    const {
      getFieldDecorator,
      getFieldsError,
    } = form;

    const selectedScopesTags = this.renderTags([newScopes, selectedScopes]);

    const clientOptions = clients.map(client => (<Option key={client._id}>{client.name}</Option>));

    return (
      <Modal
        className="user-modal"
        title={isEditing ? 'Editing Group' : 'Creating Group'}
        visible={visible}
        onCancel={onCancel}
        footer={[
          <Button key="cancel" onClick={onCancel}>Cancel</Button>,
          <Button
            key="save"
            type="primary"
            loading={isSaving}
            onClick={this.handleSave}
            disabled={formRules.hasErrors(getFieldsError())}
          >
            Save
          </Button>
        ]}
      >
        <Form layout="vertical" onSubmit={this.handleSave}>
          <Row gutter={24}>
            <Col span={12}>
              <FormItem label="Name">
                {getFieldDecorator(
                  'name',
                  { rules: [formRules.required, { max: 20, message: 'Group name can\'t excede 20 characters' }] }
                )(
                  <Input />
                )}
              </FormItem>
            </Col>
            <Col span={12}>
              <FormItem label="Client">
                {getFieldDecorator(
                  'client', { rules: [formRules.required] }
                )(
                  <Select placeholder="Please select client">
                    {clientOptions}
                  </Select>
                )}
              </FormItem>
            </Col>
            <Col span={24}>
              <FormItem label="Scopes">
                {getFieldDecorator(
                  'allowedScopes'
                )(
                  <TreeSelect
                    treeData={treeData}
                    filterTreeNode={onFilterTreeData}
                    onChange={this.handleSelectedScopesChange}
                    treeCheckable
                    dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
                    showCheckedStrategy="SHOW_PARENT"
                    searchPlaceholder="Filter by scopes"
                    className="groups__filter groups__filter--fill"
                  />
                )}
              </FormItem>
            </Col>
            <Col span={24}>
              <Card
                title="Selected Scopes"
                style={{ width: '100%' }}
              >
                <div>
                  {inputVisible && (
                    <Input
                      ref={this.saveInputRef}
                      type="text"
                      size="small"
                      style={{ width: 350 }}
                      value={inputValue}
                      onChange={this.handleInputChange}
                      onBlur={this.handleInputConfirm}
                      onPressEnter={this.handleInputConfirm}
                    />
                  )}
                  {!inputVisible && (
                    <Tag
                      onClick={this.showInput}
                      style={{ background: '#fff', borderStyle: 'dashed', margin: '5px 0' }}
                    >
                      <Icon type="plus" /> New Scope
                    </Tag>
                  )}
                </div>
                { selectedScopesTags.length > 0 ? (
                  selectedScopesTags
                ) : <p>No scopes selected yet.</p> }
              </Card>
            </Col>
          </Row>
        </Form>
      </Modal>
    );
  }
}

export default Form.create()(FormModal);

Я знаю, что этот компонент требуетдля рефакторинга, но это не моя работа прямо сейчас.Мне нужно проверить это и проверить, все ли работает правильно.

Я пытаюсь проверить, правильно ли отображаются поля формы.Я использую Jest и Enzyme, и до сих пор я получил это:

describe('Groups modal', () => {
  let props;
  const groupsModal = () =>
    mount(
      <FormModal.WrappedComponent {...props} />
    )

  beforeEach(() => {
    props = {
      data: null,
      scopes: [],
      clients: [],
      treeData: [],
      isEditing: false,
      isSaving: false,
      onCancel: jest.fn(),
      onSave: jest.fn(),
      onFilterTreeData: jest.fn(),
      visible: true,
      form: {
        getFieldsError: jest.fn(() => { return {} }),
        getFieldDecorator: () => (component) => component
      }
    };
  });

  it('should render properly', () => {
    const wrapperDivChilds = groupsModal().find('.user-modal').children();

    expect(wrapperDivChilds.length).toBeGreaterThanOrEqual(1);
  });

  describe('form fields', () => {
    it('should render name input', () => {
      const nameInput = groupsModal().find(Input);

      expect(nameInput.length).toBe(1);
    });

    it('should render clients select', () => {
      const clientsSelect = groupsModal().find(Select);

      expect(clientsSelect.length).toBe(1);
    });

    it('should render scopes tree select', () => {
      const scopesTreeSelect = groupsModal().find(TreeSelect);

      expect(scopesTreeSelect.length).toBe(1);
    });
  });
});

Все мои тесты, которые проверяют, были ли обработаны входные данные, терпят неудачу.Как вы можете видеть, я пытался насмехаться над функциями декоратора форм, но все равно безуспешно ...

Итак, мой вопрос: как мне протестировать этот компонент?

1 Ответ

0 голосов
/ 12 марта 2019

Если вы хотите утверждать только изначально предоставленную информацию, вы можете напрямую импортировать упакованный компонент и сделать:

const component = mount(<WrappedComponent {...props} form={formMock} />);

С другой стороны, если вы не хотите использовать фиктивную форму или хотите утверждатьлюбую логику, связанную с цепочкой Form-Observer-wrappedComponent, вы можете сделать следующим образом:

const wrapper = mount(<FormWrapperComponent {...props} />);
// props include anything needed for initial mapPropsToFields
const component = wrapper.find(WrappedComponent);
// Do anything with form itself
const { form } = component.instance().props;

Вроде бы больше времени, но сработало идеально для меня, потратил некоторое время, чтобы понять, как избежать использования макета формы.В этом случае вам не нужно имитировать что-либо, связанное с самой формой, и если какое-либо поле не отображается, как вы ожидаете, вы можете быть уверены, что проблема в другом фрагменте кода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...