feat(ui): draw pane frames, page keys and a scroll thumb
This commit is contained in:
1 parent
49f7efe0d2
commit
0219df454c
14 files changed
+571
-64
No files matched your search
@@ -0,0 +1,104 @@
|
||||
"""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}")
|
||||
Reference in new issue
Block a user