feat: 改造云智能体桌面编辑与试用工作区

This commit is contained in:
brother7 committed 2026-09-12 17:39:31 +08:00
1 parent 6b92591129
commit dca6deab29
16 files changed
+623 -222

No files matched your search

+84 -5
View File
@@ -26,8 +26,13 @@ beforeEach(() => {
api.recovery.mockResolvedValue({ recent: null, pending: [] });
api.remember.mockResolvedValue({ saved: true });
api.list.mockResolvedValue({ agents: [draft], next_cursor: null });
api.call.mockImplementation(async (operation: string) => operation === 'knowledge'
? { databases: [], models: [] } : { models: [], resources: {}, pricing: null });
api.call.mockImplementation(async (operation: string) => {
if (operation === 'knowledge') return { databases: [], models: [] };
if (operation === 'resources') return { mcps: [], skills: [], subagents: [] };
if (operation === 'threads') return { threads: [], next_offset: null };
if (operation === 'budget') return { request_limit_points: null, daily_limit_points: null, daily_committed_points: '0.00' };
return { models: [], resources: {}, pricing: null };
});
});
afterEach(cleanup);
@@ -45,6 +50,7 @@ it('restores the exact previous thread and presents an uncertain operation witho
});
open();
await screen.findByText('恢复了旧对话');
fireEvent.click(screen.getByRole('button', { name: '查看历史对话' }));
expect(screen.getByLabelText('历史对话')).toHaveValue('older-thread');
expect(api.resolvePending).not.toHaveBeenCalled();
expect(api.call.mock.calls.some(call => call[0] === 'submit')).toBe(false);
@@ -162,11 +168,84 @@ it('keeps unsent preview text when configuration changes and waits before switch
fireEvent.change(prompt, { target: { value: '还未发送的问题' } });
fireEvent.change(screen.getByLabelText('名称'), { target: { value: '新的名称' } });
expect(prompt).toHaveValue('还未发送的问题');
expect(screen.getByText(/预览仍使用已保存的修订 1/)).toBeVisible();
expect(screen.getByRole('button', { name: '保存草稿' })).toBeDisabled();
fireEvent.change(prompt, { target: { value: '' } });
expect(screen.getByText(/保存不会清空当前试用/)).toBeVisible();
await waitFor(() => expect(screen.getByRole('button', { name: '保存草稿' })).toBeEnabled());
fireEvent.click(screen.getByRole('button', { name: '保存草稿' }));
await screen.findByRole('button', { name: '已保存' });
expect(api.save).toHaveBeenCalledTimes(1);
expect(screen.getByLabelText('消息')).toBe(prompt);
expect(prompt).toHaveValue('还未发送的问题');
expect(screen.getByText('草稿预览 · 修订 1')).toBeVisible();
expect(screen.getByRole('button', { name: '发送', exact: true })).toBeDisabled();
fireEvent.click(screen.getByRole('button', { name: '用最新草稿试用' }));
expect(screen.getByText('草稿预览 · 修订 2')).toBeVisible();
expect(prompt).toHaveValue('还未发送的问题');
expect(screen.getByRole('button', { name: '发送', exact: true })).toBeEnabled();
});
it('keeps preview text across mode switches and saves a budget independently of the draft', async () => {
const configured = { ...draft, configuration: { ...EMPTY_CLOUD_CONFIGURATION, model: 'test-model' } };
api.list.mockResolvedValue({ agents: [configured], next_cursor: null });
const base = api.call.getMockImplementation()!;
api.call.mockImplementation(async (operation, input) => operation === 'saveBudget' ? { ...input, daily_committed_points: '0.00' } : base(operation, input));
open();
fireEvent.click(await screen.findByRole('button', { name: /写作搭档/ }));
await waitFor(() => expect(screen.getByLabelText('消息')).toBeEnabled());
fireEvent.change(screen.getByLabelText('消息'), { target: { value: '待发送的问题' } });
fireEvent.change(screen.getByLabelText('角色与指令'), { target: { value: '未保存的指令' } });
fireEvent.click(screen.getByRole('button', { name: '对话', exact: true }));
fireEvent.click(screen.getByRole('button', { name: '编辑', exact: true }));
expect(screen.getByLabelText('消息')).toHaveValue('待发送的问题');
expect(screen.getByLabelText('角色与指令')).toHaveValue('未保存的指令');
fireEvent.click(screen.getByRole('button', { name: '限制', exact: true }));
fireEvent.change(screen.getByLabelText('每日上限(词元点数)'), { target: { value: '12' } });
fireEvent.click(screen.getByRole('button', { name: '保存费用上限' }));
await waitFor(() => expect(api.call).toHaveBeenCalledWith('saveBudget', expect.objectContaining({ daily_limit_points: '12' })));
expect(api.save).not.toHaveBeenCalled();
fireEvent.click(screen.getByRole('button', { name: '指令', exact: true }));
expect(screen.getByLabelText('角色与指令')).toHaveValue('未保存的指令');
expect(screen.getByLabelText('消息')).toHaveValue('待发送的问题');
});
it('keeps an existing preview when the saved draft clears its model selection', async () => {
const configured = { ...draft, configuration: { ...EMPTY_CLOUD_CONFIGURATION, model: 'test-model' } };
api.list.mockResolvedValue({ agents: [configured], next_cursor: null });
api.save.mockResolvedValue({ ...draft, draft_revision: 2 });
open();
fireEvent.click(await screen.findByRole('button', { name: /写作搭档/ }));
await waitFor(() => expect(screen.getByLabelText('消息')).toBeEnabled());
const prompt = screen.getByLabelText('消息');
fireEvent.change(prompt, { target: { value: '还没有发送' } });
fireEvent.click(screen.getByRole('button', { name: '能力', exact: true }));
fireEvent.change(screen.getByLabelText('模型'), { target: { value: '' } });
fireEvent.click(screen.getByRole('button', { name: '保存草稿' }));
await screen.findByText('云端已保存');
expect(screen.getByLabelText('消息')).toBe(prompt);
expect(prompt).toHaveValue('还没有发送');
expect(screen.getByRole('button', { name: '用最新草稿试用' })).toBeDisabled();
});
it('searches capabilities while retaining existing selections outside the results', async () => {
const configured = { ...draft, configuration: { ...EMPTY_CLOUD_CONFIGURATION, model: 'test-model', tools: ['missing-tool'] } };
api.list.mockResolvedValue({ agents: [configured], next_cursor: null });
const base = api.call.getMockImplementation()!;
api.call.mockImplementation(async (operation, input) => operation === 'catalog' ? {
models: [{ id: 'test-model', name: '写作模型' }], resources: { tools: [{ key: 'search', name: '资料搜索', description: '搜索素材' }, { key: 'clock', name: '当前时间' }] }, pricing: null,
} : base(operation, input));
api.save.mockImplementation(async (_slug, input) => ({ ...configured, ...input, draft_revision: 2 }));
open();
fireEvent.click(await screen.findByRole('button', { name: /写作搭档/ }));
fireEvent.click(screen.getByRole('button', { name: '能力', exact: true }));
expect(await screen.findByRole('checkbox', { name: /missing-tool/ })).toBeChecked();
fireEvent.click(screen.getByRole('button', { name: '选择工具', exact: true }));
fireEvent.change(screen.getByLabelText('搜索工具'), { target: { value: '资料' } });
expect(screen.queryByRole('checkbox', { name: '当前时间' })).not.toBeInTheDocument();
fireEvent.click(screen.getByRole('checkbox', { name: /资料搜索/ }));
fireEvent.click(screen.getByRole('button', { name: '完成选择' }));
expect(screen.getByRole('checkbox', { name: /missing-tool/ })).toBeChecked();
fireEvent.click(screen.getByRole('button', { name: '保存草稿' }));
await waitFor(() => expect(api.save).toHaveBeenCalledWith(draft.slug, expect.objectContaining({ configuration: expect.objectContaining({ tools: ['missing-tool', 'search'] }) })));
});