# 通过 SoloPi Desktop 控制 Android、iOS 和 NEXT 桌面左侧的 **三端设备控制** 使用同仓库的 SoloPi Harness。可以发现设备、查看整屏截图、选择真实节点、点击或输入、记录固定断言,并将操作导出为可编辑的回归用例,再调用 Harness 回放。 ## 支持范围 | 平台 | 连接方式 | 设备前提 | 当前边界 | | --- | --- | --- | --- | | Android | ADB + SoloPi Agent 会话 | 支持 Agent 协议的 SoloPi,辅助功能和内部 ADB 就绪 | 新入口不绕过观察校验;原有 Android 投屏和用例库保留 | | iOS | Harness WDA 驱动 | 已启动且与设备匹配的 WDA;真机需签名、信任和本机 USB 转发 | 已验证原生测试 App 及支付宝的特定只读流程;返回需可定位的原生节点,无通用系统返回键 | | HarmonyOS NEXT | Harness HDC / uitest 驱动 | 已启动的 NEXT 模拟器,HDC 可用 | 当前不开放 NEXT 真机;输入仅支持空字段和受限字符 | 三端使用同一个桌面入口和固定验收语义,不代表 Android 所有工具均已移植。新入口是节点控制和按需截图,不是三端实时视频投屏;无可定位节点的 Canvas 不支持直接坐标点击。模型 API 与 Laya 不参与手动录制和固定回放。 桌面入口已在 Android 和 iPhone 真机上完成录制与固定回放验收,包括两端真实支付宝的手机营业厅只读流程。NEXT 桌面入口也已在模拟器受控原生 App 完成录制、固定回放和错误预期对照;它不是 NEXT 真机或鸿蒙版支付宝验证。 本次 Android 实测在 Pixel 8 与虚构订单 Demo 上完成。通过桌面操作录制了“进入订单列表 → 打开详情 → 返回场景页”,并手动设置“已支付”和 `SCENARIOS` 两个固定检查点;正向回放通过,把第二个预期单独改为 `WRONG` 后,回放被判为 `failed / oracle_mismatch`。验收过程中辅助功能曾被关闭,该轮明确记为 `not_tested`,未计入断言对照。输入、长按、滚动等入口已接线,但本轮不把它们算作真机已验收能力。 最终重新录制的用例未作手工编辑,直接从桌面回放通过,报告区间为 **17.877 秒**;错误断言对照为 **18.087 秒**并按预期失败。该时间包含 Harness 执行与证据采集,不包含桌面启动、人工录制和 HTML 导出;单轮数据不代表稳定性或性能提升。另用仅含等待动作的用例验证桌面停止,设备实际返回 `cancelled`、`active=false`,取消身份与本次请求一致。 真实支付宝另完成了“首页 → 手机营业厅 → 返回首页”的桌面固定回放:检查营业厅标题及两档面额区域,返回后检查首页标识,全程未提交交易。相同用例三轮通过,报告时间为 **31.938 / 32.100 / 38.842 秒**;故意写错标题预期的一轮得到 `failed / oracle_mismatch`,耗时 **32.756 秒**,其未到达检查点为 `not_tested`。四轮必要清理均通过。这份用例由已确认录制动作整理,补入 `description` 断言、显式等待及清理,不是未编辑的连续录制直接导出。小样本仅证明这些轮次的功能结果。 真实 App 仍有明确边界:尝试由“我的”进入设置时,首页内容卡片与底部导航在页面树中重叠,动作被保守拒绝,设置流程尚未完成。营业厅的全局返回曾因整页动画被拒绝;选用已校验的原生返回节点后,设备回执确认整页变化而目标上下文保持一致,并执行成功。该记录不能代表所有 App 或其他两端都已验证。 iPhone 15 Plus 上的真实支付宝也已从桌面完成“首页 → 手机营业厅 → 返回首页”的连续录制,固定检查标题、50 元和 100 元面额区域,以及首页“扫一扫”。回放版本将返回和首页检查移入必要清理,并显式等待加载 4 秒、返回 2 秒;相同冻结用例三轮通过,报告执行区间为 **52.439 / 57.831 / 53.863 秒**,中位数 **53.863 秒**。错误标题预期对照为 **53.763 秒**,正确返回 `failed / oracle_mismatch`,两项面额检查仍通过。四轮业务清理均 `passed`,各自 WDA 会话关闭均 `succeeded`。 这条 iOS 流程使用实际观察到的 `accessibilityId`:“手机营业厅”进入业务页,导航栏“首页”按钮返回,未调用任意坐标或 WebView DOM。面额断言检查完整无障碍文案,与 Android 包含判断的严格度不同。两端对齐的是业务目标与验收语义,不是同一组选择器;这些小样本没有证明速度提升。耗时为报告 `finishedAt - startedAt`,包含执行、截图和业务清理,不包含首次 WDA 构建、人工录制、报告结束后的会话关闭及 HTML 导出。测试未提交充值、支付、修改设置或调用模型 API;其他支付宝流程、任意第三方 App 和真实断线接管仍需分别验证。 NEXT 模拟器受控 App 的桌面流程为“进入详情 → 检查标题 → 滚动显示 Row 5 → 必要清理返回主页”。连续录制导出后,将滚动距离从 60% 明确改为 80%,并把返回和主页检查移入清理;冻结用例三轮通过,报告执行区间为 **29.563 / 29.560 / 29.579 秒**,中位数 **29.563 秒**。只把标题预期改为 `WRONG` 的一轮为 **29.563 秒**,正确判定 `failed / oracle_mismatch`;Row 5 检查及返回主页清理仍通过,四轮驱动关闭均成功。截图为 1316 × 2832 完整原图。不同应用和设备的耗时不用于跨平台性能比较。 实测保留两次修复前录制中断和一次 60% 滚动失败(28.607 秒,只到 Row 4)。HDC 子进程现在使用独立标准输入,避免修改桌面 JSONL 管道状态导致误判 EOF;NEXT 选择器优先稳定 `id`,忽略临时无障碍编号。这些结果不代表复杂输入、NEXT 真机、任意第三方 App 或断线接管已验收。 ## 启动 需要 Node.js、Python 3.9+,以及所选平台的设备工具。使用完整仓库目录: ```text SoloPi/ solopi-app/ solopi-skill/scripts/harness/src/solopi_harness/ solopi-desktop/ ``` ```bash cd solopi-desktop npm ci npm run dev ``` 界面通过 Electron 主进程启动私有 Python 子进程。源码运行使用相邻的 `solopi-skill`;打包配置将 Harness 源码和资源复制到应用资源目录。Python 和 ADB / WDA / HDC 仍是外部依赖,当前未制作或验收新的安装包。 若系统默认 `python3` 不合适,可在启动桌面前通过 `SOLOPI_HARNESS_PYTHON` 指定可信解释器。不要只复制 `solopi-desktop/` 后期待三端入口可用。独立桌面源码快照自带的旧 APK,也不能当作已验证支持新 Agent 协议的 APK;使用当前仓库的配套构建并先跑 `doctor`。 ## 首次连接 1. 在左侧打开“三端设备控制”,点击“刷新设备”。 2. 选择平台及已发现设备;填入被测 App 的包名或 Bundle ID。界面也允许手动输入设备 ID。 3. 展开“连接配置与环境要求”,核对平台参数。 4. 点击“连接并录制”。应用启动后返回整屏原图和唯一可定位的节点列表。 Android 默认控制包为 `com.alipay.hulu`、端口 `23342`。如果使用另装的测试副本,应填写该副本的真实包名和端口;例如 `com.alipay.hulu.fastpath` 与 `23344`。端口身份不符会拒绝连接,不会自动切到另一份 App。 Android 的“录制时的点击校验”默认选择“整页一致”。存在轮播或动画时,可显式选择“目标与相关区域一致”,对应已有 `target-context-v1` 能力。连接时要求设备声明并确认支持;只有符合条件的原生点击能够使用这一策略,仍复核目标身份、位置、子树、相关上下文和窗口。普通返回动作仍要求整页一致;有稳定原生返回按钮时,应选择该真实节点进行点击。实际生效策略以动作回执的 `actionValidation.effectiveStrategy` 为准,不能只看配置。该选项仅作用于交互录制,不改变固定回放的既有执行链路。 Android 首次预检失败时,使用 [Skill 故障处理](../../solopi-skill/references/troubleshooting.md) 恢复连接。电脑端模式不强制悬浮窗和电池优化白名单。桌面不会代替用户授予系统权限。 iOS / NEXT 的工具安装、运行器启动和能力限制见 [原生回归使用指南](../../solopi-skill/references/native-regression-guide.md)。桌面显示发现设备不代表运行器已经就绪;连接时会再校验设备身份与驱动状态。 ## 录制一个能回放的用例 以仓库内的虚构订单 Demo 为例,被测包名是 `com.solopi.fastpath.demo`: 1. 从场景选择页连接,选择“标准订单流程”节点,点击“点击节点”。 2. 新页面返回后选择“查看订单详情”,再点击“点击节点”。 3. 选择订单状态节点,在“预期文字”中**手动输入** `已支付`,点击“添加文字相等断言”。 4. 选择“返回场景选择”并点击;选择页面状态节点,手动输入 `SCENARIOS`,再添加断言。 5. 点击“结束录制并生成用例”。下方出现 requirement JSON,可修改并下载。 6. 确认手机处于同样的初始场景,点击“固定回放”。完成后打开“证据目录”,查看 `result/` 中的报告、原图与耗时。 受控 Demo 的构建方式见 [Demo README](../../solopi-skill/scripts/harness/examples/fastpath/android-demo/README.md)。示例为虚构订单,不包含真实交易,也不需要外部模型服务。 其他操作包括长按、向选定节点输入、选定滚动区域后滚动、返回和等待。动作可用性由驱动决定。输入不是跨平台任意键盘注入,尤其应遵守 NEXT 输入限制。 每次动作后都重新观察。节点列表中的选择会清空,下一次必须基于当前页面重新选择。一个动作需要明确回执;传输中断后不自动重发。截图用于预览,不能凭截图猜坐标下发操作。 设备明确返回 `rejected` 时,桌面保留此前的录制步骤,展示拒绝原因并重新观察。使用者需重新选择节点或操作,系统不会自动重发;每次新动作使用新的步骤 ID。`failed` 和 `outcome_unknown` 仍中断录制,不能把传输中断当成“肯定没有点击”。被拒绝的动作保留在 `recording.json`,不进入导出的成功操作步骤。 真实页面有些文字存放在无障碍 `description` 字段。当前“添加文字相等断言”检查的是 `text`;此类页面需要在可编辑用例中显式编写 `field: description` 的固定检查点,再编译与回放。它不会根据实际结果自动改写预期。 导出时,每个非等待动作后加入显式的 `1200 ms` 等待,检查点放在等待之后,避免固定回放比交互录制更快地读取旧页面。等待步骤出现在 JSON 中,可以根据被测 App 修改;它不是所有 App 都适用的页面稳定保证。首次 Android 验证保留了未加入等待时的失败记录,未通过放宽断言来消除失败。 ## 回放与结果 - 需要至少一个动作和一个显式断言才能结束录制。 - 录制时断言失败仍保留用户填写的预期,不会用实际值覆盖它。 - 回放要求用例平台、应用标识与设备配置一致;只允许固定 operation 步骤,不从此入口调用远程决策模型。 - `passed` 表示固定检查点通过;`failed` 表示已确认结果不符;`not_tested` 表示没有足够证据完成判定。IPC 请求完成不等于测试通过。 - 回放是有副作用的设备操作。开始前恢复初态,或在用例中定义重置与清理;桌面不猜测如何重置任意业务 App。 - 点击“断开 / 停止”会结束本次控制进程。Android 固定回放按本进程创建的请求与用例身份查找运行,再按准确 `runId` 取消;不会取消另一任务。设备断连等情况下,清理可能无法确认,需查看 `desktop-cleanup.json` 并检查手机状态。未结束的录制保留为 `interrupted`,不会自动续跑。 - 连接占用桌面调度器中的设备租约,与原有任务提交互斥。其他进程或外部工具仍需由使用者协调,不能声称它们都受桌面租约管理。 ## 证据位置与实现 证据写到桌面用户数据目录下的 `harness-control/`,每次录制或回放新建随机目录。记录包括 `recording.json`、整屏 PNG 和导出的 `requirement.json`;回放保留编译计划、独立判定报告及耗时。Android 普通固定回放也会生成 `result/report.html`,可直接浏览完整截图;HTML 导出失败不改变 JSON 报告里的验收结果。失败和中断记录不会覆盖旧结果。 这些内容只保存在本地,不自动上传。通过“下载用例”导出 JSON,通过“证据目录”打开本次生成目录。该入口未并入原有“测试任务”列表;两者的用例格式也不同,不能把 requirement JSON 直接当作旧工作流 JSON 导入。 ```text 桌面 React 页面 → 受信任主窗口 IPC(操作白名单) → Electron:设备租约、受控目录、私有 stdio → Harness DesktopControl:节点句柄、录制、计划编译 → Android Agent / iOS WDA / NEXT HDC → 独立 Result Judge + 完整证据 ``` 桌面不接收任意 Shell、任意网络端点或任意证据读取路径。动作选择器在 Harness 内从当前观察恢复;渲染页面仅传当前 observationId 与节点句柄。 ## 集成来源 桌面基础导入自 `solopi-desktop` 分支提交 `4c58fa2` 的 `solopi-desktop/` 目录。该分支是独立源码快照,没有与当前分支共同的 Git 祖先,因此只导入桌面模块,未覆盖现有 Android 和 Skill 代码,也未合入另一工作树中的本地文件。 `PUBLIC_SOURCE_MANIFEST.json` 列出当前桌面模块的公开文件与哈希,并保留上游快照提交。它不包含相邻目录中的 Harness,也不代表已发布新的安装包。