feat(web): AddToPlaylistMenu submenu wired into TrackMenu (M7 #352)
The "Add to playlist…" entry that #372 reserved as a disabled slot is now active. Submenu lists the operator's own playlists alphabetically; "New playlist…" toggles an inline create form that makes the playlist + appends the track in two API calls. TrackMenu's existing test asserts the entry is enabled and opens the submenu instead of the previous "is disabled with tooltip" check.
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
import { describe, expect, test, vi } from 'vitest';
|
||||
import { render, screen, fireEvent, waitFor } from '@testing-library/svelte';
|
||||
import { readable } from 'svelte/store';
|
||||
import type { TrackRef } from '$lib/api/types';
|
||||
|
||||
const playlistsData = vi.hoisted(() => ({
|
||||
owned: [
|
||||
{
|
||||
id: 'p1',
|
||||
user_id: 'u-self',
|
||||
owner_username: 'me',
|
||||
name: 'B-list',
|
||||
description: '',
|
||||
is_public: false,
|
||||
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,
|
||||
cover_url: '',
|
||||
track_count: 5,
|
||||
duration_sec: 0,
|
||||
created_at: '',
|
||||
updated_at: ''
|
||||
}
|
||||
],
|
||||
public: [] as unknown[]
|
||||
}));
|
||||
|
||||
vi.mock('@tanstack/svelte-query', async (orig) => {
|
||||
const actual = (await orig()) as Record<string, unknown>;
|
||||
return {
|
||||
...actual,
|
||||
useQueryClient: () => ({ invalidateQueries: vi.fn().mockResolvedValue(undefined) })
|
||||
};
|
||||
});
|
||||
|
||||
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,
|
||||
cover_url: '',
|
||||
track_count: 0,
|
||||
duration_sec: 0,
|
||||
created_at: '',
|
||||
updated_at: ''
|
||||
})
|
||||
}));
|
||||
|
||||
import AddToPlaylistMenu from './AddToPlaylistMenu.svelte';
|
||||
|
||||
const track: TrackRef = {
|
||||
id: 't1',
|
||||
title: 'Roygbiv',
|
||||
album_id: 'a1',
|
||||
album_title: 'MHTRTC',
|
||||
artist_id: 'ar1',
|
||||
artist_name: 'BoC',
|
||||
duration_sec: 137,
|
||||
stream_url: '/api/tracks/t1/stream'
|
||||
};
|
||||
|
||||
describe('AddToPlaylistMenu', () => {
|
||||
test('lists own playlists alphabetically followed by "New playlist…"', () => {
|
||||
render(AddToPlaylistMenu, { props: { 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: { 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: { 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();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user