☰
VS Code Agent Sessions 会话导航与历史加载 E2E 场景剖析:从自然语言场景到确定性回归验证
2026/11/2 20:58:28 网站建设 项目流程

VS Code Agent Sessions 会话导航与历史加载 E2E 场景剖析:从自然语言场景到确定性回归验证

【免费下载链接】vscodeVisual Studio Code项目地址: https://gitcode.com/GitHub_Trending/vscode6/vscode

本文围绕 VS Code 仓库中 Agent Sessions(代理会话)工作台的端到端测试场景04-navigate-sessions,讲解"会话之间来回切换并验证历史消息正确加载"这一核心交互的测试目标、底层 UI 行为,以及它如何被 "compile-and-replay"(编译—回放)测试架构翻译成可确定性执行的playwright-cli命令。读者将掌握该场景的逐步语义、.commands.json生成物结构、测试运行方式,以及如何书写与维护同类会话场景。

场景文档说了什么

仓库中 src/vs/sessions/test/e2e/scenarios/04-navigate-sessions.scenario.md 是一个人类可读的 E2E 场景(scenario),全文以 H2## Steps下的自然语言编号列表组织,共 9 步:

  1. 在聊天输入框输入explain the code
  2. 按 Enter 提交
  3. 校验聊天区出现响应
  4. 点击New Session按钮
  5. 在聊天输入框输入build the project
  6. 按 Enter 提交
  7. 校验聊天区出现响应
  8. 在会话列表中点击另一个会话
  9. 校验当前会话已切换,且聊天内容与之前不同

它的测试意图集中且清晰:一个 Agent 工作台需要支持创建多个会话(Session)、在会话间导航,并且每次切换都必须把对应会话的历史聊天记录正确恢复出来。这是一个典型的 "状态隔离 + 历史加载" 回归验证场景——如果会话模型把多条会话的上下文串了,或切换时没有正确按会话加载历史,第 8、9 步就会失败。

该场景不是孤立的,它隶属于src/vs/sessions/test/e2e/下的场景族(编号01–05),场景文件按文件名排序后顺序执行。兄弟场景分别覆盖:01-chat-response(发送消息并收到响应)、02-chat-with-changes(聊天产出真实文件 diff)、03-session-in-sidebar(发送消息后侧边栏出现会话)、05-full-workflow(把会话与终端标签、Changes 视图串成完整工作流)。

前置理解:被测试的"会话"是什么

该场景针对的是 src/vs/sessions 目录实现的Agent Sessions 工作台——一个以"会话"为中心组织聊天、变更与文件操作的环境(内部文档见 src/vs/sessions/README.md、src/vs/sessions/SESSIONS.md)。从目录结构可以推断其 UI 构成:

  • browser/parts/sessionsPart.ts 与 browser/parts/sessionView.ts 负责承载"会话列表 / 会话视图"——即场景第 8 步点击的目标;
  • contrib/sessions/browser 下有 27 个.ts文件实现会话列表、会话条目的管理与渲染;
  • contrib/chat/browser/newSession.ts 定义了NewSessionChangeType(repoUri、isolationMode、branch、options、disabled、agent),对应"新建会话"时要变更的属性维度;
  • contrib/chat/browser/sessionsChatHistory.ts 则暗示了"会话 → 聊天历史"的加载机制。

因此场景中的 "New Session" 按钮、会话列表(sessions list)、会话条目,都有对应的真实 UI 部件;切换会话时被验证的"内容不同",本质上是工作台按照当前激活会话重新渲染其专属的历史消息序列(05-full-workflow场景还进一步验证:终端标签会随会话切换在session-1/session-2之间来回改变,佐证会话切换会带动一组相关联的会话级 UI 状态)。

逐步拆解:每一步在测什么

结合场景族与 Mock 架构(详见 test/e2e/README.md),9 个步骤可归纳为三个测试片段:

步骤用户动作真实 UI 行为验证点 / Mock 行为
1–3explain the code→ Enter消息经 Chat Widget → ChatService 提交给 Chat agent出现聊天响应。生成物中该断言具体化为ASSERT_VISIBLE: This project has a simple structure with a main entry point and utility functions.
4点击New Session创建并激活一个全新的、空的会话(保留旧会话在列表中)步骤 5–7 中旧会话历史不再显示,新输入进入新会话上下文
5–7build the project→ Enter同上提交路径;该消息关键字命中带textEdit的 canned 响应出现新响应。生成物中断言ASSERT_VISIBLE: I'll help you build the project. Here are the changes:
8点击会话列表中的另一会话工作台把激活态切回第一个会话生成物中以click listitem "explain the code"定位会话条目——可见会话条目以首条用户消息文本作为可访问性标签
9—视图重新加载目标会话的历史生成物中两条断言:ASSERT_VISIBLE: explain the code(条目仍可见)与ASSERT_VISIBLE: This project has a simple structure...(第一条会话的历史响应被正确恢复)

