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

Jest测试React组件报focus-trap无可聚焦节点错误解决方案

Jest测试AriaModal组件抛出focus-trap节点缺失报错修复

问题现象

使用Jest编写React组件单元测试时,运行测试抛出如下错误:

Your focus-trap must have at least one container with at least one tabbable node in it at all times

尝试在测试代码中添加如下聚焦断言排查问题,未解决报错:

await waitFor(() => expect(screen.getByTestId('continue')).toHaveFocus());

被测组件基于AriaModal实现评论提交成功提示弹窗,配置initialFocus指向id为continue的按钮元素,组件代码如下:

function SuccessCommentAddPopup({onSuccessComment}: SuccessCommentAddPopupProps): JSX.Element { 
  return (
    <AriaModal
      titleText='add comment success'
      initialFocus="#continue"
    >
      <div style={{position: 'relative', width: '550px', height: '410px', marginBottom: '50px'}}>
        <div className={style.modal}>
          <div className={style.modalWrapper}>
            <div className={style.modalOverlay} data-close-modal></div>
            <div className={style.modalContent}>
              <svg className={style.modalIcon} width="26" height="20" aria-hidden="true">
                <use xlinkHref="#icon-success"></use>
              </svg>
              <p className={style.modalMessage}>Thank's a lot!</p>
              <div className={style.modalButtonContainer}>
                <button
                  id='continue'
                  data-testid='continue'
                  className={style.modalButton}
                  onClick={()=>{
                    onSuccessComment(false);
                  }}
                >
                Close
                </button>
              </div>
              <button
                className={style.modalCloseBtn}
                type="button"
                aria-label="Close"
                onClick={()=>{
                  onSuccessComment(false);
                }}
              >
                <span className={style.buttonCrossIcon}>
                </span><span className={style.modalCloseBtninteractiveArea}></span>
              </button>
            </div>
          </div>
        </div>
      </div>
    </AriaModal>
  );
}

export default SuccessCommentAddPopup;

对应测试代码如下:

const history = createMemoryHistory();
history.push('/guitars/1');


describe('Component: SuccessCommentAddPopup', () => {
  it('Компонент отрисовывается корректно', async () => {
    render(
      <HistoryRouter history={history}>
        <SuccessCommentAddPopup
          onSuccessComment={jest.fn()}
        />
      </HistoryRouter>,
    );
    await waitFor(() => expect(screen.getByTestId('continue')).toHaveFocus());
    expect(screen.getByText('Спасибо за ваш отзыв!')).toBeInTheDocument();
  });
});

根因分析

  • 测试使用的jsdom环境默认不会计算元素的实际布局属性,focus-trap库初始化时会通过offsetParent等属性判定元素是否可见、可聚焦,默认配置下所有元素都会被判定为不可聚焦,直接触发校验报错。
  • 你添加的waitFor断言没有生效,是因为focus-trap的校验逻辑在组件挂载的同步阶段就会执行,错误会在waitFor启动轮询之前直接抛出,断言逻辑根本没有执行机会。
  • 额外问题:测试中断言的文本Спасибо за ваш отзыв!和组件实际渲染的Thank's a lot!不一致,就算焦点问题修复,该断言也会执行失败。

修复方案

  • 方案1:在测试全局配置文件中补充jsdom缺失的属性桩,让focus-trap可以正常识别可聚焦元素:
// 测试setup文件(一般是jest.setup.js)中添加
window.HTMLElement.prototype.focus = jest.fn();
Object.defineProperty(window.HTMLElement.prototype, 'offsetParent', {
  get() { return this.parentNode; }
});
  • 方案2:测试环境下关闭AriaModal的焦点陷阱逻辑,跳过非必要的焦点校验:
    修改组件代码,测试环境下给AriaModal传入focusTrap={false}属性:
<AriaModal
  titleText='add comment success'
  initialFocus="#continue"
  focusTrap={process.env.NODE_ENV === 'test' ? false : undefined}
>
  • 方案3:修正测试中的文本断言,将断言文本和组件实际渲染内容对齐,避免额外的断言失败。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 07:54:23