Skip to content
14 changes: 9 additions & 5 deletions qa/test-cases/ll1.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,17 +104,21 @@ Notes:

---

### LL1-TC-07 - State C table correct path + PDF export (Yes/No)
### LL1-TC-07 - State C table correct path + PDF export
Preconditions:
- Reached State C (table input dialog opens).

Steps:
| Step | Action | Expected Result |
|------|--------|-----------------|
| 1 | Fill the LL(1) table correctly and click `Finalizar`. | Table is accepted; tutor reaches final state; export prompt appears. |
| 2 | Select `Yes` to export, choose a path, and confirm. | PDF is created at the selected path and is non-empty. |
| 3 | Start a new LL(1) session, reach final state again. | Export prompt appears again. |
| 4 | Select `No` to export. | Tutor closes without creating a new PDF. |
| 1 | Fill the LL(1) table correctly and click `Finalizar`. | Table is accepted; tutor reaches final state; final action buttons appear. |
| 2 | Click `Exportar PDF`, choose a path, and confirm. | PDF is created at the selected path and is non-empty. |
| 3 | Start a new LL(1) session, reach final state again. | Final action buttons appear again. |
| 4 | Click `Salir`. | Tutor closes without creating a new PDF. |

Notes:
- The current UI does not use a `Yes/No` export confirmation dialog.
- The functional equivalents are `Exportar PDF` (export) and `Salir` (finish without exporting).

---

Expand Down
2 changes: 1 addition & 1 deletion qa/test-cases/main.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ Preconditions:
Steps:
| Step | Action | Expected Result |
|------|--------|-----------------|
| 1 | Select `Yes` to export. | File picker opens. |
| 1 | Click `Exportar PDF`. | File picker opens. |
| 2 | Choose a path and confirm. | PDF is created at the selected path. |
| 3 | Open the PDF. | PDF is non-empty and contains the expected conversation/summary. |

Expand Down
21 changes: 10 additions & 11 deletions qa/test-cases/slr1.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,32 +266,31 @@ Steps:

---

### SLR1-TC-18 - State H table incorrect -> H' wizard, cancel flows
### SLR1-TC-18 - State H incorrect table + guided mode
Preconditions:
- SLR(1) tutor at State H (table dialog opens).

Steps:
| Step | Action | Expected Result |
|------|--------|-----------------|
| 1 | Fill the table incorrectly and click `Finalizar`. | Wrong counter increments; State H' wizard opens. |
| 2 | In the wizard, click `Cancel`. | Confirmation dialog appears. |
| 3 | Select `No`. | Wizard closes; tutor returns to State H and the table dialog opens again. |
| 4 | In the wizard again, click `Cancel` and select `Yes`. | Tutor window closes. |
| 1 | Fill the table incorrectly and click `Finalizar`. | Incorrect cells are highlighted in red; an information message is shown; the table dialog stays open in State H. |
| 2 | Click `Modo guiado`. | The guided wizard opens. |
| 3 | Complete the wizard and click `Finish`. | The wizard closes and the table dialog remains available in State H. |

---

### SLR1-TC-19 - State H correct path + PDF export (Yes/No)
### SLR1-TC-19 - State H correct path + PDF export
Preconditions:
- Start a new SLR(1) session and reach State H.

Steps:
| Step | Action | Expected Result |
|------|--------|-----------------|
| 1 | Fill the table incorrectly and click `Finalizar`. | Wrong counter increments; State H' wizard opens. |
| 2 | Complete the wizard and click `Finish`. | Tutor returns to State H and the table dialog opens again. |
| 3 | Fill the table correctly and click `Finalizar`. | Tutor reaches final state; export prompt appears. |
| 4 | Select `Yes`, choose a path, and confirm. | PDF is created at the selected path and is non-empty. |
| 5 | Start a new SLR(1) session, reach final state again, and select `No`. | Tutor closes without creating a new PDF. |
| 1 | Optionally click `Modo guiado` and complete the wizard. | The wizard closes and the table dialog remains available in State H. |
| 2 | Fill the table correctly and click `Finalizar`. | Tutor reaches final state; final action buttons appear. |
| 3 | Click `Exportar PDF`, choose a path, and confirm. | PDF is created at the selected path and is non-empty. |
| 4 | Start a new SLR(1) session, reach final state again, and click `Salir`. | Tutor closes without creating a new PDF. |


---

