summaryrefslogtreecommitdiffhomepage
path: root/android/app/src/androidTest
diff options
context:
space:
mode:
authorDavid Göransson <david.goransson@mullvad.net>2025-03-31 09:36:52 +0200
committerJonatan Rhodin <jonatan.rhodin@mullvad.net>2025-04-10 17:29:33 +0200
commit042820a80d994a09a58dfcdc7dce1ee1d891ac39 (patch)
treeb35efcceebded11d98cec4adc08a52febb2eedf8 /android/app/src/androidTest
parent1c5712f028250920fe34ce7686c77a7d80da9481 (diff)
downloadmullvadvpn-042820a80d994a09a58dfcdc7dce1ee1d891ac39.tar.xz
mullvadvpn-042820a80d994a09a58dfcdc7dce1ee1d891ac39.zip
Implement quick access to active features
- Add Daita: Multihop feature indicator - Make feature indicators clickable - Add animations when accessing the features through the indicators - Rework VpnSettings in order to support navigating to a feature in the list
Diffstat (limited to 'android/app/src/androidTest')
-rw-r--r--android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt46
-rw-r--r--android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreenTest.kt136
2 files changed, 133 insertions, 49 deletions
diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt
index 4b5ea29fd6..8fefd1e33d 100644
--- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt
+++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt
@@ -25,6 +25,7 @@ import net.mullvad.mullvadvpn.compose.test.TOP_BAR_ACCOUNT_BUTTON
import net.mullvad.mullvadvpn.lib.model.ActionAfterDisconnect
import net.mullvad.mullvadvpn.lib.model.ErrorState
import net.mullvad.mullvadvpn.lib.model.ErrorStateCause
+import net.mullvad.mullvadvpn.lib.model.FeatureIndicator
import net.mullvad.mullvadvpn.lib.model.GeoIpLocation
import net.mullvad.mullvadvpn.lib.model.InAppNotification
import net.mullvad.mullvadvpn.lib.model.TransportProtocol
@@ -70,6 +71,7 @@ class ConnectScreenTest {
onDismissNewDeviceClick: () -> Unit = {},
onChangelogClick: () -> Unit = {},
onDismissChangelogClick: () -> Unit = {},
+ onNavigateToFeature: (FeatureIndicator) -> Unit = {},
) {
setContentWithTheme {
ConnectScreen(
@@ -86,6 +88,7 @@ class ConnectScreenTest {
onDismissNewDeviceClick = onDismissNewDeviceClick,
onChangelogClick = onChangelogClick,
onDismissChangelogClick = onDismissChangelogClick,
+ onNavigateToFeature = onNavigateToFeature,
)
}
}
@@ -801,4 +804,47 @@ class ConnectScreenTest {
onNodeWithText(outIpv6).assertExists()
}
}
+
+ @Test
+ fun clickOnFeatureIndicator() {
+ composeExtension.use {
+ // Arrange
+ val mockLocation: GeoIpLocation = mockk(relaxed = true)
+ val mockTunnelEndpoint: TunnelEndpoint = mockk(relaxed = true)
+ val mockHostName = "Host-Name"
+ every { mockLocation.hostname } returns mockHostName
+ every { mockLocation.entryHostname } returns null
+
+ // In
+ every { mockTunnelEndpoint.obfuscation } returns null
+
+ val mockClickHandler = mockk<(FeatureIndicator) -> Unit>(relaxed = true)
+
+ initScreen(
+ state =
+ ConnectUiState(
+ location = mockLocation,
+ selectedRelayItemTitle = null,
+ tunnelState =
+ TunnelState.Connected(
+ mockTunnelEndpoint,
+ mockLocation,
+ listOf(FeatureIndicator.MULTIHOP),
+ ),
+ showLocation = false,
+ deviceName = "",
+ daysLeftUntilExpiry = null,
+ inAppNotification = null,
+ isPlayBuild = false,
+ ),
+ onNavigateToFeature = mockClickHandler,
+ )
+
+ // Act
+ onNodeWithText("Multihop").performClick()
+
+ // Assert
+ verify(exactly = 1) { mockClickHandler.invoke(FeatureIndicator.MULTIHOP) }
+ }
+ }
}
diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreenTest.kt
index 963762c246..0eda50c8e6 100644
--- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreenTest.kt
+++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreenTest.kt
@@ -13,7 +13,6 @@ import io.mockk.mockk
import io.mockk.verify
import net.mullvad.mullvadvpn.compose.createEdgeToEdgeComposeExtension
import net.mullvad.mullvadvpn.compose.setContentWithTheme
-import net.mullvad.mullvadvpn.compose.state.VpnSettingsUiState
import net.mullvad.mullvadvpn.compose.test.LAZY_LIST_LAST_ITEM_TEST_TAG
import net.mullvad.mullvadvpn.compose.test.LAZY_LIST_QUANTUM_ITEM_OFF_TEST_TAG
import net.mullvad.mullvadvpn.compose.test.LAZY_LIST_QUANTUM_ITEM_ON_TEST_TAG
@@ -23,6 +22,7 @@ import net.mullvad.mullvadvpn.compose.test.LAZY_LIST_WIREGUARD_CUSTOM_PORT_TEXT_
import net.mullvad.mullvadvpn.compose.test.LAZY_LIST_WIREGUARD_OBFUSCATION_TITLE_TEST_TAG
import net.mullvad.mullvadvpn.compose.test.LAZY_LIST_WIREGUARD_PORT_ITEM_X_TEST_TAG
import net.mullvad.mullvadvpn.lib.model.Constraint
+import net.mullvad.mullvadvpn.lib.model.DefaultDnsOptions
import net.mullvad.mullvadvpn.lib.model.IpVersion
import net.mullvad.mullvadvpn.lib.model.Mtu
import net.mullvad.mullvadvpn.lib.model.ObfuscationMode
@@ -31,6 +31,7 @@ import net.mullvad.mullvadvpn.lib.model.PortRange
import net.mullvad.mullvadvpn.lib.model.QuantumResistantState
import net.mullvad.mullvadvpn.onNodeWithTagAndText
import net.mullvad.mullvadvpn.viewmodel.CustomDnsItem
+import net.mullvad.mullvadvpn.viewmodel.VpnSettingsUiState
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.RegisterExtension
@@ -44,8 +45,49 @@ class VpnSettingsScreenTest {
MockKAnnotations.init(this)
}
+ private fun createDefaultUiState(
+ mtu: Mtu? = null,
+ isLocalNetworkSharingEnabled: Boolean = false,
+ isCustomDnsEnabled: Boolean = false,
+ customDnsItems: List<CustomDnsItem> = emptyList(),
+ contentBlockersOptions: DefaultDnsOptions = DefaultDnsOptions(),
+ obfuscationMode: ObfuscationMode = ObfuscationMode.Auto,
+ selectedUdp2TcpObfuscationPort: Constraint<Port> = Constraint.Any,
+ selectedShadowsocksObfuscationPort: Constraint<Port> = Constraint.Any,
+ quantumResistant: QuantumResistantState = QuantumResistantState.Auto,
+ selectedWireguardPort: Constraint<Port> = Constraint.Any,
+ customWireguardPort: Port? = null,
+ availablePortRanges: List<PortRange> = emptyList(),
+ systemVpnSettingsAvailable: Boolean = true,
+ autoStartAndConnectOnBoot: Boolean = false,
+ deviceIpVersion: Constraint<IpVersion> = Constraint.Any,
+ isIpv6Enabled: Boolean = true,
+ isContentBlockersExpanded: Boolean = false,
+ isModal: Boolean = false,
+ ) =
+ VpnSettingsUiState.Content.from(
+ mtu = mtu,
+ isLocalNetworkSharingEnabled = isLocalNetworkSharingEnabled,
+ isCustomDnsEnabled = isCustomDnsEnabled,
+ customDnsItems = customDnsItems,
+ contentBlockersOptions = contentBlockersOptions,
+ obfuscationMode = obfuscationMode,
+ selectedUdp2TcpObfuscationPort = selectedUdp2TcpObfuscationPort,
+ selectedShadowsocksObfuscationPort = selectedShadowsocksObfuscationPort,
+ quantumResistant = quantumResistant,
+ selectedWireguardPort = selectedWireguardPort,
+ customWireguardPort = customWireguardPort,
+ availablePortRanges = availablePortRanges,
+ systemVpnSettingsAvailable = systemVpnSettingsAvailable,
+ autoStartAndConnectOnBoot = autoStartAndConnectOnBoot,
+ deviceIpVersion = deviceIpVersion,
+ isIpv6Enabled = isIpv6Enabled,
+ isContentBlockersExpanded = isContentBlockersExpanded,
+ isModal = isModal,
+ )
+
private fun ComposeContext.initScreen(
- state: VpnSettingsUiState = VpnSettingsUiState.createDefault(),
+ state: VpnSettingsUiState = createDefaultUiState(),
navigateToContentBlockersInfo: () -> Unit = {},
navigateToAutoConnectScreen: () -> Unit = {},
navigateToCustomDnsInfo: () -> Unit = {},
@@ -54,7 +96,7 @@ class VpnSettingsScreenTest {
navigateToQuantumResistanceInfo: () -> Unit = {},
navigateToWireguardPortInfo: (availablePortRanges: List<PortRange>) -> Unit = {},
navigateToLocalNetworkSharingInfo: () -> Unit = {},
- navigateToWireguardPortDialog: () -> Unit = {},
+ navigateToWireguardPortDialog: (Port?, List<PortRange>) -> Unit = { _, _ -> },
navigateToServerIpOverrides: () -> Unit = {},
onToggleBlockTrackers: (Boolean) -> Unit = {},
onToggleBlockAds: (Boolean) -> Unit = {},
@@ -76,6 +118,7 @@ class VpnSettingsScreenTest {
onSelectDeviceIpVersion: (Constraint<IpVersion>) -> Unit = {},
onToggleIpv6: (Boolean) -> Unit = {},
navigateToIpv6Info: () -> Unit = {},
+ onToggleDnsContentBlockers: () -> Unit = {},
navigateToDeviceIpInfo: () -> Unit = {},
) {
setContentWithTheme {
@@ -111,7 +154,9 @@ class VpnSettingsScreenTest {
onSelectDeviceIpVersion = onSelectDeviceIpVersion,
onToggleIpv6 = onToggleIpv6,
navigateToIpv6Info = navigateToIpv6Info,
+ onToggleContentBlockersExpanded = onToggleDnsContentBlockers,
navigateToDeviceIpInfo = navigateToDeviceIpInfo,
+ initialScrollToFeature = null,
)
}
}
@@ -120,7 +165,7 @@ class VpnSettingsScreenTest {
fun testDefaultState() =
composeExtension.use {
// Arrange
- initScreen(state = VpnSettingsUiState.createDefault())
+ initScreen()
onNodeWithTag(LAZY_LIST_VPN_SETTINGS_TEST_TAG)
.performScrollToNode(hasTestTag(LAZY_LIST_LAST_ITEM_TEST_TAG))
@@ -138,9 +183,7 @@ class VpnSettingsScreenTest {
// Arrange
initScreen(
state =
- VpnSettingsUiState.createDefault(
- mtu = Mtu.fromString(VALID_DUMMY_MTU_VALUE).getOrNull()!!
- )
+ createDefaultUiState(mtu = Mtu.fromString(VALID_DUMMY_MTU_VALUE).getOrNull()!!)
)
onNodeWithTag(LAZY_LIST_VPN_SETTINGS_TEST_TAG)
@@ -156,7 +199,7 @@ class VpnSettingsScreenTest {
// Arrange
initScreen(
state =
- VpnSettingsUiState.createDefault(
+ createDefaultUiState(
isCustomDnsEnabled = true,
customDnsItems =
listOf(
@@ -180,7 +223,7 @@ class VpnSettingsScreenTest {
// Arrange
initScreen(
state =
- VpnSettingsUiState.createDefault(
+ createDefaultUiState(
isCustomDnsEnabled = false,
customDnsItems =
listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, false, false)),
@@ -199,7 +242,7 @@ class VpnSettingsScreenTest {
// Arrange
initScreen(
state =
- VpnSettingsUiState.createDefault(
+ createDefaultUiState(
isCustomDnsEnabled = true,
isLocalNetworkSharingEnabled = true,
customDnsItems =
@@ -223,7 +266,7 @@ class VpnSettingsScreenTest {
// Arrange
initScreen(
state =
- VpnSettingsUiState.createDefault(
+ createDefaultUiState(
isCustomDnsEnabled = true,
customDnsItems =
listOf(
@@ -246,7 +289,7 @@ class VpnSettingsScreenTest {
// Arrange
initScreen(
state =
- VpnSettingsUiState.createDefault(
+ createDefaultUiState(
isCustomDnsEnabled = true,
customDnsItems =
listOf(
@@ -269,7 +312,7 @@ class VpnSettingsScreenTest {
// Arrange
initScreen(
state =
- VpnSettingsUiState.createDefault(
+ createDefaultUiState(
isCustomDnsEnabled = true,
customDnsItems =
listOf(
@@ -290,10 +333,7 @@ class VpnSettingsScreenTest {
fun testShowSelectedTunnelQuantumOption() =
composeExtension.use {
// Arrange
- initScreen(
- state =
- VpnSettingsUiState.createDefault(quantumResistant = QuantumResistantState.On)
- )
+ initScreen(state = createDefaultUiState(quantumResistant = QuantumResistantState.On))
onNodeWithTag(LAZY_LIST_VPN_SETTINGS_TEST_TAG)
.performScrollToNode(hasTestTag(LAZY_LIST_QUANTUM_ITEM_OFF_TEST_TAG))
@@ -309,8 +349,7 @@ class VpnSettingsScreenTest {
val mockSelectQuantumResistantSettingListener: (QuantumResistantState) -> Unit =
mockk(relaxed = true)
initScreen(
- state =
- VpnSettingsUiState.createDefault(quantumResistant = QuantumResistantState.Auto),
+ state = createDefaultUiState(quantumResistant = QuantumResistantState.Auto),
onSelectQuantumResistanceSetting = mockSelectQuantumResistantSettingListener,
)
onNodeWithTag(LAZY_LIST_VPN_SETTINGS_TEST_TAG)
@@ -329,10 +368,7 @@ class VpnSettingsScreenTest {
composeExtension.use {
// Arrange
initScreen(
- state =
- VpnSettingsUiState.createDefault(
- selectedWireguardPort = Constraint.Only(Port(53))
- )
+ state = createDefaultUiState(selectedWireguardPort = Constraint.Only(Port(53)))
)
// Act
@@ -356,10 +392,7 @@ class VpnSettingsScreenTest {
val mockSelectWireguardPortSelectionListener: (Constraint<Port>) -> Unit =
mockk(relaxed = true)
initScreen(
- state =
- VpnSettingsUiState.createDefault(
- selectedWireguardPort = Constraint.Only(Port(53))
- ),
+ state = createDefaultUiState(selectedWireguardPort = Constraint.Only(Port(53))),
onWireguardPortSelected = mockSelectWireguardPortSelectionListener,
)
@@ -384,7 +417,7 @@ class VpnSettingsScreenTest {
fun testShowWireguardCustomPort() =
composeExtension.use {
// Arrange
- initScreen(state = VpnSettingsUiState.createDefault(customWireguardPort = Port(4000)))
+ initScreen(state = createDefaultUiState(customWireguardPort = Port(4000)))
// Act
onNodeWithTag(LAZY_LIST_VPN_SETTINGS_TEST_TAG)
@@ -401,7 +434,7 @@ class VpnSettingsScreenTest {
val onWireguardPortSelected: (Constraint<Port>) -> Unit = mockk(relaxed = true)
initScreen(
state =
- VpnSettingsUiState.createDefault(
+ createDefaultUiState(
selectedWireguardPort = Constraint.Only(Port(4000)),
customWireguardPort = Port(4000),
),
@@ -424,10 +457,7 @@ class VpnSettingsScreenTest {
composeExtension.use {
// Arrange
val mockedClickHandler: (Mtu?) -> Unit = mockk(relaxed = true)
- initScreen(
- state = VpnSettingsUiState.createDefault(),
- navigateToMtuDialog = mockedClickHandler,
- )
+ initScreen(state = createDefaultUiState(), navigateToMtuDialog = mockedClickHandler)
onNodeWithTag(LAZY_LIST_VPN_SETTINGS_TEST_TAG)
.performScrollToNode(hasTestTag(LAZY_LIST_LAST_ITEM_TEST_TAG))
@@ -446,7 +476,7 @@ class VpnSettingsScreenTest {
val mockedClickHandler: (Int?, String?) -> Unit = mockk(relaxed = true)
initScreen(
state =
- VpnSettingsUiState.createDefault(
+ createDefaultUiState(
isCustomDnsEnabled = true,
customDnsItems = listOf(CustomDnsItem("1.1.1.1", false, false)),
),
@@ -467,7 +497,7 @@ class VpnSettingsScreenTest {
// Arrange
initScreen(
- state = VpnSettingsUiState.createDefault(),
+ state = createDefaultUiState(),
navigateToObfuscationInfo = mockedNavigateToObfuscationInfo,
)
@@ -487,7 +517,7 @@ class VpnSettingsScreenTest {
// Arrange
initScreen(
- state = VpnSettingsUiState.createDefault(),
+ state = createDefaultUiState(),
navigateToQuantumResistanceInfo = mockedShowTunnelQuantumInfoClick,
)
@@ -507,7 +537,7 @@ class VpnSettingsScreenTest {
// Arrange
initScreen(
- state = VpnSettingsUiState.createDefault(),
+ state = createDefaultUiState(),
navigateToWireguardPortInfo = mockedClickHandler,
)
@@ -519,11 +549,13 @@ class VpnSettingsScreenTest {
@Test
fun testShowWireguardCustomPortDialog() =
composeExtension.use {
- val mockedClickHandler: () -> Unit = mockk(relaxed = true)
+ val mockedClickHandler: (Port?, List<PortRange>) -> Unit = mockk(relaxed = true)
+
+ val availablePortRanges = listOf(Port(4000)..Port(5000))
// Arrange
initScreen(
- state = VpnSettingsUiState.createDefault(),
+ state = createDefaultUiState(availablePortRanges = availablePortRanges),
navigateToWireguardPortDialog = mockedClickHandler,
)
@@ -532,16 +564,17 @@ class VpnSettingsScreenTest {
onNodeWithText("Custom").performClick()
// Assert
- verify(exactly = 1) { mockedClickHandler.invoke() }
+ verify(exactly = 1) { mockedClickHandler.invoke(null, availablePortRanges) }
}
@Test
fun testClickWireguardCustomPortMainCell() =
composeExtension.use {
// Arrange
- val mockOnShowCustomPortDialog: () -> Unit = mockk(relaxed = true)
+ val mockOnShowCustomPortDialog: (Port?, List<PortRange>) -> Unit = mockk(relaxed = true)
+ val availablePortRanges = listOf(Port(4000)..Port(5000))
initScreen(
- state = VpnSettingsUiState.createDefault(),
+ state = createDefaultUiState(availablePortRanges = availablePortRanges),
navigateToWireguardPortDialog = mockOnShowCustomPortDialog,
)
@@ -551,18 +584,23 @@ class VpnSettingsScreenTest {
onNodeWithTag(LAZY_LIST_WIREGUARD_CUSTOM_PORT_TEXT_TEST_TAG).performClick()
// Assert
- verify { mockOnShowCustomPortDialog.invoke() }
+ verify { mockOnShowCustomPortDialog.invoke(null, availablePortRanges) }
}
@Test
fun testClickWireguardCustomPortNumberCell() =
composeExtension.use {
// Arrange
- val mockOnShowCustomPortDialog: () -> Unit = mockk(relaxed = true)
+ val mockOnShowCustomPortDialog: (port: Port?, range: List<PortRange>) -> Unit =
+ mockk(relaxed = true)
+ val customPort = Port(4000)
+ val availablePortRanges = listOf(Port(4000)..Port(5000))
initScreen(
state =
- VpnSettingsUiState.createDefault(
- selectedWireguardPort = Constraint.Only(Port(4000))
+ createDefaultUiState(
+ selectedWireguardPort = Constraint.Only(customPort),
+ customWireguardPort = customPort,
+ availablePortRanges = availablePortRanges,
),
navigateToWireguardPortDialog = mockOnShowCustomPortDialog,
)
@@ -573,14 +611,14 @@ class VpnSettingsScreenTest {
onNodeWithTag(testTag = LAZY_LIST_WIREGUARD_CUSTOM_PORT_NUMBER_TEST_TAG).performClick()
// Assert
- verify { mockOnShowCustomPortDialog.invoke() }
+ verify { mockOnShowCustomPortDialog.invoke(customPort, availablePortRanges) }
}
@Test
fun ensureConnectOnStartIsShownWhenSystemVpnSettingsAvailableIsFalse() =
composeExtension.use {
// Arrange
- initScreen(state = VpnSettingsUiState.createDefault(systemVpnSettingsAvailable = false))
+ initScreen(state = createDefaultUiState(systemVpnSettingsAvailable = false))
// Assert
onNodeWithText("Connect on device start-up").assertExists()
@@ -593,7 +631,7 @@ class VpnSettingsScreenTest {
val mockOnToggleAutoStartAndConnectOnBoot: (Boolean) -> Unit = mockk(relaxed = true)
initScreen(
state =
- VpnSettingsUiState.createDefault(
+ createDefaultUiState(
systemVpnSettingsAvailable = false,
autoStartAndConnectOnBoot = false,
),