For AI agents: the complete documentation index is available at https://a3s-lab.github.io/Test/v1.0.1/llms.txt, the full documentation bundle is available at https://a3s-lab.github.io/Test/v1.0.1/llms-full.txt, and this page is available as Markdown at https://a3s-lab.github.io/Test/v1.0.1/guide/actions-and-evidence.md.

动作、等待与证据

A3S Test 不把测试步骤当作一段可自由执行的浏览器脚本。每一步都是一个封闭的类型化动作。静态准入和会话策略在派发前执行,选定的驱动再检查自身能力并解析目标,之后才允许平台输入。驱动返回结构化结果和证据,Runner 才决定步骤状态。

这套约束解决三个实际问题。

  • Agent 只能提出协议允许的意图,不能把任意代码送进页面执行。
  • 失败会归属到规范、定位、产品断言、驱动或清理,调用方不会只得到一句“点击失败”。
  • 本地探索、ACL 回归和 MCP 会话可以复用同一份动作语义。

当前动作协议修订为 15。机器可读契约始终以本机输出为准。

a3s-test agent schema
a3s-test capabilities --json

一步动作如何完成

一次典型的 Web 动作会经过下面的过程。

ACL 或 Agent 提议
    -> Action JSON Schema
    -> 当前 surface 能力
    -> origin、网络与文件策略
    -> 当前 observation 和目标解析
    -> surface 驱动与平台输入
    -> StepOutput 和 Evidence
    -> 下一次观察捕获可能产生的新页面修订

静态 ACL 在 surface 打开前完成语法和属性准入。持久 Agent 会话还会检查动作是否位于会话的允许清单中。使用 @eN@cN@gN.M@vN 的动作会额外绑定最新观察。任何改变 DOM、路由、焦点、标签页、frame 或视口的步骤之后,都应重新观察。

先选稳定目标

目标决定测试是否能够跨页面修订重复执行。推荐顺序如下。

目标写法适用场景稳定性与边界
role("button", "Save")有正确可访问名称的控件首选,按用户可感知语义定位,可穿透开放 Shadow DOM
label("Email")表单控件绑定实际标签语义
testid("checkout")产品提供稳定测试标识语义不足时使用,标识应由产品维护
placeholder("Search")placeholder 稳定且唯一的输入框不应代替缺失的可访问标签
text("Saved", true)可见文案本身就是身份第二个参数控制精确匹配
css("[data-row]")集合、几何、浏览器协议或精确结构目标保留当前 document 语义,不穿透 Shadow DOM
ref("@e4")刚观察到的浏览器元素只在生成它的最新 observation 中有效
Page Context @cNTest Kit 暴露的当前可操作组件节点只属于最新观察;修订漂移时必须有精确 delta 证明私有节点 ID 未变
automation_id("save-button")GUI 辅助功能树中的稳定自动化 IDGUI 专用
visual_point("@v3", 120, 80)GUI 语义无法识别且已有窗口截图的目标GUI 专用,坐标、图片摘要和 observation 必须同时保持新鲜

@uN 只承担 Test Kit UI 理解证据。它可以把样式、布局、状态和组件信息关联到报告,但不能进入 clickfilldrag 或其他输入动作。

ACL 定位函数和 Action JSON 的判别字段并不完全同名。直接调用 agent act 或 MCP test_act 时,应使用下面的机器形式。

ACL 写法Action JSON target
ref("@e4"){"type":"ref","value":"@e4"}
css("#save"){"type":"css","selector":"#save"}
role("button", "Save"){"type":"role","role":"button","name":"Save"}
text("Saved", true){"type":"text","value":"Saved","exact":true}
automation_id("save-button"){"type":"automation_id","value":"save-button"}
visual_point("@v3", 120, 80){"type":"visual_point","snapshot":"@v3","x":120,"y":80}
testid("checkout"){"type":"test_id","value":"checkout"}
label("Email"){"type":"label","value":"Email"}
placeholder("Search"){"type":"placeholder","value":"Search"}

