Skip to content
Closed
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"""
This example demonstrates how to test interacting with a TableEditor.

The GUI being tested is written in the demo under the same name (minus the
preceding 'test') in the outer directory.
"""

import os
import runpy
import unittest


from traitsui.testing.api import (
Cell, KeyClick, KeySequence, MouseClick, UITester
)

#: Filename of the demo script
FILENAME = "TableEditor_demo.py"

#: Path of the demo script
DEMO_PATH = os.path.join(os.path.dirname(__file__), "..", FILENAME)


class TestTableEditorDemo(unittest.TestCase):

def test_list_editor_demo(self):
demo = runpy.run_path(DEMO_PATH)["demo"]

tester = UITester()
with tester.create_ui(demo) as ui:
employees_table = tester.find_by_name(ui, "employees")

# clicking a cell enters edit mode and selcts full text
cell_21 = employees_table.locate(Cell(2,1))
cell_21.perform(MouseClick())
cell_21.perform(KeySequence("Jones"))
cell_21.perform(KeyClick("Enter"))

self.assertEqual(demo.employees[0].last_name, 'Jones')

# third column corresponds to Full Name property
cell_32 = employees_table.locate(Cell(3,2))
cell_32.perform(MouseClick())


# Run the test(s)
unittest.TextTestRunner().run(
unittest.TestLoader().loadTestsFromTestCase(TestTableEditorDemo)
)
3 changes: 3 additions & 0 deletions traitsui/testing/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@

from .tester.command import (
MouseClick,
MouseDClick,
KeyClick,
KeySequence
)
Expand All @@ -71,6 +72,7 @@
)

from .tester.locator import (
Cell,
Index,
TargetById,
TargetByName,
Expand All @@ -84,6 +86,7 @@
DisplayedText,
IsChecked,
IsEnabled,
Selected,
SelectedText
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,127 @@ def mouse_click_item_view(model, view, index, delay):
)


def mouse_dclick_item_view(model, view, index, delay):
""" Perform mouse double click on the given QAbstractItemModel (model) and
QAbstractItemView (view) with the given row and column.

Parameters
----------
model : QAbstractItemModel
Model from which QModelIndex will be obtained
view : QAbstractItemView
View from which the widget identified by the index will be
found and key sequence be performed.
index : QModelIndex

Raises
------
LookupError
If the index cannot be located.
Note that the index error provides more
"""
check_q_model_index_valid(index)
rect = view.visualRect(index)
QTest.mouseDClick(
view.viewport(),
QtCore.Qt.LeftButton,
QtCore.Qt.NoModifier,
rect.center(),
delay=delay,
)


def key_sequence_item_view(model, view, index, sequence, delay=0):
""" Perform mouse click on the given QAbstractItemModel (model) and
QAbstractItemView (view) with the given row and column.

Parameters
----------
model : QAbstractItemModel
Model from which QModelIndex will be obtained
view : QAbstractItemView
View from which the widget identified by the index will be
found and key sequence be performed.
index : QModelIndex
sequence : str
Sequence of characters to be inserted to the widget identifed
by the row and column.

Raises
------
Disabled
If the widget cannot be edited.
LookupError
If the index cannot be located.
Note that the index error provides more
"""
check_q_model_index_valid(index)
widget = view.indexWidget(index)
if widget is None:
raise Disabled(
"No editable widget for item at row {!r} and column {!r}".format(
index.row(), index.column()
)
)
QTest.keyClicks(widget, sequence, delay=delay)


def key_click_item_view(model, view, index, key, delay=0):
""" Perform key press on the given QAbstractItemModel (model) and
QAbstractItemView (view) with the given row and column.

Parameters
----------
model : QAbstractItemModel
Model from which QModelIndex will be obtained
view : QAbstractItemView
View from which the widget identified by the index will be
found and key press be performed.
index : int
key : str
Key to be pressed.

Raises
------
Disabled
If the widget cannot be edited.
LookupError
If the index cannot be located.
Note that the index error provides more
"""
check_q_model_index_valid(index)
widget = view.indexWidget(index)
if widget is None:
raise Disabled(
"No editable widget for item at row {!r} and column {!r}".format(
index.row(), index.column()
)
)
key_click(widget, key=key, delay=delay)


