fix(settings): keep the language and save settings one after the other

This commit is contained in:
lobo committed 2026-09-29 13:27:31 +02:00
1 parent 14dd59f9e0
commit f605917874
25 files changed
+423 -98

No files matched your search

+61 -1
View File
@@ -3,7 +3,11 @@ import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest';
import {act, cleanup, render} from '@testing-library/react';
import {PreferencesProvider, usePreferences} from './PreferencesContext.jsx';
vi.mock('react-i18next', () => import('../test/i18n-mock.js'));
const i18n = vi.hoisted(() => ({language: 'de'}));
vi.mock('react-i18next', async () => {
const mock = await import('../test/i18n-mock.js');
return {...mock, useTranslation: () => ({t: mock.useTranslation().t, i18n})};
});
let preferences;
function Spy() {
@@ -17,11 +21,67 @@ beforeEach(() => {
document.head.innerHTML = '<meta name="theme-color" content="#09090b">';
});
beforeEach(() => {
i18n.language = 'de';
i18n.changeLanguage = vi.fn((lng) => { i18n.language = lng; });
});
afterEach(() => {
cleanup();
localStorage.clear();
});
const answerMe = (settings) => {
globalThis.fetch = vi.fn(async (url, init) => (init?.method === 'PUT'
? {ok: true, status: 204}
: {ok: true, status: 200, json: async () => ({displayName: 'Lobo', settings})}));
};
describe('PreferencesProvider language', () => {
it('takes the language saved in Auth', async () => {
answerMe({language: 'en', useDarkTheme: true, publicProfile: false});
render(<PreferencesProvider><Spy/></PreferencesProvider>);
await act(() => preferences.syncPreferences());
expect(i18n.changeLanguage).toHaveBeenCalledWith('en');
});
it('keeps its own language while Auth has none', async () => {
answerMe({language: '', useDarkTheme: true, publicProfile: false});
render(<PreferencesProvider><Spy/></PreferencesProvider>);
await act(() => preferences.syncPreferences());
expect(i18n.changeLanguage).not.toHaveBeenCalled();
});
it('keeps a language picked while Auth was still answering', async () => {
answerMe({language: 'de', useDarkTheme: true, publicProfile: false});
render(<PreferencesProvider><Spy/></PreferencesProvider>);
await act(async () => {
const sync = preferences.syncPreferences();
preferences.setLanguage('en');
await sync;
});
expect(i18n.changeLanguage.mock.calls).toEqual([['en']]);
});
it('saves a picked language to Auth with the other settings unchanged', async () => {
localStorage.setItem('tbp_session', '1');
answerMe({language: 'de', useDarkTheme: true, publicProfile: true});
render(<PreferencesProvider><Spy/></PreferencesProvider>);
await act(() => preferences.setLanguage('en'));
const put = fetch.mock.calls.find(([, init]) => init?.method === 'PUT');
expect(put[0]).toMatch(/\/api\/users\/settings$/);
expect(JSON.parse(put[1].body)).toEqual({useDarkTheme: true, language: 'en', displayName: 'Lobo', publicProfile: true});
});
});
describe('PreferencesProvider theme color', () => {
it('gives the installed app a light title bar in light mode and a dark one again in dark mode', () => {
localStorage.setItem('tbp_dark_mode', 'false');