Skip to content

Commit d6d1cd7

Browse files
committed
feat(i18n): update user preferences function
1 parent 837ef56 commit d6d1cd7

File tree

4 files changed

+38
-32
lines changed

4 files changed

+38
-32
lines changed

src/i18n/languageApi.js

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,31 @@
11
import { getConfig } from '../config';
2-
import { getAuthenticatedHttpClient } from '../auth';
2+
import { getAuthenticatedHttpClient, getAuthenticatedUser } from '../auth';
33
import { convertKeyNames, snakeCaseObject } from '../utils';
44

55
/**
66
* Updates user language preferences via the preferences API.
77
*
8-
* This function converts preference data to snake_case and formats specific keys
9-
* according to backend requirements before sending the PATCH request.
8+
* This function gets the authenticated user, converts preference data to snake_case
9+
* and formats specific keys according to backend requirements before sending the PATCH request.
10+
* If no user is authenticated, the function returns early without making the API call.
1011
*
11-
* @param {string} username - The username of the user whose preferences to update.
1212
* @param {Object} preferenceData - The preference parameters to update (e.g., { prefLang: 'en' }).
1313
* @returns {Promise} - A promise that resolves when the API call completes successfully,
14-
* or rejects if there's an error with the request.
14+
* or rejects if there's an error with the request. Returns early if no user is authenticated.
1515
*/
16-
export async function updateUserPreferences(username, preferenceData) {
16+
export async function updateAuthenticatedUserPreferences(preferenceData) {
17+
const user = getAuthenticatedUser();
18+
if (!user) {
19+
return Promise.resolve();
20+
}
21+
1722
const snakeCaseData = snakeCaseObject(preferenceData);
1823
const formattedData = convertKeyNames(snakeCaseData, {
1924
pref_lang: 'pref-lang',
2025
});
2126

2227
return getAuthenticatedHttpClient().patch(
23-
`${getConfig().LMS_BASE_URL}/api/user/v1/preferences/${username}`,
28+
`${getConfig().LMS_BASE_URL}/api/user/v1/preferences/${user.username}`,
2429
formattedData,
2530
{ headers: { 'Content-Type': 'application/merge-patch+json' } },
2631
);

src/i18n/languageApi.test.js

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { updateUserPreferences, setSessionLanguage } from './languageApi';
1+
import { updateAuthenticatedUserPreferences, setSessionLanguage } from './languageApi';
22
import { getConfig } from '../config';
3-
import { getAuthenticatedHttpClient } from '../auth';
3+
import { getAuthenticatedHttpClient, getAuthenticatedUser } from '../auth';
44

55
jest.mock('../config');
66
jest.mock('../auth');
@@ -11,21 +11,32 @@ describe('languageApi', () => {
1111
beforeEach(() => {
1212
jest.clearAllMocks();
1313
getConfig.mockReturnValue({ LMS_BASE_URL });
14+
getAuthenticatedUser.mockReturnValue({ username: 'testuser', userId: '123' });
1415
});
1516

16-
describe('updateUserPreferences', () => {
17+
describe('updateAuthenticatedUserPreferences', () => {
1718
it('should send a PATCH request with correct data', async () => {
1819
const patchMock = jest.fn().mockResolvedValue({});
1920
getAuthenticatedHttpClient.mockReturnValue({ patch: patchMock });
2021

21-
await updateUserPreferences('user1', { prefLang: 'es' });
22+
await updateAuthenticatedUserPreferences({ prefLang: 'es' });
2223

2324
expect(patchMock).toHaveBeenCalledWith(
24-
`${LMS_BASE_URL}/api/user/v1/preferences/user1`,
25+
`${LMS_BASE_URL}/api/user/v1/preferences/testuser`,
2526
expect.any(Object),
2627
expect.objectContaining({ headers: expect.any(Object) }),
2728
);
2829
});
30+
31+
it('should return early if no authenticated user', async () => {
32+
const patchMock = jest.fn().mockResolvedValue({});
33+
getAuthenticatedHttpClient.mockReturnValue({ patch: patchMock });
34+
getAuthenticatedUser.mockReturnValue(null);
35+
36+
await updateAuthenticatedUserPreferences({ prefLang: 'es' });
37+
38+
expect(patchMock).not.toHaveBeenCalled();
39+
});
2940
});
3041

3142
describe('setSessionLanguage', () => {

src/i18n/languageManager.js

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import { getConfig } from '../config';
2-
import { getAuthenticatedUser } from '../auth';
32
import { getCookies, handleRtl, LOCALE_CHANGED } from './lib';
43
import { publish } from '../pubSub';
54
import { logError } from '../logging';
6-
import { updateUserPreferences, setSessionLanguage } from './languageApi';
5+
import { updateAuthenticatedUserPreferences, setSessionLanguage } from './languageApi';
76

87
/**
98
* Changes the user's language preference and applies it to the current session.
@@ -29,11 +28,7 @@ export async function changeUserSessionLanguage(
2928
cookies.set(cookieName, languageCode);
3029

3130
try {
32-
const user = getAuthenticatedUser();
33-
if (user) {
34-
await updateUserPreferences(user.username, { prefLang: languageCode });
35-
}
36-
31+
await updateAuthenticatedUserPreferences({ prefLang: languageCode });
3732
await setSessionLanguage(languageCode);
3833
handleRtl(languageCode);
3934
publish(LOCALE_CHANGED, languageCode);

src/i18n/languageManager.test.js

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
import { changeUserSessionLanguage } from './languageManager';
22
import { getConfig } from '../config';
3-
import { getAuthenticatedUser } from '../auth';
43
import { getCookies, handleRtl, LOCALE_CHANGED } from './lib';
54
import { logError } from '../logging';
65
import { publish } from '../pubSub';
7-
import { updateUserPreferences, setSessionLanguage } from './languageApi';
6+
import { updateAuthenticatedUserPreferences, setSessionLanguage } from './languageApi';
87

98
jest.mock('../config');
10-
jest.mock('../auth');
119
jest.mock('./lib');
1210
jest.mock('../logging');
1311
jest.mock('../pubSub');
@@ -18,7 +16,6 @@ const LANGUAGE_PREFERENCE_COOKIE_NAME = 'lang';
1816

1917
describe('languageManager', () => {
2018
let mockCookies;
21-
let mockUser;
2219
let mockReload;
2320

2421
beforeEach(() => {
@@ -31,17 +28,14 @@ describe('languageManager', () => {
3128
mockCookies = { set: jest.fn() };
3229
getCookies.mockReturnValue(mockCookies);
3330

34-
mockUser = { username: 'testuser', userId: '123' };
35-
getAuthenticatedUser.mockReturnValue(mockUser);
36-
3731
mockReload = jest.fn();
3832
Object.defineProperty(window, 'location', {
3933
configurable: true,
4034
writable: true,
4135
value: { reload: mockReload },
4236
});
4337

44-
updateUserPreferences.mockResolvedValue({});
38+
updateAuthenticatedUserPreferences.mockResolvedValue({});
4539
setSessionLanguage.mockResolvedValue({});
4640
});
4741

@@ -53,7 +47,7 @@ describe('languageManager', () => {
5347
LANGUAGE_PREFERENCE_COOKIE_NAME,
5448
'fr',
5549
);
56-
expect(updateUserPreferences).toHaveBeenCalledWith('testuser', {
50+
expect(updateAuthenticatedUserPreferences).toHaveBeenCalledWith({
5751
prefLang: 'fr',
5852
});
5953
expect(setSessionLanguage).toHaveBeenCalledWith('fr');
@@ -63,15 +57,16 @@ describe('languageManager', () => {
6357
});
6458

6559
it('should handle errors gracefully', async () => {
66-
updateUserPreferences.mockRejectedValue(new Error('fail'));
60+
updateAuthenticatedUserPreferences.mockRejectedValue(new Error('fail'));
6761
await changeUserSessionLanguage('es', true);
6862
expect(logError).toHaveBeenCalled();
6963
});
7064

71-
it('should skip updateUserPreferences if user is not authenticated', async () => {
72-
getAuthenticatedUser.mockReturnValue(null);
65+
it('should call updateAuthenticatedUserPreferences even when user is not authenticated', async () => {
7366
await changeUserSessionLanguage('en', true);
74-
expect(updateUserPreferences).not.toHaveBeenCalled();
67+
expect(updateAuthenticatedUserPreferences).toHaveBeenCalledWith({
68+
prefLang: 'en',
69+
});
7570
});
7671

7772
it('should reload if forceReload is true', async () => {

0 commit comments

Comments
 (0)