diff options
author | Cheryl Sabella <cheryl.sabella@gmail.com> | 2020-01-27 22:15:56 (GMT) |
---|---|---|
committer | Terry Jan Reedy <tjreedy@udel.edu> | 2020-01-27 22:15:56 (GMT) |
commit | dd023ad1619b6f1ab313986e8953eea32c18f50c (patch) | |
tree | 36889888b43e651314f5480d93a28653628eec03 /Lib/idlelib | |
parent | 2528a6c3d0660c03ae43d796628462ccf8e58190 (diff) | |
download | cpython-dd023ad1619b6f1ab313986e8953eea32c18f50c.zip cpython-dd023ad1619b6f1ab313986e8953eea32c18f50c.tar.gz cpython-dd023ad1619b6f1ab313986e8953eea32c18f50c.tar.bz2 |
bpo-30780: Add IDLE configdialog tests (#3592)
Expose dialog buttons to test code and complete their test coverage.
Complete test coverage for highlights and keys tabs.
Co-authored-by: Terry Jan Reedy <tjreedy@udel.edu>
Diffstat (limited to 'Lib/idlelib')
-rw-r--r-- | Lib/idlelib/NEWS.txt | 3 | ||||
-rw-r--r-- | Lib/idlelib/configdialog.py | 18 | ||||
-rw-r--r-- | Lib/idlelib/idle_test/test_configdialog.py | 158 |
3 files changed, 148 insertions, 31 deletions
diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index eda7c27..2b54398 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -3,6 +3,9 @@ Released on 2020-10-05? ====================================== +bpo-30780: Add remaining configdialog tests for buttons and +highlights and keys tabs. + bpo-39388: Settings dialog Cancel button cancels pending changes. bpo-39050: Settings dialog Help button again displays help text. diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 2f95c9c..2235973 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -149,17 +149,19 @@ class ConfigDialog(Toplevel): else: padding_args = {'padding': (6, 3)} outer = Frame(self, padding=2) - buttons = Frame(outer, padding=2) + buttons_frame = Frame(outer, padding=2) + self.buttons = {} for txt, cmd in ( ('Ok', self.ok), ('Apply', self.apply), ('Cancel', self.cancel), ('Help', self.help)): - Button(buttons, text=txt, command=cmd, takefocus=FALSE, - **padding_args).pack(side=LEFT, padx=5) + self.buttons[txt] = Button(buttons_frame, text=txt, command=cmd, + takefocus=FALSE, **padding_args) + self.buttons[txt].pack(side=LEFT, padx=5) # Add space above buttons. Frame(outer, height=2, borderwidth=0).pack(side=TOP) - buttons.pack(side=BOTTOM) + buttons_frame.pack(side=BOTTOM) return outer def ok(self): @@ -205,7 +207,6 @@ class ConfigDialog(Toplevel): Attributes accessed: note - Methods: view_text: Method from textview module. """ @@ -852,6 +853,7 @@ class HighPage(Frame): text.configure( font=('courier', 12, ''), cursor='hand2', width=1, height=1, takefocus=FALSE, highlightthickness=0, wrap=NONE) + # Prevent perhaps invisible selection of word or slice. text.bind('<Double-Button-1>', lambda e: 'break') text.bind('<B1-Motion>', lambda e: 'break') string_tags=( @@ -1284,8 +1286,7 @@ class HighPage(Frame): theme_name - string, the name of the new theme theme - dictionary containing the new theme """ - if not idleConf.userCfg['highlight'].has_section(theme_name): - idleConf.userCfg['highlight'].add_section(theme_name) + idleConf.userCfg['highlight'].AddSection(theme_name) for element in theme: value = theme[element] idleConf.userCfg['highlight'].SetOption(theme_name, element, value) @@ -1730,8 +1731,7 @@ class KeysPage(Frame): keyset_name - string, the name of the new key set keyset - dictionary containing the new keybindings """ - if not idleConf.userCfg['keys'].has_section(keyset_name): - idleConf.userCfg['keys'].add_section(keyset_name) + idleConf.userCfg['keys'].AddSection(keyset_name) for event in keyset: value = keyset[event] idleConf.userCfg['keys'].SetOption(keyset_name, event, value) diff --git a/Lib/idlelib/idle_test/test_configdialog.py b/Lib/idlelib/idle_test/test_configdialog.py index 817a3521..1fea6d4 100644 --- a/Lib/idlelib/idle_test/test_configdialog.py +++ b/Lib/idlelib/idle_test/test_configdialog.py @@ -8,7 +8,7 @@ requires('gui') import unittest from unittest import mock from idlelib.idle_test.mock_idle import Func -from tkinter import Tk, StringVar, IntVar, BooleanVar, DISABLED, NORMAL +from tkinter import (Tk, StringVar, IntVar, BooleanVar, DISABLED, NORMAL) from idlelib import config from idlelib.configdialog import idleConf, changes, tracers @@ -30,6 +30,7 @@ highpage = changes['highlight'] keyspage = changes['keys'] extpage = changes['extensions'] + def setUpModule(): global root, dialog idleConf.userCfg = testcfg @@ -37,6 +38,7 @@ def setUpModule(): # root.withdraw() # Comment out, see issue 30870 dialog = configdialog.ConfigDialog(root, 'Test', _utest=True) + def tearDownModule(): global root, dialog idleConf.userCfg = usercfg @@ -48,22 +50,56 @@ def tearDownModule(): root = dialog = None -class DialogTest(unittest.TestCase): +class ConfigDialogTest(unittest.TestCase): + + def test_deactivate_current_config(self): + pass + + def activate_config_changes(self): + pass + - @mock.patch(__name__+'.dialog.destroy', new_callable=Func) - def test_cancel(self, destroy): +class ButtonTest(unittest.TestCase): + + def test_click_ok(self): + d = dialog + apply = d.apply = mock.Mock() + destroy = d.destroy = mock.Mock() + d.buttons['Ok'].invoke() + apply.assert_called_once() + destroy.assert_called_once() + del d.destroy, d.apply + + def test_click_apply(self): + d = dialog + deactivate = d.deactivate_current_config = mock.Mock() + save_ext = d.save_all_changed_extensions = mock.Mock() + activate = d.activate_config_changes = mock.Mock() + d.buttons['Apply'].invoke() + deactivate.assert_called_once() + save_ext.assert_called_once() + activate.assert_called_once() + del d.save_all_changed_extensions + del d.activate_config_changes, d.deactivate_current_config + + def test_click_cancel(self): + d = dialog + d.destroy = Func() changes['main']['something'] = 1 - dialog.cancel() + d.buttons['Cancel'].invoke() self.assertEqual(changes['main'], {}) - self.assertEqual(destroy.called, 1) + self.assertEqual(d.destroy.called, 1) + del d.destroy - @mock.patch('idlelib.configdialog.view_text', new_callable=Func) - def test_help(self, view): + def test_click_help(self): dialog.note.select(dialog.keyspage) - dialog.help() - s = view.kwds['contents'] - self.assertTrue(s.startswith('When you click') and - s.endswith('a different name.\n')) + with mock.patch.object(configdialog, 'view_text', + new_callable=Func) as view: + dialog.buttons['Help'].invoke() + title, contents = view.kwds['title'], view.kwds['contents'] + self.assertEqual(title, 'Help for IDLE preferences') + self.assertTrue(contents.startswith('When you click') and + contents.endswith('a different name.\n')) class FontPageTest(unittest.TestCase): @@ -438,6 +474,48 @@ class HighPageTest(unittest.TestCase): eq(d.highlight_target.get(), elem[tag]) eq(d.set_highlight_target.called, count) + def test_highlight_sample_double_click(self): + # Test double click on highlight_sample. + eq = self.assertEqual + d = self.page + + hs = d.highlight_sample + hs.focus_force() + hs.see(1.0) + hs.update_idletasks() + + # Test binding from configdialog. + hs.event_generate('<Enter>', x=0, y=0) + hs.event_generate('<Motion>', x=0, y=0) + # Double click is a sequence of two clicks in a row. + for _ in range(2): + hs.event_generate('<ButtonPress-1>', x=0, y=0) + hs.event_generate('<ButtonRelease-1>', x=0, y=0) + + eq(hs.tag_ranges('sel'), ()) + + def test_highlight_sample_b1_motion(self): + # Test button motion on highlight_sample. + eq = self.assertEqual + d = self.page + + hs = d.highlight_sample + hs.focus_force() + hs.see(1.0) + hs.update_idletasks() + + x, y, dx, dy, offset = hs.dlineinfo('1.0') + + # Test binding from configdialog. + hs.event_generate('<Leave>') + hs.event_generate('<Enter>') + hs.event_generate('<Motion>', x=x, y=y) + hs.event_generate('<ButtonPress-1>', x=x, y=y) + hs.event_generate('<B1-Motion>', x=dx, y=dy) + hs.event_generate('<ButtonRelease-1>', x=dx, y=dy) + + eq(hs.tag_ranges('sel'), ()) + def test_set_theme_type(self): eq = self.assertEqual d = self.page @@ -666,8 +744,13 @@ class HighPageTest(unittest.TestCase): idleConf.userCfg['highlight'].SetOption(theme_name, 'name', 'value') highpage[theme_name] = {'option': 'True'} + theme_name2 = 'other theme' + idleConf.userCfg['highlight'].SetOption(theme_name2, 'name', 'value') + highpage[theme_name2] = {'option': 'False'} + # Force custom theme. - d.theme_source.set(False) + d.custom_theme_on.state(('!disabled',)) + d.custom_theme_on.invoke() d.custom_name.set(theme_name) # Cancel deletion. @@ -675,7 +758,7 @@ class HighPageTest(unittest.TestCase): d.button_delete_custom.invoke() eq(yesno.called, 1) eq(highpage[theme_name], {'option': 'True'}) - eq(idleConf.GetSectionList('user', 'highlight'), ['spam theme']) + eq(idleConf.GetSectionList('user', 'highlight'), [theme_name, theme_name2]) eq(dialog.deactivate_current_config.called, 0) eq(dialog.activate_config_changes.called, 0) eq(d.set_theme_type.called, 0) @@ -685,13 +768,26 @@ class HighPageTest(unittest.TestCase): d.button_delete_custom.invoke() eq(yesno.called, 2) self.assertNotIn(theme_name, highpage) - eq(idleConf.GetSectionList('user', 'highlight'), []) - eq(d.custom_theme_on.state(), ('disabled',)) - eq(d.custom_name.get(), '- no custom themes -') + eq(idleConf.GetSectionList('user', 'highlight'), [theme_name2]) + eq(d.custom_theme_on.state(), ()) + eq(d.custom_name.get(), theme_name2) eq(dialog.deactivate_current_config.called, 1) eq(dialog.activate_config_changes.called, 1) eq(d.set_theme_type.called, 1) + # Confirm deletion of second theme - empties list. + d.custom_name.set(theme_name2) + yesno.result = True + d.button_delete_custom.invoke() + eq(yesno.called, 3) + self.assertNotIn(theme_name, highpage) + eq(idleConf.GetSectionList('user', 'highlight'), []) + eq(d.custom_theme_on.state(), ('disabled',)) + eq(d.custom_name.get(), '- no custom themes -') + eq(dialog.deactivate_current_config.called, 2) + eq(dialog.activate_config_changes.called, 2) + eq(d.set_theme_type.called, 2) + del dialog.activate_config_changes, dialog.deactivate_current_config del d.askyesno @@ -1059,8 +1155,13 @@ class KeysPageTest(unittest.TestCase): idleConf.userCfg['keys'].SetOption(keyset_name, 'name', 'value') keyspage[keyset_name] = {'option': 'True'} + keyset_name2 = 'other key set' + idleConf.userCfg['keys'].SetOption(keyset_name2, 'name', 'value') + keyspage[keyset_name2] = {'option': 'False'} + # Force custom keyset. - d.keyset_source.set(False) + d.custom_keyset_on.state(('!disabled',)) + d.custom_keyset_on.invoke() d.custom_name.set(keyset_name) # Cancel deletion. @@ -1068,7 +1169,7 @@ class KeysPageTest(unittest.TestCase): d.button_delete_custom_keys.invoke() eq(yesno.called, 1) eq(keyspage[keyset_name], {'option': 'True'}) - eq(idleConf.GetSectionList('user', 'keys'), ['spam key set']) + eq(idleConf.GetSectionList('user', 'keys'), [keyset_name, keyset_name2]) eq(dialog.deactivate_current_config.called, 0) eq(dialog.activate_config_changes.called, 0) eq(d.set_keys_type.called, 0) @@ -1078,13 +1179,26 @@ class KeysPageTest(unittest.TestCase): d.button_delete_custom_keys.invoke() eq(yesno.called, 2) self.assertNotIn(keyset_name, keyspage) - eq(idleConf.GetSectionList('user', 'keys'), []) - eq(d.custom_keyset_on.state(), ('disabled',)) - eq(d.custom_name.get(), '- no custom keys -') + eq(idleConf.GetSectionList('user', 'keys'), [keyset_name2]) + eq(d.custom_keyset_on.state(), ()) + eq(d.custom_name.get(), keyset_name2) eq(dialog.deactivate_current_config.called, 1) eq(dialog.activate_config_changes.called, 1) eq(d.set_keys_type.called, 1) + # Confirm deletion of second keyset - empties list. + d.custom_name.set(keyset_name2) + yesno.result = True + d.button_delete_custom_keys.invoke() + eq(yesno.called, 3) + self.assertNotIn(keyset_name, keyspage) + eq(idleConf.GetSectionList('user', 'keys'), []) + eq(d.custom_keyset_on.state(), ('disabled',)) + eq(d.custom_name.get(), '- no custom keys -') + eq(dialog.deactivate_current_config.called, 2) + eq(dialog.activate_config_changes.called, 2) + eq(d.set_keys_type.called, 2) + del dialog.activate_config_changes, dialog.deactivate_current_config del d.askyesno |