cui/source/dialogs/SpellDialog.cxx        |    2 -
 cui/uiconfig/ui/spellingdialog.ui         |    6 +--
 include/test/a11y/AccessibilityTools.hxx  |    8 ++++
 sc/qa/uitest/calc_tests8/tdf125051.py     |    2 -
 sw/qa/extras/accessibility/dialogs.cxx    |   55 ++++++++++++++++++++++++++++++
 sw/qa/uitest/writer_tests4/spellDialog.py |    2 -
 test/source/a11y/AccessibilityTools.cxx   |   17 +++++++++
 7 files changed, 86 insertions(+), 6 deletions(-)

New commits:
commit 39b15dc8f0adec9e856a39cf319c7c8ae6750043
Author:     Michael Weghorn <m.wegh...@posteo.de>
AuthorDate: Fri Nov 8 13:56:08 2024 +0100
Commit:     Michael Weghorn <m.wegh...@posteo.de>
CommitDate: Fri Nov 8 16:55:08 2024 +0100

    tdf#155447 a11y: Add test for accessible IDs expected by Orca
    
    Add a new AccessibilityTools::getAccessibleObjectForId
    helper that can be used in a11y tests to identify an object
    by its accessible ID.
    
    Add a test that checks that the accessible ID of the
    spelling dialog matches what Orca's logic expects
    in order to identify it, and that more UI elements
    with expected accessible IDs exist.
    
    This is to prevent breaking Orca logic without noticing.
    Any changes affecting this test should be discussed with
    the Orca maintainer first.
    
    See the comment above the newly added test for more details
    and a link to the corresponding Orca commits.
    
    The new test initially only checks that objects with corresponding
    IDs exist, could be extended to verify more of their
    properties as needed.
    
    Logic for checking availability of a dictionary, needed for
    spell check was taken over from the existing tdf155705 test in
    sw/qa/extras/accessibility/tdf155705.cxx, whose sample doc
    is also used.
    
    Change-Id: I0c5629272a89a4a570e080e62e99b6c105369cf9
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/176281
    Tested-by: Jenkins
    Reviewed-by: Michael Weghorn <m.wegh...@posteo.de>

diff --git a/include/test/a11y/AccessibilityTools.hxx 
b/include/test/a11y/AccessibilityTools.hxx
index 10d67f2a5338..687275b097a7 100644
--- a/include/test/a11y/AccessibilityTools.hxx
+++ b/include/test/a11y/AccessibilityTools.hxx
@@ -52,6 +52,14 @@ public:
         const css::uno::Reference<css::accessibility::XAccessible>& xAcc,
         const std::function<
             bool(const 
css::uno::Reference<css::accessibility::XAccessibleContext>&)>& cPredicate);
+
+    static css::uno::Reference<css::accessibility::XAccessibleContext> 
getAccessibleObjectForId(
+        const css::uno::Reference<css::accessibility::XAccessibleContext>& 
xCtx,
+        std::u16string_view sId);
+    static css::uno::Reference<css::accessibility::XAccessibleContext>
+    getAccessibleObjectForId(const 
css::uno::Reference<css::accessibility::XAccessible>& xacc,
+                             std::u16string_view sId);
+
     static css::uno::Reference<css::accessibility::XAccessibleContext> 
getAccessibleObjectForRole(
         const css::uno::Reference<css::accessibility::XAccessibleContext>& 
xCtx, sal_Int16 role);
     static css::uno::Reference<css::accessibility::XAccessibleContext>
diff --git a/sw/qa/extras/accessibility/dialogs.cxx 
b/sw/qa/extras/accessibility/dialogs.cxx
index 18662ce7cd9b..1072a2c47b1f 100644
--- a/sw/qa/extras/accessibility/dialogs.cxx
+++ b/sw/qa/extras/accessibility/dialogs.cxx
@@ -9,6 +9,10 @@
 
 #include <com/sun/star/awt/Key.hpp>
 #include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/XAccessibleContext2.hpp>
+#include <com/sun/star/linguistic2/LinguServiceManager.hpp>
+#include <com/sun/star/linguistic2/XLinguServiceManager2.hpp>
+#include <com/sun/star/linguistic2/XSpellChecker1.hpp>
 
 #include <vcl/scheduler.hxx>
 
