From c4b64b0cf4430276b4143aa4aa1ba3968f57e761 Mon Sep 17 00:00:00 2001 From: Michal Dorner Date: Thu, 14 Jan 2021 21:39:51 +0100 Subject: [PATCH] Add annotations support to dotnet-trx --- .../__snapshots__/dotnet-trx.test.ts.snap | 31 ++++++++++- src/parsers/dotnet-trx/dotnet-trx-parser.ts | 52 +++++++++++++++++-- 2 files changed, 77 insertions(+), 6 deletions(-) diff --git a/__tests__/__snapshots__/dotnet-trx.test.ts.snap b/__tests__/__snapshots__/dotnet-trx.test.ts.snap index c2e72fd..8d2e26c 100644 --- a/__tests__/__snapshots__/dotnet-trx.test.ts.snap +++ b/__tests__/__snapshots__/dotnet-trx.test.ts.snap @@ -2,7 +2,36 @@ exports[`dotnet-trx tests matches report snapshot 1`] = ` Object { - "annotations": Array [], + "annotations": Array [ + Object { + "annotation_level": "failure", + "end_line": 9, + "message": "System.DivideByZeroException : Attempted to divide by zero.", + "path": "DotnetTests.Unit/Calculator.cs", + "start_line": 9, + "title": "[DotnetTests.XUnitTests.CalculatorTests] Exception_In_TargetTest", + }, + Object { + "annotation_level": "failure", + "end_line": 39, + "message": "System.Exception : Test", + "path": "DotnetTests.XUnitTests/CalculatorTests.cs", + "start_line": 39, + "title": "[DotnetTests.XUnitTests.CalculatorTests] Exception_In_Test", + }, + Object { + "annotation_level": "failure", + "end_line": 27, + "message": "Assert.Equal() Failure + +Expected: 3 + +Actual: 2", + "path": "DotnetTests.XUnitTests/CalculatorTests.cs", + "start_line": 27, + "title": "[DotnetTests.XUnitTests.CalculatorTests] Failing_Test", + }, + ], "summary": "**7** tests were completed in **1.061s** with **3** passed, **1** skipped and **3** failed. | Result | Suite | Tests | Time | Passed ✔️ | Failed ❌ | Skipped ✖️ | | :---: | :--- | ---: | ---: | ---: | ---: | ---: | diff --git a/src/parsers/dotnet-trx/dotnet-trx-parser.ts b/src/parsers/dotnet-trx/dotnet-trx-parser.ts index a1ac221..8db88c7 100644 --- a/src/parsers/dotnet-trx/dotnet-trx-parser.ts +++ b/src/parsers/dotnet-trx/dotnet-trx-parser.ts @@ -3,6 +3,7 @@ import {ErrorInfo, Outcome, TestMethod, TrxReport} from './dotnet-trx-types' import {Annotation, ParseOptions, TestResult} from '../parser-types' import {parseStringPromise} from 'xml2js' +import {normalizeFilePath} from '../../utils/file-utils' import {parseAttribute} from '../../utils/xml-utils' import {Icon} from '../../utils/markdown-utils' @@ -55,9 +56,7 @@ export async function parseDotnetTrx(content: string, options: ParseOptions): Pr output: { title: `${options.name.trim()} ${icon}`, summary: getReport(testRun), - annotations: options.annotations - ? getAnnotations(/*testClasses, options.workDir, options.trackedFiles*/) - : undefined + annotations: options.annotations ? getAnnotations(testClasses, options.workDir, options.trackedFiles) : undefined } } } @@ -110,6 +109,49 @@ function getTestClasses(trx: TrxReport): TestClass[] { return result } -function getAnnotations(/*testClasses: TestClass[], workDir: string, trackedFiles: string[]*/): Annotation[] { - return [] +function getAnnotations(testClasses: TestClass[], workDir: string, trackedFiles: string[]): Annotation[] { + const annotations: Annotation[] = [] + for (const tc of testClasses) { + for (const t of tc.tests) { + if (t.error) { + const src = exceptionThrowSource(t.error.StackTrace[0], workDir, trackedFiles) + if (src === null) { + continue + } + annotations.push({ + annotation_level: 'failure', + start_line: src.line, + end_line: src.line, + path: src.file, + message: t.error.Message[0], + title: `[${tc.name}] ${t.name}` + }) + } + } + } + return annotations +} + +export function exceptionThrowSource( + ex: string, + workDir: string, + trackedFiles: string[] +): {file: string; line: number} | null { + const lines = ex.split(/\r*\n/) + const re = / in (.+):line (\d+)$/ + + for (const str of lines) { + const match = str.match(re) + if (match !== null) { + const [_, fileStr, lineStr] = match + const filePath = normalizeFilePath(fileStr) + const file = filePath.startsWith(workDir) ? filePath.substr(workDir.length) : filePath + if (trackedFiles.includes(file)) { + const line = parseInt(lineStr) + return {file, line} + } + } + } + + return null }