mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-23 12:31:54 +00:00
test(ui): add Kubernetes provider management E2E tests
- Introduced support for adding Kubernetes providers with kubeconfig content in the UI. - Updated the ProvidersPage interface to include fields and methods for Kubernetes provider details and credentials. - Enhanced E2E tests to validate the complete flow of adding a new Kubernetes provider, ensuring proper handling of context and kubeconfig content. - Included necessary environment variable checks for Kubernetes context and kubeconfig path to maintain test integrity.
This commit is contained in:
@@ -33,6 +33,8 @@ 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 }}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
|
||||
@@ -19,6 +19,12 @@ export interface M365ProviderData {
|
||||
alias?: string;
|
||||
}
|
||||
|
||||
// Kubernetes provider data
|
||||
export interface KubernetesProviderData {
|
||||
context: string;
|
||||
alias?: string;
|
||||
}
|
||||
|
||||
// AWS credential options
|
||||
export const AWS_CREDENTIAL_OPTIONS = {
|
||||
AWS_ROLE_ARN: "role",
|
||||
@@ -71,6 +77,19 @@ export interface M365ProviderCredential {
|
||||
certificateContent?:string;
|
||||
}
|
||||
|
||||
// Kubernetes credential options
|
||||
export const KUBERNETES_CREDENTIAL_OPTIONS = {
|
||||
KUBECONFIG_CONTENT: "kubeconfig"
|
||||
} as const;
|
||||
|
||||
// Kubernetes credential type
|
||||
type KubernetesCredentialType = (typeof KUBERNETES_CREDENTIAL_OPTIONS)[keyof typeof KUBERNETES_CREDENTIAL_OPTIONS];
|
||||
|
||||
// Kubernetes provider credential
|
||||
export interface KubernetesProviderCredential {
|
||||
type: KubernetesCredentialType;
|
||||
kubeconfigContent:string;
|
||||
}
|
||||
|
||||
// Providers page
|
||||
export class ProvidersPage extends BasePage {
|
||||
@@ -124,6 +143,10 @@ export class ProvidersPage extends BasePage {
|
||||
readonly m365TenantIdInput: Locator;
|
||||
readonly m365CertificateContentInput: Locator;
|
||||
|
||||
// Kubernetes provider form elements
|
||||
readonly kubernetesContextInput: Locator;
|
||||
readonly kubernetesKubeconfigContentInput: Locator;
|
||||
|
||||
// Delete button
|
||||
readonly deleteProviderConfirmationButton: Locator;
|
||||
|
||||
@@ -131,9 +154,7 @@ export class ProvidersPage extends BasePage {
|
||||
super(page);
|
||||
|
||||
// Button to add a new cloud provider
|
||||
this.addProviderButton = page.getByText("Add Cloud Provider", {
|
||||
exact: true,
|
||||
});
|
||||
this.addProviderButton = page.getByRole("button", { name: "Add Cloud Provider", exact: true });
|
||||
|
||||
// Table displaying existing providers
|
||||
this.providersTable = page.getByRole("table");
|
||||
@@ -157,23 +178,27 @@ export class ProvidersPage extends BasePage {
|
||||
this.githubProviderRadio = page.getByRole("radio", { name: /GitHub/i });
|
||||
|
||||
// AWS provider form inputs
|
||||
this.accountIdInput = page.getByLabel("Account ID");
|
||||
this.accountIdInput = page.getByRole("textbox", { name: "Account ID" });
|
||||
|
||||
// AZURE provider form inputs
|
||||
this.azureSubscriptionIdInput = page.getByLabel("Subscription ID");
|
||||
this.azureClientIdInput = page.getByLabel("Client ID");
|
||||
this.azureClientSecretInput = page.getByLabel("Client Secret");
|
||||
this.azureTenantIdInput = page.getByLabel("Tenant ID");
|
||||
this.azureSubscriptionIdInput = page.getByRole("textbox", { name: "Subscription ID" });
|
||||
this.azureClientIdInput = page.getByRole("textbox", { name: "Client ID" });
|
||||
this.azureClientSecretInput = page.getByRole("textbox", { name: "Client Secret" });
|
||||
this.azureTenantIdInput = page.getByRole("textbox", { name: "Tenant ID" });
|
||||
|
||||
// M365 provider form inputs
|
||||
this.m365domainIdInput = page.getByLabel("Domain ID");
|
||||
this.m365ClientIdInput = page.getByLabel("Client ID");
|
||||
this.m365ClientSecretInput = page.getByLabel("Client Secret");
|
||||
this.m365TenantIdInput = page.getByLabel("Tenant ID");
|
||||
this.m365CertificateContentInput = page.getByLabel("Certificate Content");
|
||||
this.m365domainIdInput = page.getByRole("textbox", { name: "Domain ID" });
|
||||
this.m365ClientIdInput = page.getByRole("textbox", { name: "Client ID" });
|
||||
this.m365ClientSecretInput = page.getByRole("textbox", { name: "Client Secret" });
|
||||
this.m365TenantIdInput = page.getByRole("textbox", { name: "Tenant ID" });
|
||||
this.m365CertificateContentInput = page.getByRole("textbox", { name: "Certificate Content" });
|
||||
|
||||
// Kubernetes provider form inputs
|
||||
this.kubernetesContextInput = page.getByRole("textbox", { name: "Context" });
|
||||
this.kubernetesKubeconfigContentInput = page.getByRole("textbox", { name: "Kubeconfig Content" });
|
||||
|
||||
// Alias input
|
||||
this.aliasInput = page.getByLabel("Provider alias (optional)");
|
||||
this.aliasInput = page.getByRole("textbox", { name: "Provider alias (optional)" });
|
||||
|
||||
// Navigation buttons in the form (next and back)
|
||||
this.nextButton = page
|
||||
@@ -208,12 +233,12 @@ export class ProvidersPage extends BasePage {
|
||||
});
|
||||
|
||||
// Inputs for IAM Role credentials
|
||||
this.roleArnInput = page.getByLabel("Role ARN");
|
||||
this.externalIdInput = page.getByLabel("External ID");
|
||||
this.roleArnInput = page.getByRole("textbox", { name: "Role ARN" });
|
||||
this.externalIdInput = page.getByRole("textbox", { name: "External ID" });
|
||||
|
||||
// Inputs for static credentials
|
||||
this.accessKeyIdInput = page.getByLabel("Access Key ID");
|
||||
this.secretAccessKeyInput = page.getByLabel("Secret Access Key");
|
||||
this.accessKeyIdInput = page.getByRole("textbox", { name: "Access Key ID" });
|
||||
this.secretAccessKeyInput = page.getByRole("textbox", { name: "Secret Access Key" });
|
||||
|
||||
// Delete button
|
||||
this.deleteProviderConfirmationButton = page.locator(
|
||||
@@ -255,6 +280,14 @@ export class ProvidersPage extends BasePage {
|
||||
await this.waitForPageLoad();
|
||||
}
|
||||
|
||||
async selectKubernetesProvider(): Promise<void> {
|
||||
// Select the Kubernetes provider
|
||||
|
||||
await this.kubernetesProviderRadio.click({ force: true });
|
||||
await this.waitForPageLoad();
|
||||
}
|
||||
|
||||
|
||||
async fillAWSProviderDetails(data: AWSProviderData): Promise<void> {
|
||||
// Fill the AWS provider details
|
||||
|
||||
@@ -285,6 +318,15 @@ export class ProvidersPage extends BasePage {
|
||||
}
|
||||
}
|
||||
|
||||
async fillKubernetesProviderDetails(data: KubernetesProviderData): Promise<void> {
|
||||
// Fill the Kubernetes provider details
|
||||
|
||||
await this.kubernetesContextInput.fill(data.context);
|
||||
if (data.alias) {
|
||||
await this.aliasInput.fill(data.alias);
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
@@ -458,6 +500,14 @@ export class ProvidersPage extends BasePage {
|
||||
}
|
||||
}
|
||||
|
||||
async fillKubernetesCredentials(credentials: KubernetesProviderCredential): Promise<void> {
|
||||
// Fill the Kubernetes credentials form
|
||||
|
||||
if (credentials.kubeconfigContent) {
|
||||
await this.kubernetesKubeconfigContentInput.fill(credentials.kubeconfigContent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async verifyPageLoaded(): Promise<void> {
|
||||
// Verify the providers page is loaded
|
||||
@@ -489,6 +539,12 @@ export class ProvidersPage extends BasePage {
|
||||
await expect(this.m365CertificateCredentialsRadio).toBeVisible();
|
||||
}
|
||||
|
||||
async verifyKubernetesCredentialsPageLoaded(): Promise<void> {
|
||||
// Verify the Kubernetes credentials page is loaded
|
||||
|
||||
await expect(this.page).toHaveTitle(/Prowler/);
|
||||
await expect(this.kubernetesContextInput).toBeVisible();
|
||||
}
|
||||
|
||||
|
||||
async verifyLaunchScanPageLoaded(): Promise<void> {
|
||||
|
||||
@@ -265,3 +265,61 @@
|
||||
- Provider cleanup performed before each test to ensure clean state
|
||||
- Requires valid Microsoft 365 tenant with certificate-based authentication
|
||||
- Certificate must be properly configured and have sufficient permissions for security scanning
|
||||
|
||||
---
|
||||
|
||||
## Test Case: `PROVIDER-E2E-006` - Add Kubernetes Provider with Kubeconfig Content
|
||||
|
||||
**Priority:** `critical`
|
||||
|
||||
**Tags:**
|
||||
|
||||
- type → @e2e, @serial
|
||||
- feature → @providers
|
||||
- provider → @kubernetes
|
||||
|
||||
**Description/Objective:** Validates the complete flow of adding a new Kubernetes provider using kubeconfig content authentication
|
||||
|
||||
**Preconditions:**
|
||||
|
||||
- Admin user authentication required (admin.auth.setup setup)
|
||||
- Environment variables configured: E2E_KUBERNETES_CONTEXT, E2E_KUBERNETES_KUBECONFIG_PATH
|
||||
- Kubeconfig file must exist at the specified path
|
||||
- Remove any existing provider with the same Context before starting the test
|
||||
- This test must be run serially and never in parallel with other tests, as it requires the Context not to be already registered beforehand.
|
||||
|
||||
### Flow Steps:
|
||||
|
||||
1. Navigate to providers page
|
||||
2. Click "Add Provider" button
|
||||
3. Select Kubernetes provider type
|
||||
4. Fill provider details (context and alias)
|
||||
5. Verify credentials page is loaded
|
||||
6. Fill Kubernetes credentials (kubeconfig content)
|
||||
7. Launch initial scan
|
||||
8. Verify redirect to provider management page
|
||||
|
||||
### Expected Result:
|
||||
|
||||
- Kubernetes provider successfully added with kubeconfig content
|
||||
- Initial scan launched successfully
|
||||
- User redirected to provider details page
|
||||
|
||||
### Key verification points:
|
||||
|
||||
- Provider page loads correctly
|
||||
- Connect account page displays Kubernetes option
|
||||
- Provider details form accepts context and alias
|
||||
- Credentials page loads with kubeconfig content field
|
||||
- Kubeconfig content is properly filled in the correct field
|
||||
- Launch scan page appears
|
||||
- Successful redirect to provider page after scan launch
|
||||
|
||||
### Notes:
|
||||
|
||||
- Test uses environment variables for Kubernetes context and kubeconfig file path
|
||||
- Kubeconfig content is read from file and used for authentication
|
||||
- Provider cleanup performed before each test to ensure clean state
|
||||
- Requires valid Kubernetes cluster with accessible kubeconfig
|
||||
- Kubeconfig must have sufficient permissions for security scanning
|
||||
- Test validates that kubeconfig content goes to the correct field (not the context field)
|
||||
|
||||
@@ -11,7 +11,11 @@ import {
|
||||
M365ProviderData,
|
||||
M365ProviderCredential,
|
||||
M365_CREDENTIAL_OPTIONS,
|
||||
KubernetesProviderData,
|
||||
KubernetesProviderCredential,
|
||||
KUBERNETES_CREDENTIAL_OPTIONS,
|
||||
} from "./providers-page";
|
||||
import fs from "fs";
|
||||
|
||||
test.describe.serial("AddProvider", () => {
|
||||
test.describe("Add AWS Provider", () => {
|
||||
@@ -423,4 +427,95 @@ test.describe.serial("AddProvider", () => {
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test.describe("Add Kubernetes Provider", () => {
|
||||
// Providers page object
|
||||
let providersPage: ProvidersPage;
|
||||
|
||||
// Test data from environment variables
|
||||
const context = process.env.E2E_KUBERNETES_CONTEXT;
|
||||
const kubeconfigPath = process.env.E2E_KUBERNETES_KUBECONFIG_PATH;
|
||||
|
||||
// Validate required environment variables
|
||||
if (!context || !kubeconfigPath) {
|
||||
throw new Error(
|
||||
"E2E_KUBERNETES_CONTEXT and E2E_KUBERNETES_KUBECONFIG_PATH environment variables are not set",
|
||||
);
|
||||
}
|
||||
|
||||
// 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);
|
||||
});
|
||||
|
||||
// Use admin authentication for provider management
|
||||
test.use({ storageState: "playwright/.auth/admin_user.json" });
|
||||
|
||||
test(
|
||||
"should add a new Kubernetes provider with kubeconfig context",
|
||||
{
|
||||
tag: [
|
||||
"@critical",
|
||||
"@e2e",
|
||||
"@providers",
|
||||
"@kubernetes",
|
||||
"@serial",
|
||||
"@PROVIDER-E2E-006",
|
||||
],
|
||||
},
|
||||
async ({ page }) => {
|
||||
// Prepare test data for Kubernetes provider
|
||||
const kubernetesProviderData: KubernetesProviderData = {
|
||||
context: context,
|
||||
alias: "Test E2E Kubernetes Account - Kubeconfig Context",
|
||||
};
|
||||
|
||||
// Prepare static credentials
|
||||
const kubernetesCredentials: KubernetesProviderCredential = {
|
||||
type: KUBERNETES_CREDENTIAL_OPTIONS.KUBECONFIG_CONTENT,
|
||||
kubeconfigContent: kubeconfigContent,
|
||||
};
|
||||
|
||||
// Navigate to providers page
|
||||
await providersPage.goto();
|
||||
await providersPage.verifyPageLoaded();
|
||||
|
||||
// Start adding new provider
|
||||
await providersPage.clickAddProvider();
|
||||
await providersPage.verifyConnectAccountPageLoaded();
|
||||
|
||||
// Select Kubernetes provider
|
||||
await providersPage.selectKubernetesProvider();
|
||||
|
||||
// Fill provider details
|
||||
await providersPage.fillKubernetesProviderDetails(kubernetesProviderData);
|
||||
await providersPage.clickNext();
|
||||
|
||||
// Verify credentials page is loaded
|
||||
await providersPage.verifyKubernetesCredentialsPageLoaded();
|
||||
|
||||
// Fill static credentials details
|
||||
await providersPage.fillKubernetesCredentials(kubernetesCredentials);
|
||||
await providersPage.clickNext();
|
||||
|
||||
// Launch scan
|
||||
await providersPage.verifyLaunchScanPageLoaded();
|
||||
await providersPage.clickNext();
|
||||
|
||||
// Wait for redirect to provider page
|
||||
await providersPage.verifyLoadProviderPageAfterNewProvider();
|
||||
},
|
||||
);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user