AI_UIAutomation/CLAUDE.md

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.tstest-reporter.tsagent.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.tsones-writeback-params.json)。

铁律:用例 ≠ helper ≠ 工具脚本。 通用动作沉到 utils/common/,用例只在 tests/ 下组装调用 + 加 ONES 锚点。

工作方式规则(最重要)

  1. 始终用中文回复:本项目所有会话(含后续新会话)一律用中文回复。

  2. 运行前先确认:跑脚本 / 测试 / 设备自动化 / ONES 反写前,先列出待执行命令、输入文件和目标,等用户确认再执行。即使是 dry-run,只要会启动脚本或消耗时间也要先说明。

  3. 提交/推送只按指令:

    • 不要每轮结束提醒提交;提交可攒几天一起,只在用户说提交时才 commit
    • git push 仅在用户明确说"推送/push"时执行,否则攒着等指令。
    • 该仓库 git 用 /usr/bin/git(Homebrew git 已坏),远程是 gitea。
  4. 双端同步:之后的优化/修改需同时覆盖 Android 和 iOS。改共享 helper 时确认两个 platform 分支都覆盖;改单端专属逻辑时评估另一端是否要等价改动。

  5. 别改已调通的脚本:用例失败时先分清是环境问题还是脚本 bug。环境问题(设备离线 / 首页设备过多 / 网络异常 / 继电器没触发)改环境,别改脚本。只有确证是脚本 bug 或确需优化才动代码。

  6. 调试工作流(详见 skill debugging-ui-tests):

    • 不反复重启 App:App 已在目标页就直接在当前页操作,复用状态。
    • 不反复整跑用例:只调失败的那一步,整跑确认留到全部修完。
    • 摸 UI 用截图:文本 dump 读不到的元素(自绘滚轮、图标、浮层)用截图看最有效。
    • 不为找元素反复切换系统开关(如关 WiFi→又开),既违反"不反复切换"又复现不稳定。
  7. 查找不盲滚:查设备/文案/卡片用「找到即停 + 到底即停」循环,不要盲滚固定 N 次。命中判据用可靠信号(iOS visible=true 且在 header 以下)。唯一例外:查添加入口品类(selectDeviceCategory)可用快速预滑提速。

  8. 自动导航重试 5 次:先自己尝试自动点击导航(最多 5 次),超过 5 次没进目标页才打印当前页面信息并请用户手动进入。

  9. 过夜/无人值守走 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 uuid CQz9YCNX;用例库 App 必测项 lib uuid EPfZfC9Y(97 条)。ones 二进制在 /Users/woan/local/bin/ones。用 skill onescli
    • 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 graphql mutation,不要用 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 是回写唯一变量

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.ts applySpeedSettings()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 报告