Fix picker access and finish Wolfskin on root Back

This commit is contained in:
justinzeus 2026-11-07 18:02:17 +01:00
parent febcded034
commit 9cfe9362ee
11 changed files with 481 additions and 31 deletions

View file

@ -0,0 +1,71 @@
package org.justinvisser.wolfskin.design
import android.app.Application
import androidx.activity.ComponentActivity
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.test.*
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import org.junit.Assert.*
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import org.robolectric.annotation.GraphicsMode
/** Every profile reaches Settings vertically without losing its horizontal return position. */
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [34], application = Application::class, qualifiers = "w960dp-h540dp-xhdpi")
@GraphicsMode(GraphicsMode.Mode.NATIVE)
class PickerNavigationTest {
@get:Rule val rule = createAndroidComposeRule<ComponentActivity>()
@Test fun downFromEveryAvatarReachesSettingsAndUpReturnsToTheOrigin() {
val state = ShellState(fixturePeople(), "justin")
rule.setContent { WolfskinTheme { ShellSurface(state) } }
for (index in state.people.indices) {
rule.runOnIdle { state.pickerIndex = index; state.gearFocused = false }
press(Key.DirectionDown)
assertTrue("Down from profile $index must reach Settings", state.gearFocused)
press(Key.DirectionUp)
assertFalse(state.gearFocused)
assertEquals("Up must return to profile $index", index, state.pickerIndex)
}
}
@Test fun settingsBackThenUpKeepsTheAvatarThatOpenedTheGear() {
val state = ShellState(fixturePeople(), "justin")
rule.setContent { WolfskinTheme { ShellSurface(state) } }
for (index in state.people.indices) {
rule.runOnIdle { state.pickerIndex = index; state.gearFocused = false }
press(Key.DirectionDown)
press(Key.DirectionCenter)
assertEquals("settings", state.route)
press(Key.Back)
assertTrue(state.pickerVisible)
assertTrue(state.gearFocused)
press(Key.DirectionUp)
assertEquals(index, state.pickerIndex)
}
}
@Test fun settingsGearIsCenteredUnderTheFinalAvatarForAnyProfileCount() {
val state = ShellState(fixturePeople(), "justin")
rule.setContent { WolfskinTheme { ShellSurface(state) } }
for (count in 1..5) {
val people = (0 until count).map { ShellPerson("person-$it", "Person $it", "$it", false) }
rule.runOnIdle { state.updatePeople(people); state.gearFocused = true }
rule.mainClock.advanceTimeBy(400)
val last = rule.onNodeWithTag("picker-person:${people.last().key}").fetchSemanticsNode().boundsInRoot
val gear = rule.onNodeWithTag("picker-settings").fetchSemanticsNode().boundsInRoot
assertEquals("Settings must stay under the last profile for $count people", last.center.x, gear.center.x, .1f)
assertTrue("Settings must be below the avatars", gear.top > last.bottom)
}
}
private fun press(key: Key) {
rule.onNodeWithTag("wolfskin-shell").performKeyInput { pressKey(key) }
rule.mainClock.advanceTimeBy(400)
rule.waitForIdle()
}
}

View file

@ -60,16 +60,6 @@ class ShellKeysTest {
assertEquals(2, state.settingsRow)
}
@Test fun onlyLastPersonReachesGearAndUpReturnsThere() {
replay(listOf(Key.DirectionDown, Key.DirectionRight, Key.DirectionDown), 3)
assertFalse(state.gearFocused)
rule.onNodeWithTag("wolfskin-shell").performKeyInput { pressKey(Key.DirectionRight); pressKey(Key.DirectionDown) }
assertTrue(state.gearFocused)
rule.onNodeWithTag("wolfskin-shell").performKeyInput { pressKey(Key.DirectionUp) }
assertFalse(state.gearFocused)
assertEquals(2, state.pickerIndex)
}
@Test fun rootBackAndNavDownPreserveViewAndWrap() {
replay(listOf(Key.DirectionCenter), 1)
val view = state.view