Weekly Test Reports #46
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: Weekly Test Reports | |
| on: | |
| schedule: | |
| - cron: '0 18 * * 5' | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| pages: write | |
| id-token: write | |
| concurrency: | |
| group: 'pages' | |
| cancel-in-progress: true | |
| jobs: | |
| test-and-report: | |
| runs-on: ubuntu-latest | |
| environment: | |
| name: github-pages | |
| url: ${{ steps.deployment.outputs.page_url }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '18' | |
| cache: 'npm' | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Download current Pages content | |
| run: | | |
| echo "Downloading current pages content..." | |
| mkdir -p public | |
| curl -f -s https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/ > public/index.html 2>/dev/null || echo "Failed to download main page" | |
| curl -f -s https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/coverage-summary.json > previous-coverage.json 2>/dev/null || echo '{"total":{"lines":{"pct":0},"functions":{"pct":0},"branches":{"pct":0},"statements":{"pct":0}}}' > previous-coverage.json | |
| echo "Downloaded files:" | |
| ls -la public/ 2>/dev/null || echo "No public directory yet" | |
| ls -la previous-coverage.json 2>/dev/null || echo "No previous coverage" | |
| - name: Create reports directory structure | |
| run: | | |
| mkdir -p public/weekly-reports/coverage | |
| mkdir -p public/weekly-reports/tests | |
| - name: Run tests with coverage | |
| run: | | |
| echo "Running tests with coverage..." | |
| npm run test:coverage-report || true | |
| echo "Listing all files after test run:" | |
| ls -la | |
| echo "Checking for coverage directory:" | |
| if [ -d "coverage" ]; then | |
| echo "Coverage directory exists:" | |
| ls -la coverage/ | |
| else | |
| echo "No coverage directory found" | |
| fi | |
| echo "Checking for jest-html-reporters files:" | |
| if [ -f "jest_html_reporters.html" ]; then | |
| echo "Jest HTML reporters main file exists" | |
| ls -la jest_html_reporters.html | |
| else | |
| echo "No jest_html_reporters.html found" | |
| fi | |
| if [ -d "jest-html-reporters-attach" ]; then | |
| echo "Jest HTML reporters attach directory exists:" | |
| ls -la jest-html-reporters-attach/ | |
| else | |
| echo "No jest-html-reporters-attach directory found" | |
| fi | |
| - name: Copy reports to correct structure | |
| run: | | |
| echo "Copying reports to public/weekly-reports/..." | |
| if [ -d "coverage" ] && [ "$(ls -A coverage)" ]; then | |
| echo "Copying coverage files..." | |
| cp -r coverage/* public/weekly-reports/coverage/ 2>/dev/null || echo "Failed to copy coverage files" | |
| if [ -f "coverage/coverage-summary.json" ]; then | |
| cp coverage/coverage-summary.json public/ | |
| cp coverage/coverage-summary.json public/weekly-reports/ | |
| echo "Coverage summary copied" | |
| fi | |
| else | |
| echo "No coverage data found, creating empty reports..." | |
| echo '<html><body><h1>No Coverage Data</h1><p>Tests did not generate coverage reports. Check Jest configuration.</p></body></html>' > public/weekly-reports/coverage/index.html | |
| echo '{"total":{"lines":{"pct":0},"functions":{"pct":0},"branches":{"pct":0},"statements":{"pct":0}}}' > public/coverage-summary.json | |
| echo '{"total":{"lines":{"pct":0},"functions":{"pct":0},"branches":{"pct":0},"statements":{"pct":0}}}' > public/weekly-reports/coverage-summary.json | |
| fi | |
| if [ -f "jest_html_reporters.html" ]; then | |
| echo "Copying jest-html-reporters file..." | |
| cp jest_html_reporters.html public/weekly-reports/tests/test-report.html | |
| echo "Jest HTML reporters file copied as test-report.html" | |
| else | |
| echo "Creating placeholder test report..." | |
| echo '<!DOCTYPE html> | |
| <html> | |
| <head><title>Test Results</title></head> | |
| <body> | |
| <h1>No Test Results Available</h1> | |
| <p>Tests may have failed to run or generate reports.</p> | |
| <p>Check the workflow logs for more information.</p> | |
| </body> | |
| </html>' > public/weekly-reports/tests/test-report.html | |
| fi | |
| if [ -d "jest-html-reporters-attach" ] && [ "$(ls -A jest-html-reporters-attach)" ]; then | |
| echo "Copying jest-html-reporters attach files..." | |
| cp -r jest-html-reporters-attach public/weekly-reports/tests/ | |
| echo "Jest HTML reporters attach files copied" | |
| else | |
| echo "No jest-html-reporters-attach directory found or it's empty" | |
| fi | |
| - name: Debug - List generated files | |
| run: | | |
| echo "=== Final public directory structure ===" | |
| find public -type f -exec ls -la {} \; | |
| echo "" | |
| echo "=== Content samples ===" | |
| echo "Coverage summary content:" | |
| cat public/coverage-summary.json 2>/dev/null || echo "No coverage summary" | |
| echo "" | |
| echo "Previous coverage content:" | |
| cat previous-coverage.json 2>/dev/null || echo "No previous coverage" | |
| - name: Generate coverage trend analysis | |
| run: | | |
| node -e " | |
| const fs = require('fs'); | |
| console.log('Generating trend analysis...'); | |
| let currentCoverage = {}; | |
| try { | |
| const currentData = fs.readFileSync('public/coverage-summary.json', 'utf8'); | |
| currentCoverage = JSON.parse(currentData); | |
| console.log('Current coverage loaded'); | |
| } catch (e) { | |
| console.log('Failed to load current coverage, using defaults'); | |
| currentCoverage = { total: { lines: { pct: 0 }, functions: { pct: 0 }, branches: { pct: 0 }, statements: { pct: 0 } } }; | |
| } | |
| let previousCoverage = {}; | |
| try { | |
| const previousData = fs.readFileSync('previous-coverage.json', 'utf8'); | |
| previousCoverage = JSON.parse(previousData); | |
| console.log('Previous coverage loaded'); | |
| } catch (e) { | |
| console.log('Failed to load previous coverage, using defaults'); | |
| previousCoverage = { total: { lines: { pct: 0 }, functions: { pct: 0 }, branches: { pct: 0 }, statements: { pct: 0 } } }; | |
| } | |
| function getCoveragePercent(coverage, type) { | |
| try { | |
| return coverage?.total?.[type]?.pct || 0; | |
| } catch (e) { | |
| return 0; | |
| } | |
| } | |
| const current = { | |
| lines: getCoveragePercent(currentCoverage, 'lines'), | |
| functions: getCoveragePercent(currentCoverage, 'functions'), | |
| branches: getCoveragePercent(currentCoverage, 'branches'), | |
| statements: getCoveragePercent(currentCoverage, 'statements') | |
| }; | |
| const previous = { | |
| lines: getCoveragePercent(previousCoverage, 'lines'), | |
| functions: getCoveragePercent(previousCoverage, 'functions'), | |
| branches: getCoveragePercent(previousCoverage, 'branches'), | |
| statements: getCoveragePercent(previousCoverage, 'statements') | |
| }; | |
| const trends = { | |
| lines: current.lines - previous.lines, | |
| functions: current.functions - previous.functions, | |
| branches: current.branches - previous.branches, | |
| statements: current.statements - previous.statements | |
| }; | |
| console.log('Current:', current); | |
| console.log('Previous:', previous); | |
| console.log('Trends:', trends); | |
| // HTML Trend Generation | |
| const trendHtml = \`<!DOCTYPE html> | |
| <html lang=\"uk\"> | |
| <head> | |
| <meta charset=\"UTF-8\"> | |
| <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"> | |
| <title>Test Coverage Trend Report</title> | |
| <style> | |
| body { | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; | |
| margin: 0; | |
| padding: 20px; | |
| background-color: #f8f9fa; | |
| line-height: 1.6; | |
| } | |
| .container { | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| background: white; | |
| padding: 30px; | |
| border-radius: 12px; | |
| box-shadow: 0 4px 6px rgba(0,0,0,0.1); | |
| } | |
| .header { | |
| text-align: center; | |
| margin-bottom: 30px; | |
| padding-bottom: 20px; | |
| border-bottom: 2px solid #e9ecef; | |
| } | |
| .header h1 { | |
| color: #212529; | |
| margin: 0 0 10px 0; | |
| font-size: 2.5em; | |
| } | |
| .date { | |
| font-size: 1.1em; | |
| color: #6c757d; | |
| margin: 0; | |
| } | |
| .trend-positive { color: #28a745; font-weight: bold; } | |
| .trend-negative { color: #dc3545; font-weight: bold; } | |
| .trend-neutral { color: #6c757d; } | |
| .coverage-table { | |
| border-collapse: collapse; | |
| width: 100%; | |
| margin: 30px 0; | |
| font-size: 1.1em; | |
| } | |
| .coverage-table th, .coverage-table td { | |
| border: 1px solid #dee2e6; | |
| padding: 15px; | |
| text-align: left; | |
| } | |
| .coverage-table th { | |
| background-color: #495057; | |
| color: white; | |
| font-weight: 600; | |
| text-align: center; | |
| } | |
| .coverage-table td { | |
| text-align: center; | |
| } | |
| .coverage-table tbody tr:nth-child(even) { | |
| background-color: #f8f9fa; | |
| } | |
| .coverage-table tbody tr:hover { | |
| background-color: #e9ecef; | |
| } | |
| .links { | |
| margin-top: 40px; | |
| padding: 20px; | |
| background: #f8f9fa; | |
| border-radius: 8px; | |
| } | |
| .links h2 { | |
| margin-top: 0; | |
| color: #495057; | |
| } | |
| .links ul { | |
| list-style-type: none; | |
| padding: 0; | |
| } | |
| .links li { | |
| margin: 15px 0; | |
| } | |
| .links a { | |
| color: #007bff; | |
| text-decoration: none; | |
| padding: 12px 20px; | |
| background: white; | |
| border-radius: 6px; | |
| display: inline-block; | |
| border: 1px solid #dee2e6; | |
| transition: all 0.2s; | |
| font-weight: 500; | |
| } | |
| .links a:hover { | |
| background: #007bff; | |
| color: white; | |
| transform: translateY(-1px); | |
| box-shadow: 0 2px 4px rgba(0,123,255,0.3); | |
| } | |
| .back-link { | |
| margin-bottom: 20px; | |
| } | |
| .back-link a { | |
| color: #6c757d; | |
| text-decoration: none; | |
| font-weight: 500; | |
| } | |
| .back-link a:hover { | |
| color: #007bff; | |
| } | |
| .alert { | |
| padding: 20px; | |
| border-radius: 8px; | |
| margin: 20px 0; | |
| border-left: 4px solid; | |
| } | |
| .alert.info { | |
| background: #e3f2fd; | |
| border-left-color: #2196f3; | |
| color: #0d47a1; | |
| } | |
| .alert h3 { | |
| margin-top: 0; | |
| color: inherit; | |
| } | |
| .troubleshooting ul { | |
| margin: 15px 0; | |
| padding-left: 25px; | |
| } | |
| .troubleshooting li { | |
| margin: 8px 0; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class=\"container\"> | |
| <div class=\"back-link\"> | |
| <a href=\"../\">← Back to Main Page</a> | |
| </div> | |
| <div class=\"header\"> | |
| <h1>🧪 Test Coverage Trend Report</h1> | |
| <p class=\"date\">📅 Generated: \${new Date().toLocaleString('en-GB', { | |
| timeZone: 'Europe/Kiev', | |
| dateStyle: 'full', | |
| timeStyle: 'medium' | |
| })}</p> | |
| </div> | |
| <div class=\"alert info\"> | |
| <h3>📊 About This Report</h3> | |
| <p>This report shows test coverage trends over time. Coverage data is collected automatically and compared with previous runs to show improvement or regression trends.</p> | |
| </div> | |
| <table class=\"coverage-table\"> | |
| <thead> | |
| <tr> | |
| <th>📈 Metric</th> | |
| <th>Current</th> | |
| <th>Previous</th> | |
| <th>Trend</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr> | |
| <td><strong>📄 Lines</strong></td> | |
| <td>\${current.lines.toFixed(2)}%</td> | |
| <td>\${previous.lines.toFixed(2)}%</td> | |
| <td class=\"\${trends.lines > 0 ? 'trend-positive' : trends.lines < 0 ? 'trend-negative' : 'trend-neutral'}\"> | |
| \${trends.lines > 0 ? '+' : ''}\${trends.lines.toFixed(2)}% | |
| \${trends.lines > 0 ? ' 📈' : trends.lines < 0 ? ' 📉' : ' ➡️'} | |
| </td> | |
| </tr> | |
| <tr> | |
| <td><strong>⚙️ Functions</strong></td> | |
| <td>\${current.functions.toFixed(2)}%</td> | |
| <td>\${previous.functions.toFixed(2)}%</td> | |
| <td class=\"\${trends.functions > 0 ? 'trend-positive' : trends.functions < 0 ? 'trend-negative' : 'trend-neutral'}\"> | |
| \${trends.functions > 0 ? '+' : ''}\${trends.functions.toFixed(2)}% | |
| \${trends.functions > 0 ? ' 📈' : trends.functions < 0 ? ' 📉' : ' ➡️'} | |
| </td> | |
| </tr> | |
| <tr> | |
| <td><strong>🌿 Branches</strong></td> | |
| <td>\${current.branches.toFixed(2)}%</td> | |
| <td>\${previous.branches.toFixed(2)}%</td> | |
| <td class=\"\${trends.branches > 0 ? 'trend-positive' : trends.branches < 0 ? 'trend-negative' : 'trend-neutral'}\"> | |
| \${trends.branches > 0 ? '+' : ''}\${trends.branches.toFixed(2)}% | |
| \${trends.branches > 0 ? ' 📈' : trends.branches < 0 ? ' 📉' : ' ➡️'} | |
| </td> | |
| </tr> | |
| <tr> | |
| <td><strong>📝 Statements</strong></td> | |
| <td>\${current.statements.toFixed(2)}%</td> | |
| <td>\${previous.statements.toFixed(2)}%</td> | |
| <td class=\"\${trends.statements > 0 ? 'trend-positive' : trends.statements < 0 ? 'trend-negative' : 'trend-neutral'}\"> | |
| \${trends.statements > 0 ? '+' : ''}\${trends.statements.toFixed(2)}% | |
| \${trends.statements > 0 ? ' 📈' : trends.statements < 0 ? ' 📉' : ' ➡️'} | |
| </td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| <div class=\"links\"> | |
| <h2>📋 Available Reports</h2> | |
| <ul> | |
| <li><a href=\"./coverage/index.html\">📊 Detailed Coverage Report</a></li> | |
| <li><a href=\"./tests/test-report.html\">🧪 Jest HTML Reporters</a></li> | |
| </ul> | |
| </div> | |
| <div class=\"alert info troubleshooting\"> | |
| <h3>🔧 Troubleshooting</h3> | |
| <p>If you see 0% coverage everywhere, here are common issues:</p> | |
| <ul> | |
| <li><strong>Jest Configuration:</strong> Make sure collectCoverage is true and coverageDirectory is set</li> | |
| <li><strong>Test Files:</strong> Ensure your test files match the testMatch pattern</li> | |
| <li><strong>Source Files:</strong> Check that source files are being instrumented properly</li> | |
| <li><strong>Dependencies:</strong> Verify all testing dependencies are installed</li> | |
| <li><strong>Jest HTML Reporters:</strong> Check that jest-html-reporters is properly configured</li> | |
| </ul> | |
| <p><strong>Next Steps:</strong> Check the Actions logs for specific error messages.</p> | |
| </div> | |
| </div> | |
| </body> | |
| </html>\`; | |
| fs.writeFileSync('public/weekly-reports/index.html', trendHtml); | |
| console.log('✅ Trend report generated successfully'); | |
| " | |
| - name: Ensure main page exists | |
| run: | | |
| if [ ! -f "public/index.html" ] || [ ! -s "public/index.html" ]; then | |
| echo "Creating fallback main page..." | |
| cat > public/index.html << 'EOF' | |
| <!DOCTYPE html> | |
| <html lang="uk"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Reports Hub</title> | |
| <style> | |
| body { | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; | |
| margin: 0; | |
| padding: 20px; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| min-height: 100vh; | |
| color: white; | |
| text-align: center; | |
| } | |
| .container { | |
| max-width: 800px; | |
| margin: 50px auto; | |
| background: rgba(255,255,255,0.1); | |
| backdrop-filter: blur(10px); | |
| padding: 40px; | |
| border-radius: 20px; | |
| } | |
| h1 { font-size: 3em; margin-bottom: 20px; } | |
| .report-link { | |
| display: inline-block; | |
| background: rgba(255,255,255,0.2); | |
| color: white; | |
| padding: 15px 30px; | |
| margin: 10px; | |
| border-radius: 10px; | |
| text-decoration: none; | |
| transition: all 0.3s; | |
| } | |
| .report-link:hover { | |
| background: rgba(255,255,255,0.3); | |
| transform: translateY(-2px); | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <h1>🚀 App Menu</h1> | |
| <p>Test Management and Reporting Center</p> | |
| <div style="margin-top: 40px;"> | |
| <a href="./weekly-reports/" class="report-link"> | |
| 📊 Test Coverage Reports | |
| </a> | |
| </div> | |
| </div> | |
| </body> | |
| </html> | |
| EOF | |
| fi | |
| - name: Final directory check | |
| run: | | |
| echo "=== Final public directory structure ===" | |
| find public -type f | sort | |
| echo "" | |
| echo "=== Verifying key files exist ===" | |
| echo "Main page: $([ -f public/index.html ] && echo "✅" || echo "❌")" | |
| echo "Weekly reports index: $([ -f public/weekly-reports/index.html ] && echo "✅" || echo "❌")" | |
| echo "Coverage summary: $([ -f public/coverage-summary.json ] && echo "✅" || echo "❌")" | |
| - name: Setup Pages | |
| uses: actions/configure-pages@v4 | |
| - name: Upload artifact | |
| uses: actions/upload-pages-artifact@v3 | |
| with: | |
| path: './public' | |
| - name: Deploy to GitHub Pages | |
| id: deployment | |
| uses: actions/deploy-pages@v4 |