mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-20 22:16:26 +00:00
7cb96c3d74
The avatar menu of the mobile dashboard lost the version pill that the desktop account menu shows under sign out. It now renders the same appVersion from the auth store as a small footer, with the wordmark following the active theme. Discord, help and the prerelease badge stay desktop only. Refs #2581
126 lines
4.8 KiB
TypeScript
126 lines
4.8 KiB
TypeScript
import React from 'react';
|
|
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
import { render, screen, fireEvent, act } from '../../../helpers/render';
|
|
import MUserMenu from '../../../../src/mobile/screens/dashboard/MUserMenu';
|
|
import { useAuthStore } from '../../../../src/store/authStore';
|
|
import { useSettingsStore } from '../../../../src/store/settingsStore';
|
|
|
|
// FE-MOB-MENU-001 onwards
|
|
|
|
function seedUser(role: 'user' | 'admin') {
|
|
useAuthStore.setState({
|
|
isAuthenticated: true,
|
|
user: { id: 1, username: 'Maurice', email: 'maurice@trek.app', role, avatar_url: '' } as never,
|
|
});
|
|
}
|
|
|
|
describe('MUserMenu', () => {
|
|
beforeEach(() => {
|
|
seedUser('user');
|
|
useSettingsStore.setState(s => ({ settings: { ...s.settings, dark_mode: 'dark' } }));
|
|
});
|
|
|
|
it('FE-MOB-MENU-001: shows profile header and hides the admin entry for regular users', () => {
|
|
render(<MUserMenu open onClose={() => {}} />);
|
|
|
|
expect(screen.getByText('Maurice')).toBeInTheDocument();
|
|
expect(screen.getByText('maurice@trek.app')).toBeInTheDocument();
|
|
expect(screen.getByRole('button', { name: 'Settings' })).toBeInTheDocument();
|
|
expect(screen.queryByRole('button', { name: 'Admin Settings' })).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('FE-MOB-MENU-002: shows the admin entry and badge for admins', () => {
|
|
seedUser('admin');
|
|
render(<MUserMenu open onClose={() => {}} />);
|
|
|
|
expect(screen.getByRole('button', { name: 'Admin Settings' })).toBeInTheDocument();
|
|
expect(screen.getByText('Admin')).toBeInTheDocument();
|
|
});
|
|
|
|
it('FE-MOB-MENU-003: the theme row cycles dark → light → auto', () => {
|
|
const updateSetting = vi.fn(async () => {});
|
|
useSettingsStore.setState({ updateSetting });
|
|
|
|
render(<MUserMenu open onClose={() => {}} />);
|
|
fireEvent.click(screen.getByRole('button', { name: /Color Mode/ }));
|
|
expect(updateSetting).toHaveBeenCalledWith('dark_mode', 'light');
|
|
|
|
act(() => {
|
|
useSettingsStore.setState(s => ({ settings: { ...s.settings, dark_mode: 'light' } }));
|
|
});
|
|
fireEvent.click(screen.getByRole('button', { name: /Color Mode/ }));
|
|
expect(updateSetting).toHaveBeenCalledWith('dark_mode', 'auto');
|
|
});
|
|
|
|
it('FE-MOB-MENU-004: auto cycles back to dark', () => {
|
|
const updateSetting = vi.fn(async () => {});
|
|
useSettingsStore.setState({ updateSetting });
|
|
useSettingsStore.setState(s => ({ settings: { ...s.settings, dark_mode: 'auto' } }));
|
|
|
|
render(<MUserMenu open onClose={() => {}} />);
|
|
fireEvent.click(screen.getByRole('button', { name: /Color Mode/ }));
|
|
|
|
expect(updateSetting).toHaveBeenCalledWith('dark_mode', 'dark');
|
|
});
|
|
|
|
it('FE-MOB-MENU-005: a legacy boolean dark_mode still resolves to dark', () => {
|
|
const updateSetting = vi.fn(async () => {});
|
|
useSettingsStore.setState({ updateSetting });
|
|
useSettingsStore.setState(s => ({ settings: { ...s.settings, dark_mode: true } }));
|
|
|
|
render(<MUserMenu open onClose={() => {}} />);
|
|
fireEvent.click(screen.getByRole('button', { name: /Color Mode/ }));
|
|
|
|
expect(updateSetting).toHaveBeenCalledWith('dark_mode', 'light');
|
|
});
|
|
|
|
it('FE-MOB-MENU-006: opening settings closes the menu first', () => {
|
|
const onClose = vi.fn();
|
|
render(<MUserMenu open onClose={onClose} />);
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: 'Settings' }));
|
|
|
|
expect(onClose).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('FE-MOB-MENU-007: signing out closes the menu and clears the session', async () => {
|
|
const onClose = vi.fn();
|
|
const logout = vi.fn(async () => {});
|
|
useAuthStore.setState({ logout });
|
|
render(<MUserMenu open onClose={onClose} />);
|
|
|
|
await act(async () => {
|
|
fireEvent.click(screen.getByRole('button', { name: 'Logout' }));
|
|
});
|
|
|
|
expect(onClose).toHaveBeenCalledTimes(1);
|
|
expect(logout).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('FE-MOB-MENU-008: an uploaded avatar replaces the initial', () => {
|
|
useAuthStore.setState({
|
|
isAuthenticated: true,
|
|
user: { id: 1, username: 'Maurice', email: 'm@trek.app', role: 'user', avatar_url: '/uploads/avatars/m.jpg' } as never,
|
|
});
|
|
const { container } = render(<MUserMenu open onClose={() => {}} />);
|
|
|
|
expect(container.querySelector('img')).toHaveAttribute('src', '/uploads/avatars/m.jpg');
|
|
});
|
|
|
|
it('FE-MOB-MENU-009: shows the app version as a footer under sign out', () => {
|
|
useAuthStore.setState({ appVersion: '5.0.0' });
|
|
render(<MUserMenu open onClose={() => {}} />);
|
|
|
|
expect(screen.getByText('v5.0.0')).toBeInTheDocument();
|
|
expect(screen.getByAltText('TREK')).toBeInTheDocument();
|
|
});
|
|
|
|
it('FE-MOB-MENU-010: renders no version footer while the version is unknown', () => {
|
|
useAuthStore.setState({ appVersion: '' });
|
|
render(<MUserMenu open onClose={() => {}} />);
|
|
|
|
expect(screen.queryByText(/^v\d/)).not.toBeInTheDocument();
|
|
expect(screen.queryByAltText('TREK')).not.toBeInTheDocument();
|
|
});
|
|
});
|