#1263 Doc: Add UITester documentation in User Manual

Merged Pseudo commit used to compare (81902cc...b7b6037)
Missing base report.

Unable to compare commits because the base of the pull request did not upload a coverage report.

Changes found in between 81902cc...b7b6037 (pseudo...base) which prevent comparing this pull request.

Showing 15 of 103 files from the diff.
Newly tracked file
traitsui/__init__.py changed.
Newly tracked file
traitsui/wx/enum_editor.py changed.
Other files ignored by Codecov
setup.cfg is new.
.travis.yml has changed.
etstool.py has changed.

@@ -259,6 +259,7 @@
Loading
259 259
260 260
        if action.on_perform is not None:
261 261
            action.on_perform(selection)
262 +
            return
262 263
263 264
        action.perform(selection)
264 265

@@ -116,6 +116,19 @@
Loading
116 116
        parent.Bind(wx.EVT_TEXT_ENTER, self.update_object, id=self.control.GetId())
117 117
        self.set_tooltip()
118 118
119 +
    def dispose(self):
120 +
        """ Disposes of the contents of an editor.
121 +
        """
122 +
        if self.control is not None:   # just in-case
123 +
            parent = self.control.GetParent()
124 +
            parent.Unbind(
125 +
                wx.EVT_TEXT_ENTER,
126 +
                handler=self.update_object,
127 +
                id=self.control.GetId(),
128 +
            )
129 +
            self.control.Unbind(wx.EVT_KILL_FOCUS, handler=self.update_object)
130 +
        super().dispose()
131 +
119 132
    def update_object(self, event):
120 133
        """ Handles the user changing the contents of the edit control.
121 134
        """

@@ -17,8 +17,6 @@
Loading
17 17
applied.
18 18
"""
19 19
20 -
import enum
21 -
22 20
23 21
class Index:
24 22
    """ A locator for locating a target that is uniquely specified by a single
@@ -34,14 +32,6 @@
Loading
34 32
        self.index = index
35 33
36 34
37 -
class NestedUI:
38 -
    """ A locator for locating a nested ``traitsui.ui.UI`` object assuming
39 -
    there is only one. If there are multiple, more location information
40 -
    needs to have been provided already.
41 -
    """
42 -
    pass
43 -
44 -
45 35
class TargetByName:
46 36
    """ A locator for locating the next UI target using a name.
47 37
@@ -64,14 +54,13 @@
Loading
64 54
        self.id = id
65 55
66 56
67 -
class WidgetType(enum.Enum):
68 -
    """ A locator for locating nested widgets within a UI. Many editors will
69 -
    contain many sub-widgets (e.g. a textbox, slider, tabs, buttons, etc.).
70 -
71 -
    For example when working with a range editor, one could call
72 -
    ``tester.find_by_name(ui, "number").locate(locator.WidgetType.textbox)``
73 -
    where number utilizes a Range Editor.
57 +
class Slider:
58 +
    """ A locator for locating a nested slider widget within a UI.
74 59
    """
60 +
    pass
61 +
75 62
76 -
    # A textbox within a UI
77 -
    textbox = "textbox"
63 +
class Textbox:
64 +
    """ A locator for locating a nested textbox widget within a UI.
65 +
    """
66 +
    pass

@@ -17,6 +17,18 @@
Loading
17 17
"""
18 18
19 19
20 +
class SelectedText:
21 +
    """ An object representing an interaction to obtain the displayed (echoed)
22 +
    plain text which is currently selected.
23 +
24 +
    E.g. For a Enum List, with one entry currently selected, the displayed
25 +
    selected text would be the label of that entry.
26 +
27 +
    Implementations should return a ``str``, or None if nothing is selected.
28 +
    """
29 +
    pass
30 +
31 +
20 32
class DisplayedText:
21 33
    """ An object representing an interaction to obtain the displayed (echoed)
22 34
    plain text.

@@ -9,6 +9,8 @@
Loading
9 9
#  Thanks for using Enthought open source!
10 10
#
11 11
12 +
import inspect
13 +
12 14
from traitsui.testing.tester.exceptions import (
13 15
    InteractionNotSupported,
14 16
    LocationNotSupported,
@@ -55,106 +57,32 @@
Loading
55 57
            )
56 58
        return action_to_handler[key]
57 59
60 +
    def get_keys(self, target_class):
61 +
        """ Return all the keys for the given target.
58 62
59 -
class TargetRegistry:
60 -
    """ An object for registering interaction and location resolution logic
