Files
minstrel/web/src/lib/components/FlagPopover.test.ts
T
bvandeusenandClaude Opus 5.5 ca9917d8cd
release / govulncheck (push) Successful in 23s
release / web (push) Successful in 1m22s
release / go (push) Successful in 1m41s
release / integration (push) Successful in 4m49s
release / android (push) Successful in 5m32s
release / Build signed APK (releases and dev) (push) Successful in 5m39s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 28s
release / Verify release artifacts (tag releases only) (push) Skipped
chore(web): clear kit 3's alias and Vite's config-loader deprecations (#5021)
CI run 8789 warned on both:

- kit 3 deprecates `config.alias`. The $test-utils alias becomes a
  `#test-utils/*` subpath import, matching #lib, and its 43 import sites
  move with it.
- Vite's coming native config loader needs the extension on
  vitest.config.ts's import of vite.config.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 11:03:38 -04:00

88 lines
3.2 KiB
TypeScript

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.js';
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.js', () => ({
flagTrack: vi.fn().mockResolvedValue({ track_id: 't1', reason: 'bad_rip' })
}));
import { flagTrack } from '#lib/api/quarantine.js';
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();
});
});