feat(games): add coming badge for unreleased games
Build, Test & Push Frontend / quality-check (push) Successful in 32s
Build, Test & Push Frontend / docker (push) Successful in 22s

This commit is contained in:
lobo committed 2026-10-05 20:33:25 +02:00
1 parent edeea5a43d
commit c158909083
17 files changed
+169 -3

No files matched your search

+17
View File
@@ -38,6 +38,23 @@ afterEach(() => {
});
describe('GameDetail', () => {
it('shows the coming badge of a game that is not released yet', async () => {
answer({...game, source: 'STEAM', coming: true, comingOn: '2026-12-04'});
renderDetail();
expect(await screen.findByText('LIBRARY.COMING')).toBeTruthy();
});
it('shows no coming badge for a released game', async () => {
answer({...game, source: 'STEAM', coming: false, comingOn: null});
renderDetail();
await screen.findByText('Half-Life 2');
expect(screen.queryByText('LIBRARY.COMING')).toBeNull();
expect(screen.queryByText('LIBRARY.COMING_SOON')).toBeNull();
});
it('asks "finished?" instead of "playing?" when Core has a finished hint', async () => {
answer({...game, playtime2Weeks: 90, source: 'STEAM', finishedHint: {type: 'ENDING', achievement: 'Survivor'}});
renderDetail();
+2
View File
@@ -19,6 +19,7 @@ import AddGamePanel from '../components/AddGamePanel.jsx';
import GameCover from '../components/GameCover.jsx';
import FitText from '../components/FitText.jsx';
import LibraryTabs from '../components/LibraryTabs.jsx';
import ComingBadge from '../components/ComingBadge.jsx';
import GameStatusBadge from '../components/GameStatusBadge.jsx';
import PageContainer from '../components/PageContainer.jsx';
import {usePreferences} from '../context/PreferencesContext.jsx';
@@ -316,6 +317,7 @@ export default function GameLibrary() {
<Layers className="size-3"/>x{g.editions}
</Badge>
)}
<ComingBadge game={g} compact/>
<GameStatusBadge status={g.status} compact/>
{g.achievementsTotal > 0 && g.achievementsUnlocked != null && (
<Badge variant="secondary" className="hidden sm:inline-flex tabular-nums"
+14
View File
@@ -62,6 +62,20 @@ afterEach(() => {
});
describe('GameLibrary search', () => {
it('shows the coming badge only for a game that is not released yet', async () => {
answer([
{...page.content[0], coming: true, comingOn: '2026-12-04'},
{appid: 620, name: 'Portal 2', playtimeMinutes: 0, status: 'BACKLOG', coming: false, comingOn: null},
]);
renderLibrary();
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('shows the achievement progress of a game when it is known', async () => {
answer([
{...page.content[0], achievementsUnlocked: 3, achievementsTotal: 4},
+3
View File
@@ -9,6 +9,7 @@ import {Skeleton} from '@/components/ui/skeleton';
import {cn} from '@/lib/utils';
import GameCover from '../components/GameCover.jsx';
import FitText from '../components/FitText.jsx';
import ComingBadge from '../components/ComingBadge.jsx';
import GameStatusBadge from '../components/GameStatusBadge.jsx';
import {formatDay} from '../utils/format.js';
import {backgroundEffect, bannerEffect, coverEffect, equippedAccent, equippedSkin, lookClass} from '../utils/rewards.js';
@@ -242,6 +243,7 @@ function Playing({games, compact}) {
<GameCover appid={g.appid} imageUrl={g.imageUrl} className="w-full"/>
<div className="@container text-sm font-medium"><FitText text={g.name ?? `App ${g.appid}`}/></div>
<div className="text-xs text-muted-foreground tabular-nums">{formatPlaytime(g.playtimeMinutes)}</div>
<ComingBadge game={g}/>
</li>
))}
</ul>
@@ -269,6 +271,7 @@ function Finished({games, equipped}) {
))}
</span>
)}
<ComingBadge game={g} compact/>
<GameStatusBadge status={g.status} compact/>
</li>
))}
+13
View File
@@ -34,6 +34,19 @@ const renderPage = (name = 'Lobo') => renderWithProviders(<PublicProfilePage/>,
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();