61 -
    for different UI target types.
62 -
63 -
    Registering interaction handler (register_handler)
64 -
    --------------------------------------------------
65 -
    The interaction type can be a subclass of any type. There are a few
66 -
    pre-defined interaction types in
67 -
    - ``traitsui.testing.tester.command``
68 -
    - ``traitsui.testing.tester.query``
69 -
70 -
    For example, to simulate clicking a button in TraitsUI's ButtonEditor, the
71 -
    implementation for Qt may look like this::
72 -
73 -
        def mouse_click_qt_button(wrapper, interaction):
74 -
            # wrapper is an instance of UIWrapper
75 -
            wrapper.target.control.click()
76 -
77 -
    The function can then be registered with the target type and an interaction
78 -
    type::
79 -
80 -
        registry = TargetRegistry()
81 -
        registry.register_handler(
82 -
            target_class=traitsui.qt4.button_editor.SimpleEditor,
83 -
            interaction_class=traitsui.testing.tester.command.MouseClick,
84 -
            handler=mouse_click_qt_button,
85 -
        )
86 -
87 -
    Similarly, a wx implementation of clicking a button can be registered
88 -
    to the registry (the content of ``mouse_click_wx_button`` is not shown)::
89 -
90 -
        registry.register_handler(
91 -
            target_class=traitsui.wx.button_editor.SimpleEditor,
92 -
            interaction_class=traitsui.testing.tester.command.MouseClick,
93 -
            handler=mouse_click_wx_button,
94 -
        )
95 -
96 -
    Then this registry can be used with the ``UITester`` and ``UIWrapper`` to
97 -
    support ``UIWrapper.perform`` and ``UIWrapper.inspect``.
98 -
99 -
    Registering location solver (register_solver)
100 -
    ---------------------------------------------
101 -
102 -
    Resolving a location on a UI target is logically similar to making a query
103 -
    for a nested UI target. This query is separated out to support the
104 -
    ``UIWrapper.locate`` method independently of the query method
105 -
    ``UIWrapper.inspect``.
106 -
107 -
    The locator type can be any subclass of ``type``. There are predefined
108 -
    locators in ``traitsui.testing.tester.locator``.
109 -
110 -
    For example, suppose a UI target has a nested UI and a button. Both the
111 -
    nested UI and the button are UI targets that can be located in the
112 -
    container target.
113 -
114 -
    Suppose we have a locator type for the nested UI::
115 -
116 -
        class NestedUI:
117 -
            pass
118 -
119 -
    And there is an enum for the widget type::
120 -
121 -
        class WidgetType(Enum):
122 -
            button = "button"
123 -
124 -
    The solvers for these locators may look like this::
125 -
126 -
        def get_nested_ui(wrapper, _):
127 -
            return wrapper.target._nested_ui
128 -
129 -
        def get_widget_by_type(wrapper, location):
130 -
            if location == WidgetType.button:
131 -
                return wrapper.target._button
132 -
            raise ValueError("Other widget type not supported")
63 +
        Parameters
64 +
        ----------
65 +
        target_class : subclass of type
66 +
            The type of a UI target being operated on.
133 67
134 -
    The first argument is an instance of ``UIWrapper`` that wraps the container
135 -
    UI target, in this case, the UI target that holds a nested UI and a button.
68 +
        Returns
69 +
        -------
70 +
        keys : set
71 +
        """
72 +
        return set(self._target_to_key_to_value.get(target_class, []))
136 73
137 -
    The second argument is an instance of the locator type.
138 74
139 -
    The solvers can then be registered for the container UI target::
75 +
class TargetRegistry:
76 +
    """ An object for registering interaction and location resolution logic
77 +
    for different UI target types.
140 78
141 -
        registry = TargetRegistry()
142 -
        registry.register_solver(
143 -
            target_class=MyUIContainer,
144 -
            locator_class=NestedUI,
145 -
            solver=get_nested_ui,
146 -
        )
147 -
        registry.register_solver(
148 -
            target_class=MyUIContainer,
149 -
            locator_class=WidgetType,
150 -
            solver=get_widget_by_type,
151 -
        )
79 +
    ``register_handler`` supports extending ``UIWrapper.perform`` and
80 +
    ``UIWrapper.inspect`` for a given UI target type and interaction type.
152 81
153 -
    Then this registry can be used with the ``UITester`` and ``UIWrapper`` to
