import datetime import io import os import sys import tempfile from typing import Optional import pytest from rich import errors from rich.color import ColorSystem from rich.console import ( CaptureError, Console, ConsoleDimensions, ConsoleOptions, ScreenUpdate, group, ) from rich.control import Control from rich.measure import measure_renderables from rich.pager import SystemPager from rich.panel import Panel from rich.region import Region from rich.segment import Segment from rich.status import Status from rich.style import Style from rich.text import Text def test_dumb_terminal(): console = Console(force_terminal=True, _environ={}) assert console.color_system is not None console = Console(force_terminal=True, _environ={"TERM": "dumb"}) assert console.color_system is None width, height = console.size assert width == 80 assert height == 25 def test_soft_wrap(): console = Console(file=io.StringIO(), width=20, soft_wrap=True) console.print("foo " * 10) assert console.file.getvalue() == "foo " * 20 @pytest.mark.skipif(sys.platform == "win32", reason="does not run on windows") def test_16color_terminal(): console = Console( force_terminal=True, _environ={"TERM": "xterm-16color"}, legacy_windows=False ) assert console.color_system == "standard" @pytest.mark.skipif(sys.platform == "win32", reason="does not run on windows") def test_truecolor_terminal(): console = Console( force_terminal=True, legacy_windows=False, _environ={"COLORTERM": "truecolor", "TERM": "xterm-16color"}, ) assert console.color_system == "truecolor" def test_console_options_update(): options = ConsoleOptions( ConsoleDimensions(80, 25), max_height=25, legacy_windows=False, min_width=10, max_width=20, is_terminal=False, encoding="utf-8", ) options1 = options.update(width=15) assert options1.min_width == 15 and options1.max_width == 15 options2 = options.update(min_width=5, max_width=15, justify="right") assert ( options2.min_width == 5 and options2.max_width == 15 and options2.justify == "right" ) options_copy = options.update() assert options_copy == options and options_copy is not options def test_console_options_update_height(): options = ConsoleOptions( ConsoleDimensions(80, 25), max_height=25, legacy_windows=False, min_width=10, max_width=20, is_terminal=False, encoding="utf-8", ) assert options.height is None render_options = options.update_height(12) assert options.height is None assert render_options.height == 12 assert render_options.max_height == 12 def test_init(): console = Console(color_system=None) assert console._color_system == None console = Console(color_system="standard") assert console._color_system == ColorSystem.STANDARD console = Console(color_system="auto") def test_size(): console = Console() w, h = console.size assert console.width == w console = Console(width=99, height=101, legacy_windows=False) w, h = console.size assert w == 99 and h == 101 def test_repr(): console = Console() assert isinstance(repr(console), str) assert isinstance(str(console), str) def test_print(): console = Console(file=io.StringIO(), color_system="truecolor") console.print("foo") assert console.file.getvalue() == "foo\n" def test_print_multiple(): console = Console(file=io.StringIO(), color_system="truecolor") console.print("foo", "bar") assert console.file.getvalue() == "foo bar\n" def test_print_text(): console = Console(file=io.StringIO(), color_system="truecolor") console.print(Text("foo", style="bold")) assert console.file.getvalue() == "\x1B[1mfoo\x1B[0m\n" def test_print_text_multiple(): console = Console(file=io.StringIO(), color_system="truecolor") console.print(Text("foo", style="bold"), Text("bar"), "baz") assert console.file.getvalue() == "\x1B[1mfoo\x1B[0m bar baz\n" def test_print_json(): console = Console(file=io.StringIO(), color_system="truecolor") console.print_json('[false, true, null, "foo"]', indent=4) result = console.file.getvalue() print(repr(result)) expected = '\x1b[1m[\x1b[0m\n \x1b[3;91mfalse\x1b[0m,\n \x1b[3;92mtrue\x1b[0m,\n \x1b[3;35mnull\x1b[0m,\n \x1b[32m"foo"\x1b[0m\n\x1b[1m]\x1b[0m\n' assert result == expected def test_print_json_error(): console = Console(file=io.StringIO(), color_system="truecolor") with pytest.raises(TypeError): console.print_json(["foo"], indent=4) def test_print_json_data(): console = Console(file=io.StringIO(), color_system="truecolor") console.print_json(data=[False, True, None, "foo"], indent=4) result = console.file.getvalue() print(repr(result)) expected = '\x1b[1m[\x1b[0m\n \x1b[3;91mfalse\x1b[0m,\n \x1b[3;92mtrue\x1b[0m,\n \x1b[3;35mnull\x1b[0m,\n \x1b[32m"foo"\x1b[0m\n\x1b[1m]\x1b[0m\n' assert result == expected def test_print_json_ensure_ascii(): console = Console(file=io.StringIO(), color_system="truecolor") console.print_json(data={"foo": "💩"}, ensure_ascii=False) result = console.file.getvalue() print(repr(result)) expected = '\x1b[1m{\x1b[0m\n \x1b[1;34m"foo"\x1b[0m: \x1b[32m"💩"\x1b[0m\n\x1b[1m}\x1b[0m\n' assert result == expected def test_print_json_indent_none(): console = Console(file=io.StringIO(), color_system="truecolor") data = {"name": "apple", "count": 1} console.print_json(data=data, indent=None) result = console.file.getvalue() expected = '\x1b[1m{\x1b[0m\x1b[1;34m"name"\x1b[0m: \x1b[32m"apple"\x1b[0m, \x1b[1;34m"count"\x1b[0m: \x1b[1;36m1\x1b[0m\x1b[1m}\x1b[0m\n' assert result == expected def test_log(): console = Console( file=io.StringIO(), width=80, color_system="truecolor", log_time_format="TIME", log_path=False, _environ={}, ) console.log("foo", style="red") expected = "\x1b[2;36mTIME\x1b[0m\x1b[2;36m \x1b[0m\x1b[31mfoo \x1b[0m\n" result = console.file.getvalue() print(repr(result)) assert result == expected def test_log_milliseconds(): def time_formatter(timestamp: datetime) -> Text: return Text("TIME") console = Console( file=io.StringIO(), width=40, log_time_format=time_formatter, log_path=False ) console.log("foo") result = console.file.getvalue() assert result == "TIME foo \n" def test_print_empty(): console = Console(file=io.StringIO(), color_system="truecolor") console.print() assert console.file.getvalue() == "\n" def test_markup_highlight(): console = Console(file=io.StringIO(), color_system="truecolor") console.print("'[bold]foo[/bold]'") assert ( console.file.getvalue() == "\x1b[32m'\x1b[0m\x1b[1;32mfoo\x1b[0m\x1b[32m'\x1b[0m\n" ) def test_print_style(): console = Console(file=io.StringIO(), color_system="truecolor") console.print("foo", style="bold") assert console.file.getvalue() == "\x1b[1mfoo\x1b[0m\n" def test_show_cursor(): console = Console( file=io.StringIO(), force_terminal=True, legacy_windows=False, _environ={} ) console.show_cursor(False) console.print("foo") console.show_cursor(True) assert console.file.getvalue() == "\x1b[?25lfoo\n\x1b[?25h" def test_clear(): console = Console(file=io.StringIO(), force_terminal=True, _environ={}) console.clear() console.clear(home=False) assert console.file.getvalue() == "\033[2J\033[H" + "\033[2J" def test_clear_no_terminal(): console = Console(file=io.StringIO()) console.clear() console.clear(home=False) assert console.file.getvalue() == "" def test_get_style(): console = Console() console.get_style("repr.brace") == Style(bold=True) def test_get_style_default(): console = Console() console.get_style("foobar", default="red") == Style(color="red") def test_get_style_error(): console = Console() with pytest.raises(errors.MissingStyle): console.get_style("nosuchstyle") with pytest.raises(errors.MissingStyle): console.get_style("foo bar") def test_render_error(): console = Console() with pytest.raises(errors.NotRenderableError): list(console.render([], console.options)) def test_control(): console = Console(file=io.StringIO(), force_terminal=True, _environ={}) console.control(Control.clear()) console.print("BAR") assert console.file.getvalue() == "\x1b[2JBAR\n" def test_capture(): console = Console() with console.capture() as capture: with pytest.raises(CaptureError): capture.get() console.print("Hello") assert capture.get() == "Hello\n" def test_input(monkeypatch, capsys): def fake_input(prompt=""): console.file.write(prompt) return "bar" monkeypatch.setattr("builtins.input", fake_input) console = Console() user_input = console.input(prompt="foo:") assert capsys.readouterr().out == "foo:" assert user_input == "bar" def test_input_password(monkeypatch, capsys): def fake_input(prompt, stream=None): console.file.write(prompt) return "bar" import rich.console monkeypatch.setattr(rich.console, "getpass", fake_input) console = Console() user_input = console.input(prompt="foo:", password=True) assert capsys.readouterr().out == "foo:" assert user_input == "bar" def test_status(): console = Console(file=io.StringIO(), force_terminal=True, width=20) status = console.status("foo") assert isinstance(status, Status) def test_justify_none(): console = Console(file=io.StringIO(), force_terminal=True, width=20) console.print("FOO", justify=None) assert console.file.getvalue() == "FOO\n" def test_justify_left(): console = Console(file=io.StringIO(), force_terminal=True, width=20, _environ={}) console.print("FOO", justify="left") assert console.file.getvalue() == "FOO \n" def test_justify_center(): console = Console(file=io.StringIO(), force_terminal=True, width=20, _environ={}) console.print("FOO", justify="center") assert console.file.getvalue() == " FOO \n" def test_justify_right(): console = Console(file=io.StringIO(), force_terminal=True, width=20, _environ={}) console.print("FOO", justify="right") assert console.file.getvalue() == " FOO\n" def test_justify_renderable_none(): console = Console( file=io.StringIO(), force_terminal=True, width=20, legacy_windows=False, _environ={}, ) console.print(Panel("FOO", expand=False, padding=0), justify=None) assert console.file.getvalue() == "╭───╮\n│FOO│\n╰───╯\n" def test_justify_renderable_left(): console = Console( file=io.StringIO(), force_terminal=True, width=10, legacy_windows=False, _environ={}, ) console.print(Panel("FOO", expand=False, padding=0), justify="left") assert console.file.getvalue() == "╭───╮ \n│FOO│ \n╰───╯ \n" def test_justify_renderable_center(): console = Console( file=io.StringIO(), force_terminal=True, width=10, legacy_windows=False, _environ={}, ) console.print(Panel("FOO", expand=False, padding=0), justify="center") assert console.file.getvalue() == " ╭───╮ \n │FOO│ \n ╰───╯ \n" def test_justify_renderable_right(): console = Console( file=io.StringIO(), force_terminal=True, width=20, legacy_windows=False, _environ={}, ) console.print(Panel("FOO", expand=False, padding=0), justify="right") assert ( console.file.getvalue() == " ╭───╮\n │FOO│\n ╰───╯\n" ) class BrokenRenderable: def __rich_console__(self, console, options): pass def test_render_broken_renderable(): console = Console() broken = BrokenRenderable() with pytest.raises(errors.NotRenderableError): list(console.render(broken, console.options)) def test_export_text(): console = Console(record=True, width=100) console.print("[b]foo") text = console.export_text() expected = "foo\n" assert text == expected def test_export_html(): console = Console(record=True, width=100) console.print("[b]foo