diff options
| author | Danilo M. <danix@danix.xyz> | 2026-09-29 18:02:03 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-09-29 18:02:03 +0200 |
| commit | 04e7db97ae421212614a6ed92807485f743966d3 (patch) | |
| tree | 3a6cf6c1977639c0f3bfdefaa5ce7d209aead0ca /tests | |
| parent | c4a38a5871250b9c952f3eb633ab4cf2ed3d717a (diff) | |
| download | qtmaildir-04e7db97ae421212614a6ed92807485f743966d3.tar.gz qtmaildir-04e7db97ae421212614a6ed92807485f743966d3.zip | |
fix: show the account's startup view for a lone --account
The account dropdown deliberately does not re-run the query: by hand the
user picks a filter next. A launch from another program has no next click,
so `--account work` on its own moved the dropdown and changed nothing the
user could see.
When the account selector applies and no thread or message was given, the
startup view now runs again in the new account. The constructor's startup
path moves into runStartupView() and both callers use it, so the view is
resolved the same way in each: a generated filter is asked for the
account's own query rather than having its all-accounts query wrapped in
the account's path.
The test starts on the trash view because its per-account query is the
account's own trash path; for a tag filter the generated and the wrapped
queries are the same string, and a test on one passed against the wrap.
It asserts on the generated string and on the rows, and fails with the
wrap put back.
anEmptySelectorSetChangesNothing also asserts that no query ran, which the
bar and the dropdown alone cannot show.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/test_mainwindow.cpp | 88 |
1 files changed, 88 insertions, 0 deletions
diff --git a/tests/test_mainwindow.cpp b/tests/test_mainwindow.cpp index b9bd890..e89ad69 100644 --- a/tests/test_mainwindow.cpp +++ b/tests/test_mainwindow.cpp @@ -189,6 +189,8 @@ public: // went unnoticed as broken once already. out << "[general]\n" << "notmuch_config=" << m_fixture.configPath() << "\n"; + if (!m_startupQuery.isEmpty()) + out << "startup_query=" << m_startupQuery << "\n"; if (!accountKey.isEmpty()) { // QSettings reads `/` in a section name as a group separator, // so the section is [account.key], never [account/key]. @@ -239,6 +241,12 @@ public: return true; } + /// Writes `startup_query` into [general]. Call before build(). For a test + /// that needs a startup view whose per-account query is not simply the + /// all-accounts one wrapped in the account's path, which is what tells a + /// generated scope apart from a wrapped one. + void setStartupQuery(const QString &name) { m_startupQuery = name; } + NotmuchFixture &fixture() { return m_fixture; } const Config &config() const { return m_config; } QString error() const { return m_error; } @@ -250,6 +258,7 @@ private: QString m_error; QList<AccountSpec> m_accounts; QString m_composeKey; + QString m_startupQuery; }; /// MainWindow is mostly wiring. Cases that need a real database opt into one @@ -320,6 +329,7 @@ private slots: void aThreadSelectorThatIsNotHexIsRefused(); void anEmptySelectorSetChangesNothing(); void aBracketedMessageSelectorOpensThatMessage(); + void anAccountSelectorAloneShowsThatAccountsStartupView(); void everyBuiltinFilterButtonCarriesAnIconAndItsText(); void theDraftsButtonIsAbsentWithoutADraftsFolder(); void aQueryInTheMenuCanActuallyBeRun(); @@ -18060,11 +18070,16 @@ void TestMainWindow::anEmptySelectorSetChangesNothing() window.findChild<QLineEdit *>(QStringLiteral("queryEdit")); QVERIFY(queryEdit); queryEdit->setText(QStringLiteral("tag:flagged")); + const quint64 generation = window.currentGenerationForTesting(); window.applySelectors(LaunchSelectors()); QCOMPARE(window.selectedAccountForTesting(), QStringLiteral("work")); QCOMPARE(queryEdit->text(), QStringLiteral("tag:flagged")); + // The bar and the dropdown can both survive a query that re-ran them, so + // those two alone pass against a raise that navigates. Every query bumps + // the generation, whatever it runs. + QCOMPARE(window.currentGenerationForTesting(), generation); } void TestMainWindow::aBracketedMessageSelectorOpensThatMessage() @@ -18113,4 +18128,77 @@ void TestMainWindow::aBracketedMessageSelectorOpensThatMessage() 15000); } +/// Two accounts, one inbox message each, for the scope cases below. The two +/// accounts hold DIFFERENT threads, so a query scoped to the wrong one shows a +/// different row rather than accidentally the right one. +static bool buildTwoAccounts(WorkerBackedWindow &backed, + const QString &folder = QStringLiteral("inbox")) +{ + // Friday and Saturday, verified with `date -d 2026-08-14 +%A` and + // `date -d 2026-08-15 +%A`: Qt::RFC2822Date validates the weekday. + if (!backed.fixture().addMessage( + QStringLiteral("work/") + folder, QStringLiteral("w1@example.org"), + QStringLiteral("Work subject"), QStringLiteral("a@example.org"), + QStringLiteral("Fri, 14 Aug 2026 10:00:00 +0200"), + QStringLiteral("Work body."))) + return false; + if (!backed.fixture().addMessage( + QStringLiteral("personal/") + folder, QStringLiteral("p1@example.org"), + QStringLiteral("Personal subject"), QStringLiteral("b@example.org"), + QStringLiteral("Sat, 15 Aug 2026 10:00:00 +0200"), + QStringLiteral("Personal body."))) + return false; + + WorkerBackedWindow::AccountSpec work; + work.key = QStringLiteral("work"); + work.maildir = QStringLiteral("work"); + work.trash = QStringLiteral("Trash"); + WorkerBackedWindow::AccountSpec personal; + personal.key = QStringLiteral("personal"); + personal.maildir = QStringLiteral("personal"); + personal.trash = QStringLiteral("Trash"); + return backed.buildWithAccounts({ work, personal }); +} + +void TestMainWindow::anAccountSelectorAloneShowsThatAccountsStartupView() +{ + // --account on its own moved the dropdown and nothing else, because the + // dropdown deliberately does not re-run the query: by hand the user picks + // a filter next. From another program there is no next click, so the + // launch changed nothing the user could see. + // + // The startup view is the TRASH, because its per-account query is the + // account's own trash path. Most filters are a tag, and for a tag the + // generated query and the wrapped one are the same string, so a test on + // one of those passes against the wrap it exists to rule out. + WorkerBackedWindow backed; + backed.setStartupQuery(QStringLiteral("trash")); + QVERIFY2(buildTwoAccounts(backed, QStringLiteral("Trash")), + qPrintable(backed.error())); + + MainWindow window(backed.config()); + auto *model = window.findChild<ThreadListModel *>(); + QVERIFY(model); + + // The startup view over All accounts shows both, so the assertion below + // measures a change rather than a view that was already one row. + QTRY_COMPARE_WITH_TIMEOUT(model->rowCount(QModelIndex()), 2, 15000); + + LaunchSelectors selectors; + selectors.account = QStringLiteral("work"); + window.applySelectors(selectors); + + // The GENERATED string for the work account, never the all-accounts query + // wrapped in the work scope: that wrap returns the right rows too, which + // is why a row count alone cannot tell the two apart. + const Config &config = backed.config(); + const QString expected = + config.resolvedQuery(config.startupSavedQuery(), QStringLiteral("work")); + QVERIFY(!expected.isEmpty()); + QCOMPARE(window.lastRunQueryForTesting(), expected); + + QTRY_COMPARE_WITH_TIMEOUT(model->rowCount(QModelIndex()), 1, 15000); + QCOMPARE(model->threadAt(0).subject, QStringLiteral("Work subject")); +} + #include "test_mainwindow.moc" |
