aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/test_mainwindow.cpp105
1 files changed, 104 insertions, 1 deletions
diff --git a/tests/test_mainwindow.cpp b/tests/test_mainwindow.cpp
index c569f05..b96b158 100644
--- a/tests/test_mainwindow.cpp
+++ b/tests/test_mainwindow.cpp
@@ -82,7 +82,11 @@ class WorkerBackedWindow
public:
/// Builds the database, writes the config and loads it. Check isValid()
/// and error() before constructing the window.
- bool build()
+ /// `accountKey` and `accountMaildir` add one [account.<key>] section, which
+ /// is what makes runQuery() scope the bar's text with scopedQuery(). A test
+ /// that never selects an account can leave them empty.
+ bool build(const QString &accountKey = QString(),
+ const QString &accountMaildir = QString())
{
if (!m_fixture.isValid()) {
m_error = QStringLiteral("fixture directory invalid");
@@ -113,6 +117,12 @@ public:
// went unnoticed as broken once already.
out << "[general]\n"
<< "notmuch_config=" << m_fixture.configPath() << "\n";
+ if (!accountKey.isEmpty()) {
+ // QSettings reads `/` in a section name as a group separator,
+ // so the section is [account.key], never [account/key].
+ out << "\n[account." << accountKey << "]\n"
+ << "maildir=" << accountMaildir << "\n";
+ }
}
file.close();
@@ -188,6 +198,7 @@ private slots:
void aRefreshsBatchesLeaveTheStatusBarAlone();
void selectingAThreadRootShowsItInTheMessagePane();
void anUnexpandedRootRendersOneMessageNotTheConversation();
+ void aSingleMessageIdQuerysCardOpensInTheMessagePane();
void autoSyncIsNotArmedWhenDisabledOrWithNothingPending();
void autoSyncSkipsWhileABackgroundSyncIsRunning();
void aSuccessfulSyncRefreshesRatherThanRerunningTheQuery();
@@ -7184,4 +7195,96 @@ void TestMainWindow::anUnexpandedRootRendersOneMessageNotTheConversation()
"the pane rendered a conversation, not a single message");
}
+void TestMainWindow::aSingleMessageIdQuerysCardOpensInTheMessagePane()
+{
+ // Item 66's unverified half, reported again against 0.23.0 with a
+ // screenshot: an `id:` query in the bar produces exactly one card, the
+ // status bar reports "1 thread selected (1 message)", and the pane stays
+ // on the placeholder.
+ //
+ // The id is shaped like the real one that fails: dots, digits and an @.
+ //
+ // An ACCOUNT is configured and selected, because that is the state the
+ // report was made from and it is the only thing that changes the query:
+ // runQuery() wraps the bar's text in path:"<maildir>/**" and (...).
+ WorkerBackedWindow backed;
+ const QString wanted =
+ QStringLiteral("1786718040388.1f6b48f1-64d1@mail.example.org");
+ QVERIFY(backed.fixture().addMessage(
+ QStringLiteral("acct/Inbox"), wanted,
+ QStringLiteral("A single message"),
+ QStringLiteral("sender@example.org"),
+ QStringLiteral("Fri, 14 Aug 2026 10:00:00 +0200"),
+ QStringLiteral("The only message.")));
+ QVERIFY2(backed.build(QStringLiteral("acct"), QStringLiteral("acct")),
+ qPrintable(backed.error()));
+
+ MainWindow window(backed.config());
+
+ QLineEdit *queryEdit =
+ window.findChild<QLineEdit *>(QStringLiteral("queryEdit"));
+ QVERIFY2(queryEdit, "no query bar: the window was never built");
+ auto *view = window.findChild<ThreadListView *>();
+ QVERIFY2(view, "no thread list view");
+ auto *model = window.findChild<ThreadListModel *>();
+ QVERIFY2(model, "no thread list model");
+ auto *pane = window.findChild<MessageView *>();
+ QVERIFY2(pane, "no message view");
+
+ auto *accountBox =
+ window.findChild<QComboBox *>(QStringLiteral("accountBox"));
+ QVERIFY2(accountBox, "no account dropdown");
+ const int account = accountBox->findData(QStringLiteral("acct"));
+ QVERIFY2(account >= 0, "the configured account is not in the dropdown");
+ accountBox->setCurrentIndex(account);
+
+ // THE SAME MESSAGE IS READ FIRST, and that is the whole defect. The id is
+ // copied out of the details dialog of a message being read, so the `id:`
+ // query is always typed while that very thread is the current one.
+ // runQuery() blanks the pane but leaves m_currentThreadId naming it, so
+ // when the row comes back onSelectionChanged() compares the two, finds them
+ // equal, and never calls onThreadSelected: nothing is ever loaded.
+ //
+ // A first query returning a DIFFERENT thread passes against the bug, which
+ // is why the earlier version of this test was green.
+ queryEdit->setText(QStringLiteral("tag:inbox"));
+ queryEdit->returnPressed();
+ QTRY_VERIFY_WITH_TIMEOUT(model->rowCount(QModelIndex()) == 1, 15000);
+ view->setCurrentIndex(model->index(0, 0, QModelIndex()));
+ QTRY_VERIFY_WITH_TIMEOUT(!pane->showingPlaceholder(), 15000);
+ const QString firstThreadId = window.currentThreadId();
+ QVERIFY2(!firstThreadId.isEmpty(), "the first view never opened a thread");
+
+ // Exactly what the user types, `id:` and the bare id, unquoted.
+ queryEdit->setText(QStringLiteral("id:%1").arg(wanted));
+ queryEdit->returnPressed();
+ QTRY_VERIFY_WITH_TIMEOUT(model->rowCount(QModelIndex()) == 1, 15000);
+
+ const QModelIndex root = model->index(0, 0, QModelIndex());
+ QVERIFY(root.isValid());
+
+ // The row must name the message before the click can be blamed for
+ // anything: an empty MessageIdRole is a different defect and would make
+ // the assertion below true for the wrong reason.
+ QCOMPARE(model->data(root, ThreadListModel::MessageIdRole).toString(),
+ wanted);
+
+ QVERIFY2(pane->showingPlaceholder(),
+ "the pane was not blank to begin with");
+ // The row IS the thread that was showing when the query ran. Asserted so a
+ // fixture change that made them different threads could not quietly turn
+ // this back into the passing test it was before the cause was found.
+ QCOMPARE(model->data(root, ThreadListModel::ThreadIdRole).toString(),
+ firstThreadId);
+
+ // The pane was blanked, so nothing is on display and the window must not
+ // still claim otherwise. This is the fix's own contract: leave it set and
+ // the selection below is read as "already showing" and never loads.
+ QVERIFY2(window.currentThreadId().isEmpty(),
+ "runQuery blanked the pane but still names a current thread");
+
+ view->setCurrentIndex(root);
+ QTRY_VERIFY_WITH_TIMEOUT(!pane->showingPlaceholder(), 15000);
+}
+
#include "test_mainwindow.moc"