Compare commits

...

66 Commits

Author SHA1 Message Date
StylusFrost
9b899b9f74 test(ui): update E2E test commands to include scans project
- Modified the E2E test commands in package.json to include the 'scans' project, ensuring comprehensive testing coverage for the scans functionality.
- This change aligns the test commands with recent enhancements made to the ScansPage and overall E2E testing strategy.
2025-11-03 19:16:26 +01:00
StylusFrost
722a69e7a8 test(ui): enhance ScansPage with success toast verification
- Added a new success toast element to the ScansPage class for better feedback on scan launches.
- Updated the verifyScanLaunched method to utilize the new success toast locator, improving the visibility check for successful scan initiation.
- This change enhances the user experience by providing clear confirmation of scan actions.
2025-11-03 19:13:08 +01:00
StylusFrost
f58030f68e test(ui): add scans E2E test suite and enhance scans functionality
- Introduced a new scans test suite to validate the on-demand scan execution flow.
- Added methods in the ScansPage class for selecting providers, filling scan aliases, and verifying scan launches.
- Updated the ProvidersPage class to include a method for adding AWS providers.
- Created comprehensive end-to-end tests for executing scans, including necessary preconditions and environment variable checks.
- Documented the new test cases and flow in the scans markdown file.
2025-11-03 19:06:52 +01:00
StylusFrost
fb59998447 fix(ui): add missing spaces in InvitationsPage tests
- Added missing spaces in the InvitationsPage tests to improve readability and maintain consistency in the code style.
- This change enhances the overall clarity of the test code without affecting functionality.
2025-10-29 16:00:04 +01:00
StylusFrost
f457653a8d Merge remote-tracking branch 'origin/PROWLER-197-out-of-scope-github-add-an-connect-the-provider' into PROWLER-188-invite-new-user 2025-10-29 15:58:45 +01:00
StylusFrost
57d2fef6be test(ui): add missing spaces in ProvidersPage tests
- Added missing spaces in the ProvidersPage tests to improve readability and maintain consistency in the code style.
- This change enhances the overall clarity of the test code without affecting functionality.
2025-10-29 15:57:41 +01:00
StylusFrost
77c121c6a1 Merge branch 'PROWLER-181-gcp-add-and-connect-the-provider' into PROWLER-197-out-of-scope-github-add-an-connect-the-provider 2025-10-29 15:55:26 +01:00
StylusFrost
3cc49beb45 test(ui): needs space 2025-10-29 15:50:48 +01:00
StylusFrost
9d44070486 test(ui): enhance invitations management in E2E tests
- Added support for invitations management in the UI E2E tests, including a new InvitationsPage class for handling invitation-related actions.
- Updated the Playwright configuration to include a new project for invitations tests.
- Created comprehensive end-to-end tests for inviting new users, verifying the sign-up process, and ensuring proper organization association.
- Introduced new environment variable 'E2E_ORGANIZATION_ID' for managing organization-specific tests.
- Documented the new test cases and flow for invitations management in the corresponding markdown files.
2025-10-28 11:49:01 +01:00
StylusFrost
faec92cbbc test(ui): add GitHub provider support in ProvidersPage tests
- Introduced GitHub provider data and credential interfaces to the ProvidersPage.
- Implemented methods for filling GitHub provider details and credentials for both personal access token and GitHub App.
- Added end-to-end tests for adding a GitHub provider using personal access token and GitHub App authentication.
- Updated the UI E2E tests workflow to include necessary environment variables for GitHub integration.
- Enhanced documentation with new test cases for GitHub provider integration.
2025-10-27 20:38:21 +01:00
StylusFrost
2409049c88 test(ui): add GCP provider support in ProvidersPage tests
- Introduced GCP provider data and credential interfaces to the ProvidersPage.
- Implemented methods for filling GCP provider details and service account key credentials.
- Added end-to-end tests for adding a GCP provider using service account key authentication.
- Updated documentation to include new test case for GCP provider integration.
2025-10-27 09:34:13 +01:00
StylusFrost
477de791a9 test(ui): update environment variable name for user password in sign-up tests
- Changed the environment variable name from 'E2E_NEW_PASSWORD' to 'E2E_NEW_USER_PASSWORD' in the UI E2E tests workflow and related documentation.
- This update ensures consistency across the test setup and improves clarity in the sign-up flow tests.
2025-10-24 13:15:18 +02:00
StylusFrost
53607597b6 test(ci): remove push trigger from UI E2E tests workflow
- Eliminated the push trigger for the UI E2E tests workflow to streamline the process and focus on pull request events.
- This change enhances the workflow by ensuring tests are only run during pull requests, reducing unnecessary executions.
2025-10-24 12:08:40 +02:00
StylusFrost
4250d4ee07 test(ci): correct kubeconfig cluster name in UI E2E tests workflow
- Updated the kubeconfig cluster name from 'kind' to 'kind-kind' in the UI E2E tests workflow for improved accuracy and consistency.
- This change ensures proper configuration when interacting with the Kind cluster during end-to-end tests.
2025-10-24 11:14:19 +02:00
StylusFrost
f26083772e test(ci): correct parameter name for Kind cluster in UI E2E tests workflow
- Updated the parameter name for the Kind cluster creation from 'cluster-name' to 'cluster_name' in the UI E2E tests workflow for consistency with Kubernetes naming conventions.
- This change enhances clarity and aligns with best practices in the workflow configuration.
2025-10-24 11:11:06 +02:00
StylusFrost
a2e409d10e test(ci): update cluster name parameter in UI E2E tests workflow
- Changed the parameter name for the Kind cluster creation from 'name' to 'cluster-name' in the UI E2E tests workflow for improved clarity and consistency.
- This update aligns with best practices for Kubernetes configurations and enhances the overall readability of the workflow.
2025-10-24 11:09:59 +02:00
StylusFrost
027ae6cd73 test(ci): update kubeconfig context name in UI E2E tests workflow
- Changed the Kubernetes context name from 'kind-kind' to 'kind' in the UI E2E tests workflow for better clarity and consistency.
- Added a parameter to specify the Kind cluster name during its creation, ensuring proper integration with the Kubernetes setup.
2025-10-24 11:08:49 +02:00
StylusFrost
bc76a8cf25 test(ci): add kubectl config view step in UI E2E tests workflow
- Included a step to view the current kubeconfig in the UI E2E tests workflow, aiding in debugging and verification of the Kubernetes context setup.
- This addition enhances the workflow by providing visibility into the kubeconfig configuration during the test execution.
2025-10-24 11:05:52 +02:00
StylusFrost
e0bedb06d7 test(ci): update UI E2E tests workflow for Kubernetes integration
- Modified the Kubernetes context in the UI E2E tests workflow to use a fixed context 'kind-kind' instead of a secret.
- Added steps to create a Kind cluster and modify the kubeconfig for proper integration with the Kind cluster.
- Updated the docker-compose configuration to include the Kind network, ensuring seamless connectivity with the Kubernetes cluster.
2025-10-24 10:55:17 +02:00
StylusFrost
0b372477be Merge branch 'PROWLER-184-m365-add-and-connect-the-provider' into PROWLER-182-kubernetes-add-and-connect-the-provider 2025-10-23 15:12:00 +02:00
StylusFrost
670d9a8f87 test(ui): refactor ProvidersPage and enhance M365 provider tests
- Refactored the ProvidersPage class to improve the structure and clarity of provider data and credential handling for AWS, AZURE, and M365 providers.
- Introduced new methods for filling provider details and credentials, ensuring better organization and maintainability of the test code.
- Updated M365 provider tests to utilize the new structure, including verification of credential pages and improved error handling during provider management.
- Enhanced the test setup to ensure a clean state by deleting existing providers before each test.
2025-10-23 15:10:13 +02:00
StylusFrost
f6f25d1191 Merge branch 'PROWLER-180-azure-add-and-connect-the-provider-new' into PROWLER-184-m365-add-and-connect-the-provider 2025-10-23 15:01:44 +02:00
StylusFrost
bd1aac2527 test(ui): rename Azure provider input locators for clarity
- Updated Azure provider input locators in the ProvidersPage class to include the 'azure' prefix, enhancing clarity and consistency.
- Adjusted the corresponding fill methods to reference the new locator names, ensuring proper functionality in the tests.
2025-10-23 15:00:45 +02:00
StylusFrost
cb1284e6e7 Merge branch 'PROWLER-179-aws-add-an-connect-the-provider' into PROWLER-180-azure-add-and-connect-the-provider-new 2025-10-23 14:56:43 +02:00
StylusFrost
74580291a7 test(ui): refactor provider deletion logic and improve error handling
- Updated the ProvidersPage class to include a new method for deleting a provider if it exists, enhancing the test setup by ensuring a clean state.
- Improved error handling during the test connection process to provide clearer feedback on failures.
- Refactored existing tests to utilize the new deletion method, streamlining the test code and improving maintainability.
2025-10-23 14:52:24 +02:00
StylusFrost
83540472e4 test(ci): correct kubeconfig environment variable usage in UI E2E tests
- Changed the kubeconfig file creation step in the UI E2E tests workflow to use the environment variable directly instead of accessing it through secrets.
- This adjustment ensures proper handling of the kubeconfig setup for Kubernetes provider tests.
2025-10-23 13:26:32 +02:00
StylusFrost
1cec011d8d test(ci): create kubeconfig directory for UI E2E tests
- Added a step to create the kubeconfig directory in the UI E2E tests workflow to ensure proper setup for Kubernetes provider tests.
- This change enhances the configuration process by preventing potential errors related to missing directories.
2025-10-23 13:06:39 +02:00
StylusFrost
5af7c950ac test(ci): update UI E2E tests configuration and improve Kubernetes context setup
- Updated the Kubernetes context and kubeconfig path in the E2E tests workflow to use secrets for better security.
- Added a step to create the kubeconfig file from the secret, ensuring proper configuration for Kubernetes provider tests.
- Changed the "Add Kubernetes Provider" test to run serially for improved execution flow.
2025-10-23 13:03:53 +02:00
StylusFrost
642efecd37 test(ui): refactoring tests
Correct serial execution
Best process for deleting a provider
New scans page to manage properly when creating a provider
2025-10-22 20:51:35 +02:00
StylusFrost
ccd8908128 Merge branch 'PROWLER-184-m365-add-and-connect-the-provider' into PROWLER-182-kubernetes-add-and-connect-the-provider 2025-10-21 12:34:31 +02:00
StylusFrost
c0a82910ad test(ui): add M365 provider data and credentials to provider tests
- Introduced M365ProviderData, M365ProviderCredential, and M365_CREDENTIAL_OPTIONS to enhance the provider tests.
- This addition supports the integration of M365 provider functionalities in the UI tests.
2025-10-21 12:33:41 +02:00
StylusFrost
a004a73891 Merge branch 'PROWLER-180-azure-add-and-connect-the-provider-new' into PROWLER-184-m365-add-and-connect-the-provider 2025-10-21 12:31:29 +02:00
StylusFrost
fccd2c3b9c test(ui): enhance AWS and Azure provider tests with role-based credentials
- Added tests for adding AWS providers with both static and role-based credentials, ensuring environment variables are validated.
- Refactored Azure provider tests to include role-based credential handling and improved setup for test state management.
- Updated test descriptions and tags for better clarity and organization.
2025-10-21 12:29:03 +02:00
StylusFrost
e8712359c7 test(ui): refactor ProvidersPage input locators to use role-based queries
- Updated additional input locators in ProvidersPage tests to utilize role-based queries for improved accessibility and consistency across the UI tests.
2025-10-21 12:02:36 +02:00
StylusFrost
1ea859f606 Merge branch 'PROWLER-180-azure-add-and-connect-the-provider-new' into PROWLER-184-m365-add-and-connect-the-provider 2025-10-21 11:58:48 +02:00
StylusFrost
d13182288e test(ui): refactor ProvidersPage input locators to use role-based queries
- Updated input locators in ProvidersPage tests to utilize role-based queries for improved accessibility and consistency across the UI tests.
2025-10-21 11:44:50 +02:00
StylusFrost
85d9411283 Merge branch 'PROWLER-179-aws-add-an-connect-the-provider' into PROWLER-180-azure-add-and-connect-the-provider-new 2025-10-21 11:37:35 +02:00
StylusFrost
a500339138 test(ui): update ProvidersPage locators to use role-based queries
- Refactored input locators in ProvidersPage tests to utilize role-based queries for improved accessibility and consistency across the UI tests.
2025-10-21 11:35:29 +02:00
StylusFrost
4a0f0ba5bb Merge branch 'PROWLER-179-aws-add-an-connect-the-provider' into PROWLER-180-azure-add-and-connect-the-provider-new 2025-10-21 11:28:51 +02:00
StylusFrost
5384d30fd5 Merge branch 'PROWLER-187-create-new-user' into PROWLER-179-aws-add-an-connect-the-provider 2025-10-21 11:19:50 +02:00
StylusFrost
a6121396ca test(ui): remove unnecessary blank line in admin authentication setup test
- Cleaned up the admin authentication setup test by removing an unnecessary blank line for improved readability.
2025-10-21 11:13:52 +02:00
StylusFrost
d17d519a3e test(ui): update locators in tests to use role-based queries
- Refactored locators in BasePage, HomePage, SignInPage, and SignUpPage to utilize role-based queries for improved accessibility and consistency across the UI tests.
2025-10-21 11:12:09 +02:00
StylusFrost
71f5ac5165 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.
2025-10-21 10:56:37 +02:00
StylusFrost
f15fdfc642 test(test): update button selector in ProvidersPage tests
- Changed the selector for the "Add Cloud Provider" button from text-based to role-based for improved reliability and maintainability.
2025-10-21 09:31:11 +02:00
StylusFrost
94d5322f16 test(ui): update locators in tests to use role-based queries
- Replaced text-based locators with role-based queries in various test files for improved accessibility and consistency.
- Removed deprecated HomePage and SignInPage files to streamline the test structure.
2025-10-20 18:11:29 +02:00
StylusFrost
4920f84d75 test(ui): update sign-up tests to use E2E_NEW_PASSWORD environment variable
- Modified the sign-up test to retrieve the password from the E2E_NEW_PASSWORD environment variable.
- Updated documentation to specify the requirement for the E2E_NEW_PASSWORD variable before running tests.
2025-10-20 17:35:54 +02:00
StylusFrost
7b321c8cd1 test(ui): enhance M365 provider management E2E tests
- Added support for certificate-based authentication in M365 provider management.
- Updated the ProvidersPage interface to include fields for certificate credentials.
- Enhanced E2E tests to validate the complete flow of adding a new M365 provider with certificate credentials.
- Included necessary environment variable checks for certificate content and updated test steps accordingly.
2025-10-20 17:00:42 +02:00
StylusFrost
1212356db3 Merge branch 'master' into PROWLER-184-m365-add-and-connect-the-provider 2025-10-20 14:57:57 +02:00
StylusFrost
13436613d6 test(ui): add M365 provider management E2E tests
- Introduced new E2E tests for adding Microsoft 365 providers with static credentials.
- Updated the ProvidersPage interface to include M365-specific fields and methods.
- Enhanced the test suite to validate the complete flow of adding a new M365 provider, ensuring proper handling of credentials and provider details.
- Added necessary environment variable checks and cleanup steps to maintain test integrity.
2025-10-20 14:53:43 +02:00
StylusFrost
b23b083092 Merge branch 'master' into PROWLER-180-azure-add-and-connect-the-provider-new 2025-10-20 14:13:31 +02:00
StylusFrost
d63ae0e40f Merge branch 'master' into PROWLER-179-aws-add-an-connect-the-provider 2025-10-20 14:11:46 +02:00
StylusFrost
5d86aacb2a Merge branch 'master' into PROWLER-187-create-new-user 2025-10-20 13:51:07 +02:00
StylusFrost
0d088eca13 test(ui): add Azure environment variables for E2E tests
- Added necessary Azure environment variables to the UI E2E test workflow.
- This enhancement supports the integration of Azure provider management in the testing suite.
2025-10-20 11:56:16 +02:00
StylusFrost
055964aff3 test(ui): add Azure provider management E2E tests
- Introduced new E2E tests for adding Azure providers with static credentials.
- Updated the ProvidersPage interface to include Azure-specific fields and methods.
- Enhanced the test suite to validate the complete flow of adding a new Azure provider, ensuring proper handling of credentials and provider details.
- Added necessary environment variable checks and cleanup steps to maintain test integrity.
2025-10-20 11:51:21 +02:00
StylusFrost
447d754b49 test(ui): add page load wait to sign-up page tests
- Introduced a call to `waitForPageLoad` in the sign-up page test to ensure the page is fully loaded before proceeding with visibility checks for elements.
2025-10-20 10:36:40 +02:00
StylusFrost
761563472b test(ui): improve provider page load handling in tests
- Added a wait for network idle state in the ProvidersPage test to ensure all network requests are completed before proceeding.
- This enhancement improves the reliability of the test by ensuring the page is fully loaded before assertions are made.
2025-10-20 10:30:51 +02:00
StylusFrost
5e3db29de7 test(ui): update AWS provider credential handling in tests
- Refactored AWS provider credential interfaces to improve type safety and clarity.
- Replaced `ProviderCredentials` with `AWSProviderCredential` and introduced `AWS_CREDENTIAL_OPTIONS` for credential types.
- Updated tests to utilize the new credential structure, ensuring consistency across AWS provider management actions.
2025-10-17 19:35:25 +02:00
StylusFrost
d8ca60a4ab test(ui): add AWS provider management E2E tests
- Introduced new E2E tests for adding AWS providers with both static and role-based credentials.
- Updated Playwright configuration to include a new test suite for providers.
- Enhanced the UI workflow by adding necessary environment variables for E2E testing.
- Created helper functions for provider management actions and validations.
2025-10-17 13:41:40 +02:00
StylusFrost
cef7fcc24b Merge branch 'master' into PROWLER-187-create-new-user 2025-10-14 11:03:23 +02:00
StylusFrost
fcf42937aa test(ui): enhance session management tests with new helper functions
- Updated the `verifyLogoutSuccess` function to use a regex for URL verification.
- Added new helper functions `getSession` and `verifySessionValid` to streamline session validation in tests, ensuring that session data is correctly retrieved and validated.
2025-10-14 10:26:00 +02:00
StylusFrost
2c9d8ad8ea test(ui): simplify sign-up page tests by removing redundant loading state verification
- Removed the `verifyLoadingState` method from the `SignUpPage` class as it was redundant.
- Updated comments in the sign-up test to enhance clarity and focus on key actions.
- Added a call to `verifyNoErrors` to ensure no errors occur during the sign-up process.
2025-10-10 18:21:43 +02:00
StylusFrost
f424342e7e test(ui): add E2E tests document for user sign-up flow
- Documented test case details including flow steps, expected results, and key verification points.
- Enhanced existing sign-up test specifications to include relevant tags for better categorization and tracking.
2025-10-10 11:53:18 +02:00
StylusFrost
9b7e4f59e1 test(ui): implement base page object and enhance sign-up flow tests
- Introduced a BasePage class to encapsulate common functionality for page objects, improving code reusability and maintainability.
- Created new page objects for HomePage, SignInPage, and SignUpPage to streamline the sign-up and login processes in tests.
- Added comprehensive sign-up flow tests to validate user registration and login functionality, ensuring a smooth user experience.
- Updated Playwright configuration to support new test structures and improve overall test organization.
2025-10-09 16:19:27 +02:00
StylusFrost
d21222aa3a test(ui): format Playwright configuration for consistency
- Added missing commas and improved formatting in the Playwright configuration file to enhance readability and maintain consistency across the codebase.
2025-10-09 11:05:09 +02:00
StylusFrost
bdbb2fad78 test(ui): update Playwright test commands to specify project. Compatibility with current e2e test
- Modified Playwright test commands in package.json to explicitly use the 'chromium' project.
2025-10-09 10:54:34 +02:00
StylusFrost
cf7b66101c test(ui): enhance Playwright test setups for user authentication
- Added multiple authentication setup files for different user roles (admin, manage scans, manage integrations, etc.) to streamline end-to-end testing.
- Introduced a helper function to authenticate users and save their state for reuse in tests.
- Updated Playwright configuration to include new authentication projects.

