@@ -16,8 +16,8 @@
Loading
16 16
target_class of choice to one of these as the locator_class. For an example,
17 17
see the implementation of range_editor.
18 18
"""
19 -
20 -
from traitsui.testing.tester.qt4 import registry_helper
19 +
from traitsui.testing.tester import command
20 +
from traitsui.testing.tester.qt4 import helpers, registry_helper
21 21
22 22
23 23
class LocatedTextbox:
@@ -48,3 +48,34 @@
Loading
48 48
            target_class=cls,
49 49
            widget_getter=lambda wrapper: wrapper.target.textbox,
50 50
        )
51 +
52 +
53 +
class LocatedSlider:
54 +
    """ Wrapper class for a located Slider in Qt.
55 +
56 +
    Parameters
57 +
    ----------
58 +
    slider : Instance of QtGui.QSlider
59 +
    """
60 +
61 +
    def __init__(self, slider):
62 +
        self.slider = slider
63 +
64 +
    @classmethod
65 +
    def register(cls, registry):
66 +
        """ Class method to register interactions on a LocatedSlider for the
67 +
        given registry.
68 +
69 +
        If there are any conflicts, an error will occur.
70 +
71 +
        Parameters
72 +
        ----------
73 +
        registry : TargetRegistry
74 +
            The registry being registered to.
75 +
        """
76 +
        registry.register_handler(
77 +
            target_class=cls,
78 +
            interaction_class=command.KeyClick,
79 +
            handler=lambda wrapper, interaction: helpers.key_click_qslider(
80 +
                wrapper.target.slider, interaction, wrapper.delay)
81 +
        )

@@ -12,72 +12,26 @@
Loading
12 12
13 13
from traitsui.wx.check_list_editor import CustomEditor
14 14
from traitsui.testing.tester import command, locator
15 +
from traitsui.testing.tester.common_ui_targets import _BaseSourceWithLocation
15 16
from traitsui.testing.tester.editors.layout import column_major_to_row_major
16 17
from traitsui.testing.tester.wx import helpers
17 18
18 19
19 -
class _IndexedCustomCheckListEditor:
20 -
    """ Wrapper for CheckListEditor + locator.Index """
21 -
22 -
    def __init__(self, target, index):
23 -
        """
24 -
        Parameters
25 -
        ----------
26 -
        target : CustomCheckListEditor
27 -
            The Custom Check List Editor
28 -
        index : int
29 -
            The index of interest.
30 -
        """
31 -
        self.target = target
32 -
        self.index = index
33 -
34 -
    @classmethod
35 -
    def from_location_index(cls, wrapper, location):
36 -
        """ Creates an instance of _IndexedCustomCheckListEditor from a
37 -
        wrapper wrapping a Custom CheckListEditor, and a locator.Index
38 -
        object.
39 -
40 -
        Parameters
41 -
        ----------
42 -
        wrapper : UIWrapper
43 -
            wrapper wrapping a Custom CheckListEditor
44 -
        location : Instance of locator.Index
45 -
        """
46 -
        # Conform to the call signature specified in the register
47 -
        return cls(
48 -
            target=wrapper.target,
49 -
            index=location.index,
50 -
        )
51 -
52 -
    @classmethod
53 -
    def register(cls, registry):
54 -
        """ Class method to register interactions on an
55 -
        _IndexedCustomCheckListEditor for the given registry.
56 -
57 -
        If there are any conflicts, an error will occur.
58 -
59 -
        Parameters
60 -
        ----------
61 -
        registry : TargetRegistry
62 -
            The registry being registered to.
63 -
        """
64 -
        registry.register_solver(
65 -
            target_class=CustomEditor,
66 -
            locator_class=locator.Index,
67 -
            solver=cls.from_location_index,
68 -
        )
69 -
        registry.register_handler(
70 -
            target_class=cls,
71 -
            interaction_class=command.MouseClick,
72 -
            handler=lambda wrapper, _:
73 -
                (helpers.mouse_click_checkbox_child_in_panel(
74 -
                    control=wrapper.target.target.control,
75 -
                    index=convert_index(
76 -
                        wrapper.target.target,
77 -
                        wrapper.target.index,
78 -
                    ),
79 -
                    delay=wrapper.delay))
80 -
        )
20 +
class _IndexedCustomCheckListEditor(_BaseSourceWithLocation):
21 +
    """ Wrapper for CheckListEditor + locator.Index
