diff --git a/src/parsers/junit.js b/src/parsers/junit.js index 7a4995c..7cf795f 100644 --- a/src/parsers/junit.js +++ b/src/parsers/junit.js @@ -4,14 +4,15 @@ const TestResult = require('../models/TestResult'); const TestSuite = require('../models/TestSuite'); const TestCase = require('../models/TestCase'); const TestAttachment = require('../models/TestAttachment'); -const { Test } = require('mocha'); +const JUnitTypes = import('./junit.result'); -function getTestCase(rawCase) { +function getTestCase(rawCase, suite_meta) { const test_case = new TestCase(); test_case.name = rawCase["@_name"]; test_case.duration = rawCase["@_time"] * 1000; + test_case.meta_data = new Map(suite_meta); setAttachments(rawCase, test_case); - setMetaData(rawCase.properties, test_case); + setMetaData(rawCase, test_case); if (rawCase.failure && rawCase.failure.length > 0) { test_case.status = 'FAIL'; test_case.setFailure(rawCase.failure[0]["@_message"]); @@ -38,11 +39,11 @@ function getTestSuite(rawSuite) { suite.passed = suite.total - suite.failed - suite.errors; suite.duration = rawSuite["@_time"] * 1000; suite.status = suite.total === suite.passed ? 'PASS' : 'FAIL'; - setMetaData(rawSuite.properties, suite); + setMetaData(rawSuite, suite); const raw_test_cases = rawSuite.testcase; if (raw_test_cases) { for (let i = 0; i < raw_test_cases.length; i++) { - suite.cases.push(getTestCase(raw_test_cases[i])); + suite.cases.push(getTestCase(raw_test_cases[i], suite.meta_data)); } } return suite; @@ -50,16 +51,22 @@ function getTestSuite(rawSuite) { /** * - * @param {import('./junit.result').JUnitProperties} properties + * @param {JUnitTypes.TestSuite | JUnitTypes.JUnitTestCase} rawElement * @param {TestCase | TestSuite} test_element */ -function setMetaData(properties, test_element) { - if (properties && properties.property.length > 0) { - const raw_properties = properties.property; +function setMetaData(rawElement, test_element) { + if (rawElement.properties && rawElement.properties.property.length > 0) { + const raw_properties = rawElement.properties.property; for (const raw_property of raw_properties) { test_element.meta_data.set(raw_property["@_name"], raw_property["@_value"]); } } + // handle testsuite specific attributes + if (test_element instanceof TestSuite) { + if (rawElement["@_hostname"]) { + test_element.meta_data.set("hostname", rawElement["@_hostname"]); + } + } } /** diff --git a/src/parsers/junit.result.d.ts b/src/parsers/junit.result.d.ts index ac3c9ee..c293cca 100644 --- a/src/parsers/junit.result.d.ts +++ b/src/parsers/junit.result.d.ts @@ -30,6 +30,7 @@ export type JUnitTestSuite = { '@_tests': number; '@_failures': number; '@_time': number; + '@_hostname': string; } export type JUnitResult = { diff --git a/tests/data/junit/playwright.xml b/tests/data/junit/playwright.xml new file mode 100644 index 0000000..ba0fedc --- /dev/null +++ b/tests/data/junit/playwright.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/parser.junit.spec.js b/tests/parser.junit.spec.js index d7bee05..a799184 100644 --- a/tests/parser.junit.spec.js +++ b/tests/parser.junit.spec.js @@ -357,90 +357,90 @@ describe('Parser - JUnit', () => { it('parse newman with failures', () => { const result = parse({ type: 'junit', files: [`${testDataPath}/newman-failures.xml`] }); assert.deepEqual(result, { - "id": "", - "name": "MainApi", - "total": 3, - "passed": 1, - "failed": 2, - "errors": 0, - "skipped": 0, - "retried": 0, - "duration": 37506, - "status": "FAIL", - "suites": [ + id: "", + name: "MainApi", + total: 3, + passed: 1, + failed: 2, + errors: 0, + skipped: 0, + retried: 0, + duration: 37506, + status: "FAIL", + suites: [ { - "id": "", - "name": "Main / GetSectors", - "total": 2, - "passed": 0, - "failed": 2, - "errors": 0, - "skipped": 0, - "duration": 446, - "status": "FAIL", - "meta_data": new Map(), - "cases": [ + id: "", + name: "Main / GetSectors", + total: 2, + passed: 0, + failed: 2, + errors: 0, + skipped: 0, + duration: 446, + status: "FAIL", + meta_data: new Map(), + cases: [ { - "attachments": [], - "id": "", - "name": "Sectors - Verify 'Residential' is in list", - "total": 0, - "passed": 0, - "failed": 0, - "errors": 0, - "skipped": 0, - "duration": 446, - "status": "FAIL", - "failure": "expected to include 'Residntial'", - "stack_trace": "", - "meta_data": new Map(), - "steps": [] + id: "", + name: "Sectors - Verify 'Residential' is in list", + total: 0, + passed: 0, + failed: 0, + errors: 0, + skipped: 0, + duration: 446, + status: "FAIL", + failure: "expected to include 'Residntial'", + stack_trace: "", + meta_data: new Map(), + attachments: [], + steps: [] }, { - "attachments": [], - "id": "", - "name": "Sectors EndPoint - returns a JSON response", - "total": 0, - "passed": 0, - "failed": 0, - "errors": 0, - "skipped": 0, - "duration": 446, - "status": "PASS", - "failure": "", - "stack_trace": "", - "meta_data": new Map(), - "steps": [] + id: "", + name: "Sectors EndPoint - returns a JSON response", + total: 0, + passed: 0, + failed: 0, + errors: 0, + skipped: 0, + duration: 446, + status: "PASS", + failure: "", + stack_trace: "", + meta_data: new Map(), + attachments: [], + steps: [] } ] }, { - "id": "", - "name": "Main / Verifyresponsedata-MarketAsset", - "total": 1, - "passed": 1, - "failed": 0, - "errors": 0, - "skipped": 0, - "duration": 634, - "status": "PASS", - "meta_data": new Map(), - "cases": [ + id: "", + name: "Main / Verifyresponsedata-MarketAsset", + total: 1, + passed: 1, + failed: 0, + errors: 0, + skipped: 0, + duration: 634, + status: "PASS", + meta_data: new Map(), + cases: [ { - "attachments": [], - "id": "", - "name": "Market Asset(id-387) response - data is as expected", - "total": 0, - "passed": 0, - "failed": 0, - "errors": 0, - "skipped": 0, - "duration": 634, - "status": "PASS", - "failure": "", - "stack_trace": "", - "meta_data": new Map(), - "steps": [] + id: "", + name: "Market Asset(id-387) response - data is as expected", + total: 0, + passed: 0, + failed: 0, + errors: 0, + skipped: 0, + duration: 634, + status: "PASS", + failure: "", + stack_trace: "", + meta_data: new Map(), + attachments: [], + steps: [] } ] } @@ -450,93 +450,94 @@ describe('Parser - JUnit', () => { it('parse spekt/junit.testlogger', () => { const result = parse({ type: 'junit', files: [`${testDataPath}/junit.testlogger.xml`] }); + var inheritedProperties = new Map([ ["hostname", "REDACTED"] ]); assert.deepEqual(result, { - "id": "", - "name": "", - "total": 3, - "passed": 2, - "failed": 1, - "errors": 0, - "skipped": 1, - "retried": 0, - "duration": 870.6800000000001, - "status": "FAIL", - "suites": [ + id: "", + name: "", + total: 3, + passed: 2, + failed: 1, + errors: 0, + skipped: 1, + retried: 0, + duration: 870.6800000000001, + status: "FAIL", + suites: [ { - "id": "", - "name": "JUnit.Xml.TestLogger.NetCore.Tests.dll", - "total": 3, - "passed": 2, - "failed": 1, - "errors": 0, - "skipped": 1, - "duration": 870.6800000000001, - "status": "FAIL", - "meta_data": new Map(), - "cases": [ + id: "", + name: "JUnit.Xml.TestLogger.NetCore.Tests.dll", + total: 3, + passed: 2, + failed: 1, + errors: 0, + skipped: 1, + duration: 870.6800000000001, + status: "FAIL", + meta_data: inheritedProperties, + cases: [ { - "attachments": [], - "id": "", - "name": "TestD", - "total": 0, - "passed": 0, - "failed": 0, - "errors": 0, - "skipped": 0, - "duration": 2.195, - "status": "PASS", - "failure": "", - "stack_trace": "", - "meta_data": new Map(), - "steps": [] + id: "", + name: "TestD", + total: 0, + passed: 0, + failed: 0, + errors: 0, + skipped: 0, + duration: 2.195, + status: "PASS", + failure: "", + stack_trace: "", + meta_data: inheritedProperties, + attachments: [], + steps: [] }, { - "attachments": [], - "id": "", - "name": "TestC", - "total": 0, - "passed": 0, - "failed": 0, - "errors": 0, - "skipped": 0, - "duration": 1.109, - "status": "FAIL", - "failure": "TearDown : System.InvalidOperationException : Operation is not valid due to the current state of the object.", - "stack_trace": "", - "meta_data": new Map(), - "steps": [] + id: "", + name: "TestC", + total: 0, + passed: 0, + failed: 0, + errors: 0, + skipped: 0, + duration: 1.109, + status: "FAIL", + failure: "TearDown : System.InvalidOperationException : Operation is not valid due to the current state of the object.", + stack_trace: "", + meta_data: inheritedProperties, + attachments: [], + steps: [] }, { - "attachments": [], - "id": "", - "name": "InconclusiveTest", - "total": 0, - "passed": 0, - "failed": 0, - "errors": 0, - "skipped": 0, - "duration": 0.7200000000000001, - "status": "PASS", - "failure": "", - "stack_trace": "", - "meta_data": new Map(), - "steps": [] + id: "", + name: "InconclusiveTest", + total: 0, + passed: 0, + failed: 0, + errors: 0, + skipped: 0, + duration: 0.7200000000000001, + status: "PASS", + failure: "", + stack_trace: "", + meta_data: inheritedProperties, + attachments: [], + steps: [] }, { - "attachments": [], - "id": "", - "name": "Ignored", - "total": 0, - "passed": 0, - "failed": 0, - "errors": 0, - "skipped": 0, - "duration": 0.29500000000000004, - "status": "PASS", - "failure": "", - "stack_trace": "", - "meta_data": new Map(), - "steps": [] + id: "", + name: "Ignored", + total: 0, + passed: 0, + failed: 0, + errors: 0, + skipped: 0, + duration: 0.29500000000000004, + status: "PASS", + failure: "", + stack_trace: "", + meta_data: inheritedProperties, + attachments: [], + steps: [] } ] } @@ -577,13 +578,35 @@ describe('Parser - JUnit', () => { assert.notEqual(null, result2); }); - it('meta-data from suite copied to testcase', () => { + it('meta-data from suite merged with testcase', () => { const result = parse({ type: 'junit', files: ['tests/data/junit/multiple-suites-properties.xml'] }); + + // confirm that suite level properties exist and are accurate assert.equal(result.suites[0].meta_data.size, 2); assert.equal(result.suites[0].meta_data.get("key1"), "value1"); assert.equal(result.suites[0].meta_data.get("key2"), "value2"); - assert.equal(result.suites[0].cases[0].meta_data.size, 1); - assert.equal(result.suites[0].cases[0].meta_data.get("key1"), "override-value1"); + + // confirm that the suite level properties were inherited into the test case and overridden if present + assert.equal(result.suites[0].cases[0].meta_data.size, 2); + assert.equal(result.suites[0].cases[0].meta_data.get("key1"), "override-value1"); // testcase value + assert.equal(result.suites[0].cases[0].meta_data.get("key2"), "value2"); // suite value + }); + + it('include hostname in meta-data from suite and testcase', () => { + const result = parse({ type: 'junit', files: ['tests/data/junit/playwright.xml'] }); + + assert.equal(result.suites[0].meta_data.get("hostname"), "chromium"); + assert.equal(result.suites[0].cases[0].meta_data.get("hostname"), "chromium"); + assert.equal(result.suites[0].cases[1].meta_data.get("hostname"), "chromium"); + + assert.equal(result.suites[1].meta_data.get("hostname"), "firefox"); + assert.equal(result.suites[1].cases[0].meta_data.get("hostname"), "firefox"); + assert.equal(result.suites[1].cases[1].meta_data.get("hostname"), "firefox"); + + assert.equal(result.suites[2].meta_data.get("hostname"), "webkit"); + assert.equal(result.suites[2].cases[0].meta_data.get("hostname"), "webkit"); + assert.equal(result.suites[2].cases[1].meta_data.get("hostname"), "webkit"); + }); it('parse system.out to locate attachments', () => {