bpo-30780: Add IDLE configdialog tests (GH-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>
(cherry picked from commit dd023ad161
)
Co-authored-by: Cheryl Sabella <cheryl.sabella@gmail.com>
This commit is contained in:
parent
414ab5de48
commit
5aefee6f98
|
@ -3,6 +3,9 @@ Released on 2019-12-16?
|
||||||
======================================
|
======================================
|
||||||
|
|
||||||
|
|
||||||
|
bpo-30780: Add remaining configdialog tests for buttons and
|
||||||
|
highlights and keys tabs.
|
||||||
|
|
||||||
bpo-39388: Settings dialog Cancel button cancels pending changes.
|
bpo-39388: Settings dialog Cancel button cancels pending changes.
|
||||||
|
|
||||||
bpo-39050: Settings dialog Help button again displays help text.
|
bpo-39050: Settings dialog Help button again displays help text.
|
||||||
|
|
|
@ -149,17 +149,19 @@ class ConfigDialog(Toplevel):
|
||||||
else:
|
else:
|
||||||
padding_args = {'padding': (6, 3)}
|
padding_args = {'padding': (6, 3)}
|
||||||
outer = Frame(self, padding=2)
|
outer = Frame(self, padding=2)
|
||||||
buttons = Frame(outer, padding=2)
|
buttons_frame = Frame(outer, padding=2)
|
||||||
|
self.buttons = {}
|
||||||
for txt, cmd in (
|
for txt, cmd in (
|
||||||
('Ok', self.ok),
|
('Ok', self.ok),
|
||||||
('Apply', self.apply),
|
('Apply', self.apply),
|
||||||
('Cancel', self.cancel),
|
('Cancel', self.cancel),
|
||||||
('Help', self.help)):
|
('Help', self.help)):
|
||||||
Button(buttons, text=txt, command=cmd, takefocus=FALSE,
|
self.buttons[txt] = Button(buttons_frame, text=txt, command=cmd,
|
||||||
**padding_args).pack(side=LEFT, padx=5)
|
takefocus=FALSE, **padding_args)
|
||||||
|
self.buttons[txt].pack(side=LEFT, padx=5)
|
||||||
# Add space above buttons.
|
# Add space above buttons.
|
||||||
Frame(outer, height=2, borderwidth=0).pack(side=TOP)
|
Frame(outer, height=2, borderwidth=0).pack(side=TOP)
|
||||||
buttons.pack(side=BOTTOM)
|
buttons_frame.pack(side=BOTTOM)
|
||||||
return outer
|
return outer
|
||||||
|
|
||||||
def ok(self):
|
def ok(self):
|
||||||
|
@ -205,7 +207,6 @@ class ConfigDialog(Toplevel):
|
||||||
|
|
||||||
Attributes accessed:
|
Attributes accessed:
|
||||||
note
|
note
|
||||||
|
|
||||||
Methods:
|
Methods:
|
||||||
view_text: Method from textview module.
|
view_text: Method from textview module.
|
||||||
"""
|
"""
|
||||||
|
@ -852,6 +853,7 @@ class HighPage(Frame):
|
||||||
text.configure(
|
text.configure(
|
||||||
font=('courier', 12, ''), cursor='hand2', width=1, height=1,
|
font=('courier', 12, ''), cursor='hand2', width=1, height=1,
|
||||||
takefocus=FALSE, highlightthickness=0, wrap=NONE)
|
takefocus=FALSE, highlightthickness=0, wrap=NONE)
|
||||||
|
# Prevent perhaps invisible selection of word or slice.
|
||||||
text.bind('<Double-Button-1>', lambda e: 'break')
|
text.bind('<Double-Button-1>', lambda e: 'break')
|
||||||
text.bind('<B1-Motion>', lambda e: 'break')
|
text.bind('<B1-Motion>', lambda e: 'break')
|
||||||
string_tags=(
|
string_tags=(
|
||||||
|
@ -1284,8 +1286,7 @@ class HighPage(Frame):
|
||||||
theme_name - string, the name of the new theme
|
theme_name - string, the name of the new theme
|
||||||
theme - dictionary containing the new theme
|
theme - dictionary containing the new theme
|
||||||
"""
|
"""
|
||||||
if not idleConf.userCfg['highlight'].has_section(theme_name):
|
idleConf.userCfg['highlight'].AddSection(theme_name)
|
||||||
idleConf.userCfg['highlight'].add_section(theme_name)
|
|
||||||
for element in theme:
|
for element in theme:
|
||||||
value = theme[element]
|
value = theme[element]
|
||||||
idleConf.userCfg['highlight'].SetOption(theme_name, element, value)
|
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_name - string, the name of the new key set
|
||||||
keyset - dictionary containing the new keybindings
|
keyset - dictionary containing the new keybindings
|
||||||
"""
|
"""
|
||||||
if not idleConf.userCfg['keys'].has_section(keyset_name):
|
idleConf.userCfg['keys'].AddSection(keyset_name)
|
||||||
idleConf.userCfg['keys'].add_section(keyset_name)
|
|
||||||
for event in keyset:
|
for event in keyset:
|
||||||
value = keyset[event]
|
value = keyset[event]
|
||||||
idleConf.userCfg['keys'].SetOption(keyset_name, event, value)
|
idleConf.userCfg['keys'].SetOption(keyset_name, event, value)
|
||||||
|
|
|
@ -8,7 +8,7 @@ requires('gui')
|
||||||
import unittest
|
import unittest
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
from idlelib.idle_test.mock_idle import Func
|
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 import config
|
||||||
from idlelib.configdialog import idleConf, changes, tracers
|
from idlelib.configdialog import idleConf, changes, tracers
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ highpage = changes['highlight']
|
||||||
keyspage = changes['keys']
|
keyspage = changes['keys']
|
||||||
extpage = changes['extensions']
|
extpage = changes['extensions']
|
||||||
|
|
||||||
|
|
||||||
def setUpModule():
|
def setUpModule():
|
||||||
global root, dialog
|
global root, dialog
|
||||||
idleConf.userCfg = testcfg
|
idleConf.userCfg = testcfg
|
||||||
|
@ -37,6 +38,7 @@ def setUpModule():
|
||||||
# root.withdraw() # Comment out, see issue 30870
|
# root.withdraw() # Comment out, see issue 30870
|
||||||
dialog = configdialog.ConfigDialog(root, 'Test', _utest=True)
|
dialog = configdialog.ConfigDialog(root, 'Test', _utest=True)
|
||||||
|
|
||||||
|
|
||||||
def tearDownModule():
|
def tearDownModule():
|
||||||
global root, dialog
|
global root, dialog
|
||||||
idleConf.userCfg = usercfg
|
idleConf.userCfg = usercfg
|
||||||
|
@ -48,22 +50,56 @@ def tearDownModule():
|
||||||
root = dialog = None
|
root = dialog = None
|
||||||
|
|
||||||
|
|
||||||
class DialogTest(unittest.TestCase):
|
class ConfigDialogTest(unittest.TestCase):
|
||||||
|
|
||||||
@mock.patch(__name__+'.dialog.destroy', new_callable=Func)
|
def test_deactivate_current_config(self):
|
||||||
def test_cancel(self, destroy):
|
pass
|
||||||
|
|
||||||
|
def activate_config_changes(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
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
|
changes['main']['something'] = 1
|
||||||
dialog.cancel()
|
d.buttons['Cancel'].invoke()
|
||||||
self.assertEqual(changes['main'], {})
|
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_click_help(self):
|
||||||
def test_help(self, view):
|
|
||||||
dialog.note.select(dialog.keyspage)
|
dialog.note.select(dialog.keyspage)
|
||||||
dialog.help()
|
with mock.patch.object(configdialog, 'view_text',
|
||||||
s = view.kwds['contents']
|
new_callable=Func) as view:
|
||||||
self.assertTrue(s.startswith('When you click') and
|
dialog.buttons['Help'].invoke()
|
||||||
s.endswith('a different name.\n'))
|
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):
|
class FontPageTest(unittest.TestCase):
|
||||||
|
@ -438,6 +474,48 @@ class HighPageTest(unittest.TestCase):
|
||||||
eq(d.highlight_target.get(), elem[tag])
|
eq(d.highlight_target.get(), elem[tag])
|
||||||
eq(d.set_highlight_target.called, count)
|
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):
|
def test_set_theme_type(self):
|
||||||
eq = self.assertEqual
|
eq = self.assertEqual
|
||||||
d = self.page
|
d = self.page
|
||||||
|
@ -666,8 +744,13 @@ class HighPageTest(unittest.TestCase):
|
||||||
idleConf.userCfg['highlight'].SetOption(theme_name, 'name', 'value')
|
idleConf.userCfg['highlight'].SetOption(theme_name, 'name', 'value')
|
||||||
highpage[theme_name] = {'option': 'True'}
|
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.
|
# 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)
|
d.custom_name.set(theme_name)
|
||||||
|
|
||||||
# Cancel deletion.
|
# Cancel deletion.
|
||||||
|
@ -675,7 +758,7 @@ class HighPageTest(unittest.TestCase):
|
||||||
d.button_delete_custom.invoke()
|
d.button_delete_custom.invoke()
|
||||||
eq(yesno.called, 1)
|
eq(yesno.called, 1)
|
||||||
eq(highpage[theme_name], {'option': 'True'})
|
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.deactivate_current_config.called, 0)
|
||||||
eq(dialog.activate_config_changes.called, 0)
|
eq(dialog.activate_config_changes.called, 0)
|
||||||
eq(d.set_theme_type.called, 0)
|
eq(d.set_theme_type.called, 0)
|
||||||
|
@ -685,13 +768,26 @@ class HighPageTest(unittest.TestCase):
|
||||||
d.button_delete_custom.invoke()
|
d.button_delete_custom.invoke()
|
||||||
eq(yesno.called, 2)
|
eq(yesno.called, 2)
|
||||||
self.assertNotIn(theme_name, highpage)
|
self.assertNotIn(theme_name, highpage)
|
||||||
eq(idleConf.GetSectionList('user', 'highlight'), [])
|
eq(idleConf.GetSectionList('user', 'highlight'), [theme_name2])
|
||||||
eq(d.custom_theme_on.state(), ('disabled',))
|
eq(d.custom_theme_on.state(), ())
|
||||||
eq(d.custom_name.get(), '- no custom themes -')
|
eq(d.custom_name.get(), theme_name2)
|
||||||
eq(dialog.deactivate_current_config.called, 1)
|
eq(dialog.deactivate_current_config.called, 1)
|
||||||
eq(dialog.activate_config_changes.called, 1)
|
eq(dialog.activate_config_changes.called, 1)
|
||||||
eq(d.set_theme_type.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 dialog.activate_config_changes, dialog.deactivate_current_config
|
||||||
del d.askyesno
|
del d.askyesno
|
||||||
|
|
||||||
|
@ -1059,8 +1155,13 @@ class KeysPageTest(unittest.TestCase):
|
||||||
idleConf.userCfg['keys'].SetOption(keyset_name, 'name', 'value')
|
idleConf.userCfg['keys'].SetOption(keyset_name, 'name', 'value')
|
||||||
keyspage[keyset_name] = {'option': 'True'}
|
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.
|
# 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)
|
d.custom_name.set(keyset_name)
|
||||||
|
|
||||||
# Cancel deletion.
|
# Cancel deletion.
|
||||||
|
@ -1068,7 +1169,7 @@ class KeysPageTest(unittest.TestCase):
|
||||||
d.button_delete_custom_keys.invoke()
|
d.button_delete_custom_keys.invoke()
|
||||||
eq(yesno.called, 1)
|
eq(yesno.called, 1)
|
||||||
eq(keyspage[keyset_name], {'option': 'True'})
|
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.deactivate_current_config.called, 0)
|
||||||
eq(dialog.activate_config_changes.called, 0)
|
eq(dialog.activate_config_changes.called, 0)
|
||||||
eq(d.set_keys_type.called, 0)
|
eq(d.set_keys_type.called, 0)
|
||||||
|
@ -1078,13 +1179,26 @@ class KeysPageTest(unittest.TestCase):
|
||||||
d.button_delete_custom_keys.invoke()
|
d.button_delete_custom_keys.invoke()
|
||||||
eq(yesno.called, 2)
|
eq(yesno.called, 2)
|
||||||
self.assertNotIn(keyset_name, keyspage)
|
self.assertNotIn(keyset_name, keyspage)
|
||||||
eq(idleConf.GetSectionList('user', 'keys'), [])
|
eq(idleConf.GetSectionList('user', 'keys'), [keyset_name2])
|
||||||
eq(d.custom_keyset_on.state(), ('disabled',))
|
eq(d.custom_keyset_on.state(), ())
|
||||||
eq(d.custom_name.get(), '- no custom keys -')
|
eq(d.custom_name.get(), keyset_name2)
|
||||||
eq(dialog.deactivate_current_config.called, 1)
|
eq(dialog.deactivate_current_config.called, 1)
|
||||||
eq(dialog.activate_config_changes.called, 1)
|
eq(dialog.activate_config_changes.called, 1)
|
||||||
eq(d.set_keys_type.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 dialog.activate_config_changes, dialog.deactivate_current_config
|
||||||
del d.askyesno
|
del d.askyesno
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Add remaining configdialog tests for buttons and highlights and keys tabs.
|
Loading…
Reference in New Issue