From e663e8face2a55859392e33651130e0c8cffffc0 Mon Sep 17 00:00:00 2001 From: Alan Buscaglia Date: Mon, 29 Jun 2026 15:48:20 +0200 Subject: [PATCH] test(ui): align provider e2e no-scan flow --- ui/tests/providers/providers-page.ts | 37 +++++++++++++++------------- ui/tests/providers/providers.md | 2 +- ui/tests/providers/providers.spec.ts | 5 ---- 3 files changed, 21 insertions(+), 23 deletions(-) diff --git a/ui/tests/providers/providers-page.ts b/ui/tests/providers/providers-page.ts index 1094bf50b4..9f5868dffa 100644 --- a/ui/tests/providers/providers-page.ts +++ b/ui/tests/providers/providers-page.ts @@ -936,9 +936,11 @@ export class ProvidersPage extends BasePage { } } - private async waitForProviderLaunchChoice(timeout = 30000): Promise { - const launchAction = this.page - .getByRole("button", { name: "Save", exact: true }) + private async waitForProviderReadyToClose(): Promise { + const launchStepReady = this.page + .getByText("Account Connected!", { exact: true }) + .or(this.page.getByText("Loading scan options...", { exact: true })) + .or(this.page.getByRole("button", { name: "Save", exact: true })) .or(this.page.getByRole("button", { name: "Launch scan", exact: true })); const connectionError = this.page.locator( "div.border-border-error p.text-text-error-primary", @@ -946,8 +948,8 @@ export class ProvidersPage extends BasePage { try { await Promise.race([ - launchAction.waitFor({ state: "visible", timeout }), - connectionError.waitFor({ state: "visible", timeout }), + launchStepReady.waitFor({ state: "visible", timeout: 30000 }), + connectionError.waitFor({ state: "visible", timeout: 30000 }), ]); } catch { // Continue and inspect visible state below. @@ -960,12 +962,11 @@ export class ProvidersPage extends BasePage { ); } - await expect(launchAction).toBeVisible({ timeout }); + await expect(launchStepReady).toBeVisible({ timeout: 30000 }); } async completeProviderConnectionWithoutLaunchingScan( providerUID: string, - timeout = 30000, ): Promise { await this.verifyWizardModalOpen(); @@ -973,20 +974,22 @@ export class ProvidersPage extends BasePage { name: "Check connection", exact: true, }); - const launchAction = this.page - .getByRole("button", { name: "Save", exact: true }) + const launchStepReady = this.page + .getByText("Account Connected!", { exact: true }) + .or(this.page.getByText("Loading scan options...", { exact: true })) + .or(this.page.getByRole("button", { name: "Save", exact: true })) .or(this.page.getByRole("button", { name: "Launch scan", exact: true })); const connectionError = this.page.locator( "div.border-border-error p.text-text-error-primary", ); - // The test-connection step renders its footer action only after an async - // load (canSubmit gate). Wait for the footer to settle on an actionable - // state (or surface a connection error) instead of reading visibility on - // the first frame, which races the render and falls through. + // The no-launch provider tests only need to know that the provider reached + // the post-connection step. Do not depend exclusively on scan launch/schedule + // actions, because those controls are owned by the launch flow and can be + // hidden while scan options load. await expect( - checkConnectionButton.or(launchAction).or(connectionError), - ).toBeVisible({ timeout }); + checkConnectionButton.or(launchStepReady).or(connectionError), + ).toBeVisible({ timeout: 30000 }); if (await connectionError.isVisible().catch(() => false)) { const errorText = await connectionError.textContent(); @@ -1000,9 +1003,9 @@ export class ProvidersPage extends BasePage { // scan execution itself is covered by scans.spec.ts. if (await checkConnectionButton.isVisible().catch(() => false)) { await checkConnectionButton.click(); - await this.waitForProviderLaunchChoice(timeout); + await this.waitForProviderReadyToClose(); } else { - await expect(launchAction).toBeVisible({ timeout }); + await expect(launchStepReady).toBeVisible({ timeout: 30000 }); } await this.wizardModal diff --git a/ui/tests/providers/providers.md b/ui/tests/providers/providers.md index 5dc81cbfc9..39fc715d97 100644 --- a/ui/tests/providers/providers.md +++ b/ui/tests/providers/providers.md @@ -1092,7 +1092,7 @@ - Environment variables configured: E2E_OKTA_DOMAIN, E2E_OKTA_CLIENT_ID, E2E_OKTA_BASE64_PRIVATE_KEY - Remove any existing provider with the same Org Domain before starting the test - This test must be run serially and never in parallel with other tests, as it requires the Org Domain not to be already registered beforehand. -- Okta provider authentication can take longer than the default Playwright test timeout in CI, so the test allows extra time for the provider connection step. +- The test validates provider creation and connection only; scan execution is covered by the Scans E2E suite. ### Flow Steps diff --git a/ui/tests/providers/providers.spec.ts b/ui/tests/providers/providers.spec.ts index 33cd2d2885..4dc8f010ed 100644 --- a/ui/tests/providers/providers.spec.ts +++ b/ui/tests/providers/providers.spec.ts @@ -1430,8 +1430,6 @@ test.describe("Add Provider", () => { const orgDomain = (process.env.E2E_OKTA_DOMAIN ?? "").toLowerCase(); const clientId = process.env.E2E_OKTA_CLIENT_ID ?? ""; const privateKeyB64 = process.env.E2E_OKTA_BASE64_PRIVATE_KEY ?? ""; - const oktaProviderTestTimeout = 150_000; - const oktaProviderConnectionTimeout = 60_000; // Setup before each test test.beforeEach(async ({ page }) => { @@ -1459,8 +1457,6 @@ test.describe("Add Provider", () => { ], }, async ({ page }) => { - test.setTimeout(oktaProviderTestTimeout); - // The Okta app private key is PEM-encoded (multi-line), so it is passed // base64-encoded via the environment variable and decoded here. const privateKey = Buffer.from(privateKeyB64, "base64").toString( @@ -1505,7 +1501,6 @@ test.describe("Add Provider", () => { // Confirm the provider connection without launching a scan await providersPage.completeProviderConnectionWithoutLaunchingScan( orgDomain, - oktaProviderConnectionTimeout, ); }, );