Files
TREK/client/src/pages/SettingsPage.test.tsx
T
Maurice 2f0b867685 fix(settings): the integrations tab is always there on desktop so api keys stay reachable
API keys were split off from MCP to work without any addon, but the tab
that holds them still hid unless a photo provider, MCP, AirTrail, AI
Parsing or Dawarich was enabled. On a fresh install all of those are off,
so there was no way to create a key. The desktop tab now always shows;
the addon backed sections inside it keep their own gates. The phone layout
has no API Keys section and keeps its gate.

The wiki now says where keys are created and that no addon is needed.

Refs #2561
2026-10-12 12:46:12 +02:00

180 lines
6.3 KiB
TypeScript

import { describe, it, expect, beforeEach } from 'vitest';
import { render, screen, waitFor } from '../../tests/helpers/render';
import userEvent from '@testing-library/user-event';
import { resetAllStores, seedStore } from '../../tests/helpers/store';
import { buildUser } from '../../tests/helpers/factories';
import { useAuthStore } from '../store/authStore';
import { useAddonStore } from '../store/addonStore';
import SettingsPage from './SettingsPage';
// Mock heavy settings sub-tabs to focus on page-level concerns
vi.mock('../components/Settings/DisplaySettingsTab', () => ({
default: () => <div data-testid="display-settings-tab">Display Settings</div>,
}));
vi.mock('../components/Settings/MapSettingsTab', () => ({
default: () => <div data-testid="map-settings-tab">Map Settings</div>,
}));
vi.mock('../components/Settings/NotificationsTab', () => ({
default: () => <div data-testid="notifications-tab">Notifications Settings</div>,
}));
vi.mock('../components/Settings/IntegrationsTab', () => ({
default: () => <div data-testid="integrations-tab">Integrations Settings</div>,
}));
vi.mock('../components/Settings/AccountTab', () => ({
default: () => <div data-testid="account-tab">Account Settings</div>,
}));
vi.mock('../components/Settings/AboutTab', () => ({
default: ({ appVersion }: { appVersion: string }) => (
<div data-testid="about-tab">About v{appVersion}</div>
),
}));
beforeEach(() => {
resetAllStores();
seedStore(useAuthStore, { isAuthenticated: true, user: buildUser() });
});
describe('SettingsPage', () => {
describe('FE-PAGE-SETTINGS-001: Settings page renders', () => {
it('shows the Settings heading', () => {
render(<SettingsPage />);
expect(screen.getByRole('heading', { name: /settings/i })).toBeInTheDocument();
});
});
describe('FE-PAGE-SETTINGS-002: Default tab (Display) is active', () => {
it('shows Display tab content by default', async () => {
render(<SettingsPage />);
await waitFor(() => {
expect(screen.getByTestId('display-settings-tab')).toBeInTheDocument();
});
});
});
describe('FE-PAGE-SETTINGS-003: Tab navigation', () => {
it('switching to Map tab shows map settings content', async () => {
const user = userEvent.setup();
render(<SettingsPage />);
await waitFor(() => {
expect(screen.getByRole('button', { name: /map/i })).toBeInTheDocument();
});
await user.click(screen.getByRole('button', { name: /^map$/i }));
await waitFor(() => {
expect(screen.getByTestId('map-settings-tab')).toBeInTheDocument();
});
});
it('switching to Account tab shows account settings', async () => {
const user = userEvent.setup();
render(<SettingsPage />);
await waitFor(() => {
expect(screen.getByRole('button', { name: /account/i })).toBeInTheDocument();
});
await user.click(screen.getByRole('button', { name: /account/i }));
await waitFor(() => {
expect(screen.getByTestId('account-tab')).toBeInTheDocument();
});
});
it('switching to Notifications tab shows notifications content', async () => {
const user = userEvent.setup();
render(<SettingsPage />);
await waitFor(() => {
expect(screen.getByRole('button', { name: /notifications/i })).toBeInTheDocument();
});
await user.click(screen.getByRole('button', { name: /notifications/i }));
await waitFor(() => {
expect(screen.getByTestId('notifications-tab')).toBeInTheDocument();
});
});
});
describe('FE-PAGE-SETTINGS-004: All standard tabs are present', () => {
it('renders General, Map, Notifications, Account tabs', async () => {
render(<SettingsPage />);
await waitFor(() => {
expect(screen.getByRole('button', { name: /general/i })).toBeInTheDocument();
});
expect(screen.getByRole('button', { name: /^map$/i })).toBeInTheDocument();
expect(screen.getByRole('button', { name: /notifications/i })).toBeInTheDocument();
expect(screen.getByRole('button', { name: /account/i })).toBeInTheDocument();
});
});
describe('FE-PAGE-SETTINGS-005: MFA redirect switches to Account tab', () => {
it('auto-switches to account tab when ?mfa=required is in URL', async () => {
render(<SettingsPage />, { initialEntries: ['/settings?mfa=required'] });
await waitFor(() => {
expect(screen.getByTestId('account-tab')).toBeInTheDocument();
});
});
});
describe('FE-PAGE-SETTINGS-006: About tab shown when version loads', () => {
it('About tab appears when app version is returned by API', async () => {
const { http, HttpResponse } = await import('msw');
const { server } = await import('../../tests/helpers/msw/server');
server.use(
http.get('/api/auth/app-config', () => {
return HttpResponse.json({
has_users: true,
allow_registration: true,
demo_mode: false,
oidc_configured: false,
oidc_only_mode: false,
version: '2.9.10',
});
}),
);
render(<SettingsPage />);
await waitFor(() => {
expect(screen.getByRole('button', { name: /about/i })).toBeInTheDocument();
});
});
});
describe('FE-PAGE-SETTINGS-007: Integrations tab without any integration addon', () => {
// API keys live in the Integrations tab and need no addon, so the tab has to
// be there on an instance where MCP, AirTrail, AI Parsing, Dawarich and the
// photo providers are all switched off.
it('still offers the Integrations tab and opens it', async () => {
seedStore(useAddonStore, { isEnabled: () => false, loadAddons: vi.fn().mockResolvedValue(undefined) });
const user = userEvent.setup();
render(<SettingsPage />);
await user.click(await screen.findByRole('button', { name: /integrations/i }));
expect(await screen.findByTestId('integrations-tab')).toBeInTheDocument();
});
it('keeps it on a managed install too', async () => {
seedStore(useAddonStore, { isEnabled: () => false, loadAddons: vi.fn().mockResolvedValue(undefined) });
seedStore(useAuthStore, { managed: true });
render(<SettingsPage />, { initialEntries: ['/settings?tab=integrations'] });
expect(await screen.findByTestId('integrations-tab')).toBeInTheDocument();
});
});
});