1# -*- coding: utf-8 -*- 2from __future__ import division 3from __future__ import absolute_import 4from __future__ import print_function 5from __future__ import unicode_literals 6from datetime import date, time 7from time import sleep 8from mock import patch 9from builtins import ord 10from builtins import chr 11from builtins import str 12import unittest 13import sys 14from mock.mock import MagicMock 15from asciimatics.event import KeyboardEvent, MouseEvent 16from asciimatics.exceptions import NextScene, StopApplication, InvalidFields 17from asciimatics.scene import Scene 18from asciimatics.screen import Screen, Canvas 19from asciimatics.widgets import Frame, Layout, Button, Label, TextBox, Text, \ 20 Divider, RadioButtons, CheckBox, PopUpDialog, ListBox, Widget, MultiColumnListBox, \ 21 FileBrowser, DatePicker, TimePicker, Background, DropdownList, PopupMenu, \ 22 _find_min_start, VerticalDivider 23from asciimatics.parsers import AsciimaticsParser, AnsiTerminalParser 24from asciimatics.strings import ColouredText 25 26 27class TestFrame(Frame): 28 def __init__(self, screen, has_border=True, reduce_cpu=False, label_height=1): 29 super(TestFrame, self).__init__(screen, 30 screen.height, 31 screen.width, 32 name="Test Form", 33 has_border=has_border, 34 hover_focus=True, 35 reduce_cpu=reduce_cpu) 36 layout = Layout([1, 18, 1]) 37 self.add_layout(layout) 38 self._reset_button = Button("Reset", self._reset) 39 self.label = Label("Group 1:", height=label_height) 40 layout.add_widget(self.label, 1) 41 layout.add_widget(TextBox(5, 42 label="My First Box:", 43 name="TA", 44 on_change=self._on_change), 1) 45 layout.add_widget( 46 Text(label="Text1:", name="TB", on_change=self._on_change), 1) 47 layout.add_widget( 48 Text(label="Text2:", 49 name="TC", 50 on_change=self._on_change, 51 validator="^[0-9]*$"), 1) 52 layout.add_widget( 53 Text(label="Text3:", 54 name="TD", 55 on_change=self._on_change, 56 validator=lambda x: x in ("", "a")), 1) 57 layout.add_widget(Divider(height=2), 1) 58 layout.add_widget(Label("Group 2:"), 1) 59 layout.add_widget(RadioButtons([("Option 1", 1), 60 ("Option 2", 2), 61 ("Option 3", 3)], 62 label="A Longer Selection:", 63 name="Things", 64 on_change=self._on_change), 1) 65 layout.add_widget(CheckBox("Field 1", 66 label="A very silly long name for fields:", 67 name="CA", 68 on_change=self._on_change), 1) 69 layout.add_widget( 70 CheckBox("Field 2", name="CB", on_change=self._on_change), 1) 71 layout.add_widget( 72 CheckBox("Field 3", name="CC", on_change=self._on_change), 1) 73 layout.add_widget(Divider(height=3), 1) 74 layout2 = Layout([1, 1, 1]) 75 self.add_layout(layout2) 76 layout2.add_widget(self._reset_button, 0) 77 layout2.add_widget(Button("View Data", self._view), 1) 78 layout2.add_widget(Button("Quit", self._quit), 2) 79 layout2.add_widget(Button("One", self._view), 0) 80 layout2.add_widget(Button("Two", self._view), 1) 81 layout2.add_widget(Button("Three", self._view), 2) 82 self.fix() 83 84 def _on_change(self): 85 changed = False 86 self.save() 87 for key, value in self.data.items(): 88 if isinstance(value, bool): 89 if value: 90 changed = True 91 break 92 if isinstance(value, int): 93 if value != 1: 94 changed = True 95 break 96 elif value is None: 97 continue 98 elif len(value) != 0: 99 changed = True 100 break 101 self._reset_button.disabled = not changed 102 103 def _reset(self): 104 self.reset() 105 raise NextScene() 106 107 def _view(self): 108 # Build result of this form and display it. 109 self.save() 110 raise NextScene() 111 112 @staticmethod 113 def _quit(): 114 raise StopApplication("User requested exit") 115 116 117class TestFrame2(Frame): 118 def __init__(self, screen, init_values): 119 super(TestFrame2, self).__init__(screen, 120 screen.height, 121 screen.width, 122 data={"selected": "None"}, 123 title="Test Frame 2") 124 # Create the form for displaying the list of contacts. 125 self._list_view = ListBox( 126 Widget.FILL_FRAME, 127 init_values, 128 name="contacts", 129 on_change=self._on_pick, 130 on_select=self._on_select) 131 self._edit_button = Button("Edit", self._edit) 132 self._delete_button = Button("Delete", self._delete) 133 layout = Layout([100], fill_frame=True) 134 self.add_layout(layout) 135 layout.add_widget(self._list_view) 136 layout.add_widget(Divider(line_char="#")) 137 layout2 = Layout([1, 1, 1, 1]) 138 self.add_layout(layout2) 139 layout2.add_widget(Button("Add", self._add), 0) 140 layout2.add_widget(self._edit_button, 1) 141 layout2.add_widget(self._delete_button, 2) 142 layout2.add_widget(Button("Quit", self._quit), 3) 143 layout3 = Layout([100]) 144 self.add_layout(layout3) 145 self._info_text = Text(label="Selected:", name="selected") 146 self._info_text.disabled = True 147 layout3.add_widget(self._info_text) 148 self.fix() 149 self._on_pick() 150 151 def _on_select(self): 152 self._info_text.value = str(self._list_view.value) 153 self.save() 154 155 def _on_pick(self): 156 self._edit_button.disabled = self._list_view.value is None 157 self._delete_button.disabled = self._list_view.value is None 158 159 @staticmethod 160 def _add(): 161 raise NextScene("Add") 162 163 @staticmethod 164 def _edit(): 165 raise NextScene("Edit") 166 167 @staticmethod 168 def _delete(): 169 raise NextScene("Delete") 170 171 @staticmethod 172 def _quit(): 173 raise StopApplication("User pressed quit") 174 175 176class TestFrame3(Frame): 177 def __init__(self, screen): 178 super(TestFrame3, self).__init__(screen, 10, 20, 179 name="Blank", 180 has_shadow=True) 181 self.fix() 182 183 184class TestFrame4(Frame): 185 def __init__(self, screen, file_filter=None): 186 super(TestFrame4, self).__init__( 187 screen, screen.height, screen.width, has_border=False, name="My Form") 188 189 # State tracking for callbacks 190 self.selected = None 191 self.highlighted = None 192 193 # Simple full-page Widget 194 layout = Layout([1], fill_frame=True) 195 self.add_layout(layout) 196 self.file_list = FileBrowser(Widget.FILL_FRAME, 197 "/", 198 name="file_list", 199 on_select=self.select, 200 on_change=self.change, 201 file_filter=file_filter) 202 layout.add_widget(self.file_list) 203 self.fix() 204 205 def select(self): 206 self.selected = self.file_list.value 207 208 def change(self): 209 self.highlighted = self.file_list.value 210 211 212class TestFrame5(Frame): 213 def __init__(self, screen): 214 super(TestFrame5, self).__init__( 215 screen, screen.height, screen.width, has_border=True, name="My Form") 216 217 # Simple full-page Widget 218 layout = Layout([1], fill_frame=True) 219 self.add_layout(layout) 220 self.date_widget = DatePicker( 221 label="Date:", name="date", year_range=range(1999, 2020), on_change=self._changed) 222 self.date_widget.value = date(2017, 1, 2) 223 layout.add_widget(self.date_widget) 224 self.time_widget = TimePicker( 225 label="Time:", name="time", seconds=True, on_change=self._changed) 226 self.time_widget.value = time(12, 0, 59) 227 layout.add_widget(self.time_widget) 228 self.fix() 229 230 # State tracking for widgets 231 self.changed = False 232 233 def _changed(self): 234 self.changed = True 235 236 237class TestFrame6(Frame): 238 def __init__(self, screen): 239 super(TestFrame6, self).__init__( 240 screen, screen.height, screen.width, has_border=True, name="My Form") 241 242 # Simple full-page Widget 243 layout = Layout([1], fill_frame=True) 244 self.add_layout(layout) 245 self.lbl = Label("TstFrm6Lbl", name="tf6_lbl") 246 layout.add_widget(self.lbl) 247 self.fix() 248 249 250class TestWidgets(unittest.TestCase): 251 def setUp(self): 252 self.maxDiff = None 253 254 def assert_canvas_equals(self, canvas, expected): 255 """ 256 Assert output to canvas is as expected. 257 """ 258 output = "" 259 for y in range(canvas.height): 260 for x in range(canvas.width): 261 char, _, _, _ = canvas.get_from(x, y) 262 output += chr(char) 263 output += "\n" 264 self.assertEqual(output, expected) 265 266 @staticmethod 267 def process_keys(form, values, separator=None): 268 """ 269 Inject a set of key events separated by a common key separator. 270 """ 271 for new_value in values: 272 if isinstance(new_value, int): 273 form.process_event(KeyboardEvent(new_value)) 274 else: 275 for char in new_value: 276 form.process_event(KeyboardEvent(ord(char))) 277 if separator: 278 form.process_event(KeyboardEvent(separator)) 279 280 @staticmethod 281 def process_mouse(form, values): 282 """ 283 Inject a set of mouse events. 284 """ 285 for x, y, buttons in values: 286 form.process_event(MouseEvent(x, y, buttons)) 287 288 def test_form_data(self): 289 """ 290 Check Frame.data works as expected. 291 """ 292 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 293 canvas = Canvas(screen, 10, 40, 0, 0) 294 form = TestFrame(canvas) 295 296 # Should be empty on construction 297 self.assertEqual(form.data, {}) 298 299 # Should be blank values after saving. 300 form.reset() 301 form.save() 302 self.assertEqual( 303 form.data, 304 { 305 'CA': False, 306 'CB': False, 307 'CC': False, 308 'TA': [''], 309 'TB': '', 310 'TC': '', 311 'TD': '', 312 'Things': 1 313 }) 314 315 def test_rendering(self): 316 """ 317 Check Frame renders as expected. 318 """ 319 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 320 canvas = Canvas(screen, 10, 40, 0, 0) 321 form = TestFrame(canvas) 322 form.reset() 323 324 # Check initial rendering 325 form.update(0) 326 self.assert_canvas_equals( 327 canvas, 328 "+--------------------------------------+\n" + 329 "| Group 1: |\n" + 330 "| My First O\n" + 331 "| Box: |\n" + 332 "| |\n" + 333 "| |\n" + 334 "| |\n" + 335 "| Text1: |\n" + 336 "| Text2: |\n" + 337 "+--------------------------------------+\n") 338 339 # Check scrolling works. Should also test label splitting and ellipsis. 340 form.move_to(0, 9, 8) 341 form.update(1) 342 self.assert_canvas_equals( 343 canvas, 344 "+--------------------------------------+\n" + 345 "| Text3: |\n" + 346 "| |\n" + 347 "| ---------------------------------- |\n" + 348 "| Group 2: O\n" + 349 "| A Longer (X) Option 1 |\n" + 350 "| Selection: ( ) Option 2 |\n" + 351 "| ( ) Option 3 |\n" + 352 "| A very... [ ] Field 1 |\n" + 353 "+--------------------------------------+\n") 354 355 # Now check button rendering. 356 form.move_to(0, 18, 8) 357 form.update(2) 358 self.assert_canvas_equals( 359 canvas, 360 "+--------------------------------------+\n" + 361 "| [ ] Field 3 |\n" + 362 "| |\n" + 363 "| ---------------------------------- |\n" + 364 "| |\n" + 365 "| < Reset > < View Data > < Quit > |\n" + 366 "| < One > < Two > < Three > |\n" + 367 "| O\n" + 368 "| |\n" + 369 "+--------------------------------------+\n") 370 371 def test_unicode_rendering(self): 372 """ 373 Check Frame renders as expected for unicode environments. 374 """ 375 screen = MagicMock(spec=Screen, colours=8, unicode_aware=True) 376 canvas = Canvas(screen, 10, 40, 0, 0) 377 form = TestFrame(canvas) 378 form.reset() 379 380 # Check initial rendering 381 form.update(0) 382 self.assert_canvas_equals( 383 canvas, 384 "┌──────────────────────────────────────┐\n" + 385 "│ Group 1: │\n" + 386 "│ My First █\n" + 387 "│ Box: ░\n" + 388 "│ ░\n" + 389 "│ ░\n" + 390 "│ ░\n" + 391 "│ Text1: ░\n" + 392 "│ Text2: │\n" + 393 "└──────────────────────────────────────┘\n") 394 395 # Check scrolling works. Should also test label splitting and ellipsis. 396 form.move_to(0, 9, 8) 397 form.update(1) 398 self.assert_canvas_equals( 399 canvas, 400 "┌──────────────────────────────────────┐\n" + 401 "│ Text3: │\n" + 402 "│ ░\n" + 403 "│ ────────────────────────────────── ░\n" + 404 "│ Group 2: █\n" + 405 "│ A Longer (•) Option 1 ░\n" + 406 "│ Selection: ( ) Option 2 ░\n" + 407 "│ ( ) Option 3 ░\n" + 408 "│ A very... [ ] Field 1 │\n" + 409 "└──────────────────────────────────────┘\n") 410 411 # Now check button rendering. 412 form.move_to(0, 18, 8) 413 form.update(2) 414 self.assert_canvas_equals( 415 canvas, 416 "┌──────────────────────────────────────┐\n" + 417 "│ [ ] Field 3 │\n" + 418 "│ ░\n" + 419 "│ ────────────────────────────────── ░\n" + 420 "│ ░\n" + 421 "│ < Reset > < View Data > < Quit > ░\n" + 422 "│ < One > < Two > < Three > ░\n" + 423 "│ █\n" + 424 "│ │\n" + 425 "└──────────────────────────────────────┘\n") 426 427 def test_no_border(self): 428 """ 429 Check that a Frame with no border works 430 """ 431 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 432 canvas = Canvas(screen, 10, 40, 0, 0) 433 form = TestFrame(canvas, has_border=False) 434 form.reset() 435 436 # Check initial rendering 437 form.update(0) 438 self.assert_canvas_equals( 439 canvas, 440 " Group 1: \n" + 441 " My First \n" + 442 " Box: \n" + 443 " \n" + 444 " \n" + 445 " \n" + 446 " Text1: \n" + 447 " Text2: \n" + 448 " Text3: \n" + 449 " \n") 450 451 # Check scrolling works. Should also test label splitting and ellipsis. 452 form.move_to(0, 10, 10) 453 form.update(1) 454 self.assert_canvas_equals( 455 canvas, 456 " ------------------------------------ \n" + 457 " Group 2: \n" + 458 " A Longer (X) Option 1 \n" + 459 " Selection: ( ) Option 2 \n" + 460 " ( ) Option 3 \n" + 461 " A very si...[ ] Field 1 \n" + 462 " [ ] Field 2 \n" + 463 " [ ] Field 3 \n" + 464 " \n" + 465 " ------------------------------------ \n") 466 467 def test_form_input(self): 468 """ 469 Check Frame input works as expected. 470 """ 471 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 472 canvas = Canvas(screen, 10, 40, 0, 0) 473 form = TestFrame(canvas) 474 form.reset() 475 self.process_keys(form, 476 ["ABC\nDEF", "GHI", "jkl", "MN", " ", " ", "", " "], 477 Screen.KEY_TAB) 478 form.save() 479 self.assertEqual( 480 form.data, 481 { 482 'CA': True, 483 'CB': False, 484 'CC': True, 485 'TA': ['ABC', 'DEF'], 486 'TB': 'GHI', 487 'TC': 'jkl', 488 'TD': 'MN', 489 'Things': 1 490 }) 491 492 # Check that forms ignore unrecognised events. 493 event = object() 494 self.assertEqual(event, form.process_event(event)) 495 496 # Check forms don't want global input handling. 497 self.assertFalse(form.safe_to_default_unhandled_input) 498 499 def test_textbox_input(self): 500 """ 501 Check TextBox input works as expected. 502 """ 503 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 504 canvas = Canvas(screen, 10, 40, 0, 0) 505 form = TestFrame(canvas) 506 form.reset() 507 508 # Check basic movement keys 509 self.process_keys(form, ["ABC", Screen.KEY_LEFT, "D"]) 510 form.save() 511 self.assertEqual(form.data["TA"], ["ABDC"]) 512 self.process_keys(form, [Screen.KEY_RIGHT, Screen.KEY_RIGHT, "E"]) 513 form.save() 514 self.assertEqual(form.data["TA"], ["ABDCE"]) 515 self.process_keys(form, ["\nFGH", Screen.KEY_UP, Screen.KEY_UP, "I"]) 516 form.save() 517 self.assertEqual(form.data["TA"], ["ABDICE", "FGH"]) 518 self.process_keys(form, [Screen.KEY_DOWN, Screen.KEY_DOWN, "J"]) 519 form.save() 520 self.assertEqual(form.data["TA"], ["ABDICE", "FGHJ"]) 521 self.process_keys(form, [Screen.KEY_HOME, "K"]) 522 form.save() 523 self.assertEqual(form.data["TA"], ["ABDICE", "KFGHJ"]) 524 self.process_keys(form, [Screen.KEY_END, "L"]) 525 form.save() 526 self.assertEqual(form.data["TA"], ["ABDICE", "KFGHJL"]) 527 528 # Backspace - normal and wrapping lines 529 self.process_keys(form, [Screen.KEY_BACK]) 530 form.save() 531 self.assertEqual(form.data["TA"], ["ABDICE", "KFGHJ"]) 532 self.process_keys(form, [Screen.KEY_HOME, Screen.KEY_BACK]) 533 form.save() 534 self.assertEqual(form.data["TA"], ["ABDICEKFGHJ"]) 535 536 # Check cursor line-wrapping 537 self.process_keys(form, ["\n"]) 538 form.save() 539 self.assertEqual(form.data["TA"], ["ABDICE", "KFGHJ"]) 540 self.process_keys(form, [Screen.KEY_LEFT, "M"]) 541 form.save() 542 self.assertEqual(form.data["TA"], ["ABDICEM", "KFGHJ"]) 543 self.process_keys(form, [Screen.KEY_RIGHT, "N"]) 544 form.save() 545 self.assertEqual(form.data["TA"], ["ABDICEM", "NKFGHJ"]) 546 547 # Delete - normal and wrapping lines and at end of all data. 548 self.process_keys(form, [Screen.KEY_DELETE]) 549 form.save() 550 self.assertEqual(form.data["TA"], ["ABDICEM", "NFGHJ"]) 551 self.process_keys(form, 552 [Screen.KEY_UP, Screen.KEY_END, Screen.KEY_DELETE]) 553 form.save() 554 self.assertEqual(form.data["TA"], ["ABDICEMNFGHJ"]) 555 self.process_keys(form, [Screen.KEY_END, Screen.KEY_DELETE]) 556 form.save() 557 self.assertEqual(form.data["TA"], ["ABDICEMNFGHJ"]) 558 559 # Check that page up/down work as expected. 560 self.process_keys(form, [Screen.ctrl("m"), Screen.ctrl("m"), Screen.KEY_PAGE_UP, "O"]) 561 form.save() 562 self.assertEqual(form.data["TA"], ["OABDICEMNFGHJ", "", ""]) 563 self.process_keys(form, [Screen.KEY_PAGE_DOWN, "P"]) 564 form.save() 565 self.assertEqual(form.data["TA"], ["OABDICEMNFGHJ", "", "P"]) 566 567 # Check that the current focus ignores unknown events. 568 event = object() 569 self.assertEqual(event, form.process_event(event)) 570 571 def test_text_input(self): 572 """ 573 Check Text input works as expected. 574 """ 575 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 576 canvas = Canvas(screen, 10, 40, 0, 0) 577 form = TestFrame(canvas) 578 form.reset() 579 580 # Check basic movement keys 581 self.process_keys(form, [Screen.KEY_TAB, "ABC"]) 582 form.save() 583 self.assertEqual(form.data["TB"], "ABC") 584 self.process_keys(form, [Screen.KEY_HOME, "D"]) 585 form.save() 586 self.assertEqual(form.data["TB"], "DABC") 587 self.process_keys(form, [Screen.KEY_END, "E"]) 588 form.save() 589 self.assertEqual(form.data["TB"], "DABCE") 590 self.process_keys(form, [Screen.KEY_LEFT, "F"]) 591 form.save() 592 self.assertEqual(form.data["TB"], "DABCFE") 593 self.process_keys(form, [Screen.KEY_RIGHT, "G"]) 594 form.save() 595 self.assertEqual(form.data["TB"], "DABCFEG") 596 597 # Backspace 598 self.process_keys(form, [Screen.KEY_BACK]) 599 form.save() 600 self.assertEqual(form.data["TB"], "DABCFE") 601 602 # Delete - including at end of data 603 self.process_keys(form, [Screen.KEY_DELETE]) 604 form.save() 605 self.assertEqual(form.data["TB"], "DABCFE") 606 self.process_keys(form, [Screen.KEY_HOME, Screen.KEY_DELETE]) 607 form.save() 608 self.assertEqual(form.data["TB"], "ABCFE") 609 610 # Check that the current focus ignores unknown events. 611 event = object() 612 self.assertEqual(event, form.process_event(event)) 613 614 def test_validation(self): 615 """ 616 Check free-form text validation works as expected. 617 """ 618 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 619 canvas = Canvas(screen, 10, 40, 0, 0) 620 form = TestFrame(canvas) 621 form.reset() 622 623 # Check that save still works with no validation. 624 self.process_keys(form, [Screen.KEY_TAB, Screen.KEY_TAB, "ABC"]) 625 form.save() 626 self.assertEqual(form.data["TC"], "ABC") 627 628 # Check that enforced validation throws exceptions as needed. 629 with self.assertRaises(InvalidFields) as cm: 630 form.save(validate=True) 631 self.assertEqual(cm.exception.fields, ["TC"]) 632 633 # Check valid data doesn't throw anything. 634 self.process_keys(form, 635 [Screen.KEY_BACK, Screen.KEY_BACK, Screen.KEY_BACK]) 636 form.save(validate=True) 637 638 # Check functions work as well as regexp strings. 639 self.process_keys(form, [Screen.KEY_TAB, "ABC"]) 640 with self.assertRaises(InvalidFields) as cm: 641 form.save(validate=True) 642 self.assertEqual(cm.exception.fields, ["TD"]) 643 644 def test_checkbox_input(self): 645 """ 646 Check Checkbox input works as expected. 647 """ 648 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 649 canvas = Canvas(screen, 10, 40, 0, 0) 650 form = TestFrame(canvas) 651 form.reset() 652 653 # Check basic selection keys 654 self.process_keys( 655 form, 656 [Screen.KEY_TAB, Screen.KEY_TAB, Screen.KEY_TAB, Screen.KEY_TAB, 657 Screen.KEY_TAB, " "]) 658 form.save() 659 self.assertEqual(form.data["CA"], True) 660 self.process_keys(form, ["\n"]) 661 form.save() 662 self.assertEqual(form.data["CA"], False) 663 self.process_keys(form, ["\r"]) 664 form.save() 665 self.assertEqual(form.data["CA"], True) 666 667 # Check that the current focus ignores unknown events. 668 event = object() 669 self.assertEqual(event, form.process_event(event)) 670 671 def test_radiobutton_input(self): 672 """ 673 Check RadioButton input works as expected. 674 """ 675 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 676 canvas = Canvas(screen, 10, 40, 0, 0) 677 form = TestFrame(canvas) 678 form.reset() 679 680 # Check basic selection keys - including limit checking 681 self.process_keys( 682 form, 683 [Screen.KEY_TAB, Screen.KEY_TAB, Screen.KEY_TAB, Screen.KEY_TAB]) 684 form.save() 685 self.assertEqual(form.data["Things"], 1) 686 self.process_keys(form, [Screen.KEY_UP]) 687 form.save() 688 self.assertEqual(form.data["Things"], 1) 689 self.process_keys(form, [Screen.KEY_DOWN]) 690 form.save() 691 self.assertEqual(form.data["Things"], 2) 692 self.process_keys(form, [Screen.KEY_DOWN]) 693 form.save() 694 self.assertEqual(form.data["Things"], 3) 695 self.process_keys(form, [Screen.KEY_DOWN]) 696 form.save() 697 self.assertEqual(form.data["Things"], 3) 698 self.process_keys(form, [Screen.KEY_UP]) 699 form.save() 700 self.assertEqual(form.data["Things"], 2) 701 702 # Check that a radio button ignores unknown events. 703 event = object() 704 self.assertEqual(event, form.process_event(event)) 705 706 # Check that setting an unknown value resets to first radio button. 707 form.focussed_widget.value = "Nonexistent value" 708 form.save() 709 self.assertEqual(form.data["Things"], 1) 710 711 def test_mouse_input(self): 712 """ 713 Check mouse input works as expected. 714 """ 715 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 716 scene = MagicMock(spec=Scene) 717 canvas = Canvas(screen, 10, 40, 0, 0) 718 form = TestFrame(canvas) 719 form.register_scene(scene) 720 form.reset() 721 722 # Check focus moves when clicked on a text or textbox 723 self.process_mouse(form, [(29, 7, MouseEvent.LEFT_CLICK)]) 724 self.assertEqual(form._layouts[form._focus]._live_widget, 2) 725 self.process_mouse(form, [(29, 2, MouseEvent.LEFT_CLICK)]) 726 self.assertEqual(form._layouts[form._focus]._live_widget, 1) 727 728 # Check focus doesn't change on scroll or click outside of widgets 729 self.process_mouse(form, [(0, 0, 0)]) 730 self.assertEqual(form._focus, 0) 731 self.assertEqual(form._layouts[form._focus]._live_col, 1) 732 self.assertEqual(form._layouts[form._focus]._live_widget, 1) 733 self.process_mouse(form, [(39, 7, MouseEvent.LEFT_CLICK)]) 734 self.assertEqual(form._layouts[form._focus]._live_widget, 1) 735 736 # Check focus moves when clicked on a checkbox or radiobutton 737 self.process_mouse(form, [(29, 1, MouseEvent.LEFT_CLICK)]) 738 self.assertEqual(form._layouts[form._focus]._live_widget, 8) 739 # Note that the above changes the Frame start-line. 740 self.process_mouse(form, [(29, 5, MouseEvent.LEFT_CLICK)]) 741 self.assertEqual(form._layouts[form._focus]._live_widget, 11) 742 743 # Check focus moves when hovering over a widget 744 self.process_mouse(form, [(39, 7, MouseEvent.LEFT_CLICK), (3, 8, 0)]) 745 self.assertEqual(form._focus, 1) 746 self.assertEqual(form._layouts[form._focus]._live_col, 0) 747 self.assertEqual(form._layouts[form._focus]._live_widget, 1) 748 749 # Check button click triggers an event. 750 with self.assertRaises(StopApplication): 751 self.process_mouse(form, [(30, 7, MouseEvent.LEFT_CLICK)]) 752 753 # Check that the current focus ignores unknown events. 754 event = object() 755 self.assertEqual(event, form.process_event(event)) 756 757 758 def test_frame_focus_widget_property(self): 759 """ 760 Check the frame exposes the focussed widget 761 """ 762 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 763 canvas = Canvas(screen, 10, 40, 0, 0) 764 form = TestFrame(canvas) 765 form.reset() 766 767 # If the Frame loses the focus it must not return a focussed widget. 768 form._has_focus = False 769 self.assertIsNone(form.focussed_widget) 770 771 # If the Frame focus is undefined, it must not return a focussed widget. 772 form._has_focus = True 773 form._focus = 9999 774 self.assertIsNone(form.focussed_widget) 775 776 def test_frame_focus_widget_property_when_frame_focussed(self): 777 """ 778 check the frame exposes nothing when frame is foccused 779 """ 780 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 781 canvas = Canvas(screen, 10, 40, 0, 0) 782 form = TestFrame(canvas) 783 form.reset() 784 785 # A Frame with a valid focus should return the widget in focus. 786 layout = form._layouts[form._focus] 787 layout._has_focus = True 788 form._has_focus = True 789 self.assertEqual(layout._columns[layout._live_col][layout._live_widget], 790 form.focussed_widget) 791 792 def test_widget_navigation(self): 793 """ 794 Check widget tab stops work as expected. 795 """ 796 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 797 canvas = Canvas(screen, 10, 40, 0, 0) 798 form = TestFrame(canvas) 799 form.reset() 800 801 # Check default focus at start is first visible widget 802 self.assertEqual(form._focus, 0) 803 self.assertEqual(form._layouts[form._focus]._live_col, 1) 804 self.assertEqual(form._layouts[form._focus]._live_widget, 1) 805 806 # Check BACK_TAB reverses TAB. 807 self.process_keys(form, [Screen.KEY_TAB]) 808 self.assertEqual(form._layouts[form._focus]._live_widget, 2) 809 self.process_keys(form, [Screen.KEY_BACK_TAB]) 810 self.assertEqual(form._layouts[form._focus]._live_widget, 1) 811 812 # Check BACK_TAB/TAB wraps around the form. 813 self.process_keys(form, [Screen.KEY_BACK_TAB]) 814 self.assertEqual(form._focus, 1) 815 self.process_keys(form, [Screen.KEY_TAB]) 816 self.assertEqual(form._focus, 0) 817 818 # Tab out into text fields and check UP/DOWN keys. 819 self.process_keys(form, [Screen.KEY_TAB, Screen.KEY_DOWN]) 820 self.assertEqual(form._layouts[form._focus]._live_widget, 3) 821 self.process_keys(form, [Screen.KEY_UP]) 822 self.assertEqual(form._layouts[form._focus]._live_widget, 2) 823 824 # Tab out into buttons and check LEFT/RIGHT keys. 825 self.process_keys(form, [Screen.KEY_TAB, Screen.KEY_TAB, Screen.KEY_TAB, 826 Screen.KEY_TAB, Screen.KEY_TAB, Screen.KEY_TAB, 827 Screen.KEY_TAB, Screen.KEY_TAB]) 828 self.assertEqual(form._focus, 1) 829 self.assertEqual(form._layouts[form._focus]._live_col, 1) 830 self.assertEqual(form._layouts[form._focus]._live_widget, 0) 831 832 self.process_keys(form, [Screen.KEY_RIGHT]) 833 self.assertEqual(form._layouts[form._focus]._live_col, 2) 834 self.process_keys(form, [Screen.KEY_LEFT]) 835 self.assertEqual(form._layouts[form._focus]._live_col, 1) 836 self.process_keys(form, [Screen.KEY_LEFT]) 837 # Reset will be disabled, but move down to "One" 838 self.assertEqual(form._layouts[form._focus]._live_col, 0) 839 self.assertEqual(form._layouts[form._focus]._live_widget, 1) 840 self.process_keys(form, [Screen.KEY_RIGHT]) 841 self.assertEqual(form._layouts[form._focus]._live_col, 1) 842 843 # Check up and down stay in column. 844 self.process_keys(form, [Screen.KEY_UP]) 845 self.assertEqual(form._layouts[form._focus]._live_col, 1) 846 self.assertEqual(form._layouts[form._focus]._live_widget, 0) 847 self.process_keys(form, [Screen.KEY_DOWN]) 848 self.assertEqual(form._layouts[form._focus]._live_col, 1) 849 self.assertEqual(form._layouts[form._focus]._live_widget, 1) 850 851 # Check up and down find nearest widget across Layouts 852 # - Up to checkbox 853 self.process_keys(form, [Screen.KEY_UP, Screen.KEY_UP]) 854 self.assertEqual(form._focus, 0) 855 self.assertEqual(form._layouts[form._focus]._live_col, 1) 856 self.assertEqual(form._layouts[form._focus]._live_widget, 10) 857 # - Down to first button row 858 self.process_keys(form, [Screen.KEY_DOWN]) 859 self.assertEqual(form._focus, 1) 860 self.assertEqual(form._layouts[form._focus]._live_col, 1) 861 self.assertEqual(form._layouts[form._focus]._live_widget, 0) 862 # - Down to wrap to top of form 863 self.process_keys(form, [Screen.KEY_DOWN, Screen.KEY_DOWN]) 864 self.assertEqual(form._focus, 0) 865 self.assertEqual(form._layouts[form._focus]._live_col, 1) 866 self.assertEqual(form._layouts[form._focus]._live_widget, 1) 867 868 def test_list_box(self): 869 """ 870 Check ListBox widget works as expected. 871 """ 872 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 873 scene = MagicMock(spec=Scene) 874 canvas = Canvas(screen, 10, 40, 0, 0) 875 form = TestFrame2( 876 canvas, [("One", 1), ("Two is now quite a bit longer than before", 2)]) 877 form.register_scene(scene) 878 form.reset() 879 880 # Check we have a default value for our list. 881 form.save() 882 self.assertEqual(form.data, {"selected": "None", "contacts": 1}) 883 884 # Check that UP/DOWN change selection. 885 self.process_keys(form, [Screen.KEY_DOWN]) 886 form.save() 887 self.assertEqual(form.data, {"selected": "None", "contacts": 2}) 888 self.process_keys(form, [Screen.KEY_UP]) 889 form.save() 890 self.assertEqual(form.data, {"selected": "None", "contacts": 1}) 891 892 # Check that the listbox is rendered correctly. 893 form.update(0) 894 self.assert_canvas_equals( 895 canvas, 896 "+------------ Test Frame 2 ------------+\n" + 897 "|One |\n" + 898 "|Two is now quite a bit longer than ...O\n" + 899 "| |\n" + 900 "| |\n" + 901 "| |\n" + 902 "|######################################|\n" + 903 "| < Add > < Edit > < Delete < Quit > |\n" + 904 "|Selected: None |\n" + 905 "+--------------------------------------+\n") 906 907 # Check that mouse input changes selection. 908 self.process_mouse(form, [(2, 2, MouseEvent.LEFT_CLICK)]) 909 form.save() 910 self.assertEqual(form.data, {"selected": "None", "contacts": 2}) 911 self.process_mouse(form, [(2, 1, MouseEvent.LEFT_CLICK)]) 912 form.save() 913 self.assertEqual(form.data, {"selected": "None", "contacts": 1}) 914 915 # Check that enter key handles correctly. 916 self.process_keys(form, [Screen.ctrl("m")]) 917 form.save() 918 self.assertEqual(form.data, {"selected": "1", "contacts": 1}) 919 920 form.update(0) 921 self.assert_canvas_equals( 922 canvas, 923 "+------------ Test Frame 2 ------------+\n" + 924 "|One |\n" + 925 "|Two is now quite a bit longer than ...O\n" + 926 "| |\n" + 927 "| |\n" + 928 "| |\n" + 929 "|######################################|\n" + 930 "| < Add > < Edit > < Delete < Quit > |\n" + 931 "|Selected: 1 |\n" + 932 "+--------------------------------------+\n") 933 934 # Check that mouse double click handles correctly. 935 self.process_mouse(form, [(2, 2, MouseEvent.DOUBLE_CLICK)]) 936 form.save() 937 self.assertEqual(form.data, {"selected": "2", "contacts": 2}) 938 939 form.update(0) 940 self.assert_canvas_equals( 941 canvas, 942 "+------------ Test Frame 2 ------------+\n" + 943 "|One |\n" + 944 "|Two is now quite a bit longer than ...O\n" + 945 "| |\n" + 946 "| |\n" + 947 "| |\n" + 948 "|######################################|\n" + 949 "| < Add > < Edit > < Delete < Quit > |\n" + 950 "|Selected: 2 |\n" + 951 "+--------------------------------------+\n") 952 953 # Check that the current focus ignores unknown events. 954 event = object() 955 self.assertEqual(event, form.process_event(event)) 956 957 def test_title(self): 958 """ 959 Check Frame titles work as expected. 960 """ 961 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 962 scene = MagicMock(spec=Scene) 963 canvas = Canvas(screen, 10, 40, 0, 0) 964 form = TestFrame2(canvas, [("One", 1), ("Two", 2)]) 965 form.register_scene(scene) 966 form.reset() 967 968 # Check that the title is rendered correctly. 969 form.update(0) 970 self.assert_canvas_equals( 971 canvas, 972 "+------------ Test Frame 2 ------------+\n" + 973 "|One |\n" + 974 "|Two O\n" + 975 "| |\n" + 976 "| |\n" + 977 "| |\n" + 978 "|######################################|\n" + 979 "| < Add > < Edit > < Delete < Quit > |\n" + 980 "|Selected: None |\n" + 981 "+--------------------------------------+\n") 982 983 # Check that a new title is rendered correctly. 984 form.title = "A New Title!" 985 form.update(1) 986 self.assert_canvas_equals( 987 canvas, 988 "+------------ A New Title! ------------+\n" + 989 "|One |\n" + 990 "|Two O\n" + 991 "| |\n" + 992 "| |\n" + 993 "| |\n" + 994 "|######################################|\n" + 995 "| < Add > < Edit > < Delete < Quit > |\n" + 996 "|Selected: None |\n" + 997 "+--------------------------------------+\n") 998 999 # Note that the actual stored title includes spaces for margins 1000 self.assertEqual(form.title, " A New Title! ") 1001 1002 def test_focus_callback(self): 1003 """ 1004 Check that the _on_focus & _on_blur callbacks work as expected. 1005 """ 1006 def _on_focus(): 1007 self._did_focus = True 1008 1009 def _on_blur(): 1010 self._did_blur = True 1011 1012 # Create a dummy screen 1013 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 1014 scene = MagicMock(spec=Scene) 1015 canvas = Canvas(screen, 2, 40, 0, 0) 1016 1017 # Create the form we want to test. 1018 form = Frame(canvas, canvas.height, canvas.width, has_border=False) 1019 layout = Layout([100], fill_frame=True) 1020 form.add_layout(layout) 1021 layout.add_widget(Text("Test")) 1022 layout.add_widget(Text("Test2", on_blur=_on_blur, on_focus=_on_focus)) 1023 form.fix() 1024 form.register_scene(scene) 1025 form.reset() 1026 1027 # Reset state for test 1028 self._did_blur = False 1029 self._did_focus = False 1030 1031 # Tab round to move the focus - check it has called the right function. 1032 self.process_keys(form, [Screen.KEY_TAB]) 1033 self.assertEqual(self._did_blur, False) 1034 self.assertEqual(self._did_focus, True) 1035 1036 # Reset the state and Now move the focus away with the mouse. 1037 self._did_focus = False 1038 self.process_mouse(form, [(0, 0, MouseEvent.LEFT_CLICK)]) 1039 self.assertEqual(self._did_blur, True) 1040 self.assertEqual(self._did_focus, False) 1041 1042 def test_load_callback(self): 1043 """ 1044 Check that the _on_load callback works as expected. 1045 """ 1046 def _on_load(): 1047 self._did_load = True 1048 1049 # Reset state for test 1050 self._did_load = False 1051 1052 # Create a dummy screen 1053 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 1054 canvas = Canvas(screen, 2, 40, 0, 0) 1055 1056 # Create the form we want to test. 1057 form = Frame(canvas, canvas.height, canvas.width, on_load=_on_load) 1058 form.fix() 1059 scene = Scene([form], -1) 1060 1061 # Check only called on reset. 1062 self.assertEqual(self._did_load, False) 1063 scene.reset() 1064 self.assertEqual(self._did_load, True) 1065 1066 def test_multi_column_list_box(self): 1067 """ 1068 Check MultiColumnListBox works as expected. 1069 """ 1070 # Create a dummy screen. 1071 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 1072 scene = MagicMock(spec=Scene) 1073 canvas = Canvas(screen, 10, 40, 0, 0) 1074 1075 # Create the form we want to test. 1076 form = Frame(canvas, canvas.height, canvas.width, has_border=False) 1077 layout = Layout([100], fill_frame=True) 1078 mc_list = MultiColumnListBox( 1079 Widget.FILL_FRAME, 1080 [3, "4", ">4", "<4", "^10%", "100%"], 1081 [ 1082 (["1", "2", "3", "4", "5", "6"], 1), 1083 (["11", "222", "333", "444", "555", "6"], 2), 1084 (["111", "2", "3", "4", "5", "6"], 3), 1085 (["1", "2", "33333", "4", "5", "6"], 4), 1086 (["1", "2", "3", "4", "5", "6666666666666666666666"], 5), 1087 ], 1088 titles=["A", "B", "C", "D", "E", "F"], 1089 name="mc_list") 1090 form.add_layout(layout) 1091 layout.add_widget(mc_list) 1092 form.fix() 1093 form.register_scene(scene) 1094 form.reset() 1095 1096 # Check we have a default value for our list. 1097 form.save() 1098 self.assertEqual(form.data, {"mc_list": 1}) 1099 1100 # Check that UP/DOWN change selection. 1101 self.process_keys(form, [Screen.KEY_DOWN]) 1102 form.save() 1103 self.assertEqual(form.data, {"mc_list": 2}) 1104 self.process_keys(form, [Screen.KEY_UP]) 1105 form.save() 1106 self.assertEqual(form.data, {"mc_list": 1}) 1107 1108 # Check that PGUP/PGDN change selection. 1109 self.process_keys(form, [Screen.KEY_PAGE_DOWN]) 1110 form.save() 1111 self.assertEqual(form.data, {"mc_list": 5}) 1112 self.process_keys(form, [Screen.KEY_PAGE_UP]) 1113 form.save() 1114 self.assertEqual(form.data, {"mc_list": 1}) 1115 1116 # Check that the widget is rendered correctly. 1117 form.update(0) 1118 self.assert_canvas_equals( 1119 canvas, 1120 "A B C D E F \n" + 1121 "1 2 3 4 5 6 \n" + 1122 "11 222 333 444 555 6 \n" + 1123 "1112 3 4 5 6 \n" + 1124 "1 2 3... 4 5 6 \n" + 1125 "1 2 3 4 5 66666666666666666666\n" + 1126 " \n" + 1127 " \n" + 1128 " \n" + 1129 " \n") 1130 1131 # Check that mouse input changes selection. 1132 self.process_mouse(form, [(2, 2, MouseEvent.LEFT_CLICK)]) 1133 form.save() 1134 self.assertEqual(form.data, {"mc_list": 2}) 1135 self.process_mouse(form, [(2, 1, MouseEvent.LEFT_CLICK)]) 1136 form.save() 1137 self.assertEqual(form.data, {"mc_list": 1}) 1138 1139 # Check that the start_line can be read and set - and enforces good behaviour 1140 mc_list.start_line = 0 1141 self.assertEqual(mc_list.start_line, 0) 1142 mc_list.start_line = len(mc_list.options) - 1 1143 self.assertEqual(mc_list.start_line, len(mc_list.options) - 1) 1144 mc_list.start_line = 10000000 1145 self.assertEqual(mc_list.start_line, len(mc_list.options) - 1) 1146 1147 # Check that options can be read and set. 1148 mc_list.options = [(["a", "b", "c", "d", "e", "f"], 0)] 1149 self.assertEqual(mc_list.options, [(["a", "b", "c", "d", "e", "f"], 0)]) 1150 mc_list.options = [] 1151 self.assertEqual(mc_list.options, []) 1152 1153 # Check that the form re-renders correctly afterwards. 1154 form.update(1) 1155 self.assert_canvas_equals( 1156 canvas, 1157 "A B C D E F \n" + 1158 " \n" + 1159 " \n" + 1160 " \n" + 1161 " \n" + 1162 " \n" + 1163 " \n" + 1164 " \n" + 1165 " \n" + 1166 " \n") 1167 1168 # Check that the current focus ignores unknown events. 1169 event = object() 1170 self.assertEqual(event, form.process_event(event)) 1171 1172 # Check that options retain the current value where possible. 1173 mc_list.options = [ 1174 (["a", "b", "c", "d", "e", "f"], 0), 1175 (["b", "b", "c", "d", "e", "f"], 1), 1176 (["c", "b", "c", "d", "e", "f"], 2), 1177 ] 1178 mc_list.value = 1 1179 mc_list.options = [ 1180 (["a", "b", "c", "d", "e", "f"], 0), 1181 (["b", "b", "c", "d", "e", "f"], 1), 1182 (["c", "b", "c", "d", "e", "f"], 2), 1183 (["d", "b", "c", "d", "e", "f"], 3), 1184 ] 1185 self.assertEqual(mc_list.value, 1) 1186 mc_list.options = [ 1187 (["a", "b", "c", "d", "e", "f"], 0), 1188 (["c", "b", "c", "d", "e", "f"], 2), 1189 (["d", "b", "c", "d", "e", "f"], 3), 1190 ] 1191 self.assertEqual(mc_list.value, 0) 1192 1193 1194 def test_multi_column_list_box_delimiter(self): 1195 """ 1196 Check MultiColumnListBox works as expected with space_delimiter 1197 """ 1198 # Create a dummy screen. 1199 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 1200 scene = MagicMock(spec=Scene) 1201 canvas = Canvas(screen, 10, 40, 0, 0) 1202 1203 # Create the form we want to test. 1204 form = Frame(canvas, canvas.height, canvas.width, has_border=False) 1205 layout = Layout([100], fill_frame=True) 1206 mc_list = MultiColumnListBox( 1207 Widget.FILL_FRAME, 1208 [3, "4", ">4", "<4", "^10%", "100%"], 1209 [ 1210 (["1", "2", "3", "4", "5", "6"], 1), 1211 (["11", "222", "333", "444", "555", "6"], 2), 1212 (["111", "2", "3", "4", "5", "6"], 3), 1213 (["1", "2", "33333", "4", "5", "6"], 4), 1214 (["1", "2", "3", "4", "5", "6666666666666666666666"], 5), 1215 ], 1216 titles=["A", "B", "C", "D", "E", "F"], 1217 name="mc_list", 1218 space_delimiter='|') 1219 form.add_layout(layout) 1220 layout.add_widget(mc_list) 1221 form.fix() 1222 form.register_scene(scene) 1223 form.reset() 1224 1225 # Check that the widget is rendered correctly. 1226 form.update(0) 1227 self.assert_canvas_equals( 1228 canvas, 1229 "A |B | C|D | E |F \n" + 1230 "1 |2 | 3|4 | 5 |6 \n" + 1231 "11 |222 | 333|444 |555 |6 \n" + 1232 "111|2 | 3|4 | 5 |6 \n" + 1233 "1 |2 |3...|4 | 5 |6 \n" + 1234 "1 |2 | 3|4 | 5 |6666666666666666\n" + 1235 " \n" + 1236 " \n" + 1237 " \n" + 1238 " \n") 1239 1240 def test_list_box_scrollbar(self): 1241 """ 1242 Check ListBox scrollbar works. 1243 """ 1244 # Create a dummy screen. 1245 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 1246 scene = MagicMock(spec=Scene) 1247 canvas = Canvas(screen, 10, 40, 0, 0) 1248 1249 # Create the form we want to test. 1250 form = Frame(canvas, canvas.height, canvas.width, has_border=False) 1251 layout = Layout([100], fill_frame=True) 1252 simple_list = ListBox( 1253 3, 1254 [ 1255 ("Some", 1), 1256 ("Stuff", 2), 1257 ("To", 3), 1258 ("See", 4), 1259 ], 1260 add_scroll_bar=True, 1261 name="simple_list") 1262 form.add_layout(layout) 1263 layout.add_widget(simple_list) 1264 form.fix() 1265 form.register_scene(scene) 1266 form.reset() 1267 1268 # Check that the widget is rendered correctly with the scrollbar 1269 form.update(0) 1270 self.assert_canvas_equals( 1271 canvas, 1272 "Some O\n" + 1273 "Stuff |\n" + 1274 "To |\n" + 1275 " \n" + 1276 " \n" + 1277 " \n" + 1278 " \n" + 1279 " \n" + 1280 " \n" + 1281 " \n") 1282 1283 # Check that mouse input works 1284 self.process_mouse(form, [(39, 2, MouseEvent.LEFT_CLICK)]) 1285 form.update(0) 1286 self.assert_canvas_equals( 1287 canvas, 1288 "Stuff |\n" + 1289 "To |\n" + 1290 "See O\n" + 1291 " \n" + 1292 " \n" + 1293 " \n" + 1294 " \n" + 1295 " \n" + 1296 " \n" + 1297 " \n") 1298 1299 # Check that options can be set and hide scrollbar 1300 simple_list.options = [("New list", 1)] 1301 form.update(0) 1302 self.assert_canvas_equals( 1303 canvas, 1304 "New list \n" + 1305 " \n" + 1306 " \n" + 1307 " \n" + 1308 " \n" + 1309 " \n" + 1310 " \n" + 1311 " \n" + 1312 " \n" + 1313 " \n") 1314 1315 def test_multi_column_list_box_scrollbar(self): 1316 """ 1317 Check MultiColumnListBox scrollbar works. 1318 """ 1319 # Create a dummy screen. 1320 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 1321 scene = MagicMock(spec=Scene) 1322 canvas = Canvas(screen, 10, 40, 0, 0) 1323 1324 # Create the form we want to test. 1325 form = Frame(canvas, canvas.height, canvas.width, has_border=False) 1326 layout = Layout([100], fill_frame=True) 1327 mc_list = MultiColumnListBox( 1328 3, 1329 [3, 0, ">4"], 1330 [ 1331 (["1", "2", "3"], 1), 1332 (["11", "222", "333"], 2), 1333 (["111", "2", "3"], 3), 1334 ], 1335 titles=["A", "B", "C"], 1336 add_scroll_bar=True, 1337 name="mc_list") 1338 form.add_layout(layout) 1339 layout.add_widget(mc_list) 1340 form.fix() 1341 form.register_scene(scene) 1342 form.reset() 1343 1344 # Check that the widget is rendered correctly with the scrollbar 1345 form.update(0) 1346 self.assert_canvas_equals( 1347 canvas, 1348 "A B C \n" + 1349 "1 2 3O\n" + 1350 "11 222 333|\n" + 1351 " \n" + 1352 " \n" + 1353 " \n" + 1354 " \n" + 1355 " \n" + 1356 " \n" + 1357 " \n") 1358 1359 def test_disabled_text(self): 1360 """ 1361 Check disabled TextBox can be used for pre-formatted output. 1362 """ 1363 # Create a dummy screen. 1364 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 1365 scene = MagicMock(spec=Scene) 1366 canvas = Canvas(screen, 10, 40, 0, 0) 1367 1368 # Create the form we want to test. 1369 form = Frame(canvas, canvas.height, canvas.width, has_border=False) 1370 layout = Layout([100], fill_frame=True) 1371 form.add_layout(layout) 1372 text_box = TextBox(1, as_string=True) 1373 text_box.disabled = True 1374 layout.add_widget(text_box) 1375 form.fix() 1376 form.register_scene(scene) 1377 form.reset() 1378 1379 # Check that input has no effect on the programmed value. 1380 text_box.value = "A test" 1381 self.process_keys(form, ["A"]) 1382 form.save() 1383 self.assertEqual(text_box.value, "A test") 1384 1385 # Check that we can provide a custom colour. Since the default palette has no "custom" 1386 # key, this will throw an exception. 1387 self.assertEqual(text_box._pick_colours("blah"), form.palette["disabled"]) 1388 with self.assertRaises(KeyError) as cm: 1389 text_box.custom_colour = "custom" 1390 text_box._pick_colours("blah") 1391 self.assertIn("custom", str(cm.exception)) 1392 1393 # Also check the value is returned as set 1394 self.assertEqual(text_box.custom_colour, "custom") 1395 1396 def test_line_flow(self): 1397 """ 1398 Check TextBox line-flow editing works as expected. 1399 """ 1400 # Create a dummy screen. 1401 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 1402 scene = MagicMock(spec=Scene) 1403 canvas = Canvas(screen, 10, 40, 0, 0) 1404 1405 # Create the form we want to test. 1406 form = Frame(canvas, canvas.height, canvas.width, has_border=False) 1407 layout = Layout([100], fill_frame=True) 1408 form.add_layout(layout) 1409 text_box = TextBox(5, as_string=True, line_wrap=True) 1410 layout.add_widget(text_box) 1411 form.fix() 1412 form.register_scene(scene) 1413 form.reset() 1414 1415 # start with some text that will wrap and check display works. 1416 text_box.value = "A\nSome very long text that will wrap across multiple lines\nB\n" 1417 1418 # Check that the pop-up is rendered correctly. 1419 form.update(0) 1420 self.assert_canvas_equals( 1421 canvas, 1422 "A \n" + 1423 "Some very long text that will wrap acros\n" + 1424 "s multiple lines \n" + 1425 "B \n" + 1426 " \n" + 1427 " \n" + 1428 " \n" + 1429 " \n" + 1430 " \n" + 1431 " \n") 1432 1433 # Check keyboard logic still works and display reflows on demand. 1434 self.process_mouse(form, [(0, 0, MouseEvent.LEFT_CLICK)]) 1435 self.process_keys(form, [Screen.KEY_DOWN, "A", Screen.KEY_END, "B"]) 1436 form.update(1) 1437 self.assert_canvas_equals( 1438 canvas, 1439 "A \n" + 1440 "ASome very long text that will wrap acro\n" + 1441 "ss multiple linesB \n" + 1442 "B \n" + 1443 " \n" + 1444 " \n" + 1445 " \n" + 1446 " \n" + 1447 " \n" + 1448 " \n") 1449 self.assertEqual(text_box.value, 1450 "A\nASome very long text that will wrap across multiple linesB\nB\n") 1451 1452 # Check mouse logic still works. 1453 self.process_mouse(form, [(0, 2, MouseEvent.LEFT_CLICK)]) 1454 self.process_keys(form, ["Z"]) 1455 self.process_mouse(form, [(3, 3, MouseEvent.LEFT_CLICK)]) 1456 self.process_keys(form, ["Y"]) 1457 form.update(1) 1458 self.assertEqual(text_box.value, 1459 "A\nASome very long text that will wrap acroZss multiple linesB\nBY\n") 1460 1461 def test_pop_up_widget(self): 1462 """ 1463 Check popup dialog work as expected. 1464 """ 1465 def test_on_click(selection): 1466 raise NextScene(str(selection)) 1467 1468 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 1469 scene = MagicMock(spec=Scene) 1470 canvas = Canvas(screen, 10, 40, 0, 0) 1471 form = PopUpDialog(canvas, "Message", ["Yes", "No"], test_on_click) 1472 form.register_scene(scene) 1473 form.reset() 1474 1475 # Check that the pop-up is rendered correctly. 1476 form.update(0) 1477 self.assert_canvas_equals( 1478 canvas, 1479 " \n" + 1480 " \n" + 1481 " +------------------+ \n" + 1482 " |Message | \n" + 1483 " | | \n" + 1484 " | < Yes > < No > | \n" + 1485 " +------------------+ \n" + 1486 " \n" + 1487 " \n" + 1488 " \n") 1489 1490 # Check that mouse input triggers the close function. 1491 with self.assertRaises(NextScene): 1492 self.process_mouse(form, [(14, 5, MouseEvent.LEFT_CLICK)]) 1493 1494 # Check that the pop-up swallows all events. 1495 event = object() 1496 self.assertIsNone(form.process_event(event)) 1497 1498 def test_pop_up_no_buttons(self): 1499 """ 1500 Check dialog with nobuttons work as expected. 1501 """ 1502 def test_on_click(selection): 1503 raise NextScene(str(selection)) 1504 1505 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 1506 scene = MagicMock(spec=Scene) 1507 canvas = Canvas(screen, 10, 40, 0, 0) 1508 form = PopUpDialog(canvas, "Message", [], test_on_click) 1509 form.register_scene(scene) 1510 form.reset() 1511 1512 # Check that the pop-up is rendered correctly. 1513 form.update(0) 1514 self.assert_canvas_equals( 1515 canvas, 1516 " \n" + 1517 " \n" + 1518 " \n" + 1519 " +-------+ \n" + 1520 " |Message| \n" + 1521 " +-------+ \n" + 1522 " \n" + 1523 " \n" + 1524 " \n" + 1525 " \n") 1526 1527 def test_cjk_popup(self): 1528 """ 1529 Check PopUpDialog widgets work with CJK double-width characters. 1530 """ 1531 # Apologies to anyone who actually speaks this language! I just need some double-width 1532 # glyphs so have re-used the ones from the original bug report. 1533 screen = MagicMock(spec=Screen, colours=8, unicode_aware=True) 1534 scene = MagicMock(spec=Scene) 1535 canvas = Canvas(screen, 10, 40, 0, 0) 1536 form = PopUpDialog(canvas, u"你確定嗎? 你確定嗎? 你確定嗎?", [u"是", u"否"]) 1537 form.register_scene(scene) 1538 form.reset() 1539 1540 # Check that the pop-up is rendered correctly. 1541 form.update(0) 1542 self.assert_canvas_equals( 1543 canvas, 1544 " \n" + 1545 " \n" + 1546 " ┌────────────────────────┐ \n" + 1547 " │你你確確定定嗎嗎?? 你你確確定定嗎嗎?? │ \n" + 1548 " │你你確確定定嗎嗎?? █ \n" + 1549 " │ ░ \n" + 1550 " │ < 是是 > < 否否 > │ \n" + 1551 " └────────────────────────┘ \n" + 1552 " \n" + 1553 " \n") 1554 1555 def test_cjk_forms(self): 1556 """ 1557 Check form widgets work with CJK characters. 1558 """ 1559 # Create a dummy screen. 1560 screen = MagicMock(spec=Screen, colours=8, unicode_aware=True) 1561 scene = MagicMock(spec=Scene) 1562 canvas = Canvas(screen, 10, 40, 0, 0) 1563 1564 # Create the form we want to test. 1565 form = Frame(canvas, canvas.height, canvas.width, has_border=False) 1566 layout = Layout([100], fill_frame=True) 1567 mc_list = MultiColumnListBox( 1568 4, 1569 [3, 5, 0], 1570 [ 1571 (["1", "2", "3"], 1), 1572 ([u"你", u"確", u"定"], 2), 1573 ], 1574 titles=[u"你確定嗎?", u"你確定嗎?", u"你確定嗎?"]) 1575 text = Text() 1576 text_box = TextBox(3) 1577 form.add_layout(layout) 1578 layout.add_widget(mc_list) 1579 layout.add_widget(text) 1580 layout.add_widget(text_box) 1581 form.fix() 1582 form.register_scene(scene) 1583 form.reset() 1584 1585 # Set some interesting values... 1586 text.value = u"你確定嗎? 你確定嗎? 你確定嗎?" 1587 text_box.value = [u"你確定嗎", u"?"] 1588 1589 # Check that the CJK characters render correctly - no really this is correctly aligned! 1590 form.update(0) 1591 self.assert_canvas_equals( 1592 canvas, 1593 u"你你 你你確確 你你確確定定嗎嗎?? \n" + 1594 u"1 2 3 \n" + 1595 u"你你 確確 定定 \n" + 1596 u" \n" + 1597 u"你你確確定定嗎嗎?? 你你確確定定嗎嗎?? 你你確確定定嗎嗎?? \n" + 1598 u"你你確確定定嗎嗎 \n" + 1599 u"?? \n" + 1600 u" \n" + 1601 u" \n" + 1602 u" \n") 1603 1604 # Check that mouse input takes into account the glyph width 1605 self.process_mouse(form, [(5, 4, MouseEvent.LEFT_CLICK)]) 1606 self.process_keys(form, ["b"]) 1607 self.process_mouse(form, [(2, 4, MouseEvent.LEFT_CLICK)]) 1608 self.process_keys(form, ["p"]) 1609 form.save() 1610 self.assertEqual(text.value, u"你p確b定嗎? 你確定嗎? 你確定嗎?") 1611 1612 self.process_mouse(form, [(2, 5, MouseEvent.LEFT_CLICK)]) 1613 self.process_keys(form, ["p"]) 1614 self.process_mouse(form, [(1, 6, MouseEvent.LEFT_CLICK)]) 1615 self.process_keys(form, ["b"]) 1616 form.save() 1617 self.assertEqual(text_box.value, [u"你p確定嗎", u"b?"]) 1618 1619 def test_shadow(self): 1620 """ 1621 Check Frames support shadows. 1622 """ 1623 def test_on_click(selection): 1624 raise NextScene(str(selection)) 1625 1626 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 1627 scene = MagicMock(spec=Scene) 1628 canvas = Canvas(screen, 10, 40, 0, 0) 1629 for y in range(10): 1630 canvas.print_at("X" * 40, 0, y) 1631 form = PopUpDialog( 1632 canvas, "Message", ["Yes", "No"], test_on_click, has_shadow=True) 1633 form.register_scene(scene) 1634 form.reset() 1635 1636 # Check that the pop-up is rendered correctly. 1637 form.update(0) 1638 self.assert_canvas_equals( 1639 canvas, 1640 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + 1641 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + 1642 "XXXXXXXXXX+------------------+XXXXXXXXXX\n" + 1643 "XXXXXXXXXX|Message |XXXXXXXXXX\n" + 1644 "XXXXXXXXXX| |XXXXXXXXXX\n" + 1645 "XXXXXXXXXX| < Yes > < No > |XXXXXXXXXX\n" + 1646 "XXXXXXXXXX+------------------+XXXXXXXXXX\n" + 1647 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + 1648 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + 1649 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n") 1650 1651 def test_clone(self): 1652 """ 1653 Check Frame cloning works. 1654 """ 1655 def test_on_click(selection): 1656 raise NextScene(str(selection)) 1657 1658 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 1659 scene = MagicMock(spec=Scene) 1660 scene2 = Scene([], 10) 1661 canvas = Canvas(screen, 10, 40, 0, 0) 1662 1663 # Check that pop-up dialogs get copied to the new Scene 1664 form = PopUpDialog( 1665 canvas, "Message", ["Yes", "No"], test_on_click, has_shadow=True) 1666 form.register_scene(scene) 1667 form.clone(canvas, scene2) 1668 self.assertEqual(len(scene2.effects), 1) 1669 self.assertEqual(scene2.effects[0]._text, "Message") 1670 self.assertEqual(scene2.effects[0]._buttons, ["Yes", "No"]) 1671 1672 # Check that normal Frame data gets copied to the new Scene. 1673 frame = TestFrame(canvas) 1674 frame2 = TestFrame(canvas) 1675 scene2 = Scene([frame2], 10) 1676 frame.register_scene(scene) 1677 frame2.register_scene(scene) 1678 frame.data = {"TA": ["something"]} 1679 frame2.data = {} 1680 1681 self.assertEqual(frame2.data, {}) 1682 self.assertNotEqual(frame2.data, frame.data) 1683 frame.clone(canvas, scene2) 1684 self.assertEqual(frame2.data, frame.data) 1685 1686 def test_frame_rate(self): 1687 """ 1688 Check Frame rate limiting works as expected. 1689 """ 1690 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 1691 canvas = Canvas(screen, 10, 40, 0, 0) 1692 form = TestFrame(canvas) 1693 form.reset() 1694 1695 # With no special CPU consideration, and a cursor to animate, there 1696 # should be a 5 frame pause. 1697 self.assertEqual(form.reduce_cpu, False) 1698 self.assertEqual(form.frame_update_count, 5) 1699 1700 # Shift focus away from a text input (to get no cursor animation). 1701 self.process_keys(form, [Screen.KEY_BACK_TAB]) 1702 1703 # With no special CPU consideration, and no cursors to animate, there 1704 # should be a (very!) long pause. 1705 self.assertEqual(form.reduce_cpu, False) 1706 self.assertEqual(form.frame_update_count, 1000000) 1707 1708 def test_cpu_saving(self): 1709 """ 1710 Check Frame rate limiting is even more extreme when in cpu saving mode. 1711 """ 1712 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 1713 canvas = Canvas(screen, 10, 40, 0, 0) 1714 form = TestFrame(canvas, reduce_cpu=True) 1715 form.reset() 1716 1717 # In this mode, it shouldn't matter where we are on the Frame - all 1718 # widgets will basically say they don't need animation. 1719 self.assertEqual(form.reduce_cpu, True) 1720 self.assertEqual(form.frame_update_count, 1000000) 1721 1722 # Shift focus away from a text input, just to be sure. 1723 self.process_keys(form, [Screen.KEY_BACK_TAB]) 1724 self.assertEqual(form.frame_update_count, 1000000) 1725 1726 def test_stop_frame(self): 1727 """ 1728 Check Frames always request no end to the Scene. 1729 """ 1730 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 1731 canvas = Canvas(screen, 10, 40, 0, 0) 1732 form = TestFrame(canvas, reduce_cpu=True) 1733 self.assertEqual(form.stop_frame, -1) 1734 1735 def test_empty_frame(self): 1736 """ 1737 Check empty Frames still work. 1738 """ 1739 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 1740 canvas = Canvas(screen, 10, 40, 0, 0) 1741 scene = MagicMock(spec=Scene) 1742 form = TestFrame3(canvas) 1743 form.register_scene(scene) 1744 form.reset() 1745 1746 # Check all keyboard events get swallowed 1747 self.assertIsNone(form.process_event(KeyboardEvent(ord("A")))) 1748 1749 # Check Mouse events over the Frame are swallowed and others allowed 1750 # to bubble down the input stack. 1751 self.assertIsNone( 1752 form.process_event(MouseEvent(20, 5, MouseEvent.LEFT_CLICK))) 1753 self.assertIsNotNone( 1754 form.process_event(MouseEvent(5, 5, MouseEvent.LEFT_CLICK))) 1755 1756 # Check form data is empty. 1757 form.save() 1758 self.assertEqual(form.data, {}) 1759 1760 def test_label_change(self): 1761 """ 1762 Check Labels can be dynamically updated. 1763 """ 1764 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 1765 canvas = Canvas(screen, 10, 40, 0, 0) 1766 form = TestFrame(canvas) 1767 form.reset() 1768 1769 # Check initial rendering 1770 form.update(0) 1771 self.assert_canvas_equals( 1772 canvas, 1773 "+--------------------------------------+\n" + 1774 "| Group 1: |\n" + 1775 "| My First O\n" + 1776 "| Box: |\n" + 1777 "| |\n" + 1778 "| |\n" + 1779 "| |\n" + 1780 "| Text1: |\n" + 1781 "| Text2: |\n" + 1782 "+--------------------------------------+\n") 1783 1784 # Check dynamic updates change the rendering. 1785 form.label.text = "New text here:" 1786 form.update(0) 1787 self.assert_canvas_equals( 1788 canvas, 1789 "+--------------------------------------+\n" + 1790 "| New text here: |\n" + 1791 "| My First O\n" + 1792 "| Box: |\n" + 1793 "| |\n" + 1794 "| |\n" + 1795 "| |\n" + 1796 "| Text1: |\n" + 1797 "| Text2: |\n" + 1798 "+--------------------------------------+\n") 1799 self.assertEqual(form.label.text, "New text here:") 1800 1801 # And check that values are unaffected (and still not set). 1802 self.assertEqual(form.label.value, None) 1803 1804 def test_label_height(self): 1805 """ 1806 Check Labels can be dynamically updated. 1807 """ 1808 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 1809 canvas = Canvas(screen, 10, 40, 0, 0) 1810 form = TestFrame(canvas, label_height=2) 1811 form.reset() 1812 1813 # Check Label obeys required height 1814 form.update(0) 1815 self.assert_canvas_equals( 1816 canvas, 1817 "+--------------------------------------+\n" + 1818 "| Group 1: |\n" + 1819 "| O\n" + 1820 "| My First |\n" + 1821 "| Box: |\n" + 1822 "| |\n" + 1823 "| |\n" + 1824 "| |\n" + 1825 "| Text1: |\n" + 1826 "+--------------------------------------+\n") 1827 1828 # Now check wrapping works too... 1829 form.label.text = "A longer piece of text that should wrap across multiple lines:" 1830 form.update(1) 1831 self.assert_canvas_equals( 1832 canvas, 1833 "+--------------------------------------+\n" + 1834 "| A longer piece of text that should |\n" + 1835 "| wrap across multiple lines: O\n" + 1836 "| My First |\n" + 1837 "| Box: |\n" + 1838 "| |\n" + 1839 "| |\n" + 1840 "| |\n" + 1841 "| Text1: |\n" + 1842 "+--------------------------------------+\n") 1843 1844 def test_label_alignment(self): 1845 """ 1846 Check Label alignment works. 1847 """ 1848 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 1849 scene = Scene([], duration=-1) 1850 canvas = Canvas(screen, 10, 40, 0, 0) 1851 form = Frame(canvas, canvas.height, canvas.width) 1852 layout = Layout([1]) 1853 form.add_layout(layout) 1854 layout.add_widget(Label("Left", align="<")) 1855 layout.add_widget(Label("Middle", align="^")) 1856 layout.add_widget(Label("Right", align=">")) 1857 form.fix() 1858 form.register_scene(scene) 1859 scene.add_effect(form) 1860 scene.reset() 1861 1862 # Check that the frame is rendered correctly. 1863 for effect in scene.effects: 1864 effect.update(0) 1865 1866 # Check Label obeys required height 1867 form.update(0) 1868 self.assert_canvas_equals( 1869 canvas, 1870 "+--------------------------------------+\n" + 1871 "|Left |\n" + 1872 "| Middle O\n" + 1873 "| Right|\n" + 1874 "| |\n" + 1875 "| |\n" + 1876 "| |\n" + 1877 "| |\n" + 1878 "| |\n" + 1879 "+--------------------------------------+\n") 1880 1881 def test_label_colours(self): 1882 """ 1883 Check Label custom colour works. 1884 """ 1885 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 1886 scene = Scene([], duration=-1) 1887 canvas = Canvas(screen, 10, 40, 0, 0) 1888 form = Frame(canvas, canvas.height, canvas.width) 1889 layout = Layout([1]) 1890 form.add_layout(layout) 1891 label = Label("Some text") 1892 label.custom_colour = "disabled" 1893 layout.add_widget(label) 1894 form.fix() 1895 form.register_scene(scene) 1896 scene.add_effect(form) 1897 scene.reset() 1898 1899 # Check that the label is rendered in the correct colour palette. 1900 for effect in scene.effects: 1901 effect.update(0) 1902 self.assertEqual(label.custom_colour, "disabled") 1903 self.assertEqual(canvas.get_from(1, 1), (ord("S"), 0, 1, 4)) 1904 1905 # Cheeky test that Labels always pass on events. 1906 event = object() 1907 self.assertEqual(event, label.process_event(event)) 1908 1909 @patch("os.stat") 1910 @patch("os.listdir") 1911 def test_file_browser_stat_err(self, mock_list, mock_stat): 1912 """ 1913 Check FileBrowser widget copes with permissions error on stat. 1914 """ 1915 # First we need to mock out the file system calls to have a regressible test 1916 if sys.platform == "win32": 1917 self.skipTest("File names wrong for windows") 1918 1919 mock_list.return_value = ["A Directory", "A File", "A Lnk"] 1920 mock_stat.side_effect = OSError("Fake error") 1921 1922 # Now set up the Frame ready for testing 1923 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 1924 scene = MagicMock(spec=Scene) 1925 canvas = Canvas(screen, 10, 40, 0, 0) 1926 form = TestFrame4(canvas) 1927 form.register_scene(scene) 1928 form.reset() 1929 1930 # Check that the Frame is rendered correctly. 1931 form.update(0) 1932 self.assert_canvas_equals( 1933 canvas, 1934 "/ Size Last modified\n" + 1935 "|-- A Directory 0 1970-01-01\n" + 1936 "|-- A File 0 1970-01-01\n" + 1937 "|-- A Lnk 0 1970-01-01\n" + 1938 " \n" + 1939 " \n" + 1940 " \n" + 1941 " \n" + 1942 " \n" + 1943 " \n") 1944 1945 @patch("os.listdir") 1946 def test_file_browser_list_err(self, mock_list): 1947 """ 1948 Check FileBrowser widget copes with permissions error on list. 1949 """ 1950 # First we need to mock out the file system calls to have a regressible test 1951 if sys.platform == "win32": 1952 self.skipTest("File names wrong for windows") 1953 1954 mock_list.side_effect = OSError("Fake error") 1955 1956 # Now set up the Frame ready for testing 1957 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 1958 scene = MagicMock(spec=Scene) 1959 canvas = Canvas(screen, 10, 40, 0, 0) 1960 form = TestFrame4(canvas) 1961 form.register_scene(scene) 1962 form.reset() 1963 1964 # Check that the Frame is rendered correctly. 1965 form.update(0) 1966 self.assert_canvas_equals( 1967 canvas, 1968 "/ Size Last modified\n" + 1969 " \n" + 1970 " \n" + 1971 " \n" + 1972 " \n" + 1973 " \n" + 1974 " \n" + 1975 " \n" + 1976 " \n" + 1977 " \n") 1978 1979 @patch("os.path.exists") 1980 @patch("os.path.realpath") 1981 @patch("os.path.islink") 1982 @patch("os.path.isdir") 1983 @patch("os.lstat") 1984 @patch("os.stat") 1985 @patch("os.listdir") 1986 def test_file_browser(self, mock_list, mock_stat, mock_lstat, mock_dir, mock_link, 1987 mock_real_path, mock_exists): 1988 """ 1989 Check FileBrowser widget works as expected. 1990 """ 1991 # First we need to mock out the file system calls to have a regressible test 1992 if sys.platform == "win32": 1993 self.skipTest("File names wrong for windows") 1994 1995 mock_list.return_value = ["A Directory", "A File", "A Lnk", str(b"oo\xcc\x88o\xcc\x88O\xcc\x88.txt", 'utf-8'), "Lnk Directory"] 1996 mock_result = MagicMock() 1997 mock_result.st_mtime = 0 1998 mock_result.st_size = 10000 1999 mock_stat.return_value = mock_result 2000 mock_lstat.return_value = mock_result 2001 mock_dir.side_effect = lambda x: x.endswith("Directory") 2002 mock_link.side_effect = lambda x: "Lnk" in x 2003 mock_real_path.return_value = "A Tgt" 2004 mock_exists.return_value = True 2005 2006 # Now set up the Frame ready for testing 2007 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 2008 scene = MagicMock(spec=Scene) 2009 canvas = Canvas(screen, 10, 40, 0, 0) 2010 form = TestFrame4(canvas) 2011 form.register_scene(scene) 2012 form.reset() 2013 2014 # Check we have a default value for our list. 2015 form.save() 2016 self.assertIsNone(form.selected) 2017 self.assertIsNone(form.highlighted) 2018 self.assertEqual(form.data, {"file_list": None}) 2019 2020 # Check that the Frame is rendered correctly. 2021 form.update(0) 2022 self.assert_canvas_equals( 2023 canvas, 2024 "/ Size Last modified\n" + 2025 "|-+ A Directory 9K 1970-01-01\n" + 2026 "|-+ Lnk Directo... 9K 1970-01-01\n" + 2027 "|-- A File 9K 1970-01-01\n" + 2028 "|-- A Lnk -> A Tgt 9K 1970-01-01\n" + 2029 "|-- oööÖ.txt 9K 1970-01-01\n" + 2030 " \n" + 2031 " \n" + 2032 " \n" + 2033 " \n") 2034 2035 # Check that mouse inpput changes selection. 2036 self.process_mouse(form, [(2, 3, MouseEvent.LEFT_CLICK)]) 2037 form.save() 2038 self.assertEqual(form.data, {"file_list": "/A File"}) 2039 self.assertEqual(form.highlighted, "/A File") 2040 self.assertIsNone(form.selected) 2041 2042 # Check that UP/DOWN change selection. 2043 self.process_keys(form, [Screen.KEY_UP, Screen.KEY_UP]) 2044 form.save() 2045 self.assertEqual(form.data, {"file_list": "/A Directory"}) 2046 self.assertEqual(form.highlighted, "/A Directory") 2047 self.assertIsNone(form.selected) 2048 2049 # Check that enter key handles correctly on directories. 2050 self.process_keys(form, [Screen.ctrl("m")]) 2051 self.assertEqual(form.highlighted, "/") 2052 self.assertIsNone(form.selected) 2053 form.update(1) 2054 self.assert_canvas_equals( 2055 canvas, 2056 "/A Directory Size Last modified\n" + 2057 "|-+ .. \n" + 2058 "|-+ A Directory 9K 1970-01-01\n" + 2059 "|-+ Lnk Directo... 9K 1970-01-01\n" + 2060 "|-- A File 9K 1970-01-01\n" + 2061 "|-- A Lnk -> A Tgt 9K 1970-01-01\n" + 2062 "|-- oööÖ.txt 9K 1970-01-01\n" + 2063 " \n" + 2064 " \n" + 2065 " \n") 2066 2067 # Check that enter key handles correctly on files. 2068 self.process_keys(form, [Screen.KEY_DOWN, Screen.KEY_DOWN, Screen.KEY_DOWN, Screen.ctrl("m")]) 2069 self.assertEqual(form.highlighted, "/A Directory/A File") 2070 self.assertEqual(form.selected, "/A Directory/A File") 2071 2072 @patch("os.path.exists") 2073 @patch("os.path.realpath") 2074 @patch("os.path.islink") 2075 @patch("os.path.isdir") 2076 @patch("os.lstat") 2077 @patch("os.stat") 2078 @patch("os.listdir") 2079 def test_file_filter(self, mock_list, mock_stat, mock_lstat, mock_dir, mock_link, 2080 mock_real_path, mock_exists): 2081 """ 2082 Check FileBrowser widget with a file_filter works as expected. 2083 """ 2084 # First we need to mock out the file system calls to have a regressible test 2085 if sys.platform == "win32": 2086 self.skipTest("File names wrong for windows") 2087 2088 mock_list.return_value = ["A Directory", "A File", "A Lnk", str(b"oo\xcc\x88o\xcc\x88O\xcc\x88.txt", 'utf-8'), "hello.bmp"] 2089 mock_result = MagicMock() 2090 mock_result.st_mtime = 0 2091 mock_result.st_size = 10000 2092 mock_stat.return_value = mock_result 2093 mock_lstat.return_value = mock_result 2094 mock_dir.side_effect = lambda x: x.endswith("Directory") 2095 mock_link.side_effect = lambda x: "Lnk" in x 2096 mock_real_path.return_value = "A Tgt" 2097 mock_exists.return_value = True 2098 2099 # Now set up the Frame ready for testing 2100 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 2101 scene = MagicMock(spec=Scene) 2102 canvas = Canvas(screen, 10, 40, 0, 0) 2103 form = TestFrame4(canvas, file_filter=r".*\.bmp$") 2104 form.register_scene(scene) 2105 form.reset() 2106 2107 # Check we have a default value for our list. 2108 form.save() 2109 self.assertIsNone(form.selected) 2110 self.assertIsNone(form.highlighted) 2111 self.assertEqual(form.data, {"file_list": None}) 2112 2113 # Check that the Frame is rendered correctly. 2114 form.update(0) 2115 self.assert_canvas_equals( 2116 canvas, 2117 "/ Size Last modified\n" + 2118 "|-+ A Directory 9K 1970-01-01\n" + 2119 "|-- hello.bmp 9K 1970-01-01\n" + 2120 " \n" + 2121 " \n" + 2122 " \n" + 2123 " \n" + 2124 " \n" + 2125 " \n" + 2126 " \n") 2127 2128 # Check that enter key handles correctly on files. 2129 self.process_keys(form, [Screen.KEY_DOWN, Screen.KEY_DOWN, Screen.ctrl("m")]) 2130 self.assertEqual(form.highlighted, "/hello.bmp") 2131 self.assertEqual(form.selected, "/hello.bmp") 2132 2133 def test_date_picker(self): 2134 """ 2135 Check DatePicker widget works as expected. 2136 """ 2137 # Now set up the Frame ready for testing 2138 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 2139 scene = Scene([], duration=-1) 2140 canvas = Canvas(screen, 10, 40, 0, 0) 2141 form = TestFrame5(canvas) 2142 scene.add_effect(form) 2143 scene.reset() 2144 2145 # Check that the Frame is rendered correctly. 2146 for effect in scene.effects: 2147 effect.update(0) 2148 self.assert_canvas_equals( 2149 canvas, 2150 "+--------------------------------------+\n" + 2151 "|Date: 02/Jan/2017 |\n" + 2152 "|Time: 12:00:59 O\n" + 2153 "| |\n" + 2154 "| |\n" + 2155 "| |\n" + 2156 "| |\n" + 2157 "| |\n" + 2158 "| |\n" + 2159 "+--------------------------------------+\n") 2160 2161 # Check that enter key brings up edit pop-up 2162 self.process_keys(scene, [Screen.ctrl("m")]) 2163 self.assertFalse(form.changed) 2164 for effect in scene.effects: 2165 effect.update(1) 2166 self.assert_canvas_equals( 2167 canvas, 2168 "+-----|01 2016|--------------------+\n" + 2169 "|Date:|02/Jan/2017| |\n" + 2170 "|Time:|03 Feb 2018| O\n" + 2171 "| +-----------+ |\n" + 2172 "| |\n" + 2173 "| |\n" + 2174 "| |\n" + 2175 "| |\n" + 2176 "| |\n" + 2177 "+--------------------------------------+\n") 2178 2179 # Check that you can't select an invalid date. 2180 self.process_keys(scene, ["31", "Feb", Screen.ctrl("m")], separator=Screen.KEY_TAB) 2181 self.assertFalse(form.changed) 2182 for effect in scene.effects: 2183 effect.update(2) 2184 self.assert_canvas_equals( 2185 canvas, 2186 "+-----|30 Jan 2016|--------------------+\n" + 2187 "|Date:|31/Feb/2017| |\n" + 2188 "|Time:| Mar 2018| O\n" + 2189 "| +-----------+ |\n" + 2190 "| |\n" + 2191 "| |\n" + 2192 "| |\n" + 2193 "| |\n" + 2194 "| |\n" + 2195 "+--------------------------------------+\n") 2196 2197 # Check that a valid date updates the value - wait one second to allow search to reset. 2198 sleep(1) 2199 self.process_keys(scene, ["15", "Jun", Screen.ctrl("m")], separator=Screen.KEY_TAB) 2200 self.assertTrue(form.changed) 2201 for effect in scene.effects: 2202 effect.update(2) 2203 self.assert_canvas_equals( 2204 canvas, 2205 "+--------------------------------------+\n" + 2206 "|Date: 15/Jun/2017 |\n" + 2207 "|Time: 12:00:59 O\n" + 2208 "| |\n" + 2209 "| |\n" + 2210 "| |\n" + 2211 "| |\n" + 2212 "| |\n" + 2213 "| |\n" + 2214 "+--------------------------------------+\n") 2215 self.assertEquals(form.date_widget.value, date(2017, 6, 15)) 2216 2217 # Check the mouse works too - pass mouse events to top-level effect 2218 self.process_mouse(scene.effects[-1], [(10, 1, MouseEvent.LEFT_CLICK)]) 2219 for effect in scene.effects: 2220 effect.update(3) 2221 self.assert_canvas_equals( 2222 canvas, 2223 "+-----|14 May 2016|--------------------+\n" + 2224 "|Date:|15/Jun/2017| |\n" + 2225 "|Time:|16 Jul 2018| O\n" + 2226 "| +-----------+ |\n" + 2227 "| |\n" + 2228 "| |\n" + 2229 "| |\n" + 2230 "| |\n" + 2231 "| |\n" + 2232 "+--------------------------------------+\n") 2233 2234 self.process_mouse(scene.effects[-1], [(11, 2, MouseEvent.LEFT_CLICK)]) 2235 for effect in scene.effects: 2236 effect.update(4) 2237 self.assert_canvas_equals( 2238 canvas, 2239 "+-----|14 Jun 2016|--------------------+\n" + 2240 "|Date:|15/Jul/2017| |\n" + 2241 "|Time:|16 Aug 2018| O\n" + 2242 "| +-----------+ |\n" + 2243 "| |\n" + 2244 "| |\n" + 2245 "| |\n" + 2246 "| |\n" + 2247 "| |\n" + 2248 "+--------------------------------------+\n") 2249 2250 def test_time_picker(self): 2251 """ 2252 Check TimePicker widget works as expected. 2253 """ 2254 # Now set up the Frame ready for testing 2255 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 2256 scene = Scene([], duration=-1) 2257 canvas = Canvas(screen, 10, 40, 0, 0) 2258 form = TestFrame5(canvas) 2259 scene.add_effect(form) 2260 scene.reset() 2261 2262 # Check that the Frame is rendered correctly. 2263 for effect in scene.effects: 2264 effect.update(0) 2265 self.assertFalse(form.changed) 2266 self.assert_canvas_equals( 2267 canvas, 2268 "+--------------------------------------+\n" + 2269 "|Date: 02/Jan/2017 |\n" + 2270 "|Time: 12:00:59 O\n" + 2271 "| |\n" + 2272 "| |\n" + 2273 "| |\n" + 2274 "| |\n" + 2275 "| |\n" + 2276 "| |\n" + 2277 "+--------------------------------------+\n") 2278 2279 # Check that enter key brings up edit pop-up 2280 self.process_keys(scene, [Screen.KEY_DOWN, Screen.ctrl("m")]) 2281 for effect in scene.effects: 2282 effect.update(1) 2283 self.assertFalse(form.changed) 2284 self.assert_canvas_equals( 2285 canvas, 2286 "+-----+--------+-----------------------+\n" + 2287 "|Date:|11 58|17 |\n" + 2288 "|Time:|12:00:59| O\n" + 2289 "| |13 01 | |\n" + 2290 "| +--------+ |\n" + 2291 "| |\n" + 2292 "| |\n" + 2293 "| |\n" + 2294 "| |\n" + 2295 "+--------------------------------------+\n") 2296 2297 # Check that we can change the time with cursors keys and mouse selection - and click out 2298 # to exit. 2299 self.process_mouse(scene, [(7, 1, MouseEvent.LEFT_CLICK)]) 2300 self.process_keys(scene, [Screen.KEY_TAB, Screen.KEY_DOWN, Screen.KEY_TAB, Screen.KEY_UP]) 2301 self.process_mouse(scene, [(10, 10, MouseEvent.LEFT_CLICK)]) 2302 self.assertTrue(form.changed) 2303 for effect in scene.effects: 2304 effect.update(2) 2305 self.assert_canvas_equals( 2306 canvas, 2307 "+--------------------------------------+\n" + 2308 "|Date: 02/Jan/2017 |\n" + 2309 "|Time: 11:01:58 O\n" + 2310 "| |\n" + 2311 "| |\n" + 2312 "| |\n" + 2313 "| |\n" + 2314 "| |\n" + 2315 "| |\n" + 2316 "+--------------------------------------+\n") 2317 self.assertEquals(form.time_widget.value, time(11, 1, 58)) 2318 2319 # Check the mouse works too - pass mouse events to top-level effect 2320 self.process_mouse(scene.effects[-1], [(7, 2, MouseEvent.LEFT_CLICK)]) 2321 for effect in scene.effects: 2322 effect.update(3) 2323 self.assert_canvas_equals( 2324 canvas, 2325 "+-----+--------+-----------------------+\n" + 2326 "|Date:|10 00 57|17 |\n" + 2327 "|Time:|11:01:58| O\n" + 2328 "| |12 02 59| |\n" + 2329 "| +--------+ |\n" + 2330 "| |\n" + 2331 "| |\n" + 2332 "| |\n" + 2333 "| |\n" + 2334 "+--------------------------------------+\n") 2335 2336 self.process_mouse(scene.effects[-1], [(7, 1, MouseEvent.LEFT_CLICK)]) 2337 for effect in scene.effects: 2338 effect.update(4) 2339 self.assert_canvas_equals( 2340 canvas, 2341 "+-----+--------+-----------------------+\n" + 2342 "|Date:|09 00 57|17 |\n" + 2343 "|Time:|10:01:58| O\n" + 2344 "| |11 02 59| |\n" + 2345 "| +--------+ |\n" + 2346 "| |\n" + 2347 "| |\n" + 2348 "| |\n" + 2349 "| |\n" + 2350 "+--------------------------------------+\n") 2351 2352 def test_background(self): 2353 """ 2354 Check Background widget works as expected. 2355 """ 2356 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 2357 scene = MagicMock(spec=Scene) 2358 canvas = Canvas(screen, 10, 40, 0, 0) 2359 form = Background(canvas, bg=7) 2360 form.register_scene(scene) 2361 form.reset() 2362 2363 # Check that the widget is rendered correctly. 2364 form.update(0) 2365 for y in range(canvas.height): 2366 for x in range(canvas.width): 2367 char, _, _, bg = canvas.get_from(x, y) 2368 self.assertEquals(char, ord(" ")) 2369 self.assertEquals(bg, 7) 2370 2371 # Check properties 2372 self.assertEquals(form.stop_frame, 0) 2373 self.assertGreater(form.frame_update_count, 1000) 2374 2375 def test_dropdown_list(self): 2376 """ 2377 Check DropdownList widget works as expected. 2378 """ 2379 # Now set up the Frame ready for testing 2380 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 2381 scene = Scene([], duration=-1) 2382 canvas = Canvas(screen, 10, 40, 0, 0) 2383 form = Frame(canvas, canvas.height, canvas.width) 2384 layout = Layout([100], fill_frame=True) 2385 form.add_layout(layout) 2386 layout.add_widget(DropdownList([("Item 1", 1), ("Item 2", 3), ("Item 3", 5)])) 2387 form.fix() 2388 form.register_scene(scene) 2389 scene.add_effect(form) 2390 scene.reset() 2391 2392 # Check that the Frame is rendered correctly. 2393 for effect in scene.effects: 2394 effect.update(0) 2395 self.assert_canvas_equals( 2396 canvas, 2397 "+--------------------------------------+\n" + 2398 "|[Item 1 ]|\n" + 2399 "| O\n" + 2400 "| |\n" + 2401 "| |\n" + 2402 "| |\n" + 2403 "| |\n" + 2404 "| |\n" + 2405 "| |\n" + 2406 "+--------------------------------------+\n") 2407 2408 # Check it opens as expected 2409 self.process_mouse(scene, [(7, 1, MouseEvent.LEFT_CLICK)]) 2410 for effect in scene.effects: 2411 effect.update(1) 2412 self.assert_canvas_equals( 2413 canvas, 2414 "++------------------------------------++\n" + 2415 "||Item 1 ||\n" + 2416 "||------------------------------------|O\n" + 2417 "||Item 1 ||\n" + 2418 "||Item 2 ||\n" + 2419 "||Item 3 ||\n" + 2420 "|+------------------------------------+|\n" + 2421 "| |\n" + 2422 "| |\n" + 2423 "+--------------------------------------+\n") 2424 2425 # Check ESC works as expected 2426 self.process_keys(scene, [Screen.KEY_DOWN, Screen.KEY_ESCAPE]) 2427 for effect in scene.effects: 2428 effect.update(2) 2429 self.assert_canvas_equals( 2430 canvas, 2431 "+--------------------------------------+\n" + 2432 "|[Item 1 ]|\n" + 2433 "| O\n" + 2434 "| |\n" + 2435 "| |\n" + 2436 "| |\n" + 2437 "| |\n" + 2438 "| |\n" + 2439 "| |\n" + 2440 "+--------------------------------------+\n") 2441 2442 # Check key selection works as expected 2443 self.process_keys(scene, [" ", Screen.KEY_DOWN]) 2444 for effect in scene.effects: 2445 effect.update(3) 2446 self.assert_canvas_equals( 2447 canvas, 2448 "++------------------------------------++\n" + 2449 "||Item 2 ||\n" + 2450 "||------------------------------------|O\n" + 2451 "||Item 1 ||\n" + 2452 "||Item 2 ||\n" + 2453 "||Item 3 ||\n" + 2454 "|+------------------------------------+|\n" + 2455 "| |\n" + 2456 "| |\n" + 2457 "+--------------------------------------+\n") 2458 2459 # Check Enter works as expected 2460 self.process_keys(scene, [Screen.ctrl("m")]) 2461 for effect in scene.effects: 2462 effect.update(4) 2463 self.assert_canvas_equals( 2464 canvas, 2465 "+--------------------------------------+\n" + 2466 "|[Item 2 ]|\n" + 2467 "| O\n" + 2468 "| |\n" + 2469 "| |\n" + 2470 "| |\n" + 2471 "| |\n" + 2472 "| |\n" + 2473 "| |\n" + 2474 "+--------------------------------------+\n") 2475 2476 def test_dropdown_list_options(self): 2477 """ 2478 Check DropdownList widget extra features work. 2479 """ 2480 # Now set up the Frame ready for testing 2481 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 2482 scene = Scene([], duration=-1) 2483 canvas = Canvas(screen, 10, 40, 0, 0) 2484 form = Frame(canvas, canvas.height, canvas.width) 2485 layout = Layout([100], fill_frame=True) 2486 form.add_layout(layout) 2487 layout.add_widget(Divider(draw_line=False, height=7)) 2488 dd_list = DropdownList([("Item {}".format(i), i) for i in range(10)]) 2489 layout.add_widget(dd_list) 2490 form.fix() 2491 form.register_scene(scene) 2492 scene.add_effect(form) 2493 scene.reset() 2494 2495 # Check that the Frame is rendered correctly. 2496 for effect in scene.effects: 2497 effect.update(0) 2498 self.assert_canvas_equals( 2499 canvas, 2500 "+--------------------------------------+\n" + 2501 "| |\n" + 2502 "| O\n" + 2503 "| |\n" + 2504 "| |\n" + 2505 "| |\n" + 2506 "| |\n" + 2507 "| |\n" + 2508 "|[Item 0 ]|\n" + 2509 "+--------------------------------------+\n") 2510 2511 # Check it opens as expected 2512 self.process_mouse(scene, [(7, 8, MouseEvent.LEFT_CLICK)]) 2513 for effect in scene.effects: 2514 effect.update(1) 2515 self.assert_canvas_equals( 2516 canvas, 2517 "++------------------------------------++\n" + 2518 "||Item 0 O||\n" + 2519 "||Item 1 ||O\n" + 2520 "||Item 2 |||\n" + 2521 "||Item 3 |||\n" + 2522 "||Item 4 |||\n" + 2523 "||Item 5 |||\n" + 2524 "||------------------------------------||\n" + 2525 "||Item 0 ||\n" + 2526 "++------------------------------------++\n") 2527 2528 # Check that options can be read and set. 2529 dd_list.options = [(["a", "b", "c", "d", "e", "f"], 0)] 2530 self.assertEqual(dd_list.options, [(["a", "b", "c", "d", "e", "f"], 0)]) 2531 dd_list.options = [] 2532 self.assertEqual(dd_list.options, []) 2533 2534 def test_divider(self): 2535 """ 2536 Check Divider widget sundry features work. 2537 """ 2538 # Now set up the Frame ready for testing 2539 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 2540 scene = Scene([], duration=-1) 2541 canvas = Canvas(screen, 10, 40, 0, 0) 2542 form = Frame(canvas, canvas.height, canvas.width) 2543 layout = Layout([100], fill_frame=True) 2544 form.add_layout(layout) 2545 divider = Divider(draw_line=False, height=7) 2546 layout.add_widget(divider) 2547 form.fix() 2548 form.register_scene(scene) 2549 form.reset() 2550 2551 # Check events are ignored 2552 event = object() 2553 self.assertEqual(event, divider.process_event(event)) 2554 2555 # Check value is None 2556 self.assertIsNone(divider.value) 2557 2558 def test_find_widget(self): 2559 """ 2560 Check find_widget works as expected. 2561 """ 2562 # Set up the Frame ready for testing 2563 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 2564 scene = Scene([], duration=-1) 2565 canvas = Canvas(screen, 10, 40, 0, 0) 2566 form = TestFrame5(canvas) 2567 scene.add_effect(form) 2568 scene.reset() 2569 2570 # Can't find a non-existent widget 2571 self.assertIsNone(form.find_widget("ABLAH")) 2572 2573 # Can find a defined widget 2574 self.assertEquals(form.find_widget("date"), form.date_widget) 2575 2576 def test_password(self): 2577 """ 2578 Check that we can do password input on Text widgets. 2579 """ 2580 # Create a dummy screen. 2581 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 2582 scene = MagicMock(spec=Scene) 2583 canvas = Canvas(screen, 2, 40, 0, 0) 2584 2585 # Create the form we want to test. 2586 form = Frame(canvas, canvas.height, canvas.width, has_border=False) 2587 layout = Layout([100], fill_frame=True) 2588 form.add_layout(layout) 2589 text = Text("Password", hide_char="*") 2590 layout.add_widget(text) 2591 form.fix() 2592 form.register_scene(scene) 2593 form.reset() 2594 2595 # Check that input still saves off values as expected 2596 self.process_keys(form, ["1234"]) 2597 form.save() 2598 self.assertEqual(text.value, "1234") 2599 2600 # Check that it is drawn with the obscuring character, though. 2601 form.update(0) 2602 self.assert_canvas_equals( 2603 canvas, 2604 "Password **** \n" + 2605 " \n") 2606 2607 def test_change_values(self): 2608 """ 2609 Check changing Text values resets cursor position. 2610 """ 2611 # Create a dummy screen. 2612 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 2613 scene = MagicMock(spec=Scene) 2614 canvas = Canvas(screen, 10, 40, 0, 0) 2615 2616 # Create the form we want to test. 2617 form = Frame(canvas, canvas.height, canvas.width, has_border=False) 2618 layout = Layout([100], fill_frame=True) 2619 form.add_layout(layout) 2620 text = Text() 2621 layout.add_widget(text) 2622 form.fix() 2623 form.register_scene(scene) 2624 form.reset() 2625 2626 # Check that input is put at the end of the new text 2627 text.value = "A test" 2628 self.process_keys(form, ["A"]) 2629 form.save() 2630 self.assertEqual(text.value, "A testA") 2631 2632 # Check that growing longer still puts it at the end. 2633 text.value = "A longer test" 2634 self.process_keys(form, ["A"]) 2635 form.save() 2636 self.assertEqual(text.value, "A longer testA") 2637 2638 def test_popup_meu(self): 2639 """ 2640 Check PopupMenu widget works as expected. 2641 """ 2642 # Simple function to test which item is selected. 2643 def click(x): 2644 self.clicked = self.clicked or x 2645 2646 # Now set up the Frame ready for testing 2647 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 2648 scene = Scene([], duration=-1) 2649 canvas = Canvas(screen, 10, 40, 0, 0) 2650 2651 # Reset menu for test 2652 self.clicked = 0 2653 popup = PopupMenu(canvas, 2654 [ 2655 ("First", lambda: click(1)), 2656 ("Second", lambda: click(2)), 2657 ("Third", lambda: click(4)) 2658 ], 2659 0, 0) 2660 popup.register_scene(scene) 2661 scene.add_effect(popup) 2662 scene.reset() 2663 2664 # Check that the menu is rendered correctly. 2665 for effect in scene.effects: 2666 effect.update(0) 2667 self.assert_canvas_equals( 2668 canvas, 2669 "First \n" + 2670 "Second \n" + 2671 "Third \n" + 2672 " \n" + 2673 " \n" + 2674 " \n" + 2675 " \n" + 2676 " \n" + 2677 " \n" + 2678 " \n") 2679 2680 # Check it handles a selection as expected 2681 self.process_mouse(scene, [(0, 1, MouseEvent.LEFT_CLICK)]) 2682 self.assertEquals(len(scene.effects), 0) 2683 self.assertEquals(self.clicked, 2) 2684 2685 # Check choice of location at bottom right 2686 self.clicked = 0 2687 canvas.reset() 2688 popup = PopupMenu(canvas, 2689 [ 2690 ("First", lambda: click(1)), 2691 ("Second", lambda: click(2)), 2692 ("Third", lambda: click(4)) 2693 ], 2694 39, 9) 2695 popup.register_scene(scene) 2696 scene.add_effect(popup) 2697 scene.reset() 2698 2699 # Check that the menu is rendered correctly. 2700 for effect in scene.effects: 2701 effect.update(0) 2702 self.assert_canvas_equals( 2703 canvas, 2704 " \n" + 2705 " \n" + 2706 " \n" + 2707 " \n" + 2708 " \n" + 2709 " \n" + 2710 " \n" + 2711 " First \n" + 2712 " Second\n" + 2713 " Third \n") 2714 2715 # Check it handles a selection as expected 2716 self.process_mouse(scene, [(39, 7, MouseEvent.LEFT_CLICK)]) 2717 self.assertEquals(len(scene.effects), 0) 2718 self.assertEquals(self.clicked, 1) 2719 2720 # Check clicking outside menu dismisses it - wrong X location. 2721 self.clicked = 0 2722 canvas.reset() 2723 popup = PopupMenu(canvas, [("A", lambda: click(1)), ("B", lambda: click(2))], 39, 9) 2724 popup.register_scene(scene) 2725 scene.add_effect(popup) 2726 scene.reset() 2727 self.process_mouse(scene, [(10, 9, MouseEvent.LEFT_CLICK)]) 2728 self.assertEquals(len(scene.effects), 0) 2729 self.assertEquals(self.clicked, 0) 2730 2731 # Check clicking outside menu dismisses it - wrong Y location. 2732 self.clicked = 0 2733 canvas.reset() 2734 popup = PopupMenu(canvas, [("A", lambda: click(1)), ("B", lambda: click(2))], 39, 9) 2735 popup.register_scene(scene) 2736 scene.add_effect(popup) 2737 scene.reset() 2738 self.process_mouse(scene, [(39, 1, MouseEvent.LEFT_CLICK)]) 2739 self.assertEquals(len(scene.effects), 0) 2740 self.assertEquals(self.clicked, 0) 2741 2742 # Check clicking outside menu dismisses it. 2743 self.clicked = 0 2744 canvas.reset() 2745 popup = PopupMenu(canvas, [("A", lambda: click(1)), ("B", lambda: click(2))], 39, 9) 2746 popup.register_scene(scene) 2747 scene.add_effect(popup) 2748 scene.reset() 2749 self.process_keys(popup, [Screen.KEY_ESCAPE]) 2750 self.assertEquals(len(scene.effects), 0) 2751 self.assertEquals(self.clicked, 0) 2752 2753 def test_find_min_start(self): 2754 """ 2755 Check _find_min_start works as expected. 2756 """ 2757 # Normal operation will find last <limit> characters. 2758 self.assertEqual(_find_min_start("ABCDEF", 3), 3) 2759 2760 # Allow extra space for cursor loses another 2761 self.assertEqual(_find_min_start("ABCDEF", 3, at_end=True), 4) 2762 2763 2764 def test_vertical_divider(self): 2765 """ 2766 Check VerticalDivider widget works as expected. 2767 """ 2768 # Now set up the Frame ready for testing 2769 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 2770 scene = Scene([], duration=-1) 2771 canvas = Canvas(screen, 10, 40, 0, 0) 2772 form = Frame(canvas, canvas.height, canvas.width) 2773 layout = Layout([5, 1, 26, 1, 5]) 2774 form.add_layout(layout) 2775 layout.add_widget(Label("A"), 1) 2776 layout.add_widget(VerticalDivider(), 1) 2777 layout.add_widget(Label("B"), 1) 2778 layout.add_widget(TextBox(5), 2) 2779 divider = VerticalDivider() 2780 layout.add_widget(divider, 3) 2781 layout.add_widget(Label("END"), 4) 2782 form.fix() 2783 form.register_scene(scene) 2784 scene.add_effect(form) 2785 scene.reset() 2786 2787 # Check that the frame is rendered correctly. 2788 for effect in scene.effects: 2789 effect.update(0) 2790 self.assert_canvas_equals( 2791 canvas, 2792 "+--------------------------------------+\n" + 2793 "| A |END |\n" + 2794 "| | | O\n" + 2795 "| | | |\n" + 2796 "| | | |\n" + 2797 "| B | |\n" + 2798 "| |\n" + 2799 "| |\n" + 2800 "| |\n" + 2801 "+--------------------------------------+\n") 2802 2803 # Check that a vertcial divider ignores unknown events. 2804 event = object() 2805 self.assertEqual(event, divider.process_event(event)) 2806 2807 # Check that it has no value. 2808 self.assertEqual(divider.value, None) 2809 2810 def test_value_defaults(self): 2811 """ 2812 Check Widgets can set default values from code. 2813 """ 2814 # Now set up the Frame ready for testing 2815 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 2816 scene = Scene([], duration=-1) 2817 canvas = Canvas(screen, 10, 40, 0, 0) 2818 form = Frame(canvas, canvas.height, canvas.width) 2819 layout = Layout([100]) 2820 form.add_layout(layout) 2821 2822 textbox = TextBox(2, label="TB") 2823 textbox.value = ["Hello"] 2824 text = Text(label="B") 2825 text.value = "World" 2826 listbox = ListBox(2, [("A", 1), ("B", 2), ("C", 3), ("D", 4)], label="LB") 2827 listbox.value = 3 2828 2829 layout.add_widget(textbox) 2830 layout.add_widget(text) 2831 layout.add_widget(listbox) 2832 form.fix() 2833 form.register_scene(scene) 2834 scene.add_effect(form) 2835 scene.reset() 2836 2837 # Check that the frame is rendered correctly. 2838 for effect in scene.effects: 2839 effect.update(0) 2840 self.assert_canvas_equals( 2841 canvas, 2842 "+--------------------------------------+\n" + 2843 "|TB Hello |\n" + 2844 "| O\n" + 2845 "|B World |\n" + 2846 "|LB C |\n" + 2847 "| D |\n" + 2848 "| |\n" + 2849 "| |\n" + 2850 "| |\n" + 2851 "+--------------------------------------+\n") 2852 2853 def test_frame_themes(self): 2854 """ 2855 Check we can set a colour theme for a Frame. 2856 """ 2857 # Now set up the Frame ready for testing 2858 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 2859 scene = Scene([], duration=-1) 2860 canvas = Canvas(screen, 10, 40, 0, 0) 2861 form = Frame(canvas, canvas.height, canvas.width) 2862 2863 # Check colour changes work... 2864 self.assertEqual( 2865 form.palette["background"], 2866 (Screen.COLOUR_WHITE, Screen.A_NORMAL, Screen.COLOUR_BLUE)) 2867 form.set_theme("monochrome") 2868 self.assertEqual( 2869 form.palette["background"], 2870 (Screen.COLOUR_WHITE, Screen.A_NORMAL, Screen.COLOUR_BLACK)) 2871 2872 # Check that a bad theme name has no effect. 2873 form.set_theme("blah - this doesn't exist") 2874 self.assertEqual( 2875 form.palette["background"], 2876 (Screen.COLOUR_WHITE, Screen.A_NORMAL, Screen.COLOUR_BLACK)) 2877 2878 def test_max_len(self): 2879 """ 2880 Check that the max_length setting works as expected. 2881 """ 2882 # Now set up the Frame ready for testing 2883 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 2884 scene = Scene([], duration=-1) 2885 canvas = Canvas(screen, 10, 40, 0, 0) 2886 form = Frame(canvas, canvas.height, canvas.width) 2887 layout = Layout([100]) 2888 form.add_layout(layout) 2889 2890 # Simple form with a limited length Text field. 2891 text = Text(label="Text", name="max_len_text", max_length=4) 2892 layout.add_widget(text) 2893 form.fix() 2894 form.register_scene(scene) 2895 scene.add_effect(form) 2896 scene.reset() 2897 2898 # Check it stops accepting text after hitting limit. 2899 self.process_keys(form, "123456") 2900 form.save() 2901 self.assertEqual(form.data["max_len_text"], "1234") 2902 2903 def test_clear_widgets(self): 2904 """ 2905 Check that clear_widgets works as expected. 2906 """ 2907 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 2908 scene = Scene([], duration=-1) 2909 canvas = Canvas(screen, 10, 40, 0, 0) 2910 form = Frame(canvas, canvas.height, canvas.width) 2911 layout = Layout([100], fill_frame=True) 2912 form.add_layout(layout) 2913 layout.add_widget(Text("Test")) 2914 form.fix() 2915 form.register_scene(scene) 2916 scene.add_effect(form) 2917 scene.reset() 2918 2919 # Check that the frame is rendered correctly. 2920 for effect in scene.effects: 2921 effect.update(0) 2922 self.assert_canvas_equals( 2923 canvas, 2924 "+--------------------------------------+\n" + 2925 "|Test |\n" + 2926 "| O\n" + 2927 "| |\n" + 2928 "| |\n" + 2929 "| |\n" + 2930 "| |\n" + 2931 "| |\n" + 2932 "| |\n" + 2933 "+--------------------------------------+\n") 2934 2935 # Check removing widgets clears Frame. 2936 layout.clear_widgets() 2937 form.fix() 2938 scene.reset() 2939 for effect in scene.effects: 2940 effect.update(1) 2941 self.assert_canvas_equals( 2942 canvas, 2943 "+--------------------------------------+\n" + 2944 "| |\n" + 2945 "| O\n" + 2946 "| |\n" + 2947 "| |\n" + 2948 "| |\n" + 2949 "| |\n" + 2950 "| |\n" + 2951 "| |\n" + 2952 "+--------------------------------------+\n") 2953 2954 # Check adding another widget now adds it back into the Frame. 2955 layout.add_widget(Text("Another One")) 2956 form.fix() 2957 scene.reset() 2958 for effect in scene.effects: 2959 effect.update(2) 2960 self.assert_canvas_equals( 2961 canvas, 2962 "+--------------------------------------+\n" + 2963 "|Another One |\n" + 2964 "| O\n" + 2965 "| |\n" + 2966 "| |\n" + 2967 "| |\n" + 2968 "| |\n" + 2969 "| |\n" + 2970 "| |\n" + 2971 "+--------------------------------------+\n") 2972 2973 2974 def test_inline_colours(self): 2975 """ 2976 Check inline colours work as expected. 2977 """ 2978 # Create a dummy screen. 2979 screen = MagicMock(spec=Screen, colours=8, unicode_aware=True) 2980 scene = MagicMock(spec=Scene) 2981 canvas = Canvas(screen, 10, 40, 0, 0) 2982 2983 # Create the form we want to test. 2984 form = Frame(canvas, canvas.height, canvas.width, has_border=False) 2985 layout = Layout([100], fill_frame=True) 2986 form.add_layout(layout) 2987 text_box = TextBox(3, as_string=True, parser=AsciimaticsParser()) 2988 layout.add_widget(text_box) 2989 listbox = ListBox(2, [("P", 1), ("${9,2}Q", 2), ("R", 3), ("S", 4)], parser=AsciimaticsParser()) 2990 layout.add_widget(listbox) 2991 mc_list = MultiColumnListBox( 2992 Widget.FILL_FRAME, 2993 [3, "100%"], 2994 [(["1", "\x1B[32m2"], 1)], 2995 titles=["A", "B"], 2996 parser=AnsiTerminalParser()) 2997 layout.add_widget(mc_list) 2998 form.fix() 2999 form.register_scene(scene) 3000 form.reset() 3001 3002 # Check that the Asciimatics colour parsing worked. 3003 text_box.value = "A${1}B" 3004 form.update(0) 3005 self.assertEqual(canvas.get_from(0, 0), (ord("A"), 7, 2, 4)) 3006 self.assertEqual(canvas.get_from(1, 0), (ord("B"), 1, 0, 4)) 3007 self.assertEqual(canvas.get_from(0, 3), (ord("P"), 3, 1, 4)) 3008 self.assertEqual(canvas.get_from(0, 4), (ord("Q"), 9, 2, 4)) 3009 3010 # Check that the Ansi terminal colour parsing worked. 3011 self.assertEqual(canvas.get_from(0, 5), (ord("A"), 7, 1, 4)) 3012 self.assertEqual(canvas.get_from(3, 5), (ord("B"), 7, 1, 4)) 3013 self.assertEqual(canvas.get_from(0, 6), (ord("1"), 3, 1, 4)) 3014 self.assertEqual(canvas.get_from(3, 6), (ord("2"), 2, 1, 4)) 3015 3016 def test_list_box_options(self): 3017 """ 3018 Check setting listbox options works as expected. 3019 """ 3020 # Create a dummy screen. 3021 screen = MagicMock(spec=Screen, colours=8, unicode_aware=True) 3022 scene = MagicMock(spec=Scene) 3023 canvas = Canvas(screen, 10, 40, 0, 0) 3024 3025 # Create the form we want to test. 3026 form = Frame(canvas, canvas.height, canvas.width, has_border=False) 3027 layout = Layout([100], fill_frame=True) 3028 form.add_layout(layout) 3029 listbox = ListBox(2, []) 3030 options = [("P", 1), ("Q", 2), ("R", 3), ("S", 4)] 3031 listbox.options = options 3032 layout.add_widget(listbox) 3033 3034 self.assertEquals(listbox.options, options) 3035 3036 def test_list_box_color_options(self): 3037 """ 3038 Check setting listbox options with inline colors works as expected. 3039 """ 3040 # Create a dummy screen. 3041 screen = MagicMock(spec=Screen, colours=8, unicode_aware=True) 3042 scene = MagicMock(spec=Scene) 3043 canvas = Canvas(screen, 10, 40, 0, 0) 3044 3045 # Create the form we want to test. 3046 form = Frame(canvas, canvas.height, canvas.width, has_border=False) 3047 layout = Layout([100], fill_frame=True) 3048 form.add_layout(layout) 3049 listbox = ListBox(2, [], parser=AsciimaticsParser()) 3050 options = [("P", 1), ("${9,2}Q", 2), ("R", 3), ("${10,3}S", 4)] 3051 listbox.options = options 3052 layout.add_widget(listbox) 3053 3054 color_options = listbox.options 3055 self.assertIsInstance(color_options[0][0], ColouredText) 3056 self.assertEquals(color_options[0][0].raw_text, options[0][0]) 3057 3058 self.assertIsInstance(color_options[1][0], ColouredText) 3059 self.assertEquals(color_options[1][0].raw_text, options[1][0]) 3060 3061 self.assertIsInstance(color_options[2][0], ColouredText) 3062 self.assertEquals(color_options[2][0].raw_text, options[2][0]) 3063 3064 self.assertIsInstance(color_options[3][0], ColouredText) 3065 self.assertEquals(color_options[3][0].raw_text, options[3][0]) 3066 3067 def test_readonly(self): 3068 """ 3069 Check readonly widgets work as expected. 3070 """ 3071 # Create a dummy screen. 3072 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 3073 scene = MagicMock(spec=Scene) 3074 canvas = Canvas(screen, 10, 40, 0, 0) 3075 3076 # Create the form we want to test. 3077 form = Frame(canvas, canvas.height, canvas.width, has_border=False) 3078 layout = Layout([100], fill_frame=True) 3079 form.add_layout(layout) 3080 text_box = TextBox(3, as_string=True, readonly=True) 3081 layout.add_widget(text_box) 3082 text = Text(readonly=True) 3083 layout.add_widget(text) 3084 form.fix() 3085 form.register_scene(scene) 3086 form.reset() 3087 3088 # Check that entering text has no effect. 3089 text_box.value = "Untouchable" 3090 text.value = "You can't touch this..." 3091 self.process_keys(form, ["1234\r\n", Screen.KEY_BACK, Screen.KEY_DELETE]) 3092 form.save() 3093 self.assertEqual(text_box.value, "Untouchable") 3094 self.process_keys(form, [Screen.KEY_TAB, "123", Screen.KEY_BACK, Screen.KEY_DELETE]) 3095 form.save() 3096 self.assertEqual(text.value, "You can't touch this...") 3097 3098 # Check property works. 3099 self.assertTrue(text.readonly) 3100 self.assertTrue(text_box.readonly) 3101 text.readonly = False 3102 text_box.readonly = False 3103 self.assertFalse(text.readonly) 3104 self.assertFalse(text_box.readonly) 3105 3106 # Check that entering text now has an effect. 3107 self.process_keys(form, [Screen.KEY_TAB, "1234\r\n", Screen.KEY_BACK, Screen.KEY_DELETE]) 3108 form.save() 3109 self.assertEqual(text_box.value, "Untouchable1234\n") 3110 self.process_keys(form, [Screen.KEY_TAB, "123", Screen.KEY_BACK, Screen.KEY_DELETE]) 3111 form.save() 3112 self.assertEqual(text.value, "You can't touch this...12") 3113 3114 def test_layout_disable(self): 3115 """ 3116 Check en/disable on layouts work as expected. 3117 """ 3118 # Create a dummy screen. 3119 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 3120 scene = MagicMock(spec=Scene) 3121 canvas = Canvas(screen, 10, 40, 0, 0) 3122 3123 # Create the form we want to test. 3124 form = Frame(canvas, canvas.height, canvas.width, has_border=False) 3125 layout = Layout([1,1], fill_frame=True) 3126 form.add_layout(layout) 3127 for col in range(2): 3128 layout.add_widget(Label("A field"), col) 3129 layout.add_widget(Text(), col) 3130 layout.add_widget(Button("OK", None), col) 3131 form.fix() 3132 form.register_scene(scene) 3133 form.reset() 3134 3135 def _assert_disabled(cols): 3136 for i, widgets in enumerate(layout._columns): 3137 for widget in widgets: 3138 self.assertEqual(widget.disabled, i in cols) 3139 3140 # Check focus moves away from disabled widgets. 3141 self.assertEqual(layout._live_col, 0) 3142 self.assertEqual(layout._live_widget, 1) 3143 layout.disable([0]) 3144 self.assertEqual(layout._live_col, 1) 3145 self.assertEqual(layout._live_widget, 1) 3146 layout.enable([1]) 3147 self.assertEqual(layout._live_col, 1) 3148 self.assertEqual(layout._live_widget, 1) 3149 3150 # Check that disabling only disables specified columns. 3151 for col in range(2): 3152 layout.disable([col]) 3153 _assert_disabled([col]) 3154 layout.enable([col]) 3155 _assert_disabled([]) 3156 3157 # Check that no parameter disables everything 3158 layout.disable() 3159 _assert_disabled(range(2)) 3160 layout.enable() 3161 _assert_disabled([]) 3162 3163 def test_widget_labels(self): 3164 """ 3165 Check various widgets and labels interact correctly. 3166 """ 3167 # Create a dummy screen. 3168 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 3169 scene = Scene([], duration=-1) 3170 canvas = Canvas(screen, 10, 40, 0, 0) 3171 3172 # Create the form we want to test. 3173 self.clicked = False 3174 3175 def click(): 3176 self.clicked = True 3177 3178 form = Frame(canvas, canvas.height, canvas.width, has_border=True) 3179 layout = Layout([80, 20], fill_frame=True) 3180 form.add_layout(layout) 3181 layout.add_widget(Button("one", None, label="Buttons")) 3182 layout.add_widget(Button("two", click)) 3183 listbox = ListBox(1, [("12345678901234567890", 1)], label="List") 3184 layout.add_widget(listbox) 3185 text = Text(label="Text") 3186 layout.add_widget(text) 3187 form.fix() 3188 form.register_scene(scene) 3189 scene.add_effect(form) 3190 form.reset() 3191 3192 # Check widgets are displayed correctly 3193 for effect in scene.effects: 3194 effect.update(0) 3195 self.assert_canvas_equals( 3196 canvas, 3197 "+--------------------------------------+\n" + 3198 "|Buttons < one > |\n" + 3199 "| < two > O\n" + 3200 "|List 12345678901234567890 |\n" + 3201 "|Text |\n" + 3202 "| |\n" + 3203 "| |\n" + 3204 "| |\n" + 3205 "| |\n" + 3206 "+--------------------------------------+\n") 3207 3208 # check specfic bug with Listbox overflow on selection 3209 self.process_mouse(form, [(20, 3, MouseEvent.LEFT_CLICK)]) 3210 for effect in scene.effects: 3211 effect.update(1) 3212 self.assertEqual(canvas.get_from(28, 3), (ord("0"), 7, 1, 6)) 3213 self.assertEqual(canvas.get_from(29, 3), (ord(" "), 7, 1, 6)) 3214 self.assertEqual(canvas.get_from(30, 3), (ord(" "), 7, 1, 6)) 3215 self.assertEqual(canvas.get_from(31, 3), (ord(" "), 7, 2, 4)) 3216 3217 # Check that clicking just outside the button has no effect. 3218 self.process_mouse(form, [(8, 2, MouseEvent.LEFT_CLICK)]) 3219 self.process_mouse(form, [(16, 2, MouseEvent.LEFT_CLICK)]) 3220 self.assertFalse(self.clicked) 3221 3222 # Check that clicking just inside the button works. 3223 self.process_mouse(form, [(9, 2, MouseEvent.LEFT_CLICK)]) 3224 self.assertTrue(self.clicked) 3225 self.clicked = False 3226 self.process_mouse(form, [(15, 2, MouseEvent.LEFT_CLICK)]) 3227 self.assertTrue(self.clicked) 3228 3229 3230 def test_button_name1(self): 3231 """ 3232 Check Button name can be set in the constructor. 3233 """ 3234 def _on_click(): 3235 pass 3236 3237 btn = Button("Run", _on_click, name="btn_run") 3238 3239 3240 def test_button_text(self): 3241 """ 3242 Check Button text can be set as an attribute. 3243 """ 3244 def _on_click(): 3245 pass 3246 3247 btn = Button("Before", _on_click) 3248 self.assertEqual(btn.text, "Before") 3249 btn.text = "After" 3250 self.assertEqual(btn.text, "After") 3251 3252 3253 def test_label_name1(self): 3254 """ 3255 Check Label name can be set in the constructor. 3256 """ 3257 lbl = Label("Winner", name="my_lbl") 3258 3259 3260 def test_label_name2(self): 3261 """ 3262 Check Label can be found by its containing Frame 3263 """ 3264 # Now set up the Frame ready for testing 3265 screen = MagicMock(spec=Screen, colours=8, unicode_aware=False) 3266 scene = Scene([], duration=-1) 3267 canvas = Canvas(screen, 10, 40, 0, 0) 3268 form = TestFrame6(canvas) 3269 scene.add_effect(form) 3270 scene.reset() 3271 3272 # Check that the Label is found by its name 3273 # by confirming the label's value 3274 l = form.find_widget("tf6_lbl") 3275 self.assertEqual(l.text, "TstFrm6Lbl") 3276 3277 3278if __name__ == '__main__': 3279 unittest.main() 3280