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 { 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 { 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() expect(screen.getByText('暂无报告')).toBeInTheDocument() expect(screen.getByText('暂无会话')).toBeInTheDocument() }) it('renders summary cards, dimension scores, findings, highlights and recommendations', () => { render() 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() 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() fireEvent.click(screen.getByText(/导出 Markdown/)) await waitFor(() => expect(downloadReportMarkdown).toHaveBeenCalledWith('eval-1')) }) })