AI_UIAutomation/tests/camera/camera_setting.test.ts

1286 lines
43 KiB
TypeScript

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 { getDeviceName } from '../../config/device.config';
import { sleep, waitForSource } from '../../utils/common';
import * as dotenv from 'dotenv';
import * as path from 'path';
dotenv.config({ path: path.resolve(__dirname, '../../.env') });
const deviceName = getDeviceName('camera', 'CAMERA_DEVICE');
describe('Camera Setting - 摄像头设置页', () => {
let driver: DeviceDriver;
let reporter: TestReporter;
beforeAll(async () => {
driver = createDriver();
await driver.createSession();
reporter = new TestReporter('Camera_Setting', driver.platform.toUpperCase());
});
beforeEach(async () => {
await driver.dismissPopupIfPresent();
await driver.goBackToHomepage();
await driver.dismissPopupIfPresent();
});
afterAll(async () => {
reporter.generate();
await driver.destroySession();
});
async function findCameraCard(): Promise<string | null> {
const predicates = [
`name CONTAINS "${deviceName}" AND type == "XCUIElementTypeCell"`,
'name CONTAINS "Camera" AND type == "XCUIElementTypeCell"',
'name CONTAINS "Pan Tilt" AND type == "XCUIElementTypeCell"',
'name CONTAINS "Indoor" AND type == "XCUIElementTypeCell"',
];
for (const pred of predicates) {
const elems = await driver.findElementsRaw('predicate string', pred);
if (elems.length > 0) return elems[0];
}
await driver.scrollDown(300);
await sleep(800);
for (const pred of predicates) {
const elems = await driver.findElementsRaw('predicate string', pred);
if (elems.length > 0) return elems[0];
}
return null;
}
async function enterSettingPage(): Promise<void> {
const cardId = await findCameraCard();
if (!cardId) throw new Error('找不到摄像头卡片');
await driver.tapElement(cardId);
await sleep(3000);
const settingsEl = await driver.findElementRaw('name', 'Settings');
if (!settingsEl) {
const gearEl = await driver.findElementRaw('predicate string', 'name CONTAINS "setting" OR name CONTAINS "gear"');
if (gearEl) await driver.tapElement(gearEl);
else throw new Error('找不到Settings按钮');
} else {
await driver.tapElement(settingsEl);
}
await sleep(2000);
}
async function scrollToAndTap(name: string): Promise<boolean> {
let el = await driver.findElementRaw('name', name);
if (el) {
await driver.tapElement(el);
return true;
}
await driver.scrollDown(300);
await sleep(500);
el = await driver.findElementRaw('name', name);
if (el) {
await driver.tapElement(el);
return true;
}
await driver.scrollDown(300);
await sleep(500);
el = await driver.findElementRaw('name', name);
if (el) {
await driver.tapElement(el);
return true;
}
return false;
}
// ==================== 设备名称 ====================
it('设置页-更新设备名称', async () => {
const start = Date.now();
try {
await enterSettingPage();
// Tap device info / name area
const deviceInfoEl = await driver.findElementRaw('name', deviceName);
if (!deviceInfoEl) {
const infoEl = await driver.findElementRaw('predicate string', 'type == "XCUIElementTypeStaticText" AND y < 200');
if (infoEl) await driver.tapElement(infoEl);
} else {
await driver.tapElement(deviceInfoEl);
}
await sleep(1500);
// Tap Name row
const nameEl = await driver.findElementRaw('name', 'Name');
if (nameEl) {
const nameRect = await driver.getElementRect(nameEl);
await driver.tap(nameRect.x + nameRect.width + 100, nameRect.y + nameRect.height / 2);
await sleep(1500);
}
// Clear and type new name
const textFields = await driver.findElementsRaw('class name', 'XCUIElementTypeTextField');
if (textFields.length > 0) {
const clearEl = await driver.findElementRaw('name', 'Clear text');
if (clearEl) await driver.tapElement(clearEl);
await sleep(200);
await driver.typeText(textFields[0], 'rename');
await sleep(500);
// Cancel
const cancelEl = await driver.findElementRaw('name', 'Cancel');
if (cancelEl) {
await driver.tapElement(cancelEl);
await sleep(1000);
}
// Re-open rename dialog
if (nameEl) {
const nameRect = await driver.getElementRect(nameEl);
await driver.tap(nameRect.x + nameRect.width + 100, nameRect.y + nameRect.height / 2);
await sleep(1500);
}
const textFields2 = await driver.findElementsRaw('class name', 'XCUIElementTypeTextField');
if (textFields2.length > 0) {
const clearEl2 = await driver.findElementRaw('name', 'Clear text');
if (clearEl2) await driver.tapElement(clearEl2);
await sleep(200);
await driver.typeText(textFields2[0], 'rename');
await sleep(500);
// Save
const yesEl = await driver.findElementRaw('name', 'Yes') || await driver.findElementRaw('name', 'Save') || await driver.findElementRaw('name', 'Confirm');
if (yesEl) await driver.tapElement(yesEl);
await sleep(3000);
// Verify renamed
let source = await driver.getSource();
const renamed = source.includes('rename');
console.log('重命名为rename:', renamed);
// Restore original name
const nameEl3 = await driver.findElementRaw('name', 'Name');
if (nameEl3) {
const rect3 = await driver.getElementRect(nameEl3);
await driver.tap(rect3.x + rect3.width + 100, rect3.y + rect3.height / 2);
await sleep(1500);
const tf3 = await driver.findElementsRaw('class name', 'XCUIElementTypeTextField');
if (tf3.length > 0) {
const cl3 = await driver.findElementRaw('name', 'Clear text');
if (cl3) await driver.tapElement(cl3);
await sleep(200);
await driver.typeText(tf3[0], deviceName);
await sleep(500);
const yes3 = await driver.findElementRaw('name', 'Yes') || await driver.findElementRaw('name', 'Save');
if (yes3) await driver.tapElement(yes3);
await sleep(3000);
}
}
source = await driver.getSource();
const restored = source.includes(deviceName);
reporter.record('更新设备名称', 'PASS', Date.now() - start, `rename→${deviceName}, 还原=${restored}`);
} else {
reporter.record('更新设备名称', 'PASS', Date.now() - start, '输入框操作完成');
}
} else {
reporter.record('更新设备名称', 'SKIP', Date.now() - start, '未找到文本输入框');
}
} catch (e: any) {
reporter.record('更新设备名称', 'FAIL', Date.now() - start, e.message);
throw e;
}
});
// ==================== 房间位置 ====================
it('设置页-修改设备房间位置', async () => {
const start = Date.now();
try {
await enterSettingPage();
const deviceInfoEl = await driver.findElementRaw('name', deviceName) ||
await driver.findElementRaw('predicate string', 'type == "XCUIElementTypeStaticText" AND y < 200');
if (deviceInfoEl) await driver.tapElement(deviceInfoEl);
await sleep(1500);
const roomEl = await driver.findElementRaw('name', 'Room');
if (!roomEl) {
reporter.record('修改房间位置', 'SKIP', Date.now() - start, '未找到Room选项');
return;
}
const roomRect = await driver.getElementRect(roomEl);
await driver.tap(roomRect.x + roomRect.width + 50, roomRect.y + roomRect.height / 2);
await sleep(2000);
// Select a test room
const cells = await driver.findElementsRaw('class name', 'XCUIElementTypeCell');
if (cells.length > 1) {
await driver.tapElement(cells[1]);
await sleep(500);
const saveEl = await driver.findElementRaw('name', 'Save');
if (saveEl) await driver.tapElement(saveEl);
await sleep(2000);
// Restore default room
const roomEl2 = await driver.findElementRaw('name', 'Room');
if (roomEl2) {
const rect2 = await driver.getElementRect(roomEl2);
await driver.tap(rect2.x + rect2.width + 50, rect2.y + rect2.height / 2);
await sleep(2000);
const cells2 = await driver.findElementsRaw('class name', 'XCUIElementTypeCell');
if (cells2.length > 0) {
await driver.tapElement(cells2[0]);
await sleep(500);
const saveEl2 = await driver.findElementRaw('name', 'Save');
if (saveEl2) await driver.tapElement(saveEl2);
await sleep(2000);
}
}
}
reporter.record('修改房间位置', 'PASS', Date.now() - start, '房间切换并还原完成');
} catch (e: any) {
reporter.record('修改房间位置', 'FAIL', Date.now() - start, e.message);
throw e;
}
});
// ==================== FAQ ====================
it('设置页-查看设备FAQ', async () => {
const start = Date.now();
try {
await enterSettingPage();
const faqFound = await scrollToAndTap('FAQ');
if (!faqFound) {
const feedbackFound = await scrollToAndTap('Feedback');
if (feedbackFound) {
await sleep(1500);
await scrollToAndTap('FAQ');
}
}
await sleep(3000);
const source = await driver.getSource();
const hasFaq = source.includes('FAQ') || source.includes('Help') || source.includes('Question');
console.log('FAQ页面:', hasFaq);
reporter.record('查看FAQ', 'PASS', Date.now() - start, `FAQ页面加载=${hasFaq}`);
} catch (e: any) {
reporter.record('查看FAQ', 'FAIL', Date.now() - start, e.message);
throw e;
}
});
// ==================== 固件版本 ====================
it('设置页-查看设备固件版本', async () => {
const start = Date.now();
try {
await enterSettingPage();
const found = await scrollToAndTap('Firmware');
if (!found) {
const found2 = await scrollToAndTap('Firmware Version');
if (!found2) throw new Error('未找到Firmware选项');
}
await sleep(3000);
const source = await driver.getSource();
const hasFirmware = source.includes('Firmware') || source.includes('Version') || source.includes('V');
const versionMatch = source.match(/name="(V[\d.]+)"/);
const version = versionMatch ? versionMatch[1] : 'found';
console.log('固件版本:', version);
reporter.record('查看固件版本', 'PASS', Date.now() - start, `固件版本=${version}`);
} catch (e: any) {
reporter.record('查看固件版本', 'FAIL', Date.now() - start, e.message);
throw e;
}
});
// ==================== 设备信息 ====================
it('设置页-查看设备信息', async () => {
const start = Date.now();
try {
await enterSettingPage();
const found = await scrollToAndTap('Device Info');
if (!found) throw new Error('未找到Device Info选项');
await sleep(2000);
const source = await driver.getSource();
const hasInfo = source.includes('MAC') || source.includes('IP') || source.includes('Device Info');
console.log('设备信息页:', hasInfo);
expect(hasInfo).toBe(true);
reporter.record('查看设备信息', 'PASS', Date.now() - start, `设备信息页面加载成功`);
} catch (e: any) {
reporter.record('查看设备信息', 'FAIL', Date.now() - start, e.message);
throw e;
}
});
// ==================== 侦测开关 ====================
it('设置页-打开/关闭侦测开关', async () => {
const start = Date.now();
try {
await enterSettingPage();
const found = await scrollToAndTap('Motion Detection');
if (!found) throw new Error('未找到Motion Detection选项');
await sleep(2000);
// Toggle motion switch
const switches = await driver.findElementsRaw('class name', 'XCUIElementTypeSwitch');
if (switches.length > 0) {
await driver.tapElement(switches[0]);
await sleep(5000);
await driver.tapElement(switches[0]);
await sleep(3000);
}
reporter.record('侦测开关', 'PASS', Date.now() - start, 'Motion Detection开关切换完成');
} catch (e: any) {
reporter.record('侦测开关', 'FAIL', Date.now() - start, e.message);
throw e;
}
});
// ==================== 侦测报警开关 ====================
it('设置页-打开/关闭侦测报警开关', async () => {
const start = Date.now();
try {
await enterSettingPage();
const found = await scrollToAndTap('Motion Detection');
if (!found) throw new Error('未找到Motion Detection选项');
await sleep(2000);
// Ensure motion is on first
const switches = await driver.findElementsRaw('class name', 'XCUIElementTypeSwitch');
if (switches.length >= 2) {
const val = await driver.getElementAttribute(switches[0], 'value');
if (val === '0') {
await driver.tapElement(switches[0]);
await sleep(5000);
}
// Toggle alarm switch
await driver.tapElement(switches[1]);
await sleep(5000);
const afterVal = await driver.getElementAttribute(switches[1], 'value');
await driver.tapElement(switches[1]);
await sleep(3000);
reporter.record('侦测报警开关', 'PASS', Date.now() - start, `报警开关切换, val=${afterVal}`);
} else {
reporter.record('侦测报警开关', 'SKIP', Date.now() - start, '未找到足够的开关');
}
} catch (e: any) {
reporter.record('侦测报警开关', 'FAIL', Date.now() - start, e.message);
throw e;
}
});
// ==================== 灵敏度 ====================
it('设置页-侦测灵敏度切换为High', async () => {
const start = Date.now();
try {
await enterSettingPage();
await scrollToAndTap('Motion Detection');
await sleep(2000);
const sensEl = await driver.findElementRaw('name', 'Sensitivity');
if (!sensEl) {
reporter.record('灵敏度-High', 'SKIP', Date.now() - start, '未找到Sensitivity');
return;
}
await driver.tapElement(sensEl);
await sleep(1500);
const highEl = await driver.findElementRaw('name', 'High');
expect(highEl).not.toBeNull();
await driver.tapElement(highEl!);
await sleep(5000);
reporter.record('灵敏度-High', 'PASS', Date.now() - start, 'Sensitivity=High');
} catch (e: any) {
reporter.record('灵敏度-High', 'FAIL', Date.now() - start, e.message);
throw e;
}
});
it('设置页-侦测灵敏度切换为Medium', async () => {
const start = Date.now();
try {
await enterSettingPage();
await scrollToAndTap('Motion Detection');
await sleep(2000);
const sensEl = await driver.findElementRaw('name', 'Sensitivity');
if (!sensEl) {
reporter.record('灵敏度-Medium', 'SKIP', Date.now() - start, '未找到Sensitivity');
return;
}
await driver.tapElement(sensEl);
await sleep(1500);
const medEl = await driver.findElementRaw('name', 'Medium');
expect(medEl).not.toBeNull();
await driver.tapElement(medEl!);
await sleep(5000);
reporter.record('灵敏度-Medium', 'PASS', Date.now() - start, 'Sensitivity=Medium');
} catch (e: any) {
reporter.record('灵敏度-Medium', 'FAIL', Date.now() - start, e.message);
throw e;
}
});
it('设置页-侦测灵敏度切换为Low', async () => {
const start = Date.now();
try {
await enterSettingPage();
await scrollToAndTap('Motion Detection');
await sleep(2000);
const sensEl = await driver.findElementRaw('name', 'Sensitivity');
if (!sensEl) {
reporter.record('灵敏度-Low', 'SKIP', Date.now() - start, '未找到Sensitivity');
return;
}
await driver.tapElement(sensEl);
await sleep(1500);
const lowEl = await driver.findElementRaw('name', 'Low');
expect(lowEl).not.toBeNull();
await driver.tapElement(lowEl!);
await sleep(5000);
reporter.record('灵敏度-Low', 'PASS', Date.now() - start, 'Sensitivity=Low');
} catch (e: any) {
reporter.record('灵敏度-Low', 'FAIL', Date.now() - start, e.message);
throw e;
}
});
// ==================== 侦测区域 ====================
it('设置页-设置侦测区域', async () => {
const start = Date.now();
try {
await enterSettingPage();
await scrollToAndTap('Motion Detection');
await sleep(2000);
const zoneEl = await driver.findElementRaw('name', 'Detection Zone');
if (!zoneEl) {
reporter.record('设置侦测区域', 'SKIP', Date.now() - start, '当前设备不支持Detection Zone');
return;
}
// Check zone switch
const switches = await driver.findElementsRaw('class name', 'XCUIElementTypeSwitch');
for (const sw of switches) {
const val = await driver.getElementAttribute(sw, 'value');
if (val === '0') {
await driver.tapElement(sw);
await sleep(5000);
break;
}
}
// Edit zone
const editEl = await driver.findElementRaw('name', 'Edit');
if (editEl) {
await driver.tapElement(editEl);
await sleep(3000);
const saveEl = await driver.findElementRaw('name', 'Save');
if (saveEl) await driver.tapElement(saveEl);
await sleep(3000);
}
reporter.record('设置侦测区域', 'PASS', Date.now() - start, 'Detection Zone设置完成');
} catch (e: any) {
reporter.record('设置侦测区域', 'FAIL', Date.now() - start, e.message);
throw e;
}
});
// ==================== 夜视设置 ====================
it('设置页-切换夜视为打开', async () => {
const start = Date.now();
try {
await enterSettingPage();
await scrollToAndTap('Basic Settings');
await sleep(1500);
const nvEl = await driver.findElementRaw('name', 'Night Vision');
expect(nvEl).not.toBeNull();
await driver.tapElement(nvEl!);
await sleep(1500);
const onEl = await driver.findElementRaw('name', 'On') || await driver.findElementRaw('name', 'Always on');
expect(onEl).not.toBeNull();
await driver.tapElement(onEl!);
await sleep(5000);
reporter.record('设置夜视-打开', 'PASS', Date.now() - start, 'Night Vision=On');
} catch (e: any) {
reporter.record('设置夜视-打开', 'FAIL', Date.now() - start, e.message);
throw e;
}
});
it('设置页-切换夜视为关闭', async () => {
const start = Date.now();
try {
await enterSettingPage();
await scrollToAndTap('Basic Settings');
await sleep(1500);
const nvEl = await driver.findElementRaw('name', 'Night Vision');
expect(nvEl).not.toBeNull();
await driver.tapElement(nvEl!);
await sleep(1500);
const offEl = await driver.findElementRaw('name', 'Off');
expect(offEl).not.toBeNull();
await driver.tapElement(offEl!);
await sleep(5000);
reporter.record('设置夜视-关闭', 'PASS', Date.now() - start, 'Night Vision=Off');
} catch (e: any) {
reporter.record('设置夜视-关闭', 'FAIL', Date.now() - start, e.message);
throw e;
}
});
it('设置页-切换夜视为自动', async () => {
const start = Date.now();
try {
await enterSettingPage();
await scrollToAndTap('Basic Settings');
await sleep(1500);
const nvEl = await driver.findElementRaw('name', 'Night Vision');
expect(nvEl).not.toBeNull();
await driver.tapElement(nvEl!);
await sleep(1500);
const autoEl = await driver.findElementRaw('name', 'Auto');
expect(autoEl).not.toBeNull();
await driver.tapElement(autoEl!);
await sleep(5000);
reporter.record('设置夜视-自动', 'PASS', Date.now() - start, 'Night Vision=Auto');
} catch (e: any) {
reporter.record('设置夜视-自动', 'FAIL', Date.now() - start, e.message);
throw e;
}
});
// ==================== 存储模式 ====================
it('设置页-切换存储模式为仅事件', async () => {
const start = Date.now();
try {
await enterSettingPage();
await scrollToAndTap('Local Storage');
await sleep(2000);
const modeEl = await driver.findElementRaw('name', 'Storage Mode');
if (!modeEl) {
reporter.record('存储模式-仅事件', 'SKIP', Date.now() - start, '未找到Storage Mode');
return;
}
await driver.tapElement(modeEl);
await sleep(1500);
const eventsEl = await driver.findElementRaw('name', 'Events Only');
expect(eventsEl).not.toBeNull();
await driver.tapElement(eventsEl!);
await sleep(5000);
reporter.record('存储模式-仅事件', 'PASS', Date.now() - start, 'Storage=Events Only');
} catch (e: any) {
reporter.record('存储模式-仅事件', 'FAIL', Date.now() - start, e.message);
throw e;
}
});
it('设置页-切换存储模式为持续', async () => {
const start = Date.now();
try {
await enterSettingPage();
await scrollToAndTap('Local Storage');
await sleep(2000);
const modeEl = await driver.findElementRaw('name', 'Storage Mode');
if (!modeEl) {
reporter.record('存储模式-持续', 'SKIP', Date.now() - start, '未找到Storage Mode');
return;
}
await driver.tapElement(modeEl);
await sleep(1500);
const contEl = await driver.findElementRaw('name', 'Continuous');
expect(contEl).not.toBeNull();
await driver.tapElement(contEl!);
await sleep(5000);
reporter.record('存储模式-持续', 'PASS', Date.now() - start, 'Storage=Continuous');
} catch (e: any) {
reporter.record('存储模式-持续', 'FAIL', Date.now() - start, e.message);
throw e;
}
});
// ==================== 对讲模式 ====================
it('设置页-切换对讲为单向', async () => {
const start = Date.now();
try {
await enterSettingPage();
await scrollToAndTap('Basic Settings');
await sleep(1500);
const talkEl = await driver.findElementRaw('name', 'Talk Mode');
expect(talkEl).not.toBeNull();
await driver.tapElement(talkEl!);
await sleep(1500);
const oneEl = await driver.findElementRaw('name', 'One-way');
expect(oneEl).not.toBeNull();
await driver.tapElement(oneEl!);
await sleep(5000);
reporter.record('对讲-单向', 'PASS', Date.now() - start, 'Talk Mode=One-way');
} catch (e: any) {
reporter.record('对讲-单向', 'FAIL', Date.now() - start, e.message);
throw e;
}
});
it('设置页-切换对讲为双向', async () => {
const start = Date.now();
try {
await enterSettingPage();
await scrollToAndTap('Basic Settings');
await sleep(1500);
const talkEl = await driver.findElementRaw('name', 'Talk Mode');
expect(talkEl).not.toBeNull();
await driver.tapElement(talkEl!);
await sleep(1500);
const twoEl = await driver.findElementRaw('name', 'Two-way');
expect(twoEl).not.toBeNull();
await driver.tapElement(twoEl!);
await sleep(5000);
reporter.record('对讲-双向', 'PASS', Date.now() - start, 'Talk Mode=Two-way');
} catch (e: any) {
reporter.record('对讲-双向', 'FAIL', Date.now() - start, e.message);
throw e;
}
});
// ==================== 对讲音量 ====================
it('设置页-切换对讲音量为High', async () => {
const start = Date.now();
try {
await enterSettingPage();
await scrollToAndTap('Basic Settings');
await sleep(1500);
const volEl = await driver.findElementRaw('name', 'Volume');
if (!volEl) {
reporter.record('对讲音量-High', 'SKIP', Date.now() - start, '当前设备不支持Volume设置');
return;
}
await driver.tapElement(volEl);
await sleep(1500);
const highEl = await driver.findElementRaw('name', 'High');
expect(highEl).not.toBeNull();
await driver.tapElement(highEl!);
await sleep(5000);
reporter.record('对讲音量-High', 'PASS', Date.now() - start, 'Volume=High');
} catch (e: any) {
reporter.record('对讲音量-High', 'FAIL', Date.now() - start, e.message);
throw e;
}
});
it('设置页-切换对讲音量为Medium', async () => {
const start = Date.now();
try {
await enterSettingPage();
await scrollToAndTap('Basic Settings');
await sleep(1500);
const volEl = await driver.findElementRaw('name', 'Volume');
if (!volEl) {
reporter.record('对讲音量-Medium', 'SKIP', Date.now() - start, '当前设备不支持');
return;
}
await driver.tapElement(volEl);
await sleep(1500);
const medEl = await driver.findElementRaw('name', 'Medium');
expect(medEl).not.toBeNull();
await driver.tapElement(medEl!);
await sleep(5000);
reporter.record('对讲音量-Medium', 'PASS', Date.now() - start, 'Volume=Medium');
} catch (e: any) {
reporter.record('对讲音量-Medium', 'FAIL', Date.now() - start, e.message);
throw e;
}
});
it('设置页-切换对讲音量为Low', async () => {
const start = Date.now();
try {
await enterSettingPage();
await scrollToAndTap('Basic Settings');
await sleep(1500);
const volEl = await driver.findElementRaw('name', 'Volume');
if (!volEl) {
reporter.record('对讲音量-Low', 'SKIP', Date.now() - start, '当前设备不支持');
return;
}
await driver.tapElement(volEl);
await sleep(1500);
const lowEl = await driver.findElementRaw('name', 'Low');
expect(lowEl).not.toBeNull();
await driver.tapElement(lowEl!);
await sleep(5000);
reporter.record('对讲音量-Low', 'PASS', Date.now() - start, 'Volume=Low');
} catch (e: any) {
reporter.record('对讲音量-Low', 'FAIL', Date.now() - start, e.message);
throw e;
}
});
// ==================== 校准 ====================
it('设置页-摄像头校准', async () => {
const start = Date.now();
try {
await enterSettingPage();
await scrollToAndTap('Basic Settings');
await sleep(1500);
const calEl = await driver.findElementRaw('name', 'Calibrate');
if (!calEl) {
reporter.record('摄像头校准', 'SKIP', Date.now() - start, '当前设备不支持Calibrate');
return;
}
await driver.tapElement(calEl);
await sleep(1500);
const confirmEl = await driver.findElementRaw('name', 'Confirm');
if (confirmEl) await driver.tapElement(confirmEl);
await sleep(5000);
reporter.record('摄像头校准', 'PASS', Date.now() - start, '校准操作完成');
} catch (e: any) {
reporter.record('摄像头校准', 'FAIL', Date.now() - start, e.message);
throw e;
}
});
// ==================== 指示灯 ====================
it('设置页-打开/关闭指示灯', async () => {
const start = Date.now();
try {
await enterSettingPage();
await scrollToAndTap('Basic Settings');
await sleep(1500);
const lightEl = await driver.findElementRaw('name', 'Indicator Light');
if (!lightEl) {
reporter.record('指示灯', 'SKIP', Date.now() - start, '未找到Indicator Light');
return;
}
const switches = await driver.findElementsRaw('class name', 'XCUIElementTypeSwitch');
for (const sw of switches) {
await driver.tapElement(sw);
await sleep(5000);
await driver.tapElement(sw);
await sleep(3000);
break;
}
reporter.record('指示灯', 'PASS', Date.now() - start, 'Indicator Light切换完成');
} catch (e: any) {
reporter.record('指示灯', 'FAIL', Date.now() - start, e.message);
throw e;
}
});
// ==================== 水印 ====================
it('设置页-打开/关闭水印', async () => {
const start = Date.now();
try {
await enterSettingPage();
await scrollToAndTap('Basic Settings');
await sleep(1500);
const vdEl = await driver.findElementRaw('name', 'Video Display');
if (!vdEl) {
reporter.record('水印', 'SKIP', Date.now() - start, '未找到Video Display');
return;
}
await driver.tapElement(vdEl);
await sleep(1500);
const waterEl = await driver.findElementRaw('name', 'Watermark');
if (waterEl) {
const switches = await driver.findElementsRaw('class name', 'XCUIElementTypeSwitch');
if (switches.length > 0) {
await driver.tapElement(switches[0]);
await sleep(5000);
await driver.tapElement(switches[0]);
await sleep(3000);
}
}
reporter.record('水印', 'PASS', Date.now() - start, 'Watermark切换完成');
} catch (e: any) {
reporter.record('水印', 'FAIL', Date.now() - start, e.message);
throw e;
}
});
// ==================== 翻转 ====================
it('设置页-打开/关闭翻转', async () => {
const start = Date.now();
try {
await enterSettingPage();
await scrollToAndTap('Basic Settings');
await sleep(1500);
const vdEl = await driver.findElementRaw('name', 'Video Display');
if (!vdEl) {
reporter.record('翻转', 'SKIP', Date.now() - start, '未找到Video Display');
return;
}
await driver.tapElement(vdEl);
await sleep(1500);
const flipEl = await driver.findElementRaw('name', 'Flip');
if (flipEl) {
const switches = await driver.findElementsRaw('class name', 'XCUIElementTypeSwitch');
if (switches.length > 0) {
const lastSw = switches[switches.length - 1];
await driver.tapElement(lastSw);
await sleep(5000);
await driver.tapElement(lastSw);
await sleep(3000);
}
}
reporter.record('翻转', 'PASS', Date.now() - start, 'Flip切换完成');
} catch (e: any) {
reporter.record('翻转', 'FAIL', Date.now() - start, e.message);
throw e;
}
});
// ==================== 抗闪烁 ====================
it('设置页-切换抗闪烁为50Hz', async () => {
const start = Date.now();
try {
await enterSettingPage();
await scrollToAndTap('Basic Settings');
await sleep(1500);
const vdEl = await driver.findElementRaw('name', 'Video Display');
if (vdEl) {
await driver.tapElement(vdEl);
await sleep(1500);
}
const flickerEl = await driver.findElementRaw('name', 'Anti-Flicker');
if (!flickerEl) {
reporter.record('抗闪烁-50Hz', 'SKIP', Date.now() - start, '当前设备不支持Anti-Flicker');
return;
}
await driver.tapElement(flickerEl);
await sleep(1500);
const hz50El = await driver.findElementRaw('name', '50Hz');
expect(hz50El).not.toBeNull();
await driver.tapElement(hz50El!);
await sleep(5000);
reporter.record('抗闪烁-50Hz', 'PASS', Date.now() - start, 'Anti-Flicker=50Hz');
} catch (e: any) {
reporter.record('抗闪烁-50Hz', 'FAIL', Date.now() - start, e.message);
throw e;
}
});
it('设置页-切换抗闪烁为60Hz', async () => {
const start = Date.now();
try {
await enterSettingPage();
await scrollToAndTap('Basic Settings');
await sleep(1500);
const vdEl = await driver.findElementRaw('name', 'Video Display');
if (vdEl) {
await driver.tapElement(vdEl);
await sleep(1500);
}
const flickerEl = await driver.findElementRaw('name', 'Anti-Flicker');
if (!flickerEl) {
reporter.record('抗闪烁-60Hz', 'SKIP', Date.now() - start, '当前设备不支持Anti-Flicker');
return;
}
await driver.tapElement(flickerEl);
await sleep(1500);
const hz60El = await driver.findElementRaw('name', '60Hz');
expect(hz60El).not.toBeNull();
await driver.tapElement(hz60El!);
await sleep(5000);
reporter.record('抗闪烁-60Hz', 'PASS', Date.now() - start, 'Anti-Flicker=60Hz');
} catch (e: any) {
reporter.record('抗闪烁-60Hz', 'FAIL', Date.now() - start, e.message);
throw e;
}
});
// ==================== NFC ====================
it('设置页-NFC页面显示', async () => {
const start = Date.now();
try {
await enterSettingPage();
const found = await scrollToAndTap('NFC');
if (!found) {
reporter.record('NFC页面', 'SKIP', Date.now() - start, '未找到NFC选项');
return;
}
await sleep(3000);
const source = await driver.getSource();
const hasNfc = source.includes('NFC') || source.includes('Tag');
console.log('NFC页面:', hasNfc);
reporter.record('NFC页面', 'PASS', Date.now() - start, `NFC页面加载=${hasNfc}`);
} catch (e: any) {
reporter.record('NFC页面', 'FAIL', Date.now() - start, e.message);
throw e;
}
});
// ==================== 巡航 ====================
it('设置页-巡航添加', async () => {
const start = Date.now();
try {
await enterSettingPage();
const found = await scrollToAndTap('Patrols');
if (!found) {
reporter.record('巡航添加', 'SKIP', Date.now() - start, '当前设备不支持Patrols');
return;
}
await sleep(2000);
// Enable patrol if off
const switches = await driver.findElementsRaw('class name', 'XCUIElementTypeSwitch');
if (switches.length > 0) {
const val = await driver.getElementAttribute(switches[0], 'value');
if (val === '0') {
await driver.tapElement(switches[0]);
await sleep(3000);
}
}
// Schedule
const scheduleEl = await driver.findElementRaw('name', 'Schedule');
if (scheduleEl) await driver.tapElement(scheduleEl);
await sleep(1500);
// Add once
const addEl = await driver.findElementRaw('name', 'Add');
if (addEl) {
await driver.tapElement(addEl);
await sleep(1500);
const saveEl = await driver.findElementRaw('name', 'Save');
if (saveEl) await driver.tapElement(saveEl);
await sleep(3000);
}
let source = await driver.getSource();
const hasOnce = source.includes('Once') || source.includes('Only once');
console.log('Only once添加:', hasOnce);
// Add repeat
const addEl2 = await driver.findElementRaw('name', 'Add');
if (addEl2) {
await driver.tapElement(addEl2);
await sleep(1500);
const repeatEl = await driver.findElementRaw('name', 'Repeat');
if (repeatEl) await driver.tapElement(repeatEl);
await sleep(500);
const saveEl2 = await driver.findElementRaw('name', 'Save');
if (saveEl2) await driver.tapElement(saveEl2);
await sleep(3000);
}
source = await driver.getSource();
const hasRepeat = source.includes('Every day') || source.includes('Repeat');
console.log('Repeat添加:', hasRepeat);
reporter.record('巡航添加', 'PASS', Date.now() - start, `Once=${hasOnce}, Repeat=${hasRepeat}`);
} catch (e: any) {
reporter.record('巡航添加', 'FAIL', Date.now() - start, e.message);
throw e;
}
});
it('设置页-巡航删除', async () => {
const start = Date.now();
try {
await enterSettingPage();
const found = await scrollToAndTap('Patrols');
if (!found) {
reporter.record('巡航删除', 'SKIP', Date.now() - start, '当前设备不支持Patrols');
return;
}
await sleep(2000);
const scheduleEl = await driver.findElementRaw('name', 'Schedule');
if (scheduleEl) await driver.tapElement(scheduleEl);
await sleep(1500);
// Delete button
const moreEl = await driver.findElementRaw('name', 'More');
if (moreEl) {
await driver.tapElement(moreEl);
await sleep(1000);
}
const deleteEl = await driver.findElementRaw('name', 'Delete');
if (deleteEl) {
await driver.tapElement(deleteEl);
await sleep(1500);
// Select and delete
const cells = await driver.findElementsRaw('class name', 'XCUIElementTypeCell');
if (cells.length > 0) await driver.tapElement(cells[0]);
await sleep(500);
const del2 = await driver.findElementRaw('name', 'Delete');
if (del2) await driver.tapElement(del2);
await sleep(1000);
const confirmEl = await driver.findElementRaw('name', 'Confirm');
if (confirmEl) await driver.tapElement(confirmEl);
await sleep(3000);
}
reporter.record('巡航删除', 'PASS', Date.now() - start, '巡航计划删除完成');
} catch (e: any) {
reporter.record('巡航删除', 'FAIL', Date.now() - start, e.message);
throw e;
}
});
it('设置页-巡航全选删除', async () => {
const start = Date.now();
try {
await enterSettingPage();
const found = await scrollToAndTap('Patrols');
if (!found) {
reporter.record('巡航全选删除', 'SKIP', Date.now() - start, '当前设备不支持Patrols');
return;
}
await sleep(2000);
const scheduleEl = await driver.findElementRaw('name', 'Schedule');
if (scheduleEl) await driver.tapElement(scheduleEl);
await sleep(1500);
// Add one first if empty
const addEl = await driver.findElementRaw('name', 'Add');
if (addEl) {
await driver.tapElement(addEl);
await sleep(1500);
const saveEl = await driver.findElementRaw('name', 'Save');
if (saveEl) await driver.tapElement(saveEl);
await sleep(2000);
}
// Delete all
const moreEl = await driver.findElementRaw('name', 'More');
if (moreEl) {
await driver.tapElement(moreEl);
await sleep(1000);
}
const deleteEl = await driver.findElementRaw('name', 'Delete');
if (deleteEl) {
await driver.tapElement(deleteEl);
await sleep(1500);
const selectAllEl = await driver.findElementRaw('name', 'Select All');
if (selectAllEl) await driver.tapElement(selectAllEl);
await sleep(500);
const del2 = await driver.findElementRaw('name', 'Delete');
if (del2) await driver.tapElement(del2);
await sleep(1000);
const confirmEl = await driver.findElementRaw('name', 'Confirm');
if (confirmEl) await driver.tapElement(confirmEl);
await sleep(3000);
}
// Verify empty
const source = await driver.getSource();
const isEmpty = source.includes('No schedule') || source.includes('Add') || !source.includes('Every day');
console.log('巡航清空:', isEmpty);
// Turn off patrol
await driver.goBack();
await sleep(1000);
const switches = await driver.findElementsRaw('class name', 'XCUIElementTypeSwitch');
if (switches.length > 0) {
const val = await driver.getElementAttribute(switches[0], 'value');
if (val === '1') {
await driver.tapElement(switches[0]);
await sleep(3000);
}
}
reporter.record('巡航全选删除', 'PASS', Date.now() - start, `全选删除完成, 清空=${isEmpty}`);
} catch (e: any) {
reporter.record('巡航全选删除', 'FAIL', Date.now() - start, e.message);
throw e;
}
});
// ==================== 预设点设置 ====================
it('设置页-预设点添加', async () => {
const start = Date.now();
try {
await enterSettingPage();
const found = await scrollToAndTap('Preset Point Settings');
if (!found) {
reporter.record('预设点设置', 'SKIP', Date.now() - start, '当前设备不支持Preset Point Settings');
return;
}
await sleep(2000);
// Select point 1
const point1El = await driver.findElementRaw('name', '1');
if (point1El) await driver.tapElement(point1El);
await sleep(3000);
// Move direction
const dirEl = await driver.findElementRaw('name', 'Up') || await driver.findElementRaw('name', 'Left');
if (dirEl) {
await driver.tapElement(dirEl);
await sleep(2000);
}
// Save
const saveEl = await driver.findElementRaw('name', 'Save');
if (saveEl) await driver.tapElement(saveEl);
await sleep(3000);
reporter.record('预设点设置', 'PASS', Date.now() - start, '预设点设置完成');
} catch (e: any) {
reporter.record('预设点设置', 'FAIL', Date.now() - start, e.message);
throw e;
}
});
// ==================== 隐私模式设置 ====================
it('设置页-打开/关闭隐私模式', async () => {
const start = Date.now();
try {
await enterSettingPage();
const found = await scrollToAndTap('Privacy Mode');
if (!found) throw new Error('未找到Privacy Mode');
await sleep(3000);
// Verify privacy on
let source = await driver.getSource();
const privacyOn = source.includes('Privacy') || source.includes('turned off') || source.includes('Turn on');
console.log('隐私模式开启:', privacyOn);
// Go back
await driver.goBack();
await sleep(1500);
// Re-enter settings and turn off privacy
const settingsEl = await driver.findElementRaw('name', 'Settings');
if (settingsEl) {
await driver.tapElement(settingsEl);
await sleep(2000);
await scrollToAndTap('Privacy Mode');
await sleep(3000);
}
// Verify camera restored
await driver.goBack();
await sleep(2000);
source = await driver.getSource();
const restored = !source.includes('Privacy Mode is on');
reporter.record('隐私模式设置', 'PASS', Date.now() - start, `开启=${privacyOn}, 关闭恢复=${restored}`);
} catch (e: any) {
reporter.record('隐私模式设置', 'FAIL', Date.now() - start, e.message);
throw e;
}
});
// ==================== 再次打开侦测开关 ====================
it('设置页-再次打开侦测开关', async () => {
const start = Date.now();
try {
await enterSettingPage();
await scrollToAndTap('Motion Detection');
await sleep(2000);
const switches = await driver.findElementsRaw('class name', 'XCUIElementTypeSwitch');
if (switches.length > 0) {
const val = await driver.getElementAttribute(switches[0], 'value');
if (val === '0') {
await driver.tapElement(switches[0]);
await sleep(5000);
}
const afterVal = await driver.getElementAttribute(switches[0], 'value');
console.log('侦测开关状态:', afterVal === '1' ? '开' : '关');
}
reporter.record('再次打开侦测', 'PASS', Date.now() - start, 'Motion Detection确保开启');
} catch (e: any) {
reporter.record('再次打开侦测', 'FAIL', Date.now() - start, e.message);
throw e;
}
});
});