diff options
| author | Niklas Berglund <niklas.berglund@gmail.com> | 2024-10-18 11:08:01 +0200 |
|---|---|---|
| committer | David Göransson <david.goransson@mullvad.net> | 2024-12-03 08:34:59 +0100 |
| commit | f59eb65f21621092ee21c292b7ae1da0518a09ac (patch) | |
| tree | e3f6a68e2d62e404f2170c8ae2d53fb6ff2c1975 | |
| parent | 46ec55fa5a9f249058d999a5c50fc094f4f46930 (diff) | |
| download | mullvadvpn-f59eb65f21621092ee21c292b7ae1da0518a09ac.tar.xz mullvadvpn-f59eb65f21621092ee21c292b7ae1da0518a09ac.zip | |
Implement basic of page object pattern
5 files changed, 119 insertions, 18 deletions
diff --git a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/ConnectPage.kt b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/ConnectPage.kt new file mode 100644 index 0000000000..fe1fafcc7f --- /dev/null +++ b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/ConnectPage.kt @@ -0,0 +1,10 @@ +package net.mullvad.mullvadvpn.test.common.page + +import androidx.test.uiautomator.By +import net.mullvad.mullvadvpn.test.common.extension.findObjectWithTimeout + +class ConnectPage internal constructor() : Page() { + override fun assertIsDisplayed() { + uiDevice.findObjectWithTimeout(By.res("connect_card_header_test_tag")) + } +} 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 new file mode 100644 index 0000000000..9fdffe1eae --- /dev/null +++ b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/LoginPage.kt @@ -0,0 +1,29 @@ +package net.mullvad.mullvadvpn.test.common.page + +import android.widget.Button +import androidx.test.uiautomator.By +import androidx.test.uiautomator.Until +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.extension.findObjectWithTimeout + +class LoginPage internal constructor() : Page() { + fun enterAccountNumber(accountNumber: String) { + uiDevice.findObjectWithTimeout(By.clazz("android.widget.EditText")).text = accountNumber + } + + fun tapLoginButton() { + 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 verifyShowingInvalidAccount() { + uiDevice.findObjectWithTimeout(By.text("Invalid account number"), EXTREMELY_LONG_TIMEOUT) + } + + override fun assertIsDisplayed() { + uiDevice.findObjectWithTimeout(By.text("Login")) + } +} 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 new file mode 100644 index 0000000000..3caac5bd95 --- /dev/null +++ b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/Page.kt @@ -0,0 +1,16 @@ +package net.mullvad.mullvadvpn.test.common.page + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.uiautomator.UiDevice + +sealed class Page { + protected val uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + + abstract fun assertIsDisplayed() +} + +inline fun <reified T : Page> on(scope: T.() -> Unit = {}) { + val page = T::class.java.getConstructor().newInstance() + page.assertIsDisplayed() + return page.scope() +} diff --git a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/PrivacyPage.kt b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/PrivacyPage.kt new file mode 100644 index 0000000000..43ec183c50 --- /dev/null +++ b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/PrivacyPage.kt @@ -0,0 +1,38 @@ +package net.mullvad.mullvadvpn.test.common.page + +import android.os.Build +import androidx.test.uiautomator.By +import androidx.test.uiautomator.Until +import net.mullvad.mullvadvpn.test.common.constant.DEFAULT_TIMEOUT +import net.mullvad.mullvadvpn.test.common.extension.findObjectWithTimeout + +class PrivacyPage internal constructor() : Page() { + override fun assertIsDisplayed() { + uiDevice.findObjectWithTimeout(By.text("Privacy")) + } + + fun clickAgreeOnPrivacyDisclaimer() { + uiDevice.findObjectWithTimeout(By.text("Agree and continue")).click() + } + + fun 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") + + uiDevice.wait(Until.hasObject(selector), timeout) + + try { + uiDevice.findObjectWithTimeout(selector).click() + } catch (e: IllegalArgumentException) { + throw IllegalArgumentException( + "Failed to allow notification permission within timeout ($timeout)" + ) + } + } +} 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 5a5d70fc9f..bfd78422c0 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 @@ -1,10 +1,9 @@ package net.mullvad.mullvadvpn.test.e2e -import androidx.test.uiautomator.By -import net.mullvad.mullvadvpn.test.common.constant.EXTREMELY_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.findObjectWithTimeout +import net.mullvad.mullvadvpn.test.common.page.ConnectPage +import net.mullvad.mullvadvpn.test.common.page.LoginPage +import net.mullvad.mullvadvpn.test.common.page.PrivacyPage +import net.mullvad.mullvadvpn.test.common.page.on import net.mullvad.mullvadvpn.test.e2e.misc.AccountTestRule import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test @@ -16,30 +15,39 @@ class LoginTest : EndToEndTest(BuildConfig.FLAVOR_infrastructure) { @Test fun testLoginWithValidCredentials() { - // Given val validTestAccountNumber = accountTestRule.validAccountNumber - // When - app.launchAndEnsureLoggedIn(validTestAccountNumber) + app.launch() + + on<PrivacyPage> { + clickAgreeOnPrivacyDisclaimer() + clickAllowOnNotificationPermissionPromptIfApiLevel33AndAbove() + } - // Then - app.ensureLoggedIn() + on<LoginPage> { + enterAccountNumber(validTestAccountNumber) + tapLoginButton() + } + + on<ConnectPage>() } @Test @Disabled("Failed login attempts are highly rate limited and cause test flakiness") fun testLoginWithInvalidCredentials() { - // Given val invalidDummyAccountNumber = accountTestRule.invalidAccountNumber - // When app.launch() - device.clickAgreeOnPrivacyDisclaimer() - device.clickAllowOnNotificationPermissionPromptIfApiLevel33AndAbove() - app.waitForLoginPrompt() - app.attemptLogin(invalidDummyAccountNumber) - // Then - device.findObjectWithTimeout(By.text("Invalid account number"), EXTREMELY_LONG_TIMEOUT) + on<PrivacyPage> { + clickAgreeOnPrivacyDisclaimer() + clickAllowOnNotificationPermissionPromptIfApiLevel33AndAbove() + } + + on<LoginPage> { + enterAccountNumber(invalidDummyAccountNumber) + tapLoginButton() + verifyShowingInvalidAccount() + } } } |
