feat: add reporter for salesforce apex

This commit is contained in:
Ross Reicks 2024-07-11 11:34:33 -05:00
parent 95058abb17
commit c4f7701aa1
9 changed files with 11991 additions and 5 deletions

View file

@ -19,6 +19,7 @@ This [Github Action](https://github.com/features/actions) displays test results
- Java / [JUnit](https://junit.org/) - Java / [JUnit](https://junit.org/)
- JavaScript / [JEST](https://jestjs.io/) / [Mocha](https://mochajs.org/) - JavaScript / [JEST](https://jestjs.io/) / [Mocha](https://mochajs.org/)
- Swift / xUnit - Swift / xUnit
- Apex / [JSON](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_testing.htm)
For more information see [Supported formats](#supported-formats) section. For more information see [Supported formats](#supported-formats) section.
@ -341,6 +342,16 @@ Please update Mocha to version [v9.1.0](https://github.com/mochajs/mocha/release
Support for Swift test results in xUnit format is experimental - should work but it was not extensively tested. Support for Swift test results in xUnit format is experimental - should work but it was not extensively tested.
</details> </details>
<details>
<summary>apex-json (Experimental)</summary>
Support for [Salesforce Apex](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_testing.htm)
```bash
sf apex test run --result-format json --wait 30 > test-results.json
```
</details>
## GitHub limitations ## GitHub limitations
Unfortunately, there are some known issues and limitations caused by GitHub API: Unfortunately, there are some known issues and limitations caused by GitHub API:

View file

@ -0,0 +1,45 @@
import path from 'node:path';
import fs from 'node:fs';
import { ParseOptions } from '../src/test-parser';
import { normalizeFilePath } from '../src/utils/path-utils';
import { ApexJsonParser } from '../src/parsers/apex-json/apex-json-parsers';
import { getReport } from '../src/report/get-report';
describe('apex-json tests', () => {
it('produces empty test run result when there are no test cases', async () => {
const fixturePath = path.join(__dirname, 'fixtures', 'empty', 'apex-json.json')
const filePath = normalizeFilePath(path.relative(__dirname, fixturePath))
const fileContent = fs.readFileSync(fixturePath, {encoding: 'utf8'})
const opts: ParseOptions = {
parseErrors: true,
trackedFiles: []
}
const parser = new ApexJsonParser(opts)
const result = await parser.parse(filePath, fileContent)
expect(result.tests).toBe(0)
expect(result.result).toBe('success')
});
it('matches report snapshot', async () => {
const opts: ParseOptions = {
parseErrors: true,
trackedFiles: []
}
const fixturePath = path.join(__dirname, 'fixtures', 'apex-json.json')
const outputPath = path.join(__dirname, '__outputs__', 'apex-json.md')
const filePath = normalizeFilePath(path.relative(__dirname, fixturePath))
const fileContent = fs.readFileSync(fixturePath, {encoding: 'utf8'})
const parser = new ApexJsonParser(opts)
const result = await parser.parse(filePath, fileContent)
expect(result).toMatchSnapshot()
const report = getReport([result])
fs.mkdirSync(path.dirname(outputPath), {recursive: true})
fs.writeFileSync(outputPath, report)
})
});

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,25 @@
{
"result": {
"summary": {
"failRate": "0%",
"failing": 0,
"hostname": "https://scratch.my.salesforce.com",
"orgId": "0000000000000",
"outcome": "Passed",
"passRate": "0%",
"passing": 0,
"skipped": 0,
"testRunId": "000000000000",
"testStartTime": "2024-07-11T15:21:00.000Z",
"testsRan": 0,
"userId": "000000000000000",
"username": "test@example.com",
"commandTime": "357 ms",
"testExecutionTime": "1000 ms",
"testTotalTime": "1000 ms",
"orgWideCoverage": "0%",
"testRunCoverage": "0%"
},
"tests": []
}
}

8
package-lock.json generated
View file

@ -23,7 +23,7 @@
"@types/adm-zip": "^0.5.5", "@types/adm-zip": "^0.5.5",
"@types/github-slugger": "^1.3.0", "@types/github-slugger": "^1.3.0",
"@types/jest": "^29.5.12", "@types/jest": "^29.5.12",
"@types/node": "^20.14.8", "@types/node": "^20.14.10",
"@types/picomatch": "^2.3.3", "@types/picomatch": "^2.3.3",
"@types/xml2js": "^0.4.14", "@types/xml2js": "^0.4.14",
"@typescript-eslint/eslint-plugin": "^7.14.1", "@typescript-eslint/eslint-plugin": "^7.14.1",
@ -1654,9 +1654,9 @@
} }
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "20.14.8", "version": "20.14.10",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.8.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz",
"integrity": "sha512-DO+2/jZinXfROG7j7WKFn/3C6nFwxy2lLpgLjEXJz+0XKphZlTLJ14mo8Vfg8X5BWN6XjyESXq+LcYdT7tR3bA==", "integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==",
"dependencies": { "dependencies": {
"undici-types": "~5.26.4" "undici-types": "~5.26.4"
} }

View file

@ -47,7 +47,7 @@
"@types/adm-zip": "^0.5.5", "@types/adm-zip": "^0.5.5",
"@types/github-slugger": "^1.3.0", "@types/github-slugger": "^1.3.0",
"@types/jest": "^29.5.12", "@types/jest": "^29.5.12",
"@types/node": "^20.14.8", "@types/node": "^20.14.10",
"@types/picomatch": "^2.3.3", "@types/picomatch": "^2.3.3",
"@types/xml2js": "^0.4.14", "@types/xml2js": "^0.4.14",
"@typescript-eslint/eslint-plugin": "^7.14.1", "@typescript-eslint/eslint-plugin": "^7.14.1",

View file

@ -21,6 +21,7 @@ import {SwiftXunitParser} from './parsers/swift-xunit/swift-xunit-parser'
import {normalizeDirPath, normalizeFilePath} from './utils/path-utils' import {normalizeDirPath, normalizeFilePath} from './utils/path-utils'
import {getCheckRunContext} from './utils/github-utils' import {getCheckRunContext} from './utils/github-utils'
import {ApexJsonParser} from './parsers/apex-json/apex-json-parsers'
async function main(): Promise<void> { async function main(): Promise<void> {
try { try {
@ -243,6 +244,8 @@ class TestReporter {
return new RspecJsonParser(options) return new RspecJsonParser(options)
case 'swift-xunit': case 'swift-xunit':
return new SwiftXunitParser(options) return new SwiftXunitParser(options)
case 'apex-json':
return new ApexJsonParser(options)
default: default:
throw new Error(`Input variable 'reporter' is set to invalid value '${reporter}'`) throw new Error(`Input variable 'reporter' is set to invalid value '${reporter}'`)
} }

View file

@ -0,0 +1,10 @@
import {ParseOptions, TestParser} from '../../test-parser'
import {TestRunResult} from '../../test-results'
export class ApexJsonParser implements TestParser {
constructor(readonly options: ParseOptions) {}
async parse(path: string, content: string): Promise<TestRunResult> {
throw new Error('Method not implemented.')
}
}

View file

@ -0,0 +1,92 @@
export interface RootObject {
result: Result
}
export interface Result {
summary: SuiteSummary
tests: Test[]
coverage: SuiteCoverage
}
export interface SuiteCoverage {
coverage?: Coverage[]
records: ApexRecord[]
summary: SuiteCoverageSummary
}
export interface SuiteCoverageSummary {
totalLines: number
coveredLines: number
orgWideCoverage: string
testRunCoverage: string
}
export interface ApexRecord {
ApexTestClass: ApexTestClass
Coverage: RecordCoverage
TestMethodName: string
NumLinesCovered: number
ApexClassOrTrigger: ApexTestClass
NumLinesUncovered: number
}
export interface RecordCoverage {
coveredLines: number[]
uncoveredLines: number[]
}
export interface ApexTestClass {
Id: string
Name: string
}
export interface Coverage {
id: string
name: string
totalLines: number
lines: Lines
totalCovered: number
coveredPercent: number
}
export type Lines = Record<number, number>
export interface Test {
Id: string
QueueItemId: string
StackTrace: null
Message: null
AsyncApexJobId: string
MethodName: string
Outcome: string
ApexClass: ApexClass
RunTime: number
FullName: string
}
export interface ApexClass {
Id: string
Name: string
NamespacePrefix: string
}
export interface SuiteSummary {
failRate: string
failing: number
hostname: string
orgId: string
outcome: string
passRate: string
passing: number
skipped: number
testRunId: string
testStartTime: string
testsRan: number
userId: string
username: string
commandTime: string
testExecutionTime: string
testTotalTime: string
orgWideCoverage: string
testRunCoverage: string
}