TypeScript - invoking React prop with Enzyme

Viewed 719

I'm trying to convert my Jest tests using Enzyme to TypeScript, but running into one particular case that I'm not sure how to handle. Basically, I'm trying to call a function that is passed as a prop to a sub-component. The error I'm seeing is:

spec/javascript/_common/components/sidebar_spec.tsx:85:5 - error TS2349:

Cannot invoke an expression whose type lacks a call signature. Type '{}' has no compatible call signatures.

85     component.find(Link).at(0).prop('onNavigate')();
       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

How do I get past this error? Not sure if it's helpful, but more context for the test:

it('does not hide the sidebar after a link is clicked', () => {
  const component = shallow(<Sidebar />);

  component.find(Link).at(0).prop('onNavigate')();
  component.update();

  expect(component.find(Link)).toHaveLength(3);
});

And a chunk of code from the Sidebar component:

class Sidebar extends React.Component<any, any> {

  ...

  hideIfMobile() {
    const {mobile} = this.state;

    if (mobile) { this.setState({visible: false}); }
  }

  render() {
    const {visible} = this.state;

    if (!visible) {
      return (
        <div className='sidebar sidebar--hidden'>
          {this.sidebarToggle()}
        </div>
      );
    }

    const linkProps = {
      baseClass: 'sidebar__link',
      onNavigate: this.hideIfMobile,
    };

    return (
      <div className='sidebar sidebar--visible'>
        <h2 className='sidebar__header'>{'Menu'}{this.sidebarToggle()}</h2>
        <hr className='sidebar__divider' />
        <Link to='root' {...linkProps}><h2>{'FOCUS'}</h2></Link>
        <Link to='tasks' {...linkProps}><h2>{'ALL TASKS'}</h2></Link>
        <Link to='timeframes' {...linkProps}><h2>{'TIMEFRAMES'}</h2></Link>
      </div>
    );
  }
}

the Link component is wrapped in react-redux:

import {connect} from 'react-redux';

import Link from 'src/route/components/link';
import {getRouteName} from 'src/route/selectors';
import {setRoute} from 'src/route/action_creators';

function mapStateToProps(state) {
  return {routeName: getRouteName(state)};
}

export default connect(mapStateToProps, {setRoute})(Link);

and the actual component:

class Link extends React.Component<any, any> {
  navigate(event) {
    event.preventDefault();

    const {onNavigate, params, setRoute, to} = this.props;

    setRoute({name: to, ...params});

    if (onNavigate) { onNavigate(); }
  }

  path() {
    const {params, to} = this.props;
    const pathParams = mapValues(params, value => value.toString());

    return findRoute(to).toPath(pathParams);
  }

  className() {
    const {baseClass, className, to, routeName} = this.props;

    return classnames(
      baseClass,
      {[`${baseClass}--active`]: baseClass && routeName === to},
      className,
    );
  }

  render() {
    const {children} = this.props;

    return (
      <a
        href={this.path()}
        className={this.className()}
        onClick={this.navigate}
      >
        {children}
      </a>
    );
  }
}
1 Answers

It turns out Link in this case was defined earlier in my test file as:

const Link = 'Connect(Link)';

I switched this to import the actual link container and it resolved the issue.

import Link from 'src/route/containers/link';
Related