这里最容易混淆的是 ACL 的 testid() 与 JSON 的 type = "test_id",以及 CSS target 使用 selector 而非 value。当前修订的生成式 Schema 仍以 a3s-test agent schema 为准。

同一个定位器匹配零个或多个节点时,驱动返回缺失或歧义错误。A3S Test 不会取列表第一个元素,也不会把定位失败解释成产品状态。

页面、观察与视口

动作什么时候使用最小 ACL 写法内部行为与输出
navigate打开确定的 HTTP(S) 页面url = "https://example.test"先检查 exact origin,再导航并使旧观察失效
snapshot在 ACL 中显式保存当前语义观察interactive = true生成 bounded observation 和新的 surface 引用
viewport验证响应式断点或设备像素比例width = 390height = 844调整真实视口,推进页面修订,不等同于滚动

完整写法如下。

navigate "checkout" {
    url = "http://127.0.0.1:3000/checkout"
}

snapshot "interactive-controls" {
    interactive = true
}

viewport "mobile" {
    width = 390
    height = 844
    scale = 2
}

Agent 会话使用 agent observe 完成同一类观察。snapshot 是动作协议中的显式动作,observe 是会话循环的一次感知操作。两者都可能推进 observation,不能把先前 ref 当作长期 selector。

指针、键盘与表单

动作用途关键区别或限制
click激活按钮、链接或其他可点击目标Web 的直接 ref 或 CSS 目标会先滚动到可见区域
hover触发 hover 样式、提示层或延迟菜单只移动指针,不隐含点击
focus建立明确的输入和键盘上下文Web 当前协议要求 ref 或 CSS
double_click执行双击语义不应由两个独立 click 猜测替代
context_click触发页面的 contextmenu 语义Web 不读取浏览器原生右键菜单
fill替换输入框中的完整值适合确定性表单输入
type在目标的当前值后输入文本会重新解析并聚焦目标
insert_text在已经建立的光标或选择范围插入文本没有 target,不会重新聚焦或重置选择
checkuncheck设置 checkbox 或 radio 的目标状态非适用控件会关闭失败
select精确选择一个或多个 option 值至少一个值,当前 Web 协议要求 ref 或 CSS
drag从一个目标拖到另一个目标source 与 target 都独立解析并校验
press发送一个按键或组合键使用当前键盘上下文
wheel真实滚动或带 modifier 的滚轮手势delta_y 必填,至少一个 delta 非零,target 可省略

当前 Web adapter 对一组浏览器原生命令只接受最新 ref 或明确 CSS。它们包括 focusdouble_clickcontext_clicktypeuncheckselectdrag 的两端、uploaddownload 和带 target 的 wheel。这些动作不会为 role、label 或 text 自动补一条定位退路。clickhoverfillcheck 才会把已支持的语义目标转换成 adapter 能执行的形式。

focus "title" {
    target = css("#title")
}

type "append-title" {
    target = css("#title")
    value = " plan"
}

insert_text "insert-at-caret" {
    value = " approved"
}

check "terms" {
    target = label("Accept terms")
}

select "status" {
    target = css("#status")
    values = ["review", "published"]
}

drag "reorder" {
    source = css("[data-item='second']")
    target = css("[data-item='first']")
}

press "submit" {
    key = "Enter"
}

wheel "zoom-canvas" {
    target = css(".document-canvas")
    delta_x = 0
    delta_y = -120
    modifiers = ["control"]
}

filltypeinsert_text 看起来相近,实际拥有不同的输入语义。需要把字段变成一个确定值时用 fill。需要验证逐字输入行为时用 type。编辑器已经建立光标和选区时才用 insert_text

标签页、frame 与对话框

这些动作改变浏览器上下文,下一步应重新观察。

tab "list" {
    operation = "list"
}

tab "open-docs" {
    operation = "new"
    url = "https://example.test/docs"
    label = "docs"
}

tab "switch-docs" {
    operation = "switch"
    tab = "docs"
}

frame "payment" {
    target = css("#payment-frame")
}

frame "return-main" {
    target = main()
}

dialog "accept-prompt" {
    operation = "accept"
    text = "Approved"
}