22 +
    """
23 +
    source_class = CustomEditor
24 +
    locator_class = locator.Index
25 +
    handlers = [
26 +
        (command.MouseClick,
27 +
            (lambda wrapper, _: helpers.mouse_click_checkbox_child_in_panel(
28 +
                control=wrapper.target.source.control,
29 +
                index=convert_index(
30 +
                    source=wrapper.target.source,
31 +
                    index=wrapper.target.location.index
32 +
                ),
33 +
                delay=wrapper.delay))),
34 +
    ]
81 35
82 36
83 37
def convert_index(source, index):

@@ -11,72 +11,24 @@
Loading
11 11
12 12
from traitsui.qt4.check_list_editor import CustomEditor
13 13
from traitsui.testing.tester import command, locator
14 +
from traitsui.testing.tester.common_ui_targets import _BaseSourceWithLocation
14 15
from traitsui.testing.tester.editors.layout import column_major_to_row_major
15 16
from traitsui.testing.tester.qt4 import helpers
16 17
17 18
18 -
class _IndexedCustomCheckListEditor:
19 +
class _IndexedCustomCheckListEditor(_BaseSourceWithLocation):
19 20
    """ Wrapper for CheckListEditor + locator.Index """
20 -
21 -
    def __init__(self, target, index):
22 -
        """
23 -
        Parameters
24 -
        ----------
25 -
        target : CustomCheckListEditor
26 -
            The Custom Check List Editor
27 -
        index : int
28 -
            The index of interest.
29 -
        """
30 -
        self.target = target
31 -
        self.index = index
32 -
33 -
    @classmethod
34 -
    def from_location_index(cls, wrapper, location):
35 -
        """ Creates an instance of _IndexedCustomCheckListEditor from a
36 -
        wrapper wrapping a Custom CheckListEditor, and a locator.Index
37 -
        object.
38 -
39 -
        Parameters
40 -
        ----------
41 -
        wrapper : UIWrapper
42 -
            wrapper wrapping a Custom CheckListEditor
43 -
        location : Instance of locator.Index
44 -
        """
45 -
        # Conform to the call signature specified in the register
46 -
        return cls(
47 -
            target=wrapper.target,
48 -
            index=location.index,
49 -
        )
50 -
51 -
    @classmethod
52 -
    def register(cls, registry):
53 -
        """ Class method to register interactions on an
54 -
        _IndexedCustomCheckListEditor for the given registry.
55 -
56 -
        If there are any conflicts, an error will occur.
57 -
58 -
        Parameters
59 -
        ----------
60 -
        registry : TargetRegistry
61 -
            The registry being registered to.
62 -
        """
63 -
        registry.register_solver(
64 -
            target_class=CustomEditor,
65 -
            locator_class=locator.Index,
66 -
            solver=cls.from_location_index,
67 -
        )
68 -
        registry.register_handler(
69 -
            target_class=cls,
70 -
            interaction_class=command.MouseClick,
71 -
            handler=lambda wrapper, _: helpers.mouse_click_qlayout(
72 -
                layout=wrapper.target.target.control.layout(),
73 -
                index=convert_index(
74 -
                    wrapper.target.target.control.layout(),
75 -
                    wrapper.target.index
76 -
                ),
77 -
                delay=wrapper.delay,
78 -
            )
79 -
        )
21 +
    source_class = CustomEditor
22 +
    locator_class = locator.Index
23 +
    handlers = [
24 +
        (command.MouseClick, (lambda wrapper, _: helpers.mouse_click_qlayout(
25 +
            layout=wrapper.target.source.control.layout(),
26 +
            index=convert_index(
27 +
                layout=wrapper.target.source.control.layout(),
28 +
                index=wrapper.target.location.index,
29 +
            ),
30 +
            delay=wrapper.delay))),
31 +
    ]
80 32
81 33
82 34
def convert_index(layout, index):

@@ -67,3 +67,6 @@
Loading
67 67
68 68
    # A textbox within a UI
69 69
    textbox = "textbox"
70 +
71 +
    # A slider within a UI
72 +
    slider = "slider"

@@ -14,15 +14,21 @@
Loading
14 14
    LogRangeSliderEditor,
15 15
    RangeTextEditor,
16 16
    SimpleSliderEditor,
17 +
    SimpleSpinEditor,
17 18
)
18 19
19 20
from traitsui.testing.tester import locator
20 -
from traitsui.testing.tester.qt4.common_ui_targets import LocatedTextbox
21 +
from traitsui.testing.tester.qt4 import registry_helper
22 +
from traitsui.testing.tester.qt4.common_ui_targets import (
23 +
    LocatedSlider,
24 +
    LocatedTextbox
25 +
)
21 26
22 27
23 28
def resolve_location_slider(wrapper, location):
24 29
    """ Solver from a UIWrapper wrapped Range Editor to a LocatedTextbox
