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 { 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 { 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 { 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; } }); });