mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-23 12:31:54 +00:00
test(ui): improve auth and invitation E2E test stability
Simplify auth middleware test to use clearCookies and null session assertion. Remove unnecessary login and navigation from session error test. Fix InvitationsPage role selector with .or() fallback.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { test } from "@playwright/test";
|
||||
import { expect, test } from "@playwright/test";
|
||||
|
||||
import { TEST_CREDENTIALS } from "../helpers";
|
||||
import { getSessionWithoutCookies, TEST_CREDENTIALS } from "../helpers";
|
||||
import { ProvidersPage } from "../providers/providers-page";
|
||||
import { ScansPage } from "../scans/scans-page";
|
||||
import { SignInPage } from "../sign-in-base/sign-in-base-page";
|
||||
@@ -40,24 +40,14 @@ test.describe("Middleware Error Handling", () => {
|
||||
await providersPage.goto();
|
||||
await providersPage.verifyPageLoaded();
|
||||
|
||||
const cookies = await context.cookies();
|
||||
const sessionCookie = cookies.find((c) =>
|
||||
c.name.includes("authjs.session-token"),
|
||||
);
|
||||
// Remove auth cookies to simulate a broken/expired session deterministically.
|
||||
await context.clearCookies();
|
||||
|
||||
if (sessionCookie) {
|
||||
await context.clearCookies();
|
||||
await context.addCookies([
|
||||
{
|
||||
...sessionCookie,
|
||||
value: "invalid-session-token",
|
||||
},
|
||||
]);
|
||||
const expiredSession = await getSessionWithoutCookies(page);
|
||||
expect(expiredSession).toBeNull();
|
||||
|
||||
await scansPage.goto();
|
||||
// With invalid session, should redirect to sign-in
|
||||
await signInPage.verifyOnSignInPage();
|
||||
}
|
||||
await scansPage.goto();
|
||||
await signInPage.verifyOnSignInPage();
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
|
||||
import { TEST_CREDENTIALS } from "../helpers";
|
||||
import { ProvidersPage } from "../providers/providers-page";
|
||||
import { ScansPage } from "../scans/scans-page";
|
||||
import { SignInPage } from "../sign-in-base/sign-in-base-page";
|
||||
|
||||
test.describe("Session Error Messages", () => {
|
||||
@@ -65,28 +62,10 @@ test.describe("Session Error Messages", () => {
|
||||
{ tag: ["@e2e", "@auth", "@session", "@AUTH-SESSION-E2E-004"] },
|
||||
async ({ page, context }) => {
|
||||
const signInPage = new SignInPage(page);
|
||||
const scansPage = new ScansPage(page);
|
||||
const providersPage = new ProvidersPage(page);
|
||||
|
||||
await signInPage.loginAndVerify(TEST_CREDENTIALS.VALID);
|
||||
|
||||
// Navigate to a specific page (just need to be on a protected route)
|
||||
await scansPage.goto();
|
||||
await expect(page.locator("main")).toBeVisible();
|
||||
|
||||
// Navigate to a safe public page before clearing cookies
|
||||
// This prevents background requests from the protected page (scans)
|
||||
// triggering a client-side redirect race condition when cookies are cleared
|
||||
await signInPage.goto();
|
||||
|
||||
// Clear cookies to simulate session expiry
|
||||
await context.clearCookies();
|
||||
|
||||
// Try to navigate to a different protected route
|
||||
// Use fresh navigation to force middleware evaluation
|
||||
await providersPage.gotoFresh();
|
||||
|
||||
// Should be redirected to login with callbackUrl
|
||||
// Navigate directly to a protected route and assert callbackUrl preservation.
|
||||
await page.goto("/providers", { waitUntil: "commit" });
|
||||
await signInPage.verifyRedirectWithCallback("/providers");
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { Page, Locator, expect } from "@playwright/test";
|
||||
import { BasePage } from "../base-page";
|
||||
|
||||
|
||||
export class InvitationsPage extends BasePage {
|
||||
|
||||
// Page heading
|
||||
readonly pageHeadingSendInvitation: Locator;
|
||||
readonly pageHeadingInvitations: Locator;
|
||||
@@ -18,34 +16,52 @@ export class InvitationsPage extends BasePage {
|
||||
readonly reviewInvitationDetailsButton: Locator;
|
||||
readonly shareUrl: Locator;
|
||||
|
||||
|
||||
constructor(page: Page) {
|
||||
super(page);
|
||||
|
||||
// Page heading
|
||||
this.pageHeadingInvitations = page.getByRole("heading", { name: "Invitations" });
|
||||
this.pageHeadingSendInvitation = page.getByRole("heading", { name: "Send Invitation" });
|
||||
this.pageHeadingInvitations = page.getByRole("heading", {
|
||||
name: "Invitations",
|
||||
});
|
||||
this.pageHeadingSendInvitation = page.getByRole("heading", {
|
||||
name: "Send Invitation",
|
||||
});
|
||||
|
||||
// Button to invite a new user
|
||||
this.inviteButton = page.getByRole("link", { name: "Send Invitation", exact: true });
|
||||
this.sendInviteButton = page.getByRole("button", { name: "Send Invitation", exact: true });
|
||||
this.inviteButton = page.getByRole("link", {
|
||||
name: "Send Invitation",
|
||||
exact: true,
|
||||
});
|
||||
this.sendInviteButton = page.getByRole("button", {
|
||||
name: "Send Invitation",
|
||||
exact: true,
|
||||
});
|
||||
|
||||
// Form inputs
|
||||
this.emailInput = page.getByRole("textbox", { name: "Email" });
|
||||
|
||||
// Form select
|
||||
this.roleSelect = page.getByRole("combobox", { name: /Role|Select a role/i });
|
||||
this.roleSelect = page
|
||||
.getByRole("combobox", { name: /Role|Select a role/i })
|
||||
.or(page.getByRole("button", { name: /Role|Select a role/i }))
|
||||
.first();
|
||||
|
||||
// Form details
|
||||
this.reviewInvitationDetailsButton = page.getByRole('button', { name: /Review Invitation Details/i });
|
||||
this.reviewInvitationDetailsButton = page.getByRole("button", {
|
||||
name: /Review Invitation Details/i,
|
||||
});
|
||||
|
||||
// Multiple strategies to find the share URL
|
||||
this.shareUrl = page.locator('a[href*="/sign-up?invitation_token="], [data-testid="share-url"], .share-url, code, pre').first();
|
||||
this.shareUrl = page
|
||||
.locator(
|
||||
'a[href*="/sign-up?invitation_token="], [data-testid="share-url"], .share-url, code, pre',
|
||||
)
|
||||
.first();
|
||||
}
|
||||
|
||||
async goto(): Promise<void> {
|
||||
// Navigate to the invitations page
|
||||
|
||||
|
||||
await super.goto("/invitations");
|
||||
}
|
||||
|
||||
@@ -83,18 +99,21 @@ export class InvitationsPage extends BasePage {
|
||||
// Select the role option
|
||||
|
||||
// Open the role dropdown
|
||||
await expect(this.roleSelect).toBeVisible({ timeout: 15000 });
|
||||
await this.roleSelect.click();
|
||||
|
||||
// Prefer ARIA role option inside listbox
|
||||
const option = this.page.getByRole("option", { name: new RegExp(`^${role}$`, "i") });
|
||||
const option = this.page.getByRole("option", {
|
||||
name: new RegExp(`^${role}$`, "i"),
|
||||
});
|
||||
|
||||
if (await option.count()) {
|
||||
await option.first().click();
|
||||
} else {
|
||||
throw new Error(`Role option ${role} not found`);
|
||||
}
|
||||
// Ensure the combobox now shows the chosen value
|
||||
await expect(this.roleSelect).toContainText(new RegExp(role, "i"));
|
||||
// Ensure a role value was selected in the trigger
|
||||
await expect(this.roleSelect).not.toContainText(/Select a role/i);
|
||||
}
|
||||
|
||||
async verifyInviteDataPageLoaded(): Promise<void> {
|
||||
@@ -108,7 +127,7 @@ export class InvitationsPage extends BasePage {
|
||||
|
||||
// Get the share url text content
|
||||
const text = await this.shareUrl.textContent();
|
||||
|
||||
|
||||
if (!text) {
|
||||
throw new Error("Share url not found");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user