会议日期: 2026-01-10 Epic 状态: ✅ Done 参与人员: Root (Project Lead), Bob (Scrum Master), Alice (Product Owner), Charlie (Senior Dev), Dana (QA Engineer), Elena (Junior Dev)
Epic 2 目标: 在 web/tests/e2e/ 的现有残疾人管理测试中使用 Select 工具,验证工具在真实场景中的可用性和稳定性。
交付成果:
会议目的:
Epic 1 回顾中的关键决策在 Epic 2 中得到了验证:
原 Epic 规划顺序:
修正后的顺序:
Alice (Product Owner): "这个决策非常正确。如果我们先扩展更多工具,Select 工具的问题会在 Epic 3/4 中才暴露,那时修复成本会高得多。"
验证结果:
| Story | 验证内容 | 结果 | 发现问题 |
|---|---|---|---|
| 2.1 | 工具包安装和类型安全 | ✅ 通过 | 无 |
| 2.2 | 静态 Select 迁移(5 处) | ✅ 通过 | 无 |
| 2.3 | 异步 Select 迁移(省份/城市) | ✅ 通过 | 移除了 waitForTimeout hack |
| 2.4 | 运行测试并收集问题 | ⚠️ 发现 | DOM 结构假设错误 |
| 2.5 | 验证修复效果 | ✅ 通过 | 无 |
问题详情 (Story 2.4):
// ❌ 原代码 - 等待 listbox 元素
await page.waitForSelector('[role="listbox"]', {
timeout: DEFAULT_TIMEOUTS.static,
state: 'visible'
});
// ✅ 修复 - 直接等待 option 元素
// findTrigger 和 findAndClickOption 中使用 getByRole("option")
Charlie (Senior Dev): "这个 bug 很关键。Epic 1 的单元测试没有发现这个问题,因为单元测试模拟了完整的 DOM 结构。真实环境中的 Radix UI Select v2.2.5 结构与假设不同。"
修复内容:
selectRadixOption: 改用 getByRole("option") 代替 waitForSelector("[role=listbox]")selectRadixOptionAsync: 应用相同修复验证结果 (Story 2.5):
✅ 姓名: 完整测试_1768004957534
✅ 性别: 男
✅ 身份证号: 420101199001011234
✅ 残疾证号: 51100119900104
✅ 残疾类型: 视力残疾
✅ 残疾等级: 一级
✅ 联系电话: 13800138004
✅ 身份证地址: 湖北省武汉市测试街道1号
✅ 省份: 湖北省
✅ 城市: 武汉市
Epic 2 每个故事都经过了代码审查,问题全部修复:
| Story | 审查问题数 | 状态 |
|---|---|---|
| 2.1 | 3 个(文档相关) | ✅ 全部修复 |
| 2.2 | 多个(行号更新、实现调整) | ✅ 全部修复 |
| 2.3 | 9 个(MEDIUM、LOW) | ✅ 全部修复 |
| 2.4 | 初始发现问题 | ✅ 已修复 |
| 2.5 | 1 个(.bak 文件) | ✅ 已修复 |
Dana (QA Engineer): "代码审查继续发挥价值。Story 2.5 中发现的 .bak 文件问题虽然小,但这种细节关注保持了代码库的整洁。"
单元测试 vs E2E 测试:
| 测试类型 | 覆盖场景 | 发现的问题 |
|---|---|---|
| 单元测试 (Epic 1) | 模拟 DOM 结构 | 无 |
| E2E 测试 (Epic 2) | 真实 Radix UI 组件 | listbox DOM 问题 |
Elena (Junior Dev): "Epic 1 的单元测试覆盖率达到了 93.65%,但仍然没有发现这个 DOM 结构问题。真实 E2E 测试是不可替代的。"
[第一部分完 - 会议概览和成功经验]
问题描述:
工具假设 Select 组件的 DOM 结构包含 [role="listbox"] 元素,但 Radix UI Select v2.2.5 的实际结构是:
[role="combobox"]
└── [role="option"] ← 选项直接在 combobox 内部
影响:
waitForSelector('[role="listbox"]') 超时根本原因分析:
Bob (Scrum Master): "为什么单元测试没有发现这个问题?"
Charlie (Senior Dev): "因为单元测试中的 mock DOM 结构是我们假设的完美结构。我们没有使用真实的 Radix UI 组件进行测试。"
教训:
Epic 1 HIGH 优先级行动项跟进情况:
| 行动项 | 状态 | 影响 |
|---|---|---|
| HIGH #1: 配置 ESLint 规则 | ❌ 未完成 | Epic 2 中仍有重复的代码风格问题 |
| HIGH #2: 更新架构文档记录 TS+Playwright 陷阱 | ⏳ 部分完成 | 开发者需要自己摸索 |
| HIGH #3: 创建新的 Epic 2 | ✅ 完成 | Epic 2 成功执行 |
| HIGH #4: 更新 Epic 编号 | ✅ 完成 | sprint-status 已更新 |
Alice (Product Owner): "HIGH #1 和 #2 没有完成。这可能是为什么我们在 Epic 2 中仍遇到一些预期内的问题。"
未完成的行动项影响:
ESLint 规则未配置
架构文档未更新
Story 2.2 → Story 2.3 的迁移策略:
| Story | 范围 | 状态 |
|---|---|---|
| 2.2 | 静态 Select | ✅ 完成,但保留自定义方法 |
| 2.3 | 异步 Select | ✅ 完成,移除自定义方法 |
额外成本:
Charlie (Senior Dev): "渐进式迁移策略保持了测试连续性,但确实增加了协调成本。如果一次性迁移所有 Select,风险会更高。这是一个权衡。"
问题描述:
当前状态:
Dana (QA Engineer): "测试超时不是 Select 工具的问题,但它影响了 Epic 2 的完整验证。我们无法运行完整的测试套件来验证所有字段。"
[第二部分完 - 挑战和问题分析]
| # | 行动项 | 负责人 | 预计时间 | Epic 2 状态 |
|---|---|---|---|---|
| 1 | 配置 ESLint 规则捕获常见问题 | Charlie | 2h | ❌ 未完成 |
| 2 | 更新架构文档记录 TS+Playwright 陷阱 | Charlie | 1h | ⏳ 部分完成 |
| 3 | 创建新的 Epic 2 | Bob | 1h | ✅ 已完成 |
| 4 | 更新 Epic 编号(2→3, 3→4, 4→5) | Bob | 0.5h | ✅ 已完成 |
原计划 (Epic 1 回顾):
// .eslintrc.js
{
rules: {
'no-constant-binary-expression': 'error',
'no-unused-vars': 'error',
'no-empty': ['error', { allowEmptyCatch: false }],
'prefer-const': 'error'
}
}
Epic 2 中的影响:
建议: 在 Epic 3 开始前完成此行动项
Epic 2 中的证据:
Charlie (Senior Dev): "我在故事中引用了这些经验,但没有正式更新到架构文档中。这是我的疏忽。"
Epic 2 成功创建并执行:
web/tests/e2e/ 测试基础设施Alice (Product Owner): "Epic 规划调整非常成功。'先验证再扩展'的策略得到了验证。"
| # | 行动项 | 负责人 | Epic 2 状态 |
|---|---|---|---|
| 5 | 创建开发者自查清单 | Elena | ❌ 未完成 |
| 6 | 更新代码审查检查清单 | Charlie | ⏳ 部分完成 |
负责人: Charlie 预计时间: 2 小时 截止日期: Epic 3 开始前
需要配置的规则:
// .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 小时 截止日期: Epic 3 开始前
文档位置: _bmad-output/planning-artifacts/architecture.md
需要添加的内容:
负责人: Dana (QA Engineer) 预计时间: 2 小时 截止日期: Epic 3 开始前
问题: 当前单元测试使用模拟 DOM,无法发现真实 DOM 结构问题
建议解决方案:
Elena (Junior Dev): "如果我们使用真实组件进行单元测试,Epic 2 的 DOM 问题就能在 Epic 1 中发现。"
负责人: Elena 预计时间: 1 小时
清单内容:
@internal 标记E2ETestError 而非原生 Error:text-is() 而非 :has-text()BaseOptionsDEFAULT_TIMEOUTS 常量负责人: Dana (QA Engineer) 预计时间: 1-2 小时
问题描述:
可能的解决方案:
当前计划: 在 Epic 4(文件上传测试)中处理
负责人: Charlie 预计时间: 0.5 小时
添加检查点:
原计划:
实际情况:
决策: 跳过 Story 2.6,Epic 2 标记为完成
理由:
Alice (Product Owner): "我同意跳过 Story 2.6。Select 工具已经验证充分,继续扩展工具集的价值更高。"
问题: 单元测试使用模拟 DOM,无法发现真实 DOM 结构问题
可选方案:
| 方案 | 优点 | 缺点 |
|---|---|---|
| A. 继续使用模拟 DOM | 快速、独立 | 无法发现 DOM 问题 |
| B. 使用真实组件 | 发现真实问题 | 依赖外部组件 |
| C. 添加集成测试 | 两全其美 | 增加测试层级 |
决策: 采用方案 C - 添加集成测试级别
Charlie (Senior Dev): "单元测试应该保持快速和独立。我们添加一个集成测试级别,使用真实组件验证工具。"
未完成的 HIGH 行动项:
决策: Epic 3 暂缓,先完成这些行动项
理由:
Bob (Scrum Master): "我们花 2-3 小时完成这些改进,Epic 3 会更顺利。这是值得的前期投入。"
Epic 2 状态: ✅ Done
关键成果:
关键经验:
下一步:
Bob (Scrum Master): "Epic 2 是一个成功的验证 Epic。我们发现并修复了关键问题,验证了架构决策的正确性。更重要的是,我们学到了真实 E2E 测试的价值,这将指导后续的开发。"
[文档完]