aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/test_config.cpp69
-rw-r--r--tests/test_mainwindow.cpp359
2 files changed, 214 insertions, 214 deletions
diff --git a/tests/test_config.cpp b/tests/test_config.cpp
index 4d56ec1..a5dce9a 100644
--- a/tests/test_config.cpp
+++ b/tests/test_config.cpp
@@ -57,7 +57,7 @@ private slots:
void unknownFieldsSurviveARoundTrip();
void savedQueriesRoundTripUnchanged();
void migrationWritesJsonAndLeavesTheIniByteIdentical();
- void migrationPinsEveryEntry();
+ void migrationKeepsEveryEntry();
void jsonWinsOnceItExists();
void malformedQueriesFileIsAProblemNotACrash();
void futureVersionIsRefusedAndReported();
@@ -96,7 +96,7 @@ private slots:
void allSentQueryIsEmptyWhenNoAccountHasOne();
void allSentQuerySkipsAccountsWithoutTheKey();
void allSentQueryJoinsEveryConfiguredAccount();
- void aStoredGeneratedQueryIsUnpinnedNotDropped();
+ void aStoredGeneratedQueryIsKeptNotDropped();
void theStartupAccountIsReadAndValidated();
void theStartupAccountTakesTheKeyNotTheSyncChannel();
void theStartupQueryCanNameABuiltinFilter();
@@ -1818,12 +1818,7 @@ void TestConfig::savedQueryFieldsAreRead()
QCOMPARE(queries.at(0).name, QStringLiteral("Inbox"));
QCOMPARE(queries.at(0).query, QStringLiteral("tag:inbox"));
- QVERIFY(queries.at(0).pinned);
QVERIFY(queries.at(0).account.isEmpty());
-
- // pinned defaults to false, which is what puts a query in the menu rather
- // than on the row.
- QVERIFY(!queries.at(1).pinned);
QCOMPARE(queries.at(1).account, QStringLiteral("work"));
}
@@ -1883,7 +1878,6 @@ void TestConfig::savedQueriesRoundTripUnchanged()
for (int i = 0; i < a.size(); ++i) {
QCOMPARE(b.at(i).name, a.at(i).name);
QCOMPARE(b.at(i).query, a.at(i).query);
- QCOMPARE(b.at(i).pinned, a.at(i).pinned);
QCOMPARE(b.at(i).account, a.at(i).account);
}
}
@@ -1929,9 +1923,11 @@ void TestConfig::migrationWritesJsonAndLeavesTheIniByteIdentical()
QVERIFY(afterBytes.contains("; a comment the user wrote"));
}
-/// A migrated query that was not pinned would vanish from the query row, which
-/// on the first launch after an upgrade looks like data loss.
-void TestConfig::migrationPinsEveryEntry()
+/// Every [queries] entry must survive the migration with its query intact. It
+/// used to also assert they came across PINNED, which item 94 retired along
+/// with the flag; losing an entry is the data loss that assertion was really
+/// guarding against, so that half is kept.
+void TestConfig::migrationKeepsEveryEntry()
{
QTemporaryDir dir;
const QString path = writeIni(dir, QStringLiteral(
@@ -1945,9 +1941,14 @@ void TestConfig::migrationPinsEveryEntry()
const QList<SavedQuery> queries = config.savedQueries();
QCOMPARE(queries.size(), 2);
+
+ QMap<QString, QString> byName;
for (const SavedQuery &query : queries)
- QVERIFY2(query.pinned, qPrintable(
- QStringLiteral("migrated query '%1' is not pinned").arg(query.name)));
+ byName.insert(query.name, query.query);
+ QCOMPARE(byName.value(QStringLiteral("Inbox")),
+ QStringLiteral("tag:inbox"));
+ QCOMPARE(byName.value(QStringLiteral("Unread")),
+ QStringLiteral("tag:unread"));
}
/// Once the JSON exists, [queries] is dead. Two sources of truth was the
@@ -1972,17 +1973,19 @@ void TestConfig::jsonWinsOnceItExists()
QCOMPARE(queries.at(0).name, QStringLiteral("FromTheJson"));
}
-void TestConfig::aStoredGeneratedQueryIsUnpinnedNotDropped()
+void TestConfig::aStoredGeneratedQueryIsKeptNotDropped()
{
// An existing install carries a Sent entry in queries.json: 0.19.0 migrated
// the hardcoded button into one. Item 93 ships Sent as a built-in filter,
- // so that stored entry is now a DUPLICATE and would put two Sent buttons on
- // the row, one editable and one not.
+ // so that stored entry is now a DUPLICATE.
//
- // Unpinned rather than deleted. This file's whole design is that a reader
- // preserves what it does not own, and the user's instruction for their own
- // redundant queries was the same: fold them into the menu, do not drop
- // them. An unpin is reversible from the UI; a delete is not.
+ // It used to be UNPINNED to keep it off the row beside the built-in of the
+ // same name. Item 94 removed the row for saved queries entirely, so the
+ // duplicate can no longer collide with anything and there is nothing to
+ // unpin. What must still hold is that it is KEPT: this file's whole design
+ // is that a reader preserves what it does not own, and the user's
+ // instruction for their own redundant queries was to fold them into the
+ // menu rather than drop them.
QTemporaryDir dir;
const QString path = writeIni(dir, QStringLiteral(
"[account.work]\n"
@@ -1991,8 +1994,8 @@ void TestConfig::aStoredGeneratedQueryIsUnpinnedNotDropped()
writeQueries(dir, QStringLiteral(R"({
"version": 1,
"queries": [
- { "name": "Sent", "generated": "sent", "pinned": true },
- { "name": "Mine", "query": "tag:todo", "pinned": true }
+ { "name": "Sent", "generated": "sent" },
+ { "name": "Mine", "query": "tag:todo" }
]
})"));
@@ -2004,21 +2007,21 @@ void TestConfig::aStoredGeneratedQueryIsUnpinnedNotDropped()
bool sawSent = false;
for (const SavedQuery &query : queries) {
- if (query.generated != QStringLiteral("sent"))
- continue;
- sawSent = true;
- QVERIFY2(!query.pinned,
- "the stored Sent entry is still a button beside the built-in "
- "filter of the same name");
+ if (query.generated == QStringLiteral("sent"))
+ sawSent = true;
}
- QVERIFY2(sawSent, "the stored Sent entry was DROPPED rather than unpinned");
+ QVERIFY2(sawSent, "the stored Sent entry was DROPPED rather than kept");
- // The user's own query is untouched: only the entry duplicating a built-in
- // filter is unpinned.
+ // And the user's own query beside it, so the assertion above is not passing
+ // on a reader that kept everything by doing nothing at all.
+ bool sawMine = false;
for (const SavedQuery &query : queries) {
- if (query.name == QStringLiteral("Mine"))
- QVERIFY2(query.pinned, "an unrelated pinned query was unpinned");
+ if (query.name == QStringLiteral("Mine")) {
+ sawMine = true;
+ QCOMPARE(query.query, QStringLiteral("tag:todo"));
+ }
}
+ QVERIFY2(sawMine, "an unrelated saved query was lost");
}
void TestConfig::theStartupQueryPrefersASavedQueryOverAFilterOfTheSameName()
diff --git a/tests/test_mainwindow.cpp b/tests/test_mainwindow.cpp
index c5e88db..f935cac 100644
--- a/tests/test_mainwindow.cpp
+++ b/tests/test_mainwindow.cpp
@@ -434,22 +434,20 @@ private slots:
void theMessageBarSitsAboveTheBodyAndBelowTheHeader();
void theMessageBarIconsAreSmallerThanTheToolbars();
- void onlyPinnedQueriesBecomeButtons();
- void unpinnedQueriesReachTheMenu();
- void pinnedButtonsFollowTheDocumentOrder();
- void theSavedQueryMenuIsHiddenWhenEveryQueryIsPinned();
+ void everySavedQueryLivesInTheMenu();
+ void menuEntriesFollowTheDocumentOrder();
void aScopedSavedQuerySelectsItsAccount();
void anUnscopedSavedQueryClearsTheAccount();
void theSaveQueryActionIsDisabledOnAnEmptyQuery();
void thereIsASaveButtonBesideTheQueryBar();
void theMenuIsRightAlignedAwayFromTheButtons();
- void theRowSurvivesWithNothingButUnpinnedQueries();
+ void theRowSurvivesWithNothingButAMenu();
void aStoredGeneratedQueryRunsFlatAndComposed();
void aRenamedSentEntryKeepsWorking();
void aGeneratedQueryWithNothingToShowIsSkipped();
- void aSavedQueryButtonOffersEditUnpinAndDelete();
+ void aSavedQueryEntryOffersRunEditAndDelete();
void onlyAStoredQueryOffersToBecomeATaggingRule();
- void unpinningMovesAQueryToTheMenu();
+ void savingStripsTheRetiredPinnedField();
void deletingRemovesTheQueryFromTheFile();
void anEditedQueryKeepsItsUnknownFields();
void renamingReplacesRatherThanDuplicating();
@@ -7637,46 +7635,44 @@ static QStringList savedQueryButtonLabels(MainWindow &window)
/// Positional lookup does not work any more: the built-in filters occupy the
/// first four places on the row, so row->findChild<QPushButton *>() returns
/// Unread rather than the query a test means.
-static QPushButton *savedQueryButton(MainWindow &window, const QString &label)
+/// The submenu carrying one saved query's actions, which since item 94 is where
+/// every saved query lives: the row is built-in filters only, so there is no
+/// button to right-click and the entry's own submenu is the whole affordance.
+static QMenu *savedQueryEntryMenu(MainWindow &window, const QString &label)
{
- auto *row = window.findChild<QWidget *>(QStringLiteral("savedQueryRow"));
- if (!row)
+ auto *menuButton =
+ window.findChild<QPushButton *>(QStringLiteral("savedQueryMenuButton"));
+ if (!menuButton || !menuButton->menu())
return nullptr;
- const QList<QPushButton *> buttons =
- row->findChildren<QPushButton *>(QString(), Qt::FindDirectChildrenOnly);
- for (QPushButton *button : buttons) {
- if (isBuiltinFilterButton(button))
- continue;
- if (button->text() == label)
- return button;
+ for (QAction *action : menuButton->menu()->actions()) {
+ if (action->text() == label)
+ return action->menu();
}
return nullptr;
}
-void TestMainWindow::onlyPinnedQueriesBecomeButtons()
+/// One action from a saved query's submenu, by object name. The counterpart of
+/// contextActionNamed() for the menu, which holds its actions directly rather
+/// than as a context menu on a widget.
+static QAction *savedQueryActionNamed(MainWindow &window, const QString &label,
+ const QString &objectName)
{
- QTemporaryDir dir;
- QVERIFY(dir.isValid());
- Config config;
- loadWithQueries(config, dir, QStringLiteral(R"({
- "version": 1,
- "queries": [
- { "name": "Inbox", "query": "tag:inbox", "pinned": true },
- { "name": "Buried", "query": "tag:buried" }
- ]
- })"));
-
- MainWindow window(config);
- const QStringList labels = savedQueryButtonLabels(window);
-
- QVERIFY2(labels.contains(QStringLiteral("Inbox")),
- "a pinned query must have a button");
- QVERIFY2(!labels.contains(QStringLiteral("Buried")),
- "an unpinned query must NOT have a button");
+ QMenu *menu = savedQueryEntryMenu(window, label);
+ if (!menu)
+ return nullptr;
+ for (QAction *action : menu->actions()) {
+ if (action->objectName() == objectName)
+ return action;
+ }
+ return nullptr;
}
-void TestMainWindow::unpinnedQueriesReachTheMenu()
+void TestMainWindow::everySavedQueryLivesInTheMenu()
{
+ // Item 94. The row is built-in filters ONLY, and every saved query is in
+ // the menu whatever queries.json says: `pinned` is no longer read, so a
+ // file still carrying it from before the removal must not put a button
+ // back on the row.
QTemporaryDir dir;
QVERIFY(dir.isValid());
Config config;
@@ -7689,48 +7685,35 @@ void TestMainWindow::unpinnedQueriesReachTheMenu()
})"));
MainWindow window(config);
+
+ // No saved query is a button any more, INCLUDING the one the file pins.
+ // That entry is the whole point of the fixture: a test with nothing
+ // pinned passes against `pinned` still being honoured.
+ const QStringList labels = savedQueryButtonLabels(window);
+ QVERIFY2(!labels.contains(QStringLiteral("Inbox")),
+ "a saved query marked pinned in the file still became a button");
+ QVERIFY2(!labels.contains(QStringLiteral("Buried")),
+ "a saved query became a button");
+
auto *menuButton =
window.findChild<QPushButton *>(QStringLiteral("savedQueryMenuButton"));
- QVERIFY2(menuButton, "an unpinned query needs a menu to live in");
+ QVERIFY2(menuButton, "the saved queries have no menu to live in");
QVERIFY(menuButton->menu());
QStringList entries;
- const QList<QAction *> actions = menuButton->menu()->actions();
- for (QAction *action : actions)
+ for (QAction *action : menuButton->menu()->actions())
entries.append(action->text());
+ QVERIFY2(entries.contains(QStringLiteral("Inbox")),
+ "the formerly pinned query is missing from the menu");
QVERIFY2(entries.contains(QStringLiteral("Buried")),
"the unpinned query is missing from the menu");
- // A pinned query is already a button; listing it twice is the duplicate
- // this asserts against.
- QVERIFY2(!entries.contains(QStringLiteral("Inbox")),
- "a pinned query must not also appear in the menu");
-}
-
-/// The property the whole storage change was made for. "Zebra" is written
-/// first and must stay first; alphabetical order would put it last.
-void TestMainWindow::pinnedButtonsFollowTheDocumentOrder()
-{
- QTemporaryDir dir;
- QVERIFY(dir.isValid());
- Config config;
- loadWithQueries(config, dir, QStringLiteral(R"({
- "version": 1,
- "queries": [
- { "name": "Zebra", "query": "tag:zebra", "pinned": true },
- { "name": "Apple", "query": "tag:apple", "pinned": true }
- ]
- })"));
-
- MainWindow window(config);
- const QStringList labels = savedQueryButtonLabels(window);
-
- QCOMPARE(labels.size(), 2);
- QCOMPARE(labels.at(0), QStringLiteral("Zebra"));
- QCOMPARE(labels.at(1), QStringLiteral("Apple"));
}
-void TestMainWindow::theSavedQueryMenuIsHiddenWhenEveryQueryIsPinned()
+/// Document order, not alphabetical: "Zebra" is written first and must stay
+/// first. The property the storage change was made for, kept from the deleted
+/// pinnedButtonsFollowTheDocumentOrder now that the menu is the only home.
+void TestMainWindow::menuEntriesFollowTheDocumentOrder()
{
QTemporaryDir dir;
QVERIFY(dir.isValid());
@@ -7738,23 +7721,23 @@ void TestMainWindow::theSavedQueryMenuIsHiddenWhenEveryQueryIsPinned()
loadWithQueries(config, dir, QStringLiteral(R"({
"version": 1,
"queries": [
- { "name": "Inbox", "query": "tag:inbox", "pinned": true }
+ { "name": "Zebra", "query": "tag:zebra" },
+ { "name": "Apple", "query": "tag:apple" }
]
})"));
MainWindow window(config);
-
- // The guard the assertion below needs. Asserting only that the menu button
- // is absent passed against NO implementation at all, before any of this
- // was built, so it has to prove first that the row it is looking in was
- // populated and that a button was found.
- const QStringList labels = savedQueryButtonLabels(window);
- QCOMPARE(labels, QStringList{ QStringLiteral("Inbox") });
-
auto *menuButton =
window.findChild<QPushButton *>(QStringLiteral("savedQueryMenuButton"));
- QVERIFY2(!menuButton,
- "an empty menu button is a control that always does nothing");
+ QVERIFY(menuButton && menuButton->menu());
+
+ QStringList entries;
+ for (QAction *action : menuButton->menu()->actions())
+ entries.append(action->text());
+
+ QCOMPARE(entries.size(), 2);
+ QCOMPARE(entries.at(0), QStringLiteral("Zebra"));
+ QCOMPARE(entries.at(1), QStringLiteral("Apple"));
}
/// The scope goes through the account dropdown rather than being baked into
@@ -7796,11 +7779,12 @@ void TestMainWindow::aScopedSavedQuerySelectsItsAccount()
QStringLiteral("personal")));
QCOMPARE(accountBox->currentData().toString(), QStringLiteral("personal"));
- auto *row = window.findChild<QWidget *>(QStringLiteral("savedQueryRow"));
- QVERIFY(row);
- auto *button = savedQueryButton(window, QStringLiteral("Billing"));
- QVERIFY(button);
- button->click();
+ // Through the menu's Run entry: since item 94 a saved query is never a
+ // button, and Qt emits no triggered for the entry that owns the submenu.
+ QAction *run = savedQueryActionNamed(window, QStringLiteral("Billing"),
+ QStringLiteral("runQuery"));
+ QVERIFY(run);
+ run->trigger();
QCOMPARE(accountBox->currentData().toString(), QStringLiteral("work"));
// The text is the bare query. The path scope is applied once, by
@@ -7840,9 +7824,10 @@ void TestMainWindow::anUnscopedSavedQueryClearsTheAccount()
auto *row = window.findChild<QWidget *>(QStringLiteral("savedQueryRow"));
QVERIFY(row);
- auto *button = savedQueryButton(window, QStringLiteral("Everywhere"));
- QVERIFY(button);
- button->click();
+ QAction *run = savedQueryActionNamed(window, QStringLiteral("Everywhere"),
+ QStringLiteral("runQuery"));
+ QVERIFY(run);
+ run->trigger();
QVERIFY2(accountBox->currentData().toString().isEmpty(),
"an unscoped saved query must clear the account selection");
@@ -7970,9 +7955,9 @@ void TestMainWindow::theMenuIsRightAlignedAwayFromTheButtons()
"the menu must come AFTER the stretch to sit at the right edge");
}
-/// The row must not vanish when every saved query is unpinned: the menu is
-/// then the only way to reach any of them, and hiding the row buries it.
-void TestMainWindow::theRowSurvivesWithNothingButUnpinnedQueries()
+/// The row must not vanish when the only content is the menu, which since item
+/// 94 is where every saved query lives: hiding the row buries all of them.
+void TestMainWindow::theRowSurvivesWithNothingButAMenu()
{
QTemporaryDir dir;
QVERIFY(dir.isValid());
@@ -8077,9 +8062,8 @@ void TestMainWindow::aRenamedSentEntryKeepsWorking()
found = true;
QVERIFY2(saved.isGenerated(),
"the entry lost its generator when renamed");
- QVERIFY2(!saved.pinned, "the entry was not unpinned");
}
- QVERIFY2(found, "the renamed entry was DROPPED rather than unpinned");
+ QVERIFY2(found, "the renamed entry was DROPPED rather than kept");
// The built-in Sent still resolves the same query, so nothing the user
// could reach before became unreachable.
@@ -8115,12 +8099,12 @@ void TestMainWindow::aGeneratedQueryWithNothingToShowIsSkipped()
MainWindow window(config);
- // The guard: the row was built and the other entry did get a button, so a
- // missing Sent means it was skipped rather than that nothing was built.
- QCOMPARE(savedQueryButtonLabels(window),
- QStringList{ QStringLiteral("Inbox") });
- QVERIFY2(!window.findChild<QAbstractButton *>(QStringLiteral("sentButton")),
- "a generated query with nothing to show must not get a button");
+ // The guard: the menu was built and the other entry is in it, so a missing
+ // Sent means it was skipped rather than that nothing was built at all.
+ QVERIFY2(savedQueryEntryMenu(window, QStringLiteral("Inbox")),
+ "the menu was not built, so the assertion below proves nothing");
+ QVERIFY2(!savedQueryEntryMenu(window, QStringLiteral("Sent")),
+ "a generated query with nothing to show must not get an entry");
}
/// Reads queries.json back from disk, which is what "it was saved" means.
@@ -8145,29 +8129,38 @@ static QAction *contextActionNamed(MainWindow &window, QWidget *target,
return nullptr;
}
-void TestMainWindow::aSavedQueryButtonOffersEditUnpinAndDelete()
+void TestMainWindow::aSavedQueryEntryOffersRunEditAndDelete()
{
+ // Item 94 removed the pin action along with the row, so the entry offers
+ // three things rather than four. Run is among them and is load-bearing:
+ // Qt emits no triggered for the action owning the submenu, so without an
+ // item INSIDE it the query could not be run at all.
QTemporaryDir dir;
QVERIFY(dir.isValid());
Config config;
loadWithQueries(config, dir, QStringLiteral(R"({
"version": 1,
"queries": [
- { "name": "Inbox", "query": "tag:inbox", "pinned": true }
+ { "name": "Inbox", "query": "tag:inbox" }
]
})"));
MainWindow window(config);
- auto *row = window.findChild<QWidget *>(QStringLiteral("savedQueryRow"));
- QVERIFY(row);
- auto *button = savedQueryButton(window, QStringLiteral("Inbox"));
- QVERIFY(button);
+ QVERIFY2(savedQueryEntryMenu(window, QStringLiteral("Inbox")),
+ "the saved query has no menu entry to carry its actions");
- // A context menu, so the actions live on the widget itself.
- QCOMPARE(button->contextMenuPolicy(), Qt::ActionsContextMenu);
- QVERIFY(contextActionNamed(window, button, QStringLiteral("editQuery")));
- QVERIFY(contextActionNamed(window, button, QStringLiteral("pinQuery")));
- QVERIFY(contextActionNamed(window, button, QStringLiteral("deleteQuery")));
+ QVERIFY(savedQueryActionNamed(window, QStringLiteral("Inbox"),
+ QStringLiteral("runQuery")));
+ QVERIFY(savedQueryActionNamed(window, QStringLiteral("Inbox"),
+ QStringLiteral("editQuery")));
+ QVERIFY(savedQueryActionNamed(window, QStringLiteral("Inbox"),
+ QStringLiteral("deleteQuery")));
+
+ // The retired affordance, asserted absent rather than merely unused: item
+ // 94 removed pinning, and an action left behind would still be clickable.
+ QVERIFY2(!savedQueryActionNamed(window, QStringLiteral("Inbox"),
+ QStringLiteral("pinQuery")),
+ "the retired pin action is still offered");
}
void TestMainWindow::onlyAStoredQueryOffersToBecomeATaggingRule()
@@ -8200,41 +8193,41 @@ void TestMainWindow::onlyAStoredQueryOffersToBecomeATaggingRule()
"sent=work-mail/Sent\n"));
MainWindow window(config);
- auto *row = window.findChild<QWidget *>(QStringLiteral("savedQueryRow"));
- QVERIFY(row);
-
- // By object name, which rebuildSavedQueryRow assigns precisely so a test
- // need not depend on a label the user can rename.
- auto *generated = row->findChild<QAbstractButton *>(
- QStringLiteral("sentButton"));
-
- // savedQueryButton(), not a scan for the label: item 93 puts a BUILT-IN
- // Inbox filter on the row too, and it carries no context actions by design,
- // so a scan finds that one and the assertion below fails against correct
- // code.
- QPushButton *stored = savedQueryButton(window, QStringLiteral("Inbox"));
- QVERIFY2(stored, "no button was built for the stored query");
- QVERIFY2(generated, "no button was built for the generated query");
+ // Both are menu entries since item 94, so the comparison is between two
+ // entries rather than between a button and a built-in filter. The stored
+ // one is found by name, which the user can rename, but nothing else
+ // distinguishes an entry in the menu.
+ QVERIFY2(savedQueryEntryMenu(window, QStringLiteral("Inbox")),
+ "no menu entry was built for the stored query");
+ QVERIFY2(savedQueryEntryMenu(window, QStringLiteral("Sent")),
+ "no menu entry was built for the generated query");
- QVERIFY2(contextActionNamed(window, stored, QStringLiteral("queryToRule")),
+ QVERIFY2(savedQueryActionNamed(window, QStringLiteral("Inbox"),
+ QStringLiteral("queryToRule")),
"a stored query must offer Create tagging rule");
- QVERIFY2(!contextActionNamed(window, generated,
- QStringLiteral("queryToRule")),
+ QVERIFY2(!savedQueryActionNamed(window, QStringLiteral("Sent"),
+ QStringLiteral("queryToRule")),
"a generated query must not: its query is a snapshot");
- // The guard, and it has moved since item 93. It used to prove the generated
- // button HAS a menu, so the assertion above was about one action rather
- // than about a button with none. `generated` is now the BUILT-IN Sent
- // filter, which correctly carries no actions at all, so proving the
- // machinery works has to happen on the button that does have them.
- QVERIFY2(contextActionNamed(window, stored, QStringLiteral("deleteQuery")),
- "the stored button lost its other actions, so the assertion above "
- "is not about queryToRule in particular");
+ // The guard: without it, an entry carrying NO actions at all would pass the
+ // assertion above. Asserted on the generated entry, since that is the one
+ // the absence is claimed of.
+ QVERIFY2(savedQueryActionNamed(window, QStringLiteral("Sent"),
+ QStringLiteral("deleteQuery")),
+ "the generated entry lost its other actions, so the assertion "
+ "above is not about queryToRule in particular");
}
-void TestMainWindow::unpinningMovesAQueryToTheMenu()
+void TestMainWindow::savingStripsTheRetiredPinnedField()
{
+ // Item 94, and the half that is NOT just deleting UI: the user chose to
+ // strip `pinned` rather than leave it ignored in the file. A key that is
+ // no longer read but still written back is the shape that makes a later
+ // reader disagree with this one.
+ //
+ // The fixture pins BOTH entries, so the assertion cannot pass by the field
+ // having been absent all along.
QTemporaryDir dir;
QVERIFY(dir.isValid());
Config config;
@@ -8247,32 +8240,38 @@ void TestMainWindow::unpinningMovesAQueryToTheMenu()
})"));
MainWindow window(config);
- auto *row = window.findChild<QWidget *>(QStringLiteral("savedQueryRow"));
- QVERIFY(row);
- QCOMPARE(savedQueryButtonLabels(window).size(), 2);
- QVERIFY(!window.findChild<QAbstractButton *>(
- QStringLiteral("savedQueryMenuButton")));
- auto *button = savedQueryButton(window, QStringLiteral("Inbox"));
- QVERIFY(button);
- QAction *pin = contextActionNamed(window, button, QStringLiteral("pinQuery"));
- QVERIFY(pin);
- pin->trigger();
+ // Neither is a button any more, which is what makes the file the only
+ // place the retired field could still be observed.
+ QVERIFY(savedQueryButtonLabels(window).isEmpty());
- // Off the row, into the menu, and written to the file: an unpin that only
- // redrew would come back pinned on the next launch.
- QCOMPARE(savedQueryButtonLabels(window), QStringList{ QStringLiteral("Other") });
- auto *menuButton =
- window.findChild<QPushButton *>(QStringLiteral("savedQueryMenuButton"));
- QVERIFY(menuButton);
- QCOMPARE(menuButton->menu()->actions().size(), 1);
+ // Any save rewrites the whole array, so editing one entry puts BOTH through
+ // the writer, including the one that was not touched. Through the testing
+ // seam rather than the Delete action, which raises a confirmation modal
+ // with nothing to dismiss it under the offscreen platform.
+ SavedQuery edited;
+ edited.name = QStringLiteral("Inbox");
+ edited.query = QStringLiteral("tag:inbox and not tag:muted");
+ window.replaceSavedQueryForTesting(QStringLiteral("Inbox"), edited);
const QJsonArray stored = storedQueries(dir);
QCOMPARE(stored.size(), 2);
- QCOMPARE(stored.at(0).toObject().value(QStringLiteral("name")).toString(),
- QStringLiteral("Inbox"));
- QVERIFY2(!stored.at(0).toObject().contains(QStringLiteral("pinned")),
- "the unpin did not reach the file");
+ for (const QJsonValue &value : stored) {
+ const QJsonObject entry = value.toObject();
+ QVERIFY2(!entry.contains(QStringLiteral("pinned")),
+ qPrintable(QStringLiteral("the retired pinned field was "
+ "written back for '%1'")
+ .arg(entry.value(QStringLiteral("name"))
+ .toString())));
+ }
+
+ // The untouched entry is otherwise intact: a strip that took the query with
+ // it would pass the loop above and lose the user's data.
+ const QJsonObject other = stored.at(1).toObject();
+ QCOMPARE(other.value(QStringLiteral("name")).toString(),
+ QStringLiteral("Other"));
+ QCOMPARE(other.value(QStringLiteral("query")).toString(),
+ QStringLiteral("tag:other"));
}
void TestMainWindow::deletingRemovesTheQueryFromTheFile()
@@ -8289,22 +8288,21 @@ void TestMainWindow::deletingRemovesTheQueryFromTheFile()
})"));
MainWindow window(config);
- auto *row = window.findChild<QWidget *>(QStringLiteral("savedQueryRow"));
- QVERIFY(row);
- auto *button = savedQueryButton(window, QStringLiteral("Doomed"));
- QVERIFY(button);
- QCOMPARE(button->text(), QStringLiteral("Doomed"));
+ QVERIFY2(savedQueryEntryMenu(window, QStringLiteral("Doomed")),
+ "the query to be deleted is not in the menu");
- QAction *del =
- contextActionNamed(window, button, QStringLiteral("deleteQuery"));
+ QAction *del = savedQueryActionNamed(window, QStringLiteral("Doomed"),
+ QStringLiteral("deleteQuery"));
QVERIFY(del);
// Destructive and not on the undo stack, so it confirms. Suppressed here
// rather than driven through the modal dialog, which would hang the test.
window.setConfirmDeleteForTesting(false);
del->trigger();
- QCOMPARE(savedQueryButtonLabels(window),
- QStringList{ QStringLiteral("Keeper") });
+ QVERIFY2(!savedQueryEntryMenu(window, QStringLiteral("Doomed")),
+ "the deleted query is still in the menu");
+ QVERIFY2(savedQueryEntryMenu(window, QStringLiteral("Keeper")),
+ "the surviving query was removed too");
const QJsonArray stored = storedQueries(dir);
QCOMPARE(stored.size(), 1);
@@ -8337,7 +8335,6 @@ void TestMainWindow::anEditedQueryKeepsItsUnknownFields()
SavedQuery edited;
edited.name = QStringLiteral("Inbox");
edited.query = QStringLiteral("tag:inbox and not tag:muted");
- edited.pinned = true;
QVERIFY(edited.unknown.isEmpty());
window.replaceSavedQueryForTesting(QStringLiteral("Inbox"), edited);
@@ -8371,14 +8368,19 @@ void TestMainWindow::renamingReplacesRatherThanDuplicating()
SavedQuery renamed;
renamed.name = QStringLiteral("New");
renamed.query = QStringLiteral("tag:old");
- renamed.pinned = true;
window.replaceSavedQueryForTesting(QStringLiteral("Old"), renamed);
const QJsonArray stored = storedQueries(dir);
QCOMPARE(stored.size(), 1);
QCOMPARE(stored.at(0).toObject().value(QStringLiteral("name")).toString(),
QStringLiteral("New"));
- QCOMPARE(savedQueryButtonLabels(window), QStringList{ QStringLiteral("New") });
+
+ // And the menu followed the rename rather than keeping both, which is the
+ // duplication this asserts against.
+ QVERIFY2(savedQueryEntryMenu(window, QStringLiteral("New")),
+ "the renamed query is missing from the menu");
+ QVERIFY2(!savedQueryEntryMenu(window, QStringLiteral("Old")),
+ "the old name is still in the menu, so the rename duplicated");
}
void TestMainWindow::aMalformedAccountIsReportedWithoutBlockingTheConstructor()
@@ -9999,19 +10001,14 @@ void TestMainWindow::aSavedQueryStillClearsTheAccountSelection()
MainWindow window(config);
window.selectAccountForTesting(QStringLiteral("work"));
- // The migrated [queries] entry is pinned, so it is a button beside the
- // filters. Found by its label, since only the filters have stable object
- // names.
- QPushButton *mine = nullptr;
- auto *row = window.findChild<QWidget *>(QStringLiteral("savedQueryRow"));
- QVERIFY(row);
- for (QPushButton *button : row->findChildren<QPushButton *>()) {
- if (button->text() == QStringLiteral("Mine"))
- mine = button;
- }
- QVERIFY2(mine, "the user's own pinned query is not on the row");
+ // The migrated [queries] entry lives in the menu since item 94, like every
+ // saved query. Run rather than the entry itself: Qt emits no triggered for
+ // an action that owns a submenu.
+ QAction *run = savedQueryActionNamed(window, QStringLiteral("Mine"),
+ QStringLiteral("runQuery"));
+ QVERIFY2(run, "the user's own query is not in the menu");
- mine->click();
+ run->trigger();
QVERIFY2(window.selectedAccountForTesting().isEmpty(),
"an unscoped saved query no longer clears the account selection");
}