- BFF 薄适配层(FastAPI,8090):QA 代理/SSE 中继/Neo4j 直连/抽取代理/会话持久化 - Vue3 前端:五相位检索流水线、文献来源与证据链、知识图谱、提取任务四 tab - 四档响应式(桌面/笔记本/平板/手机)、深浅主题 - 81 个 FR 全部实现,测试追溯矩阵 81/81,后端 121 测/前端 156 测 - 真实凭据已脱敏,运行时配置在 backend/.env(不入 git) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
100 lines
3.7 KiB
JavaScript
100 lines
3.7 KiB
JavaScript
// retrieval.pane.test.js —— B3 单测:RetrievalPane(相位筛选、空态)
|
||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||
import { createPinia, setActivePinia, getActivePinia } from 'pinia';
|
||
import { useBenchStore, normalizeStageEvent } from '../../../../frontend/src/stores/bench.js';
|
||
import { STAGE_PHASE } from '../../../../frontend/src/i18n/labels.js';
|
||
|
||
vi.mock('../../../../frontend/src/api/extraction.js', () => ({
|
||
listJobs: vi.fn(),
|
||
getJob: vi.fn(),
|
||
uploadPaper: vi.fn(),
|
||
getRunLog: vi.fn(),
|
||
getProcessingStatus: vi.fn(),
|
||
getSummary: vi.fn(),
|
||
}));
|
||
|
||
const frames = [
|
||
{ stage: 'understanding_started', label: '开始理解问题', details: {} },
|
||
{ stage: 'entities_identified', label: '已识别实体', details: { entities: ['SPL', 'OsSPL14'] } },
|
||
{ stage: 'concepts_found', label: '发现相关概念', details: { concept_count: 3 } },
|
||
{ stage: 'evidence_review_completed', label: '证据审核完成', details: { accepted_count: 4 } },
|
||
{ stage: 'completed', label: '检索完成', details: {} },
|
||
];
|
||
|
||
describe('RetrievalPane 相位筛选(FR-RETRIEVAL-002)', () => {
|
||
let store;
|
||
|
||
beforeEach(() => {
|
||
setActivePinia(createPinia());
|
||
store = useBenchStore();
|
||
for (const f of frames) {
|
||
const phase = STAGE_PHASE[f.stage] || 'retrieval';
|
||
store.recordStageEvent({ ...f, phase, at: '2026-08-25T09:41:00Z' });
|
||
}
|
||
});
|
||
|
||
it('阶段卡数量与 progress 事件数一致(FR-RETRIEVAL-001)', () => {
|
||
expect(store.stageEvents).toHaveLength(frames.length);
|
||
});
|
||
|
||
it('全部:显示全部卡片', () => {
|
||
expect(store.filteredStageEvents).toHaveLength(5);
|
||
});
|
||
|
||
it('筛选 planning:仅 2 张', () => {
|
||
store.setPhaseFilter('planning');
|
||
const out = store.filteredStageEvents;
|
||
expect(out).toHaveLength(2);
|
||
expect(out.every((e) => e.phase === 'planning')).toBe(true);
|
||
});
|
||
|
||
it('筛选 retrieval / evidence / generation:各归其位', () => {
|
||
store.setPhaseFilter('retrieval');
|
||
expect(store.filteredStageEvents).toHaveLength(1);
|
||
expect(store.filteredStageEvents[0].stage).toBe('concepts_found');
|
||
|
||
store.setPhaseFilter('evidence');
|
||
expect(store.filteredStageEvents).toHaveLength(1);
|
||
expect(store.filteredStageEvents[0].stage).toBe('evidence_review_completed');
|
||
});
|
||
|
||
it('筛选后无匹配 → 空列表(不渲染假卡片)', () => {
|
||
store.setPhaseFilter('generation');
|
||
expect(store.filteredStageEvents).toHaveLength(0);
|
||
});
|
||
|
||
it('未知 stage 兜底 phase=retrieval(API.md §4 规则)', () => {
|
||
const n = normalizeStageEvent({ stage: 'mystery_stage', label: 'mystery_stage' });
|
||
expect(n.phase).toBe('retrieval');
|
||
});
|
||
});
|
||
|
||
describe('RetrievalPane 空态(FR-RETRIEVAL-006)', () => {
|
||
it('未提问时无阶段卡(不显示假卡片)', () => {
|
||
setActivePinia(createPinia());
|
||
const store = useBenchStore();
|
||
expect(store.stageEvents).toHaveLength(0);
|
||
expect(store.filteredStageEvents).toHaveLength(0);
|
||
});
|
||
|
||
it('B2 chat store 暴露事件时优先采用其数据源', () => {
|
||
setActivePinia(createPinia());
|
||
const store = useBenchStore();
|
||
const chatStore = {
|
||
currentEvents: [
|
||
{ stage: 'entities_identified', phase: 'planning', label: '已识别实体', details: {}, at: '2026-08-25T09:41:01Z' },
|
||
],
|
||
};
|
||
getActivePinia()._s.set('chat', chatStore);
|
||
expect(store.stageEvents).toHaveLength(1);
|
||
expect(store.stageEvents[0].label).toBe('已识别实体');
|
||
});
|
||
|
||
it('B2 未就绪时回退本地队列', () => {
|
||
setActivePinia(createPinia());
|
||
const store = useBenchStore();
|
||
store.recordStageEvent({ stage: 'concepts_found', phase: 'retrieval', label: '发现相关概念', details: {}, at: '2026-08-25T09:41:02Z' });
|
||
expect(store.stageEvents).toHaveLength(1);
|
||
});
|
||
});
|