"use strict";
/**
* [TestRail](https://www.gurock.com/testrail) reporter publishs test results
* to remote testrail server via its API.
*
* @module
*/
const util = require("util");
const expect = require("chai").expect;
const Testrail = require("testrail-api");
const LOG = require("glace-utils").logger;
const CONF = require("../config");
const TestCase = require("../testing").TestCase;
for (const opt in CONF.testrail) {
expect(CONF.testrail[opt],
`TestRail option '${opt}' is not specified in config`)
.to.exist;
}
const testrail = new Testrail({
host: CONF.testrail.host,
user: CONF.testrail.user,
password: CONF.testrail.token });
testrail.isFailed = false;
const Results = {
PASSED: 1,
BLOCKED: 2,
UNTESTED: 3,
RETEST: 4,
FAILED: 5
};
const cases = {};
let report = Promise.resolve();
const Reporter = {
/**
* Called on tests start.
*
* @method
* @instance
*/
start: () => {
report = report.then(() => {
return testrail.getCases(
CONF.testrail.projectId, { suite_id: CONF.testrail.suiteId });
}).then(result => {
for (const remoteCase of result.body) {
if (cases[remoteCase.title]) {
throw new Error("Detect duplicated cases in TestRail for " +
`name '${remoteCase.title}'. Only unique names should be.`);
}
cases[remoteCase.title] = { id: remoteCase.id };
}
}).then(() => {
return testrail.addRun(CONF.testrail.projectId, {
suite_id: CONF.testrail.suiteId, name: CONF.testrail.runName,
description: CONF.testrail.runDescription });
}).then(result => {
CONF.testrail.runId = result.body.id;
}).catch(e => {
testrail.isFailed = true;
LOG.error(util.format("Error to init TestRail report:", e));
});
},
/**
* Called before tests end.
*
* @method
* @instance
*/
end: () => {
if (testrail.isFailed) return;
console.log();
const reportMsg = "TestRail report is " + CONF.testrail.host +
"/index.php?/runs/view/" + CONF.testrail.runId;
console.log(Array(reportMsg.length + 1).join("-").yellow);
console.log(reportMsg.yellow);
},
/**
* Called on test end.
*
* @method
* @instance
*/
testEnd: () => {
if (testrail.isFailed) return;
const testrailCase = cases[CONF.test.curCase.name];
if (!testrailCase) {
LOG.error(`Testrail case '${CONF.test.curCase.name}' is absent`);
return;
}
const testResult = { status_id: Results.PASSED, comment: "" };
testResult.comment = Reporter.setComment();
if (CONF.test.curCase.status === TestCase.SKIPPED) {
testResult.status_id = Results.BLOCKED;
}
if (CONF.test.curCase.screenshots.length) {
testResult.comment += Reporter.processScreens(CONF.test.curCase.screenshots);
}
if (CONF.test.curCase.videos.length) {
testResult.comment += Reporter.processVideos(CONF.test.curCase.videos);
}
if (CONF.test.curCase.status === TestCase.FAILED) {
testResult.status_id = Results.FAILED;
testResult.comment += Reporter.processErrors(CONF.test.curCase.errors);
}
if (CONF.test.curCase.rawInfo.length) {
testResult.comment += Reporter.processExtras(CONF.test.curCase.rawInfo);
}
testResult.comment = testResult.comment.trim();
report = report.then(() => {
return testrail.addResultForCase(
CONF.testrail.runId, testrailCase.id, testResult);
}).catch(e => {
LOG.error(util.format(
`Error to publish test '${CONF.test.curCase.name}' report to TestRail:`, e));
});
},
/**
* Called on report finalizing.
*
* @method
* @instance
*/
done: () => report,
};
/**
* Entry point to set comment of test before testrail publication.
* Can be overridden with custom function.
*
* @memberOf module:reporter/testrail
* @instance
* @function setComment
* @return {string} - Test comment.
*
* @example <caption><b>Overriding with custom function</b></caption>
* // should be after configuration but before tests run
* const testrail = require("glace-core/lib/reporter/testrail");
* testrail.setComment = myFuncToSetComment;
*/
Reporter.setComment = () => "";
/**
* Entry point to process test screenshot paths before testrail publication.
* Can be overridden with custom function.
*
* @memberOf module:reporter/testrail
* @instance
* @function processScreens
* @arg {string[]} screens - List of screenshot paths.
* @return {string} - Test screenshots info, attaching to test comment.
*
* @example <caption><b>Overriding with custom function</b></caption>
* // should be after configuration but before tests run
* const testrail = require("glace-core/lib/reporter/testrail");
* testrail.processScreens = myFuncToProcessScreens;
*/
Reporter.processScreens = screens => {
let result = "\n\nScreenshots:";
screens.forEach((screen, i) => {
result += `\n${i + 1}. ${screen}`;
});
return result;
};
/**
* Entry point to process test video paths before testrail publication.
* Can be overridden with custom function.
*
* @memberOf module:reporter/testrail
* @instance
* @function processVideos
* @arg {string[]} videos - List of video paths.
* @return {string} - Test videos info, attaching to test comment.
*
* @example <caption><b>Overriding with custom function</b></caption>
* // should be after configuration but before tests run
* const testrail = require("glace-core/lib/reporter/testrail");
* testrail.processVideos = myFuncToProcessVideos;
*/
Reporter.processVideos = videos => {
let result = "\n\nVideos:";
videos.forEach((video, i) => {
result += `\n${i + 1}. ${video}`;
});
return result;
};
/**
* Entry point to process test errors before testrail publication.
* Can be overridden with custom function.
*
* @memberOf module:reporter/testrail
* @instance
* @function processErrors
* @arg {string[]} errors - List of test errors.
* @return {string} - Test errors info, attaching to test comment.
*
* @example <caption><b>Overriding with custom function</b></caption>
* // should be after configuration but before tests run
* const testrail = require("glace-core/lib/reporter/testrail");
* testrail.processErrors = myFuncToProcessErrors;
*/
Reporter.processErrors = errors => {
let result = "\n\nErrors:";
errors.forEach((error, i) => {
result += `${i ? "\n" : ""}\n${i + 1}. ${error}`;
});
return result;
};
/**
* Entry point to process test extra details before testrail publication.
* Can be overridden with custom function.
*
* @memberOf module:reporter/testrail
* @instance
* @function processExtras
* @arg {string[]} extras - List of extra details.
* @return {string} - Test extra details, attaching to test comment.
*
* @example <caption><b>Overriding with custom function</b></caption>
* // should be after configuration but before tests run
* const testrail = require("glace-core/lib/reporter/testrail");
* testrail.processExtras = myFuncToProcessExtras;
*/
Reporter.processExtras = extras => {
let result = "\n\nExtra details:";
extras.forEach((extra, i) => {
result += `${i ? "\n" : ""}\n${i + 1}. ${extra}`;
});
return result;
};
module.exports = Reporter;