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 metadata to TestCase #35

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
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,8 @@
"devDependencies": {
"c8": "^7.12.0",
"mocha": "^10.0.0"
},
"mocha": {
"spec": "tests/**/*.spec.js"
}
}
4 changes: 4 additions & 0 deletions src/helpers/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
1 change: 1 addition & 0 deletions src/models/TestCase.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ declare class TestCase {
failure: string;
stack_trace: string;
steps: TestStep[];
meta_data: Map<string,string>;
}

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

}
Expand Down
15 changes: 15 additions & 0 deletions src/parsers/cucumber.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
20 changes: 18 additions & 2 deletions src/parsers/junit.js
Original file line number Diff line number Diff line change
Expand Up @@ -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"];
Expand All @@ -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;
Expand Down
16 changes: 16 additions & 0 deletions src/parsers/mocha.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(","));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ASaiAnudeep - what are your thoughts on preserving the original tags?

}
if (rawCase["state"] == "pending") {
test_case.status = 'SKIP';
}
Expand Down
59 changes: 52 additions & 7 deletions src/parsers/testng.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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;
Expand All @@ -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;
}
Expand All @@ -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;
Expand All @@ -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;
}
Expand Down Expand Up @@ -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];
Expand Down
7 changes: 7 additions & 0 deletions src/parsers/xunit.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
8 changes: 8 additions & 0 deletions tests/data/cucumber/single-suite-single-test.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@
{
"name": "@green",
"line": 4
},
{
"name": "@fast",
"line": 4
},
{
"name": "@testCase=1234",
"line": 4
}
],
"type": "scenario"
Expand Down
24 changes: 24 additions & 0 deletions tests/data/junit/multiple-suites-properties.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8" ?>
<testsuites id="id" name="result name" tests="2" failures="1" errors="" time="20">
<testsuite id="codereview.cobol.analysisProvider" name="suite name 1" tests="1" failures="1" time="10">
<properties>
<property name="key1" value="value1" />
<property name="key2" value="value2" />
</properties>
<testcase id="codereview.cobol.rules.ProgramIdRule" name="Use a program name that matches the source file name" time="10">
<properties>
<property name="key1" value="override-value1" />
</properties>
<failure message="PROGRAM.cbl:2 Use a program name that matches the source file name" type="WARNING">
Some Text
</failure>
</testcase>
</testsuite>
<testsuite id="codereview.cobol.analysisProvider" name="suite name 2" tests="1" failures="0" time="10">
<testcase id="codereview.cobol.rules.ProgramIdRule" name="Use a program name that matches the source file name" time="10">
<failure message="PROGRAM.cbl:2 Use a program name that matches the source file name" type="WARNING">
Some Text
</failure>
</testcase>
</testsuite>
</testsuites>
85 changes: 85 additions & 0 deletions tests/data/mocha/json/multiple-suites-multiple-tests-tags.json
Original file line number Diff line number Diff line change
@@ -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",
bryanbcook marked this conversation as resolved.
Show resolved Hide resolved
"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.<anonymous> (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.<anonymous> (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": {}
}
]
}
Loading
Loading