import { afterEach, describe, expect, test, vi } from 'vitest'; import { render, screen, fireEvent } from '@testing-library/svelte'; import FlagPopover from './FlagPopover.svelte'; import { makeTrack } from '$test-utils/fixtures/track'; const invalidateMock = vi.fn(); vi.mock('@tanstack/svelte-query', async (orig) => { const actual = (await orig()) as Record; return { ...actual, useQueryClient: () => ({ invalidateQueries: invalidateMock }) }; }); vi.mock('$lib/api/quarantine', () => ({ flagTrack: vi.fn().mockResolvedValue({ track_id: 't1', reason: 'bad_rip' }) })); import { flagTrack } from '$lib/api/quarantine'; const track = makeTrack(); afterEach(() => vi.clearAllMocks()); describe('FlagPopover', () => { test('default reason is bad_rip; button reads "Flag" when not initialReason', () => { render(FlagPopover, { props: { track, onClose: vi.fn() } }); const select = screen.getByRole('combobox') as HTMLSelectElement; expect(select.value).toBe('bad_rip'); expect(screen.getByRole('button', { name: /^flag$/i })).toBeInTheDocument(); }); test('pre-fills reason and notes when initial values are provided; button reads "Update flag"', () => { render(FlagPopover, { props: { track, onClose: vi.fn(), initialReason: 'wrong_tags', initialNotes: 'wrong artist' } }); const select = screen.getByRole('combobox') as HTMLSelectElement; expect(select.value).toBe('wrong_tags'); const textarea = screen.getByPlaceholderText(/what's wrong/i) as HTMLTextAreaElement; expect(textarea.value).toBe('wrong artist'); expect(screen.getByRole('button', { name: /update flag/i })).toBeInTheDocument(); }); test('submits with reason and notes; calls invalidateQueries on success', async () => { const onClose = vi.fn(); render(FlagPopover, { props: { track, onClose } }); const select = screen.getByRole('combobox'); await fireEvent.change(select, { target: { value: 'duplicate' } }); const textarea = screen.getByPlaceholderText(/what's wrong/i); await fireEvent.input(textarea, { target: { value: 'same recording' } }); await fireEvent.click(screen.getByRole('button', { name: /^flag$/i })); await Promise.resolve(); await Promise.resolve(); expect(flagTrack).toHaveBeenCalledWith({ track_id: 't1', reason: 'duplicate', notes: 'same recording' }); expect(invalidateMock).toHaveBeenCalled(); expect(onClose).toHaveBeenCalled(); }); test('empty notes are not sent', async () => { render(FlagPopover, { props: { track, onClose: vi.fn() } }); await fireEvent.click(screen.getByRole('button', { name: /^flag$/i })); await Promise.resolve(); await Promise.resolve(); expect(flagTrack).toHaveBeenCalledWith({ track_id: 't1', reason: 'bad_rip', notes: undefined }); }); test('cancel button calls onClose without firing flagTrack', async () => { const onClose = vi.fn(); render(FlagPopover, { props: { track, onClose } }); await fireEvent.click(screen.getByRole('button', { name: /cancel/i })); expect(flagTrack).not.toHaveBeenCalled(); expect(onClose).toHaveBeenCalled(); }); });