tab 支持 listnewswitchclose。标签页可以用稳定 ID 或用户标签引用。frame 支持主文档、当前 ref 或 CSS frame 目标。dialog 支持 statusacceptdismiss,只有 accept 可以携带 prompt 文本。不存在待处理 dialog 时不会假定操作成功。

用条件同步,不使用固定 sleep

Web 的 wait 每次只接受一个条件。

wait "document-ready" {
    load = "domcontentloaded"
}

wait "network-settled" {
    load = "networkidle"
}

wait "confirmation-copy" {
    text = "Order confirmed"
}

wait "confirmation-route" {
    url = "http://127.0.0.1:3000/orders/42"
}

wait "dialog-visible" {
    visible = role("dialog", "Checkout")
}

wait "dialog-gone" {
    hidden = role("dialog", "Checkout")
}

domcontentloaded 读取当前 document readiness,即使导航在 wait 开始前已经完成也能确定判断。networkidle 使用驱动的有界空闲检测。hidden 由 Runner 每 50 ms 执行一次只读可见性探测,目标已经隐藏或不存在时立即通过。deadline、取消和最多 1,201 次探测共同限制这段等待。

定位器缺失可以证明稳定目标当前没有可见匹配,却不能把 ref 过期当成隐藏。因此 wait hidden 禁止 ref()visual_point()。完整的断言语义见断言与稳定性

上传、下载与网络替身

upload "attachments" {
    target = css("input[type=file]")
    paths = ["tests/fixtures/one.txt", "tests/fixtures/two.txt"]
}

download "report" {
    target = css("[data-download-report]")
    path = "downloads/report.pdf"
}

network_route "empty-users" {
    pattern = "**/api/users"
    body = "{\"users\":[]}"
}

network_route "block-analytics" {
    pattern = "**/analytics"
    abort = true
}

network_unroute "users" {
    pattern = "**/api/users"
}

network_unroute "all" {}

上传路径在发送给浏览器前由 CLI 相对当前工作目录解析。驱动还会应用允许根、数量和大小限制。下载路径始终相对当前 scenario 或 session 的 artifact 根。

每条 network_route 必须在静态 body 和 abort 中选择一种响应模式。规则仍受浏览器网络策略约束,不能借此访问未准入域名。network_unroute 带 pattern 时只移除匹配规则,空 block 移除当前会话创建的全部 route。

只记录需要的证据

动作产物适合回答的问题
screenshotPNG最终界面像什么
accessibility交互树或完整语义树 JSON用户和 Agent 能感知哪些语义
console浏览器 console JSON页面是否产生脚本警告或错误
page_errors未捕获页面错误 JSON页面运行时是否崩溃
har显式窗口内的网络 HAR请求、响应和时序是否符合预期
trace显式窗口内的浏览器 trace多步骤交互为何失败
videoWebM状态变化过程是否需要人工复查
download产品下载文件下载是否真实发生并产生预期文件
har "start-network" {
    operation = "start"
}

trace "start-trace" {
    operation = "start"
}

video "start-video" {
    operation = "start"
    path = "video/checkout.webm"
}

accessibility "semantic-tree" {
    path = "evidence/accessibility.json"
    interactive = false
}

console "console-log" {
    path = "evidence/console.json"
    clear = false
}

page_errors "runtime-errors" {
    path = "evidence/page-errors.json"
    clear = false
}

screenshot "confirmation" {
    path = "screenshots/confirmation.png"
}

har "stop-network" {
    operation = "stop"
    path = "network/checkout.har"
}

trace "stop-trace" {
    operation = "stop"
    path = "traces/checkout.zip"
}

video "stop-video" {
    operation = "stop"
}

HAR 和 trace 在 stop 时指定产物路径。video 在 start 时指定路径,stop 时附加完成后的文件。clear = true 只清空当前驱动维护的 console 或 page-error 缓冲,不修改页面业务状态。

所有证据路径必须是 artifact 根下的相对路径。路径穿越、符号链接、Windows reparse point、非普通文件和根外解析都会关闭失败。旧文件会在新捕获前移除,成功命令不能复用陈旧证据。截图还会经过 1 byte 到 32 MiB 的边界校验。

