The last part of the UI test tutorial introduced the python part of the UI testing framework. In general most times you only need to care about the python part of the test but sometimes you actually need to extend the introspection part of the framework inside of the LibreOffice code. Many UI elements are already supported in the introspection library either directly or through one of its parent classes.
This blog post will give a short summary of how the introspection code works and how support for new elements can be added. In contrast to the other three parts this blog post requires some understanding of the LibreOffice source code and an idea about VCL, our window toolkit.
The introspection library provides two simple UNO interfaces to the outside world. The XUITest interface provides access to static data in VCL. Currently this is limited to executing UNO commands and getting access to the current top level window. Access to this object is provided by UITestCase.xUITest in the python test code. In the future we might extend this interface to provide access to more VCL data that is not otherwise available and is useful for the UI testing.
The more interesting one and the one that abstracts all the different UI objects is the XUIObject interface with the corresponding implementation on the LibreOffice side through UIObjectUnoObj, which just wraps a virtual class called UIObject.
The two important methods are get_state and execute. The get_type and get_name methods should also be overridden but the other methods can often just be taken from the base class.
Adding support for a new object
For now we are just going to talk about UI elements that inherit from vcl::Window, so basically most of our GUI elements. On the introspection side the corresponding class is WindowUIObject, which provides the base class for all vcl::Window based introspection abstraction. All classes inheriting from vcl::Window provide the virtual method GetUITestFactory that returns a factory function for the introspection library.
Adding support for a previously not well covered UI object – all have basic coverage through at least the WindowUIObject – normally requires three steps: First adding a GetUITestFactory method to the UI object class, secondly adding the corresponding factory method and finally implementing the introspection wrapper class.
Most of the time the factory method just casts the passed in vcl::Window pointer to the correct type and create the introspection wrapper object. The actual work is in implementing the actual introspection class for the UI element, which should expose the properties that the tests need as well as the operations that can be done on the UI element.
The inheritance from WindowUIObject or one of its subclasses already provides a number of properties as well as some common operations like typing text.
An example for adding support for a previously unsupported object can be found at . This one has a slightly more complicated factory method but does not add more than a basic property.
Non vcl::Window UI objects
Sometimes there are non-vcl::Window UI objects that need to be wrapped which makes everything a bit more complicated. Examples for this are e.g. tree lists or tables where we want to expose elements of the vcl::Window UI object like single tree entries as own objects. As these objects don’t correspond to actual UI objects in our code we have to employ some tricks to provide them to the introspection library. The basic idea behind supporting such pseudo objects is to hold a pointer to the corresponding vcl::Window based UI object through a VclPtr and a way to get the correct property that represents the object we want to cover. Additionally as these objects don’t have an ID we need to override the get_child and get_children methods inside of the the wrapper class for the vcl::Window. The get_children method should return all the IDs of descendants, and the get_child method should return for each of these IDs the corresponding wrapper object.
Handling missing IDs
As has been mentioned in the first part we identify UI elements through a locally unique ID. Normally this ID is loaded from the UI files that describe our dialogs. However for UI elements that are dynamically generated in the code we need to set the ID in the code. For this case the vcl::Window provides the set_id method.
Hopefully this gave a short overview over the C++ part of the UI testing. The next tutorial will cover the directory layout of the python code together with some information about adding normal UNO code to the python tests.