Public Access
Get started links to registration and says an admin approves accounts. Back navigation restores the library scroll; search no longer fills history. Steam and PSN unlink ask first. Any close marks news as seen. Confetti plays only when the level rises on the page. Count-ups respect reduced motion. Html lang follows the UI language. Add a 404 page, translated role and dialog close label.
119 lines
5.1 KiB
React
119 lines
5.1 KiB
React
// @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'));
|
|
const session = vi.hoisted(() => ({active: false}));
|
|
vi.mock('../hooks/useSession.js', () => ({default: () => session}));
|
|
|
|
// 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(<MemoryRouter><LandingPage/></MemoryRouter>);
|
|
|
|
describe('LandingPage', () => {
|
|
afterEach(() => {
|
|
cleanup();
|
|
session.active = false;
|
|
});
|
|
|
|
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('links "get started" to the registration and shows the approval hint, but not when logged in', async () => {
|
|
answer({});
|
|
const {unmount} = renderPage();
|
|
|
|
expect(screen.getByRole('link', {name: /MAIN.GET_STARTED/}).getAttribute('href')).toBe('/register');
|
|
expect(screen.getByText('MAIN.APPROVAL_HINT')).toBeTruthy();
|
|
await vi.waitFor(() => expect(fetch).toHaveBeenCalled());
|
|
unmount();
|
|
|
|
session.active = true;
|
|
renderPage();
|
|
expect(screen.queryByText('MAIN.APPROVAL_HINT')).toBeNull();
|
|
await vi.waitFor(() => expect(fetch).toHaveBeenCalled());
|
|
});
|
|
|
|
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();
|
|
});
|
|
|
|
it('opensTheUserListOnlyWithASession', async () => {
|
|
fetchRoutes({
|
|
'GET /api/stats/public': json(200, {topPlayedGames: [], topOwnedGames: [], totalUsers: 4, usersWithSteamLinked: 2}),
|
|
'GET /api/stats/users': json(200, {listed: [{displayName: 'Lobo'}], listedTotal: 1, privateCount: 3,
|
|
includesYou: false, page: 0, totalPages: 1}),
|
|
});
|
|
const {unmount} = renderPage();
|
|
await screen.findByText('LANDING.TOTAL_USERS');
|
|
expect(screen.queryByRole('button', {name: /LANDING\.TOTAL_USERS/})).toBeNull();
|
|
unmount();
|
|
|
|
session.active = true;
|
|
renderPage();
|
|
fireEvent.click(await screen.findByRole('button', {name: /LANDING\.TOTAL_USERS/}));
|
|
|
|
expect(await screen.findByRole('link', {name: 'Lobo'})).toBeTruthy();
|
|
expect(screen.getByText('PEOPLE.PRIVATE:3')).toBeTruthy();
|
|
expect(fetch.mock.calls.at(-1)[0]).toContain('/api/stats/users?key=TOTAL_USERS&page=0');
|
|
});
|
|
});
|