import { describe, expect, test, vi } from 'vitest'; import { render, screen, fireEvent, waitFor } from '@testing-library/svelte'; import { readable } from 'svelte/store'; import { makeTrack } from '$test-utils/fixtures/track'; const playlistsData = vi.hoisted(() => ({ owned: [ { id: 'p1', user_id: 'u-self', owner_username: 'me', name: 'B-list', description: '', is_public: false, kind: 'user', system_variant: null, refreshable: false, seed_artist_id: null, cover_url: '', track_count: 3, duration_sec: 0, created_at: '', updated_at: '' }, { id: 'p2', user_id: 'u-self', owner_username: 'me', name: 'A-list', description: '', is_public: false, kind: 'user', system_variant: null, refreshable: false, seed_artist_id: null, cover_url: '', track_count: 5, duration_sec: 0, created_at: '', updated_at: '' } ], public: [] as unknown[] })); vi.mock('$lib/auth/store.svelte', () => ({ user: { value: { id: 'u-self', username: 'me', is_admin: false } } })); vi.mock('$lib/api/playlists', () => ({ createPlaylistsQuery: () => readable({ data: playlistsData, isPending: false, isError: false }), appendTracks: vi.fn().mockResolvedValue({ id: 'p1', tracks: [] }), createPlaylist: vi.fn().mockResolvedValue({ id: 'p3', user_id: 'u-self', owner_username: 'me', name: 'New', description: '', is_public: false, kind: 'user', system_variant: null, refreshable: false, seed_artist_id: null, cover_url: '', track_count: 0, duration_sec: 0, created_at: '', updated_at: '' }) })); import AddToPlaylistMenu from './AddToPlaylistMenu.svelte'; const track = makeTrack(); describe('AddToPlaylistMenu', () => { test('lists own playlists alphabetically followed by "New playlist…"', () => { render(AddToPlaylistMenu, { props: { tracks: [track], onClose: vi.fn() } }); const items = screen.getAllByRole('menuitem'); // First two should be the playlists in alpha order, then "New playlist…". expect(items[0].textContent).toContain('A-list'); expect(items[1].textContent).toContain('B-list'); expect(items[2].textContent).toContain('New playlist'); }); test('clicking a playlist appends and closes', async () => { const onClose = vi.fn(); const { appendTracks } = await import('$lib/api/playlists'); render(AddToPlaylistMenu, { props: { tracks: [track], onClose } }); await fireEvent.click(screen.getByRole('menuitem', { name: /A-list/ })); await waitFor(() => expect(onClose).toHaveBeenCalled()); expect(appendTracks).toHaveBeenCalledWith('p2', ['t1']); }); test('"New playlist…" toggles inline create form', async () => { render(AddToPlaylistMenu, { props: { tracks: [track], onClose: vi.fn() } }); await fireEvent.click(screen.getByRole('menuitem', { name: /New playlist/ })); expect(screen.getByPlaceholderText(/playlist name/i)).toBeInTheDocument(); expect(screen.getByText(/Create & add/i)).toBeInTheDocument(); }); });