def get_display_text_item_view(model, view, index):
""" Return the textural representation for the given model, row and column.

Parameters
----------
model : QAbstractItemModel
Model from which QModelIndex will be obtained
view : QAbstractItemView
View from which the widget identified by the index will be
found and key press be performed.
index : int

Raises
------
LookupError
If the index cannot be located.
Note that the index error provides more
"""
check_q_model_index_valid(index)
return model.data(index, QtCore.Qt.DisplayRole)


def mouse_click_combobox(combobox, index, delay):
""" Perform a mouse click on a QComboBox at a given index.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Copyright (c) 2005-2020, Enthought, Inc.
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only
# under the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!
#

from traitsui.qt4.table_editor import SimpleEditor

from traitsui.testing.api import (
Cell,
DisplayedText,
MouseClick,
MouseDClick,
KeyClick,
KeySequence,
Selected,
)
from traitsui.testing.tester._ui_tester_registry._common_ui_targets import (
BaseSourceWithLocation
)
from traitsui.testing.tester._ui_tester_registry.qt4 import (
_interaction_helpers,
_registry_helper
)


class _SimpleEditorWithCell(BaseSourceWithLocation):
source_class = SimpleEditor
locator_class = Cell
handlers = [
(MouseClick, lambda wrapper, _: wrapper._target._mouse_click(
delay=wrapper.delay)),
(KeyClick, lambda wrapper, interaction: wrapper._target._key_click(
key=interaction.key,
delay=wrapper.delay,)),
(KeySequence, lambda wrapper, interaction: wrapper._target._key_sequence(
sequence=interaction.sequence,
delay=wrapper.delay,)),
(DisplayedText, lambda wrapper, _: wrapper._target._get_displayed_text()),
(MouseDClick, lambda wrapper, _: wrapper._target._mouse_dclick(
delay=wrapper.delay,)),
]

def _get_model_view_index(self):
table_view = self.source.table_view
return dict(
model=table_view.model(),
view=table_view,
index=table_view.model().index(self.location.row, self.location.column),
)

def _mouse_click(self, delay=0):
_interaction_helpers.mouse_click_item_view(
**self._get_model_view_index(),
delay=delay,
)

def _mouse_dclick(self, delay=0):
_interaction_helpers.mouse_dclick_item_view(
**self._get_model_view_index(),
delay=delay,
)

def _key_sequence(self, sequence, delay=0):
_interaction_helpers.key_sequence_item_view(
**self._get_model_view_index(),
sequence=sequence,
delay=delay,
)

def _key_click(self, key, delay=0):
_interaction_helpers.key_click_item_view(
**self._get_model_view_index(),
key=key,
delay=delay,
)

def _get_displayed_text(self):
return _interaction_helpers.get_display_text_item_view(
**self._get_model_view_index(),
)


def register(registry):
""" Register interactions for the given registry.

If there are any conflicts, an error will occur.

Parameters
----------
registry : TargetRegistry
The registry being registered to.
"""
_SimpleEditorWithCell.register(registry)
registry.register_interaction(
target_class=SimpleEditor,
interaction_class=Selected,
handler=lambda wrapper, _: wrapper._target.selected
)
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
instance_editor,
list_editor,
range_editor,
table_editor,
text_editor,
ui_base,
)
Expand Down Expand Up @@ -69,4 +70,7 @@ def get_default_registry():
# Editor Factory
editor_factory.register(registry)

# TableEditor
table_editor.register(registry)

return registry
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,28 @@ def _create_event(event_type, control):
return event


def _create_grid_event(event_type, control, *args, **kwargs):
""" Creates a wxGridEvent of a given type.

Parameters
----------
event_type : wxEventType
The type of the event to be created
control :
The wx control the event is occurring on.

Returns
-------
wxGridEvent
The created event, of the given type, with the given control set as
the event object.
"""
event = wx.grid.GridEvent(
control.GetId(), event_type, control, *args, **kwargs
)
return event


def mouse_click(func):
""" Decorator function for mouse clicks. Decorated functions will return
if they are not enabled. Additionally, this handles the delay for the
Expand Down Expand Up @@ -187,6 +209,14 @@ def mouse_click_object(control, delay):
control.ProcessWindowEvent(click_event)


@mouse_click
def mouse_click_cell_in_grid(control, cell, delay):
click_event = _create_grid_event(
wx.grid.wxEVT_GRID_CELL_LEFT_CLICK, control, row=cell.row, col=cell.column
)
control.ProcessWindowEvent(click_event)


def mouse_click_notebook_tab_index(control, index, delay):
""" Performs a mouseclick on a Noteboook List Editor on the tab specified
by index.
Expand Down
Loading