aitestuirunner
v2.0.1
Published
H5QA runner daemon: executes AI-driven UI test cases (Midscene / Playwright) on web and Android, reporting results back to the H5QA platform.
Maintainers
Readme
H5QA Runner · aitestuirunner
在本地或云端执行 H5QA 平台的 AI UI 测试用例(基于
Midscene / Playwright),并把执行结果回传平台。本包发布到 npm 后提供 aitestui 命令行工具。
- npm 包:
aitestuirunner - CLI 名:
aitestui - 文档/发布页:https://www.npmjs.com/package/aitestuirunner
30 秒上手(推荐)
路线一:从平台页面复制一行命令(最省事,ADR-0041)
打开平台「项目配置 → 本地测试机」,页面顶部已按你的访问地址生成好完整命令, 复制 → 粘到终端 → 回车即可:
npx aitestuirunner join --token <项目 Runner Token>join 一条龙做完四件事:① 写 .env;② 下载 Chromium;③ doctor 自检;④ 常驻启动。
全程无需交互(缺参时若为终端环境才提问)。首次下载 Chromium 较慢,之后这台机器直接跑:
npx aitestuirunner start路线二:init 逐步向导
想一项一项填、或不方便复制长命令时用:
npx aitestuirunner init # 交互填 url / TOKEN / 测试机名称,自动安装 Chromium 并自检环境
aitestuirunner start # 启动本地测试机,开始领取并执行任务或先全局安装,后续用更短的 aitestui:
npm install -g aitestuirunner
aitestui join --token <项目 Runner Token>
# 之后每天只需:
aitestui startjoin / init 都会做三件事:① 收集最小配置并写入 .env;② 自动 install-browser
下载 Chromium;③ 跑 doctor 自检环境。区别只在配置从哪来——join 从命令行参数来
(平台页面已拼好),init 逐项提问。填错的 Token 可随时重跑,或手动编辑 .env
(见 附录 · 全部环境变量)。
命令
aitestui [command],不传子命令默认执行 start:
| 命令 | 说明 |
|---|---|
| aitestui join --url <api> --token <token> | 一行接入(写 .env + 装 Chromium + 自检 + 启动)。平台页面生成的就是它,首次使用首选。 |
| aitestui init | 逐步向导:交互式填 url / TOKEN / 机器名,随后装 Chromium 并自检。 |
| aitestui / aitestui start | 启动 Runner 守护进程(默认)。已接入过的机器每天只用这一条。 |
| aitestui install-browser | 下载 Playwright 使用的 Chromium 二进制(首次部署执行一次)。 |
| aitestui doctor | 自检 Node.js、Chromium 与后端连通性;安卓端还会检查 adb 与在线设备。 |
| aitestui help | 显示命令帮助(--help / -h 同)。 |
| aitestui version | 显示当前版本号(--version / -v 同)。 |
从源码开发时请用仓库内等价脚本:
pnpm dev(=start)、pnpm install-browser、pnpm doctor、pnpm cloud:*等。上面的aitestui用法面向已通过 npm 安装的使用者。
join 的退出码
| 退出码 | 含义 | 下一步 |
|---|---|---|
| 0 | 已启动测试机(常驻进程,不会自己退出) | Ctrl+C 停止 |
| 1 | 参数缺失或无法识别 | 回平台页面重新复制命令 |
| 2 | .env 已写好,但自检未过(后端不可达 / Token 无效) | 修好后直接 aitestui start,不用重新接入 |
| 3 | .env 已写好,但 Chromium 装不上 | 换镜像后重跑:H5QA_CHROMIUM_MIRROR=https://npmmirror.com/mirrors/playwright aitestui join ... |
前置条件:Node 18+
npx 依赖本机 Node(18+,推荐 20 LTS)。没装 Node 时终端会直接报
command not found: npx,Runner 此刻还没被启动,没法自己提示你——
平台页面会按你的操作系统给出安装命令(含国内镜像)。离线/内网环境请先配好 npm 镜像,
否则 npx 拉不到包。
最小配置:只有 TOKEN 必填
绝大多数使用者只需关心一项,init 已经帮你写好了:
| 变量 | 必填 | 说明 |
|---|---|---|
| TOKEN | 是 | Runner Token(本地用项目共享 Token;云用一次性云 Token)。 |
| url | 否 | 后端 API 基址,默认 https://aitestui.com/api(官方站);私有化部署必须显式设置。 |
其余变量都有合理默认值,一般不需要动。想看全部可选项见 附录。
配置可写在运行目录的 .env(H5QA_ENV_FILE 可指定其它路径;文件缺失静默跳过),
已导出的环境变量优先于 .env。
连接与鉴权
Runner 通过最小权限 Token 访问 /api/v1/runner/*。本地 Runner 用项目共享 Token;
云 Runner 用管理员创建、仅显示一次的云 Runner Token。模型调用默认走服务端代理,
Runner 不持有任何厂商 Key(详见 附录 · 模型代理/直连)。
附录:进阶配置
全部环境变量
日常只需要 TOKEN 一个;url 有默认值(官方站),私有化部署才必须显式设置。其余项都有合理默认,绝大多数机器用不到。
| 变量 | 必填 | 默认值 | 说明 |
|---|---|---|---|
| TOKEN | 是 | — | 项目 Runner Token。 |
| url | 否 | https://aitestui.com/api | 后端 API 基址。私有化部署必须显式设置,否则会连到官方站。容错:只写域名会自动补 /api,末尾误带的 /v1 会自动去掉。 |
| H5QA_RUNNER_NAME | 否 | 主机名 | 人类可读的测试机名称。 |
| H5QA_HEADFUL | 否 | false | 本地调试时显示浏览器窗口;云测试机必须保持 false。 |
| H5QA_RUNNER_DEBUG | 否 | false | 开启本地全量调试日志(仅 stdout/日志文件,不上行)。 |
| H5QA_CHROMIUM_MIRROR | 否 | npmmirror | 覆盖 Chromium 下载主机。 |
| H5QA_MIDSCENE_CACHE | 否 | true | 设 0/false/no 强制本机禁用用例缓存。只能把「服务端说开」压成「本机不开」,不能反向打开。 |
| ~~H5QA_CREDENTIAL_KEY~~ | 已下线(2.0.0 起) | — | ADR-0010 E17:claim 里的保密变量改由本机 Runner Token 现算派生密钥解锁,本机不再持有任何静态密钥。配了也不读取,无需与部署侧同步。 |
| H5QA_TARGET_TYPE | 否 | web | 执行端:web 或 android。 |
| H5QA_ANDROID_DEVICE_ID | 否 | 第一台在线设备 | 安卓设备 serial。 |
| H5QA_ENV_FILE | 否 | <cwd>/.env | 指定要加载的 .env 文件路径。 |
| H5QA_RUNNER_MAX_MEMORY | 否 | 2G | 仅限云测试机的 PM2 守护(ecosystem.config.cjs):内存超限重启阈值。Runner 进程本身不读它。 |
浏览器来源:优先复用本机标准路径下的 Chrome(macOS 的 /Applications/Google Chrome.app、
Linux 的 /usr/bin/google-chrome),找不到才回退 Playwright 自带的 Chromium。这里刻意不可配——
手动指到某个自定义 Chrome 后,它会在某次自动升级时与 Playwright 的协议不再兼容,症状是一批用例
毫无征兆地集体失败,而那时没人会想起配过这个变量。
逐步执行的步数上限固定为 30 步,不可在本机调(见「自然语言(prompt)用例的执行模式」一节)。
不接受本机覆盖的原因:这是一个成本上限而不是偏好,由测试机单方面放宽等于让被计费的一方
自己决定这个用例能烧多少模型积分;真要放宽,应改服务端 executionTimeoutSeconds 或由平台下发。
调试模式
在 .env 设 H5QA_RUNNER_DEBUG=true:每个 /v1/runner/* 请求打印脱敏请求/响应摘要,
关键节点(领取、YAML 落盘、导航、回放、上报、OSS 上传)打印日志,输出走 stdout 并 tee 到
~/.h5qa-runner/logs/runner-YYYY-MM-DD.log,不进入上行日志队列。
自然语言(prompt)用例的执行模式
用例格式为 prompt 时,Runner 用 Midscene JS SDK 逐步执行:每步只让模型给一个动作指令并立即执行,
把结果与截图回喂模型,步骤随页面变化调整。有可用缓存脚本时仍按 YAML 回放;无缓存才进入逐步执行。
失败步骤回喂原因换走法,连续失败 3 次判失败;跑通后把实际步骤反汇编成 YAML 回写缓存。
单步上限固定 30 步(不提供本机开关,理由见上方变量表注释)。Debug Run 同样逐步执行,每步前等控制命令。
模型代理与直连(平台侧 ADR-0022)
Runner 不持有任何厂商 API Key。走哪条路由服务端在 claim 时裁定,本机没有任何开关能单方面改变:
| 模式 | 触发条件 | Runner 侧配置 | 计费用量来源 | |---|---|---|---| | 代理模式(默认) | 平台付费模型;或自带 Key 且项目开着「安全代理」 | 无需任何配置 | 服务端权威计数(可熔断) | | 直连模式 | 自带 Key,且项目关掉了「安全代理」 | 无需任何配置(Key 由服务端随 claim 下发) | runner 自报 |
代理模式:服务端随 claim 下发 attempt 级短票据,Runner 注入为 MIDSCENE_MODEL_BASE_URL /
MIDSCENE_MODEL_API_KEY,模型请求全部经后端转发。平台付费模型恒定走代理。
直连仅对「服务端够不到的内网/私有 Endpoint」的自带 Key 项目可能获批。
为什么本机没有开关:让测试机单方面决定要不要走代理,等于让被计费的一方自己决定是否被计费。 是否直连挂在项目的模型配置上(模型配置 → 安全代理开关),Runner 只负责执行服务端的裁定。
安卓执行(平台侧 ADR-0026 一期)
执行目标扩展到安卓真机/模拟器(驱动层 @midscene/android)。前置:本机装好 adb,设备 adb devices
状态为 device。用 H5QA_TARGET_TYPE=android 启动即可(H5QA_ANDROID_DEVICE_ID 缺省取第一台在线设备)。
端判定顺序:用例 YAML android 段 > 项目测试机类型 > 本机 H5QA_TARGET_TYPE > 网页。
ios/computer/harmony 段与非法 H5QA_TARGET_TYPE 会明确报 unsupported_target,不会静默拿 Chromium 跑。
安卓端暂不具备网页端的部分能力(回放录像、视觉基线比对、DOM 阻断检测、locate 缓存),日志会说明,不静默降级。
交互式 Debug
Debug Run 被领取后打开目标页并暂停,等待运行报告页发命令:「下一步」只跑当前步;
「运行剩余步骤」连续跑遇失败暂停;「重试当前失败步骤」在超时内可重复;「取消」持久化取消状态。
同一 Debug Run 复用同一浏览器上下文。云 Runner 恒 headless;本地可 H5QA_HEADFUL=true 看窗口。
云 Runner(Linux + Docker / PM2)
每台云主机只启动一个进程,固定并发一(勿用 cluster/多实例)。模型默认走服务端代理,云机不需要任何模型环境变量。
方式一:Docker(推荐)
docker build -t aitestuirunner .
docker run -d --name h5qa-cloud-runner \
-e url=https://aitestui.com/api \
-e TOKEN=<one-time-cloud-runner-token> \
-e H5QA_RUNNER_NAME=cloud-runner-region-a \
-e H5QA_CHROMIUM_MIRROR=https://npmmirror.com/mirrors/playwright \
aitestuirunner镜像 CMD 即 node dist/index.js start(headless)。Token 轮换后重建/重启容器替换 TOKEN 即可。
改 Dockerfile 时别把 Chromium 下载并回
pnpm build那一层。 Chromium(~184MB)单独成层且排在COPY src之前,改业务代码才不会重下一次浏览器;.dockerignore里的.h5qa-runs也别删—— 它是本地运行产物,漏掉会让每次 build 都打包上传几百 MB 上下文。
方式二:PM2(基于源码仓库部署)
pnpm install --frozen-lockfile && pnpm build && pnpm install-browser
export url=https://aitestui.com/api TOKEN=<one-time-cloud-runner-token> \
H5QA_RUNNER_NAME=cloud-runner-region-a H5QA_CHROMIUM_MIRROR=https://npmmirror.com/mirrors/playwright
pnpm cloud:start && pnpm cloud:save常用运维:pnpm cloud:status / cloud:logs / cloud:restart / cloud:stop / cloud:delete。
Token 轮换后 pnpm cloud:restart(脚本 --update-env 读取新值)。云 Runner 必须 H5QA_HEADFUL=false、HTTPS 访问后端。
裸机 / 容器依赖
Debian/Ubuntu(bookworm)安装 Chromium 系统库与中文字体:
apt-get update && apt-get install -y --no-install-recommends \
libnss3 libnspr4 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 \
libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 \
libgbm1 libpango-1.0-0 libcairo2 libasound2 libatspi2.0-0 \
fonts-noto-cjk fonts-wqy-microhei
fc-cache -f中文字体是硬性依赖(ADR-0027)。容器镜像默认不含任何 CJK 字体,Chromium 会把每个汉字 都渲染成同一块「豆腐块」□□□——截图看着像乱码,视觉模型读到的只有等宽方框,用例必然全挂。 而本机 macOS / Windows 自带中文字体,所以这个故障只在云 runner 上复现,排查时容易误判成 页面编码或模型侧问题。
aitestui doctor会显式跑一次中文渲染探针并报出结论。 若已装过字体仍报不通过,检查 fontconfig 缓存(执行fc-cache -f)。
docker build(基于 node:22-bookworm)已含上述依赖;裸机请自行保证。安卓端另需本机可用 adb。
源码与发布(保护说明,ADR-0002)
发布的 npm 包只含 3 个文件:压缩后的 dist/index.js(单文件、已 minify)、README.md、
ecosystem.config.cjs。src/ 从不进包;构建产物不带 .d.ts(不暴露内部 API 面);
源码映射(.map)留在构建机/私有位置,不随 npm 发布,仅供团队内部解栈排障。
因此从包里只能看到混淆后的执行逻辑,看不到可读源码与类型定义。
