name: UI - E2E Tests on: pull_request: branches: - master - "v5.*" paths: - '.github/workflows/ui-e2e-tests.yml' - 'ui/**' jobs: e2e-tests: if: github.repository == 'prowler-cloud/prowler' runs-on: ubuntu-latest env: AUTH_SECRET: 'fallback-ci-secret-for-testing' 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_GCP_BASE64_SERVICE_ACCOUNT_KEY: ${{ secrets.E2E_GCP_BASE64_SERVICE_ACCOUNT_KEY }} E2E_GCP_PROJECT_ID: ${{ secrets.E2E_GCP_PROJECT_ID }} 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_OCI_TENANCY_ID: ${{ secrets.E2E_OCI_TENANCY_ID }} E2E_OCI_USER_ID: ${{ secrets.E2E_OCI_USER_ID }} E2E_OCI_FINGERPRINT: ${{ secrets.E2E_OCI_FINGERPRINT }} E2E_OCI_KEY_CONTENT: ${{ secrets.E2E_OCI_KEY_CONTENT }} E2E_OCI_REGION: ${{ secrets.E2E_OCI_REGION }} E2E_NEW_USER_PASSWORD: ${{ secrets.E2E_NEW_USER_PASSWORD }} steps: - name: Checkout repository uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - 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: Add AWS credentials for testing AWS SDK Default Adding Provider run: | echo "Adding AWS credentials for testing AWS SDK Default Adding Provider..." echo "AWS_ACCESS_KEY_ID=${{ secrets.E2E_AWS_PROVIDER_ACCESS_KEY }}" >> .env echo "AWS_SECRET_ACCESS_KEY=${{ secrets.E2E_AWS_PROVIDER_SECRET_KEY }}" >> .env - name: Start API services run: | # Override docker-compose image tag to use latest instead of stable # This overrides any PROWLER_API_VERSION set in .env file export PROWLER_API_VERSION=latest echo "Using PROWLER_API_VERSION=${PROWLER_API_VERSION}" docker compose up -d api worker worker-beat - name: Wait for API to be ready run: | echo "Waiting for prowler-api..." timeout=150 # 5 minutes max elapsed=0 while [ $elapsed -lt $timeout ]; do if curl -s ${NEXT_PUBLIC_API_BASE_URL}/docs >/dev/null 2>&1; then echo "Prowler API is ready!" exit 0 fi echo "Waiting for prowler-api... (${elapsed}s elapsed)" sleep 5 elapsed=$((elapsed + 5)) done echo "Timeout waiting for prowler-api to start" exit 1 - name: Load database fixtures for E2E tests run: | docker compose exec -T api sh -c ' echo "Loading all fixtures from api/fixtures/dev/..." for fixture in api/fixtures/dev/*.json; do if [ -f "$fixture" ]; then echo "Loading $fixture" poetry run python manage.py loaddata "$fixture" --database admin fi done echo "All database fixtures loaded successfully!" ' - name: Setup Node.js environment uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 with: node-version: '24.13.0' - name: Setup pnpm uses: pnpm/action-setup@v4 with: version: 10 run_install: false - name: Get pnpm store directory shell: bash run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV - name: Setup pnpm and Next.js cache uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 with: path: | ${{ env.STORE_PATH }} ./ui/node_modules ./ui/.next/cache key: ${{ runner.os }}-pnpm-nextjs-${{ hashFiles('ui/pnpm-lock.yaml') }}-${{ hashFiles('ui/**/*.ts', 'ui/**/*.tsx', 'ui/**/*.js', 'ui/**/*.jsx') }} restore-keys: | ${{ runner.os }}-pnpm-nextjs-${{ hashFiles('ui/pnpm-lock.yaml') }}- ${{ runner.os }}-pnpm-nextjs- - name: Install UI dependencies working-directory: ./ui run: pnpm install --frozen-lockfile --prefer-offline - name: Build UI application working-directory: ./ui run: pnpm run build - name: Cache Playwright browsers uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 id: playwright-cache with: path: ~/.cache/ms-playwright key: ${{ runner.os }}-playwright-${{ hashFiles('ui/pnpm-lock.yaml') }} restore-keys: | ${{ runner.os }}-playwright- - name: Install Playwright browsers working-directory: ./ui if: steps.playwright-cache.outputs.cache-hit != 'true' run: pnpm run test:e2e:install - name: Run E2E tests working-directory: ./ui run: pnpm run test:e2e - name: Upload test reports uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 if: failure() with: name: playwright-report path: ui/playwright-report/ retention-days: 30 - name: Cleanup services if: always() run: | echo "Shutting down services..." docker compose down -v || true echo "Cleanup completed"