1"""Test textview, coverage 100%.
2
3Since all methods and functions create (or destroy) a ViewWindow, which
4is a widget containing a widget, etcetera, all tests must be gui tests.
5Using mock Text would not change this.  Other mocks are used to retrieve
6information about calls.
7"""
8from idlelib import textview as tv
9from test.support import requires
10requires('gui')
11
12import os
13import unittest
14from tkinter import Tk, TclError, CHAR, NONE, WORD
15from tkinter.ttk import Button
16from idlelib.idle_test.mock_idle import Func
17from idlelib.idle_test.mock_tk import Mbox_func
18
19def setUpModule():
20    global root
21    root = Tk()
22    root.withdraw()
23
24def tearDownModule():
25    global root
26    root.update_idletasks()
27    root.destroy()
28    del root
29
30# If we call ViewWindow or wrapper functions with defaults
31# modal=True, _utest=False, test hangs on call to wait_window.
32# Have also gotten tk error 'can't invoke "event" command'.
33
34
35class VW(tv.ViewWindow):  # Used in ViewWindowTest.
36    transient = Func()
37    grab_set = Func()
38    wait_window = Func()
39
40
41# Call wrapper class VW with mock wait_window.
42class ViewWindowTest(unittest.TestCase):
43
44    def setUp(self):
45        VW.transient.__init__()
46        VW.grab_set.__init__()
47        VW.wait_window.__init__()
48
49    def test_init_modal(self):
50        view = VW(root, 'Title', 'test text')
51        self.assertTrue(VW.transient.called)
52        self.assertTrue(VW.grab_set.called)
53        self.assertTrue(VW.wait_window.called)
54        view.ok()
55
56    def test_init_nonmodal(self):
57        view = VW(root, 'Title', 'test text', modal=False)
58        self.assertFalse(VW.transient.called)
59        self.assertFalse(VW.grab_set.called)
60        self.assertFalse(VW.wait_window.called)
61        view.ok()
62
63    def test_ok(self):
64        view = VW(root, 'Title', 'test text', modal=False)
65        view.destroy = Func()
66        view.ok()
67        self.assertTrue(view.destroy.called)
68        del view.destroy  # Unmask real function.
69        view.destroy()
70
71
72class AutoHideScrollbarTest(unittest.TestCase):
73    # Method set is tested in ScrollableTextFrameTest
74    def test_forbidden_geometry(self):
75        scroll = tv.AutoHideScrollbar(root)
76        self.assertRaises(TclError, scroll.pack)
77        self.assertRaises(TclError, scroll.place)
78
79
80class ScrollableTextFrameTest(unittest.TestCase):
81
82    @classmethod
83    def setUpClass(cls):
84        cls.root = root = Tk()
85        root.withdraw()
86
87    @classmethod
88    def tearDownClass(cls):
89        cls.root.update_idletasks()
90        cls.root.destroy()
91        del cls.root
92
93    def make_frame(self, wrap=NONE, **kwargs):
94        frame = tv.ScrollableTextFrame(self.root, wrap=wrap, **kwargs)
95        def cleanup_frame():
96            frame.update_idletasks()
97            frame.destroy()
98        self.addCleanup(cleanup_frame)
99        return frame
100
101    def test_line1(self):
102        frame = self.make_frame()
103        frame.text.insert('1.0', 'test text')
104        self.assertEqual(frame.text.get('1.0', '1.end'), 'test text')
105
106    def test_horiz_scrollbar(self):
107        # The horizontal scrollbar should be shown/hidden according to
108        # the 'wrap' setting: It should only be shown when 'wrap' is
109        # set to NONE.
110
111        # wrap = NONE -> with horizontal scrolling
112        frame = self.make_frame(wrap=NONE)
113        self.assertEqual(frame.text.cget('wrap'), NONE)
114        self.assertIsNotNone(frame.xscroll)
115
116        # wrap != NONE -> no horizontal scrolling
117        for wrap in [CHAR, WORD]:
118            with self.subTest(wrap=wrap):
119                frame = self.make_frame(wrap=wrap)
120                self.assertEqual(frame.text.cget('wrap'), wrap)
121                self.assertIsNone(frame.xscroll)
122
123
124class ViewFrameTest(unittest.TestCase):
125
126    @classmethod
127    def setUpClass(cls):
128        cls.root = root = Tk()
129        root.withdraw()
130        cls.frame = tv.ViewFrame(root, 'test text')
131
132    @classmethod
133    def tearDownClass(cls):
134        del cls.frame
135        cls.root.update_idletasks()
136        cls.root.destroy()
137        del cls.root
138
139    def test_line1(self):
140        get = self.frame.text.get
141        self.assertEqual(get('1.0', '1.end'), 'test text')
142
143
144# Call ViewWindow with modal=False.
145class ViewFunctionTest(unittest.TestCase):
146
147    @classmethod
148    def setUpClass(cls):
149        cls.orig_error = tv.showerror
150        tv.showerror = Mbox_func()
151
152    @classmethod
153    def tearDownClass(cls):
154        tv.showerror = cls.orig_error
155        del cls.orig_error
156
157    def test_view_text(self):
158        view = tv.view_text(root, 'Title', 'test text', modal=False)
159        self.assertIsInstance(view, tv.ViewWindow)
160        self.assertIsInstance(view.viewframe, tv.ViewFrame)
161        view.viewframe.ok()
162
163    def test_view_file(self):
164        view = tv.view_file(root, 'Title', __file__, 'ascii', modal=False)
165        self.assertIsInstance(view, tv.ViewWindow)
166        self.assertIsInstance(view.viewframe, tv.ViewFrame)
167        get = view.viewframe.textframe.text.get
168        self.assertIn('Test', get('1.0', '1.end'))
169        view.ok()
170
171    def test_bad_file(self):
172        # Mock showerror will be used; view_file will return None.
173        view = tv.view_file(root, 'Title', 'abc.xyz', 'ascii', modal=False)
174        self.assertIsNone(view)
175        self.assertEqual(tv.showerror.title, 'File Load Error')
176
177    def test_bad_encoding(self):
178        p = os.path
179        fn = p.abspath(p.join(p.dirname(__file__), '..', 'CREDITS.txt'))
180        view = tv.view_file(root, 'Title', fn, 'ascii', modal=False)
181        self.assertIsNone(view)
182        self.assertEqual(tv.showerror.title, 'Unicode Decode Error')
183
184    def test_nowrap(self):
185        view = tv.view_text(root, 'Title', 'test', modal=False, wrap='none')
186        text_widget = view.viewframe.textframe.text
187        self.assertEqual(text_widget.cget('wrap'), 'none')
188
189
190# Call ViewWindow with _utest=True.
191class ButtonClickTest(unittest.TestCase):
192
193    def setUp(self):
194        self.view = None
195        self.called = False
196
197    def tearDown(self):
198        if self.view:
199            self.view.destroy()
200
201    def test_view_text_bind_with_button(self):
202        def _command():
203            self.called = True
204            self.view = tv.view_text(root, 'TITLE_TEXT', 'COMMAND', _utest=True)
205        button = Button(root, text='BUTTON', command=_command)
206        button.invoke()
207        self.addCleanup(button.destroy)
208
209        self.assertEqual(self.called, True)
210        self.assertEqual(self.view.title(), 'TITLE_TEXT')
211        self.assertEqual(self.view.viewframe.textframe.text.get('1.0', '1.end'),
212                         'COMMAND')
213
214    def test_view_file_bind_with_button(self):
215        def _command():
216            self.called = True
217            self.view = tv.view_file(root, 'TITLE_FILE', __file__,
218                                     encoding='ascii', _utest=True)
219        button = Button(root, text='BUTTON', command=_command)
220        button.invoke()
221        self.addCleanup(button.destroy)
222
223        self.assertEqual(self.called, True)
224        self.assertEqual(self.view.title(), 'TITLE_FILE')
225        get = self.view.viewframe.textframe.text.get
226        with open(__file__) as f:
227            self.assertEqual(get('1.0', '1.end'), f.readline().strip())
228            f.readline()
229            self.assertEqual(get('3.0', '3.end'), f.readline().strip())
230
231
232if __name__ == '__main__':
233    unittest.main(verbosity=2)
234