Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 73 additions & 76 deletions traitsui/testing/tester/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,12 @@ def get_value(self, target_class, key):
return action_to_handler[key]


class InteractionRegistry:
""" A registry for mapping from target type + interaction type to a specific
implementation for simulating user interaction.
class TargetRegistry:
""" An object for registring interaction and location resolution logic
for different UI target types.

Registering interaction handler (register_handler)
--------------------------------------------------
The interaction type can be a subclass of any type. There are a few
pre-defined interaction types in
- ``traitsui.testing.tester.command``
Expand All @@ -75,7 +77,7 @@ def mouse_click_qt_button(wrapper, interaction):
The functon can then be registered with the target type and an interaction
type::

registry = InteractionRegistry()
registry = TargetRegistry()
registry.register_handler(
target_class=traitsui.qt4.button_editor.SimpleEditor,
interaction_class=traitsui.testing.tester.command.MouseClick,
Expand All @@ -93,75 +95,14 @@ def mouse_click_qt_button(wrapper, interaction):

Then this registry can be used with the ``UITester`` and ``UIWrapper`` to
support ``UIWrapper.perform`` and ``UIWrapper.inspect``.
"""

def __init__(self):
self._registry = _TargetToKeyRegistry(
exception_maker=(
lambda target_class, key, available_keys: (
InteractionNotSupported(
target_class=target_class,
interaction_class=key,
supported=available_keys,
)
)
),
)

def register_handler(self, target_class, interaction_class, handler):
""" Register a handler for a given target type and interaction type.

Parameters
----------
target_class : subclass of type
The type of a UI target being operated on.
interaction_class : subclass of type
Any class.
handler : callable(UIWrapper, interaction) -> any
The function to handle the particular interaction on a target.
``interaction`` should be an instance of ``interaction_class``.

Raises
------
ValueError
If a handler has already be registered for the same target type
and interaction class.
"""
self._registry.register(
target_class=target_class,
key=interaction_class,
value=handler,
)

def get_handler(self, target_class, interaction_class):
""" Return a callable for handling an interaction for a given target
type.

Parameters
----------
target_class : subclass of type
The type of a UI target being operated on.
interaction_class : subclass of type
Any class.

Returns
-------
handler : callable(UIWrapper, interaction) -> any
The function to handle the particular interaction on a target.
``interaction`` should be an instance of ``interaction_class``.
"""
return self._registry.get_value(
target_class=target_class,
key=interaction_class,
)

Registering location solver (register_solver)
---------------------------------------------

class LocationRegistry:
""" A registry for mapping from target type + locator type to a specific
implementation for resolving a new target for furture user interactions
(or further location resolutions).

Used for supporting ``UIWrapper.locate``.
Resoling a location on a UI target is logically similar to making a query
for a nested UI target. This query is separated out to support the
``UIWrapper.locate`` method independently of the query method
``UIWrapper.inspect``.

The locator type can be any subclass of ``type``. There are predefined
locators in ``traitsui.testing.tester.locator``.
Expand Down Expand Up @@ -217,9 +158,18 @@ def get_widget_by_type(wrapper, location):
"""

def __init__(self):
""" Initial registry is empty.
"""
self._registry = _TargetToKeyRegistry(
self._interaction_registry = _TargetToKeyRegistry(
exception_maker=(
lambda target_class, key, available_keys: (
InteractionNotSupported(
target_class=target_class,
interaction_class=key,
supported=available_keys,
)
)
),
)
self._location_registry = _TargetToKeyRegistry(
exception_maker=(
lambda target_class, key, available_keys: LocationNotSupported(
target_class=target_class,
Expand All @@ -229,6 +179,53 @@ def __init__(self):
),
)

def register_handler(self, target_class, interaction_class, handler):
""" Register a handler for a given target type and interaction type.

Parameters
----------
target_class : subclass of type
The type of a UI target being operated on.
interaction_class : subclass of type
Any class.
handler : callable(UIWrapper, interaction) -> any
The function to handle the particular interaction on a target.
``interaction`` should be an instance of ``interaction_class``.

Raises
------
ValueError
If a handler has already be registered for the same target type
and interaction class.
"""
self._interaction_registry.register(
target_class=target_class,
key=interaction_class,
value=handler,
)

def get_handler(self, target_class, interaction_class):
""" Return a callable for handling an interaction for a given target
type.

Parameters
----------
target_class : subclass of type
The type of a UI target being operated on.
interaction_class : subclass of type
Any class.

Returns
-------
handler : callable(UIWrapper, interaction) -> any
The function to handle the particular interaction on a target.
``interaction`` should be an instance of ``interaction_class``.
"""
return self._interaction_registry.get_value(
target_class=target_class,
key=interaction_class,
)

def register_solver(self, target_class, locator_class, solver):
""" Register a solver for resolving the next UI target for the given
target type and locator type.
Expand All @@ -249,7 +246,7 @@ def register_solver(self, target_class, locator_class, solver):
If a solver has already been registered for the given target
type and locator type.
"""
self._registry.register(
self._location_registry.register(
target_class=target_class,
key=locator_class,
value=solver,
Expand All @@ -270,7 +267,7 @@ def get_solver(self, target_class, locator_class):
------
LocationNotSupported
"""
return self._registry.get_value(
return self._location_registry.get_value(
target_class=target_class,
key=locator_class,
)
17 changes: 8 additions & 9 deletions traitsui/testing/tester/tests/test_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@
import unittest

from traitsui.testing.tester.registry import (
InteractionRegistry,
LocationRegistry,
TargetRegistry,
)
from traitsui.testing.tester.exceptions import (
InteractionNotSupported,
Expand All @@ -24,7 +23,7 @@
class TestInteractionRegistry(unittest.TestCase):

def test_registry_empty(self):
registry = InteractionRegistry()
registry = TargetRegistry()
with self.assertRaises(InteractionNotSupported) as exception_context:
registry.get_handler(None, None)

Expand All @@ -35,7 +34,7 @@ def test_registry_empty(self):
)

def test_register_editor_with_action(self):
registry = InteractionRegistry()
registry = TargetRegistry()

class SpecificEditor:
pass
Expand Down Expand Up @@ -79,7 +78,7 @@ class UserAction3:
def handler(wrapper, interaction):
pass

registry = InteractionRegistry()
registry = TargetRegistry()
registry.register_handler(SpecificEditor, UserAction, handler)
registry.register_handler(SpecificEditor2, UserAction2, handler)
registry.register_handler(SpecificEditor2, UserAction3, handler)
Expand All @@ -103,7 +102,7 @@ class UserAction:
def handler(wrapper, interaction):
pass

registry = InteractionRegistry()
registry = TargetRegistry()
registry.register_handler(SpecificEditor, UserAction, handler)

with self.assertRaises(ValueError):
Expand All @@ -113,7 +112,7 @@ def handler(wrapper, interaction):
class TestLocationRegistry(unittest.TestCase):

def test_location_registry_empty(self):
registry = LocationRegistry()
registry = TargetRegistry()
with self.assertRaises(LocationNotSupported) as exception_context:
registry.get_solver(None, None)

Expand All @@ -128,7 +127,7 @@ def test_register_location(self):
def solver(wrapper, location):
return 1

registry = LocationRegistry()
registry = TargetRegistry()
registry.register_solver(
target_class=float, locator_class=str, solver=solver)

Expand All @@ -139,7 +138,7 @@ def test_register_location_report_existing(self):
def solver(wrapper, location):
return 1

registry = LocationRegistry()
registry = TargetRegistry()
registry.register_solver(
target_class=float, locator_class=str, solver=solver)

Expand Down
8 changes: 2 additions & 6 deletions traitsui/testing/tester/tests/test_ui_tester.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,8 @@ def test_interactor_found_if_editor_found(self):
expected, = ui.get_editors("submit_button")
self.assertEqual(wrapper.target, expected)
self.assertEqual(
wrapper._interaction_registries,
tester._interaction_registries,
)
self.assertEqual(
wrapper._location_registries,
tester._location_registries,
wrapper._registries,
tester._registries,
)

def test_no_editors_found(self):
Expand Down
Loading