- Updated chat message types to include tool statuses. - Enhanced localization files for English, Thai, and Chinese to support new tool status messages. - Modified HomePage and SkillsPage components to handle tool statuses in chat messages. - Implemented tool status merging and updating logic in the chat store. - Added handling for tool status events in the gateway event processing. - Created tests for chat message rendering with tool statuses and skill installation shortcuts. - Improved gateway event dispatching for tool lifecycle events.
154 lines
4.8 KiB
TypeScript
154 lines
4.8 KiB
TypeScript
import React from 'react';
|
||
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||
import { setLocale } from '../src/i18n';
|
||
import ChatMessageList from '../src/components/chat/ChatMessageList';
|
||
|
||
const mocks = vi.hoisted(() => ({
|
||
apiOpenSkillPath: vi.fn(),
|
||
apiOpenSkillReadme: vi.fn(),
|
||
writeText: vi.fn(),
|
||
}));
|
||
|
||
vi.mock('../src/lib/skills-api', () => ({
|
||
apiOpenSkillPath: mocks.apiOpenSkillPath,
|
||
apiOpenSkillReadme: mocks.apiOpenSkillReadme,
|
||
}));
|
||
|
||
describe('ChatMessageList', () => {
|
||
beforeEach(() => {
|
||
setLocale('zh');
|
||
vi.clearAllMocks();
|
||
mocks.apiOpenSkillPath.mockResolvedValue(undefined);
|
||
mocks.apiOpenSkillReadme.mockResolvedValue(undefined);
|
||
mocks.writeText.mockResolvedValue(undefined);
|
||
Object.defineProperty(window.navigator, 'clipboard', {
|
||
value: {
|
||
writeText: mocks.writeText,
|
||
},
|
||
configurable: true,
|
||
});
|
||
});
|
||
|
||
it('renders standalone streaming tool status cards when there is no streaming assistant text yet', () => {
|
||
render(
|
||
<ChatMessageList
|
||
messages={[
|
||
{
|
||
id: 'user-1',
|
||
role: 'user',
|
||
name: '你',
|
||
time: '10:00',
|
||
content: '帮我安装这个 skill',
|
||
},
|
||
]}
|
||
streamingTools={[
|
||
{
|
||
id: 'tool-1',
|
||
toolCallId: 'skills.install:run-1',
|
||
name: 'skills.install',
|
||
status: 'running',
|
||
summary: 'Installing minimax-xlsx',
|
||
updatedAt: Date.now(),
|
||
},
|
||
]}
|
||
/>,
|
||
);
|
||
|
||
expect(screen.getByText('正在执行工具...')).toBeTruthy();
|
||
expect(screen.getByText('安装 Skill')).toBeTruthy();
|
||
expect(screen.getByText('运行中')).toBeTruthy();
|
||
expect(screen.getByText('Installing minimax-xlsx')).toBeTruthy();
|
||
});
|
||
|
||
it('renders streaming tool status cards above a streaming assistant message', () => {
|
||
render(
|
||
<ChatMessageList
|
||
messages={[
|
||
{
|
||
id: 'assistant-stream',
|
||
role: 'assistant',
|
||
name: 'YINIAN',
|
||
time: '10:01',
|
||
content: '已安装完成',
|
||
isStreaming: true,
|
||
},
|
||
]}
|
||
streamingTools={[
|
||
{
|
||
id: 'tool-2',
|
||
toolCallId: 'browser.open_url:run-2',
|
||
name: 'browser.open_url',
|
||
status: 'completed',
|
||
durationMs: 1200,
|
||
summary: '已为你打开 http://www.baidu.com/',
|
||
updatedAt: Date.now(),
|
||
},
|
||
]}
|
||
/>,
|
||
);
|
||
|
||
expect(screen.getByText('打开网页')).toBeTruthy();
|
||
expect(screen.getByText('已完成')).toBeTruthy();
|
||
expect(screen.getByText('1.2s')).toBeTruthy();
|
||
expect(screen.getByText('已为你打开 http://www.baidu.com/')).toBeTruthy();
|
||
expect(screen.getByText('已安装完成')).toBeTruthy();
|
||
});
|
||
|
||
it('renders persistent skill-install tool cards with follow-up actions', async () => {
|
||
render(
|
||
<ChatMessageList
|
||
messages={[
|
||
{
|
||
id: 'assistant-1',
|
||
role: 'assistant',
|
||
name: 'YINIAN',
|
||
time: '10:03',
|
||
content: '已安装并启用 skill minimax-xlsx(github-url)。位置:/tmp/minimax-xlsx',
|
||
toolStatuses: [
|
||
{
|
||
id: 'tool-3',
|
||
toolCallId: 'skills.install:run-3',
|
||
name: 'skills.install',
|
||
status: 'completed',
|
||
summary: '已安装并启用 skill minimax-xlsx(github-url)。位置:/tmp/minimax-xlsx',
|
||
durationMs: 980,
|
||
updatedAt: Date.now(),
|
||
input: {
|
||
kind: 'github-url',
|
||
url: 'https://github.com/MiniMax-AI/skills/blob/main/skills/minimax-xlsx/SKILL.md',
|
||
},
|
||
result: {
|
||
slug: 'minimax-xlsx',
|
||
source: 'github-url',
|
||
baseDir: '/tmp/minimax-xlsx',
|
||
},
|
||
},
|
||
],
|
||
},
|
||
]}
|
||
/>,
|
||
);
|
||
|
||
expect(screen.getAllByText('已安装并启用 skill minimax-xlsx(github-url)。位置:/tmp/minimax-xlsx')).toHaveLength(1);
|
||
expect(screen.getByText('Skill')).toBeTruthy();
|
||
expect(screen.getByText('/tmp/minimax-xlsx')).toBeTruthy();
|
||
expect(screen.getByText('后续动作')).toBeTruthy();
|
||
|
||
fireEvent.click(screen.getByText('打开目录'));
|
||
await waitFor(() => {
|
||
expect(mocks.apiOpenSkillPath).toHaveBeenCalledWith(
|
||
'minimax-xlsx',
|
||
'minimax-xlsx',
|
||
'/tmp/minimax-xlsx',
|
||
);
|
||
});
|
||
|
||
fireEvent.click(screen.getByText('复制路径'));
|
||
await waitFor(() => {
|
||
expect(mocks.writeText).toHaveBeenCalledWith('/tmp/minimax-xlsx');
|
||
});
|
||
expect(screen.getByText('路径已复制')).toBeTruthy();
|
||
});
|
||
});
|