MailJD nbsp;·nbsp; Test Dashboard nbsp;·nbsp; Coverage
LCOV - code coverage report
Current view: top level - ui - SetupWizard.cpp (source / functions) Coverage Total Hit
Test: MailJD Coverage (Unit + E2E) Lines: 84.0 % 150 126
Test Date: 2026-06-21 21:10:19 Functions: 94.7 % 19 18
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 41.6 % 356 148

             Branch data     Line data    Source code
       1                 :             : #include "SetupWizard.h"
       2                 :             : 
       3                 :             : #include <QLabel>
       4                 :             : #include <QLoggingCategory>
       5                 :             : #include <QMessageBox>
       6                 :             : #include <QProgressBar>
       7                 :             : #include <QPushButton>
       8                 :             : #include <QScrollArea>
       9                 :             : #include <QTimer>
      10                 :             : #include <QVBoxLayout>
      11                 :             : 
      12                 :             : #include "data/AccountConfig.h"
      13                 :             : #include "service/ImapService.h"
      14                 :             : #include "ui/AccountFormWidget.h"
      15                 :             : #include "ui/ThemeManager.h"
      16                 :             : #include <QEvent>
      17                 :             : 
      18   [ +  -  +  -  :           1 : Q_LOGGING_CATEGORY(lcWizard, "mailjd.wizard")
             +  -  -  - ]
      19                 :             : 
      20                 :             : // --- SetupWizard ---
      21                 :             : 
      22                 :          10 : SetupWizard::SetupWizard(QWidget *parent)
      23   [ +  -  +  - ]:          10 :     : QWizard(parent), m_configDir(AccountConfigLoader::defaultConfigDir()) {
      24   [ +  -  +  - ]:          10 :   setWindowTitle(tr("MailJD – Initial Setup"));
      25         [ +  - ]:          10 :   setMinimumSize(650, 700);
      26         [ +  - ]:          10 :   setWizardStyle(QWizard::ModernStyle);
      27                 :             : 
      28   [ +  -  +  -  :          10 :   addPage(new WelcomePage(this));
          +  -  -  +  -  
                      - ]
      29   [ +  -  +  -  :          10 :   addPage(new AccountPage(this));
          +  -  -  +  -  
                      - ]
      30   [ +  -  +  -  :          10 :   addPage(new TestPage(this));
          +  -  -  +  -  
                      - ]
      31                 :             : 
      32                 :             :   // On finish: save the account
      33         [ +  - ]:          10 :   connect(this, &QWizard::accepted, this, [this]() {
      34         [ +  - ]:           2 :     auto acc = configuredAccount();
      35         [ +  + ]:           2 :     if (!acc.name.isEmpty()) {
      36         [ +  - ]:           1 :       AccountConfigLoader::save(acc, m_configDir);
      37   [ +  -  +  -  :           2 :       qCInfo(lcWizard) << "Wizard completed, saved account:" << acc.name;
          +  -  +  -  +  
                      + ]
      38                 :             :     }
      39                 :           2 :   });
      40                 :          10 : }
      41                 :             : 
      42                 :           6 : void SetupWizard::setConfigDir(const QString &dir) { m_configDir = dir; }
      43                 :             : 
      44                 :           3 : AccountConfig SetupWizard::configuredAccount() const {
      45                 :           3 :   auto *page = qobject_cast<AccountPage *>(this->page(1));
      46         [ +  - ]:           3 :   if (page) {
      47                 :           3 :     return page->form()->config();
      48                 :             :   }
      49                 :           0 :   return {};
      50   [ #  #  #  #  :           0 : }
          #  #  #  #  #  
                      # ]
      51                 :             : 
      52                 :             : // --- WelcomePage ---
      53                 :             : 
      54                 :          10 : WelcomePage::WelcomePage(QWidget *parent) : QWizardPage(parent) {
      55   [ +  -  +  - ]:          10 :   setTitle(tr("Welcome to MailJD"));
      56   [ +  -  +  - ]:          10 :   setSubTitle(tr("Set up your first email account now."));
      57                 :             : 
      58   [ +  -  +  -  :          10 :   auto *layout = new QVBoxLayout(this);
             -  +  -  - ]
      59                 :             : 
      60                 :             :   auto *label = new QLabel(
      61                 :             :       "<p>MailJD ist ein schneller, tastaturgesteuerter Mail-Client für "
      62                 :             :       "Linux.</p>"
      63                 :             :       "<p>Im nächsten Schritt kannst du deinen IMAP/SMTP-Server konfigurieren. "
      64                 :             :       "Du brauchst folgende Informationen:</p>"
      65                 :             :       "<ul>"
      66                 :             :       "<li>IMAP-Server und Port (z.B. imap.example.com:993)</li>"
      67                 :             :       "<li>SMTP-Server und Port (z.B. smtp.example.com:587)</li>"
      68                 :             :       "<li>Dein Benutzername und Passwort</li>"
      69                 :             :       "</ul>",
      70   [ +  -  +  -  :          10 :       this);
          +  -  -  +  -  
                      - ]
      71         [ +  - ]:          10 :   label->setWordWrap(true);
      72         [ +  - ]:          10 :   layout->addWidget(label);
      73         [ +  - ]:          10 :   layout->addStretch();
      74                 :          10 : }
      75                 :             : 
      76                 :             : // --- AccountPage ---
      77                 :             : 
      78                 :          10 : AccountPage::AccountPage(QWidget *parent) : QWizardPage(parent) {
      79   [ +  -  +  - ]:          10 :   setTitle(tr("Account Setup"));
      80   [ +  -  +  - ]:          10 :   setSubTitle(tr("Enter your email account server details."));
      81                 :             : 
      82   [ +  -  +  -  :          10 :   auto *layout = new QVBoxLayout(this);
             -  +  -  - ]
      83                 :             : 
      84   [ +  -  +  -  :          10 :   auto *scrollArea = new QScrollArea(this);
             -  +  -  - ]
      85         [ +  - ]:          10 :   scrollArea->setWidgetResizable(true);
      86         [ +  - ]:          10 :   scrollArea->setFrameShape(QFrame::NoFrame);
      87                 :             : 
      88   [ +  -  +  -  :          10 :   m_form = new AccountFormWidget(this);
             -  +  -  - ]
      89         [ +  - ]:          10 :   scrollArea->setWidget(m_form);
      90                 :             : 
      91         [ +  - ]:          10 :   layout->addWidget(scrollArea);
      92                 :          10 : }
      93                 :             : 
      94                 :           2 : bool AccountPage::validatePage() {
      95         [ +  - ]:           2 :   auto errors = m_form->validate();
      96         [ +  + ]:           2 :   if (!errors.isEmpty()) {
      97   [ +  -  +  - ]:           1 :     QMessageBox::warning(this, tr("Missing Information"),
      98                 :           1 :                          "Bitte korrigiere folgende Fehler:\n\n• " +
      99   [ +  -  +  -  :           2 :                              errors.join("\n• "));
                   +  - ]
     100                 :           1 :     return false;
     101                 :             :   }
     102                 :           1 :   return true;
     103                 :           2 : }
     104                 :             : 
     105                 :             : // --- TestPage ---
     106                 :             : 
     107                 :          10 : TestPage::TestPage(QWidget *parent) : QWizardPage(parent) {
     108   [ +  -  +  - ]:          10 :   setTitle(tr("Connection Test"));
     109   [ +  -  +  - ]:          10 :   setSubTitle(tr("Your IMAP server connection is being tested."));
     110                 :             : 
     111   [ +  -  +  -  :          10 :   auto *layout = new QVBoxLayout(this);
             -  +  -  - ]
     112                 :             : 
     113   [ +  -  +  -  :          10 :   m_summaryLabel = new QLabel(this);
             -  +  -  - ]
     114         [ +  - ]:          10 :   m_summaryLabel->setWordWrap(true);
     115         [ +  - ]:          10 :   layout->addWidget(m_summaryLabel);
     116                 :             : 
     117   [ +  -  +  -  :          10 :   m_progressBar = new QProgressBar(this);
             -  +  -  - ]
     118         [ +  - ]:          10 :   m_progressBar->setRange(0, 0); // Indeterminate
     119         [ +  - ]:          10 :   layout->addWidget(m_progressBar);
     120                 :             : 
     121   [ +  -  +  -  :          10 :   m_statusLabel = new QLabel(tr("Testing connection..."), this);
          +  -  -  +  -  
                      - ]
     122         [ +  - ]:          10 :   m_statusLabel->setWordWrap(true);
     123         [ +  - ]:          10 :   layout->addWidget(m_statusLabel);
     124                 :             : 
     125   [ +  -  +  -  :          10 :   m_retryButton = new QPushButton("Erneut testen", this);
          +  -  -  +  -  
                      - ]
     126         [ +  - ]:          10 :   m_retryButton->setVisible(false);
     127         [ +  - ]:          10 :   connect(m_retryButton, &QPushButton::clicked, this, &TestPage::runTest);
     128         [ +  - ]:          10 :   layout->addWidget(m_retryButton);
     129                 :             : 
     130         [ +  - ]:          10 :   layout->addStretch();
     131                 :          10 : }
     132                 :             : 
     133                 :           2 : void TestPage::initializePage() {
     134                 :             :   // Get account info from the previous page
     135   [ +  -  +  -  :           2 :   auto *accountPage = qobject_cast<AccountPage *>(wizard()->page(1));
                   +  - ]
     136         [ -  + ]:           2 :   if (!accountPage)
     137                 :           0 :     return;
     138                 :             : 
     139         [ +  - ]:           2 :   auto cfg = accountPage->form()->config();
     140         [ +  - ]:           2 :   m_summaryLabel->setText(QString("<b>Account:</b> %1<br>"
     141                 :             :                                   "<b>E-Mail:</b> %2<br>"
     142         [ +  - ]:           2 :                                   "<b>IMAP:</b> %3:%4 (%5)")
     143         [ +  - ]:           4 :                               .arg(cfg.name, cfg.email, cfg.imap.host,
     144         [ +  - ]:           4 :                                    QString::number(cfg.imap.port),
     145         [ +  - ]:           4 :                                    cfg.imap.security.toUpper()));
     146                 :             : 
     147                 :           2 :   m_testComplete = false;
     148         [ +  - ]:           2 :   emit completeChanged();
     149                 :             : 
     150         [ +  - ]:           2 :   runTest();
     151                 :           2 : }
     152                 :             : 
     153                 :           9 : bool TestPage::isComplete() const {
     154                 :             :   // Always allow "Finish" – even if the test failed.
     155                 :             :   // The user may want to save the config and fix connection later.
     156                 :           9 :   return m_testComplete;
     157                 :             : }
     158                 :             : 
     159                 :           2 : void TestPage::runTest() {
     160         [ +  - ]:           2 :   m_progressBar->setVisible(true);
     161         [ +  - ]:           2 :   m_progressBar->setRange(0, 0);
     162   [ +  -  +  - ]:           2 :   m_statusLabel->setText("Verbindung wird getestet...");
     163   [ +  -  +  - ]:           2 :   m_statusLabel->setStyleSheet("");
     164         [ +  - ]:           2 :   m_retryButton->setVisible(false);
     165                 :             : 
     166                 :             :   // Clean up previous
     167         [ -  + ]:           2 :   if (m_testService) {
     168         [ #  # ]:           0 :     m_testService->disconnect();
     169         [ #  # ]:           0 :     m_testService->deleteLater();
     170                 :             :   }
     171                 :             : 
     172   [ +  -  +  -  :           2 :   m_testService = new ImapService(this);
             -  +  -  - ]
     173                 :             : 
     174   [ +  -  +  -  :           2 :   auto *accountPage = qobject_cast<AccountPage *>(wizard()->page(1));
                   +  - ]
     175         [ -  + ]:           2 :   if (!accountPage)
     176                 :           0 :     return;
     177                 :             : 
     178         [ +  - ]:           2 :   auto cfg = accountPage->form()->config();
     179                 :             : 
     180                 :           2 :   connect(m_testService, &ImapService::stateChanged, this,
     181         [ +  - ]:           2 :           [this](ImapService::State state) {
     182         [ -  + ]:          10 :             if (state == ImapService::State::Authenticated) {
     183                 :           0 :               onTestSuccess();
     184                 :             :             }
     185                 :          10 :           });
     186                 :             : 
     187                 :           2 :   connect(m_testService, &ImapService::errorOccurred, this,
     188         [ +  - ]:           4 :           [this](const QString &error) { onTestFailed(error); });
     189                 :             : 
     190                 :             :   // Timeout: 10s
     191         [ +  - ]:           2 :   QTimer::singleShot(10000, this, [this]() {
     192   [ #  #  #  # ]:           0 :     if (m_testService && !m_testComplete) {
     193   [ #  #  #  # ]:           0 :       onTestFailed("Zeitüberschreitung (10s)");
     194                 :             :     }
     195                 :           0 :   });
     196                 :             : 
     197         [ +  - ]:           2 :   m_testService->connectToServer(cfg.imap);
     198                 :           2 : }
     199                 :             : 
     200                 :           0 : void TestPage::onTestSuccess() {
     201                 :           0 :   m_progressBar->setVisible(false);
     202   [ #  #  #  # ]:           0 :   m_statusLabel->setText(
     203                 :             :       "✅ Verbindung erfolgreich! Der Account kann gespeichert werden.");
     204                 :             :   // Sprint 69: @success token instead of CSS named color.
     205         [ #  # ]:           0 :   m_statusLabel->setStyleSheet(
     206                 :           0 :       QStringLiteral("color: %1; font-weight: bold;")
     207   [ #  #  #  #  :           0 :           .arg(ThemeManager::instance().color(QLatin1String("@success"))));
             #  #  #  # ]
     208                 :           0 :   m_retryButton->setVisible(false);
     209                 :             : 
     210                 :           0 :   m_testComplete = true;
     211                 :           0 :   emit completeChanged();
     212                 :             : 
     213         [ #  # ]:           0 :   if (m_testService) {
     214                 :           0 :     m_testService->disconnect();
     215                 :           0 :     m_testService->deleteLater();
     216                 :           0 :     m_testService = nullptr;
     217                 :             :   }
     218                 :           0 : }
     219                 :             : 
     220                 :           2 : void TestPage::onTestFailed(const QString &error) {
     221                 :           2 :   m_progressBar->setVisible(false);
     222   [ +  -  +  - ]:           2 :   m_statusLabel->setText("❌ Verbindungstest fehlgeschlagen: " + error);
     223                 :             :   // Sprint 69: @danger token instead of CSS named color.
     224         [ +  - ]:           4 :   m_statusLabel->setStyleSheet(
     225                 :           4 :       QStringLiteral("color: %1;")
     226   [ +  -  +  -  :           4 :           .arg(ThemeManager::instance().color(QLatin1String("@danger"))));
             +  -  +  - ]
     227                 :           2 :   m_retryButton->setVisible(true);
     228                 :             : 
     229                 :             :   // Allow finish even on failure
     230                 :           2 :   m_testComplete = true;
     231                 :           2 :   emit completeChanged();
     232                 :             : 
     233         [ +  - ]:           2 :   if (m_testService) {
     234                 :           2 :     m_testService->disconnect();
     235                 :           2 :     m_testService->deleteLater();
     236                 :           2 :     m_testService = nullptr;
     237                 :             :   }
     238                 :           2 : }
     239                 :             : 
     240                 :             : // T-304: Runtime language switching
     241                 :          19 : void SetupWizard::changeEvent(QEvent *event) {
     242         [ +  + ]:          19 :   if (event->type() == QEvent::LanguageChange)
     243                 :           1 :     retranslateUi();
     244                 :          19 :   QWizard::changeEvent(event);
     245                 :          19 : }
     246                 :             : 
     247                 :           1 : void SetupWizard::retranslateUi() {
     248   [ +  -  +  - ]:           1 :   setWindowTitle(tr("MailJD \u2013 Initial Setup"));
     249                 :           1 : }
        

Generated by: LCOV version 2.0-1