Files
minstrel/web/src/lib/components/QueueTrackRow.test.ts
T
bvandeusen 6912dadf2b
test-web / test (push) Successful in 36s
fix(test): order likes mock before component import in queue tests — #1596
The prior test fix registered the emptyLikesMock stub but imported it
(and the component under test) in the wrong order: importing QueueTrackRow
/ QueueDrawer transitively loads LikeButton → the mocked $lib/api/likes,
whose hoisted factory runs before the emptyLikesMock import initialized —
"Cannot access '__vi_import_N__' before initialization".

Move the emptyLikesMock import above, and the component import below, the
vi.mock call — matching the ArtistMenu/PlayerBar test layout so the
factory's binding is ready when the component graph loads.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 13:12:37 -04:00

98 lines
4.2 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/svelte';
import { emptyLikesMock } from '../../test-utils/mocks/likes';
import { makeTrack } from '$test-utils/fixtures/track';
// QueueTrackRow now renders a LikeButton, which reads createLikedIdsQuery.
// Stub the likes API so the row doesn't need a real QueryClient in context.
// The mock (and its emptyLikesMock import) must precede the component import
// below: importing QueueTrackRow transitively loads LikeButton → the mocked
// module, and the hoisted factory needs emptyLikesMock already initialized.
vi.mock('$lib/api/likes', () => emptyLikesMock());
import QueueTrackRow from './QueueTrackRow.svelte';
const playFromQueueIndex = vi.fn();
const removeFromQueue = vi.fn();
const moveQueueItem = vi.fn();
vi.mock('$lib/player/store.svelte', () => ({
playFromQueueIndex: (...args: unknown[]) => playFromQueueIndex(...args),
removeFromQueue: (...args: unknown[]) => removeFromQueue(...args),
moveQueueItem: (...args: unknown[]) => moveQueueItem(...args)
}));
const sampleTrack = makeTrack({
title: 'Song Title',
artist_name: 'Artist Name'
});
describe('QueueTrackRow', () => {
beforeEach(() => {
playFromQueueIndex.mockClear();
removeFromQueue.mockClear();
moveQueueItem.mockClear();
});
it('renders track title and artist', () => {
render(QueueTrackRow, { props: { track: sampleTrack, index: 3, isCurrent: false } });
expect(screen.getByText('Song Title')).toBeInTheDocument();
expect(screen.getByText(/Artist Name/)).toBeInTheDocument();
});
it('clicking the body calls playFromQueueIndex with the row index', async () => {
render(QueueTrackRow, { props: { track: sampleTrack, index: 3, isCurrent: false } });
const body = screen.getByRole('button', { name: /play song title/i });
await fireEvent.click(body);
expect(playFromQueueIndex).toHaveBeenCalledWith(3);
});
it('clicking the remove button calls removeFromQueue with the row index', async () => {
render(QueueTrackRow, { props: { track: sampleTrack, index: 3, isCurrent: false } });
const remove = screen.getByLabelText(/remove from queue/i);
await fireEvent.click(remove);
expect(removeFromQueue).toHaveBeenCalledWith(3);
});
it('current row shows the "Now playing" chip', () => {
render(QueueTrackRow, { props: { track: sampleTrack, index: 3, isCurrent: true } });
expect(screen.getByText(/now playing/i)).toBeInTheDocument();
});
it('current row body click does NOT call playFromQueueIndex', async () => {
render(QueueTrackRow, { props: { track: sampleTrack, index: 3, isCurrent: true } });
// The body button has a different aria-label when isCurrent
const body = screen.getByLabelText(/now playing/i);
await fireEvent.click(body);
expect(playFromQueueIndex).not.toHaveBeenCalled();
});
it('ArrowDown on the drag handle calls moveQueueItem with index+1', async () => {
render(QueueTrackRow, { props: { track: sampleTrack, index: 3, isCurrent: false } });
const handle = screen.getByLabelText(/reorder track/i);
await fireEvent.keyDown(handle, { key: 'ArrowDown' });
expect(moveQueueItem).toHaveBeenCalledWith(3, 4);
});
it('ArrowUp on the drag handle calls moveQueueItem with index-1', async () => {
render(QueueTrackRow, { props: { track: sampleTrack, index: 3, isCurrent: false } });
const handle = screen.getByLabelText(/reorder track/i);
await fireEvent.keyDown(handle, { key: 'ArrowUp' });
expect(moveQueueItem).toHaveBeenCalledWith(3, 2);
});
it('Enter on the drag handle prevents default but does not call moveQueueItem', async () => {
render(QueueTrackRow, { props: { track: sampleTrack, index: 3, isCurrent: false } });
const handle = screen.getByLabelText(/reorder track/i);
await fireEvent.keyDown(handle, { key: 'Enter' });
expect(moveQueueItem).not.toHaveBeenCalled();
});
it('Space on the drag handle prevents default but does not call moveQueueItem', async () => {
render(QueueTrackRow, { props: { track: sampleTrack, index: 3, isCurrent: false } });
const handle = screen.getByLabelText(/reorder track/i);
await fireEvent.keyDown(handle, { key: ' ' });
expect(moveQueueItem).not.toHaveBeenCalled();
});
});