import { renderToStaticMarkup } from 'react-dom/server'; import { expect, test } from 'vitest'; import { type Character, type StoryMoment, WorldType } from '../../types'; import { RpgAdventurePanel } from './RpgAdventurePanel'; function createCharacter(): Character { return { id: 'hero', name: '沈行', title: '试剑客', description: '测试主角', backstory: '测试背景', avatar: '/hero.png', portrait: '/hero.png', assetFolder: 'hero', assetVariant: 'default', attributes: { strength: 10, agility: 10, intelligence: 8, spirit: 9, }, personality: 'calm', skills: [], adventureOpenings: {}, } as Character; } test('adventure panel renders system turns without special relationship labels', () => { const currentStory: StoryMoment = { text: '你们的语气忽然冷了下来。', displayMode: 'dialogue', dialogue: [ { speaker: 'npc', speakerName: '柳无声', text: '这件事你最好别再追问。' }, { speaker: 'system', text: '这轮交谈先在这里收束。' }, ], options: [], }; const html = renderToStaticMarkup( undefined} onChoice={() => undefined} onOpenCharacter={() => undefined} onOpenInventory={() => undefined} playerCharacter={createCharacter()} worldType={WorldType.WUXIA} quests={[]} questUi={{ acknowledgeQuestCompletion: () => undefined, claimQuestReward: () => null, }} npcChatQuestOfferUi={{ replacePendingOffer: () => false, abandonPendingOffer: () => false, acceptPendingOffer: () => null, }} goalStack={{ northStarGoal: null, activeGoal: null, immediateStepGoal: null, supportGoals: [], }} goalPulse={null} onDismissGoalPulse={() => undefined} battleRewardUi={{ reward: null, dismiss: () => undefined, }} playerHp={100} playerMaxHp={100} playerMana={20} playerMaxMana={20} playerSkillCooldowns={{}} inBattle={false} currentNpcBattleMode={null} statistics={{ playTimeMs: 0, hostileNpcsDefeated: 0, questsAccepted: 0, questsCompleted: 0, questsTurnedIn: 0, itemsUsed: 0, scenesTraveled: 0, currentSceneName: '竹林古道', playerCurrency: 0, inventoryItemCount: 0, inventoryStackCount: 0, activeCompanionCount: 0, rosterCompanionCount: 0, }} musicVolume={0.6} onMusicVolumeChange={() => undefined} onSaveAndExit={() => undefined} />, ); expect(html).toContain('系统'); expect(html).toContain('这轮交谈先在这里收束。'); expect(html).not.toContain('关系变化'); }); test('adventure panel shows current act label and remaining turns for limited hostile npc chat', () => { const currentStory: StoryMoment = { text: '断桥客仍在压着最后那半句真相。', displayMode: 'dialogue', dialogue: [ { speaker: 'player', text: '你到底还在替谁守着这座桥?' }, { speaker: 'npc', speakerName: '断桥客', text: '你还没资格知道全名。' }, ], options: [], npcChatState: { npcId: 'npc-rival', npcName: '断桥客', turnCount: 3, customInputPlaceholder: '输入你想对 TA 说的话', sceneActId: 'scene-bridge-act-1', turnLimit: 5, remainingTurns: 2, limitReason: 'negative_affinity', forceExitAfterTurn: false, }, }; const html = renderToStaticMarkup( undefined} onChoice={() => undefined} onSubmitNpcChatInput={() => true} onExitNpcChat={() => true} onOpenCharacter={() => undefined} onOpenInventory={() => undefined} playerCharacter={createCharacter()} worldType={WorldType.WUXIA} quests={[]} questUi={{ acknowledgeQuestCompletion: () => undefined, claimQuestReward: () => null, }} npcChatQuestOfferUi={{ replacePendingOffer: () => false, abandonPendingOffer: () => false, acceptPendingOffer: () => null, }} goalStack={{ northStarGoal: null, activeGoal: null, immediateStepGoal: null, supportGoals: [], }} goalPulse={null} onDismissGoalPulse={() => undefined} battleRewardUi={{ reward: null, dismiss: () => undefined, }} playerHp={100} playerMaxHp={100} playerMana={20} playerMaxMana={20} playerSkillCooldowns={{}} inBattle={false} currentNpcBattleMode={null} statistics={{ playTimeMs: 0, hostileNpcsDefeated: 0, questsAccepted: 0, questsCompleted: 0, questsTurnedIn: 0, itemsUsed: 0, scenesTraveled: 0, currentSceneName: '断桥口', playerCurrency: 0, inventoryItemCount: 0, inventoryStackCount: 0, activeCompanionCount: 0, rosterCompanionCount: 0, }} musicVolume={0.6} onMusicVolumeChange={() => undefined} onSaveAndExit={() => undefined} currentSceneActTitle="断桥口 · 对峙幕" currentSceneActIndex={1} currentSceneActCount={3} />, ); expect(html).toContain('当前幕'); expect(html).toContain('断桥口 · 对峙幕'); expect(html).toContain('1/3'); expect(html).toContain('剩余交谈'); expect(html).toContain('2 轮'); });