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

