mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-23 20:42:02 +00:00
test(ui): refactoring tests
Correct serial execution Best process for deleting a provider New scans page to manage properly when creating a provider
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
name: UI - E2E Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "PROWLER-182-kubernetes-add-and-connect-the-provider"
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
@@ -10,6 +13,7 @@ on:
|
||||
- 'ui/**'
|
||||
|
||||
jobs:
|
||||
|
||||
e2e-tests:
|
||||
if: github.repository == 'prowler-cloud/prowler'
|
||||
runs-on: ubuntu-latest
|
||||
@@ -33,9 +37,10 @@ jobs:
|
||||
E2E_M365_SECRET_ID: ${{ secrets.E2E_M365_SECRET_ID }}
|
||||
E2E_M365_TENANT_ID: ${{ secrets.E2E_M365_TENANT_ID }}
|
||||
E2E_M365_CERTIFICATE_CONTENT: ${{ secrets.E2E_M365_CERTIFICATE_CONTENT }}
|
||||
E2E_KUBERNETES_CONTEXT: ${{ secrets.E2E_KUBERNETES_CONTEXT }}
|
||||
E2E_KUBERNETES_KUBECONFIG_PATH: ${{ secrets.E2E_KUBERNETES_KUBECONFIG_PATH }}
|
||||
E2E_KUBERNETES_CONTEXT: 'kind-kind'
|
||||
E2E_KUBERNETES_KUBECONFIG_PATH: '/home/runner/.kube/config'
|
||||
E2E_NEW_PASSWORD: ${{ secrets.E2E_NEW_PASSWORD }}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
@@ -115,4 +120,4 @@ jobs:
|
||||
run: |
|
||||
echo "Shutting down services..."
|
||||
docker compose down -v || true
|
||||
echo "Cleanup completed"
|
||||
echo "Cleanup completed"
|
||||
+3
-8
@@ -147,7 +147,9 @@ export async function authenticateAndSaveState(
|
||||
storagePath: string,
|
||||
) {
|
||||
if (!email || !password) {
|
||||
throw new Error('Email and password are required for authentication and save state');
|
||||
throw new Error(
|
||||
"Email and password are required for authentication and save state",
|
||||
);
|
||||
}
|
||||
|
||||
// Create SignInPage instance
|
||||
@@ -188,10 +190,3 @@ export async function verifySessionValid(page: Page) {
|
||||
expect(session.refreshToken).toBeTruthy();
|
||||
return session;
|
||||
}
|
||||
|
||||
export async function deleteProviderIfExists(page: Page, accountId: string) {
|
||||
const providersPage = new ProvidersPage(page);
|
||||
if (await providersPage.verifyProviderExists(accountId)) {
|
||||
await providersPage.actionDeleteProvider(accountId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,10 +239,11 @@ export class ProvidersPage extends BasePage {
|
||||
this.accessKeyIdInput = page.getByRole("textbox", { name: "Access Key ID" });
|
||||
this.secretAccessKeyInput = page.getByRole("textbox", { name: "Secret Access Key" });
|
||||
|
||||
// Delete button
|
||||
this.deleteProviderConfirmationButton = page.locator(
|
||||
'button[aria-label="Delete"]',
|
||||
);
|
||||
// Delete button in confirmation modal
|
||||
this.deleteProviderConfirmationButton = page.getByRole("button", {
|
||||
name: "Delete",
|
||||
exact: true,
|
||||
});
|
||||
}
|
||||
|
||||
async goto(): Promise<void> {
|
||||
@@ -365,6 +366,43 @@ export class ProvidersPage extends BasePage {
|
||||
|
||||
await buttonByText.click();
|
||||
await this.waitForPageLoad();
|
||||
|
||||
// Wait for either success (redirect to scans) or error message to appear
|
||||
// The error container has multiple p.text-danger elements, we want the first one with the technical error
|
||||
const errorMessage = this.page.locator("p.text-danger").first();
|
||||
|
||||
try {
|
||||
// Wait up to 15 seconds for either the error message or redirect
|
||||
await Promise.race([
|
||||
// Wait for error message to appear
|
||||
errorMessage.waitFor({ state: "visible", timeout: 15000 }),
|
||||
// Wait for redirect to scans page (success case)
|
||||
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())) {
|
||||
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"}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// If timeout or other error, check if error message is present
|
||||
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"}`,
|
||||
);
|
||||
}
|
||||
// Re-throw original error if no error message found
|
||||
throw error;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -534,8 +572,18 @@ export class ProvidersPage extends BasePage {
|
||||
// Verify the M365 credentials page is loaded
|
||||
|
||||
await expect(this.page).toHaveTitle(/Prowler/);
|
||||
await expect(this.m365StaticCredentialsRadio).toBeVisible();
|
||||
await expect(this.m365CertificateCredentialsRadio).toBeVisible();
|
||||
await expect(this.m365ClientIdInput).toBeVisible();
|
||||
await expect(this.m365ClientSecretInput).toBeVisible();
|
||||
await expect(this.m365TenantIdInput).toBeVisible();
|
||||
}
|
||||
|
||||
async verifyM365CertificateCredentialsPageLoaded(): Promise<void> {
|
||||
// Verify the M365 certificate credentials page is loaded
|
||||
|
||||
await expect(this.page).toHaveTitle(/Prowler/);
|
||||
await expect(this.m365ClientIdInput).toBeVisible();
|
||||
await expect(this.m365TenantIdInput).toBeVisible();
|
||||
await expect(this.m365CertificateContentInput).toBeVisible();
|
||||
}
|
||||
|
||||
async verifyKubernetesCredentialsPageLoaded(): Promise<void> {
|
||||
@@ -559,20 +607,10 @@ export class ProvidersPage extends BasePage {
|
||||
await expect(launchScanButton).toBeVisible();
|
||||
}
|
||||
|
||||
async verifyProviderExists(providerUID: string): Promise<boolean> {
|
||||
// Filter search by providerUID
|
||||
|
||||
await this.filterProvidersByProviderUID(providerUID);
|
||||
// Verify if table has 1 row
|
||||
if (!(await this.verifySingleRowForProviderUID(providerUID))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async verifyLoadProviderPageAfterNewProvider(): Promise<void> {
|
||||
// Verify the provider page is loaded
|
||||
|
||||
await this.waitForPageLoad();
|
||||
await expect(this.page).toHaveTitle(/Prowler/);
|
||||
await expect(this.providersTable).toBeVisible();
|
||||
}
|
||||
@@ -593,95 +631,119 @@ export class ProvidersPage extends BasePage {
|
||||
return true;
|
||||
}
|
||||
|
||||
async filterProvidersByProviderUID(providerUID: string): Promise<void> {
|
||||
// Filter providers by providerUID
|
||||
async deleteProviderIfExists(providerUID: string): Promise<void> {
|
||||
// Delete the provider if it exists
|
||||
|
||||
// Go to the providers page
|
||||
// Navigate to providers page
|
||||
await this.goto();
|
||||
await expect(this.providersTable).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// Verify the providers table is visible
|
||||
await expect(this.providersTable).toBeVisible();
|
||||
|
||||
// Get the search input
|
||||
// Find and use the search input to filter the table
|
||||
const searchInput = this.page.getByPlaceholder(/search|filter/i);
|
||||
if (!searchInput) {
|
||||
throw new Error("No search input available");
|
||||
}
|
||||
|
||||
// Clear existing content and type the providerUID
|
||||
await searchInput.fill("");
|
||||
await searchInput.type(providerUID, { delay: 20 });
|
||||
|
||||
// Try to submit the filter if the input acts on Enter
|
||||
try {
|
||||
await searchInput.press("Enter");
|
||||
} catch (error) {
|
||||
// Enter press might not be needed for all UIs
|
||||
}
|
||||
|
||||
// Wait for table to update
|
||||
await expect(searchInput).toBeVisible({ timeout: 5000 });
|
||||
|
||||
// Clear and search for the specific provider
|
||||
await searchInput.clear();
|
||||
await searchInput.fill(providerUID);
|
||||
await searchInput.press("Enter");
|
||||
|
||||
// Wait for the table to finish loading/filtering
|
||||
await this.waitForPageLoad();
|
||||
}
|
||||
|
||||
// Additional wait for React table to re-render with the server-filtered data
|
||||
// The filtering happens on the server, but the table component needs time
|
||||
// to process the response and update the DOM after network idle
|
||||
await this.page.waitForTimeout(1500);
|
||||
|
||||
async actionDeleteProvider(providerUID: string): Promise<void> {
|
||||
// Delete the provider
|
||||
// Get all rows from the table
|
||||
const allRows = this.providersTable.locator("tbody tr");
|
||||
|
||||
// Helper function to check if a row is the "No results" row
|
||||
const isNoResultsRow = async (row: Locator): Promise<boolean> => {
|
||||
const text = await row.textContent();
|
||||
return text?.includes("No results") || text?.includes("No data") || false;
|
||||
};
|
||||
|
||||
// Filter search by providerUID
|
||||
await this.filterProvidersByProviderUID(providerUID);
|
||||
// Helper function to find the row with the specific UID
|
||||
const findProviderRow = async (): Promise<Locator | null> => {
|
||||
const count = await allRows.count();
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const row = allRows.nth(i);
|
||||
|
||||
// Skip "No results" rows
|
||||
if (await isNoResultsRow(row)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if this row contains the UID in the UID column (column 3)
|
||||
const uidCell = row.locator("td").nth(3);
|
||||
const uidText = await uidCell.textContent();
|
||||
|
||||
if (uidText?.includes(providerUID)) {
|
||||
return row;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
// Verify the providers table is visible
|
||||
await expect(this.providersTable).toBeVisible();
|
||||
// Wait for filtering to complete (max 0 or 1 data rows)
|
||||
await expect(async () => {
|
||||
const targetRow = await findProviderRow();
|
||||
const count = await allRows.count();
|
||||
|
||||
// Count only real data rows (not "No results")
|
||||
let dataRowCount = 0;
|
||||
for (let i = 0; i < count; i++) {
|
||||
if (!(await isNoResultsRow(allRows.nth(i)))) {
|
||||
dataRowCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// Should have 0 or 1 data row
|
||||
expect(dataRowCount).toBeLessThanOrEqual(1);
|
||||
}).toPass({ timeout: 20000 });
|
||||
|
||||
// Find the first row in the filtered results
|
||||
const firstRow = this.providersTable.locator("tbody tr").first();
|
||||
// Find the provider row
|
||||
const targetRow = await findProviderRow();
|
||||
|
||||
if (!targetRow) {
|
||||
// Provider not found, nothing to delete
|
||||
// Navigate back to providers page to ensure clean state
|
||||
await this.goto();
|
||||
await expect(this.providersTable).toBeVisible({ timeout: 10000 });
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify the first row is visible
|
||||
await expect(firstRow).toBeVisible();
|
||||
|
||||
// Get button in the last cell
|
||||
const lastCell = firstRow.locator("td").last();
|
||||
const actionButton = lastCell.locator("button").first();
|
||||
|
||||
// Verify the action button is visible
|
||||
// Find and click the action button (last cell = actions column)
|
||||
const actionButton = targetRow.locator("td").last().locator("button").first();
|
||||
await expect(actionButton).toBeVisible({ timeout: 5000 });
|
||||
await actionButton.click();
|
||||
|
||||
// Wait for the dropdown menu to appear
|
||||
await this.page.waitForSelector('[role="menu"], [role="menuitem"]', {
|
||||
timeout: 5000,
|
||||
});
|
||||
|
||||
// Find the delete menu item
|
||||
// Wait for dropdown menu to appear and find delete option
|
||||
const deleteMenuItem = this.page.getByRole("menuitem", {
|
||||
name: /delete.*provider/i,
|
||||
});
|
||||
await expect(deleteMenuItem).toBeVisible({ timeout: 5000 });
|
||||
await deleteMenuItem.click();
|
||||
|
||||
// Verify the delete menu item is visible
|
||||
await expect(deleteMenuItem).toBeVisible();
|
||||
// Wait for confirmation modal to appear
|
||||
const modal = this.page.locator('[role="dialog"], .modal, [data-testid*="modal"]').first();
|
||||
await expect(modal).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// Click the delete menu item with force to handle unstable elements
|
||||
await deleteMenuItem.click({ force: true });
|
||||
// Find and click the delete confirmation button
|
||||
await expect(this.deleteProviderConfirmationButton).toBeVisible({ timeout: 5000 });
|
||||
await this.deleteProviderConfirmationButton.click();
|
||||
|
||||
// Wait for the delete confirmation modal to appear
|
||||
await this.page.waitForSelector(
|
||||
'[role="dialog"], .modal, [data-testid*="modal"]',
|
||||
{ timeout: 10000 },
|
||||
);
|
||||
// Wait for modal to close (this indicates deletion was initiated)
|
||||
await expect(modal).not.toBeVisible({ timeout: 10000 });
|
||||
|
||||
// Find the delete confirmation button with multiple approaches
|
||||
let deleteButton = this.deleteProviderConfirmationButton;
|
||||
|
||||
await expect(deleteButton).toBeVisible();
|
||||
|
||||
// Click the delete button with force to handle unstable elements
|
||||
await deleteButton.click({ force: true });
|
||||
|
||||
// Wait for the modal to disappear and the page to update
|
||||
const modalToCheck = this.page
|
||||
.locator('[role="dialog"], .modal, [data-testid*="modal"]')
|
||||
.first();
|
||||
|
||||
// Verify the modal is not visible
|
||||
await expect(modalToCheck).not.toBeVisible();
|
||||
// Wait for page to reload
|
||||
await this.waitForPageLoad();
|
||||
|
||||
// Navigate back to providers page to ensure clean state
|
||||
await this.goto();
|
||||
await expect(this.providersTable).toBeVisible({ timeout: 10000 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,13 +15,14 @@ import {
|
||||
KubernetesProviderCredential,
|
||||
KUBERNETES_CREDENTIAL_OPTIONS,
|
||||
} from "./providers-page";
|
||||
import { ScansPage } from "../scans/scans-page";
|
||||
import fs from "fs";
|
||||
|
||||
test.describe.serial("Add Provider", () => {
|
||||
test.describe("Add AWS Provider", () => {
|
||||
test.describe("Add Provider", () => {
|
||||
test.describe.serial("Add AWS Provider", () => {
|
||||
// Providers page object
|
||||
let providersPage: ProvidersPage;
|
||||
|
||||
let scansPage: ScansPage;
|
||||
// Test data from environment variables
|
||||
const accountId = process.env.E2E_AWS_PROVIDER_ACCOUNT_ID;
|
||||
const accessKey = process.env.E2E_AWS_PROVIDER_ACCESS_KEY;
|
||||
@@ -39,7 +40,7 @@ test.describe.serial("Add Provider", () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
providersPage = new ProvidersPage(page);
|
||||
// Clean up existing provider to ensure clean test state
|
||||
await helpers.deleteProviderIfExists(page, accountId);
|
||||
await providersPage.deleteProviderIfExists(accountId);
|
||||
});
|
||||
|
||||
// Use admin authentication for provider management
|
||||
@@ -108,7 +109,8 @@ test.describe.serial("Add Provider", () => {
|
||||
await providersPage.clickNext();
|
||||
|
||||
// Wait for redirect to provider page
|
||||
await providersPage.verifyLoadProviderPageAfterNewProvider();
|
||||
scansPage = new ScansPage(page);
|
||||
await scansPage.verifyPageLoaded();
|
||||
},
|
||||
);
|
||||
|
||||
@@ -176,14 +178,16 @@ test.describe.serial("Add Provider", () => {
|
||||
await providersPage.clickNext();
|
||||
|
||||
// Wait for redirect to provider page
|
||||
await providersPage.verifyLoadProviderPageAfterNewProvider();
|
||||
scansPage = new ScansPage(page);
|
||||
await scansPage.verifyPageLoaded();
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test.describe("Add AZURE Provider", () => {
|
||||
test.describe.serial("Add AZURE Provider", () => {
|
||||
// Providers page object
|
||||
let providersPage: ProvidersPage;
|
||||
let scansPage: ScansPage;
|
||||
|
||||
// Test data from environment variables
|
||||
const subscriptionId = process.env.E2E_AZURE_SUBSCRIPTION_ID;
|
||||
@@ -202,7 +206,7 @@ test.describe.serial("Add Provider", () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
providersPage = new ProvidersPage(page);
|
||||
// Clean up existing provider to ensure clean test state
|
||||
await helpers.deleteProviderIfExists(page, subscriptionId);
|
||||
await providersPage.deleteProviderIfExists(subscriptionId);
|
||||
});
|
||||
|
||||
// Use admin authentication for provider management
|
||||
@@ -258,15 +262,17 @@ test.describe.serial("Add Provider", () => {
|
||||
await providersPage.verifyLaunchScanPageLoaded();
|
||||
await providersPage.clickNext();
|
||||
|
||||
// Wait for redirect to provider page
|
||||
await providersPage.verifyLoadProviderPageAfterNewProvider();
|
||||
// Wait for redirect to scan page
|
||||
scansPage = new ScansPage(page);
|
||||
await scansPage.verifyPageLoaded();
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test.describe("Add M365 Provider", () => {
|
||||
test.describe.serial("Add M365 Provider", () => {
|
||||
// Providers page object
|
||||
let providersPage: ProvidersPage;
|
||||
let scansPage: ScansPage;
|
||||
|
||||
// Test data from environment variables
|
||||
const domainId = process.env.E2E_M365_DOMAIN_ID;
|
||||
@@ -284,7 +290,7 @@ test.describe.serial("Add Provider", () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
providersPage = new ProvidersPage(page);
|
||||
// Clean up existing provider to ensure clean test state
|
||||
await helpers.deleteProviderIfExists(page, domainId);
|
||||
await providersPage.deleteProviderIfExists(domainId);
|
||||
});
|
||||
|
||||
// Use admin authentication for provider management
|
||||
@@ -342,6 +348,8 @@ test.describe.serial("Add Provider", () => {
|
||||
await providersPage.selectM365CredentialsType(
|
||||
M365_CREDENTIAL_OPTIONS.M365_CREDENTIALS,
|
||||
);
|
||||
|
||||
// Verify M365 credentials page is loaded
|
||||
await providersPage.verifyM365CredentialsPageLoaded();
|
||||
|
||||
// Fill static credentials details
|
||||
@@ -352,8 +360,9 @@ test.describe.serial("Add Provider", () => {
|
||||
await providersPage.verifyLaunchScanPageLoaded();
|
||||
await providersPage.clickNext();
|
||||
|
||||
// Wait for redirect to provider page
|
||||
await providersPage.verifyLoadProviderPageAfterNewProvider();
|
||||
// Wait for redirect to scan page
|
||||
scansPage = new ScansPage(page);
|
||||
await scansPage.verifyPageLoaded();
|
||||
},
|
||||
);
|
||||
|
||||
@@ -412,7 +421,9 @@ test.describe.serial("Add Provider", () => {
|
||||
await providersPage.selectM365CredentialsType(
|
||||
M365_CREDENTIAL_OPTIONS.M365_CERTIFICATE_CREDENTIALS,
|
||||
);
|
||||
await providersPage.verifyM365CredentialsPageLoaded();
|
||||
|
||||
// Verify M365 certificate credentials page is loaded
|
||||
await providersPage.verifyM365CertificateCredentialsPageLoaded();
|
||||
|
||||
// Fill static credentials details
|
||||
await providersPage.fillM365CertificateCredentials(m365Credentials);
|
||||
@@ -422,15 +433,17 @@ test.describe.serial("Add Provider", () => {
|
||||
await providersPage.verifyLaunchScanPageLoaded();
|
||||
await providersPage.clickNext();
|
||||
|
||||
// Wait for redirect to provider page
|
||||
await providersPage.verifyLoadProviderPageAfterNewProvider();
|
||||
// Wait for redirect to scan page
|
||||
scansPage = new ScansPage(page);
|
||||
await scansPage.verifyPageLoaded();
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test.describe("Add Kubernetes Provider", () => {
|
||||
test.describe.skip("Add Kubernetes Provider", () => {
|
||||
// Providers page object
|
||||
let providersPage: ProvidersPage;
|
||||
let scansPage: ScansPage;
|
||||
|
||||
// Test data from environment variables
|
||||
const context = process.env.E2E_KUBERNETES_CONTEXT;
|
||||
@@ -443,19 +456,12 @@ test.describe.serial("Add Provider", () => {
|
||||
);
|
||||
}
|
||||
|
||||
// Read kubeconfig content from file
|
||||
const kubeconfigContent = fs.readFileSync(kubeconfigPath, 'utf8');
|
||||
|
||||
// Verify kubeconfig file exists
|
||||
if (!fs.existsSync(kubeconfigPath)) {
|
||||
throw new Error(`Kubeconfig file not found at ${kubeconfigPath}`);
|
||||
}
|
||||
|
||||
// Setup before each test
|
||||
test.beforeEach(async ({ page }) => {
|
||||
providersPage = new ProvidersPage(page);
|
||||
// Clean up existing provider to ensure clean test state
|
||||
await helpers.deleteProviderIfExists(page, context);
|
||||
await providersPage.deleteProviderIfExists(context);
|
||||
});
|
||||
|
||||
// Use admin authentication for provider management
|
||||
@@ -474,6 +480,21 @@ test.describe.serial("Add Provider", () => {
|
||||
],
|
||||
},
|
||||
async ({ page }) => {
|
||||
// Verify kubeconfig file exists
|
||||
if (!fs.existsSync(kubeconfigPath)) {
|
||||
throw new Error(`Kubeconfig file not found at ${kubeconfigPath}`);
|
||||
}
|
||||
|
||||
// Read kubeconfig content from file
|
||||
let kubeconfigContent: string;
|
||||
try {
|
||||
kubeconfigContent = fs.readFileSync(kubeconfigPath, "utf8");
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Failed to read kubeconfig file at ${kubeconfigPath}: ${error}`,
|
||||
);
|
||||
}
|
||||
|
||||
// Prepare test data for Kubernetes provider
|
||||
const kubernetesProviderData: KubernetesProviderData = {
|
||||
context: context,
|
||||
@@ -498,7 +519,9 @@ test.describe.serial("Add Provider", () => {
|
||||
await providersPage.selectKubernetesProvider();
|
||||
|
||||
// Fill provider details
|
||||
await providersPage.fillKubernetesProviderDetails(kubernetesProviderData);
|
||||
await providersPage.fillKubernetesProviderDetails(
|
||||
kubernetesProviderData,
|
||||
);
|
||||
await providersPage.clickNext();
|
||||
|
||||
// Verify credentials page is loaded
|
||||
@@ -513,9 +536,9 @@ test.describe.serial("Add Provider", () => {
|
||||
await providersPage.clickNext();
|
||||
|
||||
// Wait for redirect to provider page
|
||||
await providersPage.verifyLoadProviderPageAfterNewProvider();
|
||||
scansPage = new ScansPage(page);
|
||||
await scansPage.verifyPageLoaded();
|
||||
},
|
||||
);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Page, Locator, expect } from "@playwright/test";
|
||||
import { BasePage } from "../base-page";
|
||||
|
||||
// Scan page
|
||||
export class ScansPage extends BasePage {
|
||||
|
||||
// Main content elements
|
||||
readonly scanTable: Locator;
|
||||
constructor(page: Page) {
|
||||
super(page);
|
||||
|
||||
// Main content elements
|
||||
this.scanTable = page.locator("table");
|
||||
|
||||
}
|
||||
|
||||
// Navigation methods
|
||||
async goto(): Promise<void> {
|
||||
await super.goto("/scans");
|
||||
}
|
||||
|
||||
// Verification methods
|
||||
async verifyPageLoaded(): Promise<void> {
|
||||
await expect(this.page).toHaveTitle(/Prowler/);
|
||||
await expect(this.scanTable).toBeVisible();
|
||||
await this.waitForPageLoad();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user