aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-08-28 18:14:58 +0200
committerDanilo M. <danix@danix.xyz>2026-08-28 18:14:58 +0200
commit2c51c9653e4e045373120d50ce026f0a752a608f (patch)
tree664f7c758d219b70346abebc83b1925936c35562 /tests
parenta346bef6c08e9725f3ab41f8237f01e9eb69eea7 (diff)
downloadqtmaildir-2c51c9653e4e045373120d50ce026f0a752a608f.tar.gz
qtmaildir-2c51c9653e4e045373120d50ce026f0a752a608f.zip
feat: add the thread dashboard
A widget over a ThreadDigest: header, tags, counts, the unread list capped with a link to the rest, and an activity sparkline, scrolling under a pinned action strip. It invents no colours. ThreadDigest::unread is QVector<MessageNode> rather than QVector<MessageRef>. The dashboard draws rows for messages it never opens, which is what MessageNode exists for; MessageRef carries only what the pane needs once a message is already open and has no subject, sender or date. The struct's no-file-opening contract still holds, since all four of those fields are served from the notmuch index.
Diffstat (limited to 'tests')
-rw-r--r--tests/CMakeLists.txt1
-rw-r--r--tests/test_threaddashboard.cpp74
2 files changed, 75 insertions, 0 deletions
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 1646028..e67d7e0 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -72,6 +72,7 @@ add_qtmaildir_test(searchterm)
add_qtmaildir_test(signatures)
add_qtmaildir_test(busyindicator)
add_qtmaildir_test(tagstrip)
+add_qtmaildir_test(threaddashboard)
add_qtmaildir_test(messagedetailsdialog)
add_qtmaildir_test(markdownrenderer)
add_qtmaildir_test(messagebuilder)
diff --git a/tests/test_threaddashboard.cpp b/tests/test_threaddashboard.cpp
new file mode 100644
index 0000000..66f187f
--- /dev/null
+++ b/tests/test_threaddashboard.cpp
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 Danilo M. <danix@danix.xyz>
+ */
+#include <QtTest>
+
+#include "threaddashboard.h"
+#include "threaddigest.h"
+
+class TestThreadDashboard : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void itNamesTheUnreadCountItWasGiven();
+ void itSaysAllCaughtUpWhenNothingIsUnread();
+ void itOffersMoreWhenTheListIsCapped();
+ void itOffersNoMoreLinkWhenTheListIsComplete();
+};
+
+static ThreadDigest digestWith(int unreadTotal, int shown)
+{
+ ThreadDigest d;
+ d.threadId = QStringLiteral("t1");
+ d.totalCount = 44;
+ d.unreadTotal = unreadTotal;
+ for (int i = 0; i < shown; ++i) {
+ MessageNode ref;
+ ref.messageId = QStringLiteral("m%1@example.org").arg(i);
+ ref.subject = QStringLiteral("Re: something");
+ // The avatar hashes the bare address, so a node without one exercises
+ // only the no-sender fallback.
+ ref.senderAddress = QStringLiteral("someone@example.org");
+ d.unread.append(ref);
+ }
+ d.buckets = QVector<int>(ThreadDigest::kBuckets, 1);
+ return d;
+}
+
+void TestThreadDashboard::itNamesTheUnreadCountItWasGiven()
+{
+ ThreadDashboard dashboard;
+ dashboard.setDigest(digestWith(3, 3));
+ QCOMPARE(dashboard.unreadCountShown(), 3);
+ QVERIFY(!dashboard.showingAllCaughtUp());
+}
+
+void TestThreadDashboard::itSaysAllCaughtUpWhenNothingIsUnread()
+{
+ ThreadDashboard dashboard;
+ dashboard.setDigest(digestWith(0, 0));
+ QVERIFY2(dashboard.showingAllCaughtUp(),
+ "a fully read thread shows an empty Waiting for you block rather "
+ "than saying so");
+}
+
+void TestThreadDashboard::itOffersMoreWhenTheListIsCapped()
+{
+ // 20 unread, 5 shown: the link has to name the 15 the pane cannot list, or
+ // the dashboard under-reports exactly when the number matters most.
+ ThreadDashboard dashboard;
+ dashboard.setDigest(digestWith(20, ThreadDigest::kUnreadShown));
+ QCOMPARE(dashboard.hiddenUnreadCount(), 15);
+}
+
+void TestThreadDashboard::itOffersNoMoreLinkWhenTheListIsComplete()
+{
+ ThreadDashboard dashboard;
+ dashboard.setDigest(digestWith(4, 4));
+ QCOMPARE(dashboard.hiddenUnreadCount(), 0);
+}
+
+QTEST_MAIN(TestThreadDashboard)
+#include "test_threaddashboard.moc"