junit-xml
Version:
JUnit XML report builder with TypeScript support
131 lines (109 loc) • 4.17 kB
text/typescript
import { TestSuiteReport, TestSuite, TestCase } from './TestResults';
/**
*
* @param report A report in the default schema, as supported by GitLab
*/
export function getXmlObject(report: TestSuiteReport) {
const testSuites = report.suites.map(attachTestSuiteMetadata);
const xmlObject: { testsuites: any[] } = {
testsuites: testSuites.map((testSuite) => ({ testsuite: getTestSuiteXmlObject(testSuite) })),
};
const testsuitesAttributes: { [key: string]: string } = {
tests: testSuites.reduce((tests, suite) => tests + suite.tests, 0).toString(),
errors: testSuites.reduce((errors, suite) => errors + suite.errors, 0).toString(),
failures: testSuites.reduce((failures, suite) => failures + suite.failures, 0).toString(),
skipped: testSuites.reduce((skipped, suite) => skipped + suite.skipped, 0).toString(),
};
if (report.name) {
testsuitesAttributes.name = report.name;
}
if (report.time) {
testsuitesAttributes.time = report.time.toString();
}
xmlObject.testsuites.push({ _attr: testsuitesAttributes });
return xmlObject;
}
function getTestSuiteXmlObject(testSuite: TestSuite & TestSuiteMetadata) {
const testSuiteXmlObject: any[] = testSuite.testCases
.map((testCase) => ({ testcase: getTestCaseXmlObject(testCase) }));
const testsuiteAttributes: { [key: string]: string } = {
tests: testSuite.tests.toString(),
errors: testSuite.errors.toString(),
failures: testSuite.failures.toString(),
skipped: testSuite.skipped.toString(),
};
if (testSuite.name) {
testsuiteAttributes.name = testSuite.name;
}
if (testSuite.hostname) {
testsuiteAttributes.hostname = testSuite.hostname;
}
if (testSuite.time) {
testsuiteAttributes.time = testSuite.time.toString();
}
if (testSuite.timestamp) {
testsuiteAttributes.timestamp = testSuite.timestamp.toISOString();
}
testSuiteXmlObject.push({ _attr: testsuiteAttributes });
return testSuiteXmlObject;
}
function getTestCaseXmlObject(testCase: TestCase) {
const testCaseXmlObject: any[] = [];
const testcaseAttributes: { [key: string]: string } = {
name: testCase.name,
classname: testCase.classname || testCase.name,
};
if (testCase.time) {
testcaseAttributes.time = testCase.time.toString();
}
if (testCase.assertions) {
testcaseAttributes.assertions = testCase.assertions.toString();
}
testCaseXmlObject.push({ _attr: testcaseAttributes });
if (testCase.skipped) {
testCaseXmlObject.push({ skipped: '' });
}
const errors = (Array.isArray(testCase.errors))
? testCase.errors.map(error => ({ error: formatMessage(error) }))
: [];
const failures = (Array.isArray(testCase.failures))
? testCase.failures.map(failure => ({ error: formatMessage(failure) }))
: [];
const systemOuts = (Array.isArray(testCase.systemOut))
? testCase.systemOut.map(systemOut => ({ 'system-out': systemOut }))
: [];
const systemErrs = (Array.isArray(testCase.systemErr))
? testCase.systemErr.map(systemErr => ({ 'system-err': systemErr }))
: [];
return testCaseXmlObject.concat(errors).concat(failures).concat(systemOuts).concat(systemErrs);
}
interface TestSuiteMetadata {
tests: number;
failures: number;
errors: number;
skipped: number;
};
function attachTestSuiteMetadata(testSuite: TestSuite): TestSuite & TestSuiteMetadata {
return {
...testSuite,
tests: testSuite.testCases.length,
failures: testSuite.testCases.filter(isFailing).length,
errors: testSuite.testCases.filter(hasErrors).length,
skipped: testSuite.testCases.filter(isSkipped).length,
};
}
function isSkipped(testCase: TestCase): boolean {
return testCase.skipped === true;
}
function isFailing(testCase: TestCase): boolean {
return Array.isArray(testCase.failures) && testCase.failures.length > 0;
}
function hasErrors(testCase: TestCase): boolean {
return Array.isArray(testCase.errors) && testCase.errors.length > 0;
}
function formatMessage(message: { message: string; type?: string }) {
if (typeof message.type === 'string') {
return [ message.message, { _attr: { type: message.type } } ];
}
return message.message;
}