Files
beefy-player/tests/test_station_list.py

105 lines
3.7 KiB
Python

"""The station pane: paging, the position readout and the scrollbar."""
from __future__ import annotations
import re
import pytest
from beefy_player.app import BeefyPlayerApp
from beefy_player.widgets.station_list import StationList
def app_for(provider: str, *, offline: bool = True) -> BeefyPlayerApp:
return BeefyPlayerApp(provider=provider, autoplay=False, offline=offline, audio=False)
def test_thumb_geometry() -> None:
# nothing to scroll: the whole track is the thumb
assert StationList.thumb(10, 10, 0) == set(range(10))
# top of a long list
top = StationList.thumb(10, 100, 0)
assert 0 in top and len(top) >= 1
# bottom of the same list ends at the last row
bottom = StationList.thumb(10, 100, 90)
assert 9 in bottom
# mid-list sits between the two
middle = StationList.thumb(10, 100, 45)
assert min(middle) > min(top) and max(middle) < max(bottom)
@pytest.mark.asyncio
async def test_paging_moves_a_screen_and_back() -> None:
app = app_for("somafm")
async with app.run_test(size=(100, 26)) as pilot:
await pilot.pause()
pane = app.query_one(StationList)
page = pane.size.height - 1
start = app.index
await pilot.press("pagedown")
await pilot.pause()
assert app.index == min(start + page, len(app.view) - 1)
await pilot.press("pageup")
await pilot.pause()
assert app.index == start
# the bottom of the list clamps instead of wrapping
await pilot.press("shift+g", "pagedown")
await pilot.pause()
assert app.index == len(app.view) - 1
@pytest.mark.asyncio
async def test_the_title_reads_out_the_position() -> None:
app = app_for("somafm")
async with app.run_test(size=(100, 26)) as pilot:
await pilot.pause()
pane = app.query_one(StationList)
assert re.fullmatch(r"STATIONS · \d+/\d+", pane.border_title)
await pilot.press("down")
await pilot.pause()
first, total = pane.border_title.split(" · ")[1].split("/")
assert int(total) == len(app.view)
assert int(first) == app.index + 1
@pytest.mark.asyncio
async def test_the_scrollbar_appears_only_when_the_list_overflows() -> None:
app = app_for("somafm")
async with app.run_test(size=(100, 26)) as pilot:
await pilot.pause()
pane = app.query_one(StationList)
rows = [line.plain for line in pane.render().renderables]
assert rows[0].startswith("╭") and rows[-1].startswith("╰")
content = rows[1:-1]
# thumb only, in the frame grey: blank cells elsewhere, no rail
assert all(row[-2] in "▐ " for row in content)
assert any(row[-2] == "▐" for row in content)
app = app_for("rinse") # three stations, no scrolling
async with app.run_test(size=(100, 26)) as pilot:
await pilot.pause()
pane = app.query_one(StationList)
rows = [line.plain for line in pane.render().renderables]
assert rows[0].startswith("╭") and rows[-1].startswith("╰")
assert sum("Rinse" in row or "Kool" in row or "SWU" in row for row in rows) == 3
# three stations fit: no thumb in the content rows
assert not any(row[-2] == "▐" for row in rows[1:-1])
@pytest.mark.asyncio
async def test_the_filter_keeps_the_readout_in_step() -> None:
app = app_for("somafm")
async with app.run_test(size=(100, 26)) as pilot:
await pilot.pause()
await pilot.press("slash")
await pilot.pause()
app.query_one("SearchInput").value = "drone"
await pilot.pause()
pane = app.query_one(StationList)
assert pane.border_title.startswith("STATIONS · /drone · ")
filtered = len(app.view)
assert pane.border_title.endswith(f"/{filtered}")