mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-03-21 18:58:04 +00:00
test(ui): update E2E page objects and improve test stability (#10158)
This commit is contained in:
@@ -18,6 +18,7 @@ All notable changes to the **Prowler UI** are documented in this file.
|
||||
- CSA CCM detailed view and small fix related with `Top Failed Sections` width [(#10018)](https://github.com/prowler-cloud/prowler/pull/10018)
|
||||
- Attack Paths: Show scan data availability status with badges and tooltips, allow selecting scans for querying while a new scan is in progress [(#10089)](https://github.com/prowler-cloud/prowler/pull/10089)
|
||||
- Attack Paths: Catches not found and permissions (for read only queries) errors [(#10140)](https://github.com/prowler-cloud/prowler/pull/10140)
|
||||
- Provider connection flow was unified into a modal wizard with AWS Organizations bulk onboarding, safer secret retry handling, and more stable E2E coverage [(#10153)](https://github.com/prowler-cloud/prowler/pull/10153) [(#10154)](https://github.com/prowler-cloud/prowler/pull/10154) [(#10155)](https://github.com/prowler-cloud/prowler/pull/10155) [(#10156)](https://github.com/prowler-cloud/prowler/pull/10156) [(#10157)](https://github.com/prowler-cloud/prowler/pull/10157) [(#10158)](https://github.com/prowler-cloud/prowler/pull/10158)
|
||||
|
||||
### 🔐 Security
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -194,6 +194,9 @@ export interface AlibabaCloudProviderCredential {
|
||||
|
||||
// Providers page
|
||||
export class ProvidersPage extends BasePage {
|
||||
readonly wizardModal: Locator;
|
||||
readonly wizardTitle: Locator;
|
||||
|
||||
// Alias input
|
||||
readonly aliasInput: Locator;
|
||||
|
||||
@@ -288,12 +291,31 @@ export class ProvidersPage extends BasePage {
|
||||
constructor(page: Page) {
|
||||
super(page);
|
||||
|
||||
// Button to add a new cloud provider
|
||||
this.addProviderButton = page.getByRole("link", {
|
||||
name: "Add Cloud Provider",
|
||||
exact: true,
|
||||
this.wizardModal = page
|
||||
.getByRole("dialog")
|
||||
.filter({
|
||||
has: page.getByRole("heading", {
|
||||
name: /Adding A Cloud Provider|Update Provider Credentials/i,
|
||||
}),
|
||||
})
|
||||
.first();
|
||||
this.wizardTitle = page.getByRole("heading", {
|
||||
name: /Adding A Cloud Provider|Update Provider Credentials/i,
|
||||
});
|
||||
|
||||
// Button to add a new cloud provider
|
||||
this.addProviderButton = page
|
||||
.getByRole("button", {
|
||||
name: "Add Cloud Provider",
|
||||
exact: true,
|
||||
})
|
||||
.or(
|
||||
page.getByRole("link", {
|
||||
name: "Add Cloud Provider",
|
||||
exact: true,
|
||||
}),
|
||||
);
|
||||
|
||||
// Table displaying existing providers
|
||||
this.providersTable = page.getByRole("table");
|
||||
|
||||
@@ -507,6 +529,25 @@ export class ProvidersPage extends BasePage {
|
||||
await this.addProviderButton.click();
|
||||
}
|
||||
|
||||
async openProviderWizardModal(): Promise<void> {
|
||||
await this.clickAddProvider();
|
||||
await this.verifyWizardModalOpen();
|
||||
}
|
||||
|
||||
async closeProviderWizardModal(): Promise<void> {
|
||||
await this.page.keyboard.press("Escape");
|
||||
await expect(this.wizardModal).not.toBeVisible();
|
||||
}
|
||||
|
||||
async verifyWizardModalOpen(): Promise<void> {
|
||||
await expect(this.wizardModal).toBeVisible();
|
||||
await expect(this.wizardTitle).toBeVisible();
|
||||
}
|
||||
|
||||
async advanceWizardStep(): Promise<void> {
|
||||
await this.clickNext();
|
||||
}
|
||||
|
||||
private async selectProviderRadio(radio: Locator): Promise<void> {
|
||||
// Force click to handle overlay intercepts
|
||||
await radio.click({ force: true });
|
||||
@@ -536,8 +577,71 @@ export class ProvidersPage extends BasePage {
|
||||
await this.selectProviderRadio(this.githubProviderRadio);
|
||||
}
|
||||
|
||||
async selectAWSSingleAccountMethod(): Promise<void> {
|
||||
await this.page
|
||||
.getByRole("button", {
|
||||
name: "Add A Single AWS Cloud Account",
|
||||
exact: true,
|
||||
})
|
||||
.click();
|
||||
}
|
||||
|
||||
async selectAWSOrganizationsMethod(): Promise<void> {
|
||||
await this.page
|
||||
.getByRole("button", {
|
||||
name: "Add Multiple Accounts With AWS Organizations",
|
||||
exact: true,
|
||||
})
|
||||
.click();
|
||||
}
|
||||
|
||||
async verifyOrganizationsAuthenticationStepLoaded(): Promise<void> {
|
||||
await this.verifyWizardModalOpen();
|
||||
await expect(
|
||||
this.page.getByRole("heading", {
|
||||
name: /Authentication Details/i,
|
||||
}),
|
||||
).toBeVisible();
|
||||
}
|
||||
|
||||
async verifyOrganizationsAccountSelectionStepLoaded(): Promise<void> {
|
||||
await this.verifyWizardModalOpen();
|
||||
await expect(
|
||||
this.page.getByText(
|
||||
/Confirm all accounts under this Organization you want to add to Prowler\./i,
|
||||
),
|
||||
).toBeVisible();
|
||||
}
|
||||
|
||||
async verifyOrganizationsLaunchStepLoaded(): Promise<void> {
|
||||
await this.verifyWizardModalOpen();
|
||||
await expect(this.page.getByText(/Accounts Connected!/i)).toBeVisible();
|
||||
}
|
||||
|
||||
async chooseOrganizationsScanSchedule(
|
||||
option: "daily" | "single",
|
||||
): Promise<void> {
|
||||
const trigger = this.page.getByRole("combobox");
|
||||
await trigger.click();
|
||||
|
||||
const optionName =
|
||||
option === "single"
|
||||
? "Run a single scan (no recurring schedule)"
|
||||
: "Scan Daily (every 24 hours)";
|
||||
|
||||
await this.page.getByRole("option", { name: optionName }).click();
|
||||
}
|
||||
|
||||
async fillAWSProviderDetails(data: AWSProviderData): Promise<void> {
|
||||
// Fill the AWS provider details
|
||||
const singleAccountButton = this.page.getByRole("button", {
|
||||
name: "Add A Single AWS Cloud Account",
|
||||
exact: true,
|
||||
});
|
||||
|
||||
if (await singleAccountButton.isVisible().catch(() => false)) {
|
||||
await singleAccountButton.click();
|
||||
}
|
||||
|
||||
await this.accountIdInput.fill(data.accountId);
|
||||
|
||||
@@ -599,116 +703,83 @@ export class ProvidersPage extends BasePage {
|
||||
}
|
||||
|
||||
async clickNext(): Promise<void> {
|
||||
// The wizard interface may use different labels for its primary action button on each step.
|
||||
// This function determines which button to click depending on the current URL and page content.
|
||||
await this.verifyWizardModalOpen();
|
||||
|
||||
// Get the current page URL
|
||||
const url = this.page.url();
|
||||
|
||||
// If on the "connect-account" step, click the "Next" button
|
||||
if (/\/providers\/connect-account/.test(url)) {
|
||||
await this.nextButton.click();
|
||||
const launchScanButton = this.page.getByRole("button", {
|
||||
name: "Launch scan",
|
||||
exact: true,
|
||||
});
|
||||
if (await launchScanButton.isVisible().catch(() => false)) {
|
||||
await launchScanButton.click();
|
||||
await this.handleLaunchScanCompletion();
|
||||
return;
|
||||
}
|
||||
|
||||
// If on the "add-credentials" step, check for "Save" and "Next" buttons
|
||||
if (/\/providers\/add-credentials/.test(url)) {
|
||||
// Some UI implementations use "Save" instead of "Next" for primary action
|
||||
const actionNames = [
|
||||
"Go to scans",
|
||||
"Authenticate",
|
||||
"Next",
|
||||
"Save",
|
||||
"Check connection",
|
||||
] as const;
|
||||
|
||||
if (await this.saveButton.count()) {
|
||||
await this.saveButton.click();
|
||||
return;
|
||||
}
|
||||
// If "Save" is not present, try clicking the "Next" button
|
||||
if (await this.nextButton.count()) {
|
||||
await this.nextButton.click();
|
||||
for (const actionName of actionNames) {
|
||||
const button = this.page.getByRole("button", {
|
||||
name: actionName,
|
||||
exact: true,
|
||||
});
|
||||
if (await button.isVisible().catch(() => false)) {
|
||||
await button.click();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If on the "test-connection" step, click the "Launch scan" button
|
||||
if (/\/providers\/test-connection/.test(url)) {
|
||||
const buttonByText = this.page
|
||||
.locator("button")
|
||||
.filter({ hasText: "Launch scan" });
|
||||
|
||||
await buttonByText.click();
|
||||
|
||||
// Wait for either success (redirect to scans) or error message to appear
|
||||
const errorMessage = this.page
|
||||
.locator(
|
||||
"div.border-border-error, div.bg-red-100, p.text-text-error-primary, p.text-danger",
|
||||
)
|
||||
.first();
|
||||
|
||||
// Helper to check and throw error if visible
|
||||
const checkAndThrowError = async (): Promise<void> => {
|
||||
const isErrorVisible = await errorMessage
|
||||
.isVisible()
|
||||
.catch(() => false);
|
||||
|
||||
if (isErrorVisible) {
|
||||
const errorText = await errorMessage.textContent();
|
||||
|
||||
throw new Error(
|
||||
`Test connection failed with error: ${errorText?.trim() || "Unknown error"}`,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
// Wait up to 15 seconds for either the error message or redirect
|
||||
await Promise.race([
|
||||
errorMessage.waitFor({ state: "visible", timeout: 15000 }),
|
||||
this.page.waitForURL(/\/scans/, { timeout: 15000 }),
|
||||
]);
|
||||
|
||||
// If we're still on test-connection page, check for error
|
||||
if (/\/providers\/test-connection/.test(this.page.url())) {
|
||||
await checkAndThrowError();
|
||||
}
|
||||
} catch (error) {
|
||||
await checkAndThrowError();
|
||||
throw error;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Fallback logic: try finding any common primary action buttons in expected order
|
||||
const candidates: Array<{ name: string | RegExp; exact?: boolean }> = [
|
||||
{ name: "Next", exact: true }, // Try the "Next" button (exact match to avoid Next.js dev tools)
|
||||
{ name: "Save", exact: true }, // Try the "Save" button
|
||||
{ name: "Launch scan" }, // Try the "Launch scan" button
|
||||
{ name: /Continue|Proceed/i }, // Try "Continue" or "Proceed" (case-insensitive)
|
||||
];
|
||||
|
||||
// Try each candidate name and click it if found
|
||||
for (const candidate of candidates) {
|
||||
// Exclude Next.js dev tools button by filtering out buttons with aria-haspopup attribute
|
||||
const btn = this.page
|
||||
.getByRole("button", {
|
||||
name: candidate.name,
|
||||
exact: candidate.exact,
|
||||
})
|
||||
.and(this.page.locator(":not([aria-haspopup])"));
|
||||
|
||||
if (await btn.count()) {
|
||||
await btn.click();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If none of the expected action buttons are present, throw an error
|
||||
throw new Error(
|
||||
"Could not find an actionable Next/Save/Launch scan button on this step",
|
||||
"Could not find an actionable primary button in the provider wizard modal.",
|
||||
);
|
||||
}
|
||||
|
||||
async selectCredentialsType(type: AWSCredentialType): Promise<void> {
|
||||
// Ensure we are on the add-credentials page where the selector exists
|
||||
private async handleLaunchScanCompletion(): Promise<void> {
|
||||
const errorMessage = this.page
|
||||
.locator(
|
||||
"div.border-border-error, div.bg-red-100, p.text-text-error-primary, p.text-danger",
|
||||
)
|
||||
.first();
|
||||
const goToScansButton = this.page.getByRole("button", {
|
||||
name: "Go to scans",
|
||||
exact: true,
|
||||
});
|
||||
|
||||
await expect(this.page).toHaveURL(/\/providers\/add-credentials/);
|
||||
try {
|
||||
await Promise.race([
|
||||
this.page.waitForURL(/\/scans/, { timeout: 30000 }),
|
||||
goToScansButton.waitFor({ state: "visible", timeout: 30000 }),
|
||||
errorMessage.waitFor({ state: "visible", timeout: 30000 }),
|
||||
]);
|
||||
} catch {
|
||||
// Continue and inspect visible state below.
|
||||
}
|
||||
|
||||
const isErrorVisible = await errorMessage.isVisible().catch(() => false);
|
||||
if (isErrorVisible) {
|
||||
const errorText = await errorMessage.textContent();
|
||||
throw new Error(
|
||||
`Test connection failed with error: ${errorText?.trim() || "Unknown error"}`,
|
||||
);
|
||||
}
|
||||
|
||||
const isGoToScansVisible = await goToScansButton
|
||||
.isVisible()
|
||||
.catch(() => false);
|
||||
if (isGoToScansVisible) {
|
||||
await goToScansButton.click();
|
||||
await this.page.waitForURL(/\/scans/, { timeout: 30000 });
|
||||
}
|
||||
}
|
||||
|
||||
async selectCredentialsType(type: AWSCredentialType): Promise<void> {
|
||||
await this.verifyWizardModalOpen();
|
||||
await expect(this.roleCredentialsRadio).toBeVisible();
|
||||
|
||||
if (type === AWS_CREDENTIAL_OPTIONS.AWS_ROLE_ARN) {
|
||||
await this.roleCredentialsRadio.click({ force: true });
|
||||
@@ -720,9 +791,8 @@ export class ProvidersPage extends BasePage {
|
||||
}
|
||||
|
||||
async selectM365CredentialsType(type: M365CredentialType): Promise<void> {
|
||||
// Ensure we are on the add-credentials page where the selector exists
|
||||
|
||||
await expect(this.page).toHaveURL(/\/providers\/add-credentials/);
|
||||
await this.verifyWizardModalOpen();
|
||||
await expect(this.m365StaticCredentialsRadio).toBeVisible();
|
||||
|
||||
if (type === M365_CREDENTIAL_OPTIONS.M365_CREDENTIALS) {
|
||||
await this.m365StaticCredentialsRadio.click({ force: true });
|
||||
@@ -734,9 +804,8 @@ export class ProvidersPage extends BasePage {
|
||||
}
|
||||
|
||||
async selectGCPCredentialsType(type: GCPCredentialType): Promise<void> {
|
||||
// Ensure we are on the add-credentials page where the selector exists
|
||||
|
||||
await expect(this.page).toHaveURL(/\/providers\/add-credentials/);
|
||||
await this.verifyWizardModalOpen();
|
||||
await expect(this.gcpServiceAccountRadio).toBeVisible();
|
||||
if (type === GCP_CREDENTIAL_OPTIONS.GCP_SERVICE_ACCOUNT) {
|
||||
await this.gcpServiceAccountRadio.click({ force: true });
|
||||
} else {
|
||||
@@ -745,9 +814,8 @@ export class ProvidersPage extends BasePage {
|
||||
}
|
||||
|
||||
async selectGitHubCredentialsType(type: GitHubCredentialType): Promise<void> {
|
||||
// Ensure we are on the add-credentials page where the selector exists
|
||||
|
||||
await expect(this.page).toHaveURL(/\/providers\/add-credentials/);
|
||||
await this.verifyWizardModalOpen();
|
||||
await expect(this.githubPersonalAccessTokenRadio).toBeVisible();
|
||||
|
||||
if (type === GITHUB_CREDENTIAL_OPTIONS.GITHUB_PERSONAL_ACCESS_TOKEN) {
|
||||
await this.githubPersonalAccessTokenRadio.click({ force: true });
|
||||
@@ -960,9 +1028,8 @@ export class ProvidersPage extends BasePage {
|
||||
async selectAlibabaCloudCredentialsType(
|
||||
type: AlibabaCloudCredentialType,
|
||||
): Promise<void> {
|
||||
// Ensure we are on the add-credentials page where the selector exists
|
||||
|
||||
await expect(this.page).toHaveURL(/\/providers\/add-credentials/);
|
||||
await this.verifyWizardModalOpen();
|
||||
await expect(this.alibabacloudStaticCredentialsRadio).toBeVisible();
|
||||
|
||||
if (type === ALIBABACLOUD_CREDENTIAL_OPTIONS.ALIBABACLOUD_CREDENTIALS) {
|
||||
await this.alibabacloudStaticCredentialsRadio.click({ force: true });
|
||||
@@ -1047,6 +1114,7 @@ export class ProvidersPage extends BasePage {
|
||||
// Verify the connect account page is loaded
|
||||
|
||||
await this.verifyPageHasProwlerTitle();
|
||||
await this.verifyWizardModalOpen();
|
||||
await expect(this.awsProviderRadio).toBeVisible();
|
||||
await expect(this.ociProviderRadio).toBeVisible();
|
||||
await expect(this.gcpProviderRadio).toBeVisible();
|
||||
@@ -1061,6 +1129,7 @@ export class ProvidersPage extends BasePage {
|
||||
// Verify the credentials page is loaded
|
||||
|
||||
await this.verifyPageHasProwlerTitle();
|
||||
await this.verifyWizardModalOpen();
|
||||
await expect(this.roleCredentialsRadio).toBeVisible();
|
||||
}
|
||||
|
||||
@@ -1115,7 +1184,7 @@ export class ProvidersPage extends BasePage {
|
||||
// Verify the launch scan page is loaded
|
||||
|
||||
await this.verifyPageHasProwlerTitle();
|
||||
await expect(this.page).toHaveURL(/\/providers\/test-connection/);
|
||||
await this.verifyWizardModalOpen();
|
||||
|
||||
// Verify the Launch scan button is visible
|
||||
const launchScanButton = this.page
|
||||
@@ -1202,12 +1271,24 @@ export class ProvidersPage extends BasePage {
|
||||
async verifyUpdateCredentialsPageLoaded(): Promise<void> {
|
||||
// Verify the update credentials page is loaded
|
||||
await this.verifyPageHasProwlerTitle();
|
||||
await expect(this.page).toHaveURL(/\/providers\/update-credentials/);
|
||||
await this.verifyWizardModalOpen();
|
||||
await expect(
|
||||
this.page.getByRole("button", { name: "Authenticate", exact: true }),
|
||||
).toBeVisible();
|
||||
}
|
||||
|
||||
async verifyTestConnectionPageLoaded(): Promise<void> {
|
||||
// Verify the test connection page is loaded
|
||||
await this.verifyPageHasProwlerTitle();
|
||||
await expect(this.page).toHaveURL(/\/providers\/test-connection/);
|
||||
await this.verifyWizardModalOpen();
|
||||
const testConnectionAction = this.page
|
||||
.getByRole("button", { name: "Launch scan", exact: true })
|
||||
.or(
|
||||
this.page.getByRole("button", {
|
||||
name: "Check connection",
|
||||
exact: true,
|
||||
}),
|
||||
);
|
||||
await expect(testConnectionAction).toBeVisible();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,10 +32,7 @@
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"vitest.config.ts",
|
||||
"vitest.setup.ts",
|
||||
"**/*.test.ts",
|
||||
"**/*.test.tsx"
|
||||
"vitest.config.ts"
|
||||
],
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
|
||||
Reference in New Issue
Block a user