mercredi 26 février 2020

Jest: Cannot spy the property because it is not a function; undefined given instead

I'm trying to write a Jest test for a simple React component to confirm that a function has been called when I simulate a click.

However, when I use spyOn method, I keep getting TypeError: Cannot read property 'validateOnSave' of undefined. My code looks like this:

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]);
  });
});

Ultimate goal is to test two cases when save is clicked:

  1. When state.codeSelectionIsInvalid: [false,false,true]

  2. When state.codeSelectionIsInvalid: [false,false,false]

Where am I going wrong here. Any help is appreciated!

Aucun commentaire:

Enregistrer un commentaire