feat(GODT-2793): Feed the bug report body with the answered questions.

This commit is contained in:
Romain LE JEUNE
2023-07-26 17:09:16 +02:00
committed by Romain Le Jeune
parent 2c2f816f3a
commit c4bcc38c53
9 changed files with 61 additions and 51 deletions

View File

@ -247,7 +247,7 @@ void QMLBackend::setQuestionAnswer(quint8 questionId, QString const &answer) {
/// \param[in] categoryId The id of the question set. /// \param[in] categoryId The id of the question set.
/// \return concatenate answers for set of questions. /// \return concatenate answers for set of questions.
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
QString QMLBackend::collectAnswer(quint8 categoryId) const { QString QMLBackend::collectAnswers(quint8 categoryId) const {
return reportFlow_.collectAnswers(categoryId); return reportFlow_.collectAnswers(categoryId);
} }

View File

@ -60,7 +60,7 @@ public: // member functions.
Q_INVOKABLE bool areSameFileOrFolder(QUrl const &lhs, QUrl const &rhs) const; ///< Check if two local URL point to the same file. Q_INVOKABLE bool areSameFileOrFolder(QUrl const &lhs, QUrl const &rhs) const; ///< Check if two local URL point to the same file.
Q_INVOKABLE QVariantList getQuestionSet(quint8 categoryId) const; ///< Retrieve the set of question for a given bug category. Q_INVOKABLE QVariantList getQuestionSet(quint8 categoryId) const; ///< Retrieve the set of question for a given bug category.
Q_INVOKABLE void setQuestionAnswer(quint8 questionId, QString const &answer); ///< Feed an answer for a given question. Q_INVOKABLE void setQuestionAnswer(quint8 questionId, QString const &answer); ///< Feed an answer for a given question.
Q_INVOKABLE QString collectAnswer(quint8 categoryId) const; ///< Collect answer for a given set of questions. Q_INVOKABLE QString collectAnswers(quint8 categoryId) const; ///< Collect answer for a given set of questions.
public: // Qt/QML properties. Note that the NOTIFY-er signal is required even for read-only properties (QML warning otherwise) public: // Qt/QML properties. Note that the NOTIFY-er signal is required even for read-only properties (QML warning otherwise)
Q_PROPERTY(bool showOnStartup READ showOnStartup NOTIFY showOnStartupChanged) Q_PROPERTY(bool showOnStartup READ showOnStartup NOTIFY showOnStartupChanged)

View File

@ -24,9 +24,6 @@ SettingsView {
signal questionAnswered signal questionAnswered
function setDefaultValue() {
}
function setCategoryId(catId) { function setCategoryId(catId) {
root.categoryId = catId; root.categoryId = catId;
} }
@ -38,11 +35,6 @@ SettingsView {
onCategoryIdChanged: { onCategoryIdChanged: {
root.questionSet = Backend.getQuestionSet(root.categoryId) root.questionSet = Backend.getQuestionSet(root.categoryId)
root.setDefaultValue();
}
onVisibleChanged: {
root.setDefaultValue();
} }
Label { Label {
@ -67,7 +59,12 @@ SettingsView {
tips: root.questions[modelData].tips ? root.questions[modelData].tips : "" tips: root.questions[modelData].tips ? root.questions[modelData].tips : ""
label: root.questions[modelData].label ? root.questions[modelData].label : "" label: root.questions[modelData].label ? root.questions[modelData].label : ""
type: root.questions[modelData].type type: root.questions[modelData].type
mandatory: root.questions[modelData].mandatory ? root.questions[modelData].mandatory : false
answerList: root.questions[modelData].answerList ? root.questions[modelData].answerList : [] answerList: root.questions[modelData].answerList ? root.questions[modelData].answerList : []
onAnswerChanged:{
Backend.setQuestionAnswer(modelData, answer);
}
} }
} }
// fill height so the footer label will always be attached to the bottom // fill height so the footer label will always be attached to the bottom

View File

@ -35,10 +35,11 @@ Item {
bugReportFlow.currentIndex = 0; bugReportFlow.currentIndex = 0;
} }
function showBugQuestion() { function showBugQuestion() {
bugReportFlow.currentIndex = 1;
bugQuestion.setCategoryId(root.categoryId); bugQuestion.setCategoryId(root.categoryId);
bugReportFlow.currentIndex = 1;
} }
function showBugReport() { function showBugReport() {
bugReport.setCategoryId(root.categoryId);
bugReportFlow.currentIndex = 2; bugReportFlow.currentIndex = 2;
} }

View File

@ -19,6 +19,7 @@ SettingsView {
id: root id: root
property var selectedAddress property var selectedAddress
property var categoryId:-1
signal bugReportWasSent signal bugReportWasSent
@ -26,15 +27,18 @@ SettingsView {
const reEmail = /^[^@]+@[^@]+\.[A-Za-z]+\s*$/; const reEmail = /^[^@]+@[^@]+\.[A-Za-z]+\s*$/;
return reEmail.test(text); return reEmail.test(text);
} }
function setCategoryId(catId) {
root.categoryId = catId;
}
function setDefaultValue() { function setDefaultValue() {
description.text = ""; description.text = Backend.collectAnswers(root.categoryId);
address.text = root.selectedAddress; address.text = root.selectedAddress;
emailClient.text = Backend.currentEmailClient; emailClient.text = Backend.currentEmailClient;
includeLogs.checked = true; includeLogs.checked = true;
} }
function setDescription(message) {
description.text = message;
}
function submit() { function submit() {
sendButton.loading = true; sendButton.loading = true;
Backend.reportBug(description.text, address.text, emailClient.text, includeLogs.checked); Backend.reportBug(description.text, address.text, emailClient.text, includeLogs.checked);
@ -54,39 +58,19 @@ SettingsView {
TextArea { TextArea {
id: description id: description
property int _maxLength: 800
property int _minLength: 150
KeyNavigation.priority: KeyNavigation.BeforeItem KeyNavigation.priority: KeyNavigation.BeforeItem
KeyNavigation.tab: address KeyNavigation.tab: address
Layout.fillHeight: true Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
Layout.minimumHeight: heightForLinesVisible(4) Layout.minimumHeight: heightForLinesVisible(4)
colorScheme: root.colorScheme colorScheme: root.colorScheme
hint: description.text.length + "/" + _maxLength
// set implicitHeight to explicit height because se don't // set implicitHeight to explicit height because se don't
// want TextArea implicitHeight (which is height of all text) // want TextArea implicitHeight (which is height of all text)
// to be considered in SettingsView internal scroll view // to be considered in SettingsView internal scroll view
implicitHeight: height implicitHeight: height
label: qsTr("Description") label: qsTr("Your answers")
placeholderText: qsTr("Tell us what went wrong or isn't working (min. %1 characters).").arg(_minLength) readOnly : true
validator: function (text) {
if (description.text.length < description._minLength) {
return qsTr("Enter a problem description (min. %1 characters).").arg(_minLength);
}
if (description.text.length > description._maxLength) {
return qsTr("Enter a problem description (max. %1 characters).").arg(_maxLength);
}
return;
}
onTextChanged: {
// Rise max length error immediately while typing
if (description.text.length > description._maxLength) {
validate();
}
}
} }
TextField { TextField {
id: address id: address

View File

@ -31,6 +31,7 @@ Item {
property string text: "" property string text: ""
property string tips: "" property string tips: ""
property string label: "" property string label: ""
property bool mandatory: false
property var type: QuestionItem.InputType.TextInput property var type: QuestionItem.InputType.TextInput
property var answerList: ListModel{} property var answerList: ListModel{}
@ -67,8 +68,30 @@ Item {
Layout.minimumHeight: root.type === QuestionItem.InputType.TextInput ? heightForLinesVisible(2) : 0 Layout.minimumHeight: root.type === QuestionItem.InputType.TextInput ? heightForLinesVisible(2) : 0
colorScheme: root.colorScheme colorScheme: root.colorScheme
property int _maxLength: 400
property int _minLength: 10
label: qsTr(root.label) label: qsTr(root.label)
placeholderText: qsTr(root.tips) hint: mandatory ? textInput.text.length + "/" + _maxLength : ""
placeholderText: mandatory ? qsTr("%1... (min. %2 characters)").arg(root.text).arg(_minLength) : ""
validator: function (text) {
if (!mandatory)
return;
if (textInput.text.length < textInput._minLength) {
return qsTr("min. %1 characters").arg(_minLength);
}
if (textInput.text.length > textInput._maxLength) {
return qsTr("max. %1 characters").arg(_maxLength);
}
return;
}
onTextChanged: {
// Rise max length error immediately while typing if mandatory field
if (mandatory && textInput.text.length > textInput._maxLength) {
validate();
}
}
visible: root.type === QuestionItem.InputType.TextInput visible: root.type === QuestionItem.InputType.TextInput
} }
@ -96,10 +119,10 @@ Item {
var str = ""; var str = "";
for (var i = 0; i < buttons.length; ++i) { for (var i = 0; i < buttons.length; ++i) {
if (buttons[i].checked) { if (buttons[i].checked) {
str += buttons[i].text + " "; str += buttons[i].text + ", ";
} }
} }
return str; return str.slice(0, -2);
} }
} }
Repeater { Repeater {

View File

@ -35,13 +35,15 @@
"id": 0, "id": 0,
"text": "What happened?", "text": "What happened?",
"tips": "Expected behavior", "tips": "Expected behavior",
"type": 1 "type": 1,
"mandatory": true
}, },
{ {
"id": 1, "id": 1,
"text": "What did you want or expect to happen?", "text": "What did you want or expect to happen?",
"tips": "Result", "tips": "Result",
"type": 1 "type": 1,
"mandatory": true
}, },
{ {
"id": 2, "id": 2,

View File

@ -129,10 +129,10 @@ TEST_F(BugReportFlowFixture, validFile) {
EXPECT_TRUE(flow_.setAnswer(0, "pwet")); EXPECT_TRUE(flow_.setAnswer(0, "pwet"));
EXPECT_FALSE(flow_.setAnswer(1, "pwet")); EXPECT_FALSE(flow_.setAnswer(1, "pwet"));
qDebug() << flow_.collectAnswers(0); qDebug() << flow_.collectAnswers(0);
EXPECT_EQ(flow_.collectAnswers(0), " - What happened? pwet\n\r"); EXPECT_EQ(flow_.collectAnswers(0), "Category: I can't receive mail\n\r - What happened?\n\rpwet\n\r");
EXPECT_EQ(flow_.collectAnswers(1), ""); EXPECT_EQ(flow_.collectAnswers(1), "");
flow_.clearAnswers(); flow_.clearAnswers();
EXPECT_EQ(flow_.collectAnswers(0), " - What happened? \n\r"); EXPECT_EQ(flow_.collectAnswers(0), "Category: I can't receive mail\n\r");
} }

View File

@ -97,14 +97,17 @@ bool BugReportFlow::setAnswer(quint8 questionId, QString const &answer) {
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
QString BugReportFlow::collectAnswers(quint8 categoryId) const { QString BugReportFlow::collectAnswers(quint8 categoryId) const {
QString answers; QString answers;
if (categoryId > categories_.count() - 1)
return answers;
answers += "Category: " + categories_[categoryId] + "\n\r";
QVariantList sets = this->questionSet(categoryId); QVariantList sets = this->questionSet(categoryId);
foreach(const QVariant& var, sets) { for (QVariant const &var: sets) {
answers += " - "; const QString& answer = answers_[var.toInt()];
answers += questions_[var.toInt()].toMap()["text"].toString(); if (answer.isEmpty())
answers += " "; continue;
answers += answers_[var.toInt()]; answers += " - " + questions_[var.toInt()].toMap()["text"].toString() + "\n\r";
answers += "\n\r"; answers += answer + "\n\r";
} }
return answers; return answers;
} }
@ -129,7 +132,7 @@ bool BugReportFlow::parseFile() {
QJsonObject data = getJsonDataObj(getJsonRootObj()); QJsonObject data = getJsonDataObj(getJsonRootObj());
QJsonArray categoriesJson = data.value("categories").toArray(); QJsonArray categoriesJson = data.value("categories").toArray();
foreach (const QJsonValue & v, categoriesJson) { for (const QJsonValueRef &v : categoriesJson) {
categories_.append(v.toObject()["name"].toString()); categories_.append(v.toObject()["name"].toString());
questionsSet_.append(v.toObject()["questions"].toArray().toVariantList()); questionsSet_.append(v.toObject()["questions"].toArray().toVariantList());
} }