Files
PlantAI-rag-new/docs/TEST_CASES.md
T
jerryandClaude Fable 5 0875bb036c P0/M1: 四部分重构——frontend→ui、backend→sdk 改名 + 引用修复 + 骨架
- git mv 保留历史:ui/(Vue3+Vite)、sdk/(BFF 中间层)
- 路径语义修正:db_path/figures_cache 改相对 sdk/(BFF cwd),
  历史库 sdk/backend/data 迁移至 sdk/data/(会话/图片缓存无损)
- 引用修复 ~15 处:config.py、deploy/*.sh、vitest/playwright 配置、
  单测导入路径、docs、README、gitignore、.env.example
- 骨架:agent/{qa,methodology,legacy-rag,vendor}、knowledge/manifest.yaml、
  tooling/{compile,kb,crypto,assemble,audit,tools,scripts} + README
- 门禁验证:sdk pytest 125 通过、ui vitest 158 通过、
  生产 BFF 脚本重启四上游全绿 + 静态托管 + 会话库无损

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 12:56:47 +08:00

68 KiB
Raw Blame History

TEST_CASES.md —— 需求测试用例与追溯矩阵

版本 1.0 | 作者 T(测试 agent)| 用例唯一来源:PRD.md(81 FR);请求/响应依据:API.md;分层与轮次纪律:TEST_PLAN.md;上游事实:DISCOVERY.md 本波(M0)交付:用例清单 + 双向追溯矩阵 + 执行说明 + E2E 骨架(2 条冒烟)。全量执行从 M2 集成后开始,按 TEST_PLAN §5 轮次纪律。 覆盖判据:每个 FR 至少 1 条 TC,且每条 TC 至少映射 1 个 FR(双向完整 = 主门禁 100%)。


1. 用例清单

1.1 编号与通用约定

  • 编号:TC-<模块>-<序号>;E2E 主线场景:TC-E2E-<主线号>-<视口>(视口 M=Mobile 390×844 / T=Tablet 768×1024 / D=Desktop 1440×900)。
  • 测试层:L1 后端单元(pytest -m unit,A 组自测)|L2 后端接口(pytest -m api,respx mock 上游,T 主写)|L2s 上游 smoke(bash tests/smoke_upstreams.sh)|L3 前端单元(vitest)|L4 前端组件(vitest + @vue/test-utils)|L5 端到端(Playwright,三档视口)|L6 验收(PM 人工)。
  • 优先级:P0 = 主流程/数据正确性/安全;P1 = 重要功能与降级;P2 = 视觉细节/辅助路径。
  • 可断言性要求:预期列均为可观察结果(元素存在/计数/请求体字段/HTTP 状态/存储值),无「检查是否正确」类断言。
  • 「依赖充值」标注:上游 QA 当前 LLM 402 降级(DISCOVERY.md §6.1),凡断言涉及自然语言答案内容的用例均标注「依赖充值」——充值前只能验证结构(answer 字段存在、非空或为 fallback 证据列表、text 块已渲染),充值后验证完整断言。检索/证据/图谱/来源类断言不受 402 影响。
  • 时序说明:流水线用例中的「pipeline 块状态依次 running→done」属 UI 状态推断,若无专门组件埋点,用例从渲染的相位卡/pipeline 卡状态文本断言;帧时序由 mock sse_frames.json 保证(与 TEST_CASES 无关的 fixture 细节以 PM 的 mock 为准)。

1.2 SHELL 应用外壳(FR-SHELL-001~007)

编号 对应 FR 标题 前置条件 步骤 预期结果 测试层 优先级
TC-SHELL-001 FR-SHELL-001 桌面三栏网格精确尺寸 Desktop 1440×900;AppShell 已挂载 1. 打开应用;2. 计算 .app-shell 的 grid-template-columns 与 rows 实际值(getComputedStyle);3. 计算 .session-panel/.chat-panel/.bench-panel 宽度 columns = 260px minmax(0,1fr) 380px;rows = 48px minmax(0,1fr) 28px;三栏实际宽度 260/800/380(1440 下);body computed overflow:hidden L5-D P0
TC-SHELL-002 FR-SHELL-001 缩放下无横向滚动条 Desktop 初始 1440×900 1. 将视口改为 1280×900、再 1440×900;2. 检查 document.documentElement.scrollWidth 与 clientWidth 两档下 scrollWidth ≤ clientWidth;body 不出现横向滚动条 L5-D P1
TC-SHELL-003 FR-SHELL-002 顶栏结构:Logo/标题/徽章/主题按钮 已有一个活动会话;浅色主题 1. 打开应用;2. 断言 .top-bar 高 48px;3. 检查左 Logo 圆点与「海古纪 PlantAI」文字(gradient text);4. 检查中部会话标题与状态徽章;5. 检查右侧主题切换按钮 .top-bar offsetHeight=48;logo 文字存在且带 background-clip:text 渐变;中部标题=当前会话 title;主题按钮可点 L5-D P1
TC-SHELL-004 FR-SHELL-002 顶栏无会话时显示「就绪」 数据库无任何会话 打开应用 顶栏中部徽章文本=「就绪」(labels.js 对应键) L4 P2
TC-SHELL-005 FR-SHELL-002 顶栏长标题省略 存在 title 为 60+ 字符的会话 1. 打开该会话;2. 检查顶栏标题元素 computed style 标题 text-overflow=ellipsis;不换行(white-space:nowrap);单行渲染 L5-D P2
TC-SHELL-006 FR-SHELL-003 状态栏静态结构 AppShell 已挂载;/api/health、/api/qa/info 正常 1. 检查 .status-bar 高 28px;2. 检查左 4 个 .agent-dot-item;3. 检查右侧版本/会话数/健康标记 高=28px;agent 点数量=4;右侧文本含 PlantAI RAG v0.1 与 QA 版本号(来自 /api/qa/info 的 version,如 1.1.73-assertion-anchored) L5-D P1
TC-SHELL-007 FR-SHELL-003 问答进行中状态栏四点联动 会话空闲;mock normal(SSE 回放) 1. 发起提问;2. 在 SSE 进行中读取四个 agent 点的状态类 相位推进到 X 时第 X 个点激活(如 planning 时第 1 点 pulse);progress 全部结束后四点定格(成功态) L5-D P1
TC-SHELL-008 FR-SHELL-004 左栏折叠为 64px 图标轨 Desktop 1440×900;左栏展开 1. 点击左栏折叠按钮;2. 检查 .session-panel 宽度与图标按钮(3 个 34×34);3. 刷新页面;4. 再次检查 折叠后宽度=64px;图标按钮=3 个;过渡 300ms;刷新后仍折叠 L5-D P1
TC-SHELL-009 FR-SHELL-004 左栏折叠状态持久化 同上 1. 折叠;2. 检查 localStorage['pa-side-collapsed'] localStorage 值为折叠标记;展开后标记被清除/反转 L5-D P2
TC-SHELL-010 FR-SHELL-005 右栏折叠保留手柄条 Desktop 1440×900;右栏展开 1. 点击右栏折叠;2. 检查手柄条尺寸与可见性;3. 点击手柄展开;4. 检查 localStorage['pa-bench-collapsed'] 折叠后 .bench-panel 隐藏但手柄条 28px 可见可点;点击后恢复展开;刷新后保持折叠态 L5-D P1
TC-SHELL-011 FR-SHELL-006 Aurora 背景不遮挡交互 任意视口;AppShell 已挂载 1. 检查 aurora 层 computed style;2. 点击顶栏主题按钮、会话列表项、作曲器输入框(逐个执行且均生效) aurora 层 position:fixed、z-index:0、pointer-events:none;上述交互全部可操作 L5-D P1
TC-SHELL-012 FR-SHELL-006 浅色主题 aurora 衰减 已切浅色 检查 aurora 层或根元素的 --pa-aurora-opacity computed 值=.08(UI_SPEC §1.2 令牌值) L4 P2
TC-SHELL-013 FR-SHELL-007 首屏初始化顺序与并行请求 深色主题已存 localStorage;无会话 1. 打开应用;2. 网络面板记录启动期请求 documentElement.dataset.theme 在应用挂载前已写入(断言:无白色闪屏——深色下首帧背景即深色;实现断言:main.js 在 createApp 前写 dataset);启动期存在 GET /api/sessions、/api/health、/api/qa/info 三个请求且并行(无串行依赖) L5-D + L3 P0
TC-SHELL-014 FR-SHELL-007 首屏接口失败不阻塞渲染 mock 对 /api/health 返回 500(或 BFF 停止) 打开应用 页面仍渲染 AppShell 与欢迎空态;健康标记显示降级/离线态;无未捕获异常 L5-D(mock error 场景) P1
TC-SHELL-015 FR-SHELL-007 无会话显示欢迎空态 数据库无会话 打开应用 对话区显示欢迎空态(渐变菱形 + 标题 + 3 张建议卡) L5-D P1

1.3 THEME 主题与无障碍(FR-THEME-001~006)

编号 对应 FR 标题 前置条件 步骤 预期结果 测试层 优先级
TC-THEME-001 FR-THEME-001 深色默认主题 首次打开(无 pa-theme 存储) 1. 打开应用;2. 检查 documentElement.dataset.theme 与 --pa-canvas computed 值 theme='dark';canvas=#0A0A10(rgb(10,10,16)) L5-D P0
TC-THEME-002 FR-THEME-001 浅色主题逐组件走查 已切浅色 逐个检查顶栏、会话项、气泡、作曲器、bench 面板的计算样式 无失色:各元素背景/文字均来自浅色令牌(canvas=#F6F8FC,surface-1=#FFFFFF);无深色令牌残留(如 #0A0A10 不再出现于背景) L5-D(或 L6 人工走查) P0
TC-THEME-003 FR-THEME-002 主题切换即时生效 默认深色 1. 点击顶栏主题按钮;2. 立即检查 documentElement.dataset.theme 与 localStorage;3. 刷新后复查 dataset.theme 即时='light';localStorage['pa-theme']='light';刷新后仍为 light;再切回同理 L5-D P0
TC-THEME-004 FR-THEME-002 主题图标旋转动画 同上 切换主题时检查图标 transform 图标日/月互换且 rotate 180°(transition 400ms) L5-D P2
TC-THEME-005 FR-THEME-003 浅色 glow 衰减 已切浅色 检查根元素 --pa-glow-opacity computed 值 值=.35(深色下=1) L4 P2
TC-THEME-006 FR-THEME-004 动效降级(reduced motion) 系统「减少动态效果」开启;Playwright 以 reducedMotion:'reduce' 启动 context 1. 打开应用;2. 检查 aurora/状态点/按钮的 animation 计算样式;3. 发起一次问答 @media (prefers-reduced-motion: reduce) 生效:所有元素 animation-duration=0 或 animation 为 none;running 状态以静态符号(实心点 + 文字徽章)可辨认,无 pulse/shimmer/spin 运动 L5-D(reducedMotion context) P1
TC-THEME-007 FR-THEME-005 焦点环样式 任意视口 Tab 键聚焦顶栏主题按钮与作曲器 textarea :focus-visible 输出 2px rgba(94,106,210,.5) 焦点环(outline-offset 1px) L5-D P1
TC-THEME-008 FR-THEME-005 纯键盘完成主流程 空数据库;mock normal 只用 Tab/Enter/Esc 完成:新建会话 → 聚焦输入 → 输入问题 → ⌘/Ctrl+Enter 发送 → Tab 切换工作台 tab → Esc 关闭面板 每一步可达且生效;Tab 顺序符合视觉顺序(顶栏 → 会话列表 → 对话 → 作曲器 → 工作台) L5-D(键盘驱动) P0
TC-THEME-009 FR-THEME-006 对比度达标(深色) 深色主题 抽检 .app-shell 内 3 处文本(正文 ink、muted、subtle)的计算颜色与 canvas 背景,按 WCAG 公式计算对比度 ink/canvas ≥15:1;ink-muted/canvas ≥4.5:1;ink-subtle 仅用于非关键信息 L5-D + L3(工具函数断言,可脚本化) P1
TC-THEME-010 FR-THEME-006 对比度达标(浅色) 浅色主题 同 TC-THEME-009 步骤,浅色令牌 ink(#16182A)/canvas(#F6F8FC) ≈ 计算值 ≥ 深色同等标准;ink-muted/canvas ≥4.5:1 L5-D P1

1.4 SESSION 会话管理(FR-SESSION-001~009)

编号 对应 FR 标题 前置条件 步骤 预期结果 测试层 优先级
TC-SESSION-001 FR-SESSION-001 列表按 updated_at 倒序 数据库 ≥3 个会话,updated_at 互异(fixtures 保证) 1. GET /api/sessions;2. 前端渲染列表;3. 依次读取条目标题 条目顺序 = updated_at 从新到旧 L2(fixture 会话数据)+ L4 P0
TC-SESSION-002 FR-SESSION-001 分组「今天/昨天/更早」 ≥3 个会话,时间分别落在今天/昨天/2 天前 渲染列表 三个组标签(11px/500/字距0.6px)按序出现,组内条目归属正确;组内按时间倒序 L4(clock mock) P1
TC-SESSION-003 FR-SESSION-001 空列表引导文案 数据库无会话 渲染列表 显示引导文案(「暂无会话」类 labels 文案);不渲染列表条目 L4 P2
TC-SESSION-004 FR-SESSION-002 状态点四态区分 4 个会话分别 idle/running/done/error 渲染列表 形状可区分:idle=空心、running=空心+脉冲动画、done=实心 success 色、error=实心 error 色;running 有 animation,其余无 L4 P1
TC-SESSION-005 FR-SESSION-003 新建会话 任意初始状态 1. 点击「新建会话」按钮;2. 观察网络请求与界面 发出 POST /api/sessions;列表新增一条「新会话」;切换为活动会话;对话区显示欢迎空态;不产生空消息(GET 详情 messages 为空数组) L5-D + L2 P0
TC-SESSION-006 FR-SESSION-003 新建连点不重复 同上 快速连点新建按钮 2 次 产生 2 个独立会话(列表+2,两条 POST);均切到最后创建的一个 L5-D P1
TC-SESSION-007 FR-SESSION-004 选择会话加载历史 会话含 text/table/sources/graph 块消息 点击该会话条目 发出 GET /api/sessions/{id};加载中显示骨架屏;完成后消息按块类型渲染:markdown 文本、表格(md-table 样式)、来源、图谱块均出现 L5-D(mock 会话详情含全块) P0
TC-SESSION-008 FR-SESSION-004 切换会话中断进行中流 会话 A 流式进行中;会话 B 存在 点击会话 B 会话 A 的 SSE 连接 abort(Network 面板连接关闭);会话 A 状态置 error;切回会话 A 后其消息标注「已中断」 L5-D(mock slow/正常流) P0
TC-SESSION-009 FR-SESSION-005 搜索过滤(中文/英文/大小写) ≥3 个会话,标题含中英文混排 1. 输入中文关键词;2. 输入英文小写;3. 输入英文大写;4. 输入无匹配词;5. 清空 匹配为 title 大小写不敏感子串;无匹配显示「无匹配会话」;清空恢复全量;过滤期间无网络请求 L5-D + L4 P1
TC-SESSION-010 FR-SESSION-006 重命名提交 活动会话 1. 双击标题进入编辑;2. 输入新名;3. 回车 PATCH /api/sessions/{id} 请求体 {title:新名};列表与顶栏标题同步更新 L5-D + L2 P1
TC-SESSION-011 FR-SESSION-006 重命名取消与空标题 同上 1. 编辑中按 Esc;2. 再次编辑并清空标题后回车 Esc 放弃改动;空标题提交按钮禁用或提交被拒,不发出 PATCH 空 title L5-D P2
TC-SESSION-012 FR-SESSION-007 删除需二次确认 2 个会话 1. 右键/菜单点删除;2. 出现确认弹层时点取消 未删除(列表不变、无 DELETE 请求) L5-D P1
TC-SESSION-013 FR-SESSION-007 删除当前会话切换 2 个会话;选中其中一个 删除并确认 DELETE /api/sessions/{id} 204;当前会话切换到剩余最新一个;刷新列表后已删会话不再出现 L5-D + L2 P0
TC-SESSION-014 FR-SESSION-007 删除最后一个会话显示空态 仅 1 个会话 删除并确认 列表空;对话区显示欢迎空态 L5-D P1
TC-SESSION-015 FR-SESSION-007 级联删除消息 会话含 2 条消息;BFF 真实 删除会话后 GET /api/sessions/{id} 返回 404(NOT_FOUND 信封);SQLite 中该会话消息行一并删除(L2 断言数据库) L2 P0
TC-SESSION-016 FR-SESSION-008 首问自动标题 新建会话 1. 提问(问题 40+ 字符);2. 检查 PATCH 请求与列表标题 PATCH 请求体 title = 问题前 30 字符 + 省略号;列表标题同步变化 L5-D + L2 P0
TC-SESSION-017 FR-SESSION-008 手动改名后不覆盖 会话 meta.title_manual=true(改过名) 1. 再次提问;2. 检查是否发出 title PATCH 不发出改 title 的 PATCH;标题保持手动值 L2(会话固定数据)+ L4 P1
TC-SESSION-018 FR-SESSION-009 活动态高亮 ≥2 个会话 1. 选中会话 A;2. 检查其 computed style;3. 悬停另一项 B 对比 A 有 2px 渐变左竖条 + g-to 8% 底色 + 内描边;与悬停态(B)可区分 L5-D P2

1.5 CHAT 对话与问答(FR-CHAT-001~015)

1.5.1 基础渲染

编号 对应 FR 标题 前置条件 步骤 预期结果 测试层 优先级
TC-CHAT-001 FR-CHAT-001 欢迎空态元素与建议卡 无消息 打开应用 显示 46px 渐变菱形(✧)+ 渐变标题「海古纪 PlantAI 知识工作台」+ 副标题 + 3 张建议卡 L5-D P1
TC-CHAT-002 FR-CHAT-001 建议卡点击即提问 无消息;mock normal 点击第 1 张建议卡 卡片文本进入作曲器并直接发送;出现用户气泡(内容=卡片文本)与助手骨架;发出 POST /api/qa/chat/stream L5-D P0
TC-CHAT-003 FR-CHAT-002 用户气泡样式与长文本换行 任意 发送 300 字无空格长文本 气泡右对齐,宽度 ≤ 父容器 70%;文本换行且不溢出(scrollWidth ≤ 气泡宽度) L5-D P2
TC-CHAT-004 FR-CHAT-003 助手消息头部与悬停竖线 已有助手消息 1. 检查头部(24px 渐变头像 ✦ + 「PlantAI 智能体」+ 时间);2. 悬停消息左缘 头部三要素齐全;左缘 2px 竖线 hover 时出现渐变 L5-D P2
TC-CHAT-005 FR-CHAT-003 多块混排顺序 消息 blocks 为 [pipeline,text,table,sources,graph](mock 会话详情) 渲染该消息 各块按 blocks 数组顺序渲染;无块丢失 L5-D P1
TC-CHAT-006 FR-CHAT-004 Markdown 表格样式 消息含表格块(mock 含 md-table 内容的 text 块) 渲染 表格带 md-table 类:thead 2px 渐变下边框;数据格 mono 字体(表头为「说明」的列保持比例字体) L5-D P1
TC-CHAT-007 FR-CHAT-004 脚本注入被消毒 向问答返回的 answer 注入 <script>window.__pwned=1</script> 与 <img src=x onerror=...>(mock fixture 或 BFF 层构造) 渲染该答案 window.__pwned 为 undefined;DOM 中不存在 script 标签;onerror 未执行;内容经 DOMPurify 后 v-html L5-D(注入 fixture)+ L3(sanitize 单测) P0
TC-CHAT-008 FR-CHAT-014 流式自动滚动到底 mock normal;消息区可滚动 发起提问 流式更新期间滚动条持续贴近底部(scrollTop+clientHeight ≥ scrollHeight−10) L5-D P1
TC-CHAT-009 FR-CHAT-014 上滚暂停自动滚动 流式进行中 向上滚动 ≥100px 不再自动贴底;出现「回到最新」按钮 L5-D P1
TC-CHAT-010 FR-CHAT-014 「回到最新」恢复滚动 上滚暂停状态 点击「回到最新」按钮 滚动回底部;恢复自动滚动;按钮消失 L5-D P1
TC-CHAT-011 FR-CHAT-015 内联引用 chip 跳转高亮 答案含引用标记(citation 编号)与对应来源 点击引用 chip bench 切换到「文献来源」tab 并滚动定位到对应条目;该条目高亮 2s 后消失;chip 编号与列表条目序号一致 L5-D P1

1.5.2 作曲器

编号 对应 FR 标题 前置条件 步骤 预期结果 测试层 优先级
TC-CHAT-012 FR-CHAT-005 输入框高度自适应 桌面 1. 输入 1 行文本测基线(min-height 46px);2. 输入 10 行文本 基线 ≥46px;随内容增高,最大 160px 后内部滚动(scrollHeight > clientHeight) L5-D P2
TC-CHAT-013 FR-CHAT-006 三模式切换 桌面;mock normal 依次点击 问答/方案/检索 pill 激活 pill 渐变底+白字+青光;非激活态可区分;切换即生效 L5-D P0
TC-CHAT-014 FR-CHAT-006 问答模式请求体 mock 正常;问答模式 提问并抓取 POST /api/qa/chat/stream 请求体 body.use_llm=true;question 为输入文本;无 native_chain 字段(或为 modes.js 定义默认) L5-D + L3(modes.js 参数集断言) P0
TC-CHAT-015 FR-CHAT-006 方案模式请求体 方案模式 同上 body.use_llm=true;native_chain_enabled=true;native_chain_max_hops=3;native_chain_top_k=5;端点仍为 /api/qa/chat/stream L5-D + L3 P0
TC-CHAT-016 FR-CHAT-006 检索模式请求与响应 检索模式 1. 提问;2. 观察端点为 /api/qa/search;3. 等待完成;4. 检查消息块 body.use_llm=false;响应 answer 为空;消息只含 sources(+pipeline)块,无 text 答案块 L5-D + L2 P0
TC-CHAT-017 FR-CHAT-007 快捷键发送 桌面 输入文字后按 ⌘/Ctrl+Enter 发送;出现用户气泡 L5-D P0
TC-CHAT-018 FR-CHAT-007 Enter 换行 桌面 输入文字后按 Enter(非组合键) 不发送;textarea 内换行(行数+1) L5-D P1
TC-CHAT-019 FR-CHAT-007 中英文输入法快捷键 IME 输入法激活(模拟 composition 事件) composition 期间按 Enter 不触发发送(组合输入确认才触发) L3(composition 事件模拟) P1
TC-CHAT-020 FR-CHAT-007 空输入与忙碌禁用 流式进行中 1. 空输入点发送;2. 流式中点发送 按钮 disabled(opacity .45);无新请求;底部提示文案显示快捷键说明 L5-D P1
TC-CHAT-021 FR-CHAT-011 停止流式 mock slow(长流) 1. 提问;2. 流式中点击「停止」 发送按钮已变为「停止」;点击后 SSE 连接关闭(Network 断言);消息置 error 并标注「已停止」;已收到的块保留 L5-D P0
TC-CHAT-022 FR-CHAT-011 停止后无悬挂请求 BFF 真实 + 上游 QA 流式中停止;随后检查 BFF 日志 前端 abort 后 BFF 取消上游请求(BFF 侧无悬挂 upstream 连接;日志有取消记录) L2(respx 断言 abort)+ L5 P1

1.5.3 流式主流程(FR-CHAT-008)

编号 对应 FR 标题 前置条件 步骤 预期结果 测试层 优先级
TC-CHAT-023 FR-CHAT-008 主流程-用户消息立即落库 mock normal;新会话 发送问题 用户气泡立即出现(不等后端);发出 POST /api/sessions/{id}/messages,body={role:user,text};落库成功 L5-D + L2 P0
TC-CHAT-024 FR-CHAT-008 主流程-会话置 running 与骨架 同上 发送后观察 会话状态置 running(左栏状态点脉冲动画);底栏 4 agent 点复位;出现助手骨架消息(status=streaming)含 pipeline 块,五相位全 waiting L5-D P0
TC-CHAT-025 FR-CHAT-008 主流程-progress 相位推进与阶段卡 mock normal(sse_frames 12 帧) 提问后观察 每帧 progress:pipeline 对应相位 running、此前相位 done;工作台「检索过程」新增阶段卡,数量=progress 帧数(11 个 stage 帧);阶段卡 label 为中文(如「已识别实体」) L5-D P0
TC-CHAT-026 FR-CHAT-008 主流程-complete 生成三块 mock normal;提问完成 等待 complete 消息含 text 块(揭示动画结束为最终文本)、sources 块、graph 块;pipeline 五相位全部 done;消息状态 done;会话状态 done L5-D P0
TC-CHAT-027 FR-CHAT-008 主流程-答案内容(依赖充值) mock normal(mock 答案为固定文本);真实上游需充值 断言答案文本 mock 下:答案文本与 mock fixture 一致(可断言内容);真实上游:answer 为自然语言答案(依赖充值,当前 402 下为 fallback 证据列表) L5-D / L6 P1
TC-CHAT-028 FR-CHAT-008 主流程-检查点持久化节奏 BFF 真实;流 ≥3s 提问并抓取 PATCH 请求 流式中每约 3s 发出 PATCH /api/sessions/{id}/messages/{mid}(块检查点);终态再发一次终态 PATCH(done/error) L2 + L5 P0
TC-CHAT-029 FR-CHAT-008 主流程-长任务持续反馈(240s) mock slow(长流,或上游真实 ~92s) 提问并计时观察 240s 内任一时刻页面均有视觉反馈(相位卡 shimmer/已耗时计数);无静止超过 5s 的状态(5s 间隔采样断言有动画元素或文本变化) L5-D(slow 场景) P0
TC-CHAT-030 FR-CHAT-008 网络中断重试按钮 流式中断网(playwright context.setOffline(true)) 1. 流式中断网;2. 观察消息状态;3. 恢复网络;4. 观察是否自动重连;5. 点「重试」 消息置 error,文案「连接中断」;出现「重试」按钮;不自动重连(恢复网络后无新请求,需手动点击);点重试后重新发起同问题请求 L5-D(offline 模拟) P0
TC-CHAT-031 FR-CHAT-008 刷新恢复中断会话 流式进行中刷新页面 刷新后检查该会话 显示最后检查点内容;标注「已中断」;会话状态 error;不自动重试 L5-D P0
TC-CHAT-032 FR-CHAT-008 同一会话并发提问防护 流式进行中 观察发送按钮 按钮 disabled;无法发送第二个问题 L5-D P1
TC-CHAT-033 FR-CHAT-008 同会话提问互相独立(无上下文记忆) 同一会话已完成一次提问 第二次提问并抓取请求体 第二次请求只含当前 question,无历史消息拼接(max_rounds:1 语义);UI 有「每次提问独立检索」提示文案 L2 + L5 P1

1.5.4 流水线卡与揭示动画(FR-CHAT-009 / FR-CHAT-010)

编号 对应 FR 标题 前置条件 步骤 预期结果 测试层 优先级
TC-CHAT-034 FR-CHAT-009 五相位视觉与颜色 流式中与完成后分别观察 pipeline 卡 检查五个 p-step 的颜色令牌与状态字形 相位色:规划#C0A8DD、检索#9FBBE0、证据#9FC9A2、生成#DFA88F、完成#C08532;状态字形 ✓done/●running/◌waiting;running 相位带 11px 旋转 spinner;卡头「⚙ 检索流水线」+ 整体状态徽章(语义色) L5-D P0
TC-CHAT-035 FR-CHAT-009 相位与语义色不混用 流式中出现错误(mock error 场景) 检查 pipeline 卡 相位点颜色只随相位走(五相位色);整体成败徽章只用语义色(success/error)——两者不互换 L5-D P1
TC-CHAT-036 FR-CHAT-010 揭示动画与跳过 mock normal;答案长度已知 1. 观察 complete 后答案揭示;2. 点击「跳过」 揭示速度 30ms/字符,尾部 8×15px 青色闪烁光标 + 3px shimmer 进度线;点「跳过」后立即显示全文,reveal 元素移除 L5-D P1
TC-CHAT-037 FR-CHAT-010 长答案 8s 上限 mock 构造 >2000 字符答案 测量揭示总时长 速度自动加快,总时长 ≤8s L5-D P2

1.5.5 错误与重试(FR-CHAT-012)

编号 对应 FR 标题 前置条件 步骤 预期结果 测试层 优先级
TC-CHAT-038 FR-CHAT-012 引用校验失败重试 BFF 对 invalid_llm_citation 已自动重试一次仍失败(L2 构造) 观察响应与界面 前端展示「引用校验失败,请重试」(INVALID_CITATION 信封 message)并提供重试按钮 L2(respx 断言 BFF 重试一次)+ L5 P0
TC-CHAT-039 FR-CHAT-012 其他错误展示 message mock scenario=error 提问 消息置 error;展示 error.message 文案(上游错误中文文案);提供重试按钮;已收到块保留 L5-D(error 场景) P0
TC-CHAT-040 FR-CHAT-012 重试同位置替换 错误后 点击重试 用相同问题与模式重新发起;不新增用户消息(消息列表长度不变,同一条助手消息位置更新) L5-D P0
TC-CHAT-041 FR-CHAT-012 SSE error/timeout 事件 mock 构造 event:error 与 event:timeout 帧 提问 error 帧→展示 error.message;timeout 帧→文案「检索超时」;两者均置消息 error 并保留已收到块 L5-D(定制 mock 流) P1

1.5.6 持久化与恢复(FR-CHAT-013)

编号 对应 FR 标题 前置条件 步骤 预期结果 测试层 优先级
TC-CHAT-042 FR-CHAT-013 块联合完整落库 BFF 真实;完成一次问答 检查 SQLite 该消息行 blocks 含 text/table/sources/graph/pipeline 等全部完成块;无 stream 块(瞬态已转 text);raw 字段保存完整归一化响应 L2 P0
TC-CHAT-043 FR-CHAT-013 刷新后恢复全部块 完成一次含表格/来源/图谱的问答 刷新页面并打开该会话 GET /api/sessions/{id} 后全部块正确重渲染(表格、来源、图谱可见);无数据丢失 L5-D P0

1.6 RETRIEVAL 检索过程(FR-RETRIEVAL-001~006)

编号 对应 FR 标题 前置条件 步骤 预期结果 测试层 优先级
TC-RETRIEVAL-001 FR-RETRIEVAL-001 阶段卡与 progress 帧一一对应 mock normal;完成一次问答 统计工作台 Tab1 卡片数与 Network 中 progress 帧数 卡片数 = progress 帧数(sse_frames 11 个 stage 帧 → 11 张卡);每卡含相位色图标 + 阶段中文名 + 状态点 + 耗时 mono L5-D P0
TC-RETRIEVAL-002 FR-RETRIEVAL-001 阶段卡展开体参数块 已有一张阶段卡 展开卡头 展开体显示 details 参数块(mono)+ 摘要行(如 entities 列表) L5-D P2
TC-RETRIEVAL-003 FR-RETRIEVAL-002 相位筛选 一次问答后存在 4 个相位卡片 依次点击 全部/规划/检索/证据/生成 chip 激活 chip 实心 #5E6AD2 底 + 白字;筛选后仅显示对应相位卡片(计数=对应相位帧数) L5-D P1
TC-RETRIEVAL-004 FR-RETRIEVAL-003 展开折叠与默认态 流式中(running 卡)与完成后各观察一次 1. 流式中看卡默认态;2. 完成后看默认态;3. 点击头部 运行中的卡默认展开;完成后自动折叠;点击切换展开/折叠;chevron 旋转 90°;展开体 fade-slide 200ms;各卡状态互相独立 L5-D P1
TC-RETRIEVAL-005 FR-RETRIEVAL-004 运行中效果 流式中 检查 running 卡 头部 shimmer 扫光(1.8s 循环)+ 状态点脉冲;完成后动效停止(animation none/移除) L5-D P2
TC-RETRIEVAL-006 FR-RETRIEVAL-005 检索计划展示与无 plan 不显示 mock normal(fixture 含 retrieval_plan)与无 plan 的响应各一次 检查列表顶部 含 retrieval_plan 时顶部只读信息块展示 plan;无 plan 时该区域不渲染(无空框);另展示 /api/qa/info 的 12 段 pipeline 静态说明 L5-D P1
TC-RETRIEVAL-007 FR-RETRIEVAL-006 空态 未提问 打开工作台 Tab1 显示「发起提问后,这里会实时展示检索过程」;无假卡片 L5-D P2

1.7 SOURCE 文献来源(FR-SOURCE-001~006)

编号 对应 FR 标题 前置条件 步骤 预期结果 测试层 优先级
TC-SOURCE-001 FR-SOURCE-001 来源列表条目数与元素 mock normal 完成问答(source_count=4) 打开工作台 Tab2 条目数 = 4(与响应 source_count 一致);每条:序号 + 标题(超长省略)+ mono 元信息行(PMID:xxx / DOI:xxx / 分数)+ 证据片段(默认 2 行截断) L5-D P0
TC-SOURCE-002 FR-SOURCE-001 证据片段展开 有一条来源 点击片段 展开显示完整 snippet;再点收起 L5-D P2
TC-SOURCE-003 FR-SOURCE-002 字段缺失不渲染占位 mock 构造 source 缺 pmid/pmcid/doi/section 的组合 渲染列表 缺失字段位置不显示占位符;页面文本中不含 undefined、null 字样 L5-D P0
TC-SOURCE-004 FR-SOURCE-003 外链优先级 4 个 source:a 有 url;b 无 url 有 doi;c 无 url/doi 有 pmid;d 无 url/doi/pmid 有 pmcid;e 全无 检查各条目链接 a 链=url;b=https://doi.org/{doi};c=PubMed PMID 链接;d=PMC 链接;e 无链接图标;点击新标签打开且带 rel="noopener" L5-D P1
TC-SOURCE-005 FR-SOURCE-004 证据链分组与展开 响应 chains 含 ≥2 个 chain_id 打开证据链区 按 chain_id 分组显示;组徽章显示跳数;展开后逐跳列表 源实体 —[关系]→ 目标实体,每跳附 paper_id/PMID;顺序与 hops 数组一致 L5-D P1
TC-SOURCE-006 FR-SOURCE-005 会话级聚合 同一会话完成 2 次问答(相同 paper 出现多次) 切到「本会话全部」 发出 GET /api/sessions/{id}/sources;按 paper_id 去重;每条 count = 该 paper 累计出现次数;去重后条数正确 L5-D + L2 P1
TC-SOURCE-007 FR-SOURCE-006 空态 mock scenario=empty 完成问答(source_count=0) 打开工作台 Tab2 显示「本次检索未命中文献」;无空列表框(无 .ft-item 元素) L5-D(empty 场景) P1

1.8 EXTRACT 提取任务(FR-EXTRACT-001~006)

编号 对应 FR 标题 前置条件 步骤 预期结果 测试层 优先级
TC-EXTRACT-001 FR-EXTRACT-001 任务列表与派生百分比 mock 任务列表(含 queued/running/completed/failed 各一) 打开工作台 Tab3 发出 GET /api/extraction/jobs;列表与响应一致;百分比派生正确:queued=5、running(35/75 按 stage)、completed=100、failed 无进度条且显示错误态;100% 为纯 success 色且无 sweep 扫光 L5-D + L2(BFF 派生断言) P0
TC-EXTRACT-002 FR-EXTRACT-002 任务详情 已有一个任务 点击展开 GET /api/extraction/jobs/{id};显示 status、message(中文进度)、stats 统计字段(stage/species/figure_table 计数) L5-D P1
TC-EXTRACT-003 FR-EXTRACT-002 未完成任务 plan 不显示 一个 running 任务 展开详情 plan 为 null 时不显示 plan 区块且无报错 L5-D P1
TC-EXTRACT-004 FR-EXTRACT-003 运行日志 mock 正常 打开运行日志 GET /api/extraction/run-log;mono 等宽展示;长日志在最大高度内滚动(scrollHeight > clientHeight 时出现内部滚动);自动贴底 L5-D P2
TC-EXTRACT-005 FR-EXTRACT-004 上传 PDF 成功 构造 ≤50MB PDF;mock 上传成功 附件按钮 → 选 PDF → 上传 POST /api/extraction/upload(multipart,Content-Type 含 multipart/form-data);显示上传进度;成功返回 job_id 并刷新列表(出现新任务) L5-D P1
TC-EXTRACT-006 FR-EXTRACT-004 非 PDF 被拒 一个 .txt 文件 尝试上传 被拒绝(前端拦截或 422);不发出上传请求(前端拦截时)或返回 VALIDATION 信封 L5-D + L2 P1
TC-EXTRACT-007 FR-EXTRACT-004 超 50MB 被拒 一个 >50MB PDF 尝试上传 被拒绝;提示超出大小限制;不发出上传请求 L5-D P2
TC-EXTRACT-008 FR-EXTRACT-004 上游失败错误文案 mock 返回 502(MinerU 不可用) 上传 展示上游 message 的明确错误文案(非堆栈);任务列表/详情为失败态 L5-D P1
TC-EXTRACT-009 FR-EXTRACT-005 轮询节奏 列表含一个 running 任务 观察 12s 内网络请求 每 5s 轮询一次 GET /api/extraction/jobs;全部终态后轮询停止(再观察 12s 无新请求) L5-D P1
TC-EXTRACT-010 FR-EXTRACT-005 切 tab 暂停轮询 running 任务存在;切换到其他 tab 观察网络 离开 Tab3 期间无 jobs 轮询请求;切回恢复 L5-D P2
TC-EXTRACT-011 FR-EXTRACT-006 离线降级 mock 对 extraction 返回 503(degraded) 打开 Tab3 显示离线提示与重试按钮;其他 tab(检索/来源/图谱)不受影响可正常使用 L5-D(degraded 场景) P1

1.9 KG 知识图谱(FR-KG-001~008)

编号 对应 FR 标题 前置条件 步骤 预期结果 测试层 优先级
TC-KG-001 FR-KG-001 答案图谱渲染(SVG 力导向) mock normal 完成问答(graph 块 6 节点 4 边) 打开工作台 Tab4 渲染手写 SVG 力导向图:节点圆点+标签;边为渐变线(#5E6AD2→#00D4FF)带关系名;节点与答案引用实体一致(与 graph 块节点 label 一致) L5-D P0
TC-KG-002 FR-KG-001 节点 >50 降级列表 mock 构造 graph 块 >50 节点 打开 Tab4 自动降级为列表视图并提示;不渲染 SVG 画布 L5-D P1
TC-KG-003 FR-KG-002 节点着色与图例 graph 块含 ≥2 种实体 type 检查节点颜色与图例 按 type 着色(品牌+辅助紫+五相位色板);底部图例存在且与节点颜色一一对应;同 type 在会话内颜色稳定(两次问答同一 type 颜色相同) L5-D P2
TC-KG-004 FR-KG-003 节点详情面板 graph 块有节点;Neo4j 不可用(mock 无 kg 或 503) 点击一个节点 侧滑详情面板显示实体属性(消息内已有属性);Neo4j 不可用时仍显示(不报错、不空白) L5-D P1
TC-KG-005 FR-KG-003 Neo4j 可用时拉全量属性 Neo4j 可用(真实上游或 mock) 点击节点 面板内额外数据来自 GET /api/kg/entities/{name}(发出该请求并渲染返回 properties) L5-D P2
TC-KG-006 FR-KG-004 实体搜索(精确/前缀) mock normal 1. 输入「OsSPL」;2. 回车 GET /api/kg/entities?q=OsSPL;结果显示(精确或前缀匹配项);响应 <1s(断言用时) L5-D + L2 P0
TC-KG-007 FR-KG-004 实体搜索选中渲染邻域 上一步结果 点击第一个结果 渲染该实体邻域(调 neighborhood 接口或 mock 数据);画布更新为该实体为中心 L5-D P1
TC-KG-008 FR-KG-005 邻域深度切换 mock normal 1. 深度选 1(默认)观察请求;2. 深度选 2 观察 请求 depth 参数分别为 1/2;渲染节点数随之变化;深度 2 时显示「较慢」提示文案(两跳实测约 13s) L5-D + L2 P1
TC-KG-009 FR-KG-005 节点上限 200 截断 mock 构造 >200 节点邻域 深度 2 展开 渲染节点数 ≤200;每跳按 paper_count/support_count 降序截断(L2 断言 BFF 截断逻辑);超限有提示不卡死 L2 + L5 P1
TC-KG-010 FR-KG-006 缩放平移(桌面) 画布有图 滚轮缩放;拖拽平移;双击 缩放范围 0.3×–3×;平移生效;双击复位到初始视图 L5-D P1
TC-KG-011 FR-KG-006 触摸缩放平移(移动端) Mobile 视口;画布有图 双指捏合;单指拖拽 双指缩放与单指拖拽均可用且流畅(缩放比例改变;拖拽后画布位移) L5-M(touch 模拟) P1
TC-KG-012 FR-KG-007 Neo4j 不可用降级 mock kg 接口返回 503(或 BFF 断开 Neo4j) 打开 Tab4 搜索框与邻域功能禁用并提示离线;答案图谱照常渲染;tab 不白屏 L5-D P1
TC-KG-013 FR-KG-008 空态 本次回答无 graph 数据(mock empty) 打开 Tab4 提示「本次检索未产生图谱关系」+ 引导使用实体搜索;不显示空画布 L5-D(empty 场景) P2

1.10 RESP 响应式(FR-RESP-001~013)

编号 对应 FR 标题 前置条件 步骤 预期结果 测试层 优先级
TC-RESP-001 FR-RESP-001 Desktop ≥1280 完整三栏 Desktop 1440×900 检查布局 三栏 260/1fr/380 可见(同 TC-SHELL-001) L5-D P0
TC-RESP-002 FR-RESP-002 Laptop 1024–1279 左栏自动折叠 视口 1200×900 检查布局 三栏保留;左栏宽=64px(自动折叠,无需点击);右栏=340px L5-D(1200×900) P1
TC-RESP-003 FR-RESP-003 Tablet 左抽屉 Tablet 768×1024 1. 检查布局(对话单栏铺满);2. 点顶栏汉堡 左栏默认隐藏;点汉堡后左抽屉 260px 滑入 + scrim 遮罩;顶栏有汉堡按钮;右栏为右抽屉(顶栏工作台按钮带来源数角标);点 scrim 或 Esc 关闭;动效 ease-out 300ms L5-T P0
TC-RESP-004 FR-RESP-004 Mobile 单栏与工作台覆盖层 Mobile 390×844 1. 检查布局;2. 点顶栏工作台按钮 顶栏=汉堡+标题+工作台按钮;对话铺满;作曲器吸底;工作台打开为全屏覆盖层+顶部 tab 条;底部状态栏隐藏(不可见);四个 agent 点合并为顶栏一枚聚合状态点;点 scrim 或 Esc 关闭 L5-M P0
TC-RESP-005 FR-RESP-005 viewport 与安全区 Mobile(iPhone 视口) 检查 meta 与 computed 值 <meta viewport> 含 viewport-fit=cover;作曲器 padding-bottom = env(safe-area-inset-bottom);顶栏 padding-top = env(safe-area-inset-top)(computed 值>0) L5-M P1
TC-RESP-006 FR-RESP-006 iOS 输入放大规避 Mobile 视口(<768px) 检查 textarea computed font-size font-size=16px;未使用 maximum-scale=1(meta 内容断言) L5-M P0
TC-RESP-007 FR-RESP-007 悬停态触摸等价 Mobile 视口;有来源条目与助手消息 检查来源外链图标、助手消息左缘线、会话条目样式 外链图标常驻可见;助手消息左缘线常驻;会话条目无 hover 底色依赖(点击反馈存在);@media (hover:hover) 包裹 hover 样式(L3 样式断言) L5-M P1
TC-RESP-008 FR-RESP-008 触控目标 44px Mobile 视口 检查会话条目、bench tab、模式 pill、图标按钮高度 min-height ≥44px(computed) L5-M P1
TC-RESP-009 FR-RESP-009 100dvh 视口高度 Mobile 视口 检查应用根容器高度 使用 100dvh(computed 解析为实际视口高度);存在 100vh 回退声明(L3 样式断言) L5-M P2
TC-RESP-010 FR-RESP-010 虚拟键盘适配 Mobile 视口;visualViewport 可模拟 聚焦 textarea(模拟键盘弹出压缩 visualViewport) 消息区高度压缩并保持贴底;作曲器始终可见(boundingBox 在视口内) L5-M P1
TC-RESP-011 FR-RESP-011 表格横向滚动 Mobile 视口;消息含宽表格 检查 md-table 容器 表格外层有横向滚动容器;容器 scrollWidth > clientWidth 时可横滑查看完整内容;字体字号未被压缩(computed font-size ≥ 桌面值) L5-M P1
TC-RESP-012 FR-RESP-012 移动端性能降级 Mobile 视口 检查 aurora 与图谱上限 aurora 模糊半径降低或 --pa-aurora-opacity:0;图谱节点数上限=30(构造 >30 节点 graph 块时渲染 ≤30 或降级列表) L5-M P2
TC-RESP-013 FR-RESP-013 手势(可选 N/A) Mobile 视口 左缘右滑 若实现:右滑开会话抽屉、右滑关工作台覆盖层可用;未实现不算缺陷,本条 N/A 记录 L5-M(标记 N/A) P2

1.11 HEALTH 健康与降级(FR-HEALTH-001~005)

编号 对应 FR 标题 前置条件 步骤 预期结果 测试层 优先级
TC-HEALTH-001 FR-HEALTH-001 健康轮询 30s 打开应用 观察 65s 内 GET /api/health 请求 挂载后立即一次;此后每 30s 一次(65s 内共 3 次) L5-D P1
TC-HEALTH-002 FR-HEALTH-001 轮询失败不弹窗 mock /api/health 持续 500 打开应用 无错误弹窗/未捕获异常;状态栏健康标记显示降级态 L5-D P1
TC-HEALTH-003 FR-HEALTH-001 上游恢复 30s 内解禁 mock 先 503 后恢复正常(场景切换) 1. 观察禁用态;2. 切回正常场景;3. ≤30s 内观察 上游恢复后 ≤30s(一个轮询周期内)对应功能自动解禁,无需手动刷新 L5-D P1
TC-HEALTH-004 FR-HEALTH-002 QA 不可用降级 mock scenario=degraded(/api/qa/* 503) 打开应用 作曲器禁用并显示「知识问答服务当前不可用」;历史会话仍可浏览(点击会话可看历史消息);无错误弹窗 L5-D(degraded 场景) P0
TC-HEALTH-005 FR-HEALTH-002 QA 降级信封语义 mock degraded 检查响应结构 HTTP 503 + {"ok":false,"error":{"code":"UPSTREAM_DOWN","message":"…","degraded":true}};前端按 degraded 处理(横幅/离线态)而非错误弹窗 L2 + L5 P0
TC-HEALTH-006 FR-HEALTH-003 Neo4j 不可用降级 mock kg 503 打开 Tab4 搜索/邻域禁用并提示离线;答案图谱照常渲染(同 TC-KG-012) L5-D P1
TC-HEALTH-007 FR-HEALTH-004 抽取不可用降级 mock extraction 503 打开 Tab3 仅 Tab3 降级(离线提示+重试按钮);其他 tab 正常(同 TC-EXTRACT-011) L5-D P1
TC-HEALTH-008 FR-HEALTH-005 网络中断提示与不自动重连 流式中断网(同 TC-CHAT-030) 1. 断网;2. 恢复网络观察 提示「连接中断」+ 重试按钮;不自动重连;无重复 LLM 调用(Network 无自动新请求) L5-D P0

1.12 非功能要求(PRD §14)对照用例

编号 对应要求 标题 前置条件 步骤 预期结果 测试层 优先级
TC-NFR-001 首屏可交互 桌面;mock normal 打开应用并计时(performance.timing / Playwright 计时) 桌面 <2s 可交互(首屏渲染+会话列表加载完成);手机 <3s L5-D / L5-M P1
TC-NFR-002 长任务反馈 mock slow(长流) 提问并采样 240s 内无静止 >5s 状态(同 TC-CHAT-029) L5-D P0
TC-NFR-003 Neo4j 凭据不下发 打开应用并抓取全部网络响应与 JS bundle 文本 浏览器侧任何响应/源码中不含 Neo4j 密码(<neo4j-password-in-.env> 等)与 bolt:// URI L5-D + L3(bundle 扫描) P0
TC-NFR-004 零外网请求 打开应用并抓取全部网络请求 所有请求 host 均为 127.0.0.1(或部署域);无 fonts.googleapis.com / CDN 域名请求 L5-D P1
TC-NFR-005 浏览器兼容基线 Chrome 110+/Firefox 110+/Safari 16+ Playwright 三浏览器各跑冒烟 2 条 冒烟通过(页面加载 + AppShell 存在) L5(chromium 常驻;firefox/webkit 每轮按需) P1

1.13 E2E 主线场景(TEST_PLAN §6 九条主线 × 三档视口)

编号规则 TC-E2E-<主线号>-<视口>,M=Mobile(390×844)|T=Tablet(768×1024)|D=Desktop(1440×900)。每条为一条用例,步骤与预期为该视口下的完整流程断言。全量用例在后续轮次落为 spec 文件;本波仅实现 TC-E2E-SMOKE-01/02(§1.14)。

编号 主线 视口 步骤 预期结果 优先级
TC-E2E-01-M 1 主流程 M 新建会话 → 问答模式提问 → 观察流水线五相位推进 → 等待答案揭示(含表格)→ 来源 tab 有数据 → 图谱 tab 渲染 → 刷新后历史恢复 五相位按序推进(waiting→running→done);答案揭示并含 md-table;Tab2 条目数=source_count;Tab4 SVG 图渲染;刷新后该会话消息完整恢复 P0
TC-E2E-01-T 1 主流程 T 同 TC-E2E-01-M(左栏经抽屉进入会话) 同 TC-E2E-01-M;另断言左/右抽屉开合正常 P0
TC-E2E-01-D 1 主流程 D 同 TC-E2E-01-M 同 TC-E2E-01-M P0
TC-E2E-02-M 2 检索模式 M 检索模式提问 → 等待完成 → 检查消息块 无 text 答案块;仅来源数据;请求体 use_llm=false 且端点 /api/qa/search P0
TC-E2E-02-T 2 检索模式 T 同 TC-E2E-02-M 同 TC-E2E-02-M P0
TC-E2E-02-D 2 检索模式 D 同 TC-E2E-02-M 同 TC-E2E-02-M P0
TC-E2E-03-M 3 错误与重试 M scenario=error 下提问 → 观察错误;scenario=degraded 下观察作曲器 error:错误文案展示+重试按钮可用(点击后重新发起);degraded:作曲器禁用+「知识问答服务当前不可用」提示 P0
TC-E2E-03-T 3 错误与重试 T 同 TC-E2E-03-M 同 TC-E2E-03-M P0
TC-E2E-03-D 3 错误与重试 D 同 TC-E2E-03-M 同 TC-E2E-03-M P0
TC-E2E-04-M 4 会话管理 M 新建/切换/重命名/删除/搜索过滤/观察分组 每步按 SESSION 用例断言:新建+1、切换加载历史、重命名同步顶栏、删除需确认且列表-1、搜索实时过滤、分组标签正确 P0
TC-E2E-04-T 4 会话管理 T 同 TC-E2E-04-M 同 TC-E2E-04-M P0
TC-E2E-04-D 4 会话管理 D 同 TC-E2E-04-M 同 TC-E2E-04-M P0
TC-E2E-05-M 5 中断 M 流式中途切会话 → 切回原会话 原会话置 error 且标注「已中断」;已收到块保留;无悬挂流 P0
TC-E2E-05-T 5 中断 T 同 TC-E2E-05-M 同 TC-E2E-05-M P0
TC-E2E-05-D 5 中断 D 同 TC-E2E-05-M 同 TC-E2E-05-M P0
TC-E2E-06-M 6 主题 M 深浅切换 → 刷新 切换即时生效(dataset.theme 与 localStorage);刷新后保持 P0
TC-E2E-06-T 6 主题 T 同 TC-E2E-06-M 同 TC-E2E-06-M P0
TC-E2E-06-D 6 主题 D 同 TC-E2E-06-M 同 TC-E2E-06-M P0
TC-E2E-07-M 7 图谱 M 实体搜索 → 选中渲染邻域 → 节点详情 → 双指缩放 + 单指拖拽 搜索结果渲染;邻域画布更新;详情面板打开;触摸缩放/拖拽生效 P1
TC-E2E-07-T 7 图谱 T 同 TC-E2E-07-M(触摸为单指拖拽+双指捏合) 同 TC-E2E-07-M P1
TC-E2E-07-D 7 图谱 D 实体搜索 → 邻域 → 详情 → 滚轮缩放/拖拽平移/双击复位 同 TC-E2E-07-M(桌面交互) P1
TC-E2E-08-M 8 抽取 M 任务列表 → 详情展开 → 运行日志;mock 下上传 PDF 列表渲染+百分比正确;详情字段齐全;日志滚动;上传成功返回 job_id 并刷新列表 P1
TC-E2E-08-T 8 抽取 T 同 TC-E2E-08-M 同 TC-E2E-08-M P1
TC-E2E-08-D 8 抽取 D 同 TC-E2E-08-M 同 TC-E2E-08-M P1
TC-E2E-09-M 9 响应式专项 M 布局断言(FR-RESP-004 全项)+ 移动端专项:安全区、16px 输入、44px 触控、表格横滚、键盘适配 顶栏=汉堡+标题+工作台按钮;作曲器吸底;工作台全屏覆盖层+tab 条;底栏隐藏;聚合状态点存在;safe-area padding 生效;textarea 16px;触控目标 ≥44px;宽表格横滚;聚焦时作曲器可见 P0
TC-E2E-09-T 9 响应式专项 T 布局断言(FR-RESP-003 全项:抽屉/scrim/角标) 单栏铺满;左右抽屉开合+scrim+Esc 关闭;汉堡与工作台按钮带来源数角标 P0
TC-E2E-09-D 9 响应式专项 D 布局断言(FR-RESP-001/002:1280 边界与 1200 折叠) 1440 完整三栏;1200 左栏自动折叠 64px 右栏 340px P0

1.14 E2E 冒烟骨架用例(本波已实现)

编号 对应 FR 标题 前置条件 步骤 预期结果 测试层 优先级
TC-E2E-SMOKE-01 FR-SHELL-007(页面加载) 页面加载冒烟 dev server 5180 可用;三档视口各跑一次 打开 / title 含「海古纪 PlantAI」;#app 可见且内容非空 L5-M/T/D P0
TC-E2E-SMOKE-02 FR-SHELL-001/003/FR-RESP-004(壳层;移动端底栏隐藏断言) AppShell 存在冒烟 同上 打开 / 存在 .app-shell 时:shell/top-bar 可见且无占位符;status-bar 在 ≥768px 视口可见、移动端隐藏(FR-RESP-004);M0 占位期(无 .app-shell)回退:.app-shell-placeholder 可见 L5-M/T/D P0

2. 追溯矩阵

2.1 统计总览

项 值
FR 总数 81
已覆盖 FR 81
未覆盖清单 (空)
TC 总数 179
孤儿 TC(无 FR 映射) 0
主门禁(FR→TC 且 TC→FR 双向完整) 81/81 = 100%

2.2 FR→TC 矩阵(每个 FR 列出其全部 TC)

FR 用例
FR-SHELL-001 TC-SHELL-001, TC-SHELL-002, TC-E2E-SMOKE-02, TC-E2E-09-D
FR-SHELL-002 TC-SHELL-003, TC-SHELL-004, TC-SHELL-005
FR-SHELL-003 TC-SHELL-006, TC-SHELL-007, TC-E2E-SMOKE-02
FR-SHELL-004 TC-SHELL-008, TC-SHELL-009
FR-SHELL-005 TC-SHELL-010
FR-SHELL-006 TC-SHELL-011, TC-SHELL-012
FR-SHELL-007 TC-SHELL-013, TC-SHELL-014, TC-SHELL-015, TC-E2E-SMOKE-01
FR-THEME-001 TC-THEME-001, TC-THEME-002
FR-THEME-002 TC-THEME-003, TC-THEME-004, TC-E2E-06-M, TC-E2E-06-T, TC-E2E-06-D
FR-THEME-003 TC-THEME-005
FR-THEME-004 TC-THEME-006
FR-THEME-005 TC-THEME-007, TC-THEME-008
FR-THEME-006 TC-THEME-009, TC-THEME-010
FR-SESSION-001 TC-SESSION-001, TC-SESSION-002, TC-SESSION-003, TC-E2E-04-M, TC-E2E-04-T, TC-E2E-04-D
FR-SESSION-002 TC-SESSION-004
FR-SESSION-003 TC-SESSION-005, TC-SESSION-006, TC-E2E-04-M, TC-E2E-04-T, TC-E2E-04-D
FR-SESSION-004 TC-SESSION-007, TC-SESSION-008, TC-E2E-04-M, TC-E2E-04-T, TC-E2E-04-D
FR-SESSION-005 TC-SESSION-009, TC-E2E-04-M, TC-E2E-04-T, TC-E2E-04-D
FR-SESSION-006 TC-SESSION-010, TC-SESSION-011, TC-E2E-04-M, TC-E2E-04-T, TC-E2E-04-D
FR-SESSION-007 TC-SESSION-012, TC-SESSION-013, TC-SESSION-014, TC-SESSION-015, TC-E2E-04-M, TC-E2E-04-T, TC-E2E-04-D
FR-SESSION-008 TC-SESSION-016, TC-SESSION-017
FR-SESSION-009 TC-SESSION-018
FR-CHAT-001 TC-CHAT-001, TC-CHAT-002
FR-CHAT-002 TC-CHAT-003
FR-CHAT-003 TC-CHAT-004, TC-CHAT-005
FR-CHAT-004 TC-CHAT-006, TC-CHAT-007
FR-CHAT-005 TC-CHAT-012
FR-CHAT-006 TC-CHAT-013, TC-CHAT-014, TC-CHAT-015, TC-CHAT-016, TC-E2E-02-M, TC-E2E-02-T, TC-E2E-02-D
FR-CHAT-007 TC-CHAT-017, TC-CHAT-018, TC-CHAT-019, TC-CHAT-020
FR-CHAT-008 TC-CHAT-023, TC-CHAT-024, TC-CHAT-025, TC-CHAT-026, TC-CHAT-027, TC-CHAT-028, TC-CHAT-029, TC-CHAT-030, TC-CHAT-031, TC-CHAT-032, TC-CHAT-033, TC-E2E-01-M, TC-E2E-01-T, TC-E2E-01-D, TC-E2E-05-M, TC-E2E-05-T, TC-E2E-05-D
FR-CHAT-009 TC-CHAT-034, TC-CHAT-035
FR-CHAT-010 TC-CHAT-036, TC-CHAT-037
FR-CHAT-011 TC-CHAT-021, TC-CHAT-022, TC-E2E-05-M, TC-E2E-05-T, TC-E2E-05-D
FR-CHAT-012 TC-CHAT-038, TC-CHAT-039, TC-CHAT-040, TC-CHAT-041, TC-E2E-03-M, TC-E2E-03-T, TC-E2E-03-D
FR-CHAT-013 TC-CHAT-042, TC-CHAT-043, TC-E2E-01-M, TC-E2E-01-T, TC-E2E-01-D
FR-CHAT-014 TC-CHAT-008, TC-CHAT-009, TC-CHAT-010
FR-CHAT-015 TC-CHAT-011
FR-RETRIEVAL-001 TC-RETRIEVAL-001, TC-RETRIEVAL-002
FR-RETRIEVAL-002 TC-RETRIEVAL-003
FR-RETRIEVAL-003 TC-RETRIEVAL-004
FR-RETRIEVAL-004 TC-RETRIEVAL-005
FR-RETRIEVAL-005 TC-RETRIEVAL-006
FR-RETRIEVAL-006 TC-RETRIEVAL-007
FR-SOURCE-001 TC-SOURCE-001, TC-SOURCE-002
FR-SOURCE-002 TC-SOURCE-003
FR-SOURCE-003 TC-SOURCE-004
FR-SOURCE-004 TC-SOURCE-005
FR-SOURCE-005 TC-SOURCE-006
FR-SOURCE-006 TC-SOURCE-007
FR-EXTRACT-001 TC-EXTRACT-001, TC-E2E-08-M, TC-E2E-08-T, TC-E2E-08-D
FR-EXTRACT-002 TC-EXTRACT-002, TC-EXTRACT-003, TC-E2E-08-M, TC-E2E-08-T, TC-E2E-08-D
FR-EXTRACT-003 TC-EXTRACT-004, TC-E2E-08-M, TC-E2E-08-T, TC-E2E-08-D
FR-EXTRACT-004 TC-EXTRACT-005, TC-EXTRACT-006, TC-EXTRACT-007, TC-EXTRACT-008, TC-E2E-08-M, TC-E2E-08-T, TC-E2E-08-D
FR-EXTRACT-005 TC-EXTRACT-009, TC-EXTRACT-010
FR-EXTRACT-006 TC-EXTRACT-011
FR-KG-001 TC-KG-001, TC-KG-002
FR-KG-002 TC-KG-003
FR-KG-003 TC-KG-004, TC-KG-005
FR-KG-004 TC-KG-006, TC-KG-007, TC-E2E-07-M, TC-E2E-07-T, TC-E2E-07-D
FR-KG-005 TC-KG-008, TC-KG-009, TC-E2E-07-M, TC-E2E-07-T, TC-E2E-07-D
FR-KG-006 TC-KG-010, TC-KG-011, TC-E2E-07-M, TC-E2E-07-T, TC-E2E-07-D
FR-KG-007 TC-KG-012
FR-KG-008 TC-KG-013
FR-RESP-001 TC-RESP-001, TC-E2E-09-D
FR-RESP-002 TC-RESP-002, TC-E2E-09-D
FR-RESP-003 TC-RESP-003, TC-E2E-09-T
FR-RESP-004 TC-RESP-004, TC-E2E-09-M, TC-E2E-SMOKE-02
FR-RESP-005 TC-RESP-005, TC-E2E-09-M
FR-RESP-006 TC-RESP-006, TC-E2E-09-M
FR-RESP-007 TC-RESP-007, TC-E2E-09-M
FR-RESP-008 TC-RESP-008, TC-E2E-09-M
FR-RESP-009 TC-RESP-009, TC-E2E-09-M
FR-RESP-010 TC-RESP-010, TC-E2E-09-M
FR-RESP-011 TC-RESP-011, TC-E2E-09-M
FR-RESP-012 TC-RESP-012, TC-E2E-09-M
FR-RESP-013 TC-RESP-013(N/A 可选)
FR-HEALTH-001 TC-HEALTH-001, TC-HEALTH-002, TC-HEALTH-003
FR-HEALTH-002 TC-HEALTH-004, TC-HEALTH-005, TC-E2E-03-M, TC-E2E-03-T, TC-E2E-03-D
FR-HEALTH-003 TC-HEALTH-006, TC-KG-012
FR-HEALTH-004 TC-HEALTH-007, TC-EXTRACT-011
FR-HEALTH-005 TC-HEALTH-008, TC-CHAT-030

非功能要求(PRD §14,非 FR 编号)由 TC-NFR-001~005 覆盖:首屏性能(TC-NFR-001)、长任务反馈(TC-NFR-002)、凭据安全(TC-NFR-003)、零外网(TC-NFR-004)、浏览器兼容(TC-NFR-005)。

2.3 TC→FR 矩阵(每条 TC 列出其全部 FR,证明无孤儿)

TC FR
TC-SHELL-001 FR-SHELL-001
TC-SHELL-002 FR-SHELL-001
TC-SHELL-003 FR-SHELL-002
TC-SHELL-004 FR-SHELL-002
TC-SHELL-005 FR-SHELL-002
TC-SHELL-006 FR-SHELL-003
TC-SHELL-007 FR-SHELL-003
TC-SHELL-008 FR-SHELL-004
TC-SHELL-009 FR-SHELL-004
TC-SHELL-010 FR-SHELL-005
TC-SHELL-011 FR-SHELL-006
TC-SHELL-012 FR-SHELL-006
TC-SHELL-013 FR-SHELL-007
TC-SHELL-014 FR-SHELL-007
TC-SHELL-015 FR-SHELL-007
TC-THEME-001 FR-THEME-001
TC-THEME-002 FR-THEME-001
TC-THEME-003 FR-THEME-002
TC-THEME-004 FR-THEME-002
TC-THEME-005 FR-THEME-003
TC-THEME-006 FR-THEME-004
TC-THEME-007 FR-THEME-005
TC-THEME-008 FR-THEME-005
TC-THEME-009 FR-THEME-006
TC-THEME-010 FR-THEME-006
TC-SESSION-001 FR-SESSION-001
TC-SESSION-002 FR-SESSION-001
TC-SESSION-003 FR-SESSION-001
TC-SESSION-004 FR-SESSION-002
TC-SESSION-005 FR-SESSION-003
TC-SESSION-006 FR-SESSION-003
TC-SESSION-007 FR-SESSION-004
TC-SESSION-008 FR-SESSION-004
TC-SESSION-009 FR-SESSION-005
TC-SESSION-010 FR-SESSION-006
TC-SESSION-011 FR-SESSION-006
TC-SESSION-012 FR-SESSION-007
TC-SESSION-013 FR-SESSION-007
TC-SESSION-014 FR-SESSION-007
TC-SESSION-015 FR-SESSION-007
TC-SESSION-016 FR-SESSION-008
TC-SESSION-017 FR-SESSION-008
TC-SESSION-018 FR-SESSION-009
TC-CHAT-001 FR-CHAT-001
TC-CHAT-002 FR-CHAT-001
TC-CHAT-003 FR-CHAT-002
TC-CHAT-004 FR-CHAT-003
TC-CHAT-005 FR-CHAT-003
TC-CHAT-006 FR-CHAT-004
TC-CHAT-007 FR-CHAT-004
TC-CHAT-008 FR-CHAT-014
TC-CHAT-009 FR-CHAT-014
TC-CHAT-010 FR-CHAT-014
TC-CHAT-011 FR-CHAT-015
TC-CHAT-012 FR-CHAT-005
TC-CHAT-013 FR-CHAT-006
TC-CHAT-014 FR-CHAT-006
TC-CHAT-015 FR-CHAT-006
TC-CHAT-016 FR-CHAT-006
TC-CHAT-017 FR-CHAT-007
TC-CHAT-018 FR-CHAT-007
TC-CHAT-019 FR-CHAT-007
TC-CHAT-020 FR-CHAT-007
TC-CHAT-021 FR-CHAT-011
TC-CHAT-022 FR-CHAT-011
TC-CHAT-023 FR-CHAT-008
TC-CHAT-024 FR-CHAT-008
TC-CHAT-025 FR-CHAT-008
TC-CHAT-026 FR-CHAT-008
TC-CHAT-027 FR-CHAT-008
TC-CHAT-028 FR-CHAT-008
TC-CHAT-029 FR-CHAT-008
TC-CHAT-030 FR-CHAT-008, FR-HEALTH-005
TC-CHAT-031 FR-CHAT-008
TC-CHAT-032 FR-CHAT-008
TC-CHAT-033 FR-CHAT-008
TC-CHAT-034 FR-CHAT-009
TC-CHAT-035 FR-CHAT-009
TC-CHAT-036 FR-CHAT-010
TC-CHAT-037 FR-CHAT-010
TC-CHAT-038 FR-CHAT-012
TC-CHAT-039 FR-CHAT-012
TC-CHAT-040 FR-CHAT-012
TC-CHAT-041 FR-CHAT-012
TC-CHAT-042 FR-CHAT-013
TC-CHAT-043 FR-CHAT-013
TC-RETRIEVAL-001 FR-RETRIEVAL-001
TC-RETRIEVAL-002 FR-RETRIEVAL-001
TC-RETRIEVAL-003 FR-RETRIEVAL-002
TC-RETRIEVAL-004 FR-RETRIEVAL-003
TC-RETRIEVAL-005 FR-RETRIEVAL-004
TC-RETRIEVAL-006 FR-RETRIEVAL-005
TC-RETRIEVAL-007 FR-RETRIEVAL-006
TC-SOURCE-001 FR-SOURCE-001
TC-SOURCE-002 FR-SOURCE-001
TC-SOURCE-003 FR-SOURCE-002
TC-SOURCE-004 FR-SOURCE-003
TC-SOURCE-005 FR-SOURCE-004
TC-SOURCE-006 FR-SOURCE-005
TC-SOURCE-007 FR-SOURCE-006
TC-EXTRACT-001 FR-EXTRACT-001
TC-EXTRACT-002 FR-EXTRACT-002
TC-EXTRACT-003 FR-EXTRACT-002
TC-EXTRACT-004 FR-EXTRACT-003
TC-EXTRACT-005 FR-EXTRACT-004
TC-EXTRACT-006 FR-EXTRACT-004
TC-EXTRACT-007 FR-EXTRACT-004
TC-EXTRACT-008 FR-EXTRACT-004
TC-EXTRACT-009 FR-EXTRACT-005
TC-EXTRACT-010 FR-EXTRACT-005
TC-EXTRACT-011 FR-EXTRACT-006, FR-HEALTH-004
TC-KG-001 FR-KG-001
TC-KG-002 FR-KG-001
TC-KG-003 FR-KG-002
TC-KG-004 FR-KG-003
TC-KG-005 FR-KG-003
TC-KG-006 FR-KG-004
TC-KG-007 FR-KG-004
TC-KG-008 FR-KG-005
TC-KG-009 FR-KG-005
TC-KG-010 FR-KG-006
TC-KG-011 FR-KG-006
TC-KG-012 FR-KG-007, FR-HEALTH-003
TC-KG-013 FR-KG-008
TC-RESP-001 FR-RESP-001
TC-RESP-002 FR-RESP-002
TC-RESP-003 FR-RESP-003
TC-RESP-004 FR-RESP-004
TC-RESP-005 FR-RESP-005
TC-RESP-006 FR-RESP-006
TC-RESP-007 FR-RESP-007
TC-RESP-008 FR-RESP-008
TC-RESP-009 FR-RESP-009
TC-RESP-010 FR-RESP-010
TC-RESP-011 FR-RESP-011
TC-RESP-012 FR-RESP-012
TC-RESP-013 FR-RESP-013(N/A 可选)
TC-HEALTH-001 FR-HEALTH-001
TC-HEALTH-002 FR-HEALTH-001
TC-HEALTH-003 FR-HEALTH-001
TC-HEALTH-004 FR-HEALTH-002
TC-HEALTH-005 FR-HEALTH-002
TC-HEALTH-006 FR-HEALTH-003
TC-HEALTH-007 FR-HEALTH-004
TC-HEALTH-008 FR-HEALTH-005
TC-NFR-001 PRD §14 首屏可交互(非 FR)
TC-NFR-002 PRD §14 长任务反馈(非 FR)
TC-NFR-003 PRD §14 数据安全(非 FR)
TC-NFR-004 PRD §14 离线容忍(非 FR)
TC-NFR-005 PRD §14 浏览器支持(非 FR)
TC-E2E-01-M FR-CHAT-008, FR-CHAT-013
TC-E2E-01-T FR-CHAT-008, FR-CHAT-013
TC-E2E-01-D FR-CHAT-008, FR-CHAT-013
TC-E2E-02-M FR-CHAT-006
TC-E2E-02-T FR-CHAT-006
TC-E2E-02-D FR-CHAT-006
TC-E2E-03-M FR-CHAT-012, FR-HEALTH-002
TC-E2E-03-T FR-CHAT-012, FR-HEALTH-002
TC-E2E-03-D FR-CHAT-012, FR-HEALTH-002
TC-E2E-04-M FR-SESSION-001, FR-SESSION-003, FR-SESSION-004, FR-SESSION-005, FR-SESSION-006, FR-SESSION-007
TC-E2E-04-T FR-SESSION-001, FR-SESSION-003, FR-SESSION-004, FR-SESSION-005, FR-SESSION-006, FR-SESSION-007
TC-E2E-04-D FR-SESSION-001, FR-SESSION-003, FR-SESSION-004, FR-SESSION-005, FR-SESSION-006, FR-SESSION-007
TC-E2E-05-M FR-CHAT-008, FR-CHAT-011
TC-E2E-05-T FR-CHAT-008, FR-CHAT-011
TC-E2E-05-D FR-CHAT-008, FR-CHAT-011
TC-E2E-06-M FR-THEME-002
TC-E2E-06-T FR-THEME-002
TC-E2E-06-D FR-THEME-002
TC-E2E-07-M FR-KG-004, FR-KG-005, FR-KG-006
TC-E2E-07-T FR-KG-004, FR-KG-005, FR-KG-006
TC-E2E-07-D FR-KG-004, FR-KG-005, FR-KG-006
TC-E2E-08-M FR-EXTRACT-001, FR-EXTRACT-002, FR-EXTRACT-003, FR-EXTRACT-004
TC-E2E-08-T FR-EXTRACT-001, FR-EXTRACT-002, FR-EXTRACT-003, FR-EXTRACT-004
TC-E2E-08-D FR-EXTRACT-001, FR-EXTRACT-002, FR-EXTRACT-003, FR-EXTRACT-004
TC-E2E-09-M FR-RESP-004, FR-RESP-005, FR-RESP-006, FR-RESP-007, FR-RESP-008, FR-RESP-009, FR-RESP-010, FR-RESP-011, FR-RESP-012
TC-E2E-09-T FR-RESP-003
TC-E2E-09-D FR-RESP-001, FR-RESP-002, FR-SHELL-001
TC-E2E-SMOKE-01 FR-SHELL-007
TC-E2E-SMOKE-02 FR-SHELL-001, FR-SHELL-003, FR-RESP-004

2.4 模块分布

模块 TC 条数(含 E2E 拆分)
SHELL 15
THEME 10
SESSION 18
CHAT 43
RETRIEVAL 7
SOURCE 7
EXTRACT 11
KG 13
RESP 13
HEALTH 8
NFR(PRD §14) 5
E2E 主线/冒烟 29
合计 179

3. 执行说明

3.1 分层运行命令(TEST_PLAN §7)

# L1 后端单元(远端 plantai-bff conda 环境;A 组自测,T 复跑确认)
cd sdk && pytest tests -m unit -q

# L2 后端接口(respx mock 上游;用例见 L2 标注项)
cd sdk && pytest tests -m api -q

# L2s 上游 smoke(仅集成点 M2 与验收前手动各跑一次;远端本机执行)
bash tests/smoke_upstreams.sh

# L3+L4 前端(vitest;远端 ui/)
cd ui && npx vitest run

# L5 E2E(三档视口;dev server 由 playwright webServer 自动拉起或复用已启动的 5180)
cd ui && npx playwright test        # playwright 未安装时:npx playwright@1.49.1 test

3.2 Mock 场景使用(ARCHITECTURE.md §6)

mock server:python sdk/mock/mock_server.py(127.0.0.1:8099),场景经查询参数或请求头 X-Mock-Scenario 切换。E2E 中由测试代码在请求头注入或 mock 路由拦截实现:

scenario 用途 使用用例示例
normal 主流程(SSE 回放 12 帧真实时序) TC-CHAT-023~033、TC-E2E-01/02
empty 空结果(source_count=0、answer 为「未找到…」fallback) TC-SOURCE-007、TC-KG-013、TC-CHAT-027(空态分支)
slow 慢响应/长流(持续视觉反馈断言) TC-CHAT-021、TC-CHAT-029、TC-NFR-002
error 上游故障(502 UPSTREAM_ERROR) TC-CHAT-039、TC-E2E-03
degraded QA 不可用(503 UPSTREAM_DOWN) TC-HEALTH-004/005、TC-EXTRACT-011、TC-KG-012、TC-E2E-03

注意:mock 的场景切换仅覆盖 QA 端点;extraction/kg 的 503 降级在 E2E 中用 Playwright route 拦截模拟。

3.3 真实上游注意事项(DISCOVERY.md)

  1. QA LLM 402 降级(最重要):上游 DeepSeek 余额不足,degraded_execution=true,answer 为 fallback 证据列表而非自然语言答案。涉及自然语言答案内容的断言(TC-CHAT-027 等,标注「依赖充值」)在充值前执行时只验结构不断内容:answer 字段存在、text 块渲染、fallback 文案可见即可。检索/证据/来源/图谱断言(sources、chains、graph 块)不受 402 影响,照常执行。
  2. 耗时:单次 search ≈92s、stream ≈37–38s(DISCOVERY.md §4/§6.8)。真实上游的 L5/L6 用例需把 expect 超时调到 ≥180s(search 模式)/≥120s(chat 模式);BFF 读超时 300s。
  3. 上游只绑 127.0.0.1:L2s 必须在远端本机跑;Mac 本地经 SSH 隧道跑(见脚本注释)。
  4. Neo4j 当选实例 direct-v1-full(bolt 18788 / HTTP 18575),schema canonical;/api/kg/* 依赖 BFF 的 Bolt 直连(上游 kg 路由已死,不探测)。
  5. 抽取上游:8000 正常;7900 为容器(jobs 空属正常,BFF 只在 8000 不可达时回落);新上传任务可能因 MinerU 路径缺失 failed——TC-EXTRACT-008 就是断言该错误文案路径。

3.4 L5 运行环境一次性准备(已验证)

  • Node 18(远端):npx playwright@1.49.1 test 首次运行自动下载 chromium 1148(≈150MB)。实测 CDN 可达(Mac 直连 2.9MB/s)。
  • @playwright/test 模块解析:spec 文件位于 tests/e2e/,在 ui/node_modules 之外,Node 默认解析路径找不到 @playwright/test,会报 ERR_MODULE_NOT_FOUND。已在 Mac 本地验证(playwright 1.61.1 实测复现;1.49.1 CJS 变换同样回退到默认 ESM 解析,机制相同)。运行处须一次性执行(不污染任何 agent 归属文件——node_modules 不入库不同步):
    # 远端,一次性
    cd /home/xzq/5090/teamWorkspace/zh/PlantAI-rag-new
    ln -sfn ui/node_modules node_modules   # 若目录已存在先确认其内容再覆盖
    cd ui && npm install --save-dev @playwright/test@1.49.1   # 写入 package.json 需 PM 裁决,见 §5 未决问题 4
    
    Mac 本地本次验证的等价做法(未改动 ui/package.json):安装到 /tmp/pw-test-runner 后 ln -sfn /tmp/pw-test-runner/node_modules/@playwright/test ui/node_modules/@playwright/test,再 ln -sfn ui/node_modules <repo>/node_modules,以 playwright 1.61.1(匹配本机已缓存 chromium-1228)跑通 6/6。
  • 本地 Node 26 已知问题:playwright 1.49.1 的 module.register() loader 在 Node 26 + 系统高负载下启动极慢/挂起;远端 Node 18 无此问题。本地验证用 playwright 1.61.1 替代。
  • 本机验证结果(M0 冒烟):6 passed(mobile/tablet/desktop × 2 条冒烟)。冒烟断言对 B1 已交付的 AppShell 走真实分支(.app-shell/.top-bar/.status-bar,移动端 .status-bar 按 FR-RESP-004 断言隐藏),M0 占位期走占位回退分支。

4. E2E 骨架交付说明(本波)

文件 内容
ui/playwright.config.js 三项目 mobile(iPhone 14 390×844,触摸+UA)/tablet(iPad 768×1024)/desktop(1440×900);chromium;baseURL http://127.0.0.1:5180;webServer npm run dev -- --strictPort(已有 dev 复用);失败保留 trace/screenshot
tests/e2e/smoke.spec.js 2 条冒烟(TC-E2E-SMOKE-01/02),三档视口各跑一次共 6 条结果。AppShell 断言带 M0 占位回退(B1 交付后自动走真实分支,无需改测试)
tests/smoke_upstreams.sh L2s 冒烟:QA 7920 health/info、抽取 8000 health/jobs、7900 health、Neo4j(HTTP 18575 Cypher 只读查询)、mock 8099 health;PASS/FAIL 表 + 非零退出码;支持 QA_URL/EXTRACT_URL/ALT_URL/MOCK_URL/NEO4J_HTTP/TIMEOUT 环境变量覆盖

全量 L1–L5 执行从 M2 集成后开始(TEST_PLAN §5 轮次纪律);完整 E2E 用例(TC-E2E-01~09)按本文件在后续轮次落为 spec 文件。

5. 未决问题(提请 PM)

  1. 若干 UI 类断言依赖 UI_SPEC 类名(如 agent-dot-item、p-step);若 B1 实现时类名有偏差,需按 UI_SPEC §5.3 对齐(T 不自行改用例契约)。
  2. TC-EXTRACT-005/007(上传进度/超限)依赖 B2/B3 上传 UI 与 50MB 校验落点(前端或 BFF)——已按「前端拦截或 422」双路径写预期,无需改用例。
  3. TC-KG 节点详情在 Neo4j 不可用时的属性来源为「消息内 graph 块已有属性」;graph 块节点若不含属性字段,该断言在实现时需以实际块结构微调步骤(预期语义不变)。
  4. L5 依赖安装需 PM 裁决:tests/e2e/ 的 spec 需要解析 @playwright/test,而它位于 ui/node_modules 之外。两个方案二选一:(a) 远端一次性 npm install --save-dev @playwright/test@1.49.1(写入 B1 归属的 ui/package.json,需 PM 批准);(b) 保持 npx playwright@1.49.1 并在远端建 node_modules 根符号链接 + @playwright/test 符号链接(不改 package.json,M2 前由 T/PM 在远端一次性执行)。本波按 (b) 在 Mac 本地验证通过(6/6 pass),建议远端集成点采用 (b)。
  5. task 验证命令差异:任务书给的验证命令 npx playwright test(无版本)在本机无已安装 playwright 时会从 npm 拉最新版;远端 Node 18 + npx playwright@1.49.1 仍会拉 1.49.1 的 chromium 1148(≈150MB,CDN 实测可达)。已在本文件 §3.4 记录完整步骤。