2025-12-03 21:56:50 +00:00
|
|
|
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
|
|
|
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
|
|
|
import { PreferenceSettings } from './PreferenceSettings';
|
|
|
|
|
import { PreferenceSettings as PreferenceSettingsType } from '../../types/settings';
|
|
|
|
|
|
|
|
|
|
// Mock ResizeObserver for tests
|
|
|
|
|
global.ResizeObserver = vi.fn().mockImplementation(() => ({
|
|
|
|
|
observe: vi.fn(),
|
|
|
|
|
unobserve: vi.fn(),
|
|
|
|
|
disconnect: vi.fn(),
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
// Mock Select component
|
|
|
|
|
vi.mock('@/components/ui/select', () => ({
|
|
|
|
|
Select: ({ value, onChange, options, placeholder }: any) => (
|
|
|
|
|
<div data-testid="select">
|
|
|
|
|
<select
|
|
|
|
|
value={typeof value === 'string' ? value : ''}
|
|
|
|
|
onChange={(e) => onChange(e.target.value)}
|
|
|
|
|
data-placeholder={placeholder}
|
|
|
|
|
>
|
|
|
|
|
<option value="">{placeholder}</option>
|
|
|
|
|
{options.map((opt: any) => (
|
|
|
|
|
<option key={opt.value} value={opt.value}>
|
|
|
|
|
{opt.label}
|
|
|
|
|
</option>
|
|
|
|
|
))}
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
),
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
// Mock RadioGroup
|
|
|
|
|
vi.mock('@/components/ui/radio-group', () => ({
|
|
|
|
|
RadioGroup: ({ children, value, onValueChange }: any) => (
|
|
|
|
|
<div data-testid="radio-group" data-value={value}>
|
|
|
|
|
{children}
|
|
|
|
|
<button onClick={() => onValueChange('light')}>Select Light</button>
|
|
|
|
|
<button onClick={() => onValueChange('dark')}>Select Dark</button>
|
|
|
|
|
<button onClick={() => onValueChange('auto')}>Select Auto</button>
|
|
|
|
|
</div>
|
|
|
|
|
),
|
|
|
|
|
RadioGroupItem: ({ value, id }: any) => (
|
|
|
|
|
<input type="radio" value={value} id={id} data-testid={`radio-${value}`} />
|
|
|
|
|
),
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
// Mock Label
|
|
|
|
|
vi.mock('@/components/ui/label', () => ({
|
|
|
|
|
Label: ({ children, htmlFor }: any) => (
|
|
|
|
|
<label htmlFor={htmlFor}>{children}</label>
|
|
|
|
|
),
|
|
|
|
|
}));
|
|
|
|
|
|
2026-03-12 13:33:48 +00:00
|
|
|
// Mock useTranslation hook
|
|
|
|
|
vi.mock('@/hooks/useTranslation', () => ({
|
|
|
|
|
useTranslation: () => ({
|
|
|
|
|
t: (key: string) => {
|
|
|
|
|
const translations: Record<string, string> = {
|
|
|
|
|
'settings.language.language': 'Language',
|
|
|
|
|
'settings.language.title': 'Language and Region',
|
2026-03-25 23:24:24 +00:00
|
|
|
'settings.preferences.timezone': 'Time Zone',
|
2026-03-12 13:33:48 +00:00
|
|
|
'settings.language.description': 'Choose your preferred language',
|
|
|
|
|
'settings.appearance.theme': 'Theme',
|
|
|
|
|
'settings.appearance.light': 'Light',
|
|
|
|
|
'settings.appearance.dark': 'Dark',
|
|
|
|
|
'settings.appearance.system': 'System',
|
|
|
|
|
'common.search': 'Search',
|
|
|
|
|
};
|
|
|
|
|
return translations[key] ?? key;
|
|
|
|
|
},
|
|
|
|
|
changeLanguage: vi.fn(),
|
|
|
|
|
language: 'en',
|
|
|
|
|
isReady: true,
|
|
|
|
|
}),
|
|
|
|
|
}));
|
|
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
describe('PreferenceSettings Component', () => {
|
|
|
|
|
const mockPreferences: PreferenceSettingsType = {
|
|
|
|
|
language: 'en',
|
|
|
|
|
timezone: 'UTC',
|
|
|
|
|
theme: 'auto',
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const mockOnChange = vi.fn();
|
|
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
|
vi.clearAllMocks();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should render all preference fields', () => {
|
|
|
|
|
render(
|
2025-12-13 02:34:34 +00:00
|
|
|
<PreferenceSettings
|
|
|
|
|
preferences={mockPreferences}
|
|
|
|
|
onChange={mockOnChange}
|
|
|
|
|
/>,
|
2025-12-03 21:56:50 +00:00
|
|
|
);
|
|
|
|
|
|
2026-03-12 13:33:48 +00:00
|
|
|
expect(screen.getByText('Language')).toBeInTheDocument();
|
2026-03-25 23:24:24 +00:00
|
|
|
// Bug #18: label was "Language and Region", now correctly "Time Zone"
|
|
|
|
|
expect(screen.getByText('Time Zone')).toBeInTheDocument();
|
2026-03-12 13:33:48 +00:00
|
|
|
expect(screen.getByText('Theme')).toBeInTheDocument();
|
2025-12-03 21:56:50 +00:00
|
|
|
expect(screen.getByTestId('radio-group')).toBeInTheDocument();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should display current language value', () => {
|
|
|
|
|
render(
|
2025-12-13 02:34:34 +00:00
|
|
|
<PreferenceSettings
|
|
|
|
|
preferences={mockPreferences}
|
|
|
|
|
onChange={mockOnChange}
|
|
|
|
|
/>,
|
2025-12-03 21:56:50 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const selects = screen.getAllByTestId('select');
|
|
|
|
|
const languageSelect = selects[0].querySelector('select');
|
|
|
|
|
expect(languageSelect).toHaveValue('en');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should call onChange when language changes', () => {
|
|
|
|
|
render(
|
2025-12-13 02:34:34 +00:00
|
|
|
<PreferenceSettings
|
|
|
|
|
preferences={mockPreferences}
|
|
|
|
|
onChange={mockOnChange}
|
|
|
|
|
/>,
|
2025-12-03 21:56:50 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const selects = screen.getAllByTestId('select');
|
|
|
|
|
const languageSelect = selects[0].querySelector('select');
|
|
|
|
|
if (languageSelect) {
|
|
|
|
|
fireEvent.change(languageSelect, { target: { value: 'fr' } });
|
|
|
|
|
expect(mockOnChange).toHaveBeenCalledWith({
|
|
|
|
|
...mockPreferences,
|
|
|
|
|
language: 'fr',
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should display current timezone value', () => {
|
|
|
|
|
render(
|
2025-12-13 02:34:34 +00:00
|
|
|
<PreferenceSettings
|
|
|
|
|
preferences={mockPreferences}
|
|
|
|
|
onChange={mockOnChange}
|
|
|
|
|
/>,
|
2025-12-03 21:56:50 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const selects = screen.getAllByTestId('select');
|
|
|
|
|
const timezoneSelect = selects[1].querySelector('select');
|
|
|
|
|
expect(timezoneSelect).toHaveValue('UTC');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should call onChange when timezone changes', () => {
|
|
|
|
|
render(
|
2025-12-13 02:34:34 +00:00
|
|
|
<PreferenceSettings
|
|
|
|
|
preferences={mockPreferences}
|
|
|
|
|
onChange={mockOnChange}
|
|
|
|
|
/>,
|
2025-12-03 21:56:50 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const selects = screen.getAllByTestId('select');
|
|
|
|
|
const timezoneSelect = selects[1].querySelector('select');
|
|
|
|
|
if (timezoneSelect) {
|
|
|
|
|
fireEvent.change(timezoneSelect, { target: { value: 'Europe/Paris' } });
|
|
|
|
|
expect(mockOnChange).toHaveBeenCalledWith({
|
|
|
|
|
...mockPreferences,
|
|
|
|
|
timezone: 'Europe/Paris',
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should display current theme value', () => {
|
|
|
|
|
render(
|
2025-12-13 02:34:34 +00:00
|
|
|
<PreferenceSettings
|
|
|
|
|
preferences={mockPreferences}
|
|
|
|
|
onChange={mockOnChange}
|
|
|
|
|
/>,
|
2025-12-03 21:56:50 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const radioGroup = screen.getByTestId('radio-group');
|
|
|
|
|
expect(radioGroup).toHaveAttribute('data-value', 'auto');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should call onChange when theme changes to light', () => {
|
|
|
|
|
render(
|
2025-12-13 02:34:34 +00:00
|
|
|
<PreferenceSettings
|
|
|
|
|
preferences={mockPreferences}
|
|
|
|
|
onChange={mockOnChange}
|
|
|
|
|
/>,
|
2025-12-03 21:56:50 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const lightButton = screen.getByText('Select Light');
|
|
|
|
|
fireEvent.click(lightButton);
|
|
|
|
|
|
|
|
|
|
expect(mockOnChange).toHaveBeenCalledWith({
|
|
|
|
|
...mockPreferences,
|
|
|
|
|
theme: 'light',
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should call onChange when theme changes to dark', () => {
|
|
|
|
|
render(
|
2025-12-13 02:34:34 +00:00
|
|
|
<PreferenceSettings
|
|
|
|
|
preferences={mockPreferences}
|
|
|
|
|
onChange={mockOnChange}
|
|
|
|
|
/>,
|
2025-12-03 21:56:50 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const darkButton = screen.getByText('Select Dark');
|
|
|
|
|
fireEvent.click(darkButton);
|
|
|
|
|
|
|
|
|
|
expect(mockOnChange).toHaveBeenCalledWith({
|
|
|
|
|
...mockPreferences,
|
|
|
|
|
theme: 'dark',
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should call onChange when theme changes to auto', () => {
|
|
|
|
|
render(
|
2025-12-13 02:34:34 +00:00
|
|
|
<PreferenceSettings
|
|
|
|
|
preferences={{ ...mockPreferences, theme: 'light' }}
|
|
|
|
|
onChange={mockOnChange}
|
|
|
|
|
/>,
|
2025-12-03 21:56:50 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const autoButton = screen.getByText('Select Auto');
|
|
|
|
|
fireEvent.click(autoButton);
|
|
|
|
|
|
|
|
|
|
expect(mockOnChange).toHaveBeenCalledWith({
|
|
|
|
|
...mockPreferences,
|
|
|
|
|
theme: 'auto',
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should render all theme options', () => {
|
|
|
|
|
render(
|
2025-12-13 02:34:34 +00:00
|
|
|
<PreferenceSettings
|
|
|
|
|
preferences={mockPreferences}
|
|
|
|
|
onChange={mockOnChange}
|
|
|
|
|
/>,
|
2025-12-03 21:56:50 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
expect(screen.getByTestId('radio-light')).toBeInTheDocument();
|
|
|
|
|
expect(screen.getByTestId('radio-dark')).toBeInTheDocument();
|
|
|
|
|
expect(screen.getByTestId('radio-auto')).toBeInTheDocument();
|
|
|
|
|
});
|
|
|
|
|
});
|