diff --git a/paymentsheet-example/src/androidTest/java/com/stripe/android/lpm/TestSepaDebit.kt b/paymentsheet-example/src/androidTest/java/com/stripe/android/lpm/TestSepaDebit.kt index a1546ad3c98..1a6a9a116c3 100644 --- a/paymentsheet-example/src/androidTest/java/com/stripe/android/lpm/TestSepaDebit.kt +++ b/paymentsheet-example/src/androidTest/java/com/stripe/android/lpm/TestSepaDebit.kt @@ -1,7 +1,9 @@ package com.stripe.android.lpm import androidx.compose.ui.test.assertContentDescriptionEquals +import androidx.compose.ui.test.hasTestTag import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performTextInput import androidx.test.ext.junit.runners.AndroidJUnit4 import com.stripe.android.BasePlaygroundTest @@ -34,11 +36,7 @@ internal class TestSepaDebit : BasePlaygroundTest() { testDriver.confirmNewOrGuestComplete( testParameters = testParameters, ) { - rules.compose.onNodeWithText("IBAN").apply { - performTextInput( - "DE89370400440532013000" - ) - } + fillOutIban() } } @@ -50,11 +48,7 @@ internal class TestSepaDebit : BasePlaygroundTest() { settings[CheckoutModeSettingsDefinition] = CheckoutMode.PAYMENT_WITH_SETUP } ) { - rules.compose.onNodeWithText("IBAN").apply { - performTextInput( - "DE89370400440532013000" - ) - } + fillOutIban() } } @@ -66,11 +60,7 @@ internal class TestSepaDebit : BasePlaygroundTest() { settings[CheckoutModeSettingsDefinition] = CheckoutMode.SETUP } ) { - rules.compose.onNodeWithText("IBAN").apply { - performTextInput( - "DE89370400440532013000" - ) - } + fillOutIban() } } @@ -79,11 +69,7 @@ internal class TestSepaDebit : BasePlaygroundTest() { testDriver.confirmCustom( testParameters = testParameters, populateCustomLpmFields = { - rules.compose.onNodeWithText("IBAN").apply { - performTextInput( - "DE89370400440532013000" - ) - } + fillOutIban() }, verifyCustomLpmFields = { rules.compose.onNodeWithText("IBAN").apply { @@ -94,4 +80,61 @@ internal class TestSepaDebit : BasePlaygroundTest() { } ) } + + @Test + fun testSepaDebitDefaultReturningUserFlowWithoutShowingFlowController() { + val testParameters = testParameters.copyPlaygroundSettings { settings -> + settings[AutomaticPaymentMethodsSettingsDefinition] = true + settings[CheckoutModeSettingsDefinition] = CheckoutMode.SETUP + } + + // Create the payment method and set it as default by going through the whole flow. + val playgroundState = testDriver.confirmNewOrGuestComplete( + testParameters = testParameters, + ) { + fillOutIban() + } + + testDriver.confirmCustomWithDefaultSavedPaymentMethod( + customerId = playgroundState?.customerConfig?.id, + testParameters = testParameters, + afterBuyAction = { + rules.compose.onNode(hasTestTag("SEPA_MANDATE_CONTINUE_BUTTON")) + .performClick() + } + ) + } + + @Test + fun testSepaDebitDefaultReturningUserFlowWithShowingFlowController() { + val testParameters = testParameters.copyPlaygroundSettings { settings -> + settings[AutomaticPaymentMethodsSettingsDefinition] = true + settings[CheckoutModeSettingsDefinition] = CheckoutMode.SETUP + } + + // Create the payment method and set it as default by going through the whole flow. + val playgroundState = testDriver.confirmNewOrGuestComplete( + testParameters = testParameters, + ) { + fillOutIban() + } + + testDriver.confirmCustomWithDefaultSavedPaymentMethod( + customerId = playgroundState?.customerConfig?.id, + testParameters = testParameters, + beforeBuyAction = { selectors -> + selectors.multiStepSelect.click() + selectors.paymentSelection.click() + selectors.continueButton.click() + } + ) + } + + private fun fillOutIban() { + rules.compose.onNodeWithText("IBAN").apply { + performTextInput( + "DE89370400440532013000" + ) + } + } } diff --git a/paymentsheet-example/src/androidTest/java/com/stripe/android/test/core/PlaygroundTestDriver.kt b/paymentsheet-example/src/androidTest/java/com/stripe/android/test/core/PlaygroundTestDriver.kt index 6f68ad6c5f4..e90f2ab1881 100644 --- a/paymentsheet-example/src/androidTest/java/com/stripe/android/test/core/PlaygroundTestDriver.kt +++ b/paymentsheet-example/src/androidTest/java/com/stripe/android/test/core/PlaygroundTestDriver.kt @@ -27,14 +27,18 @@ import com.google.common.truth.Truth.assertThat import com.karumi.shot.ScreenshotTest import com.stripe.android.paymentsheet.PAYMENT_OPTION_CARD_TEST_TAG import com.stripe.android.paymentsheet.example.playground.PaymentSheetPlaygroundActivity +import com.stripe.android.paymentsheet.example.playground.PlaygroundState import com.stripe.android.paymentsheet.example.playground.settings.CheckoutMode import com.stripe.android.paymentsheet.example.playground.settings.CheckoutModeSettingsDefinition +import com.stripe.android.paymentsheet.example.playground.settings.CustomerSettingsDefinition +import com.stripe.android.paymentsheet.example.playground.settings.CustomerType import com.stripe.android.paymentsheet.example.playground.settings.IntegrationType import com.stripe.android.paymentsheet.example.playground.settings.IntegrationTypeSettingsDefinition import com.stripe.android.test.core.ui.BrowserUI import com.stripe.android.test.core.ui.Selectors import com.stripe.android.test.core.ui.UiAutomatorText import kotlinx.coroutines.launch +import org.junit.Assert.fail import org.junit.Assume import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit @@ -55,6 +59,7 @@ internal class PlaygroundTestDriver( private val device: UiDevice, private val composeTestRule: ComposeTestRule, ) : ScreenshotTest { + @Volatile private var resultValue: String? = null private lateinit var testParameters: TestParameters private lateinit var selectors: Selectors @@ -62,6 +67,9 @@ internal class PlaygroundTestDriver( private val currentActivity = Array(1) { null } private var application: Application? = null + @Volatile + private var playgroundState: PlaygroundState? = null + private val activityLifecycleCallbacks = object : Application.ActivityLifecycleCallbacks { override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {} override fun onActivityStarted(activity: Activity) {} @@ -201,6 +209,36 @@ internal class PlaygroundTestDriver( teardown() } + fun confirmCustomWithDefaultSavedPaymentMethod( + customerId: String?, + testParameters: TestParameters, + beforeBuyAction: (Selectors) -> Unit = {}, + afterBuyAction: (Selectors) -> Unit = {}, + ) { + if (customerId == null) { + fail("No customer id") + return + } + + setup( + testParameters.copyPlaygroundSettings { settings -> + settings[IntegrationTypeSettingsDefinition] = IntegrationType.FlowController + settings[CustomerSettingsDefinition] = CustomerType.Existing(customerId) + } + ) + launchCustom(clickMultiStep = false) + + beforeBuyAction(selectors) + + selectors.playgroundBuyButton.click() + + afterBuyAction(selectors) + + doAuthorization() + + teardown() + } + private fun pressMultiStepSelect() { selectors.multiStepSelect.click() waitForNotPlaygroundActivity() @@ -221,7 +259,7 @@ internal class PlaygroundTestDriver( testParameters: TestParameters, values: FieldPopulator.Values = FieldPopulator.Values(), populateCustomLpmFields: () -> Unit = {}, - ) { + ): PlaygroundState? { setup(testParameters) launchComplete() @@ -241,11 +279,15 @@ internal class PlaygroundTestDriver( testParameters.useBrowser ) + val result = playgroundState + pressBuy() doAuthorization() teardown() + + return result } /** @@ -378,16 +420,18 @@ internal class PlaygroundTestDriver( waitForNotPlaygroundActivity() } - private fun launchCustom() { + private fun launchCustom(clickMultiStep: Boolean = true) { selectors.reload.click() Espresso.onIdle() selectors.composeTestRule.waitForIdle() selectors.multiStepSelect.waitForEnabled() - selectors.multiStepSelect.click() + if (clickMultiStep) { + selectors.multiStepSelect.click() - // PaymentOptionsActivity is now on screen - waitForNotPlaygroundActivity() + // PaymentOptionsActivity is now on screen + waitForNotPlaygroundActivity() + } } private fun doAuthorization() { @@ -513,6 +557,13 @@ internal class PlaygroundTestDriver( resultValue = it?.message } } + + activity.lifecycleScope.launch { + activity.viewModel.state.collect { playgroundState -> + this@PlaygroundTestDriver.playgroundState = playgroundState + } + } + launchPlayground.countDown() } diff --git a/paymentsheet-example/src/androidTest/java/com/stripe/android/test/core/ui/Selectors.kt b/paymentsheet-example/src/androidTest/java/com/stripe/android/test/core/ui/Selectors.kt index f28a9b035f4..0c5e46283ae 100644 --- a/paymentsheet-example/src/androidTest/java/com/stripe/android/test/core/ui/Selectors.kt +++ b/paymentsheet-example/src/androidTest/java/com/stripe/android/test/core/ui/Selectors.kt @@ -67,6 +67,8 @@ internal class Selectors( } ) + val playgroundBuyButton = ComposeButton(composeTestRule, hasTestTag(CHECKOUT_TEST_TAG)) + val addPaymentMethodButton = AddPaymentMethodButton(device) val selectBrowserPrompt = UiAutomatorText("Verify your payment", device = device) diff --git a/paymentsheet-example/src/main/java/com/stripe/android/paymentsheet/example/playground/settings/CustomerSettingsDefinition.kt b/paymentsheet-example/src/main/java/com/stripe/android/paymentsheet/example/playground/settings/CustomerSettingsDefinition.kt index 87ebf96caf1..33f3b3d5641 100644 --- a/paymentsheet-example/src/main/java/com/stripe/android/paymentsheet/example/playground/settings/CustomerSettingsDefinition.kt +++ b/paymentsheet-example/src/main/java/com/stripe/android/paymentsheet/example/playground/settings/CustomerSettingsDefinition.kt @@ -6,11 +6,7 @@ import com.stripe.android.paymentsheet.example.playground.model.CheckoutRequest internal object CustomerSettingsDefinition : PlaygroundSettingDefinition, - PlaygroundSettingDefinition.Saveable by EnumSaveable( - key = "customer", - values = CustomerType.values(), - defaultValue = CustomerType.GUEST, - ), + PlaygroundSettingDefinition.Saveable, PlaygroundSettingDefinition.Displayable { override val displayName: String = "Customer" override val options: List> = @@ -35,11 +31,36 @@ internal object CustomerSettingsDefinition : ) { configurationBuilder.customer(playgroundState.customerConfig) } + + override val key: String = "customer" + override val defaultValue: CustomerType = CustomerType.GUEST + + override fun convertToValue(value: String): CustomerType { + val hardcodedCustomerTypes = mapOf( + CustomerType.GUEST.value to CustomerType.GUEST, + CustomerType.NEW.value to CustomerType.NEW, + CustomerType.RETURNING.value to CustomerType.RETURNING, + ) + return if (value.startsWith("cus_")) { + CustomerType.Existing(value) + } else if (hardcodedCustomerTypes.containsKey(value)) { + hardcodedCustomerTypes[value]!! + } else { + defaultValue + } + } + + override fun convertToString(value: CustomerType): String { + return value.value + } } -enum class CustomerType(override val value: String) : ValueEnum { - GUEST("guest"), - NEW("new"), - RETURNING("returning"), - SNAPSHOT("snapshot"), +sealed class CustomerType(val value: String) { + object GUEST : CustomerType("guest") + + object NEW : CustomerType("new") + + object RETURNING : CustomerType("returning") + + class Existing(customerId: String) : CustomerType(customerId) }