122 lines
5.0 KiB
TypeScript
122 lines
5.0 KiB
TypeScript
/**
|
||
* 双协议网络前置:按 PROTO 切手机蓝牙/WiFi,用于必测项控制用例的 BLE/WiFi 两种模式。
|
||
*
|
||
* - ble 模式: 开蓝牙、关 WiFi → app 走 BLE 直连
|
||
* - wifi 模式: 关蓝牙、开 WiFi → app 走 WiFi/云
|
||
*
|
||
* 平台能力(已在三星真机实测):
|
||
* - Android WiFi : adb `svc wifi enable/disable` 可行(dumpsys 实测真关/开)
|
||
* - Android 蓝牙 : adb `svc bluetooth` 被新系统禁用(exit 137)、`cmd bluetooth_manager` 无实现、
|
||
* `settings put bluetooth_on` 不动 radio → 改为 `am start 蓝牙设置` + 点 switch_widget(实测可切 ON↔BLE_ON)
|
||
* - iOS : 无公开 API,走系统设置(com.apple.Preferences)UI;locator 需 iOS 真机校准
|
||
*/
|
||
import { execSync } from 'child_process';
|
||
import { DeviceDriver } from '../../drivers/types';
|
||
import { APP_CONFIG } from '../../config/app.config';
|
||
import { sleep } from './element.helper';
|
||
|
||
function adbShell(cmd: string): string {
|
||
return execSync(`adb shell ${cmd}`, { encoding: 'utf-8', timeout: 20000 });
|
||
}
|
||
|
||
// ---------- Android(纯 adb,已实测) ----------
|
||
async function androidSetWifi(on: boolean): Promise<void> {
|
||
adbShell(`svc wifi ${on ? 'enable' : 'disable'}`);
|
||
await sleep(2000);
|
||
}
|
||
|
||
async function androidSetBluetooth(on: boolean): Promise<void> {
|
||
adbShell('am start -a android.settings.BLUETOOTH_SETTINGS');
|
||
await sleep(2500);
|
||
adbShell('uiautomator dump /sdcard/ui.xml');
|
||
const xml = adbShell('cat /sdcard/ui.xml');
|
||
// 三星实测节点: resource-id="com.android.settings:id/switch_widget" class=Switch checked=.. bounds=[x1,y1][x2,y2]
|
||
const m = xml.match(/switch_widget"[^>]*?checked="(true|false)"[^>]*?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
|
||
if (!m) {
|
||
console.warn('[network] 未找到蓝牙开关 switch_widget,跳过(请真机校准 locator)');
|
||
return;
|
||
}
|
||
const isOn = m[1] === 'true';
|
||
if (isOn !== on) {
|
||
const cx = Math.round((Number(m[2]) + Number(m[4])) / 2);
|
||
const cy = Math.round((Number(m[3]) + Number(m[5])) / 2);
|
||
adbShell(`input tap ${cx} ${cy}`);
|
||
await sleep(3000);
|
||
}
|
||
}
|
||
|
||
// ---------- iOS(系统设置 UI,locator 已在 iPhone/iOS26.5 实测) ----------
|
||
async function iosSetBluetooth(driver: DeviceDriver, on: boolean): Promise<void> {
|
||
await driver.activateApp('com.apple.Preferences');
|
||
await sleep(2000);
|
||
// 进入蓝牙二级页:设置首页 cell(文案本地化,中文"蓝牙"/英文"Bluetooth")
|
||
const entry =
|
||
(await driver.findElementRaw('accessibility id', '蓝牙').catch(() => null)) ||
|
||
(await driver.findElementRaw('accessibility id', 'Bluetooth').catch(() => null));
|
||
if (entry) {
|
||
await driver.tapElement(entry);
|
||
await sleep(2000);
|
||
}
|
||
// 蓝牙开关:name="BLUETOOTH"(与语言无关),value "1"=开 / "0"=关
|
||
const sw = await driver.findElementRaw('accessibility id', 'BLUETOOTH').catch(() => null);
|
||
if (!sw) {
|
||
console.warn('[network] iOS 未找到蓝牙开关(accessibility id=BLUETOOTH),跳过');
|
||
return;
|
||
}
|
||
const val = await driver.getElementAttribute(sw, 'value').catch(() => '');
|
||
const isOn = val === '1' || val === 'true';
|
||
if (isOn !== on) {
|
||
await driver.tapElement(sw);
|
||
await sleep(2500);
|
||
}
|
||
}
|
||
|
||
// iOS WiFi(无 adb,走系统设置;locator 已在 iOS26.5 实测:开关 name="无线局域网" value 1/0)
|
||
async function iosSetWifi(driver: DeviceDriver, on: boolean): Promise<void> {
|
||
await driver.activateApp('com.apple.Preferences');
|
||
await sleep(2000);
|
||
// 进入 WiFi 页:cell 文案本地化(中文"无线局域网" / 英文"Wi-Fi"/"WLAN")
|
||
const entry =
|
||
(await driver.findElementRaw('accessibility id', '无线局域网').catch(() => null)) ||
|
||
(await driver.findElementRaw('accessibility id', 'Wi-Fi').catch(() => null)) ||
|
||
(await driver.findElementRaw('accessibility id', 'WLAN').catch(() => null));
|
||
if (entry) {
|
||
await driver.tapElement(entry);
|
||
await sleep(2000);
|
||
}
|
||
// WiFi 开关:name="无线局域网"(本地化), value "1"=开 / "0"=关
|
||
const sw =
|
||
(await driver.findElementRaw('accessibility id', '无线局域网').catch(() => null)) ||
|
||
(await driver.findElementRaw('accessibility id', 'Wi-Fi').catch(() => null));
|
||
if (!sw) {
|
||
console.warn('[network] iOS 未找到 WiFi 开关,跳过');
|
||
return;
|
||
}
|
||
const val = await driver.getElementAttribute(sw, 'value').catch(() => '');
|
||
const isOn = val === '1' || val === 'true';
|
||
if (isOn !== on) {
|
||
await driver.tapElement(sw);
|
||
await sleep(2500);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 按协议设置手机网络状态,然后切回 SwitchBot app。
|
||
* 无人值守可用(Android 全自动;iOS 走设置 UI)。
|
||
*/
|
||
export async function applyProtoNetwork(driver: DeviceDriver, proto: 'ble' | 'wifi'): Promise<void> {
|
||
const wantBluetooth = proto === 'ble';
|
||
const wantWifi = proto === 'wifi';
|
||
|
||
if (driver.platform === 'android') {
|
||
await androidSetWifi(wantWifi);
|
||
await androidSetBluetooth(wantBluetooth);
|
||
await driver.activateApp(APP_CONFIG.android.appPackage);
|
||
} else {
|
||
await iosSetWifi(driver, wantWifi);
|
||
await iosSetBluetooth(driver, wantBluetooth);
|
||
await driver.activateApp(APP_CONFIG.ios.bundleId);
|
||
}
|
||
await sleep(2000);
|
||
}
|