Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How can i test a dynamically/lazy loaded component on jest #2212

Open
sahithikol opened this issue Aug 1, 2019 · 24 comments
Open

How can i test a dynamically/lazy loaded component on jest #2212

sahithikol opened this issue Aug 1, 2019 · 24 comments

Comments

@sahithikol
Copy link

sahithikol commented Aug 1, 2019

How can i test a dyanmically/lazy loaded component on jest
I have a test component which loads as

import React, { lazy, Suspense } from "react";

const Component2 = lazy(() => import("./Component2"));
class Component1 extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isClicked: false
    };
  }
  click () {
    this.setState({ isClicked: true });
  };
  render() {
    return (
      <div>
        <div>component1 </div>
        <button onClick={this.click.bind(this)}>get component2</button>
        {this.state.isClicked && (
          <Suspense fallback={<div>loading...</div>}>
            <Component2 />
          </Suspense>
        )}
      </div>
    );
  }
}

export default Component1;

i tried to write tests for this as follows
but could not get a success

import React from 'react';
import Enzyme, { shallow } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import Component1 from '../src/Component1.js';
Enzyme.configure({ adapter: new Adapter() })
describe('MyComponent', () => {
  it('should render correctly i mode', () => {
    const component = shallow(<Component1 />);
    expect(component).toMatchSnapshot();
  });

  it('should not have component2', () => {
    const component = shallow(<Component1 />);
    expect(component.find('Component2').exists()).toEqual(true);
  })

  it('should contain button getComponent2 and should have component2', ()=> {
    const component = shallow(<Component1 />);
    expect(component.instance().state.isClicked).toEqual(false);
    expect(component.find('button').exists());
    const button = component.find('button');
    button.simulate("click");
    expect(component.instance().state.isClicked).toEqual(true);
    expect(component.find('Component2').exists()).toEqual(true);
    expect(component.find('.component-2').exists()).toEqual(true);
  });  

});
@ljharb
Copy link
Member

ljharb commented Aug 3, 2019

In general, I'd avoid using simulate.

In your code above, I'm not really clear what's going on - you're not actually using lazy or Suspense.

@sahithikol
Copy link
Author

sorry @ljharb i have updated the code
added the code snippets where i use react lazy and suspense

@shridharkalagi
Copy link

I'm also stuck on a similar issue. Can someone please help?

@shridharkalagi
Copy link

@ljharb Can you please help?

@sahithikol
Copy link
Author

@ljharb i am stuck on this , can you please suggest

@shridharkalagi
Copy link

Any luck on this @sahithikol ?

@sahithikol
Copy link
Author

no i could not make any progress on this
@shridharkalagi any luck for you
@ljharb please help

@ljharb ljharb changed the title How can i test a dyanmically/lazy loaded component on jest How can i test a dynamically/lazy loaded component on jest Sep 11, 2019
@ljharb
Copy link
Member

ljharb commented Sep 11, 2019

@sahithikol Can you try again without using simulate, and using a wrapper.update() after invoking your onClick prop?

@sahithikol
Copy link
Author

@ljharb i have tried to use to call component.find('button').props().onClick()
instead of simulate , can you please help how can i test this

@sahithikol
Copy link
Author

sahithikol commented Sep 19, 2019

@ljharb i tried testing it this way

import React from 'react';
import Enzyme, { mount, shallow } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import Component1 from '../src/Component1.js';
Enzyme.configure({ adapter: new Adapter() })
describe('MyComponent', () => {
  it('should render correctly i mode', () => {
    const component = shallow(<Component1 />);
    expect(component).toMatchSnapshot();
  });

  it('should contain button getComponent2 and should have component2', ()=> {
    const component = shallow(<Component1 />);
    expect(component.instance().state.isClicked).toEqual(false);
    expect(component.find('button').exists());
    component.find('button').prop('onClick')();
    component.update();
    expect(component.instance().state.isClicked).toEqual(true);

    expect(component.find('Component2').exists()).toEqual(true);
    expect(component.find('.component-2').exists()).toEqual(true);
  });

});

