/* @vitest-environment jsdom */ import { render, screen } from '@testing-library/react'; import { afterEach, expect, test, vi } from 'vitest'; import { AnimationState, type Encounter, type GameState, type EquipmentLoadout, WorldType, } from '../types'; import { AdventureEntityModal } from './AdventureEntityModal'; vi.mock('./CharacterAnimator', () => ({ CharacterAnimator: () =>
, })); vi.mock('./MedievalNpcAnimator', () => ({ MedievalNpcAnimator: () =>
, })); vi.mock('./HostileNpcAnimator', () => ({ HostileNpcAnimator: () =>
, })); function createGameState(overrides: Partial = {}): GameState { return { worldType: WorldType.WUXIA, customWorldProfile: null, playerCharacter: null, runtimeStats: { playTimeMs: 0, lastPlayTickAt: null, hostileNpcsDefeated: 0, questsAccepted: 0, itemsUsed: 0, scenesTraveled: 0, }, currentScene: 'test-scene', storyHistory: [], characterChats: {}, animationState: AnimationState.IDLE, currentEncounter: null, npcInteractionActive: false, currentScenePreset: null, sceneHostileNpcs: [], playerX: 0, playerOffsetY: 0, playerFacing: 'right', playerActionMode: 'idle', scrollWorld: false, inBattle: false, playerHp: 100, playerMaxHp: 100, playerMana: 30, playerMaxMana: 30, playerSkillCooldowns: {}, activeCombatEffects: [], playerCurrency: 0, playerInventory: [], playerEquipment: {} as EquipmentLoadout, npcStates: {}, quests: [], roster: [], companions: [], currentBattleNpcId: null, currentNpcBattleMode: null, currentNpcBattleOutcome: null, sparReturnEncounter: null, sparPlayerHpBefore: null, sparPlayerMaxHpBefore: null, sparStoryHistoryBefore: null, ...overrides, } as GameState; } function createEncounter(overrides: Partial = {}): Encounter { return { id: 'runtime-npc', kind: 'npc', npcName: '雾中来客', npcDescription: '带着临时生成形象的相遇者', npcAvatar: '/avatar.png', context: '桥边试探', ...overrides, }; } afterEach(() => { vi.restoreAllMocks(); }); test('NPC 详情立绘优先展示遭遇实例形象,而不是 characterId 对应预设', () => { const encounter = createEncounter({ characterId: 'sword-princess', imageSrc: '/runtime-npc-preview.png', }); render( undefined} />, ); const portrait = screen.getByAltText('雾中来客'); expect(portrait.getAttribute('src')).toBe('/runtime-npc-preview.png'); expect(screen.queryByTestId('character-portrait')).toBeNull(); }); test('NPC 背包物品空 id 会被规范成稳定渲染 id', () => { const consoleErrorSpy = vi .spyOn(console, 'error') .mockImplementation(() => undefined); const encounter = createEncounter(); render( undefined} />, ); expect(screen.getAllByTitle(/裂纹石片 x/)).toHaveLength(2); expect( consoleErrorSpy.mock.calls.some((call) => call.some( (arg) => typeof arg === 'string' && arg.includes('Encountered two children with the same key'), ), ), ).toBe(false); });