keep the artist targets narrow and clear of the controls

Both targets were too greedy. They now hug the text instead of spanning the row, so a tap
beside the line does nothing, and the album detail target stops short of the controls
instead of reaching down to them.

Album detail: the line plus its 4dp lead-in and 8dp below (32dp), with the original
spacing kept, so the layout is exactly where it was. Now playing keeps the title+artist
block (58dp) but is no longer full width.

Both are shorter than the 48dp recommendation on purpose, to stay clear of the slider and
the play/shuffle buttons. Tests assert the measured geometry: no full-width target, a
12dp gap above the controls, and the now playing block staying at its own 58dp.
This commit is contained in:
austinried
2026-10-04 00:30:01 +00:00
parent 17a41fd579
commit 07fb07762c
4 changed files with 61 additions and 9 deletions
@@ -7,7 +7,9 @@ import androidx.compose.ui.test.filter
import androidx.compose.ui.test.hasClickAction
import androidx.compose.ui.test.junit4.v2.createAndroidComposeRule
import androidx.compose.ui.test.onAllNodesWithText
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performClick
import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -68,10 +70,39 @@ class AlbumDetailTapsTest {
}
@Test
fun theArtistTargetIsAtLeastTheMinimumTouchSize() {
fun theArtistTargetCoversTheLineAndItsGaps() {
render(onArtistClick = {})
composeRule.onNodeWithText("Radiohead \u00B7 2000").assertHeightIsAtLeast(48.dp)
composeRule.onNodeWithText("Radiohead \u00B7 2000").assertHeightIsAtLeast(32.dp)
}
@Test
fun theArtistTargetHugsTheTextRatherThanTheRow() {
render(onArtistClick = {})
val target =
composeRule
.onNodeWithText("Radiohead \u00B7 2000")
.fetchSemanticsNode()
.boundsInRoot.width
val screen =
composeRule
.onRoot()
.fetchSemanticsNode()
.boundsInRoot.width
assertTrue("the target should not span the row", target < screen)
}
@Test
fun theArtistTargetKeepsClearOfTheControls() {
render(onArtistClick = {})
val target = composeRule.onNodeWithText("Radiohead \u00B7 2000").fetchSemanticsNode().boundsInRoot
val controls = composeRule.onNodeWithContentDescription("Download").fetchSemanticsNode().boundsInRoot
val gap = controls.top - target.bottom
assertTrue("expected a clear gap above the controls, was ${gap}px", gap >= with(composeRule.density) { 12.dp.roundToPx() })
}
@Test
@@ -2,11 +2,13 @@ package com.subtracks.ui.playback
import androidx.activity.ComponentActivity
import androidx.compose.ui.test.assertHeightIsAtLeast
import androidx.compose.ui.test.assertHeightIsEqualTo
import androidx.compose.ui.test.assertIsNotEnabled
import androidx.compose.ui.test.assertWidthIsAtLeast
import androidx.compose.ui.test.junit4.v2.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performClick
import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -92,10 +94,31 @@ class NowPlayingTapsTest {
fun theArtistTargetIsAtLeastTheMinimumTouchSize() {
render(onArtistClick = {})
// The block is the title line (32dp) plus the gap (6dp) plus the artist line
// (20dp); it must not absorb the slider's own top padding.
composeRule.onNodeWithText("Radiohead").assertHeightIsAtLeast(48.dp)
composeRule.onNodeWithText("Radiohead").assertHeightIsEqualTo(58.dp)
composeRule.onNodeWithText("Radiohead").assertWidthIsAtLeast(48.dp)
}
@Test
fun theArtistTargetHugsTheTextRatherThanTheRow() {
render(onArtistClick = {})
val target =
composeRule
.onNodeWithText("Radiohead")
.fetchSemanticsNode()
.boundsInRoot.width
val screen =
composeRule
.onRoot()
.fetchSemanticsNode()
.boundsInRoot.width
assertTrue("the target should not span the row", target < screen)
}
@Test
fun theTapTargetsStayInertWithoutADestination() {
render()