diff --git a/auth/test/src/main/kotlin/me/proton/core/auth/test/MinimalExternalRegistrationWithSubscriptionTest.kt b/auth/test/src/main/kotlin/me/proton/core/auth/test/MinimalExternalRegistrationWithSubscriptionTest.kt index 176e1d3fb..5f30e5a7c 100644 --- a/auth/test/src/main/kotlin/me/proton/core/auth/test/MinimalExternalRegistrationWithSubscriptionTest.kt +++ b/auth/test/src/main/kotlin/me/proton/core/auth/test/MinimalExternalRegistrationWithSubscriptionTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Proton AG + * Copyright (c) 2024 Proton AG * This file is part of Proton AG and ProtonCore. * * ProtonCore is free software: you can redistribute it and/or modify @@ -18,6 +18,7 @@ package me.proton.core.auth.test +import androidx.test.filters.SdkSuppress import androidx.test.platform.app.InstrumentationRegistry import kotlinx.coroutines.runBlocking import me.proton.core.auth.test.robot.AddAccountRobot @@ -27,10 +28,12 @@ import me.proton.core.humanverification.test.robot.HvCodeRobot import me.proton.core.paymentiap.test.robot.GPBottomSheetSubscribeErrorRobot import me.proton.core.paymentiap.test.robot.GPBottomSheetSubscribeRobot import me.proton.core.plan.test.SubscriptionHelper -import me.proton.core.plan.test.robot.Plan +import me.proton.core.plan.test.BillingPlan import me.proton.core.plan.test.robot.SubscriptionRobot import me.proton.core.util.kotlin.random +import me.proton.test.fusion.Fusion.byObject import me.proton.test.fusion.FusionConfig +import org.junit.After import org.junit.Before import org.junit.Test import kotlin.time.Duration.Companion.seconds @@ -40,7 +43,7 @@ import kotlin.time.Duration.Companion.seconds * Clients tests should extend it in order to run payments tests from their repositories. * Should be run on payments test environment with PlayStore licensed test accounts. * When running on Android emulator it should support PlayStore API. - * This test is parametrized and uses [Plan]s as parameters. + * This test is parametrized and uses [BillingPlan]s as parameters. * Client plans should be created in advance and maintained in clients repository. * * Usage (below code should be added on client side): @@ -67,7 +70,8 @@ import kotlin.time.Duration.Companion.seconds * } * } */ -public abstract class MinimalExternalRegistrationWithSubscriptionTest(private val plan: Plan) { +@SdkSuppress(minSdkVersion = 33) +public abstract class MinimalExternalRegistrationWithSubscriptionTest(private val billingPlan: BillingPlan) { public abstract fun afterSubscriptionSteps() @@ -99,24 +103,31 @@ public abstract class MinimalExternalRegistrationWithSubscriptionTest(private va .fillAndClickNext(String.random(12)) SubscriptionRobot - .selectBillingCycle(plan.billingCycle) - .selectPlan(plan) + .selectBillingCycle(billingPlan.billingCycle) + .selectPlan(billingPlan) .openPaymentMethods() .selectAlwaysDeclines() .clickSubscribeButton() .errorMessageIsShown() .clickGotIt() - .selectExpandedPlan(plan) + .selectExpandedPlan(billingPlan) .openPaymentMethods() .selectAlwaysApproves() .clickSubscribeButton() + InstrumentationRegistry.getInstrumentation().uiAutomation.waitForIdle(5_000L, 60_000L) + byObject.withPkg(InstrumentationRegistry.getInstrumentation().targetContext.packageName).waitForExists() + HvCodeRobot .apply { waitForWebView() } afterSubscriptionSteps() - SubscriptionHelper.cancelSubscription(plan) + } + + @After + public fun cancelPlayStoreSubscription() { + SubscriptionHelper.cancelSubscription(billingPlan) } } diff --git a/auth/test/src/main/kotlin/me/proton/core/auth/test/MinimalInternalRegistrationWithSubscriptionTest.kt b/auth/test/src/main/kotlin/me/proton/core/auth/test/MinimalInternalRegistrationWithSubscriptionTest.kt index 7f1059366..2b6e5f1ee 100644 --- a/auth/test/src/main/kotlin/me/proton/core/auth/test/MinimalInternalRegistrationWithSubscriptionTest.kt +++ b/auth/test/src/main/kotlin/me/proton/core/auth/test/MinimalInternalRegistrationWithSubscriptionTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Proton AG + * Copyright (c) 2024 Proton AG * This file is part of Proton AG and ProtonCore. * * ProtonCore is free software: you can redistribute it and/or modify @@ -21,6 +21,7 @@ package me.proton.core.auth.test import android.provider.Settings.Global.ANIMATOR_DURATION_SCALE import android.provider.Settings.Global.TRANSITION_ANIMATION_SCALE import android.provider.Settings.Global.WINDOW_ANIMATION_SCALE +import androidx.test.filters.SdkSuppress import androidx.test.platform.app.InstrumentationRegistry import kotlinx.coroutines.runBlocking import me.proton.core.auth.test.robot.AddAccountRobot @@ -28,10 +29,12 @@ import me.proton.core.auth.test.robot.signup.SignupInternal import me.proton.core.paymentiap.test.robot.GPBottomSheetSubscribeErrorRobot import me.proton.core.paymentiap.test.robot.GPBottomSheetSubscribeRobot import me.proton.core.plan.test.SubscriptionHelper -import me.proton.core.plan.test.robot.Plan +import me.proton.core.plan.test.BillingPlan import me.proton.core.plan.test.robot.SubscriptionRobot import me.proton.core.util.kotlin.random +import me.proton.test.fusion.Fusion.byObject import me.proton.test.fusion.FusionConfig +import org.junit.After import org.junit.Before import org.junit.Test import kotlin.time.Duration.Companion.seconds @@ -41,7 +44,7 @@ import kotlin.time.Duration.Companion.seconds * Clients tests should extend it in order to run payments tests from their repositories. * Should be run on payments test environment with PlayStore licensed test accounts. * When running on Android emulator it should support PlayStore API. - * This test is parametrized and uses [Plan]s as parameters. + * This test is parametrized and uses [BillingPlan]s as parameters. * Client plans should be created in advance and maintained in clients repository. * * Usage (below code should be added on client side): @@ -68,25 +71,16 @@ import kotlin.time.Duration.Companion.seconds * } * } */ -public abstract class MinimalInternalRegistrationWithSubscriptionTest(private val plan: Plan) { +@SdkSuppress(minSdkVersion = 33) +public abstract class MinimalInternalRegistrationWithSubscriptionTest(private val billingPlan: BillingPlan) { public abstract fun afterSubscriptionSteps() @Before public fun setUpTimeouts() { - val animationScale: Float = 0.0F - val transitionAnimationScale: Float = 0.0F - val animatorDurationScale: Float = 0.0F - InstrumentationRegistry.getInstrumentation().uiAutomation - .executeShellCommand("settings put secure $ANIMATOR_DURATION_SCALE $animationScale") - InstrumentationRegistry.getInstrumentation().uiAutomation - .executeShellCommand("settings put secure $TRANSITION_ANIMATION_SCALE $transitionAnimationScale") - InstrumentationRegistry.getInstrumentation().uiAutomation - .executeShellCommand("settings put secure $WINDOW_ANIMATION_SCALE $animatorDurationScale") FusionConfig.Compose.waitTimeout.set(60.seconds) FusionConfig.Espresso.waitTimeout.set(60.seconds) FusionConfig.UiAutomator.waitTimeout.set(60.seconds) - FusionConfig.UiAutomator.shouldSearchByObjectEachAction = false InstrumentationRegistry.getInstrumentation().uiAutomation .executeShellCommand("settings put secure autofill_service null") } @@ -112,20 +106,28 @@ public abstract class MinimalInternalRegistrationWithSubscriptionTest(private va .skipConfirm() SubscriptionRobot - .selectBillingCycle(plan.billingCycle) - .selectPlan(plan) + .selectBillingCycle(billingPlan.billingCycle) + .selectPlan(billingPlan) + .openPaymentMethods() .selectAlwaysDeclines() .clickSubscribeButton() .errorMessageIsShown() .clickGotIt() - .selectExpandedPlan(plan) + + .selectExpandedPlan(billingPlan) .openPaymentMethods() .selectAlwaysApproves() .clickSubscribeButton() - afterSubscriptionSteps() + InstrumentationRegistry.getInstrumentation().uiAutomation.waitForIdle(5_000L, 60_000L) + byObject.withPkg(InstrumentationRegistry.getInstrumentation().targetContext.packageName).waitForExists() - SubscriptionHelper.cancelSubscription(plan) + afterSubscriptionSteps() + } + + @After + public fun cancelPlayStoreSubscription() { + SubscriptionHelper.cancelSubscription(billingPlan) } } diff --git a/coreexample/src/androidTestLocalProperties/kotlin/me/proton/core/test/android/uitests/tests/medium/payments/DynamicExistingPaymentMethodTests.kt b/coreexample/src/androidTestLocalProperties/kotlin/me/proton/core/test/android/uitests/tests/medium/payments/DynamicExistingPaymentMethodTests.kt index 3317212e7..e2a364a0d 100644 --- a/coreexample/src/androidTestLocalProperties/kotlin/me/proton/core/test/android/uitests/tests/medium/payments/DynamicExistingPaymentMethodTests.kt +++ b/coreexample/src/androidTestLocalProperties/kotlin/me/proton/core/test/android/uitests/tests/medium/payments/DynamicExistingPaymentMethodTests.kt @@ -23,6 +23,7 @@ import androidx.test.core.app.ApplicationProvider import dagger.hilt.android.testing.HiltAndroidTest import me.proton.core.domain.entity.AppStore import me.proton.core.payment.presentation.R +import me.proton.core.plan.test.BillingPlan import me.proton.core.plan.test.robot.SubscriptionRobot import me.proton.core.test.android.instrumented.utils.StringUtils.stringFromResource import me.proton.core.test.android.robot.CoreexampleRobot @@ -63,7 +64,7 @@ class DynamicExistingPaymentMethodTests( login(userWithPaypal) CoreexampleRobot().plansUpgrade() - SubscriptionRobot.selectPlan(Plan.Dev) + SubscriptionRobot.selectPlan(BillingPlan.Free) ExistingPaymentMethodsRobot().verify { paymentMethodDisplayed("PayPal", userWithPaypal.paypal) } @@ -74,7 +75,7 @@ class DynamicExistingPaymentMethodTests( login(userWithCard) CoreexampleRobot().plansUpgrade() - SubscriptionRobot.selectPlan(Plan.Dev) + SubscriptionRobot.selectPlan(BillingPlan.Free) ExistingPaymentMethodsRobot().verify { paymentMethodDisplayed(Card.default.details(), Card.default.name) if (inApp) { @@ -93,7 +94,7 @@ class DynamicExistingPaymentMethodTests( login(user) CoreexampleRobot().plansUpgrade() - SubscriptionRobot.selectPlan(Plan.Dev) + SubscriptionRobot.selectPlan(BillingPlan.Free) ExistingPaymentMethodsRobot().verify { paymentMethodDisplayed(card.details(), card.name) paymentMethodDisplayed("PayPal", user.paypal) @@ -106,7 +107,7 @@ class DynamicExistingPaymentMethodTests( val user = users.getUser { it.paypal.isNotEmpty() && it.cards.isNotEmpty() && !it.isPaid } CoreexampleRobot().plansUpgrade() - SubscriptionRobot.selectPlan(Plan.Dev) + SubscriptionRobot.selectPlan(BillingPlan.Free) ExistingPaymentMethodsRobot().verify { paymentMethod(user.paypal).checkIsNotChecked() paymentMethod(user.cards[0].details()).checkIsChecked() diff --git a/coreexample/src/androidTestLocalProperties/kotlin/me/proton/core/test/android/uitests/tests/medium/payments/DynamicNewCreditCardTests.kt b/coreexample/src/androidTestLocalProperties/kotlin/me/proton/core/test/android/uitests/tests/medium/payments/DynamicNewCreditCardTests.kt index 7b70c4533..a6b9c10ea 100644 --- a/coreexample/src/androidTestLocalProperties/kotlin/me/proton/core/test/android/uitests/tests/medium/payments/DynamicNewCreditCardTests.kt +++ b/coreexample/src/androidTestLocalProperties/kotlin/me/proton/core/test/android/uitests/tests/medium/payments/DynamicNewCreditCardTests.kt @@ -21,11 +21,11 @@ package me.proton.core.test.android.uitests.tests.medium.payments import dagger.hilt.android.testing.HiltAndroidTest import me.proton.core.domain.entity.AppStore import me.proton.core.payment.presentation.R +import me.proton.core.plan.test.BillingPlan import me.proton.core.plan.test.robot.SubscriptionRobot -import me.proton.core.test.android.robots.payments.AddCreditCardRobot import me.proton.core.test.android.robot.CoreexampleRobot +import me.proton.core.test.android.robots.payments.AddCreditCardRobot import me.proton.core.test.android.uitests.tests.BaseTest -import me.proton.core.test.quark.data.Plan import me.proton.core.test.quark.data.User import org.junit.After import org.junit.Test @@ -46,7 +46,7 @@ class DynamicNewCreditCardTests : BaseTest() { login(userWithoutCard) CoreexampleRobot().plansCurrent() - SubscriptionRobot.selectPlan(Plan.Dev) + SubscriptionRobot.selectPlan(BillingPlan.Free) } @Test diff --git a/coreexample/src/androidTestLocalProperties/kotlin/me/proton/core/test/android/uitests/tests/medium/plans/CurrentPlanTests.kt b/coreexample/src/androidTestLocalProperties/kotlin/me/proton/core/test/android/uitests/tests/medium/plans/CurrentPlanTests.kt index 9e48db981..6a9cb7119 100644 --- a/coreexample/src/androidTestLocalProperties/kotlin/me/proton/core/test/android/uitests/tests/medium/plans/CurrentPlanTests.kt +++ b/coreexample/src/androidTestLocalProperties/kotlin/me/proton/core/test/android/uitests/tests/medium/plans/CurrentPlanTests.kt @@ -21,7 +21,6 @@ package me.proton.core.test.android.uitests.tests.medium.plans import android.content.Context import androidx.core.text.HtmlCompat import androidx.test.core.app.ApplicationProvider -import dagger.hilt.android.testing.HiltAndroidTest import me.proton.core.domain.entity.AppStore import me.proton.core.plan.presentation.R import me.proton.core.plan.presentation.entity.PlanCycle diff --git a/coreexample/src/androidTestLocalProperties/kotlin/me/proton/core/test/android/uitests/tests/medium/plans/DynamicCurrentPlanTests.kt b/coreexample/src/androidTestLocalProperties/kotlin/me/proton/core/test/android/uitests/tests/medium/plans/DynamicCurrentPlanTests.kt index bfa567df9..94506d344 100644 --- a/coreexample/src/androidTestLocalProperties/kotlin/me/proton/core/test/android/uitests/tests/medium/plans/DynamicCurrentPlanTests.kt +++ b/coreexample/src/androidTestLocalProperties/kotlin/me/proton/core/test/android/uitests/tests/medium/plans/DynamicCurrentPlanTests.kt @@ -37,6 +37,7 @@ import java.text.DateFormat import java.util.Calendar import java.util.Date +@Ignore("Outdated") @HiltAndroidTest class DynamicCurrentPlanTests { diff --git a/coreexample/src/androidTestLocalProperties/kotlin/me/proton/core/test/android/uitests/tests/medium/plans/DynamicSelectPlanTests.kt b/coreexample/src/androidTestLocalProperties/kotlin/me/proton/core/test/android/uitests/tests/medium/plans/DynamicSelectPlanTests.kt index cce232c2e..499b13552 100644 --- a/coreexample/src/androidTestLocalProperties/kotlin/me/proton/core/test/android/uitests/tests/medium/plans/DynamicSelectPlanTests.kt +++ b/coreexample/src/androidTestLocalProperties/kotlin/me/proton/core/test/android/uitests/tests/medium/plans/DynamicSelectPlanTests.kt @@ -27,12 +27,11 @@ import me.proton.core.auth.test.robot.signup.SetPasswordRobot import me.proton.core.domain.entity.AppStore import me.proton.core.humanverification.test.robot.HvCodeRobot import me.proton.core.paymentiap.test.robot.GoogleIAPRobot +import me.proton.core.plan.test.BillingPlan import me.proton.core.plan.test.robot.SubscriptionRobot import me.proton.core.test.android.robots.CoreRobot import me.proton.core.test.android.robots.payments.AddCreditCardRobot import me.proton.core.test.android.uitests.tests.SmokeTest -import me.proton.core.test.quark.data.Plan.Free -import me.proton.core.test.quark.data.Plan.MailPlus import me.proton.core.test.rule.annotation.TestUserData import me.proton.core.test.rule.annotation.payments.TestPaymentMethods import me.proton.core.test.rule.annotation.payments.annotationTestData @@ -42,7 +41,7 @@ import org.junit.Ignore import org.junit.Rule import org.junit.Test -//@Ignore("Outdated") +@Ignore("Outdated") @HiltAndroidTest class DynamicSelectPlanTests { @@ -82,7 +81,7 @@ class DynamicSelectPlanTests { @Test fun selectFreeAndCancelHumanVerification() { - SubscriptionRobot.selectPlan(Free) + SubscriptionRobot.selectPlan(BillingPlan.Free) HvCodeRobot .apply { @@ -95,7 +94,7 @@ class DynamicSelectPlanTests { @Test fun selectFreeAndCancelHumanVerification2() { - SubscriptionRobot.selectPlan(MailPlus) + SubscriptionRobot.selectPlan(BillingPlan.Free) HvCodeRobot .apply { @@ -109,7 +108,7 @@ class DynamicSelectPlanTests { @Test @SmokeTest fun selectPlusAndCancelPayment() { - SubscriptionRobot.selectPlan(MailPlus) + SubscriptionRobot.selectPlan(BillingPlan.Free) AddCreditCardRobot() .apply { verify { @@ -125,7 +124,7 @@ class DynamicSelectPlanTests { @SmokeTest @TestPaymentMethods(AppStore.GooglePlay, card = false, paypal = false, inApp = true) fun selectPlusAndCancelPaymentIAPOnly() { - SubscriptionRobot.selectPlan(MailPlus) + SubscriptionRobot.selectPlan(BillingPlan.Free) GoogleIAPRobot() .apply { verify { @@ -141,7 +140,7 @@ class DynamicSelectPlanTests { @SmokeTest @TestPaymentMethods(AppStore.GooglePlay, card = true, paypal = false, inApp = true) fun selectPlusAndCancelPaymentIAPAndCard() { - SubscriptionRobot.selectPlan(MailPlus) + SubscriptionRobot.selectPlan(BillingPlan.Free) AddCreditCardRobot() .apply { verify { nextPaymentProviderButtonDisplayed() } @@ -159,7 +158,7 @@ class DynamicSelectPlanTests { @SmokeTest @TestPaymentMethods(AppStore.GooglePlay, card = false, paypal = false, inApp = false) fun selectPlusNoPaymentProvidersAvailable() { - SubscriptionRobot.selectPlan(MailPlus) + SubscriptionRobot.selectPlan(BillingPlan.Free) SubscriptionRobot.verifyAtLeastOnePlanIsShown() } } diff --git a/coreexample/src/androidTestLocalProperties/kotlin/me/proton/core/test/android/uitests/tests/medium/plans/DynamicUpgradePlanTests.kt b/coreexample/src/androidTestLocalProperties/kotlin/me/proton/core/test/android/uitests/tests/medium/plans/DynamicUpgradePlanTests.kt index 8de3b0811..e8b23876a 100644 --- a/coreexample/src/androidTestLocalProperties/kotlin/me/proton/core/test/android/uitests/tests/medium/plans/DynamicUpgradePlanTests.kt +++ b/coreexample/src/androidTestLocalProperties/kotlin/me/proton/core/test/android/uitests/tests/medium/plans/DynamicUpgradePlanTests.kt @@ -21,6 +21,7 @@ package me.proton.core.test.android.uitests.tests.medium.plans import dagger.hilt.android.testing.HiltAndroidTest import me.proton.core.domain.entity.AppStore import me.proton.core.plan.presentation.entity.PlanCycle +import me.proton.core.plan.test.BillingPlan import me.proton.core.plan.test.robot.SubscriptionRobot import me.proton.core.test.android.robot.CoreexampleRobot import me.proton.core.test.android.uitests.tests.BaseTest @@ -29,8 +30,10 @@ import me.proton.core.test.quark.data.Plan import me.proton.core.test.quark.data.User import org.junit.After import org.junit.Before +import org.junit.Ignore import org.junit.Test +@Ignore("Outdated") @HiltAndroidTest class DynamicUpgradePlanTests : BaseTest() { private val coreExampleRobot = CoreexampleRobot() @@ -55,8 +58,8 @@ class DynamicUpgradePlanTests : BaseTest() { coreExampleRobot.plansUpgrade() SubscriptionRobot.apply { - togglePlan(Plan.Dev) - verifyCanGetPlan(Plan.Dev) + togglePlan(BillingPlan.Free) + verifyCanGetPlan(BillingPlan.Free) close() } diff --git a/coreexample/src/androidTestLocalProperties/kotlin/me/proton/core/test/android/uitests/tests/medium/plans/SelectPlanForIAPTests.kt b/coreexample/src/androidTestLocalProperties/kotlin/me/proton/core/test/android/uitests/tests/medium/plans/SelectPlanForIAPTests.kt index 909802573..58c4e1c51 100644 --- a/coreexample/src/androidTestLocalProperties/kotlin/me/proton/core/test/android/uitests/tests/medium/plans/SelectPlanForIAPTests.kt +++ b/coreexample/src/androidTestLocalProperties/kotlin/me/proton/core/test/android/uitests/tests/medium/plans/SelectPlanForIAPTests.kt @@ -18,7 +18,6 @@ package me.proton.core.test.android.uitests.tests.medium.plans -import dagger.hilt.android.testing.HiltAndroidTest import me.proton.core.domain.entity.AppStore import me.proton.core.test.quark.data.Plan.Dev import me.proton.core.test.quark.data.Plan.Free diff --git a/coreexample/src/androidTestLocalProperties/kotlin/me/proton/core/test/android/uitests/tests/medium/plans/UpgradePlanTests.kt b/coreexample/src/androidTestLocalProperties/kotlin/me/proton/core/test/android/uitests/tests/medium/plans/UpgradePlanTests.kt index dc373d0d9..096df2fd6 100644 --- a/coreexample/src/androidTestLocalProperties/kotlin/me/proton/core/test/android/uitests/tests/medium/plans/UpgradePlanTests.kt +++ b/coreexample/src/androidTestLocalProperties/kotlin/me/proton/core/test/android/uitests/tests/medium/plans/UpgradePlanTests.kt @@ -18,7 +18,6 @@ package me.proton.core.test.android.uitests.tests.medium.plans -import dagger.hilt.android.testing.HiltAndroidTest import me.proton.core.domain.entity.AppStore import me.proton.core.plan.presentation.entity.PlanCycle import me.proton.core.test.quark.data.Plan diff --git a/gradle.properties b/gradle.properties index 61475f969..0308c8c13 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,4 +10,3 @@ android.enableJetifier=false kotlin.code.style=official android.nonTransitiveRClass=false android.nonFinalResIds=false -android.testInstrumentationRunnerArguments.no-uninstall=true diff --git a/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/GPBottomSheetPaymentMethodsRobot.kt b/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/GPBottomSheetPaymentMethodsRobot.kt index 1b411080b..df1959ee4 100644 --- a/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/GPBottomSheetPaymentMethodsRobot.kt +++ b/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/GPBottomSheetPaymentMethodsRobot.kt @@ -1,15 +1,34 @@ +/* + * Copyright (c) 2024 Proton AG + * This file is part of Proton AG and ProtonCore. + * + * ProtonCore is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ProtonCore is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ProtonCore. If not, see . + */ + package me.proton.core.paymentiap.test.robot import android.widget.TextView +import androidx.test.filters.SdkSuppress import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.uiautomator.UiDevice import me.proton.test.fusion.Fusion.byObject +/** + * Payment method bottom sheet robot to select always declines or always approves methods. + */ +@SdkSuppress(minSdkVersion = 33) public class GPBottomSheetPaymentMethodsRobot { - public val device: UiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) - - public inline fun selectAlwaysApproves(): T { selectCardItem(testCardAlwaysApprovesText) return T::class.java.getDeclaredConstructor().newInstance() diff --git a/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/GPBottomSheetSubscribeErrorRobot.kt b/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/GPBottomSheetSubscribeErrorRobot.kt index 1eb507434..96935b892 100644 --- a/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/GPBottomSheetSubscribeErrorRobot.kt +++ b/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/GPBottomSheetSubscribeErrorRobot.kt @@ -1,10 +1,33 @@ +/* + * Copyright (c) 2024 Proton AG + * This file is part of Proton AG and ProtonCore. + * + * ProtonCore is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ProtonCore is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ProtonCore. If not, see . + */ + package me.proton.core.paymentiap.test.robot import android.widget.Button import android.widget.TextView +import androidx.test.filters.SdkSuppress import me.proton.core.test.android.instrumented.FusionConfig import me.proton.test.fusion.Fusion.byObject +/** + * Google Play payment error bottom sheet robot, containing actions and validations. + */ +@SdkSuppress(minSdkVersion = 33) public class GPBottomSheetSubscribeErrorRobot { public inline fun clickGotIt(): T { diff --git a/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/GPBottomSheetSubscribeRobot.kt b/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/GPBottomSheetSubscribeRobot.kt index 58fc07496..bd281a65f 100644 --- a/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/GPBottomSheetSubscribeRobot.kt +++ b/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/GPBottomSheetSubscribeRobot.kt @@ -1,7 +1,27 @@ +/* + * Copyright (c) 2024 Proton AG + * This file is part of Proton AG and ProtonCore. + * + * ProtonCore is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ProtonCore is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ProtonCore. If not, see . + */ + package me.proton.core.paymentiap.test.robot import android.widget.Button import android.widget.LinearLayout +import android.widget.RadioButton +import androidx.test.filters.SdkSuppress import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.By import androidx.test.uiautomator.UiDevice @@ -9,13 +29,17 @@ import androidx.test.uiautomator.UiWatcher import me.proton.core.test.android.instrumented.FusionConfig import me.proton.test.fusion.Fusion.byObject +/** + * Google Play bottom sheet robot, containing Subscribe button with additional actions. + */ +@SdkSuppress(minSdkVersion = 33) public class GPBottomSheetSubscribeRobot { private val device: UiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) public fun registerPlayPointsNotNowButtonWatcher() { - device.registerWatcher("Google play Points Not now button watcher", UiWatcher { + device.registerWatcher(notNowButtonUiWatcherName, UiWatcher { val registerButton = device.findObject(By.text("Not now").clazz(Button::class.java)) if (registerButton != null && registerButton.isEnabled) { println("'Google play Points Not now' button detected! Clicking it now...") @@ -27,6 +51,25 @@ public class GPBottomSheetSubscribeRobot { device.runWatchers() } + public fun registerPlayRequireAuthenticationWatcher() { + device.registerWatcher(authRequiredScreenWatcherName, UiWatcher { + val noThanksButton = + device.findObject(By.text("No, thanks").clazz(RadioButton::class.java)) + if (noThanksButton != null) { + println("'Google play authentication required screen is shown. Dealing with it.") + noThanksButton.click() + val okButton = + device.findObject(By.text("OK").clazz(Button::class.java).enabled(true)) + if (okButton != null) { + okButton.click() + return@UiWatcher true + } + } + false + }) + device.runWatchers() + } + public fun openPaymentMethods(): GPBottomSheetPaymentMethodsRobot { FusionConfig.uiAutomator.boost() byObject @@ -48,7 +91,11 @@ public class GPBottomSheetSubscribeRobot { .waitForExists() .click() + // Below UIWatchers should handle one time pop-up bottom sheets: + // PlayStore points and require PlayStore authentication. registerPlayPointsNotNowButtonWatcher() + registerPlayRequireAuthenticationWatcher() + return T::class.java.getDeclaredConstructor().newInstance() } } \ No newline at end of file diff --git a/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/PlayStoreAccountViewRobot.kt b/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/PlayStoreAccountViewRobot.kt index cce541aa2..8ff7d6b31 100644 --- a/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/PlayStoreAccountViewRobot.kt +++ b/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/PlayStoreAccountViewRobot.kt @@ -1,12 +1,34 @@ +/* + * Copyright (c) 2024 Proton AG + * This file is part of Proton AG and ProtonCore. + * + * ProtonCore is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ProtonCore is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ProtonCore. If not, see . + */ + package me.proton.core.paymentiap.test.robot import android.widget.TextView +import androidx.test.filters.SdkSuppress import me.proton.test.fusion.Fusion.byObject +/** + * Google Play bottom sheet containing Subscribe button with additional actions. + */ +@SdkSuppress(minSdkVersion = 33) public class PlayStoreAccountViewRobot { public fun selectPaymentsAndSubscriptionsItem(): PlayStorePaymentsAndSubscriptionsRobot { - Thread.sleep(3000) byObject.withText("Payments & subscriptions") .instanceOf(TextView::class.java) .waitForExists() diff --git a/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/PlayStoreCancelSubscriptionBottomSheetRobot.kt b/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/PlayStoreCancelSubscriptionBottomSheetRobot.kt index 8bee3323e..4a591907e 100644 --- a/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/PlayStoreCancelSubscriptionBottomSheetRobot.kt +++ b/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/PlayStoreCancelSubscriptionBottomSheetRobot.kt @@ -1,8 +1,28 @@ +/* + * Copyright (c) 2024 Proton AG + * This file is part of Proton AG and ProtonCore. + * + * ProtonCore is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ProtonCore is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ProtonCore. If not, see . + */ + package me.proton.core.paymentiap.test.robot import android.widget.TextView +import androidx.test.filters.SdkSuppress import me.proton.test.fusion.Fusion.byObject +@SdkSuppress(minSdkVersion = 33) public class PlayStoreCancelSubscriptionBottomSheetRobot { public fun clickCancelSubscription(): PlayStoreManageSubscriptionRobot { diff --git a/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/PlayStoreHomeRobot.kt b/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/PlayStoreHomeRobot.kt index 61f0afb50..b76e4aa11 100644 --- a/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/PlayStoreHomeRobot.kt +++ b/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/PlayStoreHomeRobot.kt @@ -1,11 +1,40 @@ +/* + * Copyright (c) 2024 Proton AG + * This file is part of Proton AG and ProtonCore. + * + * ProtonCore is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ProtonCore is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ProtonCore. If not, see . + */ + package me.proton.core.paymentiap.test.robot import android.content.Context import android.content.Intent +import android.widget.Button import android.widget.FrameLayout +import android.widget.TextView +import androidx.annotation.RequiresApi +import androidx.test.filters.SdkSuppress import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.uiautomator.By +import androidx.test.uiautomator.UiDevice +import androidx.test.uiautomator.UiWatcher import me.proton.test.fusion.Fusion.byObject +/** + * Contains code to launch PlayStore app and deal with PlayStore application home screen. + */ +@SdkSuppress(minSdkVersion = 33) public class PlayStoreHomeRobot { public fun clickOnAccountButton(): PlayStoreAccountViewRobot { @@ -17,10 +46,31 @@ public class PlayStoreHomeRobot { intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP) context.startActivity(intent) + registerMeetTheSearchTabWatcher() + byObject.withContentDescContains("Signed in as") .instanceOf(FrameLayout::class.java) .waitForExists() .click() return PlayStoreAccountViewRobot() } + + private val device: UiDevice = + UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + + public fun registerMeetTheSearchTabWatcher() { + device.registerWatcher(playStoreMeetTheSearchTabName, UiWatcher { + val meetTheSearchTab = device.findObject(By.text("Meet the Search tab").clazz(TextView::class.java)) + if (meetTheSearchTab != null) { + println("Handling 'Google play Meet the Search tab' pop up.") + val searchTab = device.findObject(By.text("Search").clazz(TextView::class.java).pkg("com.android.vending")) + if (searchTab != null) { + searchTab.click() + return@UiWatcher true + } + } + false + }) + device.runWatchers() + } } \ No newline at end of file diff --git a/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/PlayStoreManageSubscriptionRobot.kt b/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/PlayStoreManageSubscriptionRobot.kt index ec1f16a88..ee815ddd2 100644 --- a/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/PlayStoreManageSubscriptionRobot.kt +++ b/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/PlayStoreManageSubscriptionRobot.kt @@ -1,9 +1,29 @@ +/* + * Copyright (c) 2024 Proton AG + * This file is part of Proton AG and ProtonCore. + * + * ProtonCore is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ProtonCore is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ProtonCore. If not, see . + */ + package me.proton.core.paymentiap.test.robot import android.view.View import android.widget.TextView +import androidx.test.filters.SdkSuppress import me.proton.test.fusion.Fusion.byObject +@SdkSuppress(minSdkVersion = 33) public class PlayStoreManageSubscriptionRobot { public fun clickCancelSubscription(): PlayStorePauseSubscriptionBottomSheetRobot { diff --git a/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/PlayStorePauseSubscriptionBottomSheetRobot.kt b/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/PlayStorePauseSubscriptionBottomSheetRobot.kt index 22637fd19..4343be5f1 100644 --- a/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/PlayStorePauseSubscriptionBottomSheetRobot.kt +++ b/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/PlayStorePauseSubscriptionBottomSheetRobot.kt @@ -1,8 +1,28 @@ +/* + * Copyright (c) 2024 Proton AG + * This file is part of Proton AG and ProtonCore. + * + * ProtonCore is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ProtonCore is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ProtonCore. If not, see . + */ + package me.proton.core.paymentiap.test.robot import android.widget.TextView +import androidx.test.filters.SdkSuppress import me.proton.test.fusion.Fusion.byObject +@SdkSuppress(minSdkVersion = 33) public class PlayStorePauseSubscriptionBottomSheetRobot { public fun clickNoThanksButton(isMonthlyBillingCycle: Boolean): PlayStoreWhatsMakingYouCancelRobot { diff --git a/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/PlayStorePaymentsAndSubscriptionsRobot.kt b/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/PlayStorePaymentsAndSubscriptionsRobot.kt index 0d7ac5727..4fc164a7b 100644 --- a/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/PlayStorePaymentsAndSubscriptionsRobot.kt +++ b/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/PlayStorePaymentsAndSubscriptionsRobot.kt @@ -1,8 +1,28 @@ +/* + * Copyright (c) 2024 Proton AG + * This file is part of Proton AG and ProtonCore. + * + * ProtonCore is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ProtonCore is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ProtonCore. If not, see . + */ + package me.proton.core.paymentiap.test.robot import android.widget.TextView +import androidx.test.filters.SdkSuppress import me.proton.test.fusion.Fusion.byObject +@SdkSuppress(minSdkVersion = 33) public class PlayStorePaymentsAndSubscriptionsRobot { public fun selectSubscriptionsItem(): PlayStoreSubscriptionsRobot { diff --git a/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/PlayStoreSubscriptionsRobot.kt b/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/PlayStoreSubscriptionsRobot.kt index 8a2994557..058f3c57b 100644 --- a/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/PlayStoreSubscriptionsRobot.kt +++ b/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/PlayStoreSubscriptionsRobot.kt @@ -1,8 +1,28 @@ +/* + * Copyright (c) 2024 Proton AG + * This file is part of Proton AG and ProtonCore. + * + * ProtonCore is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ProtonCore is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ProtonCore. If not, see . + */ + package me.proton.core.paymentiap.test.robot import android.widget.TextView +import androidx.test.filters.SdkSuppress import me.proton.test.fusion.Fusion.byObject +@SdkSuppress(minSdkVersion = 33) public class PlayStoreSubscriptionsRobot { public fun clickActiveSubscriptionByText(planName: String): PlayStoreManageSubscriptionRobot { diff --git a/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/PlayStoreWhatsMakingYouCancelRobot.kt b/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/PlayStoreWhatsMakingYouCancelRobot.kt index 8ae4ff8ea..91a8f532f 100644 --- a/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/PlayStoreWhatsMakingYouCancelRobot.kt +++ b/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/PlayStoreWhatsMakingYouCancelRobot.kt @@ -1,9 +1,29 @@ +/* + * Copyright (c) 2024 Proton AG + * This file is part of Proton AG and ProtonCore. + * + * ProtonCore is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ProtonCore is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ProtonCore. If not, see . + */ + package me.proton.core.paymentiap.test.robot import android.widget.RadioButton import android.widget.TextView +import androidx.test.filters.SdkSuppress import me.proton.test.fusion.Fusion.byObject +@SdkSuppress(minSdkVersion = 33) public class PlayStoreWhatsMakingYouCancelRobot { public fun clickIDontUseServiceEnoughItemAndPressContinue(): PlayStoreCancelSubscriptionBottomSheetRobot { diff --git a/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/SharedRobotResources.kt b/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/SharedRobotResources.kt index 6acd6cf71..4f67e63ae 100644 --- a/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/SharedRobotResources.kt +++ b/payment-iap/test/src/main/kotlin/me/proton/core/paymentiap/test/robot/SharedRobotResources.kt @@ -1,10 +1,28 @@ -package me.proton.core.paymentiap.test.robot +/* + * Copyright (c) 2024 Proton AG + * This file is part of Proton AG and ProtonCore. + * + * ProtonCore is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ProtonCore is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ProtonCore. If not, see . + */ -import me.proton.core.paymentiap.test.R -import me.proton.core.presentation.ui.view.ProtonButton +package me.proton.core.paymentiap.test.robot public const val playStorePkg: String = "com.android.vending" public const val testCardAlwaysDeclinesText: String = "Test card, always declines" public const val testCardAlwaysApprovesText: String = "Test card, always approves" public const val testCardAlwaysText: String = "Test card, always" public const val googlePlayManagedSubscriptionText: String = "Your subscription is managed by Google Play." +public const val notNowButtonUiWatcherName: String = "Google play Points Not now button watcher" +public const val authRequiredScreenWatcherName: String = "Google play Points Not now button watcher" +public const val playStoreMeetTheSearchTabName: String = "Google play meet the search tab" diff --git a/plan/test/build.gradle.kts b/plan/test/build.gradle.kts index f88be0aea..e4ce1fbdb 100644 --- a/plan/test/build.gradle.kts +++ b/plan/test/build.gradle.kts @@ -42,9 +42,7 @@ dependencies { project(Module.humanVerificationTest), project(Module.planPresentation), project(Module.paymentIapTest), - project(Module.paymentIapData), project(Module.androidUtilDagger), -// project(Module.authTest), project(Module.testRule), fusion, `kotlin-test`, diff --git a/plan/test/src/main/kotlin/me/proton/core/plan/test/MinimalSubscriptionTests.kt b/plan/test/src/main/kotlin/me/proton/core/plan/test/MinimalSubscriptionTests.kt index bed12d782..5e2bb14b2 100644 --- a/plan/test/src/main/kotlin/me/proton/core/plan/test/MinimalSubscriptionTests.kt +++ b/plan/test/src/main/kotlin/me/proton/core/plan/test/MinimalSubscriptionTests.kt @@ -61,5 +61,4 @@ public abstract class MinimalSubscriptionTests { currentPlanIsDisplayed() } } - } diff --git a/plan/test/src/main/kotlin/me/proton/core/plan/test/MinimalUpgradeFreeUserTest.kt b/plan/test/src/main/kotlin/me/proton/core/plan/test/MinimalUpgradeFreeUserTest.kt index 4b642a43a..c0d1ac1b1 100644 --- a/plan/test/src/main/kotlin/me/proton/core/plan/test/MinimalUpgradeFreeUserTest.kt +++ b/plan/test/src/main/kotlin/me/proton/core/plan/test/MinimalUpgradeFreeUserTest.kt @@ -18,31 +18,31 @@ package me.proton.core.plan.test +import androidx.test.filters.SdkSuppress +import androidx.test.platform.app.InstrumentationRegistry import dagger.hilt.android.testing.HiltAndroidTest import kotlinx.coroutines.runBlocking import me.proton.core.payment.domain.PurchaseManager import me.proton.core.payment.domain.entity.PurchaseState -import me.proton.core.paymentiap.data.GooglePurchaseStateHandler import me.proton.core.paymentiap.test.robot.GPBottomSheetSubscribeErrorRobot import me.proton.core.paymentiap.test.robot.GPBottomSheetSubscribeRobot -import me.proton.core.plan.test.robot.Plan import me.proton.core.plan.test.robot.SubscriptionRobot import me.proton.core.test.rule.annotation.PrepareUser +import me.proton.test.fusion.Fusion.byObject import me.proton.test.fusion.FusionConfig +import org.junit.After import org.junit.Before import org.junit.Test import javax.inject.Inject import kotlin.time.Duration.Companion.seconds @HiltAndroidTest -public abstract class MinimalUpgradeFreeUserTest(private val plan: Plan) { +@SdkSuppress(minSdkVersion = 33) +public abstract class MinimalUpgradeFreeUserTest(private val billingPlan: BillingPlan) { @Inject internal lateinit var purchaseManager: PurchaseManager - @Inject - internal lateinit var giapHandler: GooglePurchaseStateHandler - public abstract fun startSubscription(): SubscriptionRobot public abstract fun afterSubscriptionSteps() @@ -58,23 +58,43 @@ public abstract class MinimalUpgradeFreeUserTest(private val plan: Plan) { @PrepareUser(loginBefore = true) public fun upgradeFreeUserToPlanFailFirstPaymentAttempt(): Unit = runBlocking { startSubscription() - .selectBillingCycle(plan.billingCycle) - .selectPlan(plan) + .selectBillingCycle(billingPlan.billingCycle) + .selectPlan(billingPlan) + // Error flow .openPaymentMethods() .selectAlwaysDeclines() .clickSubscribeButton() .errorMessageIsShown() + // Success flow .clickGotIt() - .selectExpandedPlan(plan) + .selectExpandedPlan(billingPlan) .openPaymentMethods() .selectAlwaysApproves() .clickSubscribeButton() - GiapHandler(giapHandler).waitForGiapSubscribed(plan) - GiapHandler(giapHandler).waitForGiapAcknowledged(plan) - PurchaseManagerHandler(purchaseManager).waitForPurchaseState(plan, PurchaseState.Deleted) + PurchaseManagerHandler(purchaseManager).waitForPurchaseState( + billingPlan, + PurchaseState.Subscribed + ) + PurchaseManagerHandler(purchaseManager).waitForPurchaseState( + billingPlan, + PurchaseState.Acknowledged + ) + PurchaseManagerHandler(purchaseManager).waitForPurchaseState( + billingPlan, + PurchaseState.Deleted + ) + + InstrumentationRegistry.getInstrumentation() + .uiAutomation.waitForIdle(5_000L, 30_000L) + byObject.withPkg(InstrumentationRegistry.getInstrumentation().targetContext.packageName) + .waitForExists() afterSubscriptionSteps() - SubscriptionHelper.cancelSubscription(plan) + } + + @After + public fun cancelPlayStoreSubscription() { + SubscriptionHelper.cancelSubscription(billingPlan) } } diff --git a/plan/test/src/main/kotlin/me/proton/core/plan/test/Plans.kt b/plan/test/src/main/kotlin/me/proton/core/plan/test/Plans.kt new file mode 100644 index 000000000..37bbb740a --- /dev/null +++ b/plan/test/src/main/kotlin/me/proton/core/plan/test/Plans.kt @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2024 Proton AG + * This file is part of Proton AG and ProtonCore. + * + * ProtonCore is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ProtonCore is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ProtonCore. If not, see . + */ + +package me.proton.core.plan.test + +/** + * Represents billing plan object used in Payments tests with its billing cycle. + */ + +public data class BillingPlan( + val id: String, + val name: String, + val price: Double, + val billingCycle: BillingCycle +) { + + override fun toString(): String { + // Show only BillingPlan name & BillingPlan cycle in parametrized test + return "$name, ${billingCycle.value}" + } + + public companion object { + // Predefined Billing Cycles + public val Free: BillingPlan = + BillingPlan("Free", "Free", 0.0, BillingCycle(BillingCycle.PAY_MONTHLY)) + } +} + +public data class BillingCycle( + val value: String +) { + public companion object { + // Predefined payment period values + public const val PAY_ANNUALLY: String = "Pay annually" + public const val PAY_MONTHLY: String = "Pay monthly" + } +} diff --git a/plan/test/src/main/kotlin/me/proton/core/plan/test/PurchaseHelpers.kt b/plan/test/src/main/kotlin/me/proton/core/plan/test/PurchaseHelpers.kt index 6a50f7e5a..6e5f09568 100644 --- a/plan/test/src/main/kotlin/me/proton/core/plan/test/PurchaseHelpers.kt +++ b/plan/test/src/main/kotlin/me/proton/core/plan/test/PurchaseHelpers.kt @@ -1,91 +1,50 @@ package me.proton.core.plan.test import android.util.Log -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withTimeoutOrNull import me.proton.core.payment.domain.PurchaseManager -import me.proton.core.payment.domain.entity.Purchase import me.proton.core.payment.domain.entity.PurchaseState -import me.proton.core.paymentiap.data.GooglePurchaseStateHandler -import me.proton.core.paymentiap.data.onGiapAcknowledged -import me.proton.core.paymentiap.data.onGiapSubscribed +import me.proton.core.payment.domain.onPurchaseState import me.proton.core.paymentiap.test.robot.PlayStoreHomeRobot -import me.proton.core.plan.test.robot.BillingCycle -import me.proton.core.plan.test.robot.Plan - -public class GiapHandler(private val giapHandler: GooglePurchaseStateHandler) { - - public fun waitForGiapSubscribed(plan: Plan) { - runBlocking { - repeat(10) { - val job = giapHandler.onGiapSubscribed(plan.id) { - Log.d( - "GIAP_TEST", - "✅ GIAP -> Subscription subscribed: ${plan.name} - ${plan.id}" - ) - } - if (job.isCompleted) { - return@repeat - } - delay(1000) - } - } - } - - public fun waitForGiapAcknowledged(plan: Plan) { - runBlocking { - repeat(10) { - val job = giapHandler.onGiapAcknowledged(plan.id) - { - Log.d( - "GIAP_TEST", - "✅ GIAP -> Subscription acknowledged: ${plan.name} - ${plan.id}" - ) - } - if (job.isCompleted) { - return@repeat - } - delay(1000) - } - } - } -} public class PurchaseManagerHandler(private val purchaseManager: PurchaseManager) { - public suspend fun waitForPurchaseState(plan: Plan, state: PurchaseState): Purchase? { - repeat(10) { - val purchase = purchaseManager - .observePurchase(plan.id) - .filter { it?.purchaseState == state } - .firstOrNull() - if (purchase != null) { + public fun waitForPurchaseState( + billingPlan: BillingPlan, + state: PurchaseState, + timeMills: Long = 10000 + ) { + runBlocking { + withTimeoutOrNull(timeMills) { + purchaseManager.onPurchaseState(state, planName = billingPlan.id).first() Log.d( "GIAP_TEST", - "✅ PurchaseManager -> Purchase deleted from database: ${plan.name} - ${plan.id}" + "✅ PurchaseManager -> Purchase is in state '$state': " + + "${billingPlan.name} - ${billingPlan.id}" ) - return purchase } - delay(1000) } - return null } } public object SubscriptionHelper { - public fun cancelSubscription(plan: Plan) { + public fun cancelSubscription(billingPlan: BillingPlan) { PlayStoreHomeRobot() .clickOnAccountButton() .selectPaymentsAndSubscriptionsItem() .selectSubscriptionsItem() .clickActiveSubscriptionByText( - plan.name.takeIf { plan.billingCycle.value == BillingCycle.PAY_ANNUALLY } - ?: "${plan.name} 1 month" + billingPlan.name.takeIf { + billingPlan.billingCycle.value == BillingCycle.PAY_ANNUALLY + } + ?: "${billingPlan.name} 1 month" ) .clickCancelSubscription() - .clickNoThanksButton(plan.billingCycle.value == BillingCycle.PAY_MONTHLY) + .clickNoThanksButton( + billingPlan.billingCycle.value == BillingCycle.PAY_MONTHLY + ) .clickIDontUseServiceEnoughItemAndPressContinue() .clickCancelSubscription() .subscriptionIsCancelled() diff --git a/plan/test/src/main/kotlin/me/proton/core/plan/test/robot/Plans.kt b/plan/test/src/main/kotlin/me/proton/core/plan/test/robot/Plans.kt deleted file mode 100644 index 3794e29a0..000000000 --- a/plan/test/src/main/kotlin/me/proton/core/plan/test/robot/Plans.kt +++ /dev/null @@ -1,35 +0,0 @@ -package me.proton.core.plan.test.robot - -public data class BillingCycle( - val value: String, - val monthlyPrice: Double -) { - public companion object { - // Predefined payment period values - public const val PAY_ANNUALLY: String = "Pay annually" - public const val PAY_MONTHLY: String = "Pay monthly" - } -} - -public data class Plan( - val id: String, - val name: String, - val billingCycle: BillingCycle -) { - - override fun toString(): String { - return "$name, ${billingCycle.value}" // Shows only Plan name in parametrized test - } - - public companion object { - // Predefined Billing Cycles - public val Free: Plan = - Plan( - "Free", "Free", - BillingCycle( - BillingCycle.PAY_MONTHLY, - 0.0 - ) - ) - } -} diff --git a/plan/test/src/main/kotlin/me/proton/core/plan/test/robot/SubscriptionRobot.kt b/plan/test/src/main/kotlin/me/proton/core/plan/test/robot/SubscriptionRobot.kt index 036c7e905..ba235fa4f 100644 --- a/plan/test/src/main/kotlin/me/proton/core/plan/test/robot/SubscriptionRobot.kt +++ b/plan/test/src/main/kotlin/me/proton/core/plan/test/robot/SubscriptionRobot.kt @@ -23,14 +23,14 @@ import me.proton.core.payment.presentation.view.ProtonPaymentButton import me.proton.core.paymentiap.test.robot.GPBottomSheetSubscribeRobot import me.proton.core.paymentiap.test.robot.PlayStoreSubscriptionsRobot import me.proton.core.plan.presentation.R +import me.proton.core.plan.test.BillingCycle +import me.proton.core.plan.test.BillingPlan import me.proton.core.presentation.ui.view.ProtonButton -import me.proton.test.fusion.Fusion.byObject import me.proton.test.fusion.Fusion.device import me.proton.test.fusion.Fusion.view import me.proton.test.fusion.FusionConfig import me.proton.test.fusion.ui.common.enums.SwipeDirection import me.proton.test.fusion.ui.espresso.builders.OnView -import okhttp3.internal.wait import kotlin.time.Duration.Companion.seconds public object SubscriptionRobot { @@ -67,12 +67,12 @@ public object SubscriptionRobot { view.withCustomMatcher(ViewMatchers.withSubstring("Get")) } - internal fun togglePlanItem(plan: Plan) { - view.withId(R.id.title).withText(plan.name).scrollTo().click() + internal fun togglePlanItem(billingPlan: BillingPlan) { + view.withId(R.id.title).withText(billingPlan.name).scrollTo().click() } - internal fun getPlanButton(plan: Plan): OnView { - val buttonText = FusionConfig.targetContext.getString(R.string.plans_get_proton, plan.name) + internal fun getPlanButton(billingPlan: BillingPlan): OnView { + val buttonText = FusionConfig.targetContext.getString(R.string.plans_get_proton, billingPlan.name) return view.instanceOf(ProtonPaymentButton::class.java).containsText(buttonText) } @@ -80,31 +80,31 @@ public object SubscriptionRobot { return view.withText(R.string.plans_proton_for_free) } - private fun expandPlan(plan: Plan) { - togglePlanItem(plan) + private fun expandPlan(billingPlan: BillingPlan) { + togglePlanItem(billingPlan) } - public fun selectExpandedPlan(plan: Plan): GPBottomSheetSubscribeRobot { + public fun selectExpandedPlan(billingPlan: BillingPlan): GPBottomSheetSubscribeRobot { view.withId(R.id.scrollContent).hasDescendant(view.withId(R.id.plans)).swipe(SwipeDirection.Up) - getPlanButton(plan).click() + getPlanButton(billingPlan).click() return GPBottomSheetSubscribeRobot() } public fun selectFreePlan() { view.withText("Free").await(timeout = 90.seconds) { checkIsDisplayed() } view.withText("Free").scrollTo().click() - getPlanButton(Plan.Free).scrollTo().click() + getPlanButton(BillingPlan.Free).scrollTo().click() } - public fun selectPlan(plan: Plan): GPBottomSheetSubscribeRobot { + public fun selectPlan(billingPlan: BillingPlan): GPBottomSheetSubscribeRobot { planSelectionIsDisplayed() - expandPlan(plan) - selectExpandedPlan(plan) + expandPlan(billingPlan) + selectExpandedPlan(billingPlan) return GPBottomSheetSubscribeRobot() } - public fun togglePlan(plan: Plan) { - togglePlanItem(plan) + public fun togglePlan(billingPlan: BillingPlan) { + togglePlanItem(billingPlan) } public fun selectBillingCycle(cycle: BillingCycle): SubscriptionRobot { @@ -164,8 +164,8 @@ public object SubscriptionRobot { view.withId(R.id.price_cycle).withText(value).await { checkIsDisplayed() } } - public fun verifyCanGetPlan(plan: Plan) { - getPlanButton(plan) + public fun verifyCanGetPlan(billingPlan: BillingPlan) { + getPlanButton(billingPlan) .scrollTo() .checkIsDisplayed() .checkIsEnabled() diff --git a/plugins/core/src/main/kotlin/versionsConfig.kt b/plugins/core/src/main/kotlin/versionsConfig.kt index c69c76251..7b0392a84 100644 --- a/plugins/core/src/main/kotlin/versionsConfig.kt +++ b/plugins/core/src/main/kotlin/versionsConfig.kt @@ -141,5 +141,5 @@ public const val `json-simple version`: String = "1.1.1" public const val `turbine version`: String = "0.12.1" public const val `junit version`: String = "4.13.2" public const val `junit-ktx version`: String = "1.1.4" -public const val `fusion version`: String = "1.0.1" +public const val `fusion version`: String = "1.0.4" // endregion