import { describe, it, expect, vi, beforeEach } from 'vitest'; import { render, screen, fireEvent } from '@testing-library/react'; import LanguageSelector from '../LanguageSelector'; // Create mock function for changeLanguage const mockChangeLanguage = vi.fn(); // Mock react-i18next vi.mock('react-i18next', () => ({ useTranslation: () => ({ i18n: { language: 'en', changeLanguage: mockChangeLanguage, }, }), })); // Mock i18n module vi.mock('../../i18n', () => ({ supportedLanguages: [ { code: 'en', name: 'English', flag: '🇺🇸' }, { code: 'es', name: 'Español', flag: '🇪🇸' }, { code: 'fr', name: 'Français', flag: '🇫🇷' }, ], })); describe('LanguageSelector', () => { beforeEach(() => { mockChangeLanguage.mockClear(); }); describe('dropdown variant', () => { it('renders dropdown button', () => { render(); const button = screen.getByRole('button'); expect(button).toBeInTheDocument(); }); it('shows current language flag by default', () => { render(); expect(screen.getByText('🇺🇸')).toBeInTheDocument(); }); it('shows current language name on larger screens', () => { render(); expect(screen.getByText('English')).toBeInTheDocument(); }); it('opens dropdown on click', () => { render(); const button = screen.getByRole('button'); fireEvent.click(button); expect(screen.getByRole('listbox')).toBeInTheDocument(); }); it('shows all languages when open', () => { render(); const button = screen.getByRole('button'); fireEvent.click(button); expect(screen.getByText('Español')).toBeInTheDocument(); expect(screen.getByText('Français')).toBeInTheDocument(); }); it('hides flag when showFlag is false', () => { render(); expect(screen.queryByText('🇺🇸')).not.toBeInTheDocument(); }); it('applies custom className', () => { const { container } = render(); expect(container.firstChild).toHaveClass('custom-class'); }); it('changes language when clicking a language option in dropdown', () => { render(); const button = screen.getByRole('button'); fireEvent.click(button); const spanishOption = screen.getByText('Español').closest('button'); expect(spanishOption).toBeInTheDocument(); fireEvent.click(spanishOption!); expect(mockChangeLanguage).toHaveBeenCalledWith('es'); }); it('closes dropdown when language is selected', () => { render(); const button = screen.getByRole('button'); fireEvent.click(button); expect(screen.getByRole('listbox')).toBeInTheDocument(); const frenchOption = screen.getByText('Français').closest('button'); fireEvent.click(frenchOption!); expect(screen.queryByRole('listbox')).not.toBeInTheDocument(); }); it('closes dropdown when clicking outside', () => { render(); const button = screen.getByRole('button'); fireEvent.click(button); expect(screen.getByRole('listbox')).toBeInTheDocument(); // Click outside the dropdown fireEvent.mouseDown(document.body); expect(screen.queryByRole('listbox')).not.toBeInTheDocument(); }); it('does not close dropdown when clicking inside dropdown', () => { render(); const button = screen.getByRole('button'); fireEvent.click(button); expect(screen.getByRole('listbox')).toBeInTheDocument(); const listbox = screen.getByRole('listbox'); fireEvent.mouseDown(listbox); expect(screen.getByRole('listbox')).toBeInTheDocument(); }); it('toggles dropdown open/closed on button clicks', () => { render(); const button = screen.getByRole('button'); // Open dropdown fireEvent.click(button); expect(screen.getByRole('listbox')).toBeInTheDocument(); // Close dropdown fireEvent.click(button); expect(screen.queryByRole('listbox')).not.toBeInTheDocument(); }); }); describe('inline variant', () => { it('renders all language buttons', () => { render(); const buttons = screen.getAllByRole('button'); expect(buttons.length).toBe(3); }); it('renders language names', () => { render(); expect(screen.getByText(/English/)).toBeInTheDocument(); expect(screen.getByText(/Español/)).toBeInTheDocument(); expect(screen.getByText(/Français/)).toBeInTheDocument(); }); it('highlights current language', () => { render(); const englishButton = screen.getByText(/English/).closest('button'); expect(englishButton).toHaveClass('bg-brand-600'); }); it('shows flags by default', () => { render(); expect(screen.getByText(/🇺🇸/)).toBeInTheDocument(); }); it('changes language when clicking a language button', () => { render(); const spanishButton = screen.getByText(/Español/).closest('button'); expect(spanishButton).toBeInTheDocument(); fireEvent.click(spanishButton!); expect(mockChangeLanguage).toHaveBeenCalledWith('es'); }); it('calls changeLanguage with correct code for each language', () => { render(); // Test English const englishButton = screen.getByText(/English/).closest('button'); fireEvent.click(englishButton!); expect(mockChangeLanguage).toHaveBeenCalledWith('en'); mockChangeLanguage.mockClear(); // Test French const frenchButton = screen.getByText(/Français/).closest('button'); fireEvent.click(frenchButton!); expect(mockChangeLanguage).toHaveBeenCalledWith('fr'); }); it('hides flags when showFlag is false', () => { render(); // Flags should not be visible expect(screen.queryByText('🇺🇸')).not.toBeInTheDocument(); expect(screen.queryByText('🇪🇸')).not.toBeInTheDocument(); expect(screen.queryByText('🇫🇷')).not.toBeInTheDocument(); // But names should still be there expect(screen.getByText('English')).toBeInTheDocument(); expect(screen.getByText('Español')).toBeInTheDocument(); expect(screen.getByText('Français')).toBeInTheDocument(); }); it('applies custom className', () => { const { container } = render(); expect(container.firstChild).toHaveClass('custom-inline-class'); }); }); });