Files
ToBePlayed-Frontend/src/pages/SettingsPage.steam.test.jsx
T
lobo 599b083591
Build, Test & Push Frontend / quality-check (push) Successful in 34s
Build, Test & Push Frontend / docker (push) Successful in 22s
feat(psn): add PlayStation link card and PSN import
Settings card to link and unlink with the NPSSO, shows the expiry
date. Import page gets a PSN source; blocked when the link has ended.
English and German texts.
2026-10-07 13:30:48 +02:00

118 lines
5.2 KiB
React

// @vitest-environment jsdom
import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest';
import {cleanup, fireEvent, screen, waitFor} from '@testing-library/react';
import {toast} from 'sonner';
import SettingsPage from './SettingsPage.jsx';
import {fetchRoutes, json} from '../test/fetchRoutes.js';
import {renderWithProviders} from '../test/renderWithProviders.jsx';
vi.mock('react-i18next', async () => {
const mock = await import('../test/i18n-mock.js');
const i18n = {language: 'de', changeLanguage: vi.fn()};
return {...mock, useTranslation: () => ({t: mock.useTranslation().t, i18n})};
});
vi.mock('sonner', () => ({toast: {success: vi.fn(), error: vi.fn(), info: vi.fn()}}));
const STEAM_URL = 'https://steamcommunity.com/openid/login?openid.mode=checkid_setup';
const profile = (overrides = {}) => ({
id: 1, email: 'a@b.de', role: 'USER', displayName: 'Lobo', steamId: '76561198005679788', steamIdVerified: false,
settings: {useDarkTheme: true, language: 'de', publicProfile: false}, ...overrides,
});
const calls = (method) => fetch.mock.calls.filter(([, init]) => init?.method === method);
const noBody = {ok: true, status: 204, json: async () => { throw new SyntaxError('no body'); }};
function renderPage(me, {redirectUrl = STEAM_URL, entry = '/settings'} = {}) {
fetchRoutes({
'GET /api/users/me': json(200, me),
'POST /api/users/steam-link': json(200, {redirectUrl}),
'DELETE /api/users/steam-link': noBody,
'PUT /api/users/settings': noBody,
'POST /api/auth/refresh': noBody,
'GET /api/version': json(404, {}),
'GET /api/follows': json(200, {following: [], followers: []}),
'GET /api/library/psn-account': json(200, {enabled: false, linked: false}),
});
renderWithProviders(<SettingsPage/>, {path: '/settings', entries: [entry]});
}
describe('SettingsPage Steam account', () => {
const assign = vi.fn();
const realLocation = window.location;
beforeEach(() => {
localStorage.setItem('tbp_session', '1');
Object.defineProperty(window, 'location', {configurable: true, value: {...realLocation, assign}});
});
afterEach(() => {
cleanup();
localStorage.clear();
vi.clearAllMocks();
Object.defineProperty(window, 'location', {configurable: true, value: realLocation});
});
it('shows a typed-in ID as not verified and sends the user to Steam to verify it', async () => {
renderPage(profile());
expect(await screen.findByText('SETTINGS.STEAM_LINK.NOT_VERIFIED')).toBeTruthy();
fireEvent.click(screen.getByRole('button', {name: /SETTINGS.STEAM_LINK.VERIFY/}));
await waitFor(() => expect(assign).toHaveBeenCalledWith(STEAM_URL));
expect(calls('POST').map(([url]) => url).some((url) => url.endsWith('/api/users/steam-link'))).toBe(true);
});
it('does not follow a link that is not Steam', async () => {
renderPage(profile({steamId: null}), {redirectUrl: 'https://evil.example/openid/login?x'});
fireEvent.click(await screen.findByRole('button', {name: /SETTINGS.STEAM_LINK.LINK/}));
await waitFor(() => expect(toast.error).toHaveBeenCalledWith('SETTINGS.STEAM_LINK.ERROR'));
expect(assign).not.toHaveBeenCalled();
});
it('shows a verified ID with the option to link another account', async () => {
renderPage(profile({steamIdVerified: true}));
expect(await screen.findByText('SETTINGS.STEAM_LINK.VERIFIED')).toBeTruthy();
expect(screen.getByRole('button', {name: /SETTINGS.STEAM_LINK.CHANGE/})).toBeTruthy();
});
it('back from Steam: refreshes the session so Core gets the new Steam ID, and says so', async () => {
renderPage(profile({steamIdVerified: true}), {entry: '/settings?steam=linked'});
await screen.findByText('SETTINGS.STEAM_LINK.VERIFIED');
expect(calls('POST').some(([url]) => url.endsWith('/api/auth/refresh'))).toBe(true);
expect(toast.success).toHaveBeenCalledWith('SETTINGS.STEAM_LINK.LINKED');
});
it.each([['in-use', 'error', 'SETTINGS.STEAM_LINK.IN_USE'], ['failed', 'error', 'SETTINGS.STEAM_LINK.FAILED'],
['cancelled', 'info', 'SETTINGS.STEAM_LINK.CANCELLED']])('back from Steam with %s', async (outcome, kind, key) => {
renderPage(profile(), {entry: `/settings?steam=${outcome}`});
await screen.findByText('SETTINGS.STEAM_LINK.NOT_VERIFIED');
expect(toast[kind]).toHaveBeenCalledWith(key);
expect(calls('POST').some(([url]) => url.endsWith('/api/auth/refresh'))).toBe(false);
});
it('unlinks the Steam account', async () => {
renderPage(profile({steamIdVerified: true}));
fireEvent.click(await screen.findByRole('button', {name: /SETTINGS.STEAM_LINK.UNLINK/}));
expect(await screen.findByText('SETTINGS.STEAM_LINK.NONE')).toBeTruthy();
expect(calls('DELETE')[0][0]).toMatch(/\/api\/users\/steam-link$/);
});
it('never sends a Steam ID with the settings', async () => {
renderPage(profile());
fireEvent.click(await screen.findByRole('switch', {name: 'SETTINGS.DARK_THEME'}));
await waitFor(() => expect(calls('PUT')).toHaveLength(1));
expect(JSON.parse(calls('PUT')[0][1].body)).not.toHaveProperty('steamId');
});
});