add support for karma-junit-reporter

This commit is contained in:
George Young 2025-11-18 16:51:07 +13:00
parent aef3d726a6
commit 08646e4d97
17 changed files with 12441 additions and 1 deletions

View file

@ -22,6 +22,7 @@ import {RspecJsonParser} from './parsers/rspec-json/rspec-json-parser'
import {SwiftXunitParser} from './parsers/swift-xunit/swift-xunit-parser'
import {normalizeDirPath, normalizeFilePath} from './utils/path-utils'
import {getCheckRunContext} from './utils/github-utils'
import {KarmaJunitParser} from './parsers/karma-junit/karma-junit-parser'
async function main(): Promise<void> {
try {
@ -269,6 +270,9 @@ class TestReporter {
return new JavaJunitParser(options)
case 'jest-junit':
return new JestJunitParser(options)
case 'karma-junit':
return new KarmaJunitParser(options)
case 'mocha-json':
return new MochaJsonParser(options)
case 'python-xunit':

View file

@ -0,0 +1,125 @@
import {ParseOptions, TestParser} from '../../test-parser'
import {parseStringPromise} from 'xml2js'
import {KarmaReport, TestCase, TestSuite} from './karma-junit-types'
import {getExceptionSource} from '../../utils/node-utils'
import {getBasePath, normalizeFilePath} from '../../utils/path-utils'
import {
TestExecutionResult,
TestRunResult,
TestSuiteResult,
TestGroupResult,
TestCaseResult,
TestCaseError
} from '../../test-results'
export class KarmaJunitParser implements TestParser {
assumedWorkDir: string | undefined
constructor(readonly options: ParseOptions) {}
async parse(path: string, content: string): Promise<TestRunResult> {
const ju = await this.getJunitReport(path, content)
return this.getTestRunResult(path, ju)
}
private async getJunitReport(path: string, content: string): Promise<KarmaReport> {
try {
return (await parseStringPromise(content)) as KarmaReport
} catch (e) {
throw new Error(`Invalid XML at ${path}\n\n${e}`)
}
}
private getTestRunResult(path: string, karma: KarmaReport): TestRunResult {
const suites =
karma.testsuite === undefined
? []
: [karma.testsuite].map(ts => {
const name = this.escapeCharacters(ts.$.name.trim())
const time = parseFloat(ts.$.time) * 1000
const sr = new TestSuiteResult(name, this.getGroups(ts), time)
return sr
})
const time = karma.testsuite?.$ && parseFloat(karma.testsuite.$.time) * 1000
return new TestRunResult(path, suites, time)
}
protected getGroups(suite: TestSuite): TestGroupResult[] {
if (!suite.testcase) {
return []
}
const groups: {describe: string; tests: TestCase[]}[] = []
for (const tc of suite.testcase) {
let grp = groups.find(g => g.describe === tc.$.classname)
if (grp === undefined) {
grp = {describe: tc.$.classname, tests: []}
groups.push(grp)
}
grp.tests.push(tc)
}
return groups.map(grp => {
const tests = grp.tests.map(tc => {
const name = tc.$.name.trim()
const result = this.getTestCaseResult(tc)
const time = parseFloat(tc.$.time) * 1000
const error = this.getTestCaseError(tc)
return new TestCaseResult(name, result, time, error)
})
return new TestGroupResult(grp.describe, tests)
})
}
private getTestCaseResult(test: TestCase): TestExecutionResult {
if (test.failure) return 'failed'
if (test.skipped) return 'skipped'
return 'success'
}
private getTestCaseError(tc: TestCase): TestCaseError | undefined {
if (!this.options.parseErrors || !tc.failure) {
return undefined
}
const details = typeof tc.failure[0] === 'string' ? tc.failure[0] : tc.failure[0]['_']
let path
let line
const src = getExceptionSource(details, this.options.trackedFiles, file => this.getRelativePath(file))
if (src) {
path = src.path
line = src.line
}
return {
path,
line,
details
}
}
private getRelativePath(path: string): string {
path = normalizeFilePath(path)
const workDir = this.getWorkDir(path)
if (workDir !== undefined && path.startsWith(workDir)) {
path = path.substr(workDir.length)
}
return path
}
private getWorkDir(path: string): string | undefined {
return (
this.options.workDir ??
this.assumedWorkDir ??
(this.assumedWorkDir = getBasePath(path, this.options.trackedFiles))
)
}
protected escapeCharacters(s: string): string {
return s.replace(/([<>])/g, '\\$1')
}
}

View file

@ -0,0 +1,34 @@
export interface KarmaReport {
testsuite: TestSuite
}
export interface TestSuites {
$: {
time: string
}
testsuite?: TestSuite[]
}
export interface TestSuite {
$: {
name: string
tests: string
errors: string
failures: string
skipped: string
time: string
timestamp?: Date
}
testcase?: TestCase[]
}
export interface TestCase {
$: {
classname: string
file?: string
name: string
time: string
}
failure?: string[]
skipped?: string[]
}