UNPKG

newman-reporter-xunit

Version:

A Newman JUnit Reporter providing full reports (without aggregation of results) based on newman-reporter-junitfull package

255 lines (202 loc) 8.5 kB
var _ = require('lodash'), xml = require('xmlbuilder'), moment = require('moment'), JunitFullReporter; // JUnit Reporter based on XSD specified by Publish Test Results task for Azure Pipeline / TFS 2018 / TFS 2017 and TFS 2015 // Source: https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/test/publish-test-results?view=vsts&tabs=yaml // XSD: https://github.com/windyroad/JUnit-Schema/blob/master/JUnit.xsd const SEPARATOR = '->'; const REQ_NAME_KEY = '__name__' /** * Resolves the parent qualified name for the provided item * * @param {PostmanItem|PostmanItemGroup} item The item for which to resolve the full name * @param {?String} [separator=SEP] The separator symbol to join path name entries with * @returns {String} The full name of the provided item, including prepended parent item names * @private */ function getParentName (item, separator) { if (_.isEmpty(item) || !_.isFunction(item.parent) || !_.isFunction(item.forEachParent)) { return; } var chain = []; item.forEachParent(function (parent) { chain.unshift(parent.name || parent.id); }); return chain.join(_.isString(separator) ? separator : SEPARATOR); } /** * A function that creates raw XML to be written to Newman JUnit reports. * * @param {Object} newman - The collection run object, with a event handler setter, used to enable event wise reporting. * @param {Object} reporterOptions - A set of JUnit reporter run options. * @param {String=} reporterOptions.export - Optional custom path to create the XML report at. * @returns {*} */ JunitFullReporter = function (newman, reporterOptions, options) { newman.on('beforeDone', function () { var excludeRequestNames = [] if (reporterOptions.excludeRequest) { excludeRequestNames = reporterOptions.excludeRequest.split(',') } var executions = _.get(newman, 'summary.run.executions'), globalValues = _.get(newman, 'summary.globals.values.members', []), environmentValues = _.get(newman, 'summary.environment.values.members', []), collection = _.get(newman, 'summary.collection'), root; //console.log(newman.summary.run.executions[0]) var date = moment(new Date()).local().format('YYYY-MM-DDTHH:mm:ss.SSS'); if (!executions) { return; } root = xml.create('testsuites', { version: '1.0', encoding: 'UTF-8' }); root.att('name', collection.name); root.att('tests', _.get(newman, 'summary.run.stats.tests.total', 'unknown')); var failuresTotal = 0, errorsTotal = 0; // Process executions (testsuites) _.forEach(executions, function (execution) { // exclude requests as requested by user if (excludeRequestNames.includes(execution.item.name)) { return } var iterationName = 'Iteration ' + execution.cursor.iteration.toString() var testsuite = root.ele('testsuite'); var failures = 0, errors = 0; var propertyValues = _.merge(environmentValues, globalValues); var requestName = execution.item.name // Process properties if (propertyValues && propertyValues.length) { properties = testsuite.ele('properties'); _.forEach(propertyValues, function (propertyItem) { // do not log overriden name '__name__' properties if (propertyItem.key.toLowerCase().startsWith(REQ_NAME_KEY)) { // request name: __name__<id_iteration><request_name> if (propertyItem.value != null && propertyItem.value.trim() !== '' && propertyItem.key.toLowerCase() === REQ_NAME_KEY + execution.cursor.iteration + execution.item.name.toLowerCase()) { requestName = propertyItem.value; // iteration name: __name__<id_iteration> } else if (propertyItem.value != null && propertyItem.value.trim() !== '' && propertyItem.key.toLowerCase() === REQ_NAME_KEY + execution.cursor.iteration) { iterationName = propertyItem.value; } return } if (reporterOptions.hideSensitiveData && ( propertyItem.key.toLowerCase().includes("user") || propertyItem.key.toLowerCase().includes("token") || propertyItem.key.toLowerCase().includes("password") || propertyItem.key.toLowerCase().includes("pwd") || propertyItem.key.toLowerCase().includes("passwd") || propertyItem.key.toLowerCase().includes("usr") )) { return } var property = properties.ele('property'); property.att('name', propertyItem.key); if (propertyItem.value === null) { propertyItem.value = ""; } property.att('value', propertyItem.value.toString().substring(0, 70)); }); } testsuite.att('id', (execution.cursor.iteration * execution.cursor.length) + execution.cursor.position); // Hostname var protocol = _.get(execution, 'request.url.protocol', 'https') + '://'; var hostName = _.get(execution, 'request.url.host', ['localhost']); testsuite.att('hostname', protocol + hostName.join('.')); // Package var packageName = getParentName(execution.item) if (execution.cursor.cycles > 1) { if (packageName) { testsuite.att('package', iterationName + SEPARATOR + packageName); } else { testsuite.att('package', iterationName); } } else { testsuite.att('package', getParentName(execution.item)); } // Name testsuite.att('name', requestName); // Tests if (execution.assertions) { testsuite.att('tests', execution.assertions.length); } else { testsuite.att('tests', 0); } // Timestamp testsuite.att('timestamp', date); // Time testsuite.att('time', (_.get(execution, 'response.responseTime') / 1000 || 0).toFixed(3)); // Timestamp (add time) date = moment(date).add(_.get(execution, 'response.responseTime'), 'ms').local().format('YYYY-MM-DDTHH:mm:ss.SSS'); // Process assertions (testcases) _.forEach(['prerequestScript', 'assertions', 'testScript'], function (property) { _.forEach(execution[property], function (testExecution) { var testcase = testsuite.ele('testcase'); // Classname var className = []; className.push(_.get(testcase.up(), 'attributes.package.value')); className.push(_.get(testcase.up(), 'attributes.name.value')); testcase.att('classname', className.join(SEPARATOR)); if (property === 'assertions') { // Name testcase.att('name', testExecution.assertion); // Time (testsuite time divided by number of assertions) testcase.att('time', (_.get(testcase.up(), 'attributes.time.value') / execution.assertions.length || 0).toFixed(3)); } else { // Name testcase.att('name', property === 'testScript' ? 'Tests' : 'Pre-request Script'); } // Errors / Failures var errorItem = testExecution.error; if (errorItem) { var result; if (property !== 'assertions') { // Error ++errors; result = testcase.ele('error'); if (errorItem.stacktrace) { result.dat(errorItem.stacktrace); } } else { // Failure ++failures; result = testcase.ele('failure'); result.dat(errorItem.stack); } result.att('type', errorItem.name); result.att('message', errorItem.message); } }); }); // Failures testsuite.att('failures', failures); failuresTotal += failures; // Errors testsuite.att('errors', errors); errorsTotal += errors; }); // Total failures and errors across all test suites // Add flag --reporter-xunit-aggregate to trigger if (reporterOptions.aggregate) { root.att('failures', failuresTotal); root.att('errors', errorsTotal); } newman.exports.push({ name: 'junit-reporter-full', default: 'newman-run-report-full.xml', path: reporterOptions.export, content: root.end({ pretty: true, indent: ' ', newline: '\n', allowEmpty: false }) }); }); }; module.exports = JunitFullReporter;