77import  pytest 
88
99import  cmd2 
10+ import  cmd2 .string_utils  as  su 
1011from  cmd2  import  (
1112    Cmd2ArgumentParser ,
1213    CompletionError ,
1516    argparse_custom ,
1617    with_argparser ,
1718)
19+ from  cmd2  import  rich_utils  as  ru 
1820
1921from  .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 ) 
707717def  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 ( 
0 commit comments