import { describe, it, expect, beforeEach, vi } from 'vitest'; import { render, screen, fireEvent, act } from '@testing-library/react'; import { MemoryRouter } from 'react-router-dom'; import { useInputBarStore } from '../../src/store/inputBar'; // Mock the auth store to prevent localStorage access issues at module load vi.mock('../../src/store/auth', () => ({ useAuthStore: Object.assign( (selector: (s: any) => any) => selector({ user: null, isAuthenticated: false, isLoading: false, quota: null, logout: vi.fn(), initialize: vi.fn(), }), { getState: () => ({ initialize: vi.fn(), logout: vi.fn() }) } ), })); // We need to test that key components render and integrate correctly. // Since CSS Modules are not fully resolved in jsdom, we focus on DOM structure and logic. describe('PromptInput Component', () => { beforeEach(() => { useInputBarStore.getState().reset(); }); it('should render textarea with universal placeholder', async () => { const { PromptInput } = await import('../../src/components/PromptInput'); render(); const textarea = screen.getByPlaceholderText(/上传1-5张参考图/); expect(textarea).toBeInTheDocument(); }); it('should render textarea with keyframe placeholder after mode switch', async () => { useInputBarStore.getState().switchMode('keyframe'); const { PromptInput } = await import('../../src/components/PromptInput'); render(); const textarea = screen.getByPlaceholderText(/输入描述,定义首帧到尾帧/); expect(textarea).toBeInTheDocument(); }); it('should update store on input', async () => { const { PromptInput } = await import('../../src/components/PromptInput'); render(); const textarea = screen.getByRole('textbox'); fireEvent.change(textarea, { target: { value: 'test prompt' } }); expect(useInputBarStore.getState().prompt).toBe('test prompt'); }); }); describe('UniversalUpload Component', () => { beforeEach(() => { useInputBarStore.getState().reset(); }); it('should render trigger button when no files', async () => { const { UniversalUpload } = await import('../../src/components/UniversalUpload'); render(); expect(screen.getByText('参考内容')).toBeInTheDocument(); }); it('should render thumbnails when files are added', async () => { const file = new File(['mock'], 'test.jpg', { type: 'image/jpeg' }); useInputBarStore.getState().addReferences([file]); const { UniversalUpload } = await import('../../src/components/UniversalUpload'); render(); // Should not show the trigger expect(screen.queryByText('参考内容')).not.toBeInTheDocument(); }); it('should have hidden file input with correct accept', async () => { const { UniversalUpload } = await import('../../src/components/UniversalUpload'); const { container } = render(); const input = container.querySelector('input[type="file"]') as HTMLInputElement; expect(input).toBeTruthy(); expect(input.accept).toBe('image/*,video/*'); expect(input.multiple).toBe(true); }); }); describe('KeyframeUpload Component', () => { beforeEach(() => { useInputBarStore.getState().reset(); useInputBarStore.getState().switchMode('keyframe'); }); it('should render first and last frame triggers', async () => { const { KeyframeUpload } = await import('../../src/components/KeyframeUpload'); render(); expect(screen.getByText('首帧')).toBeInTheDocument(); expect(screen.getByText('尾帧')).toBeInTheDocument(); }); it('should have file inputs accepting only images', async () => { const { KeyframeUpload } = await import('../../src/components/KeyframeUpload'); const { container } = render(); const inputs = container.querySelectorAll('input[type="file"]'); expect(inputs).toHaveLength(2); inputs.forEach((input) => { expect((input as HTMLInputElement).accept).toBe('image/*'); }); }); }); describe('Dropdown Component', () => { it('should render trigger and not show menu initially', async () => { const { Dropdown } = await import('../../src/components/Dropdown'); render( Click me} /> ); expect(screen.getByText('Click me')).toBeInTheDocument(); expect(screen.getByText('Option A')).toBeInTheDocument(); expect(screen.getByText('Option B')).toBeInTheDocument(); }); it('should call onSelect when item clicked', async () => { const onSelect = vi.fn(); const { Dropdown } = await import('../../src/components/Dropdown'); render( Click me} /> ); // Open dropdown fireEvent.click(screen.getByText('Click me')); // Select option B fireEvent.click(screen.getByText('Option B')); expect(onSelect).toHaveBeenCalledWith('b'); }); }); describe('Toast Component', () => { it('should render and be controllable via showToast', async () => { const { Toast, showToast } = await import('../../src/components/Toast'); render(); act(() => { showToast('Test message'); }); expect(screen.getByText('Test message')).toBeInTheDocument(); }); }); describe('Toolbar Component', () => { beforeEach(() => { useInputBarStore.getState().reset(); }); it('should render all toolbar buttons in universal mode', async () => { const { Toolbar } = await import('../../src/components/Toolbar'); render(); // These texts may appear in both trigger and dropdown items expect(screen.getAllByText('视频生成').length).toBeGreaterThanOrEqual(1); expect(screen.getAllByText('Seedance 2.0').length).toBeGreaterThanOrEqual(1); expect(screen.getAllByText('全能参考').length).toBeGreaterThanOrEqual(1); expect(screen.getAllByText('21:9').length).toBeGreaterThanOrEqual(1); expect(screen.getAllByText('15s').length).toBeGreaterThanOrEqual(1); expect(screen.getByText('@')).toBeInTheDocument(); }); it('should show auto-match and hide @ button in keyframe mode', async () => { useInputBarStore.getState().switchMode('keyframe'); const { Toolbar } = await import('../../src/components/Toolbar'); render(); expect(screen.getByText('自动匹配')).toBeInTheDocument(); expect(screen.getAllByText('首尾帧').length).toBeGreaterThanOrEqual(1); expect(screen.getAllByText('5s').length).toBeGreaterThanOrEqual(1); expect(screen.queryByText('@')).not.toBeInTheDocument(); }); it('should show credits section with +30', async () => { const { Toolbar } = await import('../../src/components/Toolbar'); render(); expect(screen.getByText('30')).toBeInTheDocument(); }); }); describe('VideoGenerationPage Component', () => { beforeEach(() => { useInputBarStore.getState().reset(); }); it('should render the page with hint text and input bar', async () => { const { VideoGenerationPage } = await import('../../src/components/VideoGenerationPage'); render( ); expect(screen.getByText('在下方输入提示词,开始创作 AI 视频')).toBeInTheDocument(); }); }); describe('Sidebar Component', () => { it('should render navigation items', async () => { const { Sidebar } = await import('../../src/components/Sidebar'); render(); expect(screen.getByText('灵感')).toBeInTheDocument(); expect(screen.getByText('生成')).toBeInTheDocument(); expect(screen.getByText('资产')).toBeInTheDocument(); expect(screen.getByText('画布')).toBeInTheDocument(); expect(screen.getByText('API')).toBeInTheDocument(); }); });