AionUi aionrs(Aion CLI)E2E 测试用例全解:从 P0 基线路径到 P2 边界验证的设计与实践
【免费下载链接】AionUi免费、本地、开源的 24/7 全天候 Cowork 应用,以及适用于 Gemini CLI、Claude Code、Codex、OpenCode、Qwen Code、Goose CLI、Auggie 等的 OpenClaw | 🌟 喜欢就点star吧项目地址: https://gitcode.com/iOfficeAI/AionUi
导读:本文基于 AionUi 开源仓库中的 aionrs E2E 测试用例设计文档(tests/e2e/docs/chat-aionrs/test-cases.zh.md),系统讲解针对 aionrs(Aion CLI)对话功能设计的 15 个端到端测试用例,涵盖用例编号规则、全局前置条件、五维测试矩阵(关联文件夹 / 上传文件 / 模型 / 权限 / 对话中操作)、SQL 级 DB 断言模板与清理约定。读完本文,你将掌握如何为基于本地 CLI binary 的 AI Agent 对话功能编写可跳过、可断言、可清理的高质量 E2E 用例,并理解 AionUi 中 guid 页 → 对话页的完整链路实现。
1. 背景:为什么需要一套独立的 aionrs E2E 用例
AionUi 是一个本地优先的开源 AI 应用,支持接入 Gemini CLI、Claude Code、Codex、Qwen Code、Goose CLI 等本地 CLI binary。其中aionrs(Aion CLI)是一种以binary + IPC方式驱动的 agent 后端:用户在前端(guid 页)选择 agent、配置模型与权限,随后在对话页与运行在本地进程中的 binary 交互。
这类后端的 E2E 测试与普通 API 对话测试有本质差异:
- 依赖本地 binary 可用性:binary 不存在时整套测试必须优雅跳过,而不是失败;
- 依赖用户真实配置的 provider:模型列表来自用户配置文件,测试不能 hardcode 模型 ID;
- 涉及真实文件系统与进程生命周期:关联文件夹、上传文件、停止进程、清理 DB 与临时目录都是用例的一部分;
- 权限模式影响交互行为:
default/auto_edit/yolo三档权限直接决定是否出现工具确认弹窗。
为此,设计文档以Gate 2 初稿的形式产出了一套编号为 TC-A-01 ~ TC-A-15 的完整用例矩阵,并在仓库 tests/e2e/features/conversations/aionrs/ 目录下落地为 6 个 spec 文件。
2. 用例结构:编号规则、前置条件与五维矩阵
2.1 用例编号规则(P0 / P1 / P2)
| 优先级 | 用例范围 | 定位 |
|---|---|---|
| P0(必测) | TC-A-01 ~ TC-A-05 | 基线路径,最小可用功能 |
| P1(常规) | TC-A-06 ~ TC-A-12 | 维度组合,常用场景 |
| P2(边界) | TC-A-13 ~ TC-A-15 | 异常处理,边界验证 |
P0 保证"能跑通",P1 覆盖"常用变体",P2 守护"异常不崩"。从源码结构看,这一分层与 spec 文件的划分一一对应:basic-flow.e2e.ts承载 TC-A-01~03,model-selection.e2e.ts承载 TC-A-04,permission-modes.e2e.ts承载 TC-A-05/06,mid-conversation-switch.e2e.ts承载 TC-A-07~09,combo-scenarios.e2e.ts承载 TC-A-10~12,edge-cases.e2e.ts承载 TC-A-13~15。
2.2 全局前置条件
所有 aionrs 测试用例共享三项前置条件,缺一不可:
- aionrs binary 可用:通过
ipcBridge.fs.findAionrsBinary.invoke()验证,否则 skip 全部测试; - 至少 1 个可用的非 Google Auth provider:
- 调用
ipcBridge.mode.getModelConfig.invoke()获取用户配置的 provider 列表; - 过滤掉
platform包含gemini-with-google-auth的 provider(AionCore 不支持 Google Auth); - 验证至少 1 个 provider 包含
apiKey且有可用model; - 若无可用 provider:skip 全部测试,原因标记为
"No non-Google-Auth provider with apiKey configured"。
- 调用
- 测试数据准备:临时工作目录
/tmp/e2e-aionrs-<timestamp>/。
第二点在源码中有明确印证:useAionrsModelSelection.ts 第 36-40 行对 provider 列表执行同样的过滤逻辑:allProviders.filter((p) => !p.platform?.toLowerCase().includes('gemini-with-google-auth'))。测试前置条件与产品代码保持同一套过滤规则,保证"测试看到的模型列表"与"产品实际可选的模型列表"一致。
模型获取 helper(由 engineer 实现,位于 tests/e2e/helpers/chatAionrs.ts):
export async function getAionrsTestModels(page: Page): Promise<{ modelA: TProviderWithModel; // 第一个可用模型(默认模型) modelB: TProviderWithModel | null; // 第二个可用模型(若存在) }>;该 helper 还负责把原始 provider 数据规范化为测试友好的结构(如apiKey/api_key双字段归一、model/models数组归一、useModel默认取第一个模型),确保测试代码不依赖具体配置格式。
2.3 五维测试维度
| 维度 | 可选值 | 说明 |
|---|---|---|
| 关联文件夹 | 无 / 单 / 多 | atPath数组(源码:AionrsSendBox.tsx) |
| 上传文件 | 无 / 单 / 多 | uploadFile数组(源码:useSendBoxFiles.ts) |
| 模型 | 默认 / 自选 | 从用户配置的 provider 列表选择(ipcBridge.mode.getModelConfig.invoke(),过滤 Google Auth) |
| 权限 | default / auto_edit / yolo | 来源:aionrs runtime capabilities |
| 对话中操作 | 切换模型 / 切换权限 / 无 | 对话页AionrsModelSelector/AgentModeSelector |
从实现看,权限选项并非硬编码:AionrsSendBox 通过useAionrsMessage的onConfigChanged回调接收 runtime capabilities 中的modes数组,动态生成选项(modeOptionsFromCapabilities),同时保留default/auto_edit/yolo兜底列表。这意味着测试文档中"权限来源:aionrs runtime capabilities"的描述与运行时行为一致。
2.4 清理约定与截图要求
命名模式:所有测试对话命名为E2E-aionrs-<timestamp>-<scenario>,便于统一清理与检索。
清理顺序(每个用例afterEach执行,防止用例间数据污染):
- 停止 binary 进程:
ipcBridge.conversation.stopAgent.invoke(conversationId); - 删除 DB 记录:
DELETE FROM conversations WHERE name LIKE 'E2E-aionrs-%'(级联删除 messages); - 删除临时目录:
fs.rm('/tmp/e2e-aionrs-*', { recursive: true }); - 清理 sessionStorage:
sessionStorage.removeItem('aionrs_initial_message_*')+sessionStorage.removeItem('aionrs_initial_processed_*')。
其中 sessionStorage 清理对应 guid 页 → 对话页的"初始消息接力"机制:AionrsSendBox 在 AionrsSendBox.tsx 中读取aionrs_initial_message_${conversation_id},发送成功后写入aionrs_initial_processed_${conversation_id}标记,避免重复发送。测试清理这两项是保证"下一条用例能重新从 guid 页带消息跳转"的前提。
截图要求:每个用例最少 3 张——
- guid 页选择 agent 后(显示输入框 + 配置项);
- 对话页首条消息发送后(显示用户消息 + AI 回复流式中);
- 对话完成后(显示最终消息列表 + DB 断言通过)。
3. P0 基线用例(TC-A-01 ~ TC-A-05)
3.1 TC-A-01:最小可用路径
验证无附件 + 默认模型 + default 权限的最小对话流程,是整个套件的"试金石"。
维度组合:关联文件夹=无,上传文件=无,模型=默认(modelA),权限=default,对话中操作=无。
操作步骤:
- 打开应用,导航至 guid 页(
/#/guid); - 选择 aionrs agent(点击
[data-agent-backend="aionrs"]); - 确认权限选择器默认值为
default(AgentModeSelector默认值); - 输入测试消息:
"Hello, aionrs! Please list files in current directory."; - 点击发送按钮;
- 等待跳转至对话页(URL 匹配
/conversation/aionrs/*); - 等待 AI 回复流式完成(轮询 DB
messages.status='finish',超时 60s)。
DB 断言点(对应文档附录 B 的查询模板):
-- 1. 验证 conversation 创建 SELECT id, name, type, model, status, json_extract(extra, '$.sessionMode') as mode FROM conversations WHERE name LIKE 'E2E-aionrs-%' AND id = ?; -- 期望: type='aionrs', mode='default', status='finished' -- 2. 验证用户消息 SELECT id, type, position, content, status FROM messages WHERE conversation_id = ? AND position = 'right'; -- 期望: type='text', position='right', json_extract(content, '$.content') 包含 "Hello, aionrs!" -- 3. 验证 AI 回复 SELECT id, type, position, status, created_at FROM messages WHERE conversation_id = ? AND position = 'left' AND type = 'text'; -- 期望: 至少 1 条, status='finish', created_at > 用户消息 created_at -- 4. 验证消息顺序 SELECT COUNT(*) FROM messages WHERE conversation_id = ?; -- 期望: ≥ 2(用户 + AI)清理义务:conversation 名E2E-aionrs-<timestamp>-minimal-path;temp dir/tmp/e2e-aionrs-<timestamp>-tc-a-01/(workspace);sessionStorage 键aionrs_initial_message_${conversationId}与aionrs_initial_processed_${conversationId}。截图数 3。
对应实现见 basic-flow.e2e.ts 中的TC-A-01: should complete minimal conversation with no attachments。
3.2 TC-A-02:关联单个文件夹
验证关联文件夹后,消息内容包含文件夹引用,即attachedDirs正确写入用户消息。
前置准备:
mkdir -p /tmp/e2e-aionrs-<timestamp>/test-folder/ echo "sample content" > /tmp/e2e-aionrs-<timestamp>/test-folder/sample.txt关键步骤:从文件树选择test-folder/(触发emitter.emit('aionrs.selected.file', [{ path, name, isFile: false }])),确认 guid 页显示文件夹 Tag(data-testid="folder-tag-0"),发送消息"What files are in the attached folder?"。
DB 断言点:
-- 1. 验证用户消息包含文件夹引用 SELECT json_extract(content, '$.attachedDirs') as dirs FROM messages WHERE conversation_id = ? AND position = 'right'; -- 期望: dirs 是 JSON 数组, 包含 '{"path": ".../test-folder", "name": "test-folder", "isFile": false}' -- 2. 验证消息内容 SELECT json_extract(content, '$.content') as text FROM messages WHERE conversation_id = ? AND position = 'right'; -- 期望: text 包含 "What files are in the attached folder?"实现佐证:文件夹 Tag 的渲染与 testid 生成位于 AionrsSendBox.tsx,其中folderIndex = atPath.filter((v) => typeof v !== 'string' && !v.isFile).indexOf(item)计算文件夹序号,data-testid={\aionrs-folder-tag-${folderIndex}`};文件预览卡片同理使用aionrs-file-tag-${index}。文档中提到的data-testid="folder-tag-0"` 是测试对实现 testid 的引用,实际以源码为准。
3.3 TC-A-03:上传单个文件
验证上传文件后,binary 接收files参数。
前置准备:
echo "Test file content for aionrs E2E" > /tmp/e2e-test-file.txt关键步骤:WebUI 端使用<input type="file">选择文件;Desktop 端调用ipcBridge.dialog.showOpen()选择文件。确认文件预览卡片显示(data-testid="file-card-0")后发送"What is the content of the attached file?"。
DB 断言点:
-- 1. 验证用户消息包含文件引用 SELECT json_extract(content, '$.attachedFiles') as files FROM messages WHERE conversation_id = ? AND position = 'right'; -- 期望: files 是 JSON 数组, 包含 '/tmp/e2e-test-file.txt' -- 2. 验证 AI 回复提到文件内容 SELECT json_extract(content, '$.content') as text FROM messages WHERE conversation_id = ? AND position = 'left' AND type = 'text'; -- 期望: text 包含 "Test file content" 或文件名 "e2e-test-file.txt"实现佐证:发送消息时文件通过collectChatFileRefs(uploadFile, atPath)收集为ChatFileRef[]并随ipcBridge.conversation.sendMessage.invoke({ input, conversation_id, files, sessions })传递(AionrsSendBox.tsx),由后端在发送边缘将每个 ChatFileRef 解析为绝对路径并注入[[AION_FILES]]标记。因此测试断言attachedFiles包含绝对路径/tmp/e2e-test-file.txt是符合设计意图的。
3.4 TC-A-04:使用第二个模型
验证在 guid 页选择非默认模型后,DB 正确记录模型 ID。
前置条件:getAionrsTestModels(page)返回{ modelA, modelB };若modelB === null,skip 此用例(原因:"Only 1 model available, skipping guid page model selection test")。
关键步骤:在 guid 页打开模型选择器(GuidModelSelector,仅当isGeminiMode=true可见),选择modelB(不 hardcode 具体模型 ID),发送"What model are you using?"。
DB 断言点:
SELECT model, json_extract(extra, '$.model.useModel') as extra_model FROM conversations WHERE id = ?; -- 期望: extra_model = modelB.useModel SELECT status FROM conversations WHERE id = ?; -- 期望: status='finished' SELECT COUNT(*) FROM messages WHERE conversation_id = ? AND position = 'left'; -- 期望: ≥ 1实现佐证:guid 页模型选择器的可见性由isGeminiMode控制,即PROVIDER_BASED_AGENTS.has(agentSelection.selectedAssistantBackend)(GuidPage.tsx)。备注:若 guid 页模型选择器未启用(isGeminiMode为 false),此用例改为在对话页切换模型(见 TC-A-07)。
3.5 TC-A-05:使用 yolo 权限
验证yolo 模式下工具调用自动批准(无确认弹窗)。
关键步骤:打开权限选择器(AgentModeSelector,data-testid="agent-mode-selector-aionrs"),选择yolo(label: "YOLO"),发送"Please create a file named test.txt with content 'E2E test'.",验证无确认弹窗出现,等待工具执行完成(轮询 DBmessages.type='tool_group'且status='Success')。
DB 断言点:
-- 1. 验证权限模式持久化 SELECT json_extract(extra, '$.sessionMode') as mode FROM conversations WHERE id = ?; -- 期望: mode='yolo' -- 2. 验证工具调用记录 SELECT type, json_extract(content, '$[0].status') as tool_status FROM messages WHERE conversation_id = ? AND type = 'tool_group'; -- 期望: 至少 1 条, tool_status IN ('Success', 'Executing', 'Error') -- 注意: yolo 模式不应出现 'Confirming' 状态 -- 3. 验证无确认弹窗(前端逻辑验证) -- E2E 层:page.waitForSelector('.confirmation-dialog', { timeout: 2000 }) 应超时(证明无弹窗)4. P1 常规组合用例(TC-A-06 ~ TC-A-12)
4.1 TC-A-06:使用 auto_edit 权限
验证auto_edit 模式下 edit/info 工具自动批准,exec 工具仍需确认——这是对权限语义最精细的验证。
操作步骤:
- 选择
auto_edit(label: "Auto-Accept Edits"); - 发送
"Please read the file ./README.md and summarize it."(触发 info 工具),等待执行完成,无确认弹窗; - 发送第二条消息
"Now run command 'ls -la'."(触发 exec 工具),验证出现确认弹窗(ConversationChatConfirm显示); - 点击 "Yes, Allow Once" 批准,等待命令执行完成。
DB 断言点:
SELECT json_extract(content, '$[0].name') as tool_name, json_extract(content, '$[0].status') as tool_status FROM messages WHERE conversation_id = ? AND type = 'tool_group'; -- 期望: -- - info 类工具: status 直接从 Executing → Success(跳过 Confirming) -- - exec 类工具: status 经历 Confirming → Executing → Success(需用户确认)截图数:4(比基线多 1 张确认弹窗截图)。
4.2 TC-A-07:对话中切换模型
验证对话中切换模型后,DBconversations.extra.model更新为新模型 ID。
操作步骤:按 TC-A-01 创建对话(modelA)→ 等待首条回复完成 → 点击对话页模型选择器(AionrsModelSelector,data-testid="aionrs-model-selector")→ 选择modelB→ 轮询 DBconversations.extra.model更新 → 发送第二条消息"What model are you using now?"。
DB 断言点:
SELECT json_extract(extra, '$.model.useModel') as current_model FROM conversations WHERE id = ?; -- 期望: current_model = modelB.useModel SELECT COUNT(*) FROM messages WHERE conversation_id = ?; -- 期望: ≥ 4(用户消息1 + AI回复1 + 用户消息2 + AI回复2)实现佐证:AionrsModelSelector.tsx 使用${providerId}::${modelName}复合 ID 将扁平模型列表映射回 provider+model,选择后调用handleSelectModel(provider, modelName);模型切换状态经useAionrsModelSelection的handleSelectModel回调(onSelectModel返回 true 才更新current_model)持久化。备注:根据设计文档中"议题 1"的决策,此用例只验证 DB 字段更新,不验证 binary 内部 sessionId 变化。
4.3 TC-A-08 / TC-A-09:对话中切换权限
这两个用例验证权限切换后工具确认行为立即变化,是"权限档位动态生效"的证明。
TC-A-08(default → auto_edit):先以 default 权限发送 edit 类消息 →验证出现确认弹窗→ 取消("No")→ 切换为auto_edit→ 重新发送相同消息 →验证无确认弹窗。
TC-A-09(auto_edit → yolo):先以 auto_edit 发送"Please run command 'pwd'."(exec 工具)→验证出现确认弹窗→ 取消 → 切换yolo→ 重新发送 →验证无确认弹窗(yolo 自动批准所有工具)。
DB 断言点(以 TC-A-08 为例):
-- 1. 验证权限切换后 DB 更新 SELECT json_extract(extra, '$.sessionMode') as mode FROM conversations WHERE id = ?; -- 期望: mode='auto_edit' -- 2. 验证第二次工具调用无 Confirming 状态(取最新一条) SELECT json_extract(content, '$[0].status') as tool_status, created_at FROM messages WHERE conversation_id = ? AND type = 'tool_group' ORDER BY created_at DESC LIMIT 1; -- 期望: tool_status = 'Executing' 或 'Success'(跳过 Confirming) -- 3. 验证第一次工具调用被取消(取最早一条) SELECT json_extract(content, '$[0].status') as tool_status, created_at FROM messages WHERE conversation_id = ? AND type = 'tool_group' ORDER BY created_at ASC LIMIT 1; -- 期望: tool_status = 'Canceled'截图数:TC-A-08 为 5 张(2 张确认弹窗:第一次出现 + 第二次未出现),TC-A-09 为 4 张。
4.4 TC-A-10 / TC-A-11 / TC-A-12:多维度组合场景
这三个用例验证维度交叉时的附件完整传递。
TC-A-10(关联文件夹 + 上传文件 + 非默认模型 + auto_edit):
mkdir -p /tmp/e2e-aionrs-<timestamp>/folder-a/ echo "content A" > /tmp/e2e-aionrs-<timestamp>/folder-a/file-a.txt echo "content B" > /tmp/e2e-test-file-b.txt发送"Compare the content of the attached folder and file.",断言dirs包含folder-a、files包含e2e-test-file-b.txt、extra.model.useModel为第二个模型 ID、sessionMode='auto_edit'。
TC-A-11(上传多个文件):创建/tmp/e2e-file-1.txt、/tmp/e2e-file-2.txt、/tmp/e2e-file-3.txt三个文件批量上传,断言attachedFiles数组长度 = 3,AI 回复提及 "3" 或 "three"。
TC-A-12(关联多个文件夹):创建folder-x/与folder-y/并各含一个文件,依次从文件树选择,断言dirs数组长度 = 2、包含folder-x与folder-y,AI 回复提及x.txt与y.txt。
对应实现见 combo-scenarios.e2e.ts,其中 TC-A-10/12 在只有 1 个模型时同样以test.skip优雅降级('Need at least 2 models for this test')。
5. P2 边界用例(TC-A-13 ~ TC-A-15)
5.1 TC-A-13:Binary 不可达时跳过测试
验证E2E 测试前的 binary 检查机制生效。通过设置process.env.AION_CLI_PATH = '/dev/null'模拟 binary 不可用,运行测试后断言:测试框架输出test.skip()标记;控制台输出 skip 原因"aionrs binary not found, skipping E2E tests";CI 报告显示 skipped(非 failed)。
实现参考(对应 spec 中的beforeAll钩子):
// tests/e2e/setup/aionrs.setup.ts export async function checkAionrsBinary(): Promise<boolean> { try { const binary = await ipcBridge.fs.findAionrsBinary.invoke(); return binary !== null; } catch { return false; } } // tests/e2e/specs/chat-aionrs/*.spec.ts test.beforeAll(async () => { const hasBinary = await checkAionrsBinary(); if (!hasBinary) { test.skip('aionrs binary not found, skipping E2E tests'); } });实际实现中,edge-cases.e2e.ts 与各 spec 文件在beforeAll中除 binary 检查外,还会校验是否存在 aionrs 兼容的 provider,否则test.skip(true, 'No aionrs-compatible provider found, skipping E2E tests')——把"依赖外部环境"的用例从"失败"降级为"跳过",是本地 binary 型 E2E 的通用最佳实践。
5.2 TC-A-14:超大文件上传限制
验证前端文件大小限制机制。创建 100MB 文件:
dd if=/dev/zero of=/tmp/e2e-large-file.bin bs=1M count=100尝试上传后断言:前端显示错误提示(Toast / Message 组件);文件预览卡片不显示;发送按钮可用(但消息体不包含该文件);DB 无记录(消息未发送)。
备注:若前端未实现大小限制(需确认FileService.processDroppedFiles()逻辑),此用例改为验证 binary 层错误处理——这种"实现优先、备选方案兜底"的写法避免了用例因实现偏差而僵死。
5.3 TC-A-15:关联不存在的文件夹
验证文件系统异常处理。通过手动触发选择事件模拟不存在的路径:
emitter.emit('aionrs.selected.file', [ { path: '/tmp/e2e-nonexistent-folder/', name: 'e2e-nonexistent-folder', isFile: false }, ]);DB 断言点:
-- 1. 验证用户消息记录文件夹引用(即使路径不存在) SELECT json_extract(content, '$.attachedDirs') as dirs FROM messages WHERE conversation_id = ? AND position = 'right'; -- 期望: dirs 包含 '/tmp/e2e-nonexistent-folder/' -- 2. 验证 AI 回复提示错误 SELECT json_extract(content, '$.content') as text FROM messages WHERE conversation_id = ? AND position = 'left' AND type = 'text'; -- 期望: text 包含 "not found" 或 "does not exist" 或类似错误提示 -- 3. 验证对话状态 SELECT status FROM conversations WHERE id = ?; -- 期望: status='finished'(非 'error',因为是用户输入错误而非系统错误)第三点是整份文档中最值得注意的断言设计:用户输入错误不应把对话标记为 error,而应正常结束(finished)——这区分了"用户输入导致的业务异常"与"系统故障",是语义化断言的高阶用法。
6. 未覆盖场景:明确"不测什么"同样是设计
根据设计文档中"议题 3"的决策,以下场景本轮 E2E明确不覆盖,并记录理由:
- 工具确认中途切换权限(弹窗确认中切换 yolo 的行为未定义,E2E 不应固化不确定行为);
- 流式输出中途切换模型(当前 turn 是否中断/继续/使用新模型未定义);
- 跨对话/跨进程权限持久化("always allow" 重启后是否记住,本轮只测同一对话内切换生效);
- 并发对话场景(暂缓至后续 Gate,需 Playwright 多标签页 + 进程监控的复杂编排)。
这条"负面清单"的价值在于:它划定了当前测试的信任边界,避免测试断言了未定义的行为而变成"测试驱动实现"的隐性约束。
7. 测试数据矩阵与实现优先级
7.1 用例汇总矩阵
| 用例 ID | 关联文件夹 | 上传文件 | 模型 | 权限 | 对话中操作 | 优先级 | 截图数 |
|---|---|---|---|---|---|---|---|
| TC-A-01 | 无 | 无 | 默认 | default | 无 | P0 | 3 |
| TC-A-02 | 单 | 无 | 默认 | default | 无 | P0 | 3 |
| TC-A-03 | 无 | 单 | 默认 | default | 无 | P0 | 3 |
| TC-A-04 | 无 | 无 | 第二个 | default | 无 | P0 | 3 |
| TC-A-05 | 无 | 无 | 默认 | yolo | 无 | P0 | 3 |
| TC-A-06 | 无 | 无 | 默认 | auto_edit | 无 | P1 | 4 |
| TC-A-07 | 无 | 无 | 默认→第二个 | default | 切换模型 | P1 | 4 |
| TC-A-08 | 无 | 无 | 默认 | default→auto_edit | 切换权限 | P1 | 5 |
| TC-A-09 | 无 | 无 | 默认 | auto_edit→yolo | 切换权限 | P1 | 4 |
| TC-A-10 | 单 | 单 | 第二个 | auto_edit | 无 | P1 | 4 |
| TC-A-11 | 无 | 多(3个) | 默认 | default | 无 | P1 | 3 |
| TC-A-12 | 多(2个) | 无 | 默认 | default | 无 | P1 | 3 |
| TC-A-13 | N/A | N/A | N/A | N/A | N/A | P2 | 1 |
| TC-A-14 | 无 | 超大 | 默认 | default | 无 | P2 | 2 |
| TC-A-15 | 不存在 | 无 | 默认 | default | 无 | P2 | 3 |
统计:P0 5 个(基线路径)、P1 7 个(常规组合 + 对话中操作)、P2 3 个(边界验证),总计 15 个用例,总截图数 50 张。
7.2 分阶段落地建议
- Phase 1(P0 必测):TC-A-01(最小路径,优先实现验证端到端基础流程)→ TC-A-02/03(附件传递)→ TC-A-04(模型选择)→ TC-A-05(权限档位);
- Phase 2(P1 常规覆盖):TC-A-06(完整权限档位覆盖)→ TC-A-07/08/09(对话中动态切换)→ TC-A-10/11/12(维度交叉);
- Phase 3(P2 边界收尾):TC-A-13(CI 跳过机制)→ TC-A-14/15(健壮性验证)。
8. 下一步流程(Gate 2 → Gate 3)
设计文档明确了后续交付路径:chat-aionrs-engineerreview 用例设计并评估工作量 →team-lead批准后进入 Gate 3(实现)→ engineer 实现完成后,designer 产出implementation-mapping.zh.md(TC ID → 文件:行号:函数名 映射)。这一"设计-评审-实现-映射"的闭环保证了测试用例与源码实现可双向追溯。
附录 A:关键源码参考
| 文件 | 关键位置 | 说明 |
|---|---|---|
| GuidPage.tsx | isGeminiMode | guid 页模型选择器可见性 |
| AionrsSendBox.tsx | atPath状态 | 关联文件夹数组 |
| AionrsSendBox.tsx | sendMessage.invoke | 发送消息(传递files参数) |
| AionrsSendBox.tsx | 初始消息接力 | guid 页消息经 sessionStorage 注入对话页 |
| useAionrsModelSelection.ts | 过滤 Google Auth | 测试与产品共用同一过滤规则 |
| AionrsModelSelector.tsx | 复合 ID 映射 | 对话页模型切换 |
| chatAionrs.ts | provider 归一化 | E2E 测试模型获取 helper |
| basic-flow.e2e.ts | TC-A-01~03 | P0 基线实现 |
| combo-scenarios.e2e.ts | TC-A-10~12 | 组合场景实现 |
| edge-cases.e2e.ts | TC-A-13~15 | 边界场景实现 |
附录 B:DB 断言查询模板
以下为整套用例共用的 SQL 模板,覆盖对话、消息、工具调用、思考四类断言,以及统一的 E2E 数据清理:
-- 查询对话基本信息 SELECT id, name, type, model, status, json_extract(extra, '$.sessionMode') as mode, json_extract(extra, '$.model.useModel') as extra_model, json_extract(extra, '$.lastTokenUsage.totalTokens') as tokens FROM conversations WHERE name LIKE 'E2E-aionrs-%'; -- 查询消息列表 SELECT id, msg_id, type, position, status, json_extract(content, '$.content') as text, json_extract(content, '$.attachedFiles') as files, json_extract(content, '$.attachedDirs') as dirs, created_at FROM messages WHERE conversation_id = ? ORDER BY created_at ASC; -- 查询工具调用 SELECT json_extract(content, '$[0].name') as tool_name, json_extract(content, '$[0].status') as tool_status, json_extract(content, '$[0].callId') as call_id, created_at FROM messages WHERE conversation_id = ? AND type = 'tool_group' ORDER BY created_at ASC; -- 查询思考消息 SELECT json_extract(content, '$.content') as thinking_text, json_extract(content, '$.duration') as duration_ms, json_extract(content, '$.status') as thinking_status FROM messages WHERE conversation_id = ? AND type = 'thinking'; -- 清理所有 E2E 数据 DELETE FROM conversations WHERE name LIKE 'E2E-aionrs-%';结语
这套 15 用例的 aionrs E2E 设计给出了一条可复用的范式:用维度矩阵穷举输入组合,用 SQL 断言锁定持久化语义,用 skip 机制容忍环境依赖,用清理约定保证可重复运行,用负面清单划定信任边界。对于任何"本地 binary + 用户配置 + 文件系统交互"型 AI 应用的测试设计,它都是一份值得对照的模板;而其设计文档(test-cases.zh.md)与 tests/e2e/features/conversations/aionrs/ 下的落地实现,则展示了"设计先行、实现映射"的完整工程闭环。
【免费下载链接】AionUi免费、本地、开源的 24/7 全天候 Cowork 应用,以及适用于 Gemini CLI、Claude Code、Codex、OpenCode、Qwen Code、Goose CLI、Auggie 等的 OpenClaw | 🌟 喜欢就点star吧项目地址: https://gitcode.com/iOfficeAI/AionUi
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考