Coverage for tests / test_cli / test_help.py: 100%
67 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-11-09 00:15 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-11-09 00:15 +0000
1import subprocess 1abcdefg
2import sys 1abcdefg
4import typer 1abcdefg
5from typer.testing import CliRunner 1abcdefg
7runner = CliRunner() 1abcdefg
10def test_script_help(): 1abcdefg
11 result = subprocess.run( 1abcdefg
12 [
13 sys.executable,
14 "-m",
15 "coverage",
16 "run",
17 "-m",
18 "typer",
19 "tests/assets/cli/sample.py",
20 "--help",
21 ],
22 capture_output=True,
23 encoding="utf-8",
24 )
25 assert "run" in result.stdout 1abcdefg
28def test_not_python(): 1abcdefg
29 result = subprocess.run( 1abcdefg
30 [
31 sys.executable,
32 "-m",
33 "coverage",
34 "run",
35 "-m",
36 "typer",
37 "tests/assets/cli/not_python.txt",
38 "run",
39 ],
40 capture_output=True,
41 encoding="utf-8",
42 )
43 assert "Could not import as Python file" in result.stderr 1abcdefg
46def test_short_help() -> None: 1abcdefg
47 app = typer.Typer( 1abcdefg
48 rich_markup_mode=None,
49 context_settings={"max_content_width": 50},
50 )
52 @app.command(help=" \n\t ") 1abcdefg
53 def empty() -> None: 1abcdefg
54 pass # pragma: no cover
56 @app.command(help="\b first sentence.") 1abcdefg
57 def marker() -> None: 1abcdefg
58 pass # pragma: no cover
60 # Forcing truncation
61 @app.command(help=f"{'x' * 30} {'y' * 5} z trailing") 1abcdefg
62 def long() -> None: 1abcdefg
63 pass # pragma: no cover
65 result = runner.invoke(app, ["--help"], terminal_width=50) 1abcdefg
66 assert result.exit_code == 0 1abcdefg
67 assert "empty" in result.output 1abcdefg
68 assert "marker" in result.output 1abcdefg
69 assert "long" in result.output 1abcdefg
70 assert "first sentence." in result.output 1abcdefg
71 assert f"{'x' * 30}..." in result.output 1abcdefg
74def test_help_wrapping() -> None: 1abcdefg
75 app = typer.Typer( 1abcdefg
76 rich_markup_mode=None,
77 context_settings={"max_content_width": 50},
78 )
80 @app.command( 1abcdefg
81 help=(
82 "Wrapped paragraph has enough words to wrap in help output.\n"
83 "\n"
84 "\n"
85 "\b\n"
86 "RAW-LINE-ONE stays on one line even with many many many words.\n"
87 "RAW-LINE-TWO keeps original formatting.\n"
88 "\n"
89 "Final paragraph wraps normally as well."
90 )
91 )
92 def cmd() -> None: 1abcdefg
93 pass # pragma: no cover
95 result = runner.invoke(app, ["cmd", "--help"], terminal_width=50) 1abcdefg
96 assert result.exit_code == 0 1abcdefg
97 assert "Wrapped paragraph has enough words to wrap" in result.output 1abcdefg
98 assert ( 1abcdefg
99 "RAW-LINE-ONE stays on one line even with many many many words."
100 in result.output
101 )
102 assert "RAW-LINE-TWO keeps original formatting." in result.output 1abcdefg
103 assert "Final paragraph wraps normally as well." in result.output 1abcdefg
106def test_help_wrapping_long_name() -> None: 1abcdefg
107 app = typer.Typer(rich_markup_mode=None) 1abcdefg
109 @app.command() 1abcdefg
110 def cmd(value: str) -> None: 1abcdefg
111 pass # pragma: no cover
113 result = runner.invoke( 1abcdefg
114 app,
115 ["cmd", "--help"],
116 terminal_width=40,
117 prog_name="very-long-program-name-that-forces-wrap",
118 )
119 assert result.exit_code == 0 1abcdefg
121 output_lines = result.output.splitlines() 1abcdefg
122 usage_idx = output_lines.index("Usage: very-long-program-name-that-forces-wrap ") 1abcdefg
123 args_line = output_lines[usage_idx + 1] 1abcdefg
124 assert args_line.lstrip() == "[OPTIONS] {value}" 1abcdefg
125 assert args_line.startswith(" ") 1abcdefg
128def test_format_long_help_option() -> None: 1abcdefg
129 app = typer.Typer(rich_markup_mode=None) 1abcdefg
131 @app.command() 1abcdefg
132 def cmd( 1abcdefg
133 very_long: str = typer.Option(
134 ...,
135 "--this-is-a-very-very-very-long-option-name",
136 help="Description is rendered in the next line for long option labels.",
137 ),
138 ) -> None:
139 pass # pragma: no cover
141 result = runner.invoke(app, ["cmd", "--help"], terminal_width=80) 1abcdefg
142 assert result.exit_code == 0 1abcdefg
144 output_lines = result.output.splitlines() 1abcdefg
145 option_idx = next( 1abcdefg
146 i
147 for i, line in enumerate(output_lines)
148 if "--this-is-a-very-very-very-long-option-name" in line
149 )
150 assert "Description is rendered" not in output_lines[option_idx] 1abcdefg
151 first_desc_line = output_lines[option_idx + 1] 1abcdefg
152 assert first_desc_line.lstrip().startswith("Description is rendered") 1abcdefg
153 continuation_block = " ".join( 1abcdefg
154 line.strip() for line in output_lines[option_idx + 1 :] if line.startswith(" ")
155 )
156 assert ( 1abcdefg
157 "Description is rendered in the next line for long option labels."
158 in continuation_block
159 )
162def test_typer_run_usage() -> None: 1abcdefg
163 from typer.cli import app as typer_cli_app 1abcdefg
165 result = runner.invoke( 1abcdefg
166 typer_cli_app,
167 ["tests/assets/cli/minimum_main.py", "run"],
168 prog_name="typer",
169 )
171 assert result.exit_code == 2 1abcdefg
172 usage_line = result.output.splitlines()[0] 1abcdefg
173 assert usage_line.startswith("Usage: typer [PATH_OR_MODULE] run [OPTIONS] {name}") 1abcdefg
174 assert "Try 'typer [PATH_OR_MODULE] run --help' for help." in result.output 1abcdefg