"""Round 21: transport buttons fill their bubble as equal tap targets. The prev/play/next FlashButtons used to hug their ~20px icons in the middle of the 62px-tall rounded box, leaving most of the bubble unclickable. Now _box(split=True) tiles the bubble interior with the buttons: each gets an equal third, expands vertically to the margins, and keeps its glyph centered. The bubble itself keeps its pre-split footprint (no bigger, no smaller). """ from unittest.mock import MagicMock, patch from lintunes.models import Library, Track from lintunes.library_manager import LibraryManager from lintunes.preferences import Preferences from lintunes import player as player_module from lintunes.player import Player from lintunes.gui.transport import TransportBar, CONTROL_HEIGHT def _mock_player(): """A real Player with the Qt multimedia backend stubbed out (it blocks on init headless — see tests/test_player.py).""" library = Library(tracks={1: Track(track_id=1, name="One")}) with patch.multiple( player_module, QMediaPlayer=MagicMock(), QAudioOutput=MagicMock(), QAudioBufferOutput=MagicMock(), QMediaDevices=MagicMock(), ): return Player(MagicMock(library=library)) def _build_bar(qapp, tmp_path): prefs = Preferences(tmp_path) manager = LibraryManager( Library(tracks={1: Track(track_id=1, name="One")}), tmp_path) bar = TransportBar(_mock_player(), manager, prefs) bar.resize(1200, 90) bar.show() qapp.processEvents() return bar class TestTransportTapTargets: def test_buttons_split_the_bubble_equally(self, qapp, tmp_path): bar = _build_bar(qapp, tmp_path) widths = [b.width() for b in (bar._prev_btn, bar._play_btn, bar._next_btn)] assert max(widths) - min(widths) <= 1 def test_buttons_fill_the_bubble_vertically(self, qapp, tmp_path): bar = _build_bar(qapp, tmp_path) for btn in (bar._prev_btn, bar._play_btn, bar._next_btn, bar._shuffle_btn): # Bubble height minus margins+border (4px each side). assert btn.height() >= CONTROL_HEIGHT - 10 def test_buttons_span_the_bubble_horizontally(self, qapp, tmp_path): bar = _build_bar(qapp, tmp_path) box = bar._prev_btn.parentWidget() assert bar._prev_btn.x() <= 5 # flush against the left margin right_edge = bar._next_btn.x() + bar._next_btn.width() assert right_edge >= box.width() - 5 # ...and the right one # No stretch gaps between the buttons. assert bar._play_btn.x() == bar._prev_btn.x() + bar._prev_btn.width() assert bar._next_btn.x() == bar._play_btn.x() + bar._play_btn.width() def test_bubble_keeps_its_footprint(self, qapp, tmp_path): """The bubble must not grow (trav's constraint) nor shrink (the minimum button widths preserve the old stretch-padded size).""" bar = _build_bar(qapp, tmp_path) box = bar._prev_btn.parentWidget() assert box.height() == CONTROL_HEIGHT assert 106 <= box.width() <= 126 # was 116 pre-split