File indexing completed on 2024-04-28 05:45:19
0001 /* 0002 * SPDX-FileCopyrightText: 2017 Elvis Angelaccio <elvis.angelaccio@kde.org> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "dolphinmainwindow.h" 0008 #include "dolphinnewfilemenu.h" 0009 #include "dolphintabpage.h" 0010 #include "dolphintabwidget.h" 0011 #include "dolphinviewcontainer.h" 0012 #include "kitemviews/kitemlistcontainer.h" 0013 #include "testdir.h" 0014 0015 #include <KActionCollection> 0016 #include <KConfig> 0017 #include <KConfigGui> 0018 0019 #include <QAccessible> 0020 #include <QFileSystemWatcher> 0021 #include <QScopedPointer> 0022 #include <QSignalSpy> 0023 #include <QStandardPaths> 0024 #include <QTest> 0025 0026 #include <set> 0027 0028 class DolphinMainWindowTest : public QObject 0029 { 0030 Q_OBJECT 0031 0032 private Q_SLOTS: 0033 void initTestCase(); 0034 void init(); 0035 void testClosingTabsWithSearchBoxVisible(); 0036 void testActiveViewAfterClosingSplitView_data(); 0037 void testActiveViewAfterClosingSplitView(); 0038 void testUpdateWindowTitleAfterClosingSplitView(); 0039 void testUpdateWindowTitleAfterChangingSplitView(); 0040 void testOpenInNewTabTitle(); 0041 void testNewFileMenuEnabled_data(); 0042 void testNewFileMenuEnabled(); 0043 void testWindowTitle_data(); 0044 void testWindowTitle(); 0045 void testPlacesPanelWidthResistance(); 0046 void testGoActions(); 0047 void testOpenFiles(); 0048 void testAccessibilityAncestorTree(); 0049 void testAutoSaveSession(); 0050 void cleanupTestCase(); 0051 0052 private: 0053 QScopedPointer<DolphinMainWindow> m_mainWindow; 0054 }; 0055 0056 void DolphinMainWindowTest::initTestCase() 0057 { 0058 QStandardPaths::setTestModeEnabled(true); 0059 } 0060 0061 void DolphinMainWindowTest::init() 0062 { 0063 m_mainWindow.reset(new DolphinMainWindow()); 0064 } 0065 0066 // See https://bugs.kde.org/show_bug.cgi?id=379135 0067 void DolphinMainWindowTest::testClosingTabsWithSearchBoxVisible() 0068 { 0069 m_mainWindow->openDirectories({QUrl::fromLocalFile(QDir::homePath())}, false); 0070 m_mainWindow->show(); 0071 // Without this call the searchbox doesn't get FocusIn events. 0072 QVERIFY(QTest::qWaitForWindowExposed(m_mainWindow.data())); 0073 QVERIFY(m_mainWindow->isVisible()); 0074 0075 auto tabWidget = m_mainWindow->findChild<DolphinTabWidget *>("tabWidget"); 0076 QVERIFY(tabWidget); 0077 0078 // Show search box on first tab. 0079 tabWidget->currentTabPage()->activeViewContainer()->setSearchModeEnabled(true); 0080 0081 tabWidget->openNewActivatedTab(QUrl::fromLocalFile(QDir::homePath())); 0082 QCOMPARE(tabWidget->count(), 2); 0083 0084 // Triggers the crash in bug #379135. 0085 tabWidget->closeTab(); 0086 QCOMPARE(tabWidget->count(), 1); 0087 } 0088 0089 void DolphinMainWindowTest::testActiveViewAfterClosingSplitView_data() 0090 { 0091 QTest::addColumn<bool>("closeLeftView"); 0092 0093 QTest::newRow("close left view") << true; 0094 QTest::newRow("close right view") << false; 0095 } 0096 0097 void DolphinMainWindowTest::testActiveViewAfterClosingSplitView() 0098 { 0099 m_mainWindow->openDirectories({QUrl::fromLocalFile(QDir::homePath())}, false); 0100 m_mainWindow->show(); 0101 QVERIFY(QTest::qWaitForWindowExposed(m_mainWindow.data())); 0102 QVERIFY(m_mainWindow->isVisible()); 0103 0104 auto tabWidget = m_mainWindow->findChild<DolphinTabWidget *>("tabWidget"); 0105 QVERIFY(tabWidget); 0106 QVERIFY(tabWidget->currentTabPage()->primaryViewContainer()); 0107 QVERIFY(!tabWidget->currentTabPage()->secondaryViewContainer()); 0108 0109 // Open split view. 0110 m_mainWindow->actionCollection()->action(QStringLiteral("split_view"))->trigger(); 0111 QVERIFY(tabWidget->currentTabPage()->splitViewEnabled()); 0112 QVERIFY(tabWidget->currentTabPage()->secondaryViewContainer()); 0113 0114 // Make sure the right view is the active one. 0115 auto leftViewContainer = tabWidget->currentTabPage()->primaryViewContainer(); 0116 auto rightViewContainer = tabWidget->currentTabPage()->secondaryViewContainer(); 0117 QVERIFY(!leftViewContainer->isActive()); 0118 QVERIFY(rightViewContainer->isActive()); 0119 0120 QFETCH(bool, closeLeftView); 0121 if (closeLeftView) { 0122 // Activate left view. 0123 leftViewContainer->setActive(true); 0124 QVERIFY(leftViewContainer->isActive()); 0125 QVERIFY(!rightViewContainer->isActive()); 0126 0127 // Close left view. The secondary view (which was on the right) will become the primary one and must be active. 0128 m_mainWindow->actionCollection()->action(QStringLiteral("split_view"))->trigger(); 0129 QVERIFY(!leftViewContainer->isActive()); 0130 QVERIFY(rightViewContainer->isActive()); 0131 QCOMPARE(rightViewContainer, tabWidget->currentTabPage()->activeViewContainer()); 0132 } else { 0133 // Close right view. The left view will become active. 0134 m_mainWindow->actionCollection()->action(QStringLiteral("split_view"))->trigger(); 0135 QVERIFY(leftViewContainer->isActive()); 0136 QVERIFY(!rightViewContainer->isActive()); 0137 QCOMPARE(leftViewContainer, tabWidget->currentTabPage()->activeViewContainer()); 0138 } 0139 } 0140 0141 // Test case for bug #385111 0142 void DolphinMainWindowTest::testUpdateWindowTitleAfterClosingSplitView() 0143 { 0144 m_mainWindow->openDirectories({QUrl::fromLocalFile(QDir::homePath())}, false); 0145 m_mainWindow->show(); 0146 QVERIFY(QTest::qWaitForWindowExposed(m_mainWindow.data())); 0147 QVERIFY(m_mainWindow->isVisible()); 0148 0149 auto tabWidget = m_mainWindow->findChild<DolphinTabWidget *>("tabWidget"); 0150 QVERIFY(tabWidget); 0151 QVERIFY(tabWidget->currentTabPage()->primaryViewContainer()); 0152 QVERIFY(!tabWidget->currentTabPage()->secondaryViewContainer()); 0153 0154 // Open split view. 0155 m_mainWindow->actionCollection()->action(QStringLiteral("split_view"))->trigger(); 0156 QVERIFY(tabWidget->currentTabPage()->splitViewEnabled()); 0157 QVERIFY(tabWidget->currentTabPage()->secondaryViewContainer()); 0158 0159 // Make sure the right view is the active one. 0160 auto leftViewContainer = tabWidget->currentTabPage()->primaryViewContainer(); 0161 auto rightViewContainer = tabWidget->currentTabPage()->secondaryViewContainer(); 0162 QVERIFY(!leftViewContainer->isActive()); 0163 QVERIFY(rightViewContainer->isActive()); 0164 0165 // Activate left view. 0166 leftViewContainer->setActive(true); 0167 QVERIFY(leftViewContainer->isActive()); 0168 QVERIFY(!rightViewContainer->isActive()); 0169 0170 // Close split view. The secondary view (which was on the right) will become the primary one and must be active. 0171 m_mainWindow->actionCollection()->action(QStringLiteral("split_view"))->trigger(); 0172 QVERIFY(!leftViewContainer->isActive()); 0173 QVERIFY(rightViewContainer->isActive()); 0174 QCOMPARE(rightViewContainer, tabWidget->currentTabPage()->activeViewContainer()); 0175 0176 // Change URL and make sure we emit the currentUrlChanged signal (which triggers the window title update). 0177 QSignalSpy currentUrlChangedSpy(tabWidget, &DolphinTabWidget::currentUrlChanged); 0178 tabWidget->currentTabPage()->activeViewContainer()->setUrl(QUrl::fromLocalFile(QDir::rootPath())); 0179 QCOMPARE(currentUrlChangedSpy.count(), 1); 0180 } 0181 0182 // Test case for bug #402641 0183 void DolphinMainWindowTest::testUpdateWindowTitleAfterChangingSplitView() 0184 { 0185 m_mainWindow->openDirectories({QUrl::fromLocalFile(QDir::homePath())}, false); 0186 m_mainWindow->show(); 0187 QVERIFY(QTest::qWaitForWindowExposed(m_mainWindow.data())); 0188 QVERIFY(m_mainWindow->isVisible()); 0189 0190 auto tabWidget = m_mainWindow->findChild<DolphinTabWidget *>("tabWidget"); 0191 QVERIFY(tabWidget); 0192 0193 // Open split view. 0194 m_mainWindow->actionCollection()->action(QStringLiteral("split_view"))->trigger(); 0195 QVERIFY(tabWidget->currentTabPage()->splitViewEnabled()); 0196 0197 auto leftViewContainer = tabWidget->currentTabPage()->primaryViewContainer(); 0198 auto rightViewContainer = tabWidget->currentTabPage()->secondaryViewContainer(); 0199 0200 // Store old window title. 0201 const auto oldTitle = m_mainWindow->windowTitle(); 0202 0203 // Change URL in the right view and make sure the title gets updated. 0204 rightViewContainer->setUrl(QUrl::fromLocalFile(QDir::rootPath())); 0205 QVERIFY(m_mainWindow->windowTitle() != oldTitle); 0206 0207 // Activate back the left view and check whether the old title gets restored. 0208 leftViewContainer->setActive(true); 0209 QCOMPARE(m_mainWindow->windowTitle(), oldTitle); 0210 } 0211 0212 // Test case for bug #397910 0213 void DolphinMainWindowTest::testOpenInNewTabTitle() 0214 { 0215 m_mainWindow->openDirectories({QUrl::fromLocalFile(QDir::homePath())}, false); 0216 m_mainWindow->show(); 0217 QVERIFY(QTest::qWaitForWindowExposed(m_mainWindow.data())); 0218 QVERIFY(m_mainWindow->isVisible()); 0219 0220 auto tabWidget = m_mainWindow->findChild<DolphinTabWidget *>("tabWidget"); 0221 QVERIFY(tabWidget); 0222 0223 tabWidget->openNewTab(QUrl::fromLocalFile(QDir::tempPath())); 0224 QCOMPARE(tabWidget->count(), 2); 0225 QVERIFY(tabWidget->tabText(0) != tabWidget->tabText(1)); 0226 if (!tabWidget->tabIcon(0).isNull() && !tabWidget->tabIcon(1).isNull()) { 0227 QCOMPARE(QStringLiteral("inode-directory"), tabWidget->tabIcon(0).name()); 0228 QCOMPARE(QStringLiteral("inode-directory"), tabWidget->tabIcon(1).name()); 0229 } 0230 } 0231 0232 void DolphinMainWindowTest::testNewFileMenuEnabled_data() 0233 { 0234 QTest::addColumn<QUrl>("activeViewUrl"); 0235 QTest::addColumn<bool>("expectedEnabled"); 0236 0237 QTest::newRow("home") << QUrl::fromLocalFile(QDir::homePath()) << true; 0238 QTest::newRow("root") << QUrl::fromLocalFile(QDir::rootPath()) << false; 0239 QTest::newRow("trash") << QUrl::fromUserInput(QStringLiteral("trash:/")) << false; 0240 } 0241 0242 void DolphinMainWindowTest::testNewFileMenuEnabled() 0243 { 0244 QFETCH(QUrl, activeViewUrl); 0245 m_mainWindow->openDirectories({activeViewUrl}, false); 0246 m_mainWindow->show(); 0247 QVERIFY(QTest::qWaitForWindowExposed(m_mainWindow.data())); 0248 QVERIFY(m_mainWindow->isVisible()); 0249 0250 auto newFileMenu = m_mainWindow->findChild<DolphinNewFileMenu *>("new_menu"); 0251 QVERIFY(newFileMenu); 0252 0253 QFETCH(bool, expectedEnabled); 0254 QTRY_COMPARE(newFileMenu->isEnabled(), expectedEnabled); 0255 } 0256 0257 void DolphinMainWindowTest::testWindowTitle_data() 0258 { 0259 QTest::addColumn<QUrl>("activeViewUrl"); 0260 QTest::addColumn<QString>("expectedWindowTitle"); 0261 0262 // TODO: this test should enforce the english locale. 0263 QTest::newRow("home") << QUrl::fromLocalFile(QDir::homePath()) << QStringLiteral("Home"); 0264 QTest::newRow("home with trailing slash") << QUrl::fromLocalFile(QStringLiteral("%1/").arg(QDir::homePath())) << QStringLiteral("Home"); 0265 QTest::newRow("trash") << QUrl::fromUserInput(QStringLiteral("trash:/")) << QStringLiteral("Trash"); 0266 } 0267 0268 void DolphinMainWindowTest::testWindowTitle() 0269 { 0270 QFETCH(QUrl, activeViewUrl); 0271 m_mainWindow->openDirectories({activeViewUrl}, false); 0272 m_mainWindow->show(); 0273 QVERIFY(QTest::qWaitForWindowExposed(m_mainWindow.data())); 0274 QVERIFY(m_mainWindow->isVisible()); 0275 0276 QFETCH(QString, expectedWindowTitle); 0277 QCOMPARE(m_mainWindow->windowTitle(), expectedWindowTitle); 0278 } 0279 0280 /** 0281 * The places panel will resize itself if any of the other widgets requires too much horizontal space 0282 * but a user never wants the size of the places panel to change unless they resized it themselves explicitly. 0283 */ 0284 void DolphinMainWindowTest::testPlacesPanelWidthResistance() 0285 { 0286 m_mainWindow->openDirectories({QUrl::fromLocalFile(QDir::homePath())}, false); 0287 m_mainWindow->show(); 0288 m_mainWindow->resize(800, m_mainWindow->height()); // make sure the size is sufficient so a places panel resize shouldn't be necessary. 0289 QVERIFY(QTest::qWaitForWindowExposed(m_mainWindow.data())); 0290 QVERIFY(m_mainWindow->isVisible()); 0291 0292 QWidget *placesPanel = reinterpret_cast<QWidget *>(m_mainWindow->m_placesPanel); 0293 QVERIFY2(QTest::qWaitFor( 0294 [&]() { 0295 return placesPanel && placesPanel->isVisible() && placesPanel->width() > 0; 0296 }, 0297 5000), 0298 "The test couldn't be initialised properly. The places panel should be visible."); 0299 QTest::qWait(100); 0300 const int initialPlacesPanelWidth = placesPanel->width(); 0301 0302 m_mainWindow->actionCollection()->action(QStringLiteral("split_view"))->trigger(); // enable split view (starts animation) 0303 QTest::qWait(300); // wait for animation 0304 QCOMPARE(placesPanel->width(), initialPlacesPanelWidth); 0305 0306 m_mainWindow->actionCollection()->action(QStringLiteral("show_filter_bar"))->trigger(); 0307 QCOMPARE(placesPanel->width(), initialPlacesPanelWidth); 0308 0309 // Make all selection mode bars appear and test for each that this doesn't affect the places panel's width. 0310 // One of the bottom bars (SelectionMode::BottomBar::GeneralContents) only shows up when at least one item is selected so we do that before we begin iterating. 0311 m_mainWindow->actionCollection()->action(KStandardAction::name(KStandardAction::SelectAll))->trigger(); 0312 for (int selectionModeStates = SelectionMode::BottomBar::CopyContents; selectionModeStates != SelectionMode::BottomBar::RenameContents; 0313 selectionModeStates++) { 0314 const auto contents = static_cast<SelectionMode::BottomBar::Contents>(selectionModeStates); 0315 m_mainWindow->slotSetSelectionMode(true, contents); 0316 QTest::qWait(20); // give time for a paint/resize 0317 QCOMPARE(placesPanel->width(), initialPlacesPanelWidth); 0318 } 0319 0320 m_mainWindow->actionCollection()->action(KStandardAction::name(KStandardAction::Find))->trigger(); 0321 QCOMPARE(placesPanel->width(), initialPlacesPanelWidth); 0322 0323 #if HAVE_BALOO 0324 m_mainWindow->actionCollection()->action(QStringLiteral("show_information_panel"))->setChecked(true); // toggle visible 0325 QCOMPARE(placesPanel->width(), initialPlacesPanelWidth); 0326 #endif 0327 0328 #if HAVE_TERMINAL 0329 m_mainWindow->actionCollection()->action(QStringLiteral("show_terminal_panel"))->setChecked(true); // toggle visible 0330 QCOMPARE(placesPanel->width(), initialPlacesPanelWidth); 0331 #endif 0332 0333 m_mainWindow->actionCollection()->action(QStringLiteral("split_view"))->trigger(); // disable split view (starts animation) 0334 QCOMPARE(placesPanel->width(), initialPlacesPanelWidth); 0335 0336 #if HAVE_BALOO 0337 m_mainWindow->actionCollection()->action(QStringLiteral("show_information_panel"))->trigger(); // toggle invisible 0338 QCOMPARE(placesPanel->width(), initialPlacesPanelWidth); 0339 #endif 0340 0341 #if HAVE_TERMINAL 0342 m_mainWindow->actionCollection()->action(QStringLiteral("show_terminal_panel"))->trigger(); // toggle invisible 0343 QCOMPARE(placesPanel->width(), initialPlacesPanelWidth); 0344 #endif 0345 0346 m_mainWindow->showMaximized(); 0347 QCOMPARE(placesPanel->width(), initialPlacesPanelWidth); 0348 0349 QTest::qWait(300); // wait for split view closing animation 0350 QCOMPARE(placesPanel->width(), initialPlacesPanelWidth); 0351 } 0352 0353 void DolphinMainWindowTest::testGoActions() 0354 { 0355 QScopedPointer<TestDir> testDir{new TestDir()}; 0356 testDir->createDir("a"); 0357 testDir->createDir("b"); 0358 testDir->createDir("b/b-1"); 0359 testDir->createFile("b/b-2"); 0360 testDir->createDir("c"); 0361 QUrl childDirUrl(QDir::cleanPath(testDir->url().toString() + "/b")); 0362 m_mainWindow->openDirectories({childDirUrl}, false); // Open "b" dir 0363 m_mainWindow->show(); 0364 QVERIFY(QTest::qWaitForWindowExposed(m_mainWindow.data())); 0365 QVERIFY(m_mainWindow->isVisible()); 0366 QVERIFY(!m_mainWindow->actionCollection()->action(KStandardAction::name(KStandardAction::Forward))->isEnabled()); 0367 0368 m_mainWindow->actionCollection()->action(KStandardAction::name(KStandardAction::Up))->trigger(); 0369 /** 0370 * Now, after going "up" in the file hierarchy (to "testDir"), the folder one has emerged from ("b") should have keyboard focus. 0371 * This is especially important when a user wants to peek into multiple folders in quick succession. 0372 */ 0373 QSignalSpy spyDirectoryLoadingCompleted(m_mainWindow->m_activeViewContainer->view(), &DolphinView::directoryLoadingCompleted); 0374 QVERIFY(spyDirectoryLoadingCompleted.wait()); 0375 QVERIFY(QTest::qWaitFor([&]() { 0376 return !m_mainWindow->actionCollection()->action(QStringLiteral("stop"))->isEnabled(); 0377 })); // "Stop" command should be disabled because it finished loading 0378 QTest::qWait(500); // Somehow the item we emerged from doesn't have keyboard focus yet if we don't wait a split second. 0379 const QUrl parentDirUrl = m_mainWindow->activeViewContainer()->url(); 0380 QVERIFY(parentDirUrl != childDirUrl); 0381 0382 // The item we just emerged from should now have keyboard focus but this doesn't necessarily mean that it is selected. 0383 // To test if it has keyboard focus, we press "Down" to select "c" below and then "Up" so the folder "b" we just emerged from is actually selected. 0384 m_mainWindow->actionCollection()->action(QStringLiteral("compact"))->trigger(); 0385 QTest::keyClick(m_mainWindow->activeViewContainer()->view()->m_container, Qt::Key::Key_Down, Qt::NoModifier); 0386 QCOMPARE(m_mainWindow->m_activeViewContainer->view()->selectedItems().count(), 1); 0387 QTest::keyClick(m_mainWindow->activeViewContainer()->view()->m_container, Qt::Key::Key_Up, Qt::NoModifier); 0388 QCOMPARE(m_mainWindow->m_activeViewContainer->view()->selectedItems().count(), 1); 0389 QTest::keyClick(m_mainWindow->activeViewContainer()->view()->m_container, Qt::Key::Key_Enter, Qt::NoModifier); 0390 QVERIFY(spyDirectoryLoadingCompleted.wait()); 0391 QCOMPARE(m_mainWindow->activeViewContainer()->url(), childDirUrl); 0392 QVERIFY(m_mainWindow->isUrlOpen(childDirUrl.toString())); 0393 0394 // Go back to the parent folder 0395 m_mainWindow->actionCollection()->action(KStandardAction::name(KStandardAction::Back))->trigger(); 0396 QVERIFY(spyDirectoryLoadingCompleted.wait()); 0397 QTest::qWait(100); // Somehow the item we emerged from doesn't have keyboard focus yet if we don't wait a split second. 0398 QCOMPARE(m_mainWindow->activeViewContainer()->url(), parentDirUrl); 0399 QVERIFY(m_mainWindow->isUrlOpen(parentDirUrl.toString())); 0400 0401 // Open a new tab for the "b" child dir and verify that this doesn't interfere with anything. 0402 QTest::keyClick(m_mainWindow->activeViewContainer()->view()->m_container, Qt::Key::Key_Enter, Qt::ControlModifier); // Open new inactive tab 0403 QVERIFY(m_mainWindow->m_tabWidget->count() == 2); 0404 QCOMPARE(m_mainWindow->activeViewContainer()->url(), parentDirUrl); 0405 QVERIFY(m_mainWindow->isUrlOpen(parentDirUrl.toString())); 0406 QVERIFY(!m_mainWindow->actionCollection()->action(QStringLiteral("undo_close_tab"))->isEnabled()); 0407 0408 // Go forward to the child folder. 0409 m_mainWindow->actionCollection()->action(KStandardAction::name(KStandardAction::Forward))->trigger(); 0410 QVERIFY(spyDirectoryLoadingCompleted.wait()); 0411 QCOMPARE(m_mainWindow->activeViewContainer()->url(), childDirUrl); 0412 QCOMPARE(m_mainWindow->m_activeViewContainer->view()->selectedItems().count(), 0); // There was no action in this view yet that would warrant a selection. 0413 0414 // Go back to the parent folder. 0415 m_mainWindow->actionCollection()->action(KStandardAction::name(KStandardAction::Back))->trigger(); 0416 QVERIFY(spyDirectoryLoadingCompleted.wait()); 0417 QTest::qWait(100); // Somehow the item we emerged from doesn't have keyboard focus yet if we don't wait a split second. 0418 QCOMPARE(m_mainWindow->activeViewContainer()->url(), parentDirUrl); 0419 QVERIFY(m_mainWindow->isUrlOpen(parentDirUrl.toString())); 0420 0421 // Close current tab and see if the "go" actions are correctly disabled in the remaining tab that was never active until now and shows the "b" dir 0422 m_mainWindow->actionCollection()->action(KStandardAction::name(KStandardAction::Close))->trigger(); // Close current tab 0423 QVERIFY(m_mainWindow->m_tabWidget->count() == 1); 0424 QCOMPARE(m_mainWindow->activeViewContainer()->url(), childDirUrl); 0425 QCOMPARE(m_mainWindow->m_activeViewContainer->view()->selectedItems().count(), 0); // There was no action in this tab yet that would warrant a selection. 0426 QVERIFY(!m_mainWindow->actionCollection()->action(KStandardAction::name(KStandardAction::Back))->isEnabled()); 0427 QVERIFY(!m_mainWindow->actionCollection()->action(KStandardAction::name(KStandardAction::Forward))->isEnabled()); 0428 QVERIFY(m_mainWindow->actionCollection()->action(QStringLiteral("undo_close_tab"))->isEnabled()); 0429 } 0430 0431 void DolphinMainWindowTest::testOpenFiles() 0432 { 0433 QScopedPointer<TestDir> testDir{new TestDir()}; 0434 QString testDirUrl(QDir::cleanPath(testDir->url().toString())); 0435 testDir->createDir("a"); 0436 testDir->createDir("a/b"); 0437 testDir->createDir("a/b/c"); 0438 testDir->createDir("a/b/c/d"); 0439 m_mainWindow->openDirectories({testDirUrl}, false); 0440 m_mainWindow->show(); 0441 0442 // We only see the unselected "a" folder in the test dir. There are no other tabs. 0443 QVERIFY(m_mainWindow->isUrlOpen(testDirUrl)); 0444 QVERIFY(m_mainWindow->isItemVisibleInAnyView(testDirUrl + "/a")); 0445 QVERIFY(!m_mainWindow->isUrlOpen(testDirUrl + "/a")); 0446 QVERIFY(!m_mainWindow->isItemVisibleInAnyView(testDirUrl + "/a/b")); 0447 QCOMPARE(m_mainWindow->m_tabWidget->count(), 1); 0448 QCOMPARE(m_mainWindow->m_tabWidget->currentIndex(), 0); 0449 QCOMPARE(m_mainWindow->m_activeViewContainer->view()->selectedItems().count(), 0); 0450 0451 // "a" is already in view, so "opening" "a" should simply select it without opening a new tab. 0452 m_mainWindow->openFiles({testDirUrl + "/a"}, false); 0453 QTRY_COMPARE(m_mainWindow->m_activeViewContainer->view()->selectedItems().count(), 1); 0454 QCOMPARE(m_mainWindow->m_tabWidget->count(), 1); 0455 QVERIFY(m_mainWindow->isItemVisibleInAnyView(testDirUrl + "/a")); 0456 0457 // "b" is not in view, so "opening" "b" should open a new active tab of the parent folder "a" and select "b" there. 0458 m_mainWindow->openFiles({testDirUrl + "/a/b"}, false); 0459 QTRY_VERIFY(m_mainWindow->isUrlOpen(testDirUrl + "/a")); 0460 QCOMPARE(m_mainWindow->m_tabWidget->count(), 2); 0461 QCOMPARE(m_mainWindow->m_tabWidget->currentIndex(), 1); 0462 QTRY_VERIFY(m_mainWindow->isItemVisibleInAnyView(testDirUrl + "/a/b")); 0463 QVERIFY2(!m_mainWindow->isUrlOpen(testDirUrl + "/a/b"), "The directory b is supposed to be visible but not open in its own tab."); 0464 QTRY_COMPARE(m_mainWindow->m_activeViewContainer->view()->selectedItems().count(), 1); 0465 0466 QVERIFY(m_mainWindow->isUrlOpen(testDirUrl)); 0467 QVERIFY(m_mainWindow->isItemVisibleInAnyView(testDirUrl + "/a")); 0468 // "a" is still in view in the first tab, so "opening" "a" should switch to the first tab and select "a" there. 0469 m_mainWindow->openFiles({testDirUrl + "/a"}, false); 0470 QCOMPARE(m_mainWindow->m_tabWidget->count(), 2); 0471 QCOMPARE(m_mainWindow->m_tabWidget->currentIndex(), 0); 0472 QVERIFY(m_mainWindow->isUrlOpen(testDirUrl)); 0473 QVERIFY(m_mainWindow->isUrlOpen(testDirUrl + "/a")); 0474 0475 // Directory "a" is already open in the second tab in which "b" is selected, so opening the directory "a" should switch to that tab. 0476 m_mainWindow->openDirectories({testDirUrl + "/a"}, false); 0477 QCOMPARE(m_mainWindow->m_tabWidget->count(), 2); 0478 QCOMPARE(m_mainWindow->m_tabWidget->currentIndex(), 1); 0479 0480 // In the details view mode directories can be expanded, which changes if openFiles() needs to open a new tab or not to open a file. 0481 m_mainWindow->actionCollection()->action(QStringLiteral("details"))->trigger(); 0482 QTRY_VERIFY(m_mainWindow->activeViewContainer()->view()->itemsExpandable()); 0483 0484 // Expand the already selected "b" with the right arrow key. This should make "c" visible. 0485 QVERIFY2(!m_mainWindow->isItemVisibleInAnyView(testDirUrl + "/a/b/c"), "The parent folder wasn't expanded yet, so c shouldn't be visible."); 0486 QTest::keyClick(m_mainWindow->activeViewContainer()->view()->m_container, Qt::Key::Key_Right); 0487 QTRY_VERIFY(m_mainWindow->isItemVisibleInAnyView(testDirUrl + "/a/b/c")); 0488 QVERIFY2(!m_mainWindow->isUrlOpen(testDirUrl + "/a/b"), "b is supposed to be expanded, however it shouldn't be open in its own tab."); 0489 QVERIFY(m_mainWindow->isUrlOpen(testDirUrl + "/a")); 0490 0491 // Switch to first tab by opening it even though it is already open. 0492 m_mainWindow->openDirectories({testDirUrl}, false); 0493 QCOMPARE(m_mainWindow->m_tabWidget->count(), 2); 0494 QCOMPARE(m_mainWindow->m_tabWidget->currentIndex(), 0); 0495 0496 // "c" is in view in the second tab because "b" is expanded there, so "opening" "c" should switch to that tab and select "c" there. 0497 m_mainWindow->openFiles({testDirUrl + "/a/b/c"}, false); 0498 QCOMPARE(m_mainWindow->m_tabWidget->count(), 2); 0499 QCOMPARE(m_mainWindow->m_tabWidget->currentIndex(), 1); 0500 QTRY_COMPARE(m_mainWindow->m_activeViewContainer->view()->selectedItems().count(), 1); 0501 QVERIFY(m_mainWindow->isUrlOpen(testDirUrl)); 0502 QVERIFY(m_mainWindow->isUrlOpen(testDirUrl + "/a")); 0503 0504 // Opening the directory "c" on the other hand will open it in a new tab even though it is already visible in the view 0505 // because openDirecories() and openFiles() serve different purposes. One opens views at urls, the other selects files within views. 0506 m_mainWindow->openDirectories({testDirUrl + "/a/b/c/d", testDirUrl + "/a/b/c"}, true); 0507 QCOMPARE(m_mainWindow->m_tabWidget->count(), 3); 0508 QCOMPARE(m_mainWindow->m_tabWidget->currentIndex(), 2); 0509 QVERIFY(m_mainWindow->m_tabWidget->currentTabPage()->splitViewEnabled()); 0510 QVERIFY(m_mainWindow->isItemVisibleInAnyView(testDirUrl + "/a/b/c")); // It should still be visible in the second tab. 0511 QTRY_COMPARE(m_mainWindow->m_activeViewContainer->view()->selectedItems().count(), 0); 0512 QVERIFY(m_mainWindow->isUrlOpen(testDirUrl + "/a/b/c/d")); 0513 QVERIFY(m_mainWindow->isUrlOpen(testDirUrl + "/a/b/c")); 0514 0515 // "c" is in view in the second tab because "b" is expanded there, 0516 // so "opening" "c" should switch to that tab even though "c" as a directory is open in the current tab. 0517 m_mainWindow->openFiles({testDirUrl + "/a/b/c"}, false); 0518 QCOMPARE(m_mainWindow->m_tabWidget->count(), 3); 0519 QCOMPARE(m_mainWindow->m_tabWidget->currentIndex(), 1); 0520 QVERIFY2(m_mainWindow->isItemVisibleInAnyView(testDirUrl + "/a/b/c/d"), "It should be visible in the secondary view of the third tab."); 0521 0522 // Select "b" and un-expand it with the left arrow key. This should make "c" invisible. 0523 m_mainWindow->openFiles({testDirUrl + "/a/b"}, false); 0524 QTest::keyClick(m_mainWindow->activeViewContainer()->view()->m_container, Qt::Key::Key_Left); 0525 QTRY_VERIFY(!m_mainWindow->isItemVisibleInAnyView(testDirUrl + "/a/b/c")); 0526 0527 // "d" is in view in the third tab in the secondary view, so "opening" "d" should select that view. 0528 m_mainWindow->openFiles({testDirUrl + "/a/b/c/d"}, false); 0529 QCOMPARE(m_mainWindow->m_tabWidget->count(), 3); 0530 QCOMPARE(m_mainWindow->m_tabWidget->currentIndex(), 2); 0531 QVERIFY(m_mainWindow->m_tabWidget->currentTabPage()->secondaryViewContainer()->isActive()); 0532 QTRY_COMPARE(m_mainWindow->m_activeViewContainer->view()->selectedItems().count(), 1); 0533 } 0534 0535 void DolphinMainWindowTest::testAccessibilityAncestorTree() 0536 { 0537 m_mainWindow->openDirectories({QUrl::fromLocalFile(QDir::homePath())}, false); 0538 m_mainWindow->show(); 0539 QVERIFY(QTest::qWaitForWindowExposed(m_mainWindow.data())); 0540 QVERIFY(m_mainWindow->isVisible()); 0541 0542 QAccessibleInterface *accessibleInterfaceOfMainWindow = QAccessible::queryAccessibleInterface(m_mainWindow.get()); 0543 Q_CHECK_PTR(accessibleInterfaceOfMainWindow); 0544 0545 // We will test the accessibility of objects traversing forwards and backwards. 0546 int testedObjectsSizeAfterTraversingForwards = 0; 0547 for (int i = 0; i < 2; i++) { 0548 std::tuple<Qt::Key, Qt::KeyboardModifier> focusChainTraversalKeyCombination = {Qt::Key::Key_Tab, Qt::NoModifier}; 0549 if (i) { 0550 focusChainTraversalKeyCombination = {Qt::Key::Key_Tab, Qt::ShiftModifier}; 0551 } 0552 0553 // We will do accessibility checks for every object that gets focus. Focus will be changed using the focusChainTraversalKeyCombination. 0554 std::set<const QObject *> testedObjects; // Makes sure we stop testing when we arrive at an item that was already tested. 0555 while (qApp->focusObject() && !testedObjects.count(qApp->focusObject())) { 0556 const auto currentlyFocusedObject = qApp->focusObject(); 0557 0558 QAccessibleInterface *accessibleInterface = QAccessible::queryAccessibleInterface(currentlyFocusedObject); 0559 // The accessibleInterfaces of focused objects might themselves have children. 0560 // We go down that hierarchy as far as possible and then test the ancestor tree from there. 0561 while (accessibleInterface->childCount() > 0) { 0562 accessibleInterface = accessibleInterface->child(0); 0563 } 0564 while (accessibleInterface != accessibleInterfaceOfMainWindow) { 0565 QVERIFY2(accessibleInterface, 0566 qPrintable(QString("%1's accessibleInterface or one of its accessible children doesn't have the main window as an ancestor.") 0567 .arg(currentlyFocusedObject->metaObject()->className()))); 0568 accessibleInterface = accessibleInterface->parent(); 0569 } 0570 0571 testedObjects.insert(currentlyFocusedObject); // Add it to testedObjects so we won't test it again later. 0572 QTest::keyClick(m_mainWindow.get(), std::get<0>(focusChainTraversalKeyCombination), std::get<1>(focusChainTraversalKeyCombination)); 0573 QVERIFY2(currentlyFocusedObject != qApp->focusObject(), 0574 "The focus chain is broken. The focused object should have changed after pressing the focusChainTraversalKeyCombination."); 0575 } 0576 0577 if (i == 0) { 0578 testedObjectsSizeAfterTraversingForwards = testedObjects.size(); 0579 } else { 0580 QCOMPARE(testedObjects.size(), testedObjectsSizeAfterTraversingForwards); // The size after traversing backwards is different than 0581 // after going forwards which is probably not intended. 0582 } 0583 } 0584 } 0585 0586 void DolphinMainWindowTest::testAutoSaveSession() 0587 { 0588 m_mainWindow->openDirectories({QUrl::fromLocalFile(QDir::homePath())}, false); 0589 m_mainWindow->show(); 0590 QVERIFY(QTest::qWaitForWindowExposed(m_mainWindow.data())); 0591 QVERIFY(m_mainWindow->isVisible()); 0592 0593 // Create config file 0594 KConfigGui::setSessionConfig(QStringLiteral("dolphin"), QStringLiteral("dolphin")); 0595 KConfig *config = KConfigGui::sessionConfig(); 0596 m_mainWindow->saveGlobalProperties(config); 0597 m_mainWindow->savePropertiesInternal(config, 1); 0598 config->sync(); 0599 0600 // Setup watcher for config file changes 0601 const QString configFileName = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + "/" + KConfigGui::sessionConfig()->name(); 0602 QFileSystemWatcher *configWatcher = new QFileSystemWatcher({configFileName}, this); 0603 QSignalSpy spySessionSaved(configWatcher, &QFileSystemWatcher::fileChanged); 0604 0605 // Enable session autosave. 0606 m_mainWindow->setSessionAutoSaveEnabled(true); 0607 m_mainWindow->m_sessionSaveTimer->setInterval(200); // Lower the interval to speed up the testing 0608 0609 // Open a new tab 0610 auto tabWidget = m_mainWindow->findChild<DolphinTabWidget *>("tabWidget"); 0611 QVERIFY(tabWidget); 0612 tabWidget->openNewActivatedTab(QUrl::fromLocalFile(QDir::tempPath())); 0613 QCOMPARE(tabWidget->count(), 2); 0614 0615 // Wait till a session save occurs 0616 QVERIFY(spySessionSaved.wait(60000)); 0617 0618 // Disable session autosave. 0619 m_mainWindow->setSessionAutoSaveEnabled(false); 0620 } 0621 0622 void DolphinMainWindowTest::cleanupTestCase() 0623 { 0624 m_mainWindow->showNormal(); 0625 m_mainWindow->actionCollection()->action(QStringLiteral("split_view"))->setChecked(false); // disable split view (starts animation) 0626 0627 #if HAVE_BALOO 0628 m_mainWindow->actionCollection()->action(QStringLiteral("show_information_panel"))->setChecked(false); // hide panel 0629 #endif 0630 0631 #if HAVE_TERMINAL 0632 m_mainWindow->actionCollection()->action(QStringLiteral("show_terminal_panel"))->setChecked(false); // hide panel 0633 #endif 0634 0635 // Quit Dolphin to save the hiding of panels and make sure that normal Quit doesn't crash. 0636 m_mainWindow->actionCollection()->action(KStandardAction::name(KStandardAction::Quit))->trigger(); 0637 } 0638 0639 QTEST_MAIN(DolphinMainWindowTest) 0640 0641 #include "dolphinmainwindowtest.moc"