Compare commits

..
2 Commits
Author SHA1 Message Date
lobo b8f70e7578 feat(registration): send the browser language
Build, Test & Push Frontend / docker (push) Successful in 25s
Build, Test & Push Frontend / quality-check (push) Successful in 22s
2026-10-01 08:30:24 +02:00
lobo 43968b3b7a test(import): wait for the URL without the store list 2026-10-01 08:30:24 +02:00
4 changed files with 19 additions and 3 deletions

No files matched your search

+2 -1
View File
@@ -83,7 +83,8 @@ To Be Played is split into three repositories:
- **Appearance**: accent color, corner style, wide layout, page background and banner from your own game art. - **Appearance**: accent color, corner style, wide layout, page background and banner from your own game art.
Saved in Auth, so it follows you to every device. Saved in Auth, so it follows you to every device.
- **Language**: kept in the browser and saved in Auth; after the login Auth's language wins when one is saved. Dark - **Language**: kept in the browser and saved in Auth; after the login Auth's language wins when one is saved. Dark
mode stays per device. mode stays per device. The registration sends the browser language (`de` for a German browser, else `en`): Auth
writes its mails in it and saves it as the user's language.
- **Broken pictures**: a Steam cover or achievement icon that does not load shows a placeholder and is reported - **Broken pictures**: a Steam cover or achievement icon that does not load shows a placeholder and is reported
to Core once per page load (`POST /api/library/broken-images`, only with a session). Core repairs it at night; to Core once per page load (`POST /api/library/broken-images`, only with a session). Core repairs it at night;
a repaired cover comes back as `imageUrl`. a repaired cover comes back as `imageUrl`.
+3 -2
View File
@@ -171,7 +171,8 @@ describe('ImportPage', () => {
expect(await screen.findByText('IMPORT.STORE_READ:2')).toBeTruthy(); expect(await screen.findByText('IMPORT.STORE_READ:2')).toBeTruthy();
expect(screen.getByText('Pokémon Scarlet · Switch')).toBeTruthy(); expect(screen.getByText('Pokémon Scarlet · Switch')).toBeTruthy();
expect(screen.getByLabelText('url').textContent).toBe('/import'); // The router takes the new URL in a later render than the rows.
await waitFor(() => expect(screen.getByLabelText('url').textContent).toBe('/import'));
fireEvent.click(screen.getByRole('button', {name: 'IMPORT.START'})); fireEvent.click(screen.getByRole('button', {name: 'IMPORT.START'}));
await screen.findByText('IMPORT.MATCHING'); await screen.findByText('IMPORT.MATCHING');
@@ -185,7 +186,7 @@ describe('ImportPage', () => {
renderPage({'GET ': json(200, {imports: []})}, '/import#data=broken'); renderPage({'GET ': json(200, {imports: []})}, '/import#data=broken');
await waitFor(() => expect(toast.error).toHaveBeenCalledWith('IMPORT.STORE_LIST_BROKEN')); await waitFor(() => expect(toast.error).toHaveBeenCalledWith('IMPORT.STORE_LIST_BROKEN'));
expect(screen.getByLabelText('url').textContent).toBe('/import'); await waitFor(() => expect(screen.getByLabelText('url').textContent).toBe('/import'));
expect(await screen.findByLabelText('IMPORT.TEXT_LABEL')).toBeTruthy(); expect(await screen.findByLabelText('IMPORT.TEXT_LABEL')).toBeTruthy();
}); });
+2
View File
@@ -54,6 +54,8 @@ export default function RegistrationPage() {
password: values.password, password: values.password,
inviteCode: values.inviteCode, inviteCode: values.inviteCode,
displayName: values.displayName, displayName: values.displayName,
// Auth's mails use it: a German browser gets German, every other one English.
language: navigator.language?.toLowerCase().startsWith('de') ? 'de' : 'en',
}), }),
}); });
+12
View File
@@ -30,6 +30,7 @@ function submitValidForm() {
describe('RegistrationPage', () => { describe('RegistrationPage', () => {
afterEach(() => { afterEach(() => {
cleanup(); cleanup();
vi.restoreAllMocks();
}); });
it('shows the wait time when the IP is blocked', async () => { it('shows the wait time when the IP is blocked', async () => {
@@ -50,6 +51,17 @@ describe('RegistrationPage', () => {
expect(JSON.parse(fetch.mock.calls[0][1].body)).not.toHaveProperty('steamId'); expect(JSON.parse(fetch.mock.calls[0][1].body)).not.toHaveProperty('steamId');
}); });
it.each([['de-DE', 'de'], ['de', 'de'], ['en-US', 'en'], ['fr-FR', 'en'], [undefined, 'en']])(
'sends the browser language %s as %s, for the mails', async (browser, sent) => {
vi.spyOn(navigator, 'language', 'get').mockReturnValue(browser);
answerRegistration(400, 'INVALID_INVITATION_CODE');
submitValidForm();
await screen.findByText('REGISTRATION.ERROR_INVALID_INVITE_CODE');
expect(JSON.parse(fetch.mock.calls[0][1].body).language).toBe(sent);
});
it('maps a known backend error to its message', async () => { it('maps a known backend error to its message', async () => {
answerRegistration(400, 'INVALID_INVITATION_CODE'); answerRegistration(400, 'INVALID_INVITATION_CODE');