Coverage for tests / test_terminal_control_characters.py: 100%
55 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 re 1abcdefg
2from typing import Annotated 1abcdefg
4import pytest 1abcdefg
5import typer 1abcdefg
6from typer._click.exceptions import NoSuchOption 1abcdefg
7from typer.testing import CliRunner 1abcdefg
9runner = CliRunner() 1abcdefg
12@pytest.fixture(params=["rich", None], ids=["rich", "plain"]) 1abcdefg
13def app(request: pytest.FixtureRequest) -> typer.Typer: 1abcdefg
14 app = typer.Typer(add_completion=False, rich_markup_mode=request.param) 1abcdefg
16 @app.command() 1abcdefg
17 def no_args() -> None: 1abcdefg
18 pass # pragma: no cover
20 @app.command() 1abcdefg
21 def read_file(value: typer.FileText) -> None: 1abcdefg
22 pass # pragma: no cover
24 @app.command() 1abcdefg
25 def parse( 1abcdefg
26 value: Annotated[int, typer.Argument(parser=lambda x: int(x, 0))],
27 ) -> None:
28 pass # pragma: no cover
30 return app 1abcdefg
33@pytest.mark.parametrize("color", [False, True]) 1abcdefg
34@pytest.mark.parametrize( 1abcdefg
35 ("command", "prefix", "message"),
36 [
37 ("no-args", "", "Got unexpected extra argument(s)"),
38 ("no-args", "--", "No such option"),
39 ("read-file", "", "Invalid value"),
40 ("parse", "", "Invalid value"),
41 ],
42 ids=["extra-argument", "unknown-option", "file-open", "custom-parser"],
43)
44@pytest.mark.parametrize( 1abcdefg
45 ("value", "escaped"),
46 [
47 ("\x1b[2J\x1b[H", r"\x1b[2J\x1b[H"),
48 ("\x1b]52;c;YWJj\x07", r"\x1b]52;c;YWJj\x07"),
49 ("\x1b]52;c;YWJj\x1b\\", "\\x1b]52;c;YWJj\\x1b\\"),
50 ("\x9b2J\x9d52;c;YWJj\x9c", r"\x9b2J\x9d52;c;YWJj\x9c"),
51 ("\r\n\t\b\x7f", r"\x0d\x0a\x09\x08\x7f"),
52 (
53 "text-\u00e9-\u65e5\u672c-\U0001f469\u200d\U0001f4bb-'quoted'-\\text",
54 "text-\u00e9-\u65e5\u672c-\U0001f469\u200d\U0001f4bb-'quoted'-\\text",
55 ),
56 ],
57 ids=["screen", "osc-bel", "osc-st", "c1", "whitespace-controls", "unicode"],
58)
59def test_error_values( 1abcdefg
60 app: typer.Typer,
61 command: str,
62 prefix: str,
63 message: str,
64 value: str,
65 escaped: str,
66 color: bool,
67 monkeypatch: pytest.MonkeyPatch,
68) -> None:
69 from typer import rich_utils 1abcdefg
71 monkeypatch.delenv("NO_COLOR", raising=False) 1abcdefg
72 monkeypatch.setenv("TERM", "xterm-256color") 1abcdefg
73 monkeypatch.setattr(rich_utils, "FORCE_TERMINAL", color) 1abcdefg
74 monkeypatch.setattr(rich_utils, "MAX_WIDTH", 3000) 1abcdefg
75 result = runner.invoke(app, [command, prefix + "input-" + value], color=color) 1abcdefg
77 assert result.exit_code == 2 1abcdefg
78 # Remove only generated color styles so other terminal controls cannot hide.
79 output = re.sub(r"\x1b\[[0-9;]*m", "", result.stderr) 1abcdefg
80 assert message in output 1abcdefg
81 assert prefix + "input-" + escaped in output 1abcdefg
82 assert re.search(r"[\x00-\x09\x0b-\x1f\x7f-\x9f]", output) is None 1abcdefg
85def test_unknown_option_preserves_original_value() -> None: 1abcdefg
86 value = "--input-\x00\x1f\x7f\x80\x9f" 1abcdefg
87 error = NoSuchOption(value) 1abcdefg
89 assert error.option_name == value 1abcdefg
90 assert error.format_message() == r"No such option: --input-\x00\x1f\x7f\x80\x9f" 1abcdefg
93def test_argument_preserves_original_value() -> None: 1abcdefg
94 app = typer.Typer(add_completion=False) 1abcdefg
95 value = "input-\x1b[2J\x1b]52;c;YWJj\x07" 1abcdefg
97 @app.command() 1abcdefg
98 def main(value: str) -> None: 1abcdefg
99 typer.echo(repr(value)) 1abcdefg
101 result = runner.invoke(app, [value]) 1abcdefg
103 assert result.exit_code == 0 1abcdefg
104 assert result.stdout == repr(value) + "\n" 1abcdefg
107def test_echo_preserves_terminal_sequences() -> None: 1abcdefg
108 app = typer.Typer(add_completion=False) 1abcdefg
109 value = "\x1b[2J\x1b]52;c;YWJj\x07" 1abcdefg
111 @app.command() 1abcdefg
112 def main() -> None: 1abcdefg
113 typer.echo(value, nl=False) 1abcdefg
115 result = runner.invoke(app, color=True) 1abcdefg
117 assert result.exit_code == 0 1abcdefg
118 assert result.stdout_bytes == value.encode() 1abcdefg