Files
makelore/tests/unit/image-canvas-page.test.tsx
2026-07-29 17:22:35 +08:00

246 lines
9.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { ImageWorkspaceApiError } from '@/lib/image-workspace';
import { ImageCanvas } from '@/pages/ImageCanvas';
import { useAuthStore } from '@/stores/auth';
import { useImageWorkspaceStore } from '@/stores/image-workspace';
import type { ImageWorkspaceSnapshot } from '../../shared/image-workspace';
const fetchImageWorkspaceMock = vi.hoisted(() => vi.fn());
const addImageWorkspaceAgentMock = vi.hoisted(() => vi.fn());
const sendImageWorkspaceMessageMock = vi.hoisted(() => vi.fn());
const uploadImageWorkspaceReferenceMock = vi.hoisted(() => vi.fn());
vi.mock('@/lib/image-workspace', async (importOriginal) => {
const actual = await importOriginal<typeof import('@/lib/image-workspace')>();
return {
...actual,
fetchImageWorkspace: (...args: unknown[]) => fetchImageWorkspaceMock(...args),
addImageWorkspaceAgent: (...args: unknown[]) => addImageWorkspaceAgentMock(...args),
sendImageWorkspaceMessage: (...args: unknown[]) => sendImageWorkspaceMessageMock(...args),
uploadImageWorkspaceReference: (...args: unknown[]) => uploadImageWorkspaceReferenceMock(...args),
};
});
function workspaceFixture(): ImageWorkspaceSnapshot {
return {
activeProjectId: 'project-cloud',
capabilities: {
modes: [{ id: 'generate', label: '图片生成' }],
models: [{ id: 'cloud-model', label: '云端模型 A' }],
aspectRatios: [
{ id: 'square', label: '1:1' },
{ id: 'wide', label: '16:9' },
],
resolutions: [{ id: '2k', label: '2K' }],
outputCounts: [{ id: 'four', label: '4 张' }],
defaultModeId: 'generate',
defaultModelId: 'cloud-model',
defaultAspectRatioId: 'square',
defaultResolutionId: '2k',
defaultOutputCountId: 'four',
maxReferenceImages: 3,
referenceUpload: {
enabled: true,
acceptedMimeTypes: ['image/png', 'image/jpeg'],
maxBytes: 5 * 1024 * 1024,
},
},
projects: [{
id: 'project-cloud',
name: '云端角色设定',
activeAgentId: 'agent-default',
updatedAt: '2026-07-28T10:00:00Z',
agents: [{ id: 'agent-default', name: '视觉创作 Agent' }],
messages: [{
id: 'message-result',
role: 'assistant',
agentId: 'agent-default',
text: '已经为你生成一组角色图。',
status: 'succeeded',
createdAt: '2026-07-28T10:00:00Z',
images: [{
id: 'image-cloud-1',
url: 'https://example.com/cloud-result.png',
thumbnailUrl: 'https://example.com/cloud-result-thumb.png',
alt: '云端角色结果',
}],
}],
}],
};
}
function resetAuthStore() {
useAuthStore.setState({
initialized: true,
loading: false,
error: null,
authBase: '',
clientId: 'app',
accessToken: 'access-token',
refreshToken: null,
tokenType: 'Bearer',
expiresAt: Date.now() + 60_000,
user: {
username: 'creator',
userId: 'user_1',
tenantId: null,
deptId: null,
authorities: ['ROLE_USER'],
},
});
}
describe('ImageCanvas cloud conversation workspace', () => {
beforeEach(() => {
vi.clearAllMocks();
window.electron.imageWorkspaceLocalDevelopment = false;
useImageWorkspaceStore.getState().reset();
resetAuthStore();
const workspace = workspaceFixture();
fetchImageWorkspaceMock.mockResolvedValue(workspace);
addImageWorkspaceAgentMock.mockResolvedValue(workspace);
sendImageWorkspaceMessageMock.mockResolvedValue(workspace);
uploadImageWorkspaceReferenceMock.mockResolvedValue({
workspace,
reference: {
id: 'uploaded-reference',
url: 'https://example.com/uploaded.png',
alt: '上传的参考图',
},
});
});
it('shows an honest unavailable state instead of local or fabricated projects', async () => {
fetchImageWorkspaceMock.mockRejectedValueOnce(new ImageWorkspaceApiError(
501,
'IMAGE_WORKSPACE_UNAVAILABLE',
'创作空间暂不可用',
));
render(
<MemoryRouter initialEntries={['/image-canvas']}>
<ImageCanvas />
</MemoryRouter>,
);
expect(await screen.findByTestId('image-workspace-unavailable')).toHaveTextContent('创作空间暂不可用');
expect(screen.getByText(/暂时无法连接创作空间/)).toBeInTheDocument();
expect(screen.queryByText('制作中心')).not.toBeInTheDocument();
expect(screen.queryByText('任务模块')).not.toBeInTheDocument();
});
it('renders cloud projects, conversations, agents, and server-provided generation controls', async () => {
render(
<MemoryRouter initialEntries={['/image-canvas']}>
<ImageCanvas />
</MemoryRouter>,
);
expect(await screen.findByTestId('image-workspace-page')).toHaveTextContent('云端角色设定');
expect(screen.getByText('当前 Agent视觉创作 Agent')).toBeInTheDocument();
expect(screen.getByTestId('image-workspace-conversation')).toHaveTextContent('已经为你生成一组角色图');
expect(screen.getByRole('img', { name: '云端角色结果' })).toHaveAttribute(
'src',
'https://example.com/cloud-result-thumb.png',
);
expect(screen.getByLabelText('创作模式')).toHaveValue('generate');
expect(screen.getByLabelText('模型')).toHaveValue('cloud-model');
expect(screen.getByLabelText('画幅')).toHaveValue('square');
expect(screen.getByLabelText('分辨率')).toHaveValue('2k');
expect(screen.getByLabelText('生成数量')).toHaveValue('four');
expect(screen.queryByText('Image2')).not.toBeInTheDocument();
});
it('renders the anonymous development adapter with the same production copy', async () => {
useAuthStore.setState({
accessToken: null,
refreshToken: null,
expiresAt: null,
});
window.electron.imageWorkspaceLocalDevelopment = true;
fetchImageWorkspaceMock.mockResolvedValueOnce({
...workspaceFixture(),
activeProjectId: null,
projects: [],
});
render(
<MemoryRouter initialEntries={['/image-canvas']}>
<ImageCanvas />
</MemoryRouter>,
);
expect(await screen.findByText('创建第一个项目')).toBeInTheDocument();
expect(screen.getByRole('button', { name: '新建项目' })).toBeInTheDocument();
expect(fetchImageWorkspaceMock).toHaveBeenCalledWith(null);
expect(screen.queryByText('本地开发')).not.toBeInTheDocument();
expect(screen.queryByText('创建第一个本地项目')).not.toBeInTheDocument();
expect(screen.queryByText('创建第一个云端项目')).not.toBeInTheDocument();
});
it('adds an output only after an explicit action and clears references after a successful turn', async () => {
render(
<MemoryRouter initialEntries={['/image-canvas']}>
<ImageCanvas />
</MemoryRouter>,
);
await screen.findByTestId('image-workspace-page');
fireEvent.click(screen.getByRole('button', { name: '继续编辑' }));
expect(screen.getByTestId('image-workspace-selected-references')).toHaveTextContent('云端角色结果');
fireEvent.change(screen.getByLabelText('创作描述'), { target: { value: '把角色换成夜景光线' } });
fireEvent.change(screen.getByLabelText('画幅'), { target: { value: 'wide' } });
fireEvent.click(screen.getByRole('button', { name: '生成图片' }));
await waitFor(() => expect(sendImageWorkspaceMessageMock).toHaveBeenCalledWith('access-token', {
projectId: 'project-cloud',
agentId: 'agent-default',
prompt: '把角色换成夜景光线',
referenceImageIds: ['image-cloud-1'],
settings: {
modeId: 'generate',
modelId: 'cloud-model',
aspectRatioId: 'wide',
resolutionId: '2k',
outputCountId: 'four',
},
}));
await waitFor(() => expect(screen.queryByTestId('image-workspace-selected-references')).not.toBeInTheDocument());
fireEvent.change(screen.getByLabelText('创作描述'), { target: { value: '另起一张纯文字创作' } });
fireEvent.click(screen.getByRole('button', { name: '生成图片' }));
await waitFor(() => expect(sendImageWorkspaceMessageMock).toHaveBeenLastCalledWith(
'access-token',
expect.objectContaining({
prompt: '另起一张纯文字创作',
referenceImageIds: [],
}),
));
});
it('uploads references through the cloud boundary and selects the returned cloud image', async () => {
render(
<MemoryRouter initialEntries={['/image-canvas']}>
<ImageCanvas />
</MemoryRouter>,
);
await screen.findByTestId('image-workspace-page');
const file = new File(['image-bytes'], 'reference.png', { type: 'image/png' });
fireEvent.change(screen.getByLabelText('选择参考图'), { target: { files: [file] } });
await waitFor(() => expect(uploadImageWorkspaceReferenceMock).toHaveBeenCalledWith(
'access-token',
expect.objectContaining({
projectId: 'project-cloud',
fileName: 'reference.png',
mimeType: 'image/png',
}),
));
expect(await screen.findByTestId('image-workspace-selected-references')).toHaveTextContent('上传的参考图');
});
});