import { afterEach, describe, expect, test, vi } from 'vitest'; import { render, screen, fireEvent } from '@testing-library/svelte'; import type { AlbumRef, AlbumDetail, TrackRef } from '$lib/api/types'; import { FALLBACK_COVER } from '$lib/media/covers'; vi.mock('$lib/api/client', () => ({ api: { get: vi.fn() } })); vi.mock('$lib/player/store.svelte', () => ({ enqueueTracks: vi.fn() })); import AlbumCard from './AlbumCard.svelte'; import { api } from '$lib/api/client'; import { enqueueTracks } from '$lib/player/store.svelte'; 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', }; afterEach(() => vi.clearAllMocks()); 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 } } }); 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); }); test('+ queue button fetches album detail and calls enqueueTracks', async () => { const tracks: TrackRef[] = [ { id: 't1', title: 'So What', album_id: 'xyz', album_title: 'Kind of Blue', artist_id: 'm-davis', artist_name: 'Miles Davis', track_number: 1, disc_number: 1, duration_sec: 545, stream_url: '/api/tracks/t1/stream' } ]; const detail: AlbumDetail = { ...album, tracks }; (api.get as ReturnType).mockResolvedValueOnce(detail); render(AlbumCard, { props: { album } }); await fireEvent.click(screen.getByRole('button', { name: /add to queue/i })); expect(api.get).toHaveBeenCalledWith('/api/albums/xyz'); await Promise.resolve(); await Promise.resolve(); expect(enqueueTracks).toHaveBeenCalledWith(tracks); }); });