You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用React Testing Library的findByRole检测react-modal存在报错

React Testing Library 无法检测到 react-modal 的 dialog 角色

问题描述

尝试使用 React Testing Library 的 findByRole 方法检测 react-modal 的存在时,持续收到错误提示:Unable to find role="dialog",但测试控制台中清晰可见带有该角色的元素。

测试代码

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: {
        // 无关属性省略
    },
    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();
    });
});

模态框组件代码

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;

测试控制台输出

● 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"
          >
          ...

问题原因

从控制台输出可见,modal 打开时,body 被添加了 aria-hidden="true" 属性,而 react-modal 的内容挂载在 body 下的 ReactModalPortal 中。React Testing Library 默认会忽略带有 aria-hidden="true" 的元素及其子元素,因此即使 modal 本身带有 role="dialog" 和 aria-modal="true",也会被过滤,导致查询失败。

解决方案

方案1:修改 react-modal 的 appElement 配置

不要将 body 设为 appElement,改用页面的根容器(如 #root)。这样 react-modal 只会将根容器标记为 aria-hidden,而 modal 作为 body 的直接子元素,不会被包含在 aria-hidden 的容器内:

// 测试代码中修改
import Modal from 'react-modal';
Modal.setAppElement('#root'); // 替换为你的根容器选择器

或在组件中配置:

// PackagerModal.js
import ReactModal from 'react-modal';
ReactModal.setAppElement('#root');

方案2:查询时允许查找隐藏元素

在 findByRole 中添加 { hidden: true } 选项,让 RTL 不忽略 aria-hidden 容器内的元素:

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

方案3:通过模态框内的可见内容查询

直接查询模态框内的可见文本(如标题),间接验证模态框存在,这更符合 RTL「测试用户实际可见行为」的设计理念:

test('Should show packager modal', async () => {
    render(<Component />);
    expect(await screen.findByText('Packager Order Checklist')).toBeInTheDocument();
});

内容的提问来源于stack exchange,提问作者dmikester1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 08:30:52