From a60965a35c1bda6a8c163c7b55927fe2646a2b7f Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 30 Oct 2025 19:09:40 +0000
Subject: [PATCH 1/4] Initial plan
From b4d7976db0c6ae5451c1c1d839ebaf34bd1f32c9 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 30 Oct 2025 19:16:39 +0000
Subject: [PATCH 2/4] Add test coverage CI workflow with PR comments and
summary
Co-authored-by: Mossaka <5447827+Mossaka@users.noreply.github.com>
---
.github/workflows/test-coverage.yml | 165 ++++++++++++++++++++++++++++
1 file changed, 165 insertions(+)
create mode 100644 .github/workflows/test-coverage.yml
diff --git a/.github/workflows/test-coverage.yml b/.github/workflows/test-coverage.yml
new file mode 100644
index 00000000..f524618d
--- /dev/null
+++ b/.github/workflows/test-coverage.yml
@@ -0,0 +1,165 @@
+name: Test Coverage
+
+on:
+ pull_request:
+ branches: [main]
+ push:
+ branches: [main]
+
+permissions:
+ contents: read
+ pull-requests: write
+ checks: write
+
+jobs:
+ coverage:
+ name: Test Coverage Report
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
+ with:
+ node-version: '20'
+ cache: 'npm'
+
+ - name: Install dependencies
+ run: npm ci
+
+ - name: Build project
+ run: npm run build
+
+ - name: Run tests with coverage
+ run: npm run test:coverage
+
+ - name: Generate coverage summary
+ id: coverage
+ run: |
+ # Read the coverage summary
+ COVERAGE_JSON=$(cat coverage/coverage-summary.json)
+
+ # Extract metrics using jq
+ LINES_PCT=$(echo "$COVERAGE_JSON" | jq -r '.total.lines.pct')
+ STATEMENTS_PCT=$(echo "$COVERAGE_JSON" | jq -r '.total.statements.pct')
+ FUNCTIONS_PCT=$(echo "$COVERAGE_JSON" | jq -r '.total.functions.pct')
+ BRANCHES_PCT=$(echo "$COVERAGE_JSON" | jq -r '.total.branches.pct')
+
+ LINES_COVERED=$(echo "$COVERAGE_JSON" | jq -r '.total.lines.covered')
+ LINES_TOTAL=$(echo "$COVERAGE_JSON" | jq -r '.total.lines.total')
+ STATEMENTS_COVERED=$(echo "$COVERAGE_JSON" | jq -r '.total.statements.covered')
+ STATEMENTS_TOTAL=$(echo "$COVERAGE_JSON" | jq -r '.total.statements.total')
+ FUNCTIONS_COVERED=$(echo "$COVERAGE_JSON" | jq -r '.total.functions.covered')
+ FUNCTIONS_TOTAL=$(echo "$COVERAGE_JSON" | jq -r '.total.functions.total')
+ BRANCHES_COVERED=$(echo "$COVERAGE_JSON" | jq -r '.total.branches.covered')
+ BRANCHES_TOTAL=$(echo "$COVERAGE_JSON" | jq -r '.total.branches.total')
+
+ # Create summary for GitHub Actions Summary
+ echo "## Test Coverage Report 📊" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "| Metric | Coverage | Covered/Total |" >> $GITHUB_STEP_SUMMARY
+ echo "|--------|----------|---------------|" >> $GITHUB_STEP_SUMMARY
+ echo "| **Lines** | ${LINES_PCT}% | ${LINES_COVERED}/${LINES_TOTAL} |" >> $GITHUB_STEP_SUMMARY
+ echo "| **Statements** | ${STATEMENTS_PCT}% | ${STATEMENTS_COVERED}/${STATEMENTS_TOTAL} |" >> $GITHUB_STEP_SUMMARY
+ echo "| **Functions** | ${FUNCTIONS_PCT}% | ${FUNCTIONS_COVERED}/${FUNCTIONS_TOTAL} |" >> $GITHUB_STEP_SUMMARY
+ echo "| **Branches** | ${BRANCHES_PCT}% | ${BRANCHES_COVERED}/${BRANCHES_TOTAL} |" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+
+ # Determine coverage status emoji
+ if (( $(echo "$LINES_PCT >= 80" | bc -l) )); then
+ EMOJI="✅"
+ elif (( $(echo "$LINES_PCT >= 60" | bc -l) )); then
+ EMOJI="⚠️"
+ else
+ EMOJI="❌"
+ fi
+
+ # Create PR comment body
+ COMMENT_BODY="## Test Coverage Report ${EMOJI}
+
+ | Metric | Coverage | Covered/Total |
+ |--------|----------|---------------|
+ | **Lines** | ${LINES_PCT}% | ${LINES_COVERED}/${LINES_TOTAL} |
+ | **Statements** | ${STATEMENTS_PCT}% | ${STATEMENTS_COVERED}/${STATEMENTS_TOTAL} |
+ | **Functions** | ${FUNCTIONS_PCT}% | ${FUNCTIONS_COVERED}/${FUNCTIONS_TOTAL} |
+ | **Branches** | ${BRANCHES_PCT}% | ${BRANCHES_COVERED}/${BRANCHES_TOTAL} |
+
+
+ Coverage Thresholds
+
+ The project has the following coverage thresholds configured:
+ - Lines: 38%
+ - Statements: 38%
+ - Functions: 35%
+ - Branches: 30%
+
+
+
+ ---
+ *Coverage report generated by \\\`npm run test:coverage\\\`*"
+
+ # Save for next step (escape newlines for GitHub Actions)
+ echo "COMMENT_BODY<> $GITHUB_ENV
+ echo "$COMMENT_BODY" >> $GITHUB_ENV
+ echo "EOF" >> $GITHUB_ENV
+
+ # Also save individual metrics as outputs
+ echo "lines_pct=${LINES_PCT}" >> $GITHUB_OUTPUT
+ echo "statements_pct=${STATEMENTS_PCT}" >> $GITHUB_OUTPUT
+ echo "functions_pct=${FUNCTIONS_PCT}" >> $GITHUB_OUTPUT
+ echo "branches_pct=${BRANCHES_PCT}" >> $GITHUB_OUTPUT
+
+ - name: Comment PR with coverage report
+ if: github.event_name == 'pull_request'
+ uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ const commentBody = process.env.COMMENT_BODY;
+
+ // Find existing coverage 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')
+ );
+
+ if (botComment) {
+ // Update existing comment
+ await github.rest.issues.updateComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ comment_id: botComment.id,
+ body: commentBody
+ });
+ } else {
+ // Create new comment
+ await github.rest.issues.createComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: context.issue.number,
+ body: commentBody
+ });
+ }
+
+ - name: Upload coverage reports
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
+ with:
+ name: coverage-report
+ path: |
+ coverage/
+ retention-days: 30
+
+ - name: Check coverage thresholds
+ run: |
+ echo "Checking if coverage meets minimum thresholds..."
+ # Jest will fail if coverage is below thresholds defined in jest.config.js
+ # This step is informational since the test:coverage command already checks
From 2c7d0d2595e37b517c518d07f822c493234af925 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 30 Oct 2025 19:20:20 +0000
Subject: [PATCH 3/4] Update documentation for test coverage CI workflow
Co-authored-by: Mossaka <5447827+Mossaka@users.noreply.github.com>
---
CONTRIBUTING.md | 4 +++-
TESTING.md | 18 ++++++++++++++++++
2 files changed, 21 insertions(+), 1 deletion(-)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index d5eb3f0f..f70e5d0a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -109,10 +109,12 @@ logger.success('Operation completed successfully');
- Clear description of what the PR does
- Reference any related issues
- Include tests for new functionality
- - Ensure CI passes
+ - Ensure CI passes (including test coverage checks)
+ - Review the automated coverage report posted as a PR comment
3. **Review process:**
- Maintainers will review your PR
+ - The coverage report bot will automatically comment with test coverage metrics
- Address any feedback
- Once approved, your PR will be merged
diff --git a/TESTING.md b/TESTING.md
index 4844c27e..dcbfbfa4 100644
--- a/TESTING.md
+++ b/TESTING.md
@@ -53,6 +53,24 @@ After running `npm run test:coverage`, coverage reports are available in the `co
- **LCOV**: `coverage/lcov.info` for integration with CI/CD tools
- **JSON**: `coverage/coverage-summary.json` for programmatic access
+### CI/CD Coverage Reporting
+
+The project includes automated test coverage reporting via GitHub Actions (`.github/workflows/test-coverage.yml`):
+
+- **Automatic PR Comments**: Coverage reports are automatically posted as comments on pull requests
+- **GitHub Actions Summary**: Each workflow run includes a coverage summary in the job output
+- **Coverage Artifacts**: Full coverage reports are uploaded as artifacts for 30 days
+- **Update Strategy**: Existing coverage comments are updated on subsequent pushes to avoid comment spam
+
+The coverage workflow runs on:
+- All pull requests to `main`
+- All pushes to `main`
+
+Required permissions:
+- `contents: read` - To checkout the repository
+- `pull-requests: write` - To post/update PR comments
+- `checks: write` - To update check status
+
### Coverage Thresholds
The project maintains the following minimum coverage thresholds (configured in `jest.config.js`):
From fe9ba8ddddfb9662a6a747c07aaea2b0da67ae73 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 30 Oct 2025 19:38:52 +0000
Subject: [PATCH 4/4] Remove visual indicators from coverage report
Co-authored-by: Mossaka <5447827+Mossaka@users.noreply.github.com>
---
.github/workflows/test-coverage.yml | 13 ++-----------
1 file changed, 2 insertions(+), 11 deletions(-)
diff --git a/.github/workflows/test-coverage.yml b/.github/workflows/test-coverage.yml
index f524618d..32754371 100644
--- a/.github/workflows/test-coverage.yml
+++ b/.github/workflows/test-coverage.yml
@@ -58,7 +58,7 @@ jobs:
BRANCHES_TOTAL=$(echo "$COVERAGE_JSON" | jq -r '.total.branches.total')
# Create summary for GitHub Actions Summary
- echo "## Test Coverage Report 📊" >> $GITHUB_STEP_SUMMARY
+ echo "## Test Coverage Report" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Metric | Coverage | Covered/Total |" >> $GITHUB_STEP_SUMMARY
echo "|--------|----------|---------------|" >> $GITHUB_STEP_SUMMARY
@@ -68,17 +68,8 @@ jobs:
echo "| **Branches** | ${BRANCHES_PCT}% | ${BRANCHES_COVERED}/${BRANCHES_TOTAL} |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
- # Determine coverage status emoji
- if (( $(echo "$LINES_PCT >= 80" | bc -l) )); then
- EMOJI="✅"
- elif (( $(echo "$LINES_PCT >= 60" | bc -l) )); then
- EMOJI="⚠️"
- else
- EMOJI="❌"
- fi
-
# Create PR comment body
- COMMENT_BODY="## Test Coverage Report ${EMOJI}
+ COMMENT_BODY="## Test Coverage Report
| Metric | Coverage | Covered/Total |
|--------|----------|---------------|