UNPKG

mimik

Version:

Write end-to-end automation tests in natural language

238 lines (227 loc) 8.39 kB
/*jshint node:true*/ /** * TestRailReporter * * Supports reporting using annotations at feature or scenario level. * * Global configuration: * Enable the reporter with the required parameters in a config file: * "reporters": [ * { * "name": "testrail", * "host": "http://testrail.somesever.com", * "username": "username", * "password": "somepassword" * } * ] * * Supported Annotations: * @TestRailReporter=addResult(test_id). Example: @TestRailReporter=addResult(1234) * @TestRailReporter=addResultForCase(run_id,case_id). Example: @TestRailReporter=addResultForCase(12,34) * See http://docs.gurock.com/testrail-api2/reference-results for details */ 'use strict'; var url = require('url'), async = require('async'), utils = require('../../lib/utils'), logger = require('winston').loggers.get('mimik'), serverPath = 'index.php?/api/v2'; function Reporter(runner, config) { var me = this; if (!runner) { return; } me.config = config; me.validateConfig(config); } Reporter.prototype.validateConfig = function(config) { var valid = true; ['host', 'username', 'password'].forEach(function(param) { if(!config[param]) { console.error('TestRailReporter requires "' + param + '" as a parameter.'); valid = false; } }); return valid; }; Reporter.prototype.process = function(stats, target, callback) { var me = this; logger.profile('[testrail reporter] update results'); async.series([ function(cb) { me.reportFeatureResults(stats, cb); }, function(cb) { me.reportScenarioResults(stats, cb); } ], function() { logger.profile('[testrail reporter] results updated'); callback(); }); }; Reporter.prototype.reportFeatureResults = function (stats, cb) { var me = this, tag, tags = []; utils.each(stats.results, function(report) { tag = report.feature.annotations.TestRailReporter; if(tag) { tags.push([me.getQueryParameters(tag), me.getFeatureData(report)]); } }); async.each(tags, function(data, callback) { me.sendRequest(data[0], data[1], callback); }, cb); }; Reporter.prototype.reportScenarioResults = function (stats, cb) { var me = this, tag, tags = []; utils.each(stats.results, function(report) { utils.each(report.scenarios, function(scenario) { tag = scenario.scenario.annotations.TestRailReporter; // exclude pending scenarios if(tag && !scenario.scenario.annotations.pending) { tags.push([me.getQueryParameters(tag), me.getScenarioData(scenario)]); } }); }); async.each(tags, function(data, callback) { me.sendRequest(data[0], data[1], callback); }, cb); }; Reporter.prototype.getQueryParameters = function(tag) { var params = tag.match(/(\w+)\s*(?:\(([^)]+)\))?/), query, method = params[1], args = params[2] ? params[2].match(/\d+/g) : []; // transforms addResultsForCase(37,71) to 'add_result_for_case/37/71' switch(method) { case 'addResultForCase': query = ['add_result_for_case'].concat(args).join('/'); break; case 'addResult': query = ['add_result'].concat(args).join('/'); break; default: console.error('[testrail reporter] Method ' + method + ' not supported'); logger.debug('[testrail reporter] Method ' + method + ' not supported'); } return query; }; Reporter.prototype.getFeatureData = function (report) { var me = this, status = { passed: 1, blocked: 2, untested: 3, retest: 4, failed: 5 }, comment = ['# Mimik Feature Results #'], browserName = report.driver.getBrowserName(); comment.push('Feature: ' + report.feature.title); comment.push('File: ' + report.feature.file); comment.push('Duration: ' + me.getDuration(report.stats.duration)); if(browserName) { comment.push('Agent: ' + browserName); } comment.push('\nScenarios: ' + report.scenarios.length); comment.push('Tests: ' + report.stats.tests); comment.push('Passed: ' + report.stats.passes); comment.push('Pending: ' + report.stats.pending); comment.push('Failed: ' + report.stats.failures); var data = { "status_id": report.stats.failures ? status.failed : status.passed, "comment": comment.join('\n'), "elapsed": report.stats.duration < 10 ? '0' : me.getDuration(report.stats.duration) }; return data; }; Reporter.prototype.getScenarioData = function (scenario) { var me = this, status = { passed: 1, blocked: 2, untested: 3, retest: 4, failed: 5 }, report = me.getTopSuite(scenario.suite)._reporterData, feature = report.feature, comment = ['# Mimik Scenario Results #'], browserName = report.driver.getBrowserName(); comment.push('Scenario: ' + scenario.title); comment.push('Feature: ' + feature.title); comment.push('File: ' + feature.file); comment.push('Duration: ' + me.getDuration(scenario.stats.duration)); if(browserName) { comment.push('Agent: ' + browserName); } comment.push('\nTests: ' + scenario.stats.tests); comment.push('Passed: ' + scenario.stats.passes); comment.push('Pending: ' + scenario.stats.pending); comment.push('Failed: ' + scenario.stats.failures); var data = { 'status_id': scenario.stats.failures ? status.failed : status.passed, 'comment': comment.join('\n'), 'elapsed': scenario.stats.duration < 10 ? '0' : me.getDuration(scenario.stats.duration), 'custom_step_results': [] }; utils.each(scenario.steps, function(step) { data.custom_step_results.push({ 'content': step.title, 'expected': !step.success && !step.pending ? step.test.err.expected : null, 'actual': !step.success && !step.pending ? step.test.err.actual : null, 'status_id': step.pending ? status.untested : (step.success ? status.passed : status.failed) }); }); return data; }; Reporter.prototype.sendRequest = function (query, data, cb) { var me = this; var options = { uri: url.resolve(me.config.host, serverPath + '/' + query), method: 'POST', headers: { 'Content-Type': 'application/json' }, auth: { user: me.config.username, pass: me.config.password, sendImmediately: true } }; options.body = JSON.stringify(data); var request = require('request'); request(options, function (err, response, body) { if (err) { logger.debug('[testrail reporter] Request failed: %s', err); console.error('[testrail reporter] Request failed:', err); } else if (response.statusCode !== 200) { var error = JSON.parse(body).error; logger.debug('[testrail reporter] Request failed: Returned with error code %s', response.statusCode); logger.debug(error); logger.debug(query); console.error('[testrail reporter] Request failed: Returned with error code', response.statusCode); console.error('[testrail reporter]', error, query); } else { logger.info('[testrail reporter] Request successful! Server responded with:', body); } cb(err); }); }; Reporter.prototype.getDuration = function(ms) { var parts = []; var seconds = Math.round(ms / 10) / 100; var h = Math.floor(seconds / 3600); var m = Math.floor((seconds - (h * 3600)) / 60); var s = seconds - (h * 3600) - (m * 60); if (h > 0) { parts.push(h + 'h'); } if (m > 0) { parts.push(m + 'm'); } if (s > 0 || !parts.length) { parts.push(s + 's'); } return parts.join(' '); }; Reporter.prototype.getTopSuite = function(suite) { var parent = suite; while(parent && !parent.root) { parent = parent.parent; } return parent ? parent.suites[0] : null; }; exports = module.exports = { name: 'testrail', proto: Reporter };