diff --git a/apps/web/src/components/settings/smtp-settings-form.test.tsx b/apps/web/src/components/settings/smtp-settings-form.test.tsx new file mode 100644 index 0000000..b703c39 --- /dev/null +++ b/apps/web/src/components/settings/smtp-settings-form.test.tsx @@ -0,0 +1,76 @@ +import { cleanup, render, screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import de from '@/messages/de.json'; +import { fetchSmtp, saveSmtp } from '@/lib/settings-api'; +import { SmtpSettingsForm } from './smtp-settings-form'; + +/** + * smtp-settings-form.test — NEU (quick-260914-m97). Das SMTP-Formular + * hatte bisher keine Testdatei. Zwei Tests fuer das neue Feld + * „Fehlermeldungen an“: Vorbelegung aus GET /settings/smtp und der + * PUT-Vertrag mit `saveSmtpConfig` (Wert bzw. `null` zum Loeschen). + */ +vi.mock('@/lib/settings-api', () => ({ + fetchSmtp: vi.fn(), + saveSmtp: vi.fn(), + testSmtp: vi.fn(), +})); + +vi.mock('next-intl', async () => { + const messages = (await import('@/messages/de.json')).default as Record; + const lookup = (path: string): string | undefined => + path.split('.').reduce((o, k) => (o && typeof o === 'object' ? (o as any)[k] : undefined), messages) as + | string + | undefined; + return { + useTranslations: (ns?: string) => (key: string) => lookup(ns ? `${ns}.${key}` : key) ?? key, + }; +}); + +const LABEL = de.settings.smtp.bugReportRecipient; + +beforeEach(() => { + vi.mocked(fetchSmtp).mockResolvedValue({ + host: 'h', + port: 587, + encryption: 'starttls', + fromAddress: 'a@b.invalid', + hasPassword: false, + bugReportRecipient: 'fehler@b.invalid', + }); + vi.mocked(saveSmtp).mockResolvedValue({} as any); +}); + +afterEach(() => { + cleanup(); + vi.clearAllMocks(); +}); + +describe('SmtpSettingsForm — Feld Fehlermeldungen an (quick-260914-m97)', () => { + it('Test 1: das Feld ist aus GET /settings/smtp vorbelegt', async () => { + render(); + + const input = (await screen.findByLabelText(LABEL)) as HTMLInputElement; + await waitFor(() => expect(input.value).toBe('fehler@b.invalid')); + expect(input.type).toBe('email'); + }); + + it('Test 2: PUT-Payload traegt den Wert; leeres Feld -> null', async () => { + const user = userEvent.setup(); + render(); + const input = (await screen.findByLabelText(LABEL)) as HTMLInputElement; + await waitFor(() => expect(input.value).toBe('fehler@b.invalid')); + + await user.clear(input); + await user.type(input, 'neu@b.invalid'); + await user.click(screen.getByRole('button', { name: de.settings.smtp.save })); + await waitFor(() => expect(saveSmtp).toHaveBeenCalledTimes(1)); + expect(vi.mocked(saveSmtp).mock.calls[0][0]).toMatchObject({ bugReportRecipient: 'neu@b.invalid' }); + + await user.clear(input); + await user.click(screen.getByRole('button', { name: de.settings.smtp.save })); + await waitFor(() => expect(saveSmtp).toHaveBeenCalledTimes(2)); + expect(vi.mocked(saveSmtp).mock.calls[1][0]).toMatchObject({ bugReportRecipient: null }); + }); +}); diff --git a/apps/web/src/components/settings/smtp-settings-form.tsx b/apps/web/src/components/settings/smtp-settings-form.tsx index 909221a..57c65d2 100644 --- a/apps/web/src/components/settings/smtp-settings-form.tsx +++ b/apps/web/src/components/settings/smtp-settings-form.tsx @@ -23,6 +23,7 @@ interface FormState { username: string; password: string; // T-07-17: always starts blank; never pre-filled from server fromAddress: string; + bugReportRecipient: string; // quick-260914-m97: Postfach fuer den Fehler-melden-Knopf } const DEFAULT_FORM: FormState = { @@ -32,6 +33,7 @@ const DEFAULT_FORM: FormState = { username: '', password: '', fromAddress: '', + bugReportRecipient: '', }; type TestFeedback = @@ -70,6 +72,7 @@ export function SmtpSettingsForm() { username: config.username ?? '', password: '', // T-07-17: never pre-filled fromAddress: config.fromAddress, + bugReportRecipient: config.bugReportRecipient ?? '', }); } }) @@ -95,6 +98,8 @@ export function SmtpSettingsForm() { if (form.username.trim()) payload.username = form.username.trim(); // T-07-17: only include password when user has typed a new one if (form.password) payload.password = form.password; + // quick-260914-m97: das Formular ist der einzige Klient — leer bedeutet loeschen (null) + payload.bugReportRecipient = form.bugReportRecipient.trim() || null; return payload; }; @@ -300,6 +305,26 @@ export function SmtpSettingsForm() {

+ {/* Fehlermeldungen an (quick-260914-m97) */} +
+ + + setForm((prev) => ({ ...prev, bugReportRecipient: e.target.value })) + } + /> +

+ {t('smtp.bugReportRecipientHelp')} +

+
+ {/* Test-E-Mail Empfänger */}