]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/tests/dolphinmainwindowtest.cpp
Always focus the view after place activation
[dolphin.git] / src / tests / dolphinmainwindowtest.cpp
index 61a72ad51b055d92e2f00ceb9b520031d196c02f..6ac85e4f04a966c54aa4ba99ca71c4f4fdbc190a 100644 (file)
@@ -48,11 +48,12 @@ private Q_SLOTS:
     void testNewFileMenuEnabled();
     void testWindowTitle_data();
     void testWindowTitle();
+    void testFocusLocationBar();
     void testFocusPlacesPanel();
     void testPlacesPanelWidthResistance();
     void testGoActions();
     void testOpenFiles();
-    void testAccessibilityAncestorTree();
+    void testAccessibilityTree();
     void testAutoSaveSession();
     void cleanupTestCase();
 
@@ -389,6 +390,36 @@ void DolphinMainWindowTest::testWindowTitle()
     QCOMPARE(m_mainWindow->windowTitle(), expectedWindowTitle);
 }
 
+void DolphinMainWindowTest::testFocusLocationBar()
+{
+    const QUrl homePathUrl{QUrl::fromLocalFile(QDir::homePath())};
+    m_mainWindow->openDirectories({homePathUrl}, false);
+    m_mainWindow->show();
+    QVERIFY(QTest::qWaitForWindowExposed(m_mainWindow.data()));
+    QVERIFY(m_mainWindow->isVisible());
+
+    QAction *replaceLocationAction = m_mainWindow->actionCollection()->action(QStringLiteral("replace_location"));
+    replaceLocationAction->trigger();
+    QVERIFY(m_mainWindow->activeViewContainer()->urlNavigator()->isAncestorOf(QApplication::focusWidget()));
+    replaceLocationAction->trigger();
+    QVERIFY(m_mainWindow->activeViewContainer()->view()->hasFocus());
+
+    QAction *editableLocationAction = m_mainWindow->actionCollection()->action(QStringLiteral("editable_location"));
+    editableLocationAction->trigger();
+    QVERIFY(m_mainWindow->activeViewContainer()->urlNavigator()->isAncestorOf(QApplication::focusWidget()));
+    QVERIFY(m_mainWindow->activeViewContainer()->urlNavigator()->isUrlEditable());
+    editableLocationAction->trigger();
+    QVERIFY(!m_mainWindow->activeViewContainer()->urlNavigator()->isUrlEditable());
+
+    replaceLocationAction->trigger();
+    QVERIFY(m_mainWindow->activeViewContainer()->urlNavigator()->isAncestorOf(QApplication::focusWidget()));
+
+    // Pressing Escape multiple times should eventually move the focus back to the active view.
+    QTest::keyClick(QApplication::focusWidget(), Qt::Key_Escape); // Focus might not go the view yet because it toggles the editable state of the location bar.
+    QTest::keyClick(QApplication::focusWidget(), Qt::Key_Escape);
+    QVERIFY(m_mainWindow->activeViewContainer()->view()->hasFocus());
+}
+
 void DolphinMainWindowTest::testFocusPlacesPanel()
 {
     m_mainWindow->openDirectories({QUrl::fromLocalFile(QDir::homePath())}, false);
@@ -425,6 +456,18 @@ void DolphinMainWindowTest::testFocusPlacesPanel()
     showPlacesPanelAction->trigger();
     QVERIFY(placesPanel->isVisible());
     QVERIFY2(placesPanel->hasFocus(), "Enabling the Places panel should move keyboard focus there.");
+
+    /// Test that activating a place always moves focus to the view.
+    QTest::keyClick(QApplication::focusWidget(), Qt::Key::Key_Enter);
+    QVERIFY2(m_mainWindow->activeViewContainer()->isAncestorOf(QApplication::focusWidget()),
+             "Activating a place should move focus to the view that loads that place.");
+
+    focusPlacesPanelAction->trigger();
+    QVERIFY(placesPanel->hasFocus());
+
+    QTest::keyClick(QApplication::focusWidget(), Qt::Key::Key_Enter);
+    QVERIFY2(m_mainWindow->activeViewContainer()->isAncestorOf(QApplication::focusWidget()),
+             "Activating a place should move focus to the view even if the view already has that place loaded.");
 }
 
 /**
@@ -718,7 +761,7 @@ void DolphinMainWindowTest::testOpenFiles()
     QTRY_COMPARE(m_mainWindow->m_activeViewContainer->view()->selectedItems().count(), 1);
 }
 
-void DolphinMainWindowTest::testAccessibilityAncestorTree()
+void DolphinMainWindowTest::testAccessibilityTree()
 {
     m_mainWindow->openDirectories({QUrl::fromLocalFile(QDir::homePath())}, false);
     m_mainWindow->show();
@@ -728,7 +771,7 @@ void DolphinMainWindowTest::testAccessibilityAncestorTree()
     QAccessibleInterface *accessibleInterfaceOfMainWindow = QAccessible::queryAccessibleInterface(m_mainWindow.get());
     Q_CHECK_PTR(accessibleInterfaceOfMainWindow);
 
-    // We will test the accessibility of objects traversing forwards and backwards.
+    /// Test the accessibility of objects while traversing forwards (Tab key) and backwards (Shift+Tab).
     int testedObjectsSizeAfterTraversingForwards = 0;
     for (int i = 0; i < 2; i++) {
         std::tuple<Qt::Key, Qt::KeyboardModifier> focusChainTraversalKeyCombination = {Qt::Key::Key_Tab, Qt::NoModifier};
@@ -736,11 +779,42 @@ void DolphinMainWindowTest::testAccessibilityAncestorTree()
             focusChainTraversalKeyCombination = {Qt::Key::Key_Tab, Qt::ShiftModifier};
         }
 
-        // We will do accessibility checks for every object that gets focus. Focus will be changed using the focusChainTraversalKeyCombination.
+        /// @see firstNamedAncestor below.
+        QAccessibleInterface *firstNamedAncestorOfPreviousIteration = nullptr;
+
+        /// Perform accessibility checks for every object that gets focus. Focus will be changed using the focusChainTraversalKeyCombination.
         std::set<const QObject *> testedObjects; // Makes sure we stop testing when we arrive at an item that was already tested.
         while (qApp->focusObject() && !testedObjects.count(qApp->focusObject())) {
             const auto currentlyFocusedObject = qApp->focusObject();
+            const QAccessibleInterface *accessibleIntefaceOfCurrentlyFocusedObject = QAccessible::queryAccessibleInterface(currentlyFocusedObject);
+            QVERIFY(accessibleIntefaceOfCurrentlyFocusedObject);
+
+            /// Test that each object reachable by Tab or Shift+Tab has at least some accessible information. Objects without any accessible information
+            /// are even less useful to accessibility software users than unlabeled buttons are e.g. to sighted users, because unlabeled buttons at least
+            /// convey some information through their placement and icon.
+            if (currentlyFocusedObject != m_mainWindow->m_activeViewContainer->view()->m_container) { // Skip the custom container widget which has no
+                                                                                                      // accessible name on purpose.
+                /**
+                 * The first ancestor with an accessible name is interesting because it is sometimes used to identify an object if the object itself has no
+                 * name. We keep it in mind to check if two subsequent objects without a name can at least be told apart by their first named ancestor.
+                 */
+                QAccessibleInterface *firstNamedAncestor = accessibleIntefaceOfCurrentlyFocusedObject->parent();
+                while (firstNamedAncestor) {
+                    if (!firstNamedAncestor->text(QAccessible::Name).isEmpty()) {
+                        break;
+                    }
+                    firstNamedAncestor = firstNamedAncestor->parent();
+                }
+                QTRY_VERIFY2(!accessibleIntefaceOfCurrentlyFocusedObject->text(QAccessible::Name).isEmpty()
+                                 || (firstNamedAncestor && firstNamedAncestor != firstNamedAncestorOfPreviousIteration),
+                             qPrintable(QStringLiteral("%1's accessibleInterface does not have an accessible name and can not be distinguished from the object"
+                                                       " that had focus previously. Please fix this. You can find this %1 within its parent %2.")
+                                            .arg(currentlyFocusedObject->metaObject()->className())
+                                            .arg(currentlyFocusedObject->parent()->metaObject()->className())));
+                firstNamedAncestorOfPreviousIteration = firstNamedAncestor;
+            }
 
