Files
makelore/tests/e2e/image-workspace-conversations.spec.ts
inman 26b52d76e3
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
feat: 完善图像工作区与创作工具体验
2026-08-16 14:08:27 +08:00

176 lines
10 KiB
TypeScript

import { closeElectronApp, expect, getStableWindow, test } from './fixtures/electron';
test.describe('AI Design workspace', () => {
test('keeps the conversation and generation task panes full height', async ({ launchElectronApp }) => {
test.setTimeout(150_000);
const app = await launchElectronApp({
imageWorkspaceMode: 'local',
skipSetup: true,
});
try {
const page = await getStableWindow(app);
await expect(page.getByTestId('ai-module-selection-page')).toBeVisible();
await page.getByTestId('ai-module-option-painting').click();
const imageSidebar = page.getByTestId('sidebar-image-workspace');
await expect(imageSidebar.getByTestId('sidebar-open-image-prompt-museum')).toBeVisible();
await expect(imageSidebar.getByTestId('sidebar-create-image-project')).toBeVisible();
await page.getByTestId('sidebar-create-image-project').click();
await page.getByLabel('项目名称').fill('多会话验收项目');
await page.getByRole('button', { name: '创建项目' }).click();
const projectCard = page.locator('[data-testid^="sidebar-image-project-"]').first();
await expect(projectCard).not.toContainText('1 个会话');
await expect(projectCard).not.toContainText('设计会话');
await expect(page.getByRole('button', { name: '收起 多会话验收项目 的会话' })).toBeVisible();
await expect(page.getByRole('button', { name: '重命名 多会话验收项目' })).toBeVisible();
await expect(page.locator('[data-testid^="sidebar-image-conversations-"]').first())
.not.toHaveClass(/border-t/);
await expect(page.getByTestId('image-workspace-conversation')).toBeVisible();
await expect(page.getByTestId('image-workspace-conversation-fade')).toBeVisible();
await expect(page.getByTestId('design-task-list')).toBeVisible();
await expect(page.getByTestId('design-task-fade')).toBeVisible();
await expect(page.getByTestId('image-canvas-conversation-select')).toHaveCount(0);
const emptyState = page.getByTestId('image-workspace-empty-state');
await expect(emptyState).toContainText('今天一起描绘什么?');
await expect(emptyState.getByRole('img', { name: 'Makelore' })).toHaveCount(1);
const fileChooserPromise = page.waitForEvent('filechooser');
await page.getByRole('button', { name: '上传参考图' }).click();
await fileChooserPromise;
await expect(page.getByRole('dialog', { name: '选择视频首帧' })).toHaveCount(0);
const layoutBounds = await page.evaluate(() => {
const readBounds = (testId: string) => {
const element = document.querySelector(`[data-testid="${testId}"]`);
if (!element) return null;
const rect = element.getBoundingClientRect();
const style = getComputedStyle(element);
return {
top: rect.top,
bottom: rect.bottom,
borderLeftWidth: style.borderLeftWidth,
overflowY: style.overflowY,
};
};
return {
shell: readBounds('main-layout'),
canvas: readBounds('image-canvas-page'),
conversation: readBounds('image-workspace-conversation-pane'),
tasks: readBounds('design-task-list'),
taskScroll: readBounds('design-task-scroll'),
};
});
expect(layoutBounds.shell).not.toBeNull();
expect(layoutBounds.canvas).not.toBeNull();
expect(layoutBounds.conversation).not.toBeNull();
expect(layoutBounds.tasks).not.toBeNull();
expect(layoutBounds.taskScroll).not.toBeNull();
expect(Math.abs(layoutBounds.canvas!.top - layoutBounds.shell!.top)).toBeLessThanOrEqual(1);
expect(Math.abs(layoutBounds.canvas!.bottom - layoutBounds.shell!.bottom)).toBeLessThanOrEqual(1);
expect(Math.abs(layoutBounds.conversation!.top - layoutBounds.canvas!.top)).toBeLessThanOrEqual(1);
expect(Math.abs(layoutBounds.conversation!.bottom - layoutBounds.canvas!.bottom)).toBeLessThanOrEqual(1);
expect(Math.abs(layoutBounds.tasks!.top - layoutBounds.canvas!.top)).toBeLessThanOrEqual(1);
expect(Math.abs(layoutBounds.tasks!.bottom - layoutBounds.canvas!.bottom)).toBeLessThanOrEqual(1);
expect(layoutBounds.tasks!.borderLeftWidth).toBe('1px');
expect(layoutBounds.taskScroll!.overflowY).toBe('auto');
const firstPrompt = '为项目设计一张蓝色星空海报';
await page.getByLabel('设计需求').fill(firstPrompt);
await page.getByRole('button', { name: '发送给设计 Agent' }).click();
await expect(page.getByTestId('image-workspace-conversation')).toContainText(firstPrompt);
await expect(page.getByText('设计 Agent', { exact: true })).toHaveCount(0);
await expect(page.getByText('你', { exact: true })).toHaveCount(0);
await expect(page.getByTestId('image-workspace-message').first()).toHaveClass(/w-full/);
const firstQuote = page.locator('[data-testid^="design-quote-"]').first();
await expect(firstQuote.getByRole('button', { name: '继续调整' })).toBeVisible();
await expect(firstQuote).not.toContainText('提示词模式');
await page.getByRole('button', { name: '确认并开始生成' }).click();
await expect(page.getByRole('button', { name: '确认生成' })).toHaveCount(0);
await expect(page.getByRole('button', { name: '继续调整' })).toHaveCount(0);
const task = page
.locator('[data-testid^="design-task-local-task-"]')
.filter({ hasText: firstPrompt })
.first();
await expect(task).toContainText(firstPrompt);
await expect(task).toContainText(firstPrompt);
const firstConversation = page.locator('[data-testid^="sidebar-image-conversation-"]').first();
await expect(firstConversation).toContainText(firstPrompt);
await expect(firstConversation).not.toContainText('新会话');
const firstConversationTestId = await page
.locator('[data-testid^="sidebar-image-conversation-"]')
.first()
.getAttribute('data-testid');
expect(firstConversationTestId).toBeTruthy();
await projectCard.hover();
await page.getByRole('button', { name: '新建设计会话' }).click();
await expect(page.locator('[data-testid^="sidebar-image-conversation-"]')).toHaveCount(2);
const secondPrompt = '这一条会话只讨论红色角色动态视频';
await page.getByLabel('设计需求').fill(secondPrompt);
await page.getByRole('button', { name: '发送给设计 Agent' }).click();
await expect(page.getByTestId('image-workspace-conversation')).toContainText(secondPrompt);
await expect(page.getByTestId('design-quote-final-prompt')).toHaveValue(secondPrompt);
await page.getByTestId('design-quote-final-prompt').fill('用户编辑后的动态视频完整提示词');
await page.getByTestId('design-quote-resolution').selectOption('720P');
await page.getByTestId('design-quote-aspect-ratio').selectOption('9:16');
await page.getByTestId('design-quote-duration').selectOption('4');
await expect(page.getByRole('button', { name: '重新报价中' })).toBeDisabled();
await expect(page.getByRole('button', { name: '确认并开始生成' })).toBeEnabled({ timeout: 10_000 });
await page.getByRole('button', { name: '确认并开始生成' }).click();
await expect(page.getByRole('button', { name: '确认并开始生成' })).toHaveCount(0);
await page.getByTestId(firstConversationTestId!).click();
await expect(page.getByTestId('image-workspace-conversation')).toContainText(firstPrompt);
await expect(page.getByTestId('image-workspace-conversation')).not.toContainText(secondPrompt);
// The local adapter deterministically advances queued -> running -> succeeded as the
// task fallback reconciles. The delete confirmation also explains what happens to
// queued and running tasks on the server.
await task.getByRole('button', { name: '查看生图任务详情' }).click();
const taskDetails = page.getByRole('dialog', { name: '生成内容详情' });
await expect(taskDetails).toContainText('已完成', { timeout: 40_000 });
await taskDetails.getByRole('button', { name: '关闭生成内容详情' }).click();
await projectCard.hover();
await page.getByRole('button', { name: '删除 多会话验收项目' }).click();
const deleteDialog = page.getByRole('dialog', { name: '删除项目' });
await expect(deleteDialog).toContainText('未提交的任务会被取消并释放预留积分');
await expect(deleteDialog).toContainText('已提交或运行中的任务会继续后台结算');
await expect(deleteDialog.getByTestId('confirm-delete-image-project')).toBeDisabled();
await expect(projectCard).toBeVisible();
await deleteDialog.getByTestId('delete-image-project-name-input').fill('多会话验收');
await expect(deleteDialog.getByText('项目名称必须完全匹配。')).toBeVisible();
await expect(deleteDialog.getByTestId('confirm-delete-image-project')).toBeDisabled();
await expect(projectCard).toBeVisible();
await deleteDialog.getByTestId('delete-image-project-name-input').fill('多会话验收项目');
await expect(deleteDialog.getByTestId('confirm-delete-image-project')).toBeEnabled();
await deleteDialog.getByTestId('confirm-delete-image-project').click();
await expect(deleteDialog).toHaveCount(0);
await expect(page.locator('[data-testid^="sidebar-image-project-"]')).toHaveCount(0);
await expect(imageSidebar).toContainText('还没有设计项目');
await expect(page.getByRole('heading', { name: '创建第一个设计项目' })).toBeVisible();
await imageSidebar.getByTestId('sidebar-open-image-prompt-museum').click();
await expect(page.getByTestId('image-prompt-museum-page')).toBeVisible();
await expect(page.getByRole('heading', { name: '获取灵感' })).toBeVisible();
await expect(page.getByText('提示词博物馆', { exact: true })).toHaveCount(0);
await expect(page.getByTestId('titlebar-page-title')).toHaveText('获取灵感');
await expect(page.getByTestId('titlebar-logo')).toHaveCount(1);
await expect(page.getByTestId('image-prompt-museum-filters')).toHaveClass(/sticky/);
await expect(page.getByTestId('image-prompt-museum-filters')).toHaveClass(/top-0/);
} finally {
await closeElectronApp(app);
}
});
});