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 } from '../../utils/common'; import * as dotenv from 'dotenv'; import * as path from 'path'; dotenv.config({ path: path.resolve(__dirname, '../../.env') }); // 摄像头录屏/回放(普通功能用例,非 P0)。manifest 摄像头控制 P0 只有 15974「出流」,无单列录屏 step → 不挂 P0/ONES。 // 流程:进功能页 → 底部 Playback tab → 校验三区块(Cloud Videos / microSD Card Videos / Recordings and Screenshots) // → 进 Recordings and Screenshots(本地录屏+截图画廊) → 校验进入(All Types 筛选/标题)。 // 全用真实文本节点定位。⚠️ 事件/录屏列表为空属当前设备问题(无 SD 卡/未触发录制),不判用例失败——只校验页面可达。 // 与已保留的 iOS 骨架 camera_recording.test.ts 并存(那份后续单独调 iOS/相册删除等细分动作)。 interface CamCfg { name: string; } const CAMERAS: CamCfg[] = [ { name: 'Pan/Tilt Cam 2K oy' }, ]; const ONLY = process.env.CAMERA_NAME || ''; const TARGETS = ONLY ? CAMERAS.filter((c) => c.name.includes(ONLY)) : CAMERAS; describe('Camera Record/Playback - 摄像头录屏回放(Playback)', () => { let driver: DeviceDriver; let reporter: TestReporter; beforeAll(async () => { driver = createDriver(); await driver.createSession(); reporter = new TestReporter('Camera_Recording', driver.platform.toUpperCase()); }); afterAll(async () => { reporter.generate(); await driver.destroySession(); }); beforeEach(async () => { await driver.dismissPopupIfPresent(); await driver.goBackToHomepage(); await driver.dismissPopupIfPresent(); }); async function enterCamera(name: string): Promise { for (let i = 0; i < 8; i++) { const el = await driver.findElementRaw('-android uiautomator', `new UiSelector().textContains("${name}")`).catch(() => null); if (el) { const r = await driver.getElementRect(el); await driver.tap(r.x + Math.min(r.width / 2, 120), Math.max(r.y - 40, r.y + 20)); await sleep(7000); return true; } await driver.scrollDown(500); await sleep(800); } return false; } const tapText = async (t: string): Promise => { const el = await driver.findElementRaw('-android uiautomator', `new UiSelector().textContains("${t}")`).catch(() => null); if (el) { await driver.tapElement(el); return true; } return false; }; const srcHas = async (t: string): Promise => (await driver.getSource()).includes(t); for (const cam of TARGETS) { it(`[Camera] ${cam.name} 录屏回放(Playback)`, { timeout: 120000 }, async () => { const start = Date.now(); const label = `录屏回放${cam.name}`; try { if (!(await enterCamera(cam.name))) throw new Error(`首页找不到摄像头卡片 "${cam.name}"`); if (!(await tapText('Playback'))) throw new Error('功能页底部无 Playback tab'); await sleep(4000); const hasMicroSD = await srcHas('microSD Card Videos'); const hasRec = await srcHas('Recordings and Screenshots'); const hasCloud = await srcHas('Cloud Videos'); if (!hasRec || !hasMicroSD) { const ss = await driver.screenshot().catch(() => ''); reporter.record(label, 'FAIL', Date.now() - start, `Playback 页区块缺失(microSD=${hasMicroSD} Recordings=${hasRec} Cloud=${hasCloud})`, ss); throw new Error('未进入 Playback 页'); } console.log(`${cam.name} Playback 就绪: Cloud=${hasCloud} microSD=${hasMicroSD} Recordings=${hasRec}`); if (!(await tapText('Recordings and Screenshots'))) throw new Error('点不到 Recordings and Screenshots'); await sleep(4000); const inGallery = (await srcHas('All Types')) || (await srcHas('Recordings and Screenshots')); if (!inGallery) { const ss = await driver.screenshot().catch(() => ''); reporter.record(label, 'FAIL', Date.now() - start, '未进入录屏/截图画廊页', ss); throw new Error('未进入画廊页'); } const elapsed = ((Date.now() - start) / 1000).toFixed(1); reporter.record(label, 'PASS', Date.now() - start, `Playback 三区块齐+进入录屏截图画廊(列表空=设备无录制,非用例失败), 耗时${elapsed}s`); expect(inGallery).toBe(true); } catch (e: any) { const ss = await driver.screenshot().catch(() => ''); reporter.record(label, 'FAIL', Date.now() - start, e.message, ss); throw e; } }); } });