fix(client): show more descriptive error message on fetchUserError (#64061)

Co-authored-by: Huyen Nguyen <25715018+huyenltnguyen@users.noreply.github.com>
This commit is contained in:
leputz
2025-12-12 12:28:43 +02:00
committed by GitHub
parent 9f16487a20
commit 5c3dd55406
6 changed files with 31 additions and 7 deletions

View File

@@ -1083,7 +1083,8 @@
"err-3": "Something went wrong trying to save your survey.",
"success": "Thank you. Your survey was submitted."
},
"classroom-mode-updated": "We have updated your classroom mode settings"
"classroom-mode-updated": "We have updated your classroom mode settings",
"user-fetch-error": "Unable to retrieve your user information. You can still use the site, but your progress may not be saved."
},
"validation": {
"max-characters": "There is a maximum limit of 288 characters, you have {{charsLeft}} left",

View File

@@ -16,6 +16,7 @@ export enum FlashMessages {
CourseUrlCopyError = 'flash.course-url-copy-error',
DeleteTokenErr = 'flash.delete-token-err',
EmailValid = 'flash.email-valid',
UserFetchError = 'flash.user-fetch-error',
GenerateExamError = 'flash.generate-exam-error',
HonestFirst = 'flash.honest-first',
IncompleteSteps = 'flash.incomplete-steps',

View File

@@ -1,5 +1,7 @@
import { call, cancel, delay, fork, put, takeEvery } from 'redux-saga/effects';
import { getSessionUser, getUserProfile } from '../utils/ajax';
import { wrapHandledError } from '../utils/handled-error';
import { UserFetchErrorMessage } from '../utils/error-messages';
import {
fetchProfileForUserComplete,
fetchProfileForUserError,
@@ -33,7 +35,8 @@ function* fetchSessionUser() {
yield put(fetchUserComplete({ user }));
} catch (e) {
console.log('failed to fetch user', e);
yield put(fetchUserError(e));
const handledError = wrapHandledError(e, UserFetchErrorMessage);
yield put(fetchUserError(handledError));
} finally {
yield cancel(timeoutTask);
}

View File

@@ -24,3 +24,8 @@ export const msTrophyVerified = {
type: 'success',
message: FlashMessages.MsTrophyVerified
};
export const UserFetchErrorMessage = {
type: 'danger',
message: FlashMessages.UserFetchError
};

View File

@@ -89,7 +89,8 @@ const toneUrls = {
[FlashMessages.UserTokenGenerateError]: TRY_AGAIN,
[FlashMessages.WrongName]: TRY_AGAIN,
[FlashMessages.WrongUpdating]: TRY_AGAIN,
[FlashMessages.WentWrong]: TRY_AGAIN
[FlashMessages.WentWrong]: TRY_AGAIN,
[FlashMessages.UserFetchError]: TRY_AGAIN
} as const;
type ToneStates = keyof typeof toneUrls;

View File

@@ -1,10 +1,6 @@
import { test, expect, type Page } from '@playwright/test';
import translations from '../client/i18n/locales/english/translations.json';
test.beforeEach(async ({ page }) => {
await page.goto('/settings');
});
const checkFlashMessageVisibility = async (page: Page, translation: string) => {
const flashMessage = page.getByText(translation);
await expect(flashMessage).toBeVisible();
@@ -15,6 +11,8 @@ const checkFlashMessageVisibility = async (page: Page, translation: string) => {
test.describe('Flash Message component E2E test', () => {
test('Flash Message Visibility for Night Mode Toggle', async ({ page }) => {
await page.goto('/settings');
await page
.getByRole('button', { name: translations.buttons.menu, exact: true })
.click();
@@ -31,6 +29,8 @@ test.describe('Flash Message component E2E test', () => {
});
test('Flash Message Visibility for Sound Mode Toggle', async ({ page }) => {
await page.goto('/settings');
await page
.getByLabel(translations.settings.labels['sound-mode'])
.getByRole('button', { name: translations.buttons.on })
@@ -40,4 +40,17 @@ test.describe('Flash Message component E2E test', () => {
translations.flash['updated-sound']
);
});
test('should be visible when a network error occurs', async ({ page }) => {
await page.route(
'*/**/user/get-session-user',
async route => await route.fulfill({ status: 500 })
);
await page.goto('/');
await checkFlashMessageVisibility(
page,
translations.flash['user-fetch-error']
);
});
});