Source code for traitsui.testing.tester.tests.test_ui_tester
# (C) Copyright 2004-2023 Enthought, Inc., Austin, TX
# 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!
import unittest
from unittest import mock
from pyface.api import GUI
from traits.api import (
Button,
Instance,
HasTraits,
Str,
)
from traitsui.api import Item, ModelView, View
from traitsui.tests._tools import (
process_cascade_events,
requires_toolkit,
ToolkitName,
)
from traitsui.testing.tester.exceptions import InteractionNotSupported
from traitsui.testing.tester.target_registry import TargetRegistry
from traitsui.testing.tester.ui_tester import (
UITester,
)
from traitsui.testing.tester.ui_wrapper import (
UIWrapper,
)
[docs]class Order(HasTraits):
submit_button = Button()
submit_label = Str("Submit")
[docs]class Model(HasTraits):
order = Instance(Order, ())
[docs]class SimpleApplication(ModelView):
model = Instance(Model)
[docs]@requires_toolkit([ToolkitName.qt, ToolkitName.wx])
class TestUITesterCreateUI(unittest.TestCase):
"""Test UITester.create_ui"""
[docs] def test_ui_disposed(self):
tester = UITester()
order = Order()
view = View(Item("submit_button"))
with tester.create_ui(order, dict(view=view)) as ui:
pass
self.assertTrue(ui.destroyed)
[docs] def test_create_ui_reraise_exception(self):
tester = UITester()
order = Order()
view = View(Item("submit_button"))
with self.assertRaises(RuntimeError), self.assertLogs(
"traitsui", level="ERROR"
):
with tester.create_ui(order, dict(view=view)) as ui:
def raise_error():
raise ZeroDivisionError()
GUI().invoke_later(raise_error)
self.assertIsNone(ui.control)
[docs] def test_create_ui_respect_auto_process_events_flag(self):
tester = UITester(auto_process_events=False)
order = Order()
view = View(Item("_"))
side_effect = mock.Mock()
gui = GUI()
gui.invoke_later(side_effect)
# Make sure all pending events are processed at the end of the test.
self.addCleanup(process_cascade_events)
with tester.create_ui(order, dict(view=view)) as ui:
pass
# dispose is called.
self.assertIsNone(ui.control)
# But the GUI events are not processed.
self.assertEqual(side_effect.call_count, 0)
[docs]@requires_toolkit([ToolkitName.qt, ToolkitName.wx])
class TestUITesterRegistry(unittest.TestCase):
"""Test maintaining registries."""
[docs] def test_traitsui_registry_added(self):
# Even if we have a custom registry list, the builtin TraitsUI
# registry is always added.
custom_registry = TargetRegistry()
tester = UITester(registries=[custom_registry])
view = View(Item("submit_button"))
with tester.create_ui(Order(), dict(view=view)) as ui:
# this relies on TraitsUI builtin registry.
wrapper = tester.find_by_name(ui, "submit_button")
# custom registry is accessible
# sanity check
with self.assertRaises(InteractionNotSupported):
wrapper.perform(1)
custom_registry.register_interaction(
target_class=wrapper._target.__class__,
interaction_class=int,
handler=lambda wrapper, interaction: None,
)
wrapper.perform(1)
[docs]@requires_toolkit([ToolkitName.qt, ToolkitName.wx])
class TestUITesterFindEditor(unittest.TestCase):
"""Test logic for finding a target."""
[docs] def test_interactor_found_if_editor_found(self):
tester = UITester()
view = View(Item("submit_button"))
with tester.create_ui(Order(), dict(view=view)) as ui:
wrapper = tester.find_by_name(ui, "submit_button")
self.assertIsInstance(wrapper, UIWrapper)
(expected,) = ui.get_editors("submit_button")
self.assertEqual(wrapper._target, expected)
self.assertEqual(
wrapper._registries,
tester._registries,
)
[docs] def test_no_editors_found(self):
# The view does not have "submit_n_events"
tester = UITester()
view = View(Item("submit_button"))
with tester.create_ui(Order(), dict(view=view)) as ui:
with self.assertRaises(ValueError) as exception_context:
tester.find_by_name(ui, "submit_n_events")
self.assertIn(
"No editors can be found",
str(exception_context.exception),
)
[docs] def test_multiple_editors_found(self):
# There may be more than one target with the same name.
# find_by_name cannot be used in this case.
tester = UITester()
view = View(Item("submit_button"), Item("submit_button"))
with tester.create_ui(Order(), dict(view=view)) as ui:
with self.assertRaises(ValueError) as exception_context:
tester.find_by_name(ui, "submit_button")
self.assertIn(
"Found multiple editors",
str(exception_context.exception),
)
[docs] def test_delay_persisted(self):
tester = UITester(delay=0.01)
view = View(Item("submit_button"))
with tester.create_ui(Order(), dict(view=view)) as ui:
wrapped = tester.find_by_name(ui, "submit_button")
self.assertEqual(wrapped.delay, 0.01)
[docs] def test_find_by_id(self):
tester = UITester(delay=123)
item1 = Item("submit_button", id="item1")
item2 = Item("submit_button", id="item2")
view = View(item1, item2)
with tester.create_ui(Order(), dict(view=view)) as ui:
wrapper = tester.find_by_id(ui, "item2")
self.assertIs(wrapper._target.item, item2)
self.assertEqual(wrapper._registries, tester._registries)
self.assertEqual(wrapper.delay, tester.delay)
[docs] def test_find_by_id_multiple(self):
# The uniqueness is not enforced. The first one is returned.
tester = UITester()
item1 = Item("submit_button", id="item1")
item2 = Item("submit_button", id="item2")
item3 = Item("submit_button", id="item2")
view = View(item1, item2, item3)
with tester.create_ui(Order(), dict(view=view)) as ui:
wrapper = tester.find_by_id(ui, "item2")
self.assertIs(wrapper._target.item, item2)
[docs] def test_auto_process_events_skipped(self):
# Event processing can be skipped.
tester = UITester(auto_process_events=False)
side_effect = mock.Mock()
gui = GUI()
gui.invoke_later(side_effect)
# Make sure all pending events are processed at the end of the test.
self.addCleanup(process_cascade_events)
view = View(Item("submit_button", id="item"))
with tester.create_ui(Order(), dict(view=view)) as ui:
tester.find_by_id(ui, "item")
self.assertEqual(side_effect.call_count, 0)
[docs]class TestUITesterGuiFree(unittest.TestCase):
"""Test GUI free interface on UITester."""
[docs] def test_auto_process_events_readonly(self):
# auto_process_events can be inspected, but it cannot be changed.
tester = UITester(auto_process_events=False)
self.assertIs(tester.auto_process_events, False)
with self.assertRaises(AttributeError):
tester.auto_process_events = True