E2E Tests #27
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Copyright (C) Siemens AG, 2026. Part of the SW360 Frontend Project. | |
| # | |
| # This program and the accompanying materials are made | |
| # available under the terms of the Eclipse Public License 2.0 | |
| # which is available at https://www.eclipse.org/legal/epl-2.0/ | |
| # | |
| # SPDX-License-Identifier: EPL-2.0 | |
| # License-Filename: LICENSE | |
| name: E2E Tests | |
| on: | |
| pull_request: | |
| paths: | |
| - 'src/**' | |
| - 'messages/**' | |
| - 'tests/**' | |
| schedule: | |
| - cron: '0 2 * * *' # Daily at 2 AM UTC (runs on main) | |
| workflow_dispatch: | |
| inputs: | |
| module: | |
| description: 'Test module to run (empty = all). Access control tests always run.' | |
| required: false | |
| type: choice | |
| options: | |
| - '' | |
| - projects | |
| - components | |
| - licenses | |
| - packages | |
| - vulnerabilities | |
| - search | |
| - home | |
| - admin | |
| - ecc | |
| - preferences | |
| - requests | |
| permissions: | |
| contents: read | |
| jobs: | |
| e2e: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| steps: | |
| - name: Harden Runner | |
| uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0 | |
| with: | |
| egress-policy: audit | |
| - name: Checkout source code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 0 | |
| - name: Determine test scope | |
| id: scope | |
| run: | | |
| # workflow_dispatch: use selected module | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ -n "${{ inputs.module }}" ]; then | |
| echo "modules=${{ inputs.module }}" >> $GITHUB_OUTPUT | |
| echo "📋 Running module: ${{ inputs.module }} (from workflow_dispatch)" | |
| exit 0 | |
| fi | |
| # schedule (cron on main): run everything | |
| if [ "${{ github.event_name }}" = "schedule" ]; then | |
| echo "modules=" >> $GITHUB_OUTPUT | |
| echo "📋 Running all modules (scheduled nightly on main)" | |
| exit 0 | |
| fi | |
| # pull_request: detect affected modules from changed files | |
| echo "📋 Detecting affected modules from PR changed files..." | |
| CHANGED_FILES=$(git diff --name-only ${{ github.event.pull_request.base.sha }}...${{ github.sha }} 2>/dev/null || git diff --name-only HEAD~1) | |
| MODULES="" | |
| add_module() { | |
| if ! echo "$MODULES" | grep -qw "$1"; then | |
| MODULES="${MODULES:+$MODULES }$1" | |
| fi | |
| } | |
| # Check if shared/core code changed → run all | |
| SHARED_PATTERN="^(src/components/sw360/|src/utils/|src/services/|src/hooks/|src/object-types/|src/contexts/|src/styles/|src/navigation\.|src/proxy\.|next\.config|tests/config\.ts|tests/e2e/auth\.setup\.ts|playwright\.config)" | |
| if echo "$CHANGED_FILES" | grep -qE "$SHARED_PATTERN"; then | |
| echo " ⚡ Shared code changed — running all modules" | |
| echo "modules=" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| # Map source paths to test modules | |
| for file in $CHANGED_FILES; do | |
| case "$file" in | |
| src/app/*/projects/*|src/components/ProjectAddSummary/*|tests/e2e/projects/*) add_module "projects" ;; | |
| src/app/*/components/*|tests/e2e/components/*) add_module "components" ;; | |
| src/app/*/licenses/*|tests/e2e/licenses/*) add_module "licenses" ;; | |
| src/app/*/packages/*|tests/e2e/packages/*) add_module "packages" ;; | |
| src/app/*/vulnerabilities/*|src/components/ComponentVulnerabilities/*|tests/e2e/vulnerabilities/*) add_module "vulnerabilities" ;; | |
| src/app/*/search/*|tests/e2e/search/*) add_module "search" ;; | |
| src/app/*/home/*|tests/e2e/home/*) add_module "home" ;; | |
| src/app/*/admin/*|tests/e2e/admin/*) add_module "admin" ;; | |
| src/app/*/ecc/*|tests/e2e/ecc/*) add_module "ecc" ;; | |
| src/app/*/preferences/*|tests/e2e/preferences/*) add_module "preferences" ;; | |
| src/app/*/requests/*|tests/e2e/requests/*) add_module "requests" ;; | |
| esac | |
| done | |
| if [ -z "$MODULES" ]; then | |
| echo " ℹ️ No module-specific changes detected — running all" | |
| else | |
| echo " 🎯 Affected modules: $MODULES" | |
| fi | |
| echo "modules=$MODULES" >> $GITHUB_OUTPUT | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10 | |
| with: | |
| version: 11 | |
| - name: Setup Node | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | |
| with: | |
| node-version: '24' | |
| cache: 'pnpm' | |
| - name: Install dependencies | |
| run: pnpm install | |
| - name: Install Playwright browsers | |
| run: npx playwright install chromium --with-deps | |
| - name: Build frontend | |
| env: | |
| NEXT_PUBLIC_SW360_API_URL: http://localhost:8080 | |
| NEXT_PUBLIC_SW360_AUTH_PROVIDER: sw360basic | |
| NEXTAUTH_URL: http://localhost:3000 | |
| AUTH_SECRET: mysecret | |
| run: pnpm build | |
| - name: Start backend services (SW360 + CouchDB) | |
| run: | | |
| # Start only backend services (not sw360-frontend — we use our built version) | |
| docker compose -f docker-compose.yml up -d sw360 couchdb couchdb-nouveau | |
| echo "⏳ Waiting for CouchDB..." | |
| timeout 60 bash -c 'until curl -fsS http://localhost:5984/_up >/dev/null 2>&1; do sleep 3; done' | |
| echo "✅ CouchDB is ready" | |
| echo "⏳ Waiting for SW360 backend (this may take 4-5 minutes)..." | |
| timeout 600 bash -c 'until curl -fsS http://localhost:8080/resource/api/health 2>/dev/null | grep -iq "status.*up"; do sleep 5; done' | |
| echo "✅ SW360 backend is healthy" | |
| # Verify the REST API actually accepts authenticated requests (DB init may lag behind health) | |
| SW360_TOKEN=$(echo -n "setup@sw360.org:sw360fossie" | base64) | |
| echo "⏳ Verifying REST API accepts authenticated requests..." | |
| timeout 120 bash -c " | |
| until curl -sS -o /dev/null -w '%{http_code}' \ | |
| -H 'Authorization: Basic ${SW360_TOKEN}' \ | |
| http://localhost:8080/resource/api/users/setup@sw360.org 2>/dev/null | grep -q '200'; do | |
| echo ' ...backend not ready yet, retrying in 5s' | |
| sleep 5 | |
| done | |
| " | |
| echo "✅ Backend REST API is responding to authenticated requests" | |
| # Verify write operations work (POST a test project, then delete it) | |
| echo "⏳ Verifying write operations..." | |
| HEALTHCHECK_NAME="e2ehealthcheck$(date +%s)" | |
| timeout 60 bash -c " | |
| until RESPONSE=\$(curl -sS -w '\\n%{http_code}' \ | |
| -X POST http://localhost:8080/resource/api/projects \ | |
| -H 'Authorization: Basic ${SW360_TOKEN}' \ | |
| -H 'Content-Type: application/json' \ | |
| -d '{\"name\":\"'\"${HEALTHCHECK_NAME}\"'\",\"version\":\"1.0\"}' 2>/dev/null) && \ | |
| echo \"\$RESPONSE\" | tail -1 | grep -q '201'; do | |
| echo ' ...write operations not ready yet, retrying in 5s' | |
| sleep 5 | |
| done | |
| " | |
| PROJECT_URL=$(curl -sS \ | |
| -H "Authorization: Basic ${SW360_TOKEN}" \ | |
| "http://localhost:8080/resource/api/projects?name=${HEALTHCHECK_NAME}" 2>/dev/null \ | |
| | grep -o '"href":"[^"]*"' | head -1 | cut -d'"' -f4) | |
| echo "✅ Backend write operations verified" | |
| # Clean up the test project | |
| if [ -n "$PROJECT_URL" ]; then | |
| curl -sS -X DELETE "$PROJECT_URL" -H "Authorization: Basic ${SW360_TOKEN}" >/dev/null 2>&1 || true | |
| fi | |
| echo "✅ All backend checks passed — ready for E2E tests" | |
| - name: Start frontend (production mode) | |
| run: | | |
| pnpm start & | |
| echo "⏳ Waiting for Next.js..." | |
| timeout 30 bash -c 'until curl -fsS http://localhost:3000 >/dev/null 2>&1; do sleep 2; done' | |
| echo "✅ Frontend is ready" | |
| env: | |
| NEXT_PUBLIC_SW360_API_URL: http://localhost:8080 | |
| NEXT_PUBLIC_SW360_AUTH_PROVIDER: sw360basic | |
| NEXTAUTH_URL: http://localhost:3000 | |
| NEXTAUTH_SECRET: mysecret | |
| - name: Run E2E tests | |
| run: | | |
| MODULES="${{ steps.scope.outputs.modules }}" | |
| if [ -n "$MODULES" ]; then | |
| # Build test paths from space-separated module list | |
| TEST_PATHS="" | |
| for mod in $MODULES; do | |
| TEST_PATHS="$TEST_PATHS tests/e2e/$mod/" | |
| done | |
| echo "🧪 Running tests for: $MODULES" | |
| npx playwright test $TEST_PATHS --project=chromium | |
| else | |
| echo "🧪 Running all E2E tests (standard modules)" | |
| npx playwright test \ | |
| tests/e2e/projects/ \ | |
| tests/e2e/components/ \ | |
| tests/e2e/licenses/ \ | |
| tests/e2e/packages/ \ | |
| tests/e2e/vulnerabilities/ \ | |
| tests/e2e/search/ \ | |
| tests/e2e/home/ \ | |
| tests/e2e/admin/ \ | |
| tests/e2e/ecc/ \ | |
| tests/e2e/preferences/ \ | |
| tests/e2e/requests/ \ | |
| --project=chromium | |
| fi | |
| # Always run accessControl tests with role-based projects | |
| echo "🔐 Running accessControl tests with role-based projects" | |
| npx playwright test tests/e2e/accessControl/ \ | |
| --project=chromium-user \ | |
| --project=chromium-viewer \ | |
| --project=chromium-clearing | |
| env: | |
| CI: true | |
| ENABLE_ROLE_TESTS: 'true' | |
| SKIP_ADMIN_TESTS: 'true' | |
| SW360_BASE_URL: http://localhost:3000 | |
| SW360_API_URL: http://localhost:8080 | |
| SW360_TEST_ADMIN_EMAIL: setup@sw360.org | |
| SW360_TEST_ADMIN_PASSWORD: sw360fossie | |
| - name: Upload Playwright report | |
| if: always() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: playwright-report | |
| path: tests/reports/ | |
| retention-days: 14 | |
| - name: Upload test screenshots & traces (on failure) | |
| if: failure() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: test-results | |
| path: tests/results/ | |
| retention-days: 7 | |
| - name: Report viewing instructions | |
| if: always() | |
| run: | | |
| echo "📊 To view the HTML report with screenshots:" | |
| echo " 1. Download the 'playwright-report' artifact" | |
| echo " 2. Extract the zip" | |
| echo " 3. Run: npx playwright show-report <extracted-folder>" | |
| echo "" | |
| echo "📸 Failed test screenshots can be viewed directly in the 'test-results' artifact" | |
| - name: Print backend logs (on failure) | |
| if: failure() | |
| run: | | |
| echo "=== Running containers ===" | |
| docker compose -f docker-compose.yml ps -a || true | |
| echo "" | |
| echo "=== Nouveau Diagnostic ===" | |
| echo "Checking Nouveau container internals..." | |
| docker compose -f docker-compose.yml exec -T couchdb-nouveau ls -la /opt/nouveau/data 2>&1 || echo "Could not list /opt/nouveau/data" | |
| docker compose -f docker-compose.yml exec -T couchdb-nouveau cat /opt/nouveau/etc/nouveau.yaml 2>&1 || echo "Could not read nouveau.yaml" | |
| echo "" | |
| echo "=== SW360 Backend Logs (last 500 lines) ===" | |
| docker compose -f docker-compose.yml logs --tail 500 sw360 2>&1 || echo "Failed to retrieve sw360 logs" | |
| echo "" | |
| echo "=== CouchDB Nouveau Logs (FULL) ===" | |
| docker compose -f docker-compose.yml logs couchdb-nouveau 2>&1 || echo "Failed to retrieve couchdb-nouveau logs" | |
| echo "" | |
| echo "=== CouchDB Logs (last 300 lines) ===" | |
| docker compose -f docker-compose.yml logs --tail 300 couchdb 2>&1 || echo "Failed to retrieve couchdb logs" | |
| - name: Stop backend services | |
| if: always() | |
| run: docker compose -f docker-compose.yml down -v |