From 2738a72b4337ef92f400d1052f893b6bb4bacd9e Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Sat, 19 Oct 2024 00:09:32 -0400 Subject: [PATCH] Fix assert hit when viewing entry history * Fixes #11371 * Adds test for showing entry history * Improved page switching capabilities for entry edit widget --- src/gui/DatabaseWidget.cpp | 3 +- src/gui/EditWidget.cpp | 15 ++++++++-- src/gui/EditWidget.h | 3 +- src/gui/entry/EditEntryWidget.cpp | 47 +++++++++++++++++++++++++++-- src/gui/entry/EditEntryWidget.h | 15 ++++++++++ tests/gui/TestGui.cpp | 50 +++++++++++++++++++++---------- 6 files changed, 110 insertions(+), 23 deletions(-) diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index fba27d5a8..cbe27ee4e 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -183,6 +183,7 @@ DatabaseWidget::DatabaseWidget(QSharedPointer db, QWidget* parent) m_previewSplitter->setSizes({1, 1}); m_editEntryWidget->setObjectName("editEntryWidget"); + m_historyEditEntryWidget->setObjectName("editEntryHistoryWidget"); m_editGroupWidget->setObjectName("editGroupWidget"); m_reportsDialog->setObjectName("reportsDialog"); m_databaseSettingDialog->setObjectName("databaseSettingsDialog"); @@ -277,7 +278,7 @@ DatabaseWidget::Mode DatabaseWidget::currentMode() const mode = Mode::ReportsMode; } else if (widget == m_databaseSettingDialog) { mode = Mode::DatabaseSettingsMode; - } else if (widget == m_editEntryWidget) { + } else if (widget == m_editEntryWidget || widget == m_historyEditEntryWidget) { mode = Mode::EditEntryMode; } else if (widget == m_editGroupWidget) { mode = Mode::EditGroupMode; diff --git a/src/gui/EditWidget.cpp b/src/gui/EditWidget.cpp index 291a08b17..ab05758da 100644 --- a/src/gui/EditWidget.cpp +++ b/src/gui/EditWidget.cpp @@ -71,16 +71,25 @@ void EditWidget::addPage(const QString& labelText, const QIcon& icon, QWidget* w m_ui->categoryList->addCategory(labelText, icon); } -bool EditWidget::hasPage(QWidget* widget) +bool EditWidget::hasPage(const QWidget* widget) const { + return pageIndex(widget) >= 0; +} + +int EditWidget::pageIndex(const QWidget* widget) const +{ + if (!widget) { + return -1; + } + for (int i = 0; i < m_ui->stackedWidget->count(); i++) { auto* scrollArea = qobject_cast(m_ui->stackedWidget->widget(i)); if (scrollArea && scrollArea->widget() == widget) { - return true; + return i; } } - return false; + return -1; } void EditWidget::setPageHidden(QWidget* widget, bool hidden) diff --git a/src/gui/EditWidget.h b/src/gui/EditWidget.h index b70dfc0f1..4839d5877 100644 --- a/src/gui/EditWidget.h +++ b/src/gui/EditWidget.h @@ -42,7 +42,8 @@ public: ~EditWidget() override; void addPage(const QString& labelText, const QIcon& icon, QWidget* widget); - bool hasPage(QWidget* widget); + bool hasPage(const QWidget* widget) const; + int pageIndex(const QWidget* widget) const; void setPageHidden(QWidget* widget, bool hidden); void setCurrentPage(int index); void setHeadline(const QString& text); diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp index 8b29333d0..9af9aa601 100644 --- a/src/gui/entry/EditEntryWidget.cpp +++ b/src/gui/entry/EditEntryWidget.cpp @@ -139,6 +139,47 @@ EditEntryWidget::EditEntryWidget(QWidget* parent) EditEntryWidget::~EditEntryWidget() = default; +bool EditEntryWidget::switchToPage(Page page) +{ + auto index = pageIndex(widgetForPage(page)); + if (index >= 0) { + setCurrentPage(index); + return true; + } + return false; +} + +QWidget* EditEntryWidget::widgetForPage(Page page) const +{ + switch (page) { + case Page::Main: + return m_mainWidget; + case Page::Advanced: + return m_advancedWidget; + case Page::Icon: + return m_iconsWidget; + case Page::AutoType: + return m_autoTypeWidget; + case Page::Browser: +#ifdef WITH_XC_BROWSER + return m_browserWidget; +#else + return nullptr; +#endif + case Page::SSHAgent: +#ifdef WITH_XC_SSHAGENT + return m_sshAgentWidget; +#else + return nullptr; +#endif + case Page::Properties: + return m_editWidgetProperties; + case Page::History: + return m_historyWidget; + } + return nullptr; +} + void EditEntryWidget::setupMain() { m_mainUi->setupUi(m_mainWidget); @@ -886,7 +927,7 @@ void EditEntryWidget::loadEntry(Entry* entry, setForms(entry); setReadOnly(m_history); - setCurrentPage(0); + switchToPage(Page::Main); setPageHidden(m_historyWidget, m_history || m_entry->historyItems().count() < 1); #ifdef WITH_XC_SSHAGENT setPageHidden(m_sshAgentWidget, !sshAgent()->isEnabled()); @@ -1131,7 +1172,7 @@ bool EditEntryWidget::commitEntry() MessageBox::Yes | MessageBox::No, MessageBox::Yes); if (res == MessageBox::Yes) { - setCurrentPage(3); + switchToPage(Page::AutoType); return false; } } @@ -1146,7 +1187,7 @@ bool EditEntryWidget::commitEntry() MessageBox::Yes | MessageBox::No, MessageBox::Yes); if (res == MessageBox::Yes) { - setCurrentPage(3); + switchToPage(Page::AutoType); return false; } } diff --git a/src/gui/entry/EditEntryWidget.h b/src/gui/entry/EditEntryWidget.h index 0507b5dfb..323a85b8f 100644 --- a/src/gui/entry/EditEntryWidget.h +++ b/src/gui/entry/EditEntryWidget.h @@ -77,6 +77,19 @@ public: Entry* currentEntry() const; void clear(); + enum class Page + { + Main, + Advanced, + Icon, + AutoType, + Browser, + SSHAgent, + Properties, + History + }; + bool switchToPage(Page page); + signals: void editFinished(bool accepted); void historyEntryActivated(Entry* entry); @@ -162,6 +175,8 @@ private: void displayAttribute(QModelIndex index, bool showProtected); + QWidget* widgetForPage(Page page) const; + QPointer m_entry; QSharedPointer m_db; diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp index 600002545..f54c97137 100644 --- a/tests/gui/TestGui.cpp +++ b/tests/gui/TestGui.cpp @@ -627,8 +627,28 @@ void TestGui::testEditEntry() QCOMPARE(entry->historyItems().size(), ++editCount); QVERIFY(!applyButton->isEnabled()); + // Test viewing entry history + auto historyView = editEntryWidget->findChild("historyView"); + auto showButton = editEntryWidget->findChild("showButton"); + QVERIFY(historyView); + editEntryWidget->switchToPage(EditEntryWidget::Page::History); + QApplication::processEvents(); + QVERIFY(historyView->isVisible()); + QVERIFY(!showButton->isEnabled()); + // Select the second row in the history view + historyView->setCurrentIndex(historyView->model()->index(1, 0)); + QVERIFY(showButton->isEnabled()); + QTest::mouseClick(showButton, Qt::LeftButton); + // Verify that the entry history widget is shown + auto entryHistoryWidget = m_dbWidget->findChild("editEntryHistoryWidget"); + QVERIFY(entryHistoryWidget); + QVERIFY(entryHistoryWidget->isVisible()); + QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditEntryMode); + QTest::keyClick(entryHistoryWidget, Qt::Key_Escape); + QVERIFY(historyView->isVisible()); + // Test the "known bad" checkbox - editEntryWidget->setCurrentPage(1); + editEntryWidget->switchToPage(EditEntryWidget::Page::Advanced); auto excludeReportsCheckBox = editEntryWidget->findChild("excludeReportsCheckBox"); QVERIFY(excludeReportsCheckBox); QCOMPARE(excludeReportsCheckBox->isChecked(), false); @@ -651,7 +671,7 @@ void TestGui::testEditEntry() QCOMPARE(tags->tags().last(), QString("tag 2_is!awesome")); // Test entry colors (simulate choosing a color) - editEntryWidget->setCurrentPage(1); + editEntryWidget->switchToPage(EditEntryWidget::Page::Advanced); auto fgColor = QString("#FF0000"); auto bgColor = QString("#0000FF"); // Set foreground color @@ -668,7 +688,7 @@ void TestGui::testEditEntry() QCOMPARE(entry->historyItems().size(), ++editCount); // Test protected attributes - editEntryWidget->setCurrentPage(1); + editEntryWidget->switchToPage(EditEntryWidget::Page::Advanced); auto* attrTextEdit = editEntryWidget->findChild("attributesEdit"); QTest::mouseClick(editEntryWidget->findChild("addAttributeButton"), Qt::LeftButton); QString attrText = "TEST TEXT"; @@ -678,7 +698,7 @@ void TestGui::testEditEntry() QVERIFY(attrTextEdit->toPlainText().contains("PROTECTED")); QTest::mouseClick(editEntryWidget->findChild("revealAttributeButton"), Qt::LeftButton); QCOMPARE(attrTextEdit->toPlainText(), attrText); - editEntryWidget->setCurrentPage(0); + editEntryWidget->switchToPage(EditEntryWidget::Page::Main); // Save the edit (press OK) QTest::mouseClick(okButton, Qt::LeftButton); @@ -1057,7 +1077,7 @@ void TestGui::testTotp() QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditEntryMode); auto* editEntryWidget = m_dbWidget->findChild("editEntryWidget"); - editEntryWidget->setCurrentPage(1); + editEntryWidget->switchToPage(EditEntryWidget::Page::Advanced); auto* attrTextEdit = editEntryWidget->findChild("attributesEdit"); QTest::mouseClick(editEntryWidget->findChild("revealAttributeButton"), Qt::LeftButton); QCOMPARE(attrTextEdit->toPlainText(), expectedFinalSeed); @@ -1779,7 +1799,7 @@ void TestGui::testDatabaseSettings() QTest::keyClicks(titleEdit, "Test autosaveDelay 1"); // 2.b) Save changes - editEntryWidget->setCurrentPage(0); + editEntryWidget->switchToPage(EditEntryWidget::Page::Main); auto* editEntryWidgetButtonBox = editEntryWidget->findChild("buttonBox"); QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton); @@ -1793,7 +1813,7 @@ void TestGui::testDatabaseSettings() QTest::keyClicks(titleEdit, "Test autosaveDelay 2"); // 2.e) Save changes - editEntryWidget->setCurrentPage(0); + editEntryWidget->switchToPage(EditEntryWidget::Page::Main); editEntryWidgetButtonBox = editEntryWidget->findChild("buttonBox"); QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton); @@ -1813,7 +1833,7 @@ void TestGui::testDatabaseSettings() QTest::keyClicks(titleEdit, "Test autosaveDelay 3"); // 4.b) Save changes - editEntryWidget->setCurrentPage(0); + editEntryWidget->switchToPage(EditEntryWidget::Page::Main); editEntryWidgetButtonBox = editEntryWidget->findChild("buttonBox"); QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton); @@ -1832,7 +1852,7 @@ void TestGui::testDatabaseSettings() QTest::mouseClick(entryNewWidget, Qt::LeftButton); QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditEntryMode); QTest::keyClicks(titleEdit, "Test autosaveDelay 4"); - editEntryWidget->setCurrentPage(0); + editEntryWidget->switchToPage(EditEntryWidget::Page::Main); editEntryWidgetButtonBox = editEntryWidget->findChild("buttonBox"); QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton); Tools::wait(150); // due to modify timer @@ -2124,7 +2144,7 @@ void TestGui::testAutoType() QTest::keyClicks(usernameComboBox, "AutocompletionUsername"); // 1.b) Uncheck Auto-Type checkbox - editEntryWidget->setCurrentPage(3); + editEntryWidget->switchToPage(EditEntryWidget::Page::AutoType); auto* enableAutoTypeButton = editEntryWidget->findChild("enableButton"); QVERIFY(enableAutoTypeButton); QVERIFY(enableAutoTypeButton->isVisible()); @@ -2134,7 +2154,7 @@ void TestGui::testAutoType() QVERIFY(!enableAutoTypeButton->isChecked()); // 1.c) Save changes - editEntryWidget->setCurrentPage(0); + editEntryWidget->switchToPage(EditEntryWidget::Page::Main); auto* editEntryWidgetButtonBox = editEntryWidget->findChild("buttonBox"); QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton); @@ -2148,13 +2168,13 @@ void TestGui::testAutoType() QTest::keyClicks(usernameComboBox, "AutocompletionUsername"); // 2.b) Confirm AutoType is enabled and default - editEntryWidget->setCurrentPage(3); + editEntryWidget->switchToPage(EditEntryWidget::Page::AutoType); QVERIFY(enableAutoTypeButton->isChecked()); auto* inheritSequenceButton = editEntryWidget->findChild("inheritSequenceButton"); QVERIFY(inheritSequenceButton->isChecked()); // 2.c) Save changes - editEntryWidget->setCurrentPage(0); + editEntryWidget->switchToPage(EditEntryWidget::Page::Main); QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton); // 3. Create an entry with custom Auto-Type sequence @@ -2167,7 +2187,7 @@ void TestGui::testAutoType() QTest::keyClicks(usernameComboBox, "AutocompletionUsername"); // 3.b) Confirm AutoType is enabled and set custom sequence - editEntryWidget->setCurrentPage(3); + editEntryWidget->switchToPage(EditEntryWidget::Page::AutoType); QVERIFY(enableAutoTypeButton->isChecked()); auto* customSequenceButton = editEntryWidget->findChild("customSequenceButton"); QTest::mouseClick(customSequenceButton, Qt::LeftButton); @@ -2180,7 +2200,7 @@ void TestGui::testAutoType() QTest::keyClicks(sequenceEdit, "{USERNAME}{TAB}{TAB}{PASSWORD}{ENTER}"); // 3.c) Save changes - editEntryWidget->setCurrentPage(0); + editEntryWidget->switchToPage(EditEntryWidget::Page::Main); QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton); QApplication::processEvents();