ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

生成式 UI 的交互回归集:输出合法,页面也可能不好用

生成式 UI 的交互回归集:输出合法,页面也可能不好用

卡片 JSON 通过校验,组件也成功渲染,用户却发现刚输入的内容被清空,键盘焦点跳回页面顶部。结构合法只能说明数据符合约定,无法说明任务做得完。

AI SDK 的生成式 UI 文档展示了工具结果与组件之间的映射。真正接到页面,还需要一组固定的用户操作和可观察结果。不要用一张“生成成功”的截图代替交互回归。

从用户会失去什么开始列样例

至少覆盖用户输入、当前选择、焦点、错误解释与重复操作。空结果要能继续搜索;重复事件不能重复创建卡片;工具中断要保留已编辑内容;组件卸载后迟到结果不能影响新会话。窄屏与长文本则检查按钮和错误提示能否触达。

每个样例固定输入与离线工具结果,写清预期。不让模型每次生成不同商品后再由另一个模型自由判断,否则失败难以重放,金标准也可能悄悄变化。

一个可以独立打开的交互夹具

下面刻意让模型内容只更新结果区。输入框、按钮和焦点不随结果重建;工具失败有可见提示。保存为fixture.html,在浏览器打开即可手动检查,不需要模型或服务端。

<!doctype html> <meta charset="utf-8"> <title>生成式 UI 离线交互夹具</title> <style> body{font:18px/1.6 sans-serif;max-width:40rem;margin:2rem auto;padding:1rem} button,input{font:inherit} #result{min-height:5rem} </style> <label for="query">商品要求</label> <input id="query" value="保留我的输入"> <button id="load" type="button">加载候选</button> <button id="fail" type="button">模拟工具失败</button> <section id="result" aria-label="推荐结果"></section> <p id="status" role="status"></p> <pre id="checks"></pre> <script type="module"> const query = document.querySelector("#query"); const result = document.querySelector("#result"); const status = document.querySelector("#status"); const load = document.querySelector("#load"); function renderCandidate() { result.replaceChildren(); const p = document.createElement("p"); p.textContent = "离线候选 A;未核价,不能下单"; result.append(p); status.textContent = "候选已加载"; } load.addEventListener("click", renderCandidate); document.querySelector("#fail").addEventListener("click", () => { status.textContent = "工具失败,请重试;你的输入已保留"; }); // 非真实点击,仅检查此夹具的 DOM 更新是否保留输入和活动元素。 query.value = "用户已经编辑"; query.focus(); const before = document.activeElement; renderCandidate(); const checks = [ ["输入保留", query.value === "用户已经编辑"], ["主动更新不抢焦点", document.activeElement === before], ["候选未被当作订单", result.textContent.includes("不能下单")] ]; document.querySelector("#checks").textContent = checks.map(([name, ok]) => name + ": " + (ok ? "PASS" : "FAIL")).join("\n"); </script>

这里的启动断言不模拟真实键盘导航、输入法或屏幕阅读器。点击按钮时焦点通常在按钮上,结果更新后应按产品定义保留或移动;不能套用“永远保持输入框焦点”的规则。自动化应进一步从真实用户动作开始检查,而不是只调用 render 函数。

回归集需要保存哪些证据

样例 ID、输入、工具夹具版本、预期状态、操作步骤和失败截图要绑定同一版本。错误发生时保存 DOM 或 trace,说明是结构失败、业务状态失败、焦点失败还是环境失败。测试超时也要区分页面未响应与断言写错。

Playwright 的自动重试断言可等待页面达到预期,适合替代随意 sleep。自动重试并不意味着可以把多次失败隐藏起来;原始失败与重试记录都应保留。对于阅读器提示和复杂焦点顺序,仍需人工检查。

Shadow DOM 组件要额外测事件传播、可访问名称与宿主更新;结构封装不保证这些契约自动正确。工具输出合法而权限不足时,UI 应显示明确拒绝,不能用一张降级卡片偷偷执行同一动作。

当前这份夹具能证明什么

本轮仅提取页面脚本做 Node 语法检查,未运行浏览器页面或 Playwright。外部测试依赖安装仍未获准;上面的 PASS 是浏览器打开后由脚本计算的显示逻辑,本文没有声称它们已在浏览器通过。它也没有覆盖真实 React/Vue 组件、移动端或阅读器。

上线前应把这个最小夹具替换为实际组件,保留相同任务与断言,再补业务确认、卸载重挂、长流和错误恢复。没有真实运行记录时,回归集只能作为待执行验收材料,不能写成“全部交互已通过”。

参考资料:AI SDK 生成式 UI,滚动文档;Playwright 断言,滚动文档;Shadow DOM,滚动文档。采用前人工复核依赖版本与实际测试结果。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进