diff options
| author | David Göransson <david.goransson@mullvad.net> | 2025-05-12 14:10:59 +0200 |
|---|---|---|
| committer | David Göransson <david.goransson@mullvad.net> | 2025-05-12 14:10:59 +0200 |
| commit | 64baf404f469ff7ec8f2842663cbf5bb548c7304 (patch) | |
| tree | 8195719809c07921056735b991c6fb797ce8adf9 | |
| parent | 405b0be1f551906890be84f5e8aca3539b6bcfbf (diff) | |
| parent | 069e7a52de87e665f7fdae8a664182f23150da55 (diff) | |
| download | mullvadvpn-64baf404f469ff7ec8f2842663cbf5bb548c7304.tar.xz mullvadvpn-64baf404f469ff7ec8f2842663cbf5bb548c7304.zip | |
Merge branch 'convert-tests-page-pattern'
28 files changed, 245 insertions, 392 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 aa993025f5..c14be06208 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 @@ -1,6 +1,5 @@ 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 @@ -18,10 +17,6 @@ fun UiDevice.findObjectByCaseInsensitiveText(text: String): UiObject2 { return findObjectWithTimeout(By.text(Pattern.compile(text, Pattern.CASE_INSENSITIVE))) } -fun UiObject2.findObjectByCaseInsensitiveText(text: String): UiObject2 { - return findObjectWithTimeout(By.text(Pattern.compile(text, Pattern.CASE_INSENSITIVE))) -} - fun UiDevice.hasObjectWithTimeout(selector: BySelector, timeout: Long = DEFAULT_TIMEOUT): Boolean = wait(Until.hasObject(selector), timeout) @@ -89,40 +84,6 @@ fun UiDevice.clickObjectAwaitIsChecked(selector: BySelector, timeout: Long = LON ) } -fun UiDevice.clickAgreeOnPrivacyDisclaimer() { - findObjectWithTimeout(By.text("Agree and continue")).click() -} - -// The dialog will only be shown when there's a new version code and bundled release notes. -fun UiDevice.dismissChangelogDialogIfShown() { - try { - findObjectWithTimeout(By.text("Got it!")).click() - } catch (e: IllegalArgumentException) { - // This is OK since it means the changes dialog wasn't shown. - } -} - -fun UiDevice.clickAllowOnNotificationPermissionPromptIfApiLevel33AndAbove( - timeout: Long = DEFAULT_TIMEOUT -) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { - // Skipping as notification permissions are not shown. - return - } - - val selector = By.text("Allow") - - wait(Until.hasObject(selector), timeout) - - try { - findObjectWithTimeout(selector).click() - } catch (e: IllegalArgumentException) { - throw IllegalArgumentException( - "Failed to allow notification permission within timeout ($timeout ms)" - ) - } -} - fun UiDevice.pressBackTwice() { pressBack() pressBack() 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 b3a35d21e3..7bf2a5911d 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 @@ -2,37 +2,29 @@ package net.mullvad.mullvadvpn.test.common.interactor import android.content.Context import android.content.Intent -import android.widget.Button +import android.os.Build import androidx.test.uiautomator.By import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.Until import net.mullvad.mullvadvpn.lib.endpoint.ApiEndpointOverride import net.mullvad.mullvadvpn.lib.endpoint.putApiEndpointConfigurationExtra -import net.mullvad.mullvadvpn.lib.ui.tag.CONNECT_CARD_HEADER_TEST_TAG -import net.mullvad.mullvadvpn.lib.ui.tag.LOCATION_INFO_CONNECTION_IN_TEST_TAG -import net.mullvad.mullvadvpn.lib.ui.tag.LOCATION_INFO_CONNECTION_OUT_TEST_TAG -import net.mullvad.mullvadvpn.lib.ui.tag.OUT_OF_TIME_SCREEN_TITLE_TEST_TAG -import net.mullvad.mullvadvpn.lib.ui.tag.TOP_BAR_ACCOUNT_BUTTON_TEST_TAG -import net.mullvad.mullvadvpn.lib.ui.tag.TOP_BAR_SETTINGS_BUTTON_TEST_TAG import net.mullvad.mullvadvpn.test.common.constant.DEFAULT_TIMEOUT -import net.mullvad.mullvadvpn.test.common.constant.EXTREMELY_LONG_TIMEOUT import net.mullvad.mullvadvpn.test.common.constant.LONG_TIMEOUT -import net.mullvad.mullvadvpn.test.common.constant.VERY_LONG_TIMEOUT -import net.mullvad.mullvadvpn.test.common.extension.clickAgreeOnPrivacyDisclaimer -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.LoginPage +import net.mullvad.mullvadvpn.test.common.page.on class AppInteractor( private val device: UiDevice, private val targetContext: Context, - private val targetPackageName: String, + private val customApiEndpointConfiguration: ApiEndpointOverride? = null, ) { - fun launch(customApiEndpointConfiguration: ApiEndpointOverride? = null) { + fun launch() { device.pressHome() // Wait for launcher device.wait(Until.hasObject(By.pkg(device.launcherPackageName).depth(0)), LONG_TIMEOUT) + val targetPackageName = targetContext.packageName val intent = targetContext.packageManager.getLaunchIntentForPackage(targetPackageName)?.apply { // Clear out any previous instances @@ -47,111 +39,41 @@ class AppInteractor( fun launchAndEnsureOnLoginPage() { launch() - device.clickAgreeOnPrivacyDisclaimer() - device.clickAllowOnNotificationPermissionPromptIfApiLevel33AndAbove() - waitForLoginPrompt() + clickAgreeOnPrivacyDisclaimer() + clickAllowOnNotificationPermissionPromptIfApiLevel33AndAbove() + on<LoginPage>() } - fun launchAndEnsureLoggedIn(accountNumber: String) { + fun launchAndLogIn(accountNumber: String) { launchAndEnsureOnLoginPage() - attemptLogin(accountNumber) - device.dismissChangelogDialogIfShown() - ensureLoggedIn() - } - - fun attemptLogin(accountNumber: String) { - val loginObject = - device.findObjectWithTimeout(By.clazz("android.widget.EditText")).apply { - text = accountNumber - } - val loginButton = loginObject.parent.findObject(By.clazz(Button::class.java)) - loginButton.wait(Until.enabled(true), DEFAULT_TIMEOUT) - loginButton.click() - } - - fun attemptCreateAccount() { - device.findObjectWithTimeout(By.text("Create account")).click() - } - - fun ensureAccountCreated(accountNumber: String? = null) { - device.findObjectWithTimeout(By.text("Congrats!"), VERY_LONG_TIMEOUT) - accountNumber?.let { device.findObjectWithTimeout(By.text(accountNumber), DEFAULT_TIMEOUT) } - } - - fun ensureAccountCreationFailed() { - device.findObjectWithTimeout(By.text("Failed to create account"), EXTREMELY_LONG_TIMEOUT) - } - - fun ensureLoggedIn() { - device.findObjectWithTimeout(By.text("DISCONNECTED"), VERY_LONG_TIMEOUT) - } - - fun ensureOutOfTime() { - device.findObjectWithTimeout(By.res(OUT_OF_TIME_SCREEN_TITLE_TEST_TAG)) - } - - fun ensureAccountScreen() { - device.findObjectWithTimeout(By.text("Account")) - } - - fun extractOutIpv4Address(): String { - device.findObjectWithTimeout(By.res(CONNECT_CARD_HEADER_TEST_TAG)).click() - return device - .findObjectWithTimeout( - // Text exist and contains IP address - By.res(LOCATION_INFO_CONNECTION_OUT_TEST_TAG).textContains("."), - VERY_LONG_TIMEOUT, - ) - .text - } - - fun extractInIpv4Address(): String { - device.findObjectWithTimeout(By.res(CONNECT_CARD_HEADER_TEST_TAG)).click() - val inString = - device - .findObjectWithTimeout( - By.res(LOCATION_INFO_CONNECTION_IN_TEST_TAG), - VERY_LONG_TIMEOUT, - ) - .text - - val extractedIpAddress = inString.split(" ")[0].split(":")[0] - return extractedIpAddress - } - - fun clickSettingsCog() { - device.findObjectWithTimeout(By.res(TOP_BAR_SETTINGS_BUTTON_TEST_TAG)).click() - } - - fun clickAccountCog() { - device.findObjectWithTimeout(By.res(TOP_BAR_ACCOUNT_BUTTON_TEST_TAG)).click() + on<LoginPage> { + enterAccountNumber(accountNumber) + clickLoginButton() + } } - fun clickListItemByText(text: String) { - device.findObjectWithTimeout(By.text(text)).click() + private fun clickAgreeOnPrivacyDisclaimer() { + device.findObjectWithTimeout(By.text("Agree and continue")).click() } - fun clickActionButtonByText(text: String) { - device.findObjectWithTimeout(By.text(text)).click() - } + private fun clickAllowOnNotificationPermissionPromptIfApiLevel33AndAbove( + timeout: Long = DEFAULT_TIMEOUT + ) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { + // Skipping as notification permissions are not shown. + return + } - fun waitForLoginPrompt(timeout: Long = VERY_LONG_TIMEOUT) { - device.findObjectWithTimeout(By.text("Login"), timeout) - } + val selector = By.text("Allow") - fun attemptToRemoveDevice() { - device.findObjectWithTimeout(By.desc("Remove")).click() - clickActionButtonByText("Yes, log out device") - } + device.wait(Until.hasObject(selector), timeout) - fun dismissStorePasswordPromptIfShown() { try { - device.waitForIdle() - val selector = By.textContains("password") - device.wait(Until.hasObject(selector), DEFAULT_TIMEOUT) - device.pressBack() - } catch (e: IllegalArgumentException) { - // This is OK since it means the password prompt wasn't shown. + device.findObjectWithTimeout(selector).click() + } catch (_: IllegalArgumentException) { + throw IllegalArgumentException( + "Failed to allow notification permission within timeout ($timeout ms)" + ) } } } diff --git a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/misc/CaptureScreenRecordingsExtension.kt b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/misc/CaptureScreenRecordingsExtension.kt index 21cc915482..de804ccb8e 100644 --- a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/misc/CaptureScreenRecordingsExtension.kt +++ b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/misc/CaptureScreenRecordingsExtension.kt @@ -34,7 +34,7 @@ class CaptureScreenRecordingsExtension : BeforeEachCallback, AfterEachCallback { } private fun startScreenRecord(fileName: String) { - if (File(OUTPUT_DIRECTORY).exists().not()) { + if (!File(OUTPUT_DIRECTORY).exists()) { File(OUTPUT_DIRECTORY).mkdirs() } @@ -49,6 +49,7 @@ class CaptureScreenRecordingsExtension : BeforeEachCallback, AfterEachCallback { device.executeShellCommand("pkill -2 screenrecord") runBlocking { job.join() } } catch (e: Exception) { + Logger.e("Failed to stop recording", e) fail("Failed to stop screen recording") } } diff --git a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/LoginPage.kt b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/LoginPage.kt index 173fde50fa..223ea378c6 100644 --- a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/LoginPage.kt +++ b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/LoginPage.kt @@ -20,17 +20,27 @@ class LoginPage internal constructor() : Page() { uiDevice.findObjectWithTimeout(By.clazz("android.widget.EditText")).text = accountNumber } - fun tapLoginButton() { + fun clickLoginButton() { val accountTextField = uiDevice.findObjectWithTimeout(By.clazz("android.widget.EditText")) val loginButton = accountTextField.parent.findObject(By.clazz(Button::class.java)) loginButton.wait(Until.enabled(true), DEFAULT_TIMEOUT) loginButton.click() } + fun clickCreateAccount() { + uiDevice.findObjectWithTimeout(By.text("Create account")).click() + } + fun verifyShowingInvalidAccount() { uiDevice.findObjectWithTimeout(invalidAccountNumberSelector, EXTREMELY_LONG_TIMEOUT) } + fun assertHasAccountHistory(accountNumber: String) { + // This can be improved, if we've entered the same account number in the TextField we might + // get a false positive. + uiDevice.findObjectWithTimeout(By.text(accountNumber)) + } + override fun assertIsDisplayed() { uiDevice.findObjectWithTimeout(loginSelector) } diff --git a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/OutOfTimePage.kt b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/OutOfTimePage.kt new file mode 100644 index 0000000000..33e11f4a2e --- /dev/null +++ b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/OutOfTimePage.kt @@ -0,0 +1,13 @@ +package net.mullvad.mullvadvpn.test.common.page + +import androidx.test.uiautomator.By +import net.mullvad.mullvadvpn.lib.ui.tag.OUT_OF_TIME_SCREEN_TITLE_TEST_TAG +import net.mullvad.mullvadvpn.test.common.extension.findObjectWithTimeout + +class OutOfTimePage internal constructor() : Page() { + private val outOfTimeSelector = By.res(OUT_OF_TIME_SCREEN_TITLE_TEST_TAG) + + override fun assertIsDisplayed() { + uiDevice.findObjectWithTimeout(outOfTimeSelector) + } +} diff --git a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/TooManyDevicesPage.kt b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/TooManyDevicesPage.kt new file mode 100644 index 0000000000..2f5f8c4133 --- /dev/null +++ b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/TooManyDevicesPage.kt @@ -0,0 +1,46 @@ +package net.mullvad.mullvadvpn.test.common.page + +import androidx.test.uiautomator.By +import androidx.test.uiautomator.waitForStableInActiveWindow +import net.mullvad.mullvadvpn.test.common.extension.expectObjectToDisappearWithTimeout +import net.mullvad.mullvadvpn.test.common.extension.findObjectWithTimeout + +class TooManyDevicesPage internal constructor() : Page() { + private val tooManyDevicesSelector = By.text("Too many devices") + + override fun assertIsDisplayed() { + uiDevice.findObjectWithTimeout(tooManyDevicesSelector) + + // Assert that we have too many devices + // And that the continue with login button is disabled + uiDevice.findObjectWithTimeout( + By.text("Continue with login").hasParent(By.enabled((false))) + ) + } + + fun clickRemoveDevice(deviceName: String) { + val deviceRow = uiDevice.findObjectWithTimeout(By.text(deviceName)).parent + deviceRow.findObjectWithTimeout(By.desc("Remove")).click() + } + + fun assertReadyToLogin() { + uiDevice.findObjectWithTimeout(By.text("Super!")) + } + + fun clickContinueWithLogin() { + uiDevice.findObjectWithTimeout(By.text("Continue with login")).click() + } +} + +/** Remove a device and click confirm on the confirmation dialog. */ +fun TooManyDevicesPage.removeDeviceFlow(deviceName: String) { + clickRemoveDevice(deviceName) + + // Wait for the confirmation dialog to appear + uiDevice.waitForStableInActiveWindow() + // Confirm logout + uiDevice.findObjectWithTimeout(By.text("Yes, log out device")).click() + + // Await the device to be removed + uiDevice.expectObjectToDisappearWithTimeout(By.text(deviceName)) +} diff --git a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/WelcomePage.kt b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/WelcomePage.kt new file mode 100644 index 0000000000..4589f82e56 --- /dev/null +++ b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/WelcomePage.kt @@ -0,0 +1,25 @@ +package net.mullvad.mullvadvpn.test.common.page + +import androidx.test.uiautomator.By +import androidx.test.uiautomator.UiDevice +import androidx.test.uiautomator.Until +import net.mullvad.mullvadvpn.test.common.constant.DEFAULT_TIMEOUT +import net.mullvad.mullvadvpn.test.common.extension.findObjectWithTimeout + +class WelcomePage internal constructor() : Page() { + private val welcomeSelector = By.text("Congrats!") + + override fun assertIsDisplayed() { + uiDevice.findObjectWithTimeout(welcomeSelector) + } +} + +fun UiDevice.dismissStorePasswordPromptIfShown() { + try { + val selector = By.textContains("password") + wait(Until.hasObject(selector), DEFAULT_TIMEOUT) + pressBack() + } catch (_: IllegalArgumentException) { + // This is OK since it means the password prompt wasn't shown. + } +} diff --git a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/WireGuardCustomPortDialog.kt b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/WireGuardCustomPortDialog.kt index ee112a5d46..2966cdeccf 100644 --- a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/WireGuardCustomPortDialog.kt +++ b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/WireGuardCustomPortDialog.kt @@ -23,8 +23,4 @@ class WireGuardCustomPortDialog internal constructor() : Page() { fun clickCancel() { uiDevice.findObjectWithTimeout(cancelSelector).click() } - - companion object { - const val TEXT_FIELD_TEST_TAG = "custom_port_dialog_input_test_tag" - } } diff --git a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/rule/CaptureScreenshotOnFailedTestRule.kt b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/rule/CaptureScreenshotOnFailedTestRule.kt index ecabb3c60a..adb195fec8 100644 --- a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/rule/CaptureScreenshotOnFailedTestRule.kt +++ b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/rule/CaptureScreenshotOnFailedTestRule.kt @@ -83,7 +83,7 @@ class CaptureScreenshotOnFailedTestRule(private val testTag: String) : TestWatch ) .toFile() .apply { - if (exists().not()) { + if (!exists()) { mkdirs() } } diff --git a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/rule/ForgetAllVpnAppsInSettingsTestRule.kt b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/rule/ForgetAllVpnAppsInSettingsTestRule.kt index 2a4a4dfeb1..b1cd894871 100644 --- a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/rule/ForgetAllVpnAppsInSettingsTestRule.kt +++ b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/rule/ForgetAllVpnAppsInSettingsTestRule.kt @@ -34,19 +34,16 @@ class ForgetAllVpnAppsInSettingsTestRule : BeforeTestExecutionCallback { .filter { !it.isHardcodedVpn() } .forEach { button -> button.click() - - if (device.hasObjectWithTimeout(By.text(FORGET_VPN_VPN_BUTTON_TEXT))) { - device.findObjectWithTimeout(By.text(FORGET_VPN_VPN_BUTTON_TEXT)).click() - device - .findObjectByCaseInsensitiveText(FORGET_VPN_VPN_CONFIRM_BUTTON_TEXT) - .click() + if (device.hasObjectWithTimeout(By.text(FORGET_VPN_BUTTON_TEXT))) { + device.findObjectWithTimeout(By.text(FORGET_VPN_BUTTON_TEXT)).click() + device.findObjectByCaseInsensitiveText(FORGET_VPN_CONFIRM_BUTTON_TEXT).click() } else if (device.hasObjectWithTimeout(By.text(DELETE_VPN_PROFILE_TEXT))) { device.findObjectWithTimeout(By.text(DELETE_VPN_PROFILE_TEXT)).click() device .findObjectWithTimeout(By.text(DELETE_VPN_CONFIRM_BUTTON_TEXT_REGEXP)) .click() - } else if (device.hasObjectWithTimeout(By.text(FORGET_VPN_BUTTON_TEXT))) { - device.findObjectWithTimeout(By.text(FORGET_VPN_BUTTON_TEXT)).click() + } else if (device.hasObjectWithTimeout(By.text(FORGET_BUTTON_TEXT))) { + device.findObjectWithTimeout(By.text(FORGET_BUTTON_TEXT)).click() } else { fail("Unable to find forget or delete button") } @@ -61,10 +58,10 @@ class ForgetAllVpnAppsInSettingsTestRule : BeforeTestExecutionCallback { companion object { private val HARDCODED_VPN_PROFILE_NAMES = listOf("VPN by Google") - private const val FORGET_VPN_VPN_BUTTON_TEXT = "Forget VPN" - private const val FORGET_VPN_BUTTON_TEXT = "Forget" // Legacy VPN + private const val FORGET_VPN_BUTTON_TEXT = "Forget VPN" + private const val FORGET_BUTTON_TEXT = "Forget" // Legacy VPN private const val DELETE_VPN_PROFILE_TEXT = "Delete VPN profile" - private const val FORGET_VPN_VPN_CONFIRM_BUTTON_TEXT = "Forget" + private const val FORGET_VPN_CONFIRM_BUTTON_TEXT = "Forget" // Samsung S22 shows "Delete" // Stock Android shows "DELETE" private val DELETE_VPN_CONFIRM_BUTTON_TEXT_REGEXP = Pattern.compile("DELETE|Delete") diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/ConnectionTest.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/ConnectionTest.kt index e7761b163f..0908d8f4ed 100644 --- a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/ConnectionTest.kt +++ b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/ConnectionTest.kt @@ -3,7 +3,6 @@ package net.mullvad.mullvadvpn.test.e2e import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.delay import kotlinx.coroutines.test.runTest -import net.mullvad.mullvadvpn.BuildConfig import net.mullvad.mullvadvpn.test.common.constant.EXTREMELY_LONG_TIMEOUT import net.mullvad.mullvadvpn.test.common.page.ConnectPage import net.mullvad.mullvadvpn.test.common.page.SelectLocationPage @@ -25,7 +24,7 @@ import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.RegisterExtension -class ConnectionTest : EndToEndTest(BuildConfig.FLAVOR_infrastructure) { +class ConnectionTest : EndToEndTest() { @RegisterExtension @JvmField val accountTestRule = AccountTestRule() @@ -39,7 +38,7 @@ class ConnectionTest : EndToEndTest(BuildConfig.FLAVOR_infrastructure) { @Test fun testConnect() { // Given - app.launchAndEnsureLoggedIn(accountTestRule.validAccountNumber) + app.launchAndLogIn(accountTestRule.validAccountNumber) on<ConnectPage> { clickConnect() } @@ -51,7 +50,7 @@ class ConnectionTest : EndToEndTest(BuildConfig.FLAVOR_infrastructure) { @Test fun testConnectAndVerifyWithConnectionCheck() = runTest { // Given - app.launchAndEnsureLoggedIn(accountTestRule.validAccountNumber) + app.launchAndLogIn(accountTestRule.validAccountNumber) on<ConnectPage> { clickConnect() } @@ -74,7 +73,7 @@ class ConnectionTest : EndToEndTest(BuildConfig.FLAVOR_infrastructure) { @HasDependencyOnLocalAPI @ClearFirewallRules fun testWireGuardObfuscationAutomatic() = runTest { - app.launchAndEnsureLoggedIn(accountTestRule.validAccountNumber) + app.launchAndLogIn(accountTestRule.validAccountNumber) on<ConnectPage> { enableLocalNetworkSharingStory() } on<ConnectPage> { clickSelectLocation() } @@ -111,7 +110,7 @@ class ConnectionTest : EndToEndTest(BuildConfig.FLAVOR_infrastructure) { @HasDependencyOnLocalAPI @ClearFirewallRules fun testWireGuardObfuscationOff() = runTest { - app.launchAndEnsureLoggedIn(accountTestRule.validAccountNumber) + app.launchAndLogIn(accountTestRule.validAccountNumber) on<ConnectPage> { enableLocalNetworkSharingStory() } on<ConnectPage> { clickSelectLocation() } @@ -163,7 +162,7 @@ class ConnectionTest : EndToEndTest(BuildConfig.FLAVOR_infrastructure) { @HasDependencyOnLocalAPI @ClearFirewallRules fun testUDPOverTCP() = runTest { - app.launchAndEnsureLoggedIn(accountTestRule.validAccountNumber) + app.launchAndLogIn(accountTestRule.validAccountNumber) on<ConnectPage> { enableLocalNetworkSharingStory() } on<ConnectPage> { clickSelectLocation() } @@ -212,7 +211,7 @@ class ConnectionTest : EndToEndTest(BuildConfig.FLAVOR_infrastructure) { @HasDependencyOnLocalAPI @ClearFirewallRules fun testShadowsocks() = runTest { - app.launchAndEnsureLoggedIn(accountTestRule.validAccountNumber) + app.launchAndLogIn(accountTestRule.validAccountNumber) on<ConnectPage> { enableLocalNetworkSharingStory() } on<ConnectPage> { disableObfuscationStory() } diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/EndToEndTest.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/EndToEndTest.kt index f74355ae2a..e944dd3606 100644 --- a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/EndToEndTest.kt +++ b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/EndToEndTest.kt @@ -16,7 +16,7 @@ import org.junit.jupiter.api.extension.ExtendWith import org.junit.jupiter.api.extension.RegisterExtension @ExtendWith(CaptureScreenRecordingsExtension::class) -abstract class EndToEndTest(private val infra: String) { +abstract class EndToEndTest { @RegisterExtension @JvmField val rule = CaptureScreenshotOnFailedTestRule(LOG_TAG) @@ -43,14 +43,7 @@ abstract class EndToEndTest(private val infra: String) { device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) targetContext = InstrumentationRegistry.getInstrumentation().targetContext - val targetPackageNameSuffix = - when (infra) { - "devmole" -> ".devmole" - "stagemole" -> ".stagemole" - else -> "" - } - - app = AppInteractor(device, targetContext, "net.mullvad.mullvadvpn$targetPackageNameSuffix") + app = AppInteractor(device, targetContext) } companion object { diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/LaunchAppTest.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/LaunchAppTest.kt index 724bffcad0..f68df92854 100644 --- a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/LaunchAppTest.kt +++ b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/LaunchAppTest.kt @@ -2,7 +2,7 @@ package net.mullvad.mullvadvpn.test.e2e import org.junit.jupiter.api.Test -class LaunchAppTest : EndToEndTest(BuildConfig.FLAVOR_infrastructure) { +class LaunchAppTest : EndToEndTest() { @Test fun testLaunchApp() { app.launch() diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/LeakTest.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/LeakTest.kt index 70146e9dc4..0ccc42e80b 100644 --- a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/LeakTest.kt +++ b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/LeakTest.kt @@ -29,7 +29,7 @@ import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.RegisterExtension -class LeakTest : EndToEndTest(BuildConfig.FLAVOR_infrastructure) { +class LeakTest : EndToEndTest() { @RegisterExtension @JvmField val accountTestRule = AccountTestRule() @@ -39,7 +39,7 @@ class LeakTest : EndToEndTest(BuildConfig.FLAVOR_infrastructure) { @BeforeEach fun setupVPNSettings() { - app.launchAndEnsureLoggedIn(accountTestRule.validAccountNumber) + app.launchAndLogIn(accountTestRule.validAccountNumber) on<ConnectPage> { clickSettings() } diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/LoginTest.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/LoginTest.kt index bfd78422c0..824f9b6aad 100644 --- a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/LoginTest.kt +++ b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/LoginTest.kt @@ -9,7 +9,7 @@ import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.RegisterExtension -class LoginTest : EndToEndTest(BuildConfig.FLAVOR_infrastructure) { +class LoginTest : EndToEndTest() { @RegisterExtension @JvmField val accountTestRule = AccountTestRule() @@ -26,7 +26,7 @@ class LoginTest : EndToEndTest(BuildConfig.FLAVOR_infrastructure) { on<LoginPage> { enterAccountNumber(validTestAccountNumber) - tapLoginButton() + clickLoginButton() } on<ConnectPage>() @@ -46,7 +46,7 @@ class LoginTest : EndToEndTest(BuildConfig.FLAVOR_infrastructure) { on<LoginPage> { enterAccountNumber(invalidDummyAccountNumber) - tapLoginButton() + clickLoginButton() verifyShowingInvalidAccount() } } diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/LogoutTest.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/LogoutTest.kt index f99cfc5161..96678b3f26 100644 --- a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/LogoutTest.kt +++ b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/LogoutTest.kt @@ -8,14 +8,14 @@ import net.mullvad.mullvadvpn.test.e2e.misc.AccountTestRule import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.RegisterExtension -class LogoutTest : EndToEndTest(BuildConfig.FLAVOR_infrastructure) { +class LogoutTest : EndToEndTest() { @RegisterExtension @JvmField val accountTestRule = AccountTestRule() @Test fun testLogout() { // Given - app.launchAndEnsureLoggedIn(accountTestRule.validAccountNumber) + app.launchAndLogIn(accountTestRule.validAccountNumber) on<ConnectPage> { clickAccount() } diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/WebLinkTest.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/WebLinkTest.kt index 8d8467392c..1c2cbb6a4a 100644 --- a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/WebLinkTest.kt +++ b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/WebLinkTest.kt @@ -7,7 +7,7 @@ import net.mullvad.mullvadvpn.test.common.page.SettingsPage import net.mullvad.mullvadvpn.test.common.page.on import org.junit.jupiter.api.Test -class WebLinkTest : EndToEndTest(BuildConfig.FLAVOR_infrastructure) { +class WebLinkTest : EndToEndTest() { @Test @SkipForFlavors(currentFlavor = BuildConfig.FLAVOR_billing, "play") fun testOpenFaqFromApp() { diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/constant/TextConstants.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/constant/TextConstants.kt deleted file mode 100644 index cfc4080ea4..0000000000 --- a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/constant/TextConstants.kt +++ /dev/null @@ -1,4 +0,0 @@ -package net.mullvad.mullvadvpn.test.e2e.constant - -const val CONNECTION_CHECK_IS_CONNECTED = "Using Mullvad VPN" -const val CONNECTION_CHECK_IS_NOT_CONNECTED = "Not using Mullvad VPN" diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/interactor/SystemSettingsInteractor.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/interactor/SystemSettingsInteractor.kt deleted file mode 100644 index bcf4adcc9e..0000000000 --- a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/interactor/SystemSettingsInteractor.kt +++ /dev/null @@ -1,33 +0,0 @@ -package net.mullvad.mullvadvpn.test.e2e.interactor - -import android.content.ComponentName -import android.content.Context -import android.content.Intent -import androidx.test.uiautomator.By -import androidx.test.uiautomator.UiDevice -import net.mullvad.mullvadvpn.test.common.extension.findObjectByCaseInsensitiveText - -class SystemSettingsInteractor(private val uiDevice: UiDevice, private val context: Context) { - fun openVpnSettings() { - val intent = - Intent("com.intent.MAIN").apply { - addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) - addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - } - intent.component = - ComponentName.unflattenFromString("com.android.settings/.Settings\$VpnSettingsActivity") - context.startActivity(intent) - Thread.sleep(1000) - } - - fun removeAllVpnPermissions() { - openVpnSettings() - uiDevice.findObjects(By.descContains("Settings")).forEach { - it.click() - Thread.sleep(1000) - uiDevice.findObjectByCaseInsensitiveText("forget vpn").click() - Thread.sleep(1000) - uiDevice.findObjectByCaseInsensitiveText("forget").click() - } - } -} diff --git a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/AccountExpiryMockApiTest.kt b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/AccountExpiryMockApiTest.kt index d89313fc45..7aec8f4725 100644 --- a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/AccountExpiryMockApiTest.kt +++ b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/AccountExpiryMockApiTest.kt @@ -2,10 +2,11 @@ package net.mullvad.mullvadvpn.test.mockapi import androidx.test.uiautomator.By import java.time.ZonedDateTime -import net.mullvad.mullvadvpn.test.common.extension.clickAgreeOnPrivacyDisclaimer -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.AccountPage +import net.mullvad.mullvadvpn.test.common.page.ConnectPage +import net.mullvad.mullvadvpn.test.common.page.OutOfTimePage +import net.mullvad.mullvadvpn.test.common.page.on import net.mullvad.mullvadvpn.test.mockapi.constant.DEFAULT_DEVICE_LIST import net.mullvad.mullvadvpn.test.mockapi.constant.DUMMY_DEVICE_NAME_2 import net.mullvad.mullvadvpn.test.mockapi.constant.DUMMY_ID_2 @@ -27,25 +28,18 @@ class AccountExpiryMockApiTest : MockApiTest() { } // Act - app.launch(endpoint) - device.clickAgreeOnPrivacyDisclaimer() - device.clickAllowOnNotificationPermissionPromptIfApiLevel33AndAbove() - app.waitForLoginPrompt() - app.attemptLogin(validAccountNumber) - - // Assert logged in - device.dismissChangelogDialogIfShown() - app.ensureLoggedIn() + app.launchAndLogIn(validAccountNumber) // Add one month to the account expiry val newAccountExpiry = oldAccountExpiry.plusMonths(1) apiDispatcher.accountExpiry = newAccountExpiry - // Go to account page to update the account expiry - app.clickAccountCog() + on<ConnectPage> { clickAccount() } - app.ensureAccountScreen() - device.findObjectWithTimeout(By.text(newAccountExpiry.toExpiryDateString())) + on<AccountPage> { + // Assert that the account expiry date is updated + device.findObjectWithTimeout(By.text(newAccountExpiry.toExpiryDateString())) + } } @Test @@ -61,28 +55,24 @@ class AccountExpiryMockApiTest : MockApiTest() { } // Act - app.launch(endpoint) - device.clickAgreeOnPrivacyDisclaimer() - device.clickAllowOnNotificationPermissionPromptIfApiLevel33AndAbove() - app.waitForLoginPrompt() - app.attemptLogin(validAccountNumber) + app.launchAndLogIn(validAccountNumber) - // Assert logged in - device.dismissChangelogDialogIfShown() - app.ensureLoggedIn() + // Wait for us to be on connect page before changing expiry + on<ConnectPage>() // Set account time as expired val newAccountExpiry = oldAccountExpiry.minusMonths(2) apiDispatcher.accountExpiry = newAccountExpiry + on<ConnectPage> { clickAccount() } + // Go to account page to update the account expiry - app.clickAccountCog() - app.ensureAccountScreen() + on<AccountPage>() // Go back to the main screen device.pressBack() // Assert that we show the out of time screen - app.ensureOutOfTime() + on<OutOfTimePage>() } } diff --git a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/AccountHistoryMockApiTest.kt b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/AccountHistoryMockApiTest.kt index 095ad66c64..2f15d83d09 100644 --- a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/AccountHistoryMockApiTest.kt +++ b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/AccountHistoryMockApiTest.kt @@ -2,15 +2,14 @@ package net.mullvad.mullvadvpn.test.mockapi import androidx.test.uiautomator.By import java.time.ZonedDateTime -import net.mullvad.mullvadvpn.lib.ui.tag.LOGIN_INPUT_TEST_TAG -import net.mullvad.mullvadvpn.test.common.extension.clickAgreeOnPrivacyDisclaimer -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.AccountPage +import net.mullvad.mullvadvpn.test.common.page.ConnectPage +import net.mullvad.mullvadvpn.test.common.page.LoginPage +import net.mullvad.mullvadvpn.test.common.page.on import net.mullvad.mullvadvpn.test.mockapi.constant.DEFAULT_DEVICE_LIST import net.mullvad.mullvadvpn.test.mockapi.constant.DUMMY_DEVICE_NAME_2 import net.mullvad.mullvadvpn.test.mockapi.constant.DUMMY_ID_2 -import org.junit.jupiter.api.Assertions.assertNotNull import org.junit.jupiter.api.Test class AccountHistoryMockApiTest : MockApiTest() { @@ -26,25 +25,18 @@ class AccountHistoryMockApiTest : MockApiTest() { devicePendingToGetCreated = DUMMY_ID_2 to DUMMY_DEVICE_NAME_2 } - // Act - app.launch(endpoint) - device.clickAgreeOnPrivacyDisclaimer() - device.clickAllowOnNotificationPermissionPromptIfApiLevel33AndAbove() - app.waitForLoginPrompt() - app.attemptLogin(validAccountNumber) - device.dismissChangelogDialogIfShown() - app.ensureLoggedIn() - app.clickAccountCog() - app.clickActionButtonByText("Log out") - app.waitForLoginPrompt() - device.findObjectWithTimeout(By.res(LOGIN_INPUT_TEST_TAG)).click() + app.launchAndLogIn(validAccountNumber) + + on<ConnectPage> { clickAccount() } + + on<AccountPage> { clickLogOut() } - // Assert val expectedResult = "1234 1234 1234 1234" - assertNotNull(device.findObjectWithTimeout(By.text(expectedResult))) + on<LoginPage> { + assertHasAccountHistory(expectedResult) + device.findObjectWithTimeout(By.text(expectedResult)).click() + } - // Try to login with the same account again - device.findObjectWithTimeout(By.text(expectedResult)).click() - app.ensureLoggedIn() + on<ConnectPage>() } } diff --git a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/CreateAccountMockApiTest.kt b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/CreateAccountMockApiTest.kt index 9b5cf85b95..259691f7df 100644 --- a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/CreateAccountMockApiTest.kt +++ b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/CreateAccountMockApiTest.kt @@ -1,8 +1,11 @@ package net.mullvad.mullvadvpn.test.mockapi -import net.mullvad.mullvadvpn.test.common.extension.clickAgreeOnPrivacyDisclaimer -import net.mullvad.mullvadvpn.test.common.extension.clickAllowOnNotificationPermissionPromptIfApiLevel33AndAbove -import net.mullvad.mullvadvpn.test.common.extension.dismissChangelogDialogIfShown +import androidx.test.uiautomator.By +import net.mullvad.mullvadvpn.test.common.extension.findObjectWithTimeout +import net.mullvad.mullvadvpn.test.common.page.LoginPage +import net.mullvad.mullvadvpn.test.common.page.WelcomePage +import net.mullvad.mullvadvpn.test.common.page.dismissStorePasswordPromptIfShown +import net.mullvad.mullvadvpn.test.common.page.on import net.mullvad.mullvadvpn.test.mockapi.constant.DUMMY_DEVICE_NAME_2 import net.mullvad.mullvadvpn.test.mockapi.constant.DUMMY_ID_2 import org.junit.jupiter.api.Test @@ -16,34 +19,26 @@ class CreateAccountMockApiTest : MockApiTest() { expectedAccountNumber = createdAccountNumber devicePendingToGetCreated = DUMMY_ID_2 to DUMMY_DEVICE_NAME_2 } - app.launch(endpoint) + app.launchAndEnsureOnLoginPage() - // Act - device.clickAgreeOnPrivacyDisclaimer() - device.clickAllowOnNotificationPermissionPromptIfApiLevel33AndAbove() - app.waitForLoginPrompt() - app.attemptCreateAccount() + on<LoginPage> { clickCreateAccount() } - app.dismissStorePasswordPromptIfShown() + device.dismissStorePasswordPromptIfShown() - // Assert - val expectedResult = "1234 1234 1234 1234" - app.ensureAccountCreated(expectedResult) + on<WelcomePage> { + // Ensure account number is visible + device.findObjectWithTimeout(By.text("1234 1234 1234 1234")) + } } @Test fun testCreateAccountFailed() { // Arrange - app.launch(endpoint) - - // Act - device.clickAgreeOnPrivacyDisclaimer() - device.clickAllowOnNotificationPermissionPromptIfApiLevel33AndAbove() - device.dismissChangelogDialogIfShown() - app.waitForLoginPrompt() - app.attemptCreateAccount() + app.launchAndEnsureOnLoginPage() - // Assert - app.ensureAccountCreationFailed() + on<LoginPage> { + clickCreateAccount() + device.findObjectWithTimeout(By.text("Failed to create account")) + } } } diff --git a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/LoginMockApiTest.kt b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/LoginMockApiTest.kt index b246bf2062..699b19da32 100644 --- a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/LoginMockApiTest.kt +++ b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/LoginMockApiTest.kt @@ -5,9 +5,9 @@ import androidx.test.uiautomator.Until import java.time.ZonedDateTime import net.mullvad.mullvadvpn.lib.ui.tag.LOGIN_TITLE_TEST_TAG import net.mullvad.mullvadvpn.test.common.constant.DEFAULT_TIMEOUT -import net.mullvad.mullvadvpn.test.common.extension.clickAgreeOnPrivacyDisclaimer -import net.mullvad.mullvadvpn.test.common.extension.clickAllowOnNotificationPermissionPromptIfApiLevel33AndAbove -import net.mullvad.mullvadvpn.test.common.extension.dismissChangelogDialogIfShown +import net.mullvad.mullvadvpn.test.common.page.ConnectPage +import net.mullvad.mullvadvpn.test.common.page.OutOfTimePage +import net.mullvad.mullvadvpn.test.common.page.on import net.mullvad.mullvadvpn.test.mockapi.constant.DEFAULT_DEVICE_LIST import net.mullvad.mullvadvpn.test.mockapi.constant.DUMMY_DEVICE_NAME_2 import net.mullvad.mullvadvpn.test.mockapi.constant.DUMMY_ID_2 @@ -23,14 +23,9 @@ class LoginMockApiTest : MockApiTest() { expectedAccountNumber = null accountExpiry = ZonedDateTime.now().plusDays(1) } - app.launch(endpoint) - // Act - device.clickAgreeOnPrivacyDisclaimer() - device.clickAllowOnNotificationPermissionPromptIfApiLevel33AndAbove() - app.waitForLoginPrompt() - app.attemptLogin(validAccountNumber) - app.attemptLogin(validAccountNumber) + // Act login with invalid credentials + app.launchAndLogIn(validAccountNumber) // Assert val result = @@ -53,15 +48,10 @@ class LoginMockApiTest : MockApiTest() { } // Act - app.launch(endpoint) - device.clickAgreeOnPrivacyDisclaimer() - device.clickAllowOnNotificationPermissionPromptIfApiLevel33AndAbove() - app.waitForLoginPrompt() - app.attemptLogin(validAccountNumber) - device.dismissChangelogDialogIfShown() + app.launchAndLogIn(validAccountNumber) // Assert - app.ensureLoggedIn() + on<ConnectPage>() } @Test @@ -75,15 +65,9 @@ class LoginMockApiTest : MockApiTest() { devicePendingToGetCreated = DUMMY_ID_2 to DUMMY_DEVICE_NAME_2 } - // Act - app.launch(endpoint) - device.clickAgreeOnPrivacyDisclaimer() - device.clickAllowOnNotificationPermissionPromptIfApiLevel33AndAbove() - app.waitForLoginPrompt() - app.attemptLogin(validAccountNumber) - device.dismissChangelogDialogIfShown() + app.launchAndLogIn(validAccountNumber) // Assert - app.ensureOutOfTime() + on<OutOfTimePage>() } } diff --git a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/LogoutMockApiTest.kt b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/LogoutMockApiTest.kt index da41d41066..08bc8d7862 100644 --- a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/LogoutMockApiTest.kt +++ b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/LogoutMockApiTest.kt @@ -1,15 +1,13 @@ package net.mullvad.mullvadvpn.test.mockapi -import androidx.test.uiautomator.By import java.time.ZonedDateTime -import net.mullvad.mullvadvpn.test.common.extension.clickAgreeOnPrivacyDisclaimer -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.AccountPage +import net.mullvad.mullvadvpn.test.common.page.ConnectPage +import net.mullvad.mullvadvpn.test.common.page.LoginPage +import net.mullvad.mullvadvpn.test.common.page.on import net.mullvad.mullvadvpn.test.mockapi.constant.DEFAULT_DEVICE_LIST import net.mullvad.mullvadvpn.test.mockapi.constant.DUMMY_DEVICE_NAME_2 import net.mullvad.mullvadvpn.test.mockapi.constant.DUMMY_ID_2 -import org.junit.jupiter.api.Assertions.assertNotNull import org.junit.jupiter.api.Test class LogoutMockApiTest : MockApiTest() { @@ -26,17 +24,12 @@ class LogoutMockApiTest : MockApiTest() { } // Act - app.launch(endpoint) - device.clickAgreeOnPrivacyDisclaimer() - device.clickAllowOnNotificationPermissionPromptIfApiLevel33AndAbove() - app.waitForLoginPrompt() - app.attemptLogin(validAccountNumber) - device.dismissChangelogDialogIfShown() - app.ensureLoggedIn() - app.clickAccountCog() - app.clickActionButtonByText("Log out") + app.launchAndLogIn(validAccountNumber) - // Assert - assertNotNull(device.findObjectWithTimeout(By.text("Login"))) + on<ConnectPage> { clickAccount() } + + on<AccountPage> { clickLogOut() } + + on<LoginPage>() } } diff --git a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/ManageDevicesMockApiTest.kt b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/ManageDevicesMockApiTest.kt index 1b087ec0fe..3a729032ab 100644 --- a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/ManageDevicesMockApiTest.kt +++ b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/ManageDevicesMockApiTest.kt @@ -2,9 +2,6 @@ package net.mullvad.mullvadvpn.test.mockapi import androidx.test.uiautomator.By import java.time.ZonedDateTime -import net.mullvad.mullvadvpn.test.common.extension.clickAgreeOnPrivacyDisclaimer -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.AccountPage import net.mullvad.mullvadvpn.test.common.page.ConnectPage @@ -29,13 +26,7 @@ class ManageDevicesMockApiTest : MockApiTest() { } // Act - go to devices screen - app.launch(endpoint) - device.clickAgreeOnPrivacyDisclaimer() - device.clickAllowOnNotificationPermissionPromptIfApiLevel33AndAbove() - app.waitForLoginPrompt() - app.attemptLogin(validAccountNumber) - device.waitForIdle() - device.dismissChangelogDialogIfShown() + app.launchAndLogIn(validAccountNumber) on<ConnectPage> { clickAccount() } @@ -48,8 +39,8 @@ class ManageDevicesMockApiTest : MockApiTest() { removeDevice("Yellow Hat") - // Confirm removal of device - app.clickActionButtonByText("Remove") + // Confirm the removal of the device + device.findObjectWithTimeout(By.text("Remove")).click() expectDeviceToBeRemoved("Yellow Hat") } diff --git a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/MockApiTest.kt b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/MockApiTest.kt index c7cb8f0377..6327a54757 100644 --- a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/MockApiTest.kt +++ b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/MockApiTest.kt @@ -12,7 +12,6 @@ import net.mullvad.mullvadvpn.lib.endpoint.ApiEndpointOverride import net.mullvad.mullvadvpn.test.common.interactor.AppInteractor import net.mullvad.mullvadvpn.test.common.rule.CaptureScreenshotOnFailedTestRule import net.mullvad.mullvadvpn.test.mockapi.constant.LOG_TAG -import net.mullvad.mullvadvpn.test.mockapi.constant.PACKAGE_NAME import okhttp3.mockwebserver.MockWebServer import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach @@ -42,11 +41,12 @@ abstract class MockApiTest { device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) targetContext = InstrumentationRegistry.getInstrumentation().targetContext - app = AppInteractor(device, targetContext, PACKAGE_NAME) - mockWebServer.start() Logger.d("Mocked web server started using port: ${mockWebServer.port}") endpoint = createEndpoint(mockWebServer.port) + + Logger.d("targetContext packageName: ${targetContext.packageName}") + app = AppInteractor(device, targetContext, endpoint) } @AfterEach diff --git a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/TooManyDevicesMockApiTest.kt b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/TooManyDevicesMockApiTest.kt index 2a5d91feaa..15638c6add 100644 --- a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/TooManyDevicesMockApiTest.kt +++ b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/TooManyDevicesMockApiTest.kt @@ -1,11 +1,10 @@ package net.mullvad.mullvadvpn.test.mockapi -import androidx.test.uiautomator.By import java.time.ZonedDateTime -import net.mullvad.mullvadvpn.test.common.extension.clickAgreeOnPrivacyDisclaimer -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.TooManyDevicesPage +import net.mullvad.mullvadvpn.test.common.page.on +import net.mullvad.mullvadvpn.test.common.page.removeDeviceFlow import net.mullvad.mullvadvpn.test.mockapi.constant.DUMMY_DEVICE_NAME_6 import net.mullvad.mullvadvpn.test.mockapi.constant.DUMMY_ID_6 import net.mullvad.mullvadvpn.test.mockapi.constant.FULL_DEVICE_LIST @@ -24,31 +23,16 @@ class TooManyDevicesMockApiTest : MockApiTest() { } // Act - app.launch(endpoint) - device.clickAgreeOnPrivacyDisclaimer() - device.clickAllowOnNotificationPermissionPromptIfApiLevel33AndAbove() - app.waitForLoginPrompt() - app.attemptLogin(validAccountNumber) + app.launchAndLogIn(validAccountNumber) // Assert that we have too many devices - device.findObjectWithTimeout(By.text("Too many devices")) - // And that the continue with login button is disabled - device.findObjectWithTimeout(By.text("Continue with login").hasParent(By.enabled((false)))) + on<TooManyDevicesPage> { + removeDeviceFlow(FULL_DEVICE_LIST.values.first()) - // Act - // Wait until the application is idle to avoid skipping input events that are filtered out - // depending on lifecycle state (dropUnlessResumed). - device.waitForIdle() - app.attemptToRemoveDevice() - - // Assert that a device was removed - device.findObjectWithTimeout(By.text("Super!")) - - // Act - app.clickActionButtonByText("Continue with login") + assertReadyToLogin() + clickContinueWithLogin() + } - // Assert that we are logged in - device.dismissChangelogDialogIfShown() - app.ensureLoggedIn() + on<ConnectPage>() } } diff --git a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/constant/Constants.kt b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/constant/Constants.kt index 083fcc1160..4e907e106d 100644 --- a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/constant/Constants.kt +++ b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/constant/Constants.kt @@ -1,7 +1,5 @@ package net.mullvad.mullvadvpn.test.mockapi.constant -const val PACKAGE_NAME = "net.mullvad.mullvadvpn" - const val LOG_TAG = "mullvad-mockapi" const val AUTH_TOKEN_URL_PATH = "/auth/v1/token" @@ -10,17 +8,17 @@ const val ACCOUNT_URL_PATH = "/accounts/v1/accounts/me" const val CREATE_ACCOUNT_URL_PATH = "/accounts/v1/accounts" const val DUMMY_ID_1 = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" -const val DUMMY_DEVICE_NAME_1 = "mole mole" +const val DUMMY_DEVICE_NAME_1 = "Mole Mole" const val DUMMY_ID_2 = "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" -const val DUMMY_DEVICE_NAME_2 = "elom elom" +const val DUMMY_DEVICE_NAME_2 = "Elom Elom" const val DUMMY_ID_3 = "cccccccc-cccc-cccc-cccc-cccccccccccc" -const val DUMMY_DEVICE_NAME_3 = "mole elom" +const val DUMMY_DEVICE_NAME_3 = "Mole Elom" const val DUMMY_ID_4 = "dddddddd-dddd-dddd-dddd-dddddddddddd" -const val DUMMY_DEVICE_NAME_4 = "yellow hat" +const val DUMMY_DEVICE_NAME_4 = "Yellow Hat" const val DUMMY_ID_5 = "eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee" -const val DUMMY_DEVICE_NAME_5 = "secure tunnel" +const val DUMMY_DEVICE_NAME_5 = "Secure Tunnel" const val DUMMY_ID_6 = "ffffffff-ffff-ffff-ffff-ffffffffffff" -const val DUMMY_DEVICE_NAME_6 = "red lobster" +const val DUMMY_DEVICE_NAME_6 = "Red Lobster" const val DUMMY_ACCESS_TOKEN = "mva_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" |
