b20738f925
Operator can refresh their Discover playlist without waiting for the next daily cron tick. Two surfaces, both gated on system_variant === 'discover': - Detail page header: "Refresh" button next to the cover art / meta. - Home Playlists row tile kebab: "Refresh Discover" menu item. Both call POST /api/playlists/system/discover/refresh, show a confirmation toast, and invalidate the playlist + playlists queries so the new content lands without a manual page reload. Extends Playlist.system_variant union with 'discover'. Adds refreshDiscover() typed client. Tests cover client behaviour (success + empty-library) plus conditional rendering on both surfaces.
32 lines
1.0 KiB
TypeScript
32 lines
1.0 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import { refreshDiscover } from './playlists';
|
|
|
|
vi.mock('./client', () => ({
|
|
apiFetch: vi.fn()
|
|
}));
|
|
|
|
import { apiFetch } from './client';
|
|
|
|
describe('refreshDiscover', () => {
|
|
beforeEach(() => vi.clearAllMocks());
|
|
|
|
it('POSTs the correct path and returns the response', async () => {
|
|
const sample = { playlist_id: 'abc-123', track_count: 100 };
|
|
(apiFetch as unknown as ReturnType<typeof vi.fn>).mockResolvedValueOnce(sample);
|
|
const got = await refreshDiscover();
|
|
expect(apiFetch).toHaveBeenCalledWith(
|
|
'/api/playlists/system/discover/refresh',
|
|
expect.objectContaining({ method: 'POST' })
|
|
);
|
|
expect(got).toEqual(sample);
|
|
});
|
|
|
|
it('handles empty-library response', async () => {
|
|
const sample = { playlist_id: null, track_count: 0 };
|
|
(apiFetch as unknown as ReturnType<typeof vi.fn>).mockResolvedValueOnce(sample);
|
|
const got = await refreshDiscover();
|
|
expect(got.playlist_id).toBe(null);
|
|
expect(got.track_count).toBe(0);
|
|
});
|
|
});
|