apex-code-coverage-transformer 2.13.4 → 2.14.1
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 +15 -0
- package/README.md +276 -9
- package/lib/handlers/BaseHandler.d.ts +51 -0
- package/lib/handlers/BaseHandler.js +63 -0
- package/lib/handlers/BaseHandler.js.map +1 -0
- package/lib/handlers/HandlerRegistry.d.ts +90 -0
- package/lib/handlers/HandlerRegistry.js +103 -0
- package/lib/handlers/HandlerRegistry.js.map +1 -0
- package/lib/handlers/clover.d.ts +16 -2
- package/lib/handlers/clover.js +35 -16
- package/lib/handlers/clover.js.map +1 -1
- package/lib/handlers/cobertura.d.ts +11 -2
- package/lib/handlers/cobertura.js +26 -14
- package/lib/handlers/cobertura.js.map +1 -1
- package/lib/handlers/getHandler.d.ts +24 -0
- package/lib/handlers/getHandler.js +27 -19
- package/lib/handlers/getHandler.js.map +1 -1
- package/lib/handlers/istanbulJson.d.ts +18 -2
- package/lib/handlers/istanbulJson.js +35 -2
- package/lib/handlers/istanbulJson.js.map +1 -1
- package/lib/handlers/jacoco.d.ts +18 -2
- package/lib/handlers/jacoco.js +33 -2
- package/lib/handlers/jacoco.js.map +1 -1
- package/lib/handlers/jsonSummary.d.ts +49 -0
- package/lib/handlers/jsonSummary.js +110 -0
- package/lib/handlers/jsonSummary.js.map +1 -0
- package/lib/handlers/lcov.d.ts +17 -2
- package/lib/handlers/lcov.js +29 -9
- package/lib/handlers/lcov.js.map +1 -1
- package/lib/handlers/opencover.d.ts +71 -0
- package/lib/handlers/opencover.js +169 -0
- package/lib/handlers/opencover.js.map +1 -0
- package/lib/handlers/simplecov.d.ts +50 -0
- package/lib/handlers/simplecov.js +90 -0
- package/lib/handlers/simplecov.js.map +1 -0
- package/lib/handlers/sonar.d.ts +10 -2
- package/lib/handlers/sonar.js +21 -3
- package/lib/handlers/sonar.js.map +1 -1
- package/lib/transformers/coverageTransformer.js +7 -2
- package/lib/transformers/coverageTransformer.js.map +1 -1
- package/lib/transformers/reportGenerator.d.ts +2 -2
- package/lib/transformers/reportGenerator.js +7 -8
- package/lib/transformers/reportGenerator.js.map +1 -1
- package/lib/utils/buildFilePathCache.d.ts +9 -0
- package/lib/utils/buildFilePathCache.js +64 -0
- package/lib/utils/buildFilePathCache.js.map +1 -0
- package/lib/utils/constants.d.ts +5 -0
- package/lib/utils/constants.js +8 -1
- package/lib/utils/constants.js.map +1 -1
- package/lib/utils/findFilePath.d.ts +8 -1
- package/lib/utils/findFilePath.js +26 -35
- package/lib/utils/findFilePath.js.map +1 -1
- package/lib/utils/types.d.ts +82 -4
- package/oclif.manifest.json +8 -5
- package/package.json +1 -1
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
import { BaseHandler } from './BaseHandler.js';
|
|
3
|
+
import { HandlerRegistry } from './HandlerRegistry.js';
|
|
4
|
+
/**
|
|
5
|
+
* Handler for generating JSON Summary coverage reports.
|
|
6
|
+
*
|
|
7
|
+
* This format provides a concise summary of coverage statistics,
|
|
8
|
+
* ideal for badges, PR comments, and quick analysis.
|
|
9
|
+
*
|
|
10
|
+
* **Format Origin**: Istanbul/NYC coverage tools
|
|
11
|
+
*
|
|
12
|
+
* @see https://istanbul.js.org/
|
|
13
|
+
* @see https://github.com/istanbuljs/nyc
|
|
14
|
+
*
|
|
15
|
+
* **Apex-Specific Adaptations**:
|
|
16
|
+
* - Salesforce Apex only provides line-level coverage data
|
|
17
|
+
* - `statements`, `functions`, and `branches` metrics mirror line coverage
|
|
18
|
+
* - In native JavaScript environments, these would be distinct metrics
|
|
19
|
+
* - `skipped` is always 0 (Apex doesn't report skipped lines)
|
|
20
|
+
*
|
|
21
|
+
* **Limitations**:
|
|
22
|
+
* - No branch coverage (if/else paths) - Apex doesn't provide this data
|
|
23
|
+
* - No function/method coverage separate from lines
|
|
24
|
+
* - No statement coverage distinct from line coverage
|
|
25
|
+
*
|
|
26
|
+
* Compatible with:
|
|
27
|
+
* - GitHub Actions (for badges and PR comments)
|
|
28
|
+
* - GitLab CI (for MR comments)
|
|
29
|
+
* - Custom reporting dashboards
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```json
|
|
33
|
+
* {
|
|
34
|
+
* "total": {
|
|
35
|
+
* "lines": { "total": 100, "covered": 75, "skipped": 0, "pct": 75 }
|
|
36
|
+
* },
|
|
37
|
+
* "files": {
|
|
38
|
+
* "path/to/file.cls": {
|
|
39
|
+
* "lines": { "total": 50, "covered": 40, "skipped": 0, "pct": 80 }
|
|
40
|
+
* }
|
|
41
|
+
* }
|
|
42
|
+
* }
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export class JsonSummaryCoverageHandler extends BaseHandler {
|
|
46
|
+
coverageObj;
|
|
47
|
+
constructor() {
|
|
48
|
+
super();
|
|
49
|
+
this.coverageObj = {
|
|
50
|
+
total: {
|
|
51
|
+
lines: { total: 0, covered: 0, skipped: 0, pct: 0 },
|
|
52
|
+
statements: { total: 0, covered: 0, skipped: 0, pct: 0 },
|
|
53
|
+
},
|
|
54
|
+
files: {},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
processFile(filePath, _fileName, lines) {
|
|
58
|
+
const { totalLines, coveredLines } = this.calculateCoverage(lines);
|
|
59
|
+
const pct = totalLines > 0 ? Number(((coveredLines / totalLines) * 100).toFixed(2)) : 0;
|
|
60
|
+
// NOTE: Apex only provides line coverage, so we use the same values for statements
|
|
61
|
+
// In JavaScript/TypeScript environments, statements would be a distinct metric
|
|
62
|
+
const fileCoverage = {
|
|
63
|
+
lines: {
|
|
64
|
+
total: totalLines,
|
|
65
|
+
covered: coveredLines,
|
|
66
|
+
skipped: 0, // Apex doesn't report skipped lines
|
|
67
|
+
pct,
|
|
68
|
+
},
|
|
69
|
+
statements: {
|
|
70
|
+
total: totalLines, // Using line count as statement count (Apex limitation)
|
|
71
|
+
covered: coveredLines, // Using covered lines as covered statements
|
|
72
|
+
skipped: 0,
|
|
73
|
+
pct,
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
this.coverageObj.files[filePath] = fileCoverage;
|
|
77
|
+
// Update totals
|
|
78
|
+
this.coverageObj.total.lines.total += totalLines;
|
|
79
|
+
this.coverageObj.total.lines.covered += coveredLines;
|
|
80
|
+
this.coverageObj.total.statements.total += totalLines;
|
|
81
|
+
this.coverageObj.total.statements.covered += coveredLines;
|
|
82
|
+
}
|
|
83
|
+
finalize() {
|
|
84
|
+
// Calculate total percentages
|
|
85
|
+
const totalLines = this.coverageObj.total.lines.total;
|
|
86
|
+
const totalCovered = this.coverageObj.total.lines.covered;
|
|
87
|
+
if (totalLines > 0) {
|
|
88
|
+
const pct = Number(((totalCovered / totalLines) * 100).toFixed(2));
|
|
89
|
+
this.coverageObj.total.lines.pct = pct;
|
|
90
|
+
this.coverageObj.total.statements.pct = pct;
|
|
91
|
+
}
|
|
92
|
+
// Sort files object by path for deterministic output
|
|
93
|
+
const sortedKeys = Object.keys(this.coverageObj.files).sort();
|
|
94
|
+
const sortedFiles = {};
|
|
95
|
+
for (const key of sortedKeys) {
|
|
96
|
+
sortedFiles[key] = this.coverageObj.files[key];
|
|
97
|
+
}
|
|
98
|
+
this.coverageObj.files = sortedFiles;
|
|
99
|
+
return this.coverageObj;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// Self-register this handler
|
|
103
|
+
HandlerRegistry.register({
|
|
104
|
+
name: 'json-summary',
|
|
105
|
+
description: 'JSON Summary format for badges and quick analysis',
|
|
106
|
+
fileExtension: '.json',
|
|
107
|
+
handler: () => new JsonSummaryCoverageHandler(),
|
|
108
|
+
compatibleWith: ['GitHub Actions', 'GitLab CI', 'Custom Dashboards'],
|
|
109
|
+
});
|
|
110
|
+
//# sourceMappingURL=jsonSummary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonSummary.js","sourceRoot":"","sources":["../../src/handlers/jsonSummary.ts"],"names":[],"mappings":"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"}
|
package/lib/handlers/lcov.d.ts
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { LcovCoverageObject } from '../utils/types.js';
|
|
2
|
+
import { BaseHandler } from './BaseHandler.js';
|
|
3
|
+
/**
|
|
4
|
+
* Handler for generating LCOV coverage reports.
|
|
5
|
+
*
|
|
6
|
+
* LCOV is a widely-used format for code coverage reporting,
|
|
7
|
+
* particularly common in JavaScript/Node.js projects.
|
|
8
|
+
*
|
|
9
|
+
* Compatible with:
|
|
10
|
+
* - Codecov
|
|
11
|
+
* - Coveralls
|
|
12
|
+
* - GitHub Actions
|
|
13
|
+
* - LCOV analysis tools
|
|
14
|
+
*
|
|
15
|
+
* @see http://ltp.sourceforge.net/coverage/lcov.php
|
|
16
|
+
*/
|
|
17
|
+
export declare class LcovCoverageHandler extends BaseHandler {
|
|
3
18
|
private readonly coverageObj;
|
|
4
19
|
constructor();
|
|
5
20
|
processFile(filePath: string, fileName: string, lines: Record<string, number>): void;
|
package/lib/handlers/lcov.js
CHANGED
|
@@ -1,21 +1,33 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
|
|
2
|
+
import { BaseHandler } from './BaseHandler.js';
|
|
3
|
+
import { HandlerRegistry } from './HandlerRegistry.js';
|
|
4
|
+
/**
|
|
5
|
+
* Handler for generating LCOV coverage reports.
|
|
6
|
+
*
|
|
7
|
+
* LCOV is a widely-used format for code coverage reporting,
|
|
8
|
+
* particularly common in JavaScript/Node.js projects.
|
|
9
|
+
*
|
|
10
|
+
* Compatible with:
|
|
11
|
+
* - Codecov
|
|
12
|
+
* - Coveralls
|
|
13
|
+
* - GitHub Actions
|
|
14
|
+
* - LCOV analysis tools
|
|
15
|
+
*
|
|
16
|
+
* @see http://ltp.sourceforge.net/coverage/lcov.php
|
|
17
|
+
*/
|
|
18
|
+
export class LcovCoverageHandler extends BaseHandler {
|
|
3
19
|
coverageObj;
|
|
4
20
|
constructor() {
|
|
21
|
+
super();
|
|
5
22
|
this.coverageObj = { files: [] };
|
|
6
23
|
}
|
|
7
24
|
processFile(filePath, fileName, lines) {
|
|
8
|
-
const
|
|
9
|
-
.filter((lineNumber) => lines[lineNumber] === 0)
|
|
10
|
-
.map(Number);
|
|
11
|
-
const coveredLines = Object.keys(lines)
|
|
12
|
-
.filter((lineNumber) => lines[lineNumber] === 1)
|
|
13
|
-
.map(Number);
|
|
25
|
+
const { totalLines, coveredLines } = this.calculateCoverage(lines);
|
|
14
26
|
const lcovFile = {
|
|
15
27
|
sourceFile: filePath,
|
|
16
28
|
lines: [],
|
|
17
|
-
totalLines
|
|
18
|
-
coveredLines
|
|
29
|
+
totalLines,
|
|
30
|
+
coveredLines,
|
|
19
31
|
};
|
|
20
32
|
for (const [lineNumber, isCovered] of Object.entries(lines)) {
|
|
21
33
|
lcovFile.lines.push({
|
|
@@ -32,4 +44,12 @@ export class LcovCoverageHandler {
|
|
|
32
44
|
return this.coverageObj;
|
|
33
45
|
}
|
|
34
46
|
}
|
|
47
|
+
// Self-register this handler
|
|
48
|
+
HandlerRegistry.register({
|
|
49
|
+
name: 'lcovonly',
|
|
50
|
+
description: 'LCOV format for JavaScript and C/C++ coverage',
|
|
51
|
+
fileExtension: '.info',
|
|
52
|
+
handler: () => new LcovCoverageHandler(),
|
|
53
|
+
compatibleWith: ['Codecov', 'Coveralls', 'GitHub Actions'],
|
|
54
|
+
});
|
|
35
55
|
//# sourceMappingURL=lcov.js.map
|
package/lib/handlers/lcov.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lcov.js","sourceRoot":"","sources":["../../src/handlers/lcov.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"lcov.js","sourceRoot":"","sources":["../../src/handlers/lcov.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAGb,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,mBAAoB,SAAQ,WAAW;IACjC,WAAW,CAAqB;IAEjD;QACE,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,WAAW,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IACnC,CAAC;IAEM,WAAW,CAAC,QAAgB,EAAE,QAAgB,EAAE,KAA6B;QAClF,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,GAAG,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;QAEnE,MAAM,QAAQ,GAAa;YACzB,UAAU,EAAE,QAAQ;YACpB,KAAK,EAAE,EAAE;YACT,UAAU;YACV,YAAY;SACb,CAAC;QAEF,KAAK,MAAM,CAAC,UAAU,EAAE,SAAS,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5D,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC;gBAClB,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;gBAC9B,QAAQ,EAAE,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;aAClC,CAAC,CAAC;QACL,CAAC;QAED,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACxC,CAAC;IAEM,QAAQ;QACb,IAAI,OAAO,IAAI,IAAI,CAAC,WAAW,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;YACzE,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;QAClF,CAAC;QACD,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;CACF;AAED,6BAA6B;AAC7B,eAAe,CAAC,QAAQ,CAAC;IACvB,IAAI,EAAE,UAAU;IAChB,WAAW,EAAE,+CAA+C;IAC5D,aAAa,EAAE,OAAO;IACtB,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,mBAAmB,EAAE;IACxC,cAAc,EAAE,CAAC,SAAS,EAAE,WAAW,EAAE,gBAAgB,CAAC;CAC3D,CAAC,CAAC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { OpenCoverCoverageObject } from '../utils/types.js';
|
|
2
|
+
import { BaseHandler } from './BaseHandler.js';
|
|
3
|
+
/**
|
|
4
|
+
* Handler for generating OpenCover XML coverage reports.
|
|
5
|
+
*
|
|
6
|
+
* OpenCover is a code coverage tool for .NET, but its XML format
|
|
7
|
+
* is also accepted by Azure DevOps, Visual Studio, and other tools.
|
|
8
|
+
*
|
|
9
|
+
* **Format Origin**: OpenCover (.NET coverage tool)
|
|
10
|
+
*
|
|
11
|
+
* @see https://github.com/OpenCover/opencover
|
|
12
|
+
* @see https://github.com/OpenCover/opencover/wiki/Reports
|
|
13
|
+
*
|
|
14
|
+
* **Apex-Specific Adaptations**:
|
|
15
|
+
* - Salesforce Apex only provides line-level coverage data
|
|
16
|
+
* - Each Apex class is represented as an OpenCover "Module"
|
|
17
|
+
* - Line coverage is mapped to "SequencePoints" (executable code locations)
|
|
18
|
+
* - Branch coverage is always 0 (Apex doesn't provide branch/decision coverage)
|
|
19
|
+
* - Column information (`@sc`, `@ec`) defaults to 0 (not available in Apex)
|
|
20
|
+
*
|
|
21
|
+
* **Limitations**:
|
|
22
|
+
* - No branch/decision coverage - OpenCover supports this, Apex does not
|
|
23
|
+
* - No method-level coverage granularity - treating entire class as one method
|
|
24
|
+
* - No cyclomatic complexity metrics
|
|
25
|
+
* - No column-level positioning data
|
|
26
|
+
*
|
|
27
|
+
* **Structure Mapping**:
|
|
28
|
+
* - Apex Class → OpenCover Module/Class
|
|
29
|
+
* - Apex Class → OpenCover Method (single method per class)
|
|
30
|
+
* - Apex Lines → OpenCover SequencePoints
|
|
31
|
+
*
|
|
32
|
+
* Compatible with:
|
|
33
|
+
* - Azure DevOps
|
|
34
|
+
* - Visual Studio
|
|
35
|
+
* - Codecov
|
|
36
|
+
* - JetBrains tools (ReSharper, Rider)
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```xml
|
|
40
|
+
* <CoverageSession>
|
|
41
|
+
* <Summary numSequencePoints="100" visitedSequencePoints="75" />
|
|
42
|
+
* <Modules>
|
|
43
|
+
* <Module>
|
|
44
|
+
* <Files>
|
|
45
|
+
* <File uid="1" fullPath="path/to/file.cls" />
|
|
46
|
+
* </Files>
|
|
47
|
+
* <Classes>
|
|
48
|
+
* <Class fullName="ClassName">
|
|
49
|
+
* <Methods>
|
|
50
|
+
* <Method name="MethodName">
|
|
51
|
+
* <SequencePoints>
|
|
52
|
+
* <SequencePoint vc="1" sl="1" />
|
|
53
|
+
* </SequencePoints>
|
|
54
|
+
* </Method>
|
|
55
|
+
* </Methods>
|
|
56
|
+
* </Class>
|
|
57
|
+
* </Classes>
|
|
58
|
+
* </Module>
|
|
59
|
+
* </Modules>
|
|
60
|
+
* </CoverageSession>
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export declare class OpenCoverCoverageHandler extends BaseHandler {
|
|
64
|
+
private readonly coverageObj;
|
|
65
|
+
private readonly module;
|
|
66
|
+
private fileIdCounter;
|
|
67
|
+
private filePathToId;
|
|
68
|
+
constructor();
|
|
69
|
+
processFile(filePath: string, fileName: string, lines: Record<string, number>): void;
|
|
70
|
+
finalize(): OpenCoverCoverageObject;
|
|
71
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
import { BaseHandler } from './BaseHandler.js';
|
|
3
|
+
import { HandlerRegistry } from './HandlerRegistry.js';
|
|
4
|
+
/**
|
|
5
|
+
* Handler for generating OpenCover XML coverage reports.
|
|
6
|
+
*
|
|
7
|
+
* OpenCover is a code coverage tool for .NET, but its XML format
|
|
8
|
+
* is also accepted by Azure DevOps, Visual Studio, and other tools.
|
|
9
|
+
*
|
|
10
|
+
* **Format Origin**: OpenCover (.NET coverage tool)
|
|
11
|
+
*
|
|
12
|
+
* @see https://github.com/OpenCover/opencover
|
|
13
|
+
* @see https://github.com/OpenCover/opencover/wiki/Reports
|
|
14
|
+
*
|
|
15
|
+
* **Apex-Specific Adaptations**:
|
|
16
|
+
* - Salesforce Apex only provides line-level coverage data
|
|
17
|
+
* - Each Apex class is represented as an OpenCover "Module"
|
|
18
|
+
* - Line coverage is mapped to "SequencePoints" (executable code locations)
|
|
19
|
+
* - Branch coverage is always 0 (Apex doesn't provide branch/decision coverage)
|
|
20
|
+
* - Column information (`@sc`, `@ec`) defaults to 0 (not available in Apex)
|
|
21
|
+
*
|
|
22
|
+
* **Limitations**:
|
|
23
|
+
* - No branch/decision coverage - OpenCover supports this, Apex does not
|
|
24
|
+
* - No method-level coverage granularity - treating entire class as one method
|
|
25
|
+
* - No cyclomatic complexity metrics
|
|
26
|
+
* - No column-level positioning data
|
|
27
|
+
*
|
|
28
|
+
* **Structure Mapping**:
|
|
29
|
+
* - Apex Class → OpenCover Module/Class
|
|
30
|
+
* - Apex Class → OpenCover Method (single method per class)
|
|
31
|
+
* - Apex Lines → OpenCover SequencePoints
|
|
32
|
+
*
|
|
33
|
+
* Compatible with:
|
|
34
|
+
* - Azure DevOps
|
|
35
|
+
* - Visual Studio
|
|
36
|
+
* - Codecov
|
|
37
|
+
* - JetBrains tools (ReSharper, Rider)
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```xml
|
|
41
|
+
* <CoverageSession>
|
|
42
|
+
* <Summary numSequencePoints="100" visitedSequencePoints="75" />
|
|
43
|
+
* <Modules>
|
|
44
|
+
* <Module>
|
|
45
|
+
* <Files>
|
|
46
|
+
* <File uid="1" fullPath="path/to/file.cls" />
|
|
47
|
+
* </Files>
|
|
48
|
+
* <Classes>
|
|
49
|
+
* <Class fullName="ClassName">
|
|
50
|
+
* <Methods>
|
|
51
|
+
* <Method name="MethodName">
|
|
52
|
+
* <SequencePoints>
|
|
53
|
+
* <SequencePoint vc="1" sl="1" />
|
|
54
|
+
* </SequencePoints>
|
|
55
|
+
* </Method>
|
|
56
|
+
* </Methods>
|
|
57
|
+
* </Class>
|
|
58
|
+
* </Classes>
|
|
59
|
+
* </Module>
|
|
60
|
+
* </Modules>
|
|
61
|
+
* </CoverageSession>
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export class OpenCoverCoverageHandler extends BaseHandler {
|
|
65
|
+
coverageObj;
|
|
66
|
+
module;
|
|
67
|
+
fileIdCounter = 1;
|
|
68
|
+
filePathToId = new Map();
|
|
69
|
+
constructor() {
|
|
70
|
+
super();
|
|
71
|
+
this.module = {
|
|
72
|
+
'@hash': 'apex-module',
|
|
73
|
+
Files: { File: [] },
|
|
74
|
+
Classes: { Class: [] },
|
|
75
|
+
};
|
|
76
|
+
this.coverageObj = {
|
|
77
|
+
CoverageSession: {
|
|
78
|
+
Summary: {
|
|
79
|
+
'@numSequencePoints': 0,
|
|
80
|
+
'@visitedSequencePoints': 0,
|
|
81
|
+
'@numBranchPoints': 0,
|
|
82
|
+
'@visitedBranchPoints': 0,
|
|
83
|
+
'@sequenceCoverage': 0,
|
|
84
|
+
'@branchCoverage': 0,
|
|
85
|
+
},
|
|
86
|
+
Modules: {
|
|
87
|
+
Module: [this.module],
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
processFile(filePath, fileName, lines) {
|
|
93
|
+
// Register file if not already registered
|
|
94
|
+
if (!this.filePathToId.has(filePath)) {
|
|
95
|
+
const fileId = this.fileIdCounter++;
|
|
96
|
+
this.filePathToId.set(filePath, fileId);
|
|
97
|
+
const fileObj = {
|
|
98
|
+
'@uid': fileId,
|
|
99
|
+
'@fullPath': filePath,
|
|
100
|
+
};
|
|
101
|
+
this.module.Files.File.push(fileObj);
|
|
102
|
+
}
|
|
103
|
+
const { totalLines, coveredLines } = this.calculateCoverage(lines);
|
|
104
|
+
// Create sequence points for each line
|
|
105
|
+
// In OpenCover, a SequencePoint represents an executable statement location
|
|
106
|
+
// We map each Apex line to a SequencePoint
|
|
107
|
+
const sequencePoints = [];
|
|
108
|
+
for (const [lineNumber, hits] of Object.entries(lines)) {
|
|
109
|
+
sequencePoints.push({
|
|
110
|
+
'@vc': hits, // visit count (number of times this line was executed)
|
|
111
|
+
'@sl': Number(lineNumber), // start line
|
|
112
|
+
'@sc': 0, // start column (not available in Apex coverage data)
|
|
113
|
+
'@el': Number(lineNumber), // end line (same as start for line-level coverage)
|
|
114
|
+
'@ec': 0, // end column (not available in Apex coverage data)
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
// Create a method for this file
|
|
118
|
+
// NOTE: Apex classes are treated as a single method since we don't have
|
|
119
|
+
// method-level coverage granularity from Salesforce
|
|
120
|
+
const method = {
|
|
121
|
+
'@name': fileName,
|
|
122
|
+
'@isConstructor': false,
|
|
123
|
+
'@isStatic': false,
|
|
124
|
+
SequencePoints: {
|
|
125
|
+
SequencePoint: sequencePoints,
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
// Create a class for this file
|
|
129
|
+
const classObj = {
|
|
130
|
+
'@fullName': fileName,
|
|
131
|
+
Methods: {
|
|
132
|
+
Method: [method],
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
this.module.Classes.Class.push(classObj);
|
|
136
|
+
// Update summary statistics
|
|
137
|
+
this.coverageObj.CoverageSession.Summary['@numSequencePoints'] += totalLines;
|
|
138
|
+
this.coverageObj.CoverageSession.Summary['@visitedSequencePoints'] += coveredLines;
|
|
139
|
+
}
|
|
140
|
+
finalize() {
|
|
141
|
+
const summary = this.coverageObj.CoverageSession.Summary;
|
|
142
|
+
// Calculate sequence coverage percentage
|
|
143
|
+
if (summary['@numSequencePoints'] > 0) {
|
|
144
|
+
const coverage = (summary['@visitedSequencePoints'] / summary['@numSequencePoints']) * 100;
|
|
145
|
+
summary['@sequenceCoverage'] = Number(coverage.toFixed(2));
|
|
146
|
+
}
|
|
147
|
+
// Branch coverage is always 0 for Apex (no branch/decision coverage data available)
|
|
148
|
+
// In .NET environments, this would track if/else branches, switch cases, etc.
|
|
149
|
+
summary['@branchCoverage'] = 0;
|
|
150
|
+
// Sort classes by name for consistent output
|
|
151
|
+
this.module.Classes.Class.sort((a, b) => a['@fullName'].localeCompare(b['@fullName']));
|
|
152
|
+
// Sort files by path and reassign UIDs sequentially for deterministic output
|
|
153
|
+
this.module.Files.File.sort((a, b) => a['@fullPath'].localeCompare(b['@fullPath']));
|
|
154
|
+
// Reassign UIDs based on sorted order
|
|
155
|
+
for (let i = 0; i < this.module.Files.File.length; i++) {
|
|
156
|
+
this.module.Files.File[i]['@uid'] = i + 1;
|
|
157
|
+
}
|
|
158
|
+
return this.coverageObj;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// Self-register this handler
|
|
162
|
+
HandlerRegistry.register({
|
|
163
|
+
name: 'opencover',
|
|
164
|
+
description: 'OpenCover XML format for .NET and Azure DevOps',
|
|
165
|
+
fileExtension: '.xml',
|
|
166
|
+
handler: () => new OpenCoverCoverageHandler(),
|
|
167
|
+
compatibleWith: ['Azure DevOps', 'Visual Studio', 'Codecov', 'JetBrains Tools'],
|
|
168
|
+
});
|
|
169
|
+
//# sourceMappingURL=opencover.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opencover.js","sourceRoot":"","sources":["../../src/handlers/opencover.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { SimpleCovCoverageObject } from '../utils/types.js';
|
|
2
|
+
import { BaseHandler } from './BaseHandler.js';
|
|
3
|
+
/**
|
|
4
|
+
* Handler for generating SimpleCov JSON coverage reports.
|
|
5
|
+
*
|
|
6
|
+
* SimpleCov is a popular Ruby code coverage tool. This format is also
|
|
7
|
+
* accepted by Codecov and other coverage aggregation tools.
|
|
8
|
+
*
|
|
9
|
+
* **Format Origin**: SimpleCov (Ruby coverage tool)
|
|
10
|
+
*
|
|
11
|
+
* @see https://github.com/simplecov-ruby/simplecov
|
|
12
|
+
* @see https://github.com/vicentllongo/simplecov-json
|
|
13
|
+
* @see https://docs.codecov.com/docs/codecov-uploader
|
|
14
|
+
*
|
|
15
|
+
* **Format Structure**:
|
|
16
|
+
* The format uses an array of hit counts per line, with null for non-executable lines.
|
|
17
|
+
* Array indices are 0-based (index 0 = line 1, index 1 = line 2, etc.)
|
|
18
|
+
*
|
|
19
|
+
* **Apex-Specific Adaptations**:
|
|
20
|
+
* - Only lines present in Apex coverage data are tracked
|
|
21
|
+
* - Lines not in coverage data are marked as `null` (non-executable)
|
|
22
|
+
* - This works well with Apex since Salesforce only reports executable lines
|
|
23
|
+
*
|
|
24
|
+
* **Advantages for Apex**:
|
|
25
|
+
* - Simple, compact format
|
|
26
|
+
* - Direct mapping from Apex line coverage to SimpleCov format
|
|
27
|
+
* - Well-supported by Codecov and similar platforms
|
|
28
|
+
*
|
|
29
|
+
* Compatible with:
|
|
30
|
+
* - Codecov
|
|
31
|
+
* - SimpleCov analyzers
|
|
32
|
+
* - Ruby coverage tools
|
|
33
|
+
* - Custom parsers
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```json
|
|
37
|
+
* {
|
|
38
|
+
* "coverage": {
|
|
39
|
+
* "path/to/file.cls": [1, 1, 0, 1, null, 1]
|
|
40
|
+
* },
|
|
41
|
+
* "timestamp": 1234567890
|
|
42
|
+
* }
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export declare class SimpleCovCoverageHandler extends BaseHandler {
|
|
46
|
+
private readonly coverageObj;
|
|
47
|
+
constructor();
|
|
48
|
+
processFile(filePath: string, _fileName: string, lines: Record<string, number>): void;
|
|
49
|
+
finalize(): SimpleCovCoverageObject;
|
|
50
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
import { BaseHandler } from './BaseHandler.js';
|
|
3
|
+
import { HandlerRegistry } from './HandlerRegistry.js';
|
|
4
|
+
/**
|
|
5
|
+
* Handler for generating SimpleCov JSON coverage reports.
|
|
6
|
+
*
|
|
7
|
+
* SimpleCov is a popular Ruby code coverage tool. This format is also
|
|
8
|
+
* accepted by Codecov and other coverage aggregation tools.
|
|
9
|
+
*
|
|
10
|
+
* **Format Origin**: SimpleCov (Ruby coverage tool)
|
|
11
|
+
*
|
|
12
|
+
* @see https://github.com/simplecov-ruby/simplecov
|
|
13
|
+
* @see https://github.com/vicentllongo/simplecov-json
|
|
14
|
+
* @see https://docs.codecov.com/docs/codecov-uploader
|
|
15
|
+
*
|
|
16
|
+
* **Format Structure**:
|
|
17
|
+
* The format uses an array of hit counts per line, with null for non-executable lines.
|
|
18
|
+
* Array indices are 0-based (index 0 = line 1, index 1 = line 2, etc.)
|
|
19
|
+
*
|
|
20
|
+
* **Apex-Specific Adaptations**:
|
|
21
|
+
* - Only lines present in Apex coverage data are tracked
|
|
22
|
+
* - Lines not in coverage data are marked as `null` (non-executable)
|
|
23
|
+
* - This works well with Apex since Salesforce only reports executable lines
|
|
24
|
+
*
|
|
25
|
+
* **Advantages for Apex**:
|
|
26
|
+
* - Simple, compact format
|
|
27
|
+
* - Direct mapping from Apex line coverage to SimpleCov format
|
|
28
|
+
* - Well-supported by Codecov and similar platforms
|
|
29
|
+
*
|
|
30
|
+
* Compatible with:
|
|
31
|
+
* - Codecov
|
|
32
|
+
* - SimpleCov analyzers
|
|
33
|
+
* - Ruby coverage tools
|
|
34
|
+
* - Custom parsers
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```json
|
|
38
|
+
* {
|
|
39
|
+
* "coverage": {
|
|
40
|
+
* "path/to/file.cls": [1, 1, 0, 1, null, 1]
|
|
41
|
+
* },
|
|
42
|
+
* "timestamp": 1234567890
|
|
43
|
+
* }
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export class SimpleCovCoverageHandler extends BaseHandler {
|
|
47
|
+
coverageObj;
|
|
48
|
+
constructor() {
|
|
49
|
+
super();
|
|
50
|
+
this.coverageObj = {
|
|
51
|
+
coverage: {},
|
|
52
|
+
timestamp: Math.floor(Date.now() / 1000),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
processFile(filePath, _fileName, lines) {
|
|
56
|
+
// Find the maximum line number to determine array size
|
|
57
|
+
const lineNumbers = Object.keys(lines).map(Number);
|
|
58
|
+
const maxLine = Math.max(...lineNumbers);
|
|
59
|
+
// Create array with nulls for non-executable lines
|
|
60
|
+
// SimpleCov uses null to indicate lines that are not executable/trackable
|
|
61
|
+
const lineArray = new Array(maxLine).fill(null);
|
|
62
|
+
// Fill in the coverage data
|
|
63
|
+
// SimpleCov arrays are 0-indexed, but line numbers are 1-indexed
|
|
64
|
+
// So line 1 goes into array index 0, line 2 into index 1, etc.
|
|
65
|
+
for (const [lineNumber, hits] of Object.entries(lines)) {
|
|
66
|
+
const lineIdx = Number(lineNumber) - 1; // Convert to 0-index
|
|
67
|
+
lineArray[lineIdx] = hits; // Store hit count (0 = uncovered, >0 = covered)
|
|
68
|
+
}
|
|
69
|
+
this.coverageObj.coverage[filePath] = lineArray;
|
|
70
|
+
}
|
|
71
|
+
finalize() {
|
|
72
|
+
// Sort coverage object by file path for deterministic output
|
|
73
|
+
const sortedKeys = Object.keys(this.coverageObj.coverage).sort();
|
|
74
|
+
const sortedCoverage = {};
|
|
75
|
+
for (const key of sortedKeys) {
|
|
76
|
+
sortedCoverage[key] = this.coverageObj.coverage[key];
|
|
77
|
+
}
|
|
78
|
+
this.coverageObj.coverage = sortedCoverage;
|
|
79
|
+
return this.coverageObj;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// Self-register this handler
|
|
83
|
+
HandlerRegistry.register({
|
|
84
|
+
name: 'simplecov',
|
|
85
|
+
description: 'SimpleCov JSON format compatible with Ruby coverage tools',
|
|
86
|
+
fileExtension: '.json',
|
|
87
|
+
handler: () => new SimpleCovCoverageHandler(),
|
|
88
|
+
compatibleWith: ['Codecov', 'SimpleCov', 'Ruby Tools'],
|
|
89
|
+
});
|
|
90
|
+
//# sourceMappingURL=simplecov.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"simplecov.js","sourceRoot":"","sources":["../../src/handlers/simplecov.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAGb,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,MAAM,OAAO,wBAAyB,SAAQ,WAAW;IACtC,WAAW,CAA0B;IAEtD;QACE,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,WAAW,GAAG;YACjB,QAAQ,EAAE,EAAE;YACZ,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;SACzC,CAAC;IACJ,CAAC;IAEM,WAAW,CAAC,QAAgB,EAAE,SAAiB,EAAE,KAA6B;QACnF,uDAAuD;QACvD,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACnD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,WAAW,CAAC,CAAC;QAEzC,mDAAmD;QACnD,0EAA0E;QAC1E,MAAM,SAAS,GAAyB,IAAI,KAAK,CAAgB,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAErF,4BAA4B;QAC5B,iEAAiE;QACjE,+DAA+D;QAC/D,KAAK,MAAM,CAAC,UAAU,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACvD,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,qBAAqB;YAC7D,SAAS,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,gDAAgD;QAC7E,CAAC;QAED,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,SAAS,CAAC;IAClD,CAAC;IAEM,QAAQ;QACb,6DAA6D;QAC7D,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;QACjE,MAAM,cAAc,GAAyC,EAAE,CAAC;QAEhE,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;YAC7B,cAAc,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QACvD,CAAC;QAED,IAAI,CAAC,WAAW,CAAC,QAAQ,GAAG,cAAc,CAAC;QAE3C,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;CACF;AAED,6BAA6B;AAC7B,eAAe,CAAC,QAAQ,CAAC;IACvB,IAAI,EAAE,WAAW;IACjB,WAAW,EAAE,2DAA2D;IACxE,aAAa,EAAE,OAAO;IACtB,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,wBAAwB,EAAE;IAC7C,cAAc,EAAE,CAAC,SAAS,EAAE,WAAW,EAAE,YAAY,CAAC;CACvD,CAAC,CAAC"}
|
package/lib/handlers/sonar.d.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
import { SonarCoverageObject
|
|
2
|
-
|
|
1
|
+
import { SonarCoverageObject } from '../utils/types.js';
|
|
2
|
+
import { BaseHandler } from './BaseHandler.js';
|
|
3
|
+
/**
|
|
4
|
+
* Handler for generating SonarQube Generic Coverage reports.
|
|
5
|
+
*
|
|
6
|
+
* This is the default format and is compatible with SonarQube and SonarCloud.
|
|
7
|
+
*
|
|
8
|
+
* @see https://docs.sonarqube.org/latest/analysis/generic-test/
|
|
9
|
+
*/
|
|
10
|
+
export declare class SonarCoverageHandler extends BaseHandler {
|
|
3
11
|
private readonly coverageObj;
|
|
4
12
|
constructor();
|
|
5
13
|
processFile(filePath: string, _fileName: string, lines: Record<string, number>): void;
|
package/lib/handlers/sonar.js
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
|
|
2
|
+
import { BaseHandler } from './BaseHandler.js';
|
|
3
|
+
import { HandlerRegistry } from './HandlerRegistry.js';
|
|
4
|
+
/**
|
|
5
|
+
* Handler for generating SonarQube Generic Coverage reports.
|
|
6
|
+
*
|
|
7
|
+
* This is the default format and is compatible with SonarQube and SonarCloud.
|
|
8
|
+
*
|
|
9
|
+
* @see https://docs.sonarqube.org/latest/analysis/generic-test/
|
|
10
|
+
*/
|
|
11
|
+
export class SonarCoverageHandler extends BaseHandler {
|
|
3
12
|
coverageObj;
|
|
4
13
|
constructor() {
|
|
14
|
+
super();
|
|
5
15
|
this.coverageObj = { coverage: { '@version': '1', file: [] } };
|
|
6
16
|
}
|
|
7
17
|
processFile(filePath, _fileName, lines) {
|
|
@@ -10,7 +20,7 @@ export class SonarCoverageHandler {
|
|
|
10
20
|
lineToCover: [],
|
|
11
21
|
};
|
|
12
22
|
for (const [lineNumberString, value] of Object.entries(lines)) {
|
|
13
|
-
const covered = value === 1
|
|
23
|
+
const covered = value === 1;
|
|
14
24
|
fileObj.lineToCover.push({
|
|
15
25
|
'@lineNumber': Number(lineNumberString),
|
|
16
26
|
'@covered': covered,
|
|
@@ -20,9 +30,17 @@ export class SonarCoverageHandler {
|
|
|
20
30
|
}
|
|
21
31
|
finalize() {
|
|
22
32
|
if (this.coverageObj.coverage?.file) {
|
|
23
|
-
this.coverageObj.coverage.file
|
|
33
|
+
this.coverageObj.coverage.file = this.sortByPath(this.coverageObj.coverage.file);
|
|
24
34
|
}
|
|
25
35
|
return this.coverageObj;
|
|
26
36
|
}
|
|
27
37
|
}
|
|
38
|
+
// Self-register this handler
|
|
39
|
+
HandlerRegistry.register({
|
|
40
|
+
name: 'sonar',
|
|
41
|
+
description: 'SonarQube Generic Coverage format',
|
|
42
|
+
fileExtension: '.xml',
|
|
43
|
+
handler: () => new SonarCoverageHandler(),
|
|
44
|
+
compatibleWith: ['SonarQube', 'SonarCloud'],
|
|
45
|
+
});
|
|
28
46
|
//# sourceMappingURL=sonar.js.map
|