105 lines
3.7 KiB
Python
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}")
|