diff options
| author | Jonatan Rhodin <jonatan.rhodin@mullvad.net> | 2025-10-15 21:34:45 +0200 |
|---|---|---|
| committer | Jonatan Rhodin <jonatan.rhodin@mullvad.net> | 2025-10-20 09:03:06 +0200 |
| commit | 5df909dac31557f306d3ecec3e987b7b0adf083f (patch) | |
| tree | 6c939c6566b71f1eb9d7770e9f4497f583fa5cea /android/app/src | |
| parent | 95979120f049bfb13305290a900202304fb61c2f (diff) | |
| download | mullvadvpn-5df909dac31557f306d3ecec3e987b7b0adf083f.tar.xz mullvadvpn-5df909dac31557f306d3ecec3e987b7b0adf083f.zip | |
Add scroll to selection tests
Diffstat (limited to 'android/app/src')
3 files changed, 365 insertions, 1 deletions
diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/data/DummyRelayItems.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/data/DummyRelayItems.kt index c6c69e088b..0e24d25f41 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/data/DummyRelayItems.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/data/DummyRelayItems.kt @@ -7,6 +7,7 @@ import net.mullvad.mullvadvpn.lib.model.GeoLocationId import net.mullvad.mullvadvpn.lib.model.Ownership import net.mullvad.mullvadvpn.lib.model.PortRange import net.mullvad.mullvadvpn.lib.model.ProviderId +import net.mullvad.mullvadvpn.lib.model.Quic import net.mullvad.mullvadvpn.lib.model.RelayItem import net.mullvad.mullvadvpn.lib.model.RelayList import net.mullvad.mullvadvpn.lib.model.WireguardEndpointData @@ -39,6 +40,146 @@ private val DUMMY_RELAY_2 = quic = null, lwo = false, ) +private val DUMMY_RELAY_3 = + RelayItem.Location.Relay( + id = + GeoLocationId.Hostname( + city = GeoLocationId.City(GeoLocationId.Country("RCo3"), "Relay City 3"), + "Relay host 3", + ), + active = true, + provider = ProviderId("PROVIDER OWNED"), + ownership = Ownership.MullvadOwned, + daita = true, + quic = null, + lwo = true, + ) +private val DUMMY_RELAY_4 = + RelayItem.Location.Relay( + id = + GeoLocationId.Hostname( + city = GeoLocationId.City(GeoLocationId.Country("RCo4"), "Relay City 4"), + "Relay host 4", + ), + active = true, + provider = ProviderId("PROVIDER OWNED"), + ownership = Ownership.MullvadOwned, + daita = false, + quic = Quic(inAddresses = listOf()), + lwo = true, + ) +private val DUMMY_RELAY_5 = + RelayItem.Location.Relay( + id = + GeoLocationId.Hostname( + city = GeoLocationId.City(GeoLocationId.Country("RCo4"), "Relay City 5"), + "Relay host 5", + ), + active = true, + provider = ProviderId("PROVIDER RENTED"), + ownership = Ownership.Rented, + daita = false, + quic = Quic(inAddresses = listOf()), + lwo = true, + ) +private val DUMMY_RELAY_6 = + RelayItem.Location.Relay( + id = + GeoLocationId.Hostname( + city = GeoLocationId.City(GeoLocationId.Country("RCo4"), "Relay City 5"), + "Relay host 6", + ), + active = true, + provider = ProviderId("PROVIDER RENTED"), + ownership = Ownership.Rented, + daita = true, + quic = Quic(inAddresses = listOf()), + lwo = true, + ) +private val DUMMY_RELAY_7 = + RelayItem.Location.Relay( + id = + GeoLocationId.Hostname( + city = GeoLocationId.City(GeoLocationId.Country("RCo4"), "Relay City 5"), + "Relay host 7", + ), + active = true, + provider = ProviderId("PROVIDER RENTED"), + ownership = Ownership.Rented, + daita = false, + quic = Quic(inAddresses = listOf()), + lwo = false, + ) +private val DUMMY_RELAY_8 = + RelayItem.Location.Relay( + id = + GeoLocationId.Hostname( + city = GeoLocationId.City(GeoLocationId.Country("RCo4"), "Relay City 5"), + "Relay host 8", + ), + active = true, + provider = ProviderId("PROVIDER RENTED"), + ownership = Ownership.Rented, + daita = false, + quic = null, + lwo = false, + ) +private val DUMMY_RELAY_9 = + RelayItem.Location.Relay( + id = + GeoLocationId.Hostname( + city = GeoLocationId.City(GeoLocationId.Country("RCo4"), "Relay City 5"), + "Relay host 9", + ), + active = true, + provider = ProviderId("PROVIDER RENTED"), + ownership = Ownership.Rented, + daita = true, + quic = null, + lwo = true, + ) +private val DUMMY_RELAY_10 = + RelayItem.Location.Relay( + id = + GeoLocationId.Hostname( + city = GeoLocationId.City(GeoLocationId.Country("RCo4"), "Relay City 5"), + "Relay host 10", + ), + active = true, + provider = ProviderId("PROVIDER OWNED"), + ownership = Ownership.MullvadOwned, + daita = true, + quic = null, + lwo = false, + ) +private val DUMMY_RELAY_11 = + RelayItem.Location.Relay( + id = + GeoLocationId.Hostname( + city = GeoLocationId.City(GeoLocationId.Country("RCo4"), "Relay City 5"), + "Relay host 11", + ), + active = true, + provider = ProviderId("PROVIDER OWNED"), + ownership = Ownership.MullvadOwned, + daita = false, + quic = Quic(inAddresses = listOf()), + lwo = false, + ) +private val DUMMY_RELAY_12 = + RelayItem.Location.Relay( + id = + GeoLocationId.Hostname( + city = GeoLocationId.City(GeoLocationId.Country("RCo4"), "Relay City 5"), + "Relay host 12", + ), + active = true, + provider = ProviderId("PROVIDER OWNED"), + ownership = Ownership.MullvadOwned, + daita = false, + quic = Quic(inAddresses = listOf()), + lwo = true, + ) private val DUMMY_RELAY_CITY_1 = RelayItem.Location.City( name = "Relay City 1", @@ -51,6 +192,34 @@ private val DUMMY_RELAY_CITY_2 = id = GeoLocationId.City(country = GeoLocationId.Country("RCo2"), code = "RCi2"), relays = listOf(DUMMY_RELAY_2), ) +private val DUMMY_RELAY_CITY_3 = + RelayItem.Location.City( + name = "Relay City 3", + id = GeoLocationId.City(country = GeoLocationId.Country("RCo3"), code = "RCi3"), + relays = listOf(DUMMY_RELAY_3), + ) +private val DUMMY_RELAY_CITY_4 = + RelayItem.Location.City( + name = "Relay City 4", + id = GeoLocationId.City(country = GeoLocationId.Country("RCo4"), code = "RCi4"), + relays = listOf(DUMMY_RELAY_4), + ) +private val DUMMY_RELAY_CITY_5 = + RelayItem.Location.City( + name = "Relay City 5", + id = GeoLocationId.City(country = GeoLocationId.Country("RCo4"), code = "RCi5"), + relays = + listOf( + DUMMY_RELAY_5, + DUMMY_RELAY_6, + DUMMY_RELAY_7, + DUMMY_RELAY_8, + DUMMY_RELAY_9, + DUMMY_RELAY_10, + DUMMY_RELAY_11, + DUMMY_RELAY_12, + ), + ) private val DUMMY_RELAY_COUNTRY_1 = RelayItem.Location.Country( name = "Relay Country 1", @@ -63,13 +232,31 @@ private val DUMMY_RELAY_COUNTRY_2 = id = GeoLocationId.Country("RCo2"), cities = listOf(DUMMY_RELAY_CITY_2), ) +private val DUMMY_RELAY_COUNTRY_3 = + RelayItem.Location.Country( + name = "Relay Country 3", + id = GeoLocationId.Country("RCo3"), + cities = listOf(DUMMY_RELAY_CITY_3), + ) +private val DUMMY_RELAY_COUNTRY_4 = + RelayItem.Location.Country( + name = "Relay Country 4", + id = GeoLocationId.Country("RCo4"), + cities = listOf(DUMMY_RELAY_CITY_4, DUMMY_RELAY_CITY_5), + ) private val DUMMY_WIREGUARD_PORT_RANGES = ArrayList<PortRange>() private val DUMMY_SHADOWSOCKS_PORT_RANGES = emptyList<PortRange>() private val DUMMY_WIREGUARD_ENDPOINT_DATA = WireguardEndpointData(DUMMY_WIREGUARD_PORT_RANGES, DUMMY_SHADOWSOCKS_PORT_RANGES) -val DUMMY_RELAY_COUNTRIES = listOf(DUMMY_RELAY_COUNTRY_1, DUMMY_RELAY_COUNTRY_2) +val DUMMY_RELAY_COUNTRIES = + listOf( + DUMMY_RELAY_COUNTRY_1, + DUMMY_RELAY_COUNTRY_2, + DUMMY_RELAY_COUNTRY_3, + DUMMY_RELAY_COUNTRY_4, + ) val DUMMY_RELAY_LIST = RelayList(DUMMY_RELAY_COUNTRIES, DUMMY_WIREGUARD_ENDPOINT_DATA) diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/data/DummyRelayListItem.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/data/DummyRelayListItem.kt new file mode 100644 index 0000000000..b732594773 --- /dev/null +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/data/DummyRelayListItem.kt @@ -0,0 +1,83 @@ +package net.mullvad.mullvadvpn.compose.data + +import net.mullvad.mullvadvpn.lib.model.Hop +import net.mullvad.mullvadvpn.lib.model.RelayItem +import net.mullvad.mullvadvpn.lib.model.RelayItemId +import net.mullvad.mullvadvpn.lib.ui.component.relaylist.ItemPosition +import net.mullvad.mullvadvpn.lib.ui.component.relaylist.RelayListItem +import net.mullvad.mullvadvpn.relaylist.descendants + +@Suppress("CyclomaticComplexMethod") +fun createSimpleRelayListItemList( + recentItems: List<RelayItem.Location> = emptyList(), + customListItem: List<RelayItem.CustomList> = emptyList(), + locationItems: List<RelayItem.Location.Country> = emptyList(), + selectedItem: RelayItemId? = null, +): List<RelayListItem> = buildList { + if (recentItems.isNotEmpty()) { + add(RelayListItem.RecentsListHeader) + recentItems.forEach { + add(RelayListItem.RecentListItem(Hop.Single(it), isSelected = it.id == selectedItem)) + } + } + if (customListItem.isNotEmpty()) { + add(RelayListItem.CustomListHeader) + customListItem.forEach { + add(RelayListItem.CustomListItem(Hop.Single(it), isSelected = it.id == selectedItem)) + } + add(RelayListItem.CustomListFooter(hasCustomList = true)) + } + if (locationItems.isNotEmpty()) { + add(RelayListItem.LocationHeader) + locationItems.forEach { country -> + val descendantIsSelected = country.descendants().any { it.id == selectedItem } + add( + RelayListItem.GeoLocationItem( + hop = Hop.Single(country), + isSelected = country == selectedItem, + expanded = descendantIsSelected, + itemPosition = + if (descendantIsSelected) { + ItemPosition.Top + } else { + ItemPosition.Single + }, + ) + ) + if (descendantIsSelected) { + country.cities.forEach { city -> + val childIsSelected = city.relays.any { it.id == selectedItem } + add( + RelayListItem.GeoLocationItem( + hop = Hop.Single(city), + isSelected = city.id == selectedItem, + expanded = childIsSelected, + itemPosition = + if (country.cities.last() == city && !childIsSelected) { + ItemPosition.Bottom + } else { + ItemPosition.Middle + }, + ) + ) + if (childIsSelected) { + city.relays.forEach { relay -> + add( + RelayListItem.GeoLocationItem( + hop = Hop.Single(relay), + isSelected = relay.id == selectedItem, + itemPosition = + if (city.relays.last() == relay) { + ItemPosition.Bottom + } else { + ItemPosition.Middle + }, + ) + ) + } + } + } + } + } + } +} diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SelectLocationScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SelectLocationScreenTest.kt index 96dd9ae1cc..208cdd99da 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SelectLocationScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SelectLocationScreenTest.kt @@ -14,6 +14,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import net.mullvad.mullvadvpn.compose.createEdgeToEdgeComposeExtension import net.mullvad.mullvadvpn.compose.data.DUMMY_RELAY_COUNTRIES import net.mullvad.mullvadvpn.compose.data.DUMMY_RELAY_ITEM_CUSTOM_LISTS +import net.mullvad.mullvadvpn.compose.data.createSimpleRelayListItemList import net.mullvad.mullvadvpn.compose.setContentWithTheme import net.mullvad.mullvadvpn.compose.state.MultihopRelayListType import net.mullvad.mullvadvpn.compose.state.RelayListType @@ -24,8 +25,12 @@ import net.mullvad.mullvadvpn.lib.model.Hop import net.mullvad.mullvadvpn.lib.model.RelayItem import net.mullvad.mullvadvpn.lib.ui.component.relaylist.ItemPosition import net.mullvad.mullvadvpn.lib.ui.component.relaylist.RelayListItem +import net.mullvad.mullvadvpn.lib.ui.designsystem.RelayListItem +import net.mullvad.mullvadvpn.lib.ui.tag.GEOLOCATION_NAME_TAG +import net.mullvad.mullvadvpn.lib.ui.tag.RECENT_NAME_TAG import net.mullvad.mullvadvpn.lib.ui.tag.SELECT_LOCATION_CUSTOM_LIST_BOTTOM_SHEET_TEST_TAG import net.mullvad.mullvadvpn.lib.ui.tag.SELECT_LOCATION_LOCATION_BOTTOM_SHEET_TEST_TAG +import net.mullvad.mullvadvpn.onNodeWithTagAndText import net.mullvad.mullvadvpn.performLongClick import net.mullvad.mullvadvpn.util.Lc import net.mullvad.mullvadvpn.util.Lce @@ -342,6 +347,95 @@ class SelectLocationScreenTest { onNodeWithTag(SELECT_LOCATION_LOCATION_BOTTOM_SHEET_TEST_TAG).assertExists() } + @Test + fun whenOpeningScreenAndRecentsEnabledShouldScrollToTheSelectedRecent() { + composeExtension.use { + // Arrange + val selectableItem = DUMMY_RELAY_COUNTRIES[3].relays.last() + every { listViewModel.uiState } returns + MutableStateFlow( + Lce.Content( + SelectLocationListUiState( + relayListItems = + createSimpleRelayListItemList( + recentItems = listOf(selectableItem), + customListItem = DUMMY_RELAY_ITEM_CUSTOM_LISTS, + locationItems = DUMMY_RELAY_COUNTRIES, + selectedItem = selectableItem.id, + ), + customLists = DUMMY_RELAY_ITEM_CUSTOM_LISTS, + relayListType = RelayListType.Single, + ) + ) + ) + initScreen( + state = + Lc.Content( + SelectLocationUiState( + filterChips = emptyList(), + multihopEnabled = false, + relayListType = RelayListType.Single, + isSearchButtonEnabled = true, + isFilterButtonEnabled = true, + isRecentsEnabled = true, + ) + ) + ) + + // Assert + onNodeWithTagAndText( + testTag = RECENT_NAME_TAG, + text = selectableItem.name, + useUnmergedTree = true, + ) + .assertExists() + } + } + + @Test + fun whenOpeningScreenAndRecentsDisabledShouldScrollToTheSelectedLocation() { + composeExtension.use { + // Arrange + val selectableItem = DUMMY_RELAY_COUNTRIES[3].relays.last() + every { listViewModel.uiState } returns + MutableStateFlow( + Lce.Content( + SelectLocationListUiState( + relayListItems = + createSimpleRelayListItemList( + customListItem = DUMMY_RELAY_ITEM_CUSTOM_LISTS, + locationItems = DUMMY_RELAY_COUNTRIES, + selectedItem = selectableItem.id, + ), + customLists = DUMMY_RELAY_ITEM_CUSTOM_LISTS, + relayListType = RelayListType.Single, + ) + ) + ) + initScreen( + state = + Lc.Content( + SelectLocationUiState( + filterChips = emptyList(), + multihopEnabled = false, + relayListType = RelayListType.Single, + isSearchButtonEnabled = true, + isFilterButtonEnabled = true, + isRecentsEnabled = false, + ) + ) + ) + + // Assert + onNodeWithTagAndText( + testTag = GEOLOCATION_NAME_TAG, + text = selectableItem.name, + useUnmergedTree = true, + ) + .assertExists() + } + } + companion object { private const val CUSTOM_LISTS_EMPTY_TEXT = "To create a custom list press the \"+\"" } |
