mirror of
https://github.com/AleziaKurdis/overte.git
synced 2025-04-14 09:06:51 +02:00
Added milestones to TestRail cases.
This commit is contained in:
parent
ad0a1c289d
commit
3402d7836a
5 changed files with 261 additions and 65 deletions
|
@ -16,7 +16,6 @@
|
|||
#include <QDateTime>
|
||||
#include <QFile>
|
||||
#include <QMessageBox>
|
||||
#include <QProcess>
|
||||
#include <QTextStream>
|
||||
|
||||
TestRailInterface::TestRailInterface() {
|
||||
|
@ -39,8 +38,8 @@ QString TestRailInterface::getObject(const QString& path) {
|
|||
|
||||
// Creates the testrail.py script
|
||||
// This is the file linked to from http://docs.gurock.com/testrail-api2/bindings-python
|
||||
void TestRailInterface::createTestRailDotPyScript(const QString& outputDirectory) {
|
||||
QFile file(outputDirectory + "/testrail.py");
|
||||
void TestRailInterface::createTestRailDotPyScript() {
|
||||
QFile file(_outputDirectory + "/testrail.py");
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
|
||||
"Could not create 'testrail.py'");
|
||||
|
@ -145,8 +144,13 @@ void TestRailInterface::createTestRailDotPyScript(const QString& outputDirectory
|
|||
}
|
||||
|
||||
// Creates a Stack class
|
||||
void TestRailInterface::createStackDotPyScript(const QString& outputDirectory) {
|
||||
QFile file(outputDirectory + "/stack.py");
|
||||
void TestRailInterface::createStackDotPyScript() {
|
||||
QString filename = _outputDirectory + "/stack.py";
|
||||
if (QFile::exists(filename)) {
|
||||
QFile::remove(filename);
|
||||
}
|
||||
QFile file(filename);
|
||||
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
|
||||
"Could not create 'stack.py'");
|
||||
|
@ -184,7 +188,7 @@ void TestRailInterface::createStackDotPyScript(const QString& outputDirectory) {
|
|||
file.close();
|
||||
}
|
||||
|
||||
void TestRailInterface::requestDataFromUser() {
|
||||
void TestRailInterface::requestTestRailDataFromUser() {
|
||||
_testRailSelectorWindow.exec();
|
||||
|
||||
if (_testRailSelectorWindow.getUserCancelled()) {
|
||||
|
@ -249,10 +253,15 @@ void TestRailInterface::processDirectoryPython(const QString& directory,
|
|||
// Therefore, the tree is built top-down, using a stack to store the IDs of each node
|
||||
//
|
||||
void TestRailInterface::createAddTestCasesPythonScript(const QString& testDirectory,
|
||||
const QString& outputDirectory,
|
||||
const QString& userGitHub,
|
||||
const QString& branchGitHub) {
|
||||
QFile file(outputDirectory + "/addTestCases.py");
|
||||
const QString& userGitHub,
|
||||
const QString& branchGitHub) {
|
||||
|
||||
QString filename = _outputDirectory + "/addTestCases.py";
|
||||
if (QFile::exists(filename)) {
|
||||
QFile::remove(filename);
|
||||
}
|
||||
QFile file(filename);
|
||||
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
|
||||
"Could not create 'addTestCases.py'");
|
||||
|
@ -283,48 +292,141 @@ void TestRailInterface::createAddTestCasesPythonScript(const QString& testDirect
|
|||
file.close();
|
||||
|
||||
if (QMessageBox::Yes == QMessageBox(QMessageBox::Information, "Python script has been created", "Do you want to run the script and update TestRail?", QMessageBox::Yes | QMessageBox::No).exec()) {
|
||||
QString command(_pythonPath + "/" + pythonExe);
|
||||
QStringList parameters = QStringList() << outputDirectory + "/addTestCases.py";
|
||||
QProcess* process = new QProcess();
|
||||
connect(
|
||||
process, &QProcess::started,
|
||||
this, [=]() {
|
||||
connect(process, &QProcess::started, this,
|
||||
[=]() {
|
||||
_busyWindow.exec();
|
||||
}
|
||||
);
|
||||
|
||||
connect(
|
||||
process, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
|
||||
this, [=](int exitCode, QProcess::ExitStatus exitStatus) {
|
||||
connect(process, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this,
|
||||
[=](int exitCode, QProcess::ExitStatus exitStatus) {
|
||||
_busyWindow.hide();
|
||||
}
|
||||
);
|
||||
|
||||
process->start(command, parameters);
|
||||
QStringList parameters = QStringList() << _outputDirectory + "/addTestCases.py";
|
||||
process->start(_pythonCommand, parameters);
|
||||
}
|
||||
}
|
||||
|
||||
void TestRailInterface::updateMilestonesComboData(int exitCode, QProcess::ExitStatus exitStatus) {
|
||||
// Check if process completed successfully
|
||||
if (exitStatus != QProcess::NormalExit) {
|
||||
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
|
||||
"Could not get milestones from TestRail");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Create map of milestones from the file created by the process
|
||||
_milestoneNames.clear();
|
||||
|
||||
QString filename = _outputDirectory + "/milestones.txt";
|
||||
if (!QFile::exists(filename)) {
|
||||
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
|
||||
"Could not find milestones.txt in " + _outputDirectory);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
QFile file(filename);
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
|
||||
"Could not open " + _outputDirectory + "/milestones.txt");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
QTextStream in(&file);
|
||||
QString line = in.readLine();
|
||||
while (!line.isNull()) {
|
||||
QStringList words = line.split(' ');
|
||||
_milestones[words[0]] = words[1].toInt();
|
||||
_milestoneNames << words[0];
|
||||
|
||||
line = in.readLine();
|
||||
}
|
||||
|
||||
file.close();
|
||||
|
||||
// Update the combo
|
||||
_testRailSelectorWindow.updateMilestoneComboBoxData(_milestoneNames);
|
||||
|
||||
_testRailSelectorWindow.exec();
|
||||
|
||||
if (_testRailSelectorWindow.getUserCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
createAddTestCasesPythonScript(_testDirectory, _userGitHub, _branchGitHub);
|
||||
}
|
||||
|
||||
void TestRailInterface::getMilestonesFromTestRail() {
|
||||
QString filename = _outputDirectory + "/getMilestones.py";
|
||||
if (QFile::exists(filename)) {
|
||||
QFile::remove(filename);
|
||||
}
|
||||
QFile file(filename);
|
||||
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
|
||||
"Could not create 'getMilestones.py'");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
QTextStream stream(&file);
|
||||
|
||||
// Code to access TestRail
|
||||
stream << "from testrail import *\n";
|
||||
stream << "client = APIClient('" << _url.toStdString().c_str() << "')\n";
|
||||
stream << "client.user = '" << _user << "'\n";
|
||||
stream << "client.password = '" << _password << "'\n\n";
|
||||
|
||||
// Print the list of uncompleted milestones
|
||||
stream << "file = open('" + _outputDirectory + "/milestones.txt', 'w')\n\n";
|
||||
stream << "milestones = client.send_get('get_milestones/" + _project + "')\n";
|
||||
stream << "for milestone in milestones:\n";
|
||||
stream << "\tif milestone['is_completed'] == False:\n";
|
||||
stream << "\t\tfile.write(milestone['name'] + ' ' + str(milestone['id']) + '\\n')\n\n";
|
||||
stream << "file.close()\n";
|
||||
|
||||
file.close();
|
||||
|
||||
QProcess* process = new QProcess();
|
||||
connect(process, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this,
|
||||
[=](int exitCode, QProcess::ExitStatus exitStatus) {
|
||||
updateMilestonesComboData(exitCode, exitStatus);
|
||||
}
|
||||
);
|
||||
|
||||
QStringList parameters = QStringList() << _outputDirectory + "/getMilestones.py ";
|
||||
process->start(_pythonCommand, parameters);
|
||||
}
|
||||
|
||||
void TestRailInterface::createTestSuitePython(const QString& testDirectory,
|
||||
const QString& outputDirectory,
|
||||
const QString& userGitHub,
|
||||
const QString& branchGitHub) {
|
||||
|
||||
|
||||
_testDirectory = testDirectory;
|
||||
_outputDirectory = outputDirectory;
|
||||
_userGitHub = userGitHub;
|
||||
_branchGitHub = branchGitHub;
|
||||
|
||||
// First check that Python is available
|
||||
QProcessEnvironment e = QProcessEnvironment::systemEnvironment();
|
||||
QStringList sl = e.toStringList();
|
||||
if (QProcessEnvironment::systemEnvironment().contains("PYTHON_PATH")) {
|
||||
_pythonPath = QProcessEnvironment::systemEnvironment().value("PYTHON_PATH");
|
||||
QString _pythonPath = QProcessEnvironment::systemEnvironment().value("PYTHON_PATH");
|
||||
if (!QFile::exists(_pythonPath + "/" + pythonExe)) {
|
||||
QMessageBox::critical(0, pythonExe, QString("Python executable not found in ") + _pythonPath);
|
||||
}
|
||||
_pythonCommand = _pythonPath + "/" + pythonExe;
|
||||
} else {
|
||||
QMessageBox::critical(0, "PYTHON_PATH not defined", "Please set PYTHON_PATH to directory containing the Python executable");
|
||||
return;
|
||||
}
|
||||
|
||||
createTestRailDotPyScript(outputDirectory);
|
||||
createStackDotPyScript(outputDirectory);
|
||||
requestDataFromUser();
|
||||
createAddTestCasesPythonScript(testDirectory, outputDirectory, userGitHub, branchGitHub);
|
||||
requestTestRailDataFromUser();
|
||||
getMilestonesFromTestRail();
|
||||
createTestRailDotPyScript();
|
||||
createStackDotPyScript();
|
||||
}
|
||||
|
||||
void TestRailInterface::createTestSuiteXML(const QString& testDirectory,
|
||||
|
@ -332,6 +434,8 @@ void TestRailInterface::createTestSuitePython(const QString& testDirectory,
|
|||
const QString& userGitHub,
|
||||
const QString& branchGitHub) {
|
||||
|
||||
_outputDirectory = outputDirectory;
|
||||
|
||||
QDomProcessingInstruction instruction = _document.createProcessingInstruction("xml", "version='1.0' encoding='UTF-8'");
|
||||
_document.appendChild(instruction);
|
||||
|
||||
|
@ -353,7 +457,7 @@ void TestRailInterface::createTestSuitePython(const QString& testDirectory,
|
|||
root.appendChild(topLevelSection);
|
||||
|
||||
// Write to file
|
||||
const QString testRailsFilename{ outputDirectory + "/TestRailSuite.xml" };
|
||||
const QString testRailsFilename{ _outputDirectory + "/TestRailSuite.xml" };
|
||||
QFile file(testRailsFilename);
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Could not create XML file");
|
||||
|
@ -559,9 +663,12 @@ void TestRailInterface::processTestPython(const QString& fullDirectory,
|
|||
QString testContent = QString("Execute instructions in [THIS TEST](") + testMDName + ")";
|
||||
QString testExpected = QString("Refer to the expected result in the linked description.");
|
||||
|
||||
int milestone_id = _milestones[_milestoneNames[_testRailSelectorWindow.getMilestoneID()]];
|
||||
|
||||
stream << "data = {\n\t"
|
||||
<< "'title': '" << title << "',\n\t"
|
||||
<< "'template_id': 2,\n\t"
|
||||
<< "'milestone_id': " << milestone_id << ",\n\t"
|
||||
<< "'custom_preconds': " << "'Tester is in an empty region of a domain in which they have edit rights\\n\\n*Note: Press \\'n\\' to advance test script',\n\t"
|
||||
<< "'custom_steps_separated': " << "[\n\t\t{\n\t\t\t'content': '" << testContent << "',\n\t\t\t'expected': '" << testExpected << "'\n\t\t}\n\t]\n"
|
||||
<< "}\n";
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "ui/TestRailSelectorWindow.h"
|
||||
#include <QDirIterator>
|
||||
#include <QtXml/QDomDocument>
|
||||
#include <QProcess>
|
||||
#include <QString>
|
||||
|
||||
class TestRailInterface : public QObject{
|
||||
|
@ -49,11 +50,12 @@ public:
|
|||
const QString& userGitHub,
|
||||
const QString& branchGitHub);
|
||||
|
||||
void createTestRailDotPyScript(const QString& outputDirectory);
|
||||
void createStackDotPyScript(const QString& outputDirectory);
|
||||
void requestDataFromUser();
|
||||
void getMilestonesFromTestRail();
|
||||
void createTestRailDotPyScript();
|
||||
void createStackDotPyScript();
|
||||
void requestTestRailDataFromUser();
|
||||
void requestMilestoneFromUser();
|
||||
void createAddTestCasesPythonScript(const QString& testDirectory,
|
||||
const QString& outputDirectory,
|
||||
const QString& userGitHub,
|
||||
const QString& branchGitHub);
|
||||
|
||||
|
@ -65,6 +67,8 @@ public:
|
|||
bool isAValidTestDirectory(const QString& directory);
|
||||
QString getObject(const QString& path);
|
||||
|
||||
void updateMilestonesComboData(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
|
||||
private:
|
||||
QDomDocument _document;
|
||||
|
||||
|
@ -76,9 +80,15 @@ private:
|
|||
QString _password;
|
||||
QString _project;
|
||||
|
||||
QString _pythonPath;
|
||||
QString _testDirectory;
|
||||
QString _outputDirectory;
|
||||
QString _userGitHub;
|
||||
QString _branchGitHub;
|
||||
|
||||
const QString pythonExe{ "python.exe" };
|
||||
QString _pythonCommand;
|
||||
std::map<QString, int> _milestones;
|
||||
QStringList _milestoneNames;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -19,6 +19,17 @@ TestRailSelectorWindow::TestRailSelectorWindow(QWidget *parent) {
|
|||
projectLineEdit->setValidator(new QIntValidator(1, 999, this));
|
||||
}
|
||||
|
||||
void TestRailSelectorWindow::on_acceptButton_clicked() {
|
||||
urlLineEdit->setDisabled(true);
|
||||
userLineEdit->setDisabled(true);
|
||||
passwordLineEdit->setDisabled(true);
|
||||
projectLineEdit->setDisabled(true);
|
||||
|
||||
OKButton->setDisabled(false);
|
||||
milestoneComboBox->setDisabled(false);
|
||||
close();
|
||||
}
|
||||
|
||||
void TestRailSelectorWindow::on_OKButton_clicked() {
|
||||
userCancelled = false;
|
||||
close();
|
||||
|
@ -34,19 +45,19 @@ bool TestRailSelectorWindow::getUserCancelled() {
|
|||
}
|
||||
|
||||
void TestRailSelectorWindow::setURL(const QString& user) {
|
||||
URLTextEdit->setText(user);
|
||||
urlLineEdit->setText(user);
|
||||
}
|
||||
|
||||
QString TestRailSelectorWindow::getURL() {
|
||||
return URLTextEdit->toPlainText();
|
||||
return urlLineEdit->text();
|
||||
}
|
||||
|
||||
void TestRailSelectorWindow::setUser(const QString& user) {
|
||||
userTextEdit->setText(user);
|
||||
userLineEdit->setText(user);
|
||||
}
|
||||
|
||||
QString TestRailSelectorWindow::getUser() {
|
||||
return userTextEdit->toPlainText();
|
||||
return userLineEdit->text();
|
||||
}
|
||||
|
||||
QString TestRailSelectorWindow::getPassword() {
|
||||
|
@ -60,3 +71,11 @@ void TestRailSelectorWindow::setProject(const int project) {
|
|||
int TestRailSelectorWindow::getProject() {
|
||||
return projectLineEdit->text().toInt();
|
||||
}
|
||||
|
||||
void TestRailSelectorWindow::updateMilestoneComboBoxData(QStringList data) {
|
||||
milestoneComboBox->insertItems(0, data);
|
||||
}
|
||||
|
||||
int TestRailSelectorWindow::getMilestoneID() {
|
||||
return milestoneComboBox->currentIndex();
|
||||
}
|
|
@ -33,7 +33,11 @@ public:
|
|||
|
||||
bool userCancelled{ false };
|
||||
|
||||
void updateMilestoneComboBoxData(QStringList data);
|
||||
int getMilestoneID();
|
||||
|
||||
private slots:
|
||||
void on_acceptButton_clicked();
|
||||
void on_OKButton_clicked();
|
||||
void on_cancelButton_clicked();
|
||||
};
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>489</width>
|
||||
<height>312</height>
|
||||
<height>415</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
|
@ -35,7 +35,7 @@
|
|||
<property name="geometry">
|
||||
<rect>
|
||||
<x>70</x>
|
||||
<y>115</y>
|
||||
<y>125</y>
|
||||
<width>121</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
|
@ -67,21 +67,14 @@
|
|||
<string>TestRail URL</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QTextEdit" name="URLTextEdit">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>200</x>
|
||||
<y>25</y>
|
||||
<width>231</width>
|
||||
<height>24</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="OKButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>120</x>
|
||||
<y>240</y>
|
||||
<y>350</y>
|
||||
<width>93</width>
|
||||
<height>28</height>
|
||||
</rect>
|
||||
|
@ -94,7 +87,7 @@
|
|||
<property name="geometry">
|
||||
<rect>
|
||||
<x>280</x>
|
||||
<y>240</y>
|
||||
<y>350</y>
|
||||
<width>93</width>
|
||||
<height>28</height>
|
||||
</rect>
|
||||
|
@ -107,7 +100,7 @@
|
|||
<property name="geometry">
|
||||
<rect>
|
||||
<x>200</x>
|
||||
<y>115</y>
|
||||
<y>120</y>
|
||||
<width>231</width>
|
||||
<height>24</height>
|
||||
</rect>
|
||||
|
@ -116,21 +109,11 @@
|
|||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QTextEdit" name="userTextEdit">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>200</x>
|
||||
<y>70</y>
|
||||
<width>231</width>
|
||||
<height>24</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>70</x>
|
||||
<y>70</y>
|
||||
<y>75</y>
|
||||
<width>121</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
|
@ -148,7 +131,7 @@
|
|||
<property name="geometry">
|
||||
<rect>
|
||||
<x>200</x>
|
||||
<y>160</y>
|
||||
<y>170</y>
|
||||
<width>231</width>
|
||||
<height>24</height>
|
||||
</rect>
|
||||
|
@ -161,7 +144,7 @@
|
|||
<property name="geometry">
|
||||
<rect>
|
||||
<x>70</x>
|
||||
<y>160</y>
|
||||
<y>175</y>
|
||||
<width>121</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
|
@ -175,6 +158,79 @@
|
|||
<string>TestRail Project</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="acceptButton">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>200</x>
|
||||
<y>220</y>
|
||||
<width>231</width>
|
||||
<height>28</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Accept</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QComboBox" name="milestoneComboBox">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>270</x>
|
||||
<y>280</y>
|
||||
<width>161</width>
|
||||
<height>22</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="milestoneLabel">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>140</x>
|
||||
<y>280</y>
|
||||
<width>121</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>TestRail Milestone</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="urlLineEdit">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>200</x>
|
||||
<y>20</y>
|
||||
<width>231</width>
|
||||
<height>24</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Normal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="userLineEdit">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>200</x>
|
||||
<y>70</y>
|
||||
<width>231</width>
|
||||
<height>24</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Normal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources/>
|
||||
|
|
Loading…
Reference in a new issue