import { describe, it, expect, afterEach } from 'vitest'; import { cleanup, render } from 'vitest-browser-svelte'; import { page } from 'vitest/browser'; import DashboardResumeStrip from './DashboardResumeStrip.svelte'; afterEach(cleanup); const makeResume = (overrides: Record = {}) => ({ documentId: 'd1', title: 'Brief 1923', caption: 'Sender → Receiver', excerpt: 'First paragraph', totalBlocks: 12, pct: 50, thumbnailUrl: '/api/d1/thumb', collaborators: [{ initials: 'AS', color: '#012851', name: null }], ...overrides }); describe('DashboardResumeStrip', () => { it('renders the empty card when resumeDoc is null', async () => { render(DashboardResumeStrip, { props: { resumeDoc: null } }); const empty = document.querySelector('[data-testid="resume-strip-empty"]'); expect(empty).not.toBeNull(); await expect .element(page.getByRole('heading', { name: /noch kein dokument begonnen/i })) .toBeVisible(); }); it('renders the resume strip when resumeDoc is provided', async () => { render(DashboardResumeStrip, { props: { resumeDoc: makeResume() } }); expect(document.querySelector('[data-testid="resume-strip"]')).not.toBeNull(); }); it('renders the document title', async () => { render(DashboardResumeStrip, { props: { resumeDoc: makeResume() } }); await expect.element(page.getByRole('heading', { name: /brief 1923/i })).toBeVisible(); }); it('renders the thumbnail image when thumbnailUrl is set', async () => { render(DashboardResumeStrip, { props: { resumeDoc: makeResume() } }); expect(document.querySelector('[data-testid="resume-thumbnail-img"]')).not.toBeNull(); }); it('renders the placeholder icon when thumbnailUrl is missing', async () => { render(DashboardResumeStrip, { props: { resumeDoc: makeResume({ thumbnailUrl: null }) } }); expect(document.querySelector('[data-testid="resume-thumbnail-fallback"]')).not.toBeNull(); }); it('renders the progress bar with correct aria-valuenow', async () => { render(DashboardResumeStrip, { props: { resumeDoc: makeResume({ pct: 75 }) } }); const progress = document.querySelector('[role="progressbar"]') as HTMLElement; expect(progress.getAttribute('aria-valuenow')).toBe('75'); }); it('renders the resume CTA link to the document detail', async () => { render(DashboardResumeStrip, { props: { resumeDoc: makeResume({ documentId: 'doc-42' }) } }); const link = document.querySelector('a[href="/documents/doc-42"]') as HTMLAnchorElement; expect(link).not.toBeNull(); }); it('renders the collaborators stack', async () => { render(DashboardResumeStrip, { props: { resumeDoc: makeResume({ collaborators: [ { initials: 'XR', color: '#012851', name: null }, { initials: 'YQ', color: '#5A3080', name: null } ] }) } }); await expect.element(page.getByText('XR')).toBeVisible(); await expect.element(page.getByText('YQ')).toBeVisible(); }); it('falls back to the default color when collaborator color is invalid', async () => { render(DashboardResumeStrip, { props: { resumeDoc: makeResume({ collaborators: [{ initials: 'ZQ', color: 'not-a-hex', name: null }] }) } }); // safeColor falls back to #8c9aa3 — browser may serialize as rgb(140, 154, 163) const span = Array.from(document.querySelectorAll('span')).find( (s) => s.textContent?.trim() === 'ZQ' ) as HTMLElement; const style = span.getAttribute('style') ?? ''; expect(style.toLowerCase()).toMatch(/(8c9aa3|140,\s*154,\s*163)/); }); });