feat(web/m7-380): animate pending placeholder + add play overlay to PlaylistCard
This commit is contained in:
@@ -1,13 +1,48 @@
|
||||
import { describe, expect, test, vi } from 'vitest';
|
||||
import { render, screen } from '@testing-library/svelte';
|
||||
import { describe, expect, test, vi, beforeEach } from 'vitest';
|
||||
import { render, screen, fireEvent } from '@testing-library/svelte';
|
||||
import PlaylistCard from './PlaylistCard.svelte';
|
||||
import type { Playlist } from '$lib/api/types';
|
||||
import type { Playlist, PlaylistDetail } from '$lib/api/types';
|
||||
|
||||
vi.mock('$lib/auth/store.svelte', () => ({
|
||||
user: { value: { id: 'u-self', username: 'me', is_admin: false } }
|
||||
}));
|
||||
|
||||
vi.mock('$app/navigation', () => ({ goto: vi.fn() }));
|
||||
vi.mock('$lib/api/playlists', () => ({
|
||||
getPlaylist: vi.fn().mockResolvedValue({
|
||||
id: 'p-1',
|
||||
user_id: 'u-self',
|
||||
owner_username: 'me',
|
||||
name: 'Saturday morning',
|
||||
description: '',
|
||||
is_public: false,
|
||||
kind: 'user',
|
||||
system_variant: null,
|
||||
seed_artist_id: null,
|
||||
cover_url: '',
|
||||
track_count: 1,
|
||||
duration_sec: 60,
|
||||
created_at: '2026-01-01T00:00:00Z',
|
||||
updated_at: '2026-01-01T00:00:00Z',
|
||||
tracks: [
|
||||
{
|
||||
position: 0,
|
||||
track_id: 't-1',
|
||||
album_id: 'al-1',
|
||||
artist_id: 'ar-1',
|
||||
title: 'Test Track',
|
||||
artist_name: 'Test Artist',
|
||||
album_title: 'Test Album',
|
||||
duration_sec: 60,
|
||||
stream_url: '/s/t-1',
|
||||
added_at: '2026-01-01T00:00:00Z'
|
||||
}
|
||||
]
|
||||
} as PlaylistDetail)
|
||||
}));
|
||||
|
||||
vi.mock('$lib/player/store.svelte', () => ({
|
||||
playQueue: vi.fn()
|
||||
}));
|
||||
|
||||
const base: Playlist = {
|
||||
id: 'p-1',
|
||||
@@ -27,6 +62,10 @@ const base: Playlist = {
|
||||
};
|
||||
|
||||
describe('PlaylistCard', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
test('renders own playlist without owner attribution', () => {
|
||||
render(PlaylistCard, { props: { playlist: base } });
|
||||
expect(screen.getByText('Saturday morning')).toBeInTheDocument();
|
||||
@@ -34,6 +73,12 @@ describe('PlaylistCard', () => {
|
||||
expect(screen.queryByText(/by /)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('navigates to playlist when clicking the card', () => {
|
||||
render(PlaylistCard, { props: { playlist: base } });
|
||||
const link = screen.getByRole('link');
|
||||
expect(link).toHaveAttribute('href', '/playlists/p-1');
|
||||
});
|
||||
|
||||
test('renders cover image when cover_url is set', () => {
|
||||
// The <img> has alt="" (decorative — the playlist name is in the
|
||||
// sibling text below, so duplicating it for screen readers would be
|
||||
@@ -60,4 +105,32 @@ describe('PlaylistCard', () => {
|
||||
render(PlaylistCard, { props: { playlist: { ...base, track_count: 1 } } });
|
||||
expect(screen.getByText(/1\s+track\b/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('play button exists with correct aria label', () => {
|
||||
render(PlaylistCard, { props: { playlist: base } });
|
||||
const playButton = screen.getByLabelText(/Play Saturday morning/i);
|
||||
expect(playButton).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('play button is disabled when track_count is 0', () => {
|
||||
render(PlaylistCard, { props: { playlist: { ...base, track_count: 0 } } });
|
||||
const playButton = screen.getByLabelText(/Play Saturday morning/i);
|
||||
expect(playButton).toBeDisabled();
|
||||
});
|
||||
|
||||
test('play button calls playQueue with tracks when clicked', async () => {
|
||||
const { getPlaylist } = await import('$lib/api/playlists');
|
||||
const { playQueue } = await import('$lib/player/store.svelte');
|
||||
|
||||
render(PlaylistCard, { props: { playlist: base } });
|
||||
const playButton = screen.getByLabelText(/Play Saturday morning/i);
|
||||
|
||||
await fireEvent.click(playButton);
|
||||
|
||||
// Allow promises to resolve
|
||||
await new Promise(resolve => setTimeout(resolve, 10));
|
||||
|
||||
expect(getPlaylist).toHaveBeenCalledWith('p-1');
|
||||
expect(playQueue).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user