Skip to content

Commit ef49284

Browse files
CopilotMossaka
andauthored
Add test coverage CI with PR comments and job summaries (#36)
* Initial plan * Add test coverage CI workflow with PR comments and summary Co-authored-by: Mossaka <[email protected]> * Update documentation for test coverage CI workflow Co-authored-by: Mossaka <[email protected]> * Remove visual indicators from coverage report Co-authored-by: Mossaka <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: Mossaka <[email protected]>
1 parent 817122e commit ef49284

File tree

3 files changed

+177
-1
lines changed

3 files changed

+177
-1
lines changed
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
name: Test Coverage
2+
3+
on:
4+
pull_request:
5+
branches: [main]
6+
push:
7+
branches: [main]
8+
9+
permissions:
10+
contents: read
11+
pull-requests: write
12+
checks: write
13+
14+
jobs:
15+
coverage:
16+
name: Test Coverage Report
17+
runs-on: ubuntu-latest
18+
timeout-minutes: 10
19+
20+
steps:
21+
- name: Checkout repository
22+
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4
23+
24+
- name: Setup Node.js
25+
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
26+
with:
27+
node-version: '20'
28+
cache: 'npm'
29+
30+
- name: Install dependencies
31+
run: npm ci
32+
33+
- name: Build project
34+
run: npm run build
35+
36+
- name: Run tests with coverage
37+
run: npm run test:coverage
38+
39+
- name: Generate coverage summary
40+
id: coverage
41+
run: |
42+
# Read the coverage summary
43+
COVERAGE_JSON=$(cat coverage/coverage-summary.json)
44+
45+
# Extract metrics using jq
46+
LINES_PCT=$(echo "$COVERAGE_JSON" | jq -r '.total.lines.pct')
47+
STATEMENTS_PCT=$(echo "$COVERAGE_JSON" | jq -r '.total.statements.pct')
48+
FUNCTIONS_PCT=$(echo "$COVERAGE_JSON" | jq -r '.total.functions.pct')
49+
BRANCHES_PCT=$(echo "$COVERAGE_JSON" | jq -r '.total.branches.pct')
50+
51+
LINES_COVERED=$(echo "$COVERAGE_JSON" | jq -r '.total.lines.covered')
52+
LINES_TOTAL=$(echo "$COVERAGE_JSON" | jq -r '.total.lines.total')
53+
STATEMENTS_COVERED=$(echo "$COVERAGE_JSON" | jq -r '.total.statements.covered')
54+
STATEMENTS_TOTAL=$(echo "$COVERAGE_JSON" | jq -r '.total.statements.total')
55+
FUNCTIONS_COVERED=$(echo "$COVERAGE_JSON" | jq -r '.total.functions.covered')
56+
FUNCTIONS_TOTAL=$(echo "$COVERAGE_JSON" | jq -r '.total.functions.total')
57+
BRANCHES_COVERED=$(echo "$COVERAGE_JSON" | jq -r '.total.branches.covered')
58+
BRANCHES_TOTAL=$(echo "$COVERAGE_JSON" | jq -r '.total.branches.total')
59+
60+
# Create summary for GitHub Actions Summary
61+
echo "## Test Coverage Report" >> $GITHUB_STEP_SUMMARY
62+
echo "" >> $GITHUB_STEP_SUMMARY
63+
echo "| Metric | Coverage | Covered/Total |" >> $GITHUB_STEP_SUMMARY
64+
echo "|--------|----------|---------------|" >> $GITHUB_STEP_SUMMARY
65+
echo "| **Lines** | ${LINES_PCT}% | ${LINES_COVERED}/${LINES_TOTAL} |" >> $GITHUB_STEP_SUMMARY
66+
echo "| **Statements** | ${STATEMENTS_PCT}% | ${STATEMENTS_COVERED}/${STATEMENTS_TOTAL} |" >> $GITHUB_STEP_SUMMARY
67+
echo "| **Functions** | ${FUNCTIONS_PCT}% | ${FUNCTIONS_COVERED}/${FUNCTIONS_TOTAL} |" >> $GITHUB_STEP_SUMMARY
68+
echo "| **Branches** | ${BRANCHES_PCT}% | ${BRANCHES_COVERED}/${BRANCHES_TOTAL} |" >> $GITHUB_STEP_SUMMARY
69+
echo "" >> $GITHUB_STEP_SUMMARY
70+
71+
# Create PR comment body
72+
COMMENT_BODY="## Test Coverage Report
73+
74+
| Metric | Coverage | Covered/Total |
75+
|--------|----------|---------------|
76+
| **Lines** | ${LINES_PCT}% | ${LINES_COVERED}/${LINES_TOTAL} |
77+
| **Statements** | ${STATEMENTS_PCT}% | ${STATEMENTS_COVERED}/${STATEMENTS_TOTAL} |
78+
| **Functions** | ${FUNCTIONS_PCT}% | ${FUNCTIONS_COVERED}/${FUNCTIONS_TOTAL} |
79+
| **Branches** | ${BRANCHES_PCT}% | ${BRANCHES_COVERED}/${BRANCHES_TOTAL} |
80+
81+
<details>
82+
<summary>Coverage Thresholds</summary>
83+
84+
The project has the following coverage thresholds configured:
85+
- Lines: 38%
86+
- Statements: 38%
87+
- Functions: 35%
88+
- Branches: 30%
89+
90+
</details>
91+
92+
---
93+
*Coverage report generated by \\\`npm run test:coverage\\\`*"
94+
95+
# Save for next step (escape newlines for GitHub Actions)
96+
echo "COMMENT_BODY<<EOF" >> $GITHUB_ENV
97+
echo "$COMMENT_BODY" >> $GITHUB_ENV
98+
echo "EOF" >> $GITHUB_ENV
99+
100+
# Also save individual metrics as outputs
101+
echo "lines_pct=${LINES_PCT}" >> $GITHUB_OUTPUT
102+
echo "statements_pct=${STATEMENTS_PCT}" >> $GITHUB_OUTPUT
103+
echo "functions_pct=${FUNCTIONS_PCT}" >> $GITHUB_OUTPUT
104+
echo "branches_pct=${BRANCHES_PCT}" >> $GITHUB_OUTPUT
105+
106+
- name: Comment PR with coverage report
107+
if: github.event_name == 'pull_request'
108+
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
109+
with:
110+
github-token: ${{ secrets.GITHUB_TOKEN }}
111+
script: |
112+
const commentBody = process.env.COMMENT_BODY;
113+
114+
// Find existing coverage comment
115+
const { data: comments } = await github.rest.issues.listComments({
116+
owner: context.repo.owner,
117+
repo: context.repo.repo,
118+
issue_number: context.issue.number,
119+
});
120+
121+
const botComment = comments.find(comment =>
122+
comment.user.type === 'Bot' &&
123+
comment.body.includes('Test Coverage Report')
124+
);
125+
126+
if (botComment) {
127+
// Update existing comment
128+
await github.rest.issues.updateComment({
129+
owner: context.repo.owner,
130+
repo: context.repo.repo,
131+
comment_id: botComment.id,
132+
body: commentBody
133+
});
134+
} else {
135+
// Create new comment
136+
await github.rest.issues.createComment({
137+
owner: context.repo.owner,
138+
repo: context.repo.repo,
139+
issue_number: context.issue.number,
140+
body: commentBody
141+
});
142+
}
143+
144+
- name: Upload coverage reports
145+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
146+
with:
147+
name: coverage-report
148+
path: |
149+
coverage/
150+
retention-days: 30
151+
152+
- name: Check coverage thresholds
153+
run: |
154+
echo "Checking if coverage meets minimum thresholds..."
155+
# Jest will fail if coverage is below thresholds defined in jest.config.js
156+
# This step is informational since the test:coverage command already checks

CONTRIBUTING.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,12 @@ logger.success('Operation completed successfully');
109109
- Clear description of what the PR does
110110
- Reference any related issues
111111
- Include tests for new functionality
112-
- Ensure CI passes
112+
- Ensure CI passes (including test coverage checks)
113+
- Review the automated coverage report posted as a PR comment
113114
114115
3. **Review process:**
115116
- Maintainers will review your PR
117+
- The coverage report bot will automatically comment with test coverage metrics
116118
- Address any feedback
117119
- Once approved, your PR will be merged
118120

TESTING.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,24 @@ After running `npm run test:coverage`, coverage reports are available in the `co
5353
- **LCOV**: `coverage/lcov.info` for integration with CI/CD tools
5454
- **JSON**: `coverage/coverage-summary.json` for programmatic access
5555

56+
### CI/CD Coverage Reporting
57+
58+
The project includes automated test coverage reporting via GitHub Actions (`.github/workflows/test-coverage.yml`):
59+
60+
- **Automatic PR Comments**: Coverage reports are automatically posted as comments on pull requests
61+
- **GitHub Actions Summary**: Each workflow run includes a coverage summary in the job output
62+
- **Coverage Artifacts**: Full coverage reports are uploaded as artifacts for 30 days
63+
- **Update Strategy**: Existing coverage comments are updated on subsequent pushes to avoid comment spam
64+
65+
The coverage workflow runs on:
66+
- All pull requests to `main`
67+
- All pushes to `main`
68+
69+
Required permissions:
70+
- `contents: read` - To checkout the repository
71+
- `pull-requests: write` - To post/update PR comments
72+
- `checks: write` - To update check status
73+
5674
### Coverage Thresholds
5775

5876
The project maintains the following minimum coverage thresholds (configured in `jest.config.js`):

0 commit comments

Comments
 (0)