// @vitest-environment jsdom import {afterEach, describe, expect, it, vi} from 'vitest'; import {cleanup, fireEvent, render, screen} from '@testing-library/react'; import {MemoryRouter} from 'react-router-dom'; import LandingPage from './LandingPage.jsx'; import {fetchRoutes, json} from '../test/fetchRoutes.js'; vi.mock('react-i18next', () => import('../test/i18n-mock.js')); vi.mock('../hooks/useSession.js', () => ({default: () => ({active: false})})); // Auth and Core answer the same path; one object serves both shapes. const answer = (core) => fetchRoutes({'GET /api/stats/public': json(200, {topPlayedGames: [], topOwnedGames: [], ...core})}); const renderPage = () => render(); describe('LandingPage', () => { afterEach(cleanup); it('shows games per platform, top genres and finished per year', async () => { answer({ gamesPerPlatform: [{name: 'PS5', games: 3, minutes: 600}], topGenres: [{name: 'Shooter', games: 2, minutes: 1200}], finishedPerYear: [{year: 2025, games: 4}], }); renderPage(); expect(await screen.findByText('LANDING.PLATFORMS')).toBeTruthy(); expect(screen.getByText('PS5')).toBeTruthy(); expect(screen.getByText('LANDING.TOP_GENRES')).toBeTruthy(); expect(screen.getByText('STATS.GENRE.SHOOTER')).toBeTruthy(); expect(screen.getByText('LANDING.FINISHED_PER_YEAR')).toBeTruthy(); expect(screen.getByText('2025')).toBeTruthy(); }); it('hides the three blocks while their lists are empty', async () => { answer({gamesPerPlatform: [], topGenres: [], finishedPerYear: []}); renderPage(); await screen.findByText('LANDING.STATS_TITLE'); await vi.waitFor(() => expect(fetch).toHaveBeenCalledTimes(2)); for (const title of ['PLATFORMS', 'TOP_GENRES', 'FINISHED_PER_YEAR']) { expect(screen.queryByText(`LANDING.${title}`)).toBeNull(); } }); it('topGamesHaveNoStoreLink', async () => { answer({topPlayedGames: [{appid: 220, name: 'Half-Life 2', value: 600}]}); const {container} = renderPage(); await screen.findByText('Half-Life 2'); expect(container.querySelector('a[href*="steampowered"]')).toBeNull(); }); it('opensTheGamesBehindTheTotalButNotBehindOneUsersLibrary', async () => { fetchRoutes({ 'GET /api/stats/public': json(200, {topPlayedGames: [], topOwnedGames: [], totalLibraryEntries: 9, biggestLibrary: 4}), 'GET /api/stats/public/games': json(200, {content: [{appid: 3, name: 'Common', libraries: 2, minutes: 60}], page: 0, size: 40, totalElements: 1, totalPages: 1}), }); renderPage(); fireEvent.click(await screen.findByRole('button', {name: /LANDING\.TOTAL_GAMES/})); expect(await screen.findByText('Common')).toBeTruthy(); expect(fetch.mock.calls.at(-1)[0]).toContain('/api/stats/public/games?key=ENTRIES&page=0'); expect(screen.queryByRole('button', {name: /LANDING\.BIGGEST_LIBRARY/})).toBeNull(); expect(screen.queryByRole('button', {name: /LANDING\.TOTAL_USERS/})).toBeNull(); }); it('aZeroTotalIsNotClickable', async () => { answer({totalLibraryEntries: 0}); renderPage(); await screen.findByText('LANDING.TOTAL_GAMES'); expect(screen.queryByRole('button', {name: /LANDING\.TOTAL_GAMES/})).toBeNull(); }); });