Spaces:
Sleeping
Sleeping
File size: 9,219 Bytes
cd99321 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | // FE-COMP-DISPLAY-001 to FE-COMP-DISPLAY-027
import { render, screen, waitFor } from '../../../tests/helpers/render';
import userEvent from '@testing-library/user-event';
import { http, HttpResponse } from 'msw';
import { server } from '../../../tests/helpers/msw/server';
import { useAuthStore } from '../../store/authStore';
import { useSettingsStore } from '../../store/settingsStore';
import { resetAllStores, seedStore } from '../../../tests/helpers/store';
import { buildUser, buildSettings } from '../../../tests/helpers/factories';
import DisplaySettingsTab from './DisplaySettingsTab';
import { ToastContainer } from '../shared/Toast';
beforeEach(() => {
resetAllStores();
server.use(
http.put('/api/settings', async () => HttpResponse.json({ success: true })),
);
seedStore(useAuthStore, { user: buildUser(), isAuthenticated: true });
seedStore(useSettingsStore, { settings: buildSettings({ dark_mode: 'light', language: 'en' }) });
});
describe('DisplaySettingsTab', () => {
it('FE-COMP-DISPLAY-001: renders without crashing', () => {
render(<DisplaySettingsTab />);
expect(document.body).toBeInTheDocument();
});
it('FE-COMP-DISPLAY-002: shows Display section title', () => {
render(<DisplaySettingsTab />);
expect(screen.getByText('Display')).toBeInTheDocument();
});
it('FE-COMP-DISPLAY-003: shows Light mode button', () => {
render(<DisplaySettingsTab />);
expect(screen.getByText('Light')).toBeInTheDocument();
});
it('FE-COMP-DISPLAY-004: shows Dark mode button', () => {
render(<DisplaySettingsTab />);
expect(screen.getByText('Dark')).toBeInTheDocument();
});
it('FE-COMP-DISPLAY-005: shows Auto mode button', () => {
render(<DisplaySettingsTab />);
expect(screen.getByRole('button', { name: /Auto/i })).toBeInTheDocument();
});
it('FE-COMP-DISPLAY-006: shows Language section', () => {
render(<DisplaySettingsTab />);
expect(screen.getByText('Language')).toBeInTheDocument();
});
it('FE-COMP-DISPLAY-007: shows Time Format section', () => {
render(<DisplaySettingsTab />);
expect(screen.getByText('Time Format')).toBeInTheDocument();
});
it('FE-COMP-DISPLAY-008: clicking Dark mode button calls updateSetting', async () => {
const user = userEvent.setup();
const updateSetting = vi.fn().mockResolvedValue(undefined);
seedStore(useSettingsStore, { settings: buildSettings({ dark_mode: 'light' }), updateSetting });
render(<DisplaySettingsTab />);
await user.click(screen.getByText('Dark'));
expect(updateSetting).toHaveBeenCalledWith('dark_mode', 'dark');
});
it('FE-COMP-DISPLAY-009: shows Color Mode label', () => {
render(<DisplaySettingsTab />);
expect(screen.getByText('Color Mode')).toBeInTheDocument();
});
it('FE-COMP-DISPLAY-010: shows 24h time format option', () => {
render(<DisplaySettingsTab />);
// Label is "24h (14:30)"
expect(screen.getByText(/24h/i)).toBeInTheDocument();
});
it('FE-COMP-DISPLAY-011: shows 12h time format option', () => {
render(<DisplaySettingsTab />);
// Label is "12h (2:30 PM)"
expect(screen.getByText(/12h/i)).toBeInTheDocument();
});
it('FE-COMP-DISPLAY-012: clicking Light mode calls updateSetting with light', async () => {
const user = userEvent.setup();
const updateSetting = vi.fn().mockResolvedValue(undefined);
seedStore(useSettingsStore, { settings: buildSettings({ dark_mode: 'dark' }), updateSetting });
render(<DisplaySettingsTab />);
await user.click(screen.getByText('Light'));
expect(updateSetting).toHaveBeenCalledWith('dark_mode', 'light');
});
it('FE-COMP-DISPLAY-013: clicking Auto mode button calls updateSetting with auto', async () => {
const user = userEvent.setup();
const updateSetting = vi.fn().mockResolvedValue(undefined);
seedStore(useSettingsStore, { settings: buildSettings({ dark_mode: 'light' }), updateSetting });
render(<DisplaySettingsTab />);
await user.click(screen.getByRole('button', { name: /Auto/i }));
expect(updateSetting).toHaveBeenCalledWith('dark_mode', 'auto');
});
it('FE-COMP-DISPLAY-014: active color mode button has border with var(--text-primary)', () => {
seedStore(useSettingsStore, { settings: buildSettings({ dark_mode: 'dark' }) });
render(<DisplaySettingsTab />);
const darkBtn = screen.getByRole('button', { name: /^Dark$/i });
const lightBtn = screen.getByRole('button', { name: /^Light$/i });
const autoBtn = screen.getByRole('button', { name: /Auto/i });
expect(darkBtn.style.border).toContain('var(--text-primary)');
expect(lightBtn.style.border).toContain('var(--border-primary)');
expect(autoBtn.style.border).toContain('var(--border-primary)');
});
it('FE-COMP-DISPLAY-015: clicking a language button calls updateSetting with that language code', async () => {
const user = userEvent.setup();
const updateSetting = vi.fn().mockResolvedValue(undefined);
seedStore(useSettingsStore, { settings: buildSettings({ language: 'en' }), updateSetting });
render(<DisplaySettingsTab />);
await user.click(screen.getByText('Deutsch'));
expect(updateSetting).toHaveBeenCalledWith('language', 'de');
});
it('FE-COMP-DISPLAY-016: active language button is visually highlighted', () => {
seedStore(useSettingsStore, { settings: buildSettings({ language: 'en' }) });
render(<DisplaySettingsTab />);
// Multiple elements contain "English" (desktop grid button + mobile dropdown trigger).
// The desktop grid button is the one with the active border style.
const englishMatches = screen.getAllByText('English').map(el => el.closest('button')!).filter(Boolean);
const activeBtn = englishMatches.find(btn => (btn.style.border || '').includes('var(--text-primary)'));
expect(activeBtn).toBeDefined();
});
it('FE-COMP-DISPLAY-017: shows Temperature section label', () => {
render(<DisplaySettingsTab />);
expect(screen.getByText(/temperature/i)).toBeInTheDocument();
});
it('FE-COMP-DISPLAY-018: celsius button is active when temperature_unit is celsius', () => {
seedStore(useSettingsStore, { settings: buildSettings({ temperature_unit: 'celsius' }) });
render(<DisplaySettingsTab />);
const celsiusBtn = screen.getByText('°C Celsius').closest('button')!;
expect(celsiusBtn.style.border).toContain('var(--text-primary)');
});
it('FE-COMP-DISPLAY-019: clicking fahrenheit button calls updateSetting with fahrenheit', async () => {
const user = userEvent.setup();
const updateSetting = vi.fn().mockResolvedValue(undefined);
seedStore(useSettingsStore, { settings: buildSettings({ temperature_unit: 'celsius' }), updateSetting });
render(<DisplaySettingsTab />);
await user.click(screen.getByText('°F Fahrenheit'));
expect(updateSetting).toHaveBeenCalledWith('temperature_unit', 'fahrenheit');
});
it('FE-COMP-DISPLAY-020: clicking 24h time format calls updateSetting with 24h', async () => {
const user = userEvent.setup();
const updateSetting = vi.fn().mockResolvedValue(undefined);
seedStore(useSettingsStore, { settings: buildSettings({ time_format: '12h' }), updateSetting });
render(<DisplaySettingsTab />);
// The label is split across a text node ('24h') and a responsive span (' (14:30)').
// Click the button that contains the 24h text instead of matching the full string.
await user.click(screen.getByRole('button', { name: /24h/ }));
expect(updateSetting).toHaveBeenCalledWith('time_format', '24h');
});
it('FE-COMP-DISPLAY-024: shows Blur Booking Codes section', () => {
render(<DisplaySettingsTab />);
expect(screen.getByText(/blur booking codes/i)).toBeInTheDocument();
});
it('FE-COMP-DISPLAY-025: blur booking codes On button is active when blur_booking_codes is true', () => {
seedStore(useSettingsStore, { settings: buildSettings({ blur_booking_codes: true }) });
render(<DisplaySettingsTab />);
const onButtons = screen.getAllByText(/^On$/i);
const blurOnBtn = onButtons[1].closest('button')!;
expect(blurOnBtn.style.border).toContain('var(--text-primary)');
});
it('FE-COMP-DISPLAY-026: updateSetting failure shows toast error', async () => {
const user = userEvent.setup();
const updateSetting = vi.fn().mockRejectedValue(new Error('Server error'));
seedStore(useSettingsStore, { settings: buildSettings({ dark_mode: 'light' }), updateSetting });
render(<><ToastContainer /><DisplaySettingsTab /></>);
await user.click(screen.getByText('Dark'));
await screen.findByText('Server error');
});
it('FE-COMP-DISPLAY-027: temperature unit local state updates optimistically before API resolves', async () => {
const user = userEvent.setup();
const updateSetting = vi.fn().mockReturnValue(new Promise(() => {}));
seedStore(useSettingsStore, { settings: buildSettings({ temperature_unit: 'celsius' }), updateSetting });
render(<DisplaySettingsTab />);
await user.click(screen.getByText('°F Fahrenheit'));
const fahrenheitBtn = screen.getByText('°F Fahrenheit').closest('button')!;
expect(fahrenheitBtn.style.border).toContain('var(--text-primary)');
});
});
|