From f1055ab5abc34639307fb476f7a9fc3c9bc2f68d Mon Sep 17 00:00:00 2001 From: jimmychakkalakal <47317106+jimmychakkalakal@users.noreply.github.com> Date: Wed, 1 Apr 2026 11:53:25 +0100 Subject: [PATCH] Fix/issue 46081 fetch with error throwing network error (#47597) closes #46081 Signed-off-by: Jimmy Chakkalakal --- .../test/personal-info/personal-info.spec.ts | 75 ++++++++++++------- js/apps/account-ui/test/resources.spec.ts | 15 +++- js/apps/account-ui/test/support/test-utils.ts | 37 +++++++++ 3 files changed, 98 insertions(+), 29 deletions(-) create mode 100644 js/apps/account-ui/test/support/test-utils.ts diff --git a/js/apps/account-ui/test/personal-info/personal-info.spec.ts b/js/apps/account-ui/test/personal-info/personal-info.spec.ts index 408a1f962bd..361c32b0a94 100644 --- a/js/apps/account-ui/test/personal-info/personal-info.spec.ts +++ b/js/apps/account-ui/test/personal-info/personal-info.spec.ts @@ -1,6 +1,7 @@ import { expect, test } from "@playwright/test"; import { assertLastAlert, login } from "../support/actions.ts"; import { createTestBed } from "../support/testbed.ts"; +import { retryOperation, waitForRealmReady } from "../support/test-utils.ts"; import userProfile from "./user-profile.json" with { type: "json" }; import { adminClient } from "../support/admin-client.ts"; import userProfileRealm from "../realms/user-profile-realm.json" with { type: "json" }; @@ -23,68 +24,80 @@ test.describe("Personal info", () => { test.describe("Personal info (user profile enabled)", () => { test("renders user profile fields", async ({ page }) => { await using testBed = await createTestBed(userProfileRealm); + await waitForRealmReady(); - await adminClient.users.updateProfile({ - ...userProfile, - realm: testBed.realm, - }); + await retryOperation(() => + adminClient.users.updateProfile({ + ...userProfile, + realm: testBed.realm, + }), + ); await login(page, testBed.realm); await expect(page.locator("#select")).toBeVisible(); await expect(page.getByTestId("help-label-select")).toBeVisible(); - await expect(page.getByText("Alternative email")).toHaveCount(1); - await expect(page.getByPlaceholder("Deutsch")).toHaveCount(1); + await expect(page.getByText("Alternative email")).toBeVisible(); + await expect(page.getByPlaceholder("Deutsch")).toBeVisible(); await page.getByTestId("help-label-email2").click(); - await expect(page.getByText("Español")).toHaveCount(1); + await expect(page.getByText("Español")).toBeVisible(); }); test("renders long select options as typeahead", async ({ page }) => { await using testBed = await createTestBed(userProfileRealm); + await waitForRealmReady(); - await adminClient.users.updateProfile({ - ...userProfile, - realm: testBed.realm, - }); + await retryOperation(() => + adminClient.users.updateProfile({ + ...userProfile, + realm: testBed.realm, + }), + ); await login(page, testBed.realm); await page.locator("#alternatelang").click(); - await page.waitForSelector("text=Italiano"); + await expect(page.getByText("Italiano")).toBeVisible(); await page.locator("#alternatelang").click(); await page.locator("*:focus").press("Control+A"); await page.locator("*:focus").pressSequentially("S"); - await expect(page.getByText("Italiano")).toHaveCount(0); + await expect(page.getByText("Italiano")).toBeHidden(); await expect(page.getByText("Slovak")).toBeVisible(); await expect(page.getByText('Create "S"')).toBeHidden(); }); test("renders long list of locales as typeahead", async ({ page }) => { await using testBed = await createTestBed(userProfileRealm); + await waitForRealmReady(); - await adminClient.users.updateProfile({ - ...userProfile, - realm: testBed.realm, - }); + await retryOperation(() => + adminClient.users.updateProfile({ + ...userProfile, + realm: testBed.realm, + }), + ); await login(page, testBed.realm); await page.locator("#attributes\\.locale").click(); - await page.waitForSelector("text=Italiano"); + await expect(page.getByText("Italiano")).toBeVisible(); await page.locator("#attributes\\.locale").click(); await page.locator("*:focus").press("Control+A"); await page.locator("*:focus").pressSequentially("S"); - await expect(page.getByText("Italiano")).toHaveCount(0); + await expect(page.getByText("Italiano")).toBeHidden(); await expect(page.getByText("Slovak")).toBeVisible(); await expect(page.getByText('Create "S"')).toBeHidden(); }); test("saves user profile", async ({ page }) => { await using testBed = await createTestBed(userProfileRealm); + await waitForRealmReady(); - await adminClient.users.updateProfile({ - ...userProfile, - realm: testBed.realm, - }); + await retryOperation(() => + adminClient.users.updateProfile({ + ...userProfile, + realm: testBed.realm, + }), + ); await login(page, testBed.realm); await page.locator("#select").click(); @@ -106,7 +119,6 @@ test.describe("Personal info (user profile enabled)", () => { await assertLastAlert(page, "Your account has been updated."); await page.reload(); - await page.locator("delete-account").isVisible(); await expect(page.getByTestId("email2")).toHaveValue("valid@email.com"); }); }); @@ -117,18 +129,25 @@ test.describe("Realm localization", () => { internationalizationEnabled: true, supportedLocales: ["en", "nl", "de"], }); + await waitForRealmReady(); await login(page, testBed.realm); + + await page.locator("#attributes\\.locale").waitFor({ state: "visible" }); await page.locator("#attributes\\.locale").click(); - page.getByRole("option").filter({ hasText: "Deutsch" }); + + await page + .getByRole("option", { name: "English" }) + .waitFor({ state: "visible" }); await page.getByRole("option", { name: "English" }).click(); + await page.getByTestId("save").click(); await assertLastAlert(page, "Your account has been updated."); await page.reload(); - expect( - page.locator("#attributes\\.locale").filter({ hasText: /^English$/ }), - ).toBeDefined(); + await page.locator("#attributes\\.locale").waitFor({ state: "visible" }); + + await expect(page.locator("#attributes\\.locale")).toContainText("English"); }); }); diff --git a/js/apps/account-ui/test/resources.spec.ts b/js/apps/account-ui/test/resources.spec.ts index bc656b71f28..e2dcd134954 100644 --- a/js/apps/account-ui/test/resources.spec.ts +++ b/js/apps/account-ui/test/resources.spec.ts @@ -3,15 +3,22 @@ import { expect, test } from "@playwright/test"; import resourcesRealm from "./realms/resources-realm.json" with { type: "json" }; import { login } from "./support/actions.ts"; import { createTestBed } from "./support/testbed.ts"; +import { waitForRealmReady } from "./support/test-utils.ts"; test.describe("Resources", () => { test("shows the resources owned by the user", async ({ page }) => { await using testBed = await createTestBed( resourcesRealm as RealmRepresentation, ); + await waitForRealmReady(); await login(page, testBed.realm); + const responsePromise = page.waitForResponse( + (resp) => resp.url().includes("/realms/") && resp.status() === 200, + ); + await page.getByTestId("resources").click(); + await responsePromise; await expect(page.getByRole("gridcell", { name: "one" })).toBeVisible(); }); @@ -20,11 +27,14 @@ test.describe("Resources", () => { await using testBed = await createTestBed( resourcesRealm as RealmRepresentation, ); + await waitForRealmReady(); await login(page, testBed.realm, "alice", "alice"); await page.getByTestId("resources").click(); - await page.getByTestId("sharedWithMe").click(); + + const table = page.locator("table"); + await expect(table).toBeVisible(); const tableData = await page.locator("table > tr").count(); expect(tableData).toBe(0); }); @@ -33,6 +43,7 @@ test.describe("Resources", () => { await using testBed = await createTestBed( resourcesRealm as RealmRepresentation, ); + await waitForRealmReady(); await using context1 = await browser.newContext(); await using context2 = await browser.newContext(); @@ -76,6 +87,8 @@ test.describe("Resources", () => { await page2.getByTestId("resources").click(); await page2.getByTestId("sharedWithMe").click(); + const table = page2.locator("table"); + await expect(table).toBeVisible(); const rowData = page2.getByTestId("row[0].name"); await expect(rowData).toHaveText("one"); }); diff --git a/js/apps/account-ui/test/support/test-utils.ts b/js/apps/account-ui/test/support/test-utils.ts new file mode 100644 index 00000000000..b2dace026ae --- /dev/null +++ b/js/apps/account-ui/test/support/test-utils.ts @@ -0,0 +1,37 @@ +export async function retryOperation( + operation: () => Promise, + maxRetries = 15, + initialDelay = 300, +): Promise { + let lastError: Error | undefined; + + for (let attempt = 0; attempt < maxRetries; attempt++) { + try { + return await operation(); + } catch (error) { + lastError = error as Error; + + // Only retry on server errors or network errors, not on validation errors + const isRetryableError = + error instanceof Error && + (error.message.includes("unknown_error") || + error.message.includes("500") || + error.message.includes("ECONNREFUSED")); + + if (isRetryableError) { + const delay = initialDelay * Math.pow(1.5, attempt); + await new Promise((resolve) => setTimeout(resolve, delay)); + continue; + } + + // For other errors (validation, 4xx, etc.), throw immediately + throw error; + } + } + + throw lastError; +} + +export async function waitForRealmReady(delayMs = 500): Promise { + await new Promise((resolve) => setTimeout(resolve, delayMs)); +}