Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for testcase.attachments #51

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/helpers/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@ const FORCED_ARRAY_KEYS = [
"testng-results.suite.test.class.test-method",
"testng-results.suite.test.class.test-method.exception",
"TestRun.Results.UnitTestResult",
"TestRun.Results.UnitTestResult.ResultFiles.ResultFile",
"TestRun.TestDefinitions.UnitTest",
"TestRun.TestDefinitions.UnitTest.TestCategory.TestCategoryItem",
"TestRun.TestDefinitions.UnitTest.Properties.Property"
"TestRun.TestDefinitions.UnitTest.Properties.Property",
"TestRun.TestDefinitions.UnitTest.TestCategory.TestCategoryItem"
];

const configured_parser = new XMLParser({
Expand All @@ -51,6 +52,7 @@ const configured_parser = new XMLParser({
case "property":
case "test-suite":
case "test-case":
case "attachment":
return true;
default:
return false;
Expand Down
8 changes: 8 additions & 0 deletions src/models/TestAttachment.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
declare class TestAttachment {
name: string;
path: string;
}

declare namespace TestAttachment { }

export = TestAttachment;
10 changes: 10 additions & 0 deletions src/models/TestAttachment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class TestAttachment {

constructor() {
this.name = '';
this.path = '';
}

}

module.exports = TestAttachment;
2 changes: 2 additions & 0 deletions src/models/TestCase.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as TestStep from './TestStep';
import * as TestAttachment from './TestAttachment';

declare class TestCase {
name: string;
Expand All @@ -12,6 +13,7 @@ declare class TestCase {
failure: string;
stack_trace: string;
steps: TestStep[];
attachments: TestAttachment[];
meta_data: Map<string,string>;

setFailure: SetFailureFunction
Expand Down
1 change: 1 addition & 0 deletions src/models/TestCase.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class TestCase {
this.failure = '';
this.stack_trace = '';
this.steps = [];
this.attachments = [];
this.meta_data = new Map();
}

Expand Down
33 changes: 32 additions & 1 deletion src/parsers/junit.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ const { getJsonFromXMLFile } = require('../helpers/helper');
const TestResult = require('../models/TestResult');
const TestSuite = require('../models/TestSuite');
const TestCase = require('../models/TestCase');
const TestAttachment = require('../models/TestAttachment');
const { Test } = require('mocha');

function getTestCase(rawCase) {
const test_case = new TestCase();
test_case.name = rawCase["@_name"];
test_case.duration = rawCase["@_time"] * 1000;
setMetaData(rawCase.properties, test_case);
setAttachments(rawCase, test_case);
setMetaData(rawCase.properties, test_case);
if (rawCase.failure && rawCase.failure.length > 0) {
test_case.status = 'FAIL';
test_case.setFailure(rawCase.failure[0]["@_message"]);
Expand Down Expand Up @@ -59,6 +62,34 @@ function setMetaData(properties, test_element) {
}
}

/**
* @param {import('./junit.result').JUnitTestCase} rawCase
* @param {TestCase} test_element
*/
function setAttachments(rawCase, test_element) {
if (rawCase['system.out']) {
const systemOut = rawCase['system.out'];

// junit attachments plug syntax is [[ATTACHMENT|/absolute/path/to/file.png]]
const regex = new RegExp('\\[\\[ATTACHMENT\\|([^\\]]+)\\]\\]', 'g');

while ((m = regex.exec(systemOut)) !== null) {
// avoid infinite loops with zero-width matches
if (m.index === regex.lastIndex) {
regex.lastIndex++;
}

let filePath = m[1].trim();

if (filePath.length > 0) {
const attachment = new TestAttachment();
attachment.path = filePath;
test_element.attachments.push(attachment);
}
}
}
}

/**
* @param {TestResult} result
*/
Expand Down
3 changes: 2 additions & 1 deletion src/parsers/junit.result.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ export type JUnitTestCase = {
'@_id': string;
'@_name': string;
'@_time': number;
'system.out': string;
}

export type TestSuite = {
export type JUnitTestSuite = {
properties?: JUnitProperties;
testcase: JUnitTestCase[];
'@_id': string;
Expand Down
41 changes: 39 additions & 2 deletions src/parsers/mstest.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
const { getJsonFromXMLFile } = require('../helpers/helper');
const path = require('path');

const TestResult = require('../models/TestResult');
const TestSuite = require('../models/TestSuite');
const TestCase = require('../models/TestCase');
const TestAttachment = require('../models/TestAttachment');

const RESULT_MAP = {
Passed: "PASS",
Expand Down Expand Up @@ -39,6 +41,27 @@ function populateMetaData(rawElement, map) {
}
}

function populateAttachments(rawResultElement, attachments, testRunName) {

// attachments are in /TestRun/Results/UnitTestResult/ResultFiles/ResultFile[@path]
if (rawResultElement.ResultFiles && rawResultElement.ResultFiles.ResultFile) {
let executionId = rawResultElement["@_executionId"];
let rawAttachments = rawResultElement.ResultFiles.ResultFile;
for (let i = 0; i < rawAttachments.length; i++) {
let filePath = rawAttachments[i]["@_path"];
if (filePath) {

// file path is relative to testresults.trx
// stored in ./<testrunname>/in/<executionId>/path

let attachment = new TestAttachment();
attachment.path = path.join(testRunName, "In", executionId, ...(filePath.split(/[\\/]/g)));
attachments.push(attachment);
}
}
}
}

function getTestResultDuration(rawTestResult) {
// durations are represented in a timeformat with 7 digit microsecond precision
// TODO: introduce d3-time-format after https://github.com/test-results-reporter/parser/issues/42 is fixed.
Expand Down Expand Up @@ -68,7 +91,16 @@ function getTestSuiteName(testCase) {
return testCase.name.substring(0, index);
}

function getTestCase(rawTestResult, definitionMap) {
function getTestRunName(rawTestRun) {
// testrun.name contains '@', spaces and ':'
let name = rawTestRun["@_name"];
if (name) {
return name.replace(/[ @:]/g, '_');
}
return '';
}

function getTestCase(rawTestResult, definitionMap, testRunName) {
let id = rawTestResult["@_testId"];

if (definitionMap.has(id)) {
Expand All @@ -85,6 +117,8 @@ function getTestCase(rawTestResult, definitionMap) {
testCase.setFailure(rawTestResult.Output.ErrorInfo.Message);
testCase.stack_trace = rawTestResult.Output.ErrorInfo.StackTrace ?? '';
}
// populate attachments
populateAttachments(rawTestResult, testCase.attachments, testRunName);
// populate meta
populateMetaData(rawDefinition, testCase.meta_data);

Expand Down Expand Up @@ -126,6 +160,9 @@ function getTestResults(rawTestResults) {
}

function getTestSuites(rawTestRun) {

// test attachments are stored in a testrun specific folder <name>/in/<executionid>/<computername>
const testRunName = getTestRunName(rawTestRun);
// outcomes + durations are stored in /TestRun/TestResults/*
const testResults = getTestResults(rawTestRun.Results);
// test names and details are stored in /TestRun/TestDefinitions/*
Expand All @@ -137,7 +174,7 @@ function getTestSuites(rawTestRun) {

for (let i = 0; i < testResults.length; i++) {
let rawTestResult = testResults[i];
let testCase = getTestCase(rawTestResult, testDefinitions);
let testCase = getTestCase(rawTestResult, testDefinitions, testRunName);
let suiteName = getTestSuiteName(testCase);

if (!suiteMap.has(suiteName)) {
Expand Down
17 changes: 17 additions & 0 deletions src/parsers/nunit.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const { getJsonFromXMLFile } = require('../helpers/helper');
const TestResult = require('../models/TestResult');
const TestSuite = require('../models/TestSuite');
const TestCase = require('../models/TestCase');
const TestAttachment = require('../models/TestAttachment');

const SUITE_TYPES_WITH_TEST_CASES = [
"TestFixture",
Expand All @@ -24,6 +25,20 @@ const RESULT_MAP = {
Skipped: "SKIP", // v3
}

function populateAttachments(rawCase, attachments) {
if (rawCase.attachments && rawCase.attachments.attachment) {
let rawAttachments = rawCase.attachments.attachment;
for (var i = 0; i < rawAttachments.length; i++) {
var attachment = new TestAttachment();
attachment.path = rawAttachments[i].filePath;
if (rawAttachments[i].description) {
attachment.name = rawAttachments[i].description;
}
attachments.push(attachment);
}
}
}

function mergeMeta(map1, map2) {
for (let kvp of map1) {
map2.set(kvp[0], kvp[1]);
Expand Down Expand Up @@ -130,6 +145,8 @@ function getTestCases(rawSuite, parent_meta) {
testCase.stack_trace = errorDetails["stack-trace"]
}
}
// populate attachments
populateAttachments(rawCase, testCase.attachments);
// copy parent_meta data to test case
mergeMeta(parent_meta, testCase.meta_data);
populateMetaData(rawCase, testCase.meta_data);
Expand Down
29 changes: 29 additions & 0 deletions tests/data/junit/test-case-attachments.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8" ?>
<testsuites id="id" name="result name" tests="1" failures="1" errors="" time="10">
<testsuite id="testsuite" name="suite name" tests="1" failures="1" time="10">
<!-- single attachment -->
<testcase id="testsuite.withattachment" name="include attachments" time="10">
<system.out>[[ATTACHMENT|/path/to/attachment.png]]
</system.out>
</testcase>

<!-- multiple attachments-->
<testcase id="testsuite.withattachmentmultiple" name="include multiple attachments" time="10">
<system.out>[[ATTACHMENT|/path/to/attachment1.png]]
Other output
[[ATTACHMENT|/path/to/attachment2.png]]
</system.out>
</testcase>

<!-- no attachments -->
<testcase id="testsuite.testcase" name="with no attachments" time="10">
</testcase>

<!-- zero length attachments -->
<testcase id="testsuite.malformedattachment" name="malformed" time="10">
<system.out>[[ATTACHMENT| ]]
</system.out>
</testcase>

</testsuite>
</testsuites>
32 changes: 31 additions & 1 deletion tests/data/mstest/testresults.trx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
<Deployment runDeploymentRoot="bryan.b.cook_MYCOMPUTER_2023-11-12_19_21_51" />
</TestSettings>
<Results>

<!-- MockTestFixture -->
<UnitTestResult executionId="e01a54d8-305c-4828-9bc0-8c935270dafe" testId="5370a586-74b0-a647-4839-100eef3a4188" testName="FailingTest" computerName="MYCOMPUTER" duration="00:00:00.0259239" startTime="2023-11-12T19:21:51.6583003-05:00" endTime="2023-11-12T19:21:51.6978162-05:00" testType="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b" outcome="Failed" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" relativeResultsDirectory="e01a54d8-305c-4828-9bc0-8c935270dafe">
<Output>
<ErrorInfo>
Expand Down Expand Up @@ -50,11 +52,25 @@ System.NotImplementedException: The method or operation is not implemented.</Mes
</UnitTestResult>
<UnitTestResult executionId="05c69927-e30b-4b5c-a5b8-2e44e9a88785" testId="c09170d3-a997-9a92-b6d5-9a86a4225591" testName="TestWithManyProperties" computerName="MYCOMPUTER" duration="00:00:00.0000490" startTime="2023-11-12T19:21:51.7069158-05:00" endTime="2023-11-12T19:21:51.7071317-05:00" testType="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b" outcome="Passed" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" relativeResultsDirectory="05c69927-e30b-4b5c-a5b8-2e44e9a88785" />

<!-- FixtureWithTestCases -->
<UnitTestResult executionId="0942eb3b-2633-4c82-aa3a-374236dc6dab" testId="dac0f7bf-eced-b506-706a-66ed33c7be60" testName="TestWithArg (1)" computerName="MYCOMPUTER" duration="00:00:00.0000574" startTime="2023-11-12T19:21:51.7314236-05:00" endTime="2023-11-12T19:21:51.7362844-05:00" testType="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b" outcome="Passed" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" relativeResultsDirectory="0942eb3b-2633-4c82-aa3a-374236dc6dab" />


<!-- FixtureWithTestCaseAttachments -->
<UnitTestResult executionId="238c26aa-9963-4623-aeda-95ef9a6799d0" testId="40ee30b0-1c98-09a4-5020-0b5d9d182630" testName="AddOneAttachment" computerName="MYCOMPUTER" duration="00:00:00.1221116" startTime="2024-02-28T14:30:39.4675415-05:00" endTime="2024-02-28T14:30:39.6333725-05:00" testType="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b" outcome="Passed" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" relativeResultsDirectory="238c26aa-9963-4623-aeda-95ef9a6799d0">
<ResultFiles>
<ResultFile path="MYCOMPUTER/dummy1.txt" />
</ResultFiles>
</UnitTestResult>
<UnitTestResult executionId="2b2b0f58-3d88-432c-9955-1040315f96e9" testId="b63c50f5-d54a-4566-894b-c15b37bea962" testName="AddTwoAttachment" computerName="MYCOMPUTER" duration="00:00:00.1221116" startTime="2024-02-28T14:30:39.4675415-05:00" endTime="2024-02-28T14:30:39.6333725-05:00" testType="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b" outcome="Passed" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" relativeResultsDirectory="2b2b0f58-3d88-432c-9955-1040315f96e9">
<ResultFiles>
<ResultFile path="MYCOMPUTER/dummy2.txt" />
<ResultFile path="MYCOMPUTER/dummy3.txt" />
</ResultFiles>
</UnitTestResult>
</Results>
<TestDefinitions>

<!-- MockTestFixture -->
<UnitTest name="FailingTest" storage="c:\dev\code\_experiments\nunitsample\mstestsample\bin\debug\net6.0\mstestsample.dll" id="5370a586-74b0-a647-4839-100eef3a4188">
<TestCategory>
<TestCategoryItem TestCategory="FixtureCategory" />
Expand Down Expand Up @@ -122,10 +138,23 @@ System.NotImplementedException: The method or operation is not implemented.</Mes
<Execution id="05c69927-e30b-4b5c-a5b8-2e44e9a88785" />
<TestMethod codeBase="C:\dev\code\_Experiments\MSTestSample\bin\Debug\net6.0\MSTestSample.dll" adapterTypeName="executor://mstestadapter/v2" className="MSTestSample.MockTestFixture" name="TestWithManyProperties" />
</UnitTest>

<!-- FixtureWithTestCases -->
<UnitTest name="TestWithArg (1)" storage="c:\dev\code\_experiments\nunitsample\mstestsample\bin\debug\net6.0\mstestsample.dll" id="dac0f7bf-eced-b506-706a-66ed33c7be60">
<Execution id="0942eb3b-2633-4c82-aa3a-374236dc6dab" />
<TestMethod codeBase="C:\dev\code\_Experiments\MSTestSample\bin\Debug\net6.0\MSTestSample.dll" adapterTypeName="executor://mstestadapter/v2" className="MSTestSample.FixtureWithTestCases" name="TestWithArg" />
</UnitTest>

<!-- FixtureWithTestCaseAttachments -->
<UnitTest name="AddOneAttachment" storage="c:\dev\code\_experiments\nunitsample\mstestsample\bin\debug\net6.0\mstestsample.dll" id="40ee30b0-1c98-09a4-5020-0b5d9d182630">
<Execution id="238c26aa-9963-4623-aeda-95ef9a6799d0" />
<TestMethod codeBase="C:\dev\code\_Experiments\NUnitSample\MSTestSample\bin\Debug\net6.0\MSTestSample.dll" adapterTypeName="executor://mstestadapter/v2" className="MSTestSample.FixtureWithTestCaseAttachments" name="AddOneAttachment" />
</UnitTest>
<UnitTest name="AddTwoAttachment" storage="c:\dev\code\_experiments\nunitsample\mstestsample\bin\debug\net6.0\mstestsample.dll" id="b63c50f5-d54a-4566-894b-c15b37bea962">
<Execution id="2b2b0f58-3d88-432c-9955-1040315f96e9" />
<TestMethod codeBase="C:\dev\code\_Experiments\NUnitSample\MSTestSample\bin\Debug\net6.0\MSTestSample.dll" adapterTypeName="executor://mstestadapter/v2" className="MSTestSample.FixtureWithTestCaseAttachments" name="AddTwoAttachment" />
</UnitTest>

</TestDefinitions>
<TestEntries>
<TestEntry testId="c09170d3-a997-9a92-b6d5-9a86a4225591" executionId="05c69927-e30b-4b5c-a5b8-2e44e9a88785" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
Expand All @@ -138,6 +167,7 @@ System.NotImplementedException: The method or operation is not implemented.</Mes
<TestEntry testId="0b5c3ebb-3086-951a-1724-1821d30aef04" executionId="68e29212-0297-4843-8f1e-cad3dfe2dd06" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
<TestEntry testId="20aced79-1a63-4e66-d444-ee4575c65515" executionId="685aeaf1-023c-4eb1-8727-fc6516eb1b61" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
<TestEntry testId="6b0e8fc2-5324-dbeb-0c5f-0479cd14f351" executionId="4db5da11-faef-4e70-98d1-39ee00d6ed00" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
<TestEntry testId="40ee30b0-1c98-09a4-5020-0b5d9d182630" executionId="238c26aa-9963-4623-aeda-95ef9a6799d0" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
</TestEntries>
<TestLists>
<TestList name="Results Not in a List" id="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
Expand Down
10 changes: 10 additions & 0 deletions tests/data/nunit/nunit_v3.xml
Original file line number Diff line number Diff line change
Expand Up @@ -121,5 +121,15 @@
<test-suite type="TestFixture" id="1014" name="MockTestFixture" fullname="NUnit.Tests.TestAssembly.MockTestFixture" testcasecount="1" result="Passed" time="0.001" total="1" passed="1" failed="0" inconclusive="0" skipped="0" asserts="0">
<test-case id="1015" name="MyTest" fullname="NUnit.Tests.TestAssembly.MockTestFixture.MyTest" result="Passed" time="0.001" asserts="0" />
</test-suite>
<test-suite type="TestFixture" id="1037" name="AttachmentsFixture" fullname="NUnit.Tests.TestAssembly.AttachmentsFixture" testcasecount="1" result="Passed" time="0.001" total="1" passed="1" failed="0" inconclusive="0" skipped="0" asserts="0">
<test-case id="1038" name="TestOneAttachment" fullname="NUnit.Tests.TestAssembly.AttachmentsFixture.TestOneAttachment" result="Passed" time="0.001" asserts="0">
<attachments>
<attachment>
<filePath>c:\absolute\filepath\dummy.txt</filePath>
<description><![CDATA[my description]]></description>
</attachment>
</attachments>
</test-case>
</test-suite>
</test-suite>
</test-run>
4 changes: 4 additions & 0 deletions tests/parser.cucumber.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ describe('Parser - Cucumber Json', () => {
meta_data: newMap({ tags: "blue,slow", suite: "1234", tagsRaw: "@blue,@slow" }),
cases: [
{
attachments: [],
duration: 1.59,
errors: 0,
failed: 0,
Expand Down Expand Up @@ -97,6 +98,7 @@ describe('Parser - Cucumber Json', () => {
meta_data: new Map(),
cases: [
{
attachments: [],
duration: 2.56,
errors: 0,
failed: 0,
Expand All @@ -112,6 +114,7 @@ describe('Parser - Cucumber Json', () => {
total: 0
},
{
attachments: [],
duration: 0.28,
errors: 0,
failed: 0,
Expand Down Expand Up @@ -141,6 +144,7 @@ describe('Parser - Cucumber Json', () => {
meta_data: new Map(),
cases: [
{
attachments: [],
duration: 0.52,
errors: 0,
failed: 0,
Expand Down
Loading
Loading