+            /// Test that each accessible interface has the main window as its parent.
             QAccessibleInterface *accessibleInterface = QAccessible::queryAccessibleInterface(currentlyFocusedObject);
             // The accessibleInterfaces of focused objects might themselves have children.
             // We go down that hierarchy as far as possible and then test the ancestor tree from there.
@@ -749,7 +823,7 @@ void DolphinMainWindowTest::testAccessibilityAncestorTree()
             }
             while (accessibleInterface != accessibleInterfaceOfMainWindow) {
                 QVERIFY2(accessibleInterface,
-                         qPrintable(QString("%1's accessibleInterface or one of its accessible children doesn't have the main window as an ancestor.")
+                         qPrintable(QStringLiteral("%1's accessibleInterface or one of its accessible children doesn't have the main window as an ancestor.")
                                         .arg(currentlyFocusedObject->metaObject()->className())));
                 accessibleInterface = accessibleInterface->parent();
             }
@@ -767,6 +841,8 @@ void DolphinMainWindowTest::testAccessibilityAncestorTree()
                                                                                       // after going forwards which is probably not intended.
         }
     }
+    QCOMPARE_GE(testedObjectsSizeAfterTraversingForwards, 12); // The test did not reach many objects while using the Tab key to move through Dolphin. Did the
+                                                               // test run correctly?
 }
 
 void DolphinMainWindowTest::testAutoSaveSession()