test_tooltip.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. """Test tooltip, coverage 100%.
  2. Coverage is 100% after excluding 6 lines with "# pragma: no cover".
  3. They involve TclErrors that either should or should not happen in a
  4. particular situation, and which are 'pass'ed if they do.
  5. """
  6. from idlelib.tooltip import TooltipBase, Hovertip
  7. from test.support import requires
  8. requires('gui')
  9. from functools import wraps
  10. import time
  11. from tkinter import Button, Tk, Toplevel
  12. import unittest
  13. def setUpModule():
  14. global root
  15. root = Tk()
  16. def tearDownModule():
  17. global root
  18. root.update_idletasks()
  19. root.destroy()
  20. del root
  21. def add_call_counting(func):
  22. @wraps(func)
  23. def wrapped_func(*args, **kwargs):
  24. wrapped_func.call_args_list.append((args, kwargs))
  25. return func(*args, **kwargs)
  26. wrapped_func.call_args_list = []
  27. return wrapped_func
  28. def _make_top_and_button(testobj):
  29. global root
  30. top = Toplevel(root)
  31. testobj.addCleanup(top.destroy)
  32. top.title("Test tooltip")
  33. button = Button(top, text='ToolTip test button')
  34. button.pack()
  35. testobj.addCleanup(button.destroy)
  36. top.lift()
  37. return top, button
  38. class ToolTipBaseTest(unittest.TestCase):
  39. def setUp(self):
  40. self.top, self.button = _make_top_and_button(self)
  41. def test_base_class_is_unusable(self):
  42. global root
  43. top = Toplevel(root)
  44. self.addCleanup(top.destroy)
  45. button = Button(top, text='ToolTip test button')
  46. button.pack()
  47. self.addCleanup(button.destroy)
  48. with self.assertRaises(NotImplementedError):
  49. tooltip = TooltipBase(button)
  50. tooltip.showtip()
  51. class HovertipTest(unittest.TestCase):
  52. def setUp(self):
  53. self.top, self.button = _make_top_and_button(self)
  54. def is_tipwindow_shown(self, tooltip):
  55. return tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()
  56. def test_showtip(self):
  57. tooltip = Hovertip(self.button, 'ToolTip text')
  58. self.addCleanup(tooltip.hidetip)
  59. self.assertFalse(self.is_tipwindow_shown(tooltip))
  60. tooltip.showtip()
  61. self.assertTrue(self.is_tipwindow_shown(tooltip))
  62. def test_showtip_twice(self):
  63. tooltip = Hovertip(self.button, 'ToolTip text')
  64. self.addCleanup(tooltip.hidetip)
  65. self.assertFalse(self.is_tipwindow_shown(tooltip))
  66. tooltip.showtip()
  67. self.assertTrue(self.is_tipwindow_shown(tooltip))
  68. orig_tipwindow = tooltip.tipwindow
  69. tooltip.showtip()
  70. self.assertTrue(self.is_tipwindow_shown(tooltip))
  71. self.assertIs(tooltip.tipwindow, orig_tipwindow)
  72. def test_hidetip(self):
  73. tooltip = Hovertip(self.button, 'ToolTip text')
  74. self.addCleanup(tooltip.hidetip)
  75. tooltip.showtip()
  76. tooltip.hidetip()
  77. self.assertFalse(self.is_tipwindow_shown(tooltip))
  78. def test_showtip_on_mouse_enter_no_delay(self):
  79. tooltip = Hovertip(self.button, 'ToolTip text', hover_delay=None)
  80. self.addCleanup(tooltip.hidetip)
  81. tooltip.showtip = add_call_counting(tooltip.showtip)
  82. root.update()
  83. self.assertFalse(self.is_tipwindow_shown(tooltip))
  84. self.button.event_generate('<Enter>', x=0, y=0)
  85. root.update()
  86. self.assertTrue(self.is_tipwindow_shown(tooltip))
  87. self.assertGreater(len(tooltip.showtip.call_args_list), 0)
  88. def test_hover_with_delay(self):
  89. # Run multiple tests requiring an actual delay simultaneously.
  90. # Test #1: A hover tip with a non-zero delay appears after the delay.
  91. tooltip1 = Hovertip(self.button, 'ToolTip text', hover_delay=100)
  92. self.addCleanup(tooltip1.hidetip)
  93. tooltip1.showtip = add_call_counting(tooltip1.showtip)
  94. root.update()
  95. self.assertFalse(self.is_tipwindow_shown(tooltip1))
  96. self.button.event_generate('<Enter>', x=0, y=0)
  97. root.update()
  98. self.assertFalse(self.is_tipwindow_shown(tooltip1))
  99. # Test #2: A hover tip with a non-zero delay doesn't appear when
  100. # the mouse stops hovering over the base widget before the delay
  101. # expires.
  102. tooltip2 = Hovertip(self.button, 'ToolTip text', hover_delay=100)
  103. self.addCleanup(tooltip2.hidetip)
  104. tooltip2.showtip = add_call_counting(tooltip2.showtip)
  105. root.update()
  106. self.button.event_generate('<Enter>', x=0, y=0)
  107. root.update()
  108. self.button.event_generate('<Leave>', x=0, y=0)
  109. root.update()
  110. time.sleep(0.15)
  111. root.update()
  112. # Test #1 assertions.
  113. self.assertTrue(self.is_tipwindow_shown(tooltip1))
  114. self.assertGreater(len(tooltip1.showtip.call_args_list), 0)
  115. # Test #2 assertions.
  116. self.assertFalse(self.is_tipwindow_shown(tooltip2))
  117. self.assertEqual(tooltip2.showtip.call_args_list, [])
  118. def test_hidetip_on_mouse_leave(self):
  119. tooltip = Hovertip(self.button, 'ToolTip text', hover_delay=None)
  120. self.addCleanup(tooltip.hidetip)
  121. tooltip.showtip = add_call_counting(tooltip.showtip)
  122. root.update()
  123. self.button.event_generate('<Enter>', x=0, y=0)
  124. root.update()
  125. self.button.event_generate('<Leave>', x=0, y=0)
  126. root.update()
  127. self.assertFalse(self.is_tipwindow_shown(tooltip))
  128. self.assertGreater(len(tooltip.showtip.call_args_list), 0)
  129. if __name__ == '__main__':
  130. unittest.main(verbosity=2)