154 -
    support ``UIWrapper.locate``. If the nested UI has other locator solvers,
155 -
    then it would be possible to do chain location resolutions, like this::
82 +
    ``register_solver`` supports extending ``UIWrapper.locate`` for a given
83 +
    UI target type and location type.
156 84
157 -
        container.locate(NestedUI()).locate(NestedUI())
85 +
    See :ref:`advanced-testing` Section in the User Manual for further details.
158 86
    """
159 87
160 88
    def __init__(self):
@@ -220,12 +148,60 @@
Loading
220 148
        handler : callable(UIWrapper, interaction) -> any
221 149
            The function to handle the particular interaction on a target.
222 150
            ``interaction`` should be an instance of ``interaction_class``.
151 +
152 +
        Raises
153 +
        ------
154 +
        InteractionNotSupported
155 +
            If the given target and interaction types are not supported
156 +
            by this registry.
223 157
        """
224 158
        return self._interaction_registry.get_value(
225 159
            target_class=target_class,
226 160
            key=interaction_class,
227 161
        )
228 162
163 +
    def get_interactions(self, target_class):
164 +
        """ Returns all the interactions supported for the given target type.
165 +
166 +
        Parameters
167 +
        ----------
168 +
        target_class : subclass of type
169 +
            The type of a UI target being operated on.
170 +
171 +
        Returns
172 +
        -------
173 +
        interaction_classes : set
174 +
            Supported interaction types for the given target type.
175 +
        """
176 +
        return self._interaction_registry.get_keys(target_class=target_class)
177 +
178 +
    def get_interaction_doc(self, target_class, interaction_class):
179 +
        """ Return the documentation for the given target and locator type.
180 +
181 +
        Parameters
182 +
        ----------
183 +
        target_class : subclass of type
184 +
            The type of a UI target being operated on.
185 +
        interaction_class : subclass of type
186 +
            Any class.
187 +
188 +
        Returns
189 +
        -------
190 +
        doc : str
191 +
192 +
        Raises
193 +
        ------
194 +
        InteractionNotSupported
195 +
            If the given target and interaction types are not supported
196 +
            by this registry.
197 +
        """
198 +
        self._interaction_registry.get_value(
199 +
            target_class=target_class,
200 +
            key=interaction_class,
201 +
        )
202 +
        # This maybe configurable in the future via register_handler
203 +
        return inspect.getdoc(interaction_class)
204 +
229 205
    def register_solver(self, target_class, locator_class, solver):
230 206
        """ Register a solver for resolving the next UI target for the given
231 207
        target type and locator type.
@@ -266,8 +242,50 @@
Loading
266 242
        Raises
267 243
        ------
268 244
        LocationNotSupported
245 +
            If the given locator and target types are not supported.
269 246
        """
270 247
        return self._location_registry.get_value(
271 248
            target_class=target_class,
272 249
            key=locator_class,
273 250
        )
251 +
252 +
    def get_locations(self, target_class):
253 +
        """ Returns all the location types supported for the given target type.
254 +
255 +
        Parameters
256 +
        ----------
257 +
        target_class : subclass of type
258 +
            The type of a UI target being operated on.
259 +
260 +
        Returns
261 +
        -------
262 +
        locators_classes : set
263 +
            Supported locator types for the given target type.
264 +
        """
265 +
        return self._location_registry.get_keys(target_class=target_class)
266 +
267 +
    def get_location_doc(self, target_class, locator_class):
268 +
        """ Return the documentation for the given target and locator type.
269 +
270 +
        Parameters
271 +
        ----------
272 +
        target_class : subclass of type
273 +
            The type of a UI target being operated on.
274 +
        locator_class : subclass of type
275 +
            Any class.
276 +
277 +
        Returns
278 +
        -------
279 +
        doc : str
280 +
281 +
        Raises
282 +
        ------
283 +
        LocationNotSupported
284 +
            If the given locator and target types are not supported.
285 +
        """
286 +
        self._location_registry.get_value(
287 +
            target_class=target_class,
288 +
            key=locator_class,
289 +
        )
290 +
        # This maybe configurable in the future via register_solver
291 +
        return inspect.getdoc(locator_class)

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Unable to process changes.

No base report to compare against.

55 Commits

Hiding 4 contexual commits
Hiding 7 contexual commits
Hiding 41 contexual commits
+12 Files
+386
+426
+14
-54
Pull Request Base Commit
Files Coverage
traitsui 34.6%
Project Totals (295 files) 34.6%
Loading