refactor(ui): rename route /korrespondenz → /briefwechsel

Update all internal links (AppNav, CoCorrespondentsList, goto) to the
new URL. No redirect needed — no production URLs exist yet.

Refs: #179

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-04-06 19:22:22 +02:00
parent a863f8baad
commit a9228d156f
13 changed files with 6 additions and 6 deletions

View File

@@ -0,0 +1,246 @@
import { afterEach, describe, expect, it, vi } from 'vitest';
import { cleanup, render } from 'vitest-browser-svelte';
import { page } from 'vitest/browser';
import Page from './+page.svelte';
vi.mock('$app/navigation', () => ({ goto: vi.fn() }));
afterEach(cleanup);
// ─── Test data ────────────────────────────────────────────────────────────────
const baseData = {
user: undefined,
canWrite: true,
canAnnotate: false,
documents: [],
initialValues: { senderName: '', receiverName: '' },
filters: { senderId: '', receiverId: '', from: '', to: '', dir: 'DESC' as const }
};
const withSender = {
...baseData,
initialValues: { senderName: 'Hans Müller', receiverName: '' },
filters: { ...baseData.filters, senderId: 'p1' }
};
const withPersons = {
...baseData,
initialValues: { senderName: 'Hans Müller', receiverName: 'Anna Schmidt' },
filters: { ...baseData.filters, senderId: 'p1', receiverId: 'p2' }
};
const makeDoc = (overrides: Record<string, unknown> = {}) => ({
id: 'd1',
title: 'Testbrief',
originalFilename: 'testbrief.pdf',
status: 'UPLOADED' as const,
documentDate: '1923-04-12',
location: 'Berlin',
metadataComplete: false,
sender: { id: 'p1', firstName: 'Hans', lastName: 'Müller' },
receivers: [{ id: 'p2', firstName: 'Anna', lastName: 'Schmidt' }],
tags: [],
transcription: undefined,
filePath: undefined,
createdAt: '1923-04-12T00:00:00Z',
updatedAt: '1923-04-12T00:00:00Z',
...overrides
});
const withDocs = {
...withPersons,
documents: [makeDoc()]
};
// ─── Empty state (no senderId) ────────────────────────────────────────────────
describe('Korrespondenz page empty state', () => {
it('shows the search heading when no person is selected', async () => {
render(Page, { data: baseData });
await expect.element(page.getByText(/Korrespondenz durchsuchen/i)).toBeInTheDocument();
});
it('shows the empty-search button', async () => {
render(Page, { data: baseData });
await expect.element(page.getByTestId('conv-empty-search')).toBeInTheDocument();
});
it('does not show the new document link when no person is selected', async () => {
render(Page, { data: baseData });
await expect.element(page.getByTestId('conv-new-doc-link')).not.toBeInTheDocument();
});
it('does not show a year divider when no person is selected', async () => {
render(Page, { data: baseData });
await expect.element(page.getByTestId('year-divider')).not.toBeInTheDocument();
});
});
// ─── Recent persons chips ─────────────────────────────────────────────────────
describe('Korrespondenz page recent persons', () => {
it('shows recent person chips from localStorage', async () => {
localStorage.setItem(
'korrespondenz_recent_persons',
JSON.stringify([{ id: 'r1', name: 'Clara Braun' }])
);
render(Page, { data: baseData });
await expect.element(page.getByText('Clara Braun')).toBeInTheDocument();
localStorage.removeItem('korrespondenz_recent_persons');
});
it('does not crash when localStorage contains corrupt JSON', async () => {
localStorage.setItem('korrespondenz_recent_persons', '}{not valid json');
render(Page, { data: baseData });
// Empty state heading is still shown — no chip list crash
await expect.element(page.getByText(/Korrespondenz durchsuchen/i)).toBeInTheDocument();
localStorage.removeItem('korrespondenz_recent_persons');
});
});
// ─── Single-person hint bar ───────────────────────────────────────────────────
describe('Korrespondenz page single-person hint bar', () => {
it('shows hint bar when only senderId is set', async () => {
render(Page, { data: withSender });
await expect.element(page.getByText(/Alle Briefe von Hans Müller/i)).toBeInTheDocument();
});
it('does not show hint bar when both persons are set', async () => {
render(Page, { data: { ...withPersons, documents: [makeDoc()] } });
await expect.element(page.getByText(/Alle Briefe von Hans Müller/i)).not.toBeInTheDocument();
});
it('does not show hint bar when no person is set', async () => {
render(Page, { data: baseData });
await expect.element(page.getByText(/Alle Briefe von/i)).not.toBeInTheDocument();
});
});
// ─── Filter controls disabled state ──────────────────────────────────────────
describe('Korrespondenz page filter strip Row 2 disabled state', () => {
it('renders filter controls with aria-disabled when no senderId', async () => {
render(Page, { data: baseData });
const strip = document.querySelector('[aria-disabled="true"]');
expect(strip).not.toBeNull();
});
it('filter controls are not aria-disabled when senderId is set', async () => {
render(Page, { data: withSender });
const strip = document.querySelector('[aria-disabled="false"]');
expect(strip).not.toBeNull();
});
});
// ─── Strip letter count ───────────────────────────────────────────────────────
describe('Korrespondenz page strip letter count', () => {
it('shows 0 Briefe when senderId is set but no documents', async () => {
render(Page, { data: withSender });
await expect.element(page.getByTestId('conv-strip-count')).toHaveTextContent('0 Briefe');
});
it('shows correct count when documents are loaded', async () => {
render(Page, { data: { ...withPersons, documents: [makeDoc()] } });
await expect.element(page.getByTestId('conv-strip-count')).toHaveTextContent('1 Briefe');
});
});
// ─── No results ───────────────────────────────────────────────────────────────
describe('Korrespondenz page no results', () => {
it('shows "no documents found" when a person is selected but there are no documents', async () => {
render(Page, { data: withSender });
await expect.element(page.getByText(/Keine Dokumente gefunden/i)).toBeInTheDocument();
});
});
// ─── Swap button ──────────────────────────────────────────────────────────────
describe('Korrespondenz page swap button', () => {
it('swap button is invisible when only one person is set', async () => {
render(Page, { data: withSender });
const btn = document.querySelector<HTMLElement>('[data-testid="conv-swap-btn"]');
expect(btn).not.toBeNull();
// opacity-0 is applied via class when swapVisible is false
expect(btn!.className).toMatch(/opacity-0/);
});
it('swap button is visible when both persons are set', async () => {
render(Page, { data: withPersons });
const btn = document.querySelector<HTMLElement>('[data-testid="conv-swap-btn"]');
expect(btn).not.toBeNull();
expect(btn!.className).not.toMatch(/opacity-0/);
});
it('calls goto with swapped sender and receiver when clicked', async () => {
const { goto } = await import('$app/navigation');
vi.mocked(goto).mockClear();
render(Page, { data: withPersons });
document.querySelector<HTMLElement>('[data-testid="conv-swap-btn"]')!.click();
expect(goto).toHaveBeenCalledWith(expect.stringContaining('senderId=p2'), expect.anything());
expect(goto).toHaveBeenCalledWith(expect.stringContaining('receiverId=p1'), expect.anything());
});
});
// ─── Year dividers ────────────────────────────────────────────────────────────
describe('Korrespondenz page year dividers', () => {
it('renders a year divider for the first document', async () => {
render(Page, { data: withDocs });
await expect.element(page.getByTestId('year-divider').first()).toHaveTextContent('1923');
});
it('renders a divider for each new year in the document list', async () => {
const data = {
...withPersons,
documents: [
makeDoc({ documentDate: '1923-04-12' }),
makeDoc({ id: 'd2', documentDate: '1965-08-03' })
]
};
render(Page, { data });
await expect.element(page.getByTestId('year-divider').first()).toHaveTextContent('1923');
await expect.element(page.getByTestId('year-divider').nth(1)).toHaveTextContent('1965');
});
it('does not render a second divider for documents from the same year', async () => {
const data = {
...withPersons,
documents: [
makeDoc({ documentDate: '1923-04-12' }),
makeDoc({ id: 'd2', documentDate: '1923-09-01' })
]
};
render(Page, { data });
await expect.element(page.getByTestId('year-divider').first()).toHaveTextContent('1923');
await expect.element(page.getByTestId('year-divider').nth(1)).not.toBeInTheDocument();
});
});
// ─── New document link ────────────────────────────────────────────────────────
describe('Korrespondenz page new document link', () => {
it('shows the link with correct href for a write user (bilateral)', async () => {
render(Page, { data: { ...withDocs, canWrite: true } });
const link = page.getByTestId('conv-new-doc-link');
await expect.element(link).toBeInTheDocument();
await expect.element(link).toHaveAttribute('href', expect.stringContaining('senderId=p1'));
await expect.element(link).toHaveAttribute('href', expect.stringContaining('receiverId=p2'));
});
it('shows the link with correct href for single-person mode', async () => {
render(Page, { data: { ...withSender, documents: [makeDoc()], canWrite: true } });
const link = page.getByTestId('conv-new-doc-link');
await expect.element(link).toBeInTheDocument();
await expect.element(link).toHaveAttribute('href', expect.stringContaining('senderId=p1'));
await expect.element(link).not.toHaveAttribute('href', expect.stringContaining('receiverId'));
});
it('hides the link for a read-only user', async () => {
render(Page, { data: { ...withDocs, canWrite: false } });
await expect.element(page.getByTestId('conv-new-doc-link')).not.toBeInTheDocument();
});
});