Files
ToBePlayed-Frontend/src/pages/PublicProfilePage.test.jsx
T
lobo 43c4862606
Build, Test & Push Frontend / quality-check (push) Successful in 34s
Build, Test & Push Frontend / docker (push) Successful in 23s
feat(ui): add follow button, follows card, and inbox page
2026-10-06 16:22:22 +02:00

565 lines
26 KiB
React

// @vitest-environment jsdom
import {afterEach, describe, expect, it, vi} from 'vitest';
import {cleanup, fireEvent, screen, waitFor, within} from '@testing-library/react';
import PublicProfilePage from './PublicProfilePage.jsx';
import {fetchRoutes, json} from '../test/fetchRoutes.js';
import {renderWithProviders} from '../test/renderWithProviders.jsx';
vi.mock('react-i18next', () => import('../test/i18n-mock.js'));
const library = {
totalGames: 233, totalPlaytimeMinutes: 60000,
byStatus: {BACKLOG: 142, PLAYING: 6, FINISHED: 51, COMPLETED: 9, DROPPED: 25},
playing: [{appid: 220, name: 'Half-Life 2', playtimeMinutes: 300, status: 'PLAYING', rating: null}],
finished: [{appid: 620, name: 'Portal 2', playtimeMinutes: 900, status: 'COMPLETED', rating: 5}],
};
// `me` only for a test with a session: without one the page asks nothing about the viewer.
function mockBackends({name = 'Lobo', profileStatus = 200, libraryStatus = 200, style, answer = library, me, equipped = {}, rewardsStatus = 200, extra = {}} = {}) {
fetchRoutes({
[`GET /api/public/profiles/${encodeURIComponent(name)}`]:
json(profileStatus, {displayName: 'Lobo', memberSince: '2026-03-02', ticket: 'the-ticket', style}),
'GET /api/public/profiles/library': json(libraryStatus, answer),
'GET /api/public/profiles/rewards': json(rewardsStatus, {equipped}),
...extra,
...(me && {
'GET /api/users/me': json(200, me),
'PUT /api/users/settings/profile-tiles': json(204, {}),
'GET /api/follows': json(200, {following: [], followers: []}),
}),
});
}
const renderPage = (name = 'Lobo') => renderWithProviders(<PublicProfilePage/>, {path: '/u/:name', entries: [`/u/${name}`]});
describe('PublicProfilePage', () => {
afterEach(cleanup);
it('shows the coming badge on a game that is not released yet, in both lists', async () => {
mockBackends({answer: {...library,
playing: [{...library.playing[0], status: 'PLAYING', coming: true, comingOn: '2026-12-04'}],
finished: [{...library.finished[0], coming: false, comingOn: null}]}});
renderPage();
await screen.findByText('Portal 2');
expect(screen.getAllByText('LIBRARY.COMING')).toHaveLength(1);
expect(screen.getByText('Half-Life 2').closest('li').textContent).toContain('LIBRARY.COMING');
expect(screen.getByText('Portal 2').closest('li').textContent).not.toContain('LIBRARY.COMING');
});
it('wears the premium accent and the badge of the owner', async () => {
mockBackends({style: {accent: 'red'}, equipped: {ACCENT: 'ACCENT_GOLD', BADGE: 'BADGE_PILE_SLAYER', BANNER_FRAME: 'FRAME_GOLD'}});
renderPage();
expect(await screen.findByText('POINTS.ITEMS.BADGE_PILE_SLAYER')).toBeTruthy();
const wrapper = screen.getByText('Lobo').closest('[data-accent]');
expect(wrapper.dataset.accent).toBe('gold');
expect(wrapper.querySelector('[data-slot="card"]').className).toContain('ring-amber-400');
expect(fetch.mock.calls[2][1].headers['X-Profile-Ticket']).toBe('the-ticket');
});
it('wears the effects of the owner: background, banner and the cover of a COMPLETED game', async () => {
mockBackends({style: {accent: 'red'}, equipped: {
PAGE_BACKGROUND: 'BG_STARFIELD', BANNER_BACKGROUND: 'BN_WAVES', COMPLETED_EFFECT: 'FX_NEON_PULSE'}});
const {container} = renderPage();
expect(await screen.findByText('Portal 2')).toBeTruthy();
await waitFor(() => expect(container.querySelector('[data-effect="starfield"]')).not.toBeNull());
expect(screen.getByText('Lobo').closest('[data-slot="card"]').querySelector('[data-effect="waves"]')).not.toBeNull();
expect(screen.getByText('Portal 2').closest('li').querySelector('.fx-cover-neon-pulse')).not.toBeNull();
});
it('wears the skin of the owner on the wrapper with its own base layer, or resets to the plain theme', async () => {
mockBackends({style: {accent: 'red'}, equipped: {SKIN: 'SKIN_CYBERPUNK'}});
const {unmount} = renderPage();
expect(await screen.findByText('Portal 2')).toBeTruthy();
const wrapper = screen.getByText('Lobo').closest('[data-skin]');
expect(wrapper.dataset.skin).toBe('cyberpunk');
expect(wrapper.querySelector('.skin-bg')).not.toBeNull();
unmount();
cleanup();
mockBackends({style: {accent: 'red'}, equipped: {}});
renderPage();
expect(await screen.findByText('Portal 2')).toBeTruthy();
expect(screen.getByText('Lobo').closest('[data-skin]').dataset.skin).toBe('none');
});
it('still shows the profile when the looks call fails', async () => {
mockBackends({style: {accent: 'red'}, rewardsStatus: 500});
renderPage();
expect(await screen.findByText('Portal 2')).toBeTruthy();
expect(screen.getByText('Lobo').closest('[data-accent]').dataset.accent).toBe('red');
});
it('ignores a code the app does not know', async () => {
mockBackends({style: {accent: 'red'}, equipped: {ACCENT: 'ACCENT_X', BADGE: 'BADGE_X'}});
renderPage();
expect(await screen.findByText('Portal 2')).toBeTruthy();
expect(screen.getByText('Lobo').closest('[data-accent]').dataset.accent).toBe('red');
});
it('asks Auth for the ticket, then Core for the library with it', async () => {
mockBackends({name: 'Lo bo'});
renderPage('Lo bo');
expect(await screen.findByText('Portal 2')).toBeTruthy();
expect(fetch.mock.calls[0][0]).toMatch(/\/api\/public\/profiles\/Lo%20bo$/);
expect(fetch.mock.calls[1][1].headers['X-Profile-Ticket']).toBe('the-ticket');
expect(screen.getByText('Lobo')).toBeTruthy();
expect(screen.getByText('Half-Life 2')).toBeTruthy();
expect(screen.getAllByText('233')).toHaveLength(2);
expect(screen.queryByText('PROFILE.PERFECT_GAMES')).toBeNull();
});
it('shows perfect games and the average completion when there are achievements', async () => {
library.perfectGames = 12;
library.averageCompletion = 64.5;
try {
mockBackends();
renderPage();
expect(await screen.findByText('PROFILE.PERFECT_GAMES')).toBeTruthy();
expect(screen.getByText('12')).toBeTruthy();
expect(screen.getByText('64,5 %')).toBeTruthy();
} finally {
delete library.perfectGames;
delete library.averageCompletion;
}
});
it('shows only the stat groups the user shares, without links', async () => {
library.stats = {pile: null, trend: null, topGames: null, taste: null, finished: null,
habits: {moonTrips: 2.5, longestDay: null, underOneHourGames: 0, underOneHourShare: null}};
mockBackends({style: {tiles: [{id: 'STATS_HABITS', size: 'M'}, {id: 'STATS_PILE', size: 'M'}]}});
renderPage();
expect(await screen.findByText('STATS.HABITS.TITLE')).toBeTruthy();
expect(screen.queryByText('STATS.PILE.TITLE')).toBeNull();
expect(screen.queryByText('STATS.TOP_GAMES.TITLE')).toBeNull();
delete library.stats;
});
it('applies the owner\'s accent and corners to the page only, with banner and background', async () => {
mockBackends({style: {accent: 'red', corners: 'round', bannerAppid: 620, backgroundAppid: 220, tiles: [{id: 'PLAYING', size: 'M'}]}});
const {container} = renderPage();
await screen.findByText('Half-Life 2');
const page = container.querySelector('[data-accent="red"]');
expect(page.dataset.corners).toBe('round');
expect(document.documentElement.dataset.accent).not.toBe('red');
expect([...container.querySelectorAll('img')].map((img) => img.getAttribute('src')))
.toEqual(expect.arrayContaining([expect.stringContaining('/620/'), expect.stringContaining('/220/')]));
});
it('shows the sections in the owner\'s order and leaves out hidden ones', async () => {
mockBackends({
style: {tiles: [{id: 'FINISHED', size: 'M'}, {id: 'PLAYING', size: 'S'}, {id: 'SUMMARY', size: 'M'}]},
answer: {...library, byStatus: null, finishedGames: 60},
});
const {container} = renderPage();
await screen.findByText('Portal 2');
expect([...container.querySelectorAll('[data-tile]')].map((el) => el.dataset.tile))
.toEqual(['FINISHED', 'PLAYING', 'SUMMARY']);
expect(screen.getByText('60')).toBeTruthy();
expect(screen.queryByText('BACKLOG.STATUS.BACKLOG')).toBeNull();
});
it('leaves out the status bar of a profile without games', async () => {
mockBackends({answer: {...library, totalGames: 0, byStatus: {BACKLOG: 0, PLAYING: 0}, playing: [], finished: []}});
const {container} = renderPage();
await screen.findByText('PROFILE.GAMES');
expect([...container.querySelectorAll('[data-tile]')].map((el) => el.dataset.tile)).toEqual(['SUMMARY']);
});
it('shows every section for an Auth without a style', async () => {
mockBackends();
const {container} = renderPage();
await screen.findByText('Portal 2');
expect([...container.querySelectorAll('[data-tile]')].map((el) => el.dataset.tile))
.toEqual(['SUMMARY', 'STATUS', 'PLAYING', 'FINISHED']);
expect(container.querySelector('[data-accent="violet"]')).toBeTruthy();
});
it('shows the small playing tile with half the games and at half width', async () => {
const games = Array.from({length: 8}, (_, i) => ({appid: 100 + i, name: `Game ${i}`, playtimeMinutes: 60, status: 'PLAYING'}));
mockBackends({style: {tiles: [{id: 'PLAYING', size: 'S'}]}, answer: {...library, playing: games}});
const {container} = renderPage();
await screen.findByText('Game 0');
expect(screen.queryByText('Game 4')).toBeNull();
expect(container.querySelector('[data-tile="PLAYING"]').className).toContain('md:col-span-6');
});
const libraryLoads = () => fetch.mock.calls.filter(([url]) => url.includes('/api/public/profiles/library')).length;
const putBody = () => JSON.parse(fetch.mock.calls.find(([, init]) => init?.method === 'PUT')[1].body);
it('offers "Customize" to the owner and hides a tile without a new load', async () => {
localStorage.setItem('tbp_session', '1');
try {
mockBackends({me: {displayName: 'lobo', role: 'USER'}});
const {container} = renderPage();
fireEvent.click(await screen.findByRole('button', {name: 'TILES.CUSTOMIZE'}));
// The test texts carry no tile name; the first tile is SUMMARY.
fireEvent.click(screen.getAllByRole('button', {name: 'TILES.HIDE'})[0]);
fireEvent.click(screen.getByRole('button', {name: 'TILES.DONE'}));
expect(await screen.findByRole('button', {name: 'TILES.CUSTOMIZE'})).toBeTruthy();
expect(fetch.mock.calls.find(([, init]) => init?.method === 'PUT')[0]).toMatch(/\/api\/users\/settings\/profile-tiles$/);
expect(putBody().tiles.map((tile) => tile.id)).toEqual(['STATUS', 'PLAYING', 'FINISHED']);
expect([...container.querySelectorAll('[data-tile]')].map((el) => el.dataset.tile)).not.toContain('SUMMARY');
expect(libraryLoads()).toBe(1);
} finally {
localStorage.clear();
}
});
it('loads again after adding a tile, because Core sent no data for it', async () => {
localStorage.setItem('tbp_session', '1');
try {
mockBackends({me: {displayName: 'Lobo', role: 'USER'}});
renderPage();
fireEvent.click(await screen.findByRole('button', {name: 'TILES.CUSTOMIZE'}));
fireEvent.click(screen.getByRole('button', {name: 'STATS.PILE.TITLE'}));
fireEvent.click(screen.getByRole('button', {name: 'TILES.DONE'}));
await waitFor(() => expect(libraryLoads()).toBe(2));
expect(putBody().tiles.at(-1)).toEqual({id: 'STATS_PILE', size: 'M'});
} finally {
localStorage.clear();
}
});
it('offers "Follow" to a logged-in visitor, not to the owner and not to a guest', async () => {
localStorage.setItem('tbp_session', '1');
try {
mockBackends({me: {displayName: 'Someone', role: 'USER'}});
const visitor = renderPage();
expect(await screen.findByRole('button', {name: 'FOLLOW.FOLLOW'})).toBeTruthy();
visitor.unmount();
mockBackends({me: {displayName: 'Lobo', role: 'USER'}});
renderPage();
await screen.findByRole('button', {name: 'TILES.CUSTOMIZE'});
expect(screen.queryByRole('button', {name: 'FOLLOW.FOLLOW'})).toBeNull();
expect(fetch).not.toHaveBeenCalledWith(expect.stringContaining('/api/follows'), expect.anything());
} finally {
localStorage.clear();
}
});
it('offers no "Follow" to a guest', async () => {
mockBackends();
renderPage();
await screen.findByText('Portal 2');
expect(screen.queryByRole('button', {name: 'FOLLOW.FOLLOW'})).toBeNull();
expect(fetch).not.toHaveBeenCalledWith(expect.stringContaining('/api/follows'), expect.anything());
});
it('shows no "Customize" to a visitor', async () => {
localStorage.setItem('tbp_session', '1');
try {
mockBackends({me: {displayName: 'Someone', role: 'USER'}});
renderPage();
await screen.findByText('Portal 2');
await waitFor(() => expect(fetch).toHaveBeenCalledWith(expect.stringContaining('/api/users/me'), expect.anything()));
expect(screen.queryByRole('button', {name: 'TILES.CUSTOMIZE'})).toBeNull();
} finally {
localStorage.clear();
}
});
it('keeps three numbers in a row on a phone', async () => {
mockBackends();
const {container} = renderPage();
await screen.findByText('PROFILE.GAMES');
const summary = container.querySelector('[data-tile="SUMMARY"] [data-slot="card"]');
expect(summary.className.split(' ')).toContain('grid-cols-3');
});
it('says the profile is unknown or private on a 404', async () => {
mockBackends({profileStatus: 404});
renderPage();
expect(await screen.findByText('PROFILE.NOT_FOUND')).toBeTruthy();
expect(fetch).toHaveBeenCalledTimes(1);
});
it('shows an error when Core fails', async () => {
mockBackends({libraryStatus: 500});
renderPage();
await waitFor(() => expect(screen.getByText('PROFILE.ERROR_LOAD')).toBeTruthy());
});
it('opensTheGamesBehindAStatusCountWithTheTicket', async () => {
mockBackends({extra: {'GET /api/public/profiles/library/stats/games': json(200, {
content: [{appid: 1, name: 'Backlogged', status: 'BACKLOG', playtimeMinutes: 0}], page: 0, size: 40, totalElements: 1, totalPages: 1})}});
const {container} = renderPage();
fireEvent.click(await screen.findByRole('button', {name: /BACKLOG\.STATUS\.BACKLOG/}));
expect(await screen.findByText('Backlogged')).toBeTruthy();
const call = fetch.mock.calls.find(([url]) => url.includes('/stats/games'));
expect(call[0]).toContain('key=STATUS&arg=BACKLOG');
expect(call[1].headers['X-Profile-Ticket']).toBe('the-ticket');
expect(container.querySelector('a[href^="/library"]')).toBeNull();
});
it('rendersNoButtonForAGroupTheOwnerDoesNotShare', async () => {
library.stats = {pile: null, trend: null, topGames: null, taste: null, finished: null,
habits: {moonTrips: 2.5, longestDay: null, underOneHourGames: 0, underOneHourShare: null}};
try {
mockBackends({style: {tiles: [{id: 'STATS_HABITS', size: 'M'}, {id: 'STATS_PILE', size: 'M'}]}});
renderPage();
await screen.findByText('STATS.HABITS.TITLE');
expect(screen.queryByRole('button', {name: /STATS\.PILE/})).toBeNull();
} finally {
delete library.stats;
}
});
const gamesRoute = {'GET /api/public/profiles/library/stats/games': json(200, {
content: [{appid: 1, name: 'Backlogged', status: 'BACKLOG', playtimeMinutes: 0}], page: 0, size: 40, totalElements: 1, totalPages: 1})};
it('drawsTheStatusTileAsADonut', async () => {
mockBackends();
const {container} = renderPage();
await screen.findByText('Portal 2');
expect(container.querySelectorAll('[data-tile="STATUS"] path.stroke-card')).toHaveLength(5);
});
it('showsTheTotalInTheDonutAsTextWithoutTheSummarySection', async () => {
mockBackends({answer: {...library, totalGames: null}});
const {container} = renderPage();
await screen.findByText('Portal 2');
const tile = container.querySelector('[data-tile="STATUS"]');
expect(within(tile).getByText('233')).toBeTruthy();
expect(within(tile).queryByRole('button', {name: /STATS\.OVERVIEW\.GAMES/})).toBeNull();
});
it('opensAllGamesFromTheDonutTotalWhenTheSummaryIsShared', async () => {
mockBackends({extra: gamesRoute});
const {container} = renderPage();
await screen.findByText('Portal 2');
fireEvent.click(within(container.querySelector('[data-tile="STATUS"]')).getByRole('button', {name: 'STATS.OVERVIEW.GAMES: 233'}));
await screen.findByText('Backlogged');
expect(fetch.mock.calls.find(([url]) => url.includes('/stats/games'))[0]).toContain('key=TOTAL');
});
it('showsTheAverageCompletionAsAMeter', async () => {
mockBackends({answer: {...library, averageCompletion: 71.3, perfectGames: 2}});
renderPage();
const meter = await screen.findByRole('meter', {name: 'PROFILE.AVERAGE_COMPLETION'});
expect(meter.getAttribute('aria-valuetext')).toBe('71,3 %');
});
it('showsNoMeterWithoutAnAverageCompletion', async () => {
mockBackends();
renderPage();
await screen.findByText('PROFILE.GAMES');
expect(screen.queryByRole('meter')).toBeNull();
});
const topGamesStats = {pile: null, trend: null, taste: null, finished: null, habits: null,
topGames: {mostPlayed: [{appid: 620, name: 'Portal 2', imageUrl: null, minutes: 900}], mostLoyal: null}};
it('theOwnerOpensTheDetailPageFromTheList', async () => {
localStorage.setItem('tbp_session', '1');
try {
mockBackends({me: {displayName: 'lobo', role: 'USER'}, extra: gamesRoute});
renderPage();
fireEvent.click(await screen.findByRole('button', {name: /BACKLOG\.STATUS\.BACKLOG/}));
expect((await screen.findByRole('link', {name: /Backlogged/})).getAttribute('href')).toBe('/library/1');
} finally {
localStorage.clear();
}
});
it('theOwnerTileRowsOpenTheDetailPage', async () => {
localStorage.setItem('tbp_session', '1');
library.stats = topGamesStats;
try {
mockBackends({me: {displayName: 'lobo', role: 'USER'}, style: {tiles: [{id: 'STATS_TOP_GAMES', size: 'M'}]}});
renderPage();
expect((await screen.findByRole('link', {name: /Portal 2/})).getAttribute('href')).toBe('/library/620');
} finally {
delete library.stats;
localStorage.clear();
}
});
it('aVisitorSeesTileRowsWithoutLinks', async () => {
library.stats = topGamesStats;
try {
mockBackends({style: {tiles: [{id: 'STATS_TOP_GAMES', size: 'M'}]}});
const {container} = renderPage();
await screen.findByText('Portal 2');
expect(screen.queryByRole('link', {name: /Portal 2/})).toBeNull();
expect(container.querySelector('a[href*="steampowered"]')).toBeNull();
} finally {
delete library.stats;
}
});
const platformStats = {pile: null, trend: null, taste: null, finished: null, habits: null, topGames: null,
platforms: {perPlatform: [{name: 'Steam', games: 10, minutes: 5000}, {name: 'PS5', games: 2, minutes: 600}]}};
const platformTile = {tiles: [{id: 'STATS_PLATFORMS', size: 'M'}]};
const platformGames = {'GET /api/public/profiles/library/stats/games': json(200, {
content: [{appid: 7, name: 'Hades', status: 'BACKLOG', playtimeMinutes: 0}], page: 0, size: 40, totalElements: 1, totalPages: 1})};
it('showsTheOwnerChartFormWithoutAPicker', async () => {
library.stats = platformStats;
try {
mockBackends({style: {...platformTile, statCharts: {PLATFORMS: 'DONUT'}}});
const {container} = renderPage();
expect(await screen.findByText('Steam')).toBeTruthy();
expect(container.querySelectorAll('path.stroke-card')).toHaveLength(2);
expect(screen.queryByRole('group', {name: 'STATS.CHART.LABEL'})).toBeNull();
} finally {
delete library.stats;
}
});
const tasteStats = {pile: null, trend: null, finished: null, habits: null, topGames: null, platforms: null,
taste: {genres: [{name: 'Shooter', games: 2, minutes: 1200}, {name: 'Puzzle', games: 1, minutes: 300}], moods: []}};
it('theOwnerGetsThePickerOnATasteTile', async () => {
localStorage.setItem('tbp_session', '1');
library.stats = tasteStats;
try {
mockBackends({me: {displayName: 'lobo', role: 'USER', settings: {}}, style: {tiles: [{id: 'STATS_TASTE', size: 'M'}]},
extra: {'PUT /api/users/settings/stat-chart': json(204, {})}});
const {container} = renderPage();
fireEvent.click(await screen.findByRole('button', {name: 'STATS.CHART.DONUT'}));
await waitFor(() => expect(container.querySelectorAll('path.stroke-card').length).toBeGreaterThan(0));
const bodies = fetch.mock.calls.filter(([, init]) => init?.method === 'PUT').map(([, init]) => JSON.parse(init.body));
expect(bodies).toEqual([{group: 'TASTE', chart: 'DONUT'}]);
} finally {
delete library.stats;
localStorage.clear();
}
});
const twoGameStats = {...topGamesStats, topGames: {mostLoyal: null, mostPlayed: [
{appid: 620, name: 'Portal 2', imageUrl: null, minutes: 900}, {appid: 220, name: 'Half-Life 2', imageUrl: null, minutes: 300}]}};
it('topGamesPieLinksForTheOwnerAndIsPlainForAVisitor', async () => {
localStorage.setItem('tbp_session', '1');
library.stats = twoGameStats;
try {
const style = {tiles: [{id: 'STATS_TOP_GAMES', size: 'M'}], statCharts: {TOP_GAMES: 'PIE'}};
mockBackends({me: {displayName: 'lobo', role: 'USER', settings: {statCharts: {TOP_GAMES: 'PIE'}}}, style});
const {unmount} = renderPage();
expect((await screen.findByRole('link', {name: /Portal 2/})).getAttribute('href')).toBe('/library/620');
unmount();
cleanup();
localStorage.clear();
mockBackends({style});
renderPage();
await screen.findByText('Portal 2');
expect(screen.queryByRole('link', {name: /Portal 2/})).toBeNull();
expect(screen.queryByRole('button', {name: /Portal 2/})).toBeNull();
} finally {
delete library.stats;
localStorage.clear();
}
});
it('showsBarsForAFormTheProfileDoesNotKnow', async () => {
library.stats = platformStats;
try {
mockBackends({style: {...platformTile, statCharts: {PLATFORMS: 'RADAR'}}});
const {container} = renderPage();
expect(await screen.findByText('Steam')).toBeTruthy();
expect(container.querySelector('path.stroke-card')).toBeNull();
} finally {
delete library.stats;
}
});
it('aChartLegendRowOfTheOwnerOpensTheDetailPage', async () => {
localStorage.setItem('tbp_session', '1');
library.stats = platformStats;
try {
mockBackends({me: {displayName: 'lobo', role: 'USER'}, style: {...platformTile, statCharts: {PLATFORMS: 'PIE'}},
extra: platformGames});
const {container} = renderPage();
await waitFor(() => expect(container.querySelectorAll('path.stroke-card')).toHaveLength(2));
fireEvent.click(await screen.findByRole('button', {name: /Steam/}));
expect((await screen.findByRole('link', {name: /Hades/})).getAttribute('href')).toBe('/library/7');
} finally {
delete library.stats;
localStorage.clear();
}
});
it('aChartLegendRowOfAVisitorShowsAPlainGameRow', async () => {
library.stats = platformStats;
try {
mockBackends({style: {...platformTile, statCharts: {PLATFORMS: 'PIE'}}, extra: platformGames});
const {container} = renderPage();
await waitFor(() => expect(container.querySelectorAll('path.stroke-card')).toHaveLength(2));
fireEvent.click(await screen.findByRole('button', {name: /Steam/}));
expect(await screen.findByText('Hades')).toBeTruthy();
expect(screen.queryByRole('link', {name: /Hades/})).toBeNull();
} finally {
delete library.stats;
}
});
it('opensTheGamesBehindPerfectGames', async () => {
library.perfectGames = 12;
library.averageCompletion = 64.5;
try {
mockBackends({extra: gamesRoute});
renderPage();
fireEvent.click(await screen.findByRole('button', {name: /PROFILE\.PERFECT_GAMES: 12/}));
await screen.findByText('Backlogged');
expect(fetch.mock.calls.find(([url]) => url.includes('/stats/games'))[0]).toContain('key=PERFECT');
} finally {
delete library.perfectGames;
delete library.averageCompletion;
}
});
});