From ba0e78188bd85d75d452a96a2f66ade43822961d Mon Sep 17 00:00:00 2001 From: Alejandro Bailo <59607668+alejandrobailo@users.noreply.github.com> Date: Mon, 19 Oct 2026 15:58:16 +0200 Subject: [PATCH] fix(ui): keep the E2E suites off the first-run redirect and pick the invite role (#13091) --- ui/tests/base-page.ts | 20 ++++++++++++++++++++ ui/tests/providers/providers-page.ts | 17 +++++++++++++++-- ui/tests/providers/providers.md | 4 ++-- ui/tests/registry/registry.spec.ts | 3 +++ ui/tests/sign-in-base/sign-in-base-page.ts | 16 +++------------- 5 files changed, 43 insertions(+), 17 deletions(-) diff --git a/ui/tests/base-page.ts b/ui/tests/base-page.ts index 0f55c3deea..480f5d4b2f 100644 --- a/ui/tests/base-page.ts +++ b/ui/tests/base-page.ts @@ -38,6 +38,26 @@ export abstract class BasePage { await this.page.reload(); } + /** + * An empty tenant redirects each fresh browser context to the add-provider + * wizard once. Suites expect a plain landing, so mark that first run as done + * with the browser-wide key (the per-tenant one is only written by the app). + * Written right away when the page is already on an app origin, and + * registered as an init script for every later document load, whatever the + * origin: localStorage is per origin and the Registry fixture profiles serve + * the app from several ports. sign-up.spec covers the redirect itself with a + * brand-new tenant. + */ + async skipFirstRunRedirect(): Promise { + const markHandled = () => { + window.localStorage.setItem("prowler.onboarding.first-run", "true"); + }; + await this.page.addInitScript(markHandled); + if (/^https?:/.test(this.page.url())) { + await this.page.evaluate(markHandled); + } + } + async goBack(): Promise { await this.page.goBack(); } diff --git a/ui/tests/providers/providers-page.ts b/ui/tests/providers/providers-page.ts index da52041e0d..cb245862dc 100644 --- a/ui/tests/providers/providers-page.ts +++ b/ui/tests/providers/providers-page.ts @@ -720,11 +720,24 @@ export class ProvidersPage extends BasePage { ).toBeVisible({ timeout: 10000 }); } - // The admin role comes preselected; only the email is needed. - async sendTeammateInvitation(email: string): Promise { + // The form preselects a role only when the tenant has one named exactly + // "admin"; the E2E tenant's is "e2e_admin", so pick the role explicitly. + async sendTeammateInvitation( + email: string, + rolePattern: RegExp = /admin/i, + ): Promise { await this.wizardModal .getByRole("textbox", { name: /Teammate email/i }) .fill(email); + // Radix Select: the trigger is a combobox and its options render in a portal. + const roleSelect = this.wizardModal.getByRole("combobox", { + name: "Select a role", + }); + await roleSelect.click(); + const option = this.page.getByRole("option", { name: rolePattern }).first(); + await expect(option).toBeVisible({ timeout: 10000 }); + await option.click(); + await expect(roleSelect).not.toContainText("Select a role"); const send = this.page.getByRole("button", { name: "Send invitation", exact: true, diff --git a/ui/tests/providers/providers.md b/ui/tests/providers/providers.md index 53ba05f1f9..856a565552 100644 --- a/ui/tests/providers/providers.md +++ b/ui/tests/providers/providers.md @@ -1101,7 +1101,7 @@ 2. Click "Add Provider" button 3. Select AWS provider type 4. Select the "I don't have access, invite a teammate" option -5. Fill the teammate email (the admin role comes preselected) +5. Fill the teammate email and pick a role that can manage providers (the form preselects one only when the tenant has a role named exactly `admin`; the E2E tenant's is `e2e_admin`) 6. Click "Send invitation" 7. Verify the confirmation and the invitation link 8. Click "Done" @@ -1115,7 +1115,7 @@ ### Key verification points - The invite option is visible on the AWS step for the admin user -- "Send invitation" is enabled once a valid email is typed +- "Send invitation" is enabled once a valid email is typed and a role is selected - Confirmation text "Invitation sent to {email}" is visible - The accept link contains `/invitation/accept?invitation_token=` - The wizard modal is closed after "Done" diff --git a/ui/tests/registry/registry.spec.ts b/ui/tests/registry/registry.spec.ts index a99617ba9b..f5e3d2d0cf 100644 --- a/ui/tests/registry/registry.spec.ts +++ b/ui/tests/registry/registry.spec.ts @@ -28,6 +28,9 @@ test.describe.serial("Registry", () => { !fixtureMode, "Registry browser acceptance is available only through the self-contained fixture profile.", ); + // The saved manager state carries the first-run marker for the enabled + // profile's origin only; the flag-off and Local profiles serve other ports. + await new RegistryPage(page).skipFirstRunRedirect(); await controlledRegistryFixture.reset(); // Exercise the real media CSP without contacting the Registry service. await page.route( diff --git a/ui/tests/sign-in-base/sign-in-base-page.ts b/ui/tests/sign-in-base/sign-in-base-page.ts index 799841f68a..e7065fcbd1 100644 --- a/ui/tests/sign-in-base/sign-in-base-page.ts +++ b/ui/tests/sign-in-base/sign-in-base-page.ts @@ -198,6 +198,9 @@ export class SignInPage extends BasePage { */ async loginAndVerify(credentials: SignInCredentials): Promise { await this.goto(); + // Like the auth setups: the E2E tenant owns no providers, so a fresh + // context would otherwise land on the add-provider wizard, not Overview. + await this.skipFirstRunRedirect(); await this.login(credentials); await this.verifySuccessfulLogin(); } @@ -401,17 +404,4 @@ export class SignInPage extends BasePage { await this.verifySuccessfulLogin(); await this.page.context().storageState({ path: storagePath }); } - - /** - * An empty tenant redirects each fresh browser context to the add-provider - * wizard once. Suites expect a plain landing, so mark that first run as done - * with the browser-wide key (the per-tenant one is only written by the app) - * before signing in; sign-up.spec covers the redirect itself with a brand-new - * tenant. Call it on a page already on the app origin. - */ - async skipFirstRunRedirect(): Promise { - await this.page.evaluate(() => { - window.localStorage.setItem("prowler.onboarding.first-run", "true"); - }); - } }