diff --git a/traitsui/testing/tester/qt4/default_registry.py b/traitsui/testing/tester/qt4/default_registry.py index 3aa7c72f3..90022930b 100644 --- a/traitsui/testing/tester/qt4/default_registry.py +++ b/traitsui/testing/tester/qt4/default_registry.py @@ -14,6 +14,7 @@ from traitsui.testing.tester.qt4.implementation import ( button_editor, check_list_editor, + instance_editor, list_editor, range_editor, text_editor, @@ -48,4 +49,7 @@ def get_default_registry(): # RangeEditor range_editor.register(registry) + # InstanceEditor + instance_editor.register(registry) + return registry diff --git a/traitsui/testing/tester/qt4/implementation/instance_editor.py b/traitsui/testing/tester/qt4/implementation/instance_editor.py new file mode 100644 index 000000000..c0a290065 --- /dev/null +++ b/traitsui/testing/tester/qt4/implementation/instance_editor.py @@ -0,0 +1,59 @@ +# 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.testing.tester import command +from traitsui.testing.tester.qt4.helpers import mouse_click_qwidget +from traitsui.testing.tester.registry_helper import register_nested_ui_solvers +from traitsui.qt4.instance_editor import ( + CustomEditor, + SimpleEditor +) + + +def _get_nested_ui_simple(target): + """ Obtains a nested UI within a Simple Instance Editor. + + Parameters + ---------- + target : instance of SimpleEditor + """ + return target._dialog_ui + + +def _get_nested_ui_custom(target): + """ Obtains a nested UI within a Custom Instance Editor. + + Parameters + ---------- + target : instance of CustomEditor + """ + return target._ui + + +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. + """ + registry.register_handler( + target_class=SimpleEditor, + interaction_class=command.MouseClick, + handler=lambda wrapper, _: ( + mouse_click_qwidget(wrapper.target._button, delay=wrapper.delay) + ) + ) + register_nested_ui_solvers(registry, SimpleEditor, _get_nested_ui_simple) + register_nested_ui_solvers(registry, CustomEditor, _get_nested_ui_custom) diff --git a/traitsui/testing/tester/wx/default_registry.py b/traitsui/testing/tester/wx/default_registry.py index 3cd0d61de..cb378cd7c 100644 --- a/traitsui/testing/tester/wx/default_registry.py +++ b/traitsui/testing/tester/wx/default_registry.py @@ -14,6 +14,7 @@ from traitsui.testing.tester.wx.implementation import ( button_editor, check_list_editor, + instance_editor, list_editor, range_editor, text_editor, @@ -48,4 +49,7 @@ def get_default_registry(): # RangeEditor range_editor.register(registry) + # InstanceEditor + instance_editor.register(registry) + return registry diff --git a/traitsui/testing/tester/wx/implementation/instance_editor.py b/traitsui/testing/tester/wx/implementation/instance_editor.py new file mode 100644 index 000000000..0d1a3b2ec --- /dev/null +++ b/traitsui/testing/tester/wx/implementation/instance_editor.py @@ -0,0 +1,59 @@ +# 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.testing.tester import command +from traitsui.testing.tester.registry_helper import register_nested_ui_solvers +from traitsui.testing.tester.wx.helpers import mouse_click_button +from traitsui.wx.instance_editor import ( + CustomEditor, + SimpleEditor +) + + +def _get_nested_ui_simple(target): + """ Obtains a nested UI within a Simple Instance Editor. + + Parameters + ---------- + target : instance of SimpleEditor + """ + return target._dialog_ui + + +def _get_nested_ui_custom(target): + """ Obtains a nested UI within a Custom Instance Editor. + + Parameters + ---------- + target : instance of CustomEditor + """ + return target._ui + + +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. + """ + registry.register_handler( + target_class=SimpleEditor, + interaction_class=command.MouseClick, + handler=lambda wrapper, _: mouse_click_button( + wrapper.target._button, delay=wrapper.delay, + ) + ) + register_nested_ui_solvers(registry, SimpleEditor, _get_nested_ui_simple) + register_nested_ui_solvers(registry, CustomEditor, _get_nested_ui_custom) diff --git a/traitsui/tests/editors/test_instance_editor.py b/traitsui/tests/editors/test_instance_editor.py index 5cae65594..450064427 100644 --- a/traitsui/tests/editors/test_instance_editor.py +++ b/traitsui/tests/editors/test_instance_editor.py @@ -4,13 +4,13 @@ from traitsui.item import Item from traitsui.view import View from traitsui.tests._tools import ( - create_ui, - press_ok_button, requires_toolkit, - reraise_exceptions, ToolkitName, ) +from traitsui.testing.tester import command, query +from traitsui.testing.tester.ui_tester import UITester + class EditedInstance(HasTraits): value = Str() @@ -19,28 +19,63 @@ class EditedInstance(HasTraits): class NonmodalInstanceEditor(HasTraits): inst = Instance(EditedInstance, ()) - traits_view = View(Item("inst", style="simple"), buttons=["OK"]) +def get_view(style): + return View(Item("inst", style=style), buttons=["OK"]) + + +@requires_toolkit([ToolkitName.qt, ToolkitName.wx]) class TestInstanceEditor(unittest.TestCase): - @requires_toolkit([ToolkitName.qt]) def test_simple_editor(self): obj = NonmodalInstanceEditor() - with reraise_exceptions(), create_ui(obj) as ui: - editor = ui.get_editors("inst")[0] + tester = UITester() + with tester.create_ui(obj, dict(view=get_view("simple"))) as ui: + instance = tester.find_by_name(ui, "inst") + instance.perform(command.MouseClick()) + value_txt = instance.find_by_name("value") + value_txt.perform(command.KeySequence("abc")) + self.assertEqual(obj.inst.value, "abc") - # make the dialog appear - editor._button.click() + def test_custom_editor(self): + obj = NonmodalInstanceEditor() + tester = UITester() + with tester.create_ui(obj, dict(view=get_view("custom"))) as ui: + value_txt = tester.find_by_name(ui, "inst").find_by_name("value") + value_txt.perform(command.KeySequence("abc")) + self.assertEqual(obj.inst.value, "abc") - # close the ui dialog - press_ok_button(editor._dialog_ui) + def test_custom_editor_resynch_editor(self): + edited_inst = EditedInstance(value='hello') + obj = NonmodalInstanceEditor(inst=edited_inst) + tester = UITester() + with tester.create_ui(obj, dict(view=get_view("custom"))) as ui: + value_txt = tester.find_by_name(ui, "inst").find_by_name("value") + displayed = value_txt.inspect(query.DisplayedText()) + self.assertEqual(displayed, "hello") + edited_inst.value = "bye" + displayed = value_txt.inspect(query.DisplayedText()) + self.assertEqual(displayed, "bye") + + def test_simple_editor_resynch_editor(self): + edited_inst = EditedInstance(value='hello') + obj = NonmodalInstanceEditor(inst=edited_inst) + tester = UITester() + with tester.create_ui(obj, dict(view=get_view("simple"))) as ui: + instance = tester.find_by_name(ui, "inst") + instance.perform(command.MouseClick()) + + value_txt = instance.find_by_name("value") + displayed = value_txt.inspect(query.DisplayedText()) + self.assertEqual(displayed, "hello") + edited_inst.value = "bye" + displayed = value_txt.inspect(query.DisplayedText()) + self.assertEqual(displayed, "bye") - @requires_toolkit([ToolkitName.qt]) def test_simple_editor_parent_closed(self): obj = NonmodalInstanceEditor() - with reraise_exceptions(), create_ui(obj) as ui: - editor = ui.get_editors("inst")[0] - - # make the dialog appear - editor._button.click() + tester = UITester() + with tester.create_ui(obj, dict(view=get_view('simple'))) as ui: + instance = tester.find_by_name(ui, "inst") + instance.perform(command.MouseClick()) diff --git a/traitsui/wx/instance_editor.py b/traitsui/wx/instance_editor.py index 282636a14..adb329502 100644 --- a/traitsui/wx/instance_editor.py +++ b/traitsui/wx/instance_editor.py @@ -23,7 +23,7 @@ import wx from pyface.wx.drag_and_drop import PythonDropTarget -from traits.api import HasTraits, Property +from traits.api import HasTraits, Instance, Property # FIXME: ToolkitEditorFactory is a proxy class defined here just for backward # compatibility. The class has been moved to the @@ -488,6 +488,9 @@ class SimpleEditor(CustomEditor): orientation = wx.HORIZONTAL extra = 2 + #: The ui instance for the currently open editor dialog + _dialog_ui = Instance("traitsui.ui.UI") + def create_editor(self, parent, sizer): """ Creates the editor control (a button). """ @@ -502,6 +505,10 @@ def dispose(self): if button is not None: button.Bind(wx.EVT_BUTTON, None, id=button.GetId()) + if self._dialog_ui is not None: + self._dialog_ui.dispose() + self._dialog_ui = None + super(SimpleEditor, self).dispose() def edit_instance(self, event): @@ -527,6 +534,7 @@ def edit_instance(self, event): # have its own: if ui.history is None: ui.history = self.ui.history + self._dialog_ui = ui def resynch_editor(self): """ Resynchronizes the contents of the editor when the object trait