25 -
    containing the textbox of interest
30 +
    containing the textbox of interest, or a LocatedSlider containing the
31 +
    slider.
26 32
27 33
    If there are any conflicts, an error will occur.
28 34
@@ -36,35 +42,11 @@
Loading
36 42
    if location == locator.WidgetType.textbox:
37 43
        return LocatedTextbox(textbox=wrapper.target.control.text)
38 44
    if location in [locator.WidgetType.slider]:
39 -
        raise NotImplementedError(
40 -
            f"Logic for interacting with the {location}"
41 -
            " has not been implemented."
42 -
        )
45 +
        return LocatedSlider(slider=wrapper.target.control.slider)
43 46
    raise ValueError(
44 47
        f"Unable to resolve {location} on {wrapper.target}."
45 -
        " Currently supported: {locator.WidgetType.textbox}"
46 -
    )
47 -
48 -
49 -
def resolve_location_range_text(wrapper, location):
50 -
    """ Solver from a UIWrapper wrapped RangeTextEditor to a LocatedTextbox
51 -
    containing the textbox of interest
52 -
53 -
    If there are any conflicts, an error will occur.
54 -
55 -
    Parameters
56 -
    ----------
57 -
    wrapper : UIWrapper
58 -
        Wrapper containing the RangeTextEditor target.
59 -
    location : locator.WidgetType
60 -
        The location we are looking to resolve.
61 -
    """
62 -
63 -
    if location == locator.WidgetType.textbox:
64 -
        return LocatedTextbox(textbox=wrapper.target.control)
65 -
    raise ValueError(
66 -
        f"Unable to resolve {location} on {wrapper.target}."
67 -
        " Currently supported: {locator.WidgetType.textbox}"
48 +
        " Currently supported: {locator.WidgetType.textbox} and"
49 +
        " {locator.WidgetType.slider}"
68 50
    )
69 51
70 52
@@ -88,9 +70,8 @@
Loading
88 70
            locator_class=locator.WidgetType,
89 71
            solver=resolve_location_slider,
90 72
        )
