Public Access
feat(settings): link the Steam account through Steam
A Steam card in the settings links, verifies or unlinks the Steam account through "Sign in through Steam". After the way back the page shows the outcome and refreshes the session, so Core gets the new Steam ID at once. Registration and settings no longer ask for a typed Steam ID.
This commit is contained in:
1 parent
23ab5a91e7
commit
ca75e6a530
13 files changed
+393
-86
No files matched your search
@@ -0,0 +1,114 @@
|
||||
// @vitest-environment jsdom
|
||||
import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest';
|
||||
import {cleanup, fireEvent, render, screen, waitFor} from '@testing-library/react';
|
||||
import {MemoryRouter} from 'react-router-dom';
|
||||
import {toast} from 'sonner';
|
||||
import SettingsPage from './SettingsPage.jsx';
|
||||
import {PreferencesProvider} from '../context/PreferencesContext.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);
|
||||
|
||||
function renderPage(me, {redirectUrl = STEAM_URL, entry = '/settings'} = {}) {
|
||||
globalThis.fetch = vi.fn(async (url, init) => {
|
||||
if (init?.method === 'POST' && url.endsWith('/api/users/steam-link')) {
|
||||
return {ok: true, status: 200, json: async () => ({redirectUrl})};
|
||||
}
|
||||
if (init?.method && init.method !== 'GET') {
|
||||
return {ok: true, status: 204, json: async () => { throw new SyntaxError('no body'); }};
|
||||
}
|
||||
return {ok: true, status: 200, json: async () => me};
|
||||
});
|
||||
render(<MemoryRouter initialEntries={[entry]}><PreferencesProvider><SettingsPage/></PreferencesProvider></MemoryRouter>);
|
||||
}
|
||||
|
||||
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');
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user