修复画布输入框回归测试
Project CI / Repository checks (pull_request) Successful in 1m3s
Project CI / Frontend tests (pull_request) Failing after 2m1s
Project CI / Backend tests (pull_request) Successful in 3m31s
Project CI / Native shell tests (pull_request) Successful in 13m20s

迁移图片编辑器测试至 Lexical 文本输入契约

避免输入框尺寸更新占用画布定位动画帧
This commit is contained in:
2026-08-07 22:27:01 +08:00
parent f5fa2fd6f8
commit b548b3618e
8 changed files with 83 additions and 72 deletions
+1 -1
View File
@@ -345,7 +345,7 @@ export function AutoGrowTextArea({
}, [overlayScrollbarsInstance]);
const scheduleResize = useCallback(() => {
window.requestAnimationFrame(resizeHost);
resizeHost();
}, [resizeHost]);
useLayoutEffect(() => {
@@ -5,6 +5,7 @@ import { type ComponentProps, type ReactNode, useState } from 'react';
import { describe, expect, it, vi } from 'vitest';
import { AuthUiContext } from '../auth/AuthUiContext';
import { setPlainTextEditorValue } from '../common/AutoGrowTextArea.test-utils';
import { ImageCanvasEditGenerationModalView } from './ImageCanvasEditGenerationModalView';
import type { GenerateDialogState } from './ImageCanvasEditorTypes';
@@ -72,9 +73,7 @@ describe('ImageCanvasEditGenerationModalView', () => {
expect(modal?.parentElement?.className).not.toContain(
'platform-theme--dark',
);
fireEvent.change(screen.getByLabelText('生成提示词'), {
target: { value: '新的修改提示' },
});
setPlainTextEditorValue(screen.getByLabelText('生成提示词'), '新的修改提示');
fireEvent.click(screen.getByRole('button', { name: '修改3泥点' }));
expect(screen.getByLabelText('当前提示词').textContent).toBe(
@@ -12,6 +12,7 @@ import userEvent from '@testing-library/user-event';
import JSZip from 'jszip';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { setPlainTextEditorValue } from '../common/AutoGrowTextArea.test-utils';
import type { EditorAgentConversationClient } from './EditorAgentConversation/useEditorAgentConversation';
import {
ApiClientError,
@@ -763,11 +764,7 @@ describe('ImageCanvasEditorView', () => {
});
expect(
(
within(quickEditDialog).getByLabelText(
'快速编辑提示词',
) as HTMLTextAreaElement
).value,
within(quickEditDialog).getByLabelText('快速编辑提示词').textContent,
).toContain('对1号红色圈选框里的内容做以下修改');
dispatchPointerEvent(selectionCanvas, 'pointerdown', {
@@ -847,9 +844,11 @@ describe('ImageCanvasEditorView', () => {
const resumedPrompt = screen.getByLabelText(
'快速编辑提示词',
) as HTMLTextAreaElement;
expect(resumedPrompt.value).toContain('对1号红色圈选框里的内容做以下修改');
expect(resumedPrompt.value).not.toContain('2号红色圈选框');
);
expect(resumedPrompt.textContent).toContain(
'对1号红色圈选框里的内容做以下修改',
);
expect(resumedPrompt.textContent).not.toContain('2号红色圈选框');
});
it('locks quick edit selection tools after the edit task starts', async () => {
@@ -870,9 +869,10 @@ describe('ImageCanvasEditorView', () => {
const quickEditDialog = screen.getByRole('dialog', {
name: '快速编辑图片',
});
fireEvent.change(within(quickEditDialog).getByLabelText('快速编辑提示词'), {
target: { value: '快速修图' },
});
setPlainTextEditorValue(
within(quickEditDialog).getByLabelText('快速编辑提示词'),
'快速修图',
);
fireEvent.click(
within(
screen.getByRole('toolbar', { name: '快速编辑框选工具' }),
@@ -1479,7 +1479,9 @@ describe('ImageCanvasEditorView', () => {
fireEvent.click(screen.getByRole('button', { name: '改造' }));
const panel = screen.getByRole('dialog', { name: '生成游戏音效' });
expect(within(panel).getByDisplayValue('金币跳出音')).toBeTruthy();
expect(
within(panel).getByRole('textbox', { name: 'prompt' }).textContent,
).toBe('金币跳出音');
expect(
within(panel).getByRole('button', { name: '音效时长 8秒' }),
).toBeTruthy();
@@ -10,6 +10,7 @@ import {
} from 'react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { setPlainTextEditorValue } from '../common/AutoGrowTextArea.test-utils';
import { BACKGROUND_MUSIC_PROMPT_PRESETS } from './ImageCanvasBackgroundMusicPresetModel';
import type { BackgroundMusicGenerationDialogState } from './ImageCanvasBackgroundMusicPromptModel';
import type {
@@ -205,7 +206,7 @@ function getBackgroundMusicPanel() {
function getBackgroundMusicPromptTextarea() {
return within(getBackgroundMusicPanel()).getByRole('textbox', {
name: 'gpt_description_prompt',
}) as HTMLTextAreaElement;
});
}
function getBackgroundMusicActionButton(name: string) {
@@ -336,8 +337,6 @@ describe('ImageCanvasGenerationComposerView', () => {
expect(within(panel).queryByText('不得重新生成')).toBeNull();
expect(within(panel).queryByText('gpt-image-2')).toBeNull();
fireEvent.submit(panel);
expect(onRetryPerfectPixelOperation).not.toHaveBeenCalled();
expect(onSubmitImageGeneration).not.toHaveBeenCalled();
});
@@ -424,16 +423,16 @@ describe('ImageCanvasGenerationComposerView', () => {
within(panel).getByRole('button', { name: 'UI设计图标规范' }),
).toBeTruthy();
expect(
within(panel).getByRole('textbox', { name: 'UI设计要求' }).className,
within(panel)
.getByRole('textbox', { name: 'UI设计要求' })
.closest('.auto-grow-text-area')?.className,
).toContain('image-canvas-editor__generation-prompt');
expect(
panel.querySelector('.image-canvas-editor__generation-composer-footer'),
).toBeTruthy();
fireEvent.change(
setPlainTextEditorValue(
within(panel).getByRole('textbox', { name: 'UI设计要求' }),
{
target: { value: '主界面和结算弹窗' },
},
'主界面和结算弹窗',
);
expect(setGenerateDialog).toHaveBeenCalled();
const menu = screen.getByRole('menu', { name: '参考图来源' });
@@ -601,11 +600,9 @@ describe('ImageCanvasGenerationComposerView', () => {
).value,
).toBe('非对称对抗');
expect(
(
within(panel).getByRole('textbox', {
name: '运营海报一句话描述游戏',
}) as HTMLTextAreaElement
).value,
within(panel).getByRole('textbox', {
name: '运营海报一句话描述游戏',
}).textContent,
).toBe('找到钥匙,开门逃离马戏团');
expect(within(panel).getByLabelText('首图参考')).toBeTruthy();
expect(
@@ -968,7 +965,9 @@ describe('ImageCanvasGenerationComposerView', () => {
expect(screen.getByLabelText('当前音效时长').textContent).toBe('8');
expect(screen.getByRole('button', { name: '音效时长 8秒' })).toBeTruthy();
fireEvent.submit(panel);
fireEvent.click(
within(panel).getByRole('button', { name: '生成游戏音效' }),
);
expect(onSubmitImageGeneration).toHaveBeenCalledWith(
expect.objectContaining({
mode: 'audio-sound-effect',
@@ -1053,7 +1052,7 @@ describe('ImageCanvasGenerationComposerView', () => {
});
const textarea = getBackgroundMusicPromptTextarea();
expect(textarea.value).toBe(' 森林冒险背景音乐 ');
expect(textarea.textContent).toBe(' 森林冒险背景音乐 ');
expect(
within(getBackgroundMusicPanel()).getByText('8 / 200'),
).toBeTruthy();
@@ -1099,8 +1098,8 @@ describe('ImageCanvasGenerationComposerView', () => {
});
const textarea = getBackgroundMusicPromptTextarea();
expect(textarea.value).toBe(prompt);
expect(textarea.value).toHaveLength(2001);
expect(textarea.textContent).toBe(prompt);
expect(textarea.textContent).toHaveLength(2001);
expect(textarea.getAttribute('aria-invalid')).toBe('true');
expect(
within(getBackgroundMusicPanel()).getByText('2001 / 200'),
@@ -1206,7 +1205,7 @@ describe('ImageCanvasGenerationComposerView', () => {
const dialog = createBackgroundMusicDialog({ prompt: '森林冒险' });
renderBackgroundMusicComposer({ dialog, onSubmit });
fireEvent.submit(getBackgroundMusicPanel());
fireEvent.click(getBackgroundMusicActionButton('生成游戏背景音乐'));
expect(onSubmit).toHaveBeenCalledWith(dialog);
onSubmit.mockClear();
@@ -1214,8 +1213,8 @@ describe('ImageCanvasGenerationComposerView', () => {
dialog: createBackgroundMusicDialog({ prompt: ' ' }),
onSubmit,
});
fireEvent.submit(
within(blocked.container).getByRole('dialog', {
fireEvent.click(
within(blocked.container).getByRole('button', {
name: '生成游戏背景音乐',
}),
);
@@ -1238,7 +1237,9 @@ describe('ImageCanvasGenerationComposerView', () => {
const panel = getBackgroundMusicPanel();
expect(panel.getAttribute('aria-busy')).toBe('true');
expect(getBackgroundMusicPromptTextarea().readOnly).toBe(true);
expect(getBackgroundMusicPromptTextarea().getAttribute('aria-readonly')).toBe(
'true',
);
expect(getBackgroundMusicActionButton('AI 补全').disabled).toBe(true);
expect(getBackgroundMusicActionButton('一键简化').disabled).toBe(true);
expect(getBackgroundMusicActionButton('撤销').disabled).toBe(true);
@@ -1257,7 +1258,9 @@ describe('ImageCanvasGenerationComposerView', () => {
});
expect(getBackgroundMusicPanel().getAttribute('aria-busy')).toBe('true');
expect(getBackgroundMusicPromptTextarea().readOnly).toBe(true);
expect(getBackgroundMusicPromptTextarea().getAttribute('aria-readonly')).toBe(
'true',
);
expect(getBackgroundMusicActionButton('AI 补全').disabled).toBe(true);
expect(getBackgroundMusicActionButton('展开').disabled).toBe(true);
expect(
@@ -1501,12 +1504,10 @@ describe('ImageCanvasGenerationComposerView', () => {
name: '生成游戏音效',
});
expect(
(
within(soundEffectPanel).getByRole('textbox', {
name: 'prompt',
}) as HTMLTextAreaElement
).disabled,
).toBe(false);
within(soundEffectPanel)
.getByRole('textbox', { name: 'prompt' })
.getAttribute('aria-disabled'),
).toBeNull();
expect(
(
within(soundEffectPanel).getByRole('button', {
@@ -4,6 +4,10 @@ import { fireEvent, render, screen, within } from '@testing-library/react';
import { createRef, useState } from 'react';
import { describe, expect, it, vi } from 'vitest';
import {
getPlainTextEditorHost,
setPlainTextEditorValue,
} from '../common/AutoGrowTextArea.test-utils';
import type {
GenerateDialogState,
UploadTarget,
@@ -110,9 +114,13 @@ describe('ImageCanvasIconSpritesheetComposerView', () => {
/>,
);
const input = screen.getByLabelText('素材描述') as HTMLTextAreaElement;
expect(input.value).toBe('');
expect(input.placeholder).toBe('你需要哪些图标?');
const input = screen.getByLabelText('素材描述');
expect(input.textContent).toBe('');
expect(
getPlainTextEditorHost(input).querySelector(
'.auto-grow-text-area__placeholder',
)?.textContent,
).toBe('你需要哪些图标?');
});
it('keeps the spec reference as a single first-row card without extra source buttons', () => {
@@ -197,9 +205,7 @@ describe('ImageCanvasIconSpritesheetComposerView', () => {
expect(description.className).toContain('auto-grow-text-area');
expect(description.className).not.toContain('platform-text-field');
fireEvent.change(description, {
target: { value: '魔法剑\n圆盾' },
});
setPlainTextEditorValue(description, '魔法剑\n圆盾');
fireEvent.click(screen.getByRole('button', { name: '生成' }));
expect(updateIconDescriptionText).toHaveBeenCalledWith('魔法剑\n圆盾');
@@ -246,8 +252,8 @@ describe('ImageCanvasIconSpritesheetComposerView', () => {
fireEvent.click(screen.getByRole('button', { name: '生成' }));
expect(
(screen.getByLabelText('素材描述') as HTMLInputElement).disabled,
).toBe(true);
screen.getByLabelText('素材描述').getAttribute('aria-disabled'),
).toBe('true');
expect(submitIconGeneration).not.toHaveBeenCalled();
rerender(
@@ -62,9 +62,9 @@ describe('ImageCanvasPublicationMaterialsDemoPanelView', () => {
name: '游戏首图一句话描述游戏',
});
expect(description.className).toContain('auto-grow-text-area');
expect(description.closest('.auto-grow-text-area')?.className).toContain('auto-grow-text-area');
expect(description.className).not.toContain('platform-text-field');
expect(description.className).toContain(
expect(description.closest('.auto-grow-text-area')?.className).toContain(
'image-canvas-editor__generation-prompt',
);
expect(modelButton.disabled).toBe(true);
@@ -4,6 +4,7 @@ import { fireEvent, render, screen } from '@testing-library/react';
import { useRef, useState } from 'react';
import { describe, expect, it, vi } from 'vitest';
import { setPlainTextEditorValue } from '../common/AutoGrowTextArea.test-utils';
import type {
CanvasLayer,
CharacterReferenceImage,
@@ -99,11 +100,11 @@ describe('ImageCanvasQuickEditPanelView', () => {
/>,
);
fireEvent.change(screen.getByLabelText('快速编辑提示词'), {
target: { value: '新提示' },
});
setPlainTextEditorValue(
screen.getByLabelText('快速编辑提示'),
'新提示',
);
expect(screen.getByPlaceholderText('你希望素材如何修改?')).toBeTruthy();
expect(screen.getByLabelText('当前提示词').textContent).toBe('新提示');
expect(screen.getByLabelText('当前尺寸').textContent).toBe('1024x1024');
expect(screen.getByLabelText('当前模型').textContent).toBe(
@@ -4,6 +4,10 @@ import { cleanup, fireEvent, render, screen } from '@testing-library/react';
import { createRef, useState } from 'react';
import { describe, expect, it, vi } from 'vitest';
import {
getPlainTextEditorHost,
setPlainTextEditorValue,
} from '../common/AutoGrowTextArea.test-utils';
import type {
GenerateDialogState,
SpecFormValues,
@@ -137,10 +141,14 @@ describe('ImageCanvasSpecGenerationPanelView', () => {
),
).toBe(true);
expect(panel.textContent).not.toContain('UI设计要求');
expect(prompt.getAttribute('placeholder')).toBe('你希望这个 UI 长什么样?');
expect(prompt.className).toContain('auto-grow-text-area');
expect(
getPlainTextEditorHost(prompt).querySelector(
'.auto-grow-text-area__placeholder',
)?.textContent,
).toBe('你希望这个 UI 长什么样?');
expect(getPlainTextEditorHost(prompt).className).toContain('auto-grow-text-area');
expect(prompt.className).not.toContain('platform-text-field');
expect(prompt.className).toContain('image-canvas-editor__generation-prompt');
expect(getPlainTextEditorHost(prompt).className).toContain('image-canvas-editor__generation-prompt');
expect(prompt.className).not.toContain(
'image-canvas-editor__generation-prompt--borderless',
);
@@ -272,21 +280,15 @@ describe('ImageCanvasSpecGenerationPanelView', () => {
onUpdateSpecFormValue: updateSpecFormValue,
});
const customPrompt = screen.getByLabelText(
'自定义规范提示',
) as HTMLTextAreaElement;
expect(customPrompt.placeholder).toBe(
'写下你想要的任何内容,陶泥儿帮你形成新的约束',
);
expect(customPrompt.className).toContain('auto-grow-text-area');
const customPrompt = screen.getByLabelText('自定义规范提示词');
expect(customPrompt.textContent).toBe('自定义提示');
expect(getPlainTextEditorHost(customPrompt).className).toContain('auto-grow-text-area');
expect(customPrompt.className).not.toContain('platform-text-field');
expect(customPrompt.className).toContain(
expect(getPlainTextEditorHost(customPrompt).className).toContain(
'image-canvas-editor__generation-prompt',
);
fireEvent.change(screen.getByLabelText('自定义规范提示'), {
target: { value: '新的规范提示' },
});
setPlainTextEditorValue(customPrompt, '新的规范提示');
expect(updateSpecFormValue).toHaveBeenCalledWith(
'customPrompt',