apex-code-coverage-transformer 2.19.2 → 2.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/README.md +74 -160
- package/lib/commands/acc-transformer/transform.d.ts +2 -0
- package/lib/commands/acc-transformer/transform.js +20 -4
- package/lib/commands/acc-transformer/transform.js.map +1 -1
- package/lib/handlers/cobertura.js +5 -2
- package/lib/handlers/cobertura.js.map +1 -1
- package/lib/transformers/coverageTransformer.d.ts +6 -1
- package/lib/transformers/coverageTransformer.js +32 -11
- package/lib/transformers/coverageTransformer.js.map +1 -1
- package/lib/transformers/generators/generateGitHubActions.d.ts +2 -1
- package/lib/transformers/generators/generateGitHubActions.js +10 -3
- package/lib/transformers/generators/generateGitHubActions.js.map +1 -1
- package/lib/transformers/reportGenerator.d.ts +4 -1
- package/lib/transformers/reportGenerator.js +5 -5
- package/lib/transformers/reportGenerator.js.map +1 -1
- package/lib/utils/buildFilePathCache.d.ts +5 -9
- package/lib/utils/buildFilePathCache.js +26 -30
- package/lib/utils/buildFilePathCache.js.map +1 -1
- package/lib/utils/findFilePath.d.ts +1 -8
- package/lib/utils/findFilePath.js +6 -27
- package/lib/utils/findFilePath.js.map +1 -1
- package/messages/transformer.transform.md +8 -0
- package/oclif.manifest.json +18 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,20 @@
|
|
|
5
5
|
|
|
6
6
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
7
7
|
|
|
8
|
+
## [2.20.0](https://github.com/mcarvin8/apex-code-coverage-transformer/compare/v2.19.2...v2.20.0) (2026-05-29)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* add --min-coverage and --max-annotations flags ([#294](https://github.com/mcarvin8/apex-code-coverage-transformer/issues/294)) ([0eb4de9](https://github.com/mcarvin8/apex-code-coverage-transformer/commit/0eb4de91165471160062e673eac368fac3351a35))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Bug Fixes
|
|
17
|
+
|
|
18
|
+
* cap GitHub Actions annotations at 50 with overflow notice ([#293](https://github.com/mcarvin8/apex-code-coverage-transformer/issues/293)) ([ad3bdff](https://github.com/mcarvin8/apex-code-coverage-transformer/commit/ad3bdffb79c5271dfa32d24814dcb0c1005b9930))
|
|
19
|
+
* guard against NaN line-rate in Cobertura when no lines tracked ([#292](https://github.com/mcarvin8/apex-code-coverage-transformer/issues/292)) ([f44d69d](https://github.com/mcarvin8/apex-code-coverage-transformer/commit/f44d69d106835ce3c438fd12a5101715247a9974))
|
|
20
|
+
* warn and deduplicate on same-named Apex files across packages ([#290](https://github.com/mcarvin8/apex-code-coverage-transformer/issues/290)) ([be00604](https://github.com/mcarvin8/apex-code-coverage-transformer/commit/be00604859e1dfffae6e4bda2ad0b82f89ff5dfb))
|
|
21
|
+
|
|
8
22
|
## [2.19.2](https://github.com/mcarvin8/apex-code-coverage-transformer/compare/v2.19.1...v2.19.2) (2026-05-26)
|
|
9
23
|
|
|
10
24
|
|
package/README.md
CHANGED
|
@@ -8,11 +8,10 @@
|
|
|
8
8
|
|
|
9
9
|
A Salesforce CLI plugin that converts Apex code coverage JSON (from deploy or test runs) into formats used by SonarQube, Codecov, GitHub, GitLab, Azure DevOps, Bitbucket, and other tools. Use it to keep coverage in sync with your CI/CD and code quality pipelines.
|
|
10
10
|
|
|
11
|
-
It also produces presentation-ready output for pull-request reviews: a Markdown summary for PR/MR comments and CI job summaries, and GitHub Actions workflow command annotations that surface uncovered lines inline on the PR diff. The GitHub Actions output
|
|
11
|
+
It also produces presentation-ready output for pull-request reviews: a Markdown summary for PR/MR comments and CI job summaries, and GitHub Actions workflow command annotations that surface uncovered lines inline on the PR diff. The GitHub Actions output pairs with the Apex code quality annotations from [sf-cat](https://www.npmjs.com/package/sf-cat), so a single PR can show coverage gaps and quality findings side-by-side.
|
|
12
12
|
|
|
13
13
|
> Missing an output format via `--format`? Open an [issue](https://github.com/mcarvin8/apex-code-coverage-transformer/issues) or submit a [pull request](https://github.com/mcarvin8/apex-code-coverage-transformer/blob/main/CONTRIBUTING.md).
|
|
14
14
|
|
|
15
|
-
<!-- TABLE OF CONTENTS -->
|
|
16
15
|
<details>
|
|
17
16
|
<summary>Table of Contents</summary>
|
|
18
17
|
|
|
@@ -20,21 +19,12 @@ It also produces presentation-ready output for pull-request reviews: a Markdown
|
|
|
20
19
|
- [Install](#install)
|
|
21
20
|
- [Quick Start](#quick-start)
|
|
22
21
|
- [Usage](#usage)
|
|
23
|
-
- [Salesforce CLI](#salesforce-cli)
|
|
24
|
-
- [SFDX Hardis](#sfdx-hardis)
|
|
25
|
-
- [What This Plugin Fixes and Adds](#what-this-plugin-fixes-and-adds)
|
|
26
22
|
- [Command Reference](#command-reference)
|
|
27
|
-
- [sf acc-transformer transform](#sf-acc-transformer-transform)
|
|
28
23
|
- [Coverage Report Formats](#coverage-report-formats)
|
|
29
24
|
- [CI/CD Integration](#cicd-integration)
|
|
30
|
-
- [Codecov](#codecov)
|
|
31
|
-
- [SonarQube](#sonarqube)
|
|
32
|
-
- [GitHub Actions](#github-actions)
|
|
33
|
-
- [GitLab CI](#gitlab-ci)
|
|
34
25
|
- [Automatic Transformation (Hook)](#automatic-transformation-hook)
|
|
35
26
|
- [Troubleshooting](#troubleshooting)
|
|
36
|
-
|
|
37
|
-
- [License](#license)
|
|
27
|
+
|
|
38
28
|
</details>
|
|
39
29
|
|
|
40
30
|
## Prerequisites
|
|
@@ -52,21 +42,23 @@ sf plugins install apex-code-coverage-transformer@latest
|
|
|
52
42
|
|
|
53
43
|
## Quick Start
|
|
54
44
|
|
|
55
|
-
1. **Generate Apex code coverage (JSON)**
|
|
45
|
+
1. **Generate Apex code coverage (JSON)**
|
|
46
|
+
|
|
56
47
|
From tests:
|
|
57
48
|
|
|
58
49
|
```bash
|
|
59
50
|
sf apex run test --code-coverage --output-dir "coverage"
|
|
60
51
|
```
|
|
61
52
|
|
|
62
|
-
|
|
53
|
+
From deploy/validate:
|
|
63
54
|
|
|
64
55
|
```bash
|
|
65
56
|
sf project deploy start --coverage-formatters json --results-dir "coverage"
|
|
66
57
|
# or: sf project deploy validate --coverage-formatters json --results-dir "coverage"
|
|
67
58
|
```
|
|
68
59
|
|
|
69
|
-
2. **Transform to your target format**
|
|
60
|
+
2. **Transform to your target format**
|
|
61
|
+
|
|
70
62
|
Test output → `coverage/test-result-codecoverage.json`. Deploy output → `coverage/coverage/coverage.json`.
|
|
71
63
|
|
|
72
64
|
```bash
|
|
@@ -76,16 +68,15 @@ sf plugins install apex-code-coverage-transformer@latest
|
|
|
76
68
|
# Codecov (Cobertura)
|
|
77
69
|
sf acc-transformer transform -j "coverage/test-result-codecoverage.json" -r "coverage.xml" -f "cobertura"
|
|
78
70
|
|
|
79
|
-
# Multiple formats
|
|
71
|
+
# Multiple formats at once
|
|
80
72
|
sf acc-transformer transform -j "coverage/test-result-codecoverage.json" -f "sonar" -f "cobertura" -f "jacoco"
|
|
81
73
|
```
|
|
82
74
|
|
|
83
|
-
3. **Upload to your tool**
|
|
84
|
-
See [CI/CD Integration](#cicd-integration) for Codecov, SonarQube, GitHub Actions, and GitLab.
|
|
75
|
+
3. **Upload to your tool** — see [CI/CD Integration](#cicd-integration).
|
|
85
76
|
|
|
86
77
|
## Usage
|
|
87
78
|
|
|
88
|
-
This plugin is for Salesforce DX projects (`sfdx-project.json`).
|
|
79
|
+
This plugin is for Salesforce DX projects (`sfdx-project.json`). The Salesforce CLI coverage JSON uses Apex class names (e.g. `no-map/AccountTriggerHandler`) rather than file paths — this plugin maps those names to actual paths in your package directories and only includes files that exist there. Deploy and test coverage use different JSON structures; this plugin normalizes both. Apex from managed or unlocked packages (not in your repo) is excluded and reported with a [warning](#troubleshooting).
|
|
89
80
|
|
|
90
81
|
To run transformation automatically after deploy or test commands, use the [Hook](#automatic-transformation-hook).
|
|
91
82
|
|
|
@@ -93,7 +84,7 @@ To run transformation automatically after deploy or test commands, use the [Hook
|
|
|
93
84
|
|
|
94
85
|
> **Important:** If the generated `package.xml` only contains Apex test classes, the Salesforce CLI deploy coverage report will be empty. The deploy manifest must include actual Apex classes or triggers under test for the CLI to return coverage data in the JSON output.
|
|
95
86
|
|
|
96
|
-
###
|
|
87
|
+
### Generating coverage
|
|
97
88
|
|
|
98
89
|
**Deploy/validate** — coverage path: `coverage/coverage/coverage.json`
|
|
99
90
|
|
|
@@ -108,31 +99,21 @@ sf apex run test --code-coverage --output-dir "coverage"
|
|
|
108
99
|
sf apex get test --test-run-id <id> --code-coverage --output-dir "coverage"
|
|
109
100
|
```
|
|
110
101
|
|
|
111
|
-
|
|
102
|
+
**SFDX Hardis** — coverage path: `hardis-report/apex-coverage-results.json`
|
|
112
103
|
|
|
113
104
|
Works with [sfdx-hardis](https://github.com/hardisgroupcom/sfdx-hardis):
|
|
114
105
|
|
|
115
|
-
- `sf hardis project deploy smart` (
|
|
106
|
+
- `sf hardis project deploy smart` (requires `COVERAGE_FORMATTER_JSON=true`)
|
|
116
107
|
- `sf hardis org test apex`
|
|
117
108
|
|
|
118
|
-
Coverage file: `hardis-report/apex-coverage-results.json`.
|
|
119
|
-
|
|
120
|
-
## What This Plugin Fixes and Adds
|
|
121
|
-
|
|
122
|
-
- **File mapping** — Maps names like `no-map/AccountTriggerHandler` to paths like `force-app/main/default/classes/AccountTriggerHandler.cls`.
|
|
123
|
-
- **Normalization** — Aligns deploy and test coverage structures so external tools can consume them.
|
|
124
|
-
- **Extra formats** — Outputs Sonar, Cobertura, JaCoCo, LCOV, Clover, and more (see [Coverage Report Formats](#coverage-report-formats)).
|
|
125
|
-
- **Deploy-coverage fixes** — Corrects known CLI issues (e.g. out-of-range covered lines, wrong line counts) by re-numbering covered lines in deploy reports. Uncovered lines are already correct. This workaround will be removed in a future **breaking** release once Salesforce fixes the API; see [forcedotcom/salesforcedx-vscode#5511](https://github.com/forcedotcom/salesforcedx-vscode/issues/5511) and [forcedotcom/cli#1568](https://github.com/forcedotcom/cli/issues/1568). Test-command coverage is unaffected.
|
|
126
|
-
|
|
127
109
|
## Command Reference
|
|
128
110
|
|
|
129
|
-
Single command: `sf acc-transformer transform`.
|
|
130
|
-
|
|
131
111
|
### sf acc-transformer transform
|
|
132
112
|
|
|
133
113
|
```
|
|
134
114
|
USAGE
|
|
135
|
-
$ sf acc-transformer transform -j <value> [-r <value>] [-f <value>] [-i <value>]
|
|
115
|
+
$ sf acc-transformer transform -j <value> [-r <value>] [-f <value>] [-i <value>]
|
|
116
|
+
[--min-coverage <value>] [--max-annotations <value>] [--json]
|
|
136
117
|
|
|
137
118
|
FLAGS
|
|
138
119
|
-j, --coverage-json=<value> Path to the code coverage JSON from deploy or test.
|
|
@@ -140,6 +121,10 @@ FLAGS
|
|
|
140
121
|
-f, --format=<value> Output format (repeat for multiple). Default: sonar.
|
|
141
122
|
Multiple formats append to filename, e.g. coverage-sonar.xml.
|
|
142
123
|
-i, --ignore-package-directory=<value> Package directory to ignore (as in sfdx-project.json). Repeatable.
|
|
124
|
+
--min-coverage=<value> Minimum required line coverage percentage (0–100). Exits with an error
|
|
125
|
+
if overall coverage is below this value. Reports are written first.
|
|
126
|
+
--max-annotations=<value> Maximum ::warning annotations emitted by --format github-actions.
|
|
127
|
+
Default: 50. Overflow is summarised in a ::notice line.
|
|
143
128
|
|
|
144
129
|
GLOBAL FLAGS
|
|
145
130
|
--json Output as JSON.
|
|
@@ -166,53 +151,58 @@ Use `-f` / `--format` to choose the output format. Multiple `-f` values produce
|
|
|
166
151
|
|
|
167
152
|
## CI/CD Integration
|
|
168
153
|
|
|
169
|
-
###
|
|
154
|
+
### Shared setup (GitHub Actions)
|
|
170
155
|
|
|
171
|
-
|
|
156
|
+
All GitHub Actions examples below assume these steps run first:
|
|
172
157
|
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
158
|
+
```yaml
|
|
159
|
+
- uses: actions/checkout@v4
|
|
160
|
+
- name: Install Salesforce CLI
|
|
161
|
+
run: npm install -g @salesforce/cli
|
|
162
|
+
- name: Install Coverage Transformer Plugin
|
|
163
|
+
run: echo y | sf plugins install apex-code-coverage-transformer
|
|
164
|
+
- name: Authenticate to Salesforce
|
|
165
|
+
run: sf org login sfdx-url --sfdx-url-file ${{ secrets.SFDX_AUTH_URL }} --alias ci-org
|
|
166
|
+
- name: Run Apex Tests
|
|
167
|
+
run: sf apex run test --code-coverage --output-dir coverage --target-org ci-org
|
|
177
168
|
```
|
|
178
169
|
|
|
179
|
-
|
|
170
|
+
### Codecov
|
|
180
171
|
|
|
181
172
|
```yaml
|
|
182
|
-
name:
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
run: npm install -g @salesforce/cli
|
|
191
|
-
- name: Install Coverage Transformer Plugin
|
|
192
|
-
run: echo y | sf plugins install apex-code-coverage-transformer
|
|
193
|
-
- name: Authenticate to Salesforce
|
|
194
|
-
run: sf org login sfdx-url --sfdx-url-file ${{ secrets.SFDX_AUTH_URL }} --alias ci-org
|
|
195
|
-
- name: Run Apex Tests
|
|
196
|
-
run: sf apex run test --code-coverage --output-dir coverage --target-org ci-org
|
|
197
|
-
- name: Transform Coverage to Cobertura
|
|
198
|
-
run: sf acc-transformer transform -j "coverage/test-result-codecoverage.json" -r "coverage.xml" -f "cobertura"
|
|
199
|
-
- name: Upload to Codecov
|
|
200
|
-
uses: codecov/codecov-action@v4
|
|
201
|
-
with:
|
|
202
|
-
files: ./coverage.xml
|
|
203
|
-
flags: apex
|
|
204
|
-
token: ${{ secrets.CODECOV_TOKEN }}
|
|
173
|
+
- name: Transform Coverage to Cobertura
|
|
174
|
+
run: sf acc-transformer transform -j "coverage/test-result-codecoverage.json" -r "coverage.xml" -f "cobertura"
|
|
175
|
+
- name: Upload to Codecov
|
|
176
|
+
uses: codecov/codecov-action@v4
|
|
177
|
+
with:
|
|
178
|
+
files: ./coverage.xml
|
|
179
|
+
flags: apex
|
|
180
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
205
181
|
```
|
|
206
182
|
|
|
207
|
-
### SonarQube
|
|
183
|
+
### SonarQube / SonarCloud
|
|
208
184
|
|
|
209
|
-
|
|
185
|
+
```yaml
|
|
186
|
+
- name: Transform Coverage to Sonar Format
|
|
187
|
+
run: sf acc-transformer transform -j "coverage/test-result-codecoverage.json" -r "coverage.xml" -f "sonar"
|
|
188
|
+
- name: SonarCloud Scan
|
|
189
|
+
uses: SonarSource/sonarcloud-github-action@master
|
|
190
|
+
env:
|
|
191
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
192
|
+
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
|
193
|
+
with:
|
|
194
|
+
args: >
|
|
195
|
+
-Dsonar.projectKey=your-project-key
|
|
196
|
+
-Dsonar.organization=your-org
|
|
197
|
+
-Dsonar.sources=force-app
|
|
198
|
+
-Dsonar.tests=force-app
|
|
199
|
+
-Dsonar.test.inclusions=**/*Test.cls
|
|
200
|
+
-Dsonar.coverageReportPaths=coverage.xml
|
|
201
|
+
```
|
|
210
202
|
|
|
211
|
-
|
|
203
|
+
For a self-hosted scanner:
|
|
212
204
|
|
|
213
205
|
```bash
|
|
214
|
-
sf apex run test --code-coverage --output-dir "coverage"
|
|
215
|
-
sf acc-transformer transform -j "coverage/test-result-codecoverage.json" -r "coverage.xml" -f "sonar"
|
|
216
206
|
sonar-scanner \
|
|
217
207
|
-Dsonar.projectKey=your-project-key \
|
|
218
208
|
-Dsonar.sources=force-app \
|
|
@@ -223,87 +213,13 @@ sonar-scanner \
|
|
|
223
213
|
-Dsonar.login=$SONAR_TOKEN
|
|
224
214
|
```
|
|
225
215
|
|
|
226
|
-
**SonarCloud GitHub Action:**
|
|
227
|
-
|
|
228
|
-
```yaml
|
|
229
|
-
name: SonarCloud Analysis
|
|
230
|
-
on: [push, pull_request]
|
|
231
|
-
jobs:
|
|
232
|
-
sonarcloud:
|
|
233
|
-
runs-on: ubuntu-latest
|
|
234
|
-
steps:
|
|
235
|
-
- uses: actions/checkout@v4
|
|
236
|
-
with:
|
|
237
|
-
fetch-depth: 0
|
|
238
|
-
- name: Install Salesforce CLI
|
|
239
|
-
run: npm install -g @salesforce/cli
|
|
240
|
-
- name: Install Coverage Transformer Plugin
|
|
241
|
-
run: echo y | sf plugins install apex-code-coverage-transformer
|
|
242
|
-
- name: Authenticate to Salesforce
|
|
243
|
-
run: sf org login sfdx-url --sfdx-url-file ${{ secrets.SFDX_AUTH_URL }} --alias ci-org
|
|
244
|
-
- name: Run Apex Tests
|
|
245
|
-
run: sf apex run test --code-coverage --output-dir coverage --target-org ci-org
|
|
246
|
-
- name: Transform Coverage to Sonar Format
|
|
247
|
-
run: sf acc-transformer transform -j "coverage/test-result-codecoverage.json" -r "coverage.xml" -f "sonar"
|
|
248
|
-
- name: SonarCloud Scan
|
|
249
|
-
uses: SonarSource/sonarcloud-github-action@master
|
|
250
|
-
env:
|
|
251
|
-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
252
|
-
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
|
253
|
-
with:
|
|
254
|
-
args: >
|
|
255
|
-
-Dsonar.projectKey=your-project-key
|
|
256
|
-
-Dsonar.organization=your-org
|
|
257
|
-
-Dsonar.sources=force-app
|
|
258
|
-
-Dsonar.tests=force-app
|
|
259
|
-
-Dsonar.test.inclusions=**/*Test.cls
|
|
260
|
-
-Dsonar.coverageReportPaths=coverage.xml
|
|
261
|
-
```
|
|
262
|
-
|
|
263
216
|
### GitHub Actions
|
|
264
217
|
|
|
265
|
-
Use Cobertura (or LCOV) with a coverage summary action:
|
|
266
|
-
|
|
267
|
-
```yaml
|
|
268
|
-
name: Salesforce CI with Coverage Report
|
|
269
|
-
on: [push, pull_request]
|
|
270
|
-
jobs:
|
|
271
|
-
test:
|
|
272
|
-
runs-on: ubuntu-latest
|
|
273
|
-
steps:
|
|
274
|
-
- uses: actions/checkout@v4
|
|
275
|
-
- name: Install Salesforce CLI
|
|
276
|
-
run: npm install -g @salesforce/cli
|
|
277
|
-
- name: Install Coverage Transformer Plugin
|
|
278
|
-
run: echo y | sf plugins install apex-code-coverage-transformer
|
|
279
|
-
- name: Authenticate to Salesforce
|
|
280
|
-
run: sf org login sfdx-url --sfdx-url-file ${{ secrets.SFDX_AUTH_URL }} --alias ci-org
|
|
281
|
-
- name: Run Apex Tests
|
|
282
|
-
run: sf apex run test --code-coverage --output-dir coverage --target-org ci-org
|
|
283
|
-
- name: Transform Coverage to Cobertura
|
|
284
|
-
run: sf acc-transformer transform -j "coverage/test-result-codecoverage.json" -r "coverage.xml" -f "cobertura"
|
|
285
|
-
- name: Code Coverage Report
|
|
286
|
-
uses: irongut/CodeCoverageSummary@v1.3.0
|
|
287
|
-
with:
|
|
288
|
-
filename: coverage.xml
|
|
289
|
-
badge: true
|
|
290
|
-
format: markdown
|
|
291
|
-
output: both
|
|
292
|
-
- name: Add Coverage PR Comment
|
|
293
|
-
uses: marocchino/sticky-pull-request-comment@v2
|
|
294
|
-
if: github.event_name == 'pull_request'
|
|
295
|
-
with:
|
|
296
|
-
recreate: true
|
|
297
|
-
path: code-coverage-results.md
|
|
298
|
-
```
|
|
299
|
-
|
|
300
218
|
#### Markdown PR comments (built-in)
|
|
301
219
|
|
|
302
|
-
Skip
|
|
220
|
+
Skip third-party summary actions by using the built-in `markdown` format:
|
|
303
221
|
|
|
304
222
|
```yaml
|
|
305
|
-
- name: Run Apex Tests
|
|
306
|
-
run: sf apex run test --code-coverage --output-dir coverage --target-org ci-org
|
|
307
223
|
- name: Transform Coverage to Markdown
|
|
308
224
|
run: sf acc-transformer transform -j "coverage/test-result-codecoverage.json" -r "coverage.md" -f "markdown"
|
|
309
225
|
- name: Add coverage to job summary
|
|
@@ -316,24 +232,20 @@ Skip the third-party summary action by using the built-in `markdown` format. The
|
|
|
316
232
|
path: coverage.md
|
|
317
233
|
```
|
|
318
234
|
|
|
319
|
-
The Markdown report includes an overall summary block, a per-package-directory table, and a file-level table sorted with
|
|
235
|
+
The Markdown report includes an overall summary block, a per-package-directory table, and a file-level table sorted with lowest coverage first so reviewers see the most actionable rows at the top.
|
|
320
236
|
|
|
321
|
-
####
|
|
237
|
+
#### Inline annotations
|
|
322
238
|
|
|
323
|
-
The `github-actions` format emits one [`::warning`](https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions)
|
|
239
|
+
The `github-actions` format emits one [`::warning`](https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions) per uncovered Apex line, plus a `::notice` summary. When a step prints the file to stdout, the runner renders annotations inline on the PR diff and on the workflow run page.
|
|
324
240
|
|
|
325
241
|
```yaml
|
|
326
|
-
- name: Run Apex Tests
|
|
327
|
-
run: sf apex run test --code-coverage --output-dir coverage --target-org ci-org
|
|
328
242
|
- name: Transform Coverage to GitHub Actions Annotations
|
|
329
243
|
run: sf acc-transformer transform -j "coverage/test-result-codecoverage.json" -r "coverage.txt" -f "github-actions"
|
|
330
244
|
- name: Emit coverage annotations
|
|
331
245
|
run: cat coverage.txt
|
|
332
246
|
```
|
|
333
247
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
You can produce both at once with multiple `-f` flags:
|
|
248
|
+
Pairs with [`sf-cat`](https://www.npmjs.com/package/sf-cat) for code quality annotations on the same diff. Produce both at once:
|
|
337
249
|
|
|
338
250
|
```bash
|
|
339
251
|
sf acc-transformer transform -j "coverage/test-result-codecoverage.json" -f "markdown" -f "github-actions" -f "sonar"
|
|
@@ -341,8 +253,6 @@ sf acc-transformer transform -j "coverage/test-result-codecoverage.json" -f "mar
|
|
|
341
253
|
|
|
342
254
|
### GitLab CI
|
|
343
255
|
|
|
344
|
-
Cobertura for coverage in the UI:
|
|
345
|
-
|
|
346
256
|
```yaml
|
|
347
257
|
stages:
|
|
348
258
|
- test
|
|
@@ -357,8 +267,6 @@ apex-tests:
|
|
|
357
267
|
script:
|
|
358
268
|
- sf apex run test --code-coverage --output-dir coverage --target-org ci-org
|
|
359
269
|
- sf acc-transformer transform -j "coverage/test-result-codecoverage.json" -r "coverage.xml" -f "cobertura"
|
|
360
|
-
# Parse the coverage report and output the TOTAL coverage percentage (2 decimal places)
|
|
361
|
-
# to the job log. GitLab reads this line using the `coverage` keyword regex to track pipeline coverage.
|
|
362
270
|
- |
|
|
363
271
|
COVERAGE_FILE="coverage.xml"
|
|
364
272
|
if [ -s "$COVERAGE_FILE" ]; then
|
|
@@ -402,12 +310,20 @@ Sample configs: [Salesforce CLI](https://raw.githubusercontent.com/mcarvin8/apex
|
|
|
402
310
|
|
|
403
311
|
## Troubleshooting
|
|
404
312
|
|
|
405
|
-
**File not in package directory** — File is omitted from the report
|
|
313
|
+
**File not in package directory** — File is omitted from the report:
|
|
406
314
|
|
|
407
315
|
```
|
|
408
316
|
Warning: The file name AccountTrigger was not found in any package directory.
|
|
409
317
|
```
|
|
410
318
|
|
|
319
|
+
**Duplicate Apex file across package directories** — Two packages contain a file with the same name (e.g. `AccountHelper.cls` in both `force-app` and `package2`). The first one found is used; the second is ignored:
|
|
320
|
+
|
|
321
|
+
```
|
|
322
|
+
Warning: Duplicate Apex file "AccountHelper.cls" found in multiple package directories. Using "force-app/main/default/classes/AccountHelper.cls"; ignoring "package2/main/default/classes/AccountHelper.cls".
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
Resolve by renaming one of the files or using `--ignore-package-directory` to exclude the package whose version should not be included.
|
|
326
|
+
|
|
411
327
|
**No files matched** — Report will be empty:
|
|
412
328
|
|
|
413
329
|
```
|
|
@@ -432,9 +348,7 @@ Error (1): sfdx-project.json not found in any parent directory.
|
|
|
432
348
|
Error (1): ENOENT: no such file or directory: {packageDir}
|
|
433
349
|
```
|
|
434
350
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
Contributions are welcome. See [CONTRIBUTING.md](https://github.com/mcarvin8/apex-code-coverage-transformer/blob/main/CONTRIBUTING.md).
|
|
351
|
+
**Deploy coverage line numbers** — The Salesforce CLI deploy coverage JSON contains known out-of-range line numbers. This plugin corrects them automatically by re-numbering covered lines; uncovered lines are unaffected. Test-command coverage is unaffected. See [forcedotcom/salesforcedx-vscode#5511](https://github.com/forcedotcom/salesforcedx-vscode/issues/5511) and [forcedotcom/cli#1568](https://github.com/forcedotcom/cli/issues/1568).
|
|
438
352
|
|
|
439
353
|
## License
|
|
440
354
|
|
|
@@ -9,6 +9,8 @@ export default class TransformerTransform extends SfCommand<TransformerTransform
|
|
|
9
9
|
'output-report': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
10
|
format: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
11
|
'ignore-package-directory': import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
'min-coverage': import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
'max-annotations': import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
14
|
};
|
|
13
15
|
run(): Promise<TransformerTransformResult>;
|
|
14
16
|
}
|
|
@@ -3,6 +3,7 @@ import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
|
|
|
3
3
|
import { Messages } from '@salesforce/core';
|
|
4
4
|
import { transformCoverageReport } from '../../transformers/coverageTransformer.js';
|
|
5
5
|
import { formatOptions } from '../../utils/constants.js';
|
|
6
|
+
import { DEFAULT_MAX_ANNOTATIONS } from '../../transformers/generators/generateGitHubActions.js';
|
|
6
7
|
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
|
|
7
8
|
const messages = Messages.loadMessages('apex-code-coverage-transformer', 'transformer.transform');
|
|
8
9
|
export default class TransformerTransform extends SfCommand {
|
|
@@ -34,20 +35,35 @@ export default class TransformerTransform extends SfCommand {
|
|
|
34
35
|
required: false,
|
|
35
36
|
multiple: true,
|
|
36
37
|
}),
|
|
38
|
+
// eslint-disable-next-line sf-plugin/flag-min-max-default
|
|
39
|
+
'min-coverage': Flags.integer({
|
|
40
|
+
summary: messages.getMessage('flags.min-coverage.summary'),
|
|
41
|
+
required: false,
|
|
42
|
+
min: 0,
|
|
43
|
+
max: 100,
|
|
44
|
+
}),
|
|
45
|
+
'max-annotations': Flags.integer({
|
|
46
|
+
summary: messages.getMessage('flags.max-annotations.summary'),
|
|
47
|
+
required: false,
|
|
48
|
+
min: 1,
|
|
49
|
+
default: DEFAULT_MAX_ANNOTATIONS,
|
|
50
|
+
}),
|
|
37
51
|
};
|
|
38
52
|
async run() {
|
|
39
53
|
const { flags } = await this.parse(TransformerTransform);
|
|
40
54
|
const warnings = [];
|
|
41
|
-
const result = await transformCoverageReport(flags['coverage-json'], flags['output-report'], flags['format'] ?? ['sonar'], flags['ignore-package-directory'] ?? []
|
|
55
|
+
const result = await transformCoverageReport(flags['coverage-json'], flags['output-report'], flags['format'] ?? ['sonar'], flags['ignore-package-directory'] ?? [], {
|
|
56
|
+
minCoverage: flags['min-coverage'],
|
|
57
|
+
maxAnnotations: flags['max-annotations'],
|
|
58
|
+
});
|
|
42
59
|
warnings.push(...result.warnings);
|
|
43
|
-
|
|
60
|
+
this.log(`The coverage report has been written to: ${result.finalPaths.join(', ')}`);
|
|
44
61
|
if (warnings.length > 0) {
|
|
45
62
|
warnings.forEach((warning) => {
|
|
46
63
|
this.warn(warning);
|
|
47
64
|
});
|
|
48
65
|
}
|
|
49
|
-
|
|
50
|
-
return { path: finalPath };
|
|
66
|
+
return { path: result.finalPaths };
|
|
51
67
|
}
|
|
52
68
|
}
|
|
53
69
|
//# sourceMappingURL=transform.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transform.js","sourceRoot":"","sources":["../../../src/commands/acc-transformer/transform.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,OAAO,EAAE,uBAAuB,EAAE,MAAM,2CAA2C,CAAC;AACpF,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"transform.js","sourceRoot":"","sources":["../../../src/commands/acc-transformer/transform.ts"],"names":[],"mappings":"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"}
|
|
@@ -71,9 +71,12 @@ export class CoberturaCoverageHandler extends BaseHandler {
|
|
|
71
71
|
for (const pkg of this.coverageObj.coverage.packages.package) {
|
|
72
72
|
const totalLines = pkg.classes.class.reduce((sum, cls) => sum + cls['@line-rate'] * cls.lines.line.length, 0);
|
|
73
73
|
const totalClasses = pkg.classes.class.reduce((sum, cls) => sum + cls.lines.line.length, 0);
|
|
74
|
-
pkg['@line-rate'] = parseFloat((totalLines / totalClasses).toFixed(4));
|
|
74
|
+
pkg['@line-rate'] = totalClasses > 0 ? parseFloat((totalLines / totalClasses).toFixed(4)) : 0;
|
|
75
75
|
}
|
|
76
|
-
this.coverageObj.coverage['@line-rate'] =
|
|
76
|
+
this.coverageObj.coverage['@line-rate'] =
|
|
77
|
+
this.coverageObj.coverage['@lines-valid'] > 0
|
|
78
|
+
? parseFloat((this.coverageObj.coverage['@lines-covered'] / this.coverageObj.coverage['@lines-valid']).toFixed(4))
|
|
79
|
+
: 0;
|
|
77
80
|
this.coverageObj.coverage.packages.package.sort((a, b) => a['@name'].localeCompare(b['@name']));
|
|
78
81
|
for (const pkg of this.coverageObj.coverage.packages.package) {
|
|
79
82
|
pkg.classes.class.sort((a, b) => a['@filename'].localeCompare(b['@filename']));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cobertura.js","sourceRoot":"","sources":["../../src/handlers/cobertura.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"cobertura.js","sourceRoot":"","sources":["../../src/handlers/cobertura.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
export
|
|
1
|
+
export type TransformOptions = {
|
|
2
|
+
minCoverage?: number;
|
|
3
|
+
maxAnnotations?: number;
|
|
4
|
+
};
|
|
5
|
+
export declare function transformCoverageReport(jsonFilePath: string, outputReportPath: string, formats: string[], ignoreDirs: string[], options?: TransformOptions): Promise<{
|
|
2
6
|
finalPaths: string[];
|
|
3
7
|
warnings: string[];
|
|
8
|
+
lineRate: number;
|
|
4
9
|
}>;
|
|
@@ -10,21 +10,21 @@ import { getConcurrencyThreshold } from '../utils/getConcurrencyThreshold.js';
|
|
|
10
10
|
import { checkCoverageDataType } from '../utils/setCoverageDataType.js';
|
|
11
11
|
import { mapLimit } from '../utils/mapLimit.js';
|
|
12
12
|
import { generateAndWriteReport } from './reportGenerator.js';
|
|
13
|
-
export async function transformCoverageReport(jsonFilePath, outputReportPath, formats, ignoreDirs) {
|
|
13
|
+
export async function transformCoverageReport(jsonFilePath, outputReportPath, formats, ignoreDirs, options) {
|
|
14
14
|
const warnings = [];
|
|
15
15
|
const finalPaths = [];
|
|
16
16
|
const formatAmount = formats.length;
|
|
17
|
-
let filesProcessed = 0;
|
|
18
17
|
const jsonData = await tryReadJson(jsonFilePath, warnings);
|
|
19
18
|
if (!jsonData)
|
|
20
|
-
return { finalPaths: [outputReportPath], warnings };
|
|
19
|
+
return { finalPaths: [outputReportPath], warnings, lineRate: 0 };
|
|
21
20
|
const parsedData = JSON.parse(jsonData);
|
|
22
21
|
const { repoRoot, packageDirectories } = await getPackageDirectories(ignoreDirs);
|
|
23
22
|
const handlers = createHandlers(formats);
|
|
24
23
|
const commandType = checkCoverageDataType(parsedData);
|
|
25
24
|
const concurrencyLimit = getConcurrencyThreshold();
|
|
26
25
|
// Build file path cache upfront to avoid O(n*m) directory traversals
|
|
27
|
-
const filePathCache = await buildFilePathCache(packageDirectories, repoRoot);
|
|
26
|
+
const { cache: filePathCache, warnings: cacheWarnings } = await buildFilePathCache(packageDirectories, repoRoot);
|
|
27
|
+
warnings.push(...cacheWarnings);
|
|
28
28
|
const context = {
|
|
29
29
|
handlers,
|
|
30
30
|
packageDirs: packageDirectories,
|
|
@@ -33,24 +33,30 @@ export async function transformCoverageReport(jsonFilePath, outputReportPath, fo
|
|
|
33
33
|
warnings,
|
|
34
34
|
filePathCache,
|
|
35
35
|
};
|
|
36
|
+
let processResult;
|
|
36
37
|
if (commandType === 'DeployCoverageData') {
|
|
37
|
-
|
|
38
|
+
processResult = await processDeployCoverage(parsedData, context);
|
|
38
39
|
}
|
|
39
40
|
else if (commandType === 'TestCoverageData') {
|
|
40
|
-
|
|
41
|
+
processResult = await processTestCoverage(parsedData, context);
|
|
41
42
|
}
|
|
42
43
|
else {
|
|
43
44
|
throw new Error('The provided JSON does not match a known coverage data format from the Salesforce deploy or test command.');
|
|
44
45
|
}
|
|
45
|
-
if (
|
|
46
|
+
if (processResult.processed === 0) {
|
|
46
47
|
warnings.push('None of the files listed in the coverage JSON were processed. The coverage report will be empty.');
|
|
47
48
|
}
|
|
49
|
+
const lineRate = processResult.totalLines > 0 ? processResult.coveredLines / processResult.totalLines : 0;
|
|
50
|
+
const renderOptions = options?.maxAnnotations !== undefined ? { maxAnnotations: options.maxAnnotations } : undefined;
|
|
48
51
|
for (const [format, handler] of handlers.entries()) {
|
|
49
52
|
const coverageObj = handler.finalize();
|
|
50
|
-
const finalPath = await generateAndWriteReport(outputReportPath, coverageObj, format, formatAmount);
|
|
53
|
+
const finalPath = await generateAndWriteReport(outputReportPath, coverageObj, format, formatAmount, renderOptions);
|
|
51
54
|
finalPaths.push(finalPath);
|
|
52
55
|
}
|
|
53
|
-
|
|
56
|
+
if (options?.minCoverage !== undefined && lineRate * 100 < options.minCoverage) {
|
|
57
|
+
throw new Error(`Coverage of ${(lineRate * 100).toFixed(2)}% is below the required minimum of ${options.minCoverage}%.`);
|
|
58
|
+
}
|
|
59
|
+
return { finalPaths, warnings, lineRate };
|
|
54
60
|
}
|
|
55
61
|
function hasSourceContent(result) {
|
|
56
62
|
return typeof result === 'object' && result !== null && 'sourceContent' in result;
|
|
@@ -79,8 +85,15 @@ function createHandlers(formats) {
|
|
|
79
85
|
}
|
|
80
86
|
return handlers;
|
|
81
87
|
}
|
|
88
|
+
function countLines(lines) {
|
|
89
|
+
const totalLines = Object.keys(lines).length;
|
|
90
|
+
const coveredLines = Object.values(lines).filter((v) => v === 1).length;
|
|
91
|
+
return { totalLines, coveredLines };
|
|
92
|
+
}
|
|
82
93
|
async function processDeployCoverage(data, context) {
|
|
83
94
|
let processed = 0;
|
|
95
|
+
let totalLines = 0;
|
|
96
|
+
let coveredLines = 0;
|
|
84
97
|
await mapLimit(Object.keys(data), context.concurrencyLimit, async (fileName) => {
|
|
85
98
|
const fileInfo = data[fileName];
|
|
86
99
|
const formattedName = fileName.replace(/no-map[\\/]+/, '');
|
|
@@ -93,15 +106,20 @@ async function processDeployCoverage(data, context) {
|
|
|
93
106
|
const updatedLines = hasSourceContent(setCoveredResult) ? setCoveredResult.updatedLines : setCoveredResult;
|
|
94
107
|
const sourceContent = hasSourceContent(setCoveredResult) ? setCoveredResult.sourceContent : undefined;
|
|
95
108
|
fileInfo.s = updatedLines;
|
|
109
|
+
const counts = countLines(updatedLines);
|
|
110
|
+
totalLines += counts.totalLines;
|
|
111
|
+
coveredLines += counts.coveredLines;
|
|
96
112
|
for (const handler of context.handlers.values()) {
|
|
97
113
|
handler.processFile(path, formattedName, updatedLines, sourceContent);
|
|
98
114
|
}
|
|
99
115
|
processed++;
|
|
100
116
|
});
|
|
101
|
-
return processed;
|
|
117
|
+
return { processed, totalLines, coveredLines };
|
|
102
118
|
}
|
|
103
119
|
async function processTestCoverage(data, context) {
|
|
104
120
|
let processed = 0;
|
|
121
|
+
let totalLines = 0;
|
|
122
|
+
let coveredLines = 0;
|
|
105
123
|
await mapLimit(data, context.concurrencyLimit, async (entry) => {
|
|
106
124
|
const formattedName = entry.name.replace(/no-map[\\/]+/, '');
|
|
107
125
|
const path = findFilePath(formattedName, context.filePathCache);
|
|
@@ -109,12 +127,15 @@ async function processTestCoverage(data, context) {
|
|
|
109
127
|
context.warnings.push(`The file name ${formattedName} was not found in any package directory.`);
|
|
110
128
|
return;
|
|
111
129
|
}
|
|
130
|
+
const counts = countLines(entry.lines);
|
|
131
|
+
totalLines += counts.totalLines;
|
|
132
|
+
coveredLines += counts.coveredLines;
|
|
112
133
|
const sourceContent = context.handlers.has('html') ? await readSourceFile(join(context.repoRoot, path)) : undefined;
|
|
113
134
|
for (const handler of context.handlers.values()) {
|
|
114
135
|
handler.processFile(path, formattedName, entry.lines, sourceContent);
|
|
115
136
|
}
|
|
116
137
|
processed++;
|
|
117
138
|
});
|
|
118
|
-
return processed;
|
|
139
|
+
return { processed, totalLines, coveredLines };
|
|
119
140
|
}
|
|
120
141
|
//# sourceMappingURL=coverageTransformer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coverageTransformer.js","sourceRoot":"","sources":["../../src/transformers/coverageTransformer.ts"],"names":[],"mappings":"AAAA,qCAAqC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAC1E,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,eAAe,EAA8B,MAAM,6BAA6B,CAAC;AAC1F,OAAO,EAAE,uBAAuB,EAAE,MAAM,qCAAqC,CAAC;AAC9E,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"coverageTransformer.js","sourceRoot":"","sources":["../../src/transformers/coverageTransformer.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
import { GitHubActionsCoverageObject } from '../../utils/types.js';
|
|
2
|
-
export declare
|
|
2
|
+
export declare const DEFAULT_MAX_ANNOTATIONS = 50;
|
|
3
|
+
export declare function generateGitHubActions(coverageObj: GitHubActionsCoverageObject, maxAnnotations?: number): string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export const DEFAULT_MAX_ANNOTATIONS = 50;
|
|
1
2
|
function formatPercent(lineRate) {
|
|
2
3
|
return `${(lineRate * 100).toFixed(2)}%`;
|
|
3
4
|
}
|
|
@@ -17,11 +18,17 @@ function escapeProperty(value) {
|
|
|
17
18
|
function escapeData(value) {
|
|
18
19
|
return value.replace(/%/g, '%25').replace(/\r/g, '%0D').replace(/\n/g, '%0A');
|
|
19
20
|
}
|
|
20
|
-
export function generateGitHubActions(coverageObj) {
|
|
21
|
+
export function generateGitHubActions(coverageObj, maxAnnotations = DEFAULT_MAX_ANNOTATIONS) {
|
|
21
22
|
const { summary, uncoveredLines } = coverageObj;
|
|
22
23
|
const overall = formatPercent(summary.lineRate);
|
|
23
24
|
const summaryLine = `::notice title=Apex Code Coverage::Overall coverage ${escapeData(overall)} (${summary.coveredLines}/${summary.totalLines} lines, ${summary.uncoveredLines} uncovered across ${summary.fileCount} file${summary.fileCount === 1 ? '' : 's'})`;
|
|
24
|
-
const
|
|
25
|
-
|
|
25
|
+
const visible = uncoveredLines.slice(0, maxAnnotations);
|
|
26
|
+
const truncated = uncoveredLines.length - visible.length;
|
|
27
|
+
const annotations = visible.map(({ filePath, lineNumber }) => `::warning file=${escapeProperty(filePath)},line=${lineNumber},title=Uncovered Apex line::Line ${lineNumber} is not covered by any Apex test`);
|
|
28
|
+
const lines = [summaryLine, ...annotations];
|
|
29
|
+
if (truncated > 0) {
|
|
30
|
+
lines.push(`::notice title=Apex Code Coverage::${truncated} additional uncovered line${truncated === 1 ? '' : 's'} not shown. GitHub Actions limits annotations per step.`);
|
|
31
|
+
}
|
|
32
|
+
return lines.join('\n');
|
|
26
33
|
}
|
|
27
34
|
//# sourceMappingURL=generateGitHubActions.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateGitHubActions.js","sourceRoot":"","sources":["../../../src/transformers/generators/generateGitHubActions.ts"],"names":[],"mappings":"AAEA,SAAS,aAAa,CAAC,QAAgB;IACrC,OAAO,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;AAC3C,CAAC;AAED;;;;GAIG;AACH,SAAS,cAAc,CAAC,KAAa;IACnC,OAAO,KAAK;SACT,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;SACpB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;SACrB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;SACpB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AAC1B,CAAC;AAED,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAChF,CAAC;AAED,MAAM,UAAU,qBAAqB,
|
|
1
|
+
{"version":3,"file":"generateGitHubActions.js","sourceRoot":"","sources":["../../../src/transformers/generators/generateGitHubActions.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,CAAC;AAE1C,SAAS,aAAa,CAAC,QAAgB;IACrC,OAAO,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;AAC3C,CAAC;AAED;;;;GAIG;AACH,SAAS,cAAc,CAAC,KAAa;IACnC,OAAO,KAAK;SACT,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;SACpB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;SACrB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;SACpB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AAC1B,CAAC;AAED,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAChF,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,WAAwC,EACxC,cAAc,GAAG,uBAAuB;IAExC,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,GAAG,WAAW,CAAC;IAChD,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAEhD,MAAM,WAAW,GAAG,uDAAuD,UAAU,CACnF,OAAO,CACR,KAAK,OAAO,CAAC,YAAY,IAAI,OAAO,CAAC,UAAU,WAAW,OAAO,CAAC,cAAc,qBAC/E,OAAO,CAAC,SACV,QAAQ,OAAO,CAAC,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC;IAE9C,MAAM,OAAO,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC;IACxD,MAAM,SAAS,GAAG,cAAc,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAEzD,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAC7B,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,EAAE,CAC3B,kBAAkB,cAAc,CAAC,QAAQ,CAAC,SAAS,UAAU,oCAAoC,UAAU,kCAAkC,CAChJ,CAAC;IAEF,MAAM,KAAK,GAAG,CAAC,WAAW,EAAE,GAAG,WAAW,CAAC,CAAC;IAE5C,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;QAClB,KAAK,CAAC,IAAI,CACR,sCAAsC,SAAS,6BAA6B,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,yDAAyD,CAChK,CAAC;IACJ,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
import { AnyCoverageObject } from '../utils/types.js';
|
|
2
|
-
export
|
|
2
|
+
export type ReportRenderOptions = {
|
|
3
|
+
maxAnnotations?: number;
|
|
4
|
+
};
|
|
5
|
+
export declare function generateAndWriteReport(outputPath: string, coverageObj: AnyCoverageObject, format: string, formatAmount: number, options?: ReportRenderOptions): Promise<string>;
|
|
3
6
|
export declare function getExtensionForFormat(format: string): string;
|
|
@@ -22,13 +22,13 @@ const STRING_FORMAT_RENDERERS = {
|
|
|
22
22
|
lcovonly: (obj) => generateLcov(obj),
|
|
23
23
|
html: (obj) => generateHtml(obj),
|
|
24
24
|
markdown: (obj) => generateMarkdown(obj),
|
|
25
|
-
'github-actions': (obj) => generateGitHubActions(obj),
|
|
25
|
+
'github-actions': (obj, opts) => generateGitHubActions(obj, opts?.maxAnnotations),
|
|
26
26
|
json: (obj) => JSON.stringify(obj, null, 2),
|
|
27
27
|
'json-summary': (obj) => JSON.stringify(obj, null, 2),
|
|
28
28
|
simplecov: (obj) => JSON.stringify(obj, null, 2),
|
|
29
29
|
};
|
|
30
|
-
export async function generateAndWriteReport(outputPath, coverageObj, format, formatAmount) {
|
|
31
|
-
const content = generateReportContent(coverageObj, format);
|
|
30
|
+
export async function generateAndWriteReport(outputPath, coverageObj, format, formatAmount, options) {
|
|
31
|
+
const content = generateReportContent(coverageObj, format, options);
|
|
32
32
|
const extension = HandlerRegistry.getExtension(format);
|
|
33
33
|
const base = basename(outputPath, extname(outputPath)); // e.g., 'coverage'
|
|
34
34
|
const dir = dirname(outputPath);
|
|
@@ -37,9 +37,9 @@ export async function generateAndWriteReport(outputPath, coverageObj, format, fo
|
|
|
37
37
|
await writeFile(filePath, content, 'utf-8');
|
|
38
38
|
return filePath;
|
|
39
39
|
}
|
|
40
|
-
function generateReportContent(coverageObj, format) {
|
|
40
|
+
function generateReportContent(coverageObj, format, options) {
|
|
41
41
|
const renderer = STRING_FORMAT_RENDERERS[format];
|
|
42
|
-
return renderer ? renderer(coverageObj) : generateXmlContent(coverageObj, format);
|
|
42
|
+
return renderer ? renderer(coverageObj, options) : generateXmlContent(coverageObj, format);
|
|
43
43
|
}
|
|
44
44
|
function generateXmlContent(coverageObj, format) {
|
|
45
45
|
const isHeadless = isXmlReportFormat(format);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reportGenerator.js","sourceRoot":"","sources":["../../src/transformers/reportGenerator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC7D,OAAO,UAAU,MAAM,kBAAkB,CAAC;AAS1C,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE1E,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;
|
|
1
|
+
{"version":3,"file":"reportGenerator.js","sourceRoot":"","sources":["../../src/transformers/reportGenerator.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
* @param repoRoot - Repository root path
|
|
7
|
-
* @returns Map of filename (without path) to full relative path
|
|
8
|
-
*/
|
|
9
|
-
export declare function buildFilePathCache(packageDirectories: string[], repoRoot: string): Promise<Map<string, string>>;
|
|
1
|
+
export type FilePathCacheResult = {
|
|
2
|
+
cache: Map<string, string>;
|
|
3
|
+
warnings: string[];
|
|
4
|
+
};
|
|
5
|
+
export declare function buildFilePathCache(packageDirectories: string[], repoRoot: string): Promise<FilePathCacheResult>;
|
|
@@ -2,23 +2,17 @@
|
|
|
2
2
|
import { readdir, stat } from 'node:fs/promises';
|
|
3
3
|
import { join, relative } from 'node:path';
|
|
4
4
|
import { normalizePathToUnix } from './normalizePathToUnix.js';
|
|
5
|
-
/**
|
|
6
|
-
* Build a cache mapping filenames to their full paths.
|
|
7
|
-
* This prevents recursive directory searches for every file in the coverage report.
|
|
8
|
-
*
|
|
9
|
-
* @param packageDirectories - Array of package directory paths to scan
|
|
10
|
-
* @param repoRoot - Repository root path
|
|
11
|
-
* @returns Map of filename (without path) to full relative path
|
|
12
|
-
*/
|
|
13
5
|
export async function buildFilePathCache(packageDirectories, repoRoot) {
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
6
|
+
const ctx = {
|
|
7
|
+
repoRoot,
|
|
8
|
+
extensions: ['cls', 'trigger'],
|
|
9
|
+
cache: new Map(),
|
|
10
|
+
warnings: [],
|
|
11
|
+
};
|
|
12
|
+
await Promise.all(packageDirectories.map(async (directory) => scanDirectory(directory, ctx)));
|
|
13
|
+
return { cache: ctx.cache, warnings: ctx.warnings };
|
|
20
14
|
}
|
|
21
|
-
async function scanDirectory(directory,
|
|
15
|
+
async function scanDirectory(directory, ctx) {
|
|
22
16
|
let entries;
|
|
23
17
|
try {
|
|
24
18
|
entries = await readdir(directory);
|
|
@@ -40,25 +34,27 @@ async function scanDirectory(directory, repoRoot, extensions, cache) {
|
|
|
40
34
|
continue;
|
|
41
35
|
}
|
|
42
36
|
if (stats.isDirectory()) {
|
|
43
|
-
|
|
44
|
-
subdirPromises.push(scanDirectory(fullPath, repoRoot, extensions, cache));
|
|
37
|
+
subdirPromises.push(scanDirectory(fullPath, ctx));
|
|
45
38
|
}
|
|
46
39
|
else {
|
|
47
|
-
|
|
48
|
-
const ext = entry.split('.').pop();
|
|
49
|
-
if (ext && extensions.includes(ext)) {
|
|
50
|
-
const relativePath = normalizePathToUnix(relative(repoRoot, fullPath));
|
|
51
|
-
// Store with the full filename as key (e.g., "AccountHandler.cls")
|
|
52
|
-
cache.set(entry, relativePath);
|
|
53
|
-
// Also store without extension for lookups (e.g., "AccountHandler")
|
|
54
|
-
const nameWithoutExt = entry.substring(0, entry.lastIndexOf('.'));
|
|
55
|
-
if (!cache.has(nameWithoutExt)) {
|
|
56
|
-
cache.set(nameWithoutExt, relativePath);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
40
|
+
processApexFile(entry, fullPath, ctx);
|
|
59
41
|
}
|
|
60
42
|
}
|
|
61
|
-
// Process all subdirectories in parallel
|
|
62
43
|
await Promise.all(subdirPromises);
|
|
63
44
|
}
|
|
45
|
+
function processApexFile(entry, fullPath, ctx) {
|
|
46
|
+
const ext = entry.split('.').pop();
|
|
47
|
+
if (!ext || !ctx.extensions.includes(ext))
|
|
48
|
+
return;
|
|
49
|
+
const relativePath = normalizePathToUnix(relative(ctx.repoRoot, fullPath));
|
|
50
|
+
const nameWithoutExt = entry.substring(0, entry.lastIndexOf('.'));
|
|
51
|
+
if (ctx.cache.has(entry)) {
|
|
52
|
+
ctx.warnings.push(`Duplicate Apex file "${entry}" found in multiple package directories. Using "${ctx.cache.get(entry)}"; ignoring "${relativePath}".`);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
ctx.cache.set(entry, relativePath);
|
|
56
|
+
if (!ctx.cache.has(nameWithoutExt)) {
|
|
57
|
+
ctx.cache.set(nameWithoutExt, relativePath);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
64
60
|
//# sourceMappingURL=buildFilePathCache.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buildFilePathCache.js","sourceRoot":"","sources":["../../src/utils/buildFilePathCache.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"buildFilePathCache.js","sourceRoot":"","sources":["../../src/utils/buildFilePathCache.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* Find file path using a pre-built cache (fast) or fallback to the old method.
|
|
3
|
-
*
|
|
4
|
-
* @param fileName - Name of the file to find
|
|
5
|
-
* @param filePathCache - Optional pre-built cache of filename -> path mappings
|
|
6
|
-
* @returns Normalized Unix-style relative path or undefined if not found
|
|
7
|
-
*/
|
|
8
|
-
export declare function findFilePath(fileName: string, filePathCache?: Map<string, string>): string | undefined;
|
|
1
|
+
export declare function findFilePath(fileName: string, filePathCache: Map<string, string>): string | undefined;
|
|
@@ -1,35 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
/**
|
|
3
|
-
* Find file path using a pre-built cache (fast) or fallback to the old method.
|
|
4
|
-
*
|
|
5
|
-
* @param fileName - Name of the file to find
|
|
6
|
-
* @param filePathCache - Optional pre-built cache of filename -> path mappings
|
|
7
|
-
* @returns Normalized Unix-style relative path or undefined if not found
|
|
8
|
-
*/
|
|
9
2
|
export function findFilePath(fileName, filePathCache) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
// Fallback to old behavior should never happen in practice,
|
|
14
|
-
// but keeping for backwards compatibility
|
|
15
|
-
return undefined;
|
|
16
|
-
}
|
|
17
|
-
function findFilePathFromCache(fileName, cache) {
|
|
18
|
-
// Try exact match first
|
|
19
|
-
const exactMatch = cache.get(fileName);
|
|
20
|
-
if (exactMatch) {
|
|
3
|
+
const exactMatch = filePathCache.get(fileName);
|
|
4
|
+
if (exactMatch)
|
|
21
5
|
return exactMatch;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const clsMatch = cache.get(`${fileName}.cls`);
|
|
25
|
-
if (clsMatch) {
|
|
6
|
+
const clsMatch = filePathCache.get(`${fileName}.cls`);
|
|
7
|
+
if (clsMatch)
|
|
26
8
|
return clsMatch;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const triggerMatch = cache.get(`${fileName}.trigger`);
|
|
30
|
-
if (triggerMatch) {
|
|
9
|
+
const triggerMatch = filePathCache.get(`${fileName}.trigger`);
|
|
10
|
+
if (triggerMatch)
|
|
31
11
|
return triggerMatch;
|
|
32
|
-
}
|
|
33
12
|
return undefined;
|
|
34
13
|
}
|
|
35
14
|
//# sourceMappingURL=findFilePath.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"findFilePath.js","sourceRoot":"","sources":["../../src/utils/findFilePath.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb
|
|
1
|
+
{"version":3,"file":"findFilePath.js","sourceRoot":"","sources":["../../src/utils/findFilePath.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,MAAM,UAAU,YAAY,CAAC,QAAgB,EAAE,aAAkC;IAC/E,MAAM,UAAU,GAAG,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC/C,IAAI,UAAU;QAAE,OAAO,UAAU,CAAC;IAElC,MAAM,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,QAAQ,MAAM,CAAC,CAAC;IACtD,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAE9B,MAAM,YAAY,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,QAAQ,UAAU,CAAC,CAAC;IAC9D,IAAI,YAAY;QAAE,OAAO,YAAY,CAAC;IAEtC,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
|
@@ -31,3 +31,11 @@ Output format for the coverage report.
|
|
|
31
31
|
# flags.ignore-package-directory.summary
|
|
32
32
|
|
|
33
33
|
Ignore a package directory when looking for matching files in the coverage report.
|
|
34
|
+
|
|
35
|
+
# flags.min-coverage.summary
|
|
36
|
+
|
|
37
|
+
Minimum required line coverage percentage (0–100). The command exits with an error if overall coverage is below this threshold. Reports are still written before the check.
|
|
38
|
+
|
|
39
|
+
# flags.max-annotations.summary
|
|
40
|
+
|
|
41
|
+
Maximum number of GitHub Actions ::warning annotations to emit when using --format github-actions. Defaults to 50. Annotations beyond this limit are summarised in a ::notice line.
|
package/oclif.manifest.json
CHANGED
|
@@ -79,6 +79,23 @@
|
|
|
79
79
|
"hasDynamicHelp": false,
|
|
80
80
|
"multiple": true,
|
|
81
81
|
"type": "option"
|
|
82
|
+
},
|
|
83
|
+
"min-coverage": {
|
|
84
|
+
"name": "min-coverage",
|
|
85
|
+
"required": false,
|
|
86
|
+
"summary": "Minimum required line coverage percentage (0–100). The command exits with an error if overall coverage is below this threshold. Reports are still written before the check.",
|
|
87
|
+
"hasDynamicHelp": false,
|
|
88
|
+
"multiple": false,
|
|
89
|
+
"type": "option"
|
|
90
|
+
},
|
|
91
|
+
"max-annotations": {
|
|
92
|
+
"name": "max-annotations",
|
|
93
|
+
"required": false,
|
|
94
|
+
"summary": "Maximum number of GitHub Actions ::warning annotations to emit when using --format github-actions. Defaults to 50. Annotations beyond this limit are summarised in a ::notice line.",
|
|
95
|
+
"default": 50,
|
|
96
|
+
"hasDynamicHelp": false,
|
|
97
|
+
"multiple": false,
|
|
98
|
+
"type": "option"
|
|
82
99
|
}
|
|
83
100
|
},
|
|
84
101
|
"hasDynamicHelp": false,
|
|
@@ -104,5 +121,5 @@
|
|
|
104
121
|
]
|
|
105
122
|
}
|
|
106
123
|
},
|
|
107
|
-
"version": "2.
|
|
124
|
+
"version": "2.20.0"
|
|
108
125
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apex-code-coverage-transformer",
|
|
3
3
|
"description": "Transform Salesforce Apex code coverage JSONs into other formats accepted by SonarQube, GitHub, GitLab, Azure, Bitbucket, etc.",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.20.0",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@oclif/core": "4.8.3",
|
|
7
7
|
"@salesforce/core": "8.26.3",
|