aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/tagrulesdialog.cpp39
-rw-r--r--src/tagrulesdialog.h7
-rw-r--r--tests/test_tagrules.cpp50
3 files changed, 92 insertions, 4 deletions
diff --git a/src/tagrulesdialog.cpp b/src/tagrulesdialog.cpp
index 1d31d07..86566ba 100644
--- a/src/tagrulesdialog.cpp
+++ b/src/tagrulesdialog.cpp
@@ -465,6 +465,26 @@ void TagRulesDialog::setRowValueForTest(int index, const QString &value)
syncQueryLine();
}
+void TagRulesDialog::setQueryTextForTest(const QString &text)
+{
+ m_query->setText(text);
+}
+
+void TagRulesDialog::setTextModeForTest(bool on)
+{
+ m_textMode->setChecked(on);
+}
+
+QString TagRulesDialog::warningTextForTest() const
+{
+ // isVisible() is false for every child of a dialog that was never shown,
+ // so it would report no warning whatever the label held. isVisibleTo()
+ // answers the question actually being asked: would this be on screen if
+ // the dialog were.
+ return m_warningLabel->isVisibleTo(this) ? m_warningLabel->text()
+ : QString();
+}
+
void TagRulesDialog::selectRuleForTest(int index)
{
if (index >= 0 && index < m_list->topLevelItemCount())
@@ -634,14 +654,19 @@ void TagRulesDialog::setTextMode(bool on)
// Going back needs the typed query to be representable. If it is not, the
// checkbox cannot clear: there are no rows that mean this query.
+ //
+ // Said in the warning label rather than a modal. A modal here would block
+ // any test that reaches this branch, which is how a refusal path ends up
+ // shipping unverified, and it interrupts someone who is mid-edit to tell
+ // them something the label can hold while they keep typing.
const RuleQuery parsed = RuleQuery::parse(m_query->text().trimmed());
if (!parsed.parsed) {
const QSignalBlocker block(m_textMode);
m_textMode->setChecked(true);
- QMessageBox::information(
- this, tr("Cannot show as rows"),
- tr("This query is more than the builder can show, so it stays "
- "as text. It is still saved and applied normally."));
+ m_warningLabel->setText(
+ tr("This query is more than the builder can show, so it stays as "
+ "text. It is still saved and applied normally."));
+ m_warningLabel->setVisible(true);
return;
}
@@ -653,6 +678,12 @@ void TagRulesDialog::setTextMode(bool on)
m_loadedQuery = parsed;
m_builder->setVisible(true);
m_query->setReadOnly(true);
+
+ // The refusal above writes into the same label the load warnings use, so
+ // a successful return to the rows must clear it or a stale complaint
+ // outlives the query that caused it. showWarnings() restores whatever the
+ // file itself had to say.
+ showWarnings();
}
void TagRulesDialog::rebuildRows(const RuleQuery &query)
diff --git a/src/tagrulesdialog.h b/src/tagrulesdialog.h
index 9cee163..948ebe2 100644
--- a/src/tagrulesdialog.h
+++ b/src/tagrulesdialog.h
@@ -69,6 +69,13 @@ public:
/// Runs the Save path without showing the dialog.
void saveForTest() { onSave(); }
+ /// Types into the query field and toggles the mode, so the refusal path
+ /// can be reached without a synthetic click. The refusal reports through
+ /// the warning label rather than a modal precisely so this is testable.
+ void setQueryTextForTest(const QString &text);
+ void setTextModeForTest(bool on);
+ QString warningTextForTest() const;
+
signals:
/// Asks the owner to run countQueries() through the worker.
void countsRequested();
diff --git a/tests/test_tagrules.cpp b/tests/test_tagrules.cpp
index 49abf8a..2302111 100644
--- a/tests/test_tagrules.cpp
+++ b/tests/test_tagrules.cpp
@@ -46,6 +46,7 @@ private slots:
void anUnrepresentableRuleOpensInTextMode();
void editingARowRewritesTheQuery();
void aTextModeRuleStaysTextWhenAnotherRuleIsVisited();
+ void leavingTextModeIsRefusedWhenTheQueryCannotBeShownAsRows();
private:
QString writeRules(const QString &json);
@@ -494,5 +495,54 @@ void TestTagRules::aTextModeRuleStaysTextWhenAnotherRuleIsVisited()
QStringLiteral("from:plain.example.org"));
}
+void TestTagRules::leavingTextModeIsRefusedWhenTheQueryCannotBeShownAsRows()
+{
+ // The refusal is the only path that can strand a user, so it is the one
+ // most worth pinning. It reports through the warning label rather than a
+ // modal, which is what lets this test exist at all: a modal would block
+ // here and the branch would ship unverified.
+ QTemporaryDir configHome;
+ QVERIFY(configHome.isValid());
+ qputenv("XDG_CONFIG_HOME", configHome.path().toUtf8());
+ QVERIFY(QDir().mkpath(configHome.filePath(QStringLiteral("mailrules"))));
+
+ const QString stored = configHome.filePath(
+ QStringLiteral("mailrules/rules.json"));
+ QFile out(stored);
+ QVERIFY(out.open(QIODevice::WriteOnly));
+ out.write(R"({
+ "version": 1,
+ "rules": [
+ {"id": "vendor", "query": "from:vendor.example.org",
+ "add": ["vendor"], "stage": 50, "enabled": true}
+ ]
+ })");
+ out.close();
+
+ TagRulesDialog dialog;
+ QVERIFY(!dialog.textModeForTest());
+
+ dialog.setTextModeForTest(true);
+ QVERIFY(dialog.textModeForTest());
+
+ // Type something notmuch accepts and this builder does not model.
+ dialog.setQueryTextForTest(QStringLiteral("body:receipt"));
+ dialog.setTextModeForTest(false);
+
+ QVERIFY2(dialog.textModeForTest(),
+ "the checkbox must refuse to clear: no rows mean this query");
+ QVERIFY2(!dialog.warningTextForTest().isEmpty(),
+ "the refusal must say why, not fail silently");
+
+ // And a representable query lets the builder back, clearing the warning.
+ dialog.setQueryTextForTest(QStringLiteral("from:other.example.org"));
+ dialog.setTextModeForTest(false);
+
+ QVERIFY2(!dialog.textModeForTest(), "a representable query must return");
+ QCOMPARE(dialog.rowCountForTest(), 1);
+ QVERIFY2(dialog.warningTextForTest().isEmpty(),
+ "a stale refusal must not outlive the query that caused it");
+}
+
QTEST_MAIN(TestTagRules)
#include "test_tagrules.moc"