forked from dxatscale/sfpowerkit
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* issue #217 issue #217 exposting test result in csv or json format * json output
- Loading branch information
1 parent
06fdc8d
commit 3687576
Showing
3 changed files
with
147 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
{ | ||
"commandDescription": "Gets the apex test coverage details of an org" | ||
"commandDescription": "Gets the apex test coverage details of an org", | ||
"outputFolderDescription": " The output dir where the output will be created", | ||
"formatFlagDescription": " The format for the test result output, Possible values are json/csv" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,15 @@ | ||
import { core, flags, SfdxCommand, Result } from "@salesforce/command"; | ||
import { | ||
core, | ||
flags, | ||
FlagsConfig, | ||
SfdxCommand, | ||
Result | ||
} from "@salesforce/command"; | ||
import { SfdxError } from "@salesforce/core"; | ||
import { AnyJson } from "@salesforce/ts-types"; | ||
import fs = require("fs-extra"); | ||
import * as path from "path"; | ||
import FileUtils from "../../../utils/fileutils"; | ||
import request = require("request-promise-native"); | ||
import rimraf = require("rimraf"); | ||
const querystring = require("querystring"); | ||
|
@@ -16,18 +25,49 @@ export default class OrgCoverage extends SfdxCommand { | |
public static description = messages.getMessage("commandDescription"); | ||
|
||
public static examples = [ | ||
`$ sfdx sfpowerkit:org:orgcoverage -u [email protected] | ||
`$ sfdx sfpowerkit:org:orgcoverage -u [email protected] | ||
sfdx sfpowerkit:org:orgcoverage -u [email protected] -d testResult -f csv | ||
sfdx sfpowerkit:org:orgcoverage -u [email protected] -d testResult -f json | ||
Successfully Retrieved the Apex Test Coverage of the org XXXX | ||
coverage:85 | ||
ID NAME TYPE PERCENTAGE COMMENTS UNCOVERED LINES | ||
─────── ────────────────── ──────── ────────── ─────────────────────────────────── ────────────────── | ||
01pxxxx sampleController ApexClass 100% | ||
01pxxxx sampletriggerHandler ApexClass 80% Looks fine but target more than 85% 62;76;77; | ||
01pxxxx sampleHelper ApexClass 72% Action required 62;76;77;78;98;130;131 | ||
01qxxxx sampleTrigger ApexTrigger 100% | ||
Output testResult/output.csv is generated successfully | ||
` | ||
]; | ||
|
||
protected static flagsConfig: FlagsConfig = { | ||
output: flags.string({ | ||
char: "d", | ||
description: messages.getMessage("outputFolderDescription"), | ||
required: false | ||
}), | ||
format: flags.enum({ | ||
required: false, | ||
char: "f", | ||
description: messages.getMessage("formatFlagDescription"), | ||
options: ["json", "csv"] | ||
}) | ||
}; | ||
|
||
// Comment this out if your command does not require an org username | ||
protected static requiresUsername = true; | ||
|
||
public async run(): Promise<AnyJson> { | ||
rimraf.sync("temp_sfpowerkit"); | ||
|
||
if (this.flags.output && !this.flags.format) { | ||
throw new SfdxError("format is required to generate the output"); | ||
} else if (this.flags.format && !this.flags.output) { | ||
throw new SfdxError("output path is required to generate the output"); | ||
} | ||
|
||
await this.org.refreshAuth(); | ||
|
||
const conn = this.org.getConnection(); | ||
|
@@ -47,7 +87,12 @@ export default class OrgCoverage extends SfdxCommand { | |
); | ||
this.ux.log(`coverage:${apexcoverage.coverage}`); | ||
|
||
return { coverage: apexcoverage.coverage }; | ||
const classCoverage = await this.getApexCoverageByDetails( | ||
conn, | ||
this.flags.output | ||
); | ||
|
||
return { coverage: apexcoverage.coverage, classCoverage: classCoverage }; | ||
} | ||
|
||
public async getApexCoverage(conn: core.Connection) { | ||
|
@@ -57,8 +102,6 @@ export default class OrgCoverage extends SfdxCommand { | |
|
||
var query_uri = `${conn.instanceUrl}/services/data/v${this.flags.apiversion}/tooling/query?q=${encoded_querystring}`; | ||
|
||
//this.ux.log(`Query URI ${query_uri}`); | ||
|
||
const coverage_score_query_result = await request({ | ||
method: "get", | ||
url: query_uri, | ||
|
@@ -71,6 +114,82 @@ export default class OrgCoverage extends SfdxCommand { | |
// this.ux.logJson(health_score_query_result); | ||
return coverage_score_query_result.records[0].PercentCovered; | ||
} | ||
public async getApexCoverageByDetails( | ||
conn: core.Connection, | ||
outputDir: string | ||
) { | ||
let query = | ||
"SELECT ApexClassOrTriggerId, ApexClassOrTrigger.Name, NumLinesCovered, NumLinesUncovered, coverage FROM ApexCodeCoverageAggregate ORDER BY ApexClassOrTrigger.Name"; | ||
|
||
const results = (await conn.tooling.query(query)) as any; | ||
const output = []; | ||
if (results.size > 0) { | ||
results.records.forEach(element => { | ||
let percentage = | ||
element.NumLinesCovered === 0 | ||
? 0 | ||
: Math.round( | ||
(element.NumLinesCovered / | ||
(element.NumLinesCovered + element.NumLinesUncovered)) * | ||
100 | ||
); | ||
output.push({ | ||
id: element.ApexClassOrTriggerId, | ||
name: element.ApexClassOrTrigger.Name, | ||
type: element.ApexClassOrTrigger.attributes.url.split("/")[6], | ||
percentage: `${percentage}%`, | ||
comments: | ||
percentage < 75 | ||
? "Action required" | ||
: percentage < 85 && percentage >= 75 | ||
? "Looks fine but target more than 85%" | ||
: "", | ||
uncoveredLines: element.Coverage.uncoveredLines.join(";") | ||
}); | ||
}); | ||
|
||
this.ux.table(output, [ | ||
"id", | ||
"name", | ||
"type", | ||
"percentage", | ||
"comments", | ||
"uncoveredLines" | ||
]); | ||
|
||
if (this.flags.format && this.flags.format === "json") { | ||
rimraf.sync(outputDir); | ||
await this.generateJsonOutput(output, outputDir); | ||
} else if (this.flags.format && this.flags.format === "csv") { | ||
rimraf.sync(outputDir); | ||
await this.generateCSVOutput(output, outputDir); | ||
} | ||
} | ||
return output; | ||
} | ||
public async generateJsonOutput(testResult: AnyJson, outputDir: string) { | ||
let outputJsonPath = `${outputDir}/output.json`; | ||
let dir = path.parse(outputJsonPath).dir; | ||
if (!fs.existsSync(dir)) { | ||
FileUtils.mkDirByPathSync(dir); | ||
} | ||
fs.writeFileSync(outputJsonPath, JSON.stringify(testResult)); | ||
this.ux.log(`Output ${outputDir}/output.json is generated successfully`); | ||
} | ||
public async generateCSVOutput(testResult: any[], outputDir: string) { | ||
let outputcsvPath = `${outputDir}/output.csv`; | ||
let dir = path.parse(outputcsvPath).dir; | ||
if (!fs.existsSync(dir)) { | ||
FileUtils.mkDirByPathSync(dir); | ||
} | ||
let newLine = "\r\n"; | ||
let output = "ID,NAME,TYPE,PERCENTAGE,COMMENTS,UNCOVERED LINES" + newLine; | ||
testResult.forEach(element => { | ||
output = `${output}${element.id},${element.name},${element.type},${element.percentage},${element.comments},${element.uncoveredLines}${newLine}`; | ||
}); | ||
fs.writeFileSync(outputcsvPath, output); | ||
this.ux.log(`Output ${outputDir}/output.csv is generated successfully`); | ||
} | ||
} | ||
|
||
export class ApexCoverage { | ||
|