import { describe, expect, it, vi } from 'vitest'; import type { MedievalNpcVisualOverride } from '../data/medievalNpcVisuals'; import type { EditableNpcVisualState } from './npcVisualEditorModel'; import { NPC_LAYOUT_CONFIG_API_PATH, NPC_VISUAL_OVERRIDES_API_PATH, persistNpcLayoutConfig, persistNpcVisualOverrides, } from './npcVisualEditorPersistence'; import type { NpcLayoutConfig } from './npcVisualShared'; function createEditorState(): EditableNpcVisualState { return { race: 'human', bodyColor: 'black', headIndex: 1, hairColorIndex: 1, hairStyleFrame: 0, facialHairEnabled: false, facialHairColorIndex: 1, facialHairStyleFrame: 0, headgearType: 'none', headgearFile: '', headgearFrame: 0, mainHandType: 'none', mainHandFile: '', mainHandFrame: 0, offHandType: 'none', offHandFile: '', offHandFrame: 0, }; } function createExistingOverride(): MedievalNpcVisualOverride { return { race: 'elf', bodySrc: '/body.png', headSrc: '/head.png', hairSrc: '/hair.png', handSrc: '/hand.png', bodyFrames: [0, 1, 2, 3], headFrame: 0, hairFrame: 1, handFrame: 0, }; } function createLayoutDraft(): NpcLayoutConfig { return { body: { x: 0, y: 0 }, head: { x: 1, y: 2 }, facialHair: { x: 3, y: 4 }, hair: { x: 5, y: 6 }, headgear: { x: 7, y: 8 }, hand: { x: 9, y: 10 }, mainHand: { x: 11, y: 12 }, offHand: { x: 13, y: 14 }, }; } describe('npcVisualEditorPersistence', () => { it('persists merged npc visual overrides and returns the writeback payload', async () => { const saveJson = vi.fn(async () => undefined); const result = await persistNpcVisualOverrides({ overrideMap: { existing: createExistingOverride(), }, npcId: 'npc-1', editorState: createEditorState(), saveJson, }); expect(saveJson).toHaveBeenCalledWith( NPC_VISUAL_OVERRIDES_API_PATH, expect.objectContaining({ existing: createExistingOverride(), 'npc-1': expect.objectContaining({ race: 'human', bodyFrames: [0, 1, 2, 3], }), }), '保存角色形象覆盖配置失败', ); expect(result.nextOverrideMap.existing).toEqual(createExistingOverride()); expect(result.nextOverrideMap['npc-1']).toEqual(expect.objectContaining({ race: 'human' })); expect(result.saveMessage).toContain('npcVisualOverrides.json'); }); it('persists layout config with a cloned payload for local writeback', async () => { const saveJson = vi.fn(async () => undefined); const layoutDraft = createLayoutDraft(); const result = await persistNpcLayoutConfig({ layoutDraft, saveJson, }); expect(saveJson).toHaveBeenCalledWith( NPC_LAYOUT_CONFIG_API_PATH, expect.objectContaining(layoutDraft), '保存角色布局配置失败', ); expect(result.nextLayout).toEqual(layoutDraft); expect(result.nextLayout).not.toBe(layoutDraft); expect(result.saveMessage).toContain('角色布局'); }); });