91 -
92 -
    registry.register_solver(
73 +
    registry_helper.register_editable_textbox_handlers(
74 +
        registry=registry,
93 75
        target_class=RangeTextEditor,
94 -
        locator_class=locator.WidgetType,
95 -
        solver=resolve_location_range_text,
76 +
        widget_getter=lambda wrapper: wrapper.target.control,
96 77
    )

@@ -16,8 +16,8 @@
Loading
16 16
target_class of choice to one of these as the locator_class. For an example,
17 17
see the implementation of range_editor.
18 18
"""
19 -
20 -
from traitsui.testing.tester.wx import registry_helper
19 +
from traitsui.testing.tester import command
20 +
from traitsui.testing.tester.wx import helpers, registry_helper
21 21
22 22
23 23
class LocatedTextbox:
@@ -48,3 +48,34 @@
Loading
48 48
            target_class=cls,
49 49
            widget_getter=lambda wrapper: wrapper.target.textbox,
50 50
        )
51 +
52 +
53 +
class LocatedSlider:
54 +
    """ Wrapper class for a located Textbox in Wx.
55 +
56 +
    Parameters
57 +
    ----------
58 +
    slider : Instance of traitsui.wx.helper.Slider (wx.Slider)
59 +
    """
60 +
61 +
    def __init__(self, slider):
62 +
        self.slider = slider
63 +
64 +
    @classmethod
65 +
    def register(cls, registry):
66 +
        """ Class method to register interactions on a LocatedSlider for the
67 +
        given registry.
68 +
69 +
        If there are any conflicts, an error will occur.
70 +
71 +
        Parameters
72 +
        ----------
73 +
        registry : TargetRegistry
74 +
            The registry being registered to.
75 +
        """
76 +
        registry.register_handler(
77 +
            target_class=cls,
78 +
            interaction_class=command.KeyClick,
79 +
            handler=lambda wrapper, interaction: helpers.key_click_slider(
80 +
                wrapper.target.slider, interaction, wrapper.delay)
81 +
        )

@@ -16,12 +16,17 @@
Loading
16 16
)
17 17
18 18
from traitsui.testing.tester import locator
19 -
from traitsui.testing.tester.wx.common_ui_targets import LocatedTextbox
19 +
from traitsui.testing.tester.wx import registry_helper
20 +
from traitsui.testing.tester.wx.common_ui_targets import (
21 +
    LocatedSlider,
22 +
    LocatedTextbox
23 +
)
20 24
21 25
22 26
def resolve_location_slider(wrapper, location):
23 27
    """ Solver from a UIWrapper wrapped Range Editor to a LocatedTextbox
24 -
    containing the textbox of interest
28 +
    containing the textbox of interest, or a LocatedSlider containing the
29 +
    slider.
25 30
26 31
    If there are any conflicts, an error will occur.
27 32
@@ -35,35 +40,11 @@
Loading
35 40
    if location == locator.WidgetType.textbox:
36 41
        return LocatedTextbox(textbox=wrapper.target.control.text)
37 42
    if location in [locator.WidgetType.slider]:
38 -
        raise NotImplementedError(
39 -
            f"Logic for interacting with the {location}"
40 -
            " has not been implemented."
41 -
        )
42 -
    raise ValueError(
43 -
        f"Unable to resolve {location} on {wrapper.target}."
44 -
        " Currently supported: {locator.WidgetType.textbox}"
45 -
    )
46 -
47 -
48 -
def resolve_location_range_text(wrapper, location):
49 -
    """ Solver from a UIWrapper wrapped RangeTextEditor to a LocatedTextbox
50 -
    containing the textbox of interest
51 -
52 -
    If there are any conflicts, an error will occur.
53 -
54 -
    Parameters
55 -
    ----------
56 -
    wrapper : UIWrapper
57 -
        Wrapper containing the RangeTextEditor target.
58 -
    location : locator.WidgetType
59 -
        The location we are looking to resolve.
60 -
    """
61 -
62 -
    if location == locator.WidgetType.textbox:
63 -
        return LocatedTextbox(textbox=wrapper.target.control)
43 +
        return LocatedSlider(slider=wrapper.target.control.slider)
64 44
    raise ValueError(
65 45
        f"Unable to resolve {location} on {wrapper.target}."
66 -
        " Currently supported: {locator.WidgetType.textbox}"
46 +
        " Currently supported: {locator.WidgetType.textbox} and"
47 +
        " {locator.WidgetType.slider}"
67 48
    )
68 49
69 50
@@ -87,8 +68,8 @@
Loading
87 68
            locator_class=locator.WidgetType,
88 69
            solver=resolve_location_slider,
89 70
        )
