diff --git a/web/src/lib/components/PlaylistCard.svelte b/web/src/lib/components/PlaylistCard.svelte new file mode 100644 index 00000000..f3af7dd2 --- /dev/null +++ b/web/src/lib/components/PlaylistCard.svelte @@ -0,0 +1,38 @@ + + + diff --git a/web/src/lib/components/PlaylistCard.test.ts b/web/src/lib/components/PlaylistCard.test.ts new file mode 100644 index 00000000..95258924 --- /dev/null +++ b/web/src/lib/components/PlaylistCard.test.ts @@ -0,0 +1,54 @@ +import { describe, expect, test, vi } from 'vitest'; +import { render, screen } from '@testing-library/svelte'; +import PlaylistCard from './PlaylistCard.svelte'; +import type { Playlist } from '$lib/api/types'; + +vi.mock('$lib/auth/store.svelte', () => ({ + user: { value: { id: 'u-self', username: 'me', is_admin: false } } +})); + +vi.mock('$app/navigation', () => ({ goto: vi.fn() })); + +const base: Playlist = { + id: 'p-1', + user_id: 'u-self', + owner_username: 'me', + name: 'Saturday morning', + description: '', + is_public: false, + cover_url: '', + track_count: 12, + duration_sec: 0, + created_at: '', + updated_at: '' +}; + +describe('PlaylistCard', () => { + test('renders own playlist without owner attribution', () => { + render(PlaylistCard, { props: { playlist: base } }); + expect(screen.getByText('Saturday morning')).toBeInTheDocument(); + expect(screen.getByText(/12\s+tracks/i)).toBeInTheDocument(); + expect(screen.queryByText(/by /)).not.toBeInTheDocument(); + }); + + test('renders cover image when cover_url is set', () => { + render(PlaylistCard, { props: { playlist: { ...base, cover_url: '/api/playlists/p-1/cover' } } }); + const img = screen.getByRole('img'); + expect(img.getAttribute('src')).toBe('/api/playlists/p-1/cover'); + }); + + test('renders glyph fallback when cover_url is empty', () => { + render(PlaylistCard, { props: { playlist: base } }); + expect(screen.getByText(/no tracks yet/i)).toBeInTheDocument(); + }); + + test("attributes other users' playlists to the owner", () => { + render(PlaylistCard, { props: { playlist: { ...base, user_id: 'u-bob', owner_username: 'bob' } } }); + expect(screen.getByText(/by bob/i)).toBeInTheDocument(); + }); + + test('singular "track" when count is 1', () => { + render(PlaylistCard, { props: { playlist: { ...base, track_count: 1 } } }); + expect(screen.getByText(/1\s+track\b/i)).toBeInTheDocument(); + }); +});