Expand Down
3 changes: 3 additions & 0 deletions src/gui/lltabledialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ LLTableDialog::LLTableDialog(const QStringList& rowHeaders,
QVector<QVector<QString>>* initialData)
: QDialog(parent) {
setProperty("tableDialog", true);
setObjectName("llTableDialog");
table = new QTableWidget(rowHeaders.size(), colHeaders.size(), this);
table->setObjectName("llTableWidget");
table->setItemDelegate(new CenterAlignDelegate(table));
table->setAlternatingRowColors(true);
table->setHorizontalHeaderLabels(colHeaders);
Expand All @@ -60,6 +62,7 @@ LLTableDialog::LLTableDialog(const QStringList& rowHeaders,
table->horizontalHeader()->setStretchLastSection(true);

submitButton = new QPushButton(tr("Finalizar"), this);
submitButton->setObjectName("llTableSubmitButton");
QFont submitButtonFont = submitButton->font();
submitButtonFont.setBold(true);
submitButton->setFont(submitButtonFont);
Expand Down
103 changes: 100 additions & 3 deletions src/gui/lltutorwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,32 @@ bool LLTutorWindow::confirmExitToHome() {
return msg.exec() == QMessageBox::Yes;
}

QString LLTutorWindow::promptExportFilePath() const {
#ifdef SYNTAXTUTOR_TESTING
if (!nextExportFilePathForTest.isEmpty()) {
const QString filePath = nextExportFilePathForTest;
nextExportFilePathForTest.clear();
return filePath;
}
#endif

QFileDialog dialog(const_cast<LLTutorWindow*>(this),
tr("Guardar conversación"), "conver.pdf",
tr("Archivo PDF (*.pdf)"));
dialog.setAcceptMode(QFileDialog::AcceptSave);
dialog.setFileMode(QFileDialog::AnyFile);
dialog.selectFile("conver.pdf");
dialog.setObjectName("llTutorExportFileDialog");
#ifdef SYNTAXTUTOR_TESTING
dialog.setOption(QFileDialog::DontUseNativeDialog, true);
#endif
if (dialog.exec() != QDialog::Accepted) {
return {};
}

return dialog.selectedFiles().value(0);
}

void LLTutorWindow::on_backButton_clicked() {
if (confirmExitToHome()) {
requestExit(false);
Expand Down Expand Up @@ -910,20 +936,20 @@ void LLTutorWindow::on_confirmButton_clicked() {
layout->setSpacing(10);

auto* exportBtn = new QPushButton(tr("Exportar PDF"), actions);
exportBtn->setObjectName("llTutorExportPdfButton");
exportBtn->setCursor(Qt::PointingHandCursor);
exportBtn->setProperty("role", "primary");

auto* exitBtn = new QPushButton(tr("Salir"), actions);
exitBtn->setObjectName("llTutorExitButton");
exitBtn->setCursor(Qt::PointingHandCursor);
exitBtn->setProperty("role", "danger");

layout->addWidget(exportBtn);
layout->addWidget(exitBtn);

connect(exportBtn, &QPushButton::clicked, this, [this]() {
const QString filePath = QFileDialog::getSaveFileName(
this, tr("Guardar conversación"), "conver.pdf",
tr("Archivo PDF (*.pdf)"));
const QString filePath = promptExportFilePath();
if (!filePath.isEmpty()) {
exportConversationToPdf(filePath);
}
Expand Down Expand Up @@ -1159,6 +1185,77 @@ void LLTutorWindow::updatePlaceholder() {
ui->userResponse->setPlaceholderText(text);
}

#ifdef SYNTAXTUTOR_TESTING
QString LLTutorWindow::currentStateForTest() const {
switch (currentState) {
case State::A:
return "A";
case State::A1:
return "A1";
case State::A2:
return "A2";
case State::A_prime:
return "A'";
case State::B:
return "B";
case State::B1:
return "B1";
case State::B2:
return "B2";
case State::B_prime:
return "B'";
case State::C:
return "C";
case State::C_prime:
return "C'";
case State::fin:
return "fin";
}

return {};
}

QString LLTutorWindow::currentRuleAntecedentForTest() const {
if (static_cast<qsizetype>(currentRule) >= sortedGrammar.size()) {
return {};
}

return sortedGrammar.at(currentRule).first;
}

QStringList LLTutorWindow::currentRuleConsequentForTest() const {
if (static_cast<qsizetype>(currentRule) >= sortedGrammar.size()) {
return {};
}

QStringList consequent;
for (const QString& symbol : sortedGrammar.at(currentRule).second) {
consequent.append(symbol);
}
return consequent;
}

int LLTutorWindow::rightCountForTest() const {
return static_cast<int>(cntRightAnswers);
}

int LLTutorWindow::wrongCountForTest() const {
return static_cast<int>(cntWrongAnswers);
}

void LLTutorWindow::setAnswerForTest(const QString& text) {
ui->userResponse->setPlainText(text);
}

void LLTutorWindow::submitForTest() {
on_confirmButton_clicked();
}

void LLTutorWindow::setNextExportFilePathForTest(const QString& filePath) {
nextExportFilePathForTest = filePath;
}
#endif

/************************************************************
* VERIFY USER RESPONSE *
* Dispatches validation to the appropriate method based on
Expand Down
16 changes: 16 additions & 0 deletions src/gui/lltutorwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,18 @@ class LLTutorWindow : public QWidget {
const QStringList& colHeaders);
void updatePlaceholder();
bool confirmExitToHome();
QString promptExportFilePath() const;
#ifdef SYNTAXTUTOR_TESTING
public:
QString currentStateForTest() const;
QString currentRuleAntecedentForTest() const;
QStringList currentRuleConsequentForTest() const;
int rightCountForTest() const;
int wrongCountForTest() const;
void setAnswerForTest(const QString& text);
void submitForTest();
void setNextExportFilePathForTest(const QString& filePath);
#endif
private slots:
void on_backButton_clicked();
void on_confirmButton_clicked();
Expand Down Expand Up @@ -289,6 +301,10 @@ class LLTutorWindow : public QWidget {

TutorialManager* tm = nullptr;

#ifdef SYNTAXTUTOR_TESTING
mutable QString nextExportFilePathForTest;
#endif

const QRegularExpression kRe{"^\\s+|\\s+$"};
const QRegularExpression kWhitespace{"\\s+"};
};
Expand Down
18 changes: 15 additions & 3 deletions src/gui/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@

namespace {

#ifdef SYNTAXTUTOR_TESTING
constexpr auto kSettingsOrg = "UMA-Test";
constexpr auto kSettingsApp = "SyntaxTutor-Test";
#else
constexpr auto kSettingsOrg = "UMA";
constexpr auto kSettingsApp = "SyntaxTutor";
#endif

void showInfoDialog(QWidget* parent, const QString& windowTitle,
const QString& eyebrow, const QString& title,
const QString& html) {
Expand Down Expand Up @@ -81,7 +89,7 @@ void showInfoDialog(QWidget* parent, const QString& windowTitle,

MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent), ui(new Ui::MainWindow),
settings("UMA", "SyntaxTutor") {
settings(kSettingsOrg, kSettingsApp) {
factory.Init();
ui->setupUi(this);
ui->homeEyebrow->setText(tr("Tutores interactivos"));
Expand Down Expand Up @@ -688,14 +696,17 @@ void MainWindow::on_idiom_clicked() {
buttonsLayout->setSpacing(10);

auto* btnEs = new QPushButton(tr("Español"), &dialog);
btnEs->setObjectName("languageSpanishButton");
btnEs->setCursor(Qt::PointingHandCursor);
btnEs->setProperty("role", "primary");

auto* btnEn = new QPushButton(tr("Inglés"), &dialog);
btnEn->setObjectName("languageEnglishButton");
btnEn->setCursor(Qt::PointingHandCursor);
btnEn->setProperty("role", "primary");

auto* btnCanc = new QPushButton(tr("Cancelar"), &dialog);
btnCanc->setObjectName("languageCancelButton");
btnCanc->setCursor(Qt::PointingHandCursor);
btnCanc->setProperty("role", "danger");

Expand Down Expand Up @@ -724,8 +735,7 @@ void MainWindow::on_idiom_clicked() {
return;
}

QSettings settings("UMA", "SyntaxTutor");
QString currentLang = settings.value("lang/language", "es").toString();
QString currentLang = settings.value("lang/language", "es").toString();

if (selectedLang != currentLang) {
settings.setValue("lang/language", selectedLang);
Expand All @@ -737,7 +747,9 @@ void MainWindow::on_idiom_clicked() {
info.setStandardButtons(QMessageBox::Ok);
info.exec();

#ifndef SYNTAXTUTOR_TESTING
qApp->quit();
QProcess::startDetached(qApp->applicationFilePath(), QStringList());
#endif
}
}
4 changes: 4 additions & 0 deletions src/gui/slrtabledialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ SLRTableDialog::SLRTableDialog(int rowCount, int colCount,
QVector<QVector<QString>>* initialData)
: QDialog(parent) {
setProperty("tableDialog", true);
setObjectName("slrTableDialog");
table = new QTableWidget(rowCount, colCount, this);
table->setObjectName("slrTableWidget");
table->horizontalHeader()->setFont(table->font());
table->verticalHeader()->setFont(table->font());
table->setHorizontalHeaderLabels(colHeaders);
Expand All @@ -65,11 +67,13 @@ SLRTableDialog::SLRTableDialog(int rowCount, int colCount,
table->horizontalHeader()->setStretchLastSection(true);

submitButton = new QPushButton(tr("Finalizar"), this);
submitButton->setObjectName("slrTableSubmitButton");
submitButton->setFont(submitButton->font());
submitButton->setCursor(Qt::PointingHandCursor);
submitButton->setProperty("role", "primary");

guidedButton = new QPushButton(tr("Modo guiado"), this);
guidedButton->setObjectName("slrTableGuidedButton");
guidedButton->setFont(guidedButton->font());
guidedButton->setCursor(Qt::PointingHandCursor);
guidedButton->setProperty("role", "primary");
Expand Down
Loading
Loading