AI_UIAutomation/tests/aihub/aihub_setting.test.ts

389 lines
14 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { describe, it, beforeAll, afterAll, beforeEach, expect } from 'vitest';
import { DeviceDriver } from '../../drivers/types';
import { createDriver } from '../../drivers/factory';
import { TestReporter } from '../../utils/test-reporter';
import {
sleep,
waitForSource,
enterEditInfo,
renameDevice,
changeDeviceRoom,
scrollToAndTap,
navigateToFirmwarePage,
checkFirmwareVersion,
} from '../../utils/common';
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('AI Hub Settings - 设备设置页', () => {
let driver: DeviceDriver;
let reporter: TestReporter;
const isAndroid = () => driver.platform === 'android';
const SETTINGS_ICON = () => isAndroid() ? { x: 999, y: 175 } : { x: 361, y: 70 };
const BACK_BTN = () => isAndroid() ? { x: 0, y: 0 } : { x: 39, y: 70 };
beforeAll(async () => {
driver = createDriver();
await driver.createSession();
reporter = new TestReporter('AIHub_Settings', driver.platform.toUpperCase());
});
beforeEach(async () => {
try {
await driver.dismissPopupIfPresent();
await driver.goBackToHomepage();
await driver.dismissPopupIfPresent();
} catch {
// Session may have crashed — recreate
try { await driver.destroySession(); } catch {}
await driver.createSession();
await sleep(3000);
}
});
afterAll(async () => {
reporter.generate();
await driver.destroySession();
});
async function enterHubFunctionPage(): Promise<boolean> {
if (isAndroid()) {
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);
// Dismiss any popup that appears after entering hub page
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;
}
await driver.tapElement(hubEl);
await sleep(5000);
await driver.dismissPopupIfPresent();
await sleep(1000);
const s2 = await driver.getSource();
return s2.includes('Try OpenClaw') || (s2.includes('Cameras') && s2.includes('AI Events'));
}
// 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 enterHubSettings(): Promise<boolean> {
const entered = await enterHubFunctionPage();
if (!entered) return false;
await driver.tap(SETTINGS_ICON().x, SETTINGS_ICON().y);
await sleep(3000);
const source = await driver.getSource();
if (source.includes('Settings') && source.includes('Device Settings')) return true;
return !source.includes('Try OpenClaw') && !source.includes('AI Routines');
}
it('进入AI Hub设置页', async () => {
const start = Date.now();
try {
const entered = await enterHubSettings();
expect(entered).toBe(true);
const source = await driver.getSource();
const hasSettingsContent = source.includes('Firmware') || source.includes('Device Info')
|| source.includes('Delete') || source.includes('Wi-Fi')
|| source.includes(AIHUB_NAME) || source.includes('Cloud Service');
expect(hasSettingsContent).toBe(true);
reporter.record('进入AI Hub设置页', 'PASS', Date.now() - start, '设置页正常打开');
} catch (e: any) {
const ss = await driver.screenshot().catch(() => '');
reporter.record('进入AI Hub设置页', 'FAIL', Date.now() - start, e.message, ss);
throw e;
}
});
it('修改设备名称-保存还原', async () => {
const start = Date.now();
try {
const entered = await enterHubSettings();
expect(entered).toBe(true);
const editOpened = await enterEditInfo(driver, AIHUB_NAME);
expect(editOpened).toBe(true);
const renamed = await renameDevice(driver, 'TestAIHub', 'save');
expect(renamed).toBe(true);
let source = await driver.getSource();
expect(source).toContain('TestAIHub');
console.log('名称已修改为: TestAIHub');
const restored = await renameDevice(driver, AIHUB_NAME, 'save');
expect(restored).toBe(true);
source = await driver.getSource();
expect(source).toContain(AIHUB_NAME);
console.log('名称还原: true');
reporter.record('修改设备名称-保存还原', 'PASS', Date.now() - start, `${AIHUB_NAME} → TestAIHub → ${AIHUB_NAME} (还原=true)`);
} catch (e: any) {
const ss = await driver.screenshot().catch(() => '');
reporter.record('修改设备名称-保存还原', 'FAIL', Date.now() - start, e.message, ss);
throw e;
}
});
it('修改设备名称-输入并取消', async () => {
const start = Date.now();
try {
const entered = await enterHubSettings();
expect(entered).toBe(true);
const editOpened = await enterEditInfo(driver, AIHUB_NAME);
expect(editOpened).toBe(true);
const renamed = await renameDevice(driver, 'CancelTest', 'cancel');
expect(renamed).toBe(true);
const source = await driver.getSource();
expect(source).toContain(AIHUB_NAME);
reporter.record('修改设备名称-取消', 'PASS', Date.now() - start, `输入"CancelTest"后取消, 名称保持"${AIHUB_NAME}"`);
} catch (e: any) {
const ss = await driver.screenshot().catch(() => '');
reporter.record('修改设备名称-取消', 'FAIL', Date.now() - start, e.message, ss);
throw e;
}
});
it('修改设备房间', async () => {
const start = Date.now();
try {
const entered = await enterHubSettings();
expect(entered).toBe(true);
const editOpened = await enterEditInfo(driver, AIHUB_NAME);
expect(editOpened).toBe(true);
const result = await changeDeviceRoom(driver);
expect(result.success).toBe(true);
console.log('可选房间数:', result.roomCount);
await changeDeviceRoom(driver);
reporter.record('修改设备房间', 'PASS', Date.now() - start, `Select Room页面, ${result.roomCount}个房间可选`);
} catch (e: any) {
const ss = await driver.screenshot().catch(() => '');
reporter.record('修改设备房间', 'FAIL', Date.now() - start, e.message, ss);
throw e;
}
});
it('固件升级页面信息', async () => {
const start = Date.now();
try {
const entered = await enterHubSettings();
expect(entered).toBe(true);
const navOk = await navigateToFirmwarePage(driver);
if (!navOk) {
reporter.record('固件升级页面信息', 'SKIP', Date.now() - start, '设备无固件升级入口');
return;
}
await sleep(3000);
const version = await checkFirmwareVersion(driver);
const detail = `固件版本=${version}`;
console.log(detail);
expect(version).not.toBe('unknown');
reporter.record('固件升级页面信息', 'PASS', Date.now() - start, detail);
} catch (e: any) {
const ss = await driver.screenshot().catch(() => '');
reporter.record('固件升级页面信息', 'FAIL', Date.now() - start, e.message, ss);
throw e;
}
});
it('指示灯开关切换', async () => {
const start = Date.now();
try {
const entered = await enterHubSettings();
expect(entered).toBe(true);
let source = await driver.getSource();
let hasIndicator = source.includes('Indicator Light') || source.includes('LED');
// On Android AI Hub, Indicator Light is under "Device Settings" sub-page
if (!hasIndicator && isAndroid()) {
const deviceSettingsEl = await driver.findElementRaw('-android uiautomator', 'new UiSelector().text("Device Settings")');
if (deviceSettingsEl) {
await driver.tapElement(deviceSettingsEl);
await sleep(3000);
source = await driver.getSource();
hasIndicator = source.includes('Indicator Light') || source.includes('LED');
}
}
if (!hasIndicator) {
await driver.scrollDown(300);
await sleep(800);
source = await driver.getSource();
hasIndicator = source.includes('Indicator Light') || source.includes('LED');
}
let toggleEl: string | null = null;
if (driver.platform === 'ios') {
toggleEl = await driver.findElementRaw('predicate string',
'type == "XCUIElementTypeSwitch" AND visible == true');
} else {
toggleEl = await driver.findElementRaw('-android uiautomator',
'new UiSelector().className("android.widget.Switch")');
}
if (!toggleEl) {
const tapped = await scrollToAndTap(driver, 'Indicator Light');
if (!tapped) {
console.log('未找到指示灯开关,可能不支持');
reporter.record('指示灯开关切换', 'SKIP', Date.now() - start, '设备无指示灯开关选项(skip)');
return;
}
await sleep(2000);
} else {
await driver.tapElement(toggleEl);
await sleep(2000);
await driver.tapElement(toggleEl);
await sleep(1000);
}
reporter.record('指示灯开关切换', 'PASS', Date.now() - start, '指示灯开关切换正常');
} catch (e: any) {
const ss = await driver.screenshot().catch(() => '');
reporter.record('指示灯开关切换', 'FAIL', Date.now() - start, e.message, ss);
throw e;
}
});
it('网络设置信息显示', async () => {
const start = Date.now();
try {
const entered = await enterHubSettings();
expect(entered).toBe(true);
let source = await driver.getSource();
let found = source.includes('Network Settings') || source.includes('网络设置');
if (!found) {
await driver.scrollDown(300);
await sleep(800);
source = await driver.getSource();
found = source.includes('Network Settings') || source.includes('网络设置');
}
if (!found) {
reporter.record('网络设置信息显示', 'SKIP', Date.now() - start, '未找到网络设置入口');
return;
}
const tapped = await scrollToAndTap(driver, 'Network Settings');
if (!tapped) {
reporter.record('网络设置信息显示', 'SKIP', Date.now() - start, '网络设置入口不可点击');
return;
}
await sleep(3000);
const netSource = await driver.getSource();
const hasWiFi = netSource.includes('Wi-Fi') || netSource.includes('SSID')
|| netSource.includes('IP') || netSource.includes('MAC')
|| netSource.includes('Ethernet') || netSource.includes('Wired');
const detail = `网络设置页加载成功, 含Wi-Fi/网络信息=${hasWiFi}`;
console.log(detail);
expect(hasWiFi).toBe(true);
reporter.record('网络设置信息显示', 'PASS', Date.now() - start, detail);
} catch (e: any) {
const ss = await driver.screenshot().catch(() => '');
reporter.record('网络设置信息显示', 'FAIL', Date.now() - start, e.message, ss);
throw e;
}
});
it('Cloud Service显示', async () => {
const start = Date.now();
try {
const entered = await enterHubSettings();
expect(entered).toBe(true);
let source = await driver.getSource();
let hasCloud = source.includes('Cloud Service') || source.includes('Third-party')
|| source.includes('Alexa') || source.includes('Google');
if (!hasCloud) {
await driver.scrollDown(300);
await sleep(800);
source = await driver.getSource();
hasCloud = source.includes('Cloud Service') || source.includes('Third-party')
|| source.includes('Alexa') || source.includes('Google');
}
if (!hasCloud) {
console.log('Cloud Service选项不可见可能需要更多滚动');
reporter.record('Cloud Service显示', 'SKIP', Date.now() - start, '页面无Cloud Service入口(skip)');
return;
}
const tapped = await scrollToAndTap(driver, 'Cloud Service');
if (tapped) {
await sleep(3000);
const cloudSource = await driver.getSource();
const hasCloudPage = cloudSource.includes('Alexa') || cloudSource.includes('Google')
|| cloudSource.includes('SmartThings') || cloudSource.includes('IFTTT')
|| cloudSource.includes('Cloud Service');
expect(hasCloudPage).toBe(true);
}
reporter.record('Cloud Service显示', 'PASS', Date.now() - start, 'Cloud Service页面正常');
} catch (e: any) {
const ss = await driver.screenshot().catch(() => '');
reporter.record('Cloud Service显示', 'FAIL', Date.now() - start, e.message, ss);
throw e;
}
});
});