1
This commit is contained in:
@@ -1,6 +1,13 @@
|
||||
/* @vitest-environment jsdom */
|
||||
|
||||
import { cleanup, render, screen, waitFor, within } from '@testing-library/react';
|
||||
import {
|
||||
cleanup,
|
||||
fireEvent,
|
||||
render,
|
||||
screen,
|
||||
waitFor,
|
||||
within,
|
||||
} from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { useState } from 'react';
|
||||
import { afterEach, expect, test, vi } from 'vitest';
|
||||
@@ -191,6 +198,7 @@ function createProfile(): CustomWorldProfile {
|
||||
attributeSchema: {
|
||||
id: 'schema-1',
|
||||
worldId: 'world-1',
|
||||
schemaName: '潮雾六维',
|
||||
schemaVersion: 1,
|
||||
generatedFrom: {
|
||||
worldType: 'WUXIA',
|
||||
@@ -199,7 +207,68 @@ function createProfile(): CustomWorldProfile {
|
||||
tone: '压抑、潮湿、带着未解旧伤。',
|
||||
conflictCore: '旧航道归属',
|
||||
},
|
||||
slots: [],
|
||||
slots: [
|
||||
{
|
||||
slotId: 'axis_a',
|
||||
name: '骨势',
|
||||
definition: '扛住压力并正面推进的底子。',
|
||||
positiveSignals: ['硬顶'],
|
||||
negativeSignals: ['畏缩'],
|
||||
combatUseText: '正面承压与破阵。',
|
||||
socialUseText: '在谈判里稳住立场。',
|
||||
explorationUseText: '穿过危险地形。',
|
||||
},
|
||||
{
|
||||
slotId: 'axis_b',
|
||||
name: '身法',
|
||||
definition: '抢位、转场与把握节奏的能力。',
|
||||
positiveSignals: ['灵动'],
|
||||
negativeSignals: ['迟滞'],
|
||||
combatUseText: '移动换位。',
|
||||
socialUseText: '捕捉话锋。',
|
||||
explorationUseText: '快速穿行。',
|
||||
},
|
||||
{
|
||||
slotId: 'axis_c',
|
||||
name: '眼脉',
|
||||
definition: '看破破绽、拆解局势的能力。',
|
||||
positiveSignals: ['洞察'],
|
||||
negativeSignals: ['误判'],
|
||||
combatUseText: '识破招式。',
|
||||
socialUseText: '辨别谎言。',
|
||||
explorationUseText: '发现线索。',
|
||||
},
|
||||
{
|
||||
slotId: 'axis_d',
|
||||
name: '心焰',
|
||||
definition: '决断、压迫与坚持意志的能力。',
|
||||
positiveSignals: ['果断'],
|
||||
negativeSignals: ['犹疑'],
|
||||
combatUseText: '强行压制。',
|
||||
socialUseText: '立威推进。',
|
||||
explorationUseText: '面对险境不退。',
|
||||
},
|
||||
{
|
||||
slotId: 'axis_e',
|
||||
name: '尘缘',
|
||||
definition: '处理人情、承诺和关系牵引的能力。',
|
||||
positiveSignals: ['守信'],
|
||||
negativeSignals: ['冷漠'],
|
||||
combatUseText: '协作配合。',
|
||||
socialUseText: '建立信任。',
|
||||
explorationUseText: '借助人脉。',
|
||||
},
|
||||
{
|
||||
slotId: 'axis_f',
|
||||
name: '玄息',
|
||||
definition: '调息、稳态和久战的能力。',
|
||||
positiveSignals: ['沉稳'],
|
||||
negativeSignals: ['浮躁'],
|
||||
combatUseText: '续战恢复。',
|
||||
socialUseText: '保持耐心。',
|
||||
explorationUseText: '长线跋涉。',
|
||||
},
|
||||
],
|
||||
},
|
||||
playableNpcs: [createPlayableRole('playable-1', '沈砺')],
|
||||
storyNpcs: [createStoryRole('story-1', '顾潮音')],
|
||||
@@ -684,6 +753,57 @@ test('基本设定目标打开独立编辑面板', () => {
|
||||
expect(screen.queryByText('编辑世界信息')).toBeNull();
|
||||
});
|
||||
|
||||
test('世界信息面板可以编辑六个角色维度信息', async () => {
|
||||
const user = userEvent.setup();
|
||||
const savedProfileRef: { current: CustomWorldProfile | null } = {
|
||||
current: null,
|
||||
};
|
||||
|
||||
render(
|
||||
<RpgCreationEntityEditorModal
|
||||
profile={createProfile()}
|
||||
target={{ kind: 'world' }}
|
||||
onClose={() => {}}
|
||||
onProfileChange={(profile) => {
|
||||
savedProfileRef.current = profile;
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByText('角色维度')).toBeTruthy();
|
||||
const nameInputs = screen.getAllByLabelText('维度名称');
|
||||
await user.clear(nameInputs[0]!);
|
||||
await user.type(nameInputs[0]!, '潮骨');
|
||||
|
||||
const definitionFields = screen.getAllByLabelText('定义');
|
||||
await user.clear(definitionFields[0]!);
|
||||
await user.type(definitionFields[0]!, '顶住潮压并正面推进的角色底色。');
|
||||
|
||||
const positiveSignalFields = screen.getAllByLabelText('正向信号');
|
||||
fireEvent.change(positiveSignalFields[0]!, {
|
||||
target: { value: '硬顶, 护阵' },
|
||||
});
|
||||
|
||||
const combatFields = screen.getAllByLabelText('战斗体现');
|
||||
await user.clear(combatFields[0]!);
|
||||
await user.type(combatFields[0]!, '正面压线与护住阵脚。');
|
||||
|
||||
await user.click(screen.getByRole('button', { name: /保存修改/u }));
|
||||
|
||||
expect(savedProfileRef.current?.attributeSchema.slots[0]?.name).toBe(
|
||||
'潮骨',
|
||||
);
|
||||
expect(savedProfileRef.current?.attributeSchema.slots[0]?.definition).toBe(
|
||||
'顶住潮压并正面推进的角色底色。',
|
||||
);
|
||||
expect(
|
||||
savedProfileRef.current?.attributeSchema.slots[0]?.positiveSignals,
|
||||
).toEqual(['硬顶', '护阵']);
|
||||
expect(savedProfileRef.current?.attributeSchema.slots[0]?.combatUseText).toBe(
|
||||
'正面压线与护住阵脚。',
|
||||
);
|
||||
});
|
||||
|
||||
test('可扮演角色列表使用缩略卡片并点击进入编辑', async () => {
|
||||
const user = userEvent.setup();
|
||||
const handleEditTarget = vi.fn();
|
||||
@@ -821,11 +941,15 @@ test('场景图片保存后会同步更新编辑页和场景列表', async () =>
|
||||
const savedSceneChapter = savedProfile.sceneChapterBlueprints?.find(
|
||||
(entry) => entry.sceneId === 'landmark-1',
|
||||
);
|
||||
expect(
|
||||
savedSceneChapter?.acts.every(
|
||||
(act) => act.backgroundImageSrc === '/generated-custom-world-scenes/updated-scene.png',
|
||||
),
|
||||
).toBe(true);
|
||||
expect(savedSceneChapter?.acts[0]?.backgroundImageSrc).toBe(
|
||||
'/generated-custom-world-scenes/updated-scene.png',
|
||||
);
|
||||
expect(savedSceneChapter?.acts[1]?.backgroundImageSrc).not.toBe(
|
||||
'/generated-custom-world-scenes/updated-scene.png',
|
||||
);
|
||||
expect(savedSceneChapter?.acts[2]?.backgroundImageSrc).not.toBe(
|
||||
'/generated-custom-world-scenes/updated-scene.png',
|
||||
);
|
||||
});
|
||||
|
||||
test('开局场景图片保存后会同步更新编辑页和场景列表', async () => {
|
||||
@@ -899,11 +1023,15 @@ test('开局场景图片保存后会同步更新编辑页和场景列表', async
|
||||
const savedSceneChapter = savedProfile.sceneChapterBlueprints?.find(
|
||||
(entry) => entry.sceneId === 'custom-scene-camp',
|
||||
);
|
||||
expect(
|
||||
savedSceneChapter?.acts.every(
|
||||
(act) => act.backgroundImageSrc === '/generated-custom-world-scenes/updated-camp.png',
|
||||
),
|
||||
).toBe(true);
|
||||
expect(savedSceneChapter?.acts[0]?.backgroundImageSrc).toBe(
|
||||
'/generated-custom-world-scenes/updated-camp.png',
|
||||
);
|
||||
expect(savedSceneChapter?.acts[1]?.backgroundImageSrc).not.toBe(
|
||||
'/generated-custom-world-scenes/updated-camp.png',
|
||||
);
|
||||
expect(savedSceneChapter?.acts[2]?.backgroundImageSrc).not.toBe(
|
||||
'/generated-custom-world-scenes/updated-camp.png',
|
||||
);
|
||||
});
|
||||
|
||||
test('开局场景在场景配置面板中与普通场景使用同级参数并可保存', async () => {
|
||||
@@ -960,6 +1088,8 @@ test('开局场景在场景配置面板中与普通场景使用同级参数并
|
||||
|
||||
test('开局场景列表与详情幕预览复用同一套幕级图片', async () => {
|
||||
const profile = createProfileWithSceneChapters();
|
||||
profile.sceneChapterBlueprints![0]!.acts[1]!.backgroundPromptText =
|
||||
'第二幕专属背景提示';
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(
|
||||
@@ -1003,6 +1133,53 @@ test('开局场景列表与详情幕预览复用同一套幕级图片', async ()
|
||||
);
|
||||
});
|
||||
|
||||
test('开局场景幕背景智能生成复用当前幕图片和幕级提示词', async () => {
|
||||
mockedRpgCreationAssetClient.generateSceneImage.mockClear();
|
||||
mockedRpgCreationAssetClient.generateSceneImage.mockResolvedValue({
|
||||
imageSrc: '/generated-custom-world-scenes/camp-act-2-ai.png',
|
||||
assetId: 'asset-camp-act-2',
|
||||
model: 'wan2.2-t2i-flash',
|
||||
size: '1280*720',
|
||||
taskId: 'task-camp-act-2',
|
||||
prompt: '第二幕专属背景提示',
|
||||
});
|
||||
const profile = createProfileWithSceneChapters();
|
||||
profile.sceneChapterBlueprints![0]!.acts[1]!.backgroundPromptText =
|
||||
'第二幕专属背景提示';
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(
|
||||
<RpgCreationEntityEditorModal
|
||||
profile={profile}
|
||||
target={{ kind: 'camp' }}
|
||||
onClose={() => {}}
|
||||
onProfileChange={() => {}}
|
||||
/>,
|
||||
);
|
||||
|
||||
await user.click(within(getSceneActCard(1)).getByRole('button', { name: '配置背景' }));
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('配置幕背景:第2幕')).toBeTruthy();
|
||||
});
|
||||
|
||||
await user.click(screen.getByRole('button', { name: 'AI生成' }));
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('智能生成:潮灯居')).toBeTruthy();
|
||||
});
|
||||
|
||||
expect(screen.getByRole('img', { name: '潮灯居' }).getAttribute('src')).toBe(
|
||||
'/generated-custom-world-scenes/camp-act-2.png',
|
||||
);
|
||||
|
||||
await user.click(screen.getByRole('button', { name: '开始生成' }));
|
||||
await waitFor(() => {
|
||||
expect(mockedRpgCreationAssetClient.generateSceneImage).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
const payload = mockedRpgCreationAssetClient.generateSceneImage.mock.calls[0]?.[0];
|
||||
expect(payload?.userPrompt).toBe('第二幕专属背景提示');
|
||||
});
|
||||
|
||||
test('普通场景世界地图会包含开局场景并高亮当前场景', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user