会议日期: 2026-01-09 Epic 状态: ✅ Done 参与人员: Root (Project Lead), Bob (Scrum Master), Alice (Product Owner), Charlie (Senior Dev), Dana (QA Engineer), Elena (Junior Dev)
Epic 1 目标: 测试开发者可以安装 @d8d/e2e-test-utils 包,立即使用 Select 工具测试 Radix UI Select 组件。
交付成果:
会议目的:
代码审查在 Epic 1 中发挥了关键作用:
| 故事 | 审查问题 | HIGH | MEDIUM | LOW | 状态 |
|---|---|---|---|---|---|
| 1.1 | 9 | 3 | 3 | 2 | 全部修复 |
| 1.2 | 4 | 1 | 2 | 1 | 全部修复 |
| 1.3 | 8 | 2 | 6 | 0 | 全部修复 |
| 1.4 | 7 | 2 | 5 | 0 | 全部修复 |
| 1.5 | 6 | 0 | 4 | 2 | 全部修复 |
| 1.6 | 5 | 0 | 4 | 1 | 全部修复 |
| 总计 | 39 | 8 | 24 | 7 | 100% 修复 |
关键发现:
Charlie (Senior Dev): "Story 1.3 的审查发现的问题尤其关键。如果我们没有发现 :has-text() 会部分匹配的问题,测试可能会在错误选项上通过,导致严重的假阳性 bug。"
从前一个故事的经验传递到下一个故事:
| 经验来源 | 应用故事 | 内容 |
|---|---|---|
| Story 1.3 审查 | Story 1.4 | DOM 类型问题处理、精确文本匹配 |
| Story 1.4 实现 | Story 1.6 | 单元测试 Mock 策略、重试机制测试 |
| Story 1.6 测试 | Bug 修复 | 发现并修复网络空闲等待超时配置 bug |
Elena (Junior Dev): "Charlie 在 Story 1.3 的审查后让我把每个内部函数都加上完整的 JSDoc。虽然一开始觉得麻烦,但后来写 Story 1.4 的测试时发现这些文档真的很有帮助。"
测试覆盖率从 Story 1.1 的基础占位测试提升到 Story 1.6 的优秀覆盖率:
Dana (QA Engineer): "Story 1.6 更有意思——我们在写单元测试的过程中发现了一个实际的 bug:selectRadixOptionAsync 的网络空闲等待超时配置不对。这证明代码审查和测试是互补的。"
Epic 1 建立的类型系统为后续开发奠定了基础:
BaseOptions - 所有配置对象的基类ErrorContext - 结构化错误信息E2ETestError - 统一的错误处理AsyncSelectOptions, FileUploadOptions, FormStepOptions, DialogOptions - 特定选项类型Charlie (Senior Dev): "我们在 Epic 1 建立的类型系统是一个很大的胜利。BaseOptions 作为所有配置对象的基类、E2ETestError 提供结构化错误信息,这些设计模式在后续故事中持续复用,减少了重复代码。"
[第一部分完 - 会议概览和成功经验]
代码审查发现的 39 个问题中,有相当一部分是重复出现的:
| 问题类型 | 出现次数 | 可预防? | 预防方法 |
|---|---|---|---|
| 冗余 null 检查 | 5+ | ✅ 是 | ESLint 规则 |
| 缺少 JSDoc | 8+ | ✅ 是 | ESLint 规则 |
| 空 catch 块 | 4+ | ✅ 是 | ESLint 规则 |
| DOM 类型问题 | 1 | ✅ 是 | 架构文档 |
| 文本选择器精确性 | 1 | ✅ 是 | 编码规范 |
Dana (QA Engineer): "这 18+ 个重复问题占总数的近一半。如果能通过工具或文档预防,就能节省约 8 小时的修复时间。"
成本分析:
Elena (Junior Dev): "Story 1.3 的 DOM 类型问题真的让我很困惑。page.evaluate() 在 TypeScript 中总是报错,我花了很长时间才找到 page.locator().allTextContents() 这个解决方案。"
问题详情:
page.evaluate() 获取文本内容会触发 TypeScript DOM 类型错误解决方案:
// ❌ 不推荐 - TypeScript 类型问题
const text = await page.evaluate(el => el.textContent, element);
// ✅ 推荐 - 使用 Playwright API
const text = await element.textContent();
// 或
const texts = await page.locator(selector).allTextContents();
Charlie (Senior Dev): "我应该在架构文档中注明这一点,而不是让你自己去摸索。"
问题: 使用 :has-text() 选择器会进行部分匹配,可能导致误选错误选项。
Story 1.3 发现的案例:
解决方案:
// ❌ 部分匹配 - 可能误选
page.locator(`.option:has-text("广东省")`)
// ✅ 精确匹配 - 只匹配完全相等的文本
page.locator(`.option:text-is("广东省")`)
影响: 如果没有在代码审查中发现,测试可能会在错误选项上通过,导致假阳性 bug。
Story 1.6 单元测试中发现:
// ❌ Bug - 网络空闲等待使用了默认的 networkIdle 超时
await page.waitForLoadState('networkidle', { timeout: DEFAULT_TIMEOUTS.networkIdle });
// ✅ 修复 - 使用用户自定义的 timeout
await page.waitForLoadState('networkidle', { timeout: options.timeout ?? DEFAULT_TIMEOUTS.async });
Dana (QA Engineer): "这证明代码审查和测试是互补的。审查可以发现问题,但单元测试能在实际执行中暴露隐藏的 bug。"
问题 1: Epic 顺序错误
原规划顺序:
Root (Project Lead): "为什么先扩展更多工具,而不是先验证现有工具?如果我们现在构建的工具不能用,Epic 2 会浪费大量时间。"
正确顺序:
问题 2: 重复建设而非复用
原 Epic 3 Story 3.1 计划创建 tests/test-app/ 独立测试应用,而不是使用现有的 web/tests/e2e/。
Root (Project Lead): "现有的 web 目录已经有 E2E 测试了。为什么要另外创建一个 test-app?直接在现有测试中使用工具包更高效。"
Alice (Product Owner): "这个修正更符合敏捷原则——利用现有资源,而不是重复建设。"
Charlie (Senior Dev): "而且从测试角度看,在真实的残疾人管理测试中使用 Select 工具更能发现实际问题。"
[第二部分完 - 挑战和问题分析]
负责人: Charlie 预计时间: 2 小时
需要配置的规则:
// .eslintrc.js
{
rules: {
// 捕获冗余的 null 检查
'no-constant-binary-expression': 'error',
// 捕获未使用的变量
'no-unused-vars': 'error',
// 捕获空 catch 块
'no-empty': ['error', { allowEmptyCatch: false }],
// 首选 const
'prefer-const': 'error'
}
}
预期收益: 自动捕获约 50% 的重复审查问题
负责人: Charlie 预计时间: 1 小时
文档位置: _bmad-output/planning-artifacts/architecture.md
需要添加的内容:
负责人: Bob (Scrum Master) 预计时间: 1 小时
Epic 2: 在现有 E2E 测试中验证 Select 工具
包含的故事:
| 故事 | 内容 | 预计时间 |
|------|------|----------|
| 2.1 | 在 web 目录安装 @d8d/e2e-test-utils | 0.5h |
| 2.2 | 使用 selectRadixOption 重写残疾类型选择测试 | 1h |
| 2.3 | 使用 selectRadixOptionAsync 重写省份/城市选择测试 | 1.5h |
| 2.4 | 运行测试并收集问题和改进建议 | 1h |
| 2.5 | 修复发现的问题(如有) | 取决于发现 |
| 2.6 | 稳定性验证(连续 10 次,100% 通过) | 0.5h |
预计总工作量: 4-6 小时 + 修复时间
负责人: Bob (Scrum Master) 预计时间: 0.5 小时
变更:
负责人: Elena 预计时间: 1 小时
清单内容:
@internal 标记E2ETestError 而非原生 Error:text-is() 而非 :has-text()BaseOptionsDEFAULT_TIMEOUTS 常量负责人: Charlie 预计时间: 0.5 小时
添加检查点:
负责人: Bob (Scrum Master) 预计时间: 0.5 小时
添加指导原则:
前置条件: 行动项 1-3 必须在启动新 Epic 2 之前完成
| Epic | 内容 | 状态 |
|---|---|---|
| Epic 1 | Select 工具基础框架 | ✅ Done |
| Epic 2 (新) | 在现有 E2E 测试中验证 Select 工具 | 🆕 To Be Created |
| Epic 3 | 扩展工具集(文件上传、表单、列表、对话框) | ⏸️ Pending |
| Epic 4 | 全面验证工具包 | ⏸️ Pending |
| Epic 5 | 完善文档与开发者体验 | ⏸️ Pending |
Epic 2: 在现有 E2E 测试中验证 Select 工具
目标:
web/tests/e2e/ 的现有残疾人管理测试中使用 Select 工具范围:
web/tests/e2e/ 测试基础设施验收标准:
原顺序: 构建 → 扩展 → 验证 新顺序: 构建 → **验证 → 扩展 → 全面验证
理由:
原计划: 创建 tests/test-app/ 独立测试应用
新计划: 使用现有 web/tests/e2e/ 测试
理由:
计划: 在启动新 Epic 2 之前完成技术改进(ESLint 配置、文档更新)
理由:
Epic 1 状态: ✅ Done
关键成果:
关键经验:
下一步:
Bob (Scrum Master): "这是一次非常成功的回顾会议。Root 的洞察帮助我们发现了 Epic 规划中的关键问题,避免在错误的方向上投入更多时间。感谢所有人的坦诚分享。"
[文档完]