summaryrefslogtreecommitdiffhomepage
path: root/android/app/src
diff options
context:
space:
mode:
authorJonatan Rhodin <jonatan.rhodin@mullvad.net>2025-10-15 21:34:45 +0200
committerJonatan Rhodin <jonatan.rhodin@mullvad.net>2025-10-20 09:03:06 +0200
commit5df909dac31557f306d3ecec3e987b7b0adf083f (patch)
tree6c939c6566b71f1eb9d7770e9f4497f583fa5cea /android/app/src
parent95979120f049bfb13305290a900202304fb61c2f (diff)
downloadmullvadvpn-5df909dac31557f306d3ecec3e987b7b0adf083f.tar.xz
mullvadvpn-5df909dac31557f306d3ecec3e987b7b0adf083f.zip
Add scroll to selection tests
Diffstat (limited to 'android/app/src')
-rw-r--r--android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/data/DummyRelayItems.kt189
-rw-r--r--android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/data/DummyRelayListItem.kt83
-rw-r--r--android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SelectLocationScreenTest.kt94
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 \"+\""
}