Updated ui tests and robots

This commit is contained in:
Artiom Košelev 2021-11-04 18:48:55 +02:00 committed by dkadrikj
parent 995b785ca1
commit 5e739c94c8
12 changed files with 93 additions and 38 deletions

View File

@ -21,7 +21,7 @@ package me.proton.core.test.android.uitests.tests.medium.auth.signup
import me.proton.android.core.coreexample.BuildConfig
import me.proton.core.test.android.instrumented.utils.StringUtils.randomString
import me.proton.core.test.android.plugins.data.User
import me.proton.core.test.android.plugins.data.Plan.Free
import me.proton.core.test.android.plugins.data.Plan.Dev
import me.proton.core.test.android.robots.CoreRobot
import me.proton.core.test.android.robots.auth.ChooseUsernameRobot
import me.proton.core.test.android.robots.humanverification.CodeVerificationRobot
@ -76,6 +76,6 @@ class ExternalSetupTests : BaseTest() {
.setAndConfirmPassword<CodeVerificationRobot>(user.password)
.setCode(defaultCode)
.verifyCode<SelectPlanRobot>()
.verify { canSelectPlan(Free) }
.verify { canSelectPlan(Dev) }
}
}

View File

@ -26,7 +26,7 @@ import me.proton.core.test.android.robots.auth.signup.RecoveryMethodsRobot
import me.proton.core.test.android.robots.auth.signup.RecoveryMethodsRobot.RecoveryMethodType
import me.proton.core.test.android.uitests.CoreexampleRobot
import me.proton.core.test.android.uitests.tests.BaseTest
import me.proton.core.test.android.plugins.data.Plan.Free
import me.proton.core.test.android.plugins.data.Plan.Dev
import org.junit.Before
import org.junit.Test
@ -74,8 +74,8 @@ class RecoveryMethodsSetupTests : BaseTest() {
.skip()
.skipConfirm()
.verify {
planDetailsDisplayed(Free)
canSelectPlan(Free)
planDetailsDisplayed(Dev)
canSelectPlan(Dev)
}
}
@ -85,8 +85,8 @@ class RecoveryMethodsSetupTests : BaseTest() {
.next<RecoveryMethodsRobot.SkipRecoveryRobot>()
.skipConfirm()
.verify {
planDetailsDisplayed(Free)
canSelectPlan(Free)
planDetailsDisplayed(Dev)
canSelectPlan(Dev)
}
}
}

View File

