File indexing completed on 2024-05-12 09:51:09

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