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', }; }