@@ -16,6 +20,7 @@
 #include <test/a11y/AccessibilityTools.hxx>
 
 using namespace css;
+using namespace css::accessibility;
 
 // FIXME: dialog API doesn't work on macos yet
 #if !defined(MACOSX)
@@ -190,6 +195,56 @@ CPPUNIT_TEST_FIXTURE(test::AccessibleTestBase, 
BasicTestFrameDialog)
                          collectText());
 }
 
+/* Verify that UI elements in the spell check dialog have the accessible IDs
+ * set that that Orca screen reader's logic to identify them depends on,
+ * see tdf#155447 and following Orca commits:
+ *
+ * 
https://gitlab.gnome.org/GNOME/orca/-/commit/6221f4ecf542646a80e47ee7236380360f0e1a85
+ * 
https://gitlab.gnome.org/GNOME/orca/-/commit/40a2d302eb52295433fd84e6c254a7dbe5108a24
+ *
+ * (Changes should be discussed with the Orca maintainer first.)
+ *
+ * While the Orca logic depends only on case-insensitive name starting
+ * with a certain string, this test uses the full accessible ID
+ * (which matches the GtkBuilder ID in ./cui/uiconfig/ui/spellingdialog.ui)
+ * in order to identify the elements.
+ */
+CPPUNIT_TEST_FIXTURE(test::AccessibleTestBase, SpellingDialog)
+{
+    // spell check depends on dictionary being available, so skip test if 
unavailable
+    uno::Reference<linguistic2::XLinguServiceManager2> xLSM2
+        = linguistic2::LinguServiceManager::create(m_xContext);
+    uno::Reference<linguistic2::XSpellChecker1> 
xSpell(xLSM2->getSpellChecker(), uno::UNO_QUERY);
+    if (!xSpell.is() || 
!xSpell->hasLanguage(static_cast<sal_uInt16>(LANGUAGE_ENGLISH_US)))
+        return;
+
+    
loadFromSrc(u"/sw/qa/extras/accessibility/testdocuments/tdf155705.fodt"_ustr);
+
+    auto dialogWaiter = awaitDialog(u"Spelling: English (USA)", [](Dialog& 
dialog) {
+        uno::Reference<XAccessible> xDialogAcc = dialog.getAccessible();
+
+        uno::Reference<XAccessibleContext2> 
xDialogContext(xDialogAcc->getAccessibleContext(),
+                                                           uno::UNO_QUERY);
+        CPPUNIT_ASSERT(xDialogContext.is());
+        CPPUNIT_ASSERT_EQUAL(u"SpellingDialog"_ustr, 
xDialogContext->getAccessibleId());
+
+        uno::Reference<XAccessibleContext> xSentenceAcc
+            = AccessibilityTools::getAccessibleObjectForId(xDialogContext, 
u"errorsentence");
+        CPPUNIT_ASSERT(xSentenceAcc.is());
+
+        uno::Reference<XAccessibleContext> xSuggestionsAcc
+            = AccessibilityTools::getAccessibleObjectForId(xDialogContext, 
u"suggestionslb");
+        CPPUNIT_ASSERT(xSuggestionsAcc.is());
+
+        
CPPUNIT_ASSERT(dialog.tabTo(accessibility::AccessibleRole::PUSH_BUTTON, 
u"Close"));
+        dialog.postKeyEventAsync(0, awt::Key::RETURN);
+        Scheduler::ProcessEventsToIdle();
+    });
+
+    CPPUNIT_ASSERT(activateMenuItem(u"Tools", u"Spelling..."));
+    CPPUNIT_ASSERT(dialogWaiter->waitEndDialog());
+}
+
 #endif //defined(MACOSX)
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/test/source/a11y/AccessibilityTools.cxx 
b/test/source/a11y/AccessibilityTools.cxx
index e972836198c7..b91d431db9b5 100644
--- a/test/source/a11y/AccessibilityTools.cxx
+++ b/test/source/a11y/AccessibilityTools.cxx
@@ -70,6 +70,23 @@ AccessibilityTools::getAccessibleObjectForPredicate(
     return getAccessibleObjectForPredicate(xAcc->getAccessibleContext(), 
cPredicate);
 }
 
