summaryrefslogtreecommitdiffstats
path: root/tests/test_config.cpp
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-08-24 12:15:09 +0200
committerDanilo M. <danix@danix.xyz>2026-08-24 12:15:09 +0200
commit98ea0ee4691e60b430bd78eb615838da90bce244 (patch)
treed64d9d02e25f5b04044ad982beefbce3600a1503 /tests/test_config.cpp
parent02a795abe731da908928cd08a799ad79002ff421 (diff)
downloadqtmaildir-98ea0ee4691e60b430bd78eb615838da90bce244.tar.gz
qtmaildir-98ea0ee4691e60b430bd78eb615838da90bce244.zip
feat: add a Drafts filter, and close the composer with Ctrl+W
Items 138 and 148. The query row carried Unread, Inbox, Important, Sent and Trash, and no Drafts, though the composer has been autosaving into each account's drafts folder since compose shipped. Reaching them meant typing a query by hand. Smaller than its size suggested: Account::draftsQuery() and Config::allDraftsQuery() already existed for the placeholder pane's drafts count, and builtinFilters() derives the row from kQueryGenerators, so the work was the generator entry, two resolvedQuery branches, a label and an icon. It follows TRASH rather than Sent. Folder-matched like both, because `draft` is a Maildir flag notmuch surfaces as a tag while the folder is what the user means and what the composer actually writes into. But NOT flat: Sent is flat so a thread cannot fold the user's own message back into the conversation it answers, and a draft reply belongs with its conversation for the same reason a trashed message does. An account with no drafts folder shows no button, per item 103's rule. The existing row test surfaced that by failing until its fixture configured one, which is the rule working rather than a defect. Ctrl+W closes the composer, which bound nothing at all: the only way out was the title bar. The action is parented to the composer, so it is a WindowShortcut dispatched to the active one only and the main window's namespace is untouched, exactly like the formatting shortcuts. It calls close() rather than doing anything of its own, since closeEvent() already decides whether the draft is saved and a second route out that skipped it would lose the message. The Italian gains "Bozze"; lrelease reports 478 finished, 0 unfinished.
Diffstat (limited to 'tests/test_config.cpp')
-rw-r--r--tests/test_config.cpp67
1 files changed, 66 insertions, 1 deletions
diff --git a/tests/test_config.cpp b/tests/test_config.cpp
index c46b153..4d56ec1 100644
--- a/tests/test_config.cpp
+++ b/tests/test_config.cpp
@@ -119,6 +119,9 @@ private slots:
void anAccountCarriesItsTrashFolder();
void aBracketedTrashFolderIsQuoted();
void anAccountWithoutATrashFolderWarns();
+ void theDraftsFilterComposesPerAccount();
+ void theDraftsFilterMatchesNothingWithoutAFolder();
+ void theDraftsFilterIsThreadedNotFlat();
void theTrashFilterComposesPerAccount();
void theTrashFilterMatchesNothingWithoutAFolder();
void anAccountWithoutASendCommandIsReceiveOnly();
@@ -1021,6 +1024,67 @@ void TestConfig::anAccountWithoutATrashFolderWarns()
QVERIFY(joined.contains(QStringLiteral("trash")));
}
+void TestConfig::theDraftsFilterComposesPerAccount()
+{
+ // Item 138. Follows `sent` and `trash`, which match a FOLDER: `draft` is a
+ // Maildir flag notmuch surfaces as a tag, but the folder is what the user
+ // means by Drafts, and a provider that flags differently would disagree
+ // with the folder the composer actually writes into.
+ QTemporaryDir dir;
+ Config config;
+ config.load(writeIni(dir, QStringLiteral(
+ "[account.work]\n"
+ "maildir=work\n"
+ "drafts=Drafts\n"
+ "\n"
+ "[account.personal]\n"
+ "maildir=personal\n"
+ "drafts=[Provider]/Bozze\n")));
+
+ const SavedQuery drafts = Config::builtinFilter(QStringLiteral("drafts"));
+ QVERIFY2(drafts.isGenerated(), "drafts is not a generated filter");
+
+ const QString all = config.resolvedQuery(drafts, QString());
+ QVERIFY(all.contains(QStringLiteral("path:\"work/Drafts/**\"")));
+ QVERIFY(all.contains(
+ QStringLiteral("path:\"personal/[Provider]/Bozze/**\"")));
+
+ // The account's OWN query, asserted on the STRING: the all-accounts query
+ // wrapped in this account's path returns exactly the right rows because
+ // path: is hierarchical, so a row count passes against the wrong thing.
+ const QString scoped = config.resolvedQuery(drafts, QStringLiteral("work"));
+ QCOMPARE(scoped, QStringLiteral("path:\"work/Drafts/**\""));
+ QVERIFY(!scoped.contains(QStringLiteral("personal")));
+}
+
+void TestConfig::theDraftsFilterMatchesNothingWithoutAFolder()
+{
+ // An empty query means "match everything" to notmuch, so a button labelled
+ // Drafts would show the whole Maildir.
+ QTemporaryDir dir;
+ Config config;
+ config.load(writeIni(dir, QStringLiteral(
+ "[account.work]\n"
+ "maildir=work\n")));
+
+ const SavedQuery drafts = Config::builtinFilter(QStringLiteral("drafts"));
+ QCOMPARE(config.resolvedQuery(drafts, QString()),
+ Config::matchNothingQuery());
+}
+
+void TestConfig::theDraftsFilterIsThreadedNotFlat()
+{
+ // Unlike Sent, and deliberately. Sent is flat because a thread would fold
+ // the user's own message back into the conversation it answers, which is
+ // item 63's finding. A draft reply belongs with its conversation for the
+ // same reason a trashed message does, so drafts follow trash here.
+ const SavedQuery drafts = Config::builtinFilter(QStringLiteral("drafts"));
+ QVERIFY2(!drafts.flat, "the drafts filter is flat, like Sent");
+
+ const SavedQuery sent = Config::builtinFilter(QStringLiteral("sent"));
+ QVERIFY2(sent.flat, "Sent stopped being flat, which item 63 requires");
+}
+
void TestConfig::theTrashFilterComposesPerAccount()
{
// Two accounts, one with a plain folder and one nested under a bracketed
@@ -1492,7 +1556,7 @@ void TestConfig::everyBuiltinFilterIsAKnownGenerator()
Config config;
const QList<SavedQuery> filters = config.builtinFilters();
- QCOMPARE(filters.size(), 5);
+ QCOMPARE(filters.size(), 6);
QStringList names;
for (const SavedQuery &filter : filters) {
@@ -1514,6 +1578,7 @@ void TestConfig::everyBuiltinFilterIsAKnownGenerator()
QStringLiteral("Inbox"),
QStringLiteral("Important"),
QStringLiteral("Sent"),
+ QStringLiteral("Drafts"),
QStringLiteral("Trash") }));
}