|
4 | 4 | from textwrap import dedent |
5 | 5 | from unittest import TestCase |
6 | 6 | from unittest.mock import MagicMock |
7 | | -from test.support import force_colorized_test_class, force_not_colorized_test_class |
| 7 | +from test.support import force_colorized, force_colorized_test_class |
| 8 | +from test.support import force_not_colorized_test_class |
8 | 9 |
|
9 | 10 | from .support import handle_all_events, handle_events_narrow_console |
10 | 11 | from .support import ScreenEqualMixin, code_to_events |
@@ -72,6 +73,31 @@ def test_calc_screen_wrap_wide_characters(self): |
72 | 73 | reader, _ = handle_events_narrow_console(events) |
73 | 74 | self.assert_screen_equal(reader, f"{8*"a"}\\\n樂") |
74 | 75 |
|
| 76 | + def test_messages_wrap_on_tight_screens(self): |
| 77 | + console = prepare_console((), width=3) |
| 78 | + reader = prepare_reader(console) |
| 79 | + |
| 80 | + reader.msg = "\x1b[31mabcdef\x1b[0m" |
| 81 | + lines = reader._render_message_lines() |
| 82 | + self.assertEqual( |
| 83 | + [line.text for line in lines], |
| 84 | + ["\x1b[31mabc\x1b[0m", "\x1b[31mdef\x1b[0m"], |
| 85 | + ) |
| 86 | + |
| 87 | + reader.msg = "ab樂c" |
| 88 | + console.width = 4 |
| 89 | + lines = reader._render_message_lines() |
| 90 | + self.assertEqual([line.text for line in lines], ["ab樂", "c"]) |
| 91 | + self.assertEqual([line.width for line in lines], [4, 1]) |
| 92 | + |
| 93 | + reader.msg = "ab\N{COMBINING ACUTE ACCENT}cd" |
| 94 | + console.width = 3 |
| 95 | + lines = reader._render_message_lines() |
| 96 | + self.assertEqual( |
| 97 | + [line.text for line in lines], |
| 98 | + ["ab\N{COMBINING ACUTE ACCENT}c", "d"], |
| 99 | + ) |
| 100 | + |
75 | 101 | def test_calc_screen_wrap_three_lines(self): |
76 | 102 | events = code_to_events(20 * "a") |
77 | 103 | reader, _ = handle_events_narrow_console(events) |
@@ -431,6 +457,109 @@ def test_setpos_from_xy_for_non_printing_char(self): |
431 | 457 | reader.setpos_from_xy(8, 0) |
432 | 458 | self.assertEqual(reader.pos, 7) |
433 | 459 |
|
| 460 | + |
| 461 | +@force_not_colorized_test_class |
| 462 | +class TestErrorMessages(TestCase): |
| 463 | + @force_colorized |
| 464 | + def test_colorized_error_message(self): |
| 465 | + console = prepare_console(code_to_events("\x19")) |
| 466 | + reader = prepare_reader(console) |
| 467 | + |
| 468 | + reader.handle1() |
| 469 | + |
| 470 | + theme = default_theme.syntax |
| 471 | + self.assertEqual( |
| 472 | + reader.msg, |
| 473 | + f"{theme.error}! {theme.reset}nothing to yank", |
| 474 | + ) |
| 475 | + |
| 476 | + def _test_error_message( |
| 477 | + self, |
| 478 | + keys: str, |
| 479 | + expected: str, |
| 480 | + *, |
| 481 | + command: str | None = None, |
| 482 | + binding: str | None = None, |
| 483 | + **reader_kwargs, |
| 484 | + ): |
| 485 | + console = prepare_console(code_to_events(keys)) |
| 486 | + reader = prepare_reader(console, **reader_kwargs) |
| 487 | + if command is not None: |
| 488 | + reader.bind(binding or keys, command) |
| 489 | + |
| 490 | + while not reader.msg: |
| 491 | + reader.handle1() |
| 492 | + |
| 493 | + self.assertEqual(reader.msg, f"! {expected}") |
| 494 | + self.assertEqual(reader.screen[-1], f"! {expected}") |
| 495 | + console.beep.assert_called_once() |
| 496 | + |
| 497 | + def test_invalid_key_message(self): |
| 498 | + self._test_error_message("\x1bo", "no command is bound to this key") |
| 499 | + |
| 500 | + def test_unimplemented_command_message(self): |
| 501 | + self._test_error_message( |
| 502 | + "x", |
| 503 | + "command 'missing-command' is not implemented", |
| 504 | + command="missing-command", |
| 505 | + ) |
| 506 | + |
| 507 | + def test_nothing_to_yank_message(self): |
| 508 | + self._test_error_message("\x19", "nothing to yank") |
| 509 | + |
| 510 | + def test_yank_pop_with_empty_kill_ring_message(self): |
| 511 | + self._test_error_message("\x1by", "nothing to yank") |
| 512 | + |
| 513 | + def test_yank_pop_after_other_command_message(self): |
| 514 | + self._test_error_message( |
| 515 | + "x\x01\x0b\x1by", |
| 516 | + "previous command was not a yank", |
| 517 | + ) |
| 518 | + |
| 519 | + def test_start_of_history_message(self): |
| 520 | + self._test_error_message("\x10", "start of history list") |
| 521 | + |
| 522 | + def test_end_of_history_message(self): |
| 523 | + self._test_error_message("\x0e", "end of history list") |
| 524 | + |
| 525 | + def test_yank_arg_at_beginning_of_history_message(self): |
| 526 | + self._test_error_message( |
| 527 | + "\x1b.\x1b.\x1b.", |
| 528 | + "beginning of history list", |
| 529 | + history=["one"], |
| 530 | + historyi=1, |
| 531 | + ) |
| 532 | + |
| 533 | + def test_yank_arg_with_no_argument_message(self): |
| 534 | + self._test_error_message( |
| 535 | + "\x1b.", |
| 536 | + "no such arg", |
| 537 | + history=[""], |
| 538 | + historyi=1, |
| 539 | + ) |
| 540 | + |
| 541 | + def test_empty_incremental_search_backspace_message(self): |
| 542 | + self._test_error_message( |
| 543 | + "x", |
| 544 | + "nothing to rubout", |
| 545 | + command="isearch-backspace", |
| 546 | + ) |
| 547 | + |
| 548 | + def test_history_search_not_found_message(self): |
| 549 | + self._test_error_message( |
| 550 | + "missingx", |
| 551 | + "not found", |
| 552 | + command="history-search-backward", |
| 553 | + binding="x", |
| 554 | + ) |
| 555 | + |
| 556 | + def test_incremental_search_not_found_message(self): |
| 557 | + self._test_error_message("\x12x", "not found") |
| 558 | + |
| 559 | + def test_no_completion_matches_message(self): |
| 560 | + self._test_error_message("does_not_exist\t", "no matches") |
| 561 | + |
| 562 | + |
434 | 563 | @force_colorized_test_class |
435 | 564 | class TestReaderInColor(ScreenEqualMixin, TestCase): |
436 | 565 | def test_syntax_highlighting_basic(self): |
|
0 commit comments