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

使用Vitest的waitForElementToBeRemoved测试/profile/edit页面时出错

问题:waitForElementToBeRemoved在/profile/edit测试中抛出TypeError错误

我正在为多页面应用编写测试,/profile页面可通过按钮跳转到/profile/edit页面。在/profile页面测试中,使用waitForElementToBeRemoved等待Loading元素消失后验证默认图标,运行正常。但在/profile/edit页面测试中,同样使用该方法等待Loading消失时,Vitest抛出未处理错误:TypeError: Cannot read properties of undefined (reading 'length')。注释等待语句后,前面验证存在2个Loading元素的断言可通过,需排查原因以实现等待Loading消失后测试页面内容的需求。

相关代码片段

/profile页面组件代码

<div>
  <Header />
  <main className={styles.container}>
    {loading ? <Loading /> : (
      <section className={styles.userContainer}>
      <div className={styles.userImageContainer}>
        {userData.image === '' ? <UserCircle size={60} className={styles.userIcon}/> : <img src={ userData.image } alt={ `${userData.name} photo`} className={styles.userImage} />}            
        <NavLink to="/profile/edit"><button type="button" className={styles.editButton}>Profile Edit</button></NavLink>
      </div>
      <h4 className={styles.nameHeader}>Name:</h4>
      <p className={styles.name}>{ userData.name }</p>
      <h4 className={styles.emailHeader}>Email:</h4>
      <p className={styles.email}>{ userData.email }</p>
      <h4 className={styles.descriptionHeader}>Description:</h4>
      <p className={styles.description}>{ userData.description }</p>
      <button className={styles.logoutBtn} onClick={ handleLogoutBtn }>Logout</button>
    </section>
    )}        
  </main>
</div>

/profile页面测试代码

it('should render the default icon', async () => {
renderWithRouter(<App />, { route: '/profile' });

await waitForElementToBeRemoved(() => screen.queryAllByText('Loading...'), { timeout: 3000 });    

const defaultIconEl = screen.getByAltText(/undefined/i);
expect(defaultIconEl).toBeInTheDocument();
});

/profile/edit页面测试代码

it('should render the inputs', async () => {
renderWithRouter(<App />, { route: '/profile/edit' });

expect(screen.getAllByText('Loading...').length).toBe(2);
expect(screen.getAllByText('Loading...')).toBeDefined();
await waitForElementToBeRemoved(() => screen.queryAllByText('Loading...'), { timeout: 3000 });
});

错误信息

Vitest caught 1 unhandled error during the test run. This might cause false positive tests.
Please, resolve all the errors to make sure your tests are not affected.

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Unhandled Error ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Error: Errors occurred while running tests. For more information, see serialized error.
 ❯ Object.runTests node_modules/vitest/dist/chunk-vite-node-externalize.0791f2ed.mjs:7047:17
 ❯ processTicksAndRejections node:internal/process/task_queues:96:5
 ❯ async file:/home/kelder/Documentos/Projetos/myMusic-app/node_modules/vitest/dist/chunk-vite-node-externalize.0791f2ed.mjs:10544:9
 ❯ Vitest.runFiles node_modules/vitest/dist/chunk-vite-node-externalize.0791f2ed.mjs:10557:12
 ❯ Timeout._onTimeout node_modules/vitest/dist/chunk-vite-node-externalize.0791f2ed.mjs:10608:7

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Serialized Error: {
  "errors": [
    [TypeError: Cannot read properties of undefined (reading 'length')],
  ],
}
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯

解决方案

问题根源

waitForElementToBeRemoved要求回调函数的返回值是单个元素、元素数组,或者null/undefined。当所有Loading元素消失后,screen.queryAllByText('Loading...')本应返回空数组[],但在组件更新的某个中间状态,回调可能意外返回undefined,导致内部逻辑读取length时触发报错。

修复方案1:确保回调始终返回数组

修改回调,添加兜底逻辑保证即使查询结果异常也返回数组:

await waitForElementToBeRemoved(() => screen.queryAllByText('Loading...') || [], { timeout: 3000 });

修复方案2:改用单个元素查询(若可区分Loading元素)

如果两个Loading元素有唯一标识(比如不同的data-testid),可以分别等待每个元素移除:

// 假设两个Loading元素的data-testid分别为'page-loading'和'form-loading'
await waitForElementToBeRemoved(() => screen.queryByTestId('page-loading'), { timeout: 3000 });
await waitForElementToBeRemoved(() => screen.queryByTestId('form-loading'), { timeout: 3000 });

修复方案3:用waitFor替代,显式判断元素数量

如果前两种方法无效,改用waitFor直接等待Loading元素数量变为0:

await waitFor(() => {
  expect(screen.queryAllByText('Loading...')).toHaveLength(0);
}, { timeout: 3000 });

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 20:46:04