mirror of
https://github.com/dorny/test-reporter.git
synced 2025-12-17 06:47:09 +01:00
Refactoring & cleanup of whole codebase
Improves report summary and annotations
This commit is contained in:
parent
07a0223ee3
commit
60b35d601a
20 changed files with 38784 additions and 33667 deletions
111
src/report/get-annotations.ts
Normal file
111
src/report/get-annotations.ts
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
import {ellipsis, fixEol} from '../utils/markdown-utils'
|
||||
import {TestRunResult} from '../test-results'
|
||||
|
||||
type Annotation = {
|
||||
path: string
|
||||
start_line: number
|
||||
end_line: number
|
||||
start_column?: number
|
||||
end_column?: number
|
||||
annotation_level: 'notice' | 'warning' | 'failure'
|
||||
message: string
|
||||
title?: string
|
||||
raw_details?: string
|
||||
}
|
||||
|
||||
interface TestError {
|
||||
testRunPaths: string[]
|
||||
suiteName: string
|
||||
testName: string
|
||||
path: string
|
||||
line: number
|
||||
stackTrace: string
|
||||
message: string
|
||||
}
|
||||
|
||||
export function getAnnotations(results: TestRunResult[], maxCount: number): Annotation[] {
|
||||
if (maxCount === 0) {
|
||||
return []
|
||||
}
|
||||
|
||||
// Collect errors from TestRunResults
|
||||
// Merge duplicates if there are more test results files processed
|
||||
const errors: TestError[] = []
|
||||
const mergeDup = results.length > 1
|
||||
for (const tr of results) {
|
||||
for (const ts of tr.suites) {
|
||||
for (const tg of ts.groups) {
|
||||
for (const tc of tg.tests) {
|
||||
const err = tc.error
|
||||
if (err === undefined) {
|
||||
continue
|
||||
}
|
||||
const path = err.path ?? tr.path
|
||||
const line = err.line ?? 0
|
||||
if (mergeDup) {
|
||||
const dup = errors.find(e => path === e.path && line === e.line && err.stackTrace === e.stackTrace)
|
||||
if (dup !== undefined) {
|
||||
dup.testRunPaths.push(tr.path)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
errors.push({
|
||||
testRunPaths: [tr.path],
|
||||
suiteName: ts.name,
|
||||
testName: tc.name,
|
||||
stackTrace: err.stackTrace,
|
||||
message: err.message ?? getFirstNonEmptyLine(err.stackTrace) ?? 'Test failed',
|
||||
path,
|
||||
line
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Limit number of created annotations
|
||||
errors.splice(maxCount + 1)
|
||||
|
||||
const annotations = errors.map(e => {
|
||||
const message = [
|
||||
'Failed test found in:',
|
||||
e.testRunPaths.map(p => ` ${p}`).join('\n'),
|
||||
'Error:',
|
||||
ident(fixEol(e.message), ' ')
|
||||
].join('\n')
|
||||
|
||||
return enforceCheckRunLimits({
|
||||
path: e.path,
|
||||
start_line: e.line,
|
||||
end_line: e.line,
|
||||
annotation_level: 'failure',
|
||||
title: `${e.suiteName} ► ${e.testName}`,
|
||||
raw_details: fixEol(e.stackTrace),
|
||||
message
|
||||
})
|
||||
})
|
||||
|
||||
return annotations
|
||||
}
|
||||
|
||||
function enforceCheckRunLimits(err: Annotation): Annotation {
|
||||
err.title = ellipsis(err.title || '', 255)
|
||||
err.message = ellipsis(err.message, 65535)
|
||||
if (err.raw_details) {
|
||||
err.raw_details = ellipsis(err.raw_details, 65535)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
function getFirstNonEmptyLine(stackTrace: string): string | undefined {
|
||||
const lines = stackTrace.split(/\r?\n/g)
|
||||
return lines.find(str => !/^\s*$/.test(str))
|
||||
}
|
||||
|
||||
function ident(text: string, prefix: string): string {
|
||||
return text
|
||||
.split(/\n/g)
|
||||
.map(line => prefix + line)
|
||||
.join('\n')
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import * as core from '@actions/core'
|
||||
import {TestExecutionResult, TestRunResult, TestSuiteResult} from './test-results'
|
||||
import {TestExecutionResult, TestRunResult, TestSuiteResult} from '../test-results'
|
||||
import {Align, formatTime, Icon, link, table} from '../utils/markdown-utils'
|
||||
import {slug} from '../utils/slugger'
|
||||
|
||||
|
|
@ -112,7 +112,7 @@ function getSuitesReport(tr: TestRunResult, runIndex: number, options: ReportOpt
|
|||
const icon = getResultIcon(tr.result)
|
||||
sections.push(`## ${nameLink} ${icon}`)
|
||||
|
||||
const time = `${(tr.time / 1000).toFixed(3)}s`
|
||||
const time = formatTime(tr.time)
|
||||
const headingLine2 = `**${tr.tests}** tests were completed in **${time}** with **${tr.passed}** passed, **${tr.failed}** failed and **${tr.skipped}** skipped.`
|
||||
sections.push(headingLine2)
|
||||
|
||||
|
|
@ -161,7 +161,8 @@ function getTestsReport(ts: TestSuiteResult, runIndex: number, suiteIndex: numbe
|
|||
const icon = getResultIcon(ts.result)
|
||||
sections.push(`### ${tsNameLink} ${icon}`)
|
||||
|
||||
const headingLine2 = `**${ts.tests}** tests were completed in **${ts.time}ms** with **${ts.passed}** passed, **${ts.failed}** failed and **${ts.skipped}** skipped.`
|
||||
const tsTime = formatTime(ts.time)
|
||||
const headingLine2 = `**${ts.tests}** tests were completed in **${tsTime}** with **${ts.passed}** passed, **${ts.failed}** failed and **${ts.skipped}** skipped.`
|
||||
sections.push(headingLine2)
|
||||
|
||||
for (const grp of groups) {
|
||||
|
|
|
|||
|
|
@ -1,89 +0,0 @@
|
|||
export class TestRunResult {
|
||||
constructor(readonly path: string, readonly suites: TestSuiteResult[], private totalTime?: number) {}
|
||||
|
||||
get tests(): number {
|
||||
return this.suites.reduce((sum, g) => sum + g.tests, 0)
|
||||
}
|
||||
|
||||
get passed(): number {
|
||||
return this.suites.reduce((sum, g) => sum + g.passed, 0)
|
||||
}
|
||||
get failed(): number {
|
||||
return this.suites.reduce((sum, g) => sum + g.failed, 0)
|
||||
}
|
||||
get skipped(): number {
|
||||
return this.suites.reduce((sum, g) => sum + g.skipped, 0)
|
||||
}
|
||||
|
||||
get time(): number {
|
||||
return this.totalTime ?? this.suites.reduce((sum, g) => sum + g.time, 0)
|
||||
}
|
||||
|
||||
get result(): TestExecutionResult {
|
||||
return this.suites.some(t => t.result === 'failed') ? 'failed' : 'success'
|
||||
}
|
||||
|
||||
get failedSuites(): TestSuiteResult[] {
|
||||
return this.suites.filter(s => s.result === 'failed')
|
||||
}
|
||||
}
|
||||
|
||||
export class TestSuiteResult {
|
||||
constructor(readonly name: string, readonly groups: TestGroupResult[], private totalTime?: number) {}
|
||||
|
||||
get tests(): number {
|
||||
return this.groups.reduce((sum, g) => sum + g.tests.length, 0)
|
||||
}
|
||||
|
||||
get passed(): number {
|
||||
return this.groups.reduce((sum, g) => sum + g.passed, 0)
|
||||
}
|
||||
get failed(): number {
|
||||
return this.groups.reduce((sum, g) => sum + g.failed, 0)
|
||||
}
|
||||
get skipped(): number {
|
||||
return this.groups.reduce((sum, g) => sum + g.skipped, 0)
|
||||
}
|
||||
get time(): number {
|
||||
return this.totalTime ?? this.groups.reduce((sum, g) => sum + g.time, 0)
|
||||
}
|
||||
|
||||
get result(): TestExecutionResult {
|
||||
return this.groups.some(t => t.result === 'failed') ? 'failed' : 'success'
|
||||
}
|
||||
|
||||
get failedGroups(): TestGroupResult[] {
|
||||
return this.groups.filter(grp => grp.result === 'failed')
|
||||
}
|
||||
}
|
||||
|
||||
export class TestGroupResult {
|
||||
constructor(readonly name: string | undefined | null, readonly tests: TestCaseResult[]) {}
|
||||
|
||||
get passed(): number {
|
||||
return this.tests.reduce((sum, t) => (t.result === 'success' ? sum + 1 : sum), 0)
|
||||
}
|
||||
get failed(): number {
|
||||
return this.tests.reduce((sum, t) => (t.result === 'failed' ? sum + 1 : sum), 0)
|
||||
}
|
||||
get skipped(): number {
|
||||
return this.tests.reduce((sum, t) => (t.result === 'skipped' ? sum + 1 : sum), 0)
|
||||
}
|
||||
get time(): number {
|
||||
return this.tests.reduce((sum, t) => sum + t.time, 0)
|
||||
}
|
||||
|
||||
get result(): TestExecutionResult {
|
||||
return this.tests.some(t => t.result === 'failed') ? 'failed' : 'success'
|
||||
}
|
||||
|
||||
get failedTests(): TestCaseResult[] {
|
||||
return this.tests.filter(tc => tc.result === 'failed')
|
||||
}
|
||||
}
|
||||
|
||||
export class TestCaseResult {
|
||||
constructor(readonly name: string, readonly result: TestExecutionResult, readonly time: number) {}
|
||||
}
|
||||
|
||||
export type TestExecutionResult = 'success' | 'skipped' | 'failed' | undefined
|
||||
Loading…
Add table
Add a link
Reference in a new issue