Files
Genarrative/src/components/image-editor/ImageCanvasGenerationSubmissionModel.test.ts
T
lhk229 308d440816 新抠图算法(轻量版)上线 (#73)
新抠图算法(轻量版)上线,生图背景色同步改为多颜色+自动决策

---------

Co-authored-by: kdletters <kdletters@qq.com>
Reviewed-on: https://git.genarrative.world/git/GenarrativeAI/Genarrative/pulls/73
Reviewed-by: kdletters <kdletters@qq.com>
Co-authored-by: Linghong <ink29535@proton.me>
Co-committed-by: Linghong <ink29535@proton.me>
2026-07-07 14:57:10 +08:00

1054 lines
29 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 type { CanvasLayer } from './ImageCanvasEditorTypes';
import {
buildCharacterAnimationSubmissionPlan,
buildIconSpritesheetGenerationSubmissionPlan,
buildImageGenerationSubmissionPlan,
} from './ImageCanvasGenerationSubmissionModel';
import {
IMAGE_MODEL_GPT_IMAGE_2,
IMAGE_MODEL_NANOBANANA2,
} from './ImageCanvasGenerationModel';
function createLayer(overrides: Partial<CanvasLayer> = {}): CanvasLayer {
return {
id: 'layer-source',
resourceId: 'resource-source',
title: '源图',
src: 'data:image/png;base64,source',
x: 120,
y: 140,
width: 320,
height: 240,
originalWidth: 1024,
originalHeight: 768,
zIndex: 2,
sourceType: 'uploaded',
...overrides,
};
}
describe('ImageCanvasGenerationSubmissionModel', () => {
it('builds normal image generation submission plans', () => {
const plan = buildImageGenerationSubmissionPlan({
dialog: {
mode: 'generate',
prompt: ' 一张发光主视觉 ',
status: 'idle',
},
layers: [],
nextGeneratedIndex: 3,
});
expect(plan).toMatchObject({
kind: 'image',
normalizedPrompt: '一张发光主视觉',
input: {
prompt: '一张发光主视觉',
model: 'gemini-3.1-flash-image-preview',
aspectRatio: '1:1',
imageSize: '1K',
},
result: {
title: '生成图片 3',
generationInputs: {
fields: [{ title: '生成提示词', value: '一张发光主视觉' }],
references: [],
},
},
rememberImageModel: 'gemini-3.1-flash-image-preview',
});
});
it('normalizes legacy nanobanana aliases before submitting image generation', () => {
const plan = buildImageGenerationSubmissionPlan({
dialog: {
mode: 'generate',
prompt: '一张明亮图片',
status: 'idle',
imageModel: 'nanobanana2',
aspectRatio: '16:9',
imageSize: '2K',
},
layers: [],
nextGeneratedIndex: 5,
});
expect(plan).toMatchObject({
kind: 'image',
input: {
model: IMAGE_MODEL_NANOBANANA2,
aspectRatio: '16:9',
imageSize: '2K',
},
rememberImageModel: IMAGE_MODEL_NANOBANANA2,
});
});
it('builds edit submission plans with the source layer snapshot', () => {
const sourceLayer = createLayer();
const plan = buildImageGenerationSubmissionPlan({
dialog: {
mode: 'edit',
prompt: '',
status: 'idle',
sourceLayerId: sourceLayer.id,
},
layers: [sourceLayer],
nextGeneratedIndex: 4,
});
expect(plan).toMatchObject({
kind: 'edit',
normalizedPrompt: '修改当前图片',
sourceLayer,
generationInputs: {
fields: [{ title: '修改要求', value: '修改当前图片' }],
references: [
{
title: '参考图',
label: '源图',
refType: 'project-resource',
refId: 'resource-source',
},
],
},
});
});
it('builds quick-edit canvas generator plans as direct source edits', () => {
const sourceLayer = createLayer({
objectKey: 'generated-character-drafts/editor/source.png',
assetKind: 'character',
});
const plan = buildImageGenerationSubmissionPlan({
dialog: {
mode: 'quick-edit',
prompt: '把当前图改成雨天',
status: 'idle',
sourceLayerId: sourceLayer.id,
imageModel: IMAGE_MODEL_GPT_IMAGE_2,
aspectRatio: '4:3',
imageSize: '1K',
generationReferences: [
{
id: 'style-reference',
label: '风格参考',
src: 'data:image/png;base64,style',
},
],
},
layers: [sourceLayer],
nextGeneratedIndex: 4,
});
expect(plan).toMatchObject({
kind: 'quick-edit',
normalizedPrompt: '把当前图改成雨天',
sourceLayer,
editInput: {
size: '1024x768',
model: IMAGE_MODEL_GPT_IMAGE_2,
},
result: {
title: '源图 快速编辑',
assetKind: 'character',
generationInputs: {
fields: [{ title: '快速编辑提示词', value: '把当前图改成雨天' }],
references: [
{
title: '原图',
label: '源图',
refType: 'project-resource',
refId: 'resource-source',
},
],
},
},
rememberImageModel: IMAGE_MODEL_GPT_IMAGE_2,
});
});
it('builds spec generation plans with reference prompt semantics', () => {
const plan = buildImageGenerationSubmissionPlan({
dialog: {
mode: 'spec',
prompt: '',
status: 'idle',
specType: 'icon',
specValues: {
playSetting: '休闲消除',
artStyle: '清爽卡通',
bodyRatio: '3',
characterView: '',
customPrompt: '',
},
specReference: {
id: 'spec-ref',
label: '参考.png',
src: 'data:image/png;base64,ref',
resourceId: 'resource-spec-ref',
},
},
layers: [],
nextGeneratedIndex: 8,
});
expect(plan).toMatchObject({
kind: 'image',
normalizedPrompt: 'AI 生成图片',
input: {
size: '2048x1152',
model: 'gpt-image-2',
aspectRatio: '16:9',
imageSize: '2K',
kind: 'spec',
referenceImageSrcs: ['data:image/png;base64,ref'],
prompt: expect.stringContaining('参考图生成规范'),
},
result: {
assetKind: 'icon-spec',
title: '图标规范 8',
},
});
expect(plan.kind === 'image' ? plan.result.generationInputs : null).toEqual(
{
fields: [
{ title: '玩法设定', value: '休闲消除' },
{ title: '美术风格', value: '清爽卡通' },
],
references: [
{
title: '参考图',
label: '参考.png',
refType: 'project-resource',
refId: 'resource-spec-ref',
},
],
},
);
});
it('treats the renamed UI spec entry as an icon spec asset', () => {
const plan = buildImageGenerationSubmissionPlan({
dialog: {
mode: 'spec',
prompt: '',
status: 'idle',
specType: 'ui',
specValues: {
playSetting: '消除类派对玩法',
artStyle: '糖果玻璃拟物',
bodyRatio: '3',
characterView: '',
customPrompt: '',
},
},
layers: [],
nextGeneratedIndex: 2,
});
expect(plan).toMatchObject({
kind: 'image',
result: {
assetKind: 'icon-spec',
title: '图标规范 2',
},
});
expect(plan.kind === 'image' ? plan.input : null).not.toHaveProperty(
'referenceImageSrcs',
);
expect(plan.kind === 'image' ? plan.input.prompt : '').not.toContain(
'参考图生成规范',
);
});
it('builds character plans with references and remembered model', () => {
const plan = buildImageGenerationSubmissionPlan({
dialog: {
mode: 'character',
prompt: ' 白发骑士 ',
status: 'idle',
imageModel: 'gpt-image-2',
aspectRatio: '2:3',
imageSize: '2K',
characterSpecReference: {
id: 'spec',
label: '角色规范',
src: 'data:image/png;base64,spec',
resourceId: 'resource-character-spec',
},
characterReferences: [
{
id: 'ref-1',
label: '盔甲参考',
src: 'data:image/png;base64,armor',
resourceId: 'resource-armor',
},
],
},
layers: [],
nextGeneratedIndex: 9,
});
expect(plan).toMatchObject({
kind: 'image',
normalizedPrompt: '白发骑士',
input: {
prompt: '白发骑士',
kind: 'character',
model: 'gpt-image-2',
screenColor: 'auto',
segModel: 'birefnet',
aspectRatio: '2:3',
imageSize: '2K',
referenceImageSrcs: [
'data:image/png;base64,spec',
'data:image/png;base64,armor',
],
},
result: {
assetKind: 'character',
title: '角色形象 9',
},
rememberImageModel: 'gpt-image-2',
});
expect(plan.kind === 'image' ? plan.result.generationInputs : null).toEqual(
{
fields: [
{ title: '角色设定', value: '白发骑士' },
],
references: [
{
title: '角色规范',
label: '角色规范',
refType: 'project-resource',
refId: 'resource-character-spec',
},
{
title: '常规参考图 1',
label: '盔甲参考',
refType: 'project-resource',
refId: 'resource-armor',
},
],
},
);
});
it('locks UI design generation submissions to gpt-image-2', () => {
const plan = buildImageGenerationSubmissionPlan({
dialog: {
mode: 'ui-design',
prompt: ' 设计移动端商城首页 ',
status: 'idle',
imageModel: 'nanobanana2',
aspectRatio: '16:9',
imageSize: '0.5K',
uiDesignSpecReference: {
id: 'ui-spec',
label: 'UI图标规范',
src: 'data:image/png;base64,ui-spec',
resourceId: 'resource-ui-spec',
},
generationReferences: [
{
id: 'ui-ref',
label: '界面风格参考',
src: 'data:image/png;base64,ui-ref',
resourceId: 'resource-ui-ref',
},
],
},
layers: [],
nextGeneratedIndex: 6,
});
expect(plan).toMatchObject({
kind: 'image',
normalizedPrompt: '设计移动端商城首页',
input: {
kind: 'ui-design',
model: IMAGE_MODEL_GPT_IMAGE_2,
aspectRatio: '16:9',
imageSize: '1K',
referenceImageSrcs: [
'data:image/png;base64,ui-spec',
'data:image/png;base64,ui-ref',
],
},
result: {
assetKind: 'ui-design',
title: 'UI设计图 6',
generationInputs: {
fields: [{ title: '用户输入', value: '设计移动端商城首页' }],
references: [
{
title: '图标规范',
label: 'UI图标规范',
refType: 'project-resource',
refId: 'resource-ui-spec',
},
{
title: '参考图 1',
label: '界面风格参考',
refType: 'project-resource',
refId: 'resource-ui-ref',
},
],
},
},
rememberImageModel: IMAGE_MODEL_GPT_IMAGE_2,
});
});
it('builds publication material plans with structured game fields', () => {
const plan = buildImageGenerationSubmissionPlan({
dialog: {
mode: 'publication',
prompt: '',
status: 'idle',
publicationGameInfo: {
gameName: ' 重庆洪崖洞火锅 ',
gameCategories: '抓大鹅、休闲、治愈、手绘风',
gameDescription: '在一堆物品里点击,找到三个一样的物品将其消除',
},
publicationReferences: [
{
id: 'ref-1',
label: '参考图',
src: 'data:image/png;base64,ref',
resourceId: 'resource-publication-ref',
},
],
},
layers: [],
nextGeneratedIndex: 10,
});
expect(plan).toEqual({
kind: 'image',
normalizedPrompt: [
'游戏名:重庆洪崖洞火锅',
'游戏分类:抓大鹅、休闲、治愈、手绘风',
'一句话描述游戏:在一堆物品里点击,找到三个一样的物品将其消除',
].join('\n'),
input: {
prompt: expect.stringContaining('【宣发素材类型】游戏首图'),
size: '720x540',
kind: 'publication-material',
model: 'gpt-image-2',
aspectRatio: '4:3',
imageSize: '1K',
referenceImageSrcs: ['data:image/png;base64,ref'],
},
result: {
title: '10 宣发素材',
generationInputs: {
fields: [
{ title: '游戏名', value: '重庆洪崖洞火锅' },
{ title: '游戏分类', value: '抓大鹅、休闲、治愈、手绘风' },
{
title: '一句话描述游戏',
value: '在一堆物品里点击,找到三个一样的物品将其消除',
},
],
references: [
{
title: '宣发参考图 1',
label: '参考图',
refType: 'project-resource',
refId: 'resource-publication-ref',
},
],
},
},
});
const prompt = plan.kind === 'image' ? plan.input.prompt : '';
expect(prompt).toContain('游戏名:重庆洪崖洞火锅');
expect(prompt).toContain('标题文字必须直接进入画面');
expect(prompt).toContain('输出检查:720 x 5404:3');
});
it('builds detail gallery publication plans as one 720p portrait image', () => {
const plan = buildImageGenerationSubmissionPlan({
dialog: {
mode: 'publication',
prompt: '',
status: 'idle',
publicationWorkflowId: 'publication-detail-gallery',
publicationGameInfo: {
gameName: '云朵消消乐',
gameCategories: '',
gameDescription: '',
},
},
layers: [],
nextGeneratedIndex: 11,
});
expect(plan).toMatchObject({
kind: 'image',
input: {
prompt: expect.stringContaining('【宣发素材类型】详情页单图'),
size: '720x1280',
kind: 'publication-material',
aspectRatio: '9:16',
imageSize: '1K',
},
result: {
title: '11 宣发素材',
},
});
expect(plan.kind === 'image' ? plan.input.prompt : '').toContain(
'本次只生成一张 720 x 1280 竖版详情图',
);
expect(plan.kind === 'image' ? plan.input.prompt : '').toContain(
'不是五图合集或拼贴',
);
});
it('builds promo poster publication plans as 720p landscape images', () => {
const plan = buildImageGenerationSubmissionPlan({
dialog: {
mode: 'publication',
prompt: '',
status: 'idle',
publicationWorkflowId: 'publication-promo-poster',
publicationGameInfo: {
gameName: '火锅节活动',
gameCategories: '',
gameDescription: '',
},
},
layers: [],
nextGeneratedIndex: 12,
});
expect(plan).toMatchObject({
kind: 'image',
input: {
prompt: expect.stringContaining('【宣发素材类型】运营海报'),
size: '1280x720',
kind: 'publication-material',
aspectRatio: '16:9',
imageSize: '1K',
},
result: {
title: '12 宣发素材',
},
});
expect(plan.kind === 'image' ? plan.input.prompt : '').toContain(
'16:9 横版 720p,建议 1280 x 720',
);
expect(plan.kind === 'image' ? plan.input.prompt : '').toContain(
'此处换上你的游戏二维码',
);
expect(plan.kind === 'image' ? plan.input.prompt : '').toContain(
'方块内部上方用黑色小字',
);
});
it('locks publication material plans to gpt-image-2 and does not remember the fixed model', () => {
const plan = buildImageGenerationSubmissionPlan({
dialog: {
mode: 'publication',
prompt: '',
status: 'idle',
publicationWorkflowId: 'publication-promo-poster',
publicationGameInfo: {
gameName: '星际塔防',
gameCategories: '',
gameDescription: '',
},
publicationReferences: [],
imageModel: 'nanobanana2',
},
layers: [],
nextGeneratedIndex: 4,
});
expect(plan.kind).toBe('image');
if (plan.kind !== 'image') {
return;
}
expect(plan.input).toEqual(
expect.objectContaining({
kind: 'publication-material',
model: 'gpt-image-2',
size: '1280x720',
aspectRatio: '16:9',
imageSize: '1K',
}),
);
expect(plan.rememberImageModel).toBeUndefined();
});
it('throws when edit source layer is missing', () => {
expect(() =>
buildImageGenerationSubmissionPlan({
dialog: {
mode: 'edit',
prompt: '修图',
status: 'idle',
sourceLayerId: 'missing-layer',
},
layers: [],
nextGeneratedIndex: 1,
}),
).toThrow('未找到要修改的图片');
});
it('returns an icon spritesheet error when the spec reference is missing', () => {
const plan = buildIconSpritesheetGenerationSubmissionPlan({
mode: 'icon',
prompt: '',
status: 'idle',
iconDescriptions: ['返回按钮'],
});
expect(plan).toEqual({
ok: false,
errorMessage: '请选择图标规范',
});
});
it('returns an icon spritesheet error when descriptions are empty', () => {
const plan = buildIconSpritesheetGenerationSubmissionPlan({
mode: 'icon',
prompt: '',
status: 'idle',
iconSpecReference: {
id: 'icon-spec',
label: '图标规范',
src: 'data:image/png;base64,spec',
resourceId: 'resource-icon-spec',
},
iconDescriptions: [' ', '\n'],
});
expect(plan).toEqual({
ok: false,
errorMessage: '请填写素材描述',
});
});
it('builds icon spritesheet plans with trimmed descriptions and references', () => {
const plan = buildIconSpritesheetGenerationSubmissionPlan({
mode: 'icon',
prompt: ' 返回按钮 \n\n设置按钮',
status: 'idle',
imageModel: 'gpt-image-2',
aspectRatio: '3:2',
imageSize: '2K',
iconSpecReference: {
id: 'icon-spec',
label: '图标规范',
src: 'data:image/png;base64,spec',
resourceId: 'resource-icon-spec',
},
generationReferences: [
{
id: 'icon-ref',
label: '按钮风格参考',
src: 'data:image/png;base64,ref',
resourceId: 'resource-icon-ref',
},
],
iconDescriptions: ['旧返回', '旧设置'],
});
expect(plan).toEqual({
ok: true,
iconDescriptions: ['返回按钮', '设置按钮'],
input: {
referenceImageSrc: 'data:image/png;base64,spec',
referenceImageSrcs: ['data:image/png;base64,ref'],
iconDescriptions: ['返回按钮', '设置按钮'],
model: 'gpt-image-2',
screenColor: 'auto',
segModel: 'birefnet',
aspectRatio: '3:2',
imageSize: '2K',
},
generationInputs: {
fields: [{ title: '素材描述', value: '返回按钮\n设置按钮' }],
references: [
{
title: '图标规范',
label: '图标规范',
refType: 'project-resource',
refId: 'resource-icon-spec',
},
{
title: '参考图 1',
label: '按钮风格参考',
refType: 'project-resource',
refId: 'resource-icon-ref',
},
],
},
rememberImageModel: 'gpt-image-2',
});
});
it('builds character animation plans with trimmed prompt and object key source', () => {
const sourceLayer = createLayer({
id: 'character-layer',
title: '角色图',
objectKey: 'generated/character.png',
assetKind: 'character',
originalWidth: 960,
originalHeight: 1280,
});
const plan = buildCharacterAnimationSubmissionPlan({
panel: {
sourceLayerId: 'character-layer',
promptText: ' 循环奔跑动作 ',
resolution: '720p',
ratio: 'same',
frameCount: 48,
durationSeconds: 6,
status: 'idle',
},
sourceLayer,
});
expect(plan).toEqual({
promptText: '循环奔跑动作',
input: {
sourceLayerId: 'character-layer',
sourceImageSrc: 'generated/character.png',
sourceWidth: 960,
sourceHeight: 1280,
promptText: '循环奔跑动作',
resolution: '720p',
ratio: 'same',
frameCount: 48,
durationSeconds: 6,
model: 'seedance2.0-fast',
},
});
});
it('builds seedance video plans with multimodal references', () => {
const plan = buildImageGenerationSubmissionPlan({
dialog: {
mode: 'video',
prompt: ' 根据参考生成角色宣传片 ',
status: 'idle',
videoModel: 'seedance2.0-fast',
videoResolution: '720p',
videoDurationSeconds: 5,
generationReferences: [
{
id: 'image-ref',
label: '参考图.png',
src: 'data:image/png;base64,image',
mediaType: 'image',
mimeType: 'image/png',
sizeBytes: 1024,
resourceId: 'resource-video-image-ref',
},
{
id: 'video-ref',
label: '参考视频.mp4',
src: 'https://signed.example.com/video.mp4',
mediaType: 'video',
mimeType: 'video/mp4',
sizeBytes: 2048,
durationSeconds: 4,
objectKey:
'generated-character-drafts/editor/seedance-references/video/video.mp4',
resourceId: 'resource-video-ref',
},
{
id: 'audio-ref',
label: '参考音频.mp3',
src: 'https://signed.example.com/audio.mp3',
mediaType: 'audio',
mimeType: 'audio/mpeg',
sizeBytes: 512,
durationSeconds: 3,
objectKey:
'generated-character-drafts/editor/seedance-references/audio/audio.mp3',
resourceId: 'resource-audio-ref',
},
],
},
layers: [],
nextGeneratedIndex: 6,
});
expect(plan).toMatchObject({
kind: 'video',
input: {
model: 'seedance2.0-fast',
referenceImageSrcs: ['data:image/png;base64,image'],
referenceVideoSrcs: [
'generated-character-drafts/editor/seedance-references/video/video.mp4',
],
referenceAudioSrcs: [
'generated-character-drafts/editor/seedance-references/audio/audio.mp3',
],
},
result: {
generationInputs: {
references: [
{
title: '参考图 1',
label: '参考图.png',
refType: 'project-resource',
refId: 'resource-video-image-ref',
},
{
title: '参考视频 1',
label: '参考视频.mp4',
refType: 'project-resource',
refId: 'resource-video-ref',
},
{
title: '参考音频 1',
label: '参考音频.mp3',
refType: 'project-resource',
refId: 'resource-audio-ref',
},
],
},
},
});
});
it('does not submit references to non seedance video models', () => {
const plan = buildImageGenerationSubmissionPlan({
dialog: {
mode: 'video',
prompt: '镜头推进',
status: 'idle',
videoModel: 'kling3.0',
generationReferences: [
{
id: 'image-ref',
label: '参考图.png',
src: 'data:image/png;base64,image',
mediaType: 'image',
},
],
},
layers: [],
nextGeneratedIndex: 2,
});
expect(plan.kind).toBe('video');
if (plan.kind === 'video') {
expect(plan.input).not.toHaveProperty('referenceImageSrcs');
expect(plan.input).not.toHaveProperty('referenceVideoSrcs');
expect(plan.input).not.toHaveProperty('referenceAudioSrcs');
}
});
it('rejects seedance video plans that only contain audio references', () => {
expect(() =>
buildImageGenerationSubmissionPlan({
dialog: {
mode: 'video',
prompt: '根据音频生成视频',
status: 'idle',
videoModel: 'seedance2.0',
generationReferences: [
{
id: 'audio-ref',
label: '旁白.wav',
src: 'data:audio/wav;base64,audio',
mediaType: 'audio',
durationSeconds: 4,
},
],
},
layers: [],
nextGeneratedIndex: 2,
}),
).toThrow('参考音频必须搭配参考图片或参考视频');
});
it('rejects seedance video plans that exceed reference count limits', () => {
expect(() =>
buildImageGenerationSubmissionPlan({
dialog: {
mode: 'video',
prompt: '生成视频',
status: 'idle',
videoModel: 'seedance2.0-fast',
generationReferences: Array.from({ length: 10 }, (_, index) => ({
id: `image-${index}`,
label: `参考图${index}.png`,
src: `data:image/png;base64,${index}`,
mediaType: 'image' as const,
})),
},
layers: [],
nextGeneratedIndex: 2,
}),
).toThrow('参考图片最多 9 张');
});
it('builds video generation plans with seedance fast as the fallback model', () => {
const plan = buildImageGenerationSubmissionPlan({
dialog: {
mode: 'video',
prompt: ' 角色绕场一周 ',
status: 'idle',
generationReferences: [],
videoResolution: '480p',
videoDurationSeconds: 4,
},
layers: [],
nextGeneratedIndex: 3,
});
expect(plan).toMatchObject({
kind: 'video',
normalizedPrompt: '角色绕场一周',
input: {
model: 'seedance2.0-fast',
},
result: {
title: '生成视频 3',
},
});
});
it('builds video generation plans with explicit seedance standard model', () => {
const plan = buildImageGenerationSubmissionPlan({
dialog: {
mode: 'video',
prompt: ' 生成电影感开场动画 ',
status: 'idle',
videoModel: 'seedance2.0',
videoResolution: '720p',
videoDurationSeconds: 5,
},
layers: [],
nextGeneratedIndex: 7,
});
expect(plan).toMatchObject({
kind: 'video',
normalizedPrompt: '生成电影感开场动画',
input: {
prompt: '生成电影感开场动画',
model: 'seedance2.0',
resolution: '720p',
durationSeconds: 5,
},
result: {
title: '生成视频 7',
},
});
});
it('builds game sound effect audio submission plans', () => {
const plan = buildImageGenerationSubmissionPlan({
dialog: {
mode: 'audio-sound-effect',
prompt: ' 金币掉落叮当声 ',
status: 'idle',
soundDurationSeconds: 7,
},
layers: [],
nextGeneratedIndex: 4,
});
expect(plan).toEqual({
kind: 'audio',
audioKind: 'sound-effect',
normalizedPrompt: '金币掉落叮当声',
input: {
prompt: '金币掉落叮当声',
model: 'audio1.0',
duration: 7,
},
result: {
title: '游戏音效 4',
generationInputs: {
fields: [
{ title: 'prompt', value: '金币掉落叮当声' },
{ title: 'model', value: 'audio1.0' },
{ title: 'duration', value: '7秒' },
],
references: [],
},
},
});
});
it('uses 5 seconds as default game sound effect duration', () => {
const plan = buildImageGenerationSubmissionPlan({
dialog: {
mode: 'audio-sound-effect',
prompt: '按钮确认短促音',
status: 'idle',
},
layers: [],
nextGeneratedIndex: 6,
});
expect(plan).toMatchObject({
kind: 'audio',
audioKind: 'sound-effect',
input: {
prompt: '按钮确认短促音',
model: 'audio1.0',
duration: 5,
},
result: {
generationInputs: {
fields: [
{ title: 'prompt', value: '按钮确认短促音' },
{ title: 'model', value: 'audio1.0' },
{ title: 'duration', value: '5秒' },
],
},
},
});
});
it('builds game background music audio submission plans with instrumental fixed to true', () => {
const plan = buildImageGenerationSubmissionPlan({
dialog: {
mode: 'audio-background-music',
prompt: ' 温暖轻快的森林冒险背景音乐 ',
status: 'idle',
},
layers: [],
nextGeneratedIndex: 5,
});
expect(plan).toEqual({
kind: 'audio',
audioKind: 'background-music',
normalizedPrompt: '温暖轻快的森林冒险背景音乐',
input: {
gptDescriptionPrompt: '温暖轻快的森林冒险背景音乐',
makeInstrumental: true,
},
result: {
title: '游戏背景音乐 5',
generationInputs: {
fields: [
{
title: 'gpt_description_prompt',
value: '温暖轻快的森林冒险背景音乐',
},
],
references: [],
},
},
});
});
});