FORGE-985-Maven Publishing Redux #100
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: Test Coverage | |
| on: | |
| pull_request: | |
| branches: [main] | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| issues: write | |
| jobs: | |
| test-coverage: | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| # Workspace package names | |
| workspace: | |
| - "@dittolive/ditto-chat-core" | |
| - "@dittolive/ditto-chat-ui" | |
| include: | |
| - workspace: "@dittolive/ditto-chat-core" | |
| path: "sdks/js/ditto-chat-core" | |
| - workspace: "@dittolive/ditto-chat-ui" | |
| path: "sdks/js/ditto-chat-ui" | |
| fail-fast: false | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "24" | |
| cache: "npm" | |
| cache-dependency-path: "package-lock.json" | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Run tests with coverage | |
| run: npm run test:coverage -w ${{ matrix.workspace }} | |
| continue-on-error: false | |
| - name: Generate coverage report | |
| working-directory: ${{ matrix.path }} | |
| run: | | |
| # Generate coverage summary | |
| if [ -f "coverage/coverage-summary.json" ]; then | |
| echo "COVERAGE_SUMMARY<<EOF" >> $GITHUB_ENV | |
| cat coverage/coverage-summary.json | |
| echo "EOF" >> $GITHUB_ENV | |
| fi | |
| - name: Upload coverage reports | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: coverage-report-${{ hashFiles(format('{0}/package.json', matrix.path)) }} | |
| path: ${{ matrix.path }}/coverage/ | |
| retention-days: 15 | |
| - name: Parse coverage data | |
| id: coverage | |
| working-directory: ${{ matrix.path }} | |
| run: | | |
| if [ -f "coverage/coverage-summary.json" ]; then | |
| # Extract overall coverage percentages | |
| LINES=$(jq -r '.total.lines.pct' coverage/coverage-summary.json) | |
| STATEMENTS=$(jq -r '.total.statements.pct' coverage/coverage-summary.json) | |
| FUNCTIONS=$(jq -r '.total.functions.pct' coverage/coverage-summary.json) | |
| BRANCHES=$(jq -r '.total.branches.pct' coverage/coverage-summary.json) | |
| echo "lines=$LINES" >> $GITHUB_OUTPUT | |
| echo "statements=$STATEMENTS" >> $GITHUB_OUTPUT | |
| echo "functions=$FUNCTIONS" >> $GITHUB_OUTPUT | |
| echo "branches=$BRANCHES" >> $GITHUB_OUTPUT | |
| else | |
| echo "lines=0" >> $GITHUB_OUTPUT | |
| echo "statements=0" >> $GITHUB_OUTPUT | |
| echo "functions=0" >> $GITHUB_OUTPUT | |
| echo "branches=0" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Comment PR with coverage results | |
| if: always() | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const workspace = '${{ matrix.workspace }}'; | |
| const lines = parseFloat('${{ steps.coverage.outputs.lines }}'); | |
| const statements = parseFloat('${{ steps.coverage.outputs.statements }}'); | |
| const functions = parseFloat('${{ steps.coverage.outputs.functions }}'); | |
| const branches = parseFloat('${{ steps.coverage.outputs.branches }}'); | |
| // Determine coverage status | |
| const getCoverageEmoji = (pct) => { | |
| if (pct >= 80) return 'π’'; | |
| if (pct >= 60) return 'π‘'; | |
| if (pct >= 40) return 'π '; | |
| return 'π΄'; | |
| }; | |
| const getCoverageColor = (pct) => { | |
| if (pct >= 80) return 'green'; | |
| if (pct >= 60) return 'yellow'; | |
| if (pct >= 40) return 'orange'; | |
| return 'red'; | |
| }; | |
| const overallCoverage = (lines + statements + functions + branches) / 4; | |
| const statusEmoji = getCoverageEmoji(overallCoverage); | |
| const body = `## ${statusEmoji} Test Coverage Report - \`${workspace}\` | |
| ### Overall Coverage: ${overallCoverage.toFixed(2)}% | |
| | Metric | Coverage | Status | | |
| |--------|----------|--------| | |
| | ${getCoverageEmoji(lines)} Lines | **${lines}%** | ${getCoverageColor(lines)} | | |
| | ${getCoverageEmoji(statements)} Statements | **${statements}%** | ${getCoverageColor(statements)} | | |
| | ${getCoverageEmoji(functions)} Functions | **${functions}%** | ${getCoverageColor(functions)} | | |
| | ${getCoverageEmoji(branches)} Branches | **${branches}%** | ${getCoverageColor(branches)} | | |
| --- | |
| π **[View Detailed Coverage Report](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})** | |
| <details> | |
| <summary>βΉοΈ Coverage Thresholds</summary> | |
| - π’ **Excellent** (β₯ 80%) | |
| - π‘ **Good** (60-79%) | |
| - π **Fair** (40-59%) | |
| - π΄ **Poor** (< 40%) | |
| </details> | |
| `; | |
| // Find existing comment | |
| const { data: comments } = await github.rest.issues.listComments({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| }); | |
| const botComment = comments.find(comment => | |
| comment.user.type === 'Bot' && | |
| comment.body.includes(`Test Coverage Report - \`${workspace}\``) | |
| ); | |
| // Update or create comment | |
| if (botComment) { | |
| await github.rest.issues.updateComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| comment_id: botComment.id, | |
| body: body | |
| }); | |
| } else { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| body: body | |
| }); | |
| } | |
| // Set status based on coverage threshold | |
| if (overallCoverage < 70) { | |
| core.setFailed(`Coverage is below 70% for ${workspace}: ${overallCoverage.toFixed(2)}%`); | |
| } | |
| - name: Check coverage threshold | |
| working-directory: ${{ matrix.path }} | |
| run: | | |
| LINES=${{ steps.coverage.outputs.lines }} | |
| STATEMENTS=${{ steps.coverage.outputs.statements }} | |
| FUNCTIONS=${{ steps.coverage.outputs.functions }} | |
| BRANCHES=${{ steps.coverage.outputs.branches }} | |
| # Calculate overall coverage | |
| OVERALL=$(awk "BEGIN {printf \"%.2f\", ($LINES + $STATEMENTS + $FUNCTIONS + $BRANCHES) / 4}") | |
| THRESHOLD=70 | |
| echo "π Coverage Summary:" | |
| echo " Lines: $LINES%" | |
| echo " Statements: $STATEMENTS%" | |
| echo " Functions: $FUNCTIONS%" | |
| echo " Branches: $BRANCHES%" | |
| echo " Overall: $OVERALL%" | |
| # Compare using awk | |
| BELOW_THRESHOLD=$(awk "BEGIN {print ($OVERALL < $THRESHOLD)}") | |
| if [ "$BELOW_THRESHOLD" = "1" ]; then | |
| echo "::error::Overall coverage is below ${THRESHOLD}% threshold: $OVERALL%" | |
| echo "β Coverage threshold check failed for ${{ matrix.workspace }}" | |
| exit 1 | |
| else | |
| echo "β Coverage threshold check passed for ${{ matrix.workspace }} (${OVERALL}%)" | |
| fi |