Add test #63
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: E2E Tests with Cypress | |
| on: | |
| push: | |
| branches: [ "feature/**", "develop/**", "release/**" ] | |
| paths: | |
| - 'frontend/**' | |
| - 'backend/**' | |
| - 'docker-compose.test.yml' | |
| - '.github/workflows/e2e-tests.yml' | |
| pull_request: | |
| branches: [ main, develop, feature/**, release/** ] | |
| types: [ opened, synchronize, reopened ] | |
| workflow_dispatch: # Manual trigger for testing | |
| env: | |
| DOCKER_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} | |
| jobs: | |
| cypress-e2e: | |
| runs-on: ubuntu-latest | |
| name: Run Cypress E2E Tests | |
| steps: | |
| # 1. Checkout code | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| # 2. Set up Node.js for Cypress | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '22' | |
| cache: 'npm' | |
| cache-dependency-path: 'frontend/package-lock.json' | |
| # 3. Install Cypress dependencies | |
| - name: Install Cypress dependencies | |
| working-directory: ./frontend | |
| run: | | |
| npm ci | |
| npm install -D cypress-mochawesome-reporter mochawesome mochawesome-merge mochawesome-report-generator | |
| # 4. Log in to Docker Hub (for pulling backend image) | |
| - name: Log in to Docker Hub | |
| uses: docker/login-action@v3 | |
| with: | |
| username: ${{ env.DOCKER_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| # 5. Pull Backend Docker Image | |
| - name: Pull Backend Docker Image | |
| run: | | |
| echo "Pulling backend image from Docker Hub..." | |
| docker pull ${{ env.DOCKER_USERNAME }}/apartment-backend:latest | |
| echo "✅ Backend image pulled successfully" | |
| # 6. Tag backend image for dev compose | |
| - name: Tag Backend Image for Dev Compose | |
| run: | | |
| echo "Tagging backend image for development..." | |
| docker tag ${{ env.DOCKER_USERNAME }}/apartment-backend:latest apartment-backend:dev | |
| echo "✅ Backend image tagged" | |
| # 7. Create test environment file | |
| - name: Create .env file for testing | |
| run: | | |
| cat > .env << EOF | |
| DOCKER_USERNAME=${{ env.DOCKER_USERNAME }} | |
| MYSQL_ROOT_PASSWORD=dev_root_password | |
| MYSQL_DATABASE=apartment_db | |
| MYSQL_USER=apartment | |
| MYSQL_PASSWORD=dev_password | |
| JWT_SECRET_DEV=dGVzdC1zZWNyZXQta2V5LWZvci1qd3QtdG9rZW4tZ2VuZXJhdGlvbi1pbi10ZXN0LWVudmlyb25tZW50 | |
| JWT_EXPIRATION=86400000 | |
| CORS_ALLOWED_ORIGINS=http://localhost:5173,http://localhost:80,http://localhost:8080 | |
| ADMIN_USERNAME=apartment_admin | |
| ADMIN_PASSWORD=Admin@2024!Secure | |
| ADMIN_EMAIL=admin@apartment.local | |
| VILLAGER_USERNAME=villager | |
| VILLAGER_PASSWORD=villager123 | |
| VILLAGER_EMAIL=villager@apartment.local | |
| TEST_USERNAME=testuser | |
| TEST_PASSWORD=test123 | |
| TEST_EMAIL=testuser@apartment.local | |
| EOF | |
| # 8. Start Application Stack with Docker Compose (Dev Mode) | |
| - name: Start Application Stack | |
| run: | | |
| echo "Starting services with Docker Compose (Dev Mode)..." | |
| docker compose -f docker-compose.dev.yml up --build -d | |
| echo "Waiting for MySQL to be ready..." | |
| timeout 120s bash -c 'until docker compose -f docker-compose.dev.yml exec -T db mysqladmin ping -h localhost --silent; do echo "Waiting for MySQL..."; sleep 5; done' | |
| echo "✅ MySQL is ready" | |
| echo "Waiting for Backend to be ready..." | |
| timeout 120s bash -c 'until curl -f http://localhost:8080/health 2>/dev/null; do echo "Waiting for Backend..."; sleep 5; done' | |
| echo "✅ Backend is ready" | |
| echo "Waiting for Frontend to be ready..." | |
| timeout 90s bash -c 'until curl -f http://localhost:5173 2>/dev/null; do echo "Waiting for Frontend..."; sleep 5; done' | |
| echo "✅ Frontend is ready" | |
| # Additional wait for Vite dev server to fully initialize | |
| echo "Waiting 15 seconds for Vite dev server to fully initialize..." | |
| sleep 15 | |
| echo "🎉 All services are up and running!" | |
| # 9. Display running containers for debugging | |
| - name: Show running containers | |
| if: always() | |
| run: docker compose -f docker-compose.dev.yml ps | |
| # 10. Run Cypress E2E Tests | |
| - name: Run Cypress E2E Tests | |
| working-directory: ./frontend | |
| run: | | |
| echo "🧪 Running Cypress E2E Tests..." | |
| npm run cypress:ci | |
| echo "✅ Cypress tests completed" | |
| # 11. Generate combined test report | |
| - name: Generate Combined Report | |
| if: always() | |
| working-directory: ./frontend | |
| run: | | |
| if [ -f cypress/results/index.json ]; then | |
| echo "📊 Generating combined test report..." | |
| cp cypress/results/index.json combined-report.json | |
| echo "✅ Combined report generated" | |
| else | |
| echo "⚠️ No test results found, creating empty report" | |
| echo '{"stats":{"tests":0,"passes":0,"failures":0,"pending":0,"duration":0}}' > combined-report.json | |
| fi | |
| # 12. Upload test videos | |
| - name: Upload Test Videos | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: cypress-videos | |
| path: frontend/cypress/videos/ | |
| retention-days: 7 | |
| if-no-files-found: ignore | |
| # 13. Upload test screenshots (for failed tests) | |
| - name: Upload Test Screenshots | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: cypress-screenshots | |
| path: frontend/cypress/screenshots/ | |
| retention-days: 7 | |
| if-no-files-found: ignore | |
| # 14. Upload HTML test report | |
| - name: Upload HTML Test Report | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: cypress-html-report | |
| path: frontend/cypress/results/ | |
| retention-days: 30 | |
| if-no-files-found: warn | |
| # 15. Upload JSON test results | |
| - name: Upload JSON Test Results | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: cypress-json-results | |
| path: frontend/combined-report.json | |
| retention-days: 30 | |
| if-no-files-found: warn | |
| # 16. Comment test results on PR | |
| - name: Comment Test Results on PR | |
| if: github.event_name == 'pull_request' && always() | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| let reportData; | |
| try { | |
| const report = fs.readFileSync('frontend/combined-report.json', 'utf8'); | |
| reportData = JSON.parse(report); | |
| } catch (error) { | |
| console.log('Could not read test results, posting generic comment'); | |
| await github.rest.issues.createComment({ | |
| issue_number: context.issue.number, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| body: '⚠️ Cypress E2E tests encountered an error. Please check the workflow logs.' | |
| }); | |
| return; | |
| } | |
| const stats = reportData.stats; | |
| const passed = stats.passes; | |
| const failed = stats.failures; | |
| const pending = stats.pending; | |
| const total = stats.tests; | |
| const duration = (stats.duration / 1000).toFixed(2); | |
| const passRate = ((passed / total) * 100).toFixed(1); | |
| const emoji = failed === 0 ? '✅' : '❌'; | |
| const status = failed === 0 ? '**All tests passed!**' : '**Some tests failed!**'; | |
| const body = ` | |
| ## ${emoji} Cypress E2E Test Results | |
| ${status} | |
| | Metric | Count | | |
| |--------|-------| | |
| | 📊 Total Tests | ${total} | | |
| | ✅ Passed | ${passed} | | |
| | ❌ Failed | ${failed} | | |
| | ⏭️ Skipped | ${pending} | | |
| | ⏱️ Duration | ${duration}s | | |
| | 📈 Pass Rate | ${passRate}% | | |
| ${failed > 0 ? '### ⚠️ Failed Tests\n\nPlease review the test artifacts for details:\n- Videos: Check workflow artifacts\n- Screenshots: Available for failed tests\n- HTML Report: Full detailed report in artifacts\n' : '### 🎉 Excellent!\n\nAll E2E tests passed successfully. The application is working as expected.\n'} | |
| --- | |
| *View full report in [workflow artifacts](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})* | |
| `; | |
| await github.rest.issues.createComment({ | |
| issue_number: context.issue.number, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| body: body | |
| }); | |
| # 17. Display test summary | |
| - name: E2E Test Summary | |
| if: always() | |
| run: | | |
| echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" | |
| echo "🧪 Cypress E2E Test Execution Complete" | |
| echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" | |
| echo "" | |
| if [ -f frontend/combined-report.json ]; then | |
| echo "📊 Test Results:" | |
| cat frontend/combined-report.json | jq -r '.stats | "Total: \(.tests)\nPassed: \(.passes)\nFailed: \(.failures)\nSkipped: \(.pending)\nDuration: \(.duration)ms"' | |
| else | |
| echo "⚠️ Test results not available" | |
| fi | |
| echo "" | |
| echo "📦 Artifacts uploaded:" | |
| echo " - Videos (all tests)" | |
| echo " - Screenshots (failures only)" | |
| echo " - HTML Report" | |
| echo " - JSON Results" | |
| echo "" | |
| echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" | |
| # 18. Show container logs on failure | |
| - name: Show Container Logs on Failure | |
| if: failure() | |
| run: | | |
| echo "📋 Backend Logs:" | |
| docker compose -f docker-compose.dev.yml logs backend --tail=100 | |
| echo "" | |
| echo "📋 Frontend Logs:" | |
| docker compose -f docker-compose.dev.yml logs frontend --tail=100 | |
| echo "" | |
| echo "📋 MySQL Logs:" | |
| docker compose -f docker-compose.dev.yml logs db --tail=50 | |
| # 19. Cleanup - Teardown environment | |
| - name: Teardown Environment | |
| if: always() | |
| run: | | |
| echo "🧹 Cleaning up test environment..." | |
| docker compose -f docker-compose.dev.yml down -v | |
| echo "✅ Cleanup complete" |