var _ = require('lodash'), xml = require('xmlbuilder'), util = require('../../util'), JunitReporter; /** * 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 {*} */ JunitReporter = function (newman, reporterOptions) { newman.on('beforeDone', function () { var report = _.get(newman, 'summary.run.executions'), collection = _.get(newman, 'summary.collection'), cache, root, testSuitesExecutionTime = 0, executionTime = 0, timestamp, classname; if (!report) { return; } classname = _.upperFirst(_.camelCase(collection.name).replace(/\W/g, '')); 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')); cache = _.transform(report, function (accumulator, execution) { accumulator[execution.item.id] = accumulator[execution.id] || []; accumulator[execution.item.id].push(execution); }, {}); timestamp = new Date(_.get(newman, 'summary.run.timings.started')).toISOString(); _.forEach(cache, function (executions, itemId) { var suite = root.ele('testsuite'), currentItem, tests = {}, errors = 0, failures = 0, errorMessages; collection.forEachItem(function (item) { (item.id === itemId) && (currentItem = item); }); if (!currentItem) { return; } suite.att('name', util.getFullName(currentItem)); suite.att('id', currentItem.id); suite.att('timestamp', timestamp); _.forEach(executions, function (execution) { var iteration = execution.cursor.iteration, errored, msg = `Iteration: ${iteration}\n`; // Process errors if (execution.requestError) { ++errors; errored = true; msg += ('RequestError: ' + (execution.requestError.stack) + '\n'); } msg += '\n---\n'; _.forEach(['testScript', 'prerequestScript'], function (prop) { _.forEach(execution[prop], function (err) { if (err.error) { ++errors; errored = true; msg = (msg + prop + 'Error: ' + (err.error.stack || err.error.message)); msg += '\n---\n'; } }); }); if (errored) { errorMessages = _.isString(errorMessages) ? (errorMessages + msg) : msg; } // Process assertions _.forEach(execution.assertions, function (assertion) { var name = assertion.assertion, err = assertion.error; if (err) { ++failures; (_.isArray(tests[name]) ? tests[name].push(err) : (tests[name] = [err])); } else { tests[name] = []; } }); if (execution.assertions) { suite.att('tests', execution.assertions.length); } else { suite.att('tests', 0); } suite.att('failures', failures); suite.att('errors', errors); }); suite.att('time', _.mean(_.map(executions, function (execution) { executionTime = _.get(execution, 'response.responseTime') / 1000 || 0; testSuitesExecutionTime += executionTime; return executionTime; })).toFixed(3)); errorMessages && suite.ele('system-err').dat(errorMessages); _.forOwn(tests, function (failures, name) { var testcase = suite.ele('testcase'), failure; testcase.att('name', name); testcase.att('time', executionTime.toFixed(3)); // Set the same classname for all the tests testcase.att('classname', _.get(testcase.up(), 'attributes.name.value', classname)); if (failures && failures.length) { failure = testcase.ele('failure'); failure.att('type', 'AssertionFailure'); failure.dat('Failed ' + failures.length + ' times.'); failure.dat('Collection JSON ID: ' + collection.id + '.'); failure.dat('Collection name: ' + collection.name + '.'); failure.dat('Request name: ' + util.getFullName(currentItem) + '.'); failure.dat('Test description: ' + name + '.'); if (failures.length !== 0) { failure.att('message', failures[0].message); failure.dat('Error message: ' + failures[0].message + '.'); failure.dat('Stacktrace: ' + failures[0].stack + '.'); } } }); }); root.att('time', testSuitesExecutionTime.toFixed(3)); newman.exports.push({ name: 'junit-reporter', default: 'newman-run-report.xml', path: reporterOptions.export, content: root.end({ pretty: true, indent: ' ', newline: '\n', allowEmpty: false }) }); }); }; module.exports = JunitReporter;