summaryrefslogtreecommitdiffhomepage
path: root/android/test/common/src
diff options
context:
space:
mode:
authorKalle Lindström <karl.lindstrom@mullvad.net>2025-04-02 17:20:56 +0200
committerJonatan Rhodin <jonatan.rhodin@mullvad.net>2025-04-04 13:46:15 +0200
commitaf2a71e61c0e5c70716ddd82205583f69c80adf4 (patch)
tree65d12c88e684f15fd5b86928dcfb64f4b3ff1fed /android/test/common/src
parent1e902d3a873ce0a789de0b32cfcf52ab72dacbdf (diff)
downloadmullvadvpn-af2a71e61c0e5c70716ddd82205583f69c80adf4.tar.xz
mullvadvpn-af2a71e61c0e5c70716ddd82205583f69c80adf4.zip
Add shadowsocks obfuscation e2e test
Diffstat (limited to 'android/test/common/src')
-rw-r--r--android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/extension/UiAutomatorExtensions.kt49
-rw-r--r--android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/interactor/AppInteractor.kt15
-rw-r--r--android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/Page.kt2
-rw-r--r--android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/Story.kt50
-rw-r--r--android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/VpnSettingsPage.kt13
5 files changed, 102 insertions, 27 deletions
diff --git a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/extension/UiAutomatorExtensions.kt b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/extension/UiAutomatorExtensions.kt
index ff17a9f497..cb186463cd 100644
--- a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/extension/UiAutomatorExtensions.kt
+++ b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/extension/UiAutomatorExtensions.kt
@@ -3,11 +3,15 @@ package net.mullvad.mullvadvpn.test.common.extension
import android.os.Build
import androidx.test.uiautomator.By
import androidx.test.uiautomator.BySelector
+import androidx.test.uiautomator.StaleObjectException
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.UiObject2
+import androidx.test.uiautomator.UiObject2Condition
import androidx.test.uiautomator.Until
+import co.touchlab.kermit.Logger
import java.util.regex.Pattern
import net.mullvad.mullvadvpn.test.common.constant.DEFAULT_TIMEOUT
+import net.mullvad.mullvadvpn.test.common.constant.LONG_TIMEOUT
fun UiDevice.findObjectByCaseInsensitiveText(text: String): UiObject2 {
return findObjectWithTimeout(By.text(Pattern.compile(text, Pattern.CASE_INSENSITIVE)))
@@ -29,11 +33,45 @@ fun UiDevice.findObjectWithTimeout(
val foundObject = findObject(selector)
- require(foundObject != null) { "No matches for selector within timeout ($timeout): $selector" }
+ require(foundObject != null) {
+ "No matches for selector within timeout ($timeout ms): $selector"
+ }
return foundObject
}
+fun UiDevice.clickObjectAwaitCondition(
+ selector: BySelector,
+ condition: UiObject2Condition<Boolean>,
+ timeout: Long = LONG_TIMEOUT,
+) {
+ var foundObject = findObjectWithTimeout(selector, timeout)
+ foundObject.click()
+
+ val retryCount = 3
+ repeat(retryCount) {
+ try {
+ val wasChecked = foundObject.wait(condition, timeout)
+ require(wasChecked) {
+ "UiObject2 did not become match condition within timeout $timeout ms"
+ }
+ return
+ } catch (_: StaleObjectException) {
+ Logger.e("Caught StaleObjectException - retrying")
+ foundObject = findObjectWithTimeout(selector, timeout)
+ }
+ }
+ error("Exceeded maximum StaleObjectException count ($retryCount)")
+}
+
+fun UiDevice.clickObjectAwaitIsChecked(selector: BySelector, timeout: Long = LONG_TIMEOUT) {
+ clickObjectAwaitCondition(
+ selector = selector,
+ condition = Until.checked(true),
+ timeout = timeout,
+ )
+}
+
fun UiDevice.clickAgreeOnPrivacyDisclaimer() {
findObjectWithTimeout(By.text("Agree and continue")).click()
}
@@ -63,11 +101,16 @@ fun UiDevice.clickAllowOnNotificationPermissionPromptIfApiLevel33AndAbove(
findObjectWithTimeout(selector).click()
} catch (e: IllegalArgumentException) {
throw IllegalArgumentException(
- "Failed to allow notification permission within timeout ($timeout)"
+ "Failed to allow notification permission within timeout ($timeout ms)"
)
}
}
+fun UiDevice.pressBackTwice() {
+ pressBack()
+ pressBack()
+}
+
fun UiObject2.findObjectWithTimeout(
selector: BySelector,
timeout: Long = DEFAULT_TIMEOUT,
@@ -79,7 +122,7 @@ fun UiObject2.findObjectWithTimeout(
findObject(selector)
} catch (e: NullPointerException) {
throw IllegalArgumentException(
- "No matches for selector within timeout ($timeout): $selector"
+ "No matches for selector within timeout ($timeout ms): $selector"
)
}
}
diff --git a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/interactor/AppInteractor.kt b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/interactor/AppInteractor.kt
index c940f9bfba..a75747c76b 100644
--- a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/interactor/AppInteractor.kt
+++ b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/interactor/AppInteractor.kt
@@ -16,10 +16,6 @@ import net.mullvad.mullvadvpn.test.common.extension.clickAgreeOnPrivacyDisclaime
import net.mullvad.mullvadvpn.test.common.extension.clickAllowOnNotificationPermissionPromptIfApiLevel33AndAbove
import net.mullvad.mullvadvpn.test.common.extension.dismissChangelogDialogIfShown
import net.mullvad.mullvadvpn.test.common.extension.findObjectWithTimeout
-import net.mullvad.mullvadvpn.test.common.page.ConnectPage
-import net.mullvad.mullvadvpn.test.common.page.SettingsPage
-import net.mullvad.mullvadvpn.test.common.page.VpnSettingsPage
-import net.mullvad.mullvadvpn.test.common.page.on
class AppInteractor(
private val device: UiDevice,
@@ -57,17 +53,6 @@ class AppInteractor(
ensureLoggedIn()
}
- fun enableLocalNetworkSharing() {
- on<ConnectPage> { clickSettings() }
-
- on<SettingsPage> { clickVpnSettings() }
-
- on<VpnSettingsPage> { clickLocalNetworkSharingSwitch() }
-
- device.pressBack()
- device.pressBack()
- }
-
fun attemptLogin(accountNumber: String) {
val loginObject =
device.findObjectWithTimeout(By.clazz("android.widget.EditText")).apply {
diff --git a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/Page.kt b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/Page.kt
index 3caac5bd95..9a30112557 100644
--- a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/Page.kt
+++ b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/Page.kt
@@ -4,7 +4,7 @@ import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
sealed class Page {
- protected val uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+ val uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
abstract fun assertIsDisplayed()
}
diff --git a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/Story.kt b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/Story.kt
new file mode 100644
index 0000000000..7f919ebaf2
--- /dev/null
+++ b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/Story.kt
@@ -0,0 +1,50 @@
+package net.mullvad.mullvadvpn.test.common.page
+
+import net.mullvad.mullvadvpn.test.common.extension.pressBackTwice
+
+// This file defines extension methods on Page objects that involve multiple actions
+// that navigate multiple pages.
+
+fun ConnectPage.disableObfuscationStory() {
+ clickSettings()
+ on<SettingsPage> { clickVpnSettings() }
+ on<VpnSettingsPage> {
+ scrollUntilWireGuardObfuscationOffCell()
+ clickWireGuardObfuscationOffCell()
+ }
+ uiDevice.pressBackTwice()
+}
+
+fun ConnectPage.disablePostQuantumStory() {
+ clickSettings()
+ on<SettingsPage> { clickVpnSettings() }
+ on<VpnSettingsPage> {
+ scrollUntilPostQuantumOffCell()
+ clickPostQuantumOffCell()
+ }
+ uiDevice.pressBackTwice()
+}
+
+fun ConnectPage.enableShadowsocksStory() {
+ clickSettings()
+ on<SettingsPage> { clickVpnSettings() }
+ on<VpnSettingsPage> {
+ scrollUntilWireGuardObfuscationShadowsocksCell()
+ clickWireGuardObfuscationShadowsocksCell()
+ }
+ uiDevice.pressBackTwice()
+}
+
+fun ConnectPage.enableDAITAStory() {
+ clickSettings()
+ on<SettingsPage> { clickDaita() }
+ on<DaitaSettingsPage> { clickEnableSwitch() }
+ uiDevice.pressBackTwice()
+}
+
+fun ConnectPage.enableLocalNetworkSharingStory() {
+ clickSettings()
+ on<SettingsPage> { clickVpnSettings() }
+ on<VpnSettingsPage> { clickLocalNetworkSharingSwitch() }
+ uiDevice.pressBackTwice()
+}
diff --git a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/VpnSettingsPage.kt b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/VpnSettingsPage.kt
index 9301768100..6d5dbee492 100644
--- a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/VpnSettingsPage.kt
+++ b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/VpnSettingsPage.kt
@@ -3,6 +3,7 @@ package net.mullvad.mullvadvpn.test.common.page
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Direction
import androidx.test.uiautomator.Until
+import net.mullvad.mullvadvpn.test.common.extension.clickObjectAwaitIsChecked
import net.mullvad.mullvadvpn.test.common.extension.findObjectWithTimeout
class VpnSettingsPage internal constructor() : Page() {
@@ -39,23 +40,19 @@ class VpnSettingsPage internal constructor() : Page() {
}
fun clickWireguardObfuscationUdpOverTcpCell() {
- uiDevice
- .findObjectWithTimeout(By.res(WIREGUARD_OBFUSCATION_UDP_OVER_TCP_CELL_TEST_TAG))
- .click()
+ uiDevice.clickObjectAwaitIsChecked(By.res(WIREGUARD_OBFUSCATION_UDP_OVER_TCP_CELL_TEST_TAG))
}
fun clickWireGuardObfuscationOffCell() {
- uiDevice.findObjectWithTimeout(By.res(WIREGUARD_OBFUSCATION_OFF_CELL_TEST_TAG)).click()
+ uiDevice.clickObjectAwaitIsChecked(By.res(WIREGUARD_OBFUSCATION_OFF_CELL_TEST_TAG))
}
fun clickPostQuantumOffCell() {
- uiDevice.findObjectWithTimeout(By.res(QUANTUM_RESISTANCE_OFF_CELL_TEST_TAG)).click()
+ uiDevice.clickObjectAwaitIsChecked(By.res(QUANTUM_RESISTANCE_OFF_CELL_TEST_TAG))
}
fun clickWireGuardObfuscationShadowsocksCell() {
- uiDevice
- .findObjectWithTimeout(By.res(WIREGUARD_OBFUSCATION_SHADOWSOCKS_CELL_TEST_TAG))
- .click()
+ uiDevice.clickObjectAwaitIsChecked(By.res(WIREGUARD_OBFUSCATION_SHADOWSOCKS_CELL_TEST_TAG))
}
private fun scrollUntilCell(testTag: String) {