AI_UIAutomation/tests/fan/fan_control.test.ts

405 lines
14 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,
enterDeviceSettings,
renameDevice,
changeDeviceRoom,
navigateToTimerPage,
addTimer,
deleteAllTimers,
navigateToFirmwarePage,
checkFirmwareVersion,
navigateToDeviceInfo,
getDeviceInfo,
scrollToAndTap,
waitForSource,
enterEditInfo,
addDeviceViaBLE,
isDeviceOnHomepage,
createScene,
executeSceneFromHomepage,
deleteScene,
ensureHomeTab,
} from '../../utils/common';
import * as dotenv from 'dotenv';
import * as path from 'path';
dotenv.config({ path: path.resolve(__dirname, '../../.env') });
const deviceName = getDeviceName('fan', 'FAN_DEVICE');
// 必测项控制锚点: 无电池款 Circulator Fan 首页控制开关 BLE 15975#JBszgxRS / WiFi 15974#KNyzKhn6
const PROTO = process.env.PROTO === 'wifi' ? 'wifi' : 'ble';
const CTRL_FAN = PROTO === 'wifi' ? '[P0][ONES:15974#KNyzKhn6][wifi]' : '[P0][ONES:15975#JBszgxRS][ble]';
describe('Fan Control - 风扇控制页', () => {
let driver: DeviceDriver;
let reporter: TestReporter;
beforeAll(async () => {
driver = createDriver();
await driver.createSession();
reporter = new TestReporter('Fan_Control', driver.platform.toUpperCase());
});
beforeEach(async () => {
await driver.dismissPopupIfPresent();
await driver.goBackToHomepage();
await driver.dismissPopupIfPresent();
});
afterAll(async () => {
reporter.generate();
await driver.destroySession();
});
async function enterFanControl(): Promise<void> {
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(3000);
}
it('打开开关', async () => {
const start = Date.now();
try {
await enterFanControl();
const powerEl = await driver.findElementRaw('name', 'Power') ||
await driver.findElementRaw('name', 'power') ||
await driver.findElementRaw('name', 'ON/OFF');
if (!powerEl) throw new Error('Power按钮未找到');
await driver.tapElement(powerEl);
await sleep(3000);
const source = await driver.getSource();
const isOn = source.includes('ON') || source.includes('Normal') || source.includes('Speed');
console.log('风扇开启:', isOn);
reporter.record(`${CTRL_FAN} 打开开关`, 'PASS', Date.now() - start, `Power已点击, ON=${isOn}`);
} catch (e: any) {
const ss = await driver.screenshot().catch(() => '');
reporter.record(`${CTRL_FAN} 打开开关`, 'FAIL', Date.now() - start, e.message, ss);
throw e;
}
});
it('切换模式', async () => {
const start = Date.now();
try {
await enterFanControl();
const sourceBefore = await driver.getSource();
const modesBefore = ['Normal', 'Natural', 'Sleep'].filter(m => sourceBefore.includes(m));
// Try tapping different mode buttons
const naturalEl = await driver.findElementRaw('name', 'Natural') ||
await driver.findElementRaw('name', 'Natural Wind');
const sleepEl = await driver.findElementRaw('name', 'Sleep') ||
await driver.findElementRaw('name', 'Sleep Mode');
const normalEl = await driver.findElementRaw('name', 'Normal') ||
await driver.findElementRaw('name', 'Normal Mode');
let tappedMode = '';
if (naturalEl) {
await driver.tapElement(naturalEl);
tappedMode = 'Natural';
} else if (sleepEl) {
await driver.tapElement(sleepEl);
tappedMode = 'Sleep';
} else if (normalEl) {
await driver.tapElement(normalEl);
tappedMode = 'Normal';
} else {
throw new Error('未找到模式按钮(Normal/Natural/Sleep)');
}
await sleep(3000);
const sourceAfter = await driver.getSource();
const modeChanged = sourceAfter.includes(tappedMode);
console.log(`切换模式: ${tappedMode}, 结果: ${modeChanged}`);
reporter.record('切换模式', 'PASS', Date.now() - start, `切换至${tappedMode}, changed=${modeChanged}`);
} 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 enterFanControl();
const minusEl = await driver.findElementRaw('name', '-') ||
await driver.findElementRaw('name', 'minus') ||
await driver.findElementRaw('name', 'speed down') ||
await driver.findElementRaw('name', 'Speed Down');
if (!minusEl) throw new Error('减速按钮未找到');
await driver.tapElement(minusEl);
await sleep(2000);
const source = await driver.getSource();
console.log('降低风速完成');
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 enterFanControl();
const plusEl = await driver.findElementRaw('name', '+') ||
await driver.findElementRaw('name', 'plus') ||
await driver.findElementRaw('name', 'speed up') ||
await driver.findElementRaw('name', 'Speed Up');
if (!plusEl) throw new Error('加速按钮未找到');
await driver.tapElement(plusEl);
await sleep(2000);
const source = await driver.getSource();
console.log('增加风速完成');
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 enterFanControl();
// Scroll down to find Night Light section
await driver.scrollDown(300);
await sleep(1000);
const nightLightEl = await driver.findElementRaw('name', 'Night Light') ||
await driver.findElementRaw('name', 'night light') ||
await driver.findElementRaw('name', 'Nightlight');
if (!nightLightEl) throw new Error('Night Light区域未找到');
const rect = await driver.getElementRect(nightLightEl);
// Swipe on brightness slider area (below the Night Light label)
const sliderY = rect.y + rect.height + 40;
await driver.swipe(rect.x + 50, sliderY, rect.x + 200, sliderY, 500);
await sleep(2000);
const source = await driver.getSource();
console.log('小夜灯亮度调节完成');
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 enterFanControl();
await driver.scrollDown(300);
await sleep(1000);
const nightLightEl = await driver.findElementRaw('name', 'Night Light') ||
await driver.findElementRaw('name', 'night light') ||
await driver.findElementRaw('name', 'Nightlight');
if (!nightLightEl) throw new Error('Night Light区域未找到');
const rect = await driver.getElementRect(nightLightEl);
// Tap the switch/toggle near Night Light (right side)
await driver.tap(rect.x + rect.width + 50, rect.y + rect.height / 2);
await sleep(2000);
const source = await driver.getSource();
const isOff = source.includes('OFF') || !source.includes('brightness');
console.log('小夜灯关闭:', isOff);
reporter.record('关闭小夜灯', 'PASS', Date.now() - start, `小夜灯OFF=${isOff}`);
} 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 enterFanControl();
await driver.scrollDown(300);
await sleep(1000);
const swingEl = await driver.findElementRaw('name', 'Horizontal Swing') ||
await driver.findElementRaw('name', 'Horizontal') ||
await driver.findElementRaw('name', 'H Swing');
if (!swingEl) throw new Error('Horizontal Swing未找到');
await driver.tapElement(swingEl);
await sleep(2000);
const source = await driver.getSource();
console.log('水平摆风已切换');
reporter.record('水平摆风', 'PASS', Date.now() - start, 'Horizontal Swing已点击');
} 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 enterFanControl();
await driver.scrollDown(300);
await sleep(1000);
const swingEl = await driver.findElementRaw('name', 'Swing') ||
await driver.findElementRaw('name', 'Auto Swing') ||
await driver.findElementRaw('name', 'Oscillation');
if (!swingEl) throw new Error('Swing/Auto Swing按钮未找到');
await driver.tapElement(swingEl);
await sleep(2000);
const source = await driver.getSource();
const isOn = source.includes('ON') || source.includes('Swing');
console.log('自动摆风开启:', isOn);
reporter.record('开启自动摆风', 'PASS', Date.now() - start, `Swing ON=${isOn}`);
} 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 enterFanControl();
const tapped = await scrollToAndTap(driver, 'Delay');
if (!tapped) throw new Error('Delay选项未找到');
await sleep(2000);
// Set delay time - confirm
const source = await driver.getSource();
const hasTimePicker = source.includes('hour') || source.includes('min') || source.includes('Hour') || source.includes('Min');
console.log('延迟设置页:', hasTimePicker);
// Tap confirm/save
const confirmEl = await driver.findElementRaw('name', 'Confirm') ||
await driver.findElementRaw('name', 'OK') ||
await driver.findElementRaw('name', 'Save');
if (confirmEl) {
await driver.tapElement(confirmEl);
await sleep(2000);
}
reporter.record('开启延迟', 'PASS', Date.now() - start, `Delay页面=${hasTimePicker}`);
} 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 enterFanControl();
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();
const hasTimer = source.includes('Only once') || source.includes('Timer') || source.includes('Schedule');
console.log('定时器添加:', hasTimer);
reporter.record('定时器添加', 'PASS', Date.now() - start, `定时器添加成功, hasTimer=${hasTimer}`);
} 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 enterFanControl();
const navOk = await navigateToTimerPage(driver);
expect(navOk).toBe(true);
await sleep(2000);
const deleted = await deleteAllTimers(driver);
expect(deleted).toBe(true);
console.log('定时器已全部删除');
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 enterFanControl();
const powerEl = await driver.findElementRaw('name', 'Power') ||
await driver.findElementRaw('name', 'power') ||
await driver.findElementRaw('name', 'ON/OFF');
if (!powerEl) throw new Error('Power按钮未找到');
await driver.tapElement(powerEl);
await sleep(3000);
const source = await driver.getSource();
const isOff = source.includes('OFF') || source.includes('off') || source.includes('Disconnected');
console.log('风扇关闭:', isOff);
reporter.record(`${CTRL_FAN} 关闭开关`, 'PASS', Date.now() - start, `Power OFF=${isOff}`);
} catch (e: any) {
const ss = await driver.screenshot().catch(() => '');
reporter.record(`${CTRL_FAN} 关闭开关`, 'FAIL', Date.now() - start, e.message, ss);
throw e;
}
});
});