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

Problem with testing #666

Closed
mlobunko opened this issue May 24, 2018 · 3 comments
Closed

Problem with testing #666

mlobunko opened this issue May 24, 2018 · 3 comments

Comments

@mlobunko
Copy link

Summary:

Hi everyone!
I have an issue with testing a modal render. I expect that by using mount() can check if inner div exist. But I can't, test fails and div doesn't exist.
My dependencies: "enzyme": "^3.3.0", "react-modal": "^3.4.4".
I understand that those expamples https://remarkablemark.org/blog/2017/05/17/testing-react-modal/ are outdated. I read two threads about latest ones #71 and #563. I tried this example #71 (comment) and it works. So maybe I'm wrong in passing data? Thank you so much for your help.

Steps to reproduce:

Settings.js

import React from "react";
import ReactModal from "react-modal";
import { connect } from "react-redux";
import ContentSettings from "./ContentSettings";
import { closeSettings } from "../actions/statistics";

export const Settings = ({ isSettingsOpen }) => (
  <div>
    <ReactModal
      isOpen={isSettingsOpen}
      onRequestClose={closeSettings}
      contentLabel="Settings"
      className="Modal"
      overlayClassName="Overlay"
      ariaHideApp={false}
    
      <div className="modal-inside">
        <ContentSettings />
      </div>
    </ReactModal>
  </div>
);

const mapStateToProps = state => ({
  isSettingsOpen: state.statistics.isSettingsOpen
});

export default connect(mapStateToProps, { closeSettings })(Settings);

Settings.test.js

import React from "react";
import ReactModal from "react-modal";
import { mount, ReactWrapper, shallow } from "enzyme";
import { Settings } from "../../components/Settings";
import { ContentSettings } from "../../components/ContentSettings";

it("should render modal when isSettingsOpen is true", () => {
  const isSettingsOpen = true;
  const wrapper = mount(<Settings {...isSettingsOpen} />);
  wrapper.update();
  expect(wrapper.find(".modal-inside").exists()).toEqual(true);
});

Link to example of issue:

Link to repo https://github.com/mlobunko/pomodoro-app

@shlomitc
Copy link

The reason you can't find the modal content is that react-modal is using a portal to port the content to document.body.
The enzyme wrapper search only in the rendered tree, meaning you will not see content that is ported outside your mounting node

Here are two tests showing what happens when you render the content with react-modal and without it, and how you should query it in the last case:

import 'jsdom-global/register';
import React from 'react';
import {mount} from 'enzyme';
import ReactModal from 'react-modal';
import {expect} from 'chai';

describe('ModalExample', () => {
  let wrapper, mountingDiv;

  beforeEach(() => {
    mountingDiv = document.createElement('div');
    document.body.appendChild(mountingDiv);
  });

  afterEach(() => {
    wrapper.detach();
    document.body.removeChild(mountingDiv);
  });

  it('should find the content with enzyme wrapper when not using react-modal', () => {
    wrapper = mount(
      <div className="someContent">
        some content
      </div>,
      {attachTo: mountingDiv}
    );
    expect(wrapper.find('.someContent').exists()).to.equal(true);
  });

  it('should find the content on the body when using react-modal', () => {
    ReactModal.setAppElement('body');
    wrapper = mount(
      <ReactModal isOpen>
        <div className="someContent">
          some content
        </div>
      </ReactModal>,
      {attachTo: mountingDiv}
    );
    expect(wrapper.find('.someContent').exists()).to.equal(false);
    expect(!!document.body.querySelector('.someContent')).to.equal(true);
  });
});

And this is an output of the html when rendering with react-modal:

<body class="ReactModal__Body--open" aria-hidden="true">
  <div>
    <!-- react-empty: 1 -->
  </div>
  <div class="ReactModalPortal">
    <div data-reactroot="" class="ReactModal__Overlay ReactModal__Overlay--after-open" aria-modal="true" style="position: fixed; top: 0px; left: 0px; right: 0px; bottom: 0px; background-color: rgba(255, 255, 255, 0.75);">
      <div class="ReactModal__Content ReactModal__Content--after-open" tabindex="-1" style="position: absolute; top: 40px; left: 40px; right: 40px; bottom: 40px; border: 1px solid #ccc; background: rgb(255, 255, 255); overflow: auto; border-radius: 4px; outline: none; padding: 20px;">
        <div class="someContent">some content</div>
      </div>
    </div>
  </div>
</body>

@mlobunko
Copy link
Author

Thank you for sharing your knowledge. Your explanation is really great.
We sure can find an element with querySelector. Although I found that with just a mount() it works well too. But because I have inner components (with redux) I have to connect them to the store. So I'm using CustomProvider to pass in the redux store.
So this is what I ended up with.

CustomProvider.js

import React from "react";
import { Provider } from "react-redux";
import { createStore, combineReducers } from "redux";
import settingsReducer from "../../reducers/settings";
import statisticsReducer from "../../reducers/statistics";

const store = createStore(
  combineReducers({
    settings: settingsReducer,
    statistics: statisticsReducer
  })
);

export const CustomProvider = ({ children }) => {
  return <Provider store={store}>{children}</Provider>;
};

Settings.test.js

import React from "react";
import { shallow, mount } from "enzyme";
import { Settings } from "../../components/Settings";
import { CustomProvider } from "./CustomProvider";

it("should render Settings component correctly", () => {
  const wrapper = shallow(<Settings />);
  expect(wrapper).toMatchSnapshot();
});

describe("modal showing", () => {
  it("show modal when isOpen is true", () => {
    const props = {
      isSettingsOpen: true
    };
    const wrapper = mount(
      <CustomProvider>
        <Settings {...props} />
      </CustomProvider>
    );
    expect(wrapper.find(".Modal").exists()).toEqual(true);
    expect(wrapper.find(".content-settings").exists()).toEqual(true);
  });

  it("hide modal when isOpen is false", () => {
    const props = {
      isSettingsOpen: false
    };
    const wrapper = mount(
      <CustomProvider>
        <Settings {...props} />
      </CustomProvider>
    );
    expect(wrapper.find(".Modal").exists()).toEqual(true);
    expect(wrapper.find(".content-settings").exists()).toEqual(false);
  });
});

Thank you again for your help. I think I can close the issue.

@Rajdeepc-tech
Copy link

How do we test if isSettingsOpen is a local state inside Settings component?

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

3 participants