Files
familienarchiv/frontend/src/lib/person/personFormat.spec.ts
Marcel 507fa088fd refactor(document): move statusDotClass and statusLabel to document domain
These functions describe DocumentStatus display logic (dot colours, readable
labels) and belong in the document domain. They were incorrectly placed in
personFormat.ts. Moving them to documentStatusLabel.ts removes the
person → document dependency and prepares the codebase for the
boundaries/dependencies ESLint rule.

Refs #410
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 18:09:01 +02:00

142 lines
5.3 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { getInitials, abbreviateName, formatXsMeta, personAvatarColor } from './personFormat';
import { formatDate } from '$lib/shared/utils/date';
// ─── getInitials ─────────────────────────────────────────────────────────────
describe('getInitials', () => {
it('returns first chars of first and last word uppercased', () => {
expect(getInitials('Marcel Raddatz')).toBe('MR');
});
it('returns single char for a single-word name', () => {
expect(getInitials('Raddatz')).toBe('R');
});
it('returns empty string for an empty name', () => {
expect(getInitials('')).toBe('');
});
it('splits on whitespace only — hyphenated first word counts as one', () => {
expect(getInitials('Anna-Maria Raddatz')).toBe('AR');
});
it('ignores extra whitespace between words', () => {
expect(getInitials(' Karl Raddatz ')).toBe('KR');
});
});
// ─── abbreviateName ──────────────────────────────────────────────────────────
describe('abbreviateName', () => {
it('abbreviates first name to initial + last name', () => {
expect(abbreviateName({ firstName: 'Karl', lastName: 'Raddatz' })).toBe('K. Raddatz');
});
it('returns single name as-is when no last name', () => {
expect(abbreviateName({ firstName: 'Elfriede', lastName: '' })).toBe('Elfriede');
});
it('preserves hyphenated last name', () => {
expect(abbreviateName({ firstName: 'Karl', lastName: 'Müller-Schmidt' })).toBe(
'K. Müller-Schmidt'
);
});
it('handles leading/trailing whitespace in names', () => {
expect(abbreviateName({ firstName: ' Karl ', lastName: ' Raddatz ' })).toBe('K. Raddatz');
});
});
// ─── formatXsMeta ────────────────────────────────────────────────────────────
type Doc = {
sender?: { firstName: string; lastName: string } | null;
receivers?: { firstName: string; lastName: string }[];
documentDate?: string | null;
};
describe('formatXsMeta', () => {
const sender = { firstName: 'Karl', lastName: 'Raddatz' };
const receiver1 = { firstName: 'Elfriede', lastName: 'Raddatz' };
const receiver2 = { firstName: 'Anna', lastName: 'Müller' };
const receiver3 = { firstName: 'Hans', lastName: 'Schmidt' };
it('formats sender with no receivers and date', () => {
const doc: Doc = { sender, receivers: [], documentDate: '1943-12-24' };
expect(formatXsMeta(doc)).toBe('K.Raddatz · 24.12.1943');
});
it('formats sender with one receiver and date', () => {
const doc: Doc = { sender, receivers: [receiver1], documentDate: '1943-12-24' };
expect(formatXsMeta(doc)).toBe('K.Raddatz → E.Raddatz · 24.12.1943');
});
it('formats sender with three receivers showing +2', () => {
const doc: Doc = {
sender,
receivers: [receiver1, receiver2, receiver3],
documentDate: '1943-12-24'
};
expect(formatXsMeta(doc)).toBe('K.Raddatz → E.Raddatz +2 · 24.12.1943');
});
it('formats without sender', () => {
const doc: Doc = { sender: null, receivers: [receiver1], documentDate: '1943-12-24' };
expect(formatXsMeta(doc)).toBe('E.Raddatz · 24.12.1943');
});
it('formats without date', () => {
const doc: Doc = { sender, receivers: [], documentDate: null };
expect(formatXsMeta(doc)).toBe('K.Raddatz');
});
it('formats with no sender and no date', () => {
const doc: Doc = { sender: null, receivers: [receiver1], documentDate: null };
expect(formatXsMeta(doc)).toBe('E.Raddatz');
});
});
// ─── personAvatarColor ───────────────────────────────────────────────────────
const PALETTE = ['#012851', '#5A3080', '#007596', '#2A6040', '#803020'];
describe('personAvatarColor', () => {
it('returns a value from the palette', () => {
expect(PALETTE).toContain(personAvatarColor('abc'));
});
it('is deterministic — same id always returns same color', () => {
const id = '550e8400-e29b-41d4-a716-446655440000';
expect(personAvatarColor(id)).toBe(personAvatarColor(id));
});
it('all 5 palette entries are reachable across 1000 random UUIDs', () => {
const seen = new Set<string>();
for (let i = 0; i < 1000; i++) {
seen.add(personAvatarColor(crypto.randomUUID()));
}
expect(seen.size).toBe(5);
});
});
// ─── formatDate ──────────────────────────────────────────────────────────────
describe('formatDate', () => {
it('formats short date as dd.mm.yyyy', () => {
expect(formatDate('1943-12-24', 'short')).toBe('24.12.1943');
});
it('formats long date with German month name', () => {
expect(formatDate('1943-12-24', 'long')).toBe('24. Dezember 1943');
});
it('does not shift Dec 31 to Jan 1 (UTC off-by-one guard)', () => {
expect(formatDate('1943-12-31', 'short')).toBe('31.12.1943');
});
it('does not shift Jan 1 to Dec 31 (UTC off-by-one guard)', () => {
expect(formatDate('1944-01-01', 'short')).toBe('01.01.1944');
});
});