完善客户端模块与工作区能力
Some checks failed
Electron E2E / Electron E2E (macos-latest) (push) Has been cancelled
Electron E2E / Electron E2E (ubuntu-latest) (push) Has been cancelled
Electron E2E / Electron E2E (windows-latest) (push) Has been cancelled

This commit is contained in:
inman
2026-08-13 19:45:30 +08:00
parent 0148b91a15
commit 22add3f01f
97 changed files with 4756 additions and 3226 deletions

View File

@@ -10,7 +10,9 @@ import { createProjectConfig } from '../../shared/project-config';
import { createProjectConversationState, removeProjectSessionMetadata, upsertProjectSessionMetadata } from '../../shared/project-conversations';
const hostApiFetchMock = vi.fn();
const prepareAgentAvatarMock = vi.hoisted(() => vi.fn());
vi.mock('@/lib/host-api', () => ({ hostApiFetch: (...args: unknown[]) => hostApiFetchMock(...args) }));
vi.mock('@/lib/agent-avatar-upload', () => ({ prepareAgentAvatar: (...args: unknown[]) => prepareAgentAvatarMock(...args) }));
function LocationProbe() {
const location = useLocation();
@@ -240,6 +242,46 @@ describe('ProjectConfiguration', () => {
})]);
});
it('uploads a compressed local avatar and persists it with the partner', async () => {
prepareAgentAvatarMock.mockResolvedValue({
fileName: 'avatar.webp',
mimeType: 'image/webp',
previewUrl: 'data:image/webp;base64,YXZhdGFy',
width: 256,
height: 256,
bytes: 6,
});
render(<MemoryRouter><ProjectConfiguration /></MemoryRouter>);
fireEvent.click(await screen.findByRole('button', { name: '创建伙伴' }));
const creationDialog = screen.getByRole('dialog', { name: '创建项目伙伴' });
fireEvent.change(within(creationDialog).getByLabelText(/伙伴名称/), { target: { value: '小图' } });
fireEvent.change(within(creationDialog).getByLabelText(/职责简述/), { target: { value: '负责头像测试。' } });
fireEvent.click(within(creationDialog).getByRole('button', { name: '更换头像' }));
const avatarDialog = await screen.findByRole('dialog', { name: '选择头像' });
fireEvent.change(within(avatarDialog).getByLabelText('上传伙伴头像'), {
target: { files: [new File(['source'], 'avatar.png', { type: 'image/png' })] },
});
await waitFor(() => expect(screen.queryByRole('dialog', { name: '选择头像' })).not.toBeInTheDocument());
expect(within(creationDialog).getByAltText('当前上传头像')).toBeInTheDocument();
fireEvent.click(within(creationDialog).getByRole('button', { name: '创建伙伴' }));
await waitFor(() => expect(screen.queryByRole('dialog', { name: '创建项目伙伴' })).not.toBeInTheDocument());
const createdPartnerCard = screen.getByText('小图').closest('button');
expect(createdPartnerCard?.querySelector('img')).toHaveAttribute('src', 'data:image/webp;base64,YXZhdGFy');
fireEvent.click(screen.getByRole('button', { name: '确认并完成初始化' }));
await waitFor(() => expect(hostApiFetchMock).toHaveBeenCalledWith('/api/opencode/projects/config', expect.objectContaining({ method: 'PUT' })));
const saveCall = hostApiFetchMock.mock.calls.find(([path, init]) => path === '/api/opencode/projects/config' && (init as RequestInit | undefined)?.method === 'PUT');
const savedConfig = JSON.parse((saveCall?.[1] as RequestInit).body as string).config;
expect(savedConfig.agents).toEqual([expect.objectContaining({
avatarId: 'avatar-01',
avatarDataUrl: 'data:image/webp;base64,YXZhdGFy',
})]);
expect(prepareAgentAvatarMock).toHaveBeenCalledWith(expect.any(File));
});
it('opens existing partners in the shared maintenance dialog and saves through project settings', async () => {
const config = createProjectConfig();
const contact = {