d43719516e
test-web / test (push) Successful in 33s
Operator UI review on the merged build:
- Hero row felt 'odd' — operator chose remove-entirely on the
AskUserQuestion options. The Playlists row already leads with
For-You and Recently Added leads with the newest album, so the
hero duplicated both anchors. Pulled HomeHeroCard.svelte + all
page-level wiring (systemShuffle/getPlaylist imports + playForYou/
playLatestAlbum handlers) and reverted the within() test scoping
added in 682d7a5e.
- Rediscover tiles step down to w-32 (albums) / w-28 (artists),
matching the Most Played CompactTrackCard visual weight on web.
Reinforces the page hierarchy: fresh content gets real estate,
throwbacks recede.
151 lines
5.1 KiB
TypeScript
151 lines
5.1 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
||
import { render, screen } from '@testing-library/svelte';
|
||
import { readable } from 'svelte/store';
|
||
import { emptyLikesMock } from '../test-utils/mocks/likes';
|
||
import type { Playlist } from '$lib/api/types';
|
||
|
||
// Mock all the queries the home page constructs.
|
||
vi.mock('$lib/api/home', () => ({
|
||
createHomeQuery: () =>
|
||
readable({
|
||
data: {
|
||
recently_added_albums: [],
|
||
most_played_tracks: [],
|
||
rediscover_albums: [],
|
||
rediscover_artists: [],
|
||
last_played_artists: []
|
||
},
|
||
isPending: false,
|
||
isError: false,
|
||
refetch: vi.fn()
|
||
})
|
||
}));
|
||
|
||
vi.mock('$lib/api/playlists', () => ({
|
||
createPlaylistsQuery: vi.fn()
|
||
}));
|
||
|
||
vi.mock('$lib/api/me', () => ({
|
||
createSystemPlaylistsStatusQuery: vi.fn()
|
||
}));
|
||
|
||
// LikeButton imports — needed because the home renders cards that may use it.
|
||
vi.mock('$lib/api/likes', () => emptyLikesMock());
|
||
|
||
import Page from './+page.svelte';
|
||
import { createPlaylistsQuery } from '$lib/api/playlists';
|
||
import { createSystemPlaylistsStatusQuery } from '$lib/api/me';
|
||
|
||
const emptyPlaylistsResponse = { owned: [], public: [] };
|
||
|
||
beforeEach(() => {
|
||
(createPlaylistsQuery as unknown as ReturnType<typeof vi.fn>).mockImplementation(() =>
|
||
readable({ data: emptyPlaylistsResponse, isPending: false, isError: false })
|
||
);
|
||
(createSystemPlaylistsStatusQuery as unknown as ReturnType<typeof vi.fn>).mockReturnValue(
|
||
readable({
|
||
data: { in_flight: false, last_run_at: null, last_error: null },
|
||
isPending: false,
|
||
isError: false
|
||
})
|
||
);
|
||
});
|
||
|
||
afterEach(() => vi.clearAllMocks());
|
||
|
||
describe('home Playlists section', () => {
|
||
test('renders 5 placeholder cards when no playlists exist', () => {
|
||
// Slot layout: For-You, Discover, 3× Songs-like.
|
||
render(Page);
|
||
const placeholders = screen.queryAllByTestId('playlist-placeholder-card');
|
||
expect(placeholders).toHaveLength(5);
|
||
});
|
||
|
||
test('building status sets variant=building on placeholders', () => {
|
||
(createSystemPlaylistsStatusQuery as unknown as ReturnType<typeof vi.fn>).mockReturnValue(
|
||
readable({
|
||
data: { in_flight: true, last_run_at: null, last_error: null },
|
||
isPending: false,
|
||
isError: false
|
||
})
|
||
);
|
||
const { container } = render(Page);
|
||
const buildingPlaceholders = container.querySelectorAll('[data-variant="building"]');
|
||
expect(buildingPlaceholders.length).toBeGreaterThan(0);
|
||
});
|
||
|
||
test('renders For-You playlist + 3 placeholders when only for_you exists', () => {
|
||
const forYou: Playlist = {
|
||
id: 'fy',
|
||
user_id: 'u1',
|
||
owner_username: 'u1',
|
||
name: 'For You',
|
||
description: '',
|
||
is_public: false,
|
||
kind: 'system',
|
||
system_variant: 'for_you',
|
||
refreshable: false,
|
||
seed_artist_id: null,
|
||
cover_url: '',
|
||
track_count: 25,
|
||
duration_sec: 1500,
|
||
created_at: '2026-05-04T00:00:00Z',
|
||
updated_at: '2026-05-04T00:00:00Z'
|
||
};
|
||
(createPlaylistsQuery as unknown as ReturnType<typeof vi.fn>).mockImplementation(
|
||
(kind?: string) =>
|
||
kind === 'system'
|
||
? readable({
|
||
data: { owned: [forYou], public: [] },
|
||
isPending: false,
|
||
isError: false
|
||
})
|
||
: readable({ data: emptyPlaylistsResponse, isPending: false, isError: false })
|
||
);
|
||
render(Page);
|
||
expect(screen.getByText('For You')).toBeInTheDocument();
|
||
const placeholders = screen.queryAllByTestId('playlist-placeholder-card');
|
||
// 1 Discover + 3 Songs-like slots = 4 placeholders alongside the
|
||
// real For-You tile.
|
||
expect(placeholders).toHaveLength(4);
|
||
});
|
||
|
||
test('renders secondary system kinds (deep_cuts / new_for_you) after Songs-like slots', () => {
|
||
// Operator backflow 2026-06-01: web Home surfaces the 5 secondary
|
||
// system kinds when generated. No placeholders for them — they
|
||
// depend on library shape, so missing means "not enough data."
|
||
const makePlaylist = (id: string, variant: string, name: string): Playlist => ({
|
||
id,
|
||
user_id: 'u1',
|
||
owner_username: 'u1',
|
||
name,
|
||
description: '',
|
||
is_public: false,
|
||
kind: 'system',
|
||
system_variant: variant,
|
||
refreshable: variant !== 'songs_like_artist',
|
||
seed_artist_id: null,
|
||
cover_url: '',
|
||
track_count: 25,
|
||
duration_sec: 1500,
|
||
created_at: '2026-05-04T00:00:00Z',
|
||
updated_at: '2026-05-04T00:00:00Z'
|
||
});
|
||
const owned = [
|
||
makePlaylist('fy', 'for_you', 'For You'),
|
||
makePlaylist('dc', 'deep_cuts', 'Deep cuts'),
|
||
makePlaylist('nfy', 'new_for_you', 'New for you')
|
||
];
|
||
(createPlaylistsQuery as unknown as ReturnType<typeof vi.fn>).mockImplementation(
|
||
(kind?: string) =>
|
||
kind === 'system'
|
||
? readable({ data: { owned, public: [] }, isPending: false, isError: false })
|
||
: readable({ data: emptyPlaylistsResponse, isPending: false, isError: false })
|
||
);
|
||
render(Page);
|
||
expect(screen.getByText('For You')).toBeInTheDocument();
|
||
expect(screen.getByText('Deep cuts')).toBeInTheDocument();
|
||
expect(screen.getByText('New for you')).toBeInTheDocument();
|
||
});
|
||
});
|