124 lines
5.3 KiB
TypeScript
124 lines
5.3 KiB
TypeScript
import { describe, it, beforeAll, afterAll, expect } from 'vitest';
|
|
import { DeviceDriver } from '../../drivers/types';
|
|
import { createDriver } from '../../drivers/factory';
|
|
import { TestReporter } from '../../utils/test-reporter';
|
|
import { sleep, onesCtrl } from '../../utils/common';
|
|
import { setupResilientHooks } from "../../utils/common";
|
|
import * as dotenv from 'dotenv';
|
|
import * as path from 'path';
|
|
|
|
dotenv.config({ path: path.resolve(__dirname, '../../.env') });
|
|
|
|
// Plug Mini 控制(ONES step12 2jSFfY4k):首页卡片开关钮直接开/关,卡名下方 desText 显示 On/Off。
|
|
// 用品类前缀 "Plug Mini" 匹配:删后重加卡名后缀/区域会变,textContains 前缀仍命中首个 Plug Mini 卡,状态按坐标读不依赖全名。
|
|
const deviceName = process.env.PLUG_CTRL_DEVICE || 'Plug Mini';
|
|
const CTRL_PLUG = onesCtrl('plug', 'PlugMini 5E'); // 15975#2jSFfY4k
|
|
|
|
describe('Plug Mini Control - 首页卡片开关', () => {
|
|
let driver: DeviceDriver;
|
|
let reporter: TestReporter;
|
|
setupResilientHooks(() => driver);
|
|
|
|
beforeAll(async () => {
|
|
driver = createDriver();
|
|
await driver.createSession();
|
|
reporter = new TestReporter('PlugMini_Control', driver.platform.toUpperCase());
|
|
});
|
|
|
|
beforeEach(async () => {
|
|
await driver.dismissPopupIfPresent();
|
|
await driver.goBackToHomepage();
|
|
await sleep(500);
|
|
});
|
|
|
|
afterAll(async () => {
|
|
reporter.generate();
|
|
await driver.destroySession();
|
|
});
|
|
|
|
// 找 plug 卡片:状态 = 卡名正下方同列的 "On"/"Off";开关钮在卡名右上方。
|
|
// RN 首页 UiScrollable.scrollIntoView 对部分卡片失效 → 手动滚动 + 从 getSource 解析卡名 bounds(滚到时卡片在视口下方,开关钮 y-95 自然不落顶栏)。
|
|
function findCardRect(src: string): { x: number; y: number; w: number } | null {
|
|
for (const n of src.split('<')) {
|
|
if (!/resource-id="[^"]*\/nameText"/.test(n)) continue;
|
|
const t = (n.match(/text="([^"]*)"/) || [])[1];
|
|
if (!t || !t.includes(deviceName)) continue;
|
|
const b = n.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
|
|
if (b) return { x: +b[1], y: +b[2], w: +b[3] - +b[1] };
|
|
}
|
|
return null;
|
|
}
|
|
async function plugInfo(): Promise<{ state: 'On' | 'Off' | 'unknown'; btnX: number; btnY: number; nameX: number; nameY: number } | null> {
|
|
await driver.goBackToHomepage(); await sleep(800); await driver.dismissPopupIfPresent();
|
|
let rect: { x: number; y: number; w: number } | null = null;
|
|
for (let i = 0; i < 8 && !rect; i++) {
|
|
rect = findCardRect(await driver.getSource());
|
|
if (!rect) { await driver.scrollDown(500); await sleep(700); }
|
|
}
|
|
if (!rect) return null;
|
|
// 开关钮 y-95 若落进顶栏(~y248)会误触 +/... → 卡片太靠顶时下滑一屏让它下移再读
|
|
if (rect.y - 95 < 300) {
|
|
await driver.scrollDown(400); await sleep(800);
|
|
const r2 = findCardRect(await driver.getSource());
|
|
if (r2) rect = r2;
|
|
}
|
|
const state = readPlugState(await driver.getSource(), rect.x, rect.y);
|
|
return { state, btnX: rect.x + rect.w - 48, btnY: rect.y - 95, nameX: rect.x, nameY: rect.y };
|
|
}
|
|
|
|
function readPlugState(src: string, nameX: number, nameY: number): 'On' | 'Off' | 'unknown' {
|
|
for (const n of src.split('<')) {
|
|
const t = (n.match(/text="(On|Off)"/) || [])[1] as 'On' | 'Off' | undefined;
|
|
const b = n.match(/bounds="\[(\d+),(\d+)\]/);
|
|
if (t && b && Math.abs(+b[1] - nameX) < 200 && +b[2] > nameY && +b[2] < nameY + 150) return t;
|
|
}
|
|
return 'unknown';
|
|
}
|
|
|
|
async function controlTo(target: 'On' | 'Off'): Promise<'On' | 'Off' | 'unknown'> {
|
|
const info = await plugInfo();
|
|
if (!info) throw new Error(`首页找不到${deviceName}卡片`);
|
|
if (info.state === 'unknown') throw new Error(`读不到${deviceName}开关状态`);
|
|
if (info.state === target) return target;
|
|
await driver.tap(info.btnX, info.btnY); // 点开关钮
|
|
let st = info.state;
|
|
for (let i = 0; i < 8 && st !== target; i++) {
|
|
await sleep(2000);
|
|
st = (await plugInfo())?.state ?? 'unknown';
|
|
}
|
|
return st;
|
|
}
|
|
|
|
it(`${CTRL_PLUG} 关闭开关`, { timeout: 90000 }, async () => {
|
|
const start = Date.now();
|
|
try {
|
|
const before = (await plugInfo())?.state;
|
|
expect(before).not.toBe('unknown');
|
|
const after = await controlTo('Off');
|
|
console.log(`关闭: ${before} → ${after}`);
|
|
expect(after).toBe('Off'); // 硬断言:已关
|
|
reporter.record(`${CTRL_PLUG} 关闭开关`, 'PASS', Date.now() - start, `状态=${after}`);
|
|
} catch (e: any) {
|
|
const ss = await driver.screenshot().catch(() => '');
|
|
reporter.record(`${CTRL_PLUG} 关闭开关`, 'FAIL', Date.now() - start, e.message, ss);
|
|
throw e;
|
|
}
|
|
});
|
|
|
|
it(`${CTRL_PLUG} 打开开关`, { timeout: 90000 }, async () => {
|
|
const start = Date.now();
|
|
try {
|
|
const before = (await plugInfo())?.state;
|
|
expect(before).not.toBe('unknown');
|
|
const after = await controlTo('On');
|
|
console.log(`打开: ${before} → ${after}`);
|
|
expect(after).toBe('On'); // 硬断言:已开
|
|
reporter.record(`${CTRL_PLUG} 打开开关`, 'PASS', Date.now() - start, `状态=${after}`);
|
|
} catch (e: any) {
|
|
const ss = await driver.screenshot().catch(() => '');
|
|
reporter.record(`${CTRL_PLUG} 打开开关`, 'FAIL', Date.now() - start, e.message, ss);
|
|
throw e;
|
|
}
|
|
});
|
|
});
|