diff --git a/.gitignore b/.gitignore index d0074a9124..cd126dfc62 100644 --- a/.gitignore +++ b/.gitignore @@ -80,3 +80,6 @@ dynamic-plugins-root/* **/__pycache__/ **/.pytest_cache/ **/.venv/ + +# rhdh-local clone for e2e testing +rhdh-local/ diff --git a/docs/e2e-tests/README.md b/docs/e2e-tests/README.md index 25b245dcc1..61526184d1 100644 --- a/docs/e2e-tests/README.md +++ b/docs/e2e-tests/README.md @@ -1,6 +1,6 @@ # README for End-to-End Automation Framework -**Stack**: [Playwright](https://playwright.dev/) over TypeScript +**Stack**: [Playwright](https://playwright.dev/) over TypeScript **Repository Location**: [GitHub Repository](https://github.com/redhat-developer/rhdh/tree/main/e2e-tests) ## File Structure of the Testing Framework @@ -49,6 +49,7 @@ To run the tests, ensure you have: - [Playwright browsers installed](#install-playwright-browsers) #### macOS Users + **Important**: If you're using macOS, you need to install GNU `grep` and GNU `sed` to avoid compatibility issues with scripts and CI/CD pipelines: ```bash @@ -116,6 +117,88 @@ All Playwright project names are defined in [`e2e-tests/playwright/projects.json See the [CI documentation](CI.md#playwright-project-names-single-source-of-truth) for more details. +## Running Tests Locally with rhdh-local + +For quick local testing without a Kubernetes cluster, you can use the `run-e2e-tests.sh` script which automatically sets up [rhdh-local](https://github.com/redhat-developer/rhdh-local) and runs tests against it. + +### Prerequisites + +- **Podman** (v5.4.1+) +- **Node.js** (v18+) +- **Git** + +### Quick Start + +From the repository root: + +```bash +# Run basic sanity tests with the latest community image +./scripts/run-e2e-tests.sh --project showcase-sanity-plugins + +# Run RBAC tests +./scripts/run-e2e-tests.sh --profile rbac --project showcase-rbac + +# Build and test local changes +./scripts/run-e2e-tests.sh --build --project showcase-sanity-plugins +``` + +### Available Options + +| Option | Description | +|--------|-------------| +| `--profile ` | Test profile: `basic` (default) or `rbac` | +| `--project ` | Playwright project (default: `showcase-sanity-plugins`) | +| `--build` | Build local RHDH image from `docker/Dockerfile` | +| `--image ` | Use a specific registry image | +| `--fresh` | Force re-clone rhdh-local | +| `--skip-setup` | Skip setup, use existing running RHDH instance | +| `--skip-teardown` | Leave RHDH running after tests | + +### How It Works + +1. The script clones [rhdh-local](https://github.com/redhat-developer/rhdh-local) into `./rhdh-local/` (gitignored) +2. Copies local configs from `e2e-tests/local/` (these work without external services) +3. Starts RHDH using podman/docker compose +4. Waits for the health check endpoint +5. Runs Playwright tests with `CI=true BASE_URL=http://localhost:7007` +6. Tears down the environment (unless `--skip-teardown`) + +### Test Profiles + +| Profile | Config File | Use Case | +|---------|-------------|----------| +| `basic` | `e2e-tests/local/config-basic.yaml` | General testing with guest auth | +| `rbac` | `e2e-tests/local/config-rbac.yaml` | RBAC/permissions testing | + +> **Note**: These are minimal configs designed for local testing without external services (GitHub Apps, Keycloak, etc.). They differ from the CI configs in `.ibm/pipelines/resources/config_map/` which require a fully-configured environment. See `e2e-tests/local/README.md` for more details. + +### Test Compatibility + +Not all tests work with local rhdh-local. Here's a compatibility matrix: + +| Test Project | Compatible | Notes | +|--------------|------------|-------| +| `showcase-sanity-plugins` | ✅ Yes | Quick sanity checks | +| `showcase` | ⚠️ Partial | Some tests require GitHub integration | +| `showcase-rbac` | ✅ Yes | Use `--profile rbac` | +| `showcase-k8s` | ❌ No | Requires Kubernetes cluster | +| `showcase-auth-providers` | ❌ No | Requires Keycloak/external auth | + +### Debugging + +To keep RHDH running after tests for debugging: + +```bash +./scripts/run-e2e-tests.sh --project showcase-sanity-plugins --skip-teardown +``` + +Then access RHDH at and run tests manually: + +```bash +cd e2e-tests +BASE_URL=http://localhost:7007 yarn playwright test --ui +``` + ## Setting Up Backstage Configuration During the Pipeline [app-config-rhdh.yaml](../../.ibm/pipelines/resources/config_map/app-config-rhdh.yaml) is the configuration file used to add plugins or any other kind of configuration into Backstage during pipeline execution. diff --git a/e2e-tests/README.md b/e2e-tests/README.md index 89c0371dfc..26147457dd 100644 --- a/e2e-tests/README.md +++ b/e2e-tests/README.md @@ -3,3 +3,14 @@ The readme for the e2e framework is located [here](../docs/e2e-tests/README.md) The contribution guidelines are [here](../docs/e2e-tests/CONTRIBUTING.MD) The example and bootstraps to create tests are [here](../docs/e2e-tests/examples.md) + +## Quick Start: Local Testing + +Run e2e tests locally without a Kubernetes cluster: + +```bash +# From repository root +./scripts/run-e2e-tests.sh --project showcase-sanity-plugins +``` + +See [Local Testing Documentation](../docs/e2e-tests/README.md#running-tests-locally-with-rhdh-local) for more options. diff --git a/e2e-tests/local/README.md b/e2e-tests/local/README.md new file mode 100644 index 0000000000..ad5347fd92 --- /dev/null +++ b/e2e-tests/local/README.md @@ -0,0 +1,40 @@ +# Local E2E Testing Configuration + +This folder contains configuration files for running e2e tests locally using [rhdh-local](https://github.com/redhat-developer/rhdh-local). + +## Files + +| File | Description | +|------|-------------| +| `config-basic.yaml` | Minimal config for basic testing with guest auth | +| `config-rbac.yaml` | Config with RBAC/permissions enabled | +| `defaults.env` | Environment variable defaults (not currently used) | + +## Usage + +From the repository root: + +```bash +# Basic tests +./scripts/run-e2e-tests.sh --project showcase-sanity-plugins + +# RBAC tests +./scripts/run-e2e-tests.sh --profile rbac --project showcase-rbac + +# Build and test local changes +./scripts/run-e2e-tests.sh --build --project showcase-sanity-plugins +``` + +## Profiles + +| Profile | Config File | Use Case | +|---------|-------------|----------| +| `basic` | `config-basic.yaml` | General testing with guest auth | +| `rbac` | `config-rbac.yaml` | RBAC/permissions testing | + +## Notes + +- These configs are designed for local testing without external services +- Guest user identity is `user:development/guest` in development mode +- RBAC profile grants admin access to the guest user for testing + diff --git a/e2e-tests/local/config-basic.yaml b/e2e-tests/local/config-basic.yaml new file mode 100644 index 0000000000..b3d655f1f5 --- /dev/null +++ b/e2e-tests/local/config-basic.yaml @@ -0,0 +1,37 @@ +# Minimal local config for e2e testing with rhdh-local +# This config works without external services (GitHub Apps, Keycloak, etc.) + +app: + baseUrl: http://localhost:7007 + title: Red Hat Developer Hub (Local E2E) + +backend: + baseUrl: http://localhost:7007 + cors: + origin: http://localhost:7007 + auth: + dangerouslyDisableDefaultAuthPolicy: true + externalAccess: + - type: static + options: + token: test-token + subject: test-user + +auth: + environment: development + session: + secret: super-secret-session-key-for-local-testing + providers: + guest: + dangerouslyAllowOutsideDevelopment: true + +catalog: + import: + entityFilename: catalog-info.yaml + pullRequestBranchName: backstage-integration + rules: + - allow: [API, Component, Group, Location, Resource, System, Template, User] + locations: + - type: url + target: https://github.com/redhat-developer/rhdh/blob/main/catalog-entities/all.yaml + diff --git a/e2e-tests/local/config-rbac.yaml b/e2e-tests/local/config-rbac.yaml new file mode 100644 index 0000000000..481fa2f907 --- /dev/null +++ b/e2e-tests/local/config-rbac.yaml @@ -0,0 +1,51 @@ +# RBAC local config for e2e testing with rhdh-local +# This config enables RBAC/permissions without external services + +app: + baseUrl: http://localhost:7007 + title: Red Hat Developer Hub (Local E2E - RBAC) + +backend: + baseUrl: http://localhost:7007 + cors: + origin: http://localhost:7007 + auth: + dangerouslyDisableDefaultAuthPolicy: true + externalAccess: + - type: static + options: + token: test-token + subject: test-user + +auth: + environment: development + session: + secret: super-secret-session-key-for-local-testing + providers: + guest: + dangerouslyAllowOutsideDevelopment: true + +permission: + enabled: true + rbac: + maxDepth: 1 + policyFileReload: true + policies-csv-file: /opt/app-root/src/configs/rbac/rbac-policy.csv + pluginsWithPermission: + - catalog + - permission + - scaffolder + admin: + users: + - name: user:development/guest + +catalog: + import: + entityFilename: catalog-info.yaml + pullRequestBranchName: backstage-integration + rules: + - allow: [API, Component, Group, Location, Resource, System, Template, User] + locations: + - type: url + target: https://github.com/redhat-developer/rhdh/blob/main/catalog-entities/all.yaml + diff --git a/e2e-tests/local/defaults.env b/e2e-tests/local/defaults.env new file mode 100644 index 0000000000..db8d2f0303 --- /dev/null +++ b/e2e-tests/local/defaults.env @@ -0,0 +1,53 @@ +# Local defaults for e2e testing with rhdh-local +# These values are substituted into CI configs when running locally. + +# Base URLs +export RHDH_BASE_URL="http://localhost:7007" +export RHDH_BASE_URL_HTTP="http://localhost:7007" +export DH_TARGET_URL="localhost" + +# Backend +export BACKEND_SECRET="test-secret-for-local-e2e-testing" + +# Redis (disabled for local testing - uses in-memory) +export REDIS_USERNAME="" +export REDIS_PASSWORD="" + +# GitHub App Integration (disabled for local testing) +export GITHUB_APP_APP_ID="" +export GITHUB_APP_CLIENT_ID="" +export GITHUB_APP_CLIENT_SECRET="" +export GITHUB_APP_WEBHOOK_URL="" +export GITHUB_APP_WEBHOOK_SECRET="" +export GITHUB_APP_PRIVATE_KEY="" + +# GitHub Janus Test App (disabled for local testing) +export GITHUB_APP_JANUS_TEST_APP_ID="" +export GITHUB_APP_JANUS_TEST_CLIENT_ID="" +export GITHUB_APP_JANUS_TEST_CLIENT_SECRET="" +export GITHUB_APP_JANUS_TEST_PRIVATE_KEY="" + +# GitHub OAuth (disabled for local testing) +export GITHUB_OAUTH_APP_ID="" +export GITHUB_OAUTH_APP_SECRET="" +export GITHUB_URL="https://github.com" +export GITHUB_ORG="" +export GITHUB_ORG_2="" + +# GitLab (disabled for local testing) +export GITLAB_TOKEN="" + +# Google Auth (disabled for local testing) +export GOOGLE_CLIENT_ID="" +export GOOGLE_CLIENT_SECRET="" + +# Keycloak/OIDC (disabled for local testing) +export KEYCLOAK_AUTH_BASE_URL="" +export KEYCLOAK_AUTH_REALM="" +export KEYCLOAK_AUTH_LOGIN_REALM="" +export KEYCLOAK_AUTH_CLIENTID="" +export KEYCLOAK_AUTH_CLIENT_SECRET="" + +# Azure Container Registry (disabled for local testing) +export ACR_SECRET="" + diff --git a/scripts/run-e2e-tests.sh b/scripts/run-e2e-tests.sh new file mode 100644 index 0000000000..a6506cf750 --- /dev/null +++ b/scripts/run-e2e-tests.sh @@ -0,0 +1,297 @@ +#!/bin/bash +# +# Run e2e tests locally using rhdh-local +# +# This script: +# 1. Clones fresh rhdh-local if not present +# 2. Processes CI configs with local environment variables +# 3. Starts RHDH via podman/docker compose +# 4. Runs Playwright e2e tests +# +# Usage: +# ./scripts/run-e2e-tests.sh [options] +# +# Options: +# --profile Test profile: basic (default), rbac +# --project Playwright project (default: showcase-sanity-plugins) +# --build Build local RHDH image from docker/Dockerfile +# --image Use specific registry image +# --fresh Force re-clone rhdh-local +# --skip-setup Skip rhdh-local setup (use existing running instance) +# --skip-teardown Leave RHDH running after tests +# --help Show this help message + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +BLUE='\033[0;34m' +YELLOW='\033[0;33m' +NC='\033[0m' # No Color + +log_info() { echo -e "${BLUE}[INFO]${NC} $1"; } +log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; } +log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } +log_error() { echo -e "${RED}[ERROR]${NC} $1"; } + +# Script directory and paths +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +RHDH_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +RHDH_LOCAL_PATH="$RHDH_ROOT/rhdh-local" +RHDH_LOCAL_REPO="https://github.com/redhat-developer/rhdh-local.git" +CI_CONFIG_PATH="$RHDH_ROOT/.ibm/pipelines/resources/config_map" + +# Default values +RHDH_IMAGE="${RHDH_IMAGE:-quay.io/rhdh-community/rhdh:next}" +TEST_PROFILE="basic" +TEST_PROJECT="showcase-sanity-plugins" +BUILD_LOCAL=false +FRESH_CLONE=false +SKIP_SETUP=false +SKIP_TEARDOWN=false + +# Parse arguments +show_help() { + head -30 "$0" | grep -E "^#" | sed 's/^# \?//' + exit 0 +} + +while [[ $# -gt 0 ]]; do + case $1 in + --profile) + TEST_PROFILE="$2" + shift 2 + ;; + --project) + TEST_PROJECT="$2" + shift 2 + ;; + --build) + BUILD_LOCAL=true + shift + ;; + --image) + RHDH_IMAGE="$2" + shift 2 + ;; + --fresh) + FRESH_CLONE=true + shift + ;; + --skip-setup) + SKIP_SETUP=true + shift + ;; + --skip-teardown) + SKIP_TEARDOWN=true + shift + ;; + --help | -h) show_help ;; + *) + log_warn "Unknown option: $1" + shift + ;; + esac +done + +# Detect container runtime +detect_container_runtime() { + if command -v podman &>/dev/null; then + echo "podman" + elif command -v docker &>/dev/null; then + echo "docker" + else + log_error "Neither podman nor docker found. Please install one." + exit 1 + fi +} + +CONTAINER_RUNTIME=$(detect_container_runtime) +log_info "Using container runtime: $CONTAINER_RUNTIME" + +# Print configuration +log_info "============================================" +log_info "RHDH E2E Test Runner" +log_info "============================================" +log_info "Image: $RHDH_IMAGE" +log_info "Profile: $TEST_PROFILE" +log_info "Project: $TEST_PROJECT" +log_info "E2E Path: $RHDH_ROOT/e2e-tests" +log_info "============================================" + +# Clone rhdh-local if needed +clone_rhdh_local() { + if [ "$FRESH_CLONE" = true ] && [ -d "$RHDH_LOCAL_PATH" ]; then + log_info "Removing existing rhdh-local for fresh clone..." + rm -rf "$RHDH_LOCAL_PATH" + fi + + if [ ! -d "$RHDH_LOCAL_PATH" ]; then + log_info "Cloning rhdh-local..." + git clone --depth 1 "$RHDH_LOCAL_REPO" "$RHDH_LOCAL_PATH" + else + log_info "Using existing rhdh-local at: $RHDH_LOCAL_PATH" + fi +} + +# Setup configs for local testing +setup_configs() { + log_info "Setting up local test configs..." + + # Local config files are in e2e-tests/local/ + LOCAL_CONFIG_DIR="$RHDH_ROOT/e2e-tests/local" + + # Select config file based on profile + # We use minimal local configs that don't require external services + case $TEST_PROFILE in + basic) + LOCAL_CONFIG_FILE="$LOCAL_CONFIG_DIR/config-basic.yaml" + ;; + rbac) + LOCAL_CONFIG_FILE="$LOCAL_CONFIG_DIR/config-rbac.yaml" + ;; + *) + log_error "Unknown profile: $TEST_PROFILE" + exit 1 + ;; + esac + + if [ ! -f "$LOCAL_CONFIG_FILE" ]; then + log_error "Config file not found: $LOCAL_CONFIG_FILE" + exit 1 + fi + + log_info "Using config: $LOCAL_CONFIG_FILE" + + # Create directories if needed + mkdir -p "$RHDH_LOCAL_PATH/configs/app-config" + mkdir -p "$RHDH_LOCAL_PATH/configs/rbac" + + # Copy local config to rhdh-local + cp "$LOCAL_CONFIG_FILE" "$RHDH_LOCAL_PATH/configs/app-config/app-config.local.yaml" + log_info "Created app-config.local.yaml" + + # Copy RBAC policy if using RBAC profile + if [ "$TEST_PROFILE" = "rbac" ]; then + if [ -f "$CI_CONFIG_PATH/rbac-policy.csv" ]; then + cp "$CI_CONFIG_PATH/rbac-policy.csv" "$RHDH_LOCAL_PATH/configs/rbac/rbac-policy.csv" + log_info "Copied rbac-policy.csv" + fi + fi +} + +# Build local RHDH image +build_local_image() { + if [ "$BUILD_LOCAL" = true ]; then + log_info "Building local RHDH image..." + $CONTAINER_RUNTIME build -f "$RHDH_ROOT/docker/Dockerfile" "$RHDH_ROOT" -t rhdh:local + RHDH_IMAGE="localhost/rhdh:local" + log_success "Built local image: $RHDH_IMAGE" + fi +} + +# Start rhdh-local +start_rhdh_local() { + log_info "Starting rhdh-local with image: $RHDH_IMAGE" + + cd "$RHDH_LOCAL_PATH" + RHDH_IMAGE="$RHDH_IMAGE" $CONTAINER_RUNTIME compose up -d + + # Wait for RHDH to be ready + log_info "Waiting for RHDH to be ready..." + HEALTH_URL="http://localhost:7007" + MAX_WAIT=180 + ELAPSED=0 + + # Give the container a moment to start the app + sleep 10 + + while [ $ELAPSED -lt $MAX_WAIT ]; do + # Try curl with explicit timeout and handle Windows/Git Bash quirks + HTTP_CODE=$(curl --connect-timeout 5 --max-time 10 -s -o /dev/null -w "%{http_code}" "$HEALTH_URL" 2>/dev/null) + CURL_EXIT=$? + + # curl exit code 0 means success, check HTTP code + if [ $CURL_EXIT -eq 0 ]; then + if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "401" ]; then + log_success "RHDH is ready! (HTTP $HTTP_CODE after ${ELAPSED}s)" + return 0 + fi + fi + + sleep 5 + ELAPSED=$((ELAPSED + 5)) + log_info "Waiting for RHDH... (${ELAPSED}s elapsed, HTTP: $HTTP_CODE, curl exit: $CURL_EXIT)" + done + + log_error "RHDH failed to start within ${MAX_WAIT}s" + log_info "Container logs:" + $CONTAINER_RUNTIME logs rhdh 2>&1 | tail -50 + exit 1 +} + +# Stop rhdh-local +stop_rhdh_local() { + log_info "Stopping rhdh-local..." + cd "$RHDH_LOCAL_PATH" + $CONTAINER_RUNTIME compose down --volumes 2>/dev/null || true +} + +# Run e2e tests +run_tests() { + log_info "Running e2e tests..." + log_info "Test project: $TEST_PROJECT" + + cd "$RHDH_ROOT/e2e-tests" + + # Install dependencies if needed + if [ ! -d "node_modules" ]; then + log_info "Installing e2e-tests dependencies..." + yarn install + fi + + # Run Playwright tests in CI mode (headless, no interactive prompts) + log_info "Executing: CI=true BASE_URL=http://localhost:7007 yarn playwright test --project=$TEST_PROJECT" + CI=true BASE_URL="http://localhost:7007" yarn playwright test --project="$TEST_PROJECT" + + TEST_EXIT_CODE=$? + return $TEST_EXIT_CODE +} + +# Cleanup on exit +cleanup() { + if [ "$SKIP_TEARDOWN" = false ]; then + stop_rhdh_local + else + log_info "Skipping teardown. RHDH is still running at http://localhost:7007" + fi +} + +# Main execution +main() { + # Setup trap for cleanup + trap cleanup EXIT + + if [ "$SKIP_SETUP" = false ]; then + clone_rhdh_local + setup_configs + build_local_image + start_rhdh_local + else + log_info "Skipping setup, using existing RHDH instance" + fi + + run_tests + TEST_EXIT_CODE=$? + + if [ $TEST_EXIT_CODE -eq 0 ]; then + log_success "All tests passed!" + else + log_error "Some tests failed (exit code: $TEST_EXIT_CODE)" + fi + + exit $TEST_EXIT_CODE +} + +main