apex-code-coverage-transformer 1.0.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/README.md ADDED
@@ -0,0 +1,48 @@
1
+ # Apex Code Coverage Transformer
2
+
3
+ [![NPM](https://img.shields.io/npm/v/apex-code-coverage-transformer.svg?label=apex-code-coverage-transformer)](https://www.npmjs.com/package/apex-code-coverage-transformer) [![Downloads/week](https://img.shields.io/npm/dw/apex-code-coverage-transformer.svg)](https://npmjs.org/package/apex-code-coverage-transformer) [![License](https://img.shields.io/badge/License-BSD%203--Clause-brightgreen.svg)](https://raw.githubusercontent.com/salesforcecli/apex-code-coverage-transformer/main/LICENSE.txt)
4
+
5
+ The `apex-code-coverage-transformer` is a simple plugin to transform the JSON file for Apex Code Coverage into Generic Test Coverage Format (XML). This format is accepted by static code analysis tools like SonarQube.
6
+
7
+ Here is how you can create the JSON file with the Salesforce CLI:
8
+
9
+ ```
10
+ sf project deploy validate -x manifest/package.xml -l RunSpecifiedTests -t {testclasses} --verbose --coverage-formatters json --results-dir coverage
11
+ ```
12
+
13
+ This will create a coverage JSON in this relative path - `coverage/coverage/coverage.json`
14
+
15
+ This JSON isn't accepted by SonarQube automatically and needs to be converted using this plugin.
16
+
17
+ ## Install
18
+
19
+ ```bash
20
+ sf plugins install apex-code-coverage-transformer@x.y.z
21
+ ```
22
+
23
+ ## Commands
24
+
25
+ The `apex-code-coverage-transformer` has 1 command:
26
+
27
+ - `sf apex-code-coverage transformer transform`
28
+
29
+ ## `sf apex-code-coverage transformer transform`
30
+
31
+ ```
32
+ USAGE
33
+ $ sf apex-code-coverage transformer transform -j <value> -x <value> -d <value> [--json]
34
+
35
+ FLAGS
36
+ -j, --json=<value> The path to the JSON file created by the Salesforce CLI for code coverage.
37
+ -x, --xml=<value> [default: coverage.xml] Output path for the XML file created by this plugin
38
+ -d, --dx-directory=<value> [default: force-app/main/default] The root directory containing your Salesforce metadata.
39
+
40
+ GLOBAL FLAGS
41
+ --json Format output as json.
42
+
43
+ DESCRIPTION
44
+ This plugin will convert the JSON file created by the Salesforce CLI during Apex deployments into the Generic Test Coverage Format.
45
+
46
+ EXAMPLES
47
+ $ apex-code-coverage transformer transform -j "test.json" -x "coverage.xml" -d "force-app/main/default"
48
+ ```
@@ -0,0 +1,15 @@
1
+ import { SfCommand } from '@salesforce/sf-plugins-core';
2
+ export type TransformerTransformResult = {
3
+ path: string;
4
+ };
5
+ export default class TransformerTransform extends SfCommand<TransformerTransformResult> {
6
+ static readonly summary: string;
7
+ static readonly description: string;
8
+ static readonly examples: string[];
9
+ static readonly flags: {
10
+ 'dx-directory': import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
11
+ json: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
12
+ xml: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
13
+ };
14
+ run(): Promise<TransformerTransformResult>;
15
+ }
@@ -0,0 +1,78 @@
1
+ 'use strict';
2
+ import * as fs from 'node:fs';
3
+ import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
4
+ import { Messages } from '@salesforce/core';
5
+ Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
6
+ const messages = Messages.loadMessages('apex-code-coverage-transformer', 'transformer.transform');
7
+ export default class TransformerTransform extends SfCommand {
8
+ static summary = messages.getMessage('summary');
9
+ static description = messages.getMessage('description');
10
+ static examples = messages.getMessages('examples');
11
+ static flags = {
12
+ 'dx-directory': Flags.directory({
13
+ summary: messages.getMessage('flags.dx-directory.summary'),
14
+ char: 'd',
15
+ required: true,
16
+ exists: true,
17
+ default: 'force-app/main/default',
18
+ }),
19
+ 'json': Flags.string({
20
+ summary: messages.getMessage('flags.json.summary'),
21
+ char: 'j',
22
+ required: true,
23
+ exists: true,
24
+ }),
25
+ 'xml': Flags.string({
26
+ summary: messages.getMessage('flags.xml.summary'),
27
+ char: 'x',
28
+ required: true,
29
+ exists: true,
30
+ default: 'coverage.xml',
31
+ }),
32
+ };
33
+ async run() {
34
+ const { flags } = await this.parse(TransformerTransform);
35
+ const jsonFilePath = flags['json'];
36
+ const xmlFilePath = flags['xml'];
37
+ const dxDirectory = flags['dx-directory'];
38
+ const jsonData = fs.readFileSync(jsonFilePath, 'utf-8');
39
+ const coverageData = JSON.parse(jsonData);
40
+ const xmlData = convertToGenericTestReport(coverageData, dxDirectory);
41
+ // Write the XML data to the XML file
42
+ try {
43
+ fs.writeFileSync(xmlFilePath, xmlData);
44
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
45
+ this.log(`The XML data has been written to ${xmlFilePath}`);
46
+ }
47
+ catch (error) {
48
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
49
+ this.error(`Error writing XML data to file: ${error}`);
50
+ }
51
+ return { path: xmlFilePath };
52
+ }
53
+ }
54
+ function convertToGenericTestReport(data, dxDirectory) {
55
+ let xml = '<?xml version="1.0"?>\n<coverage version="1">\n';
56
+ for (const className in data) {
57
+ if (Object.prototype.hasOwnProperty.call(data, className)) {
58
+ const classInfo = data[className];
59
+ const formattedClassName = className.replace('no-map/', '');
60
+ const classPath = `${dxDirectory}/classes/${formattedClassName}.cls`;
61
+ xml += `\t<file path="${classPath}">\n`;
62
+ for (const lineNumber in classInfo.s) {
63
+ if (Object.prototype.hasOwnProperty.call(classInfo.s, lineNumber)) {
64
+ const count = classInfo.s[lineNumber];
65
+ const covered = count > 0 ? 'true' : 'false';
66
+ // only add uncovered lines
67
+ if (covered === 'false') {
68
+ xml += `\t\t<lineToCover lineNumber="${lineNumber}" covered="${covered}"/>\n`;
69
+ }
70
+ }
71
+ }
72
+ xml += '\t</file>\n';
73
+ }
74
+ }
75
+ xml += '</coverage>';
76
+ return xml;
77
+ }
78
+ //# sourceMappingURL=transform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transform.js","sourceRoot":"","sources":["../../../../src/commands/apex-code-coverage/transformer/transform.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ export interface CoverageData {
2
+ [className: string]: {
3
+ fnMap: Record<string, unknown>;
4
+ branchMap: Record<string, unknown>;
5
+ path: string;
6
+ f: Record<string, unknown>;
7
+ b: Record<string, unknown>;
8
+ s: Record<string, number>;
9
+ statementMap: Record<string, {
10
+ start: {
11
+ line: number;
12
+ column: number;
13
+ };
14
+ end: {
15
+ line: number;
16
+ column: number;
17
+ };
18
+ }>;
19
+ };
20
+ }
@@ -0,0 +1,3 @@
1
+ 'use strict';
2
+ export {};
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/helpers/types.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC"}
package/lib/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ declare const _default: {};
2
+ export default _default;
package/lib/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export default {};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,eAAe,EAAE,CAAC"}
@@ -0,0 +1,23 @@
1
+ # summary
2
+
3
+ Transforms the Code Coverage JSON into the Generic Test Data Format (XML).
4
+
5
+ # description
6
+
7
+ This plugin will convert the JSON file created by the Salesforce CLI during Apex deployments
8
+
9
+ # examples
10
+
11
+ - `sf apex-code-coverage transformer transform --json "path-to-cli-coverage.json"`
12
+
13
+ # flags.dx-directory.summary
14
+
15
+ Directory containing Salesforce metadata (default: `force-app/main/default`).
16
+
17
+ # flags.json.summary
18
+
19
+ Path to the JSON file created by the Salesforce CLI deployment command.
20
+
21
+ # flags.xml.summary
22
+
23
+ XML file created by this plugin (default: `coverage.xml`).