Files
minstrel/web/src/lib/api/playlists.refresh-foryou.test.ts
T
bvandeusen e48d84cde1 feat(web/playlists): For-You tile play button refreshes and plays
Click on the For-You tile's play button now triggers a synchronous
refresh, then enqueues + auto-plays the freshly-built playlist.
Two-click-target tile pattern: tile BODY click still navigates to
the playlist detail page (existing behavior); the play button is
the new generate-and-play action.

Behavior is gated on playlist.system_variant === 'for_you' — every
other playlist (user, Discover, Songs-like-X) keeps the existing
"fetch detail, enqueue, play" flow. The atomic-replace BuildSystem-
Playlists creates a new playlist_id, so the play handler uses the
refresh response's id for the follow-up getPlaylist call rather
than the stale tile prop's id.

Empty-library response (playlist_id: null) is a no-op — clicking
play in a degenerate-empty library doesn't error, it just doesn't
start playback. Caches are invalidated post-refresh so the home
view re-fetches and the tile re-renders with the new id, avoiding
the corner case of a click-then-tile-body 404.

Tests cover the refresh-and-play happy path, the empty-library
no-op, and that non-For-You playlists keep the existing flow.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 10:40:23 -04:00

32 lines
1.1 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { refreshForYou } from './playlists';
vi.mock('./client', () => ({
apiFetch: vi.fn()
}));
import { apiFetch } from './client';
describe('refreshForYou', () => {
beforeEach(() => vi.clearAllMocks());
it('POSTs the correct path and returns the response', async () => {
const sample = { playlist_id: 'pl-abc', track_count: 25, track_ids: ['t1', 't2'] };
(apiFetch as unknown as ReturnType<typeof vi.fn>).mockResolvedValueOnce(sample);
const got = await refreshForYou();
expect(apiFetch).toHaveBeenCalledWith(
'/api/playlists/system/for-you/refresh',
expect.objectContaining({ method: 'POST' })
);
expect(got).toEqual(sample);
});
it('handles empty-library response', async () => {
const sample = { playlist_id: null, track_count: 0, track_ids: [] };
(apiFetch as unknown as ReturnType<typeof vi.fn>).mockResolvedValueOnce(sample);
const got = await refreshForYou();
expect(got.playlist_id).toBe(null);
expect(got.track_ids).toEqual([]);
});
});