UNPKG

@microfocus/alm-octane-test-result-convertion

Version:

A NodeJS library for converting different kinds of test reports into OpenText SDP / SDM format.

256 lines (236 loc) 8.19 kB
/* * (c) Copyright 2024 Micro Focus or one of its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import escapeXML from 'xml-escape'; import { js2xml, xml2js } from 'xml-js'; import Error from '../model/junit/Error'; import Failure from '../model/junit/Failure'; import MultipleSuitesRoot from '../model/junit/MultipleSuitesRoot'; import SingleSuiteRoot from '../model/junit/SingleSuiteRoot'; import TestCase from '../model/junit/TestCase'; import { TestRun, TestRunResult } from '../model/octane/TestRun'; import TestRunError from '../model/octane/TestRunError'; import TestsResult from '../model/octane/TestsResult'; import OctaneBuildConfig from './OctaneBuildConfig'; /** * Convert JUnit format XML to OpenText SDP / SDM format XML * @param {string} junitXML - string containing JUnit format XML * @param {OctaneBuildConfig} octaneBuildConfig - OpenText SDP / SDM build configuration data (eg.: job id, buiild id, server id etc.) * @returns {string} - string containing converted XML (returns the OpenText SDP / SDM format XML) */ const convertJUnitXMLToOctaneXML = ( junitXML: string, octaneBuildConfig: OctaneBuildConfig ): string => { const junitReportJSON = xml2js(junitXML, { compact: true }); const octaneReportJSON = createOctaneTestsResult( <MultipleSuitesRoot | SingleSuiteRoot>junitReportJSON, octaneBuildConfig ); return js2xml(octaneReportJSON, { compact: true }); }; /** * Creates OpenText SDP / SDM test results object from JUnit XML root object * @param {MultipleSuitesRoot | SingleSuiteRoot} junitReport - JUnit XML root object * @param {OctaneBuildConfig} buildConfig - OpenText SDP / SDM build configuration data (eg.: job id, buiild id, server id etc.) * @returns {TestsResult} - OpenText SDP / SDM tests result object to be converted to XML */ const createOctaneTestsResult = ( junitReport: MultipleSuitesRoot | SingleSuiteRoot, buildConfig: OctaneBuildConfig ): TestsResult => { const { external_run_id, ...buildContext } = buildConfig; return { test_result: { build: { _attributes: { ...buildContext } }, test_fields: { test_field: [ { _attributes: { type: 'Test_Level', value: 'Unit Test' } }, { _attributes: { type: 'Test_Type', value: 'Sanity' } }, { _attributes: { type: 'Framework', value: 'JUnit' } } ] }, test_runs: { test_run: convertJUnitSuiteToOctaneRuns(junitReport, external_run_id) } } }; }; /** * Converts JUnit XML root object to a list of OpenText SDP / SDM Test Run objects * @param {MultipleSuitesRoot | SingleSuiteRoot} reportRoot - JUnit XML root object * @param {string} externalRunId - external run id (on CI server) * @returns {TestRun[]} - list of Test Run OpenText SDP / SDM objects */ const convertJUnitSuiteToOctaneRuns = ( reportRoot: MultipleSuitesRoot | SingleSuiteRoot, externalRunId?: string ): TestRun[] => { const octaneTestRuns: TestRun[] = []; if (isMultipleSuitesRoot(reportRoot)) { let testSuites = (<MultipleSuitesRoot>reportRoot).testsuites.testsuite; testSuites = convertToArray(testSuites); testSuites.forEach(testSuite => { testSuite.testcase = convertToArray(testSuite.testcase); testSuite.testcase.forEach(testCase => { octaneTestRuns.push( mapTestCaseToOctaneRun(testCase, testSuite._attributes.package, externalRunId) ); }); }); } else { const testSuite = (<SingleSuiteRoot>reportRoot).testsuite; testSuite.testcase = convertToArray(testSuite.testcase); testSuite.testcase.forEach(testCase => { octaneTestRuns.push( mapTestCaseToOctaneRun(testCase, testSuite._attributes.package, externalRunId) ); }); } return octaneTestRuns; }; /** * Maps a JUnit TestCase object to an OpenText SDP / SDM TestRun object * @param {TestCase} testCase - JUnit TestCase object * @param {string} pkg - package of the test class * @param {string} externalRunId - external run id (on CI server) * @returns {TestRun} - resulted OpenText SDP / SDM TestRun object */ const mapTestCaseToOctaneRun = ( testCase: TestCase, pkg?: string, externalRunId?: string ): TestRun => { const error: TestRunError | undefined = mapFailsToOctaneError(testCase); const testRun: TestRun = { _attributes: { module: '', ...(pkg && { package: escapeXML(pkg) }), ...(testCase._attributes.classname && { class: escapeXML(testCase._attributes.classname) }), name: testCase._attributes.name, status: getTestRunStatus(testCase), duration: testCase._attributes.time ? Math.round(Number.parseFloat(testCase._attributes.time)) : 1, external_run_id: externalRunId } }; if (error) { testRun.error = error; } return testRun; }; /** * Maps errors and failured from JUnit TestCase object to OpenText SDP / SDM TestRunError object * @param {TestCase} testCase - JUnit TestCase object to extract the errors and failures from * @returns {TestRunError | undefined} - resulted OpenText SDP / SDM TestRunError object if any errors exist, else undefined */ const mapFailsToOctaneError = ( testCase: TestCase ): TestRunError | undefined => { let error: Error | Failure; if (testCase.error) { testCase.error = convertToArray(testCase.error); if (testCase.error.length > 0) { error = testCase.error[0]; } else { return undefined; } } else if (testCase.failure) { testCase.failure = convertToArray(testCase.failure); if (testCase.failure.length > 0) { error = testCase.failure[0]; } else { return undefined; } } else { return undefined; } let stacktrace: string | undefined; if (error?._cdata) { stacktrace = error._cdata; } else if (error?._text) { stacktrace = error._text; } return { _attributes: { ...(error._attributes.message && { message: escapeXML(error._attributes.message) }), ...(error._attributes.type && { type: escapeXML(error._attributes.type) }) }, _text: stacktrace || '' }; }; /** * Generates OpenText SDP / SDM TestRunResult based on the JUnit TestCase object (run status) * @param {TestCase} testCase - JUnit TestCase object * @returns {TestRunResult} - OpenText SDP / SDM test run status */ const getTestRunStatus = (testCase: TestCase): TestRunResult => { if (testCase.skipped) { return TestRunResult.SKIPPED; } else if ( (testCase.error && testCase.error.length > 0) || (testCase.failure && testCase.failure.length > 0) ) { return TestRunResult.FAILED; } else { return TestRunResult.PASSED; } }; /** * Checks if the JUnit XML root is a single test suite or a collection of test suites * @param {MultipleSuitesRoot | SingleSuiteRoot} root - JUnit XML root object * @returns {boolean} - true if root is a collection or else otherwise */ const isMultipleSuitesRoot = ( root: MultipleSuitesRoot | SingleSuiteRoot ): boolean => { return (<MultipleSuitesRoot>root).testsuites !== undefined; }; /** * Ensures an element is an array. If element is not already an array, then it is wrapped inside an array. * @param {Type} elem - element to be checked * @returns {Type[]} - the element array */ const convertToArray = <Type>(elem: Type | Type[]): Type[] => { if (!Array.isArray(elem)) { return [elem]; } return elem; }; export default convertJUnitXMLToOctaneXML;