All change-related output (transactions, bootstrap, migrations) is now described by a mutable LogEvent carrying full state plus the preferred logger and level, and dispatched through emit_event. @kanta.logemit callbacks receive the event and decide what is logged where: falsy return marks it handled, truthy passes it (possibly modified) down the chain, with default_emit - Kanta's own formatting, now just another emitter - as the fallback. Pretty header and diff lines are lazy event properties. New kanta.tty module: Line builder (call to append content, .colorname arms a palette color for the next call with automatic folded reset, width/align padding), a mutable Colors palette storing bare SGR params (0 clears, sequential last-wins stacking), and strip_ansi/displaywidth/pad helpers that count wide chars and emoji correctly.
75 lines
2.1 KiB
Python
75 lines
2.1 KiB
Python
import pytest
|
|
|
|
from kanta.tty import ESC, Colors, Line, colors, displaywidth, pad, strip_ansi
|
|
|
|
|
|
def test_strip_ansi():
|
|
assert strip_ansi(f"{ESC}1;34mhello{ESC}0m") == "hello"
|
|
|
|
|
|
def test_displaywidth_plain_and_ansi():
|
|
assert displaywidth("hello") == 5
|
|
assert displaywidth(f"{ESC}38;5;226mhi{ESC}0m") == 2
|
|
|
|
|
|
def test_displaywidth_wide_and_combining_chars():
|
|
assert displaywidth("你好") == 4
|
|
assert displaywidth("🚀") == 2
|
|
assert displaywidth("é") == 1
|
|
|
|
|
|
def test_pad():
|
|
assert pad("ab", 4) == "ab "
|
|
assert pad("ab", 4, align="right") == " ab"
|
|
assert pad("ab", 5, align="center") == " ab "
|
|
assert pad("abcdef", 4) == "abcdef"
|
|
assert pad("你好", 6) == "你好 "
|
|
|
|
|
|
def test_line_plain_and_str_conversion():
|
|
assert str(Line()("n=", 42)) == "n=42"
|
|
|
|
|
|
def test_line_color_auto_resets_on_next_call():
|
|
assert str(Line().user("Alice")(" by ")) == f"{ESC}34mAlice{ESC}0m by "
|
|
|
|
|
|
def test_line_str_restores_active_color():
|
|
assert str(Line().user("Alice")) == f"{ESC}34mAlice{ESC}0m"
|
|
|
|
|
|
def test_line_same_color_not_reemitted():
|
|
assert str(Line().user("a").user("b")) == f"{ESC}34mab{ESC}0m"
|
|
|
|
|
|
def test_line_transition_folds_reset_into_one_sequence():
|
|
# bold blue -> plain blue: the bold clear rides in the same sequence
|
|
assert str(Line().action("a").user("b")) == f"{ESC}1;34ma{ESC}0;34mb{ESC}0m"
|
|
|
|
|
|
def test_line_unknown_color_raises():
|
|
with pytest.raises(AttributeError, match="unknown color"):
|
|
Line().nosuchcolor("x")
|
|
|
|
|
|
def test_line_palette_addition(monkeypatch):
|
|
monkeypatch.setattr(colors, "session", "38;5;226", raising=False)
|
|
assert str(Line().session("3")) == f"{ESC}38;5;226m3{ESC}0m"
|
|
|
|
|
|
def test_line_palette_override_takes_effect(monkeypatch):
|
|
monkeypatch.setattr(colors, "user", "36")
|
|
assert str(Line().user("x")) == f"{ESC}36mx{ESC}0m"
|
|
|
|
|
|
def test_line_custom_palette():
|
|
palette = Colors()
|
|
palette.brand = "35"
|
|
assert str(Line(palette).brand("x")) == f"{ESC}35mx{ESC}0m"
|
|
|
|
|
|
def test_line_width_and_align():
|
|
assert str(Line()("ab", width=4)) == "ab "
|
|
assert str(Line()("ab", width=4, align="right")) == " ab"
|
|
assert str(Line().user("ab", width=4)) == f"{ESC}34mab {ESC}0m"
|