可以看到,第 9 步的真正杀手锏是:断言恢复出来的内容是第一段会话特有的 canned 响应文本,而不是第二段会话的build the project响应。这样只要历史加载错位(例如两个会话共享同一份上下文),断言立刻失败。

需要说明的是:这些 "canned response" 来自测试 Mock agent 的关键字匹配响应,具体定义在 test/web.test.ts 的getMockResponseWithEdits(message)(该函数针对build the project这类消息返回I'll help you build the project. Here are the changes:文本并附带编辑进度项)。真实 LLM 与真实 git 均被 Mock,测试验证的是除外部后端之外的整条真实代码路径。

场景如何变成可回放的命令:compile-and-replay 架构

.scenario.md只是"剧本",真正被测试运行器执行的是配套的.commands.json。这套 E2E 采用compile-and-replay架构,由@playwright/cli与 Copilot CLI 驱动(README 对此有完整说明),包含两个阶段:

Phase 1 — 生成(npm run generate,需要 LLM,运行一次)生成脚本generate.cjs对每个.scenario.md:启动 Sessions Web 服务并打开页面 → 抓取当前页面的可访问性树(accessibility tree)快照 → 把每个自然语言步骤连同快照交给 Copilot CLI,由其给出精确的playwright-cli命令(如click e143、type "hello")→ 执行命令推进 UI → 将编译结果写入scenarios/generated/下的同名.commands.json。这些.commands.json会被提交到 git,作为人人可复现的确定性测试计划。

Phase 2 — 回放(npm test,无需 LLM,快且确定)测试运行器 test.cjs 读取每个.commands.json,机械地逐条回放playwright-cli命令并执行断言,全程没有 LLM 调用、没有正则猜 UI。

本场景的编译产物

场景04-navigate-sessions的生成产物为 src/vs/sessions/test/e2e/scenarios/generated/04-navigate-sessions.commands.json。其头部记录场景名与生成时间,steps数组把 9 个人类步骤映射为原子命令:

{ "scenario": "Scenario: Navigate between sessions and verify history loads", "generatedAt": "2026-03-06T04:56:01.957Z", "steps": [ { "description": "Type \"explain the code\"", "commands": ["click textbox \"Chat input\"", "type \"explain the code\""] }, { "description": "Press Enter to submit", "commands": ["click textbox \"Chat input\"", "press Enter"] }, { "description": "Verify there is a chat response", "commands": ["# ASSERT_VISIBLE: This project has a simple structure with a main entry point and utility functions."] }, { "description": "Click button \"New Session\"", "commands": ["click button \"New Session\""] }, { "description": "Click on a different session in the sessions list", "commands": ["click listitem \"explain the code\""] }, { "description": "Verify the session changed and the content in the chat is different", "commands": ["# ASSERT_VISIBLE: explain the code", "# ASSERT_VISIBLE: This project has a simple structure with a main entry point and utility functions."] } ] }

这个文件很好地展示了两个阶段的交接方式:

  • 场景描述(description)保留人类可读步骤,用于失败时输出清晰的错误上下文;
  • 动作型步骤编译成语义化命令(如click button "New Session"、click textbox "Chat input"、press Enter),回放时运行器先从实时快照解析出对应控件引用再执行(见test.cjs的resolveSemanticCommand);
  • 校验型步骤编译成snapshot+ 注释式断言,即# ASSERT_VISIBLE: <text>,运行器执行快照后检查文本是否可见。

test.cjs支持的注释断言格式(README 与源码均可确认)包括:ASSERT_VISIBLE(检查快照含该文本)、ASSERT_DISABLED(检查按钮带[disabled])、ASSERT_ENABLED(检查按钮不带[disabled])。

场景文件如何被解析

场景解析逻辑在 common.cjs 的parseScenario()中:以#开头行作为场景名,/^## steps?$/i之后的-或1.有序列表项全部视为步骤,遇到其他##标题即结束解析;discoverScenarios()会扫描scenarios/目录下所有*.scenario.md并按文件名排序。这解释了场景文件的硬性格式约定:首行为# Scenario: ...,随后是## Steps及其下的编号列表,任何其他 Markdown 章节都不会被当作执行步骤。

复现与运行

按 test/e2e/README.md 的说明,本场景的运行前提与命令如下:

  • 前置条件:仓库根目录已编译(npm install && npm run compile,产物out/);在src/vs/sessions/test/e2e下执行npm install安装依赖;只有运行npm run generate才需要本地有 Copilot CLI(copilot --version校验)。
  • 启动服务的等价脚本为npm run serve,即运行仓库根目录的 scripts/code-sessions-web.js 并以--mock模式启动(该标志会启用 Mock 后端)。
  • 回放执行:
cd src/vs/sessions/test/e2e npm test
  • 需要重新编译场景(UI 变化导致控件引用过期、新增或修改场景步骤)时执行npm run generate,也可按前缀选择性重编:npm run generate -- 04-navigate。

Mock 服务的最小集合(可参会话列表)来自 README:IChatEntitlementService返回ChatEntitlement.Free、IDefaultAccountService返回假登录账号、IGitService立即解析、chat agent 提供 canned 响应、mock-fs://由InMemoryFileSystemProvider在工作台内直接注册、GitHub 认证为始终登录的 Mock 扩展、PR 命令为空操作。其余服务(ChatEditingService、ChatModel、ChangesViewPane、diff 编辑器、context keys 等)全部走真实实现——Mock 是数据唯一的注入点。

与相邻场景的协同关系

04场景不是凭空出现的,它与场景族互为补充,共同逼近"多会话完整工作流":

  • 03-session-in-sidebar.scenario.md 先验证"发送一条消息后侧边栏出现对应会话"——这是04第 8 步能够点击列表条目的前置能力;
  • 02-chat-with-changes.scenario.md 验证会话内聊天会驱动 Changes 视图产生真实 diff;
  • 05-full-workflow.scenario.md 把会话与终端标签联动:新建session-2后终端标签从session-1变为session-2,点击回第一个会话后又变回session-1——从另一个维度再次印证"会话切换 = 一组会话级状态的整体恢复"。

因此,04专注的是会话模型两条不变量:(1)多个会话可共存(New Session 不销毁旧会话);(2)每个会话持有独立历史,切换时按目标会话恢复(点击列表条目后内容正确切换)。

编写与扩展此类场景的要点

若要在仓库中新增"会话导航"类场景,README 与既有场景文件给出了可复制的实践:

  1. 在src/vs/sessions/test/e2e/scenarios/下新建NN-描述.scenario.md,文件名数字前缀决定执行顺序;
  2. 首行写# Scenario: 一句话描述,随后是## Steps与编号列表,步骤用朴素英文描述,示例模式如Click button "New Session"、Type "build the project" in the chat input、Verify the session changed and the content in the chat is different——agent 能理解自然语言,不必局限于固定句式;
  3. 尽量用 UI 上的精确标签(如按钮名、会话条目文本)定位目标,每条目只做一个动作,保持步骤原子化,便于失败定位;
  4. 运行npm run generate生成.commands.json,再npm test验证,最后把.scenario.md与.commands.json一并提交;
  5. 若改动涉及 Mock 的文件编辑关键字,需同步更新 test/web.test.ts 的getMockResponseWithEdits()与 mock 扩展的文件仓库(extensions/sessions-e2e-mock),并保证路径位于/mock-repo/之下。

一个实用建议(README 亦强调):这类场景的断言应落在文本内容与可见性上,例如校验"切换回来后能看到第一段会话特有的响应文本",而不是断言 Mock 内部实现细节,这样当 Mock 文件内容演进时,场景依然稳定、可长期回归。

小结

04-navigate-sessions用 9 行自然语言精炼地锁定了一条高价值的会话产品级回归路径:创建第二个会话、回到第一个会话、并断言各自历史完整隔离加载。透过 .commands.json 可以看到它在真实 Agent Sessions 工作台上的确定性执行形态;而它对会话条目"以首条消息为标签"的依赖、对 canned 响应文本的断言策略,也为我们理解该工作台"会话 = 独立历史容器"的核心模型提供了最直观的测试侧证据。

【免费下载链接】vscodeVisual Studio Code项目地址: https://gitcode.com/GitHub_Trending/vscode6/vscode

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询