e2e-test-utils-issues-2026-01-09.md 6.8 KB

E2E 测试工具包 - 问题与改进建议

生成日期: 2026-01-09 测试文件: web/tests/e2e/specs/admin/disability-person-complete.spec.ts 工具版本: @d8d/e2e-test-utils (from Story 2.1-2.3)

执行摘要

  • 测试总数: 6
  • 通过: 0
  • 失败: 1(首个测试失败,其余未运行)
  • 跳过: 5
  • 执行时间: ~10秒
  • 失败原因: selectRadixOption 工具无法找到 [role="listbox"] 元素

问题清单

1. [Bug] selectRadixOption 工具无法找到 listbox 元素

类别: 工具 bug 严重程度: - 阻塞所有使用静态 Select 的测试 状态: 待修复

问题描述:

selectRadixOption 工具在点击 Radix UI Select 触发器后,等待 [role="listbox"] 元素出现,但该元素从未出现,导致测试超时失败。

复现步骤:

  1. 运行测试: pnpm test:e2e:chromium disability-person-complete
  2. 测试尝试使用 selectRadixOption(page, "残疾类型 *", "视力残疾") 选择残疾类型
  3. 触发器被成功找到并点击
  4. 工具等待 [role="listbox"] 出现(超时 2000ms)
  5. 超时失败

错误信息:

TimeoutError: page.waitForSelector: Timeout 2000ms exceeded.
Call log:
  - waiting for locator('[role=listbox]') to be visible

    at ../../../../packages/e2e-test-utils/src/radix-select.ts:37

预期行为: 点击触发器后,Radix UI Select 应该渲染带有 [role="listbox"] 属性的内容容器。

实际行为: 从 Playwright 错误上下文快照可以看到,点击后选项出现在 [role="combobox"] 内部,而非独立的 [role="listbox"]:

- combobox "残疾类型 *" [active] [ref=e26]:
  - option "请选择残疾类型" [selected]
  - option "视力残疾"
  - option "听力残疾"
  - option "言语残疾"
  - option "肢体残疾"
  - option "智力残疾"
  - option "精神残疾"
  - option "多重残疾"

根本原因分析:

有两种可能:

  1. Radix UI Select v2.2.5 渲染方式不同: 该版本的 Radix UI Select 可能不使用 [role="listbox"] 角色,或者将其应用在不同的元素上。

  2. 工具假设错误: 工具期望的 DOM 结构与实际 Radix UI Select 的实现不匹配。

建议解决方案:

  1. 验证 Radix UI Select 的实际角色:

    # 在浏览器中手动测试,检查打开 Select 时的 DOM 结构
    # 或者使用 Playwright 的 page.content() 查看完整 HTML
    
  2. 修复工具以支持实际的 DOM 结构:

    • 移除对 [role="listbox"] 的依赖,或改为可选
    • 直接等待 [role="option"] 元素出现
    • 或者使用更灵活的选择器
  3. 修复建议代码:

    // packages/e2e-test-utils/src/radix-select.ts
    export async function selectRadixOption(page: Page, label: string, value: string): Promise<void> {
     console.debug(`[selectRadixOption] 开始选择: label="${label}", value="${value}"`);
     const trigger = await findTrigger(page, label, value);
     console.debug(`[selectRadixOption] 找到触发器,准备点击`);
     await trigger.click();
    
     // 修复: 不等待 listbox,直接等待选项出现
     console.debug(`[selectRadixOption] 已点击触发器,等待选项`);
     await page.waitForSelector("[role=option]", { timeout: DEFAULT_TIMEOUTS.static, state: "visible" });
     console.debug(`[selectRadixOption] 选项已出现`);
    
     const availableOptions = await page.locator("[role=option]").allTextContents();
     console.debug(`[selectRadixOption] 可用选项:`, availableOptions);
     await findAndClickOption(page, value, availableOptions);
     console.debug(`[selectRadixOption] 选择完成`);
    }
    

相关文件:

  • packages/e2e-test-utils/src/radix-select.ts:37
  • packages/e2e-test-utils/src/radix-select.ts:230 (async version)

2. [Bug] selectRadixOptionAsync 存在相同的 listbox 依赖问题

类别: 工具 bug 严重程度: - 阻塞所有使用异步 Select 的测试 状态: 待验证(未运行到相关代码)

问题描述:

selectRadixOptionAsync 工具在第 230 行也使用 page.waitForSelector('[role="listbox"]'),预计会遇到与静态 Select 相同的问题。

相关代码:

// packages/e2e-test-utils/src/radix-select.ts:230
await page.waitForSelector('[role="listbox"]', {
  timeout: DEFAULT_TIMEOUTS.static,
  state: 'visible'
});

建议解决方案: 应用与问题 #1 相同的修复方案。


改进建议汇总

API 设计建议

  1. 简化选择器策略:

    • 当前的 findTrigger 函数有 4 种策略,过于复杂
    • 建议: 简化为 2-3 种最常用的策略
  2. 更好的错误消息:

    • 当前错误消息可以更清晰地指出问题
    • 建议: 在错误消息中显示实际的 DOM 结构片段

文档改进建议

  1. 更新 README 中的 Radix UI 版本说明:

    • 当前文档未明确说明支持的 Radix UI Select 版本
    • 建议添加版本兼容性说明
  2. 添加常见问题排查指南:

    • 当 listbox 未找到时的排查步骤
    • 如何使用 browser devtools 检查 DOM 结构

性能优化建议

  1. 减少硬编码超时:

    • 当前 DEFAULT_TIMEOUTS.static = 2000 可能不够
    • 建议使用动态等待或配置化超时时间
  2. 并行等待优化:

    • 可以同时等待多个选择器,第一个成功即可

测试环境信息

  • Node.js: 20.19.2
  • Playwright: 1.55.0
  • @radix-ui/react-select: ^2.2.5
  • @d8d/e2e-test-utils: 版本待确认

下一步行动

  1. 高优先级: 修复问题 #1(listbox 依赖),使测试能够运行
  2. 高优先级: 应用相同修复到问题 #2(async version)
  3. 中优先级: 验证修复后所有 6 个测试是否通过
  4. 中优先级: 收集使用体验反馈(API 易用性、错误消息等)
  5. 低优先级: 根据反馈更新文档和实现改进建议

附录:完整测试输出

Running 6 tests using 1 worker

========== 开始完整功能测试 ==========

[步骤1] 打开新增残疾人对话框...
✓ 对话框已打开

[步骤2] 填写基本信息...
[selectRadixOption] 开始选择: label="残疾类型 *", value="视力残疾"
选择器策略1失败: [data-testid="残疾类型 *-trigger"]
选择器策略2失败: [aria-label="残疾类型 *"][role="combobox"]
选择器策略3: 尝试 getByRole(combobox, { name: "残疾类型 *" })
选择器策略3成功: 找到 combobox "残疾类型 *"
[selectRadixOption] 找到触发器,准备点击
[selectRadixOption] 已点击触发器,等待 listbox

✘  1 [chromium] › 残疾人管理 - 完整功能测试 › 完整流程:新增残疾人

    TimeoutError: page.waitForSelector: Timeout 2000ms exceeded.
    Call log:
      - waiting for locator('[role=listbox]') to be visible

  1 failed
  5 did not run