aboutsummaryrefslogtreecommitdiffstats
path: root/tests/test_mainwindow.cpp
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-08-26 19:31:45 +0200
committerDanilo M. <danix@danix.xyz>2026-08-26 19:31:45 +0200
commit59a196ea849c64c51311f56bf62130e74d90b784 (patch)
treef322f955b0b5981dfafc85db72f224795941a4ad /tests/test_mainwindow.cpp
parent1c034f6358f17c5c1d0eeaa04c42c33fac125d93 (diff)
downloadqtmaildir-59a196ea849c64c51311f56bf62130e74d90b784.tar.gz
qtmaildir-59a196ea849c64c51311f56bf62130e74d90b784.zip
feat: open the unsynced-changes count to see what it counts
Item 119, and item 146 which is the same request recorded again. The status bar's count answers "is my work safe to quit on" and could not say what the work was. The label opens a read-only list on a click. A QLabel has no clicked signal, so the press is taken by MainWindow's existing event filter rather than by replacing the label with a flat QToolButton, which would have brought the style's button metrics into a status bar the label already sits correctly in. The pointing-hand cursor is the affordance, since a status-bar label has room for nothing else. The layout is the user's own: a message appears once with its actions beneath it. PendingChangesDialog::rowsFor() does the grouping over a run of rows sharing an id, which the snapshot has already ordered, so the actions under one message keep the order they were made in. Read-only, deliberately. Retrying or discarding a change from here would be a new mutation path with its own undo question, and the count exists to be understood rather than edited. Three rules the tests pin, each of which is a way the list could disagree with the count it was opened from: - Grouping must not collapse: two actions on one message are two rows. - A thread row stays thread-scoped and reports how many messages it covered. - An id the index no longer holds still opens a run of its own, showing that its subject is unknown rather than folding its actions under the message above it. This is why the row carries startsMessage rather than inferring it from a non-empty subject. The queued call carrying QStringList, QList<bool> and QList<int> is covered by a test that drives it across a real thread, since a container whose metatype does not resolve is dropped at runtime and the slot runs with a default. Both survive on Qt 6.11; the test is what says so, and what would fail if that changed. Italian ships with it: five new strings, lupdate clean, lrelease 522 finished and 0 unfinished. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P88Q3MCSCSQxKDy7pmXh9F
Diffstat (limited to 'tests/test_mainwindow.cpp')
-rw-r--r--tests/test_mainwindow.cpp48
1 files changed, 48 insertions, 0 deletions
diff --git a/tests/test_mainwindow.cpp b/tests/test_mainwindow.cpp
index be19652..a572fd0 100644
--- a/tests/test_mainwindow.cpp
+++ b/tests/test_mainwindow.cpp
@@ -47,6 +47,7 @@
#include "config.h"
#include "keymap.h"
#include "mainwindow.h"
+#include "pendingchangesdialog.h"
#include "messageview.h"
#include "mimeparser.h"
#include "notmuchworker.h"
@@ -418,6 +419,7 @@ private slots:
void everyPendingChangeCanNameItsMessages();
void theSnapshotGroupsActionsUnderTheirMessage();
void theSnapshotKeepsAThreadActionThreadScoped();
+ void theIndicatorOpensItsListOnAClick();
void anEditDuringABackgroundSyncIsNotSentYet();
void aHeldEditIsSentWhenTheBackgroundSyncEnds();
void aHeldEditCountsAsUnsynced();
@@ -6574,6 +6576,52 @@ void TestMainWindow::theSnapshotKeepsAThreadActionThreadScoped()
QCOMPARE(rows.size(), window.pendingEditCount());
}
+void TestMainWindow::theIndicatorOpensItsListOnAClick()
+{
+ // The label is a QLabel and has no clicked signal, so the click is taken
+ // by an event filter. A test that called showPendingChanges() directly
+ // would pass with that filter never installed, which is the whole gesture.
+ const Config config;
+ MainWindow window(config);
+
+ auto *label = window.findChild<QLabel *>(QStringLiteral("pendingEdits"));
+ QVERIFY(label);
+
+ TagChange change;
+ change.messageIds = { QStringLiteral("click@example.org") };
+ change.added = { QStringLiteral("deleted") };
+ change.description = QStringLiteral("Delete");
+ QVERIFY(QMetaObject::invokeMethod(&window, "onTagsApplied",
+ Q_ARG(TagChange, change)));
+ QVERIFY(!label->isHidden());
+
+ // With no worker the dialog opens directly and modally, so it is closed
+ // from a timer rather than by driving exec() to return some other way.
+ // Polled rather than checked once: exec() parents the dialog and spins its
+ // own event loop, so a single-shot timer can fire before it exists.
+ bool sawDialog = false;
+ auto *poll = new QTimer(&window);
+ poll->setInterval(1);
+ QObject::connect(poll, &QTimer::timeout, &window, [&window, &sawDialog]() {
+ if (auto *dialog = window.findChild<PendingChangesDialog *>()) {
+ sawDialog = true;
+ QCOMPARE(dialog->rows().size(), 1);
+ QCOMPARE(dialog->rows().at(0).action, QStringLiteral("Delete"));
+ dialog->reject();
+ }
+ });
+ poll->start();
+
+ QMouseEvent press(QEvent::MouseButtonRelease, QPointF(1, 1),
+ QPointF(1, 1), Qt::LeftButton, Qt::LeftButton,
+ Qt::NoModifier);
+ QCoreApplication::sendEvent(label, &press);
+
+ // The subjects are resolved on the worker thread, so the dialog appears a
+ // round trip after the click rather than inside sendEvent().
+ QTRY_VERIFY_WITH_TIMEOUT(sawDialog, 15000);
+}
+
// Item 37. A tag edit made while a background sync holds notmuch's write lock
// used to stall the worker: the read-write open BLOCKS until the lock frees
// (measured 9.158s against a 12s hold, returning NOTMUCH_STATUS_SUCCESS), so