0

I am testing on the execution of functions inside a React component that's connected to redux store. I was only able to spy on some of the functions, the rest all returned: Cannot spy the setEmail(or other function names) property because it is not a function; undefined given instead

below in code I added arrows pointing out which ones are spy-able, when I console.log(BaseForm.WrappedComponent.prototype.functionName) the ones that are not spy-able returned undefined. the one that can be spied on returned [Function:...] I really do not understand why?

class BaseForm extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      ages: [],
    };
  }

  componentDidMount() {
    this.createAges(); <----- can spy on this one as it is a function
  }

  setEmail = (elemName, value) => { <------ Cannot spy the setEmail property because it is not a function; undefined
    this.setState({ email: value });
    this.props.saveEmail(value);
  };

  handleEmailSignup = (e) => { <-------- Cannot spy the setEmail property because it is not a function; undefined
    this.setState({ offersSignup: e.target.checked });
  };

  onChangeAge = (e) => { <------ Cannot spy the setEmail property because it is not a function; undefined
    this.setState({
      selectedAge: e.target.value,
      selectedAgeIndex: e.target.index - 1,
      errorAge: '',
    });
  };

  createAges() { <------ can spyOn as it is a function
    let ages = [
      { value: '', text: !__isEmpty(sessionStorage.getItem('gf')) ? 'Kid age' : 'Your age' },
      { value: '14 and younger', text: '14 and younger' },
      { value: '15', text: '15' },
      { value: '16', text: '16' },
      { value: '17', text: '17' },
    ];

    this.setState({ ages: ages });
  }

  render() {
    return (
      <div>
        <div data-type="email" className="textbox-wrapper">
          <Textbox
            type="email"
            placeholder="Email Address"
            name="register-email"
            onChange={this.setEmail}
            onBlur={this.checkUserByEmail}
            defaultValue={this.state.email} <------- in test the value does not change, but on UI it does and functions well
            isError={!__isEmpty(this.props.emailErrorClass)}
          />
        </div>

        <Dropdown options={this.state.ages} onChange={this.onChangeAge} selectedValue={this.state.selectedAge} />

        <div>
          <input name="offersSignup" type="checkbox" onChange={this.handleEmailSignup} checked={this.state.offersSignup} />
        </div>
      </div>
    );
  }
}

const mapDispatchToProps = { saveEmail };

export default connect(null, mapDispatchToProps)(BaseForm);

it.only('set email in local state if onChange of Textbox is fired', () => {
    //console.log(BasePartnerRegistrationForm.WrappedComponent.prototype.setEmail);
    // above is undefined
    const setEmailSpy = jest.spyOn(BaseForm.WrappedComponent.prototype, 'setEmail');
    const wrapper = mount(
      <Provider store={store}>
        <BaseForm {...baseProps} />
      </Provider>
    );

    const event = { target: { value: 'event value' } };
    wrapper.find('Textbox').at(0).simulate('change', event);
    wrapper.update();
    expect(setEmailSpy).toHaveBeenCalled();
    // below shows unchanged email value in state
    // expect(wrapper.find('Textbox').at(0).props().defaultValue).toBe(event.target.value);
  });

Below tried a new approach, console errors: Expected: "event value" Received: "" in other words,per test case, the setState is not functioning, but it functions well on UI and in chrome console too.

it('set email in local state if onChange of Textbox is fired', async () => {
    const wrapper = mount(
      <Provider store={store}>
        <BasePartnerRegistrationForm {...baseProps} />
      </Provider>
    );

    const event = { target: { value: 'event value' } };
    wrapper.find('Textbox').at(0).simulate('change', event);
    await waitFor(() => expect(wrapper.find('Textbox').at(0).props().defaultValue).toBe(event.target.value));
  });
DollyBeeBee
  • 83
  • 3
  • 8

1 Answers1

0

Rather than spying on the method call, just check the DOM. First, I would suggest you look at React Testing Library for finding fields, setting values, querying the DOM for changes. This, with Jest, is the new standard in React testing (even included with React-Create-App). It's a paradigm shift, in that use test user interaction and result (as a user would), which is testing the underlying logic.

The next thing to consider is timing. When you set state it isn't immediate. It takes a few milliseconds for state changes to render. RTL provides a waitFor method that simplifies this further.

waitFor(() => /*some assertion*/);
Steve -Cutter- Blades
  • 5,057
  • 2
  • 26
  • 40
  • Hi thank you! I added the last code block per your suggestion in the question, it is not returning the updated value(new value thats set in state). the component and UI functions correctly tho. – DollyBeeBee Jan 25 '22 at 21:12