Files
MeshTerm/tests/test_widgets.py
T
Jean-Pierre MartineauandClaude Fable 5 13db8c4df3 Path composer: an insertion cursor riding the route's arrows
←/→ slide a reverse-video → along the editable leg; Enter splices the
chosen hop in at the cursor and ⌫ removes the hop to its left, so a route
can be edited anywhere, not just at its end. It opens on the last arrow,
keeping the classic append flow unchanged. Suggestions, the fetch row,
and the "Next hop from" heading all follow the node left of the cursor,
and the fetch-neighbours round trip now resumes at the same position.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 18:01:15 -04:00

116 lines
5.2 KiB
Python

"""Shared-widget helper tests: the canonical formatting primitives every screen leans on.
Most widgets are exercised through their host screens' tests; what lives here are the
pure text helpers whose exact output *is* the app-wide convention — get these right once
and every caller inherits it.
"""
from __future__ import annotations
from meshterm.ui.widgets import _format_age, format_ago, path_text
def test_format_age_is_the_bare_column_form() -> None:
"""The lane form stays suffix-free at every magnitude, for aligned age columns."""
assert _format_age(None) == "never"
assert _format_age(5) == "now"
assert _format_age(90) == "1m"
assert _format_age(7200) == "2h"
assert _format_age(180000) == "2d"
assert _format_age(1300000) == "2w"
def test_format_ago_speaks_grammatical_prose() -> None:
"""The prose form says "5m ago" but bare "now"/"never" — never "now ago"."""
assert format_ago(5) == "now"
assert format_ago(None) == "never"
assert format_ago(90) == "1m ago"
assert format_ago(7200) == "2h ago"
def _resolve(hop: str) -> str:
return {"aa": "Alice", "3d": "YUL"}.get(hop, hop)
def test_path_text_names_hops_and_keeps_hashes_bare() -> None:
"""Named hops read as their name alone; unnamed ones as their bare hash."""
text = path_text(["aa", "77", "3d"], _resolve)
assert text.plain == "Alice → 77 → YUL" # no parenthesized hash after a name
def test_path_text_marks_us_white_and_names_in_their_hue() -> None:
"""Our own node takes the white ``you`` style; other names their palette hue."""
text = path_text(["aa", "3d"], _resolve, self_name="Alice")
styles = {text.plain[s.start : s.end]: str(s.style) for s in text.spans}
assert styles.get("Alice") == "you"
assert "YUL" in styles and styles["YUL"] != "you"
def test_path_text_empty_reads_as_direct() -> None:
"""No hops (or only empty tokens) renders the caller's empty word, muted."""
assert path_text([], _resolve).plain == "direct"
assert path_text(["", ""], _resolve).plain == "direct"
assert path_text([], _resolve, empty="direct — no relays").plain == "direct — no relays"
def test_path_text_lights_the_hash_prefix() -> None:
"""An unnamed hop renders through the shared hash widget, its prefix lit."""
text = path_text(["77bb"], _resolve, prefix_bytes=1)
brand = [text.plain[s.start : s.end] for s in text.spans if "brand" in str(s.style)]
assert "77" in brand # the addressed prefix stands out within the hash
def test_path_text_hash_as_name_shows_grey_identity_hash() -> None:
"""An unnamed hop stands in its own hash at the mode width, muted, plus its byte."""
text = path_text(
["e839f2ab"], _resolve, prefix_bytes=3,
show_hash=True, hash_bytes=1, hash_as_name=True,
)
assert text.plain == "e839f2 (e8)" # mode width identity, then the addressed byte
styles = {text.plain[s.start : s.end]: str(s.style) for s in text.spans}
assert styles.get("e839f2") == "muted" # greyed — colour is the "this is a name" cue
assert not any(str(s.style) == "brand" for s in text.spans) # no prefix lit
def test_path_text_hash_as_name_drops_the_byte_when_it_is_the_whole_hash() -> None:
"""When the mode width is one byte, the identity already is the byte — no ``(e8)``."""
text = path_text(
["e839f2ab"], _resolve, prefix_bytes=1,
show_hash=True, hash_bytes=1, hash_as_name=True,
)
assert text.plain == "e8"
def test_path_text_trace_flavour_annotates_hashes_and_brackets_us() -> None:
"""show_hash appends the addressed hash to names; None hops are our device."""
text = path_text(
[None, "aa", "77bb", None], _resolve, self_name="Homestead",
show_hash=True, hash_bytes=1, device_hash="c0ffee",
)
assert text.plain == "Homestead (c0) → Alice (aa) → 77 → Homestead (c0)"
def test_path_text_dims_the_tail_from_dim_from() -> None:
"""The mirrored return leg (and the arrows into it) render faint."""
text = path_text(
[None, "aa", "3d", "aa", None], _resolve, self_name="us",
show_hash=True, dim_from=3,
)
styles = [(text.plain[s.start : s.end], str(s.style)) for s in text.spans]
assert ("us", "you") in styles # the departure keeps the white you
assert ("us", "faint") in styles # the landing back on us is faint
assert any(run.startswith("Alice") and style == "faint" for run, style in styles)
assert any(run.startswith("YUL") and style != "faint" for run, style in styles)
def test_path_text_cursor_arrow_reads_as_a_reverse_block() -> None:
"""The composer's insertion cursor: that one arrow renders as a selected block."""
text = path_text(["aa", "77", "3d"], _resolve, cursor_arrow=1)
assert text.plain == "Alice → 77 → YUL" # spacing survives the split append
styles = [(text.plain[s.start : s.end], str(s.style)) for s in text.spans]
assert styles.count(("→", "selected")) == 1 # only arrow 1, between 77 and YUL
assert (" → ", "muted") in styles # the other arrow stays plain
# The cursor wins over dim_from: the arrow into a dimmed tail still reads selected.
dimmed = path_text([None, "aa", None], _resolve, self_name="us", dim_from=2, cursor_arrow=1)
assert any(str(s.style) == "selected" for s in dimmed.spans)