import { describe, it, expect, afterEach } from 'vitest'; import { cleanup, render } from 'vitest-browser-svelte'; import * as m from '$lib/paraglide/messages.js'; import EventPill from './EventPill.svelte'; import { timelineDateLabel } from './dateLabel'; import { makeEntry } from './test-factories'; afterEach(() => cleanup()); const EVENT_ID = '33333333-3333-3333-3333-333333333333'; function derived(derivedType: 'BIRTH' | 'DEATH' | 'MARRIAGE', title: string) { return makeEntry({ kind: 'EVENT', derived: true, derivedType, title, senderName: '', receiverName: '', precision: 'YEAR', eventDate: '1914-01-01', documentId: undefined }); } describe('EventPill', () => { it('renders a derived marriage as ⚭ + "Heirat" + title (REQ-007)', () => { render(EventPill, { entry: derived('MARRIAGE', 'Heirat: Karl & Elfriede') }); expect(document.body.textContent).toContain('⚭'); expect(document.body.textContent).toContain('Heirat'); expect(document.body.textContent).toContain('Heirat: Karl & Elfriede'); }); it('renders a derived birth as * + "Geburt" (REQ-007)', () => { render(EventPill, { entry: derived('BIRTH', 'Geburt: Hans') }); expect(document.body.textContent).toContain('*'); expect(document.body.textContent).toContain('Geburt'); }); it('renders a derived death as † + "Tod" (REQ-007)', () => { render(EventPill, { entry: derived('DEATH', 'Tod: Karl') }); expect(document.body.textContent).toContain('†'); expect(document.body.textContent).toContain('Tod'); }); it('wraps the glyph aria-hidden with an sr-only label sibling (REQ-018)', () => { render(EventPill, { entry: derived('BIRTH', 'Geburt: Hans') }); const hidden = document.querySelector('[aria-hidden="true"]'); expect(hidden?.textContent).toBe('*'); const srOnly = document.querySelector('.sr-only'); expect(srOnly?.textContent).toBe('Geburt'); }); it('shows an edit affordance for a curated PERSONAL event with an eventId (REQ-008)', () => { render(EventPill, { entry: makeEntry({ kind: 'EVENT', derived: false, type: 'PERSONAL', eventId: EVENT_ID, title: 'Auswanderung', senderName: '', receiverName: '', documentId: undefined }) }); const edit = document.querySelector('[data-testid="event-edit"]') as HTMLAnchorElement | null; expect(edit).not.toBeNull(); expect(edit?.getAttribute('href')).toContain(EVENT_ID); }); it('shows no edit affordance when eventId is null (REQ-008)', () => { render(EventPill, { entry: makeEntry({ kind: 'EVENT', derived: false, type: 'PERSONAL', eventId: undefined, title: 'Auswanderung', senderName: '', receiverName: '', documentId: undefined }) }); expect(document.querySelector('[data-testid="event-edit"]')).toBeNull(); }); it('shows no edit affordance for a derived event (REQ-008)', () => { render(EventPill, { entry: derived('MARRIAGE', 'Heirat') }); expect(document.querySelector('[data-testid="event-edit"]')).toBeNull(); }); it('appends the "abgeleitet" provenance token to a derived pill subtitle (REQ-007)', () => { const entry = derived('BIRTH', 'Geburt: Hans'); const date = timelineDateLabel(entry.eventDate, entry.precision, entry.eventDateEnd); render(EventPill, { entry }); expect(document.body.textContent).toContain(`${date} · ${m.timeline_provenance_derived()}`); }); it('appends the "kuratiert" provenance token to a curated PERSONAL pill subtitle (REQ-007)', () => { const entry = makeEntry({ kind: 'EVENT', derived: false, type: 'PERSONAL', eventId: EVENT_ID, title: 'Auswanderung', senderName: '', receiverName: '', precision: 'YEAR', eventDate: '1924-01-01', documentId: undefined }); const date = timelineDateLabel(entry.eventDate, entry.precision, entry.eventDateEnd); render(EventPill, { entry }); expect(document.body.textContent).toContain(`${date} · ${m.timeline_provenance_curated()}`); }); it('never shows the spec-sheet-only "persönlich"/"SEASON" tokens (REQ-007)', () => { render(EventPill, { entry: derived('BIRTH', 'Geburt: Hans') }); expect(document.body.textContent).not.toContain('persönlich'); expect(document.body.textContent).not.toContain('SEASON'); }); it('still shows the provenance token when the event has no date label (REQ-007)', () => { // An undated / UNKNOWN-precision event (e.g. in the undated bucket) yields a // null dateLabel; provenance must not be gated behind the date. const entry = makeEntry({ kind: 'EVENT', derived: true, derivedType: 'BIRTH', title: 'Geburt: Hans', senderName: '', receiverName: '', precision: 'UNKNOWN', eventDate: undefined, documentId: undefined }); expect(timelineDateLabel(entry.eventDate, entry.precision, entry.eventDateEnd)).toBeNull(); render(EventPill, { entry }); expect(document.body.textContent).toContain(m.timeline_provenance_derived()); }); });