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
相关产品推荐
相关产品推荐

