diff options
| author | Felix Ernst <[email protected]> | 2025-04-07 21:09:00 +0000 |
|---|---|---|
| committer | Felix Ernst <[email protected]> | 2025-04-07 21:09:00 +0000 |
| commit | 4102ccb80457eea44ea280f0ace2a419602bc34b (patch) | |
| tree | 841e039cf9864276c968a397a2ae75c363199342 /src/tests | |
| parent | bfc177d3d1bc5a4a241e35d59086e4824e7c0bd3 (diff) | |
Rewrite search integration
This huge commit is a nearly complete rewrite of the Dolphin search
code. It implements most of the improved Dolphin search UI/UX as
designed and discussed in a collaborative effort by Kristen McWilliam,
Jin Liu, Andy Betts, Tagwerk, a few others and me.
See https://invent.kde.org/system/dolphin/-/issues/46.
# Notable changes
- A toggle to change the search tool is provided as most contributors
deemed that useful in
https://invent.kde.org/system/dolphin/-/merge_requests/642#note_985112.
- The default search is changed to filenamesearch for maximum
reliability.
- Removing all search parameters will take users back to the view state
prior to starting a search instead of keeping the search results open.
- The UI for choosing file types or modification dates has been made
more powerful with more granularity and more options.
- Most search parameters can be configured from a popup menu which
gives us extra space for extra clarity.
- Labels and help buttons as well as hyperlinks to settings makes sure
the user always knows why some search parameters are unavailable in
some contexts.
- Chips show important search parameters while the popup is closed.
They allow quickly removing filters.
- The titles of the search and the input field placeholder message
change to make clear whether file names or file contents are searched.
- When the user actively switches the search tool, whether content
should be searched, or whether to search everywhere, this is preserved
for the initial state of the search bar when the user opens it the next
time after restarting Dolphin.
# Architecture
- The new DolphinQuery class is independent of the UI and contains all
search parameters modifiable in Dolphin as easy setters and getters.
- DolphinQuery objects are also used to update the states of every
component in the search UI. There is now a clear separation of UI and
search configuration/DolphinQuery.
- DolphinQuery is responsible for exporting to and importing from
search URLs.
- The search UI always reflects the currently configured DolphinQuery
no matter if the user changed the UI to change the DolphinQuery or
loaded a DolphinQuery/older search URL which then is reflected in the
UI.
- I tried to simplify all classes and their interaction between each
other as much as possible.
- I added some tests
BUG: 386754
CCBUG: 435119
CCBUG: 458761
BUG: 446387
BUG: 470136
CCBUG: 471556
CCBUG: 475439
CCBUG: 477969
BUG: 480001
BUG: 483578
BUG: 488047
BUG: 488845
BUG: 500103
FIXED-IN: 25.08
Diffstat (limited to 'src/tests')
| -rw-r--r-- | src/tests/CMakeLists.txt | 6 | ||||
| -rw-r--r-- | src/tests/dolphinmainwindowtest.cpp | 2 | ||||
| -rw-r--r-- | src/tests/dolphinquerytest.cpp | 278 | ||||
| -rw-r--r-- | src/tests/dolphinsearchbartest.cpp | 124 | ||||
| -rw-r--r-- | src/tests/dolphinsearchboxtest.cpp | 63 |
5 files changed, 342 insertions, 131 deletions
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 1ef82e3d9..e77d7d7bf 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -42,10 +42,10 @@ target_link_libraries(kfileitemmodelbenchmark dolphinprivate Qt6::Test) # KItemListKeyboardSearchManagerTest ecm_add_test(kitemlistkeyboardsearchmanagertest.cpp LINK_LIBRARIES dolphinprivate Qt6::Test) -# DolphinSearchBox +# DolphinSearchBar if (KF6Baloo_FOUND) - ecm_add_test(dolphinsearchboxtest.cpp - TEST_NAME dolphinsearchboxtest + ecm_add_test(dolphinsearchbartest.cpp + TEST_NAME dolphinsearchbartest LINK_LIBRARIES dolphinprivate dolphinstatic Qt6::Test) endif() diff --git a/src/tests/dolphinmainwindowtest.cpp b/src/tests/dolphinmainwindowtest.cpp index c7d2f4b6d..4ae36e7e6 100644 --- a/src/tests/dolphinmainwindowtest.cpp +++ b/src/tests/dolphinmainwindowtest.cpp @@ -204,7 +204,7 @@ void DolphinMainWindowTest::testClosingTabsWithSearchBoxVisible() QVERIFY(tabWidget); // Show search box on first tab. - tabWidget->currentTabPage()->activeViewContainer()->setSearchModeEnabled(true); + tabWidget->currentTabPage()->activeViewContainer()->setSearchBarVisible(true); tabWidget->openNewActivatedTab(QUrl::fromLocalFile(QDir::homePath())); QCOMPARE(tabWidget->count(), 2); diff --git a/src/tests/dolphinquerytest.cpp b/src/tests/dolphinquerytest.cpp index b6feab686..76cea999f 100644 --- a/src/tests/dolphinquerytest.cpp +++ b/src/tests/dolphinquerytest.cpp @@ -1,13 +1,15 @@ /* - * SPDX-FileCopyrightText: 2019 Ismael Asensio <[email protected]> - * - * SPDX-License-Identifier: GPL-2.0-or-later - */ + SPDX-FileCopyrightText: 2019 Ismael Asensio <[email protected]> + SPDX-FileCopyrightText: 2025 Felix Ernst <[email protected]> + + SPDX-License-Identifier: GPL-2.0-or-later +*/ #include "search/dolphinquery.h" #include <QTest> +#include <QDate> #include <QJsonDocument> #include <QJsonObject> #include <QStandardPaths> @@ -23,6 +25,7 @@ private Q_SLOTS: void initTestCase(); void testBalooSearchParsing_data(); void testBalooSearchParsing(); + void testExportImport(); }; /** @@ -48,6 +51,7 @@ QUrl balooQueryUrl(const QString &searchString) void DolphinQueryTest::initTestCase() { QStandardPaths::setTestModeEnabled(true); + Search::setTestMode(); } /** @@ -56,8 +60,10 @@ void DolphinQueryTest::initTestCase() void DolphinQueryTest::testBalooSearchParsing_data() { QTest::addColumn<QUrl>("searchUrl"); - QTest::addColumn<QString>("expectedText"); - QTest::addColumn<QStringList>("expectedTerms"); + QTest::addColumn<QString>("expectedSearchTerm"); + QTest::addColumn<QDate>("expectedModifiedSinceDate"); + QTest::addColumn<int>("expectedMinimumRating"); + QTest::addColumn<QStringList>("expectedTags"); QTest::addColumn<bool>("hasContent"); QTest::addColumn<bool>("hasFileName"); @@ -73,78 +79,80 @@ void DolphinQueryTest::testBalooSearchParsing_data() const QString rating = QStringLiteral("rating>=2"); const QString modified = QStringLiteral("modified>=2019-08-07"); + QDate modifiedDate; + modifiedDate.setDate(2019, 8, 7); const QString tag = QStringLiteral("tag:tagA"); const QString tagS = QStringLiteral("tag:\"tagB with spaces\""); // in search url const QString tagR = QStringLiteral("tag:tagB with spaces"); // in result term + const QLatin1String tagA{"tagA"}; + const QLatin1String tagBWithSpaces{"tagB with spaces"}; + // Test for "Content" - QTest::newRow("content") << balooQueryUrl(text) << text << QStringList() << true << false; - QTest::newRow("content/space") << balooQueryUrl(textS) << textS << QStringList() << true << false; - QTest::newRow("content/quoted") << balooQueryUrl(textQ) << textS << QStringList() << true << false; - QTest::newRow("content/empty") << balooQueryUrl("") << "" << QStringList() << false << false; - QTest::newRow("content/single_quote") << balooQueryUrl("\"") << "\"" << QStringList() << true << false; - QTest::newRow("content/double_quote") << balooQueryUrl("\"\"") << "" << QStringList() << false << false; + QTest::newRow("content") << balooQueryUrl(text) << text << QDate{} << 0 << QStringList() << true << true; + QTest::newRow("content/space") << balooQueryUrl(textS) << textS << QDate{} << 0 << QStringList() << true << true; + QTest::newRow("content/quoted") << balooQueryUrl(textQ) << textS << QDate{} << 0 << QStringList() << true << true; + QTest::newRow("content/empty") << balooQueryUrl("") << "" << QDate{} << 0 << QStringList() << false << false; + QTest::newRow("content/single_quote") << balooQueryUrl("\"") << "\"" << QDate{} << 0 << QStringList() << true << true; + QTest::newRow("content/double_quote") << balooQueryUrl("\"\"") << "" << QDate{} << 0 << QStringList() << false << false; // Test for "FileName" - QTest::newRow("filename") << balooQueryUrl(filename) << text << QStringList() << false << true; - QTest::newRow("filename/space") << balooQueryUrl(filenameS) << textS << QStringList() << false << true; - QTest::newRow("filename/quoted") << balooQueryUrl(filenameQ) << textQ << QStringList() << false << true; - QTest::newRow("filename/mixed") << balooQueryUrl(filenameM) << textM << QStringList() << false << true; - QTest::newRow("filename/empty") << balooQueryUrl("filename:") << "" << QStringList() << false << false; - QTest::newRow("filename/single_quote") << balooQueryUrl("filename:\"") << "\"" << QStringList() << false << true; - QTest::newRow("filename/double_quote") << balooQueryUrl("filename:\"\"") << "" << QStringList() << false << false; + QTest::newRow("filename") << balooQueryUrl(filename) << text << QDate{} << 0 << QStringList() << false << true; + QTest::newRow("filename/space") << balooQueryUrl(filenameS) << textS << QDate{} << 0 << QStringList() << false << true; + QTest::newRow("filename/quoted") << balooQueryUrl(filenameQ) << textQ << QDate{} << 0 << QStringList() << false << true; + QTest::newRow("filename/mixed") << balooQueryUrl(filenameM) << textM << QDate{} << 0 << QStringList() << false << true; + QTest::newRow("filename/empty") << balooQueryUrl("filename:") << "" << QDate{} << 0 << QStringList() << false << false; + QTest::newRow("filename/single_quote") << balooQueryUrl("filename:\"") << "\"" << QDate{} << 0 << QStringList() << false << true; + QTest::newRow("filename/double_quote") << balooQueryUrl("filename:\"\"") << "" << QDate{} << 0 << QStringList() << false << false; // Combined content and filename search - QTest::newRow("content+filename") << balooQueryUrl(text + " " + filename) << text + " " + filename << QStringList() << true << true; + QTest::newRow("content+filename") << balooQueryUrl(text + " " + filename) << text << QDate{} << 0 << QStringList() << true << true; - QTest::newRow("content+filename/quoted") << balooQueryUrl(textQ + " " + filenameQ) << textS + " " + filenameQ << QStringList() << true << true; + QTest::newRow("content+filename/quoted") << balooQueryUrl(textQ + " " + filenameQ) << textS << QDate{} << 0 << QStringList() << true << true; // Test for rating - QTest::newRow("rating") << balooQueryUrl(rating) << "" << QStringList({rating}) << false << false; - QTest::newRow("rating+content") << balooQueryUrl(rating + " " + text) << text << QStringList({rating}) << true << false; - QTest::newRow("rating+filename") << balooQueryUrl(rating + " " + filename) << text << QStringList({rating}) << false << true; + QTest::newRow("rating") << balooQueryUrl(rating) << "" << QDate{} << 2 << QStringList() << false << false; + QTest::newRow("rating+content") << balooQueryUrl(rating + " " + text) << text << QDate{} << 2 << QStringList() << true << true; + QTest::newRow("rating+filename") << balooQueryUrl(rating + " " + filename) << text << QDate{} << 2 << QStringList() << false << true; // Test for modified date - QTest::newRow("modified") << balooQueryUrl(modified) << "" << QStringList({modified}) << false << false; - QTest::newRow("modified+content") << balooQueryUrl(modified + " " + text) << text << QStringList({modified}) << true << false; - QTest::newRow("modified+filename") << balooQueryUrl(modified + " " + filename) << text << QStringList({modified}) << false << true; + QTest::newRow("modified") << balooQueryUrl(modified) << "" << modifiedDate << 0 << QStringList() << false << false; + QTest::newRow("modified+content") << balooQueryUrl(modified + " " + text) << text << modifiedDate << 0 << QStringList() << true << true; + QTest::newRow("modified+filename") << balooQueryUrl(modified + " " + filename) << text << modifiedDate << 0 << QStringList() << false << true; // Test for tags - QTest::newRow("tag") << balooQueryUrl(tag) << "" << QStringList({tag}) << false << false; - QTest::newRow("tag/space") << balooQueryUrl(tagS) << "" << QStringList({tagR}) << false << false; - QTest::newRow("tag/double") << balooQueryUrl(tag + " " + tagS) << "" << QStringList({tag, tagR}) << false << false; - QTest::newRow("tag+content") << balooQueryUrl(tag + " " + text) << text << QStringList({tag}) << true << false; - QTest::newRow("tag+filename") << balooQueryUrl(tag + " " + filename) << text << QStringList({tag}) << false << true; + QTest::newRow("tag") << balooQueryUrl(tag) << "" << QDate{} << 0 << QStringList{tagA} << false << false; + QTest::newRow("tag/space") << balooQueryUrl(tagS) << "" << QDate{} << 0 << QStringList{tagBWithSpaces} << false << false; + QTest::newRow("tag/double") << balooQueryUrl(tag + " " + tagS) << "" << QDate{} << 0 << QStringList{tagA, tagBWithSpaces} << false << false; + QTest::newRow("tag+content") << balooQueryUrl(tag + " " + text) << text << QDate{} << 0 << QStringList{tagA} << true << true; + QTest::newRow("tag+filename") << balooQueryUrl(tag + " " + filename) << text << QDate{} << 0 << QStringList{tagA} << false << true; // Combined search terms - QTest::newRow("searchTerms") << balooQueryUrl(rating + " AND " + modified + " AND " + tag + " AND " + tagS) << "" - << QStringList({modified, rating, tag, tagR}) << false << false; + QTest::newRow("searchTerms") << balooQueryUrl(rating + " AND " + modified + " AND " + tag + " AND " + tagS) << "" << modifiedDate << 2 + << QStringList{tagA, tagBWithSpaces} << false << false; - QTest::newRow("searchTerms+content") << balooQueryUrl(rating + " AND " + modified + " " + text + " " + tag + " AND " + tagS) << text - << QStringList({modified, rating, tag, tagR}) << true << false; + QTest::newRow("searchTerms+content") << balooQueryUrl(rating + " AND " + modified + " " + text + " " + tag + " AND " + tagS) << text << modifiedDate << 2 + << QStringList{tagA, tagBWithSpaces} << true << true; - QTest::newRow("searchTerms+filename") << balooQueryUrl(rating + " AND " + modified + " " + filename + " " + tag + " AND " + tagS) << text - << QStringList({modified, rating, tag, tagR}) << false << true; + QTest::newRow("searchTerms+filename") << balooQueryUrl(rating + " AND " + modified + " " + filename + " " + tag + " AND " + tagS) << text << modifiedDate + << 2 << QStringList{tagA, tagBWithSpaces} << false << true; - QTest::newRow("allTerms") << balooQueryUrl(text + " " + filename + " " + rating + " AND " + modified + " AND " + tag) << text + " " + filename - << QStringList({modified, rating, tag}) << true << true; + QTest::newRow("allTerms") << balooQueryUrl(text + " " + filename + " " + rating + " AND " + modified + " AND " + tag) << text << modifiedDate << 2 + << QStringList{tagA} << true << true; - QTest::newRow("allTerms/space") << balooQueryUrl(textS + " " + filenameS + " " + rating + " AND " + modified + " AND " + tagS) << textS + " " + filenameS - << QStringList({modified, rating, tagR}) << true << true; + QTest::newRow("allTerms/space") << balooQueryUrl(textS + " " + filenameS + " " + rating + " AND " + modified + " AND " + tagS) << textS << modifiedDate << 2 + << QStringList{tagBWithSpaces} << true << true; // Test tags:/ URL scheme const auto tagUrl = [](const QString &tag) { return QUrl(QStringLiteral("tags:/%1/").arg(tag)); }; - const auto tagTerms = [](const QString &tag) { - return QStringList{QStringLiteral("tag:%1").arg(tag)}; - }; - QTest::newRow("tagsUrl") << tagUrl("tagA") << "" << tagTerms("tagA") << false << false; - QTest::newRow("tagsUrl/space") << tagUrl("tagB with spaces") << "" << tagTerms("tagB with spaces") << false << false; - QTest::newRow("tagsUrl/hash") << tagUrl("tagC#hash") << "" << tagTerms("tagC#hash") << false << false; - QTest::newRow("tagsUrl/slash") << tagUrl("tagD/with/slash") << "" << tagTerms("tagD/with/slash") << false << false; + QTest::newRow("tagsUrl") << tagUrl(tagA) << "" << QDate{} << 0 << QStringList{tagA} << false << false; + QTest::newRow("tagsUrl/space") << tagUrl(tagBWithSpaces) << "" << QDate{} << 0 << QStringList{tagBWithSpaces} << false << false; + QTest::newRow("tagsUrl/hash") << tagUrl("tagC#hash") << "" << QDate{} << 0 << QStringList{QStringLiteral("tagC#hash")} << false << false; + QTest::newRow("tagsUrl/slash") << tagUrl("tagD/with/slash") << "" << QDate{} << 0 << QStringList{QStringLiteral("tagD/with/slash")} << false << false; } /** @@ -155,31 +163,173 @@ void DolphinQueryTest::testBalooSearchParsing_data() void DolphinQueryTest::testBalooSearchParsing() { QFETCH(QUrl, searchUrl); - QFETCH(QString, expectedText); - QFETCH(QStringList, expectedTerms); + QFETCH(QString, expectedSearchTerm); + QFETCH(QDate, expectedModifiedSinceDate); + QFETCH(int, expectedMinimumRating); + QFETCH(QStringList, expectedTags); QFETCH(bool, hasContent); QFETCH(bool, hasFileName); - const DolphinQuery query = DolphinQuery::fromSearchUrl(searchUrl); + const Search::DolphinQuery query = Search::DolphinQuery{searchUrl, /** No backupSearchPath should be needed because searchUrl should be valid. */ QUrl{}}; // Checkt that the URL is supported - QVERIFY(DolphinQuery::supportsScheme(searchUrl.scheme())); + QVERIFY(Search::isSupportedSearchScheme(searchUrl.scheme())); // Check for parsed text (would be displayed on the input search bar) - QCOMPARE(query.text(), expectedText); + QCOMPARE(query.searchTerm(), expectedSearchTerm); + + QCOMPARE(query.modifiedSinceDate(), expectedModifiedSinceDate); + + QCOMPARE(query.minimumRating(), expectedMinimumRating); + + QCOMPARE(query.requiredTags(), expectedTags); + + // Check that there were no unrecognized baloo query parameters in the above strings. + Q_ASSERT(query.m_unrecognizedBalooQueryStrings.isEmpty()); + + // Check if a search term is looked up in the file names or contents + QCOMPARE(query.searchThrough() == Search::SearchThrough::FileContents && !query.searchTerm().isEmpty(), hasContent); + QCOMPARE(!query.searchTerm().isEmpty(), hasFileName); // The file names are always also searched even when searching through file contents. +} + +/** + * Tests whether exporting a DolphinQuery object to a URL and then constructing a DolphinQuery object from that URL recreates the same DolphinQuery. + */ +void DolphinQueryTest::testExportImport() +{ + /// Initialize the DolphinQuery with some standard settings. + const QUrl searchPath1{"file:///someNonExistentUrl"}; + Search::DolphinQuery query{searchPath1, searchPath1}; + query.setSearchLocations(Search::SearchLocations::FromHere); + QVERIFY(query.searchLocations() == Search::SearchLocations::FromHere); + query.setSearchThrough(Search::SearchThrough::FileNames); + QVERIFY(query.searchThrough() == Search::SearchThrough::FileNames); + query.setSearchTool(Search::SearchTool::Filenamesearch); + QVERIFY(query.searchTool() == Search::SearchTool::Filenamesearch); + QVERIFY(query == Search::DolphinQuery(query.toUrl(), searchPath1)); // Export then import + + /// Test that exporting and importing works as expected no matter which aspect we change. + query.setSearchThrough(Search::SearchThrough::FileContents); + QVERIFY(query.searchThrough() == Search::SearchThrough::FileContents); + QVERIFY(query == Search::DolphinQuery(query.toUrl(), searchPath1)); // Export then import + + constexpr QLatin1String searchTerm1{"abc"}; + query.setSearchTerm(searchTerm1); + QVERIFY(query.searchTerm() == searchTerm1); + QVERIFY(query == Search::DolphinQuery(query.toUrl(), searchPath1)); // Export then import + + query.setSearchThrough(Search::SearchThrough::FileNames); + QVERIFY(query.searchThrough() == Search::SearchThrough::FileNames); + QVERIFY(query == Search::DolphinQuery(query.toUrl(), searchPath1)); // Export then import + + QVERIFY(query.searchPath() == searchPath1); + const QUrl searchPath2{"file:///someNonExistentUrl2"}; + query.setSearchPath(searchPath2); + QVERIFY(query.searchPath() == searchPath2); + QVERIFY(query == Search::DolphinQuery(query.toUrl(), QUrl{})); // Export then import. The QUrl{} fallback does not matter because otherUrl is imported. + + query.setSearchLocations(Search::SearchLocations::Everywhere); + QVERIFY(query.searchLocations() == Search::SearchLocations::Everywhere); + QVERIFY(query == Search::DolphinQuery(query.toUrl(), searchPath2)); // Export then import. searchPath2 is required to match as the fallback. + + QVERIFY(query.searchTerm() == searchTerm1); + constexpr QLatin1String searchTerm2{"xyz"}; + query.setSearchTerm(searchTerm2); + QVERIFY(query.searchTerm() == searchTerm2); + QVERIFY(query == Search::DolphinQuery(query.toUrl(), searchPath2)); // Export then import + + QVERIFY(query.searchLocations() == Search::SearchLocations::Everywhere); + query.setSearchLocations(Search::SearchLocations::FromHere); + QVERIFY(query.searchLocations() == Search::SearchLocations::FromHere); + QVERIFY(query.searchPath() == searchPath2); + QVERIFY(query == Search::DolphinQuery(query.toUrl(), QUrl{})); // Export then import. The QUrl{} fallback does not matter because searchPath2 is imported. + +#if HAVE_BALOO + /// Test Baloo search queries + query.setSearchTool(Search::SearchTool::Baloo); + QVERIFY(query.searchTool() == Search::SearchTool::Baloo); + QVERIFY(query.searchTerm() == searchTerm2); + QVERIFY(query.searchLocations() == Search::SearchLocations::FromHere); + QVERIFY(query.searchPath() == searchPath2); + QVERIFY(query.searchThrough() == Search::SearchThrough::FileNames); + QVERIFY(query == Search::DolphinQuery(query.toUrl(), QUrl{})); // Export then import. The QUrl{} fallback does not matter because searchPath2 is imported. + + /// Test that exporting and importing works as expected no matter which aspect we change. + query.setSearchThrough(Search::SearchThrough::FileContents); + QVERIFY(query.searchThrough() == Search::SearchThrough::FileContents); + QVERIFY(query == Search::DolphinQuery(query.toUrl(), QUrl{})); // Export then import. The QUrl{} fallback does not matter because searchPath2 is imported. + + query.setSearchTerm(searchTerm1); + QVERIFY(query.searchTerm() == searchTerm1); + QVERIFY(query == Search::DolphinQuery(query.toUrl(), QUrl{})); // Export then import. The QUrl{} fallback does not matter because searchPath2 is imported. + + query.setSearchThrough(Search::SearchThrough::FileNames); + QVERIFY(query.searchThrough() == Search::SearchThrough::FileNames); + QVERIFY(query == Search::DolphinQuery(query.toUrl(), QUrl{})); // Export then import. The QUrl{} fallback does not matter because searchPath2 is imported. + + QVERIFY(query.searchPath() == searchPath2); + query.setSearchPath(searchPath1); + QVERIFY(query.searchPath() == searchPath1); + QVERIFY(query == Search::DolphinQuery(query.toUrl(), QUrl{})); // Export then import. The QUrl{} fallback does not matter because searchPath2 is imported. + + query.setSearchLocations(Search::SearchLocations::Everywhere); + QVERIFY(query.searchLocations() == Search::SearchLocations::Everywhere); + QVERIFY(query == Search::DolphinQuery(query.toUrl(), searchPath1)); // Export then import. searchPath1 is required to match as the fallback. + + QVERIFY(query.searchTerm() == searchTerm1); + query.setSearchTerm(searchTerm2); + QVERIFY(query.searchTerm() == searchTerm2); + QVERIFY(query == Search::DolphinQuery(query.toUrl(), searchPath1)); // Export then import + + QVERIFY(query.searchLocations() == Search::SearchLocations::Everywhere); + query.setSearchLocations(Search::SearchLocations::FromHere); + QVERIFY(query.searchLocations() == Search::SearchLocations::FromHere); + QVERIFY(query.searchPath() == searchPath1); + QVERIFY(query == Search::DolphinQuery(query.toUrl(), QUrl{})); // Export then import. The QUrl{} fallback does not matter because searchPath1 is imported. + + QVERIFY(query.fileType() == KFileMetaData::Type::Empty); + query.setFileType(KFileMetaData::Type::Archive); + QVERIFY(query.fileType() == KFileMetaData::Type::Archive); + QVERIFY(query == Search::DolphinQuery(query.toUrl(), QUrl{})); // Export then import. The QUrl{} fallback does not matter because searchPath1 is imported. + + QVERIFY(!query.modifiedSinceDate().isValid()); + QDate modifiedDate; + modifiedDate.setDate(2018, 6, 3); // World Bicycle Day + query.setModifiedSinceDate(modifiedDate); + QVERIFY(query.modifiedSinceDate() == modifiedDate); + QVERIFY(query == Search::DolphinQuery(query.toUrl(), QUrl{})); // Export then import. The QUrl{} fallback does not matter because searchPath1 is imported. + + QVERIFY(query.minimumRating() == 0); + query.setMinimumRating(4); + QVERIFY(query.minimumRating() == 4); + QVERIFY(query == Search::DolphinQuery(query.toUrl(), QUrl{})); // Export then import. The QUrl{} fallback does not matter because searchPath1 is imported. - // Check for parsed search terms (would be displayed by the facetsWidget) - QStringList searchTerms = query.searchTerms(); - searchTerms.sort(); + QVERIFY(query.requiredTags().isEmpty()); + query.setRequiredTags({searchTerm1, searchTerm2}); + QVERIFY(query.requiredTags().contains(searchTerm1)); + QVERIFY(query.requiredTags().contains(searchTerm2)); + QVERIFY(query == Search::DolphinQuery(query.toUrl(), QUrl{})); // Export then import. The QUrl{} fallback does not matter because searchPath1 is imported. - QCOMPARE(searchTerms.count(), expectedTerms.count()); - for (int i = 0; i < expectedTerms.count(); i++) { - QCOMPARE(searchTerms.at(i), expectedTerms.at(i)); - } + QVERIFY(query.searchTool() == Search::SearchTool::Baloo); + QVERIFY(query.searchThrough() == Search::SearchThrough::FileNames); + QVERIFY(query.searchPath() == searchPath1); + QVERIFY(query.searchTerm() == searchTerm2); + QVERIFY(query.searchLocations() == Search::SearchLocations::FromHere); + QVERIFY(query.fileType() == KFileMetaData::Type::Archive); + QVERIFY(query.modifiedSinceDate() == modifiedDate); + QVERIFY(query.minimumRating() == 4); - // Check for filename and content detection - QCOMPARE(query.hasContentSearch(), hasContent); - QCOMPARE(query.hasFileName(), hasFileName); + /// Changing the search tool should not immediately drop all the extra information even if the search tool might not support searching for them. + /// This is mostly an attempt to not drop properties set by the user earlier than we have to. + query.setSearchTool(Search::SearchTool::Filenamesearch); + QVERIFY(query.searchThrough() == Search::SearchThrough::FileNames); + QVERIFY(query.searchPath() == searchPath1); + QVERIFY(query.searchTerm() == searchTerm2); + QVERIFY(query.searchLocations() == Search::SearchLocations::FromHere); + QVERIFY(query.fileType() == KFileMetaData::Type::Archive); + QVERIFY(query.modifiedSinceDate() == modifiedDate); + QVERIFY(query.minimumRating() == 4); +#endif } QTEST_MAIN(DolphinQueryTest) diff --git a/src/tests/dolphinsearchbartest.cpp b/src/tests/dolphinsearchbartest.cpp new file mode 100644 index 000000000..0efb05dc8 --- /dev/null +++ b/src/tests/dolphinsearchbartest.cpp @@ -0,0 +1,124 @@ +/* + * SPDX-FileCopyrightText: 2011 Peter Penz <[email protected]> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "search/bar.h" +#include "search/popup.h" + +#include <QLineEdit> +#include <QSignalSpy> +#include <QStandardPaths> +#include <QTest> +#include <QToolButton> + +class DolphinSearchBarTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase(); + void init(); + void cleanup(); + + void testPopupLazyLoading(); + void testTextClearing(); + void testUrlChangeSignals(); + +private: + Search::Bar *m_searchBar; +}; + +void DolphinSearchBarTest::initTestCase() +{ + QStandardPaths::setTestModeEnabled(true); +} + +void DolphinSearchBarTest::init() +{ + const auto homeUrl{QUrl::fromUserInput(QDir::homePath())}; + m_searchBar = new Search::Bar(std::make_shared<const Search::DolphinQuery>(homeUrl, homeUrl)); +} + +void DolphinSearchBarTest::cleanup() +{ + delete m_searchBar; +} + +void DolphinSearchBarTest::testPopupLazyLoading() +{ + m_searchBar->setVisible(true, WithoutAnimation); + QVERIFY2(m_searchBar->m_popup->isEmpty(), "The popup should only be populated or updated when it was opened at least once by the user."); +} + +/** + * The test verifies whether the automatic clearing of the text works correctly. + * The text may not get cleared when the search bar gets visible or invisible, + * as this would clear the text when switching between tabs. + */ +void DolphinSearchBarTest::testTextClearing() +{ + m_searchBar->setVisible(true, WithoutAnimation); + QVERIFY(m_searchBar->text().isEmpty()); + QVERIFY(!m_searchBar->isSearchConfigured()); + + m_searchBar->updateStateToMatch(std::make_shared<const Search::DolphinQuery>(QUrl::fromUserInput("filenamesearch:?search=xyz"), QUrl{})); + m_searchBar->setVisible(false, WithoutAnimation); + m_searchBar->setVisible(true, WithoutAnimation); + QCOMPARE(m_searchBar->text(), QStringLiteral("xyz")); + QVERIFY(m_searchBar->isSearchConfigured()); + QVERIFY(!m_searchBar->queryTitle().isEmpty()); + + QTest::keyClick(m_searchBar, Qt::Key_Escape); + QVERIFY(m_searchBar->text().isEmpty()); + QVERIFY(!m_searchBar->isSearchConfigured()); +} + +void DolphinSearchBarTest::testUrlChangeSignals() +{ + QSignalSpy spyUrlChangeRequested(m_searchBar, &Search::Bar::urlChangeRequested); + m_searchBar->setVisible(true, WithoutAnimation); + QVERIFY2(spyUrlChangeRequested.isEmpty(), "Opening the Search::Bar should not trigger anything."); + + m_searchBar->m_everywhereButton->click(); + m_searchBar->m_fromHereButton->click(); + QVERIFY(!m_searchBar->isSearchConfigured()); + + while (!spyUrlChangeRequested.isEmpty()) { + const QUrl requestedUrl = qvariant_cast<QUrl>(spyUrlChangeRequested.takeFirst().at(0)); + QVERIFY2(!Search::isSupportedSearchScheme(requestedUrl.scheme()) && requestedUrl == m_searchBar->m_searchConfiguration->searchPath(), + "The search is still not in a state to search for anything specific, so any requested URLs would be identical to the current search path of " + "the Search::Bar."); + } + + Search::DolphinQuery searchConfiguration = *m_searchBar->m_searchConfiguration; + searchConfiguration.setSearchTerm(QStringLiteral("searchTerm")); + m_searchBar->updateStateToMatch(std::make_shared<const Search::DolphinQuery>(searchConfiguration)); + QVERIFY2(m_searchBar->isSearchConfigured(), "The search bar now has enough information to trigger a meaningful search."); + QVERIFY2(spyUrlChangeRequested.isEmpty(), "The visual state was updated to match a new search configuration, but the user never triggered a search."); + + m_searchBar->commitCurrentConfiguration(); // We pretend to be a user interaction that would trigger a search to happen. + const QUrl requestedUrl = qvariant_cast<QUrl>(spyUrlChangeRequested.takeFirst().at(0)); + QVERIFY2(Search::isSupportedSearchScheme(requestedUrl.scheme()), "The search bar requested to open a search url and therefore start searching."); + QVERIFY2(spyUrlChangeRequested.isEmpty(), "The search URL was requested exactly once, so no additional urlChangeRequested signals should exist."); + + Search::DolphinQuery searchConfiguration2 = *m_searchBar->m_searchConfiguration; + searchConfiguration2.setSearchTerm(QString("")); + m_searchBar->updateStateToMatch(std::make_shared<const Search::DolphinQuery>(searchConfiguration2)); + QVERIFY2(!m_searchBar->isSearchConfigured(), "The search bar does not have enough information anymore to trigger a meaningful search."); + QVERIFY2(spyUrlChangeRequested.isEmpty(), "The visual state was updated to match a new search configuration, but the user never triggered a search."); + + m_searchBar->commitCurrentConfiguration(); // We pretend to be a user interaction that would trigger a search to happen. + const QUrl requestedUrl2 = qvariant_cast<QUrl>(spyUrlChangeRequested.takeFirst().at(0)); + QVERIFY2(!Search::isSupportedSearchScheme(requestedUrl2.scheme()) && requestedUrl2 == m_searchBar->m_searchConfiguration->searchPath(), + "The Search::Bar is not in a state to search for anything specific, so the search bar requests to show the previously visited location normally " + "again instead of any previous search URL."); + QVERIFY2(spyUrlChangeRequested.isEmpty(), "The non-search URL was requested exactly once, so no additional urlChangeRequested signals should exist."); + + QVERIFY2(m_searchBar->m_popup->isEmpty(), "Through all of this, the popup should still be empty because it was never opened by the user."); +} + +QTEST_MAIN(DolphinSearchBarTest) + +#include "dolphinsearchbartest.moc" diff --git a/src/tests/dolphinsearchboxtest.cpp b/src/tests/dolphinsearchboxtest.cpp deleted file mode 100644 index bda60909d..000000000 --- a/src/tests/dolphinsearchboxtest.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2011 Peter Penz <[email protected]> - * - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#include "search/dolphinsearchbox.h" - -#include <QStandardPaths> -#include <QTest> - -class DolphinSearchBoxTest : public QObject -{ - Q_OBJECT - -private Q_SLOTS: - void initTestCase(); - void init(); - void cleanup(); - - void testTextClearing(); - -private: - DolphinSearchBox *m_searchBox; -}; - -void DolphinSearchBoxTest::initTestCase() -{ - QStandardPaths::setTestModeEnabled(true); -} - -void DolphinSearchBoxTest::init() -{ - m_searchBox = new DolphinSearchBox(); -} - -void DolphinSearchBoxTest::cleanup() -{ - delete m_searchBox; -} - -/** - * The test verifies whether the automatic clearing of the text works correctly. - * The text may not get cleared when the searchbox gets visible or invisible, - * as this would clear the text when switching between tabs. - */ -void DolphinSearchBoxTest::testTextClearing() -{ - m_searchBox->setVisible(true, WithoutAnimation); - QVERIFY(m_searchBox->text().isEmpty()); - - m_searchBox->setText("xyz"); - m_searchBox->setVisible(false, WithoutAnimation); - m_searchBox->setVisible(true, WithoutAnimation); - QCOMPARE(m_searchBox->text(), QStringLiteral("xyz")); - - QTest::keyClick(m_searchBox, Qt::Key_Escape); - QVERIFY(m_searchBox->text().isEmpty()); -} - -QTEST_MAIN(DolphinSearchBoxTest) - -#include "dolphinsearchboxtest.moc" |
