-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add junit result format to test:run command (#96)
@W-7561364@
- Loading branch information
Showing
11 changed files
with
480 additions
and
35 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 |
---|---|---|
|
@@ -6,3 +6,4 @@ | |
*/ | ||
|
||
export { TapReporter } from './tapReporter'; | ||
export { JUnitReporter } from './junitReporter'; |
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 |
---|---|---|
@@ -0,0 +1,92 @@ | ||
/* | ||
* Copyright (c) 2020, salesforce.com, inc. | ||
* All rights reserved. | ||
* Licensed under the BSD 3-Clause license. | ||
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause | ||
*/ | ||
import { | ||
ApexTestResultData, | ||
ApexTestResultOutcome, | ||
TestResult | ||
} from '../tests/types'; | ||
|
||
// cli currently has spaces in multiples of four for junit format | ||
const tab = ' '; | ||
|
||
export class JUnitReporter { | ||
public format(testResult: TestResult): string { | ||
const { summary, tests } = testResult; | ||
|
||
let output = `<?xml version="1.0" encoding="UTF-8"?>\n`; | ||
output += `<testsuites>\n`; | ||
output += `${tab}<testsuite name="force.apex" `; | ||
output += `timestamp="${summary.testStartTime}" `; | ||
output += `hostname="${summary.hostname}" `; | ||
output += `tests="${summary.numTestsRan}" `; | ||
output += `failures="${summary.failing}" `; | ||
output += `time="${this.msToSecond(summary.testExecutionTime)} s">\n`; | ||
|
||
output += this.buildProperties(testResult); | ||
output += this.buildTestCases(tests); | ||
|
||
output += `${tab}</testsuite>\n`; | ||
output += `</testsuites>\n`; | ||
return output; | ||
} | ||
|
||
private buildProperties(testResult: TestResult): string { | ||
let junitProperties = `${tab}${tab}<properties>\n`; | ||
|
||
Object.entries(testResult.summary).forEach(([key, value]) => { | ||
if ( | ||
value === null || | ||
value === undefined || | ||
(typeof value === 'string' && value.length === 0) | ||
) { | ||
return; | ||
} | ||
if (key === 'testExecutionTime') { | ||
value = `${this.msToSecond(value as number)} s`; | ||
} | ||
if (key === 'testStartTime') { | ||
const date = new Date(value); | ||
value = `${date.toDateString()} ${date.toLocaleTimeString()}`; | ||
} | ||
|
||
junitProperties += `${tab}${tab}${tab}<property name="${key}" value="${value}"/>\n`; | ||
}); | ||
|
||
junitProperties += `${tab}${tab}</properties>\n`; | ||
return junitProperties; | ||
} | ||
|
||
private buildTestCases(tests: ApexTestResultData[]): string { | ||
let junitTests = ''; | ||
|
||
for (const testCase of tests) { | ||
junitTests += `${tab}${tab}<testcase name="${ | ||
testCase.methodName | ||
}" classname="${testCase.apexClass.fullName}" time="${this.msToSecond( | ||
testCase.runTime | ||
)}">\n`; | ||
|
||
if ( | ||
testCase.outcome === ApexTestResultOutcome.Fail || | ||
testCase.outcome === ApexTestResultOutcome.CompileFail | ||
) { | ||
junitTests += `${tab}${tab}${tab}<failure message="${testCase.message}">`; | ||
if (testCase.stackTrace) { | ||
junitTests += `<![CDATA[${testCase.stackTrace}]]>`; | ||
} | ||
junitTests += `</failure>\n`; | ||
} | ||
|
||
junitTests += `${tab}${tab}</testcase>\n`; | ||
} | ||
return junitTests; | ||
} | ||
|
||
private msToSecond(timestamp: number): string { | ||
return (timestamp / 1000).toFixed(2); | ||
} | ||
} |
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
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 |
---|---|---|
@@ -0,0 +1,65 @@ | ||
/* | ||
* Copyright (c) 2020, salesforce.com, inc. | ||
* All rights reserved. | ||
* Licensed under the BSD 3-Clause license. | ||
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause | ||
*/ | ||
import { expect } from 'chai'; | ||
import { JUnitReporter } from '../../src'; | ||
import { | ||
testResults, | ||
junitResult, | ||
junitSuccess, | ||
junitCodeCov, | ||
junitMissingVal, | ||
successResult | ||
} from './testResults'; | ||
|
||
describe('JUnit Reporter Tests', () => { | ||
const reporter = new JUnitReporter(); | ||
|
||
it('should format test results with failures', () => { | ||
const result = reporter.format(testResults); | ||
expect(result).to.not.be.empty; | ||
expect(result).to.eql(junitResult); | ||
expect(result).to.contain('</failure>'); | ||
}); | ||
|
||
it('should format tests with 0 failures', async () => { | ||
const result = reporter.format(successResult); | ||
expect(result).to.not.be.empty; | ||
expect(result).to.eql(junitSuccess); | ||
expect(result).to.not.contain('</failure>'); | ||
}); | ||
|
||
it('should format test results with undefined or empty values', () => { | ||
successResult.summary.testRunId = ''; | ||
successResult.summary.userId = undefined; | ||
|
||
const result = reporter.format(successResult); | ||
expect(result).to.not.be.empty; | ||
expect(result).to.eql(junitMissingVal); | ||
expect(result).to.not.contain('testRunId'); | ||
expect(result).to.not.contain('userId'); | ||
}); | ||
|
||
it('should format test results with code coverage', () => { | ||
successResult.codecoverage = [ | ||
{ | ||
apexId: '001917xACG', | ||
name: 'ApexTestClass', | ||
type: 'ApexClass', | ||
numLinesCovered: 8, | ||
numLinesUncovered: 2, | ||
percentage: '12.5%', | ||
coveredLines: [1, 2, 3, 4, 5, 6, 7, 8], | ||
uncoveredLines: [9, 10] | ||
} | ||
]; | ||
successResult.summary.orgWideCoverage = '85%'; | ||
const result = reporter.format(successResult); | ||
expect(result).to.not.be.empty; | ||
expect(result).to.eql(junitCodeCov); | ||
expect(result).to.contain('orgWideCoverage'); | ||
}); | ||
}); |
Oops, something went wrong.