TUI 专用动作

TUI 只用于已经明确的确定性终端流程,不提供交互式 Agent 会话。

terminal_resize "working-size" {
    columns = 120
    rows = 40
}

terminal_paste "command" {
    text = "open fixtures/report.txt"
}

press "submit" {
    key = "Enter"
}

wait "loaded" {
    regex = "Loaded [0-9]+ lines"
}

terminal_recording "evidence" {
    path = "terminal/editor.vt"
}

terminal_paste 遵守应用当前的 bracketed-paste 模式。terminal_resize 调整 A3S Test 拥有的 PTY 或 ConPTY。terminal_recording 保存有界 VT 记录。TUI wait 只接受 text 或 regex,不会把浏览器 URL、load 或元素可见性条件猜成终端行为。

TUI 明确拒绝带浏览器 target 的 type,文本输入应使用 terminal_paste。通用 viewport 在没有 scale 时也可以把 width 和 height 映射为终端 columns 和 rows。面向终端的套件优先使用 terminal_resize,字段含义更清楚。

在 Agent 会话里执行完整 Action JSON

常用动作有 agent clickagent fillagent press 等紧凑命令。浏览器上下文、网络和证据动作可以直接发送完整 Action JSON。

a3s-test agent act \
  --session checkout \
  --observation 7 \
  --action-json '{"type":"click","target":{"type":"ref","value":"@e3"}}' \
  --json

不依赖 observation 的动作可以省略 --observation。只要 action 中使用当前 ref,就必须携带生成该 ref 的最新 observation ID。未知字段会因严格 Schema 被拒绝。

Surface 能力边界

能力组WebGUITUI
观察交互或完整语义,加可选 Page Context可操作辅助功能语义,window-vision 可附截图viewport、scrollback、cursor 与 VT 状态
常规输入完整浏览器动作集click、double click、context click、fill、type、drag、press、wheelpress 与 terminal paste
浏览器上下文、网络与 Web 证据支持不支持不支持
截图支持支持,受绑定窗口和 artifact 根约束不支持
终端 resize 与 recording不支持不支持支持,viewport 无 scale 时也可调整网格

表格描述的是当前实现范围。部署是否真的可用,还取决于本机 inventory、驱动版本和 GUI 认证。不要因为 Action 类型存在就假定每个 surface 都能执行它。

从错误码判断下一步

错误族通常说明什么应该检查什么
test.spec.*ACL 字段、组合或值未通过静态准入属性路径、唯一条件、稳定目标和边界值
test.session.*会话、observation 或修订不再有效重新观察、会话状态和 browser containment
test.driver.web.*Web 定位、能力、协议、I/O 或证据失败目标唯一性、驱动版本、网络和 artifact 路径
test.driver.gui.*GUI 权限、窗口、引用或 CUA 失败host 认证、应用 PID、窗口绑定和最新截图
test.driver.tui.*终端动作、PTY 或语义状态失败executable、按键、regex、输出边界和进程生命周期
test.assert.*已取得有效观察,但产品状态不匹配expected、actual 和关联证据
cleanup error 字段test.run.cleanup_*test.session.cleanup_*test.agent.cleanup_* 或 driver cleanup code保留原始产品结论,同时检查单独报告的 cleanup 证据

基础设施错误不能证明产品通过或失败。先保留原始错误归属,再根据 retryable 字段决定是否由同一会话重试。完整排查方式见故障排查

推荐的最小证据组合

大多数 Web 回归不需要全程录像。一个可复查且成本适中的组合通常包含以下内容。

  1. 用 typed wait 等待真实 ready 条件。
  2. 用一条或多条 typed expectation 证明结果。
  3. 保存最终 screenshot。
  4. 保存交互 accessibility tree。
  5. 保存 console 和 page errors。
  6. 只有网络或时序问题需要时才开启 HAR、trace 或 video。

这样既保留了 verdict 的结构化依据,也避免用大量无关二进制文件掩盖实际失败信号。