拆分图片画布编辑器前端模型
抽出编辑器共享类型、画布模型、生成模型和导出模型 补充模型层单测覆盖素材、吸附、生成快照和导出规则 新增前端拆分计划并更新 TRACKING 浏览器回归记录
This commit is contained in:
168
src/components/image-editor/ImageCanvasGenerationModel.test.ts
Normal file
168
src/components/image-editor/ImageCanvasGenerationModel.test.ts
Normal file
@@ -0,0 +1,168 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { ApiClientError } from '../../services/apiClient';
|
||||
import {
|
||||
DEFAULT_IMAGE_MODEL,
|
||||
buildCharacterGenerationInputs,
|
||||
buildEditGenerationInputs,
|
||||
buildIconGenerationInputs,
|
||||
buildImageGenerationInputs,
|
||||
buildQuickEditModelOptions,
|
||||
buildSpecGenerationInputs,
|
||||
buildSpecPrompt,
|
||||
getGenerationFrameAriaLabel,
|
||||
getGenerationFrameLabel,
|
||||
resolveCharacterAnimationSourceImageSrc,
|
||||
resolveImageGenerationErrorMessage,
|
||||
} from './ImageCanvasGenerationModel';
|
||||
import type {
|
||||
CanvasGenerationDialogState,
|
||||
CanvasLayer,
|
||||
} from './ImageCanvasEditorTypes';
|
||||
|
||||
describe('ImageCanvasGenerationModel', () => {
|
||||
it('builds user-facing generation input snapshots instead of backend prompts', () => {
|
||||
expect(buildImageGenerationInputs(' 一张明亮主视觉 ')).toEqual({
|
||||
fields: [{ title: '生成提示词', value: '一张明亮主视觉' }],
|
||||
references: [],
|
||||
});
|
||||
|
||||
expect(
|
||||
buildSpecGenerationInputs('character', {
|
||||
playSetting: '平台跳跃',
|
||||
artStyle: '像素风',
|
||||
bodyRatio: '3',
|
||||
characterView: '右向三分之二侧身',
|
||||
customPrompt: '',
|
||||
}),
|
||||
).toEqual({
|
||||
fields: [
|
||||
{ title: '玩法设定', value: '平台跳跃' },
|
||||
{ title: '美术风格', value: '像素风' },
|
||||
{ title: '头身比', value: '3' },
|
||||
{ title: '角色视角', value: '右向三分之二侧身' },
|
||||
],
|
||||
references: [],
|
||||
});
|
||||
});
|
||||
|
||||
it('builds character, icon and edit reference snapshots', () => {
|
||||
const sourceLayer = buildSourceLayer();
|
||||
|
||||
expect(
|
||||
buildCharacterGenerationInputs(
|
||||
'主角骑士',
|
||||
{ id: 'spec', label: '角色规范', src: '/spec.png' },
|
||||
[{ id: 'ref-1', label: '盔甲参考', src: '/armor.png' }],
|
||||
),
|
||||
).toEqual({
|
||||
fields: [{ title: '角色设定', value: '主角骑士' }],
|
||||
references: [
|
||||
{ title: '角色形象规范', label: '角色规范', src: '/spec.png' },
|
||||
{ title: '常规参考图 1', label: '盔甲参考', src: '/armor.png' },
|
||||
],
|
||||
});
|
||||
|
||||
expect(
|
||||
buildIconGenerationInputs(['返回按钮', '设置按钮'], {
|
||||
id: 'icon-spec',
|
||||
label: '图标规范',
|
||||
src: '/icon-spec.png',
|
||||
}),
|
||||
).toEqual({
|
||||
fields: [
|
||||
{ title: '素材描述 1', value: '返回按钮' },
|
||||
{ title: '素材描述 2', value: '设置按钮' },
|
||||
],
|
||||
references: [
|
||||
{ title: '图标素材规范', label: '图标规范', src: '/icon-spec.png' },
|
||||
],
|
||||
});
|
||||
|
||||
expect(
|
||||
buildEditGenerationInputs('修改要求', '换成夜晚', sourceLayer),
|
||||
).toEqual({
|
||||
fields: [{ title: '修改要求', value: '换成夜晚' }],
|
||||
references: [
|
||||
{ title: '参考图', label: '原图', src: '/source.png' },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('keeps generated prompts and quick edit options stable', () => {
|
||||
const prompt = buildSpecPrompt('ui', {
|
||||
playSetting: '消除玩法',
|
||||
artStyle: '清爽卡通',
|
||||
bodyRatio: '3',
|
||||
characterView: '',
|
||||
customPrompt: '',
|
||||
});
|
||||
|
||||
expect(prompt).toContain('生成一张完整游戏UI规范汇总设定展板');
|
||||
expect(prompt).toContain('玩法设定:消除玩法');
|
||||
expect(buildSpecPrompt('custom', { ...blankSpecValues, customPrompt: '自定义' }))
|
||||
.toBe('自定义');
|
||||
expect(buildQuickEditModelOptions('nano-banana')).toEqual([
|
||||
{ label: 'nano-banana', value: 'nano-banana' },
|
||||
{ label: 'GPT Image', value: DEFAULT_IMAGE_MODEL },
|
||||
]);
|
||||
});
|
||||
|
||||
it('uses objectKey for character animation references before falling back to src', () => {
|
||||
expect(
|
||||
resolveCharacterAnimationSourceImageSrc({
|
||||
...buildSourceLayer(),
|
||||
objectKey: 'generated/character.png',
|
||||
}),
|
||||
).toBe('generated/character.png');
|
||||
expect(resolveCharacterAnimationSourceImageSrc(buildSourceLayer())).toBe(
|
||||
'/source.png',
|
||||
);
|
||||
});
|
||||
|
||||
it('maps generation dialog mode and authorization errors to user-facing copy', () => {
|
||||
const iconDialog: CanvasGenerationDialogState = {
|
||||
id: 'dialog-icon',
|
||||
mode: 'icon',
|
||||
prompt: '',
|
||||
status: 'idle',
|
||||
};
|
||||
|
||||
expect(getGenerationFrameAriaLabel(iconDialog)).toBe('图标素材生成占位图');
|
||||
expect(getGenerationFrameLabel(iconDialog)).toBe('Icon Generator');
|
||||
expect(
|
||||
resolveImageGenerationErrorMessage(
|
||||
new ApiClientError({
|
||||
message: '未授权访问(requestId: one)',
|
||||
status: 401,
|
||||
code: 'UNAUTHORIZED',
|
||||
}),
|
||||
),
|
||||
).toBe('请先登录后再生成图片');
|
||||
});
|
||||
});
|
||||
|
||||
const blankSpecValues = {
|
||||
playSetting: '',
|
||||
artStyle: '',
|
||||
bodyRatio: '3',
|
||||
characterView: '',
|
||||
customPrompt: '',
|
||||
};
|
||||
|
||||
function buildSourceLayer(): CanvasLayer {
|
||||
return {
|
||||
id: 'layer-source',
|
||||
resourceId: 'resource-source',
|
||||
title: '原图',
|
||||
src: '/source.png',
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 512,
|
||||
height: 512,
|
||||
originalWidth: 512,
|
||||
originalHeight: 512,
|
||||
zIndex: 1,
|
||||
sourceType: 'uploaded',
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user