+uno::Reference<accessibility::XAccessibleContext> 
AccessibilityTools::getAccessibleObjectForId(
+    const uno::Reference<accessibility::XAccessibleContext>& xCtx, 
std::u16string_view sId)
+{
+    return getAccessibleObjectForPredicate(
+        xCtx, [&](const uno::Reference<accessibility::XAccessibleContext>& 
xObjCtx) {
+            uno::Reference<accessibility::XAccessibleContext2> 
xContext2(xObjCtx, uno::UNO_QUERY);
+            return (xContext2 && xContext2->getAccessibleId() == sId);
+        });
+}
+
+css::uno::Reference<css::accessibility::XAccessibleContext>
+AccessibilityTools::getAccessibleObjectForId(
+    const css::uno::Reference<css::accessibility::XAccessible>& xacc, const 
std::u16string_view sId)
+{
+    return getAccessibleObjectForId(xacc->getAccessibleContext(), sId);
+}
+
 uno::Reference<accessibility::XAccessibleContext> 
AccessibilityTools::getAccessibleObjectForRole(
     const uno::Reference<accessibility::XAccessibleContext>& xCtx, sal_Int16 
role)
 {
commit 4b7316e2ff6e63bbf73e0bcf04f90b807d9829ad
Author:     Michael Weghorn <m.wegh...@posteo.de>
AuthorDate: Fri Nov 8 11:34:11 2024 +0100
Commit:     Michael Weghorn <m.wegh...@posteo.de>
CommitDate: Fri Nov 8 16:55:01 2024 +0100

    tdf#155447 a11y: Set accessible ID that Orca expects
    
    Orca commit [1]
    
        commit 40a2d302eb52295433fd84e6c254a7dbe5108a24
        Author: Joanmarie Diggs <jdi...@igalia.com>
        Date:   Thu Nov 7 14:15:07 2024 +0100
    
            Spellcheck: Check for accessible id in more places
    
            Do the following case-insensitive checks:
            * If the object's accessible id starts with "suggestions" treat it
              as the suggestions list.
            * If the object's accessible id starts with "replacement" treat it
              as the object (likely entry) which contains the proposed 
replacement.
            * If the label's/widget's accessible id starts with "error" treat
              it as the container displaying the misspelled word.
    
            Note that the first of the three is based on what LO 25.2 currently
            exposes ("suggestionslb"). The other two are not in use yet, but 
adding
            them facilitates implementation in, and getting feedback from, apps 
and
            toolkits.
    
            Also modify the existing check for the window. We were doing an 
exact
            match on "SpellingDialog". Making that case insensitive and 
limiting to
            starts with "spelling" works with the current LO implementation and
            removes an implementation detail ("dialog").
    
    introduced logic to identify more UI elements in the spelling
    dialog by their accessible ID.
    
    Adjust the GtkBuilder ID (which gets reported as accessible ID as well)
    of the drawing area that holds the misspelt text to match the
    expectations as mentioned in the commit message above.
    
    [1] 
https://gitlab.gnome.org/GNOME/orca/-/commit/40a2d302eb52295433fd84e6c254a7dbe5108a24
    
    Change-Id: I0658de40a47e46a6b2451dfb4ee30d1457b9f9e3
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/176280
    Reviewed-by: Michael Weghorn <m.wegh...@posteo.de>
    Tested-by: Jenkins

diff --git a/cui/source/dialogs/SpellDialog.cxx 
b/cui/source/dialogs/SpellDialog.cxx
index dd017cf3de15..0b2e1ad3c6df 100644
--- a/cui/source/dialogs/SpellDialog.cxx
+++ b/cui/source/dialogs/SpellDialog.cxx
@@ -190,7 +190,7 @@ SpellDialog::SpellDialog(SpellDialogChildWindow* 
pChildWindow,
     , m_xUndoPB(m_xBuilder->weld_button(u"undo"_ustr))
     , m_xClosePB(m_xBuilder->weld_button(u"close"_ustr))
     , m_xToolbar(m_xBuilder->weld_toolbar(u"toolbar"_ustr))
-    , m_xSentenceEDWeld(new weld::CustomWeld(*m_xBuilder, u"sentence"_ustr, 
*m_xSentenceED))
+    , m_xSentenceEDWeld(new weld::CustomWeld(*m_xBuilder, 
u"errorsentence"_ustr, *m_xSentenceED))
 {
     m_xSentenceED->SetSpellDialog(this);
     m_xSentenceED->Init(m_xToolbar.get());
diff --git a/cui/uiconfig/ui/spellingdialog.ui 
b/cui/uiconfig/ui/spellingdialog.ui
index 9aa0ae3145e7..80103891ba71 100644
--- a/cui/uiconfig/ui/spellingdialog.ui
+++ b/cui/uiconfig/ui/spellingdialog.ui
@@ -165,12 +165,12 @@
                     <property name="visible">True</property>
                     <property name="can-focus">False</property>
                     <child>
-                      <object class="GtkDrawingArea" id="sentence">
+                      <object class="GtkDrawingArea" id="errorsentence">
                         <property name="visible">True</property>
                         <property name="can-focus">True</property>
                         <property name="events">GDK_BUTTON_MOTION_MASK | 
GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | 
GDK_KEY_RELEASE_MASK | GDK_FOCUS_CHANGE_MASK | GDK_STRUCTURE_MASK</property>
                         <child internal-child="accessible">
-                          <object class="AtkObject" id="sentence-atkobject">
+                          <object class="AtkObject" 
id="errorsentence-atkobject">
                             <property name="AtkObject::accessible-description" 
translatable="yes" context="spellingdialog|extended_tip|sentence">Displays the 
sentence with the misspelled word highlighted. Edit the word or the sentence, 
or click one of the suggestions in the text box below.</property>
                           </object>
                         </child>
@@ -275,7 +275,7 @@
                     <property name="hexpand">True</property>
                     <property name="label" translatable="yes" 
context="spellingdialog|notindictft">_Not in Dictionary</property>
                     <property name="use-underline">True</property>
-                    <property name="mnemonic-widget">sentence</property>
+                    <property name="mnemonic-widget">errorsentence</property>
                     <property name="xalign">0</property>
                   </object>
                   <packing>
diff --git a/sc/qa/uitest/calc_tests8/tdf125051.py 
b/sc/qa/uitest/calc_tests8/tdf125051.py
index c099e2e3d368..abf9836e97f9 100644
--- a/sc/qa/uitest/calc_tests8/tdf125051.py
+++ b/sc/qa/uitest/calc_tests8/tdf125051.py
@@ -23,7 +23,7 @@ class tdf125051(UITestCase):
             enter_text_to_cell(gridwin, "A1", "teext")
             gridwin.executeAction("SELECT", mkPropertyValues({"CELL": "A1"}))
             with 
self.ui_test.execute_dialog_through_command(".uno:SpellDialog", 
close_button="close") as xDialog:
-                xSentence = xDialog.getChild("sentence")
+                xSentence = xDialog.getChild("errorsentence")
                 self.assertEqual("teext", get_state_as_dict(xSentence)['Text'])
 
                 xLanguagelb = xDialog.getChild("languagelb")
diff --git a/sw/qa/uitest/writer_tests4/spellDialog.py 
b/sw/qa/uitest/writer_tests4/spellDialog.py
index 17898c4dc5b7..c01e78e10593 100644
--- a/sw/qa/uitest/writer_tests4/spellDialog.py
+++ b/sw/qa/uitest/writer_tests4/spellDialog.py
@@ -189,7 +189,7 @@ frog, dogg, catt"""
 
         with self.ui_test.load_file(get_url_for_data_file("tdf157992.odt")) as 
document:
             with 
self.ui_test.execute_modeless_dialog_through_command(".uno:SpellingAndGrammarDialog",
 close_button="") as xDialog:
-                sentence = xDialog.getChild('sentence')
+                sentence = xDialog.getChild('errorsentence')
                 sentence.executeAction('TYPE', 
mkPropertyValues({'KEYCODE':'RIGHT'}))
                 sentence.executeAction('TYPE', 
mkPropertyValues({'KEYCODE':'DELETE'}))
                 sentence.executeAction('TYPE', 
mkPropertyValues({'KEYCODE':'DELETE'}))

Reply via email to