feat: implement apex test parser and add coverage percentage

This commit is contained in:
Ross Reicks 2024-07-11 13:02:16 -05:00
parent c4f7701aa1
commit 0f142e1e2a
15 changed files with 6278 additions and 18 deletions

View file

@ -1,10 +1,61 @@
import {ParseOptions, TestParser} from '../../test-parser'
import {TestRunResult} from '../../test-results'
import {TestCaseError, TestCaseResult, TestExecutionResult, TestGroupResult, TestRunResult, TestSuiteResult} from '../../test-results'
import {ApexTestReport} from './apex-json-types'
export class ApexJsonParser implements TestParser {
constructor(readonly options: ParseOptions) {}
async parse(path: string, content: string): Promise<TestRunResult> {
throw new Error('Method not implemented.')
const report = await this.getReport(path, content)
return this.getTestRunResult(path, report)
}
private async getReport(path: string, content: string): Promise<ApexTestReport> {
try {
return JSON.parse(content) as ApexTestReport
} catch (e) {
throw new Error(`Invalid JSON at ${path}\n\n${e}`)
}
}
private getTestRunResult(path: string, report: ApexTestReport): TestRunResult {
const time = report.result.summary.testTotalTime
const timeAsNumber = Number.parseInt(time, 10)
// group tests by test.ApexClass.Name
const groupsMap: Map<string, TestCaseResult[]> = report.result.tests.reduce((map, test) => {
const key = test.ApexClass.Name
const testResults = map.get(key) || []
let result: TestExecutionResult = 'skipped'
if (test.Outcome === 'Pass') {
result = 'success'
} else if (test.Outcome === 'Fail') {
result = 'failed'
}
const testCaseError: TestCaseError | undefined = test.Message ? {details: test.Message} : undefined
const testResult = new TestCaseResult(test.MethodName, result, test.RunTime, testCaseError)
testResults.push(testResult)
map.set(key, testResults)
return map
}, new Map<string, TestCaseResult[]>())
const groups: TestGroupResult[] = []
for (const [name, tests] of groupsMap) {
const suite = new TestGroupResult(name, tests)
groups.push(suite)
}
const coverageString = report.result.summary.testRunCoverage
const coverage = coverageString ? Number.parseInt(coverageString.replace('%', ''), 10) : undefined
const suite = new TestSuiteResult('Apex Tests', groups, timeAsNumber, coverage)
return new TestRunResult(path, [suite], timeAsNumber, coverage)
}
}

View file

@ -1,4 +1,4 @@
export interface RootObject {
export interface ApexTestReport {
result: Result
}
@ -55,7 +55,7 @@ export interface Test {
Id: string
QueueItemId: string
StackTrace: null
Message: null
Message: string | null
AsyncApexJobId: string
MethodName: string
Outcome: string

View file

@ -149,6 +149,16 @@ function getTestRunsReport(testRuns: TestRunResult[], options: ReportOptions): s
sections.push(` `)
}
const shouldShowCoverage = testRuns.some(tr => tr.coverage !== undefined)
const columnNames = ['Report', 'Passed', 'Failed', 'Skipped', 'Time']
const columnAligns = [Align.Left, Align.Right, Align.Right, Align.Right, Align.Right]
if (shouldShowCoverage) {
columnNames.push('Coverage')
columnAligns.push(Align.Right)
}
if (testRuns.length > 0 || options.onlySummary) {
const tableData = testRuns
.filter(tr => tr.passed > 0 || tr.failed > 0 || tr.skipped > 0)
@ -158,14 +168,16 @@ function getTestRunsReport(testRuns: TestRunResult[], options: ReportOptions): s
const passed = tr.passed > 0 ? `${tr.passed} ${Icon.success}` : ''
const failed = tr.failed > 0 ? `${tr.failed} ${Icon.fail}` : ''
const skipped = tr.skipped > 0 ? `${tr.skipped} ${Icon.skip}` : ''
if (shouldShowCoverage) {
const coverage = tr.coverage !== undefined ? tr.coverage + '%' : ''
return [name, passed, failed, skipped, time, coverage]
}
return [name, passed, failed, skipped, time]
})
const resultsTable = table(
['Report', 'Passed', 'Failed', 'Skipped', 'Time'],
[Align.Left, Align.Right, Align.Right, Align.Right, Align.Right],
...tableData
)
const resultsTable = table(columnNames, columnAligns, ...tableData)
sections.push(resultsTable)
}
@ -198,9 +210,19 @@ function getSuitesReport(tr: TestRunResult, runIndex: number, options: ReportOpt
sections.push(headingLine2)
if (suites.length > 0) {
const shouldShowCoverage = suites.some(s => s.coverage !== undefined)
const columnNames = ['Test suite', 'Passed', 'Failed', 'Skipped', 'Time']
const columnAligns = [Align.Left, Align.Right, Align.Right, Align.Right, Align.Right]
if (shouldShowCoverage) {
columnNames.push('Coverage')
columnAligns.push(Align.Right)
}
const suitesTable = table(
['Test suite', 'Passed', 'Failed', 'Skipped', 'Time'],
[Align.Left, Align.Right, Align.Right, Align.Right, Align.Right],
columnNames,
columnAligns,
...suites.map((s, suiteIndex) => {
const tsTime = formatTime(s.time)
const tsName = s.name
@ -210,6 +232,13 @@ function getSuitesReport(tr: TestRunResult, runIndex: number, options: ReportOpt
const passed = s.passed > 0 ? `${s.passed} ${Icon.success}` : ''
const failed = s.failed > 0 ? `${s.failed} ${Icon.fail}` : ''
const skipped = s.skipped > 0 ? `${s.skipped} ${Icon.skip}` : ''
const coverage = s.coverage !== undefined ? s.coverage + '%' : ''
if (shouldShowCoverage) {
return [tsNameLink, passed, failed, skipped, tsTime, coverage]
}
return [tsNameLink, passed, failed, skipped, tsTime]
})
)

View file

@ -4,7 +4,8 @@ export class TestRunResult {
constructor(
readonly path: string,
readonly suites: TestSuiteResult[],
private totalTime?: number
private totalTime?: number,
private coveragePercentage?: number
) {}
get tests(): number {
@ -25,6 +26,10 @@ export class TestRunResult {
return this.totalTime ?? this.suites.reduce((sum, g) => sum + g.time, 0)
}
get coverage(): number | undefined {
return this.coveragePercentage
}
get result(): TestExecutionResult {
return this.suites.some(t => t.result === 'failed') ? 'failed' : 'success'
}
@ -47,7 +52,8 @@ export class TestSuiteResult {
constructor(
readonly name: string,
readonly groups: TestGroupResult[],
private totalTime?: number
private totalTime?: number,
private coveragePercentage?: number
) {}
get tests(): number {
@ -66,6 +72,9 @@ export class TestSuiteResult {
get time(): number {
return this.totalTime ?? this.groups.reduce((sum, g) => sum + g.time, 0)
}
get coverage(): number | undefined {
return this.coveragePercentage
}
get result(): TestExecutionResult {
return this.groups.some(t => t.result === 'failed') ? 'failed' : 'success'