Public Access
feat(profile): public profile page and the switch in settings
/u/<name> shows a public profile without login. Settings get the switch and the link; every settings save now sends the other saved values, and removing the display name also makes the profile private.
This commit is contained in:
1 parent
aa5124ea1e
commit
fd343da4be
7 files changed
+418
-18
No files matched your search
@@ -0,0 +1,61 @@
|
||||
// @vitest-environment jsdom
|
||||
import {afterEach, describe, expect, it, vi} from 'vitest';
|
||||
import {cleanup, render, screen, waitFor} from '@testing-library/react';
|
||||
import {MemoryRouter, Route, Routes} from 'react-router-dom';
|
||||
import PublicProfilePage from './PublicProfilePage.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}],
|
||||
};
|
||||
|
||||
function mockBackends({profileStatus = 200, libraryStatus = 200} = {}) {
|
||||
globalThis.fetch = vi.fn(async (url) => {
|
||||
if (url.includes('/api/public/profiles/library')) {
|
||||
return {ok: libraryStatus === 200, status: libraryStatus, json: async () => library};
|
||||
}
|
||||
return {ok: profileStatus === 200, status: profileStatus,
|
||||
json: async () => ({displayName: 'Lobo', memberSince: '2026-03-02', ticket: 'the-ticket'})};
|
||||
});
|
||||
}
|
||||
|
||||
const renderPage = (name = 'Lobo') => render(
|
||||
<MemoryRouter initialEntries={[`/u/${name}`]}>
|
||||
<Routes><Route path="/u/:name" element={<PublicProfilePage/>}/></Routes>
|
||||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
describe('PublicProfilePage', () => {
|
||||
afterEach(cleanup);
|
||||
|
||||
it('asks Auth for the ticket, then Core for the library with it', async () => {
|
||||
mockBackends();
|
||||
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.getByText('233')).toBeTruthy();
|
||||
});
|
||||
|
||||
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());
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user