9.0 KiB
CLAUDE.md
SwitchBot App 的 AI 驱动 UI 自动化测试项目。本文件是项目常驻规则,进项目即生效。这些规则优先于默认行为,必须严格遵守。
项目概览
- 目标:把 SwitchBot App 的「必测项」转成自动化用例,双端(Android + iOS)执行,并回写结果到 ONES 测试计划。
- 技术栈:TypeScript + Midscene.js(
@midscene/*)+ vitest;Android 走 Appium/UiAutomator2,iOS 走 WDA(XCUITest)。 - App bundle:iOS
com.wohand.wohand(即 SwitchBot iOS App)。 - 被测设备:SwitchBot 全品类 IoT(Bot / Plug / Camera / Lock / 灯类 / Hub / 温湿度计 / 扫地机 / 窗帘等)。
- 物理配对靠 64 路继电器板(
/dev/cu.usbserial-A50285BI@115200,utils/common/serial_controller.ts)按对应通道触发设备进入配对。
目录结构(职责分层,严格遵守)
| 目录 | 职责 |
|---|---|
tests/<模块>/*.test.ts |
用例脚本。薄脚本:describe/it + 调 helper + 断言 + ONES 锚点(onesAdd/onesPlatform)。不放通用流程。 |
utils/common/*.helper.ts |
通用可复用流程(如 device-add / auth / room / scene / hub / curtain)。不含 vitest/断言。 |
utils/ |
跨层工具:ones-sync.ts(ONES 回写)、wda-helper.ts、test-reporter.ts、agent.ts。 |
drivers/ |
平台驱动:android-driver.ts / wda-driver.ts / factory.ts / types.ts。 |
config/ |
配置:account/app/device/relay/wifi.config.ts。 |
locators/ |
各品类元素定位。 |
scripts/ |
生成器 / soak / relay / ONES 同步等工具脚本(非用例)。临时探索脚本(_*.ts)用完即删。 |
docs/ |
交付报告(P0 回归报告等)。 |
test-plan/ |
ONES 映射与回写参数(must-test.manifest.ts、ones-writeback-params.json)。 |
铁律:用例 ≠ helper ≠ 工具脚本。 通用动作沉到 utils/common/,用例只在 tests/ 下组装调用 + 加 ONES 锚点。
工作方式规则(最重要)
-
始终用中文回复:本项目所有会话(含后续新会话)一律用中文回复。
-
运行前先确认:跑脚本 / 测试 / 设备自动化 / ONES 反写前,先列出待执行命令、输入文件和目标,等用户确认再执行。即使是 dry-run,只要会启动脚本或消耗时间也要先说明。
-
提交/推送只按指令:
- 不要每轮结束提醒提交;提交可攒几天一起,只在用户说提交时才 commit。
git push仅在用户明确说"推送/push"时执行,否则攒着等指令。- 该仓库 git 用
/usr/bin/git(Homebrew git 已坏),远程是 gitea。
-
双端同步:之后的优化/修改需同时覆盖 Android 和 iOS。改共享 helper 时确认两个 platform 分支都覆盖;改单端专属逻辑时评估另一端是否要等价改动。
-
别改已调通的脚本:用例失败时先分清是环境问题还是脚本 bug。环境问题(设备离线 / 首页设备过多 / 网络异常 / 继电器没触发)改环境,别改脚本。只有确证是脚本 bug 或确需优化才动代码。
-
调试工作流(详见 skill
debugging-ui-tests):- 不反复重启 App:App 已在目标页就直接在当前页操作,复用状态。
- 不反复整跑用例:只调失败的那一步,整跑确认留到全部修完。
- 摸 UI 用截图:文本 dump 读不到的元素(自绘滚轮、图标、浮层)用截图看最有效。
- 不为找元素反复切换系统开关(如关 WiFi→又开),既违反"不反复切换"又复现不稳定。
-
查找不盲滚:查设备/文案/卡片用「找到即停 + 到底即停」循环,不要盲滚固定 N 次。命中判据用可靠信号(iOS
visible=true且在 header 以下)。唯一例外:查添加入口品类(selectDeviceCategory)可用快速预滑提速。 -
自动导航重试 5 次:先自己尝试自动点击导航(最多 5 次),超过 5 次没进目标页才打印当前页面信息并请用户手动进入。
-
过夜/无人值守走 nohup:soak/回归用
scripts/overnight-soak*.sh/run-*.sh独立进程,整夜不经过 Claude、不需授权。soak 主链路是纯确定性自动化(无aiAct),几乎不烧 token;token 烧在"调脚本"阶段。别让 Claude 常驻盯跑。
添加用例(add)注意
- 首页校验关键字必须用全名:
deviceKeyword(首页source.includes)和namePattern(skip 检查)不能用共享前缀,否则串台误判通过(如'Keypad'会匹配到已加的 Keypad Vision Pro → 报"成功"但实际没加上)。同前缀型号(Keypad / Plug Mini / Floor Lamp / Strip Light / Hub)尤其注意。skipScanStep:true时更致命。- 症状:报告"添加成功"但首页实际没这设备 → 八成 deviceKeyword 太宽。
- 选品类大小写不敏感:入口名照 App 实际文案填即可(
selectDeviceCategory已(?i)全字匹配,不退化成 contains)。多级品类逐级选(如 "Relay Switch" → 二级 "Relay Switch 1",在beforePairing钩子补点)。 - 通用添加流程在
utils/common/device-add.helper.ts(addDeviceWithSerialPairing/addDeviceViaBLE);注册表分平台(added-devices.ios.json)。
ONES(事实源 + 结果回写)
- 事实源:ONES 团队
98Q19ZsW(sz.ones.cn)。测试计划必测项-AI自动化plan uuidCQz9YCNX;用例库App 必测项lib uuidEPfZfC9Y(97 条)。ones二进制在/Users/woan/local/bin/ones。用 skillonescli。ones testcase case list不返回 steps,要ones testcase case search --key <number>。
- 结构:添加 = 每设备一条(~73 条);控制 = 2 条超级用例按协议分组,每 step = 一个单品核心控制:
- 蓝牙控制 ONES 15975(uuid Lqpkx6mp,前置关 WiFi/开蓝牙)
- WiFi 控制 ONES 15974(uuid Vp7vuhbu,前置开 WiFi/关蓝牙)
- 结果回写必须走
ones graphqlmutation,不要用 curl REST(token 被打码,REST 必 401 且已废弃):- case:
updateTestcasePlanCase(key: "testcase_plan_case-<plan>-<caseUUID>", result: "passed") - step:
updateTestcasePlanCaseStep(key: "testcase_plan_case_step-<plan>-<caseUUID>-<stepUuid>", step_result: "passed", actual_result: "...") - 取值
passed|failed|skipped|to_do;step 结果字段名是step_result。 - GraphQL 不支持单请求多 mutation,按 caseUUID 聚合后连续多次写。
- 代码:
utils/ones-sync.ts+scripts/sync-ones-results.ts;回写参数test-plan/ones-writeback-params.json(npm run gen:writeback-params)。plan UUID 是回写唯一变量。
- case:
P0 报告计数口径(硬规则)
- 只取最后一轮回归日志。
- 添加以最后结果为准:重试后最终 PASS 即记通过(标注"第 N 次成功"),全程未 PASS 才记真失败,重试成功不计失败。
- 控制/其他 BLE 与 WiFi 各算一条、不去重。
- 成功率 = 有效通过率(分母排除 SKIP);每平台各自一行,不算两端合计。
- 脚本
scripts/gen-p0-report.py;报告放docs/测试报告_P0回归_YYYY-MM-DD.md。 - ⚠️ SOAK 跑期间不要并发跑其它 vitest(共享
reports/.results.json会污染合并 HTML)。 - ⚠️ SKIP 退出码 0 会被 soak 统计成 PASS → 可靠性结论必须剔除 SKIP(见继电器按压计数交叉验证)。
iOS 特有(vs Android)
- 快速连接:①
idevicepair pair(手机点信任)②ideviceinfo取 UDID / iOS 版本 ③pkill -f iproxy(清 8100 占用,关键) ④ Appium XCUITest 连接,wdaLocalPort=8200,platform_version与设备一致,bundle_id=com.wohand.wohand。换新机首次须在 Xcode 给 WebDriverAgentRunner 配 Team 签名并 Run 一次(否则 xcodebuild code 65)。完整指南见iOS连接修复指南.md。 - 默认沿用 Android 配置(WiFi / 账号);确需分开再设平台专用 env。
- 推进按钮叫 "Connect device"(不是 Next);
tapNext已依次试 Next/Connect device/Continue/Done/Retry。 - 滚动用快速甩动:
wda-helper.scrollDown慢速 swipe(0.5s)不滚(RN 首页当拖拽回弹)→ 必须duration=0.1。这是很多 iOS "未找到设备"误判的根子。 - 有输入框的页,输完最后一个文本必须先收键盘再点下一步(点键盘 Return/Done);改名收键盘绝不 goBack(会退页丢命名页)。
- iOS 元素法填 SSID 前必须 clearText(typeText 是追加不替换 → 变乱码连不上)。
- Android locator(
-android uiautomator)在WDADriver.findElementRaw会自动转 iOS,写死的 Android locator 透明生效。 - iOS 找列表元素/文案优先用 WDA
scrollElement(scroll-to-visible),避免坐标 swipe 误点。 - 提速:
utils/wda-helper.tsapplySpeedSettings()关waitForIdleTimeout等(全局)。
相关 skill
debugging-ui-tests— 调试 UI 用例前加载(规则总集 / iOS 坑位 / 查找不空滚 / 添加校验陷阱等)。onescli— 与 ONES 平台交互。
常用命令
npm test # vitest run 全量
vitest run tests/<模块> # 跑单模块
npm run gen:must-test # 生成必测项 manifest
npm run gen:writeback-params# 生成 ONES 回写参数
python3 scripts/gen-p0-report.py # 生成 P0 报告