From e3c18be5628725a405318df7b052ae6b640b5ce2 Mon Sep 17 00:00:00 2001 From: Bryan Cook <3217452+bryanbcook@users.noreply.github.com> Date: Sat, 11 Nov 2023 01:46:26 -0500 Subject: [PATCH] Add metadata to TestCase (#35) * add meta-data to testcase #34 * xUnit implementation + tests * mocha-test-explorer extension config support * junit implementation * cucumber implementation * mocha implementation * testng implementation * strip tag indicator character from mocha + cucumber. add support for key/value tags --- package.json | 3 + src/helpers/helper.js | 4 + src/models/TestCase.d.ts | 1 + src/models/TestCase.js | 1 + src/parsers/cucumber.js | 15 ++++ src/parsers/junit.js | 20 ++++- src/parsers/mocha.js | 16 ++++ src/parsers/testng.js | 59 +++++++++++-- src/parsers/xunit.js | 7 ++ .../cucumber/single-suite-single-test.json | 8 ++ .../data/junit/multiple-suites-properties.xml | 24 ++++++ .../multiple-suites-multiple-tests-tags.json | 85 +++++++++++++++++++ tests/data/testng/groups.xml | 36 ++++++++ tests/data/xunit/multiple-suites.xml | 1 - tests/data/xunit/no-traits-suite.xml | 18 ++++ tests/data/xunit/skipped-suite.xml | 5 -- tests/parser.cucumber.spec.js | 12 +++ tests/parser.junit.spec.js | 22 +++++ tests/parser.mocha.spec.js | 41 +++++++++ tests/parser.testng.spec.js | 57 +++++++++++++ tests/parser.xunit.spec.js | 26 ++++++ 21 files changed, 446 insertions(+), 15 deletions(-) create mode 100644 tests/data/junit/multiple-suites-properties.xml create mode 100644 tests/data/mocha/json/multiple-suites-multiple-tests-tags.json create mode 100644 tests/data/testng/groups.xml create mode 100644 tests/data/xunit/no-traits-suite.xml diff --git a/package.json b/package.json index 433eb2f..49d3f1f 100644 --- a/package.json +++ b/package.json @@ -38,5 +38,8 @@ "devDependencies": { "c8": "^7.12.0", "mocha": "^10.0.0" + }, + "mocha": { + "spec": "tests/**/*.spec.js" } } diff --git a/src/helpers/helper.js b/src/helpers/helper.js index 5893bcc..7416c0e 100644 --- a/src/helpers/helper.js +++ b/src/helpers/helper.js @@ -17,13 +17,17 @@ const FORCED_ARRAY_KEYS = [ "testsuites.testsuite", "testsuites.testsuite.testcase", "testsuites.testsuite.testcase.failure", + "testsuites.testsuite.testcase.properties.property", "assemblies", "assemblies.assembly", "assemblies.assembly.collection", "assemblies.assembly.collection.test", "assemblies.assembly.collection.test.failure", + "assemblies.assembly.collection.test.traits.trait", "testng-results", "testng-results.suite", + "testng-results.suite.groups.group", + "testng-results.suite.groups.group.method", "testng-results.suite.test", "testng-results.suite.test.class", "testng-results.suite.test.class.test-method", diff --git a/src/models/TestCase.d.ts b/src/models/TestCase.d.ts index 81ace36..bfda0b2 100644 --- a/src/models/TestCase.d.ts +++ b/src/models/TestCase.d.ts @@ -12,6 +12,7 @@ declare class TestCase { failure: string; stack_trace: string; steps: TestStep[]; + meta_data: Map; } declare namespace TestCase { } diff --git a/src/models/TestCase.js b/src/models/TestCase.js index 6c40eb3..85ca61c 100644 --- a/src/models/TestCase.js +++ b/src/models/TestCase.js @@ -13,6 +13,7 @@ class TestCase { this.failure = ''; this.stack_trace = ''; this.steps = []; + this.meta_data = new Map(); } } diff --git a/src/parsers/cucumber.js b/src/parsers/cucumber.js index c155e36..d20b5d6 100644 --- a/src/parsers/cucumber.js +++ b/src/parsers/cucumber.js @@ -11,6 +11,21 @@ function getTestCase(rawCase) { const test_case = new TestCase(); test_case.name = rawCase["name"]; test_case.duration = rawCase["duration"]; + if (rawCase.tags && rawCase.tags.length > 0) { + const tagsArray = rawCase.tags; + let tags = []; + let rawTags = []; + for (let i = 0; i < tagsArray.length; i++) { + let rawTagName = tagsArray[i]["name"]; + let tag = rawTagName.substring(1).split("="); + let tagName = tag[0]; + test_case.meta_data.set(tagName, tag[1] ?? "") + tags.push(tagName); + rawTags.push(rawTagName); + } + test_case.meta_data.set("tags", tags.join(",")); + test_case.meta_data.set("tagsRaw", rawTags.join(",")); + } if (rawCase.state && rawCase.state === "failed") { test_case.status = 'FAIL'; test_case.failure = rawCase.errorStack; diff --git a/src/parsers/junit.js b/src/parsers/junit.js index fb012c3..094c104 100644 --- a/src/parsers/junit.js +++ b/src/parsers/junit.js @@ -4,10 +4,19 @@ const TestResult = require('../models/TestResult'); const TestSuite = require('../models/TestSuite'); const TestCase = require('../models/TestCase'); -function getTestCase(rawCase) { +function getTestCase(rawCase, suiteProperties) { const test_case = new TestCase(); test_case.name = rawCase["@_name"]; test_case.duration = rawCase["@_time"] * 1000; + for (let [key,value] of suiteProperties) { + test_case.meta_data.set(key, value); + } + if (rawCase.properties && rawCase.properties.property.length > 0) { + const raw_properties = rawCase.properties.property; + for (let i = 0; i < raw_properties.length; i++) { + test_case.meta_data.set(raw_properties[i]["@_name"], raw_properties[i]["@_value"]); + } + } if (rawCase.failure && rawCase.failure.length > 0) { test_case.status = 'FAIL'; test_case.failure = rawCase.failure[0]["@_message"]; @@ -34,10 +43,17 @@ function getTestSuite(rawSuite) { suite.passed = suite.total - suite.failed - suite.errors; suite.duration = rawSuite["@_time"] * 1000; suite.status = suite.total === suite.passed ? 'PASS' : 'FAIL'; + const properties = new Map(); + if (rawSuite.properties && rawSuite.properties.property.length > 0) { + const raw_properties = rawSuite.properties.property; + for (let i = 0; i < raw_properties.length; i++) { + properties.set(raw_properties[i]["@_name"], raw_properties[i]["@_value"]) + } + } 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], properties)); } } return suite; diff --git a/src/parsers/mocha.js b/src/parsers/mocha.js index eae0492..8754af0 100644 --- a/src/parsers/mocha.js +++ b/src/parsers/mocha.js @@ -11,6 +11,22 @@ function getTestCase(rawCase) { const test_case = new TestCase(); test_case.name = rawCase["title"]; test_case.duration = rawCase["duration"]; + const regexp = /([\@\#][^\s]*)/gm; // match @tag or #tag + let matches = [...test_case.name.matchAll(regexp)]; + if (matches.length > 0) { + let tags = []; + let rawTags = []; + for (let match of matches) { + let rawTag = match[0]; + let tag = rawTag.substring(1).split("="); + let tagName = tag[0]; + test_case.meta_data.set(tagName, tag[1] ?? ""); + tags.push(tagName); + rawTags.push(rawTag); + } + test_case.meta_data.set("tags", tags.join(",")); + test_case.meta_data.set("tagsRaw", rawTags.join(",")); + } if (rawCase["state"] == "pending") { test_case.status = 'SKIP'; } diff --git a/src/parsers/testng.js b/src/parsers/testng.js index 60c420e..3eb1738 100644 --- a/src/parsers/testng.js +++ b/src/parsers/testng.js @@ -4,11 +4,46 @@ const TestResult = require('../models/TestResult'); const TestSuite = require('../models/TestSuite'); const TestCase = require('../models/TestCase'); -function getTestCase(rawCase) { +// assemble a fully qualified test name (class.name) +function getFullTestName(raw) { + return "".concat(raw["@_class"], ".", raw["@_name"]); +} + +// create a mapping between fully qualified test name and and group +function getSuiteGroups(rawSuite) { + let testCaseToGroupMap = new Map(); + + if (rawSuite.groups && rawSuite.groups.group.length > 0) { + let raw_groups = rawSuite.groups.group; + for (let i = 0; i < raw_groups.length; i++) { + let group_methods = raw_groups[i].method; + let groupName = raw_groups[i]["@_name"]; + for (let j = 0; j < group_methods.length; j++) { + let method = group_methods[j]; + let key = getFullTestName(method); + if (!testCaseToGroupMap.has(key)) { + testCaseToGroupMap.set(key, []); + } + testCaseToGroupMap.get(key).push(groupName); + } + } + } + return testCaseToGroupMap; +} + +function getTestCase(rawCase, testCaseToGroupMap) { const test_case = new TestCase(); test_case.name = rawCase["@_name"]; test_case.duration = rawCase["@_duration-ms"]; test_case.status = rawCase["@_status"]; + const key = getFullTestName(rawCase); + if (testCaseToGroupMap.has(key)) { + let groups = testCaseToGroupMap.get(key); + test_case.meta_data.set("groups", groups.join(",")); + groups.forEach(group => { + test_case.meta_data.set(group, ""); + }) + } if (rawCase.exception) { test_case.failure = rawCase.exception[0].message; } @@ -18,14 +53,18 @@ function getTestCase(rawCase) { return test_case; } -function getTestSuiteFromTest(rawTest) { +function getTestSuiteFromTest(rawTest, testCaseToGroupMap) { const suite = new TestSuite(); suite.name = rawTest['@_name']; suite.duration = rawTest['@_duration-ms']; const rawTestMethods = []; const rawClasses = rawTest.class; for (let i = 0; i < rawClasses.length; i++) { - rawTestMethods.push(...rawClasses[i]['test-method'].filter(raw => !raw['@_is-config'])); + let testMethods = rawClasses[i]['test-method'].filter(raw => !raw['@_is-config']); + testMethods.forEach(testMethod => { + testMethod["@_class"] = rawClasses[i]["@_name"]; // push className onto test-method + }); + rawTestMethods.push(...testMethods); } suite.total = rawTestMethods.length; suite.passed = rawTestMethods.filter(test => test['@_status'] === 'PASS').length; @@ -38,7 +77,7 @@ function getTestSuiteFromTest(rawTest) { } suite.status = suite.total === suite.passed ? 'PASS' : 'FAIL'; for (let i = 0; i < rawTestMethods.length; i++) { - suite.cases.push(getTestCase(rawTestMethods[i])); + suite.cases.push(getTestCase(rawTestMethods[i], testCaseToGroupMap)); } return suite; } @@ -49,11 +88,16 @@ function getTestSuite(rawSuite) { suite.duration = rawSuite['@_duration-ms']; const rawTests = rawSuite.test; const rawTestMethods = []; + const testCaseToGroupMap = getSuiteGroups(rawSuite); for (let i = 0; i < rawTests.length; i++) { const rawTest = rawTests[i]; const rawClasses = rawTest.class; for (let j = 0; j < rawClasses.length; j++) { - rawTestMethods.push(...rawClasses[j]['test-method'].filter(raw => !raw['@_is-config'])); + let testMethods = rawClasses[i]['test-method'].filter(raw => !raw['@_is-config']); + testMethods.forEach(testMethod => { + testMethod["@_class"] = rawClasses[i]["@_name"]; // push className onto test-method + }); + rawTestMethods.push(...testMethods); } } suite.total = rawTestMethods.length; @@ -67,7 +111,7 @@ function getTestSuite(rawSuite) { } suite.status = suite.total === suite.passed ? 'PASS' : 'FAIL'; for (let i = 0; i < rawTestMethods.length; i++) { - suite.cases.push(getTestCase(rawTestMethods[i])); + suite.cases.push(getTestCase(rawTestMethods[i], testCaseToGroupMap)); } return suite; } @@ -107,12 +151,13 @@ function parse(file) { } } else if (suitesWithTests.length === 1) { const suite = suitesWithTests[0]; + const testCaseToGroupMap = getSuiteGroups(suite); result.name = suite['@_name']; result.duration = suite['@_duration-ms']; const rawTests = suite.test; const rawTestsWithClasses = rawTests.filter(_rawTest => _rawTest.class); for (let i = 0; i < rawTestsWithClasses.length; i++) { - result.suites.push(getTestSuiteFromTest(rawTestsWithClasses[i])); + result.suites.push(getTestSuiteFromTest(rawTestsWithClasses[i], testCaseToGroupMap)); } } else if (suitesWithTests.length === 0){ const suite = suites[0]; diff --git a/src/parsers/xunit.js b/src/parsers/xunit.js index 18448f1..790146f 100644 --- a/src/parsers/xunit.js +++ b/src/parsers/xunit.js @@ -19,6 +19,13 @@ function getTestCase(rawCase) { else { test_case.status = 'PASS'; } + if(rawCase.traits && rawCase.traits.trait && rawCase.traits.trait.length > 0) { + const traits = rawCase.traits.trait; + for(let i = 0; i < traits.length; i++) { + test_case.meta_data.set( traits[i]["@_name"], traits[i]["@_value"]); + } + } + return test_case; } diff --git a/tests/data/cucumber/single-suite-single-test.json b/tests/data/cucumber/single-suite-single-test.json index ba3a910..6a3e083 100644 --- a/tests/data/cucumber/single-suite-single-test.json +++ b/tests/data/cucumber/single-suite-single-test.json @@ -53,6 +53,14 @@ { "name": "@green", "line": 4 + }, + { + "name": "@fast", + "line": 4 + }, + { + "name": "@testCase=1234", + "line": 4 } ], "type": "scenario" diff --git a/tests/data/junit/multiple-suites-properties.xml b/tests/data/junit/multiple-suites-properties.xml new file mode 100644 index 0000000..99e8557 --- /dev/null +++ b/tests/data/junit/multiple-suites-properties.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + Some Text + + + + + + + Some Text + + + + \ No newline at end of file diff --git a/tests/data/mocha/json/multiple-suites-multiple-tests-tags.json b/tests/data/mocha/json/multiple-suites-multiple-tests-tags.json new file mode 100644 index 0000000..3907634 --- /dev/null +++ b/tests/data/mocha/json/multiple-suites-multiple-tests-tags.json @@ -0,0 +1,85 @@ +{ + "stats": { + "suites": 2, + "tests": 3, + "passes": 2, + "pending": 0, + "failures": 1, + "start": "2022-06-11T05:28:11.204Z", + "end": "2022-06-11T05:28:11.227Z", + "duration": 7 + }, + "tests": [ + { + "title": "sample test case @fast #1255", + "fullTitle": "Example Suite 1 sample test case @fast #1255", + "file": "", + "duration": 3, + "currentRetry": 0, + "speed": "fast", + "err": {} + }, + { + "title": "sample test case 2", + "fullTitle": "Example Suite 1 sample test case 2", + "file": "", + "duration": 1, + "currentRetry": 0, + "speed": "fast", + "err": {} + }, + { + "title": "sample test case #1234", + "fullTitle": "Example Suite 2 sample test case #1234", + "file": "", + "duration": 1, + "currentRetry": 0, + "err": { + "stack": "AssertionError [ERR_ASSERTION]: Dummy reason\n at Context. (tests\\parser.mocha.json.spec.js:7:12)\n at processImmediate (node:internal/timers:466:21)", + "message": "Dummy reason", + "generatedMessage": false, + "name": "AssertionError", + "code": "ERR_ASSERTION", + "operator": "fail" + } + } + ], + "pending": [], + "failures": [ + { + "title": "sample test case #1234", + "fullTitle": "Example Suite 2 sample test case #1234", + "file": "", + "duration": 1, + "currentRetry": 0, + "err": { + "stack": "AssertionError [ERR_ASSERTION]: Dummy reason\n at Context. (tests\\parser.mocha.json.spec.js:7:12)\n at processImmediate (node:internal/timers:466:21)", + "message": "Dummy reason", + "generatedMessage": false, + "name": "AssertionError", + "code": "ERR_ASSERTION", + "operator": "fail" + } + } + ], + "passes": [ + { + "title": "sample test case @fast #1255", + "fullTitle": "Example Suite 1 sample test case @fast #1255", + "file": "", + "duration": 3, + "currentRetry": 0, + "speed": "fast", + "err": {} + }, + { + "title": "sample test case 2", + "fullTitle": "Example Suite 1 sample test case 2", + "file": "", + "duration": 1, + "currentRetry": 0, + "speed": "fast", + "err": {} + } + ] +} \ No newline at end of file diff --git a/tests/data/testng/groups.xml b/tests/data/testng/groups.xml new file mode 100644 index 0000000..17e8998 --- /dev/null +++ b/tests/data/testng/groups.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/data/xunit/multiple-suites.xml b/tests/data/xunit/multiple-suites.xml index 8ca5f10..3dcece8 100644 --- a/tests/data/xunit/multiple-suites.xml +++ b/tests/data/xunit/multiple-suites.xml @@ -55,7 +55,6 @@ Test output - diff --git a/tests/data/xunit/no-traits-suite.xml b/tests/data/xunit/no-traits-suite.xml new file mode 100644 index 0000000..8404035 --- /dev/null +++ b/tests/data/xunit/no-traits-suite.xml @@ -0,0 +1,18 @@ + + + + + + + Test link information + + Example of a failure message + Long string with failure + + + + + + + + \ No newline at end of file diff --git a/tests/data/xunit/skipped-suite.xml b/tests/data/xunit/skipped-suite.xml index f5f3ec5..60ef999 100644 --- a/tests/data/xunit/skipped-suite.xml +++ b/tests/data/xunit/skipped-suite.xml @@ -8,11 +8,6 @@ - - - - - diff --git a/tests/parser.cucumber.spec.js b/tests/parser.cucumber.spec.js index 74f68c6..1dabe82 100644 --- a/tests/parser.cucumber.spec.js +++ b/tests/parser.cucumber.spec.js @@ -40,6 +40,7 @@ describe('Parser - Cucumber Json', () => { skipped: 0, stack_trace: "", status: "PASS", + meta_data: newMap({tags:"green,fast,testCase", green: "", fast: "", testCase: "1234", tagsRaw:"@green,@fast,@testCase=1234"}), steps: [], total: 0 } @@ -102,6 +103,7 @@ describe('Parser - Cucumber Json', () => { skipped: 0, stack_trace: "", status: "FAIL", + meta_data: new Map(), steps: [], total: 0 }, @@ -116,6 +118,7 @@ describe('Parser - Cucumber Json', () => { skipped: 0, stack_trace: "", status: "PASS", + meta_data: new Map(), steps: [], total: 0 } @@ -143,6 +146,7 @@ describe('Parser - Cucumber Json', () => { skipped: 0, stack_trace: "", status: "PASS", + meta_data: new Map(), steps: [], total: 0 } @@ -160,5 +164,13 @@ describe('Parser - Cucumber Json', () => { const result2 = parse({ type: 'cucumber', files: [ relativePath]}); assert.notEqual(null, result2); }); + + function newMap( obj ) { + let map = new Map(); + for (const property in obj) { + map.set( property, obj[property]); + } + return map; + } }); diff --git a/tests/parser.junit.spec.js b/tests/parser.junit.spec.js index 3a660f7..26d38f5 100644 --- a/tests/parser.junit.spec.js +++ b/tests/parser.junit.spec.js @@ -40,6 +40,7 @@ describe('Parser - JUnit', () => { skipped: 0, stack_trace: "", status: "FAIL", + meta_data: new Map(), steps: [], total: 0 } @@ -85,6 +86,7 @@ describe('Parser - JUnit', () => { skipped: 0, stack_trace: "", status: "PASS", + meta_data: new Map(), steps: [], total: 0 } @@ -130,6 +132,7 @@ describe('Parser - JUnit', () => { skipped: 0, stack_trace: "", status: "PASS", + meta_data: new Map(), steps: [], total: 0 } @@ -175,6 +178,7 @@ describe('Parser - JUnit', () => { skipped: 0, stack_trace: "", status: "FAIL", + meta_data: new Map(), steps: [], total: 0 } @@ -202,6 +206,7 @@ describe('Parser - JUnit', () => { skipped: 0, stack_trace: "", status: "FAIL", + meta_data: new Map(), steps: [], total: 0 } @@ -247,6 +252,7 @@ describe('Parser - JUnit', () => { skipped: 0, stack_trace: "", status: "FAIL", + meta_data: new Map(), steps: [], total: 0 } @@ -274,6 +280,7 @@ describe('Parser - JUnit', () => { skipped: 0, stack_trace: "", status: "FAIL", + meta_data: new Map(), steps: [], total: 0 } @@ -319,6 +326,7 @@ describe('Parser - JUnit', () => { skipped: 0, stack_trace: "", status: "PASS", + meta_data: new Map(), steps: [], total: 0 } @@ -365,6 +373,7 @@ describe('Parser - JUnit', () => { "status": "FAIL", "failure": "expected to include 'Residntial'", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -379,6 +388,7 @@ describe('Parser - JUnit', () => { "status": "PASS", "failure": "", "stack_trace": "", + "meta_data": new Map(), "steps": [] } ] @@ -406,6 +416,7 @@ describe('Parser - JUnit', () => { "status": "PASS", "failure": "", "stack_trace": "", + "meta_data": new Map(), "steps": [] } ] @@ -451,6 +462,7 @@ describe('Parser - JUnit', () => { "status": "PASS", "failure": "", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -465,6 +477,7 @@ describe('Parser - JUnit', () => { "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": [] }, { @@ -479,6 +492,7 @@ describe('Parser - JUnit', () => { "status": "PASS", "failure": "", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -493,6 +507,7 @@ describe('Parser - JUnit', () => { "status": "PASS", "failure": "", "stack_trace": "", + "meta_data": new Map(), "steps": [] } ] @@ -526,4 +541,11 @@ describe('Parser - JUnit', () => { const result2 = parse({ type: 'junit', files: [relativePath]}); assert.notEqual(null, result2); }); + + it('meta-data from suite copied to testcase', () => { + const result = parse({ type: 'junit', files: ['tests/data/junit/multiple-suites-properties.xml'] }); + assert.equal(result.suites[0].cases[0].meta_data.size, 2); + assert.equal(result.suites[0].cases[0].meta_data.get("key1"), "override-value1"); + }); + }); \ No newline at end of file diff --git a/tests/parser.mocha.spec.js b/tests/parser.mocha.spec.js index 9886524..8783713 100644 --- a/tests/parser.mocha.spec.js +++ b/tests/parser.mocha.spec.js @@ -40,6 +40,7 @@ describe('Parser - Mocha Json', () => { skipped: 0, stack_trace: "", status: "PASS", + meta_data: new Map(), steps: [], total: 0 } @@ -100,6 +101,7 @@ describe('Parser - Mocha Json', () => { skipped: 0, stack_trace: "", status: "PASS", + meta_data: new Map(), steps: [], total: 0 }, @@ -114,6 +116,7 @@ describe('Parser - Mocha Json', () => { skipped: 0, stack_trace: "", status: "SKIP", + meta_data: new Map(), steps: [], total: 0 } @@ -158,6 +161,7 @@ describe('Parser - Mocha Json', () => { skipped: 0, stack_trace: "", status: "PASS", + meta_data: new Map(), steps: [], total: 0 }, @@ -172,6 +176,7 @@ describe('Parser - Mocha Json', () => { skipped: 0, stack_trace: "", status: "PASS", + meta_data: new Map(), steps: [], total: 0 } @@ -199,6 +204,7 @@ describe('Parser - Mocha Json', () => { skipped: 0, stack_trace: "", status: "FAIL", + meta_data: new Map(), steps: [], total: 0 } @@ -207,6 +213,7 @@ describe('Parser - Mocha Json', () => { ] }); }); + it('can support absolute and relative file paths', () => { let relativePath = `${testDataPath}/single-suite-single-test.json`; let absolutePath = path.resolve(relativePath); @@ -215,6 +222,31 @@ describe('Parser - Mocha Json', () => { const result2 = parse({ type: 'mocha', files: [relativePath]}); assert.notEqual(null, result2); }); + + it('has multiple tags', () => { + const result = parse({ type: 'mocha', files: [`${testDataPath}/multiple-suites-multiple-tests-tags.json`] }); + let testcase = result.suites[0].cases[0]; + assert.equal(testcase.meta_data.has("tags"), true); + assert.equal(testcase.meta_data.get("tags"), "fast,1255") + assert.equal(testcase.meta_data.get("tagsRaw"), "@fast,#1255") + assert.equal(testcase.meta_data.has("fast"), true); + assert.equal(testcase.meta_data.has("1255"), true); + }); + + it('has single tag', () => { + const result = parse({ type: 'mocha', files: [`${testDataPath}/multiple-suites-multiple-tests-tags.json`] }); + let testcase = result.suites[1].cases[0]; + assert.equal(testcase.meta_data.has("tags"), true); + assert.equal(testcase.meta_data.get("tags"), "1234") + assert.equal(testcase.meta_data.get("tagsRaw"), "#1234") + assert.equal(testcase.meta_data.has("1234"), true); + }); + + it('does not include tags meta if no tags are present', () =>{ + const result = parse({ type: 'mocha', files: [`${testDataPath}/multiple-suites-multiple-tests-tags.json`] }); + let testcase = result.suites[0].cases[1]; + assert.equal(testcase.meta_data.has("tags"), false); + }); }); describe('Parser - Mocha Awesmome Json', () => { @@ -255,6 +287,7 @@ describe('Parser - Mocha Awesmome Json', () => { skipped: 0, stack_trace: "", status: "PASS", + meta_data: new Map(), steps: [], total: 0 } @@ -315,6 +348,7 @@ describe('Parser - Mocha Awesmome Json', () => { skipped: 0, stack_trace: "", status: "PASS", + meta_data: new Map(), steps: [], total: 0 }, @@ -329,6 +363,7 @@ describe('Parser - Mocha Awesmome Json', () => { skipped: 0, stack_trace: "", status: "SKIP", + meta_data: new Map(), steps: [], total: 0 } @@ -373,6 +408,7 @@ describe('Parser - Mocha Awesmome Json', () => { skipped: 0, stack_trace: "", status: "PASS", + meta_data: new Map(), steps: [], total: 0 }, @@ -387,6 +423,7 @@ describe('Parser - Mocha Awesmome Json', () => { skipped: 0, stack_trace: "", status: "PASS", + meta_data: new Map(), steps: [], total: 0 } @@ -414,6 +451,7 @@ describe('Parser - Mocha Awesmome Json', () => { skipped: 0, stack_trace: "", status: "FAIL", + meta_data: new Map(), steps: [], total: 0 } @@ -458,6 +496,7 @@ describe('Parser - Mocha Awesmome Json', () => { skipped: 0, stack_trace: "", status: "PASS", + meta_data: new Map(), steps: [], total: 0 }, @@ -472,6 +511,7 @@ describe('Parser - Mocha Awesmome Json', () => { skipped: 0, stack_trace: "", status: "PASS", + meta_data: new Map(), steps: [], total: 0 } @@ -499,6 +539,7 @@ describe('Parser - Mocha Awesmome Json', () => { skipped: 0, stack_trace: "", status: "FAIL", + meta_data: new Map(), steps: [], total: 0 } diff --git a/tests/parser.testng.spec.js b/tests/parser.testng.spec.js index 7c4c5e5..66b3637 100644 --- a/tests/parser.testng.spec.js +++ b/tests/parser.testng.spec.js @@ -41,6 +41,7 @@ describe('Parser - TestNG', () => { status: 'PASS', failure: '', stack_trace: '', + meta_data: new Map(), steps: [] }, { @@ -55,6 +56,7 @@ describe('Parser - TestNG', () => { status: 'PASS', failure: '', stack_trace: '', + meta_data: new Map(), steps: [] }, { @@ -69,6 +71,7 @@ describe('Parser - TestNG', () => { status: 'PASS', failure: '', stack_trace: '', + meta_data: new Map(), steps: [] }, { @@ -83,6 +86,7 @@ describe('Parser - TestNG', () => { status: 'PASS', failure: 'expected [true] but found [false]', stack_trace: '', + meta_data: new Map(), steps: [] } ] @@ -128,6 +132,7 @@ describe('Parser - TestNG', () => { "status": "FAIL", "failure": "expected [A] but found [948474]", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -142,6 +147,7 @@ describe('Parser - TestNG', () => { "status": "PASS", "failure": "", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -156,6 +162,7 @@ describe('Parser - TestNG', () => { "status": "PASS", "failure": "", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -170,6 +177,7 @@ describe('Parser - TestNG', () => { "status": "FAIL", "failure": "Expected condition failed: : 95ddbda01ea4b3dbcb049e681a6...}", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -184,6 +192,7 @@ describe('Parser - TestNG', () => { "status": "FAIL", "failure": "element click intercepted:", "stack_trace": "", + "meta_data": new Map(), "steps": [] } ] @@ -211,6 +220,7 @@ describe('Parser - TestNG', () => { "status": "FAIL", "failure": "expected [A] but found [948474]", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -225,6 +235,7 @@ describe('Parser - TestNG', () => { "status": "PASS", "failure": "", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -239,6 +250,7 @@ describe('Parser - TestNG', () => { "status": "PASS", "failure": "", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -253,6 +265,7 @@ describe('Parser - TestNG', () => { "status": "FAIL", "failure": "Appium error: An unknown sr='Search...']}", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -267,6 +280,7 @@ describe('Parser - TestNG', () => { "status": "SKIP", "failure": "A script did not complete ", "stack_trace": "", + "meta_data": new Map(), "steps": [] } ] @@ -312,6 +326,7 @@ describe('Parser - TestNG', () => { "status": "PASS", "failure": "", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -326,6 +341,7 @@ describe('Parser - TestNG', () => { "status": "PASS", "failure": "", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -340,6 +356,7 @@ describe('Parser - TestNG', () => { "status": "PASS", "failure": "", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -354,6 +371,7 @@ describe('Parser - TestNG', () => { "status": "PASS", "failure": "expected [true] but found [false]", "stack_trace": "", + "meta_data": new Map(), "steps": [] } ] @@ -399,6 +417,7 @@ describe('Parser - TestNG', () => { "status": "PASS", "failure": "", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -413,6 +432,7 @@ describe('Parser - TestNG', () => { "status": "PASS", "failure": "", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -427,6 +447,7 @@ describe('Parser - TestNG', () => { "status": "PASS", "failure": "", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -441,6 +462,7 @@ describe('Parser - TestNG', () => { "status": "PASS", "failure": "expected [true] but found [false]", "stack_trace": "", + "meta_data": new Map(), "steps": [] } ] @@ -468,6 +490,7 @@ describe('Parser - TestNG', () => { "status": "PASS", "failure": "", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -482,6 +505,7 @@ describe('Parser - TestNG', () => { "status": "PASS", "failure": "", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -496,6 +520,7 @@ describe('Parser - TestNG', () => { "status": "PASS", "failure": "", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -510,6 +535,7 @@ describe('Parser - TestNG', () => { "status": "PASS", "failure": "expected [true] but found [false]", "stack_trace": "", + "meta_data": new Map(), "steps": [] } ] @@ -555,6 +581,7 @@ describe('Parser - TestNG', () => { "status": "PASS", "failure": "", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -569,6 +596,7 @@ describe('Parser - TestNG', () => { "status": "RETRY", "failure": "failed", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -583,6 +611,7 @@ describe('Parser - TestNG', () => { "status": "FAIL", "failure": "failed", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -597,6 +626,7 @@ describe('Parser - TestNG', () => { "status": "PASS", "failure": "", "stack_trace": "", + "meta_data": new Map(), "steps": [] } ] @@ -624,6 +654,7 @@ describe('Parser - TestNG', () => { "status": "PASS", "failure": "", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -638,6 +669,7 @@ describe('Parser - TestNG', () => { "status": "RETRY", "failure": "failed", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -652,6 +684,7 @@ describe('Parser - TestNG', () => { "status": "FAIL", "failure": "failed", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -666,6 +699,7 @@ describe('Parser - TestNG', () => { "status": "PASS", "failure": "", "stack_trace": "", + "meta_data": new Map(), "steps": [] } ] @@ -711,6 +745,7 @@ describe('Parser - TestNG', () => { "status": "FAIL", "failure": "expected [A] but found [948474]", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -725,6 +760,7 @@ describe('Parser - TestNG', () => { "status": "PASS", "failure": "", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -739,6 +775,7 @@ describe('Parser - TestNG', () => { "status": "PASS", "failure": "", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -753,6 +790,7 @@ describe('Parser - TestNG', () => { "status": "FAIL", "failure": "Expected condition failed: : 95ddbda01ea4b3dbcb049e681a6...}", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -767,6 +805,7 @@ describe('Parser - TestNG', () => { "status": "FAIL", "failure": "element click intercepted:", "stack_trace": "", + "meta_data": new Map(), "steps": [] } ] @@ -794,6 +833,7 @@ describe('Parser - TestNG', () => { "status": "FAIL", "failure": "expected [A] but found [948474]", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -808,6 +848,7 @@ describe('Parser - TestNG', () => { "status": "PASS", "failure": "", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -822,6 +863,7 @@ describe('Parser - TestNG', () => { "status": "PASS", "failure": "", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -836,6 +878,7 @@ describe('Parser - TestNG', () => { "status": "FAIL", "failure": "Appium error: An unknown sr='Search...']}", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -850,6 +893,7 @@ describe('Parser - TestNG', () => { "status": "SKIP", "failure": "A script did not complete ", "stack_trace": "", + "meta_data": new Map(), "steps": [] } ] @@ -877,6 +921,7 @@ describe('Parser - TestNG', () => { "status": "PASS", "failure": "", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -891,6 +936,7 @@ describe('Parser - TestNG', () => { "status": "PASS", "failure": "", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -905,6 +951,7 @@ describe('Parser - TestNG', () => { "status": "PASS", "failure": "", "stack_trace": "", + "meta_data": new Map(), "steps": [] }, { @@ -919,6 +966,7 @@ describe('Parser - TestNG', () => { "status": "PASS", "failure": "expected [true] but found [false]", "stack_trace": "", + "meta_data": new Map(), "steps": [] } ] @@ -953,4 +1001,13 @@ describe('Parser - TestNG', () => { assert.notEqual(null, result2); }); + it('assign groups to testcases in single suite', () => { + const result = parse({ type: 'testng', files: ['tests/data/testng/groups.xml'] }); + assert.equal(result.suites[0].cases[0].meta_data.get("groups"), "group1"); + assert.equal(result.suites[0].cases[0].meta_data.has("group1"), true); + // 2nd testcase has multiple groups + assert.equal(result.suites[0].cases[1].meta_data.get("groups"), "group1,group2"); + assert.equal(result.suites[0].cases[1].meta_data.has("group1"), true); + assert.equal(result.suites[0].cases[1].meta_data.has("group2"), true); + }); }); \ No newline at end of file diff --git a/tests/parser.xunit.spec.js b/tests/parser.xunit.spec.js index b52450e..816e18c 100644 --- a/tests/parser.xunit.spec.js +++ b/tests/parser.xunit.spec.js @@ -43,6 +43,7 @@ describe('Parser - XUnit', () => { stack_trace: "", status: "FAIL", steps: [], + meta_data: newMap({ TestID: "ID", TestLevel: "Regression", TestProduct: "TestProductExample", TestSuite: "TestSuiteExample"}), total: 0 } ] @@ -87,6 +88,7 @@ describe('Parser - XUnit', () => { stack_trace: "", status: "SKIP", steps: [], + meta_data: newMap({ UnsupportedEnvirnoment: "uat"}), total: 0 }] } @@ -130,6 +132,7 @@ describe('Parser - XUnit', () => { stack_trace: "", status: "FAIL", steps: [], + meta_data: newMap({ TestID: "RTA-21505", TestLevel: "Regression", TestProduct: "ExampleTestProduct", TestSuite: "ExampleTestSuite"}), total: 0 }, { @@ -144,6 +147,7 @@ describe('Parser - XUnit', () => { stack_trace: "", status: "PASS", steps: [], + meta_data: newMap({ TestID: "RTA-21510", TestLevel: "Regression", TestProduct: "ExampleTestProduct", TestSuite: "ExampleTestSuite"}), total: 0 } ] @@ -171,6 +175,7 @@ describe('Parser - XUnit', () => { stack_trace: "", status: "FAIL", steps: [], + meta_data: newMap({ TestID: "RTA-21516", TestLevel: "Regression", TestProduct: "ExampleTestProduct", TestSuite: "ExampleTestSuite"}), total: 0 }, { @@ -185,6 +190,7 @@ describe('Parser - XUnit', () => { stack_trace: "", status: "PASS", steps: [], + meta_data: newMap({ TestID: "RTA-21513", TestLevel: "Regression", TestProduct: "ExampleTestProduct", TestSuite: "ExampleTestSuite"}), total: 0 } ] @@ -212,6 +218,7 @@ describe('Parser - XUnit', () => { stack_trace: "", status: "PASS", steps: [], + meta_data: newMap({ TestID: "RTA-21538", TestLevel: "Regression", TestProduct: "ExampleTestProduct", TestSuite: "ExampleTestSuite"}), total: 0 } ] @@ -239,6 +246,7 @@ describe('Parser - XUnit', () => { stack_trace: "", status: "FAIL", steps: [], + meta_data: newMap({ TestID: "RTA-37684", TestLevel: "Regression", TestProduct: "ExampleTestProduct", TestSuite: "ExampleTestSuite"}), total: 0 } ] @@ -256,4 +264,22 @@ describe('Parser - XUnit', () => { const result2 = parse({ type: 'xunit', files: [relativePath]}); assert.notEqual(null, result2); }); + + it('meta-data from traits', () => { + const result = parse({ type: 'xunit', files: ['tests/data/xunit/single-suite.xml'] }); + assert.equal(result.suites[0].cases[0].meta_data.size, 4); + }); + + it('no meta-data from empty traits', () => { + const result = parse({ type: 'xunit', files: ['tests/data/xunit/no-traits-suite.xml'] }); + assert.equal(result.suites[0].cases[0].meta_data.size, 0); + }) + + function newMap( obj ) { + let map = new Map(); + for (const property in obj) { + map.set( property, obj[property]); + } + return map; + } }); \ No newline at end of file