129 lines
4.4 KiB
Python
129 lines
4.4 KiB
Python
from __future__ import annotations
|
|
|
|
import re
|
|
import types
|
|
|
|
import pytest
|
|
from rich.cells import cell_len
|
|
|
|
from beefy_player.providers import PROVIDERS
|
|
from beefy_player.providers.somafm import SomaFMProvider
|
|
from beefy_player.providers.wordmark import render
|
|
from beefy_player.widgets.header import HeaderBar
|
|
|
|
TIME = re.compile(r"\d\d:\d\d:\d\d$")
|
|
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, 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,
|
|
provider=provider or SomaFMProvider(),
|
|
)
|
|
fake.size = types.SimpleNamespace(width=width, height=height)
|
|
return [line.plain.rstrip() for line in HeaderBar.render(fake).renderables]
|
|
|
|
|
|
@pytest.mark.parametrize("width", [54, 60, 100, 143, 200])
|
|
def test_right_block_is_flush_right(width: int) -> None:
|
|
"""Clock, date and volume all end on the same column, at the edge."""
|
|
clock, date, volume = render_header(width)
|
|
assert [cell_len(line) for line in (clock, date, volume)] == [width] * 3
|
|
assert TIME.search(clock)
|
|
assert DATE.search(date)
|
|
assert VOLUME.search(volume)
|
|
|
|
|
|
def test_ragged_middle_does_not_shift_the_right_block() -> None:
|
|
""""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
|
|
|
|
|
|
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_spells_the_whole_name_and_its_taglines() -> None:
|
|
lines = render_header(143)
|
|
assert lines[0].startswith(render("SOMAFM")[0])
|
|
assert "SOMA FM RADIO" in lines[0]
|
|
assert "LISTENER SUPPORTED" in lines[1]
|
|
assert "SAN FRANCISCO" in lines[2]
|
|
|
|
|
|
def hero_styles(provider) -> list[str]:
|
|
return [
|
|
str(span.style)
|
|
for line in HeaderBar.render(_fake(provider)).renderables
|
|
for span in line.spans
|
|
]
|
|
|
|
|
|
def test_the_i_and_t_have_thin_stems() -> None:
|
|
"""Two-pixel stems make an I look like an H and a T look like a block."""
|
|
assert render("I") == ("▀█▀", " █", "▄█▄")
|
|
assert render("T") == ("▀█▀", " █", " █")
|
|
|
|
|
|
def test_the_font_still_spells_soma_the_way_the_original_logo_did() -> None:
|
|
"""The wordmark font is the SomaFM logo, so the old art must be reproduced."""
|
|
assert render("SOMA") == (
|
|
"▄▀▀▀ ▄▀▀▄ █▄ ▄█ ▄▀▀▄",
|
|
"▀▀▀▄ █ █ █ █ █ █▀▀█",
|
|
"▄▄▄▀ ▀▄▄▀ █ █ █ █",
|
|
)
|
|
|
|
|
|
def test_every_catalogue_has_a_three_row_wordmark() -> None:
|
|
for slug, provider in PROVIDERS.items():
|
|
assert len(provider.hero) == 3, slug
|
|
assert provider.hero_lines, slug
|
|
|
|
|
|
def test_tilderadio_spells_its_own_name_in_the_default_colour() -> None:
|
|
provider = PROVIDERS["tilderadio"]
|
|
lines = render_header(143, height=len(provider.hero), provider=provider)
|
|
assert len(lines) == 3
|
|
assert "TILDERADIO" in lines[0] # the wordmark
|
|
assert "STREAMED BY / FOR USERS OF THE" in lines[1]
|
|
assert "TILDEVERSE" in lines[2]
|
|
# no colour of its own: the art inherits the terminal foreground
|
|
assert all("[" not in row for row in provider.hero)
|
|
assert not {"bright_cyan", "cyan"} & set(hero_styles(provider))
|
|
assert all("SOMA" not in line for line in lines)
|
|
|
|
|
|
def test_bassdrive_spells_its_name_in_its_own_two_blues() -> None:
|
|
provider = PROVIDERS["bassdrive"]
|
|
lines = render_header(143, height=len(provider.hero), provider=provider)
|
|
assert "BASSDRIVE" in lines[0]
|
|
assert "WORLDWIDE DRUM & BASS" in lines[1]
|
|
assert "LIVE SHOWS 24/7" in lines[2]
|
|
styles = hero_styles(provider)
|
|
assert "bright_cyan" in styles and "cyan" in styles
|
|
|
|
|
|
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
|