diff --git a/web/src/lib/components/AlbumCard.svelte b/web/src/lib/components/AlbumCard.svelte new file mode 100644 index 00000000..4ce23b53 --- /dev/null +++ b/web/src/lib/components/AlbumCard.svelte @@ -0,0 +1,27 @@ + + + + +
{album.title}
+ {#if album.year} +
{album.year}
+ {/if} +
diff --git a/web/src/lib/components/AlbumCard.test.ts b/web/src/lib/components/AlbumCard.test.ts new file mode 100644 index 00000000..5eb6d4aa --- /dev/null +++ b/web/src/lib/components/AlbumCard.test.ts @@ -0,0 +1,42 @@ +import { describe, expect, test } from 'vitest'; +import { render, screen, fireEvent } from '@testing-library/svelte'; +import AlbumCard from './AlbumCard.svelte'; +import type { AlbumRef } from '$lib/api/types'; +import { FALLBACK_COVER } from '$lib/media/covers'; + +const album: AlbumRef = { + id: 'xyz', + title: 'Kind of Blue', + artist_id: 'm-davis', + artist_name: 'Miles Davis', + year: 1959, + track_count: 5, + duration_sec: 2630, + cover_url: '/api/albums/xyz/cover', +}; + +describe('AlbumCard', () => { + test('renders cover, title, year inside a link to /albums/:id', () => { + const { container } = render(AlbumCard, { props: { album } }); + const link = screen.getByRole('link'); + expect(link).toHaveAttribute('href', '/albums/xyz'); + expect(link).toHaveTextContent('Kind of Blue'); + expect(link).toHaveTextContent('1959'); + + const img = container.querySelector('img') as HTMLImageElement; + expect(img.src).toContain('/api/albums/xyz/cover'); + }); + + test('year is omitted when not present', () => { + render(AlbumCard, { props: { album: { ...album, year: undefined } } }); + // No crash; no year text. + expect(screen.queryByText(/1959/)).not.toBeInTheDocument(); + }); + + test('broken cover falls back to FALLBACK_COVER data URL', async () => { + const { container } = render(AlbumCard, { props: { album } }); + const img = container.querySelector('img') as HTMLImageElement; + await fireEvent.error(img); + expect(img.src).toBe(FALLBACK_COVER); + }); +});