212 lines
6.8 KiB
TypeScript
212 lines
6.8 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
import { AgentSystemDocumentsSettings } from '@/components/settings/AgentSystemDocumentsSettings';
|
|
import i18n from '@/i18n';
|
|
|
|
const hostApiFetchMock = vi.fn();
|
|
const toastSuccessMock = vi.fn();
|
|
const toastErrorMock = vi.fn();
|
|
|
|
vi.mock('@/lib/host-api', () => ({
|
|
hostApiFetch: (...args: unknown[]) => hostApiFetchMock(...args),
|
|
}));
|
|
|
|
vi.mock('sonner', () => ({
|
|
toast: {
|
|
success: (...args: unknown[]) => toastSuccessMock(...args),
|
|
error: (...args: unknown[]) => toastErrorMock(...args),
|
|
},
|
|
}));
|
|
|
|
type TestDocumentKind = 'soul' | 'identity' | 'user' | 'agent' | 'tool' | 'heartbeat' | 'boot';
|
|
|
|
function makeSnapshot(agentId = 'main', overrides?: Partial<Record<TestDocumentKind, string>>) {
|
|
const documents = [
|
|
{
|
|
kind: 'soul',
|
|
fileName: 'SOUL.md',
|
|
path: `/tmp/${agentId}/SOUL.md`,
|
|
exists: true,
|
|
source: 'workspace',
|
|
content: overrides?.soul ?? '# Soul\n',
|
|
size: 7,
|
|
updatedAt: 1,
|
|
templateAvailable: true,
|
|
templatePath: '/runtime/SOUL.md',
|
|
},
|
|
{
|
|
kind: 'identity',
|
|
fileName: 'IDENTITY.md',
|
|
path: `/tmp/${agentId}/IDENTITY.md`,
|
|
exists: true,
|
|
source: 'workspace',
|
|
content: overrides?.identity ?? '# Identity\n',
|
|
size: 11,
|
|
updatedAt: 1,
|
|
templateAvailable: true,
|
|
templatePath: '/runtime/IDENTITY.md',
|
|
},
|
|
{
|
|
kind: 'user',
|
|
fileName: 'USER.md',
|
|
path: `/tmp/${agentId}/USER.md`,
|
|
exists: true,
|
|
source: 'workspace',
|
|
content: overrides?.user ?? '# User\n',
|
|
size: 7,
|
|
updatedAt: 1,
|
|
templateAvailable: true,
|
|
templatePath: '/runtime/USER.md',
|
|
},
|
|
{
|
|
kind: 'agent',
|
|
fileName: 'AGENTS.md',
|
|
path: `/tmp/${agentId}/AGENTS.md`,
|
|
exists: true,
|
|
source: 'workspace',
|
|
content: overrides?.agent ?? '# Agent\n',
|
|
size: 8,
|
|
updatedAt: 1,
|
|
templateAvailable: true,
|
|
templatePath: '/runtime/AGENTS.md',
|
|
},
|
|
{
|
|
kind: 'tool',
|
|
fileName: 'TOOLS.md',
|
|
path: `/tmp/${agentId}/TOOLS.md`,
|
|
exists: false,
|
|
source: 'template',
|
|
content: overrides?.tool ?? '# Tool\n',
|
|
size: 0,
|
|
updatedAt: null,
|
|
templateAvailable: true,
|
|
templatePath: '/runtime/TOOLS.md',
|
|
},
|
|
{
|
|
kind: 'heartbeat',
|
|
fileName: 'HEARTBEAT.md',
|
|
path: `/tmp/${agentId}/HEARTBEAT.md`,
|
|
exists: false,
|
|
source: 'template',
|
|
content: overrides?.heartbeat ?? '# Heartbeat\n',
|
|
size: 0,
|
|
updatedAt: null,
|
|
templateAvailable: true,
|
|
templatePath: '/runtime/HEARTBEAT.md',
|
|
},
|
|
{
|
|
kind: 'boot',
|
|
fileName: 'BOOT.md',
|
|
path: `/tmp/${agentId}/BOOT.md`,
|
|
exists: false,
|
|
source: 'template',
|
|
content: overrides?.boot ?? '# Boot\n',
|
|
size: 0,
|
|
updatedAt: null,
|
|
templateAvailable: true,
|
|
templatePath: '/runtime/BOOT.md',
|
|
},
|
|
];
|
|
|
|
return {
|
|
success: true,
|
|
selectedAgentId: agentId,
|
|
defaultAgentId: 'main',
|
|
agents: [
|
|
{ id: 'main', name: 'Main', isDefault: true, workspace: '~/.openclaw/workspace' },
|
|
{ id: 'coder', name: 'Coder', isDefault: false, workspace: '~/.openclaw/workspace-coder' },
|
|
],
|
|
documents,
|
|
paths: {
|
|
workspace: `/tmp/${agentId}`,
|
|
templateDir: '/runtime',
|
|
},
|
|
};
|
|
}
|
|
|
|
describe('AgentSystemDocumentsSettings', () => {
|
|
beforeEach(async () => {
|
|
vi.clearAllMocks();
|
|
await i18n.changeLanguage('zh');
|
|
hostApiFetchMock.mockImplementation((path: string, init?: { method?: string; body?: string }) => {
|
|
if (path === '/api/agent-system-documents?agentId=coder') {
|
|
return Promise.resolve(makeSnapshot('coder', { soul: '# Coder soul\n' }));
|
|
}
|
|
if (path === '/api/agent-system-documents/soul' && init?.method === 'PUT') {
|
|
const body = JSON.parse(init.body || '{}') as { content?: string };
|
|
return Promise.resolve(makeSnapshot('main', { soul: body.content ?? '' }));
|
|
}
|
|
if (path === '/api/agent-system-documents/tool/reset' && init?.method === 'POST') {
|
|
return Promise.resolve(makeSnapshot('main', { tool: '# Tool template\n' }));
|
|
}
|
|
return Promise.resolve(makeSnapshot('main'));
|
|
});
|
|
});
|
|
|
|
it('loads system documents and switches selected agent', async () => {
|
|
render(<AgentSystemDocumentsSettings />);
|
|
|
|
expect(await screen.findByTestId('agent-system-document-editor')).toHaveValue('# Soul\n');
|
|
expect(screen.getByTestId('agent-system-document-tab-soul')).toHaveTextContent('SOUL.md');
|
|
|
|
fireEvent.change(screen.getByTestId('agent-system-document-agent-select'), {
|
|
target: { value: 'coder' },
|
|
});
|
|
|
|
await waitFor(() => {
|
|
expect(hostApiFetchMock).toHaveBeenCalledWith('/api/agent-system-documents?agentId=coder');
|
|
});
|
|
expect(await screen.findByTestId('agent-system-document-editor')).toHaveValue('# Coder soul\n');
|
|
});
|
|
|
|
it('saves the edited selected document', async () => {
|
|
render(<AgentSystemDocumentsSettings />);
|
|
|
|
const editor = await screen.findByTestId('agent-system-document-editor');
|
|
fireEvent.change(editor, { target: { value: '# Updated soul\n' } });
|
|
fireEvent.click(screen.getByTestId('agent-system-document-save'));
|
|
|
|
await waitFor(() => {
|
|
expect(hostApiFetchMock).toHaveBeenCalledWith(
|
|
'/api/agent-system-documents/soul',
|
|
expect.objectContaining({
|
|
method: 'PUT',
|
|
body: JSON.stringify({ agentId: 'main', content: '# Updated soul\n' }),
|
|
}),
|
|
);
|
|
});
|
|
expect(toastSuccessMock).toHaveBeenCalledWith('SOUL.md 已保存');
|
|
});
|
|
|
|
it('restores the selected tool document from template', async () => {
|
|
render(<AgentSystemDocumentsSettings />);
|
|
|
|
await screen.findByTestId('agent-system-document-editor');
|
|
fireEvent.click(screen.getByTestId('agent-system-document-tab-tool'));
|
|
fireEvent.click(screen.getByTestId('agent-system-document-reset'));
|
|
|
|
await waitFor(() => {
|
|
expect(hostApiFetchMock).toHaveBeenCalledWith(
|
|
'/api/agent-system-documents/tool/reset',
|
|
expect.objectContaining({
|
|
method: 'POST',
|
|
body: JSON.stringify({ agentId: 'main' }),
|
|
}),
|
|
);
|
|
});
|
|
expect(await screen.findByTestId('agent-system-document-editor')).toHaveValue('# Tool template\n');
|
|
});
|
|
|
|
it('shows the OpenClaw workspace control documents', async () => {
|
|
render(<AgentSystemDocumentsSettings />);
|
|
|
|
await screen.findByTestId('agent-system-document-editor');
|
|
|
|
expect(screen.getByTestId('agent-system-document-tab-identity')).toHaveTextContent('IDENTITY.md');
|
|
expect(screen.getByTestId('agent-system-document-tab-user')).toHaveTextContent('USER.md');
|
|
expect(screen.getByTestId('agent-system-document-tab-heartbeat')).toHaveTextContent('HEARTBEAT.md');
|
|
expect(screen.getByTestId('agent-system-document-tab-boot')).toHaveTextContent('BOOT.md');
|
|
});
|
|
|
|
});
|