Add espresso tests for donations flow.

This commit is contained in:
Alex Hart 2024-08-07 09:33:06 -03:00 committed by mtang-signal
parent 11d165a17b
commit ccabd9edd8
5 changed files with 143 additions and 4 deletions

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,131 @@
package org.thoughtcrime.securesms.components.settings.app.subscription.donate
import androidx.test.core.app.ActivityScenario
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.isSelected
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import okhttp3.mockwebserver.MockResponse
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.signal.donations.InAppPaymentType
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.components.settings.app.subscription.InAppPaymentsRepository
import org.thoughtcrime.securesms.database.model.InAppPaymentSubscriberRecord
import org.thoughtcrime.securesms.database.model.databaseprotos.InAppPaymentData
import org.thoughtcrime.securesms.dependencies.InstrumentationApplicationDependencyProvider
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.testing.Delete
import org.thoughtcrime.securesms.testing.Get
import org.thoughtcrime.securesms.testing.SignalActivityRule
import org.thoughtcrime.securesms.testing.success
import org.thoughtcrime.securesms.util.JsonUtils
import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription
import org.whispersystems.signalservice.api.subscriptions.SubscriberId
import org.whispersystems.signalservice.internal.push.SubscriptionsConfiguration
import java.math.BigDecimal
import java.util.Currency
import kotlin.time.Duration.Companion.days
import kotlin.time.Duration.Companion.milliseconds
@Suppress("ClassName")
@RunWith(AndroidJUnit4::class)
class CheckoutFlowActivityTest__RecurringDonations {
@get:Rule
val harness = SignalActivityRule(othersCount = 10)
private val intent = CheckoutFlowActivity.createIntent(InstrumentationRegistry.getInstrumentation().targetContext, InAppPaymentType.RECURRING_DONATION)
@Test
fun givenRecurringDonations_whenILoadScreen_thenIExpectMonthlySelected() {
ActivityScenario.launch<CheckoutFlowActivity>(intent)
onView(withId(R.id.monthly)).check(matches(isSelected()))
}
@Test
fun givenNoCurrentDonation_whenILoadScreen_thenIExpectContinueButton() {
ActivityScenario.launch<CheckoutFlowActivity>(intent)
onView(withText("Continue")).check(matches(isDisplayed()))
}
@Test
fun givenACurrentDonation_whenILoadScreen_thenIExpectUpgradeButton() {
initialiseConfigurationResponse()
initialiseActiveSubscription()
ActivityScenario.launch<CheckoutFlowActivity>(intent)
onView(withText(R.string.SubscribeFragment__update_subscription)).check(matches(isDisplayed()))
onView(withText(R.string.SubscribeFragment__cancel_subscription)).check(matches(isDisplayed()))
}
@Test
fun givenACurrentDonation_whenIPressCancel_thenIExpectCancellationDialog() {
initialiseConfigurationResponse()
initialiseActiveSubscription()
ActivityScenario.launch<CheckoutFlowActivity>(intent)
onView(withText(R.string.SubscribeFragment__cancel_subscription)).check(matches(isDisplayed()))
onView(withText(R.string.SubscribeFragment__cancel_subscription)).perform(ViewActions.click())
onView(withText(R.string.SubscribeFragment__confirm_cancellation)).check(matches(isDisplayed()))
onView(withText(R.string.SubscribeFragment__confirm)).perform(ViewActions.click())
onView(withText(R.string.StripePaymentInProgressFragment__cancelling)).check(matches(isDisplayed()))
}
private fun initialiseConfigurationResponse() {
InstrumentationApplicationDependencyProvider.addMockWebRequestHandlers(
Get("/v1/subscription/configuration") {
val assets = InstrumentationRegistry.getInstrumentation().context.resources.assets
assets.open("inAppPaymentsTests/configuration.json").use { stream ->
MockResponse().success(JsonUtils.fromJson(stream, SubscriptionsConfiguration::class.java))
}
}
)
}
private fun initialiseActiveSubscription() {
val currency = Currency.getInstance("USD")
val subscriber = InAppPaymentSubscriberRecord(
subscriberId = SubscriberId.generate(),
currency = currency,
type = InAppPaymentSubscriberRecord.Type.DONATION,
requiresCancel = false,
paymentMethodType = InAppPaymentData.PaymentMethodType.CARD
)
InAppPaymentsRepository.setSubscriber(subscriber)
SignalStore.inAppPayments.setSubscriberCurrency(currency, subscriber.type)
InstrumentationApplicationDependencyProvider.addMockWebRequestHandlers(
Get("/v1/subscription/${subscriber.subscriberId.serialize()}") {
MockResponse().success(
ActiveSubscription(
ActiveSubscription.Subscription(
200,
currency.currencyCode,
BigDecimal.ONE,
System.currentTimeMillis().milliseconds.inWholeSeconds + 30.days.inWholeSeconds,
true,
System.currentTimeMillis().milliseconds.inWholeSeconds + 30.days.inWholeSeconds,
false,
"active",
"STRIPE",
"CARD",
false
),
null
)
)
},
Delete("/v1/subscription/${subscriber.subscriberId.serialize()}") {
Thread.sleep(10000)
MockResponse().success()
}
)
}
}