@ -22,7 +22,6 @@ import me.proton.core.test.android.uitests.tests.SmokeTest
import me.proton.core.test.android.plugins.data.Plan.Free
import me.proton.core.test.android.plugins.data.Plan.Dev
import me.proton.core.test.android.plugins.data.User
import me.proton.core.plan.R
import me.proton.core.test.android.robots.auth.AddAccountRobot
import me.proton.core.test.android.robots.auth.signup.RecoveryMethodsRobot
import me.proton.core.test.android.robots.humanverification.HumanVerificationRobot
@ -56,12 +55,6 @@ class SelectPlanTests : BaseTest() {
@Test
@SmokeTest
fun selectFreeAndCancelHumanVerification() {
selectPlanRobot
.view.withId(R.id.scrollContent)
.hasDescendant(
selectPlanRobot.view.withId(R.id.plansView)
).swipeUp()
selectPlanRobot
.selectPlan<HumanVerificationRobot>(Free)
.verify {

View File

@ -50,8 +50,7 @@ class ExistingPaymentMethodTests : BaseTest() {
loginRobot
.loginUser<CoreexampleRobot>(user)
.plansUpgrade()
.selectPlan(plan)
.upgradeToPlan(plan)
@Test
@Ignore("Requires user with paypal account linked")

View File

@ -45,8 +45,8 @@ class NewCreditCardTests : BaseTest() {
.loginUser<CoreexampleRobot>(userWithoutCard)
.plansUpgrade()
.changeCurrency(Currency.CHF)
.selectPlan<AddCreditCardRobot>(Plan.Dev)
.verify { billingDetailsDisplayed(Plan.Dev, BillingCycle.Yearly, Currency.CHF.symbol, 47.88) }
.upgradeToPlan<AddCreditCardRobot>(Plan.Dev)
.verify { billingDetailsDisplayed(Plan.Dev, BillingCycle.Yearly, Currency.CHF.symbol) }
}
@Test

View File

@ -49,10 +49,10 @@ class CurrentPlanTests : BaseTest() {
fun userWithFreePlan() {
val freeUser = users.getUser { !it.isPaid }
navigateUserToCurrentPlans(freeUser)
.scrollToPlan(Plan.Dev)
.verify {
canSelectPlan(Plan.Dev)
canUpgradeToPlan(Plan.Dev)
planDetailsDisplayed(Plan.Dev)
planDetailsDisplayed(Plan.Free)
}
}

View File

@ -57,8 +57,9 @@ class UpgradePlanTests : BaseTest() {
@SmokeTest
fun userWithFreePlan() {
navigateUserToCurrentPlans(freeUser)
.scrollToPlan(Plan.Dev)
.verify {
canSelectPlan(Plan.Dev)
canUpgradeToPlan(Plan.Dev)
planDetailsDisplayed(Plan.Dev)
}

View File

@ -46,7 +46,7 @@ import org.junit.runner.Description
open class ProtonTest(
private val activity: Class<out Activity>,
defaultTimeout: Long = 10_000L,
tries: Int = 2,
tries: Int = 1,
testWatcher: TestWatcher = TestExecutionWatcher(),
activityScenarioRule: ActivityScenarioRule<out Activity> = ActivityScenarioRule(activity),
) {

View File

@ -20,6 +20,7 @@
package me.proton.core.test.android.instrumented.builders
import android.view.View
import androidx.annotation.StringRes
import androidx.recyclerview.widget.RecyclerView
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.PerformException
@ -33,6 +34,7 @@ import androidx.test.espresso.contrib.RecyclerViewActions.actionOnHolderItem
import androidx.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition
import androidx.test.espresso.matcher.ViewMatchers
import me.proton.core.test.android.instrumented.ui.Actions.clickOnMatchedDescendant
import me.proton.core.test.android.instrumented.utils.StringUtils
import org.hamcrest.Matcher
import org.hamcrest.core.AllOf
@ -65,6 +67,10 @@ class OnRecyclerView : ConditionWatcher {
matchers.add(ViewMatchers.withText(text))
}
fun withText(@StringRes textId: Int) = apply {
matchers.add(ViewMatchers.withText(StringUtils.stringFromResource(textId)))
}
/** [RecyclerViewActions] **/
fun click() = apply {
perform(ViewActions.click())
@ -94,6 +100,10 @@ class OnRecyclerView : ConditionWatcher {
perform(ViewActions.swipeUp())
}
fun scrollTo() = apply {
perform(ViewActions.scrollTo())
}
fun waitUntilGone() = apply {
waitForCondition({ viewInteraction().check(ViewAssertions.doesNotExist()) })
}

View File

@ -25,7 +25,7 @@ import me.proton.core.test.android.instrumented.ProtonTest.Companion.getTargetCo
@Serializable
enum class Plan(var planName: String, var text: String) {
Free("free", "Free"),
Professional("pro", "Professional"),
Professional("pro", "ProtonMail Professional"),
Visionary("visionary", "Visionary"),
Plus("plus", "ProtonMail Plus"),
Dev("", "")
@ -36,9 +36,10 @@ fun randomPaidPlan(): Plan = Plan.values().filter { it != Plan.Free }.random()
val supportedBillingCycles: Array<String> =
getTargetContext().resources.getStringArray(R.array.supported_billing_cycle)
enum class BillingCycle(val value: String) {
Monthly(supportedBillingCycles[0]),
Yearly(supportedBillingCycles[1]),
enum class BillingCycle(val value: String, val monthlyPrice: Double, val yearlyPrice: Number) {
Monthly(supportedBillingCycles[0], 5.00, 0.00),
Yearly(supportedBillingCycles[1], 4.00, 48.00),
TwoYear(supportedBillingCycles[2], 3.29, 39.48)
}
enum class Currency(val symbol: String, val code: String) {

View File

@ -18,7 +18,6 @@
package me.proton.core.test.android.robots.payments
import android.widget.EditText
import me.proton.core.payment.presentation.R
import me.proton.core.test.android.plugins.data.BillingCycle
import me.proton.core.test.android.plugins.data.Plan
@ -41,12 +40,12 @@ open class PaymentRobot : CoreRobot() {
fun billingDetailsDisplayed(
plan: Plan,
billingCycle: BillingCycle,
currency: String,
amount: Number
currency: String
) {
val yearlyPriceString = String.format("%.2f", billingCycle.yearlyPrice)
view.withId(R.id.planNameText).withText(plan.text).checkDisplayed()
view.withId(R.id.billingPeriodText).withText("Billed ${billingCycle.toString().lowercase()}").checkDisplayed()
view.withId(R.id.amountText).withText("$currency$amount").checkDisplayed()
view.withId(R.id.amountText).withText("$currency$yearlyPriceString").checkDisplayed()
}
fun paymentMethodDisplayed(title: String, details: String) {

View File

@ -18,6 +18,8 @@
package me.proton.core.test.android.robots.plans
import androidx.annotation.StringRes
import androidx.core.widget.NestedScrollView
import me.proton.core.plan.presentation.R
import me.proton.core.test.android.instrumented.utils.StringUtils.stringFromResource
import me.proton.core.test.android.plugins.data.BillingCycle
@ -26,15 +28,51 @@ import me.proton.core.test.android.plugins.data.Plan
import me.proton.core.test.android.robots.CoreRobot
import me.proton.core.test.android.robots.CoreVerify
class SelectPlanRobot : CoreRobot() {
/**
* Scrolls to a provided [plan]
* @return an instance of [SelectPlanRobot]
*/
fun scrollToPlan(plan: Plan): SelectPlanRobot {
// Only one paid plan is currently implemented
val position = when (plan) {
Plan.Dev -> 0
Plan.Free -> 1
else -> -1
}
recyclerView.withId(R.id.planListRecyclerView)
.onItemAtPosition(position).scrollTo()
return this
}
/**
* Clicks 'Select' button on a provided [plan]
* @param T next Robot in flow
* @return an instance of [T]
*/
inline fun <reified T> selectPlan(plan: Plan): T {
view.withText(R.string.plans_select_plan)
inline fun <reified T> selectPlan(plan: Plan): T =
scrollToPlan(plan)
.clickPlanButtonWithText(plan, R.string.plans_select_plan)
/**
* Clicks 'Upgrade' button on a provided [plan]
* @param T next Robot in flow
* @return an instance of [T]
*/
inline fun <reified T> upgradeToPlan(plan: Plan): T =
scrollToPlan(plan)
.clickPlanButtonWithText(plan, R.string.plans_upgrade_plan)
/**
* Clicks button with [textId] resource on a provided [plan]
* @param T next Robot in flow
* @return an instance of [T]
*/
inline fun <reified T> clickPlanButtonWithText(plan: Plan, @StringRes textId: Int): T {
view.withText(textId)
.isDescendantOf(
view.withId(R.id.planGroup).hasSibling(
view.withText(plan.text)
@ -47,6 +85,7 @@ class SelectPlanRobot : CoreRobot() {
* Changes billing cycle to provided [billingCycle]
*/
fun changeBillingCycle(billingCycle: BillingCycle): SelectPlanRobot {
view.instanceOf(NestedScrollView::class.java).swipeDown()
view.withId(R.id.billingCycleSpinner).click()
view.withText(billingCycle.value).click()
return this
@ -56,12 +95,15 @@ class SelectPlanRobot : CoreRobot() {
* Changes currency to provided [currency]
*/
fun changeCurrency(currency: Currency): SelectPlanRobot {
view.withId(R.id.billingCycleSpinner).checkDisplayed()
view.instanceOf(NestedScrollView::class.java).swipeUp()
view.withId(R.id.currencySpinner).click()
view.withText(currency.code).click()
return this
}
class Verify : CoreVerify() {
fun planDetailsDisplayed(plan: Plan) {
view.withText(plan.text).checkDisplayed()
}
@ -75,17 +117,27 @@ class SelectPlanRobot : CoreRobot() {
).checkDisplayed()
}
fun canUpgradeToPlan(plan: Plan) {
view.withText(R.string.plans_upgrade_plan)
.isDescendantOf(
view.withId(R.id.planGroup).hasSibling(
view.withText(plan.text)
)
).checkDisplayed()
}
fun billingCycleIs(billingCycle: BillingCycle, currency: Currency = Currency.Euro) {
// TODO: re-work to reflect 4.5 design
view.withId(R.id.planPriceText).withText("${currency.symbol}${billingCycle.monthlyPrice}")
when (billingCycle) {
BillingCycle.Monthly -> {
view.withId(R.id.planPriceText).withText("${currency.symbol}4.99")
view.withText(R.string.plans_save_20).checkDisplayed()
}
BillingCycle.Yearly -> {
else -> {
val yearlyPriceString = String.format("%.2f", billingCycle.yearlyPrice)
val billedAsString =
stringFromResource(R.string.plans_billed_yearly).format("${currency.symbol}47.88")
view.withId(R.id.planPriceDescriptionText).checkContains(billedAsString)
view.withId(R.id.planPriceText).checkContains("${currency.symbol}3.99")
stringFromResource(R.string.plans_billed_yearly).format("${currency.symbol}$yearlyPriceString")
view.withId(R.id.planPriceDescriptionText).withText(billedAsString).checkDisplayed()
view.withText(R.string.plans_renew_info).checkDisplayed()
}
}
}