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, navigateToTimerPage, addTimer, deleteAllTimers, enterDeviceSettings, scrollToAndTap, waitForSource, } from '../../utils/common'; import { getDeviceName } from '../../config/device.config'; import * as dotenv from 'dotenv'; import * as path from 'path'; dotenv.config({ path: path.resolve(__dirname, '../../.env') }); const deviceName = getDeviceName('curtain', 'CURTAIN_DEVICE'); // 设备维度动态控制锚点:按当前 CURTAIN_DEVICE + PROTO 解析(Curtain/Curtain3/BlindTilt 各自的 step) import { onesCtrl } from '../../utils/common'; const CTRL_CURTAIN = onesCtrl('curtain', deviceName); describe('Curtain Control - 窗帘控制功能', () => { let driver: DeviceDriver; let reporter: TestReporter; beforeAll(async () => { driver = createDriver(); await driver.createSession(); reporter = new TestReporter('Curtain_Control', driver.platform.toUpperCase()); }); beforeEach(async () => { await driver.dismissPopupIfPresent(); await driver.goBackToHomepage(); await sleep(500); await driver.dismissPopupIfPresent(); }); afterAll(async () => { reporter.generate(); await driver.destroySession(); }); async function enterCurtainControl(): Promise { let el = await driver.findElementRaw('name', deviceName); if (!el) { await driver.scrollDown(250); await sleep(1000); el = await driver.findElementRaw('name', deviceName); } if (!el) throw new Error(`找不到${deviceName}卡片`); await driver.tapElement(el); await sleep(2000); } it('打开窗帘', async () => { const start = Date.now(); try { await enterCurtainControl(); const openBtn = await driver.findElementRaw('name', 'Open'); expect(openBtn).not.toBeNull(); await driver.tapElement(openBtn!); await sleep(5000); const source = await driver.getSource(); const statusChanged = source.includes('Open') || source.includes('100') || source.includes('Opening') || source.includes('Opened'); console.log('打开窗帘状态:', statusChanged); reporter.record(`${CTRL_CURTAIN} 打开窗帘`, 'PASS', Date.now() - start, `Open按钮点击成功, 状态变化=${statusChanged}`); } catch (e: any) { const ss = await driver.screenshot().catch(() => ''); reporter.record(`${CTRL_CURTAIN} 打开窗帘`, 'FAIL', Date.now() - start, e.message, ss); throw e; } }); it('关闭窗帘', async () => { const start = Date.now(); try { await enterCurtainControl(); const closeBtn = await driver.findElementRaw('name', 'Close'); expect(closeBtn).not.toBeNull(); await driver.tapElement(closeBtn!); await sleep(5000); const source = await driver.getSource(); const statusChanged = source.includes('Close') || source.includes('0') || source.includes('Closing') || source.includes('Closed'); console.log('关闭窗帘状态:', statusChanged); reporter.record(`${CTRL_CURTAIN} 关闭窗帘`, 'PASS', Date.now() - start, `Close按钮点击成功, 状态变化=${statusChanged}`); } catch (e: any) { const ss = await driver.screenshot().catch(() => ''); reporter.record(`${CTRL_CURTAIN} 关闭窗帘`, 'FAIL', Date.now() - start, e.message, ss); throw e; } }); it('暂停窗帘', async () => { const start = Date.now(); try { await enterCurtainControl(); // First open to create movement const openBtn = await driver.findElementRaw('name', 'Open'); if (openBtn) { await driver.tapElement(openBtn); await sleep(2000); } // Then pause const pauseBtn = await driver.findElementRaw('name', 'Pause'); expect(pauseBtn).not.toBeNull(); await driver.tapElement(pauseBtn!); await sleep(3000); const source = await driver.getSource(); const paused = source.includes('Pause') || source.includes('Paused') || source.includes('%') || source.includes('Position'); console.log('暂停窗帘状态:', paused); reporter.record('暂停窗帘', 'PASS', Date.now() - start, `Pause按钮点击成功, 暂停=${paused}`); } 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 enterDeviceSettings(driver, deviceName); expect(entered).toBe(true); const navOk = await navigateToTimerPage(driver); expect(navOk).toBe(true); await sleep(2000); const added = await addTimer(driver); expect(added).toBe(true); const source = await driver.getSource(); expect(source).toContain('Only once'); console.log('定时器列表有Only once: true'); reporter.record('定时器-添加', 'PASS', Date.now() - start, '添加Only once定时成功'); } 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 enterDeviceSettings(driver, deviceName); expect(entered).toBe(true); const navOk = await navigateToTimerPage(driver); expect(navOk).toBe(true); await sleep(2000); // Ensure at least one timer exists let source = await driver.getSource(); if (!source.includes('Only once') && !source.includes('Open') && !source.includes('Close')) { await addTimer(driver); } const deleted = await deleteAllTimers(driver); expect(deleted).toBe(true); console.log('定时器已清空: true'); 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 enterCurtainControl(); const delayItem = await driver.findElementRaw('name', 'Delay'); if (!delayItem) { await driver.scrollDown(300); await sleep(1000); } const delayBtn = await driver.findElementRaw('name', 'Delay'); expect(delayBtn).not.toBeNull(); await driver.tapElement(delayBtn!); await sleep(2000); // Swipe time picker to set delay const { width, height } = await driver.getWindowSize(); await driver.swipe(width / 2, height / 2, width / 2, height / 2 - 100, 500); await sleep(1000); // Confirm const confirmBtn = await driver.findElementRaw('name', 'Confirm') || await driver.findElementRaw('name', 'OK') || await driver.findElementRaw('name', 'Save'); if (confirmBtn) { await driver.tapElement(confirmBtn); await sleep(2000); } const source = await driver.getSource(); const hasDelay = source.includes('Delay') || source.includes('min') || source.includes('Cancel'); console.log('延迟开关设置:', hasDelay); reporter.record('延迟开关', 'PASS', Date.now() - start, `延迟设置成功=${hasDelay}`); } 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 enterCurtainControl(); // Scroll down to find Light Sensor await driver.scrollDown(300); await sleep(1000); let lightSensor = await driver.findElementRaw('name', 'Light Sensor'); if (!lightSensor) { await driver.scrollDown(300); await sleep(1000); lightSensor = await driver.findElementRaw('name', 'Light Sensor'); } expect(lightSensor).not.toBeNull(); await driver.tapElement(lightSensor!); await sleep(2000); const source = await driver.getSource(); const isLightSensorPage = source.includes('Light Sensor') || source.includes('Lux') || source.includes('Brightness') || source.includes('Threshold') || source.includes('Enable') || source.includes('When'); expect(isLightSensorPage).toBe(true); const detail = `Light Sensor页面验证=${isLightSensorPage}`; console.log(detail); 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; } }); });