#!/bin/bash # iOS 控制用例批量:每个用例前用 WDA terminate/launch 复位到首页,跑完记录结果。失败不阻塞。 # 对齐 Android run-p0-all 的 CONTROL,iOS 适配:① 复位走 WDA(无 adb)② 不做 BLE/WiFi 切换双跑(iOS 无 adb svc, # 控制走当前连接即可)③ plug 在 iOS 改名为 "Plug JP",PLUG_CTRL_DEVICE 用子串 "Plug"。 # 设备依赖:控制类用例依赖添加阶段(run-ios-adds.sh)已加的设备 + 账号保留的 摄像头/门铃。 # 注:本 iOS 账号当前无 Lock/Blind Tilt 实体 → lock_card/blind_tilt 多半无设备(失败隔离,不影响其它)。 # WDA 须已在 localhost:8100。 set -u cd "$(dirname "$0")/.." export PLATFORM=ios SOAK_MODE=1 SKIP_PROTO_NET=1 VITEST_RETRY=1 # 控制阶段:单个 it 失败自动重跑一次(对齐 Android) export LOCK_DEVICE="Lock 6D" LOCK_PRO_DEVICE="Lock Pro DE" BT_DEVICE="Blind Tilt 42" # PLUG_CTRL_DEVICE 不导出:plug 控制走 getDeviceName('plug') → 注册表实际名(iOS 当前 "Plug EU"),不写死 BUNDLE=com.wohand.wohand LOG=reports/ios-controls.log : > "$LOG" # WDA 健康检查 + 恢复:/status 不通则重启 iproxy 端口转发(覆盖过夜端口转发断开;设备上 WDA 进程仍在即恢复)。 ensure_wda(){ for t in 1 2 3; do curl -s --max-time 5 http://localhost:8100/status 2>/dev/null | grep -q '"ready"' && return 0 echo "[wda] /status 不通,重启 iproxy(第 $t 次)..." | tee -a "$LOG" pkill -f "iproxy 8100" 2>/dev/null; sleep 1 local udid; udid=$(idevice_id -l 2>/dev/null | head -1) nohup iproxy 8100 8100 -u "$udid" >/dev/null 2>&1 & sleep 5 done curl -s --max-time 5 http://localhost:8100/status 2>/dev/null | grep -q '"ready"' && return 0 echo "[wda] !! WDA 仍不可达(设备上 WDA 进程可能已退出,需手动重启 WDA)" | tee -a "$LOG"; return 1 } reset_home(){ ensure_wda || return 1 local sid sid=$(curl -s --max-time 5 http://localhost:8100/status 2>/dev/null | grep -oE '"sessionId" *: *"[^"]*"' | sed 's/.*"\([^"]*\)"$/\1/') [ -z "$sid" ] && { echo "[reset] 无 WDA session(WDA 掉了?)" | tee -a "$LOG"; return 1; } curl -s -X POST "http://localhost:8100/session/$sid/wda/apps/terminate" -H 'Content-Type: application/json' -d "{\"bundleId\":\"$BUNDLE\"}" >/dev/null 2>&1 sleep 1 curl -s -X POST "http://localhost:8100/session/$sid/wda/apps/launch" -H 'Content-Type: application/json' -d "{\"bundleId\":\"$BUNDLE\"}" >/dev/null 2>&1 sleep 3 } CONTROLS=( "action_panel|npx vitest run tests/platform/action_panel_settings.test.ts" "meter|npx vitest run tests/meter/meter_control.test.ts" "bot_card|npx vitest run tests/bot/bot_card.test.ts" "lock_card|npx vitest run tests/lock/lock_card.test.ts" "curtain|npx vitest run tests/curtain/curtain_control.test.ts" "curtain3|CURTAIN_DEVICE='Curtain3 Auto' npx vitest run tests/curtain/curtain_control.test.ts" "curtain3_2025|CURTAIN_DEVICE='Curtain3 2025 Auto' npx vitest run tests/curtain/curtain_control.test.ts" "plug_jp|PLUG_CTRL_DEVICE='Plug JP' npx vitest run tests/plug/plug_mini_control.test.ts" "plug_us|PLUG_CTRL_DEVICE='Plug US' npx vitest run tests/plug/plug_mini_control.test.ts" "plug_eu|PLUG_CTRL_DEVICE='Plug EU' npx vitest run tests/plug/plug_mini_control.test.ts" "blind_tilt|npx vitest run tests/curtain/blind_tilt_control.test.ts" "humidifier2|npx vitest run tests/humidifier/humidifier2_card.test.ts" "air_purifier|npx vitest run tests/air_purifier/air_purifier_card.test.ts" # 灯类首页卡片控制(电源+亮度+色温):通用用例,按 LIGHT_CARD 覆盖每个灯 "light_eave|LIGHT_REG=eaveLight LIGHT_CARD='Permanent Outdoor Lights' npx vitest run tests/ceiling_light/eave_light_control.test.ts" "light_color_bulb|LIGHT_REG=colorBulb LIGHT_CARD='Color Bulb' npx vitest run tests/ceiling_light/eave_light_control.test.ts" "light_ceiling_pro|LIGHT_REG=ceilingLight LIGHT_CARD='Ceiling Light Pro' npx vitest run tests/ceiling_light/eave_light_control.test.ts" "light_color_floor|LIGHT_REG=colorFloorLamp LIGHT_CARD='RGBICWW Floor Lamp' npx vitest run tests/ceiling_light/eave_light_control.test.ts" "light_floor|LIGHT_REG=floorLamp LIGHT_CARD='RGBWW Floor Lamp' npx vitest run tests/ceiling_light/eave_light_control.test.ts" "light_strip|LIGHT_REG=stripLight LIGHT_CARD='Strip Light SB' npx vitest run tests/ceiling_light/eave_light_control.test.ts" "light_strip3|LIGHT_REG=stripLight3 LIGHT_CARD='RGBWW Strip Light 3' npx vitest run tests/ceiling_light/eave_light_control.test.ts" "light_neon|LIGHT_REG=neonLight LIGHT_CARD='RGBIC Neon Rope Light' npx vitest run tests/ceiling_light/eave_light_control.test.ts" # 摄像头拉流(iOS 3/3 已验证;依赖保留的摄像头) "camera_stream|STREAM_HOLD_MS=60000 npx vitest run tests/camera/camera_stream.test.ts" # ===== TAIL:keypad 闭环 + 平台 + 账号(对齐 Android run-p0-all 的 TAIL,顺序一致)===== # keypad 功能页闭环(绑锁+加密码+解绑);依赖账号保留的 Lock。账号注销前跑。 "keypad_lock_passcode|npx vitest run tests/keypad/keypad_lock_passcode.test.ts" "feedback|npx vitest run tests/platform/feedback.test.ts" "rn_plugin|npx vitest run tests/platform/rn_plugin.test.ts" "create_room|npx vitest run tests/platform/create_room.test.ts" "message_center|npx vitest run tests/platform/message_center.test.ts" "scene_operation|npx vitest run tests/platform/scene_operation.test.ts" # 账号:delete_account 注册临时号再删临时号(不碰主账号);login 最后重新登录主账号收尾。third_party iOS→SKIP。 "third_party_login|npx vitest run tests/auth/third_party_login.test.ts" "eu_login|npx vitest run tests/auth/eu_login.test.ts" "register|npx vitest run tests/auth/register.test.ts" "forgot_password|npx vitest run tests/auth/forgot_password.test.ts" "delete_account|npx vitest run tests/auth/delete_account.test.ts" "login|npx vitest run tests/auth/login.test.ts" ) echo "===== $(date '+%F %T') iOS 控制批量开始 =====" | tee -a "$LOG" # 跑单条:复位→执行→打印结果;含 "✗ [FAIL]" 返回 1(SKIP/无设备不算失败,不重试)。 run_ctrl_one() { local name="$1" cmd="$2" echo "----- $(date +%T) [$name] -----" | tee -a "$LOG" reset_home eval "$cmd" > /tmp/ios-ctrl-one.log 2>&1 grep -E '✓ \[PASS\]|✗ \[FAIL\]|○ \[SKIP\]|结果:|未找到|not found|cannot' /tmp/ios-ctrl-one.log | grep -ivE "deprecat" | tail -6 | tee -a "$LOG" grep -q '✗ \[FAIL\]' /tmp/ios-ctrl-one.log && return 1 || return 0 } FAILED=() for entry in "${CONTROLS[@]}"; do name="${entry%%|*}"; cmd="${entry#*|}" run_ctrl_one "$name" "$cmd" || FAILED+=("$entry") done # 控制失败重试一轮(控制偶发性高:首页卡片漂移/浮层/同步延迟;失败常为环境抖动) if [ ${#FAILED[@]} -gt 0 ]; then echo "===== iOS 控制失败 ${#FAILED[@]} 个,重试一轮 =====" | tee -a "$LOG" for entry in "${FAILED[@]}"; do name="${entry%%|*}"; cmd="${entry#*|}" run_ctrl_one "${name}-retry" "$cmd" && echo "[CTRL-RETRY-OK] $name" | tee -a "$LOG" || echo "[CTRL-RETRY-GIVEUP] $name" | tee -a "$LOG" done fi echo "===== $(date '+%F %T') iOS 控制完成 =====" | tee -a "$LOG" # 汇总 iOS 全量(添加+控制,SOAK_MODE 累计在 reports/.results.json)逐用例报告 TS=$(date +%Y%m%d-%H%M%S) npx ts-node scripts/gen-combined.ts "iOSAll_$TS" >> "$LOG" 2>&1 \ && echo "iOS 逐用例报告: reports/iOSAll_${TS}_*.html" | tee -a "$LOG" \ || echo "报告生成失败(见 $LOG)" | tee -a "$LOG"