|
1 | 1 | import unittest |
2 | 2 | import tkinter |
| 3 | +from tkinter import messagebox |
3 | 4 | from test.support import requires, swap_attr |
4 | 5 | from test.test_tkinter.support import setUpModule # noqa: F401 |
5 | | -from test.test_tkinter.support import AbstractDefaultRootTest |
6 | | -from tkinter.simpledialog import Dialog, askinteger |
| 6 | +from test.test_tkinter.support import AbstractDefaultRootTest, AbstractTkTest |
| 7 | +from tkinter.simpledialog import (Dialog, askinteger, |
| 8 | + _QueryInteger, _QueryFloat, _QueryString) |
7 | 9 |
|
8 | 10 | requires('gui') |
9 | 11 |
|
@@ -32,5 +34,65 @@ def mock_wait_window(w): |
32 | 34 | self.assertRaises(RuntimeError, askinteger, "Go To Line", "Line number") |
33 | 35 |
|
34 | 36 |
|
| 37 | +class QueryDialogTest(AbstractTkTest, unittest.TestCase): |
| 38 | + # The query dialogs are modal: their __init__ blocks in wait_window(). |
| 39 | + # Mock that out so the dialog stays alive and can be driven with generated |
| 40 | + # events, exercising the <Return>/<Escape> bindings and the validation. |
| 41 | + |
| 42 | + def open(self, query, **kw): |
| 43 | + with swap_attr(Dialog, 'wait_window', staticmethod(lambda w: None)): |
| 44 | + d = query("Title", "Prompt", parent=self.root, **kw) |
| 45 | + self.addCleanup(lambda: d.winfo_exists() and d.destroy()) |
| 46 | + d.focus_force() |
| 47 | + d.update() |
| 48 | + return d |
| 49 | + |
| 50 | + def enter(self, d, value, key='<Return>'): |
| 51 | + d.entry.delete(0, 'end') |
| 52 | + d.entry.insert(0, value) |
| 53 | + d.event_generate(key) |
| 54 | + d.update() |
| 55 | + |
| 56 | + def test_return_accepts(self): |
| 57 | + for query, value, expected in [ |
| 58 | + (_QueryInteger, '42', 42), |
| 59 | + (_QueryFloat, '1.5', 1.5), |
| 60 | + (_QueryString, 'spam', 'spam'), |
| 61 | + ]: |
| 62 | + with self.subTest(query=query.__name__): |
| 63 | + d = self.open(query) |
| 64 | + self.enter(d, value) |
| 65 | + self.assertEqual(d.result, expected) |
| 66 | + self.assertFalse(d.winfo_exists()) # The dialog closed. |
| 67 | + |
| 68 | + def test_escape_cancels(self): |
| 69 | + d = self.open(_QueryString) |
| 70 | + self.enter(d, 'spam', '<Escape>') |
| 71 | + self.assertIsNone(d.result) |
| 72 | + self.assertFalse(d.winfo_exists()) |
| 73 | + |
| 74 | + def test_invalid_value(self): |
| 75 | + warnings = [] |
| 76 | + d = self.open(_QueryInteger) |
| 77 | + with swap_attr(messagebox, 'showwarning', |
| 78 | + lambda *a, **k: warnings.append(a)): |
| 79 | + self.enter(d, 'not a number') |
| 80 | + self.assertIsNone(d.result) |
| 81 | + self.assertTrue(d.winfo_exists()) # The dialog stays open. |
| 82 | + self.assertTrue(warnings) |
| 83 | + |
| 84 | + def test_out_of_range(self): |
| 85 | + warnings = [] |
| 86 | + d = self.open(_QueryInteger, minvalue=10, maxvalue=20) |
| 87 | + with swap_attr(messagebox, 'showwarning', |
| 88 | + lambda *a, **k: warnings.append(a)): |
| 89 | + self.enter(d, '5') # Below the minimum. |
| 90 | + self.assertIsNone(d.result) |
| 91 | + self.enter(d, '25') # Above the maximum. |
| 92 | + self.assertIsNone(d.result) |
| 93 | + self.assertTrue(d.winfo_exists()) |
| 94 | + self.assertEqual(len(warnings), 2) |
| 95 | + |
| 96 | + |
35 | 97 | if __name__ == "__main__": |
36 | 98 | unittest.main() |
0 commit comments