Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / include / test / a11y / accessibletestbase.hxx
blobe23c2e12467ebbea565e60c2f2e2d39575eb48a4
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #pragma once
12 #include <test/testdllapi.hxx>
14 #include <deque>
15 #include <string>
17 #include <com/sun/star/accessibility/AccessibleRole.hpp>
18 #include <com/sun/star/accessibility/XAccessible.hpp>
19 #include <com/sun/star/accessibility/XAccessibleAction.hpp>
20 #include <com/sun/star/accessibility/XAccessibleContext.hpp>
21 #include <com/sun/star/awt/XDialog2.hpp>
22 #include <com/sun/star/awt/XWindow.hpp>
23 #include <com/sun/star/frame/Desktop.hpp>
24 #include <com/sun/star/lang/XComponent.hpp>
25 #include <com/sun/star/uno/Reference.hxx>
27 #include <vcl/ITiledRenderable.hxx>
28 #include <vcl/window.hxx>
30 #include <rtl/ustring.hxx>
31 #include <test/bootstrapfixture.hxx>
32 #include <test/a11y/eventposter.hxx>
34 #include "AccessibilityTools.hxx"
36 namespace test
38 class OOO_DLLPUBLIC_TEST AccessibleTestBase : public test::BootstrapFixture
40 protected:
41 css::uno::Reference<css::frame::XDesktop2> mxDesktop;
42 css::uno::Reference<css::lang::XComponent> mxDocument;
43 css::uno::Reference<css::awt::XWindow> mxWindow;
45 static bool isDocumentRole(const sal_Int16 role);
47 virtual void load(const rtl::OUString& sURL);
48 virtual void loadFromSrc(const rtl::OUString& sSrcPath);
49 void close();
50 css::uno::Reference<css::accessibility::XAccessibleContext> getWindowAccessibleContext();
51 virtual css::uno::Reference<css::accessibility::XAccessibleContext>
52 getDocumentAccessibleContext();
54 void documentPostKeyEvent(int nType, int nCharCode, int nKeyCode)
56 vcl::ITiledRenderable* pTiledRenderable
57 = dynamic_cast<vcl::ITiledRenderable*>(mxDocument.get());
58 CPPUNIT_ASSERT(pTiledRenderable);
59 pTiledRenderable->postKeyEvent(nType, nCharCode, nKeyCode);
62 static css::uno::Reference<css::accessibility::XAccessibleContext> getFirstRelationTargetOfType(
63 const css::uno::Reference<css::accessibility::XAccessibleContext>& xContext,
64 sal_Int16 relationType);
66 /**
67 * @brief Tries to list all children of an accessible
68 * @param xContext An XAccessibleContext object
69 * @returns The list of all children (but no more than @c AccessibilityTools::MAX_CHILDREN)
71 * This fetches children of @p xContext. This would ideally just be the same than iterating
72 * over children the regular way up to @c AccessibilityTools::MAX_CHILDREN, but unfortunately
73 * some components (Writer, Impress, ...) do not provide all their children the regular way and
74 * require specifics to include them.
76 * There is no guarantee on *which* children are returned if there are more than
77 * @c AccessibilityTools::MAX_CHILDREN -- yet they will always be the same in a given context.
79 virtual std::deque<css::uno::Reference<css::accessibility::XAccessibleContext>>
80 getAllChildren(const css::uno::Reference<css::accessibility::XAccessibleContext>& xContext);
82 void dumpA11YTree(const css::uno::Reference<css::accessibility::XAccessibleContext>& xContext,
83 const int depth = 0);
85 css::uno::Reference<css::accessibility::XAccessibleContext>
86 getItemFromName(const css::uno::Reference<css::accessibility::XAccessibleContext>& xMenuCtx,
87 std::u16string_view name);
88 bool
89 activateMenuItem(const css::uno::Reference<css::accessibility::XAccessibleAction>& xAction);
90 /* just convenience not to have to query accessibility::XAccessibleAction manually */
91 bool activateMenuItem(const css::uno::Reference<css::accessibility::XAccessibleContext>& xCtx)
93 return activateMenuItem(css::uno::Reference<css::accessibility::XAccessibleAction>(
94 xCtx, css::uno::UNO_QUERY_THROW));
97 /* convenience to get a menu item from a list of menu item names. Unlike
98 * getItemFromName(context, name), this requires subsequently found items to implement
99 * XAccessibleAction, as each but the last item will be activated before looking for
100 * the next one, to account for the fact menus might not be fully populated before being
101 * activated. */
102 template <typename... Ts>
103 css::uno::Reference<css::accessibility::XAccessibleContext>
104 getItemFromName(const css::uno::Reference<css::accessibility::XAccessibleContext>& xMenuCtx,
105 std::u16string_view name, Ts... names)
107 auto item = getItemFromName(xMenuCtx, name);
108 CPPUNIT_ASSERT(item.is());
109 activateMenuItem(item);
110 return getItemFromName(item, names...);
113 /* convenience to activate an item by its name and all its parent menus up to xMenuCtx.
114 * @see getItemFromName() */
115 template <typename... Ts>
116 bool
117 activateMenuItem(const css::uno::Reference<css::accessibility::XAccessibleContext>& xMenuCtx,
118 Ts... names)
120 auto item = getItemFromName(xMenuCtx, names...);
121 CPPUNIT_ASSERT(item.is());
122 return activateMenuItem(item);
125 /* convenience to activate an item by its name and all its parent menus up to the main window
126 * menu bar */
127 template <typename... Ts> bool activateMenuItem(Ts... names)
129 auto menuBar = AccessibilityTools::getAccessibleObjectForRole(
130 getWindowAccessibleContext(), css::accessibility::AccessibleRole::MENU_BAR);
131 CPPUNIT_ASSERT(menuBar.is());
132 return activateMenuItem(menuBar, names...);
136 * @brief Gets the focused accessible object at @p xAcc level or below
137 * @param xAcc An accessible object
138 * @returns The accessible context of the focused object, or @c nullptr
140 * Finds the accessible object context at or under @p xAcc that has the focused state (and is
141 * showing). Normally only one such object should exist in a given hierarchy, but in all cases
142 * this function will return the first one found.
144 * @see AccessibilityTools::getAccessibleObjectForPredicate()
146 static css::uno::Reference<css::accessibility::XAccessibleContext>
147 getFocusedObject(const css::uno::Reference<css::accessibility::XAccessibleContext>& xCtx);
149 static inline css::uno::Reference<css::accessibility::XAccessibleContext>
150 getFocusedObject(const css::uno::Reference<css::accessibility::XAccessible>& xAcc)
152 return getFocusedObject(xAcc->getAccessibleContext());
156 * @brief Navigates through focusable elements using the Tab keyboard shortcut.
157 * @param xRoot The root element to look for focused elements in.
158 * @param role The accessible role of the element to tab to.
159 * @param name The accessible name of the element to tab to.
160 * @param pEventPosterHelper Pointer to a @c EventPosterHelper instance, or @c nullptr to obtain
161 * it from @p xRoot.
162 * @returns The element tabbed to, or @c nullptr if not found.
164 * Navigates through focusable elements in the top level containing @p xRoot using the Tab
165 * keyboard key until the focused elements matches @p role and @p name.
167 * Note that usually @p xRoot should be the toplevel accessible, or at least contain all
168 * focusable elements within that window. It is however *not* a requirement, but only elements
169 * actually inside it will be candidate for a match, and thus if focus goes outside it, it might
170 * lead to not finding the target element.
172 * If @p pEventPosterHelper is @c nullptr, this function will try to construct one from
173 * @p xRoot. @see EventPosterHelper.
175 static css::uno::Reference<css::accessibility::XAccessibleContext>
176 tabTo(const css::uno::Reference<css::accessibility::XAccessible>& xRoot, const sal_Int16 role,
177 const std::u16string_view name,
178 const EventPosterHelperBase* pEventPosterHelper = nullptr);
180 static bool tabTo(const css::uno::Reference<css::accessibility::XAccessible>& xRoot,
181 const css::uno::Reference<css::accessibility::XAccessibleContext>& xChild,
182 const EventPosterHelperBase* pEventPosterHelper = nullptr);
184 #if !defined(MACOSX)
185 /* Dialog handling */
186 class Dialog : public test::AccessibleEventPosterHelper
188 private:
189 bool mbAutoClose;
190 css::uno::Reference<css::awt::XDialog2> mxDialog2;
191 css::uno::Reference<css::accessibility::XAccessible> mxAccessible;
193 public:
194 Dialog(css::uno::Reference<css::awt::XDialog2>& xDialog2, bool bAutoClose = true);
195 virtual ~Dialog();
197 void setAutoClose(bool bAutoClose) { mbAutoClose = bAutoClose; }
199 css::uno::Reference<css::accessibility::XAccessible> getAccessible() const
201 return mxAccessible;
204 void close(sal_Int32 result = VclResponseType::RET_CANCEL);
206 css::uno::Reference<css::accessibility::XAccessibleContext>
207 tabTo(const sal_Int16 role, const std::u16string_view name)
209 return AccessibleTestBase::tabTo(getAccessible(), role, name, this);
212 bool tabTo(const css::uno::Reference<css::accessibility::XAccessibleContext>& xChild)
214 return AccessibleTestBase::tabTo(getAccessible(), xChild, this);
218 class DialogWaiter
220 public:
221 virtual ~DialogWaiter() {}
224 * @brief Waits for the associated dialog to close
225 * @param nTimeoutMs Maximum delay to wait the dialog for
226 * @returns @c true if the dialog closed, @c false if timeout was reached
228 * @throws css::uno::RuntimeException if an unexpected dialog popped up instead of the
229 * expected one.
230 * @throws Any exception that the user callback supplied to awaitDialog() might have thrown.
232 virtual bool waitEndDialog(sal_uInt64 nTimeoutMs = 3000) = 0;
236 * @brief Helper to call user code when a given dialog opens
237 * @param name The title of the dialog window to wait for
238 * @param callback The user code to run when the given dialog opens
239 * @param bAutoClose Whether to automatically cancel the dialog after the user code finished, if
240 * the dialog is still there. You should leave this to @c true unless you
241 * know exactly what you are doing, see below.
242 * @returns A @c DialogWaiter wrapper on which call waitEndDialog() after having triggered the
243 * dialog in some way.
245 * This function makes it fairly easy and safe to execute code once a dialog pops up:
246 * @code
247 * auto waiter = awaitDialog(u"Special Characters", [this](Dialog &dialog) {
248 * // for example, something like this:
249 * // something();
250 * // CPPUNIT_ASSERT(dialog.tabTo(...));
251 * // CPPUNIT_ASSERT(somethingElse);
252 * // dialog.postKeyEventAsync(0, awt::Key::RETURN);
253 * });
254 * CPPUNIT_ASSERT(activateMenuItem(u"Some menu", u"Some Item Triggering a Dialog..."));
255 * CPPUNIT_ASSERT(waiter->waitEndDialog());
256 * @endcode
258 * @note The user code might actually be executed before DialogWaiter::waitEndDialog() is
259 * called. It is actually likely to be called at the time the call that triggers the
260 * dialog happens. However, as letting an exception slip in a event handler is likely to
261 * cause problems, exceptions are forwarded to the DialogWaiter::waitEndDialog() call.
262 * However, note that you cannot rely on something like this:
263 * @code
264 * int foo = 0;
265 * auto waiter = awaitDialog(u"Some Dialog", [&foo](Dialog&) {
266 * CPPUNIT_ASSERT_EQUAL(1, foo);
267 * });
268 * CPPUNIT_ASSERT(activateMenuItem(u"Some menu", u"Some Item Triggering a Dialog..."));
269 * foo = 1; // here, the callback likely already ran as a result of the
270 * // Scheduler::ProcessEventsToIdle() call that activateMenuItem() did.
271 * CPPUNIT_ASSERT(waiter->waitEndDialog());
272 * @endcode
274 * @warning You should almost certainly always leave @p bAutoClose to @c true. If it is set to
275 * @c false, you have to take extreme care:
276 * - The dialog will not be canceled if the user code raises an exception.
277 * - If the dialog is run through Dialog::Execute(), control won't return to the test
278 * body until the dialog is closed. This means that the only ways to execute code
279 * until then is a separate thread or via code dispatched by the main loop.
280 * Thus, you have to make sure you DO close the dialog some way or another yourself
281 * in order for the test code to terminate at some point.
282 * - If the dialog doesn't use Dialog::Execute() but is rather similar to a second
283 * separate window (e.g. non-modal), you might still have to close the dialog before
284 * closing the test document is possible without a CloseVetoException -- which might
285 * badly break the test run.
287 static std::shared_ptr<DialogWaiter> awaitDialog(const std::u16string_view name,
288 std::function<void(Dialog&)> callback,
289 bool bAutoClose = true);
290 #endif //defined(MACOSX)
292 public:
293 virtual void setUp() override;
294 virtual void tearDown() override;
298 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */