diff options
| author | David Göransson <david.goransson@mullvad.net> | 2025-07-08 16:09:54 +0200 |
|---|---|---|
| committer | David Göransson <david.goransson@mullvad.net> | 2025-07-10 13:12:04 +0200 |
| commit | bcb4749950f75edd63b2200e4c15fc73479a7fb3 (patch) | |
| tree | cfaac4be58e42b2fdaed9aad223dd006c851935a /android/test | |
| parent | 396e0791d037fd939d9837ee1f2768ad5c73dc49 (diff) | |
| download | mullvadvpn-bcb4749950f75edd63b2200e4c15fc73479a7fb3.tar.xz mullvadvpn-bcb4749950f75edd63b2200e4c15fc73479a7fb3.zip | |
Add e2e test for Google play purchases
Diffstat (limited to 'android/test')
8 files changed, 156 insertions, 4 deletions
diff --git a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/AddTimeBottomSheet.kt b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/AddTimeBottomSheet.kt new file mode 100644 index 0000000000..ce6a29676c --- /dev/null +++ b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/page/AddTimeBottomSheet.kt @@ -0,0 +1,25 @@ +package net.mullvad.mullvadvpn.test.common.page + +import androidx.test.uiautomator.By +import androidx.test.uiautomator.UiDevice +import net.mullvad.mullvadvpn.lib.ui.tag.ADD_TIME_BOTTOM_SHEET_TITLE_TEST_TAG +import net.mullvad.mullvadvpn.test.common.constant.LONG_TIMEOUT +import net.mullvad.mullvadvpn.test.common.extension.findObjectWithTimeout + +class AddTimeBottomSheet internal constructor() : Page() { + private val oneMonthSelector = By.textStartsWith("Add 30 days time") + + override fun assertIsDisplayed() { + uiDevice.findObjectWithTimeout(By.res(ADD_TIME_BOTTOM_SHEET_TITLE_TEST_TAG)) + } + + fun click30days() { + uiDevice.findObjectWithTimeout(oneMonthSelector).click() + } +} + +fun UiDevice.buyGooglePlayTime() { + findObjectWithTimeout(By.text("1-tap buy"), LONG_TIMEOUT) + findObjectWithTimeout(By.text("1-tap buy")).click() + waitForIdle() +} 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 index 33e11f4a2e..f3add0e487 100644 --- 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 @@ -10,4 +10,8 @@ class OutOfTimePage internal constructor() : Page() { override fun assertIsDisplayed() { uiDevice.findObjectWithTimeout(outOfTimeSelector) } + + fun clickAddTime() { + uiDevice.findObjectWithTimeout(By.text("Add time")).click() + } } diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/PaymentTest.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/PaymentTest.kt new file mode 100644 index 0000000000..fbf91058f5 --- /dev/null +++ b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/PaymentTest.kt @@ -0,0 +1,49 @@ +package net.mullvad.mullvadvpn.test.e2e + +import androidx.test.uiautomator.By +import net.mullvad.mullvadvpn.lib.ui.tag.CONNECT_CARD_HEADER_TEST_TAG +import net.mullvad.mullvadvpn.test.common.annotation.SkipForFlavors +import net.mullvad.mullvadvpn.test.common.constant.VERY_LONG_TIMEOUT +import net.mullvad.mullvadvpn.test.common.extension.findObjectWithTimeout +import net.mullvad.mullvadvpn.test.common.page.AddTimeBottomSheet +import net.mullvad.mullvadvpn.test.common.page.LoginPage +import net.mullvad.mullvadvpn.test.common.page.OutOfTimePage +import net.mullvad.mullvadvpn.test.common.page.buyGooglePlayTime +import net.mullvad.mullvadvpn.test.common.page.on +import net.mullvad.mullvadvpn.test.e2e.annotations.RequiresGoogleBillingAccount +import net.mullvad.mullvadvpn.test.e2e.annotations.RequiresPartnerAuth +import net.mullvad.mullvadvpn.test.e2e.misc.AccountTestRule +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.RegisterExtension + +class PaymentTest : EndToEndTest() { + + @RegisterExtension @JvmField val accountTestRule = AccountTestRule(withTime = false) + + @Test + @SkipForFlavors(currentFlavor = BuildConfig.FLAVOR_billing, "oss") + @RequiresGoogleBillingAccount + @RequiresPartnerAuth + fun testInAppPurchaseForOutOfTime() { + val validTestAccountNumber = accountTestRule.validAccountNumber + + app.launchAndEnsureOnLoginPage() + + on<LoginPage> { + enterAccountNumber(validTestAccountNumber) + clickLoginButton() + } + + on<OutOfTimePage> { clickAddTime() } + + on<AddTimeBottomSheet> { click30days() } + + device.buyGooglePlayTime() + + // Assert we reach the Connect page after purchase + device.findObjectWithTimeout( + By.res(CONNECT_CARD_HEADER_TEST_TAG), + timeout = VERY_LONG_TIMEOUT, + ) + } +} diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/annotations/RequiresGoogleBillingAccount.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/annotations/RequiresGoogleBillingAccount.kt new file mode 100644 index 0000000000..7cedfd07d3 --- /dev/null +++ b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/annotations/RequiresGoogleBillingAccount.kt @@ -0,0 +1,34 @@ +package net.mullvad.mullvadvpn.test.e2e.annotations + +import androidx.test.platform.app.InstrumentationRegistry +import net.mullvad.mullvadvpn.test.e2e.constant.isBillingEnabled +import org.junit.jupiter.api.extension.ConditionEvaluationResult +import org.junit.jupiter.api.extension.ExecutionCondition +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.api.extension.ExtensionContext + +/** + * Annotation for tests making use of a google billing test account in order to perform purchases + */ +@Retention(AnnotationRetention.RUNTIME) +@ExtendWith(RequiresGoogleBillingAccount.AccessToBillingTestAccount::class) +annotation class RequiresGoogleBillingAccount { + class AccessToBillingTestAccount : ExecutionCondition { + override fun evaluateExecutionCondition( + context: ExtensionContext? + ): ConditionEvaluationResult { + + val enable = InstrumentationRegistry.getArguments().isBillingEnabled() + + return if (enable) { + ConditionEvaluationResult.enabled( + "Running test which requires access to billing test account." + ) + } else { + ConditionEvaluationResult.disabled( + "Skipping test which requires access to billing test account." + ) + } + } + } +} diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/annotations/RequiresPartnerAuth.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/annotations/RequiresPartnerAuth.kt new file mode 100644 index 0000000000..d9afdc1d68 --- /dev/null +++ b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/annotations/RequiresPartnerAuth.kt @@ -0,0 +1,33 @@ +package net.mullvad.mullvadvpn.test.e2e.annotations + +import androidx.test.platform.app.InstrumentationRegistry +import net.mullvad.mullvadvpn.test.e2e.constant.getPartnerAuth +import org.junit.jupiter.api.extension.ConditionEvaluationResult +import org.junit.jupiter.api.extension.ExecutionCondition +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.api.extension.ExtensionContext + +/** Annotation for tests requiring a partner api authentication. */ +@Retention(AnnotationRetention.RUNTIME) +@ExtendWith(RequiresPartnerAuth.HasPartnerAuth::class) +annotation class RequiresPartnerAuth { + class HasPartnerAuth : ExecutionCondition { + override fun evaluateExecutionCondition( + context: ExtensionContext? + ): ConditionEvaluationResult { + + val provided = + InstrumentationRegistry.getArguments().getPartnerAuth()?.isNotEmpty() ?: false + + return if (provided) { + ConditionEvaluationResult.enabled( + "Running test which requires partner authentication." + ) + } else { + ConditionEvaluationResult.disabled( + "Skipping test which requires partner authentication." + ) + } + } + } +} diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/constant/Constants.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/constant/Constants.kt index 8bcb5c2997..4326d156ea 100644 --- a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/constant/Constants.kt +++ b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/constant/Constants.kt @@ -23,6 +23,11 @@ fun Bundle.getInvalidAccountNumber() = "mullvad.test.e2e.${BuildConfig.FLAVOR_infrastructure}.accountNumber.invalid" ) +fun Bundle.isBillingEnabled(): Boolean = + InstrumentationRegistry.getArguments() + .getString("mullvad.test.e2e.config.billing.enable", "false") + .toBoolean() + fun Bundle.isRaasEnabled(): Boolean = InstrumentationRegistry.getArguments() .getRequiredArgument("mullvad.test.e2e.config.raas.enable") diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/misc/AccountProvider.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/misc/AccountProvider.kt index 799f33be64..1d967c2fe6 100644 --- a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/misc/AccountProvider.kt +++ b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/misc/AccountProvider.kt @@ -13,12 +13,14 @@ object AccountProvider { private val partnerAuth: String? = InstrumentationRegistry.getArguments().getPartnerAuth() private val partnerClient: PartnerApi by lazy { PartnerApi(partnerAuth!!) } - suspend fun getValidAccountNumber() = + suspend fun getValidAccountNumber(withTime: Boolean = true) = // If partner auth is provided, create a new account using the partner API. Otherwise we // expect and account with time to be provided. if (partnerAuth != null) { val accountNumber = partnerClient.createAccount() - partnerClient.addTime(accountNumber = accountNumber, daysToAdd = 1) + if (withTime) { + partnerClient.addTime(accountNumber = accountNumber, daysToAdd = 1) + } accountNumber } else { val validAccountNumber = InstrumentationRegistry.getArguments().getValidAccountNumber() diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/misc/AccountTestRule.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/misc/AccountTestRule.kt index 1f455af5c1..d3c8f30c19 100644 --- a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/misc/AccountTestRule.kt +++ b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/misc/AccountTestRule.kt @@ -4,12 +4,12 @@ import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.extension.BeforeEachCallback import org.junit.jupiter.api.extension.ExtensionContext -class AccountTestRule : BeforeEachCallback { +class AccountTestRule(val withTime: Boolean = true) : BeforeEachCallback { lateinit var validAccountNumber: String lateinit var invalidAccountNumber: String override fun beforeEach(context: ExtensionContext): Unit = runBlocking { - validAccountNumber = AccountProvider.getValidAccountNumber() + validAccountNumber = AccountProvider.getValidAccountNumber(withTime) invalidAccountNumber = AccountProvider.getInvalidAccountNumber() } } |
