From 5c3dd55406fa041891517ef7b4de5918f547c595 Mon Sep 17 00:00:00 2001 From: leputz <37064902+leputz@users.noreply.github.com> Date: Fri, 12 Dec 2025 12:28:43 +0200 Subject: [PATCH] fix(client): show more descriptive error message on fetchUserError (#64061) Co-authored-by: Huyen Nguyen <25715018+huyenltnguyen@users.noreply.github.com> --- client/i18n/locales/english/translations.json | 3 ++- .../components/Flash/redux/flash-messages.ts | 1 + client/src/redux/fetch-user-saga.js | 5 ++++- client/src/utils/error-messages.ts | 5 +++++ client/src/utils/tone/index.ts | 3 ++- e2e/flash.spec.ts | 21 +++++++++++++++---- 6 files changed, 31 insertions(+), 7 deletions(-) diff --git a/client/i18n/locales/english/translations.json b/client/i18n/locales/english/translations.json index 9d104c5a754..856f197b30c 100644 --- a/client/i18n/locales/english/translations.json +++ b/client/i18n/locales/english/translations.json @@ -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", diff --git a/client/src/components/Flash/redux/flash-messages.ts b/client/src/components/Flash/redux/flash-messages.ts index 71c350c099f..dfd6751708a 100644 --- a/client/src/components/Flash/redux/flash-messages.ts +++ b/client/src/components/Flash/redux/flash-messages.ts @@ -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', diff --git a/client/src/redux/fetch-user-saga.js b/client/src/redux/fetch-user-saga.js index d093dda4d66..a8755bf243e 100644 --- a/client/src/redux/fetch-user-saga.js +++ b/client/src/redux/fetch-user-saga.js @@ -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); } diff --git a/client/src/utils/error-messages.ts b/client/src/utils/error-messages.ts index 5d2b081d244..31e1cc1d401 100644 --- a/client/src/utils/error-messages.ts +++ b/client/src/utils/error-messages.ts @@ -24,3 +24,8 @@ export const msTrophyVerified = { type: 'success', message: FlashMessages.MsTrophyVerified }; + +export const UserFetchErrorMessage = { + type: 'danger', + message: FlashMessages.UserFetchError +}; diff --git a/client/src/utils/tone/index.ts b/client/src/utils/tone/index.ts index 2ebaa615fe6..5c9023c0142 100644 --- a/client/src/utils/tone/index.ts +++ b/client/src/utils/tone/index.ts @@ -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; diff --git a/e2e/flash.spec.ts b/e2e/flash.spec.ts index 2619754c626..ffb5046a97a 100644 --- a/e2e/flash.spec.ts +++ b/e2e/flash.spec.ts @@ -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'] + ); + }); });