File indexing completed on 2024-12-01 09:52:41
0001 /* 0002 This file is part of the KDE Frameworks 0003 SPDX-FileCopyrightText: 2008, 2016 David Faure <faure@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0006 */ 0007 0008 #include <KComboBox> 0009 #include <kdiroperator.h> 0010 #include <kfilewidget.h> 0011 #include <kurlrequester.h> 0012 0013 #include <QLineEdit> 0014 #include <QSignalSpy> 0015 #include <QTemporaryFile> 0016 #include <QTest> 0017 0018 /* 0019 IMPORTANT: 0020 Because this unittest interacts with the file dialog, 0021 remember to run it both with plugins/platformthemes/KDEPlasmaPlatformTheme.so (to use KFileWidget) 0022 and without it (to use the builtin QFileDialog code) 0023 */ 0024 0025 class KUrlRequesterTest : public QObject 0026 { 0027 Q_OBJECT 0028 private Q_SLOTS: 0029 void testUrlRequester(); 0030 void testComboRequester(); 0031 void testComboRequester_data(); 0032 void testNameFilters(); 0033 void testNameFilters_data(); 0034 0035 private: 0036 bool createTestFile(const QString &fileName) 0037 { 0038 QFile file(fileName); 0039 if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { 0040 return false; 0041 } 0042 file.write("Hello world\n"); 0043 return true; 0044 } 0045 }; 0046 0047 // Same as in kfiledialog_unittest.cpp 0048 static KFileWidget *findFileWidget() 0049 { 0050 QList<KFileWidget *> widgets; 0051 const QList<QWidget *> widgetsList = QApplication::topLevelWidgets(); 0052 for (QWidget *widget : widgetsList) { 0053 KFileWidget *fw = widget->findChild<KFileWidget *>(); 0054 if (fw) { 0055 widgets.append(fw); 0056 } 0057 } 0058 return (widgets.count() == 1) ? widgets.first() : nullptr; 0059 } 0060 0061 void KUrlRequesterTest::testUrlRequester() 0062 { 0063 KUrlRequester req; 0064 req.setFileDialogModality(Qt::NonModal); 0065 const QString fileName = QStringLiteral("some_test_file"); 0066 QVERIFY(createTestFile(fileName)); 0067 QTemporaryFile tempFile; 0068 QVERIFY(tempFile.open()); 0069 const QString filePath2 = tempFile.fileName(); 0070 QVERIFY(QFile::exists(filePath2)); 0071 0072 // Set start dir 0073 const QUrl dirUrl = QUrl::fromLocalFile(QDir::currentPath()); 0074 req.setStartDir(dirUrl); 0075 QCOMPARE(req.startDir().toString(), dirUrl.toString()); 0076 0077 // Click the button 0078 req.button()->click(); 0079 QFileDialog *fileDialog = req.findChild<QFileDialog *>(); 0080 QVERIFY(fileDialog); 0081 0082 // Find out if we're using KFileDialog or QFileDialog 0083 KFileWidget *fw = findFileWidget(); 0084 0085 // Wait for directory listing 0086 if (fw) { 0087 QSignalSpy spy(fw->dirOperator(), &KDirOperator::finishedLoading); 0088 QVERIFY(spy.wait()); 0089 } 0090 0091 // Select file 0092 const QString filePath = dirUrl.toLocalFile() + '/' + fileName; 0093 fileDialog->selectFile(fileName); 0094 0095 // Click OK, check URLRequester shows and returns selected file 0096 QKeyEvent keyPressEv(QKeyEvent::KeyPress, Qt::Key_Return, Qt::NoModifier); 0097 qApp->sendEvent(fw ? static_cast<QWidget *>(fw) : static_cast<QWidget *>(fileDialog), &keyPressEv); 0098 QCOMPARE(fileDialog->result(), static_cast<int>(QDialog::Accepted)); 0099 QCOMPARE(fileDialog->selectedFiles(), QStringList{filePath}); 0100 QCOMPARE(req.url().toLocalFile(), filePath); 0101 0102 // Check there is no longer any file dialog visible 0103 QVERIFY(fileDialog->isHidden()); 0104 0105 // Click KUrlRequester button again. This time the filedialog is initialized with a file URL 0106 req.button()->click(); 0107 fileDialog = req.findChild<QFileDialog *>(); 0108 QVERIFY(fileDialog); 0109 fw = findFileWidget(); 0110 if (fw) { // no need to wait for dir listing again, but we need it to be visible at least (for Key_Return to accept) 0111 // QVERIFY(QTest::qWaitForWindowExposed(fw->window())); // doesn't seem to be enough 0112 QTRY_VERIFY(fw->isVisible()); 0113 } 0114 0115 // Select file 2 0116 fileDialog->selectFile(filePath2); 0117 0118 // Click OK, check URLRequester shows and returns selected file 0119 qApp->sendEvent(fw ? static_cast<QWidget *>(fw) : static_cast<QWidget *>(fileDialog), &keyPressEv); 0120 QCOMPARE(fileDialog->result(), static_cast<int>(QDialog::Accepted)); 0121 QCOMPARE(fileDialog->selectedFiles(), QStringList{filePath2}); 0122 QCOMPARE(req.url().toLocalFile(), filePath2); 0123 } 0124 0125 void KUrlRequesterTest::testComboRequester() 0126 { 0127 QFETCH(bool, editable); 0128 0129 KUrlComboRequester req; 0130 req.show(); 0131 0132 QList<QLineEdit *> lineEdits = req.findChildren<QLineEdit *>(); 0133 QVERIFY(lineEdits.isEmpty()); // no lineedits, only a readonly combo 0134 0135 QSignalSpy textSpy(&req, &KUrlComboRequester::textChanged); 0136 QSignalSpy editSpy(&req, &KUrlComboRequester::textEdited); 0137 0138 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(5, 80) 0139 QSignalSpy returnSpy(&req, qOverload<>(&KUrlComboRequester::returnPressed)); 0140 #endif 0141 QSignalSpy returnWithTextSpy(&req, qOverload<const QString &>(&KUrlComboRequester::returnPressed)); 0142 0143 QVERIFY(!req.comboBox()->isEditable()); 0144 if (editable) { 0145 req.comboBox()->setEditable(true); 0146 0147 const auto text = QStringLiteral("foobar"); 0148 QTest::keyClicks(req.comboBox(), text, Qt::NoModifier); 0149 QCOMPARE(textSpy.size(), text.size()); 0150 QCOMPARE(editSpy.size(), text.size()); 0151 QCOMPARE(textSpy.last().first().toString(), text); 0152 QCOMPARE(editSpy.last().first().toString(), text); 0153 0154 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(5, 80) 0155 QCOMPARE(returnSpy.size(), 0); 0156 #endif 0157 QCOMPARE(returnWithTextSpy.size(), 0); 0158 0159 QTest::keyEvent(QTest::Click, req.comboBox(), Qt::Key_Return); 0160 0161 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(5, 80) 0162 QCOMPARE(returnSpy.size(), 1); 0163 #endif 0164 QCOMPARE(returnWithTextSpy.size(), 1); 0165 QCOMPARE(returnWithTextSpy.last().first().toString(), text); 0166 } else { 0167 const auto url1 = QUrl("file:///foo/bar/1"); 0168 const auto url2 = QUrl("file:///foo/bar/2"); 0169 req.comboBox()->addUrl(url1); 0170 QCOMPARE(textSpy.size(), 1); 0171 QCOMPARE(textSpy.last().first().toUrl(), url1); 0172 0173 req.comboBox()->addUrl(url2); 0174 QCOMPARE(textSpy.size(), 1); 0175 0176 QTest::keyEvent(QTest::Click, req.comboBox(), Qt::Key_Down); 0177 QCOMPARE(textSpy.size(), 2); 0178 QCOMPARE(textSpy.last().first().toUrl(), url2); 0179 0180 // only editable combo boxes get the edit and return signals emitted 0181 QCOMPARE(editSpy.size(), 0); 0182 0183 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(5, 80) 0184 QCOMPARE(returnSpy.size(), 0); 0185 #endif 0186 QCOMPARE(returnWithTextSpy.size(), 0); 0187 } 0188 } 0189 0190 void KUrlRequesterTest::testComboRequester_data() 0191 { 0192 QTest::addColumn<bool>("editable"); 0193 0194 QTest::newRow("read-only") << false; 0195 QTest::newRow("editable") << true; 0196 } 0197 0198 void KUrlRequesterTest::testNameFilters() 0199 { 0200 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(5, 108) 0201 QFETCH(QString, filter); 0202 QFETCH(QStringList, dialogNameFilters); 0203 #endif 0204 QFETCH(QString, nameFilter); 0205 QFETCH(QStringList, nameFilters); 0206 0207 KUrlRequester req; 0208 req.setFileDialogModality(Qt::NonModal); 0209 0210 // Click the button to get the filedialog 0211 req.button()->click(); 0212 QFileDialog *fileDialog = req.findChild<QFileDialog *>(); 0213 QVERIFY(fileDialog); 0214 0215 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(5, 108) 0216 // set filter 0217 req.setFilter(filter); 0218 0219 // check all 0220 QCOMPARE(req.filter(), filter); 0221 QCOMPARE(req.nameFilters(), nameFilters); 0222 QCOMPARE(fileDialog->nameFilters(), dialogNameFilters); 0223 #endif 0224 0225 // set name filter 0226 req.setNameFilter(nameFilter); 0227 0228 // check all 0229 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(5, 108) 0230 QCOMPARE(req.filter(), filter); 0231 #endif 0232 QCOMPARE(req.nameFilters(), nameFilters); 0233 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(5, 108) 0234 QCOMPARE(fileDialog->nameFilters(), dialogNameFilters); 0235 #else 0236 QCOMPARE(fileDialog->nameFilters(), nameFilters); 0237 #endif 0238 0239 // set name filters 0240 req.setNameFilters(nameFilters); 0241 0242 // check all 0243 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(5, 108) 0244 QCOMPARE(req.filter(), filter); 0245 QCOMPARE(req.filter(), filter); 0246 #endif 0247 QCOMPARE(req.nameFilters(), nameFilters); 0248 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(5, 108) 0249 QCOMPARE(fileDialog->nameFilters(), dialogNameFilters); 0250 #else 0251 QCOMPARE(fileDialog->nameFilters(), nameFilters); 0252 #endif 0253 } 0254 0255 void KUrlRequesterTest::testNameFilters_data() 0256 { 0257 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(5, 108) 0258 QTest::addColumn<QString>("filter"); 0259 // result of backward compat to older Plasma Integration plugins: 0260 // which expect a name with each filter entry 0261 // and which has been always fabricated by KUrlRequester, 0262 // so != nameFilters property 0263 QTest::addColumn<QStringList>("dialogNameFilters"); 0264 #endif 0265 QTest::addColumn<QString>("nameFilter"); 0266 QTest::addColumn<QStringList>("nameFilters"); 0267 0268 /* clang-format off */ 0269 QTest::newRow("singleglob-comment") 0270 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(5, 108) 0271 << QStringLiteral("*.foo|Comment") 0272 << QStringList{QStringLiteral("Comment (*.foo)")} 0273 #endif 0274 << QStringLiteral("Comment (*.foo)") 0275 << QStringList{QStringLiteral("Comment (*.foo)")}; 0276 0277 QTest::newRow("singleglob-nocomment") 0278 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(5, 108) 0279 << QStringLiteral("*.foo") 0280 << QStringList{QStringLiteral("*.foo (*.foo)")} 0281 #endif 0282 << QStringLiteral("*.foo") 0283 << QStringList{QStringLiteral("*.foo")}; 0284 0285 QTest::newRow("multiglob-comment") 0286 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(5, 108) 0287 << QStringLiteral("*.foo *.bar|Comment") 0288 << QStringList{QStringLiteral("Comment (*.foo *.bar)")} 0289 #endif 0290 << QStringLiteral("Comment (*.foo *.bar)") 0291 << QStringList{QStringLiteral("Comment (*.foo *.bar)")}; 0292 0293 QTest::newRow("multiglob-nocomment") 0294 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(5, 108) 0295 << QStringLiteral("*.foo *.bar") 0296 << QStringList{QStringLiteral("*.foo *.bar (*.foo *.bar)")} 0297 #endif 0298 << QStringLiteral("*.foo *.bar") 0299 << QStringList{QStringLiteral("*.foo *.bar")}; 0300 0301 QTest::newRow("multilines-comment") 0302 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(5, 108) 0303 << QStringLiteral("*.foo *.bar|Comment\n*.kde|Comment2") 0304 << QStringList{QStringLiteral("Comment (*.foo *.bar)"), QStringLiteral("Comment2 (*.kde)")} 0305 #endif 0306 << QStringLiteral("Comment (*.foo *.bar);;Comment2 (*.kde)") 0307 << QStringList{QStringLiteral("Comment (*.foo *.bar)"), QStringLiteral("Comment2 (*.kde)")}; 0308 0309 QTest::newRow("multilines-nocomment") 0310 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(5, 108) 0311 << QStringLiteral("*.foo *.bar\n*.kde") 0312 << QStringList{QStringLiteral("*.foo *.bar (*.foo *.bar)"), QStringLiteral("*.kde (*.kde)")} 0313 #endif 0314 << QStringLiteral("*.foo *.bar;;*.kde") 0315 << QStringList{QStringLiteral("*.foo *.bar"), QStringLiteral("*.kde")}; 0316 0317 QTest::newRow("multilines-commentmixed") 0318 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(5, 108) 0319 << QStringLiteral("*.foo *.bar|Comment\n*.kde") 0320 << QStringList{QStringLiteral("Comment (*.foo *.bar)"), QStringLiteral("*.kde (*.kde)")} 0321 #endif 0322 << QStringLiteral("Comment (*.foo *.bar);;*.kde") 0323 << QStringList{QStringLiteral("Comment (*.foo *.bar)"), QStringLiteral("*.kde")}; 0324 /* clang-format on */ 0325 } 0326 0327 QTEST_MAIN(KUrlRequesterTest) 0328 #include "kurlrequestertest.moc"