test-results-parser
Version:
Parse test results from JUnit, TestNG, xUnit, cucumber and many more
197 lines (184 loc) • 5.16 kB
JavaScript
const fs = require('fs');
const path = require('path');
const { totalist } = require('totalist/sync');
const globrex = require('globrex');
const { XMLParser } = require("fast-xml-parser");
/**
* fast xml parser v4's default behavior is to return an object if there is only one element
* in the array. This is not desirable for our use case. We sometimes want to force an array
* these are the keys to which that is required.
* @type {string[]}
*/
const FORCED_ARRAY_KEYS = [
"testsuites",
"testsuites.testsuite",
"testsuites.testsuite.testcase",
"testsuites.testsuite.testcase.failure",
"testsuites.testsuite.testcase.error",
"testsuites.testsuite.testcase.system-err",
"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",
"testng-results.suite.test.class.test-method.exception",
"TestRun.Results.UnitTestResult",
"TestRun.Results.UnitTestResult.ResultFiles.ResultFile",
"TestRun.TestDefinitions.UnitTest",
"TestRun.TestDefinitions.UnitTest.Properties.Property",
"TestRun.TestDefinitions.UnitTest.TestCategory.TestCategoryItem"
];
const configured_parser = new XMLParser({
isArray: (name, jpath, isLeafNode, isAttribute) => {
if( FORCED_ARRAY_KEYS.indexOf(jpath) !== -1) {
return true;
}
// handle nunit deep hierarchy
else if (jpath.startsWith("test-results") || jpath.startsWith("test-run")) {
let parts = jpath.split(".");
switch(parts[parts.length - 1]) {
case "category":
case "property":
case "test-suite":
case "test-case":
case "attachment":
return true;
default:
return false;
}
}
},
ignoreAttributes: false,
parseAttributeValue: true,
});
function resolveFilePath(filePath) {
const cwd = process.cwd();
return path.isAbsolute(filePath) ? filePath : path.join(cwd, filePath);
}
function getJsonFromXMLFile(filePath) {
const xml = fs.readFileSync(resolveFilePath(filePath)).toString();
return configured_parser.parse(xml);
}
/**
* @param {string} file_path
*/
function getMatchingFilePaths(file_path) {
if (file_path.includes('*')) {
const file_paths = [];
file_path = file_path.replace(/\/|\\/g, path.sep); // convert path separators to current OS's separator
const result = globrex(file_path);
const dir_name = path.dirname(file_path.substring(0, file_path.indexOf('*') + 1));
totalist(dir_name, (name) => {
const current_file_path = path.join(dir_name, name);
if (result.regex.test(current_file_path)) {
file_paths.push(current_file_path);
}
});
return file_paths;
}
return [file_path];
}
/**
*
* @param {string} value
*/
function decodeIfEncoded(value) {
if (!value) {
return value;
}
try {
if (value.length % 4 !== 0) {
return value;
}
const base64Regex = /^[A-Za-z0-9+/]+={0,2}$/;
if (!base64Regex.test(value)) {
return value;
}
return atob(value);
} catch (error) {
return value;
}
}
/**
*
* @param {string} value
* @returns
*/
function isEncoded(value) {
if (!value) {
return false;
}
try {
if (value.length % 4 !== 0) {
return false;
}
const base64Regex = /^[A-Za-z0-9+/]+={0,2}$/;
if (!base64Regex.test(value)) {
return false;
}
atob(value);
return true;
} catch (error) {
return false;
}
}
/**
*
* @param {string} value
*/
function isFilePath(value) {
try {
fs.statSync(value);
return true;
} catch {
return false;
}
}
/**
*
* @param {string} file_name
* @param {string} file_data
* @param {string} file_type
*/
function saveAttachmentToDisk(file_name, file_data, file_type) {
const folder_path = path.join(process.cwd(), '.testbeats', 'attachments');
fs.mkdirSync(folder_path, { recursive: true });
let data = file_data;
if (isEncoded(file_data)) {
data = Buffer.from(file_data, 'base64');
} else {
return '';
}
const file_path = path.join(folder_path, file_name);
let relative_file_path = path.relative(process.cwd(), file_path);
if (file_type.includes('png')) {
relative_file_path = `${relative_file_path}.png`;
fs.writeFileSync(relative_file_path, data);
} else if (file_type.includes('jpeg')) {
relative_file_path = `${relative_file_path}.jpeg`;
fs.writeFileSync(relative_file_path, data);
} else if (file_type.includes('json')) {
relative_file_path = `${relative_file_path}.json`;
fs.writeFileSync(relative_file_path, data);
} else {
return '';
}
return relative_file_path;
}
module.exports = {
getJsonFromXMLFile,
getMatchingFilePaths,
resolveFilePath,
decodeIfEncoded,
isFilePath,
saveAttachmentToDisk
}