AI_UIAutomation/tests/aihub/aihub_connect.test.ts

267 lines
11 KiB
TypeScript

import { describe, it, beforeAll, afterAll, expect } from 'vitest';
import { DeviceDriver } from '../../drivers/types';
import { createDriver } from '../../drivers/factory';
import { TestReporter } from '../../utils/test-reporter';
import {
sleep,
addDeviceViaBLE,
isDeviceOnHomepage,
enterPairingMode,
configureHubWifi,
} from '../../utils/common';
import { getWifiCredentials } from '../../config/wifi.config';
import * as dotenv from 'dotenv';
import * as path from 'path';
dotenv.config({ path: path.resolve(__dirname, '../../.env') });
const AIHUB_NAME = process.env.AIHUB_NAME || 'AI Hub 6C';
describe('AIHub Connect - 添加AI Hub设备', () => {
let driver: DeviceDriver;
let reporter: TestReporter;
beforeAll(async () => {
driver = createDriver();
await driver.createSession();
reporter = new TestReporter('AIHub_Connect', driver.platform.toUpperCase());
});
afterAll(async () => {
reporter.generate();
await driver.destroySession();
});
function getAIHubAddOptions() {
const wifi = getWifiCredentials();
return {
categoryName: 'AI Hub',
deviceKeyword: 'AI Hub',
scanTimeout: 30000,
skipScanStep: true,
skipNextButton: true,
wizardButtons: ['Start now', 'Return Home', 'Done', 'Use now', 'Start Using', 'Got it', 'OK', 'Skip', 'Next', 'Save'],
connectionKeywords: [
'Initial Setup', 'Start Using', 'Done', 'added successfully', 'Added successfully',
'Got it', 'cloud service', 'Pick a room', 'Display Type', 'Select a room',
'Wi-Fi', 'WiFi', 'SSID', 'Network', 'Configure Wi-Fi', 'Connecting', 'Start now',
],
preSelectSteps: async (d: DeviceDriver) => {
// 选品类后可能有 Agree/免责页,先过掉
const agreeEl = await d.findElementRaw('name', 'Agree');
if (agreeEl) { await d.clickElement(agreeEl); await sleep(2000); }
// 继电器 ch43 控制供电 Plug 的开/关状态,**短按一下即可**(holdMs 400,结束释放不保持)。
// AIHUB_NO_RELAY=1:设备已手动进配对,跳过继电器。
if (!process.env.AIHUB_NO_RELAY) {
await enterPairingMode('aihub').catch((e) => console.log(`relay ch43 短按失败: ${e.message}`));
console.log('AI Hub: 已短按继电器(切 Plug),等待设备启动...');
await sleep(5000);
} else {
console.log('AI Hub: 跳过继电器(设备已手动进配对)');
}
// 跨平台按文案点击(iOS: name/predicate;Android: uiautomator text/contains)
const tapByLabel = async (labels: string[]): Promise<boolean> => {
for (const t of labels) {
let el: string | null = null;
if (d.platform === 'ios') {
el = await d.findElementRaw('name', t).catch(() => null)
|| await d.findElementRaw('predicate string', `name CONTAINS "${t}" OR label CONTAINS "${t}"`).catch(() => null);
} else {
el = await d.findElementRaw('-android uiautomator', `new UiSelector().text("${t}")`).catch(() => null)
|| await d.findElementRaw('-android uiautomator', `new UiSelector().textContains("${t}")`).catch(() => null);
}
if (el) { console.log(`AI Hub: 点击 "${t}"`); await d.clickElement(el); return true; }
}
return false;
};
// 点 "Connect Device"(链接设备)开始搜索
await tapByLabel(['Connect Device', 'Connect device', 'Connect devices', 'Connect Devices']);
await sleep(2000);
// 点连接设备后会弹隐私协议(iOS "Please Note" 弹窗) → 同意(Android 可能先勾选框)
await sleep(1500);
{
if (d.platform === 'android') {
const cb = await d.findElementRaw('-android uiautomator', 'new UiSelector().className("android.widget.CheckBox")').catch(() => null);
if (cb) { await d.clickElement(cb).catch(() => {}); await sleep(500); }
}
if (await tapByLabel(['Agree', 'Agree and Continue', 'I Agree', 'Accept', 'Continue', '同意', '同意并继续'])) {
await sleep(2500);
}
}
// 等待发现设备 → 进配网(上电后约 1 分钟才出现,最长等 ~100s)
console.log('AI Hub: 等待发现设备...');
for (let i = 0; i < 33; i++) {
await sleep(3000);
const s = await d.getSource().catch(() => '');
if (/Configure Wi-?Fi|Wi-?Fi Settings|SSID|Network name|Pick a room|Select a room|Setting up|Connecting to your router|Verifying|Name your|added successfully|Start now/i.test(s)) {
console.log('AI Hub: 已进入配网/后续页'); break;
}
}
},
postConnectionSteps: async (d: DeviceDriver) => {
await configureHubWifi(d, wifi);
},
};
}
it('通过BLE添加AI Hub 6C设备', { timeout: 360000 }, async () => {
const start = Date.now();
try {
const alreadyExists = await isDeviceOnHomepage(driver, AIHUB_NAME);
if (alreadyExists) {
console.log(`${AIHUB_NAME}已在首页,跳过重新添加`);
reporter.record(`添加${AIHUB_NAME}`, 'SKIP', Date.now() - start, `${AIHUB_NAME}已存在, 无需重新添加`);
return;
}
const result = await addDeviceViaBLE(driver, getAIHubAddOptions());
expect(result).toBe(true);
const elapsed = ((Date.now() - start) / 1000).toFixed(1);
reporter.record(`添加${AIHUB_NAME}`, 'PASS', Date.now() - start, `${AIHUB_NAME}添加成功, 耗时${elapsed}s`);
} catch (e: any) {
const ss = await driver.screenshot().catch(() => '');
reporter.record(`添加${AIHUB_NAME}`, 'FAIL', Date.now() - start, e.message, ss);
throw e;
}
});
const SETTINGS_ICON = (process.env.PLATFORM || 'android').toLowerCase() !== 'ios' ? { x: 999, y: 175 } : { x: 361, y: 70 };
const BACK_BTN = (process.env.PLATFORM || 'android').toLowerCase() !== 'ios' ? { x: 50, y: 88 } : { x: 39, y: 70 };
async function navigateToHubFunctionPage(): Promise<boolean> {
await driver.goBackToHomepage();
await sleep(1000);
if (driver.platform === 'android') {
const hubEl = await (driver as any).findDeviceCard(AIHUB_NAME);
if (!hubEl) return false;
const rect = await driver.getElementRect(hubEl);
await driver.tap(rect.x + 100, rect.y + 30);
await sleep(6000);
await driver.dismissPopupIfPresent();
await sleep(1000);
const s = await driver.getSource();
if (s.includes('Try OpenClaw') || (s.includes('Cameras') && s.includes('AI Events'))) {
return true;
}
return false;
}
// iOS path
const maxScroll = 5;
for (let i = 0; i <= maxScroll; i++) {
let hubEl: string | null = null;
hubEl = await driver.findElementRaw('predicate string', `name CONTAINS "${AIHUB_NAME}" AND type == "XCUIElementTypeCell"`);
if (!hubEl) {
hubEl = await driver.findElementRaw('predicate string', `label CONTAINS "${AIHUB_NAME}"`);
}
if (hubEl) {
await driver.clickElement(hubEl);
await sleep(5000);
const s = await driver.getSource();
if (s.includes('Try OpenClaw') || (s.includes('Cameras') && s.includes('AI Events'))) {
return true;
}
const rect = await driver.getElementRect(hubEl);
await driver.tap(rect.x + rect.width / 2, rect.y + rect.height / 2);
await sleep(5000);
const s2 = await driver.getSource();
if (s2.includes('Try OpenClaw') || (s2.includes('Cameras') && s2.includes('AI Events'))) {
return true;
}
}
if (i < maxScroll) {
await driver.swipe(195, 650, 195, 300, 0.5);
await sleep(1500);
}
}
return false;
}
async function removeHubFromSettings(): Promise<boolean> {
// Enter Hub function page
const entered = await navigateToHubFunctionPage();
if (!entered) return false;
// Tap settings icon (top-right)
await driver.tap(SETTINGS_ICON.x, SETTINGS_ICON.y);
await sleep(3000);
// Find and tap Delete (scroll down to find it)
let delEl: string | null = null;
for (let i = 0; i < 8; i++) {
if (driver.platform === 'ios') {
delEl = await driver.findElementRaw('predicate string', 'name == "Delete" AND visible == true');
} else {
delEl = await driver.findElementRaw('-android uiautomator', 'new UiSelector().text("Delete")');
}
if (delEl) break;
await driver.scrollDown(300);
await sleep(800);
}
if (!delEl) return false;
await driver.tapElement(delEl);
await sleep(3000);
// Confirm deletion - tap OK button in confirmation dialog
const confirmNames = ['OK', 'Confirm', 'Delete', 'Yes'];
for (const name of confirmNames) {
let el: string | null = null;
if (driver.platform === 'ios') {
el = await driver.findElementRaw('name', name);
} else {
el = await driver.findElementRaw('-android uiautomator', `new UiSelector().text("${name}")`);
}
if (el) {
await driver.tapElement(el);
await sleep(5000);
break;
}
}
// Navigate back to homepage manually (avoid goBackToHomepage misdetecting "Home Assistant" as homepage)
for (let i = 0; i < 8; i++) {
const s = await driver.getSource();
const isHome = s.includes('Add') && s.includes('More') && s.includes('Home')
&& !s.includes('Home Assistant') && !s.includes('Device Settings');
if (isHome) break;
await driver.tap(BACK_BTN.x, BACK_BTN.y);
await sleep(2000);
}
await sleep(2000);
const homeSource = await driver.getSource();
return !homeSource.includes(AIHUB_NAME);
}
it('删除AI Hub 6C设备并恢复', { timeout: 180000 }, async () => {
const start = Date.now();
try {
const exists = await isDeviceOnHomepage(driver, AIHUB_NAME);
if (!exists) {
console.log(`${AIHUB_NAME}不在首页,跳过删除测试`);
reporter.record(`删除${AIHUB_NAME}`, 'PASS', Date.now() - start, `${AIHUB_NAME}不存在, 无需删除`);
return;
}
const removed = await removeHubFromSettings();
expect(removed).toBe(true);
// Data recovery: re-add the device
const reAdded = await addDeviceViaBLE(driver, getAIHubAddOptions());
expect(reAdded).toBe(true);
const elapsed = ((Date.now() - start) / 1000).toFixed(1);
reporter.record(`删除${AIHUB_NAME}`, 'PASS', Date.now() - start, `删除并重新添加${AIHUB_NAME}成功, 耗时${elapsed}s`);
} catch (e: any) {
const ss = await driver.screenshot().catch(() => '');
reporter.record(`删除${AIHUB_NAME}`, 'FAIL', Date.now() - start, e.message, ss);
throw e;
}
});
});