Skip to content

Commit abb2c5f

Browse files
committed
Added unit tests for CompletionItems.
1 parent 88e517c commit abb2c5f

File tree

3 files changed

+54
-46
lines changed

3 files changed

+54
-46
lines changed

tests/conftest.py

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,9 @@
1010
import pytest
1111

1212
import cmd2
13-
from cmd2.rl_utils import (
14-
readline,
15-
)
16-
from cmd2.utils import (
17-
StdSim,
18-
)
13+
from cmd2 import rich_utils as ru
14+
from cmd2.rl_utils import readline
15+
from cmd2.utils import StdSim
1916

2017

2118
def verify_help_text(cmd2_app: cmd2.Cmd, help_output: str | list[str], verbose_strings: list[str] | None = None) -> None:
@@ -88,6 +85,25 @@ def base_app():
8885
return cmd2.Cmd(include_py=True, include_ipy=True)
8986

9087

88+
def with_ansi_style(style):
89+
def arg_decorator(func):
90+
import functools
91+
92+
@functools.wraps(func)
93+
def cmd_wrapper(*args, **kwargs):
94+
old = ru.ALLOW_STYLE
95+
ru.ALLOW_STYLE = style
96+
try:
97+
retval = func(*args, **kwargs)
98+
finally:
99+
ru.ALLOW_STYLE = old
100+
return retval
101+
102+
return cmd_wrapper
103+
104+
return arg_decorator
105+
106+
91107
# These are odd file names for testing quoting of them
92108
odd_file_names = ['nothingweird', 'has spaces', '"is_double_quoted"', "'is_single_quoted'"]
93109

tests/test_argparse_completer.py

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import pytest
88

99
import cmd2
10+
import cmd2.string_utils as su
1011
from cmd2 import (
1112
Cmd2ArgumentParser,
1213
CompletionError,
@@ -15,11 +16,13 @@
1516
argparse_custom,
1617
with_argparser,
1718
)
19+
from cmd2 import rich_utils as ru
1820

1921
from .conftest import (
2022
complete_tester,
2123
normalize,
2224
run_cmd,
25+
with_ansi_style,
2326
)
2427

2528
# Data and functions for testing standalone choice_provider and completer
@@ -109,12 +112,18 @@ def do_pos_and_flag(self, args: argparse.Namespace) -> None:
109112
static_choices_list = ('static', 'choices', 'stop', 'here')
110113
choices_from_provider = ('choices', 'provider', 'probably', 'improved')
111114
completion_item_choices = (
112-
CompletionItem('choice_1', ['A description']),
113-
CompletionItem('choice_2', ['Another description']),
115+
CompletionItem('choice_1', ['Description 1']),
116+
# Make this the longest description so we can test display width.
117+
CompletionItem('choice_2', [su.stylize("String with style", style=cmd2.Color.BLUE)]),
118+
CompletionItem('choice_3', [su.stylize("Text with style", style=cmd2.Color.RED)]),
114119
)
115120

116121
# This tests that CompletionItems created with numerical values are sorted as numbers.
117-
num_completion_items = (CompletionItem(5, ["Five"]), CompletionItem(1.5, ["One.Five"]), CompletionItem(2, ["Five"]))
122+
num_completion_items = (
123+
CompletionItem(5, ["Five"]),
124+
CompletionItem(1.5, ["One.Five"]),
125+
CompletionItem(2, ["Five"]),
126+
)
118127

119128
def choices_provider(self) -> tuple[str]:
120129
"""Method that provides choices"""
@@ -704,6 +713,7 @@ def test_autocomp_blank_token(ac_app) -> None:
704713
assert sorted(completions) == sorted(ArgparseCompleterTester.completions_for_pos_2)
705714

706715

716+
@with_ansi_style(ru.AllowStyle.ALWAYS)
707717
def test_completion_items(ac_app) -> None:
708718
# First test CompletionItems created from strings
709719
text = ''
@@ -716,16 +726,20 @@ def test_completion_items(ac_app) -> None:
716726
assert len(ac_app.completion_matches) == len(ac_app.completion_item_choices)
717727
assert len(ac_app.display_matches) == len(ac_app.completion_item_choices)
718728

719-
# Look for both the value and description in the hint table
720-
line_found = False
721-
for line in ac_app.formatted_completions.splitlines():
722-
# Since the CompletionItems were created from strings, the left-most column is left-aligned.
723-
# Therefore choice_1 will begin the line (with 1 space for padding).
724-
if line.startswith(' choice_1') and 'A description' in line:
725-
line_found = True
726-
break
729+
lines = ac_app.formatted_completions.splitlines()
730+
731+
# Since the CompletionItems were created from strings, the left-most column is left-aligned.
732+
# Therefore choice_1 will begin the line (with 1 space for padding).
733+
assert lines[2].startswith(' choice_1')
734+
assert lines[2].strip().endswith('Description 1')
735+
736+
# Verify that the styled string was converted to a Rich Text object so that
737+
# Rich could correctly calculate its display width. Since it was the longest
738+
# description in the table, we should only see one space of padding after it.
739+
assert lines[3].endswith("\x1b[34mString with style\x1b[0m ")
727740

728-
assert line_found
741+
# Verify that the styled Rich Text also rendered.
742+
assert lines[4].endswith("\x1b[31mText with style\x1b[0m ")
729743

730744
# Now test CompletionItems created from numbers
731745
text = ''
@@ -738,16 +752,12 @@ def test_completion_items(ac_app) -> None:
738752
assert len(ac_app.completion_matches) == len(ac_app.num_completion_items)
739753
assert len(ac_app.display_matches) == len(ac_app.num_completion_items)
740754

741-
# Look for both the value and description in the hint table
742-
line_found = False
743-
for line in ac_app.formatted_completions.splitlines():
744-
# Since the CompletionItems were created from numbers, the left-most column is right-aligned.
745-
# Therefore 1.5 will be right-aligned.
746-
if line.startswith(" 1.5") and "One.Five" in line:
747-
line_found = True
748-
break
755+
lines = ac_app.formatted_completions.splitlines()
749756

750-
assert line_found
757+
# Since the CompletionItems were created from numbers, the left-most column is right-aligned.
758+
# Therefore 1.5 will be right-aligned.
759+
assert lines[2].startswith(" 1.5")
760+
assert lines[2].strip().endswith('One.Five')
751761

752762

753763
@pytest.mark.parametrize(

tests/test_cmd2.py

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -41,28 +41,10 @@
4141
odd_file_names,
4242
run_cmd,
4343
verify_help_text,
44+
with_ansi_style,
4445
)
4546

4647

47-
def with_ansi_style(style):
48-
def arg_decorator(func):
49-
import functools
50-
51-
@functools.wraps(func)
52-
def cmd_wrapper(*args, **kwargs):
53-
old = ru.ALLOW_STYLE
54-
ru.ALLOW_STYLE = style
55-
try:
56-
retval = func(*args, **kwargs)
57-
finally:
58-
ru.ALLOW_STYLE = old
59-
return retval
60-
61-
return cmd_wrapper
62-
63-
return arg_decorator
64-
65-
6648
def create_outsim_app():
6749
c = cmd2.Cmd()
6850
c.stdout = utils.StdSim(c.stdout)

0 commit comments

Comments
 (0)