File indexing completed on 2024-04-21 07:41:49

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     QSignalSpy returnWithTextSpy(&req, &KUrlComboRequester::returnPressed);
0139 
0140     QVERIFY(!req.comboBox()->isEditable());
0141     if (editable) {
0142         req.comboBox()->setEditable(true);
0143 
0144         const auto text = QStringLiteral("foobar");
0145         QTest::keyClicks(req.comboBox(), text, Qt::NoModifier);
0146         QCOMPARE(textSpy.size(), text.size());
0147         QCOMPARE(editSpy.size(), text.size());
0148         QCOMPARE(textSpy.last().first().toString(), text);
0149         QCOMPARE(editSpy.last().first().toString(), text);
0150 
0151         QCOMPARE(returnWithTextSpy.size(), 0);
0152 
0153         QTest::keyEvent(QTest::Click, req.comboBox(), Qt::Key_Return);
0154 
0155         QCOMPARE(returnWithTextSpy.size(), 1);
0156         QCOMPARE(returnWithTextSpy.last().first().toString(), text);
0157     } else {
0158         const auto url1 = QUrl("file:///foo/bar/1");
0159         const auto url2 = QUrl("file:///foo/bar/2");
0160         req.comboBox()->addUrl(url1);
0161         QCOMPARE(textSpy.size(), 1);
0162         QCOMPARE(textSpy.last().first().toUrl(), url1);
0163 
0164         req.comboBox()->addUrl(url2);
0165         QCOMPARE(textSpy.size(), 1);
0166 
0167         QTest::keyEvent(QTest::Click, req.comboBox(), Qt::Key_Down);
0168         QCOMPARE(textSpy.size(), 2);
0169         QCOMPARE(textSpy.last().first().toUrl(), url2);
0170 
0171         // only editable combo boxes get the edit and return signals emitted
0172         QCOMPARE(editSpy.size(), 0);
0173 
0174         QCOMPARE(returnWithTextSpy.size(), 0);
0175     }
0176 }
0177 
0178 void KUrlRequesterTest::testComboRequester_data()
0179 {
0180     QTest::addColumn<bool>("editable");
0181 
0182     QTest::newRow("read-only") << false;
0183     QTest::newRow("editable") << true;
0184 }
0185 
0186 void KUrlRequesterTest::testNameFilters()
0187 {
0188     QFETCH(QString, nameFilter);
0189     QFETCH(QStringList, nameFilters);
0190 
0191     KUrlRequester req;
0192     req.setFileDialogModality(Qt::NonModal);
0193 
0194     // Click the button to get the filedialog
0195     req.button()->click();
0196     QFileDialog *fileDialog = req.findChild<QFileDialog *>();
0197     QVERIFY(fileDialog);
0198 
0199     // set name filter
0200     req.setNameFilter(nameFilter);
0201 
0202     // check all
0203     QCOMPARE(req.nameFilters(), nameFilters);
0204     QCOMPARE(fileDialog->nameFilters(), nameFilters);
0205 
0206     // set name filters
0207     req.setNameFilters(nameFilters);
0208 
0209     // check all
0210     QCOMPARE(req.nameFilters(), nameFilters);
0211     QCOMPARE(fileDialog->nameFilters(), nameFilters);
0212 }
0213 
0214 void KUrlRequesterTest::testNameFilters_data()
0215 {
0216     QTest::addColumn<QString>("nameFilter");
0217     QTest::addColumn<QStringList>("nameFilters");
0218 
0219     /* clang-format off */
0220     QTest::newRow("singleglob-comment")
0221         << QStringLiteral("Comment (*.foo)")
0222         << QStringList{QStringLiteral("Comment (*.foo)")};
0223 
0224     QTest::newRow("singleglob-nocomment")
0225         << QStringLiteral("*.foo")
0226         << QStringList{QStringLiteral("*.foo")};
0227 
0228     QTest::newRow("multiglob-comment")
0229         << QStringLiteral("Comment (*.foo *.bar)")
0230         << QStringList{QStringLiteral("Comment (*.foo *.bar)")};
0231 
0232     QTest::newRow("multiglob-nocomment")
0233         << QStringLiteral("*.foo *.bar")
0234         << QStringList{QStringLiteral("*.foo *.bar")};
0235 
0236     QTest::newRow("multilines-comment")
0237         << QStringLiteral("Comment (*.foo *.bar);;Comment2 (*.kde)")
0238         << QStringList{QStringLiteral("Comment (*.foo *.bar)"), QStringLiteral("Comment2 (*.kde)")};
0239 
0240     QTest::newRow("multilines-nocomment")
0241         << QStringLiteral("*.foo *.bar;;*.kde")
0242         << QStringList{QStringLiteral("*.foo *.bar"), QStringLiteral("*.kde")};
0243 
0244     QTest::newRow("multilines-commentmixed")
0245         << QStringLiteral("Comment (*.foo *.bar);;*.kde")
0246         << QStringList{QStringLiteral("Comment (*.foo *.bar)"), QStringLiteral("*.kde")};
0247     /* clang-format on */
0248 }
0249 
0250 QTEST_MAIN(KUrlRequesterTest)
0251 #include "kurlrequestertest.moc"