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

2

8

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"
          >
          ...
Earnestineearnings answered 2/1, 2023 at 6:18 Comment(4)
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). Have you tried querying your element with the { hidden: true } option? const myModal = getByRole('dialog', { hidden: true }). Let me know if that works!Exhilaration
That worked! I will look into the way I am mocking up the react modal and see if I am doing something wrong to cause that. Thank you!Earnestineearnings
Feel free to add that as an answer and I'll accept it.Earnestineearnings
Awesome, glad I managed to help. I posted my comment as an answer :)Exhilaration
E
4

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 });
Exhilaration answered 2/1, 2023 at 22:36 Comment(1)
This really isnt the best solution though, you ideally dont want the modal to be appearing within the element that has the aria-hidden="true". Im running into the same issue where you need to have that aria label set to something else other than the bodyCasseycassi
C
3

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();
    });

Casseycassi answered 26/1, 2023 at 7:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.