新增 PlatformAsyncStatePanel 统一 profile 异步状态骨架 扩展 PlatformSegmentedTabs 支持滚动 tab 并接入创作入口与发现页 统一 PixelCloseButton 复用 PlatformModalCloseButton 像素关闭能力 抽取平台入口泥点前置提示弹层并收紧阻断语义 补充组件收口文档与共享决策记录
138 lines
4.3 KiB
TypeScript
138 lines
4.3 KiB
TypeScript
/* @vitest-environment jsdom */
|
|
|
|
import { render, screen } from '@testing-library/react';
|
|
import userEvent from '@testing-library/user-event';
|
|
import { expect, test, vi } from 'vitest';
|
|
|
|
import type { CharacterChatModalState } from '../hooks/rpg-runtime-story';
|
|
import type { Character } from '../types';
|
|
import { CharacterChatModal } from './CharacterChatModal';
|
|
|
|
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: '冷静谨慎',
|
|
skills: [],
|
|
adventureOpenings: {},
|
|
} as Character;
|
|
}
|
|
|
|
function createModalState(
|
|
overrides: Partial<CharacterChatModalState> = {},
|
|
): CharacterChatModalState {
|
|
return {
|
|
target: {
|
|
character: createCharacter(),
|
|
npcId: 'npc-hero',
|
|
roleLabel: '队友',
|
|
hp: 80,
|
|
maxHp: 100,
|
|
mana: 24,
|
|
maxMana: 30,
|
|
},
|
|
draft: '',
|
|
messages: [],
|
|
suggestions: ['先问问线索'],
|
|
summary: '',
|
|
isSending: false,
|
|
isLoadingSuggestions: false,
|
|
error: '暂时无法生成回复。',
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
test('角色聊天错误提示复用暗色 PlatformStatusMessage chrome', () => {
|
|
render(
|
|
<CharacterChatModal
|
|
modal={createModalState()}
|
|
onClose={vi.fn()}
|
|
onDraftChange={vi.fn()}
|
|
onUseSuggestion={vi.fn()}
|
|
onRefreshSuggestions={vi.fn()}
|
|
onSendDraft={vi.fn()}
|
|
/>,
|
|
);
|
|
|
|
const errorMessage = screen.getByText('暂时无法生成回复。');
|
|
|
|
expect(errorMessage.className).toContain('platform-status-message');
|
|
expect(errorMessage.className).toContain('border-amber-300/15');
|
|
expect(errorMessage.className).toContain('bg-amber-500/10');
|
|
expect(errorMessage.className).toContain('text-amber-50/90');
|
|
});
|
|
|
|
test('角色聊天状态、空态和建议复用暗色 UI Kit chrome', () => {
|
|
render(
|
|
<CharacterChatModal
|
|
modal={createModalState()}
|
|
onClose={vi.fn()}
|
|
onDraftChange={vi.fn()}
|
|
onUseSuggestion={vi.fn()}
|
|
onRefreshSuggestions={vi.fn()}
|
|
onSendDraft={vi.fn()}
|
|
/>,
|
|
);
|
|
|
|
const hpStatus = screen.getByText('生命值 80 / 100');
|
|
const summaryFallback = screen.getByText('你们还没有形成新的私下聊天总结。');
|
|
const emptyHistory = screen.getByText(
|
|
'这里会保留你和该角色的私下聊天记录。输入框支持自由发挥,上方三条文本可以帮你快速起句。',
|
|
);
|
|
const refreshButton = screen.getByRole('button', { name: '换一组' });
|
|
const suggestionButton = screen.getByRole('button', { name: '先问问线索' });
|
|
const draftTextarea = screen.getByPlaceholderText('对沈行说点什么...');
|
|
|
|
expect(hpStatus.className).toContain('border-white/10');
|
|
expect(hpStatus.className).toContain('bg-black/25');
|
|
expect(summaryFallback.className).toContain('border-white/10');
|
|
expect(summaryFallback.className).toContain('bg-black/25');
|
|
expect(emptyHistory.className).toContain('platform-empty-state');
|
|
expect(emptyHistory.className).toContain('border-dashed');
|
|
expect(refreshButton.className).toContain(
|
|
'platform-action-button--editor-dark',
|
|
);
|
|
expect(refreshButton.className).toContain('text-[10px]');
|
|
expect(suggestionButton.className).toContain('platform-dark-option-card');
|
|
expect(suggestionButton.className).toContain('border-white/8');
|
|
expect(draftTextarea.className).toContain('platform-text-field--editor-dark');
|
|
expect(draftTextarea.className).toContain('focus:border-sky-300/35');
|
|
});
|
|
|
|
test('角色聊天标题栏内联关闭按钮保持共享关闭行为', async () => {
|
|
const user = userEvent.setup();
|
|
const onClose = vi.fn();
|
|
|
|
render(
|
|
<CharacterChatModal
|
|
modal={createModalState()}
|
|
onClose={onClose}
|
|
onDraftChange={vi.fn()}
|
|
onUseSuggestion={vi.fn()}
|
|
onRefreshSuggestions={vi.fn()}
|
|
onSendDraft={vi.fn()}
|
|
/>,
|
|
);
|
|
|
|
const closeButton = screen.getByRole('button', { name: '关闭角色聊天' });
|
|
await user.click(closeButton);
|
|
|
|
expect(closeButton.className).toContain('relative');
|
|
expect(closeButton.className).toContain('shrink-0');
|
|
expect(closeButton.getAttribute('title')).toBe('关闭角色聊天');
|
|
expect(onClose).toHaveBeenCalledTimes(1);
|
|
});
|