147 lines
5.2 KiB
TypeScript
147 lines
5.2 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
||
import { render, screen } from '@testing-library/react';
|
||
import { ChatMessage } from '@/pages/Chat/ChatMessage';
|
||
import type { RawMessage } from '@/stores/chat';
|
||
import { appendBusinessResponseGuidance } from '../../shared/business-guidance';
|
||
|
||
describe('ChatMessage attachment dedupe', () => {
|
||
it('keeps attachment-only assistant replies visible even when process attachments are suppressed', () => {
|
||
const message: RawMessage = {
|
||
role: 'assistant',
|
||
content: [],
|
||
_attachedFiles: [
|
||
{
|
||
fileName: 'artifact.png',
|
||
mimeType: 'image/png',
|
||
fileSize: 0,
|
||
preview: '/tmp/artifact.png',
|
||
filePath: '/tmp/artifact.png',
|
||
source: 'tool-result',
|
||
},
|
||
],
|
||
};
|
||
|
||
render(
|
||
<ChatMessage
|
||
message={message}
|
||
suppressProcessAttachments
|
||
/>,
|
||
);
|
||
|
||
expect(screen.getByAltText('artifact.png')).toBeInTheDocument();
|
||
});
|
||
});
|
||
|
||
describe('ChatMessage LaTeX rendering', () => {
|
||
it('renders inline `$...$` math with KaTeX', () => {
|
||
const message: RawMessage = {
|
||
role: 'assistant',
|
||
content: 'Mass-energy equivalence: $E=mc^2$ is famous.',
|
||
};
|
||
const { container } = render(<ChatMessage message={message} />);
|
||
expect(container.querySelector('.katex')).not.toBeNull();
|
||
});
|
||
|
||
it('renders display `$$...$$` math as a block', () => {
|
||
const message: RawMessage = {
|
||
role: 'assistant',
|
||
content: 'Definite integral:\n\n$$\n\\int_0^1 x\\,dx = \\frac{1}{2}\n$$\n',
|
||
};
|
||
const { container } = render(<ChatMessage message={message} />);
|
||
expect(container.querySelector('.katex-display')).not.toBeNull();
|
||
});
|
||
|
||
it('renders `\\(...\\)` inline math (OpenAI-style escaping)', () => {
|
||
const message: RawMessage = {
|
||
role: 'assistant',
|
||
content: 'Quadratic formula: \\(x = \\frac{-b \\pm \\sqrt{b^2-4ac}}{2a}\\).',
|
||
};
|
||
const { container } = render(<ChatMessage message={message} />);
|
||
expect(container.querySelector('.katex')).not.toBeNull();
|
||
expect(container.querySelector('.katex-display')).toBeNull();
|
||
});
|
||
|
||
it('renders `\\[...\\]` block math (OpenAI-style escaping)', () => {
|
||
const message: RawMessage = {
|
||
role: 'assistant',
|
||
content: 'Sum formula:\n\n\\[\\sum_{i=1}^n i = \\frac{n(n+1)}{2}\\]',
|
||
};
|
||
const { container } = render(<ChatMessage message={message} />);
|
||
expect(container.querySelector('.katex-display')).not.toBeNull();
|
||
});
|
||
|
||
it('does not rewrite `\\(` inside code fences', () => {
|
||
const message: RawMessage = {
|
||
role: 'assistant',
|
||
content: 'Code sample:\n\n```\nprintf("\\(hello\\)")\n```\n',
|
||
};
|
||
const { container } = render(<ChatMessage message={message} />);
|
||
expect(container.textContent).toContain('\\(hello\\)');
|
||
expect(container.querySelector('.katex')).toBeNull();
|
||
});
|
||
});
|
||
|
||
describe('ChatMessage business answer rendering', () => {
|
||
it('renders a business summary panel for structured operational replies', () => {
|
||
const message: RawMessage = {
|
||
role: 'assistant',
|
||
content: [
|
||
'状态:需处理,携程渠道房态与 PMS 不一致。',
|
||
'依据:携程 0508 房型显示可售 3 间,PMS 为 0 间。',
|
||
'影响:可能产生超售风险。',
|
||
'下一步:请先暂停该房型自动售卖,并复核渠道登录态。',
|
||
].join('\n'),
|
||
};
|
||
|
||
render(<ChatMessage message={message} />);
|
||
|
||
const panel = screen.getByTestId('business-answer-panel');
|
||
expect(panel).toHaveTextContent('业务摘要');
|
||
expect(panel).toHaveTextContent('需处理,携程渠道房态与 PMS 不一致');
|
||
expect(panel).toHaveTextContent('携程 0508 房型显示可售 3 间');
|
||
expect(panel).toHaveTextContent('请先暂停该房型自动售卖');
|
||
});
|
||
|
||
it('does not add the business panel to ordinary assistant chat', () => {
|
||
const message: RawMessage = {
|
||
role: 'assistant',
|
||
content: '你好,我可以帮你处理问题。',
|
||
};
|
||
|
||
render(<ChatMessage message={message} />);
|
||
|
||
expect(screen.queryByTestId('business-answer-panel')).not.toBeInTheDocument();
|
||
});
|
||
});
|
||
|
||
describe('ChatMessage markdown rendering', () => {
|
||
it('hides legacy injected business guidance from visible user messages', () => {
|
||
const message: RawMessage = {
|
||
role: 'user',
|
||
content: appendBusinessResponseGuidance('会话中的 markdown 结构化,表格也可以加强 UI 渲染'),
|
||
};
|
||
|
||
render(<ChatMessage message={message} />);
|
||
|
||
expect(screen.getByText('会话中的 markdown 结构化,表格也可以加强 UI 渲染')).toBeInTheDocument();
|
||
expect(screen.queryByText(/YINIAN_BUSINESS_RESPONSE_GUIDANCE/)).not.toBeInTheDocument();
|
||
expect(screen.queryByText(/请按智念业务员工/)).not.toBeInTheDocument();
|
||
});
|
||
|
||
it('renders markdown tables inside the enhanced table shell', () => {
|
||
const message: RawMessage = {
|
||
role: 'assistant',
|
||
content: [
|
||
'| 渠道 | 房型 | 状态 |',
|
||
'| --- | --- | --- |',
|
||
'| 携程 | 豪华大床房 | 需复核 |',
|
||
].join('\n'),
|
||
};
|
||
|
||
render(<ChatMessage message={message} />);
|
||
|
||
expect(screen.getByTestId('markdown-table-scroll')).toBeInTheDocument();
|
||
expect(screen.getByTestId('markdown-table')).toHaveTextContent('豪华大床房');
|
||
});
|
||
});
|