but it fails and the error message that i see when i try to debug is
ReactDOMServer does not yet support Suspense
@shridharkalagi any luck

@orpheus
Copy link

orpheus commented Jan 21, 2020

In general, I'd avoid using simulate.

@ljharb Why?

@ljharb
Copy link
Member

ljharb commented Jan 21, 2020

@orpheus it does not actually simulate anything - it's a terrible API name that enzyme inherited from facebook's shallow renderer, before we realized how bad it is. All it does is invoke a prop (with an implicit transformation from the event name to the prop name), and sometimes provides a fake event object. It's much more explicit to directly invoke the prop you need.

@ljharb
Copy link
Member

ljharb commented Jan 21, 2020

@sahithikol you can't shallow-render nor server-render a component that uses Suspense; instead, that component should avoid using Suspense unless it's already been mounted in a DOM.

@johan-smits
Copy link

@ljharb what should be the solution for this? I see many issues about simular issues like: #2254 and #2196

@ljharb
Copy link
Member

ljharb commented Jan 28, 2020

I believe there is no solution until React provides one, short of avoiding use of Suspense in a server-rendered component.

@johan-smits
Copy link

@ljharb thanks for the feedback.

FYI @ramsy-leftclick

@sahithikolichala
Copy link

Thanks for all the answers
I found out that it works well with the react-testing-library though

@ljharb
Copy link
Member

ljharb commented Feb 4, 2020

That's because r-t-l uses a full react DOM render; mount should work just fine as well, but you need shallow to be able to unit test and cover server rendering.

@AbdelrhmanMagdy
Copy link

Hi @sahithikol , I had a similar issue and it's solved with the help of this package synchronous promise

Just add this snippet to your test file:

import { SynchronousPromise } from 'synchronous-promise';

let __awaiter: Function;

beforeEach(() => {​​​​​
    __awaiter = SynchronousPromise.installGlobally(__awaiter);
}​​​​​);

afterEach(() => {​​​​​
    SynchronousPromise.uninstallGlobally();
}​​​​​);

@shinxi
Copy link

shinxi commented Dec 24, 2021

FYI the workaround that we used at our project.

  1. Extract Lazy and Suspense to a Loadable component. E.g.,
// Loadable.js
import React, { Suspense } from 'react';
import LazyLoadSpinner from '../LazyLoadSpinner';

const Loadable = (config = {}) => {
  const LazyLoadedComponent = React.lazy(config.loader);
  return props => (
    <Suspense fallback={<LazyLoadSpinner />}>
      <LazyLoadedComponent {...props} />
    </Suspense>
  );
};

export default Loadable;
  1. Use the Loadable component to lazy load your component. E.g., For the case in this issue
import React, { lazy, Suspense } from "react";
import Loadable from 'path-to-loadable';

const Component2 = Loadable({
  loader: () => import("./Component2"),
});
class Component1 extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isClicked: false
    };
  }
  click () {
    this.setState({ isClicked: true });
  };
  render() {
    return (
      <div>
        <div>component1 </div>
        <button onClick={this.click.bind(this)}>get component2</button>
        {this.state.isClicked && (
          <Suspense fallback={<div>loading...</div>}>
            <Component2 />
          </Suspense>
        )}
      </div>
    );
  }
}

export default Component1;
  1. Mock Loadable at your test case, i.e.,
jest.mock('../Loadable', () => {
  return function({ loader }) {
    loader();
    return function LoadableComponent() {
      return null;
    };
  };
});
  1. Now you can use 'LoadableComponent' to find the lazy-loaded component. i.e.,
expect(component.find('LoadableComponent').exists()).toEqual(true);

This is not a complete solution for Suspense&Lazy, but it should be sufficient for unit testing.

@Anil-Bhimwal1

This comment was marked as resolved.

@ljharb

This comment was marked as resolved.

@Anil-Bhimwal1

This comment was marked as off-topic.

@ljharb

This comment was marked as off-topic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants