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

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

2026-09-07 16:26:26作者:段琳惟

本文围绕 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/ 下的场景族(编号 0105),场景文件按文件名排序后顺序执行。兄弟场景分别覆盖: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.mdsrc/vs/sessions/SESSIONS.md)。从目录结构可以推断其 UI 构成:

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

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

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

步骤 用户动作 真实 UI 行为 验证点 / Mock 行为
1–3 explain 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–7 build 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.tsgetMockResponseWithEdits(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 e143type "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.cjsresolveSemanticCommand);
  • 校验型步骤编译成 snapshot + 注释式断言,即 # ASSERT_VISIBLE: <text>,运行器执行快照后检查文本是否可见。

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

场景文件如何被解析

场景解析逻辑在 common.cjsparseScenario() 中:以 # 开头行作为场景名,/^## 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.FreeIDefaultAccountService 返回假登录账号、IGitService 立即解析、chat agent 提供 canned 响应、mock-fs://InMemoryFileSystemProvider 在工作台内直接注册、GitHub 认证为始终登录的 Mock 扩展、PR 命令为空操作。其余服务(ChatEditingServiceChatModelChangesViewPane、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 inputVerify 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.tsgetMockResponseWithEdits() 与 mock 扩展的文件仓库(extensions/sessions-e2e-mock),并保证路径位于 /mock-repo/ 之下。

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

小结

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

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.74 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.81 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
595
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
920
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.63 K
1.02 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
518
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
389