// @vitest-environment jsdom import { render, screen } from '@testing-library/react'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { AnimationState, type Character } from '../types'; import { CharacterAnimator } from './CharacterAnimator'; vi.mock('../hooks/useResolvedAssetReadUrl', () => ({ useResolvedAssetReadUrl: vi.fn((source: string | null | undefined) => ({ resolvedUrl: source?.trim() ?? '', isResolving: false, shouldResolve: false, })), })); function buildCharacter(overrides: Partial = {}): Character { return { id: 'generated-role', name: '沈砺', title: '守灯人', description: '', backstory: '', avatar: '/generated/portrait.png', portrait: '/generated/portrait.png', assetFolder: 'custom-world', assetVariant: 'generated', attributes: {} as Character['attributes'], personality: '', skills: [], adventureOpenings: {}, ...overrides, }; } describe('CharacterAnimator portrait fallbacks', () => { beforeEach(() => { vi.clearAllMocks(); }); it('keeps idle fallback static on the portrait when idle animation is missing', () => { render( , ); const image = screen.getByRole('img', { name: /沈砺 idle animation/i, }) as HTMLImageElement; expect(image.getAttribute('src')).toBe('/generated/portrait.png'); expect(image.style.transform).toBe(''); }); it('uses a fallen portrait fallback when death animation is missing', () => { render( , ); const image = screen.getByRole('img', { name: /沈砺 die animation/i, }) as HTMLImageElement; expect(image.getAttribute('src')).toBe('/generated/portrait.png'); expect(image.style.animation).toContain( 'character-animator-portrait-death-fall', ); expect(image.style.transform).toContain('rotate(-90deg)'); expect(image.style.transform).toContain('scaleX(-1)'); }); it('uses generated portrait for movement when generated animation is missing', () => { render( , ); const image = screen.getByRole('img', { name: /沈砺 run animation/i, }) as HTMLImageElement; expect(image.getAttribute('src')).toBe('/generated/portrait.png'); }); });