import { describe, it, expect, afterEach } from 'vitest'; import { cleanup, render } from 'vitest-browser-svelte'; import { page } from 'vitest/browser'; import ContributorStack from './ContributorStack.svelte'; import type { components } from '$lib/generated/api'; type ActivityActorDTO = components['schemas']['ActivityActorDTO']; afterEach(() => cleanup()); const makeActor = (overrides: Partial = {}): ActivityActorDTO => ({ initials: 'MR', color: '#7a4f9a', name: 'Max Raddatz', ...overrides }); describe('ContributorStack', () => { it('contributor avatar is announced by screen readers with actor name', async () => { const actor = makeActor({ name: 'Anna Meier', initials: 'AM' }); render(ContributorStack, { contributors: [actor], hasMore: false }); await expect.element(page.getByRole('img', { name: 'Anna Meier' })).toBeInTheDocument(); }); it('falls back to initials as accessible name when actor name is null', async () => { const actor = makeActor({ name: undefined, initials: 'AM' }); render(ContributorStack, { contributors: [actor], hasMore: false }); await expect.element(page.getByRole('img', { name: 'AM' })).toBeInTheDocument(); }); it('renders two avatars without crashing when actors have identical initials', async () => { const actors = [ makeActor({ name: undefined, initials: 'AM', color: '#aa0000' }), makeActor({ name: undefined, initials: 'AM', color: '#0000bb' }) ]; render(ContributorStack, { contributors: actors, hasMore: false }); await expect.element(page.getByText('AM').first()).toBeInTheDocument(); }); it('renders overflow indicator when hasMore is true', async () => { render(ContributorStack, { contributors: [makeActor()], hasMore: true }); await expect.element(page.getByText('…')).toBeInTheDocument(); }); it('renders empty placeholder when no contributors', async () => { render(ContributorStack, { contributors: [], hasMore: false }); await expect.element(page.getByTitle('Noch niemand angefangen')).toBeInTheDocument(); }); });