feat(ui): add catalogue picker, schedule pane and per-provider hero
This commit is contained in:
1 parent
058d336a2c
commit
b31ad3f6fb
16 files changed
+1211
-65
No files matched your search
+51
-4
@@ -6,6 +6,8 @@ import types
|
||||
import pytest
|
||||
from rich.cells import cell_len
|
||||
|
||||
from beefy_player.providers.somafm import SomaFMProvider
|
||||
from beefy_player.providers.tilderadio import TilderadioProvider
|
||||
from beefy_player.widgets.header import HeaderBar
|
||||
|
||||
TIME = re.compile(r"\d\d:\d\d:\d\d$")
|
||||
@@ -13,11 +15,17 @@ DATE = re.compile(r"[A-Z][a-z]{2} \d\d [A-Z][a-z]{2}$")
|
||||
VOLUME = re.compile(r"vol \d+%$")
|
||||
|
||||
|
||||
def render_header(width: int, height: int = 3) -> list[str]:
|
||||
def render_header(
|
||||
width: int, height: int = 3, provider=None
|
||||
) -> list[str]:
|
||||
"""Render the header without a running app, as plain text per line."""
|
||||
fake = type("Fake", (), {})()
|
||||
fake.app = types.SimpleNamespace(
|
||||
playing_state="playing", volume=70, current=None, notice=None
|
||||
playing_state="playing",
|
||||
volume=70,
|
||||
current=None,
|
||||
notice=None,
|
||||
provider=provider or SomaFMProvider(),
|
||||
)
|
||||
fake.size = types.SimpleNamespace(width=width, height=height)
|
||||
return [line.plain.rstrip() for line in HeaderBar.render(fake).renderables]
|
||||
@@ -34,8 +42,8 @@ def test_right_block_is_flush_right(width: int) -> None:
|
||||
|
||||
|
||||
def test_ragged_middle_does_not_shift_the_right_block() -> None:
|
||||
""""SOMA FM RADIO" and "SAN FRANCISCO" are shorter than
|
||||
"LISTENER SUPPORTED"; the right block must not follow their length."""
|
||||
""""TILDERADIO" is shorter than "STREAMED BY / FOR USERS OF THE"; the
|
||||
right block must not follow the length of the middle lines."""
|
||||
clock, date, volume = render_header(143)
|
||||
assert cell_len(clock) == cell_len(date) == cell_len(volume) == 143
|
||||
|
||||
@@ -44,3 +52,42 @@ def test_right_block_hidden_on_narrow_terminals() -> None:
|
||||
"""Below the threshold the right block is dropped, not squashed."""
|
||||
lines = render_header(30)
|
||||
assert all(":" not in line for line in lines)
|
||||
|
||||
|
||||
def test_somafm_hero_is_the_wordmark_and_its_taglines() -> None:
|
||||
lines = render_header(143)
|
||||
assert lines[0].startswith("▄▀▀▀ ▄▀▀▄ █▄ ▄█ ▄▀▀▄")
|
||||
assert "SOMA FM RADIO" in lines[0]
|
||||
assert "LISTENER SUPPORTED" in lines[1]
|
||||
assert "SAN FRANCISCO" in lines[2]
|
||||
|
||||
|
||||
def test_tilderadio_hero_is_its_own_art_and_motto() -> None:
|
||||
provider = TilderadioProvider()
|
||||
lines = render_header(143, height=len(provider.hero), provider=provider)
|
||||
assert len(lines) == len(provider.hero)
|
||||
# the caption block is centred against the seven-row radio icon
|
||||
assert "TILDERADIO" in lines[2]
|
||||
assert "STREAMED BY / FOR USERS OF THE" in lines[3]
|
||||
assert "TILDEVERSE" in lines[4]
|
||||
# the radio icon's signature green wave survives the conversion
|
||||
styles = [
|
||||
str(span.style)
|
||||
for line in HeaderBar.render(_fake(provider)).renderables
|
||||
for span in line.spans
|
||||
]
|
||||
assert any("green" in style for style in styles)
|
||||
assert all("SOMA" not in line for line in lines)
|
||||
|
||||
|
||||
def _fake(provider):
|
||||
fake = type("Fake", (), {})()
|
||||
fake.app = types.SimpleNamespace(
|
||||
playing_state="playing",
|
||||
volume=70,
|
||||
current=None,
|
||||
notice=None,
|
||||
provider=provider,
|
||||
)
|
||||
fake.size = types.SimpleNamespace(width=143, height=len(provider.hero) or 3)
|
||||
return fake
|
||||
Reference in new issue
Block a user