Files
Genarrative/src/components/image-editor/ImageCanvasGenerationModel.test.ts
高物 e970d34574 调整图片编辑器参考图选择交互
- 常规参考图入口改为先弹出来源菜单,支持从画布选择和上传图片。

- 角色规范、图标规范和常规参考图来源菜单统一向上弹出。

- 画布参考图选择拦截普通图层选中逻辑,保持生成面板不隐藏。

- 补充图片编辑器交互测试与技术文档说明。
2026-06-17 14:08:26 +08:00

205 lines
6.1 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 { 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: 'nanobanana2', value: DEFAULT_IMAGE_MODEL },
{ label: 'gpt-image-2', value: 'gpt-image-2' },
]);
});
it('adds reference image semantics and snapshots for spec generation references', () => {
const prompt = buildSpecPrompt(
'ui',
{
playSetting: '消除玩法',
artStyle: '清爽卡通',
bodyRatio: '3',
characterView: '',
customPrompt: '',
},
true,
);
expect(prompt).toContain('参考图生成规范');
expect(prompt).toContain('参考图1');
expect(prompt).toContain('生成一张完整游戏UI规范汇总设定展板');
expect(
buildSpecPrompt(
'custom',
{ ...blankSpecValues, customPrompt: '生成一张怪兽规范图' },
true,
),
).toContain('生成一张怪兽规范图');
expect(
buildSpecGenerationInputs(
'custom',
{ ...blankSpecValues, customPrompt: '生成一张怪兽规范图' },
{ id: 'spec-ref', label: '参考.png', src: '/ref.png' },
),
).toEqual({
fields: [{ title: '自定义规范提示词', value: '生成一张怪兽规范图' }],
references: [{ title: '参考图', label: '参考.png', src: '/ref.png' }],
});
});
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',
};
}