Public Access
Add /points page with wallet balance, shop grouped by slot and ledger history with game names. The wallet loads once in PreferencesContext after login, so the header shows a balance chip and the link. Premium accents gold and teal override the free accent while equipped. German and English.
112 lines
3.9 KiB
React
112 lines
3.9 KiB
React
// @vitest-environment jsdom
|
|
import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest';
|
|
import {act, cleanup, render} from '@testing-library/react';
|
|
import {PreferencesProvider, usePreferences} from './PreferencesContext.jsx';
|
|
import {fetchRoutes, json} from '../test/fetchRoutes.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() {
|
|
preferences = usePreferences();
|
|
return null;
|
|
}
|
|
|
|
const themeColor = () => document.querySelector('meta[name="theme-color"]').content;
|
|
|
|
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) => fetchRoutes({
|
|
'GET /api/users/me': json(200, {displayName: 'Lobo', settings}),
|
|
'PUT /api/users/settings': json(204),
|
|
'GET /api/rewards/wallet': json(200, {balance: 0, catalog: [], owned: [], equipped: {}, history: []}),
|
|
});
|
|
|
|
describe('PreferencesProvider storage', () => {
|
|
it('renders in dark mode when localStorage throws', () => {
|
|
['getItem', 'setItem'].forEach((method) => vi.spyOn(Storage.prototype, method).mockImplementation(() => {
|
|
throw new DOMException('blocked', 'SecurityError');
|
|
}));
|
|
try {
|
|
render(<PreferencesProvider><Spy/></PreferencesProvider>);
|
|
|
|
expect(preferences.isDarkMode).toBe(true);
|
|
} finally {
|
|
vi.restoreAllMocks();
|
|
}
|
|
});
|
|
});
|
|
|
|
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');
|
|
render(<PreferencesProvider><Spy/></PreferencesProvider>);
|
|
|
|
expect(themeColor()).toBe('#fafafa');
|
|
|
|
act(() => preferences.setDarkMode(true));
|
|
expect(themeColor()).toBe('#09090b');
|
|
});
|
|
});
|