summaryrefslogtreecommitdiff
path: root/include/test
diff options
context:
space:
mode:
authorColomban Wendling <cwendling@hypra.fr>2022-10-27 19:07:44 +0200
committerMichael Weghorn <m.weghorn@posteo.de>2023-02-24 15:13:39 +0000
commit0ccea0dd6e50199af4a7aae75d691b32c853b177 (patch)
tree6d8de9d8ee55401732645a06fb492a2f34f17d0f /include/test
parentc15412eb96bda1037c12811f5818ed8ce1e603bd (diff)
test: Add accessibility test dialog infrastructure
Interacting with dialogues in tests is non-trivial, so introduce helpers to make it simpler and less error-prone. Add tests for the infrastructure itself as well. Change-Id: I8ea6087a61380194eb2b5ec9f25091db00f5a550 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/142258 Reviewed-by: Michael Weghorn <m.weghorn@posteo.de> Tested-by: Jenkins
Diffstat (limited to 'include/test')
-rw-r--r--include/test/a11y/accessibletestbase.hxx99
1 files changed, 99 insertions, 0 deletions
diff --git a/include/test/a11y/accessibletestbase.hxx b/include/test/a11y/accessibletestbase.hxx
index 50a39f63a7dd..913e24221353 100644
--- a/include/test/a11y/accessibletestbase.hxx
+++ b/include/test/a11y/accessibletestbase.hxx
@@ -24,6 +24,7 @@
#include <com/sun/star/uno/Reference.hxx>
#include <vcl/ITiledRenderable.hxx>
+#include <vcl/window.hxx>
#include <rtl/ustring.hxx>
#include <test/bootstrapfixture.hxx>
@@ -129,6 +130,104 @@ protected:
return activateMenuItem(menuBar, names...);
}
+ /* Dialog handling */
+ class Dialog
+ {
+ friend class AccessibleTestBase;
+
+ private:
+ VclPtr<vcl::Window> mxWindow;
+ bool mbAutoClose;
+
+ Dialog(vcl::Window* pWindow, bool bAutoClose = true);
+
+ public:
+ virtual ~Dialog();
+
+ explicit operator bool() const { return mxWindow && !mxWindow->isDisposed(); }
+ bool operator!() const { return !bool(*this); }
+
+ void setAutoClose(bool bAutoClose) { mbAutoClose = bAutoClose; }
+
+ css::uno::Reference<css::accessibility::XAccessible> getAccessible() const
+ {
+ return mxWindow ? mxWindow->GetAccessible() : nullptr;
+ }
+
+ bool close(sal_Int32 result = VclResponseType::RET_CANCEL);
+ };
+
+ class DialogWaiter
+ {
+ public:
+ virtual ~DialogWaiter() {}
+
+ /**
+ * @brief Waits for the associated dialog to close
+ * @param nTimeoutMs Maximum delay to wait the dialog for
+ * @returns @c true if the dialog closed, @c false if timeout was reached
+ *
+ * @throws css::uno::RuntimeException if an unexpected dialog poped up instead of the
+ * expected one.
+ * @throws Any exception that the user callback supplied to awaitDialog() might have thrown.
+ */
+ virtual bool waitEndDialog(sal_uInt64 nTimeoutMs = 3000) = 0;
+ };
+
+ /**
+ * @brief Helper to call user code when a given dialog opens
+ * @param name The title of the dialog window to wait for
+ * @param callback The user code to run when the given dialog opens
+ * @param bAutoClose Whether to automatically cancel the dialog after the user code finished, if
+ * the dialog is still there. You should leave this to @c true unless you
+ * know exactly what you are doing, see below.
+ * @returns A @c DialogWaiter wrapper on which call waitEndDialog() after having triggered the
+ * dialog in some way.
+ *
+ * This function makes it fairly easy and safe to execute code once a dialog pops up:
+ * @code
+ * auto waiter = awaitDialog(u"Special Characters", [this](Dialog &dialog) {
+ * // for example, something like this:
+ * // something();
+ * // CPPUNIT_ASSERT(somethingElse);
+ * });
+ * CPPUNIT_ASSERT(activateMenuItem(u"Some menu", u"Some Item Triggering a Dialog..."));
+ * CPPUNIT_ASSERT(waiter->waitEndDialog());
+ * @endcode
+ *
+ * @note The user code might actually be executed before DialogWaiter::waitEndDialog() is
+ * called. It is actually likely to be called at the time the call that triggers the
+ * dialog happens. However, as letting an exception slip in a event handler is likely to
+ * cause problems, exceptions are forwarded to the DialogWaiter::waitEndDialog() call.
+ * However, note that you cannot rely on something like this:
+ * @code
+ * int foo = 0;
+ * auto waiter = awaitDialog(u"Some Dialog", [&foo](Dialog&) {
+ * CPPUNIT_ASSERT_EQUAL(1, foo);
+ * });
+ * CPPUNIT_ASSERT(activateMenuItem(u"Some menu", u"Some Item Triggering a Dialog..."));
+ * foo = 1; // here, the callback likely already ran as a result of the
+ * // Scheduler::ProcessEventsToIdle() call that activateMenuItem() did.
+ * CPPUNIT_ASSERT(waiter->waitEndDialog());
+ * @endcode
+ *
+ * @warning You should almost certainly always leave @p bAutoClose to @c true. If it is set to
+ * @c false, you have to take extreme care:
+ * - The dialog will not be canceled if the user code raises an exception.
+ * - If the dialog is run through Dialog::Execute(), control won't return to the test
+ * body until the dialog is closed. This means that the only ways to execute code
+ * until then is a separate thread or via code dispatched by the main loop.
+ * Thus, you have to make sure you DO close the dialog some way or another yourself
+ * in order for the test code to terminate at some point.
+ * - If the dialog doesn't use Dialog::Execute() but is rather similar to a second
+ * separate window (e.g. non-modal), you might still have to close the dialog before
+ * closing the test document is possible without a CloseVetoException -- which might
+ * badly break the test run.
+ */
+ static std::shared_ptr<DialogWaiter> awaitDialog(const std::u16string_view name,
+ std::function<void(Dialog&)> callback,
+ bool bAutoClose = true);
+
public:
virtual void setUp() override;
virtual void tearDown() override;