tr_cypress_reporter_simple
Version:
This is a simple module designed to easily upload information about tests to a testrail. The module does not use asynchronous requests and you can easily inject methods into the Cypress context.
765 lines (652 loc) • 31.5 kB
JavaScript
'use strict';
const TestRailConnector = require("./TestRailConnector");
const credentials = require("../../../tr_credentials.json");
const fs = require('fs');
const json = require('json-update');
const sleep = require('sleep');
let resolve = require('path').resolve
class TestRaiImporter {
constructor(run) {
this.connection();
this.run = run;
}
connection() {
this.tr = new TestRailConnector({
host: credentials.host,
user: credentials.username,
password: credentials.password,
});
console.log("Auth in TestRAil");
this._project = this.findProject(credentials.project_name);
this._suite = this.findSuite(credentials.suite_name, this._project.id);
}
findProject(name) {
console.log("Find project by " + name);
let projects = this.tr.getProjects({is_completed: 0}).projects;
if (projects.filter(p => p.name === name).length === 0) {
throw new Error("Project " + name + " does not found")
} else {
this.project = projects.filter(p => p.name === name)[0];
}
return this.project;
}
findSuite(name, projectId) {
console.log("Find suite by " + name + " in " + projectId);
let suites = this.tr.getSuites(projectId);
if (suites.filter(p => p.name === name).length === 0) {
throw new Error("Suite " + name + " does not found in a project:" + projectId)
} else {
this.suite = suites.filter(p => p.name === name)[0];
}
return this.suite;
}
getSuiteCases(projectId, suiteId) {
console.log("Get case list from suite " + suiteId + " in " + projectId);
if (projectId && suiteId)
return this.tr.getCases(projectId, {suite_id: suiteId}).cases;
else
throw new Error('Project id or Suite id eqls null')
}
getSuiteCases(projectId, suiteId, offset) {
console.log("Get case list from suite " + suiteId + " in " + projectId);
if (projectId && suiteId)
return this.tr.getCases(projectId, {suite_id: suiteId, offset: offset}).cases;
else
throw new Error('Project id or Suite id eqls null')
}
getSectionCases(projectId, suiteId, sectionId) {
console.log("Get case list from section " + sectionId + " in " + suiteId);
if (projectId && suiteId)
return this.tr.getCases(projectId, {suite_id: suiteId, section_id: sectionId}).cases;
else
throw new Error('Project id or Suite id eqls null')
}
getSuiteSections(projectId, suiteId) {
console.log("Get section list from suite " + suiteId + " in " + projectId);
if (projectId && suiteId)
return this.tr.getSections(projectId, {suite_id: suiteId}).sections;
else
throw new Error('Project id or Suite id eqls null')
}
getSuiteSections(projectId, suiteId, offset) {
console.log("Get section list from suite " + suiteId + " in " + projectId);
if (projectId && suiteId)
return this.tr.getSections(projectId, {suite_id: suiteId, offset: offset}).sections;
else
throw new Error('Project id or Suite id eqls null')
}
getRuns(projectId) {
console.log("Get run list for" + projectId);
if (projectId)
return this.tr.getRuns(projectId, {is_completed: 0}).runs;
else
throw new Error('Project id eql null')
}
createNewCase(sectionId, name, description) {
console.log("Add new case: " + name + " in section " + sectionId);
let body = {
title: name,
custom_steps_separated: [{
content: description,
expected: "Correct work of the automated test."
}]
}
return this.tr.addCase(sectionId, body);
}
createNewSection(projectId, suiteId, name, parent_id) {
console.log("Add new section: " + name + " in project/suite " + projectId + '/' + suiteId + ' for parent section ' + parent_id);
let body = {
name: name,
suite_id: suiteId,
parent_id: parent_id
}
return JSON.parse(this.tr.addSection(projectId, body).responseText);
}
createNewRun(projectId, suiteId, name, description) {
console.log("Create new run: " + name + " in project " + projectId);
let body = {
name: name,
include_all: true,
suite_id: suiteId,
description: description
}
return JSON.parse(this.tr.addRun(projectId, body).responseText);
}
updateRun(runId, suiteId, name, description) {
console.log("Update run: " + name + " in " + runId);
let body = {
name: name,
include_all: true,
suite_id: suiteId,
description: description
}
return this.tr.updateRun(runId, body);
}
attachToCaseResult(resultId, file) {
let auth = Buffer.from(credentials.username + ":" + credentials.password).toString('base64');
return this.tr.addAttachmentToResult(auth, resultId, file);
}
getResultsByRun(runId, statusesId) {
console.log("Get results for run: " + runId + " by " + statusesId);
return this.tr.getResultsForRun(runId, {status_id: statusesId}).results;
}
getResultsByCase(runId, caseId, statusesId) {
console.log("Get results for case: " + caseId + " by status " + statusesId);
return this.tr.getResultsForCase(runId, caseId, {status_id: statusesId}).results;
}
closeRun(runId, close) {
if (runId && close) {
this.tr.closeRun(runId);
console.log("Run " + runId + " has been closed.")
}
}
updateSuiteCases() {
let sections = this.getSuiteSections(this._project.id, this._suite.id);
let cases_from_suite = this.getSuiteCases(this._project.id, this._suite.id)
let completed_data = {};
let completed_sections = [];
sections.forEach(section => {
let filtered_cases = cases_from_suite.filter(c => c.section_id === section.id)
let s = {};
if (filtered_cases.length === 0) {
s.section_name = section.name;
s.section_id = section.id;
s.cases = [];
} else {
s.section_name = section.name;
s.section_id = section.id;
s.cases = filtered_cases;
}
completed_sections.push(s);
})
completed_data.sections = completed_sections;
this.run.runs.forEach(r => {
let filtered_section = completed_data.sections.filter(section => section.section_name === r.spec.name);
if (filtered_section.length === 0) {
let section = this.createNewSection(this._project.id, this._suite.id, r.spec.name);
r.tests.forEach(test => {
this.createNewCase(section.id, test.title.map((s) => {
return s.trim()
})[test.title.length - 1], test.body)
})
} else {
r.tests.forEach(test => {
if (filtered_section[0].cases.filter(c => c.title === test.title.map((s) => {
return s.trim()
})[test.title.length - 1]).length === 0) {
this.createNewCase(filtered_section[0].section_id, test.title.map((s) => {
return s.trim()
})[test.title.length - 1], test.body)
}
})
}
});
}
addCasesFromArtifacts(fromFolder) {
let resolve = require('path').resolve
fs.readdir(fromFolder, (err, files) => {
files.forEach(file => {
let absolute_path_to_file = resolve(fromFolder.concat(file));
console.log(absolute_path_to_file);
let data_from_file = require(absolute_path_to_file);
let failed_sections = data_from_file.sections.filter(
section => section.cases.filter(c => c.isCreated === false).length > 0);
let sections_from_server = this.getAllSections()
// console.log(sections_from_server)
for (let failedSection of failed_sections) {
// console.log(failedSection.section_name.split('/')[failedSection.section_name.split('/').length-1])
let section_id = sections_from_server.filter(s => s.name === failedSection.section_name.split('/')[failedSection.section_name.split('/').length - 1])[0].id;
let failed_cases = failedSection.cases.filter(c => c.isCreated === false);
if (failed_cases.length > 0) {
for (let failedCase of failed_cases) {
let request;
try {
request = this.createNewCase(section_id, failedCase.title.trim(), 'You can see more details on the Dashboard.');
} catch (e) {
console.log("Status code was ".concat(request.status, " When creating new case"))
let cases = this.getSectionCases(this._project.id, this._suite.id, section_id).filter(c => c.title === failedCase.title.trim());
if (cases.length === 0) {
console.log('Try to recreate new request '.concat(failedCase.title.trim(), ' for ', section_id, ' section'))
this.createNewCase(section_id, failedCase.title.trim(), 'You can see more details on the Dashboard.');
} else {
console.log('Case '.concat(cases[0].id, ' was created'))
}
}
}
}
}
});
});
}
createSections(fromFolder) {
let resolve = require('path').resolve
fs.readdir(fromFolder, (err, files) => {
files.forEach(file => {
let absolute_path_to_file = resolve(fromFolder.concat(file));
console.log(absolute_path_to_file);
let data_from_file = require(absolute_path_to_file);
let sections_from_server = this.getAllSections()
data_from_file.sections.forEach(s => {
console.log(s.section_name.split('/'))
let section_path = s.section_name.split('/');
this.parent_section_id = null;
for (let i = 0; i < section_path.length; i++) {
let created_sections = sections_from_server.filter(s => s.name === section_path[i]);
if (created_sections.length > 0) {
continue;
}
if (section_path.length === 1) {
this.parent_section_id = null;
} else {
if (section_path[i].includes('.spec.js')) {
let posion_s = section_path.indexOf(section_path[i]);
if (posion_s !== '-1') {
let created_sections = sections_from_server.filter(s => s.name === section_path[posion_s - 1]);
this.parent_section_id = created_sections[0].id
}
} else if (section_path.indexOf(section_path[i]) > 0) {
let posion_s = section_path.indexOf(section_path[i]);
// console.log(section_path[i])
if (posion_s !== '-1') {
let created_sections = sections_from_server.filter(s => s.name === section_path[posion_s - 1]);
this.parent_section_id = created_sections[0].id
}
}
}
this.section_body = this.createNewSection(this._project.id, this._suite.id, section_path[i], this.parent_section_id);
this.parent_section_id = this.section_body.id;
this.section_body.created_from_script = true;
sections_from_server.push(this.section_body)
}
})
});
});
}
saveArtifact(saveTo) {
let sections = this.getAllSections();
let cases_from_suite = this.getAllCases();
let completed_data = {};
completed_data.startedTestsAt = this.run.startedTestsAt;
completed_data.endedTestsAt = this.run.endedTestsAt;
completed_data.totalTests = this.run.totalTests;
completed_data.browserName = this.run.browserName;
completed_data.browserVersion = this.run.browserVersion;
completed_data.osVersion = this.run.osVersion;
completed_data.cypressVersion = this.run.cypressVersion;
completed_data.runUrl = this.run.runUrl;
let completed_sections = [];
this.run.runs.forEach(r => {
let filtered_section = sections.filter(section => section.name === r.spec.name.split('/')[r.spec.name.split('/').length - 1]);
let s = {};
if (filtered_section.length === 0) {
let cases = [];
s.section_name = r.spec.name;
s.section_id = null;
s.isCreated = false;
s.cases_length = r.tests.length;
r.tests.forEach(test => {
let runCase = {};
runCase.title = test.title[test.title.length - 1];
runCase.state_string = test.state;
runCase.isCreated = false;
if (test.state === 'passed') {
runCase.state_id = 1;
}
if (test.state === 'skipped') {
runCase.state_id = 2;
}
if (test.state === 'failed') {
runCase.state_id = 5;
}
if (test.state === 'failed' && test.attempts[0].error !== null) {
runCase.error_message = test.attempts[0].error.message;
}
if (test.state !== 'passed' && test.attempts[0].screenshots.length !== 0) {
runCase.screenshots = test.attempts[0].screenshots;
}
cases.push(runCase);
})
s.cases = cases;
} else {
let filtered_cases = cases_from_suite.filter(c => c.section_id === filtered_section[0].id)
let cases = [];
s.section_name = filtered_section[0].name;
s.section_id = filtered_section[0].id;
s.isCreated = true;
s.cases_length = r.tests.length;
r.tests.forEach(test => {
let runCase = {};
runCase.title = test.title[test.title.length - 1];
runCase.state_string = test.state;
let found_case = filtered_cases.filter(c => c.title === test.title.map((s) => {
return s.trim()
})[test.title.length - 1]);
if (found_case.length === 0) {
runCase.isCreated = false;
} else {
runCase.isCreated = true;
}
if (test.state === 'passed') {
runCase.state_id = 1;
}
if (test.state === 'skipped') {
runCase.state_id = 2;
}
if (test.state === 'failed') {
runCase.state_id = 5;
}
if (test.state !== 'passed' && test.attempts[0].error !== null) {
runCase.error_message = test.attempts[0].error.message;
}
if (test.state !== 'passed' && test.attempts[0].screenshots.length !== 0) {
runCase.screenshots = test.attempts[0].screenshots;
}
cases.push(runCase);
})
s.cases = cases;
}
completed_sections.push(s)
});
completed_data.sections = completed_sections;
json.update(saveTo.concat('run_data_', new Date().getMilliseconds(), '.json'), completed_data);
}
sendResultsFromArtifacts(projectId, suiteId, runId, dataFromArtifact, completed_data) {
let casesReport = [];
dataFromArtifact.sections.forEach(localRun => {
this.casesFromTR = completed_data.filter(s =>
s.section_name === localRun.section_name.split('/')[localRun.section_name.split('/').length - 1])[0];
this.casesFromTR.cases.forEach(caseTR => {
let caseLocal = localRun.cases.filter(c => c.title.trim() === caseTR.title)[0];
if (caseLocal !== undefined) {
let errorString = 'Test was passed!';
if (caseLocal.state_string === 'failed') {
this.statusCase = caseLocal.state_id;
errorString = caseLocal.error_message
}
if (caseLocal.state_string === 'passed') {
this.statusCase = caseLocal.state_id;
errorString = 'Looks good!';
}
if (caseLocal.state_string === 'skipped') {
this.statusCase = caseLocal.state_id;
errorString = 'Case was skipped!';
}
let caseReport = {
case_id: caseTR.id,
status_id: this.statusCase,
comment: errorString
}
casesReport.push(caseReport);
} else {
let caseReport = {
case_id: caseTR.id,
status_id: 4,
comment: "This case may have been deleted or renamed earlier. Please see the test code."
}
casesReport.push(caseReport);
}
})
})
let n = 100;
let temp = [];
for (let i = 0; i < casesReport.length; i += n) {
let request;
try {
casesReport.slice(i, i + n).forEach(c => console.log("Send results for " + c.case_id + " to run:" + runId + " by status " + c.status_id));
request = this.tr.addResultsForCases(runId, casesReport.slice(i, i + n));
console.log('Wait 5 seconds')
sleep.sleep(5);
} catch (e) {
console.log("Status code was ".concat(request.status, " When adding results"))
casesReport.slice(i, i + n).forEach(c => console.log("Send results for " + c.case_id + " to run:" + runId + " by status " + c.status_id));
request = this.tr.addResultsForCases(runId, casesReport.slice(i, i + n));
console.log('Wait 5 seconds')
sleep.sleep(5);
}
}
}
sendResults(projectId, suiteId, runId, run) {
let sections = this.getSuiteSections(this._project.id, this._suite.id);
let cases_from_suite = this.getSuiteCases(this._project.id, this._suite.id)
let completed_data = {};
let completed_sections = [];
sections.forEach(section => {
let filtered_cases = cases_from_suite.filter(c => c.section_id === section.id)
let s = {};
if (filtered_cases.length === 0) {
s.section_name = section.name;
s.section_id = section.id;
s.cases = [];
} else {
s.section_name = section.name;
s.section_id = section.id;
s.cases = filtered_cases;
}
completed_sections.push(s);
})
completed_data.sections = completed_sections;
let casesReport = [];
run.runs.forEach(localRun => {
this.casesFromTR = completed_data.sections.filter(s => s.section_name === localRun.spec.name)[0].cases;
this.casesFromTR.forEach(caseTR => {
localRun.tests.forEach(caseLocal => {
if (caseLocal.title.map((s) => {
return s.trim()
})[caseLocal.title.length - 1] === caseTR.title) {
let errorString = 'Test was passed!'
if (caseLocal.state === 'failed') {
this.statusCase = 5
caseLocal.attempts.forEach(e => {
errorString +=
e.error.name + "\n" +
e.error.message + "\n" +
e.error.stack + "\n" +
"=======================V \n\n\n"
})
}
if (caseLocal.state === 'passed') {
this.statusCase = 1
errorString = '';
}
if (caseLocal.state === 'skipped') {
this.statusCase = 2
errorString = '';
}
let caseReport = {
case_id: caseTR.id,
status_id: this.statusCase,
comment: errorString
}
casesReport.push(caseReport);
}
}
)
})
})
casesReport.forEach(c => console.log("Send results for " + c.case_id + " to run:" + runId + " by status " + c.status_id));
return this.tr.addResultsForCases(runId, casesReport);
}
getCurrentCasesBySections(projectId, suiteId) {
let currentCases = [];
let sections = this.getSuiteSections(projectId, suiteId);
for (let section of sections) {
let object = {};
object.section_name = section.name;
object.cases = this.getSectionCases(projectId, suiteId, section.id);
currentCases.push(object);
}
return currentCases;
}
importStatusesToNewRun(closeRun, send_images) {
let runs = this.getRuns(this._project.id);
let date = new Date();
let options = {
year: 'numeric',
month: 'long',
day: 'numeric',
};
let runName = "UI Test run [based on Cypress] " + date.toLocaleDateString("en-US", options);
let timeStampRun = "Started/Ended: " + this.run.startedTestsAt + "-" + this.run.endedTestsAt;
let browserName = "Browser: " + this.run.browserName + " " + this.run.browserVersion;
let os = "OS: " + this.run.osName + " " + this.run.osVersion;
let cypressVersion = "Cypress: " + this.run.cypressVersion;
let nodeVersion = "Cypress: " + this.run.config.resolvedNodeVersion;
let screenSize = "Screen size: Width: " + this.run.config.viewportWidth + " Height: " + this.run.config.viewportHeight;
let envData = "WEB_BASE_URL: " + this.run.config.env.WEB_BASE_URL +
"\n" + " ADMIN_BASE_URL: " + this.run.config.env.ADMIN_BASE_URL +
"\n" + " LOGIN: " + this.run.config.env.LOGIN +
"\n" + " PASS: ********* ";
let runDescription = timeStampRun + "\n" +
browserName + "\n" +
os + "\n" +
cypressVersion + "\n" +
nodeVersion + "\n" +
screenSize + "\n" +
envData + "\n";
let runObj;
if (runs.filter(r => r.name === runName).length === 0) {
runObj = this.createNewRun(this._project.id, this._suite.id, runName, runDescription);
} else {
runObj = runs.filter(r => r.name === runName)[0];
this.updateRun(runObj.id, this._suite.id, runName, runDescription)
}
this.sendResults(this._project.id, this._suite.id, runObj.id, this.run)
this.uploadScreenShotsToFailedTests(send_images);
this.closeRun(runObj.id, closeRun)
}
importStatusesToNewRunFromArtifacts(closeRun, fromFolder) {
fs.readdir(fromFolder, (err, files) => {
let runs = this.getRuns(this._project.id);
let date = new Date();
let options = {
year: 'numeric',
month: 'long',
day: 'numeric',
};
this.runName = "UI Test run [based on Cypress] ".concat(date.toLocaleDateString("en-US", options));
let absolute_path_to_file = resolve(fromFolder.concat(files[0]));
console.log(absolute_path_to_file);
let info = require(absolute_path_to_file);
let timeStampRun = "Started/Ended: ".concat(info.startedTestsAt, '-', info.endedTestsAt);
this.totalTests = "Total tests: ".concat(info.totalTests);
if (this.runUrl) {
this.runUrl = "Run url: ".concat(info.runUrl);
} else {
this.runUrl = '';
}
let browserName = "Browser: ".concat(info.browserName, " ", info.browserVersion);
let cypressVersion = "Cypress: ".concat(info.cypressVersion);
this.runDescription = timeStampRun + "\n" +
this.totalTests + "\n" +
browserName + "\n" +
this.runUrl + "\n" +
cypressVersion + "\n";
let runObj;
if (runs.filter(r => r.name === this.runName).length === 0) {
runObj = this.createNewRun(this._project.id, this._suite.id, this.runName, this.runDescription);
} else {
runObj = runs.filter(r => r.name === this.runName)[0];
this.updateRun(runObj.id, this._suite.id, this.runName, this.runDescription)
}
let completed_sections = [];
let sections = this.getAllSections();
let cases_from_suite = this.getAllCases();
sections.forEach(section => {
let filtered_cases = cases_from_suite.filter(c => c.section_id === section.id);
if (filtered_cases.length > 0) {
let s = {};
s.section_name = section.name;
s.section_id = section.id;
s.cases = filtered_cases;
completed_sections.push(s);
}
})
files.forEach(file => {
let absolute_path_to_file = resolve(fromFolder.concat(file));
console.log(absolute_path_to_file);
let data_from_file = require(absolute_path_to_file);
this.sendResultsFromArtifacts(this._project.id, this._suite.id, runObj.id, data_from_file, completed_sections)
});
this.closeRun(runObj.id, closeRun)
});
}
getAllCases() {
let cases_from_suite = [];
let current_page = 0;
let current_step = 0;
do {
let c = this.getSuiteCases(this._project.id, this._suite.id, current_step);
current_page = c.length;
c.forEach(cp => {
cases_from_suite.push(cp);
})
current_step += 250
} while (250 <= current_page)
return cases_from_suite;
}
getAllSections() {
let sections_from_suite = [];
let current_page = 0;
let current_step = 0;
do {
let c = this.getSuiteSections(this._project.id, this._suite.id, current_step);
current_page = c.length;
c.forEach(cp => {
sections_from_suite.push(cp);
})
current_step += 250
} while (250 <= current_page)
return sections_from_suite;
}
uploadScreenShotsToFailedTests(send_images) {
if (send_images) {
let date = new Date();
let options = {
year: 'numeric',
month: 'long',
day: 'numeric',
};
let runName = "UI Test run [based on Cypress] " + date.toLocaleDateString("en-US", options);
let runs = this.getRuns(this._project.id);
let cases = this.getSuiteCases(this._project.id, this._suite.id);
let runObj;
if (runs.filter(r => r.name === runName).length === 1) {
runObj = runs.filter(r => r.name === runName)[0];
} else {
throw new Error("Cannot upload images to the failed tests, because the run " + runName + " does not found")
}
this.run.runs.forEach(localData => {
localData.tests.forEach(caseLocal => {
if (caseLocal.state === 'failed') {
let caseTRiD = cases.filter(caseTR => caseLocal.title.map((s) => {
return s.trim()
})[caseLocal.title.length - 1] === caseTR.title)[0];
let results = this.getResultsByCase(runObj.id, caseTRiD.id, [5])
if (caseLocal.attempts[0].screenshots.length !== 0 && results.length !== 0) {
caseLocal.attempts[0].screenshots.forEach(screenShot => {
if (fs.existsSync(screenShot.path)) {
console.log("Upload screenshot to result " + results[0].id + " by path " + screenShot.path)
this.attachToCaseResult(results[0].id, screenShot.path)
} else {
console.log("Not exist file by path " + screenShot.path)
}
})
}
}
});
})
}
}
//console command: node -r esm TestRaiImporter.js
// let tr = new TestRaiImporter(this.run);
// tr.updateSuiteCases();
// tr.importStatusesToNewRun();
//file example /plugins/tr_integration/run_all_cases.json
// import js from "../../../fixtures/add_metrics.json";
// let tr = new TestRaiImporter(js);
// tr.saveArtifact('./artifact/')
// tr.createSections('../artifact/')
// tr.addCasesFromArtifacts('../artifact/');
// tr.importStatusesToNewRunFromArtifacts(false, '../artifact/');
}
module.exports = TestRaiImporter;