@@ -499,3 +499,118 @@ def test_write_usage_styled_prefix_keeps_options_on_one_line():
499499
500500 visible = strip_ansi (rendered )
501501 assert visible == "Usage: cli [OPTIONS]\n "
502+
503+
504+ @pytest .mark .parametrize (
505+ ("formatter_kwargs" , "current_indent" , "prog" , "args" , "prefix" , "expected" ),
506+ [
507+ # Issue #3360: the default prefix used to emit only
508+ # a blank line because ``wrap_text("", initial_indent=usage_prefix)``
509+ # returned ``""`` and discarded the prefix.
510+ pytest .param (
511+ {},
512+ 0 ,
513+ "Program" ,
514+ "" ,
515+ None ,
516+ "Usage: Program\n " ,
517+ id = "empty-args-default-prefix" ,
518+ ),
519+ # A caller-supplied prefix is preserved verbatim.
520+ pytest .param (
521+ {},
522+ 0 ,
523+ "Program" ,
524+ "" ,
525+ "Run: " ,
526+ "Run: Program\n " ,
527+ id = "empty-args-custom-prefix" ,
528+ ),
529+ # ``current_indent`` is preserved even with no args to render.
530+ pytest .param (
531+ {},
532+ 4 ,
533+ "Program" ,
534+ "" ,
535+ None ,
536+ "Usage: Program\n " ,
537+ id = "empty-args-indented" ,
538+ ),
539+ # Prog too long to share a line with args: the wrap branch must not
540+ # emit a second line.
541+ pytest .param (
542+ {"width" : 20 },
543+ 0 ,
544+ "VeryLongProgramName" ,
545+ "" ,
546+ None ,
547+ "Usage: VeryLongProgramName\n " ,
548+ id = "empty-args-long-prog" ,
549+ ),
550+ # With non-empty args, the separator space between prog and args is preserved.
551+ pytest .param (
552+ {},
553+ 0 ,
554+ "Program" ,
555+ "[OPTIONS]" ,
556+ None ,
557+ "Usage: Program [OPTIONS]\n " ,
558+ id = "with-args-default-prefix" ,
559+ ),
560+ ],
561+ )
562+ def test_help_formatter_write_usage (
563+ formatter_kwargs , current_indent , prog , args , prefix , expected
564+ ):
565+ """``HelpFormatter.write_usage`` renders a single usage line whose
566+ trailing separator tracks whether ``args`` is non-empty.
567+ """
568+ f = click .HelpFormatter (** formatter_kwargs )
569+ f .current_indent = current_indent
570+ if prefix is None :
571+ f .write_usage (prog , args )
572+ else :
573+ f .write_usage (prog , args , prefix = prefix )
574+ assert f .getvalue () == expected
575+
576+
577+ def test_help_formatter_write_usage_without_args_styled_prefix ():
578+ """A downstream-styled prefix is preserved when ``args`` is empty:
579+ the ANSI escape sequences survive, only the trailing separator is
580+ removed.
581+ """
582+ styled_prefix = "\x1b [38;2;38;139;210m\x1b [1mUsage:\x1b [0m "
583+ f = click .HelpFormatter ()
584+ f .write_usage ("cli" , prefix = styled_prefix )
585+ rendered = f .getvalue ()
586+ assert strip_ansi (rendered ) == "Usage: cli\n "
587+ assert "\x1b [" in rendered
588+
589+
590+ @pytest .mark .parametrize (
591+ ("command_kwargs" , "expected_usage_line" ),
592+ [
593+ # End-to-end regression for #3360: an empty ``options_metavar`` with
594+ # no parameters used to render a blank usage line.
595+ pytest .param (
596+ {"options_metavar" : "" },
597+ "Usage: cli" ,
598+ id = "empty-options-metavar-no-params" ,
599+ ),
600+ # End-to-end regression: ``options_metavar=None`` is the documented
601+ # way to suppress the ``[OPTIONS]`` slot entirely.
602+ pytest .param (
603+ {"options_metavar" : None },
604+ "Usage: cli" ,
605+ id = "none-options-metavar-no-params" ,
606+ ),
607+ ],
608+ )
609+ def test_command_write_usage_no_args (runner , command_kwargs , expected_usage_line ):
610+ """End-to-end: a command with no parameters and an empty or absent
611+ ``options_metavar`` renders a usage line with just the program name,
612+ no trailing space.
613+ """
614+ cli = click .Command ("cli" , ** command_kwargs )
615+ result = runner .invoke (cli , ["--help" ])
616+ assert result .output .splitlines ()[0 ] == expected_usage_line
0 commit comments