summaryrefslogtreecommitdiffstats
path: root/tests/test_text.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_text.py')
-rw-r--r--tests/test_text.py677
1 files changed, 677 insertions, 0 deletions
diff --git a/tests/test_text.py b/tests/test_text.py
new file mode 100644
index 0000000..3bf6888
--- /dev/null
+++ b/tests/test_text.py
@@ -0,0 +1,677 @@
+from io import StringIO
+import pytest
+
+from rich.console import Console
+from rich.text import Span, Text
+from rich.measure import Measurement
+from rich.style import Style
+
+
+def test_span():
+ span = Span(1, 10, "foo")
+ repr(span)
+ assert bool(span)
+ assert not Span(10, 10, "foo")
+
+
+def test_span_split():
+ assert Span(5, 10, "foo").split(2) == (Span(5, 10, "foo"), None)
+ assert Span(5, 10, "foo").split(15) == (Span(5, 10, "foo"), None)
+ assert Span(0, 10, "foo").split(5) == (Span(0, 5, "foo"), Span(5, 10, "foo"))
+
+
+def test_span_move():
+ assert Span(5, 10, "foo").move(2) == Span(7, 12, "foo")
+
+
+def test_span_right_crop():
+ assert Span(5, 10, "foo").right_crop(15) == Span(5, 10, "foo")
+ assert Span(5, 10, "foo").right_crop(7) == Span(5, 7, "foo")
+
+
+def test_len():
+ assert len(Text("foo")) == 3
+
+
+def test_cell_len():
+ assert Text("foo").cell_len == 3
+ assert Text("😀").cell_len == 2
+
+
+def test_bool():
+ assert Text("foo")
+ assert not Text("")
+
+
+def test_str():
+ assert str(Text("foo")) == "foo"
+
+
+def test_repr():
+ assert isinstance(repr(Text("foo")), str)
+
+
+def test_add():
+ text = Text("foo") + Text("bar")
+ assert str(text) == "foobar"
+ assert Text("foo").__add__(1) == NotImplemented
+
+
+def test_eq():
+ assert Text("foo") == Text("foo")
+ assert Text("foo") != Text("bar")
+ assert Text("foo").__eq__(1) == NotImplemented
+
+
+def test_contain():
+ test = Text("foobar")
+ assert "foo" in test
+ assert "foo " not in test
+ assert Text("bar") in test
+ assert None not in test
+
+
+def test_plain_property():
+ text = Text("foo")
+ text.append("bar")
+ text.append("baz")
+ assert text.plain == "foobarbaz"
+
+
+def test_plain_property_setter():
+ test = Text("foo")
+ test.plain = "bar"
+ assert str(test) == "bar"
+ test = Text()
+ test.append("Hello, World", "bold")
+ test.plain = "Hello"
+ assert str(test) == "Hello"
+ assert test._spans == [Span(0, 5, "bold")]
+
+
+def test_from_markup():
+ text = Text.from_markup("Hello, [bold]World![/bold]")
+ assert str(text) == "Hello, World!"
+ assert text._spans == [Span(7, 13, "bold")]
+
+
+def test_copy():
+ test = Text()
+ test.append("Hello", "bold")
+ test.append(" ")
+ test.append("World", "italic")
+ test_copy = test.copy()
+ assert test == test_copy
+ assert test is not test_copy
+
+
+def test_rstrip():
+ test = Text("Hello, World! ")
+ test.rstrip()
+ assert str(test) == "Hello, World!"
+
+
+def test_rstrip_end():
+ test = Text("Hello, World! ")
+ test.rstrip_end(14)
+ assert str(test) == "Hello, World! "
+
+
+def test_stylize():
+ test = Text("Hello, World!")
+ test.stylize("bold", 7, 11)
+ assert test._spans == [Span(7, 11, "bold")]
+ test.stylize("bold", 20, 25)
+ assert test._spans == [Span(7, 11, "bold")]
+
+
+def test_stylize_negative_index():
+ test = Text("Hello, World!")
+ test.stylize("bold", -6, -1)
+ assert test._spans == [Span(7, 12, "bold")]
+
+
+def test_highlight_regex():
+ test = Text("peek-a-boo")
+
+ count = test.highlight_regex(r"NEVER_MATCH", "red")
+ assert count == 0
+ assert len(test._spans) == 0
+
+ # text: peek-a-boo
+ # indx: 0123456789
+ count = test.highlight_regex(r"[a|e|o]+", "red")
+ assert count == 3
+ assert sorted(test._spans) == [
+ Span(1, 3, "red"),
+ Span(5, 6, "red"),
+ Span(8, 10, "red"),
+ ]
+
+ test = Text("Ada Lovelace, Alan Turing")
+ count = test.highlight_regex(
+ r"(?P<yellow>[A-Za-z]+)[ ]+(?P<red>[A-Za-z]+)(?P<NEVER_MATCH>NEVER_MATCH)*"
+ )
+
+ # The number of matched name should be 2
+ assert count == 2
+ assert sorted(test._spans) == [
+ Span(0, 3, "yellow"), # Ada
+ Span(4, 12, "red"), # Lovelace
+ Span(14, 18, "yellow"), # Alan
+ Span(19, 25, "red"), # Turing
+ ]
+
+
+def test_highlight_regex_callable():
+ test = Text("Vulnerability CVE-2018-6543 detected")
+ re_cve = r"CVE-\d{4}-\d+"
+
+ def get_style(text: str) -> Style:
+ return Style.parse(
+ f"bold yellow link https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword={text}"
+ )
+
+ count = test.highlight_regex(re_cve, get_style)
+ assert count == 1
+ assert len(test._spans) == 1
+ assert test._spans[0].start == 14
+ assert test._spans[0].end == 27
+ assert (
+ test._spans[0].style.link
+ == "https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=CVE-2018-6543"
+ )
+
+
+def test_highlight_words():
+ test = Text("Do NOT! touch anything!")
+ words = ["NOT", "!"]
+ count = test.highlight_words(words, "red")
+ assert count == 3
+ assert sorted(test._spans) == [
+ Span(3, 6, "red"), # NOT
+ Span(6, 7, "red"), # !
+ Span(22, 23, "red"), # !
+ ]
+
+ # regex escape test
+ test = Text("[o|u]aeiou")
+ words = ["[a|e|i]", "[o|u]"]
+ count = test.highlight_words(words, "red")
+ assert count == 1
+ assert test._spans == [Span(0, 5, "red")]
+
+ # case sensitive
+ test = Text("AB Ab aB ab")
+ words = ["AB"]
+
+ count = test.highlight_words(words, "red")
+ assert count == 1
+ assert test._spans == [Span(0, 2, "red")]
+
+ test = Text("AB Ab aB ab")
+ count = test.highlight_words(words, "red", case_sensitive=False)
+ assert count == 4
+
+
+def test_set_length():
+ test = Text("Hello")
+ test.set_length(5)
+ assert test == Text("Hello")
+
+ test = Text("Hello")
+ test.set_length(10)
+ assert test == Text("Hello ")
+
+ test = Text("Hello World")
+ test.stylize("bold", 0, 5)
+ test.stylize("italic", 7, 9)
+
+ test.set_length(3)
+ expected = Text()
+ expected.append("Hel", "bold")
+ assert test == expected
+
+
+def test_console_width():
+ console = Console()
+ test = Text("Hello World!\nfoobarbaz")
+ assert test.__rich_measure__(console, 80) == Measurement(9, 12)
+ assert Text(" " * 4).__rich_measure__(console, 80) == Measurement(4, 4)
+
+
+def test_join():
+ test = Text("bar").join([Text("foo", "red"), Text("baz", "blue")])
+ assert str(test) == "foobarbaz"
+ assert test._spans == [Span(0, 3, "red"), Span(3, 6, ""), Span(6, 9, "blue")]
+
+
+def test_trim_spans():
+ test = Text("Hello")
+ test._spans[:] = [Span(0, 3, "red"), Span(3, 6, "green"), Span(6, 9, "blue")]
+ test._trim_spans()
+ assert test._spans == [Span(0, 3, "red"), Span(3, 5, "green")]
+
+
+def test_pad_left():
+ test = Text("foo")
+ test.pad_left(3, "X")
+ assert str(test) == "XXXfoo"
+
+
+def test_pad_right():
+ test = Text("foo")
+ test.pad_right(3, "X")
+ assert str(test) == "fooXXX"
+
+
+def test_append():
+ test = Text("foo")
+ test.append("bar")
+ assert str(test) == "foobar"
+ test.append(Text("baz", "bold"))
+ assert str(test) == "foobarbaz"
+ assert test._spans == [Span(6, 9, "bold")]
+
+ with pytest.raises(ValueError):
+ test.append(Text("foo"), "bar")
+
+ with pytest.raises(TypeError):
+ test.append(1)
+
+
+def test_append_text():
+ test = Text("foo")
+ test.append_text(Text("bar", style="bold"))
+ assert str(test) == "foobar"
+ assert test._spans == [Span(3, 6, "bold")]
+
+
+def test_split():
+ test = Text()
+ test.append("foo", "red")
+ test.append("\n")
+ test.append("bar", "green")
+ test.append("\n")
+
+ line1 = Text()
+ line1.append("foo", "red")
+ line2 = Text()
+ line2.append("bar", "green")
+ split = test.split("\n")
+ assert len(split) == 2
+ assert split[0] == line1
+ assert split[1] == line2
+
+ assert list(Text("foo").split("\n")) == [Text("foo")]
+
+
+def test_split_spans():
+ test = Text.from_markup("[red]Hello\n[b]World")
+ lines = test.split("\n")
+ assert lines[0].plain == "Hello"
+ assert lines[1].plain == "World"
+ assert lines[0].spans == [Span(0, 5, "red")]
+ assert lines[1].spans == [Span(0, 5, "red"), Span(0, 5, "bold")]
+
+
+def test_divide():
+ lines = Text("foo").divide([])
+ assert len(lines) == 1
+ assert lines[0] == Text("foo")
+
+ text = Text()
+ text.append("foo", "bold")
+ lines = text.divide([1, 2])
+ assert len(lines) == 3
+ assert str(lines[0]) == "f"
+ assert str(lines[1]) == "o"
+ assert str(lines[2]) == "o"
+ assert lines[0]._spans == [Span(0, 1, "bold")]
+ assert lines[1]._spans == [Span(0, 1, "bold")]
+ assert lines[2]._spans == [Span(0, 1, "bold")]
+
+ text = Text()
+ text.append("foo", "red")
+ text.append("bar", "green")
+ text.append("baz", "blue")
+ lines = text.divide([8])
+ assert len(lines) == 2
+ assert str(lines[0]) == "foobarba"
+ assert str(lines[1]) == "z"
+ assert lines[0]._spans == [
+ Span(0, 3, "red"),
+ Span(3, 6, "green"),
+ Span(6, 8, "blue"),
+ ]
+ assert lines[1]._spans == [Span(0, 1, "blue")]
+
+ lines = text.divide([1])
+ assert len(lines) == 2
+ assert str(lines[0]) == "f"
+ assert str(lines[1]) == "oobarbaz"
+ assert lines[0]._spans == [Span(0, 1, "red")]
+ assert lines[1]._spans == [
+ Span(0, 2, "red"),
+ Span(2, 5, "green"),
+ Span(5, 8, "blue"),
+ ]
+
+
+def test_right_crop():
+ test = Text()
+ test.append("foobar", "red")
+ test.right_crop(3)
+ assert str(test) == "foo"
+ assert test._spans == [Span(0, 3, "red")]
+
+
+def test_wrap_3():
+ test = Text("foo bar baz")
+ lines = test.wrap(Console(), 3)
+ print(repr(lines))
+ assert len(lines) == 3
+ assert lines[0] == Text("foo")
+ assert lines[1] == Text("bar")
+ assert lines[2] == Text("baz")
+
+
+def test_wrap_4():
+ test = Text("foo bar baz", justify="left")
+ lines = test.wrap(Console(), 4)
+ assert len(lines) == 3
+ assert lines[0] == Text("foo ")
+ assert lines[1] == Text("bar ")
+ assert lines[2] == Text("baz ")
+
+
+def test_wrap_long():
+ test = Text("abracadabra", justify="left")
+ lines = test.wrap(Console(), 4)
+ assert len(lines) == 3
+ assert lines[0] == Text("abra")
+ assert lines[1] == Text("cada")
+ assert lines[2] == Text("bra ")
+
+
+def test_wrap_overflow():
+ test = Text("Some more words")
+ lines = test.wrap(Console(), 4, overflow="ellipsis")
+ assert (len(lines)) == 3
+ assert lines[0] == Text("Some")
+ assert lines[1] == Text("more")
+ assert lines[2] == Text("wor…")
+
+
+def test_wrap_overflow_long():
+ test = Text("bigword" * 10)
+ lines = test.wrap(Console(), 4, overflow="ellipsis")
+ assert len(lines) == 1
+ assert lines[0] == Text("big…")
+
+
+def test_wrap_long_words():
+ test = Text("X 123456789", justify="left")
+ lines = test.wrap(Console(), 4)
+
+ assert len(lines) == 3
+ assert lines[0] == Text("X 12")
+ assert lines[1] == Text("3456")
+ assert lines[2] == Text("789 ")
+
+
+def test_no_wrap_no_crop():
+ test = Text("Hello World!" * 3)
+
+ console = Console(width=20, file=StringIO())
+ console.print(test, no_wrap=True)
+ console.print(test, no_wrap=True, crop=False, overflow="ignore")
+
+ print(repr(console.file.getvalue()))
+ assert (
+ console.file.getvalue()
+ == "Hello World!Hello Wo\nHello World!Hello World!Hello World!\n"
+ )
+
+
+def test_fit():
+ test = Text("Hello\nWorld")
+ lines = test.fit(3)
+ assert str(lines[0]) == "Hel"
+ assert str(lines[1]) == "Wor"
+
+
+def test_wrap_tabs():
+ test = Text("foo\tbar", justify="left")
+ lines = test.wrap(Console(), 4)
+ assert len(lines) == 2
+ assert str(lines[0]) == "foo "
+ assert str(lines[1]) == "bar "
+
+
+def test_render():
+ console = Console(width=15, record=True)
+ test = Text.from_markup(
+ "[u][b]Where[/b] there is a [i]Will[/i], there is a Way.[/u]"
+ )
+ console.print(test)
+ output = console.export_text(styles=True)
+ expected = "\x1b[1;4mWhere\x1b[0m\x1b[4m there is \x1b[0m\n\x1b[4ma \x1b[0m\x1b[3;4mWill\x1b[0m\x1b[4m, there \x1b[0m\n\x1b[4mis a Way.\x1b[0m\n"
+ assert output == expected
+
+
+def test_render_simple():
+ console = Console(width=80)
+ console.begin_capture()
+ console.print(Text("foo"))
+ result = console.end_capture()
+ assert result == "foo\n"
+
+
+@pytest.mark.parametrize(
+ "print_text,result",
+ [
+ (("."), ".\n"),
+ ((".", "."), ". .\n"),
+ (("Hello", "World", "!"), "Hello World !\n"),
+ ],
+)
+def test_print(print_text, result):
+ console = Console(record=True)
+ console.print(*print_text)
+ assert console.export_text(styles=False) == result
+
+
+@pytest.mark.parametrize(
+ "print_text,result",
+ [
+ (("."), ".X"),
+ ((".", "."), "..X"),
+ (("Hello", "World", "!"), "HelloWorld!X"),
+ ],
+)
+def test_print_sep_end(print_text, result):
+ console = Console(record=True, file=StringIO())
+ console.print(*print_text, sep="", end="X")
+ assert console.file.getvalue() == result
+
+
+def test_tabs_to_spaces():
+ test = Text("\tHello\tWorld", tab_size=8)
+ test.expand_tabs()
+ assert test.plain == " Hello World"
+
+ test = Text("\tHello\tWorld", tab_size=4)
+ test.expand_tabs()
+ assert test.plain == " Hello World"
+
+ test = Text(".\t..\t...\t....\t", tab_size=4)
+ test.expand_tabs()
+ assert test.plain == ". .. ... .... "
+
+ test = Text("No Tabs")
+ test.expand_tabs()
+ assert test.plain == "No Tabs"
+
+
+def test_markup_switch():
+ """Test markup can be disabled."""
+ console = Console(file=StringIO(), markup=False)
+ console.print("[bold]foo[/bold]")
+ assert console.file.getvalue() == "[bold]foo[/bold]\n"
+
+
+def test_emoji():
+ """Test printing emoji codes."""
+ console = Console(file=StringIO())
+ console.print(":+1:")
+ assert console.file.getvalue() == "👍\n"
+
+
+def test_emoji_switch():
+ """Test emoji can be disabled."""
+ console = Console(file=StringIO(), emoji=False)
+ console.print(":+1:")
+ assert console.file.getvalue() == ":+1:\n"
+
+
+def test_assemble():
+ text = Text.assemble("foo", ("bar", "bold"))
+ assert str(text) == "foobar"
+ assert text._spans == [Span(3, 6, "bold")]
+
+
+def test_styled():
+ text = Text.styled("foo", "bold red")
+ assert text.style == ""
+ assert str(text) == "foo"
+ assert text._spans == [Span(0, 3, "bold red")]
+
+
+def test_strip_control_codes():
+ text = Text("foo\rbar")
+ assert str(text) == "foobar"
+ text.append("\x08")
+ assert str(text) == "foobar"
+
+
+def test_get_style_at_offset():
+ console = Console()
+ text = Text.from_markup("Hello [b]World[/b]")
+ assert text.get_style_at_offset(console, 0) == Style()
+ assert text.get_style_at_offset(console, 6) == Style(bold=True)
+
+
+@pytest.mark.parametrize(
+ "input, count, expected",
+ [
+ ("Hello", 10, "Hello"),
+ ("Hello", 5, "Hello"),
+ ("Hello", 4, "Hel…"),
+ ("Hello", 3, "He…"),
+ ("Hello", 2, "H…"),
+ ("Hello", 1, "…"),
+ ],
+)
+def test_truncate_ellipsis(input, count, expected):
+ text = Text(input)
+ text.truncate(count, overflow="ellipsis")
+ assert text.plain == expected
+
+
+@pytest.mark.parametrize(
+ "input, count, expected",
+ [
+ ("Hello", 5, "Hello"),
+ ("Hello", 10, "Hello "),
+ ("Hello", 3, "He…"),
+ ],
+)
+def test_truncate_ellipsis_pad(input, count, expected):
+ text = Text(input)
+ text.truncate(count, overflow="ellipsis", pad=True)
+ assert text.plain == expected
+
+
+def test_pad():
+ test = Text("foo")
+ test.pad(2)
+ assert test.plain == " foo "
+
+
+def test_align_left():
+ test = Text("foo")
+ test.align("left", 10)
+ assert test.plain == "foo "
+
+
+def test_align_right():
+ test = Text("foo")
+ test.align("right", 10)
+ assert test.plain == " foo"
+
+
+def test_align_center():
+ test = Text("foo")
+ test.align("center", 10)
+ assert test.plain == " foo "
+
+
+def test_detect_indentation():
+ test = """\
+foo
+ bar
+ """
+ assert Text(test).detect_indentation() == 4
+ test = """\
+foo
+ bar
+ baz
+ """
+ assert Text(test).detect_indentation() == 2
+ assert Text("").detect_indentation() == 1
+ assert Text(" ").detect_indentation() == 1
+
+
+def test_indentation_guides():
+ test = Text(
+ """\
+for a in range(10):
+ print(a)
+
+foo = [
+ 1,
+ {
+ 2
+ }
+]
+
+"""
+ )
+ result = test.with_indent_guides()
+ print(result.plain)
+ print(repr(result.plain))
+ expected = "for a in range(10):\n│ print(a)\n\nfoo = [\n│ 1,\n│ {\n│ │ 2\n│ }\n]\n"
+ assert result.plain == expected
+
+
+def test_slice():
+
+ text = Text.from_markup("[red]foo [bold]bar[/red] baz[/bold]")
+ assert text[0] == Text("f", spans=[Span(0, 1, "red")])
+ assert text[4] == Text("b", spans=[Span(0, 1, "red"), Span(0, 1, "bold")])
+
+ assert text[:3] == Text("foo", spans=[Span(0, 3, "red")])
+ assert text[:4] == Text("foo ", spans=[Span(0, 4, "red")])
+ assert text[:5] == Text("foo b", spans=[Span(0, 5, "red"), Span(4, 5, "bold")])
+ assert text[4:] == Text("bar baz", spans=[Span(0, 3, "red"), Span(0, 7, "bold")])
+
+ with pytest.raises(TypeError):
+ text[::-1]
+
+
+def test_wrap_invalid_style():
+ # https://github.com/willmcgugan/rich/issues/987
+ console = Console(width=100, color_system="truecolor")
+ a = "[#######.................] xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx [#######.................]"
+ console.print(a, justify="full")