Match Discover captions and catalogue collection states

This commit is contained in:
justinzeus 2026-11-05 16:50:38 +01:00
parent 7e8e232d47
commit 4b28b4f044
25 changed files with 486 additions and 74 deletions

View file

@ -40,9 +40,9 @@ class DiscoverCaptureTest {
rule.activity.window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
}
private fun capture(name: String, person: String = "justin", configure: (DiscoverState) -> Unit = {}) {
private fun capture(name: String, person: String = "justin", snapshotTransform: (DiscoverSnapshot) -> DiscoverSnapshot = { it }, configure: (DiscoverState) -> Unit = {}) {
val shell = ShellState(fixturePeople(), person).apply { pickerVisible = false; enterDestination("discover") }
val snapshot = fixture.snapshot(person)
val snapshot = snapshotTransform(fixture.snapshot(person))
val state = DiscoverState(shell, snapshot, RecordingDiscoverOperations(snapshot, person), CoroutineScope(Dispatchers.Main.immediate),
onPush = shell::enterDestination, onPop = { shell.enterDestination("discover") }, serviceLanes = fixture::service)
configure(state)
@ -61,6 +61,58 @@ class DiscoverCaptureTest {
}
@Test fun st029_discover() = capture("st-029")
@Test fun st030_picksCollection() = capture("st-030") { state -> state.openCollection(state.lanes.first { it.id == "pk-movie" }) }
@Test fun st031_hidden() = capture("st-031") { hidden(it) }
@Test fun st032_restore() = capture("st-032") { state -> hidden(state); state.overlay = DiscoverOverlayState.Restore(state.snapshot.picks[7]) }
@Test fun st033_failedBadge() = capture("st-033") { state ->
val lane = state.lanes.first { it.id == "pk-movie" }
state.requests[lane.titles[1].key] = DiscoverRequestResult(RequestPhase.Receipt, "Justin", "Failed")
state.focus(state.lanes.indexOf(lane) + 2, 1)
}
@Test fun st034_retryTail() = capture("st-034", snapshotTransform = { snapshot ->
snapshot.copy(lanes = snapshot.lanes.map { if (it.id == "trending") it.copy(tail = DiscoverTail.Retry) else it })
}) { state -> val lane = state.lanes.first { it.id == "trending" }; state.focus(state.lanes.indexOf(lane) + 2, lane.titles.size) }
@Test fun st035_duplicateTail() = capture("st-035") { state ->
val titles = state.snapshot.lanes.first { it.id == "trending" }.fullTitles.take(36)
state.openCollection(DiscoverLane("trending", "Trending", titles, tail = DiscoverTail.ShowMore))
state.collection!!.focus(6, 0)
}
@Test fun st036_needsSignIn() = capture("st-036", snapshotTransform = { DiscoverSnapshot(status = DiscoverStatus.NeedsSignIn) })
@Test fun st037_series() = capture("st-037", person = "mindel") { it.filter = "tv" }
@Test fun st038_noSeeds() = capture("st-038", snapshotTransform = { it.copy(status = DiscoverStatus.NoSeeds) })
@Test fun st039_trendingCollection() = capture("st-039") { state -> state.openCollection(state.lanes.first { it.id == "trending" }) }
@Test fun st040_service() = capture("st-040") { state -> state.openService(state.snapshot.services.first()) }
@Test fun st041_unreachable() = capture("st-041", snapshotTransform = { DiscoverSnapshot(status = DiscoverStatus.Unavailable) })
@Test fun st058_hiddenReceipt() = capture("st-058") { state -> state.openTitle(state.snapshot.picks.first()); state.titleButton = 2; state.hide(state.title!!) }
@Test fun st059_hiddenTitle() = capture("st-059") { state -> state.hide(state.snapshot.picks.first()); state.overlay = null; state.openTitle(state.snapshot.picks.first()); state.titleButton = 2 }
@Test fun st071_requestSelf() = capture("st-071") { request(it) }
@Test fun st072_requestOther() = capture("st-072") { request(it, other = true) }
@Test fun st073_addHeld() = capture("st-073") { state -> request(state, fixture.find("The Girl Who Leapt Through Time"), other = true) }
@Test fun st074_receipt() = capture("st-074") { request(it, other = true, phase = RequestPhase.Receipt) }
@Test fun st075_notConfirmed() = capture("st-075") { request(it, phase = RequestPhase.NotConfirmed) }
@Test fun st076_pending() = capture("st-076") { request(it, phase = RequestPhase.Pending) }
@Test fun st077_refused() = capture("st-077", person = "mindel") { request(it, other = true, phase = RequestPhase.Refused) }
@Test fun st078_recipientsLoading() = capture("st-078") { request(it, other = true, phase = RequestPhase.LoadingRecipients) }
@Test fun st079_recipientsUnavailable() = capture("st-079") { request(it, other = true, phase = RequestPhase.RecipientsUnavailable) }
@Test fun st080_seasonsUnknown() = capture("st-080") { state -> request(state, state.snapshot.picks.first { it.type == "tv" }) }
@Test fun st087_sharedReceipt() = capture("st-087", person = "shared") { request(it, phase = RequestPhase.Receipt) }
@Test fun st105_pickPlate() = capture("st-105") { state -> val lane = state.lanes.first { it.id == "pk-movie" }; state.focus(state.lanes.indexOf(lane) + 2, 1) }
@Test fun st110_heldRatings() = capture("st-110") { state -> request(state, fixture.find("The Girl Who Leapt Through Time"), other = true) }
private fun hidden(state: DiscoverState) {
state.hide(state.snapshot.picks[7]); state.hide(state.snapshot.picks[20]); state.overlay = null
val lane = state.lanes.first { it.hidden }
state.focus(state.lanes.indexOf(lane) + 2, lane.titles.indexOfFirst { it.key == state.snapshot.picks[7].key })
}
private fun request(state: DiscoverState, title: DiscoverTitle = state.snapshot.picks.first(), other: Boolean = false, phase: RequestPhase = RequestPhase.Choose) {
state.openTitle(title); state.openRequest(other, phase = phase)
if (phase == RequestPhase.Receipt) {
val sheet = (state.overlay as DiscoverOverlayState.Request).state
val receipt = DiscoverRequestResult(phase, state.shell.person.name, requester = sheet.selectedPerson.key)
sheet.result = receipt; state.requests[title.key] = receipt
}
}
}
internal fun discoverTestFixture(): DiscoverFixture {

View file

@ -7,6 +7,7 @@ import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.KeyEvent
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performKeyInput
import androidx.compose.ui.test.pressKey
import kotlinx.coroutines.CoroutineScope
@ -137,4 +138,46 @@ class DiscoverKeysTest {
press(Key.DirectionCenter)
assertNotNull(state.title)
}
@Test fun downClampsToOccupiedCardsAndThenTheTail() {
shell.pickerVisible = false
val items = snapshot.picks.take(8)
state.openCollection(DiscoverLane("short", "Picks", items, tail = DiscoverTail.ShowMore))
launch()
repeat(5) { press(Key.DirectionRight) }
assertEquals(5, state.collection!!.column)
press(Key.DirectionDown)
assertEquals(1, state.collection!!.row)
assertEquals(1, state.collection!!.column)
assertEquals(items.last().key, state.collection!!.items[state.collection!!.index].key)
press(Key.DirectionDown, Key.DirectionRight)
assertEquals(2, state.collection!!.row)
assertEquals(0, state.collection!!.column)
assertTrue(state.collection!!.hasTail)
}
@Test fun collectionBackRestoresTheExactOrigin() {
shell.pickerVisible = false; shell.enterDestination("discover")
state.focus(3, 2)
val origin = shell.view
state.openCollection(state.lanes.first { it.id == "pk-movie" })
launch()
press(Key.DirectionDown, Key.Back)
assertNull(state.collection)
assertSame(origin, shell.view)
assertEquals(3, shell.view.row)
assertEquals(2, shell.view.cards[state.rowKey])
}
@Test
@Config(qualifiers = "nl-rNL-w960dp-h540dp-xhdpi")
fun dutchPlateUsesLocaleAndStaysOnOneLine() {
shell.pickerVisible = false
val title = snapshot.picks.first().copy(ratings = listOf(DiscoverRating("tmdb", 7.8)))
state.openCollection(DiscoverLane("locale", "Picks", listOf(title)))
launch()
rule.mainClock.advanceTimeBy(1000)
rule.onNodeWithText("7,8").assertExists()
assertEquals("7.8", formatDiscoverRating(title.ratings.single(), java.util.Locale.ENGLISH))
assertEquals("91%", formatDiscoverRating(DiscoverRating("rtCritics", 91.7), java.util.Locale.forLanguageTag("nl-NL")))
}
}