This change improves the testing framework by allowing for more comprehensive and role-specific user authentication scenarios.
2025-10-09 10:27:37 +02:00
23 changed files with 3558 additions and 93 deletions

View File

@@ -10,6 +10,7 @@ on:
- 'ui/**'
jobs:
e2e-tests:
if: github.repository == 'prowler-cloud/prowler'
runs-on: ubuntu-latest
@@ -18,9 +19,51 @@ jobs:
AUTH_TRUST_HOST: true
NEXTAUTH_URL: 'http://localhost:3000'
NEXT_PUBLIC_API_BASE_URL: 'http://localhost:8080/api/v1'
E2E_ADMIN_USER: ${{ secrets.E2E_ADMIN_USER }}
E2E_ADMIN_PASSWORD: ${{ secrets.E2E_ADMIN_PASSWORD }}
E2E_AWS_PROVIDER_ACCOUNT_ID: ${{ secrets.E2E_AWS_PROVIDER_ACCOUNT_ID }}
E2E_AWS_PROVIDER_ACCESS_KEY: ${{ secrets.E2E_AWS_PROVIDER_ACCESS_KEY }}
E2E_AWS_PROVIDER_SECRET_KEY: ${{ secrets.E2E_AWS_PROVIDER_SECRET_KEY }}
E2E_AWS_PROVIDER_ROLE_ARN: ${{ secrets.E2E_AWS_PROVIDER_ROLE_ARN }}
E2E_AZURE_SUBSCRIPTION_ID: ${{ secrets.E2E_AZURE_SUBSCRIPTION_ID }}
E2E_AZURE_CLIENT_ID: ${{ secrets.E2E_AZURE_CLIENT_ID }}
E2E_AZURE_SECRET_ID: ${{ secrets.E2E_AZURE_SECRET_ID }}
E2E_AZURE_TENANT_ID: ${{ secrets.E2E_AZURE_TENANT_ID }}
E2E_M365_DOMAIN_ID: ${{ secrets.E2E_M365_DOMAIN_ID }}
E2E_M365_CLIENT_ID: ${{ secrets.E2E_M365_CLIENT_ID }}
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: 'kind-kind'
E2E_KUBERNETES_KUBECONFIG_PATH: /home/runner/.kube/config
E2E_GITHUB_APP_ID: ${{ secrets.E2E_GITHUB_APP_ID }}
E2E_GITHUB_BASE64_APP_PRIVATE_KEY: ${{ secrets.E2E_GITHUB_BASE64_APP_PRIVATE_KEY }}
E2E_GITHUB_USERNAME: ${{ secrets.E2E_GITHUB_USERNAME }}
E2E_GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.E2E_GITHUB_PERSONAL_ACCESS_TOKEN }}
E2E_GITHUB_ORGANIZATION: ${{ secrets.E2E_GITHUB_ORGANIZATION }}
E2E_GITHUB_ORGANIZATION_ACCESS_TOKEN: ${{ secrets.E2E_GITHUB_ORGANIZATION_ACCESS_TOKEN }}
E2E_ORGANIZATION_ID: ${{ secrets.E2E_ORGANIZATION_ID }}
E2E_NEW_USER_PASSWORD: ${{ secrets.E2E_NEW_USER_PASSWORD }}
steps:
- name: Checkout repository
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Create k8s Kind Cluster
uses: helm/kind-action@v1
with:
cluster_name: kind
- name: Modify kubeconfig
run: |
# Modify the kubeconfig to use the kind cluster server to https://kind-control-plane:6443
# from worker service into docker-compose.yml
kubectl config set-cluster kind-kind --server=https://kind-control-plane:6443
kubectl config view
- name: Add network kind to docker compose
run: |
# Add the network kind to the docker compose to interconnect to kind cluster
yq -i '.networks.kind.external = true' docker-compose.yml
# Add network kind to worker service and default network too
yq -i '.services.worker.networks = ["kind","default"]' docker-compose.yml
- name: Fix API data directory permissions
run: docker run --rm -v $(pwd)/_data/api:/data alpine chown -R 1000:1000 /data
- name: Start API services
@@ -97,4 +140,4 @@ jobs:
run: |
echo "Shutting down services..."
docker compose down -v || true
echo "Cleanup completed"
echo "Cleanup completed"

View File