90 -
    registry.register_solver(
71 +
    registry_helper.register_editable_textbox_handlers(
72 +
        registry=registry,
91 73
        target_class=RangeTextEditor,
92 -
        locator_class=locator.WidgetType,
93 -
        solver=resolve_location_range_text,
74 +
        widget_getter=lambda wrapper: wrapper.target.control,
94 75
    )

@@ -36,6 +36,8 @@
Loading
36 36
37 37
    common_ui_targets.LocatedTextbox.register(registry)
38 38
39 +
    common_ui_targets.LocatedSlider.register(registry)
40 +
39 41
    # ButtonEditor
40 42
    button_editor.register(registry)
41 43

@@ -36,6 +36,8 @@
Loading
36 36
37 37
    common_ui_targets.LocatedTextbox.register(registry)
38 38
39 +
    common_ui_targets.LocatedSlider.register(registry)
40 +
39 41
    # ButtonEditor
40 42
    button_editor.register(registry)
41 43

@@ -124,6 +124,8 @@
Loading
124 124
    delay : int
125 125
        Time delay (in ms) in which click will be performed.
126 126
    """
127 +
    if not 0 <= index < tab_widget.count():
128 +
        raise IndexError(index)
127 129
    tabbar = tab_widget.tabBar()
128 130
    rect = tabbar.tabRect(index)
129 131
    QTest.mouseClick(
@@ -272,3 +274,29 @@
Loading
272 274
    if not control.isEnabled():
273 275
        raise Disabled("{!r} is disabled.".format(control))
274 276
    key_click(control, interaction.key, delay=delay)
277 +
278 +
279 +
def key_click_qslider(control, interaction, delay):
280 +
    """ Performs simulated typing of a key on the given slider after a delay.
281 +
    Only allowed keys are:
282 +
    "Left", "Right", "Up", "Down", "Page Up", "Page Down"
283 +
    Also, note that up related keys correspond to an increment on the slider,
284 +
    and down a decrement.
285 +
286 +
    Parameters
287 +
    ----------
288 +
    control : QSlider
289 +
        The Qt slider to be acted on.
290 +
    interaction : instance of command.KeyClick
291 +
        The interaction object holding the key input
292 +
        to be simulated being typed
293 +
    delay : int
294 +
        Time delay (in ms) in which the key click will be performed.
295 +
    """
296 +
    valid_keys = {"Left", "Right", "Up", "Down", "Page Up", "Page Down"}
297 +
    if interaction.key not in valid_keys:
298 +
        raise ValueError(
299 +
            "Unexpected Key. Supported keys are: {}".format(sorted(valid_keys))
300 +
        )
301 +
    else:
302 +
        key_click(control, interaction.key, delay)

@@ -125,21 +125,27 @@
Loading
125 125
126 126
    See documentation on ``TargetRegistry`` for details.
127 127
128 -
    Attributes
128 +
    Parameters
129 129
    ----------
130 130
    registries : list of TargetRegistry, optional
131 -
        Registries of interaction for different target, in the order
132 -
        of decreasing priority. A shallow copy will be made.
131 +
        Registries of interaction for different targets, in the order
132 +
        of decreasing priority. If provided, a shallow copy will be made.
133 +
        Default registries are always appended to the list to provide
134 +
        builtin support for TraitsUI UI and editors.
133 135
    delay : int, optional
134 136
        Time delay (in ms) in which actions by the tester are performed. Note
135 -
        it is propogated through to created child wrappers. The delay allows
137 +
        it is propagated through to created child wrappers. The delay allows
136 138
        visual confirmation test code is working as desired. Defaults to 0.
139 +
140 +
    Attributes
141 +
    ----------
142 +
    delay : int
143 +
        Time delay (in ms) in which actions by the tester are performed. Note
144 +
        it is propagated through to created child wrappers. The delay allows
145 +
        visual confirmation test code is working as desired.
137 146
    """
138 147
139 148
    def __init__(self, registries=None, delay=0):
140 -
        """ Instantiate the UI tester.
141 -
        """
142 -
143 149
        if registries is None:
144 150
            self._registries = []
145 151
        else:

@@ -42,29 +42,31 @@
Loading
42 42
    For simulating user interactions such as a mouse click or visual
43 43
    inspection, the ``perform`` and ``inspect`` methods are provided.
44 44
45 -
    Attributes
45 +
    Parameters
46 46
    ----------
47 47
    target : any
48 48
        An object on which further UI target can be searched for, or can be
49 49
        a leaf target that can be operated on.
50 +
    registries : list of TargetRegistry
51 +
        Registries of interaction for different target, in the order
52 +
        of decreasing priority.
50 53
    delay : int, optional
51 54
        Time delay (in ms) in which actions by the wrapper are performed. Note
52 -
        it is propogated through to created child wrappers. The delay allows
55 +
        it is propagated through to created child wrappers. The delay allows
53 56
        visual confirmation test code is working as desired. Defaults to 0.
57 +
58 +
    Attributes
59 +
    ----------
60 +
    target : any
61 +
        An object on which further UI target can be searched for, or can be
62 +
        a leaf target that can be operated on.
63 +
    delay : int
64 +
        Time delay (in ms) in which actions by the wrapper are performed. Note
65 +
        it is propagated through to created child wrappers. The delay allows
66 +
        visual confirmation test code is working as desired.
54 67
    """
55 68
56 69
    def __init__(self, target, registries, delay=0):
57 -
        """ Initializer
58 -
59 -
        Parameters
60 -
        ----------
61 -
        target : any
62 -
            An object on which further UI target can be searched for, or can be
63 -
            a leaf target that can be operated on.
64 -
        registries : list of TargetRegistry
65 -
            Registries of interaction for different target, in the order
66 -
            of decreasing priority.
67 -
        """
68 70
        self.target = target
69 71
        self._registries = registries
70 72
        self.delay = delay

@@ -9,6 +9,7 @@
Loading
9 9
#  Thanks for using Enthought open source!
10 10
#
11 11
from traitsui.testing.tester import command, locator
12 +
from traitsui.testing.tester.common_ui_targets import _BaseSourceWithLocation
12 13
from traitsui.testing.tester.registry_helper import register_nested_ui_solvers
13 14
from traitsui.testing.tester.qt4 import helpers
14 15
from traitsui.qt4.list_editor import (
@@ -17,40 +18,18 @@
Loading
17 18
)
18 19
19 20
20 -
class _IndexedNotebookEditor:
21 +
class _IndexedNotebookEditor(_BaseSourceWithLocation):
21 22
    """ Wrapper for a ListEditor (Notebook) with an index.
22 23
    """
23 -
24 -
    def __init__(self, target, index):
25 -
        """
26 -
        Parameters
27 -
        ----------
28 -
        target : NotebookEditor
29 -
            The Notebook List Editor
30 -
        index : int
31 -
            The index of interest.
32 -
        """
33 -
        self.target = target
34 -
        self.index = index
35 -
36 -
    @classmethod
37 -
    def from_location(cls, wrapper, location):
38 -
        """ Helper method to create an instance of _IndexedNotebookEditor
39 -
        but checking if the index is valid before doing so.
40 -
41 -
        Parameters
42 -
        ----------
43 -
        wrapper : UIWrapper
44 -
            the UIWrapper wrapping the Notebook List Editor
45 -
        location : locator.Index
46 -
            the locator.Index object containing the index
47 -
        """
48 -
        # Raise IndexError early
49 -
        wrapper.target._uis[location.index]
50 -
        return cls(
51 -
            target=wrapper.target,
52 -
            index=location.index,
53 -
        )
24 +
    source_class = NotebookEditor
25 +
    locator_class = locator.Index
26 +
    handlers = [
27 +
        (command.MouseClick, (lambda wrapper, _:
28 +
            helpers.mouse_click_tab_index(
29 +
                tab_widget=wrapper.target.source.control,
30 +
                index=wrapper.target.location.index,
31 +
                delay=wrapper.delay))),
32 +
    ]
54 33
55 34
    @classmethod
56 35
    def register(cls, registry):
@@ -64,49 +43,25 @@
Loading
64 43
        registry : TargetRegistry
65 44
            The registry being registered to.
66 45
        """
67 -
        registry.register_solver(
68 -
            target_class=NotebookEditor,
69 -
            locator_class=locator.Index,
70 -
            solver=cls.from_location,
71 -
        )
72 -
46 +
        super().register(registry)
73 47
        register_nested_ui_solvers(
74 48
            registry=registry,
75 49
            target_class=cls,
76 50
            nested_ui_getter=lambda target: target._get_nested_ui()
77 51
        )
78 52
79 -
        registry.register_handler(
80 -
            target_class=cls,
81 -
            interaction_class=command.MouseClick,
82 -
            handler=lambda wrapper, _: helpers.mouse_click_tab_index(
83 -
                tab_widget=wrapper.target.target.control,
84 -
                index=wrapper.target.index,
85 -
                delay=wrapper.delay),
86 -
        )
87 -
88 53
    def _get_nested_ui(self):
89 54
        """ Method to get the nested ui corresponding to the List element at
90 55
        the given index.
91 56
        """
92 -
        return self.target._uis[self.index][1]
57 +
        return self.source._uis[self.location.index][1]
93 58
94 59
95 -
class _IndexedCustomEditor:
60 +
class _IndexedCustomEditor(_BaseSourceWithLocation):
96 61
    """ Wrapper for a ListEditor (custom) with an index.
97 62
    """
98 -
99 -
    def __init__(self, target, index):
100 -
        """
101 -
        Parameters
102 -
        ----------
103 -
        target : CustomEditor
104 -
            The Custom List Editor
105 -
        index : int
106 -
            The index of interest.
107 -
        """
108 -
        self.target = target
109 -
        self.index = index
63 +
    source_class = CustomEditor
64 +
    locator_class = locator.Index
110 65
111 66
    @classmethod
112 67
    def register(cls, registry):
@@ -120,12 +75,7 @@
Loading
120 75
        registry : TargetRegistry
121 76
            The registry being registered to.
122 77
        """
123 -
        registry.register_solver(
124 -
            target_class=CustomEditor,
125 -
            locator_class=locator.Index,
126 -
            solver=lambda wrapper, location:
127 -
                cls(target=wrapper.target, index=location.index)
128 -
        )
78 +
        super().register(registry)
129 79
        register_nested_ui_solvers(
130 80
            registry=registry,
131 81
            target_class=cls,
@@ -136,16 +86,16 @@
Loading
136 86
        """ Method to get the nested ui corresponding to the List element at
137 87
        the given index.
138 88
        """
139 -
        row, column = divmod(self.index, self.target.factory.columns)
89 +
        row, column = divmod(self.location.index, self.source.factory.columns)
140 90
        # there are two columns for each list item (one for the item itself,
141 91
        # and another for the list menu button)
142 92
        column = 2*column
143 -
        grid_layout = self.target._list_pane.layout()
93 +
        grid_layout = self.source._list_pane.layout()
144 94
        item = grid_layout.itemAtPosition(row, column)
145 95
        if item is None:
146 -
            raise IndexError(self.index)
147 -
        if self.target.scrollable:
148 -
            self.target.control.ensureWidgetVisible(item.widget())
96 +
            raise IndexError(self.location.index)
97 +
        if self.source.scrollable:
98 +
            self.source.control.ensureWidgetVisible(item.widget())
149 99
150 100
        return item.widget()._editor._ui
151 101

@@ -9,6 +9,7 @@
Loading
9 9
#  Thanks for using Enthought open source!
10 10
#
11 11
from traitsui.testing.tester import command, locator
12 +
from traitsui.testing.tester.common_ui_targets import _BaseSourceWithLocation
12 13
from traitsui.testing.tester.registry_helper import register_nested_ui_solvers
13 14
from traitsui.testing.tester.wx import helpers
14 15
from traitsui.wx.list_editor import (
@@ -17,40 +18,18 @@
Loading
17 18
)
18 19
19 20
20 -
class _IndexedNotebookEditor:
21 +
class _IndexedNotebookEditor(_BaseSourceWithLocation):
21 22
    """ Wrapper for a ListEditor (Notebook) with an index.
22 23
    """
23 -
24 -
    def __init__(self, target, index):
25 -
        """
26 -
        Parameters
27 -
        ----------
28 -
        target : NotebookEditor
29 -
            The Notebook List Editor
30 -
        index : int
31 -
            The index of interest.
32 -
        """
33 -
        self.target = target
34 -
        self.index = index
35 -
36 -
    @classmethod
37 -
    def from_location(cls, wrapper, location):
38 -
        """ Helper method to create an instance of _IndexedNotebookEditor
39 -
        but checking if the index is valid before doing so.
40 -
41 -
        Parameters
42 -
        ----------
43 -
        wrapper : UIWrapper
44 -
            the UIWrapper wrapping the Notebook List Editor
45 -
        location : locator.Index
46 -
            the locator.Index object containing the index
47 -
        """
48 -
        # Raise IndexError early
49 -
        wrapper.target._uis[location.index]
50 -
        return cls(
51 -
            target=wrapper.target,
52 -
            index=location.index,
53 -
        )
24 +
    source_class = NotebookEditor
25 +
    locator_class = locator.Index
26 +
    handlers = [
27 +
        (command.MouseClick, (lambda wrapper, _:
28 +
            helpers.mouse_click_notebook_tab_index(
29 +
                control=wrapper.target.source.control,
30 +
                index=wrapper.target.location.index,
31 +
                delay=wrapper.delay))),
32 +
    ]
54 33
55 34
    @classmethod
56 35
    def register(cls, registry):
@@ -64,49 +43,25 @@
Loading
64 43
        registry : TargetRegistry
65 44
            The registry being registered to.
66 45
        """
67 -
        registry.register_solver(
68 -
            target_class=NotebookEditor,
69 -
            locator_class=locator.Index,
70 -
            solver=cls.from_location,
71 -
        )
72 -
46 +
        super().register(registry)
73 47
        register_nested_ui_solvers(
74 48
            registry=registry,
75 49
            target_class=cls,
76 50
            nested_ui_getter=lambda target: target._get_nested_ui()
77 51
        )
78 52
79 -
        registry.register_handler(
80 -
            target_class=cls,
81 -
            interaction_class=command.MouseClick,
82 -
            handler=lambda wrapper, _: helpers.mouse_click_notebook_tab_index(
83 -
                control=wrapper.target.target.control,
84 -
                index=wrapper.target.index,
85 -
                delay=wrapper.delay),
86 -
        )
87 -
88 53
    def _get_nested_ui(self):
89 54
        """ Method to get the nested ui corresponding to the List element at
90 55
        the given index.
91 56
        """
92 -
        return self.target._uis[self.index][0].dockable.ui
57 +
        return self.source._uis[self.location.index][0].dockable.ui
93 58
94 59
95 -
class _IndexedCustomEditor:
60 +
class _IndexedCustomEditor(_BaseSourceWithLocation):
96 61
    """ Wrapper for a ListEditor (custom) with an index.
97 62
    """
98 -
99 -
    def __init__(self, target, index):
100 -
        """
101 -
        Parameters
102 -
        ----------
103 -
        target : CustomEditor
104 -
            The Custom List Editor
105 -
        index : int
106 -
            The index of interest.
107 -
        """
108 -
        self.target = target
109 -
        self.index = index
63 +
    source_class = CustomEditor
64 +
    locator_class = locator.Index
110 65
111 66
    @classmethod
112 67
    def register(cls, registry):
@@ -120,12 +75,7 @@
Loading
120 75
        registry : TargetRegistry
121 76
            The registry being registered to.
122 77
        """
123 -
        registry.register_solver(
124 -
            target_class=CustomEditor,
125 -
            locator_class=locator.Index,
126 -
            solver=lambda wrapper, location:
127 -
                cls(target=wrapper.target, index=location.index)
128 -
        )
78 +
        super().register(registry)
129 79
        register_nested_ui_solvers(
130 80
            registry=registry,
131 81
            target_class=cls,
@@ -141,8 +91,8 @@
Loading
141 91
        # item itself.  Thus, index is actually an index over the odd elements
142 92
        # of the list of children corresponding to items in the list we would
143 93
        # want to interact with
144 -
        new_index = 2*self.index + 1
145 -
        WindowList = self.target.control.GetChildren()
94 +
        new_index = 2*self.location.index + 1
95 +
        WindowList = self.source.control.GetChildren()
146 96
        item = WindowList[new_index]
147 97
        return item._editor._ui
148 98

@@ -316,3 +316,48 @@
Loading
316 316
    for char in interaction.sequence:
317 317
        wx.MilliSleep(delay)
318 318
        control.WriteText(char)
319 +
320 +
321 +
def key_click_slider(control, interaction, delay):
322 +
    """ Performs simulated typing of a key on the given wxSlider
323 +
    after a delay. Only allowed keys are:
324 +
    "Left", "Right", "Up", "Down", "Page Up", "Page Down"
325 +
    Also, note that up related keys correspond to an increment on the slider,
326 +
    and down a decrement.
327 +
328 +
    Parameters
329 +
    ----------
330 +
    control : wxSlider
331 +
        The wx Object to be acted on.
332 +
    interaction : instance of command.KeyClick
333 +
        The interaction object holding the key input
334 +
        to be simulated being typed
335 +
    delay : int
336 +
        Time delay (in ms) in which the key click will be performed.
337 +
    """
338 +
    valid_keys = {"Left", "Right", "Up", "Down", "Page Up", "Page Down"}
339 +
    if interaction.key not in valid_keys:
340 +
        raise ValueError(
341 +
            "Unexpected Key. Supported keys are: {}".format(sorted(valid_keys))
342 +
        )
343 +
    if not control.HasFocus():
344 +
        control.SetFocus()
345 +
    value = control.GetValue()
346 +
    if interaction.key in {"Up", "Right"}:
347 +
        position = min(control.GetMax(), value + control.GetLineSize())
348 +
    elif interaction.key == "Page Up":
349 +
        position = min(control.GetMax(), value + control.GetPageSize())
350 +
    elif interaction.key == "Page Down":
351 +
        position = max(control.GetMin(), value - control.GetPageSize())
352 +
    elif interaction.key in {"Down", "Left"}:
353 +
        position = max(control.GetMin(), value - control.GetLineSize())
354 +
    else:
355 +
        raise ValueError(
356 +
            "Unexpected Key. Supported keys are: {}".format(sorted(valid_keys))
357 +
        )
358 +
    wx.MilliSleep(delay)
359 +
    control.SetValue(position)
360 +
    event = wx.ScrollEvent(
361 +
        wx.wxEVT_SCROLL_CHANGED, control.GetId(), position
362 +
    )
363 +
    wx.PostEvent(control, event)
Files Coverage
traitsui 34.2%
Project Totals (291 files) 34.2%
2342.3
TRAVIS_OS_NAME=linux
2342.1
TRAVIS_OS_NAME=linux
2342.2
TRAVIS_OS_NAME=linux
jk8rwmh27bhc0tk2
1
codecov:
2
  notify:
3
    require_ci_to_pass: yes
4

5
coverage:
6
  precision: 1
7
  round: up
8

9
  status:
10
    patch:
11
      default:
12
        only_pulls: yes
13

14
comment : off
Sunburst
The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file. The size and color of each slice is representing the number of statements and the coverage, respectively.
Icicle
The top section represents the entire project. Proceeding with folders and finally individual files. The size and color of each slice is representing the number of statements and the coverage, respectively.
Grid
Each block represents a single file in the project. The size and color of each block is represented by the number of statements and the coverage, respectively.
Loading