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"