@@ -15,10 +15,10 @@
"format:check": "./node_modules/.bin/prettier --check ./app",
"format:write": "./node_modules/.bin/prettier --config .prettierrc.json --write ./app",
"prepare": "husky",
"test:e2e": "playwright test --project=chromium",
"test:e2e:ui": "playwright test --project=chromium --ui",
"test:e2e:debug": "playwright test --project=chromium --debug",
"test:e2e:headed": "playwright test --project=chromium --headed",
"test:e2e": "playwright test --project=chromium --project=sign-up --project=providers --project=invitations --project=scans",
"test:e2e:ui": "playwright test --project=chromium --project=sign-up --project=providers --project=invitations --project=scans --ui",
"test:e2e:debug": "playwright test --project=chromium --project=sign-up --project=providers --project=invitations --project=scans --debug",
"test:e2e:headed": "playwright test --project=chromium --project=sign-up --project=providers --project=invitations --project=scans --headed",
"test:e2e:report": "playwright show-report",
"test:e2e:install": "playwright install"
},

View File

@@ -89,6 +89,30 @@ export default defineConfig({
{
name: "chromium",
use: { ...devices["Desktop Chrome"] },
testMatch: "auth-login.spec.ts",
},
// This project runs the sign-up test suite
{
name: "sign-up",
testMatch: "sign-up.spec.ts",
},
// This project runs the scans test suite
{
name: "scans",
testMatch: "scans.spec.ts",
dependencies: ["admin.auth.setup"],
},
// This project runs the providers test suite
{
name: "providers",
testMatch: "providers.spec.ts",
dependencies: ["admin.auth.setup"],
},
// This project runs the invitations test suite
{
name: "invitations",
testMatch: "invitations.spec.ts",
dependencies: ["admin.auth.setup"],
},
],

159
ui/tests/base-page.ts Normal file
View File

@@ -0,0 +1,159 @@
import { Page, Locator, expect } from "@playwright/test";
/**
* Base page object class containing common functionality
* that can be shared across all page objects
*/
export abstract class BasePage {
readonly page: Page;
// Common UI elements that appear on most pages
readonly title: Locator;
readonly loadingIndicator: Locator;
readonly themeToggle: Locator;
constructor(page: Page) {
this.page = page;
// Common locators that most pages share
this.title = page.locator("h1, h2, [role='heading']").first();
this.loadingIndicator = page.getByRole("status", { name: "Loading" });
this.themeToggle = page.getByRole("button", { name: "Toggle theme" });
}
// Common navigation methods
async goto(url: string): Promise<void> {
await this.page.goto(url);
await this.waitForPageLoad();
}
async waitForPageLoad(): Promise<void> {
await this.page.waitForLoadState("networkidle");
}
async refresh(): Promise<void> {
await this.page.reload();
await this.waitForPageLoad();
}
async goBack(): Promise<void> {
await this.page.goBack();
await this.waitForPageLoad();
}
// Common verification methods
async verifyPageTitle(expectedTitle: string | RegExp): Promise<void> {
await expect(this.page).toHaveTitle(expectedTitle);
}
async verifyLoadingState(): Promise<void> {
await expect(this.loadingIndicator).toBeVisible();
}
async verifyNoLoadingState(): Promise<void> {
await expect(this.loadingIndicator).not.toBeVisible();
}
// Common form interaction methods
async clearInput(input: Locator): Promise<void> {
await input.clear();
}
async fillInput(input: Locator, value: string): Promise<void> {
await input.fill(value);
}
async clickButton(button: Locator): Promise<void> {
await button.click();
}
// Common validation methods
async verifyElementVisible(element: Locator): Promise<void> {
await expect(element).toBeVisible();
}
async verifyElementNotVisible(element: Locator): Promise<void> {
await expect(element).not.toBeVisible();
}
async verifyElementText(element: Locator, expectedText: string): Promise<void> {
await expect(element).toHaveText(expectedText);
}
async verifyElementContainsText(element: Locator, expectedText: string): Promise<void> {
await expect(element).toContainText(expectedText);
}
// Common accessibility methods
async verifyKeyboardNavigation(elements: Locator[]): Promise<void> {
for (const element of elements) {
await this.page.keyboard.press("Tab");
await expect(element).toBeFocused();
}
}
async verifyAriaLabels(elements: { locator: Locator; expectedLabel: string }[]): Promise<void> {
for (const { locator, expectedLabel } of elements) {
await expect(locator).toHaveAttribute("aria-label", expectedLabel);
}
}
// Common utility methods
async getElementText(element: Locator): Promise<string> {
return await element.textContent() || "";
}
async getElementValue(element: Locator): Promise<string> {
return await element.inputValue();
}
async isElementVisible(element: Locator): Promise<boolean> {
return await element.isVisible();
}
async isElementEnabled(element: Locator): Promise<boolean> {
return await element.isEnabled();
}
// Common error handling methods
async getFormErrors(): Promise<string[]> {
const errorElements = await this.page.locator('[role="alert"], .error-message, [data-testid="error"]').all();
const errors: string[] = [];
for (const element of errorElements) {
const text = await element.textContent();
if (text) {
errors.push(text.trim());
}
}
return errors;
}
async verifyNoErrors(): Promise<void> {
const errors = await this.getFormErrors();
expect(errors).toHaveLength(0);
}
// Common wait methods
async waitForElement(element: Locator, timeout: number = 5000): Promise<void> {
await element.waitFor({ timeout });
}
async waitForElementToDisappear(element: Locator, timeout: number = 5000): Promise<void> {
await element.waitFor({ state: "hidden", timeout });
}
async waitForUrl(expectedUrl: string | RegExp, timeout: number = 5000): Promise<void> {
await this.page.waitForURL(expectedUrl, { timeout });
}
// Common screenshot methods
async takeScreenshot(name: string): Promise<void> {
await this.page.screenshot({ path: `screenshots/${name}.png` });
}
async takeElementScreenshot(element: Locator, name: string): Promise<void> {
await element.screenshot({ path: `screenshots/${name}.png` });
}
}

View File

@@ -1,5 +1,6 @@
import { Page, expect } from "@playwright/test";
import { SignInPage, SignInCredentials } from "./page-objects/sign-in-page";
import { SignInPage, SignInCredentials } from "./sign-in/sign-in-page";
import { ProvidersPage } from "./providers/providers-page";
export const ERROR_MESSAGES = {
INVALID_CREDENTIALS: "Invalid email or password",
@@ -146,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
@@ -162,6 +165,18 @@ export async function authenticateAndSaveState(
await page.context().storageState({ path: storagePath });
}
/**
* Generate a random base36 suffix of specified length
* Used for creating unique test data to avoid conflicts
*/
export function makeSuffix(len: number): string {
let s = "";
while (s.length < len) {
s += Math.random().toString(36).slice(2);
}
return s.slice(0, len);
}
export async function getSession(page: Page) {
const response = await page.request.get("/api/auth/session");
return response.json();

View File

@@ -1,7 +1,7 @@
import { Page, Locator, expect } from "@playwright/test";
import { BasePage } from "../base-page";
export class HomePage {
readonly page: Page;
export class HomePage extends BasePage {
// Main content elements
readonly mainContent: Locator;
@@ -18,15 +18,14 @@ export class HomePage {
readonly overviewSection: Locator;
// UI elements
readonly themeToggle: Locator;
readonly logo: Locator;
constructor(page: Page) {
this.page = page;
super(page);
// Main content elements
this.mainContent = page.locator("main");
this.breadcrumbs = page.getByLabel("Breadcrumbs");
this.breadcrumbs = page.getByRole("navigation", { name: "Breadcrumbs" });
this.overviewHeading = page.getByRole("heading", { name: "Overview", exact: true });
// Navigation elements
@@ -39,18 +38,12 @@ export class HomePage {
this.overviewSection = page.locator('[data-testid="overview-section"]');
// UI elements
this.themeToggle = page.getByLabel("Toggle theme");
this.logo = page.locator('svg[width="300"]');
}
// Navigation methods
async goto(): Promise<void> {
await this.page.goto("/");
await this.waitForPageLoad();
}
async waitForPageLoad(): Promise<void> {
await this.page.waitForLoadState("networkidle");
await super.goto("/");
}
// Verification methods
@@ -58,7 +51,6 @@ export class HomePage {
await expect(this.page).toHaveURL("/");
await expect(this.mainContent).toBeVisible();
await expect(this.overviewHeading).toBeVisible();
await this.waitForPageLoad();
}
async verifyBreadcrumbs(): Promise<void> {
@@ -94,15 +86,6 @@ export class HomePage {
}
// Utility methods
async refresh(): Promise<void> {
await this.page.reload();
await this.waitForPageLoad();
}
async goBack(): Promise<void> {
await this.page.goBack();
await this.waitForPageLoad();
}
// Accessibility methods
async verifyKeyboardNavigation(): Promise<void> {
@@ -111,11 +94,6 @@ export class HomePage {
await expect(this.themeToggle).toBeFocused();
}
// Wait methods
async waitForRedirect(expectedUrl: string): Promise<void> {
await this.page.waitForURL(expectedUrl);
}
async waitForContentLoad(): Promise<void> {
await this.page.waitForFunction(() => {
const main = document.querySelector("main");

View File

@@ -0,0 +1,113 @@
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;
// UI elements
readonly sendInviteButton: Locator;
readonly emailInput: Locator;
readonly roleSelect: Locator;
// Invitation details
readonly invitationDetails: 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" });
// Button to invite a new user
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("button", { name: /Role|Select a role/i });
// Form details
this.invitationDetails = page.getByRole('heading', { name: 'Invitation details' });
// 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();
}
async goto(): Promise<void> {
// Navigate to the invitations page
await super.goto("/invitations");
}
async clickSendInviteButton(): Promise<void> {
// Click the send invitation button
await this.sendInviteButton.click();
await this.waitForPageLoad();
}
async verifyPageLoaded(): Promise<void> {
// Verify the invitations page is loaded
await expect(this.pageHeadingInvitations).toBeVisible();
await this.waitForPageLoad();
}
async verifyInvitePageLoaded(): Promise<void> {
// Verify the invite page is loaded
await expect(this.emailInput).toBeVisible();
await expect(this.sendInviteButton).toBeVisible();
await this.waitForPageLoad();
}
async fillEmail(email: string): Promise<void> {
// Fill the email input
await this.emailInput.fill(email);
}
async selectRole(role: string): Promise<void> {
// Select the role option
// Open the role dropdown
await this.roleSelect.click();
// Prefer ARIA role option inside listbox
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"));
}
async verifyInviteDataPageLoaded(): Promise<void> {
// Verify the invite data page is loaded
await expect(this.invitationDetails).toBeVisible();
await this.waitForPageLoad();
}
async getShareUrl(): Promise<string> {
// Get the share url
// Get the share url text content
const text = await this.shareUrl.textContent();
if (!text) {
throw new Error("Share url not found");
}
return text;
}
}

View File

@@ -0,0 +1,66 @@
### E2E Tests: Invitations Management
**Suite ID:** `INVITATION-E2E`
**Feature:** User Invitations.
---
## Test Case: `INVITATION-E2E-001` - Invite New User and Complete Sign-Up
**Priority:** `critical`
**Tags:**
- type → @e2e
- feature → @invitations
- id → @INVITATION-E2E-001
**Description/Objective:** Validates the full flow to invite a new user from the admin session, consume the invitation link, sign up as the invited user, authenticate, and verify the user is associated to the expected organization.
**Preconditions:**
- Admin authentication state available: `playwright/.auth/admin_user.json` (admin.auth.setup)
- Environment variables configured:
- `E2E_NEW_USER_PASSWORD` (password for the invited user)
- `E2E_ORGANIZATION_ID` (expected organization for membership verification)
- Application running with accessible UI/API endpoints
### Flow Steps:
1. Navigate to invitations page
2. Click "Send Invitation" button
3. Fill unique email address for the invite
4. Select role `e2e_admin`
5. Click "Send Invitation" to generate invitation
6. Read the generated share URL from the invitation details
7. Open a new browser context (no admin cookies) and navigate to the share URL
8. Complete sign-up with provided password and accept terms
9. Verify sign-up success (no errors) and redirect to login page
10. Log in with the newly created credentials in the new context
11. Verify successful login
12. Navigate to user profile and verify `organizationId` matches `E2E_ORGANIZATION_ID`
### Expected Result:
- Invitation is created and a valid share URL is provided
- Invited user can sign up successfully using the invitation link
- User is redirected to the login page after sign-up (OSS flow)
- Login succeeds with the new credentials
- User profile shows membership in the expected organization
### Key verification points:
- Invitations page loads and displays the heading
- Send Invitation form is visible (email + role select)
- Invitation details page shows share URL
- Sign-up page loads from invitation link and submits without errors
- Post sign-up, redirect to login is performed
- Login with the new account succeeds
- Profile page shows the expected organization id
### Notes:
- Test uses a fresh browser context for the invitee to avoid admin session leakage
- Email should be unique per run (the test uses a random suffix)
- Ensure `E2E_NEW_USER_PASSWORD` and `E2E_ORGANIZATION_ID` are set before execution
- The role `e2e_admin` must be available in the environment

View File

@@ -0,0 +1,105 @@
import { test } from "@playwright/test";
import { InvitationsPage } from "./invitations-page";
import { makeSuffix } from "../helpers";
import { SignUpPage } from "../sign-up/sign-up-page";
import { SignInPage } from "../sign-in/sign-in-page";
import { UserProfilePage } from "../profile/profile-page";
test.describe("New user invitation", () => {
// Invitations page object
let invitationsPage: InvitationsPage;
// Setup before each test
test.beforeEach(async ({ page }) => {
invitationsPage = new InvitationsPage(page);
});
// Use admin authentication for invitations management
test.use({ storageState: "playwright/.auth/admin_user.json" });
test(
"should invite a new user",
{
tag: ["@critical", "@e2e", "@invitations", "@INVITATION-E2E-001"],
},
async ({ page, browser }) => {
// Test data from environment variables
const password = process.env.E2E_NEW_USER_PASSWORD;
const organizationId = process.env.E2E_ORGANIZATION_ID;
// Validate required environment variables
if (!password || !organizationId) {
throw new Error(
"E2E_NEW_USER_PASSWORD or E2E_ORGANIZATION_ID environment variable is not set",
);
}
// Generate unique test data
const suffix = makeSuffix(10);
const uniqueEmail = `e2e+${suffix}@prowler.com`;
// Navigate to providers page
await invitationsPage.goto();
await invitationsPage.verifyPageLoaded();
// Press the invite button
await invitationsPage.clickSendInviteButton();
await invitationsPage.verifyInvitePageLoaded();
// Fill the email
await invitationsPage.fillEmail(uniqueEmail);
// Select the role option
await invitationsPage.selectRole("e2e_admin");
// Press the send invitation button
await invitationsPage.clickSendInviteButton();
await invitationsPage.verifyInviteDataPageLoaded();
// Get the share url
const shareUrl = await invitationsPage.getShareUrl();
// Navigate to the share url with a new context to avoid cookies from the admin context
const inviteContext = await browser.newContext({ storageState: { cookies: [], origins: [] } });
const signUpPage = new SignUpPage(await inviteContext.newPage());
// Navigate to the share url
await signUpPage.gotoInvite(shareUrl);
// Fill and submit the sign-up form
await signUpPage.signup({
name: `E2E User ${suffix}`,
email: uniqueEmail,
password: password,
confirmPassword: password,
acceptTerms: true,
});
// Verify no errors occurred during sign-up
await signUpPage.verifyNoErrors();
// Verify redirect to login page (OSS environment)
await signUpPage.verifyRedirectToLogin();
// Verify the newly created user can log in successfully with the new context
const signInPage = new SignInPage(await inviteContext.newPage());
await signInPage.goto();
await signInPage.login({
email: uniqueEmail,
password: password,
});
await signInPage.verifySuccessfulLogin();
// Navigate to the user profile page
const userProfilePage = new UserProfilePage(await inviteContext.newPage());
await userProfilePage.goto();
// Verify if user is added to the organization
await userProfilePage.verifyOrganizationId(organizationId);
// Close the invite context
await inviteContext.close();
},
);
});

View File

@@ -0,0 +1,32 @@
import { Page, Locator, expect } from "@playwright/test";
import { BasePage } from "../base-page";
export class UserProfilePage extends BasePage {
// Page heading
readonly pageHeadingUserProfile: Locator;
constructor(page: Page) {
super(page);
// Page heading
this.pageHeadingUserProfile = page.getByRole("heading", { name: "User Profile" });
}
async goto(): Promise<void> {
// Navigate to the user profile page
await super.goto("/profile");
}
async verifyOrganizationId(organizationId: string): Promise<void> {
// Verify the organization ID is visible
await expect(this.page.getByText(organizationId)).toBeVisible();
if (await this.page.getByText(organizationId).count() === 0) {
throw new Error(`Organization ID ${organizationId} not found`);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,552 @@
### E2E Tests: AWS Provider Management
**Suite ID:** `PROVIDER-E2E`
**Feature:** AWS Provider Management.
---
## Test Case: `PROVIDER-E2E-001` - Add AWS Provider with Static Credentials
**Priority:** `critical`
**Tags:**
- type → @e2e, @serial
- feature → @providers
- provider → @aws
**Description/Objective:** Validates the complete flow of adding a new AWS provider using static access key credentials
**Preconditions:**
- Admin user authentication required (admin.auth.setup setup)
- Environment variables configured: E2E_AWS_PROVIDER_ACCOUNT_ID, E2E_AWS_PROVIDER_ACCESS_KEY and E2E_AWS_PROVIDER_SECRET_KEY
- Remove any existing provider with the same Account ID before starting the test
- This test must be run serially and never in parallel with other tests, as it requires the Account ID not to be already registered beforehand.
### Flow Steps:
1. Navigate to providers page
2. Click "Add Provider" button
3. Select AWS provider type
4. Fill provider details (account ID and alias)
5. Select "credentials" authentication type
6. Fill static credentials (access key and secret key)
7. Launch initial scan
8. Verify redirect to provider management page
### Expected Result:
- AWS provider successfully added with static credentials
- Initial scan launched successfully
- User redirected to provider details page
### Key verification points:
- Provider page loads correctly
- Connect account page displays AWS option
- Credentials form accepts static credentials
- Launch scan page appears
- Successful redirect to provider page after scan launch
### Notes:
- Test uses environment variables for AWS credentials
- Provider cleanup performed before each test to ensure clean state
- Requires valid AWS account with appropriate permissions
---
## Test Case: `PROVIDER-E2E-002` - Add AWS Provider with Assume Role Credentials Access Key and Secret Key
**Priority:** `critical`
**Tags:**
- type → @e2e, @serial
- feature → @providers
- provider → @aws
**Description/Objective:** Validates the complete flow of adding a new AWS provider using role-based authentication with Access Key and Secret Key
**Preconditions:**
- Admin user authentication required (admin.auth.setup setup)
- Environment variables configured: E2E_AWS_PROVIDER_ACCOUNT_ID, E2E_AWS_PROVIDER_ACCESS_KEY, E2E_AWS_PROVIDER_SECRET_KEY, E2E_AWS_PROVIDER_ROLE_ARN
- Remove any existing provider with the same Account ID before starting the test
- This test must be run serially and never in parallel with other tests, as it requires the Account ID not to be already registered beforehand.
### Flow Steps:
1. Navigate to providers page
2. Click "Add Provider" button
3. Select AWS provider type
4. Fill provider details (account ID and alias)
5. Select "role" authentication type
6. Fill role credentials (access key, secret key, and role ARN)
7. Launch initial scan
8. Verify redirect to provider management page
### Expected Result:
- AWS provider successfully added with role credentials
- Initial scan launched successfully
- User redirected to provider details page
### Key verification points:
- Provider page loads correctly
- Connect account page displays AWS option
- Role credentials form accepts all required fields
- Launch scan page appears
- Successful redirect to provider page after scan launch
### Notes:
- Test uses environment variables for AWS credentials and role ARN
- Provider cleanup performed before each test to ensure clean state
- Requires valid AWS account with role assumption permissions
- Role ARN must be properly configured
---
## Test Case: `PROVIDER-E2E-003` - Add Azure Provider with Static Credentials
**Priority:** `critical`
**Tags:**
- type → @e2e, @serial
- feature → @providers
- provider → @azure
**Description/Objective:** Validates the complete flow of adding a new Azure provider using static client credentials (Client ID, Client Secret, Tenant ID)
**Preconditions:**
- Admin user authentication required (admin.auth.setup setup)
- Environment variables configured: E2E_AZURE_SUBSCRIPTION_ID, E2E_AZURE_CLIENT_ID, E2E_AZURE_SECRET_ID, E2E_AZURE_TENANT_ID
- Remove any existing provider with the same Subscription ID before starting the test
- This test must be run serially and never in parallel with other tests, as it requires the Subscription ID not to be already registered beforehand.
### Flow Steps:
1. Navigate to providers page
2. Click "Add Provider" button
3. Select Azure provider type
4. Fill provider details (subscription ID and alias)
5. Fill Azure credentials (client ID, client secret, tenant ID)
6. Launch initial scan
7. Verify redirect to provider management page
### Expected Result:
- Azure provider successfully added with static credentials
- Initial scan launched successfully
- User redirected to provider details page
### Key verification points:
- Provider page loads correctly
- Connect account page displays Azure option
- Azure credentials form accepts all required fields
- Launch scan page appears
- Successful redirect to provider page after scan launch
### Notes:
- Test uses environment variables for Azure credentials
- Provider cleanup performed before each test to ensure clean state
- Requires valid Azure subscription with appropriate permissions
- Client credentials must have sufficient permissions for security scanning
---
## Test Case: `PROVIDER-E2E-004` - Add M365 Provider with Static Credentials
**Priority:** `critical`
**Tags:**
- type → @e2e, @serial
- feature → @providers
- provider → @m365
**Description/Objective:** Validates the complete flow of adding a new Microsoft 365 provider using static client credentials (Client ID, Client Secret, Tenant ID) tied to a Domain ID.
**Preconditions:**
- Admin user authentication required (admin.auth.setup setup)
- Environment variables configured: E2E_M365_DOMAIN_ID, E2E_M365_CLIENT_ID, E2E_M365_SECRET_ID, E2E_M365_TENANT_ID
- Remove any existing provider with the same Domain ID before starting the test
- This test must be run serially and never in parallel with other tests, as it requires the Domain ID not to be already registered beforehand.
### Flow Steps:
1. Navigate to providers page
2. Click "Add Provider" button
3. Select M365 provider type
4. Fill provider details (domain ID and alias)
5. Select static credentials type
6. Fill M365 credentials (client ID, client secret, tenant ID)
7. Launch initial scan
8. Verify redirect to provider management page
### Expected Result:
- M365 provider successfully added with static credentials
- Initial scan launched successfully
- User redirected to provider details page
### Key verification points:
- Provider page loads correctly
- Connect account page displays M365 option
- M365 credentials form accepts all required fields
- Launch scan page appears
- Successful redirect to provider page after scan launch
### Notes:
- Test uses environment variables for M365 credentials
- Provider cleanup performed before each test to ensure clean state
- Requires valid Microsoft 365 tenant with appropriate permissions
- Client credentials must have sufficient permissions for security scanning
---
## Test Case: `PROVIDER-E2E-005` - Add M365 Provider with Certificate Credentials
**Priority:** `critical`
**Tags:**
- type → @e2e, @serial
- feature → @providers
- provider → @m365
**Description/Objective:** Validates the complete flow of adding a new Microsoft 365 provider using certificate-based authentication (Client ID, Tenant ID, Certificate Content) tied to a Domain ID.
**Preconditions:**
- Admin user authentication required (admin.auth.setup setup)
- Environment variables configured: E2E_M365_DOMAIN_ID, E2E_M365_CLIENT_ID, E2E_M365_TENANT_ID, E2E_M365_CERTIFICATE_CONTENT
- Remove any existing provider with the same Domain ID before starting the test
- This test must be run serially and never in parallel with other tests, as it requires the Domain ID not to be already registered beforehand.
### Flow Steps:
1. Navigate to providers page
2. Click "Add Provider" button
3. Select M365 provider type
4. Fill provider details (domain ID and alias)
5. Select certificate credentials type
6. Fill M365 certificate credentials (client ID, tenant ID, certificate content)
7. Launch initial scan
8. Verify redirect to provider management page
### Expected Result:
- M365 provider successfully added with certificate credentials
- Initial scan launched successfully
- User redirected to provider details page
### Key verification points:
- Provider page loads correctly
- Connect account page displays M365 option
- Certificate credentials form accepts all required fields
- Launch scan page appears
- Successful redirect to provider page after scan launch
### Notes:
- Test uses environment variables for M365 certificate credentials
- 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)
---
## Test Case: `PROVIDER-E2E-007` - Add GCP Provider with Service Account Key
**Priority:** `critical`
**Tags:**
- type → @e2e, @serial
- feature → @providers
- provider → @gcp
**Description/Objective:** Validates the complete flow of adding a new GCP provider using service account key authentication
**Preconditions:**
- Admin user authentication required (admin.auth.setup setup)
- Environment variables configured: E2E_GCP_PROJECT_ID, E2E_GCP_BASE64_SERVICE_ACCOUNT_KEY
- Remove any existing provider with the same Project ID before starting the test
- This test must be run serially and never in parallel with other tests, as it requires the Project ID not to be already registered beforehand.
### Flow Steps:
1. Navigate to providers page
2. Click "Add Provider" button
3. Select GCP provider type
4. Fill provider details (project ID and alias)
5. Select service account credentials type
6. Fill GCP service account key credentials
7. Launch initial scan
8. Verify redirect to provider management page
### Expected Result:
- GCP provider successfully added with service account key
- Initial scan launched successfully
- User redirected to provider details page
### Key verification points:
- Provider page loads correctly
- Connect account page displays GCP option
- Provider details form accepts project ID and alias
- Service account credentials page loads with service account key field
- Service account key 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 GCP project ID and service account key
- Service account key is provided as base64 encoded JSON content
- Provider cleanup performed before each test to ensure clean state
- Requires valid GCP project with service account having appropriate permissions
- Service account must have sufficient permissions for security scanning
- Test validates that service account key goes to the correct field
- Test uses base64 encoded environment variables for GCP service account key
---
## Test Case: `PROVIDER-E2E-008` - Add GitHub Provider with Personal Access Token
**Priority:** `critical`
**Tags:**
- type → @e2e, @serial
- feature → @providers
- provider → @github
**Description/Objective:** Validates the complete flow of adding a new GitHub provider using personal access token authentication for a user account
**Preconditions:**
- Admin user authentication required (admin.auth.setup setup)
- Environment variables configured: E2E_GITHUB_USERNAME, E2E_GITHUB_PERSONAL_ACCESS_TOKEN
- Remove any existing provider with the same Username before starting the test
- This test must be run serially and never in parallel with other tests, as it requires the Username not to be already registered beforehand.
### Flow Steps:
1. Navigate to providers page
2. Click "Add Provider" button
3. Select GitHub provider type
4. Fill provider details (username and alias)
5. Select personal access token credentials type
6. Fill GitHub personal access token credentials
7. Launch initial scan
8. Verify redirect to provider management page
### Expected Result:
- GitHub provider successfully added with personal access token
- Initial scan launched successfully
- User redirected to provider details page
### Key verification points:
- Provider page loads correctly
- Connect account page displays GitHub option
- Provider details form accepts username and alias
- Personal access token credentials page loads with token field
- Personal access token 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 GitHub username and personal access token
- Provider cleanup performed before each test to ensure clean state
- Requires valid GitHub account with personal access token
- Personal access token must have sufficient permissions for security scanning
- Test validates that personal access token goes to the correct field
---
## Test Case: `PROVIDER-E2E-009` - Add GitHub Provider with GitHub App
**Priority:** `critical`
**Tags:**
- type → @e2e, @serial
- feature → @providers
- provider → @github
**Description/Objective:** Validates the complete flow of adding a new GitHub provider using GitHub App authentication for a user account
**Preconditions:**
- Admin user authentication required (admin.auth.setup setup)
- Environment variables configured: E2E_GITHUB_USERNAME, E2E_GITHUB_APP_ID, E2E_GITHUB_BASE64_APP_PRIVATE_KEY
- Remove any existing provider with the same Username before starting the test
- This test must be run serially and never in parallel with other tests, as it requires the Username not to be already registered beforehand.
### Flow Steps:
1. Navigate to providers page
2. Click "Add Provider" button
3. Select GitHub provider type
4. Fill provider details (username and alias)
5. Select GitHub App credentials type
6. Fill GitHub App credentials (App ID and private key)
7. Launch initial scan
8. Verify redirect to provider management page
### Expected Result:
- GitHub provider successfully added with GitHub App credentials
- Initial scan launched successfully
- User redirected to provider details page
### Key verification points:
- Provider page loads correctly
- Connect account page displays GitHub option
- Provider details form accepts username and alias
- GitHub App credentials page loads with App ID and private key fields
- GitHub App credentials are properly filled in the correct fields
- Launch scan page appears
- Successful redirect to provider page after scan launch
### Notes:
- Test uses environment variables for GitHub username, App ID, and base64 encoded private key
- Private key is base64 encoded and must be decoded before use
- Provider cleanup performed before each test to ensure clean state
- Requires valid GitHub App with App ID and private key
- GitHub App must have sufficient permissions for security scanning
- Test validates that GitHub App credentials go to the correct fields
---
## Test Case: `PROVIDER-E2E-010` - Add GitHub Provider with Organization Personal Access Token
**Priority:** `critical`
**Tags:**
- type → @e2e, @serial
- feature → @providers
- provider → @github
**Description/Objective:** Validates the complete flow of adding a new GitHub provider using organization personal access token authentication
**Preconditions:**
- Admin user authentication required (admin.auth.setup setup)
- Environment variables configured: E2E_GITHUB_ORGANIZATION, E2E_GITHUB_ORGANIZATION_ACCESS_TOKEN
- Remove any existing provider with the same Organization name before starting the test
- This test must be run serially and never in parallel with other tests, as it requires the Organization name not to be already registered beforehand.
### Flow Steps:
1. Navigate to providers page
2. Click "Add Provider" button
3. Select GitHub provider type
4. Fill provider details (organization name and alias)
5. Select personal access token credentials type
6. Fill GitHub organization personal access token credentials
7. Launch initial scan
8. Verify redirect to provider management page
### Expected Result:
- GitHub provider successfully added with organization personal access token
- Initial scan launched successfully
- User redirected to provider details page
### Key verification points:
- Provider page loads correctly
- Connect account page displays GitHub option
- Provider details form accepts organization name and alias
- Personal access token credentials page loads with token field
- Organization personal access token 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 GitHub organization name and organization access token
- Provider cleanup performed before each test to ensure clean state
- Requires valid GitHub organization with organization access token
- Organization access token must have sufficient permissions for security scanning
- Test validates that organization personal access token goes to the correct field

View File

@@ -0,0 +1,936 @@
import { test } from "@playwright/test";
import * as helpers from "../helpers";
import {
ProvidersPage,
AWSProviderData,
AWSProviderCredential,
AWS_CREDENTIAL_OPTIONS,
AZUREProviderData,
AZUREProviderCredential,
AZURE_CREDENTIAL_OPTIONS,
M365ProviderData,
M365ProviderCredential,
M365_CREDENTIAL_OPTIONS,
KubernetesProviderData,
KubernetesProviderCredential,
KUBERNETES_CREDENTIAL_OPTIONS,
GCPProviderData,
GCPProviderCredential,
GCP_CREDENTIAL_OPTIONS,
GitHubProviderData,
GitHubProviderCredential,
GITHUB_CREDENTIAL_OPTIONS,
} from "./providers-page";
import { ScansPage } from "../scans/scans-page";
import fs from "fs";
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;
const secretKey = process.env.E2E_AWS_PROVIDER_SECRET_KEY;
const roleArn = process.env.E2E_AWS_PROVIDER_ROLE_ARN;
// Validate required environment variables
if (!accountId) {
throw new Error(
"E2E_AWS_PROVIDER_ACCOUNT_ID environment variable is not set",
);
}
// Setup before each test
test.beforeEach(async ({ page }) => {
providersPage = new ProvidersPage(page);
// Clean up existing provider to ensure clean test state
await providersPage.deleteProviderIfExists(accountId);
});
// Use admin authentication for provider management
test.use({ storageState: "playwright/.auth/admin_user.json" });
test(
"should add a new AWS provider with static credentials",
{
tag: [
"@critical",
"@e2e",
"@providers",
"@aws",
"@serial",
"@PROVIDER-E2E-001",
],
},
async ({ page }) => {
// Validate required environment variables
if (!accountId || !accessKey || !secretKey) {
throw new Error(
"E2E_AWS_PROVIDER_ACCOUNT_ID, E2E_AWS_PROVIDER_ACCESS_KEY, and E2E_AWS_PROVIDER_SECRET_KEY environment variables are not set",
);
}
// Prepare test data for AWS provider
const awsProviderData: AWSProviderData = {
accountId: accountId,
alias: "Test E2E AWS Account - Credentials",
};
// Prepare static credentials
const staticCredentials: AWSProviderCredential = {
type: AWS_CREDENTIAL_OPTIONS.AWS_CREDENTIALS,
accessKeyId: accessKey,
secretAccessKey: secretKey,
};
// Navigate to providers page
await providersPage.goto();
await providersPage.verifyPageLoaded();
// Start adding new provider
await providersPage.clickAddProvider();
await providersPage.verifyConnectAccountPageLoaded();
// Select AWS provider
await providersPage.selectAWSProvider();
// Fill provider details
await providersPage.fillAWSProviderDetails(awsProviderData);
await providersPage.clickNext();
// Select static credentials type
await providersPage.selectCredentialsType(
AWS_CREDENTIAL_OPTIONS.AWS_CREDENTIALS,
);
await providersPage.verifyCredentialsPageLoaded();
// Fill static credentials
await providersPage.fillStaticCredentials(staticCredentials);
await providersPage.clickNext();
// Launch scan
await providersPage.verifyLaunchScanPageLoaded();
await providersPage.clickNext();
// Wait for redirect to provider page
scansPage = new ScansPage(page);
await scansPage.verifyPageLoaded();
},
);
test(
"should add a new AWS provider with assume role credentials with Access Key and Secret Key",
{
tag: [
"@critical",
"@e2e",
"@providers",
"@aws",
"@serial",
"@PROVIDER-E2E-002",
],
},
async ({ page }) => {
// Validate required environment variables
if (!accountId || !accessKey || !secretKey || !roleArn) {
throw new Error(
"E2E_AWS_PROVIDER_ACCOUNT_ID, E2E_AWS_PROVIDER_ACCESS_KEY, E2E_AWS_PROVIDER_SECRET_KEY, and E2E_AWS_PROVIDER_ROLE_ARN environment variables are not set",
);
}
// Prepare test data for AWS provider
const awsProviderData: AWSProviderData = {
accountId: accountId,
alias: "Test E2E AWS Account - Credentials",
};
// Prepare role-based credentials
const roleCredentials: AWSProviderCredential = {
type: AWS_CREDENTIAL_OPTIONS.AWS_ROLE_ARN,
accessKeyId: accessKey,
secretAccessKey: secretKey,
roleArn: roleArn,
};
// Navigate to providers page
await providersPage.goto();
await providersPage.verifyPageLoaded();
// Start adding new provider
await providersPage.clickAddProvider();
await providersPage.verifyConnectAccountPageLoaded();
// Select AWS provider
await providersPage.selectAWSProvider();
// Fill provider details
await providersPage.fillAWSProviderDetails(awsProviderData);
await providersPage.clickNext();
// Select role credentials type
await providersPage.selectCredentialsType(
AWS_CREDENTIAL_OPTIONS.AWS_ROLE_ARN,
);
await providersPage.verifyCredentialsPageLoaded();
// Fill role credentials
await providersPage.fillRoleCredentials(roleCredentials);
await providersPage.clickNext();
// Launch scan
await providersPage.verifyLaunchScanPageLoaded();
await providersPage.clickNext();
// Wait for redirect to provider page
scansPage = new ScansPage(page);
await scansPage.verifyPageLoaded();
},
);
});
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;
const clientId = process.env.E2E_AZURE_CLIENT_ID;
const clientSecret = process.env.E2E_AZURE_SECRET_ID;
const tenantId = process.env.E2E_AZURE_TENANT_ID;
// Validate required environment variables
if (!subscriptionId || !clientId || !clientSecret || !tenantId) {
throw new Error(
"E2E_AZURE_SUBSCRIPTION_ID, E2E_AZURE_CLIENT_ID, E2E_AZURE_SECRET_ID, and E2E_AZURE_TENANT_ID environment variables are not set",
);
}
// Setup before each test
test.beforeEach(async ({ page }) => {
providersPage = new ProvidersPage(page);
// Clean up existing provider to ensure clean test state
await providersPage.deleteProviderIfExists(subscriptionId);
});
// Use admin authentication for provider management
test.use({ storageState: "playwright/.auth/admin_user.json" });
test(
"should add a new Azure provider with static credentials",
{
tag: [
"@critical",
"@e2e",
"@providers",
"@azure",
"@serial",
"@PROVIDER-E2E-003",
],
},
async ({ page }) => {
// Prepare test data for AZURE provider
const azureProviderData: AZUREProviderData = {
subscriptionId: subscriptionId,
alias: "Test E2E AZURE Account - Credentials",
};
// Prepare static credentials
const azureCredentials: AZUREProviderCredential = {
type: AZURE_CREDENTIAL_OPTIONS.AZURE_CREDENTIALS,
clientId: clientId,
clientSecret: clientSecret,
tenantId: tenantId,
};
// Navigate to providers page
await providersPage.goto();
await providersPage.verifyPageLoaded();
// Start adding new provider
await providersPage.clickAddProvider();
await providersPage.verifyConnectAccountPageLoaded();
// Select AZURE provider
await providersPage.selectAZUREProvider();
// Fill provider details
await providersPage.fillAZUREProviderDetails(azureProviderData);
await providersPage.clickNext();
// Fill static credentials details
await providersPage.fillAZURECredentials(azureCredentials);
await providersPage.clickNext();
// Launch scan
await providersPage.verifyLaunchScanPageLoaded();
await providersPage.clickNext();
// Wait for redirect to scan page
scansPage = new ScansPage(page);
await scansPage.verifyPageLoaded();
},
);
});
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;
const clientId = process.env.E2E_M365_CLIENT_ID;
const tenantId = process.env.E2E_M365_TENANT_ID;
// Validate required environment variables
if (!domainId || !clientId || !tenantId) {
throw new Error(
"E2E_M365_DOMAIN_ID, E2E_M365_CLIENT_ID, and E2E_M365_TENANT_ID environment variables are not set",
);
}
// Setup before each test
test.beforeEach(async ({ page }) => {
providersPage = new ProvidersPage(page);
// Clean up existing provider to ensure clean test state
await providersPage.deleteProviderIfExists(domainId);
});
// Use admin authentication for provider management
test.use({ storageState: "playwright/.auth/admin_user.json" });
test(
"should add a new M365 provider with static credentials",
{
tag: [
"@critical",
"@e2e",
"@providers",
"@m365",
"@serial",
"@PROVIDER-E2E-004",
],
},
async ({ page }) => {
// Validate required environment variables
const clientSecret = process.env.E2E_M365_SECRET_ID;
if (!clientSecret) {
throw new Error("E2E_M365_SECRET_ID environment variable is not set");
}
// Prepare test data for M365 provider
const m365ProviderData: M365ProviderData = {
domainId: domainId,
alias: "Test E2E M365 Account - Credentials",
};
// Prepare static credentials
const m365Credentials: M365ProviderCredential = {
type: M365_CREDENTIAL_OPTIONS.M365_CREDENTIALS,
clientId: clientId,
clientSecret: clientSecret,
tenantId: tenantId,
};
// Navigate to providers page
await providersPage.goto();
await providersPage.verifyPageLoaded();
// Start adding new provider
await providersPage.clickAddProvider();
await providersPage.verifyConnectAccountPageLoaded();
// Select M365 provider
await providersPage.selectM365Provider();
// Fill provider details
await providersPage.fillM365ProviderDetails(m365ProviderData);
await providersPage.clickNext();
// Select static credentials type
await providersPage.selectM365CredentialsType(
M365_CREDENTIAL_OPTIONS.M365_CREDENTIALS,
);
// Verify M365 credentials page is loaded
await providersPage.verifyM365CredentialsPageLoaded();
// Fill static credentials details
await providersPage.fillM365Credentials(m365Credentials);
await providersPage.clickNext();
// Launch scan
await providersPage.verifyLaunchScanPageLoaded();
await providersPage.clickNext();
// Wait for redirect to scan page
scansPage = new ScansPage(page);
await scansPage.verifyPageLoaded();
},
);
test(
"should add a new M365 provider with certificate",
{
tag: [
"@critical",
"@e2e",
"@providers",
"@m365",
"@serial",
"@PROVIDER-E2E-005",
],
},
async ({ page }) => {
// Validate required environment variables
const certificateContent = process.env.E2E_M365_CERTIFICATE_CONTENT;
if (!certificateContent) {
throw new Error(
"E2E_M365_CERTIFICATE_CONTENT environment variable is not set",
);
}
// Prepare test data for M365 provider
const m365ProviderData: M365ProviderData = {
domainId: domainId,
alias: "Test E2E M365 Account - Certificate",
};
// Prepare static credentials
const m365Credentials: M365ProviderCredential = {
type: M365_CREDENTIAL_OPTIONS.M365_CERTIFICATE_CREDENTIALS,
clientId: clientId,
tenantId: tenantId,
certificateContent: certificateContent,
};
// Navigate to providers page
await providersPage.goto();
await providersPage.verifyPageLoaded();
// Start adding new provider
await providersPage.clickAddProvider();
await providersPage.verifyConnectAccountPageLoaded();
// Select M365 provider
await providersPage.selectM365Provider();
// Fill provider details
await providersPage.fillM365ProviderDetails(m365ProviderData);
await providersPage.clickNext();
// Select static credentials type
await providersPage.selectM365CredentialsType(
M365_CREDENTIAL_OPTIONS.M365_CERTIFICATE_CREDENTIALS,
);
// Verify M365 certificate credentials page is loaded
await providersPage.verifyM365CertificateCredentialsPageLoaded();
// Fill static credentials details
await providersPage.fillM365CertificateCredentials(m365Credentials);
await providersPage.clickNext();
// Launch scan
await providersPage.verifyLaunchScanPageLoaded();
await providersPage.clickNext();
// Wait for redirect to scan page
scansPage = new ScansPage(page);
await scansPage.verifyPageLoaded();
},
);
});
test.describe.serial("Add Kubernetes Provider", () => {
// Providers page object
let providersPage: ProvidersPage;
let scansPage: ScansPage;
// 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",
);
}
// Setup before each test
test.beforeEach(async ({ page }) => {
providersPage = new ProvidersPage(page);
// Clean up existing provider to ensure clean test state
await providersPage.deleteProviderIfExists(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 }) => {
// 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,
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
scansPage = new ScansPage(page);
await scansPage.verifyPageLoaded();
},
);
});
test.describe.serial("Add GCP Provider", () => {
// Providers page object
let providersPage: ProvidersPage;
let scansPage: ScansPage;
// Test data from environment variables
const projectId = process.env.E2E_GCP_PROJECT_ID;
// Validate required environment variables
if (!projectId) {
throw new Error("E2E_GCP_PROJECT_ID environment variable is not set");
}
// Setup before each test
test.beforeEach(async ({ page }) => {
providersPage = new ProvidersPage(page);
// Clean up existing provider to ensure clean test state
await providersPage.deleteProviderIfExists(projectId);
});
// Use admin authentication for provider management
test.use({ storageState: "playwright/.auth/admin_user.json" });
test(
"should add a new GCP provider with service account key",
{
tag: [
"@critical",
"@e2e",
"@providers",
"@gcp",
"@serial",
"@PROVIDER-E2E-007",
],
},
async ({ page }) => {
// Validate required environment variables
const serviceAccountKeyB64 =
process.env.E2E_GCP_BASE64_SERVICE_ACCOUNT_KEY;
// Verify service account key is base64 encoded
if (!serviceAccountKeyB64) {
throw new Error(
"E2E_GCP_BASE64_SERVICE_ACCOUNT_KEY environment variable is not set",
);
}
// Decode service account key from base64
const serviceAccountKey = Buffer.from(
serviceAccountKeyB64,
"base64",
).toString("utf8");
// Verify service account key is valid JSON
if (!JSON.parse(serviceAccountKey)) {
throw new Error("Invalid service account key format");
}
// Prepare test data for GCP provider
const gcpProviderData: GCPProviderData = {
projectId: projectId,
alias: "Test E2E GCP Account - Service Account Key",
};
// Prepare static credentials
const gcpCredentials: GCPProviderCredential = {
type: GCP_CREDENTIAL_OPTIONS.GCP_SERVICE_ACCOUNT,
serviceAccountKey: serviceAccountKey,
};
// Navigate to providers page
await providersPage.goto();
await providersPage.verifyPageLoaded();
// Start adding new provider
await providersPage.clickAddProvider();
await providersPage.verifyConnectAccountPageLoaded();
// Select M365 provider
await providersPage.selectGCPProvider();
// Fill provider details
await providersPage.fillGCPProviderDetails(gcpProviderData);
await providersPage.clickNext();
// Select static credentials type
await providersPage.selectGCPCredentialsType(
GCP_CREDENTIAL_OPTIONS.GCP_SERVICE_ACCOUNT,
);
// Verify GCP service account page is loaded
await providersPage.verifyGCPServiceAccountPageLoaded();
// Fill static service account key details
await providersPage.fillGCPServiceAccountKeyCredentials(gcpCredentials);
await providersPage.clickNext();
// Launch scan
await providersPage.verifyLaunchScanPageLoaded();
await providersPage.clickNext();
// Wait for redirect to scan page
scansPage = new ScansPage(page);
await scansPage.verifyPageLoaded();
},
);
});
test.describe.serial("Add GitHub Provider", () => {
// Providers page object
let providersPage: ProvidersPage;
let scansPage: ScansPage;
test.describe("Add GitHub provider with username", () => {
// Test data from environment variables
const username = process.env.E2E_GITHUB_USERNAME;
// Validate required environment variables
if (!username) {
throw new Error("E2E_GITHUB_USERNAME environment variable is not set");
}
// Setup before each test
test.beforeEach(async ({ page }) => {
providersPage = new ProvidersPage(page);
// Clean up existing provider to ensure clean test state
await providersPage.deleteProviderIfExists(username);
});
// Use admin authentication for provider management
test.use({ storageState: "playwright/.auth/admin_user.json" });
test(
"should add a new GitHub provider with personal access token",
{
tag: [
"@critical",
"@e2e",
"@providers",
"@github",
"@serial",
"@PROVIDER-E2E-008",
],
},
async ({ page }) => {
// Validate required environment variables
const personalAccessToken =
process.env.E2E_GITHUB_PERSONAL_ACCESS_TOKEN;
// Verify username and personal access token are set in environment variables
if (!personalAccessToken) {
throw new Error(
"E2E_GITHUB_PERSONAL_ACCESS_TOKEN environment variables are not set",
);
}
// Prepare test data for GitHub provider
const githubProviderData: GitHubProviderData = {
username: username,
alias: "Test E2E GitHub Account - Personal Access Token",
};
// Prepare personal access token credentials
const githubCredentials: GitHubProviderCredential = {
type: GITHUB_CREDENTIAL_OPTIONS.GITHUB_PERSONAL_ACCESS_TOKEN,
personalAccessToken: personalAccessToken,
};
// Navigate to providers page
await providersPage.goto();
await providersPage.verifyPageLoaded();
// Start adding new provider
await providersPage.clickAddProvider();
await providersPage.verifyConnectAccountPageLoaded();
// Select GitHub provider
await providersPage.selectGitHubProvider();
// Fill provider details
await providersPage.fillGitHubProviderDetails(githubProviderData);
await providersPage.clickNext();
// Select GitHub personal access token credentials type
await providersPage.selectGitHubCredentialsType(
GITHUB_CREDENTIAL_OPTIONS.GITHUB_PERSONAL_ACCESS_TOKEN,
);
// Verify GitHub personal access token page is loaded
await providersPage.verifyGitHubPersonalAccessTokenPageLoaded();
// Fill static personal access token details
await providersPage.fillGitHubPersonalAccessTokenCredentials(
githubCredentials,
);
await providersPage.clickNext();
// Launch scan
await providersPage.verifyLaunchScanPageLoaded();
await providersPage.clickNext();
// Wait for redirect to scan page
scansPage = new ScansPage(page);
await scansPage.verifyPageLoaded();
},
);
test(
"should add a new GitHub provider with github app",
{
tag: [
"@critical",
"@e2e",
"@providers",
"@github",
"@serial",
"@PROVIDER-E2E-009",
],
},
async ({ page }) => {
// Validate required environment variables
const githubAppId =
process.env.E2E_GITHUB_APP_ID;
const githubAppPrivateKeyB64 =
process.env.E2E_GITHUB_BASE64_APP_PRIVATE_KEY;
// Verify github app id and private key are set in environment variables
if (!githubAppId || !githubAppPrivateKeyB64) {
throw new Error(
"E2E_GITHUB_APP_ID and E2E_GITHUB_APP_PRIVATE_KEY environment variables are not set",
);
}
// Decode github app private key from base64
const githubAppPrivateKey = Buffer.from(
githubAppPrivateKeyB64,
"base64",
).toString("utf8");
// Prepare test data for GitHub provider
const githubProviderData: GitHubProviderData = {
username: username,
alias: "Test E2E GitHub Account - GitHub App",
};
// Prepare github app credentials
const githubCredentials: GitHubProviderCredential = {
type: GITHUB_CREDENTIAL_OPTIONS.GITHUB_APP,
githubAppId: githubAppId,
githubAppPrivateKey: githubAppPrivateKey,
};
// Navigate to providers page
await providersPage.goto();
await providersPage.verifyPageLoaded();
// Start adding new provider
await providersPage.clickAddProvider();
await providersPage.verifyConnectAccountPageLoaded();
// Select GitHub provider
await providersPage.selectGitHubProvider();
// Fill provider details
await providersPage.fillGitHubProviderDetails(githubProviderData);
await providersPage.clickNext();
// Select static github app credentials type
await providersPage.selectGitHubCredentialsType(
GITHUB_CREDENTIAL_OPTIONS.GITHUB_APP,
);
// Verify GitHub github app page is loaded
await providersPage.verifyGitHubAppPageLoaded();
// Fill static github app credentials details
await providersPage.fillGitHubAppCredentials(
githubCredentials,
);
await providersPage.clickNext();
// Launch scan
await providersPage.verifyLaunchScanPageLoaded();
await providersPage.clickNext();
// Wait for redirect to scan page
scansPage = new ScansPage(page);
await scansPage.verifyPageLoaded();
},
);
});
test.describe("Add GitHub provider with organization", () => {
// Test data from environment variables
const organization = process.env.E2E_GITHUB_ORGANIZATION;
// Validate required environment variables
if (!organization) {
throw new Error(
"E2E_GITHUB_ORGANIZATION environment variable is not set",
);
}
// Setup before each test
test.beforeEach(async ({ page }) => {
providersPage = new ProvidersPage(page);
// Clean up existing provider to ensure clean test state
await providersPage.deleteProviderIfExists(organization);
});
// Use admin authentication for provider management
test.use({ storageState: "playwright/.auth/admin_user.json" });
test(
"should add a new GitHub provider with organization personal access token",
{
tag: [
"@critical",
"@e2e",
"@providers",
"@github",
"@serial",
"@PROVIDER-E2E-010",
],
},
async ({ page }) => {
// Validate required environment variables
const organizationAccessToken =
process.env.E2E_GITHUB_ORGANIZATION_ACCESS_TOKEN;
// Verify username and personal access token are set in environment variables
if (!organizationAccessToken) {
throw new Error(
"E2E_GITHUB_ORGANIZATION_ACCESS_TOKEN environment variables are not set",
);
}
// Prepare test data for GitHub provider
const githubProviderData: GitHubProviderData = {
username: organization,
alias: "Test E2E GitHub Account - Organization Access Token",
};
// Prepare personal access token credentials
const githubCredentials: GitHubProviderCredential = {
type: GITHUB_CREDENTIAL_OPTIONS.GITHUB_PERSONAL_ACCESS_TOKEN,
personalAccessToken: organizationAccessToken,
};
// Navigate to providers page
await providersPage.goto();
await providersPage.verifyPageLoaded();
// Start adding new provider
await providersPage.clickAddProvider();
await providersPage.verifyConnectAccountPageLoaded();
// Select GitHub provider
await providersPage.selectGitHubProvider();
// Fill provider details
await providersPage.fillGitHubProviderDetails(githubProviderData);
await providersPage.clickNext();
// Select GitHub organization personal access token credentials type
await providersPage.selectGitHubCredentialsType(
GITHUB_CREDENTIAL_OPTIONS.GITHUB_PERSONAL_ACCESS_TOKEN,
);
// Verify GitHub personal access token page is loaded
await providersPage.verifyGitHubPersonalAccessTokenPageLoaded();
// Fill static personal access token details
await providersPage.fillGitHubPersonalAccessTokenCredentials(
githubCredentials,
);
await providersPage.clickNext();
// Launch scan
await providersPage.verifyLaunchScanPageLoaded();
await providersPage.clickNext();
// Wait for redirect to scan page
scansPage = new ScansPage(page);
await scansPage.verifyPageLoaded();
},
);
});
});
});

View File

@@ -0,0 +1,99 @@
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;
// Scan provider selection elements
readonly scanProviderSelect: Locator;
readonly scanAliasInput: Locator;
readonly startNowButton: Locator;
// Scan state elements
readonly successToast: Locator;
constructor(page: Page) {
super(page);
// Scan provider selection elements
this.scanProviderSelect = page.getByRole("button", { name: "Select a cloud provider to launch a scan" });
this.scanAliasInput = page.getByRole("textbox", { name: "Scan label (optional)" });
this.startNowButton = page.getByRole("button", { name: /Start now|Start scan now/i });
// Scan state elements
this.successToast = page.getByRole("alert", { name: /The scan was launched successfully\.?/i });
// Main content elements
this.scanTable = page.locator("table");
}
// Navigation methods
async goto(): Promise<void> {
await super.goto("/scans");
}
async verifyPageLoaded(): Promise<void> {
// Verify the scans page is loaded
await expect(this.page).toHaveTitle(/Prowler/);
await expect(this.scanTable).toBeVisible();
await this.waitForPageLoad();
}
async selectProviderByUID(uid: string): Promise<void> {
// Select the provider by UID
await this.scanProviderSelect.click();
await this.page.getByRole("option", { name: new RegExp(uid) }).click();
}
async fillScanAlias(alias: string): Promise<void> {
// Fill the scan alias
await this.scanAliasInput.fill(alias);
}
async clickStartNowButton(): Promise<void> {
// Click the start now button
await expect(this.startNowButton).toBeVisible();
await this.startNowButton.click();
}
async verifyScanLaunched(alias: string): Promise<void> {
// Verify the scan was launched
// Verify the success toast is visible
await this.successToast.waitFor({ state: "visible", timeout: 5000 }).catch(() => {});
// Wait for the scans table to be visible
await expect(this.scanTable).toBeVisible();
// Find a row that contains the scan alias
const rowWithAlias = this.scanTable
.locator("tbody tr")
.filter({ hasText: alias })
.first();
// Verify the row with the scan alias is visible
await expect(rowWithAlias).toBeVisible();
// Basic state/assertion hint: queued/available/executing (non-blocking if not present)
await rowWithAlias.textContent().then((text) => {
if (!text) return;
const hasExpectedState = /executing|available|queued/i.test(text);
if (!hasExpectedState) {
// Fall back to just ensuring alias is present in the row
// The expectation above already ensures visibility
}
});
}
}

55
ui/tests/scans/scans.md Normal file
View File

@@ -0,0 +1,55 @@
### E2E Tests: Scans - On Demand
**Suite ID:** `SCANS-E2E`
**Feature:** On-demand Scans.
---
## Test Case: `SCANS-E2E-001` - Execute On-Demand Scan
**Priority:** `critical`
**Tags:**
- type → @e2e, @serial
- feature → @scans
**Description/Objective:** Validates the complete flow to execute an on-demand scan selecting a provider by UID and confirming success on the Scans page.
**Preconditions:**
- Admin user authentication required (admin.auth.setup setup)
- Environment variables configured for : E2E_AWS_PROVIDER_ACCOUNT_ID,E2E_AWS_PROVIDER_ACCESS_KEY and E2E_AWS_PROVIDER_SECRET_KEY
- Remove any existing AWS provider with the same Account ID before starting the test
- This test must be run serially and never in parallel with other tests, as it requires the Account ID Provider to be already registered.
### Flow Steps:
1. Navigate to Scans page
2. Open provider selector and choose the entry whose text contains E2E_AWS_PROVIDER_ACCOUNT_ID
3. Optionally fill scan label (alias)
4. Click "Start now" to launch the scan
5. Verify the success toast appears
6. Verify a row in the Scans table contains the provided scan label (or shows the new scan entry)
### Expected Result:
- Scan is launched successfully
- Success toast is displayed to the user
- Scans table displays the new scan entry (including the alias when provided)
### Key verification points:
- Scans page loads correctly
- Provider select is available and lists the configured provider UID
- "Start now" button is rendered and enabled when form is valid
- Success toast message: "The scan was launched successfully."
- Table contains a row with the scan label or new scan state (queued/available/executing)
### Notes:
- The table may take a short time to reflect the new scan; assertions look for a row containing the alias.
- Provider cleanup performed before each test to ensure clean state
- Tests should run serially to avoid state conflicts.

View File

@@ -0,0 +1,70 @@
import { test, expect } from "@playwright/test";
import { ScansPage } from "./scans-page";
import { ProvidersPage } from "../providers/providers-page";
// Scans E2E suite scaffold
test.describe("Scans", () => {
test.describe.serial("Execute Scans", () => {
// Scans page object
let scansPage: ScansPage;
// Use scans-specific authenticated user
test.use({ storageState: "playwright/.auth/admin_user.json" });
// Before each scans test, ensure an AWS provider exists using admin context
test.beforeEach(async ({ page }) => {
// Create scans page object
const providersPage = new ProvidersPage(page);
// Test data from environment variables
const accountId = process.env.E2E_AWS_PROVIDER_ACCOUNT_ID;
const accessKey = process.env.E2E_AWS_PROVIDER_ACCESS_KEY;
const secretKey = process.env.E2E_AWS_PROVIDER_SECRET_KEY;
if (!accountId || !accessKey || !secretKey) {
throw new Error(
"E2E_AWS_PROVIDER_ACCOUNT_ID, E2E_AWS_PROVIDER_ACCESS_KEY, and E2E_AWS_PROVIDER_SECRET_KEY environment variables are not set",
);
}
// Clean up existing provider to ensure clean test state
await providersPage.deleteProviderIfExists(accountId);
// Add AWS provider
await providersPage.AddAWSProvider(accountId, accessKey, secretKey);
});
test(
"should execute on demand scan",
{
tag: ["@e2e", "@scans", "@critical", "@serial", "@SCAN-E2E-001"],
},
async ({ page }) => {
const accountId = process.env.E2E_AWS_PROVIDER_ACCOUNT_ID;
if (!accountId) {
throw new Error(
"E2E_AWS_PROVIDER_ACCOUNT_ID environment variable is not set",
);
}
scansPage = new ScansPage(page);
await scansPage.goto();
// Select provider by UID (accountId)
await scansPage.selectProviderByUID(accountId);
// Complete scan alias
await scansPage.fillScanAlias("E2E Test Scan - On Demand");
// Press start now button
await scansPage.clickStartNowButton();
// Verify the scan was launched
await scansPage.verifyScanLaunched("E2E Test Scan - On Demand");
},
);
});
});

View File

@@ -4,6 +4,7 @@ import { authenticateAndSaveState } from '@/tests/helpers';
const adminUserFile = 'playwright/.auth/admin_user.json';
authAdminSetup('authenticate as admin e2e user', async ({ page }) => {
const adminEmail = process.env.E2E_ADMIN_USER;
const adminPassword = process.env.E2E_ADMIN_PASSWORD;

View File

@@ -6,7 +6,7 @@ const manageCloudProvidersUserFile = 'playwright/.auth/manage_cloud_providers_us
authManageCloudProvidersSetup('authenticate as manage cloud providers e2e user', async ({ page }) => {
const cloudProvidersEmail = process.env.E2E_MANAGE_CLOUD_PROVIDERS_USER;
const cloudProvidersPassword = process.env.E2E_MANAGE_CLOUD_PROVIDERS_PASSWORD;
if (!cloudProvidersEmail || !cloudProvidersPassword) {
throw new Error('E2E_MANAGE_CLOUD_PROVIDERS_USER and E2E_MANAGE_CLOUD_PROVIDERS_PASSWORD environment variables are required');
}

View File

@@ -20,7 +20,7 @@ import {
ERROR_MESSAGES,
URLS,
verifyLoadingState,
} from "./helpers";
} from "../helpers";
test.describe("Login Flow", () => {
test.beforeEach(async ({ page }) => {

View File

@@ -1,5 +1,6 @@
import { Page, Locator, expect } from "@playwright/test";
import { HomePage } from "./home-page";
import { BasePage } from "../base-page";
import { HomePage } from "../home/home-page";
export interface SignInCredentials {
email: string;
@@ -11,8 +12,7 @@ export interface SocialAuthConfig {
githubEnabled: boolean;
}
export class SignInPage {
readonly page: Page;
export class SignInPage extends BasePage {
readonly homePage: HomePage;
// Form elements
@@ -31,59 +31,48 @@ export class SignInPage {
readonly backButton: Locator;
// UI elements
readonly title: Locator;
readonly logo: Locator;
readonly themeToggle: Locator;
// Error messages
readonly errorMessages: Locator;
readonly loadingIndicator: Locator;
// SAML specific elements
readonly samlModeTitle: Locator;
readonly samlEmailInput: Locator;
constructor(page: Page) {
this.page = page;
super(page);
this.homePage = new HomePage(page);
// Form elements
this.emailInput = page.getByLabel("Email");
this.passwordInput = page.getByLabel("Password");
this.emailInput = page.getByRole("textbox", { name: "Email" });
this.passwordInput = page.getByRole("textbox", { name: "Password" });
this.loginButton = page.getByRole("button", { name: "Log in" });
this.form = page.locator("form");
// Social authentication buttons
this.googleButton = page.getByText("Continue with Google");
this.githubButton = page.getByText("Continue with Github");
this.samlButton = page.getByText("Continue with SAML SSO");
this.googleButton = page.getByRole("button", { name: "Continue with Google" });
this.githubButton = page.getByRole("button", { name: "Continue with Github" });
this.samlButton = page.getByRole("button", { name: "Continue with SAML SSO" });
// Navigation elements
this.signUpLink = page.getByRole("link", { name: "Sign up" });
this.backButton = page.getByText("Back");
this.backButton = page.getByRole("button", { name: "Back" });
// UI elements
this.title = page.getByText("Sign in", { exact: true });
this.logo = page.locator('svg[width="300"]');
this.themeToggle = page.getByLabel("Toggle theme");
// Error messages
this.errorMessages = page.locator('[role="alert"], .error-message, [data-testid="error"]');
this.loadingIndicator = page.getByText("Loading");
// SAML specific elements
this.samlModeTitle = page.getByText("Sign in with SAML SSO");
this.samlEmailInput = page.getByLabel("Email");
this.samlModeTitle = page.getByRole("heading", { name: "Sign in with SAML SSO" });
this.samlEmailInput = page.getByRole("textbox", { name: "Email" });
}
// Navigation methods
async goto(): Promise<void> {
await this.page.goto("/sign-in");
await this.waitForPageLoad();
}
async waitForPageLoad(): Promise<void> {
await this.page.waitForLoadState("networkidle");
await super.goto("/sign-in");
}
// Form interaction methods
@@ -148,8 +137,7 @@ export class SignInPage {
async verifyPageLoaded(): Promise<void> {
await expect(this.page).toHaveTitle(/Prowler/);
await expect(this.logo).toBeVisible();
await expect(this.title).toBeVisible();
await this.waitForPageLoad();
await expect(this.page.getByRole("heading", { name: "Sign in", exact: true })).toBeVisible();
}
async verifyFormElements(): Promise<void> {
@@ -169,7 +157,7 @@ export class SignInPage {
}
async verifyNavigationLinks(): Promise<void> {
await expect(this.page.getByText("Need to create an account?")).toBeVisible();
await expect(this.page.getByRole('link', { name: /Need to create an account\?/i })).toBeVisible();
await expect(this.signUpLink).toBeVisible();
}
@@ -178,7 +166,7 @@ export class SignInPage {
}
async verifyLoginError(errorMessage: string = "Invalid email or password"): Promise<void> {
await expect(this.page.getByText(errorMessage).first()).toBeVisible();
await expect(this.page.getByRole("alert", { name: errorMessage })).toBeVisible();
await expect(this.page).toHaveURL("/sign-in");
}
@@ -189,19 +177,19 @@ export class SignInPage {
}
async verifyNormalModeActive(): Promise<void> {
await expect(this.title).toBeVisible();
await expect(this.page.getByRole("heading", { name: "Sign in", exact: true })).toBeVisible();
await expect(this.passwordInput).toBeVisible();
}
async verifyLoadingState(): Promise<void> {
await expect(this.loginButton).toHaveAttribute("aria-disabled", "true");
await expect(this.loadingIndicator).toBeVisible();
await super.verifyLoadingState();
}
async verifyFormValidation(): Promise<void> {
// Check for common validation messages
const emailError = this.page.getByText("Please enter a valid email address.");
const passwordError = this.page.getByText("Password is required.");
const emailError = this.page.getByRole("alert", { name: "Please enter a valid email address." });
const passwordError = this.page.getByRole("alert", { name: "Password is required." });
// At least one validation error should be visible
await expect(emailError.or(passwordError)).toBeVisible();
@@ -240,30 +228,7 @@ export class SignInPage {
return emailValue.length > 0 && passwordValue.length > 0;
}
async getFormErrors(): Promise<string[]> {
const errorElements = await this.errorMessages.all();
const errors: string[] = [];
for (const element of errorElements) {
const text = await element.textContent();
if (text) {
errors.push(text.trim());
}
}
return errors;
}
// Browser interaction methods
async refresh(): Promise<void> {
await this.page.reload();
await this.waitForPageLoad();
}
async goBack(): Promise<void> {
await this.page.goBack();
await this.waitForPageLoad();
}
// Session management methods
async logout(): Promise<void> {
@@ -272,7 +237,7 @@ export class SignInPage {
async verifyLogoutSuccess(): Promise<void> {
await expect(this.page).toHaveURL("/sign-in");
await expect(this.title).toBeVisible();
await expect(this.page.getByRole("heading", { name: "Sign in", exact: true })).toBeVisible();
}
// Advanced interaction methods
@@ -295,7 +260,7 @@ export class SignInPage {
// Error handling methods
async handleSamlError(): Promise<void> {
const samlError = this.page.getByText("SAML Authentication Error");
const samlError = this.page.getByRole("alert", { name: "SAML Authentication Error" });
if (await samlError.isVisible()) {
// Handle SAML error if present
console.log("SAML authentication error detected");

View File

@@ -0,0 +1,146 @@
import { Page, Locator, expect } from "@playwright/test";
import { BasePage } from "../base-page";
export interface SignUpData {
name: string;
email: string;
password: string;
confirmPassword: string;
company?: string;
invitationToken?: string | null;
acceptTerms?: boolean;
}
export class SignUpPage extends BasePage {
// Form inputs
readonly nameInput: Locator;
readonly companyInput: Locator;
readonly emailInput: Locator;
readonly passwordInput: Locator;
readonly confirmPasswordInput: Locator;
readonly invitationTokenInput: Locator;
// UI elements
readonly submitButton: Locator;
readonly loginLink: Locator;
readonly termsCheckbox: Locator;
constructor(page: Page) {
super(page);
// Prefer stable name attributes to avoid label ambiguity in composed inputs
this.nameInput = page.locator('input[name="name"]');
this.companyInput = page.locator('input[name="company"]');
this.emailInput = page.getByRole("textbox", { name: "Email" });
this.passwordInput = page.locator('input[name="password"]');
this.confirmPasswordInput = page.locator('input[name="confirmPassword"]');
this.invitationTokenInput = page.locator('input[name="invitationToken"]');
this.submitButton = page.getByRole("button", { name: "Sign up" });
this.loginLink = page.getByRole("link", { name: "Log in" });
this.termsCheckbox = page.getByRole("checkbox", { name: /I agree with the/i });
}
async goto(): Promise<void> {
// Navigate to the sign up page
await super.goto("/sign-up");
}
async gotoInvite(shareUrl: string): Promise<void> {
// Navigate to the share url
await super.goto(shareUrl);
}
async verifyPageLoaded(): Promise<void> {
// Verify the sign up page is loaded
await expect(this.page.getByRole("heading", { name: "Sign up" })).toBeVisible();
await expect(this.emailInput).toBeVisible();
await expect(this.submitButton).toBeVisible();
await this.waitForPageLoad();
}
async fillName(name: string): Promise<void> {
// Fill the name input
await this.nameInput.fill(name);
}
async fillCompany(company?: string): Promise<void> {
// Fill the company input
if (company) {
await this.companyInput.fill(company);
}
}
async fillEmail(email: string): Promise<void> {
// Fill the email input
await this.emailInput.fill(email);
}
async fillPassword(password: string): Promise<void> {
// Fill the password input
await this.passwordInput.fill(password);
}
async fillConfirmPassword(confirmPassword: string): Promise<void> {
// Fill the confirm password input
await this.confirmPasswordInput.fill(confirmPassword);
}
async fillInvitationToken(token?: string | null): Promise<void> {
// Fill the invitation token input
if (token) {
await this.invitationTokenInput.fill(token);
}
}
async acceptTermsIfPresent(accept: boolean = true): Promise<void> {
// Accept the terms and conditions if present
if (await this.termsCheckbox.isVisible()) {
if (accept) {
await this.termsCheckbox.click();
}
}
}
async submit(): Promise<void> {
// Submit the sign up form
await this.submitButton.click();
}
async signup(data: SignUpData): Promise<void> {
// Fill the sign up form
await this.fillName(data.name);
await this.fillCompany(data.company ?? undefined);
await this.fillEmail(data.email);
await this.fillPassword(data.password);
await this.fillConfirmPassword(data.confirmPassword);
await this.acceptTermsIfPresent(data.acceptTerms ?? true);
await this.submit();
}
async verifyRedirectToLogin(): Promise<void> {
// Verify redirect to login page
await expect(this.page).toHaveURL("/sign-in");
}
async verifyRedirectToEmailVerification(): Promise<void> {
// Verify redirect to email verification page
await expect(this.page).toHaveURL("/email-verification");
}
}

View File

@@ -0,0 +1,43 @@
### E2E Tests: User Sign-Up
**Suite ID:** `SIGNUP-E2E`
**Feature:** New user registration.
---
## Test Case: `SIGNUP-E2E-001` - Successful new user registration and login
**Priority:** `critical`
**Tags:**
- type → @e2e
- feature → @signup
**Description/Objetive:** Registers a new user with valid data, verifies redirect to Login (OSS), and confirms the user can authenticate.
**Preconditions:**
- Application is running, email domain & password is acceptable for sign-up.
- No existing data in Prowler is required; the test can run on a clean state.
- `E2E_NEW_USER_PASSWORD` environment variable must be set with a valid password for the test.
### Flow Steps:
1. Navigate to the Sign up page.
2. Fill the form with valid data (unique email, valid password, terms accepted).
3. Submit the form.
4. Verify redirect to the Login page.
5. Log in with the newly created credentials.
### Expected Result:
- Sign-up succeeds and redirects to Login.
- User can log in successfully using the created credentials and reach the home page.
### Key verification points:
- After submitting sign-up, the URL changes to `/sign-in`.
- The newly created credentials can be used to sign in successfully.
- After login, the user lands on the home (`/`) and main content is visible.
### Notes:
- Test data uses a random base36 suffix to avoid collisions with email.
- The test requires the `E2E_NEW_USER_PASSWORD` environment variable to be set before running.

View File

@@ -0,0 +1,49 @@
import { test } from "@playwright/test";
import { SignUpPage } from "./sign-up-page";
import { SignInPage } from "../sign-in/sign-in-page";
import { makeSuffix } from "../helpers";
test.describe("Sign Up Flow", () => {
test(
"should register a new user successfully",
{ tag: ["@critical", "@e2e", "@signup", "@SIGNUP-E2E-001"] },
async ({ page }) => {
const password = process.env.E2E_NEW_USER_PASSWORD;
if (!password) {
throw new Error("E2E_NEW_USER_PASSWORD environment variable is not set");
}
const signUpPage = new SignUpPage(page);
await signUpPage.goto();
// Generate unique test data
const suffix = makeSuffix(10);
const uniqueEmail = `e2e+${suffix}@prowler.com`;
// Fill and submit the sign-up form
await signUpPage.signup({
name: `E2E User ${suffix}`,
company: `Test E2E Co ${suffix}`,
email: uniqueEmail,
password: password,
confirmPassword: password,
acceptTerms: true,
});
// Verify no errors occurred during sign-up
await signUpPage.verifyNoErrors();
// Verify redirect to login page (OSS environment)
await signUpPage.verifyRedirectToLogin();
// Verify the newly created user can log in successfully
const signInPage = new SignInPage(page);
await signInPage.login({
email: uniqueEmail,
password: password,
});
await signInPage.verifySuccessfulLogin();
},
);
});