AgentEvalTool/frontend/web/src/components/intelligent_eval/EvalReport.test.tsx
sinohqb 4534df7e7a test: 五个零覆盖组件补全测试(Phase 4.23)
锁定 Phase 4 读取接缝收敛后的纯渲染与关键交互契约;五个组件
此前从未被 vitest 覆盖,现全量补齐(25 个测试)。

- DecisionProcess(6):加载/空态/详情展开/上下文 JSON 折叠、
  轮询门控(executing 轮询、completed 停轮询)、补充日志触发。
- EvalOverview(6):planning / pending_approval / executing
  三种状态的渲染分支;plan 维度/虚拟用户/时间分布/完成标准;
  会话进度与过期文案。
- EvalReport(4):报告加载/空态、会话折叠、SessionMessages
  懒加载、Markdown 导出(downloadBlob 接缝)。
- ConfigSnapshots(4):列表与类型标签、详情展开、快照对比
  两选一校验、空态与差异渲染。
- TaskQueueMonitor(5):统计芯片、状态过滤、刷新按钮、
  轮询定时器(vi.useFakeTimers + advanceTimersByTimeAsync)、
  空态。

测试沿用 sessionReducer 先例:vi.mock 工厂注入 readSlot /
API 方法,按 reducer 形状构造 fixture;antd 双汉字按钮
(详情/导出/查看)使用 /\s*/ 正则匹配 autoInsertSpace 空格。
2026-08-24 05:54:28 +08:00

140 lines
4.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import type { IntelligentEval, IntelligentEvalReport } from '../../api'
import EvalReport from './EvalReport'
const downloadReportMarkdown = vi.fn()
const listMessages = vi.fn()
vi.mock('../../api', () => ({
intelligentEvalsApi: {
downloadReportMarkdown: (...args: unknown[]) => downloadReportMarkdown(...args),
listMessages: (...args: unknown[]) => listMessages(...args),
},
}))
function makeReport(overrides: Partial<IntelligentEvalReport> = {}): IntelligentEvalReport {
return {
summary: '整体服务态度良好,价保场景存在流程遗漏',
scores: { overall: 4.2, dimensions: { 服务态度: 4.5, 专业度: 3.2 } },
findings: [
{
issue: '价保流程未主动告知凭证',
severity: 'high',
dimension: '专业度',
evidence: [{ session_id: 'sess-aaaaaaaa', turn_index: 2, user_said: '需要什么凭证', assistant_replied: '您好' }],
suggestion: '补充 SOP 提示',
related_sop: 'SOP-12',
},
{
issue: '回复偏慢',
severity: 'medium',
dimension: '响应时效',
evidence: [],
},
],
highlights: [{ description: '语气友好', dimension: '服务态度' }],
priority_recommendations: ['优先整改价保流程'],
...overrides,
}
}
function makeEval(overrides: Partial<IntelligentEval> = {}): IntelligentEval {
return {
id: 'eval-1',
name: '24h 评估',
target_id: 't-1',
status: 'completed',
goal: 'goal',
seeds: {},
intent: '',
role_description: '',
plan: null,
plan_feedback: null,
time_window_hours: 24,
report: makeReport(),
created_at: null,
updated_at: null,
started_at: null,
completed_at: '2026-01-02T00:00:00Z',
session_count: 1,
completed_sessions: 1,
sessions: [
{
id: 's-1',
eval_id: 'eval-1',
target_id: 't-1',
persona: { name: '老客户' },
goal: '完成退货',
dimension: '售后',
status: 'completed',
verdict: null,
turn_count: 5,
created_at: '2026-01-01T08:10:00Z',
closed_at: '2026-01-01T08:25:00Z',
},
],
...overrides,
}
}
beforeEach(() => {
vi.clearAllMocks()
})
afterEach(() => {
cleanup()
})
describe('EvalReport', () => {
it('shows empty states when there is no report and no sessions', () => {
render(<EvalReport ev={makeEval({ report: null, sessions: [] })} />)
expect(screen.getByText('暂无报告')).toBeInTheDocument()
expect(screen.getByText('暂无会话')).toBeInTheDocument()
})
it('renders summary cards, dimension scores, findings, highlights and recommendations', () => {
render(<EvalReport ev={makeEval()} />)
expect(screen.getByText('4.2')).toBeInTheDocument()
expect(screen.getByText('问题发现2')).toBeInTheDocument()
expect(screen.getAllByText('服务态度').length).toBeGreaterThanOrEqual(1)
expect(screen.getByText('4.5')).toBeInTheDocument()
expect(screen.getByText('3.2')).toBeInTheDocument()
expect(screen.getByText('整体服务态度良好,价保场景存在流程遗漏')).toBeInTheDocument()
expect(screen.getByText('价保流程未主动告知凭证')).toBeInTheDocument()
expect(screen.getByText('回复偏慢')).toBeInTheDocument()
expect(screen.getByText('语气友好')).toBeInTheDocument()
expect(screen.getByText('优先整改价保流程')).toBeInTheDocument()
expect(document.body.textContent).toContain('高 1 · 中 1 · 低 0')
})
it('expanding a session panel loads its messages', async () => {
listMessages.mockResolvedValue({
data: {
messages: [
{ id: 'm-1', session_id: 's-1', role: 'user', content: '我想退货', latency_ms: null, created_at: null },
{ id: 'm-2', session_id: 's-1', role: 'assistant', content: '好的,请提供订单号', latency_ms: 320, created_at: null },
],
},
})
render(<EvalReport ev={makeEval()} />)
expect(listMessages).not.toHaveBeenCalled()
fireEvent.click(screen.getByText('老客户'))
await waitFor(() => expect(listMessages).toHaveBeenCalledWith('eval-1', 's-1'))
expect(await screen.findByText('我想退货')).toBeInTheDocument()
expect(screen.getByText('好的,请提供订单号')).toBeInTheDocument()
expect(screen.getByText('320ms')).toBeInTheDocument()
})
it('exports the markdown report', async () => {
downloadReportMarkdown.mockResolvedValue(undefined)
render(<EvalReport ev={makeEval()} />)
fireEvent.click(screen.getByText(/导出 Markdown/))
await waitFor(() => expect(downloadReportMarkdown).toHaveBeenCalledWith('eval-1'))
})
})