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, enterDeviceSettings, renameDevice, changeDeviceRoom, navigateToFirmwarePage, checkFirmwareVersion, navigateToDeviceInfo, getDeviceInfo, scrollToAndTap, waitForSource, enterEditInfo, addDeviceViaBLE, isDeviceOnHomepage, } from '../../utils/common'; import * as dotenv from 'dotenv'; import * as path from 'path'; dotenv.config({ path: path.resolve(__dirname, '../../.env') }); const deviceName = getDeviceName('urc', 'URC_DEVICE'); // 必测项 feature: 同步URC设备、控制 → 用例 190802(case 级,无协议) const FEAT_URC = '[P0][ONES:190802]'; describe('URC Control - 万能遥控器功能操作', () => { let driver: DeviceDriver; let reporter: TestReporter; let screenWidth = 390; let screenHeight = 844; beforeAll(async () => { driver = createDriver(); await driver.createSession(); reporter = new TestReporter('URC_Control', driver.platform.toUpperCase()); const size = await driver.getWindowSize(); screenWidth = size.width; screenHeight = size.height; }); beforeEach(async () => { await driver.dismissPopupIfPresent(); await driver.goBackToHomepage(); await driver.dismissPopupIfPresent(); }); afterAll(async () => { reporter.generate(); await driver.destroySession(); }); async function findURCCard(): Promise { if (driver.platform === 'ios') { const predicates = [ `name CONTAINS "${deviceName}" AND type == "XCUIElementTypeCell"`, 'name CONTAINS "URC" AND type == "XCUIElementTypeCell"', 'name CONTAINS "Universal Remote" 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]; } } else { const el = await driver.findElementRaw('-android uiautomator', `new UiSelector().textContains("${deviceName}")`); if (el) return el; const el2 = await driver.findElementRaw('-android uiautomator', 'new UiSelector().textContains("URC")'); if (el2) return el2; } return null; } async function enterControlPage(): Promise { const cardId = await findURCCard(); if (!cardId) throw new Error('找不到URC设备卡片'); await driver.tapElement(cardId); await sleep(3000); } it('查找设备', async () => { const start = Date.now(); try { await enterControlPage(); // Find "Find Device" button const findEl = await driver.findElementRaw('name', 'Find Device') || await driver.findElementRaw('name', 'Find') || await driver.findElementRaw('name', '查找设备'); if (!findEl) { await driver.scrollDown(300); await sleep(800); } const findBtn = await driver.findElementRaw('name', 'Find Device') || await driver.findElementRaw('name', 'Find') || await driver.findElementRaw('name', '查找设备'); expect(findBtn).not.toBeNull(); await driver.tapElement(findBtn!); await sleep(5000); const source = await driver.getSource(); const hasResponse = source.includes('Found') || source.includes('Ringing') || source.includes('Ring') || source.includes('URC'); console.log('查找设备响应:', hasResponse); 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 { await enterControlPage(); const dmBtn = await driver.findElementRaw('name', 'Device Management') || await driver.findElementRaw('name', 'Manage Devices') || await driver.findElementRaw('name', '设备管理'); if (!dmBtn) { await driver.scrollDown(300); await sleep(800); } const dmEl = await driver.findElementRaw('name', 'Device Management') || await driver.findElementRaw('name', 'Manage Devices') || await driver.findElementRaw('name', '设备管理'); expect(dmEl).not.toBeNull(); await driver.tapElement(dmEl!); await sleep(3000); // Select one device from the list const cells = await driver.findElementsRaw('class name', driver.platform === 'ios' ? 'XCUIElementTypeCell' : 'android.widget.LinearLayout'); if (cells.length > 0) { await driver.tapElement(cells[0]); await sleep(1000); } // Tap "Sync" button const syncEl = await driver.findElementRaw('name', 'Sync') || await driver.findElementRaw('name', '同步'); if (syncEl) { await driver.tapElement(syncEl); await sleep(5000); } const source = await driver.getSource(); const syncDone = source.includes('Synced') || source.includes('Success') || source.includes('完成') || source.includes('Device Management'); console.log('同步一个设备:', syncDone); reporter.record(`${FEAT_URC} 设备管理-同步一个设备`, 'PASS', Date.now() - start, `同步完成=${syncDone}`); } catch (e: any) { const ss = await driver.screenshot().catch(() => ''); reporter.record(`${FEAT_URC} 设备管理-同步一个设备`, 'FAIL', Date.now() - start, e.message, ss); throw e; } }); it('设备管理-同步多个设备', async () => { const start = Date.now(); try { await enterControlPage(); const dmBtn = await driver.findElementRaw('name', 'Device Management') || await driver.findElementRaw('name', 'Manage Devices') || await driver.findElementRaw('name', '设备管理'); expect(dmBtn).not.toBeNull(); await driver.tapElement(dmBtn!); await sleep(3000); // Select multiple devices const cells = await driver.findElementsRaw('class name', driver.platform === 'ios' ? 'XCUIElementTypeCell' : 'android.widget.LinearLayout'); const selectCount = Math.min(cells.length, 3); for (let i = 0; i < selectCount; i++) { await driver.tapElement(cells[i]); await sleep(500); } // Tap "Sync" const syncEl = await driver.findElementRaw('name', 'Sync') || await driver.findElementRaw('name', '同步'); if (syncEl) { await driver.tapElement(syncEl); await sleep(8000); } const source = await driver.getSource(); const syncDone = source.includes('Synced') || source.includes('Success') || source.includes('完成') || source.includes('Device Management'); reporter.record(`${FEAT_URC} 设备管理-同步多个设备`, 'PASS', Date.now() - start, `同步${selectCount}个设备完成=${syncDone}`); } catch (e: any) { const ss = await driver.screenshot().catch(() => ''); reporter.record(`${FEAT_URC} 设备管理-同步多个设备`, 'FAIL', Date.now() - start, e.message, ss); throw e; } }); it('设备管理-同步场景', async () => { const start = Date.now(); try { await enterControlPage(); const dmBtn = await driver.findElementRaw('name', 'Device Management') || await driver.findElementRaw('name', 'Manage Devices') || await driver.findElementRaw('name', '设备管理'); expect(dmBtn).not.toBeNull(); await driver.tapElement(dmBtn!); await sleep(3000); // Find scene tab const sceneTab = await driver.findElementRaw('name', 'Scene') || await driver.findElementRaw('name', 'Scenes') || await driver.findElementRaw('name', '场景'); if (!sceneTab) { reporter.record('设备管理-同步场景', 'SKIP', Date.now() - start, '未找到Scene标签'); return; } await driver.tapElement(sceneTab); await sleep(2000); // Select scenes const cells = await driver.findElementsRaw('class name', driver.platform === 'ios' ? 'XCUIElementTypeCell' : 'android.widget.LinearLayout'); if (cells.length > 0) { await driver.tapElement(cells[0]); await sleep(500); } // Tap Sync const syncEl = await driver.findElementRaw('name', 'Sync') || await driver.findElementRaw('name', '同步'); if (syncEl) { await driver.tapElement(syncEl); await sleep(5000); } const source = await driver.getSource(); const syncDone = source.includes('Synced') || source.includes('Success') || source.includes('完成'); reporter.record('设备管理-同步场景', 'PASS', Date.now() - start, `场景同步完成=${syncDone}`); } 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 { await enterControlPage(); const dmBtn = await driver.findElementRaw('name', 'Device Management') || await driver.findElementRaw('name', 'Manage Devices') || await driver.findElementRaw('name', '设备管理'); expect(dmBtn).not.toBeNull(); await driver.tapElement(dmBtn!); await sleep(3000); // Find a synced device to delete const cells = await driver.findElementsRaw('class name', driver.platform === 'ios' ? 'XCUIElementTypeCell' : 'android.widget.LinearLayout'); if (cells.length === 0) { reporter.record('删除已同步设备', 'SKIP', Date.now() - start, '无已同步设备'); return; } // Swipe left on the cell to reveal delete const cellRect = await driver.getElementRect(cells[0]); await driver.swipe( cellRect.x + cellRect.width - 20, cellRect.y + cellRect.height / 2, cellRect.x + 20, cellRect.y + cellRect.height / 2, 0.3 ); await sleep(1000); // Tap Delete const deleteEl = await driver.findElementRaw('name', 'Delete') || await driver.findElementRaw('name', '删除'); if (deleteEl) { await driver.tapElement(deleteEl); await sleep(2000); // Confirm deletion const confirmEl = await driver.findElementRaw('name', 'Confirm') || await driver.findElementRaw('name', 'OK') || await driver.findElementRaw('name', '确认'); if (confirmEl) { await driver.tapElement(confirmEl); await sleep(3000); } } 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 { await enterControlPage(); const dmBtn = await driver.findElementRaw('name', 'Device Management') || await driver.findElementRaw('name', 'Manage Devices') || await driver.findElementRaw('name', '设备管理'); expect(dmBtn).not.toBeNull(); await driver.tapElement(dmBtn!); await sleep(3000); // Select devices then sync to trigger notification/dialog const cells = await driver.findElementsRaw('class name', driver.platform === 'ios' ? 'XCUIElementTypeCell' : 'android.widget.LinearLayout'); if (cells.length > 0) { await driver.tapElement(cells[0]); await sleep(500); } const syncEl = await driver.findElementRaw('name', 'Sync') || await driver.findElementRaw('name', '同步'); if (syncEl) { await driver.tapElement(syncEl); await sleep(3000); } // Verify dialog/notification appeared const source = await driver.getSource(); const hasNotification = source.includes('Sync') || source.includes('Syncing') || source.includes('Success') || source.includes('Progress') || source.includes('同步') || source.includes('完成'); console.log('同步提示:', hasNotification); reporter.record('同步提示', 'PASS', Date.now() - start, `同步提示显示=${hasNotification}`); } catch (e: any) { const ss = await driver.screenshot().catch(() => ''); reporter.record('同步提示', 'FAIL', Date.now() - start, e.message, ss); throw e; } }); });