Шут: не может шпионить свойство, потому что это не функция; вместо этого дано неопределенное - PullRequest
1 голос
/ 27 февраля 2020

Я пытаюсь написать Jest-тест для простого компонента React, чтобы подтвердить, что функция была вызвана, когда я имитирую щелчок.

Однако, когда я использую метод spyOn, я получаю TypeError: Невозможно прочитать свойство 'validateOnSave' из неопределенного. Мой код выглядит так:

OptionsView . js

class OptionsView extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      reasonCode: null,
      remarkCode: null,
      otherCode: null,
      codeSelectionIsInvalid: [false, false, false],
    };
    this.validateOnSave = this.validateOnSave.bind(this);
    this.saveOptions = this.saveOptions.bind(this);

validateOnSave() {
    const copy = this.state.codeSelectionIsInvalid;
    copy[0] = !this.state.reasonCode;
    copy[1] = !this.state.remarkCode;
    copy[2] = !this.state.otherCode;
    this.setState({ codeSelectionIsInvalid: copy });

   if (!copy[0] && !copy[1] && !copy[2]) {
      this.saveOptions();
    }
  }

  saveOptions() {
    const { saveCallback } = this.props;
    if (saveCallback !== undefined) {
      saveCallback({ reasonCode: this.state.reasonCode, remarkCode: this.state.remarkCode, otherCode: this.state.otherCode,
      });
    }
  }
render() {
const cx = classNames.bind(styles);
const reasonCodes = this.props.reasonCodeset.map(reasonCode => (
      <Select.Option
        value={reasonCode.objectIdentifier}
        key={reasonCode.objectIdentifier}
        display={`${reasonCode.name}`}
      />
    ));
const remarkCodes = this.props.remarkCodeset.map(remarkCode => (
      <Select.Option
        value={remarkCode.objectIdentifier}
        key={remarkCode.objectIdentifier}
        display={`${remarkCode.name}`}
      />
    ));
const otherCodes = this.props.otherCodeset.map(otherCode => (
      <Select.Option
        value={otherCode.objectIdentifier}
        key={otherCode.objectIdentifier}
        display={`${otherCode.name}`}
      />
    ));
return (
      <ContentContainer fill>
        <Spacer marginTop="none" marginBottom="large+1" marginLeft="none" marginRight="none" paddingTop="large+2" paddingBottom="none" paddingLeft="large+2" paddingRight="large+2">
          <Fieldset legend="Code sets">
            <Grid>
              <Grid.Row>
                <Grid.Column tiny={3}>
                  <SelectField selectId="reasons" required placeholder="Select" label="Reasons:" error="Required field is missing" value={this.state.reasonCode} onChange={this.updateReasonCode} isInvalid={this.state.codeSelectionIsInvalid[0]}>
                    {reasonCodes}
                  </SelectField>
                </Grid.Column>
              </Grid.Row>
              <Grid.Row>
                <Grid.Column tiny={3}>
                  <SelectField selectId="remarks" required placeholder="Select" label="Remarks:" error="Required field is missing" value={this.state.remarkCode} onChange={this.updateRemarkCode} isInvalid={this.state.codeSelectionIsInvalid[1]}>
                    {remarkCodes}
                  </SelectField>
                </Grid.Column>
              </Grid.Row>
              <Grid.Row>
                <Grid.Column tiny={3}>
                  <SelectField selectId="other-codes" required placeholder="Select" label="Other Codes:" error="Required field is missing" value={this.state.otherCode} onChange={this.updateOtherCode} isInvalid={this.state.codeSelectionIsInvalid[2]}>
                    {otherCodes}
                  </SelectField>
</Grid.Column>
              </Grid.Row>
</Grid>

</Fieldset>
        </Spacer>
        <ActionFooter
          className={cx(['action-header-footer-color'])}
          end={(
            <React.Fragment>
              <Spacer isInlineBlock marginRight="medium">
                <Button text="Save" onClick={this.validateOnSave} />
              </Spacer>
            </React.Fragment>
          )}
        />
      </ContentContainer>
    );
  }
}

OptionsView.propTypes = propTypes;

export default injectIntl(OptionsView);

OptionsView.test

describe('RemittanceOptions View', () => {
let defaultProps = {...defined...}
beforeAll(() => {  
    Object.defineProperty(window, "matchMedia", {
      value: jest.fn(() => { 
        return { 
          matches: true,
          addEventListener: jest.fn(),
          removeEventListener: jest.fn(),
          addEventListener: jest.fn(),
          removeEventListener: jest.fn(),
          dispatchEvent: jest.fn(),
        } 
      })
    });
  });

it('should validate remit codes on save', () => {
    const wrapper = mountWithIntl(<OptionsView
      {...defaultProps}
    />); 
    const instance = wrapper.instance();
    const spy = jest.spyOn(instance, "validateOnSave");
    wrapper.setState({
      reasonCode: 84,
      remarkCode: 10,
      otherCode: null
    });
    console.log(wrapper.find('Button[text="Save"]').debug()); 
    const button = wrapper.find('Button[text="Save"]').at(0);
    expect(button.length).toBe(1);
    button.simulate('click');
    expect(spy).toHaveBeenCalled();
    expect(wrapper.state('codeSelectionIsInvalid')).toEqual([false,false,true]);
  });
});

Конечная цель - проверить два случая, когда нажата кнопка сохранения:

  1. Когда state.codeSelectionIsInvalid: [false, false, true]

  2. Когда state.codeSelectionIsInvalid: [false, false, false]

Где я тут ошибаюсь. Любая помощь приветствуется!

1 Ответ

1 голос
/ 27 февраля 2020

После нескольких часов отладки обнаружил, что к экземпляру не привязаны никакие методы. Поскольку это подключенный компонент, использование shallowWithIntl () и dive () устранило ошибку.

it('should validate remit codes on save', () => {
    const wrapper = shallowWithIntl(<RemitOptionsView
      {...testProps}
    />);
    const button = wrapper.dive().find('Button[text="Save"]'); //Not finding the button
    const instance = wrapper.dive().instance();
    const spy = jest.spyOn(instance, 'validateOnSave');
    instance.validateOnSave();
  });

Action Footer

...