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