149 lines
4.5 KiB
Python
149 lines
4.5 KiB
Python
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from typing import cast
|
|
|
|
from auditui.app.progress import AppProgressMixin
|
|
from textual.events import Key
|
|
from textual.widgets import DataTable
|
|
|
|
|
|
@dataclass(slots=True)
|
|
class FakeKeyEvent:
|
|
"""Minimal key event carrying key value and prevent_default state."""
|
|
|
|
key: str
|
|
prevented: bool = False
|
|
|
|
def prevent_default(self) -> None:
|
|
"""Mark event as prevented."""
|
|
self.prevented = True
|
|
|
|
|
|
@dataclass(slots=True)
|
|
class FakeStatic:
|
|
"""Minimal static widget with text and visibility fields."""
|
|
|
|
display: bool = False
|
|
text: str = ""
|
|
|
|
def update(self, value: str) -> None:
|
|
"""Store rendered text value."""
|
|
self.text = value
|
|
|
|
|
|
@dataclass(slots=True)
|
|
class FakeProgressBar:
|
|
"""Minimal progress bar widget storing latest progress value."""
|
|
|
|
progress: float = 0.0
|
|
|
|
def update(self, progress: float) -> None:
|
|
"""Store progress value for assertions."""
|
|
self.progress = progress
|
|
|
|
|
|
@dataclass(slots=True)
|
|
class FakeContainer:
|
|
"""Minimal container exposing display property."""
|
|
|
|
display: bool = False
|
|
|
|
|
|
class DummyPlayback:
|
|
"""Playback shim exposing only members used by AppProgressMixin."""
|
|
|
|
def __init__(self) -> None:
|
|
"""Initialize playback state and update counters."""
|
|
self.is_playing = False
|
|
self._status: str | None = None
|
|
self._progress: tuple[str, float, float] | None = None
|
|
self.saved_calls = 0
|
|
|
|
def check_status(self):
|
|
"""Return configurable status check message."""
|
|
return self._status
|
|
|
|
def get_current_progress(self):
|
|
"""Return configurable progress tuple."""
|
|
return self._progress
|
|
|
|
def update_position_if_needed(self) -> None:
|
|
"""Record periodic save invocations."""
|
|
self.saved_calls += 1
|
|
|
|
|
|
class DummyProgressApp(AppProgressMixin):
|
|
"""Mixin host that records action dispatch and widget updates."""
|
|
|
|
def __init__(self) -> None:
|
|
"""Initialize fake widgets and playback state."""
|
|
self.playback = DummyPlayback()
|
|
self.focused = object()
|
|
self.actions: list[str] = []
|
|
self.messages: list[str] = []
|
|
self.progress_info = FakeStatic()
|
|
self.progress_bar = FakeProgressBar()
|
|
self.progress_container = FakeContainer()
|
|
|
|
def action_seek_backward(self) -> None:
|
|
"""Record backward seek action dispatch."""
|
|
self.actions.append("seek_backward")
|
|
|
|
def action_toggle_playback(self) -> None:
|
|
"""Record toggle playback action dispatch."""
|
|
self.actions.append("toggle")
|
|
|
|
def update_status(self, message: str) -> None:
|
|
"""Capture status messages for assertions."""
|
|
self.messages.append(message)
|
|
|
|
def query_one(self, selector: str, _type: object):
|
|
"""Return fake widgets by selector used by progress mixin."""
|
|
return {
|
|
"#progress_info": self.progress_info,
|
|
"#progress_bar": self.progress_bar,
|
|
"#progress_bar_container": self.progress_container,
|
|
}[selector]
|
|
|
|
|
|
def test_on_key_dispatches_seek_when_playing() -> None:
|
|
"""Ensure left key is intercepted and dispatched to seek action."""
|
|
app = DummyProgressApp()
|
|
app.playback.is_playing = True
|
|
event = FakeKeyEvent("left")
|
|
app.on_key(cast(Key, event))
|
|
assert event.prevented is True
|
|
assert app.actions == ["seek_backward"]
|
|
|
|
|
|
def test_on_key_dispatches_space_when_table_focused() -> None:
|
|
"""Ensure space is intercepted and dispatched when table is focused."""
|
|
app = DummyProgressApp()
|
|
app.focused = DataTable()
|
|
event = FakeKeyEvent("space")
|
|
app.on_key(cast(Key, event))
|
|
assert event.prevented is True
|
|
assert app.actions == ["toggle"]
|
|
|
|
|
|
def test_check_playback_status_hides_progress_after_message() -> None:
|
|
"""Ensure playback status message triggers hide-progress behavior."""
|
|
app = DummyProgressApp()
|
|
app.playback._status = "Finished"
|
|
app._check_playback_status()
|
|
assert app.messages[-1] == "Finished"
|
|
assert app.progress_info.display is False
|
|
assert app.progress_container.display is False
|
|
|
|
|
|
def test_update_progress_renders_visible_progress_row() -> None:
|
|
"""Ensure valid progress data updates widgets and makes them visible."""
|
|
app = DummyProgressApp()
|
|
app.playback.is_playing = True
|
|
app.playback._progress = ("Chapter", 30.0, 60.0)
|
|
app._update_progress()
|
|
assert app.progress_bar.progress == 50.0
|
|
assert app.progress_info.display is True
|
|
assert app.progress_container.display is True
|