249 lines
8.7 KiB
TypeScript
249 lines
8.7 KiB
TypeScript
import { HubShowDriver, createHubShowDriver } from '../../drivers/hubshow-driver';
|
|
import { sleep } from '../../utils/common';
|
|
|
|
/**
|
|
* AI Hub Show 固件测试 — 导航辅助函数
|
|
*
|
|
* Hub Show 是基于 Android 的带屏设备,通过 adb 直连设备屏幕进行 UI 自动化。
|
|
* 设备本机有安防首页、事件列表、回放、摄像头实时、门铃等页面。
|
|
*/
|
|
|
|
export { createHubShowDriver };
|
|
|
|
export async function wakeUpDevice(driver: HubShowDriver): Promise<void> {
|
|
const src = await driver.getSource();
|
|
if (src.includes('安防') || src.includes('主页') || src.includes('全部事件')) return;
|
|
// 待机屏特征: 只有日期/天气信息
|
|
await driver.tap(540, 500);
|
|
await sleep(1500);
|
|
}
|
|
|
|
export async function waitForLoading(driver: HubShowDriver, maxWait = 20000): Promise<void> {
|
|
const start = Date.now();
|
|
while (Date.now() - start < maxWait) {
|
|
const s = await driver.getSource();
|
|
if (!s.includes('Loading') && !s.includes('加载中')) return;
|
|
await sleep(2000);
|
|
}
|
|
}
|
|
|
|
export async function ensureOnSecurityPage(driver: HubShowDriver): Promise<boolean> {
|
|
await wakeUpDevice(driver);
|
|
const src = await driver.getSource();
|
|
// 安防页特征: 含"全部事件"或"回放"或("Security" + 摄像头相关)
|
|
if (src.includes('全部事件') || src.includes('回放')) return true;
|
|
if (src.includes('All Events') || src.includes('Playback')) return true;
|
|
if ((src.includes('Security') || src.includes('安防')) && (src.includes('Camera') || src.includes('摄像机'))) return true;
|
|
|
|
// Try navigating to security page - Hub Show main screen should have security entry
|
|
const secEl = await driver.findElementRaw('-android uiautomator', 'new UiSelector().text("安防")');
|
|
if (secEl) {
|
|
await driver.tapElement(secEl);
|
|
await sleep(3000);
|
|
await waitForLoading(driver);
|
|
return true;
|
|
}
|
|
|
|
// Try English label
|
|
const secElEn = await driver.findElementRaw('-android uiautomator', 'new UiSelector().text("Security")');
|
|
if (secElEn) {
|
|
await driver.tapElement(secElEn);
|
|
await sleep(3000);
|
|
await waitForLoading(driver);
|
|
return true;
|
|
}
|
|
|
|
// Go back to home and retry
|
|
await driver.goBack();
|
|
await sleep(2000);
|
|
const retry = await driver.findElementRaw('-android uiautomator', 'new UiSelector().textContains("安防")');
|
|
if (retry) {
|
|
await driver.tapElement(retry);
|
|
await sleep(3000);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
export async function ensureOnEventList(driver: HubShowDriver): Promise<boolean> {
|
|
const src = await driver.getSource();
|
|
// 已在事件列表: 含筛选栏(人物/事件类型/设备) 或 有时间戳+删除按钮(列表视图)
|
|
if (src.includes('事件类型') && (src.includes('人物') || src.includes('设备'))) return true;
|
|
if (src.includes('Type') && (src.includes('Person') || src.includes('Device'))) return true;
|
|
if (src.includes('删除') && /\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/.test(src)) return true;
|
|
if (src.includes('编辑') && /\d{2}:\d{2}:\d{2}/.test(src) && !src.includes('全部事件')) return true;
|
|
|
|
// Navigate to security page first
|
|
const onSec = await ensureOnSecurityPage(driver);
|
|
if (!onSec) return false;
|
|
|
|
// 安防页有"全部事件"入口,点击进入事件列表
|
|
const allEventsEl = await driver.findElementRaw('-android uiautomator', 'new UiSelector().textContains("全部事件")');
|
|
if (allEventsEl) {
|
|
await driver.tapElement(allEventsEl);
|
|
await sleep(3000);
|
|
await waitForLoading(driver);
|
|
return true;
|
|
}
|
|
|
|
const allEventsEn = await driver.findElementRaw('-android uiautomator', 'new UiSelector().textContains("All Events")');
|
|
if (allEventsEn) {
|
|
await driver.tapElement(allEventsEn);
|
|
await sleep(3000);
|
|
await waitForLoading(driver);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
export async function enterPlaybackPage(driver: HubShowDriver): Promise<boolean> {
|
|
const src = await driver.getSource();
|
|
if (src.includes('回放') || src.includes('Playback')) return true;
|
|
|
|
const onSec = await ensureOnSecurityPage(driver);
|
|
if (!onSec) return false;
|
|
|
|
const playbackEl = await driver.findElementRaw('-android uiautomator', 'new UiSelector().textContains("回放")');
|
|
if (playbackEl) {
|
|
await driver.tapElement(playbackEl);
|
|
await sleep(3000);
|
|
await waitForLoading(driver);
|
|
return true;
|
|
}
|
|
|
|
const playbackEn = await driver.findElementRaw('-android uiautomator', 'new UiSelector().textContains("Playback")');
|
|
if (playbackEn) {
|
|
await driver.tapElement(playbackEn);
|
|
await sleep(3000);
|
|
await waitForLoading(driver);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
export async function enterCameraLive(driver: HubShowDriver, cameraName?: string): Promise<boolean> {
|
|
const onSec = await ensureOnSecurityPage(driver);
|
|
if (!onSec) return false;
|
|
|
|
// Tap on camera live feed area (depends on layout)
|
|
if (cameraName) {
|
|
const camEl = await driver.findElementRaw('-android uiautomator', `new UiSelector().textContains("${cameraName}")`);
|
|
if (camEl) {
|
|
await driver.tapElement(camEl);
|
|
await sleep(3000);
|
|
await waitForLoading(driver);
|
|
const src = await driver.getSource();
|
|
return src.includes('实时') || src.includes('Live') || src.includes('警报') || src.includes('Alarm');
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
export async function enterDoorbellLive(driver: HubShowDriver): Promise<boolean> {
|
|
const onSec = await ensureOnSecurityPage(driver);
|
|
if (!onSec) return false;
|
|
|
|
const doorbellEl = await driver.findElementRaw('-android uiautomator', 'new UiSelector().textContains("Doorbell")');
|
|
if (!doorbellEl) {
|
|
const doorbellZh = await driver.findElementRaw('-android uiautomator', 'new UiSelector().textContains("门铃")');
|
|
if (!doorbellZh) return false;
|
|
await driver.tapElement(doorbellZh);
|
|
} else {
|
|
await driver.tapElement(doorbellEl);
|
|
}
|
|
await sleep(3000);
|
|
await waitForLoading(driver);
|
|
const src = await driver.getSource();
|
|
return src.includes('门铃') || src.includes('Doorbell') || src.includes('快捷回复');
|
|
}
|
|
|
|
export async function openFilterDialog(driver: HubShowDriver, filterType: 'date' | 'person' | 'type' | 'device'): Promise<boolean> {
|
|
const onEvents = await ensureOnEventList(driver);
|
|
if (!onEvents) return false;
|
|
|
|
const filterLabels: Record<string, string[]> = {
|
|
date: ['日期', 'Date'],
|
|
person: ['人物', 'Person'],
|
|
type: ['类型', 'Type', '事件类型'],
|
|
device: ['设备', 'Device'],
|
|
};
|
|
|
|
const labels = filterLabels[filterType] || [];
|
|
for (const label of labels) {
|
|
const el = await driver.findElementRaw('-android uiautomator', `new UiSelector().textContains("${label}")`);
|
|
if (el) {
|
|
await driver.tapElement(el);
|
|
await sleep(2000);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
export async function switchToTileView(driver: HubShowDriver): Promise<boolean> {
|
|
const onEvents = await ensureOnEventList(driver);
|
|
if (!onEvents) return false;
|
|
|
|
// Look for view switch button (grid/tile icon)
|
|
const switchEl = await driver.findElementRaw('-android uiautomator', 'new UiSelector().descriptionContains("平铺")');
|
|
if (switchEl) {
|
|
await driver.tapElement(switchEl);
|
|
await sleep(2000);
|
|
return true;
|
|
}
|
|
const switchEn = await driver.findElementRaw('-android uiautomator', 'new UiSelector().descriptionContains("Tile")');
|
|
if (switchEn) {
|
|
await driver.tapElement(switchEn);
|
|
await sleep(2000);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
export async function enterEditMode(driver: HubShowDriver): Promise<boolean> {
|
|
const editEl = await driver.findElementRaw('-android uiautomator', 'new UiSelector().text("编辑")');
|
|
if (editEl) {
|
|
await driver.tapElement(editEl);
|
|
await sleep(1500);
|
|
return true;
|
|
}
|
|
const editEn = await driver.findElementRaw('-android uiautomator', 'new UiSelector().text("Edit")');
|
|
if (editEn) {
|
|
await driver.tapElement(editEn);
|
|
await sleep(1500);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
export async function enterDailyReport(driver: HubShowDriver): Promise<boolean> {
|
|
const onSec = await ensureOnSecurityPage(driver);
|
|
if (!onSec) return false;
|
|
|
|
const reportEl = await driver.findElementRaw('-android uiautomator', 'new UiSelector().textContains("家居日报")');
|
|
if (reportEl) {
|
|
await driver.tapElement(reportEl);
|
|
await sleep(3000);
|
|
await waitForLoading(driver);
|
|
return true;
|
|
}
|
|
const reportEn = await driver.findElementRaw('-android uiautomator', 'new UiSelector().textContains("Smart Report")');
|
|
if (reportEn) {
|
|
await driver.tapElement(reportEn);
|
|
await sleep(3000);
|
|
await waitForLoading(driver);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
export function logPageSource(src: string): void {
|
|
const texts: string[] = [];
|
|
const textRe = /text="([^"]{1,100})"/g;
|
|
let m;
|
|
while ((m = textRe.exec(src)) !== null) {
|
|
if (!texts.includes(m[1])) texts.push(m[1]);
|
|
}
|
|
console.log('Page texts:', texts.slice(0, 30).join(' | '));
|
|
}
|