feat(web): TrackMenu overflow + FlagPopover for the quarantine flow
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
import { afterEach, describe, expect, test, vi } from 'vitest';
|
||||
import { render, screen, fireEvent } from '@testing-library/svelte';
|
||||
import FlagPopover from './FlagPopover.svelte';
|
||||
import type { TrackRef } from '$lib/api/types';
|
||||
|
||||
const invalidateMock = vi.fn();
|
||||
vi.mock('@tanstack/svelte-query', async (orig) => {
|
||||
const actual = (await orig()) as Record<string, unknown>;
|
||||
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: TrackRef = {
|
||||
id: 't1',
|
||||
title: 'Roygbiv',
|
||||
album_id: 'a1',
|
||||
album_title: 'Geogaddi',
|
||||
artist_id: 'ar1',
|
||||
artist_name: 'Boards of Canada',
|
||||
duration_sec: 240,
|
||||
stream_url: '/api/tracks/t1/stream'
|
||||
};
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user