Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions src/Reporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class Reporter {
this.includeAllCasesDuringCreation = configService.includeAllCasesDuringCreation();
this.includeAllFailedScreenshots = configService.includeAllFailedScreenshots();
this.ignorePendingTests = configService.ignorePendingCypressTests();
this.ignoreMissingCaseIDs = configService.ignoreMissingCaseIds();

this.modeCreateRun = !configService.hasRunID();
this.closeRun = configService.shouldCloseRun();
Expand Down Expand Up @@ -256,7 +257,11 @@ class Reporter {

if (allResults.length > 0) {
for (let i = 0; i < this.runIds.length; i += 1) {
const request = this.testrail.sendBatchResults(this.runIds[i], allResults);
const request = this.testrail.sendBatchResults(
this.runIds[i],
allResults,
this.ignoreMissingCaseIDs,
);
allRequests.push(request);
}

Expand Down Expand Up @@ -371,4 +376,4 @@ class Reporter {
}
}

module.exports = Reporter;
module.exports = Reporter;
32 changes: 31 additions & 1 deletion src/components/TestRail/ApiClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const axios = require('axios');
const ApiError = require('./ApiError');
const FormData = require('form-data');
const fs = require('fs');
// const ColorConsole = require('../../services/ColorConsole');

class ApiClient {
/**
Expand All @@ -15,6 +16,35 @@ class ApiClient {
this.baseUrl = `https://${domain}/index.php?/api/v2`;
}

/**
*
* @param slug
* @returns {Promise<AxiosResponse<any>>}
*/
getData(slug, params={}) {
const fullUrl = this.baseUrl + slug;
return axios({
method: 'get',
url: fullUrl,
auth: {
username: this.username,
password: this.password,
},
params,
})
.then((response) => {
// ColorConsole.debug('>> getData response: ' + JSON.stringify(response.data));
return response;
})
.catch((error) => {
// Extract and handle the error
const apiError = new ApiError(error);
throw new Error(
`Error fetching data: ${apiError.getStatusCode()} ${apiError.getStatusText()} >> ${apiError.getErrorText()}`
);
});
}

/**
*
* @param slug
Expand Down Expand Up @@ -87,4 +117,4 @@ class ApiClient {
}
}

module.exports = ApiClient;
module.exports = ApiClient;
98 changes: 76 additions & 22 deletions src/components/TestRail/TestRail.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
const ColorConsole = require('../../services/ColorConsole');
const ApiClient = require('./ApiClient');



class TestRail {
/**
*
Expand All @@ -25,7 +27,15 @@ class TestRail {
* @param callback
* @returns {Promise<AxiosResponse<*>>}
*/
createRun(projectId, milestoneId, suiteId, name, description, includeAllCasesDuringCreation, callback) {
createRun(
projectId,
milestoneId,
suiteId,
name,
description,
includeAllCasesDuringCreation,
callback,
) {
if (typeof includeAllCasesDuringCreation !== 'boolean') {
includeAllCasesDuringCreation = false; //preserving existing functionality
}
Expand Down Expand Up @@ -170,61 +180,104 @@ class TestRail {
*
* @param {string} runID
* @param {Result[]} testResults
* @param {boolean} ignoreMissingCaseIds
* @returns {Promise<AxiosResponse<*>>}
*/
sendBatchResults(runID, testResults) {
const url = '/add_results_for_cases/' + runID;

const postData = {
results: [],
};

ColorConsole.debug('TestRail >> Sending case results to run R' + runID + ': ' + testResults.map((r) => 'C' + r.getCaseId()));
async sendBatchResults(runID, testResults, ignoreMissingCaseIds) {
if (typeof ignoreMissingCaseIds !== 'boolean') {
ignoreMissingCaseIds = false; //preserving existing functionality
}

// Check if there are any test results to send
ColorConsole.info('\n Cypress-TestRail send batch results:');
let validCaseIds = [];

if (ignoreMissingCaseIds) { // validCaseIds is only needed if Validating case_id
// Fetch valid case IDs for the given runID
let testsCaseIdPage = [];
// Pagination variables
let offset = 0;
const limit = 250; // Maximum allowed by TestRail

// Fetch valid case IDs from TestRail API
try {
// eslint-disable-next-line no-constant-condition
while (true) {
const getDataResponse = await this.client.getData(
`/get_tests/${runID}`,
{offset, limit},
);
// Extract valid case IDs from the response data
testsCaseIdPage = getDataResponse.data.tests.map((test) => test.case_id);

if (testsCaseIdPage.length === 0) break; // we've retrieved all case IDs
validCaseIds = validCaseIds.concat(testsCaseIdPage);
offset += limit; // Move to the next page
}
} catch (error) {
ColorConsole.error(`Could not fetch valid case IDs for run R${runID}: ${error.message}`);
return; // Exit the function if fetching valid case IDs fails
}
// ColorConsole.debug('>> testResults validCaseIds: ' + JSON.stringify(validCaseIds));
ColorConsole.info(` In run R${runID} there are ${validCaseIds.length} Valid Testrail cases\n`);
}

const url = '/add_results_for_cases/' + runID;
const postData = { results: [] };

// Filter testResults to include only those with valid case IDs
testResults.forEach((result) => {
var resultEntry = {
case_id: result.getCaseId(),
status_id: result.getStatusId(),
comment: result.getComment().trim(),
screenshotPaths: result.getScreenshotPaths(),
};

// only add an elapsed time, if a valid value exists
// otherwise TestRail will throw an error
if (result.hasElapsedTime()) {
resultEntry.elapsed = result.getElapsed();
}

postData.results.push(resultEntry);

// Check if the case_id is valid ie in the run
if (ignoreMissingCaseIds &&
!validCaseIds.includes(parseInt(resultEntry.case_id))) { // Check if the case_id is valid
ColorConsole.error(`Test case C${resultEntry.case_id} is not valid for run R${runID}. Skipping.`);
} else {
postData.results.push(resultEntry);
}
});


return this.client.sendData(
url,
postData,
(response) => {
ColorConsole.success('Results sent to TestRail R' + runID + ' for: ' + testResults.map((r) => 'C' + r.getCaseId()));

ColorConsole.success('Cypress results sent to TestRail R'
+ runID + ' for: ' + postData.results.map((r) => 'C' + r.case_id).join(', '));

if (this.isScreenshotsEnabled) {
const allRequests = [];

testResults.forEach((result, i) => {
const screenshotPaths = result.getScreenshotPaths();
postData.results.forEach((result, i) => { // cypress-testrail-greenwich mod
const screenshotPaths = result.screenshotPaths; // cypress-testrail-greenwich mod

if (screenshotPaths.length) {
// there is no identifier, to match both, but
// we usually get the same order back as we sent it to TestRail
const matchingResultId = response.data[i].id;

screenshotPaths.forEach((screenshot) => {
ColorConsole.debug('sending screenshot to TestRail for TestCase C' + result.getCaseId());
ColorConsole.debug(' sending screenshot to TestRail for TestCase C' + result.case_id);

const addScreenShotRequest = this.client.sendScreenshot(
matchingResultId,
screenshot.path,
() => {
ColorConsole.success('created screenshot');
ColorConsole.success(' created screenshot');
},
(error) => {
ColorConsole.error(`could not create screenshot: ${error}`);
ColorConsole.error(` could not create screenshot: ${error}`);
ColorConsole.debug('');
}
);
Expand All @@ -238,11 +291,12 @@ class TestRail {
}
},
(statusCode, statusText, errorText) => {
ColorConsole.error('Could not send list of TestRail results: ' + statusCode + ' ' + statusText + ' >> ' + errorText);
ColorConsole.error(' Could not send list of Cypress results to TestRail : '
+ statusCode + ' ' + statusText + ' >> ' + errorText);
ColorConsole.debug('');
}
);
}
}

module.exports = TestRail;
module.exports = TestRail;
8 changes: 8 additions & 0 deletions src/services/Config/ConfigService.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,14 @@ class ConfigService {
return this._valueLoader.getBooleanValue('TESTRAIL_RUN_INCLUDE_ALL', 'runIncludeAll', false);
}

/**
*
* @returns {boolean}
*/
ignoreMissingCaseIds() {
return this._valueLoader.getBooleanValue('TESTRAIL_IGNORE_MISSING_CASE_IDS', 'ignoreMissingCaseIds', false);
}

/**
*
* @returns {boolean}
Expand Down