mirror of
https://github.com/dorny/test-reporter.git
synced 2025-12-16 22:37:09 +01:00
remove auto conversion of XML attributes based on value
This commit is contained in:
parent
1a9ca69197
commit
40b5f476c7
6 changed files with 44 additions and 55 deletions
|
|
@ -5,8 +5,8 @@ import {Annotation, FileContent, ParseOptions, TestResult} from '../parser-types
|
||||||
import {parseStringPromise} from 'xml2js'
|
import {parseStringPromise} from 'xml2js'
|
||||||
|
|
||||||
import {normalizeFilePath} from '../../utils/file-utils'
|
import {normalizeFilePath} from '../../utils/file-utils'
|
||||||
import {parseAttribute} from '../../utils/xml-utils'
|
|
||||||
import {Icon, fixEol} from '../../utils/markdown-utils'
|
import {Icon, fixEol} from '../../utils/markdown-utils'
|
||||||
|
import {parseIsoDate, parseNetDuration} from '../../utils/parse-utils'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
TestExecutionResult,
|
TestExecutionResult,
|
||||||
|
|
@ -70,9 +70,7 @@ export async function parseDotnetTrx(files: FileContent[], options: ParseOptions
|
||||||
async function getTrxReport(file: FileContent): Promise<TrxReport> {
|
async function getTrxReport(file: FileContent): Promise<TrxReport> {
|
||||||
core.info(`Parsing content of '${file.path}'`)
|
core.info(`Parsing content of '${file.path}'`)
|
||||||
try {
|
try {
|
||||||
return (await parseStringPromise(file.content, {
|
return (await parseStringPromise(file.content)) as TrxReport
|
||||||
attrValueProcessors: [parseAttribute]
|
|
||||||
})) as TrxReport
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error(`Invalid XML at ${file.path}\n\n${e}`)
|
throw new Error(`Invalid XML at ${file.path}\n\n${e}`)
|
||||||
}
|
}
|
||||||
|
|
@ -80,7 +78,7 @@ async function getTrxReport(file: FileContent): Promise<TrxReport> {
|
||||||
|
|
||||||
function getTestRunResult(path: string, trx: TrxReport, testClasses: TestClass[]): TestRunResult {
|
function getTestRunResult(path: string, trx: TrxReport, testClasses: TestClass[]): TestRunResult {
|
||||||
const times = trx.TestRun.Times[0].$
|
const times = trx.TestRun.Times[0].$
|
||||||
const totalTime = times.finish.getTime() - times.start.getTime()
|
const totalTime = parseIsoDate(times.finish).getTime() - parseIsoDate(times.start).getTime()
|
||||||
|
|
||||||
const suites = testClasses.map(tc => {
|
const suites = testClasses.map(tc => {
|
||||||
const tests = tc.tests.map(t => new TestCaseResult(t.name, t.result, t.duration))
|
const tests = tc.tests.map(t => new TestCaseResult(t.name, t.result, t.duration))
|
||||||
|
|
@ -113,7 +111,8 @@ function getTestClasses(trx: TrxReport): TestClass[] {
|
||||||
}
|
}
|
||||||
const output = r.unitTestResult.Output
|
const output = r.unitTestResult.Output
|
||||||
const error = output?.length > 0 && output[0].ErrorInfo?.length > 0 ? output[0].ErrorInfo[0] : undefined
|
const error = output?.length > 0 && output[0].ErrorInfo?.length > 0 ? output[0].ErrorInfo[0] : undefined
|
||||||
const test = new Test(r.testMethod.$.name, r.unitTestResult.$.outcome, r.unitTestResult.$.duration, error)
|
const duration = parseNetDuration(r.unitTestResult.$.duration)
|
||||||
|
const test = new Test(r.testMethod.$.name, r.unitTestResult.$.outcome, duration, error)
|
||||||
tc.tests.push(test)
|
tc.tests.push(test)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,10 @@ export interface TestRun {
|
||||||
|
|
||||||
export interface Times {
|
export interface Times {
|
||||||
$: {
|
$: {
|
||||||
creation: Date
|
creation: string
|
||||||
queuing: Date
|
queuing: string
|
||||||
start: Date
|
start: string
|
||||||
finish: Date
|
finish: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,7 +43,7 @@ export interface UnitTestResult {
|
||||||
$: {
|
$: {
|
||||||
testId: string
|
testId: string
|
||||||
testName: string
|
testName: string
|
||||||
duration: number
|
duration: string
|
||||||
outcome: Outcome
|
outcome: Outcome
|
||||||
}
|
}
|
||||||
Output: Output[]
|
Output: Output[]
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ import {parseStringPromise} from 'xml2js'
|
||||||
import {JunitReport, TestCase, TestSuite} from './jest-junit-types'
|
import {JunitReport, TestCase, TestSuite} from './jest-junit-types'
|
||||||
import {fixEol, Icon} from '../../utils/markdown-utils'
|
import {fixEol, Icon} from '../../utils/markdown-utils'
|
||||||
import {normalizeFilePath} from '../../utils/file-utils'
|
import {normalizeFilePath} from '../../utils/file-utils'
|
||||||
import {parseAttribute} from '../../utils/xml-utils'
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
TestExecutionResult,
|
TestExecutionResult,
|
||||||
|
|
@ -43,9 +42,7 @@ export async function parseJestJunit(files: FileContent[], options: ParseOptions
|
||||||
async function getJunitReport(file: FileContent): Promise<JunitReport> {
|
async function getJunitReport(file: FileContent): Promise<JunitReport> {
|
||||||
core.info(`Parsing content of '${file.path}'`)
|
core.info(`Parsing content of '${file.path}'`)
|
||||||
try {
|
try {
|
||||||
return (await parseStringPromise(file.content, {
|
return (await parseStringPromise(file.content)) as JunitReport
|
||||||
attrValueProcessors: [parseAttribute]
|
|
||||||
})) as JunitReport
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error(`Invalid XML at ${file.path}\n\n${e}`)
|
throw new Error(`Invalid XML at ${file.path}\n\n${e}`)
|
||||||
}
|
}
|
||||||
|
|
@ -54,12 +51,12 @@ async function getJunitReport(file: FileContent): Promise<JunitReport> {
|
||||||
function getTestRunResult(path: string, junit: JunitReport): TestRunResult {
|
function getTestRunResult(path: string, junit: JunitReport): TestRunResult {
|
||||||
const suites = junit.testsuites.testsuite.map(ts => {
|
const suites = junit.testsuites.testsuite.map(ts => {
|
||||||
const name = ts.$.name.trim()
|
const name = ts.$.name.trim()
|
||||||
const time = ts.$.time * 1000
|
const time = parseFloat(ts.$.time) * 1000
|
||||||
const sr = new TestSuiteResult(name, getGroups(ts), time)
|
const sr = new TestSuiteResult(name, getGroups(ts), time)
|
||||||
return sr
|
return sr
|
||||||
})
|
})
|
||||||
|
|
||||||
const time = junit.testsuites.$.time * 1000
|
const time = parseFloat(junit.testsuites.$.time) * 1000
|
||||||
return new TestRunResult(path, suites, time)
|
return new TestRunResult(path, suites, time)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -78,7 +75,7 @@ function getGroups(suite: TestSuite): TestGroupResult[] {
|
||||||
const tests = grp.tests.map(tc => {
|
const tests = grp.tests.map(tc => {
|
||||||
const name = tc.$.name.trim()
|
const name = tc.$.name.trim()
|
||||||
const result = getTestCaseResult(tc)
|
const result = getTestCaseResult(tc)
|
||||||
const time = tc.$.time * 1000
|
const time = parseFloat(tc.$.time) * 1000
|
||||||
return new TestCaseResult(name, result, time)
|
return new TestCaseResult(name, result, time)
|
||||||
})
|
})
|
||||||
return new TestGroupResult(grp.describe, tests)
|
return new TestGroupResult(grp.describe, tests)
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,10 @@ export interface JunitReport {
|
||||||
export interface TestSuites {
|
export interface TestSuites {
|
||||||
$: {
|
$: {
|
||||||
name: string
|
name: string
|
||||||
tests: number
|
tests: string
|
||||||
failures: number // assertion failed
|
failures: string // assertion failed
|
||||||
errors: number // unhandled exception during test execution
|
errors: string // unhandled exception during test execution
|
||||||
time: number
|
time: string
|
||||||
}
|
}
|
||||||
testsuite: TestSuite[]
|
testsuite: TestSuite[]
|
||||||
}
|
}
|
||||||
|
|
@ -16,11 +16,11 @@ export interface TestSuites {
|
||||||
export interface TestSuite {
|
export interface TestSuite {
|
||||||
$: {
|
$: {
|
||||||
name: string
|
name: string
|
||||||
tests: number
|
tests: string
|
||||||
errors: number
|
errors: string
|
||||||
failures: number
|
failures: string
|
||||||
skipped: number
|
skipped: string
|
||||||
time: number
|
time: string
|
||||||
timestamp?: Date
|
timestamp?: Date
|
||||||
}
|
}
|
||||||
testcase: TestCase[]
|
testcase: TestCase[]
|
||||||
|
|
@ -31,7 +31,7 @@ export interface TestCase {
|
||||||
classname: string
|
classname: string
|
||||||
file?: string
|
file?: string
|
||||||
name: string
|
name: string
|
||||||
time: number
|
time: string
|
||||||
}
|
}
|
||||||
failure?: string[]
|
failure?: string[]
|
||||||
skipped?: string[]
|
skipped?: string[]
|
||||||
|
|
|
||||||
20
src/utils/parse-utils.ts
Normal file
20
src/utils/parse-utils.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
export function parseNetDuration(str: string): number {
|
||||||
|
// matches dotnet duration: 00:00:00.0010000
|
||||||
|
const durationRe = /^(\d\d):(\d\d):(\d\d\.\d+)$/
|
||||||
|
const durationMatch = str.match(durationRe)
|
||||||
|
if (durationMatch === null) {
|
||||||
|
throw new Error(`Invalid format: "${str}" is not NET duration`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const [_, hourStr, minStr, secStr] = durationMatch
|
||||||
|
return (parseInt(hourStr) * 3600 + parseInt(minStr) * 60 + parseFloat(secStr)) * 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseIsoDate(str: string): Date {
|
||||||
|
const isoDateRe = /^\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)$/
|
||||||
|
if (str === undefined || !isoDateRe.test(str)) {
|
||||||
|
throw new Error(`Invalid format: "${str}" is not ISO date`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Date(str)
|
||||||
|
}
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
const isoDateRe = /^\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)$/
|
|
||||||
|
|
||||||
// matches dotnet duration: 00:00:00.0010000
|
|
||||||
const durationRe = /^(\d\d):(\d\d):(\d\d\.\d+)$/
|
|
||||||
|
|
||||||
export function parseAttribute(str: string | undefined): string | Date | number | undefined {
|
|
||||||
if (str === '' || str === undefined) {
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isoDateRe.test(str)) {
|
|
||||||
return new Date(str)
|
|
||||||
}
|
|
||||||
|
|
||||||
const durationMatch = str.match(durationRe)
|
|
||||||
if (durationMatch !== null) {
|
|
||||||
const [_, hourStr, minStr, secStr] = durationMatch
|
|
||||||
return (parseInt(hourStr) * 3600 + parseInt(minStr) * 60 + parseFloat(secStr)) * 1000
|
|
||||||
}
|
|
||||||
|
|
||||||
const num = parseFloat(str)
|
|
||||||
if (isNaN(num)) {
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
return num
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue