Merge pull request #749 from Sitecore/bcn/bf/filter-use-shared-select… #801
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
| name: Playwright Tests | |
| on: | |
| # Run on push (commits) to these branches | |
| push: | |
| branches: | |
| - main | |
| - dev | |
| - staging | |
| - QA-Automation | |
| - QA-Automation_NextFW | |
| # Run when PR is opened, updated, or reopened targeting these branches | |
| pull_request: | |
| branches: | |
| - main | |
| - dev | |
| - staging | |
| - QA-Automation | |
| - QA-Automation_NextFW | |
| types: [ opened, synchronize, reopened ] | |
| # Allow manual trigger from GitHub Actions UI | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| actions: read | |
| jobs: | |
| # Job to check PR branch build when PR is opened to dev, staging, or main | |
| check-pr-build: | |
| if: github.event_name == 'pull_request' && (github.base_ref == 'dev' || github.base_ref == 'main' || github.base_ref == 'staging') | |
| uses: ./.github/workflows/pr-build-check.yml | |
| with: | |
| pr-branch: ${{ github.head_ref }} | |
| base-branch: ${{ github.base_ref }} | |
| pr-number: ${{ github.event.pull_request.number }} | |
| pr-title: ${{ github.event.pull_request.title }} | |
| author-name: ${{ github.event.pull_request.user.name || github.event.pull_request.user.login }} | |
| author-username: ${{ github.event.pull_request.user.login }} | |
| secrets: inherit | |
| test: | |
| timeout-minutes: 60 | |
| runs-on: ubuntu-latest | |
| steps: | |
| # Step 0: Checkout repository (needed for composite actions) | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| # Step 0.5: Determine registry URL based on PR branch | |
| - name: Determine registry URL | |
| id: registry-url | |
| run: | | |
| # Default to production URL | |
| REGISTRY_URL="https://blok.sitecore.com" | |
| REGISTRY_TYPE="production" | |
| if [ "${{ github.event_name }}" == "pull_request" ]; then | |
| BASE_REF="${{ github.base_ref }}" | |
| HEAD_REF="${{ github.head_ref }}" | |
| # If PR is targeting main, use production URL | |
| if [ "$BASE_REF" == "main" ]; then | |
| REGISTRY_URL="https://blok.sitecore.com" | |
| REGISTRY_TYPE="production" | |
| # If PR is targeting staging or dev, use staging URL | |
| elif [ "$BASE_REF" == "staging" ] || [ "$BASE_REF" == "dev" ]; then | |
| REGISTRY_URL="https://blok-staging.vercel.app" | |
| REGISTRY_TYPE="staging" | |
| fi | |
| else | |
| # For direct pushes, check the branch | |
| BRANCH="${{ github.ref_name }}" | |
| if [ "$BRANCH" == "main" ]; then | |
| REGISTRY_URL="https://blok.sitecore.com" | |
| REGISTRY_TYPE="production" | |
| else | |
| REGISTRY_URL="https://blok-staging.vercel.app" | |
| REGISTRY_TYPE="staging" | |
| fi | |
| fi | |
| echo "url=${REGISTRY_URL}" >> $GITHUB_OUTPUT | |
| echo "type=${REGISTRY_TYPE}" >> $GITHUB_OUTPUT | |
| echo "Using ${REGISTRY_TYPE} registry URL: ${REGISTRY_URL}" | |
| # Step 1: Checkout QA-Automation_NextFW branch (Next.js app) | |
| - name: Checkout Next.js app (QA-Automation_NextFW) | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: QA-Automation_NextFW | |
| path: app | |
| # Step 1.5: Update components-config.json in app directory | |
| - name: Update components-config.json URLs | |
| run: | | |
| REGISTRY_URL="${{ steps.registry-url.outputs.url }}" | |
| CONFIG_FILE="app/components-config.json" | |
| if [ ! -f "$CONFIG_FILE" ]; then | |
| echo "components-config.json not found, skipping update" | |
| exit 0 | |
| fi | |
| echo "Updating components-config.json URLs to use: ${REGISTRY_URL}" | |
| # Use Node.js to update the JSON file properly | |
| node -e " | |
| const fs = require('fs'); | |
| const configPath = '$CONFIG_FILE'; | |
| const newUrl = '${REGISTRY_URL}'; | |
| const oldUrl = 'https://blok.sitecore.com'; | |
| try { | |
| const config = JSON.parse(fs.readFileSync(configPath, 'utf8')); | |
| let updated = false; | |
| // Update bulkInstall.url if it exists | |
| if (config.bulkInstall && config.bulkInstall.url) { | |
| if (config.bulkInstall.url.includes(oldUrl)) { | |
| config.bulkInstall.url = config.bulkInstall.url.replace(oldUrl, newUrl); | |
| updated = true; | |
| console.log('✓ Updated bulkInstall.url'); | |
| } | |
| } | |
| // Update additionalComponents array URLs | |
| if (config.additionalComponents && Array.isArray(config.additionalComponents)) { | |
| config.additionalComponents = config.additionalComponents.map(url => { | |
| if (typeof url === 'string' && url.includes(oldUrl)) { | |
| updated = true; | |
| return url.replace(oldUrl, newUrl); | |
| } | |
| return url; | |
| }); | |
| if (updated) { | |
| console.log('✓ Updated additionalComponents URLs'); | |
| } | |
| } | |
| if (updated) { | |
| fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf8'); | |
| console.log('components-config.json updated successfully'); | |
| } else { | |
| console.log('ℹNo URLs to update in components-config.json'); | |
| } | |
| } catch (error) { | |
| console.error('Error updating components-config.json:', error.message); | |
| process.exit(1); | |
| } | |
| " | |
| # Step 2: Verify Docker files exist in app directory (from QA-Automation_NextFW branch) | |
| - name: Verify Docker files in app directory | |
| run: | | |
| echo "=== Verifying Docker files in app directory ===" | |
| echo "The QA-Automation_NextFW branch should contain all required files" | |
| echo "" | |
| # Check required files | |
| REQUIRED_FILES=("Dockerfile" "install-components.sh" "components-config.json" "components.json" "package.json" "tsconfig.json") | |
| MISSING_FILES=() | |
| for file in "${REQUIRED_FILES[@]}"; do | |
| if [ -f "app/$file" ]; then | |
| echo "✅ $file found in app directory" | |
| else | |
| MISSING_FILES+=("$file") | |
| echo "❌ $file NOT found in app directory" | |
| fi | |
| done | |
| # Check .dockerignore (optional but recommended) | |
| if [ -f "app/.dockerignore" ]; then | |
| echo "✅ .dockerignore found in app directory" | |
| else | |
| echo "⚠️ .dockerignore not found - creating basic one..." | |
| echo "node_modules" > app/.dockerignore | |
| echo ".next" >> app/.dockerignore | |
| echo ".git" >> app/.dockerignore | |
| echo "components/ui/" >> app/.dockerignore | |
| echo "✅ Created basic .dockerignore" | |
| fi | |
| # Exit if required files are missing | |
| if [ ${#MISSING_FILES[@]} -gt 0 ]; then | |
| echo "" | |
| echo "❌ Missing required files in QA-Automation_NextFW branch:" | |
| printf ' - %s\n' "${MISSING_FILES[@]}" | |
| echo "" | |
| echo "Please commit these files to the QA-Automation_NextFW branch:" | |
| echo " - Dockerfile" | |
| echo " - install-components.sh" | |
| echo " - components-config.json (if not already present)" | |
| exit 1 | |
| fi | |
| echo "" | |
| echo "✅ All required files found in app directory" | |
| # Step 3: Verify Docker is available | |
| - name: Verify Docker is available | |
| run: | | |
| echo "=== Verifying Docker is available ===" | |
| # Verify Docker is available | |
| if ! command -v docker &> /dev/null; then | |
| echo "❌ Docker is not available!" | |
| exit 1 | |
| else | |
| echo "✅ Docker is available" | |
| docker --version | |
| fi | |
| echo "✅ Docker setup verified" | |
| # Step 4: Build Docker image | |
| - name: Build Docker image | |
| working-directory: ./app | |
| run: | | |
| echo "=== Building Docker image ===" | |
| echo "This will install components and prepare the app in an isolated environment" | |
| echo "" | |
| echo "Build context: $(pwd)" | |
| echo "Files in build context:" | |
| ls -la | head -20 | |
| echo "" | |
| # Build with progress output | |
| docker build -t nextjs-app:dev --target dev . 2>&1 | tee docker-build.log | |
| # Check build success | |
| if [ ${PIPESTATUS[0]} -eq 0 ]; then | |
| echo "✅ Docker image built successfully" | |
| echo "" | |
| echo "Image details:" | |
| docker images nextjs-app:dev --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}" | |
| else | |
| echo "❌ Docker build failed!" | |
| echo "Build log (last 50 lines):" | |
| tail -50 docker-build.log || true | |
| exit 1 | |
| fi | |
| # Step 5: Run Docker container | |
| - name: Run Docker container | |
| run: | | |
| echo "=== Starting Docker container ===" | |
| # Remove any existing container with the same name | |
| docker rm -f nextjs-dev 2>/dev/null || true | |
| # Start container | |
| CONTAINER_ID=$(docker run -d \ | |
| --name nextjs-dev \ | |
| -p 3000:3000 \ | |
| nextjs-app:dev) | |
| if [ -n "$CONTAINER_ID" ]; then | |
| echo "✅ Docker container started" | |
| echo "Container ID: $CONTAINER_ID" | |
| echo "" | |
| echo "Container status:" | |
| docker ps -f name=nextjs-dev --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | |
| else | |
| echo "❌ Failed to start Docker container!" | |
| exit 1 | |
| fi | |
| # Step 6: Wait for dev server to be ready | |
| - name: Wait for dev server | |
| run: | | |
| echo "Waiting for Next.js dev server to start in Docker container..." | |
| timeout=120 | |
| elapsed=0 | |
| while [ $elapsed -lt $timeout ]; do | |
| if curl -f -s http://localhost:3000 > /dev/null 2>&1; then | |
| echo "✅ Dev server is ready and responding!" | |
| curl -s http://localhost:3000 | head -n 5 | |
| exit 0 | |
| fi | |
| echo "⏳ Waiting for dev server... ($elapsed/$timeout seconds)" | |
| sleep 3 | |
| elapsed=$((elapsed + 3)) | |
| done | |
| echo "❌ Dev server failed to start within $timeout seconds" | |
| echo "Docker container logs:" | |
| docker logs nextjs-dev || true | |
| exit 1 | |
| # Step 7: Setup cloudflared tunnel for dev server | |
| - name: Expose dev server via tunnel | |
| id: tunnel-dev | |
| run: | | |
| echo "🌐 Setting up public URL tunnel for dev server..." | |
| # Verify local server is still running | |
| if ! curl -f -s http://localhost:3000 > /dev/null 2>&1; then | |
| echo "❌ Local dev server is not responding on localhost:3000" | |
| exit 1 | |
| fi | |
| echo "✅ Local dev server is running on localhost:3000" | |
| # Download and install cloudflared | |
| curl -L --output cloudflared https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 | |
| chmod +x cloudflared | |
| sudo mv cloudflared /usr/local/bin/ || mv cloudflared /usr/local/bin/ | |
| # Start cloudflared tunnel in background | |
| echo "Starting cloudflared tunnel..." | |
| /usr/local/bin/cloudflared tunnel --url http://localhost:3000 > cloudflared.log 2>&1 & | |
| CLOUDFLARED_PID=$! | |
| echo $CLOUDFLARED_PID > cloudflared.pid | |
| echo "Cloudflared started with PID: $CLOUDFLARED_PID" | |
| # Wait for cloudflared to start and extract the URL | |
| echo "Waiting for tunnel to be ready..." | |
| sleep 10 | |
| # Extract the public URL from cloudflared output | |
| PUBLIC_URL="" | |
| for i in {1..50}; do | |
| # Check if cloudflared is still running | |
| if ! kill -0 $CLOUDFLARED_PID 2>/dev/null; then | |
| echo "⚠️ Cloudflared process died!" | |
| echo "Cloudflared log:" | |
| cat cloudflared.log || true | |
| break | |
| fi | |
| if [ -f cloudflared.log ]; then | |
| # Try multiple URL patterns | |
| PUBLIC_URL=$(grep -oE 'https://[a-zA-Z0-9-]+\.trycloudflare\.com' cloudflared.log 2>/dev/null | head -1) | |
| # Alternative pattern matching | |
| if [ -z "$PUBLIC_URL" ]; then | |
| PUBLIC_URL=$(grep -oE 'https://[^[:space:]]+\.trycloudflare\.com' cloudflared.log 2>/dev/null | head -1) | |
| fi | |
| # Check for "https://" anywhere in the log | |
| if [ -z "$PUBLIC_URL" ] && grep -q "https://" cloudflared.log; then | |
| PUBLIC_URL=$(grep "https://" cloudflared.log | grep -oE 'https://[^[:space:]]+' | head -1) | |
| fi | |
| if [ -n "$PUBLIC_URL" ]; then | |
| echo "Found URL: $PUBLIC_URL" | |
| # Verify the tunnel is actually working by testing the public URL | |
| echo "Verifying tunnel is working (attempt $i/50)..." | |
| sleep 3 | |
| # Test the URL multiple times to ensure it's stable | |
| SUCCESS_COUNT=0 | |
| for test_attempt in {1..3}; do | |
| if curl -f -s --max-time 15 "$PUBLIC_URL" > /dev/null 2>&1; then | |
| SUCCESS_COUNT=$((SUCCESS_COUNT + 1)) | |
| fi | |
| sleep 1 | |
| done | |
| if [ $SUCCESS_COUNT -ge 2 ]; then | |
| echo "✅ Tunnel is working and responding! ($SUCCESS_COUNT/3 tests passed)" | |
| break | |
| else | |
| echo "⏳ Tunnel URL found but not responding yet (only $SUCCESS_COUNT/3 tests passed), waiting..." | |
| sleep 2 | |
| fi | |
| fi | |
| fi | |
| sleep 1 | |
| done | |
| if [ -n "$PUBLIC_URL" ]; then | |
| # Final verification | |
| echo "Performing final verification..." | |
| VERIFY_COUNT=0 | |
| for verify_attempt in {1..5}; do | |
| if curl -f -s --max-time 15 "$PUBLIC_URL" > /dev/null 2>&1; then | |
| VERIFY_COUNT=$((VERIFY_COUNT + 1)) | |
| fi | |
| sleep 1 | |
| done | |
| if [ $VERIFY_COUNT -ge 3 ]; then | |
| echo "✅ Public URL is working: $PUBLIC_URL" | |
| echo "✅ Verified $VERIFY_COUNT/5 times successfully" | |
| echo "url=$PUBLIC_URL" >> $GITHUB_OUTPUT | |
| echo "::notice title=🌐 Dev Server URL::Your dev server is accessible at: $PUBLIC_URL" | |
| else | |
| echo "⚠️ URL extracted but tunnel verification failed ($VERIFY_COUNT/5 tests passed)" | |
| echo "URL: $PUBLIC_URL" | |
| echo "url=$PUBLIC_URL" >> $GITHUB_OUTPUT | |
| echo "::warning title=🌐 Tunnel URL (Verification Failed)::URL: $PUBLIC_URL - Only $VERIFY_COUNT/5 verification tests passed." | |
| fi | |
| else | |
| echo "⚠️ Could not extract public URL" | |
| echo "Cloudflared log:" | |
| cat cloudflared.log || true | |
| fi | |
| # Step 8: Checkout QA-Automation branch (tests) | |
| - name: Checkout tests (QA-Automation) | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: QA-Automation | |
| path: tests | |
| # Step 9: Run tests using composite action | |
| - name: Run Playwright Tests | |
| id: run-tests | |
| uses: ./.github/actions/run-tests | |
| with: | |
| tests-path: tests | |
| base-url: http://localhost:3000 | |
| # Step 11.6: Get commit/PR author information | |
| - name: Get author information | |
| id: author-info | |
| if: always() | |
| run: | | |
| if [ "${{ github.event_name }}" == "pull_request" ]; then | |
| # For Pull Requests | |
| AUTHOR_USERNAME="${{ github.event.pull_request.user.login }}" | |
| AUTHOR_NAME="${{ github.event.pull_request.user.name || github.event.pull_request.user.login }}" | |
| else | |
| # For direct commits/pushes | |
| AUTHOR_USERNAME="${{ github.actor }}" | |
| AUTHOR_NAME="${{ github.event.head_commit.author.name || github.actor }}" | |
| fi | |
| echo "username=$AUTHOR_USERNAME" >> $GITHUB_OUTPUT | |
| echo "name=$AUTHOR_NAME" >> $GITHUB_OUTPUT | |
| # Step 11.7: Generate workflow summary | |
| - name: Generate workflow summary | |
| if: always() | |
| run: | | |
| STATUS="${{ steps.run-tests.outputs.status }}" | |
| TOTAL="${{ steps.run-tests.outputs.total }}" | |
| PASSED="${{ steps.run-tests.outputs.passed }}" | |
| FAILED="${{ steps.run-tests.outputs.failed }}" | |
| SKIPPED="${{ steps.run-tests.outputs.skipped }}" | |
| # Set defaults if empty | |
| STATUS="${STATUS:-unknown}" | |
| TOTAL="${TOTAL:-0}" | |
| PASSED="${PASSED:-0}" | |
| FAILED="${FAILED:-0}" | |
| SKIPPED="${SKIPPED:-0}" | |
| # Determine emoji and text based on status | |
| if [ "$STATUS" == "failed" ]; then | |
| STATUS_EMOJI="❌" | |
| STATUS_TEXT="Failed" | |
| elif [ "$STATUS" == "passed" ]; then | |
| STATUS_EMOJI="✅" | |
| STATUS_TEXT="Passed" | |
| else | |
| STATUS_EMOJI="⚠️" | |
| STATUS_TEXT="Unknown" | |
| fi | |
| WORKFLOW_RUN_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" | |
| REPORT_URL="${WORKFLOW_RUN_URL}#artifacts" | |
| echo "## Playwright Test Results Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### ${STATUS_EMOJI} Test Status: ${STATUS_TEXT}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Test Metrics" >> $GITHUB_STEP_SUMMARY | |
| echo "| Metric | Count |" >> $GITHUB_STEP_SUMMARY | |
| echo "| --- | --- |" >> $GITHUB_STEP_SUMMARY | |
| echo "| **Total Tests** | ${TOTAL} |" >> $GITHUB_STEP_SUMMARY | |
| echo "| ✅ **Passed** | ${PASSED} |" >> $GITHUB_STEP_SUMMARY | |
| echo "| ❌ **Failed** | ${FAILED} |" >> $GITHUB_STEP_SUMMARY | |
| echo "| **Skipped** | ${SKIPPED} |" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Component Installation" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Registry URL**: \`${{ steps.registry-url.outputs.url }}\`" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Registry Type**: ${{ steps.registry-url.outputs.type }}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Test Details" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Repository**: \`${{ github.repository }}\`" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Branch**: \`${{ github.ref_name }}\`" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Commit**: \`${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Author**: ${{ steps.author-info.outputs.name }} (@${{ steps.author-info.outputs.username }})" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Workflow**: [${{ github.workflow }}](${WORKFLOW_RUN_URL})" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Run Number**: #${{ github.run_number }}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Test Report" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Playwright Report**: [View Report](${REPORT_URL})" >> $GITHUB_STEP_SUMMARY | |
| echo " - Download the \`playwright-report\` artifact to view the full HTML report" >> $GITHUB_STEP_SUMMARY | |
| echo " - Open \`index.html\` in a browser after downloading" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if [ "$STATUS" == "failed" ]; then | |
| echo "### ⚠️ Action Required" >> $GITHUB_STEP_SUMMARY | |
| echo "Some tests have failed. Please review the test results and fix the issues." >> $GITHUB_STEP_SUMMARY | |
| elif [ "$STATUS" == "passed" ]; then | |
| echo "### All Tests Passed" >> $GITHUB_STEP_SUMMARY | |
| echo "All Playwright tests completed successfully!" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "### Status Unknown" >> $GITHUB_STEP_SUMMARY | |
| echo "Unable to parse test results. Please check the workflow logs for details." >> $GITHUB_STEP_SUMMARY | |
| fi | |
| # Step 11.8: Output test status notices | |
| - name: Output test status notices | |
| if: always() | |
| run: | | |
| STATUS="${{ steps.run-tests.outputs.status }}" | |
| TOTAL="${{ steps.run-tests.outputs.total }}" | |
| PASSED="${{ steps.run-tests.outputs.passed }}" | |
| FAILED="${{ steps.run-tests.outputs.failed }}" | |
| SKIPPED="${{ steps.run-tests.outputs.skipped }}" | |
| # Set defaults if empty | |
| STATUS="${STATUS:-unknown}" | |
| TOTAL="${TOTAL:-0}" | |
| PASSED="${PASSED:-0}" | |
| FAILED="${FAILED:-0}" | |
| SKIPPED="${SKIPPED:-0}" | |
| if [ "$STATUS" == "failed" ]; then | |
| echo "::error title=Tests Failed::Playwright tests failed. Total: ${TOTAL}, Passed: ${PASSED}, Failed: ${FAILED}, Skipped: ${SKIPPED}" | |
| echo "::notice title=Test Results::Total: ${TOTAL} | Passed: ${PASSED} | Failed: ${FAILED} | Skipped: ${SKIPPED}" | |
| elif [ "$STATUS" == "passed" ]; then | |
| echo "::notice title=Tests Passed::All Playwright tests passed successfully. Total: ${TOTAL}, Passed: ${PASSED}, Skipped: ${SKIPPED}" | |
| else | |
| echo "::warning title=Test Status Unknown::Unable to parse test results. Total: ${TOTAL}, Passed: ${PASSED}, Failed: ${FAILED}, Skipped: ${SKIPPED}" | |
| fi | |
| echo "::notice title=Test Report::Download the 'playwright-report' artifact to view the full HTML test report" | |
| # Step 11.8.5: Prepare event details for Teams notification | |
| - name: Prepare event details | |
| id: event-details | |
| if: always() | |
| run: | | |
| if [ "${{ github.event_name }}" == "pull_request" ]; then | |
| EVENT_TYPE="Pull Request" | |
| EVENT_DETAILS="PR #${{ github.event.pull_request.number }}: ${{ github.event.pull_request.title }}" | |
| else | |
| EVENT_TYPE="Direct Commit" | |
| COMMIT_MSG="${{ github.event.head_commit.message }}" | |
| # Remove newlines and carriage returns, replace with spaces, then clean up multiple spaces | |
| COMMIT_MSG=$(printf '%s' "$COMMIT_MSG" | tr '\n\r' ' ' | sed 's/[[:space:]]\+/ /g' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') | |
| # Truncate if too long | |
| if [ ${#COMMIT_MSG} -gt 100 ]; then | |
| COMMIT_MSG="${COMMIT_MSG:0:97}..." | |
| fi | |
| EVENT_DETAILS="Commit: ${COMMIT_MSG}" | |
| fi | |
| echo "type=${EVENT_TYPE}" >> $GITHUB_OUTPUT | |
| echo "details=${EVENT_DETAILS}" >> $GITHUB_OUTPUT | |
| # Step 11.9: Send test results to Teams channel | |
| - name: Send test results to Teams | |
| if: always() | |
| uses: ./.github/actions/send-teams-notification | |
| with: | |
| notification-type: test-results | |
| build-status: success | |
| build-exit-code: 0 | |
| test-status: ${{ steps.run-tests.outputs.status }} | |
| total: ${{ steps.run-tests.outputs.total }} | |
| passed: ${{ steps.run-tests.outputs.passed }} | |
| failed: ${{ steps.run-tests.outputs.failed }} | |
| skipped: ${{ steps.run-tests.outputs.skipped }} | |
| author-name: ${{ steps.author-info.outputs.name }} | |
| author-username: ${{ steps.author-info.outputs.username }} | |
| pr-number: ${{ github.event.pull_request.number || '' }} | |
| pr-title: ${{ github.event.pull_request.title || '' }} | |
| event-type: ${{ steps.event-details.outputs.type }} | |
| event-details: ${{ steps.event-details.outputs.details }} | |
| registry-url: ${{ steps.registry-url.outputs.url }} | |
| registry-type: ${{ steps.registry-url.outputs.type }} | |
| webhook-url: ${{ secrets.TEAMS_WEBHOOK_URL }} | |
| github-server-url: ${{ github.server_url }} | |
| github-repository: ${{ github.repository }} | |
| github-ref-name: ${{ github.ref_name }} | |
| github-sha: ${{ github.sha }} | |
| github-workflow: ${{ github.workflow }} | |
| github-run-number: ${{ github.run_number }} | |
| github-run-id: ${{ github.run_id }} | |
| continue-on-error: true | |
| # Step 11.10: Fail workflow if tests failed | |
| - name: Fail workflow if tests failed | |
| if: always() | |
| run: | | |
| STATUS="${{ steps.run-tests.outputs.status }}" | |
| EXIT_CODE="${{ steps.run-tests.outputs.exit-code }}" | |
| # Set default if empty | |
| STATUS="${STATUS:-unknown}" | |
| EXIT_CODE="${EXIT_CODE:-0}" | |
| echo "Test Status: ${STATUS}" | |
| echo "Test Exit Code: ${EXIT_CODE}" | |
| # Fail the workflow if tests failed | |
| if [ "$STATUS" == "failed" ] || [ "$EXIT_CODE" != "0" ]; then | |
| echo "❌ Tests failed! Failing workflow..." | |
| exit 1 | |
| else | |
| echo "✅ All tests passed!" | |
| exit 0 | |
| fi | |
| # Step 12: Cleanup - Stop Docker container and tunnel | |
| - name: Stop Docker container and tunnel | |
| if: always() | |
| run: | | |
| echo "Stopping cloudflared tunnel..." | |
| if [ -f cloudflared.pid ]; then | |
| PID=$(cat cloudflared.pid) | |
| echo "Killing cloudflared process $PID" | |
| kill $PID 2>/dev/null || true | |
| rm cloudflared.pid | |
| fi | |
| pkill -f "cloudflared tunnel" || true | |
| echo "Tunnel stopped" | |
| echo "Stopping Docker container..." | |
| docker stop nextjs-dev 2>/dev/null || true | |
| docker rm nextjs-dev 2>/dev/null || true | |
| echo "✅ Docker container stopped and removed" | |
| # Clean up Docker image (optional - comment out if you want to keep it) | |
| # docker rmi nextjs-app:dev 2>/dev/null || true | |
| # Step 13-14: Upload artifacts using composite action | |
| - name: Upload Test Artifacts | |
| uses: ./.github/actions/upload-artifacts | |
| with: | |
| tests-path: tests | |
| app-path: app | |
| # Step 15: Comment on PR with test results | |
| - name: Comment on PR with test results | |
| if: github.event_name == 'pull_request' && always() | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const status = '${{ steps.run-tests.outputs.status }}' || 'unknown'; | |
| const total = '${{ steps.run-tests.outputs.total }}' || '0'; | |
| const passed = '${{ steps.run-tests.outputs.passed }}' || '0'; | |
| const failed = '${{ steps.run-tests.outputs.failed }}' || '0'; | |
| const skipped = '${{ steps.run-tests.outputs.skipped }}' || '0'; | |
| const author = '@${{ steps.author-info.outputs.username }}' || '@${{ github.actor }}'; | |
| const workflowRunUrl = `${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}`; | |
| const prUrl = `${{ github.server_url }}/${{ github.repository }}/pull/${{ github.event.pull_request.number }}`; | |
| const reportUrl = `${workflowRunUrl}#artifacts`; | |
| // Get specific person's GitHub username from secret, or use email-based lookup | |
| let specificPerson = ''; | |
| const notificationUsername = '${{ secrets.NOTIFICATION_USERNAME }}'; | |
| if (notificationUsername) { | |
| specificPerson = `@${notificationUsername}`; | |
| } else { | |
| // Try to find user by email vishath.amarasinghe@sitecore.com | |
| try { | |
| const searchResult = await github.rest.search.users({ | |
| q: 'vishath.amarasinghe@sitecore.com in:email' | |
| }); | |
| if (searchResult.data.items.length > 0) { | |
| specificPerson = `@${searchResult.data.items[0].login}`; | |
| } | |
| } catch (e) { | |
| // If search fails, mention email in comment | |
| specificPerson = 'vishath.amarasinghe@sitecore.com'; | |
| } | |
| } | |
| // Determine status emoji and text | |
| let statusEmoji, statusText; | |
| if (status === 'failed') { | |
| statusEmoji = '❌'; | |
| statusText = 'Failed'; | |
| } else if (status === 'passed') { | |
| statusEmoji = '✅'; | |
| statusText = 'Passed'; | |
| } else { | |
| statusEmoji = '⚠️'; | |
| statusText = 'Unknown'; | |
| } | |
| // Build mentions | |
| const mentions = specificPerson ? `${author} ${specificPerson}` : author; | |
| // Build comment with markdown | |
| const registryUrl = '${{ steps.registry-url.outputs.url }}' || 'https://blok.sitecore.com'; | |
| const registryType = '${{ steps.registry-url.outputs.type }}' || 'production'; | |
| const comment = `## ${statusEmoji} Playwright Test Results | |
| ${mentions} | |
| **Status:** ${statusText} | **Workflow:** [${{ github.workflow }}](${workflowRunUrl}) | |
| | Metric | Count | | |
| |--------|-------| | |
| | **Total Tests** | ${total} | | |
| | ✅ **Passed** | ${passed} | | |
| | ❌ **Failed** | ${failed} | | |
| | ⏭️ **Skipped** | ${skipped} | | |
| ### Component Installation | |
| - **Registry URL:** \`${registryUrl}\` (${registryType}) | |
| - Components were installed and tested using the ${registryType} registry | |
| ### Details | |
| - **Repository:** \`${{ github.repository }}\` | |
| - **Branch:** \`${{ github.ref_name }}\` | |
| - **Commit:** \`${{ github.sha }}\` | |
| - **Author:** ${author} | |
| - **Workflow Run:** [View Details](${workflowRunUrl}) | |
| - **Pull Request:** [View PR](${prUrl}) | |
| ### Test Report | |
| - **Playwright Report:** [Download Report](${reportUrl}) | |
| - Download the \`playwright-report\` artifact from the workflow run | |
| - Extract and open \`index.html\` in a browser to view the full HTML report | |
| - The report includes detailed test results, screenshots, and traces | |
| ${status === 'failed' || parseInt(failed) > 0 ? '**Some tests failed. Please review the test results and download the report for details.**' : status === 'passed' ? '**All tests passed successfully!**' : '**Unable to parse test results. Please check the workflow logs for details.**'} | |
| --- | |
| *This is an automated notification from GitHub Actions. Email notifications are sent to mentioned users.*`; | |
| // Create comment on PR | |
| try { | |
| await github.rest.issues.createComment({ | |
| issue_number: context.issue.number, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| body: comment | |
| }); | |
| } catch (error) { | |
| console.error('Error creating PR comment:', error.message); | |
| } | |
| # Step 16: Log test results for direct commits | |
| - name: Log test results for direct commits | |
| if: github.event_name != 'pull_request' && always() | |
| run: | | |
| STATUS="${{ steps.run-tests.outputs.status }}" | |
| TOTAL="${{ steps.run-tests.outputs.total }}" | |
| PASSED="${{ steps.run-tests.outputs.passed }}" | |
| FAILED="${{ steps.run-tests.outputs.failed }}" | |
| SKIPPED="${{ steps.run-tests.outputs.skipped }}" | |
| # Set defaults if empty | |
| STATUS="${STATUS:-unknown}" | |
| TOTAL="${TOTAL:-0}" | |
| PASSED="${PASSED:-0}" | |
| FAILED="${FAILED:-0}" | |
| SKIPPED="${SKIPPED:-0}" | |
| echo "## Test Results Summary" | |
| echo "Status: ${STATUS}" | |
| echo "Total: ${TOTAL}" | |
| echo "Passed: ${PASSED}" | |
| echo "Failed: ${FAILED}" | |
| echo "Skipped: ${SKIPPED}" | |
| echo "Registry URL: ${{ steps.registry-url.outputs.url }} (${{ steps.registry-url.outputs.type }})" | |
| echo "Author: ${{ steps.author-info.outputs.name }} (@${{ steps.author-info.outputs.username }})" | |
| echo "Workflow Run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" | |
| echo "" | |
| echo "Note: GitHub will automatically send email notifications to the commit author." | |
| echo "To notify vishath.amarasinghe@sitecore.com, add their GitHub username to NOTIFICATION_USERNAME secret." |