dmikester1
dmikester1

Reputation: 1362

Testing for existence of react-modal using React Testing Library with findByRole

I am trying to test for the existence of my modal using React Testing Library with findByRole. I keep getting an error saying: Unable to find role="dialog" even though I can see clearly see it printed out in the console of the test.

Here is my test:

import React from "react";
import {
    render,
    screen,
    within,
    fireEvent, cleanup, waitFor
} from '@testing-library/react';
import '@testing-library/jest-dom';
import "@testing-library/react/dont-cleanup-after-each";
import Modal from 'react-modal';

import PackagerModal from './PackagerModal';

const mockedEmptyFn = jest.fn();

const mockBaseProps = {
    openPackager: true,
    setOpenPackager: mockedEmptyFn,
    activeOrder: {
        // ... // lots of irrelevant properties here
        ]
    },
    setOrders: mockedEmptyFn,
    customStyles: {
        content: {
            backgroundColor: "var(--color-primary)",
            border: "1px solid #ccc",
            boxShadow: "-2rem 2rem 2rem rgba(0, 0, 0, 0.5)",
            color: "rgba(var(--RGB-text), 0.8)",
            filter: "blur(0)",
            fontSize: "1.1em",
            fontWeight: "bold",
            margin: "50px auto",
            opacity: 1,
            outline: 0,
            position: "relative",
            visibility: "visible",
            width: "500px"
        },
        overlay: {
            backgroundColor: "rgba(255, 255, 255, 0.9)"
        }
    },
    readOnly: false,
    setReadOnly: mockedEmptyFn,
};

Modal.setAppElement('body');


const Component = (props) =>    <PackagerModal {...mockBaseProps} {...props} />

describe('Packager Modal tests with editable inputs', () => {
    afterAll(() => {
        cleanup();
    });


    test('Should show packager modal', async () => {
        render(
            <Component/>
        );
        // screen.debug();
        const modalWindow = await screen.findByRole('dialog');
        expect(modalWindow).toBeInTheDocument();
    });
});

And here is my modal:

import ReactModal from 'react-modal';
import React, { useEffect, useRef, useState } from 'react';
import CloseButton from './CloseButton';
import PropTypes from 'prop-types';

const PackagerModal = (props) => {
    const {
        openPackager,
        setOpenPackager,
        activeOrder,
        setOrders,
        customStyles,
        readOnly,
        setReadOnly,
    } = props;

    const cleanUpModal = () => {
        setReadOnly(false);
        setUserInput(initialState);
    };

    return (
        <ReactModal
            isOpen={openPackager}
            style={customStyles}
            className={'order-details-modal'}
            closeTimeoutMS={1000}
            onAfterClose={cleanUpModal}
        >
            <CloseButton setOpenModal={setOpenPackager} />
            <h2 className={'title'}>Packager Order Checklist</h2>
        </ReactModal>
    );
};

PackagerModal.propTypes = {
    openPackager: PropTypes.bool.isRequired,
    setOpenPackager: PropTypes.func.isRequired,
    customStyles: PropTypes.object.isRequired,
    activeOrder: PropTypes.object.isRequired,
    setOrders: PropTypes.func.isRequired,
    readOnly: PropTypes.bool.isRequired,
    setReadOnly: PropTypes.func.isRequired,
};

export default PackagerModal;

And finally, here is some of the output I see in the console from the test:

 ● Packager Modal tests with editable inputs › Should show packager modal

    Unable to find role="dialog"

    Ignored nodes: comments, script, style
    <body
      aria-hidden="true"
      class="ReactModal__Body--open"
    >
      <div
        data-react-modal-body-trap=""
        style="position: absolute; opacity: 0;"
        tabindex="0"
      />
      <div />
      <div
        data-react-modal-body-trap=""
        style="position: absolute; opacity: 0;"
        tabindex="0"
      />
      <div
        class="ReactModalPortal"
      >
        <div
          class="ReactModal__Overlay ReactModal__Overlay--after-open"
          style="position: fixed; top: 0px; left: 0px; right: 0px; bottom: 0px; background-color: rgba(255, 255, 255, 0.9);"
        >
          <div
            aria-modal="true"
            class="ReactModal__Content ReactModal__Content--after-open order-details-modal"
            role="dialog"
            style="border: 1px solid #ccc; box-shadow: -2rem 2rem 2rem rgba(0, 0, 0, 0.5); filter: blur(0); font-size: 1.1em; font-weight: bold; margin: 50px auto; opacity: 1; outline: 0; position: relative; visibility: visible; width: 500px;"
            tabindex="-1"
          >
          ...

Upvotes: 8

Views: 10908

Answers (2)

Richard Hpa
Richard Hpa

Reputation: 2857

Though Avi's answer works, it technically isn't the best for testing. I was having the same issue and have realised we also have the same issue with our code so here is what is happening.

The issue is that you are saying Modal.setAppElement('body'); in your test file. If you read the docs for react-modal, they say you need to tell the modal where your apps content is so that when the modal is open, everything within that element will have aria-hidden: true so that screen readers don't pick up any of the other content other than whats inside the modal since that gets rendered via a portal appended to the end of your body. So you setting setAppElement to body is making everything inside that hidden to screen readers, including your modal.

So to solve this you need to set that to something else, I personally have my own render function that has things like providers so I just added a new div in there. But in your case you could add it to your Component

const Component = (props) => <div id="root"><PackagerModal {...mockBaseProps} {...props} /></div>

Then you can chance the line setAppElement line to

Modal.setAppElement('root');

This will mean that you don't have to add the { hidden: true } option to your queries. You also dont have to use findBy since there isn't any async changes happening in your test so far

    test('Should show packager modal', () => {
        render(<Component/>);
        const modalWindow = screen.getByRole('dialog');
        expect(modalWindow).toBeInTheDocument();
    });

Upvotes: 3

Avi
Avi

Reputation: 1407

Looks like the body of your test is set to hidden - aria-hidden="true" (not sure why as I'm not familiar with the package). React Testing Library is heavily accessibility oriented, so by default it ignores any elements that are not accessible.

Try querying your element with the { hidden: true } option

const myModal = getByRole('dialog', { hidden: true });

Upvotes: 4

Related Questions