diff --git a/package-lock.json b/package-lock.json index 2f6c297..b600c85 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "test-results-parser", - "version": "0.1.5", + "version": "0.1.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "test-results-parser", - "version": "0.1.5", + "version": "0.1.6", "license": "MIT", "dependencies": { "fast-xml-parser": "^4.3.2", diff --git a/package.json b/package.json index 6160b3b..19c3e3d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "test-results-parser", - "version": "0.1.5", + "version": "0.1.6", "description": "Parse test results from JUnit, TestNG, xUnit, cucumber and many more", "main": "src/index.js", "types": "./src/index.d.ts", @@ -22,7 +22,8 @@ "result", "automation", "mocha", - "cucumber" + "cucumber", + "nUnit" ], "author": "Anudeep ", "license": "MIT", diff --git a/src/models/TestSuite.d.ts b/src/models/TestSuite.d.ts index 98977f0..11ca293 100644 --- a/src/models/TestSuite.d.ts +++ b/src/models/TestSuite.d.ts @@ -9,6 +9,7 @@ declare class TestSuite { skipped: number; duration: number; status: string; + meta_data: Map; cases: TestCase[]; } diff --git a/src/models/TestSuite.js b/src/models/TestSuite.js index d4c6a0d..6d87c1f 100644 --- a/src/models/TestSuite.js +++ b/src/models/TestSuite.js @@ -10,6 +10,7 @@ class TestSuite { this.skipped = 0; this.duration = 0; this.status = 'NA'; + this.meta_data = new Map(); this.cases = []; } diff --git a/src/parsers/cucumber.js b/src/parsers/cucumber.js index cd7cffd..049a6be 100644 --- a/src/parsers/cucumber.js +++ b/src/parsers/cucumber.js @@ -8,21 +8,7 @@ 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(",")); - } + setMetaData(rawCase, test_case); if (rawCase.state && rawCase.state === "failed") { test_case.status = 'FAIL'; test_case.setFailure(rawCase.errorStack); @@ -33,6 +19,30 @@ function getTestCase(rawCase) { return test_case; } +/** + * + * @param {import('./cucumber.result').CucumberElement} element + * @param {TestCase | TestSuite} test_element + */ +function setMetaData(element, test_element) { + const meta_tags = []; + const meta_raw_tags = []; + const tags = element.tags; + if (tags && tags.length > 0) { + for (const tag of tags) { + const [name, value] = tag["name"].substring(1).split("="); + if (value) { + test_element.meta_data.set(name, value); + } else { + meta_tags.push(name); + meta_raw_tags.push(tag["name"]); + } + } + test_element.meta_data.set("tags", meta_tags.join(",")); + test_element.meta_data.set("tagsRaw", meta_raw_tags.join(",")); + } +} + function getTestSuite(rawSuite) { const suite = new TestSuite(); suite.name = rawSuite["name"]; @@ -41,6 +51,7 @@ function getTestSuite(rawSuite) { suite.failed = rawSuite["failures"]; suite.duration = rawSuite["duration"]; suite.status = suite.total === suite.passed ? 'PASS' : 'FAIL'; + setMetaData(rawSuite, suite); const raw_test_cases = rawSuite.elements; if (raw_test_cases) { for (let i = 0; i < raw_test_cases.length; i++) { @@ -50,6 +61,9 @@ function getTestSuite(rawSuite) { return suite; } +/** + * @param {import("./cucumber.result").CucumberJsonResult} json + */ function getTestResult(json) { const result = new TestResult(); const { stats, suites } = preprocess(json); @@ -74,13 +88,13 @@ function getTestResult(json) { /** * Function to format the raw json report - * @param {*} rawjson + * @param {import("./cucumber.result").CucumberJsonResult} json * @returns formatted json object */ -function preprocess(rawjson) { +function preprocess(json) { const formattedResult = { stats: {}, suites: [] }; - rawjson.forEach(testSuite => { + json.forEach(testSuite => { testSuite.elements.forEach(testCase => { testCase.state = testCase.steps.every(step => step.result.status === "passed") ? "passed" : "failed"; testCase.duration = testCase.steps.map(step => step.result.duration).reduce((total, currVal) => total + currVal, 0) / 1000000; diff --git a/src/parsers/cucumber.result.d.ts b/src/parsers/cucumber.result.d.ts new file mode 100644 index 0000000..c1bcbfa --- /dev/null +++ b/src/parsers/cucumber.result.d.ts @@ -0,0 +1,55 @@ +export type CucumberResult = { + status: string; + duration: number; +}; + +export type CucumberMatch = { + location: string; +}; + +export type CucumberArgument = { + arguments: any[]; + keyword: string; + line: number; + name: string; + match: CucumberMatch; + result: CucumberResult; +}; + +export type CucumberStep = { + arguments: any[]; + keyword: string; + line: number; + name: string; + match: CucumberMatch; + result: CucumberResult; +}; + +export type CucumberTag = { + name: string; + line: number; +}; + +export type CucumberElement = { + description: string; + id: string; + keyword: string; + line: number; + name: string; + steps: CucumberStep[]; + tags: CucumberTag[]; + type: string; +}; + +export type CucumberFeature = { + description: string; + elements: CucumberElement[]; + id: string; + line: number; + keyword: string; + name: string; + tags: CucumberTag[]; + uri: string; +}; + +export type CucumberJsonResult = CucumberFeature[]; diff --git a/src/parsers/junit.js b/src/parsers/junit.js index dc5037d..b733077 100644 --- a/src/parsers/junit.js +++ b/src/parsers/junit.js @@ -4,19 +4,11 @@ const TestResult = require('../models/TestResult'); const TestSuite = require('../models/TestSuite'); const TestCase = require('../models/TestCase'); -function getTestCase(rawCase, suiteProperties) { +function getTestCase(rawCase) { 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"]); - } - } + setMetaData(rawCase.properties, test_case); if (rawCase.failure && rawCase.failure.length > 0) { test_case.status = 'FAIL'; test_case.setFailure(rawCase.failure[0]["@_message"]); @@ -43,22 +35,30 @@ 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"]) - } - } + setMetaData(rawSuite.properties, 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], properties)); + suite.cases.push(getTestCase(raw_test_cases[i])); } } return suite; } +/** + * + * @param {import('./junit.result').JUnitProperties} properties + * @param {TestCase | TestSuite} test_element + */ +function setMetaData(properties, test_element) { + if (properties && properties.property.length > 0) { + const raw_properties = properties.property; + for (const raw_property of raw_properties) { + test_element.meta_data.set(raw_property["@_name"], raw_property["@_value"]); + } + } +} + /** * @param {TestResult} result */ @@ -89,6 +89,11 @@ function setAggregateResults(result) { } } +/** + * + * @param {import('./junit.result').JUnitResultJson} json + * @returns + */ function getTestResult(json) { const result = new TestResult(); const rawResult = json["testsuites"][0]; diff --git a/src/parsers/junit.result.d.ts b/src/parsers/junit.result.d.ts new file mode 100644 index 0000000..6a4d326 --- /dev/null +++ b/src/parsers/junit.result.d.ts @@ -0,0 +1,51 @@ +export type JUnitProperty = { + '@_name': string; + '@_value': string; +} + +export type JUnitProperties = { + property: JUnitProperty[]; +} + +export type JUnitFailure = { + '#text': string; + '@_message': string; + '@_type': string; +} + +export type JUnitTestCase = { + properties?: JUnitProperties; + failure?: JUnitFailure[]; + '@_id': string; + '@_name': string; + '@_time': number; +} + +export type TestSuite = { + properties?: JUnitProperties; + testcase: JUnitTestCase[]; + '@_id': string; + '@_name': string; + '@_tests': number; + '@_failures': number; + '@_time': number; +} + +export type JUnitResult = { + testsuite: JUnitTestSuite[]; + '@_id': string; + '@_name': string; + '@_tests': number; + '@_failures': number; + '@_errors': string; + '@_time': number; +} + +export type JUnitResultJson = { + '?xml': { + '@_version': number; + '@_encoding': string; + }; + testsuites: JUnitResult[]; +} + diff --git a/src/parsers/mocha.js b/src/parsers/mocha.js index 2aedfd0..01aa96f 100644 --- a/src/parsers/mocha.js +++ b/src/parsers/mocha.js @@ -11,22 +11,7 @@ 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(",")); - } + setMetaData(test_case); if (rawCase["state"] == "pending") { test_case.status = 'SKIP'; } @@ -50,6 +35,7 @@ function getTestSuite(rawSuite) { suite.duration = rawSuite["duration"]; suite.skipped = rawSuite["pending"].length; suite.status = suite.total === (suite.passed + suite.skipped) ? 'PASS' : 'FAIL'; + setMetaData(suite); const raw_test_cases = rawSuite.tests; if (raw_test_cases) { for (let i = 0; i < raw_test_cases.length; i++) { @@ -66,7 +52,7 @@ function getTestSuite(rawSuite) { function getTestResult(raw_json) { const result = new TestResult(); const { stats, results } = formatMochaJsonReport(raw_json); - + /** @type {import('./mocha.result').MochaResult} */ const formattedResult = results[0] || {}; const suites = formattedResult["suites"] || []; @@ -107,7 +93,7 @@ function formatMochaJsonReport(raw_json) { const suites = []; raw_json.failures.forEach(test => test.state = "failed"); raw_json.passes.forEach(test => test.state = "passed"); - raw_json.pending.forEach( test => { + raw_json.pending.forEach(test => { test.state = "pending"; test.duration = 0; }); @@ -137,7 +123,7 @@ function formatMochaJsonReport(raw_json) { */ function flattenTestSuite(suite) { if (!suite.suites) { - return; + return; } for (const child_suite of suite.suites) { flattenTestSuite(child_suite); @@ -150,6 +136,31 @@ function flattenTestSuite(suite) { } } +/** + * + * @param {TestCase | TestSuite} test_element + */ +function setMetaData(test_element) { + const regexp = /([\@\#][^\s]*)/gm; // match @tag or #tag + const matches = [...test_element.name.matchAll(regexp)]; + if (matches.length > 0) { + const meta_tags = []; + const meta_raw_tags = []; + for (const match of matches) { + const rawTag = match[0]; + const [name, value] = rawTag.substring(1).split("="); + if (value) { + test_element.meta_data.set(name, value); + } else { + meta_tags.push(name); + meta_raw_tags.push(rawTag); + } + } + test_element.meta_data.set("tags", meta_tags.join(",")); + test_element.meta_data.set("tagsRaw", meta_raw_tags.join(",")); + } +} + function parse(file) { const json = require(resolveFilePath(file)); diff --git a/src/parsers/nunit.js b/src/parsers/nunit.js index 0805480..72102c3 100644 --- a/src/parsers/nunit.js +++ b/src/parsers/nunit.js @@ -4,209 +4,208 @@ const TestResult = require('../models/TestResult'); const TestSuite = require('../models/TestSuite'); const TestCase = require('../models/TestCase'); -const SUITE_TYPES_WITH_TESTCASES = [ - "TestFixture", - "ParameterizedTest", - "GenericFixture", - "ParameterizedMethod" // v3 +const SUITE_TYPES_WITH_TEST_CASES = [ + "TestFixture", + "ParameterizedTest", + "GenericFixture", + "ParameterizedMethod" // v3 ] -const RESULTMAP = { - Success: "PASS", // v2 - Failure: "FAIL", // v2 - Ignored: "SKIP", // v2 - NotRunnable: "SKIP", // v2 - Error: "ERROR", // v2 - Inconclusive: "FAIL", // v2 - - Passed: "PASS", // v3 - Failed: "FAIL", // v3 - Skipped: "SKIP", // v3 +const RESULT_MAP = { + Success: "PASS", // v2 + Failure: "FAIL", // v2 + Ignored: "SKIP", // v2 + NotRunnable: "SKIP", // v2 + Error: "ERROR", // v2 + Inconclusive: "FAIL", // v2 + + Passed: "PASS", // v3 + Failed: "FAIL", // v3 + Skipped: "SKIP", // v3 } function mergeMeta(map1, map2) { - for(let kvp of map1) { - map2.set(kvp[0], kvp[1]); - } + for (let kvp of map1) { + map2.set(kvp[0], kvp[1]); + } } function populateMetaData(raw, map) { - // v2 supports categories - if (raw.categories) { - let categories = raw.categories.category; - for (let i = 0; i < categories.length; i++) { - let categoryName = categories[i]["@_name"]; - map.set(categoryName, ""); - - // create comma-delimited list of categories - if (map.has("Categories")) { - map.set("Categories", map.get("Categories").concat(",", categoryName)); - } else { - map.set("Categories", categoryName); - } - } + // v2 supports categories + if (raw.categories) { + let categories = raw.categories.category; + for (let i = 0; i < categories.length; i++) { + let categoryName = categories[i]["@_name"]; + map.set(categoryName, ""); + + // create comma-delimited list of categories + if (map.has("Categories")) { + map.set("Categories", map.get("Categories").concat(",", categoryName)); + } else { + map.set("Categories", categoryName); + } } - - // v2/v3 support properties - if (raw.properties) { - let properties = raw.properties.property; - for (let i = 0; i < properties.length; i++) { - let property = properties[i]; - let propName = property["@_name"]; - let propValue = property["@_value"]; - - // v3 treats 'Categories' as property "Category" - if (propName == "Category") { - - if (map.has("Categories")) { - map.set("Categories", map.get("Categories").concat(",", propValue)); - } else { - map.set("Categories", propValue); - } - map.set(propValue, ""); - - } else { - map.set(propName, propValue); - } + } + + // v2/v3 support properties + if (raw.properties) { + let properties = raw.properties.property; + for (let i = 0; i < properties.length; i++) { + let property = properties[i]; + let propName = property["@_name"]; + let propValue = property["@_value"]; + + // v3 treats 'Categories' as property "Category" + if (propName == "Category") { + + if (map.has("Categories")) { + map.set("Categories", map.get("Categories").concat(",", propValue)); + } else { + map.set("Categories", propValue); } + map.set(propValue, ""); + + } else { + map.set(propName, propValue); + } } + } } function getNestedTestCases(rawSuite) { - if (rawSuite.results) { - return rawSuite.results["test-case"]; - } else { - return rawSuite["test-case"]; - } + if (rawSuite.results) { + return rawSuite.results["test-case"]; + } else { + return rawSuite["test-case"]; + } } function hasNestedSuite(rawSuite) { - return getNestedSuite(rawSuite) !== null; + return getNestedSuite(rawSuite) !== null; } function getNestedSuite(rawSuite) { - // nunit v2 nests test-suite inside 'results' - if (rawSuite.results && rawSuite.results["test-suite"]) { - return rawSuite.results["test-suite"]; - } else { - // nunit v3 nests test-suites as immediate children - if (rawSuite["test-suite"]) { - return rawSuite["test-suite"]; - } - else { - // not nested - return null; - } + // nunit v2 nests test-suite inside 'results' + if (rawSuite.results && rawSuite.results["test-suite"]) { + return rawSuite.results["test-suite"]; + } else { + // nunit v3 nests test-suites as immediate children + if (rawSuite["test-suite"]) { + return rawSuite["test-suite"]; + } + else { + // not nested + return null; } + } } function getTestCases(rawSuite, parent_meta) { - var cases = []; - - let rawTestCases = getNestedTestCases(rawSuite); - if (rawTestCases) { - for (let i = 0; i < rawTestCases.length; i++) { - let rawCase = rawTestCases[i]; - let testCase = new TestCase(); - let result = rawCase["@_result"] - testCase.id = rawCase["@_id"] ?? ""; - testCase.name = rawCase["@_fullname"] ?? rawCase["@_name"]; - testCase.duration = rawCase["@_time"] * 1000; // in milliseconds - testCase.status = RESULTMAP[result]; - - // v2 : non-executed should be tests should be Ignored - if (rawCase["@_executed"] == "False") { - testCase.status = "SKIP"; // exclude failures that weren't executed. - } - // v3 : failed tests with error label should be Error - if (rawCase["@_label"] == "Error") { - testCase.status = "ERROR"; - } - let errorDetails = rawCase.reason ?? rawCase.failure; - if (errorDetails !== undefined) { - testCase.setFailure(errorDetails.message); - if (errorDetails["stack-trace"]) { - testCase.stack_trace = errorDetails["stack-trace"] - } - } - // copy parent_meta data to test case - mergeMeta(parent_meta, testCase.meta_data); - populateMetaData(rawCase, testCase.meta_data); - - cases.push( testCase ); + const cases = []; + + let rawTestCases = getNestedTestCases(rawSuite); + if (rawTestCases) { + for (let i = 0; i < rawTestCases.length; i++) { + let rawCase = rawTestCases[i]; + let testCase = new TestCase(); + let result = rawCase["@_result"] + testCase.id = rawCase["@_id"] ?? ""; + testCase.name = rawCase["@_fullname"] ?? rawCase["@_name"]; + testCase.duration = rawCase["@_time"] * 1000; // in milliseconds + testCase.status = RESULT_MAP[result]; + + // v2 : non-executed should be tests should be Ignored + if (rawCase["@_executed"] == "False") { + testCase.status = "SKIP"; // exclude failures that weren't executed. + } + // v3 : failed tests with error label should be Error + if (rawCase["@_label"] == "Error") { + testCase.status = "ERROR"; + } + let errorDetails = rawCase.reason ?? rawCase.failure; + if (errorDetails !== undefined) { + testCase.setFailure(errorDetails.message); + if (errorDetails["stack-trace"]) { + testCase.stack_trace = errorDetails["stack-trace"] } + } + // copy parent_meta data to test case + mergeMeta(parent_meta, testCase.meta_data); + populateMetaData(rawCase, testCase.meta_data); + + cases.push(testCase); } + } - return cases; + return cases; } function getTestSuites(rawSuites, assembly_meta) { - var suites = []; - - for(let i = 0; i < rawSuites.length; i++) { - let rawSuite = rawSuites[i]; - - if (rawSuite["@_type"] == "Assembly") { - assembly_meta = new Map(); - populateMetaData(rawSuite, assembly_meta); - } - - if (hasNestedSuite(rawSuite)) { - // handle nested test-suites - suites.push(...getTestSuites(getNestedSuite(rawSuite), assembly_meta)); - } else if (SUITE_TYPES_WITH_TESTCASES.indexOf(rawSuite["@_type"]) !== -1) { - - let suite = new TestSuite(); - suite.id = rawSuite["@_id"] ?? ''; - suite.name = rawSuite["@_fullname"] ?? rawSuite["@_name"]; - suite.duration = rawSuite["@_time"] * 1000; // in milliseconds - suite.status = RESULTMAP[rawSuite["@_result"]]; - - var meta_data = new Map(); - mergeMeta(assembly_meta, meta_data); - populateMetaData(rawSuite, meta_data); - suite.cases.push(...getTestCases(rawSuite, meta_data)); - - // calculate totals - suite.total = suite.cases.length; - suite.passed = suite.cases.filter(i => i.status == "PASS").length; - suite.failed = suite.cases.filter(i => i.status == "FAIL").length; - suite.errors = suite.cases.filter(i => i.status == "ERROR").length; - suite.skipped = suite.cases.filter(i => i.status == "SKIP").length; - - suites.push(suite); - } + const suites = []; + + for (let i = 0; i < rawSuites.length; i++) { + let rawSuite = rawSuites[i]; + + if (rawSuite["@_type"] == "Assembly") { + assembly_meta = new Map(); + populateMetaData(rawSuite, assembly_meta); } - - return suites; -} + if (hasNestedSuite(rawSuite)) { + // handle nested test-suites + suites.push(...getTestSuites(getNestedSuite(rawSuite), assembly_meta)); + } else if (SUITE_TYPES_WITH_TEST_CASES.indexOf(rawSuite["@_type"]) !== -1) { + + let suite = new TestSuite(); + suite.id = rawSuite["@_id"] ?? ''; + suite.name = rawSuite["@_fullname"] ?? rawSuite["@_name"]; + suite.duration = rawSuite["@_time"] * 1000; // in milliseconds + suite.status = RESULT_MAP[rawSuite["@_result"]]; + + const meta_data = new Map(); + mergeMeta(assembly_meta, meta_data); + populateMetaData(rawSuite, meta_data); + suite.cases.push(...getTestCases(rawSuite, meta_data)); + + // calculate totals + suite.total = suite.cases.length; + suite.passed = suite.cases.filter(i => i.status == "PASS").length; + suite.failed = suite.cases.filter(i => i.status == "FAIL").length; + suite.errors = suite.cases.filter(i => i.status == "ERROR").length; + suite.skipped = suite.cases.filter(i => i.status == "SKIP").length; + + suites.push(suite); + } + } + + return suites; +} function getTestResult(json) { - const nunitVersion = (json["test-results"] !== undefined) ? "v2" : - (json["test-run"] !== undefined) ? "v3" : null; + const nunitVersion = (json["test-results"] !== undefined) ? "v2" : + (json["test-run"] !== undefined) ? "v3" : null; - if (nunitVersion == null) { - throw new Error("Unrecognized xml format"); - } + if (nunitVersion == null) { + throw new Error("Unrecognized xml format"); + } + + const result = new TestResult(); + const rawResult = json["test-results"] ?? json["test-run"]; + const rawSuite = rawResult["test-suite"][0]; + + result.name = rawResult["@_fullname"] ?? rawResult["@_name"]; + result.duration = rawSuite["@_time"] * 1000; // in milliseconds + + result.suites.push(...getTestSuites([rawSuite], null)); + + result.total = result.suites.reduce((total, suite) => { return total + suite.cases.length }, 0); + result.passed = result.suites.reduce((total, suite) => { return total + suite.passed }, 0); + result.failed = result.suites.reduce((total, suite) => { return total + suite.failed }, 0); + result.skipped = result.suites.reduce((total, suite) => { return total + suite.skipped }, 0); + result.errors = result.suites.reduce((total, suite) => { return total + suite.errors }, 0); - const result = new TestResult(); - const rawResult = json["test-results"] ?? json["test-run"]; - const rawSuite = rawResult["test-suite"][0]; - - result.name = rawResult["@_fullname"] ?? rawResult["@_name"]; - result.duration = rawSuite["@_time"] * 1000; // in milliseconds - - result.suites.push(...getTestSuites( [ rawSuite ], null)); - - result.total = result.suites.reduce( (total, suite) => { return total + suite.cases.length}, 0); - result.passed = result.suites.reduce( (total, suite) => { return total + suite.passed}, 0); - result.failed = result.suites.reduce( (total, suite) => { return total + suite.failed}, 0); - result.skipped = result.suites.reduce( (total, suite) => { return total + suite.skipped}, 0); - result.errors = result.suites.reduce( (total, suite) => { return total + suite.errors}, 0); - - return result; + return result; } function parse(file) { diff --git a/tests/data/cucumber/single-suite-single-test.json b/tests/data/cucumber/single-suite-single-test.json index 6a3e083..51cc0c4 100644 --- a/tests/data/cucumber/single-suite-single-test.json +++ b/tests/data/cucumber/single-suite-single-test.json @@ -70,7 +70,20 @@ "line": 1, "keyword": "Feature", "name": "Addition", - "tags": [], + "tags": [ + { + "name": "@blue", + "line": 4 + }, + { + "name": "@slow", + "line": 4 + }, + { + "name": "@suite=1234", + "line": 4 + } + ], "uri": "features\\sample.feature" } ] \ 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 index 3907634..4280bea 100644 --- a/tests/data/mocha/json/multiple-suites-multiple-tests-tags.json +++ b/tests/data/mocha/json/multiple-suites-multiple-tests-tags.json @@ -12,7 +12,7 @@ "tests": [ { "title": "sample test case @fast #1255", - "fullTitle": "Example Suite 1 sample test case @fast #1255", + "fullTitle": "Example Suite 1 @type=api sample test case @fast #1255", "file": "", "duration": 3, "currentRetry": 0, @@ -21,7 +21,7 @@ }, { "title": "sample test case 2", - "fullTitle": "Example Suite 1 sample test case 2", + "fullTitle": "Example Suite 1 @type=api sample test case 2", "file": "", "duration": 1, "currentRetry": 0, @@ -65,7 +65,7 @@ "passes": [ { "title": "sample test case @fast #1255", - "fullTitle": "Example Suite 1 sample test case @fast #1255", + "fullTitle": "Example Suite 1 @type=api sample test case @fast #1255", "file": "", "duration": 3, "currentRetry": 0, @@ -74,7 +74,7 @@ }, { "title": "sample test case 2", - "fullTitle": "Example Suite 1 sample test case 2", + "fullTitle": "Example Suite 1 @type=api sample test case 2", "file": "", "duration": 1, "currentRetry": 0, diff --git a/tests/parser.cucumber.spec.js b/tests/parser.cucumber.spec.js index 1dabe82..4321f6b 100644 --- a/tests/parser.cucumber.spec.js +++ b/tests/parser.cucumber.spec.js @@ -3,7 +3,9 @@ const assert = require('assert'); const path = require('path'); describe('Parser - Cucumber Json', () => { + const testDataPath = "tests/data/cucumber" + it('single suite with single test', () => { const result = parse({ type: 'cucumber', files: [`${testDataPath}/single-suite-single-test.json`] }); assert.deepEqual(result, { @@ -28,6 +30,7 @@ describe('Parser - Cucumber Json', () => { skipped: 0, duration: 1.59, status: 'PASS', + meta_data: newMap({ tags: "blue,slow", suite: "1234", tagsRaw: "@blue,@slow" }), cases: [ { duration: 1.59, @@ -40,7 +43,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"}), + meta_data: newMap({ tags: "green,fast", testCase: "1234", tagsRaw: "@green,@fast" }), steps: [], total: 0 } @@ -49,7 +52,7 @@ describe('Parser - Cucumber Json', () => { ] }); }); - + it('empty suite report', () => { const result = parse({ type: 'cucumber', files: [`${testDataPath}/empty-suite.json`] }); assert.deepEqual(result, { @@ -91,6 +94,7 @@ describe('Parser - Cucumber Json', () => { skipped: 0, duration: 2.84, status: 'FAIL', + meta_data: new Map(), cases: [ { duration: 2.56, @@ -134,6 +138,7 @@ describe('Parser - Cucumber Json', () => { skipped: 0, duration: 0.52, status: 'PASS', + meta_data: new Map(), cases: [ { duration: 0.52, @@ -161,14 +166,14 @@ describe('Parser - Cucumber Json', () => { let absolutePath = path.resolve(relativePath); const result1 = parse({ type: 'cucumber', files: [absolutePath] }); assert.notEqual(null, result1); - const result2 = parse({ type: 'cucumber', files: [ relativePath]}); + const result2 = parse({ type: 'cucumber', files: [relativePath] }); assert.notEqual(null, result2); }); - - function newMap( obj ) { + + function newMap(obj) { let map = new Map(); for (const property in obj) { - map.set( property, obj[property]); + map.set(property, obj[property]); } return map; } diff --git a/tests/parser.junit.spec.js b/tests/parser.junit.spec.js index 26d38f5..057e842 100644 --- a/tests/parser.junit.spec.js +++ b/tests/parser.junit.spec.js @@ -3,7 +3,9 @@ const assert = require('assert'); const path = require('path'); describe('Parser - JUnit', () => { + const testDataPath = "tests/data/junit" + it('single suite with single test', () => { const result = parse({ type: 'junit', files: [`${testDataPath}/single-suite.xml`] }); assert.deepEqual(result, { @@ -28,6 +30,7 @@ describe('Parser - JUnit', () => { skipped: 0, duration: 10000, status: 'FAIL', + meta_data: new Map(), cases: [ { duration: 10000, @@ -74,6 +77,7 @@ describe('Parser - JUnit', () => { skipped: 0, duration: 10000, status: 'FAIL', + meta_data: new Map(), cases: [ { duration: 10000, @@ -120,6 +124,7 @@ describe('Parser - JUnit', () => { skipped: 0, duration: 10000, status: 'PASS', + meta_data: new Map(), cases: [ { duration: 10000, @@ -166,6 +171,7 @@ describe('Parser - JUnit', () => { skipped: 0, duration: 10000, status: 'FAIL', + meta_data: new Map(), cases: [ { duration: 10000, @@ -194,6 +200,7 @@ describe('Parser - JUnit', () => { skipped: 0, duration: 10000, status: 'PASS', + meta_data: new Map(), cases: [ { duration: 10000, @@ -240,6 +247,7 @@ describe('Parser - JUnit', () => { skipped: 0, duration: 10000, status: 'FAIL', + meta_data: new Map(), cases: [ { duration: 10000, @@ -268,6 +276,7 @@ describe('Parser - JUnit', () => { skipped: 0, duration: 10000, status: 'FAIL', + meta_data: new Map(), cases: [ { duration: 10000, @@ -314,6 +323,7 @@ describe('Parser - JUnit', () => { skipped: 0, duration: 807, status: 'PASS', + meta_data: new Map(), cases: [ { duration: 807, @@ -360,6 +370,7 @@ describe('Parser - JUnit', () => { "skipped": 0, "duration": 446, "status": "FAIL", + meta_data: new Map(), "cases": [ { "id": "", @@ -403,6 +414,7 @@ describe('Parser - JUnit', () => { "skipped": 0, "duration": 634, "status": "PASS", + meta_data: new Map(), "cases": [ { "id": "", @@ -449,6 +461,7 @@ describe('Parser - JUnit', () => { "skipped": 1, "duration": 870.6800000000001, "status": "FAIL", + meta_data: new Map(), "cases": [ { "id": "", @@ -544,7 +557,10 @@ describe('Parser - JUnit', () => { 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].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"); }); diff --git a/tests/parser.mocha.spec.js b/tests/parser.mocha.spec.js index 8783713..63b00ba 100644 --- a/tests/parser.mocha.spec.js +++ b/tests/parser.mocha.spec.js @@ -3,7 +3,9 @@ const assert = require('assert'); const path = require('path'); describe('Parser - Mocha Json', () => { + const testDataPath = "tests/data/mocha/json" + it('single suite with single test', () => { const result = parse({ type: 'mocha', files: [`${testDataPath}/single-suite-single-test.json`] }); assert.deepEqual(result, { @@ -28,6 +30,7 @@ describe('Parser - Mocha Json', () => { skipped: 0, duration: 1, status: 'PASS', + meta_data: new Map(), cases: [ { duration: 1, @@ -49,6 +52,7 @@ describe('Parser - Mocha Json', () => { ] }); }); + it('empty suite report', () => { const result = parse({ type: 'mocha', files: [`${testDataPath}/empty-suite.json`] }); assert.deepEqual(result, { @@ -65,6 +69,7 @@ describe('Parser - Mocha Json', () => { suites: [] }); }); + it('suite with skipped tests', () => { const result = parse({ type: 'mocha', files: [`${testDataPath}/skipped-tests.json`] }); assert.deepEqual(result, { @@ -89,6 +94,7 @@ describe('Parser - Mocha Json', () => { skipped: 1, duration: 1, status: 'PASS', + meta_data: new Map(), cases: [ { duration: 1, @@ -149,6 +155,7 @@ describe('Parser - Mocha Json', () => { skipped: 0, duration: 4, status: 'PASS', + meta_data: new Map(), cases: [ { duration: 3, @@ -192,6 +199,7 @@ describe('Parser - Mocha Json', () => { skipped: 0, duration: 1, status: 'FAIL', + meta_data: new Map(), cases: [ { duration: 1, @@ -225,21 +233,23 @@ describe('Parser - Mocha Json', () => { 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); + let test_suite = result.suites[0]; + let test_case = result.suites[0].cases[0]; + assert.equal(test_suite.meta_data.has("tags"), true); + assert.equal(test_suite.meta_data.get("tags"), ""); + assert.equal(test_suite.meta_data.get("tagsRaw"), ""); + assert.equal(test_suite.meta_data.get("type"), "api"); + assert.equal(test_case.meta_data.has("tags"), true); + assert.equal(test_case.meta_data.get("tags"), "fast,1255"); + assert.equal(test_case.meta_data.get("tagsRaw"), "@fast,#1255"); }); 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); + let test_case = result.suites[1].cases[0]; + assert.equal(test_case.meta_data.has("tags"), true); + assert.equal(test_case.meta_data.get("tags"), "1234"); + assert.equal(test_case.meta_data.get("tagsRaw"), "#1234"); }); it('does not include tags meta if no tags are present', () =>{ @@ -250,7 +260,8 @@ describe('Parser - Mocha Json', () => { }); describe('Parser - Mocha Awesmome Json', () => { - const testDataPath = "tests/data/mocha/awesome" + const testDataPath = "tests/data/mocha/awesome"; + it('single suite with single test', () => { const result = parse({ type: 'mocha', files: [`${testDataPath}/single-suite-single-test.json`] }); assert.deepEqual(result, { @@ -275,6 +286,7 @@ describe('Parser - Mocha Awesmome Json', () => { skipped: 0, duration: 1, status: 'PASS', + meta_data: new Map(), cases: [ { duration: 1, @@ -296,6 +308,7 @@ describe('Parser - Mocha Awesmome Json', () => { ] }); }); + it('empty suite report', () => { const result = parse({ type: 'mocha', files: [`${testDataPath}/empty-suite.json`] }); assert.deepEqual(result, { @@ -312,6 +325,7 @@ describe('Parser - Mocha Awesmome Json', () => { suites: [] }); }); + it('suite with skipped tests', () => { const result = parse({ type: 'mocha', files: [`${testDataPath}/skipped-tests.json`] }); assert.deepEqual(result, { @@ -336,6 +350,7 @@ describe('Parser - Mocha Awesmome Json', () => { skipped: 1, duration: 1, status: 'PASS', + meta_data: new Map(), cases: [ { duration: 1, @@ -372,6 +387,7 @@ describe('Parser - Mocha Awesmome Json', () => { ] }); }); + it('multiple suites', () => { const result = parse({ type: 'mocha', files: [`${testDataPath}/multiple-suites-multiple-tests.json`] }); assert.deepEqual(result, { @@ -396,6 +412,7 @@ describe('Parser - Mocha Awesmome Json', () => { skipped: 0, duration: 4, status: 'PASS', + meta_data: new Map(), cases: [ { duration: 3, @@ -439,6 +456,7 @@ describe('Parser - Mocha Awesmome Json', () => { skipped: 0, duration: 1, status: 'FAIL', + meta_data: new Map(), cases: [ { duration: 1, @@ -460,6 +478,7 @@ describe('Parser - Mocha Awesmome Json', () => { ] }); }); + it('nested suites', () => { const result = parse({ type: 'mocha', files: [`${testDataPath}/nested-suites.json`] }); assert.deepEqual(result, { @@ -484,6 +503,7 @@ describe('Parser - Mocha Awesmome Json', () => { skipped: 0, duration: 4, status: 'PASS', + meta_data: new Map(), cases: [ { duration: 3, @@ -527,6 +547,7 @@ describe('Parser - Mocha Awesmome Json', () => { skipped: 0, duration: 1, status: 'FAIL', + meta_data: new Map(), cases: [ { duration: 1, @@ -557,4 +578,5 @@ describe('Parser - Mocha Awesmome Json', () => { const result2 = parse({ type: 'mocha', files: [relativePath]}); assert.notEqual(null, result2); }); + }); \ No newline at end of file diff --git a/tests/parser.testng.spec.js b/tests/parser.testng.spec.js index 1912971..89bb2c1 100644 --- a/tests/parser.testng.spec.js +++ b/tests/parser.testng.spec.js @@ -28,6 +28,7 @@ describe('Parser - TestNG', () => { skipped: 0, duration: 2000, status: 'PASS', + meta_data: new Map(), cases: [ { id: '', @@ -119,6 +120,7 @@ describe('Parser - TestNG', () => { "skipped": 0, "duration": 202082, "status": "FAIL", + meta_data: new Map(), "cases": [ { "id": "", @@ -207,6 +209,7 @@ describe('Parser - TestNG', () => { "skipped": 1, "duration": 545598, "status": "FAIL", + meta_data: new Map(), "cases": [ { "id": "", @@ -313,6 +316,7 @@ describe('Parser - TestNG', () => { "skipped": 0, "duration": 2000, "status": "PASS", + meta_data: new Map(), "cases": [ { "id": "", @@ -404,6 +408,7 @@ describe('Parser - TestNG', () => { "skipped": 0, "duration": 2000, "status": "PASS", + meta_data: new Map(), "cases": [ { "id": "", @@ -477,6 +482,7 @@ describe('Parser - TestNG', () => { "skipped": 0, "duration": 2000, "status": "PASS", + meta_data: new Map(), "cases": [ { "id": "", @@ -568,6 +574,7 @@ describe('Parser - TestNG', () => { "skipped": 0, "duration": 1164451, "status": "FAIL", + meta_data: new Map(), "cases": [ { "id": "", @@ -641,6 +648,7 @@ describe('Parser - TestNG', () => { "skipped": 0, "duration": 714100, "status": "FAIL", + meta_data: new Map(), "cases": [ { "id": "", @@ -732,6 +740,7 @@ describe('Parser - TestNG', () => { "skipped": 0, "duration": 202082, "status": "FAIL", + meta_data: new Map(), "cases": [ { "id": "", @@ -820,6 +829,7 @@ describe('Parser - TestNG', () => { "skipped": 1, "duration": 545598, "status": "FAIL", + meta_data: new Map(), "cases": [ { "id": "", @@ -908,6 +918,7 @@ describe('Parser - TestNG', () => { "skipped": 0, "duration": 2000, "status": "PASS", + meta_data: new Map(), "cases": [ { "id": "", diff --git a/tests/parser.xunit.spec.js b/tests/parser.xunit.spec.js index 816e18c..29eca69 100644 --- a/tests/parser.xunit.spec.js +++ b/tests/parser.xunit.spec.js @@ -30,6 +30,7 @@ describe('Parser - XUnit', () => { skipped: 0, duration: 86006.5, status: 'FAIL', + meta_data: new Map(), cases: [ { duration: 86006.5, @@ -75,6 +76,7 @@ describe('Parser - XUnit', () => { skipped: 1, duration: 1, status: 'PASS', + meta_data: new Map(), cases: [ { duration: 1, @@ -119,6 +121,7 @@ describe('Parser - XUnit', () => { skipped: 0, duration: 92155, status: 'FAIL', + meta_data: new Map(), cases: [ { duration: 84201.1799, @@ -162,6 +165,7 @@ describe('Parser - XUnit', () => { skipped: 0, duration: 85450, status: 'FAIL', + meta_data: new Map(), cases: [ { duration: 1411.6188, @@ -205,6 +209,7 @@ describe('Parser - XUnit', () => { skipped: 0, duration: 84195, status: 'PASS', + meta_data: new Map(), cases: [ { duration: 84195.474, @@ -233,6 +238,7 @@ describe('Parser - XUnit', () => { skipped: 0, duration: 86007, status: 'FAIL', + meta_data: new Map(), cases: [ { duration: 86006.7435,