aboutsummaryrefslogtreecommitdiffstats
path: root/tests/test_querycompleter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_querycompleter.cpp')
-rw-r--r--tests/test_querycompleter.cpp146
1 files changed, 146 insertions, 0 deletions
diff --git a/tests/test_querycompleter.cpp b/tests/test_querycompleter.cpp
index 5ae9312..c1d7530 100644
--- a/tests/test_querycompleter.cpp
+++ b/tests/test_querycompleter.cpp
@@ -63,6 +63,16 @@ private slots:
void tabIsIgnoredWhileThePopupIsHidden();
void returnIsIgnoredWhileThePopupIsHidden();
void focusOpensThePopupOnlyWhenConfigured();
+
+ // Delivered to the widget the window system actually gives the key to,
+ // rather than straight to the line edit. While the popup is up that is the
+ // popup, which has grabbed the keyboard, and a filter on the line edit
+ // never runs. Sending to the edit hides exactly the bug the user reports.
+ void tabAcceptsWhenTheKeyGoesToTheGrabbingPopup();
+ void returnAcceptsWhenTheKeyGoesToTheGrabbingPopup();
+ void acceptingAPrefixReopensThePopupForValues();
+ void acceptingAValueDoesNotReopenAnEmptyPopup();
+ void keysFallThroughWhileThePopupIsHidden();
};
// Copied from tests/test_config.cpp rather than shared, so the two test files
@@ -497,5 +507,141 @@ void TestQueryCompleter::focusOpensThePopupOnlyWhenConfigured()
}
}
+// The widget the window system would hand the next key to. While the popup is
+// up it has grabbed the keyboard, so that is the popup and NOT the line edit,
+// which has by then lost focus entirely. Routing test keys through here is what
+// makes these tests reproduce the user's experience instead of a synthetic one.
+static QWidget *keyboardTarget(QLineEdit *edit)
+{
+ if (QWidget *popup = QApplication::activePopupWidget())
+ return popup;
+ return edit;
+}
+
+void TestQueryCompleter::tabAcceptsWhenTheKeyGoesToTheGrabbingPopup()
+{
+ Config config;
+ QLineEdit edit;
+ edit.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&edit));
+ edit.setFocus();
+ QueryCompleter completer(&edit, config);
+
+ QTest::keyClicks(&edit, QStringLiteral("t"));
+ QVERIFY(findPopup() && findPopup()->isVisible());
+
+ QTest::keyClick(keyboardTarget(&edit), Qt::Key_Tab);
+
+ QCOMPARE(edit.text(), QStringLiteral("tag:"));
+}
+
+void TestQueryCompleter::returnAcceptsWhenTheKeyGoesToTheGrabbingPopup()
+{
+ // Return must be consumed too, or it reaches the thread list and opens a
+ // thread, which is what the user sees.
+ Config config;
+ QLineEdit edit;
+ edit.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&edit));
+ edit.setFocus();
+ QueryCompleter completer(&edit, config);
+
+ bool ran = false;
+ connect(&edit, &QLineEdit::returnPressed, &edit, [&ran]() { ran = true; });
+
+ QTest::keyClicks(&edit, QStringLiteral("t"));
+ QVERIFY(findPopup() && findPopup()->isVisible());
+
+ QTest::keyClick(keyboardTarget(&edit), Qt::Key_Return);
+
+ QCOMPARE(edit.text(), QStringLiteral("tag:"));
+ QVERIFY(!ran);
+}
+
+void TestQueryCompleter::acceptingAPrefixReopensThePopupForValues()
+{
+ // The user's third complaint: after taking "tag:" the caret sits where a
+ // tag value goes, so the values must be offered without a second Ctrl+Space.
+ Config config;
+ QLineEdit edit;
+ edit.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&edit));
+ edit.setFocus();
+ QueryCompleter completer(&edit, config);
+ completer.setTags({ QStringLiteral("unread"), QStringLiteral("inbox") });
+
+ QTest::keyClicks(&edit, QStringLiteral("t"));
+ QVERIFY(findPopup() && findPopup()->isVisible());
+
+ QTest::keyClick(keyboardTarget(&edit), Qt::Key_Tab);
+ QCOMPARE(edit.text(), QStringLiteral("tag:"));
+
+ QListView *popup = findPopup();
+ QVERIFY(popup);
+ QVERIFY(popup->isVisible());
+ QStringList offered;
+ for (int row = 0; row < popup->model()->rowCount(); ++row)
+ offered << popup->model()->index(row, 0).data().toString();
+ QCOMPARE(offered, QStringList({ QStringLiteral("unread"),
+ QStringLiteral("inbox") }));
+
+ // And the chain completes: typing into the reopened popup and accepting
+ // yields the finished term.
+ QTest::keyClicks(&edit, QStringLiteral("un"));
+ QVERIFY(findPopup() && findPopup()->isVisible());
+ QTest::keyClick(keyboardTarget(&edit), Qt::Key_Tab);
+ QCOMPARE(edit.text(), QStringLiteral("tag:unread"));
+}
+
+void TestQueryCompleter::acceptingAValueDoesNotReopenAnEmptyPopup()
+{
+ // "tag:unread" is complete. Reopening here would put an empty list under
+ // the caret and swallow the next Return.
+ Config config;
+ QLineEdit edit;
+ edit.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&edit));
+ edit.setFocus();
+ QueryCompleter completer(&edit, config);
+ completer.setTags({ QStringLiteral("unread") });
+
+ QTest::keyClicks(&edit, QStringLiteral("tag:un"));
+ QVERIFY(findPopup() && findPopup()->isVisible());
+
+ QTest::keyClick(keyboardTarget(&edit), Qt::Key_Tab);
+
+ QCOMPARE(edit.text(), QStringLiteral("tag:unread"));
+ QVERIFY(!findPopup() || !findPopup()->isVisible());
+}
+
+void TestQueryCompleter::keysFallThroughWhileThePopupIsHidden()
+{
+ // The filter is application-wide, so proving it does nothing with the popup
+ // down is what keeps it from breaking the rest of the application.
+ Config config;
+ QLineEdit edit;
+ edit.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&edit));
+ edit.setFocus();
+ QueryCompleter completer(&edit, config);
+
+ if (QListView *popup = findPopup())
+ popup->hide();
+
+ QLineEdit other;
+ other.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&other));
+ other.setFocus();
+
+ bool ran = false;
+ connect(&other, &QLineEdit::returnPressed, &other, [&ran]() { ran = true; });
+
+ QTest::keyClicks(&other, QStringLiteral("hello"));
+ QTest::keyClick(&other, Qt::Key_Return);
+
+ QCOMPARE(other.text(), QStringLiteral("hello"));
+ QVERIFY(ran);
+}
+
QTEST_MAIN(TestQueryCompleter)
#include "test_querycompleter.moc"