@xray-app/xray-automation
Version:
Library for uploading test results to Xray Test Management
292 lines (291 loc) • 11.5 kB
JavaScript
import axios from "axios";
import fs from "fs";
import FormData from "form-data";
import XrayErrorResponse from "./xray-error-response";
// import XrayDatacenterResponseV1 from './xray-datacenter-response-v1';
import XrayDatacenterResponseV2 from "./xray-datacenter-response-v2";
import { XRAY_FORMAT, JUNIT_FORMAT, TESTNG_FORMAT, ROBOT_FORMAT, NUNIT_FORMAT, XUNIT_FORMAT, CUCUMBER_FORMAT, BEHAVE_FORMAT, } from "./index";
export class XrayDatacenterClient {
constructor(xraySettings) {
this.xraySettings = xraySettings;
this.supportedFormats = [
XRAY_FORMAT,
JUNIT_FORMAT,
TESTNG_FORMAT,
ROBOT_FORMAT,
NUNIT_FORMAT,
XUNIT_FORMAT,
CUCUMBER_FORMAT,
BEHAVE_FORMAT,
];
this.jiraBaseUrl = xraySettings.jiraBaseUrl;
this.jiraUsername = xraySettings.jiraUsername;
this.jiraPassword = xraySettings.jiraPassword;
this.jiraToken = xraySettings.jiraToken;
if (xraySettings.timeout !== undefined)
this.timeout = xraySettings.timeout;
else
this.timeout = 50000;
axios.defaults.timeout = this.timeout;
}
async submitResults(reportPath, config) {
if (config.format === undefined)
throw new XrayErrorResponse("ERROR: format must be specified");
if (!this.supportedFormats.includes(config.format))
throw new XrayErrorResponse("ERROR: unsupported format " + config.format);
let endpointUrl;
if (config.format === XRAY_FORMAT) {
endpointUrl = this.jiraBaseUrl + "/rest/raven/2.0/import/execution";
}
else {
endpointUrl =
this.jiraBaseUrl + "/rest/raven/2.0/import/execution/" + config.format;
}
let authorizationHeaderValue;
if (this.jiraToken !== undefined) {
authorizationHeaderValue = "Bearer " + this.jiraToken;
}
else {
authorizationHeaderValue =
"Basic " +
Buffer.from(this.jiraUsername + ":" + this.jiraPassword).toString("base64");
}
let reportContent;
try {
reportContent = fs.readFileSync(reportPath).toString();
}
catch (error) {
throw new XrayErrorResponse(error.message);
}
// for xray, cucumber and behave reports send the report directly on the body
if ([XRAY_FORMAT, CUCUMBER_FORMAT, BEHAVE_FORMAT].includes(config.format)) {
// use a CancelToken as the timeout setting is not reliable
const cancelTokenSource = axios.CancelToken.source();
const timeoutFn = setTimeout(() => {
cancelTokenSource.cancel("request timeout");
}, this.timeout);
return axios
.post(endpointUrl, reportContent, {
timeout: this.timeout,
cancelToken: cancelTokenSource.token,
headers: {
Authorization: authorizationHeaderValue,
"Content-Type": "application/json",
},
})
.then((response) => {
clearTimeout(timeoutFn);
return new XrayDatacenterResponseV2(response);
})
.catch((error) => {
throw new XrayErrorResponse(error.response);
});
}
else {
const params = {};
if (config.projectKey === undefined && config.testExecKey === undefined) {
throw new XrayErrorResponse("ERROR: projectKey or testExecKey must be defined");
}
if (config.projectKey !== undefined) {
params.projectKey = config.projectKey;
}
if (config.testPlanKey !== undefined) {
params.testPlanKey = config.testPlanKey;
}
if (config.testExecKey !== undefined) {
params.testExecKey = config.testExecKey;
}
if (config.version !== undefined) {
params.fixVersion = config.version;
}
if (config.revision !== undefined) {
params.revision = config.revision;
}
if (config.testEnvironment !== undefined) {
params.testEnvironments = config.testEnvironment;
}
if (config.testEnvironments !== undefined) {
params.testEnvironments = config.testEnvironments.join(";");
}
const urlParams = new URLSearchParams({ ...params }).toString();
const url = endpointUrl + "?" + urlParams;
const bodyFormData = new FormData();
let fileName;
if ([
JUNIT_FORMAT,
TESTNG_FORMAT,
NUNIT_FORMAT,
XUNIT_FORMAT,
ROBOT_FORMAT,
].includes(config.format)) {
fileName = "report.xml";
}
else {
fileName = "report.json";
}
bodyFormData.append("file", reportContent, fileName);
// use a CancelToken as the timeout setting is not reliable
const cancelTokenSource = axios.CancelToken.source();
const timeoutFn = setTimeout(() => {
cancelTokenSource.cancel("request timeout");
}, this.timeout);
return axios
.post(url, bodyFormData, {
timeout: this.timeout,
cancelToken: cancelTokenSource.token,
headers: {
Authorization: authorizationHeaderValue,
...bodyFormData.getHeaders(),
},
})
.then((response) => {
clearTimeout(timeoutFn);
return new XrayDatacenterResponseV2(response);
})
.catch((error) => {
throw new XrayErrorResponse(error.message || error.response);
});
}
}
async submitResultsMultipart(reportPath, config) {
if (config.format === undefined)
throw new XrayErrorResponse("ERROR: format must be specified");
if (!this.supportedFormats.includes(config.format))
throw new XrayErrorResponse("ERROR: unsupported format " + config.format);
if (config.testExecInfoFile === undefined &&
config.testExecInfo === undefined)
throw new XrayErrorResponse("ERROR: testExecInfoFile or testExecInfo must be defined");
let endpointUrl;
if (config.format === XRAY_FORMAT) {
endpointUrl =
this.jiraBaseUrl + "/rest/raven/2.0/import/execution/multipart";
}
else {
endpointUrl =
this.jiraBaseUrl +
"/rest/raven/2.0/import/execution/" +
config.format +
"/multipart";
}
let authorizationHeaderValue;
if (this.jiraToken !== undefined) {
authorizationHeaderValue = "Bearer " + this.jiraToken;
}
else {
authorizationHeaderValue =
"Basic " +
Buffer.from(this.jiraUsername + ":" + this.jiraPassword).toString("base64");
}
let reportContent;
let testInfoContent;
let testExecInfoContent;
try {
reportContent = fs.readFileSync(reportPath).toString();
if (config.testInfoFile !== undefined)
testInfoContent = fs.readFileSync(config.testInfoFile).toString();
if (config.testInfo !== undefined)
testInfoContent = config.testInfo.toString();
if (config.testExecInfoFile !== undefined)
testExecInfoContent = fs
.readFileSync(config.testExecInfoFile)
.toString();
else
testExecInfoContent = config.testExecInfo.toString();
}
catch (error) {
throw new XrayErrorResponse(error.message);
}
const bodyFormData = new FormData();
let filePartName;
let fileName;
if ([
JUNIT_FORMAT,
TESTNG_FORMAT,
NUNIT_FORMAT,
XUNIT_FORMAT,
ROBOT_FORMAT,
].includes(config.format)) {
filePartName = "file";
fileName = "report.xml";
}
else {
filePartName = "result";
fileName = "report.json";
}
bodyFormData.append(filePartName, reportContent, fileName);
bodyFormData.append("info", testExecInfoContent, "info.json");
if (testInfoContent !== undefined &&
[
JUNIT_FORMAT,
TESTNG_FORMAT,
NUNIT_FORMAT,
XUNIT_FORMAT,
ROBOT_FORMAT,
].includes(config.format))
bodyFormData.append("testInfo", testInfoContent, "testInfo.json");
// use a CancelToken as the timeout setting is not reliable
const cancelTokenSource = axios.CancelToken.source();
const timeoutFn = setTimeout(() => {
cancelTokenSource.cancel("request timeout");
}, this.timeout);
return axios
.post(endpointUrl, bodyFormData, {
timeout: this.timeout,
cancelToken: cancelTokenSource.token,
headers: {
Authorization: authorizationHeaderValue,
...bodyFormData.getHeaders(),
},
})
.then((response) => {
clearTimeout(timeoutFn);
return new XrayDatacenterResponseV2(response);
})
.catch((error) => {
throw new XrayErrorResponse(error.response);
});
}
async associateTestExecutionToTestPlan(testExecKey, testPlanKey) {
let authorizationHeaderValue;
if (this.jiraToken !== undefined) {
authorizationHeaderValue = "Bearer " + this.jiraToken;
}
else {
authorizationHeaderValue =
"Basic " +
Buffer.from(this.jiraUsername + ":" + this.jiraPassword).toString("base64");
}
const content = {
add: [testExecKey],
};
const endpointUrl = `${this.jiraBaseUrl}/rest/raven/2.0/api/testplan/${testPlanKey}/testexecution`;
// use a CancelToken as the timeout setting is not reliable
const cancelTokenSource = axios.CancelToken.source();
const timeoutFn = setTimeout(() => {
cancelTokenSource.cancel("request timeout");
}, this.timeout);
return axios
.post(endpointUrl, content, {
timeout: this.timeout,
cancelToken: cancelTokenSource.token,
headers: {
Authorization: authorizationHeaderValue,
"Content-Type": "application/json",
},
})
.then((response) => {
clearTimeout(timeoutFn);
if (response.data.length === 0)
return testExecKey;
else
throw new XrayErrorResponse(response.data[0]);
})
.catch((error) => {
if (error instanceof XrayErrorResponse)
throw error;
else
throw new XrayErrorResponse(error.response);
});
}
}
/// export default { XrayClient };