View file

@ -392,7 +392,13 @@ class DonateToSignalViewModel(
if (selectedCurrency !in priceCurrencies) {
Log.w(TAG, "Unsupported currency selection. Defaulting to USD. $selectedCurrency isn't supported.")
val usd = PlatformCurrencyUtil.USD
val newSubscriber = InAppPaymentsRepository.getSubscriber(usd, InAppPaymentSubscriberRecord.Type.DONATION) ?: InAppPaymentSubscriberRecord(SubscriberId.generate(), usd, InAppPaymentSubscriberRecord.Type.DONATION, false, InAppPaymentData.PaymentMethodType.UNKNOWN)
val newSubscriber = InAppPaymentsRepository.getSubscriber(usd, InAppPaymentSubscriberRecord.Type.DONATION) ?: InAppPaymentSubscriberRecord(
subscriberId = SubscriberId.generate(),
currency = usd,
type = InAppPaymentSubscriberRecord.Type.DONATION,
requiresCancel = false,
paymentMethodType = InAppPaymentData.PaymentMethodType.UNKNOWN
)
InAppPaymentsRepository.setSubscriber(newSubscriber)
RecurringInAppPaymentRepository.syncAccountRecord().subscribe()
}

View file

@ -88,13 +88,13 @@ public final class SignalExecutors {
return handlerThread;
}
private static class NumberedThreadFactory implements ThreadFactory {
public static class NumberedThreadFactory implements ThreadFactory {
private final int priority;
private final String baseName;
private final AtomicInteger counter;
NumberedThreadFactory(@NonNull String baseName, int priority) {
public NumberedThreadFactory(@NonNull String baseName, int priority) {
this.priority = priority;
this.baseName = baseName;
this.counter = new AtomicInteger();

View file

@ -181,6 +181,7 @@ dependencyResolutionManagement {
version("androidx-test", "1.5.0")
version("androidx-test-ext-junit", "1.1.5")
version("robolectric", "4.10.3")
version("espresso", "3.4.0")
library("junit-junit", "junit:junit:4.13.2")
library("androidx-test-core", "androidx.test", "core").versionRef("androidx-test")
@ -190,7 +191,7 @@ dependencyResolutionManagement {
library("androidx-test-monitor", "androidx.test:monitor:1.6.1")
library("androidx-test-orchestrator", "androidx.test:orchestrator:1.4.1")
library("androidx-test-runner", "androidx.test", "runner").versionRef("androidx-test")
library("espresso-core", "androidx.test.espresso:espresso-core:3.4.0")
library("espresso-core", "androidx.test.espresso", "espresso-core").versionRef("espresso")
library("mockito-core", "org.mockito:mockito-inline:4.6.1")
library("mockito-kotlin", "org.mockito.kotlin:mockito-kotlin:4.0.0")
library("mockito-android", "org.mockito:mockito-android:4.6.1")