diff options
| author | Kalle Lindström <karl.lindstrom@mullvad.net> | 2025-04-02 17:20:56 +0200 |
|---|---|---|
| committer | Jonatan Rhodin <jonatan.rhodin@mullvad.net> | 2025-04-04 13:46:15 +0200 |
| commit | af2a71e61c0e5c70716ddd82205583f69c80adf4 (patch) | |
| tree | 65d12c88e684f15fd5b86928dcfb64f4b3ff1fed /android/test/common/src | |
| parent | 1e902d3a873ce0a789de0b32cfcf52ab72dacbdf (diff) | |
| download | mullvadvpn-af2a71e61c0e5c70716ddd82205583f69c80adf4.tar.xz mullvadvpn-af2a71e61c0e5c70716ddd82205583f69c80adf4.zip | |
Add shadowsocks obfuscation e2e test
Diffstat (limited to 'android/test/common/src')
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) { |
