From 476b725afaaa42a007be43ef3a283113118ddd01 Mon Sep 17 00:00:00 2001 From: Samer Alabi Date: Thu, 23 May 2024 17:47:15 -0400 Subject: [PATCH] Add initial `CustomerSheet` end-to-end tests. --- .../java/com/stripe/android/lpm/TestCard.kt | 114 ++++++++++++++++++ .../android/test/core/PlaygroundTestDriver.kt | 88 ++++++++++++++ .../stripe/android/test/core/ui/Selectors.kt | 12 ++ .../customersheet/ui/CustomerSheetScreen.kt | 8 +- 4 files changed, 220 insertions(+), 2 deletions(-) diff --git a/paymentsheet-example/src/androidTest/java/com/stripe/android/lpm/TestCard.kt b/paymentsheet-example/src/androidTest/java/com/stripe/android/lpm/TestCard.kt index 4f38c545ed4..ecf1c72237d 100644 --- a/paymentsheet-example/src/androidTest/java/com/stripe/android/lpm/TestCard.kt +++ b/paymentsheet-example/src/androidTest/java/com/stripe/android/lpm/TestCard.kt @@ -11,8 +11,14 @@ import com.stripe.android.paymentsheet.example.playground.settings.CollectAddres import com.stripe.android.paymentsheet.example.playground.settings.CollectEmailSettingsDefinition import com.stripe.android.paymentsheet.example.playground.settings.CollectNameSettingsDefinition import com.stripe.android.paymentsheet.example.playground.settings.CollectPhoneSettingsDefinition +import com.stripe.android.paymentsheet.example.playground.settings.Country +import com.stripe.android.paymentsheet.example.playground.settings.CountrySettingsDefinition +import com.stripe.android.paymentsheet.example.playground.settings.CustomerSettingsDefinition +import com.stripe.android.paymentsheet.example.playground.settings.CustomerSheetPaymentMethodModeDefinition +import com.stripe.android.paymentsheet.example.playground.settings.CustomerType import com.stripe.android.paymentsheet.example.playground.settings.DefaultBillingAddress import com.stripe.android.paymentsheet.example.playground.settings.DefaultBillingAddressSettingsDefinition +import com.stripe.android.paymentsheet.example.playground.settings.PaymentMethodMode import com.stripe.android.paymentsheet.example.samples.ui.shared.PAYMENT_METHOD_SELECTOR_TEST_TAG import com.stripe.android.paymentsheet.ui.SAVED_PAYMENT_OPTION_TEST_TAG import com.stripe.android.test.core.AuthorizeAction @@ -312,4 +318,112 @@ internal class TestCard : BasePlaygroundTest() { values = FieldPopulator.Values(cardNumber = "4000000000003220") ) } + + @Test + fun testCardInCustomerSheet() { + testDriver.savePaymentMethodInCustomerSheet( + TestParameters.create(paymentMethodCode = "card").copyPlaygroundSettings { settings -> + settings[CustomerSettingsDefinition] = CustomerType.NEW + settings[CountrySettingsDefinition] = Country.US + settings[CustomerSheetPaymentMethodModeDefinition] = PaymentMethodMode.CreateAndAttach + }, + populateCustomLpmFields = { + populateCardDetails() + }, + ) + } + + @Test + fun testCardWithSetupIntentInCustomerSheet() { + testDriver.savePaymentMethodInCustomerSheet( + TestParameters.create(paymentMethodCode = "card").copyPlaygroundSettings { settings -> + settings[CustomerSettingsDefinition] = CustomerType.NEW + settings[CountrySettingsDefinition] = Country.US + settings[CustomerSheetPaymentMethodModeDefinition] = PaymentMethodMode.SetupIntent + }, + populateCustomLpmFields = { + populateCardDetails() + }, + ) + } + + @Test + fun testCardWithNonUsMerchantInCustomerSheet() { + testDriver.savePaymentMethodInCustomerSheet( + TestParameters.create(paymentMethodCode = "card").copyPlaygroundSettings { settings -> + settings[CustomerSettingsDefinition] = CustomerType.NEW + settings[CountrySettingsDefinition] = Country.FR + settings[CustomerSheetPaymentMethodModeDefinition] = PaymentMethodMode.CreateAndAttach + }, + populateCustomLpmFields = { + populateCardDetails() + }, + ) + } + + @Test + fun testCardWithSetupIntentAndNonUsMerchantInCustomerSheet() { + testDriver.savePaymentMethodInCustomerSheet( + TestParameters.create(paymentMethodCode = "card").copyPlaygroundSettings { settings -> + settings[CustomerSettingsDefinition] = CustomerType.NEW + settings[CountrySettingsDefinition] = Country.FR + settings[CustomerSheetPaymentMethodModeDefinition] = PaymentMethodMode.SetupIntent + }, + populateCustomLpmFields = { + populateCardDetails() + }, + ) + } + + @Test + fun testCardWithBillingDetailsCollectionInCustomerSheet() { + testDriver.savePaymentMethodInCustomerSheet( + TestParameters.create( + paymentMethodCode = "card", + ) { settings -> + settings[CustomerSettingsDefinition] = CustomerType.NEW + settings[CountrySettingsDefinition] = Country.US + settings[DefaultBillingAddressSettingsDefinition] = DefaultBillingAddress.Off + settings[CollectNameSettingsDefinition] = + PaymentSheet.BillingDetailsCollectionConfiguration.CollectionMode.Always + settings[CollectEmailSettingsDefinition] = + PaymentSheet.BillingDetailsCollectionConfiguration.CollectionMode.Always + settings[CollectPhoneSettingsDefinition] = + PaymentSheet.BillingDetailsCollectionConfiguration.CollectionMode.Always + settings[CollectAddressSettingsDefinition] = + PaymentSheet.BillingDetailsCollectionConfiguration.AddressCollectionMode.Full + }, + populateCustomLpmFields = { + populateCardDetails() + populateEmail() + populateName("Name on card") + populateAddress() + populatePhoneNumber() + }, + ) + } + + @Test + fun testCardWithBillingDetailsCollectionWithDefaultsInCustomerSheet() { + testDriver.savePaymentMethodInCustomerSheet( + TestParameters.create( + paymentMethodCode = "card", + ) { settings -> + settings[CustomerSettingsDefinition] = CustomerType.NEW + settings[CountrySettingsDefinition] = Country.US + settings[DefaultBillingAddressSettingsDefinition] = DefaultBillingAddress.On + settings[CollectNameSettingsDefinition] = + PaymentSheet.BillingDetailsCollectionConfiguration.CollectionMode.Always + settings[CollectEmailSettingsDefinition] = + PaymentSheet.BillingDetailsCollectionConfiguration.CollectionMode.Always + settings[CollectPhoneSettingsDefinition] = + PaymentSheet.BillingDetailsCollectionConfiguration.CollectionMode.Always + settings[CollectAddressSettingsDefinition] = + PaymentSheet.BillingDetailsCollectionConfiguration.AddressCollectionMode.Full + }, + populateCustomLpmFields = { + populateCardDetails() + }, + ) + } } 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 75834d42ad6..972b10d82f7 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 @@ -10,6 +10,7 @@ import androidx.compose.ui.test.SemanticsNodeInteraction import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.assertTextEquals import androidx.compose.ui.test.hasTestTag +import androidx.compose.ui.test.hasText import androidx.compose.ui.test.junit4.ComposeTestRule import androidx.compose.ui.test.onAllNodesWithTag import androidx.compose.ui.test.onAllNodesWithText @@ -313,6 +314,54 @@ internal class PlaygroundTestDriver( teardown() } + fun savePaymentMethodInCustomerSheet( + testParameters: TestParameters, + values: FieldPopulator.Values = FieldPopulator.Values(), + populateCustomLpmFields: FieldPopulator.() -> Unit = {}, + ): PlaygroundState? { + setup( + testParameters.copyPlaygroundSettings { settings -> + settings.updateConfigurationData { configurationData -> + configurationData.copy( + integrationType = PlaygroundConfigurationData.IntegrationType.CustomerSheet + ) + } + } + ) + + launchCustomerSheet() + + if (isManagePaymentMethodScreen()) { + addPaymentMethodNode().performClick() + } + + selectors.paymentSelection.click() + + val fieldPopulator = FieldPopulator( + selectors, + testParameters, + populateCustomLpmFields, + {}, + values, + ) + fieldPopulator.populateFields() + + val result = playgroundState + + pressCustomerSheetSave() + + waitForManageSavedPaymentMethods() + + pressCustomerSheetConfirm() + + Espresso.onIdle() + composeTestRule.waitForIdle() + + teardown() + + return result + } + private fun pressMultiStepSelect() { selectors.multiStepSelect.click() waitForNotPlaygroundActivity() @@ -325,6 +374,20 @@ internal class PlaygroundTestDriver( } } + private fun pressCustomerSheetSave() { + Espresso.onIdle() + composeTestRule.waitForIdle() + + selectors.customerSheetSaveButton.click() + } + + private fun pressCustomerSheetConfirm() { + Espresso.onIdle() + composeTestRule.waitForIdle() + + selectors.customerSheetConfirmButton.click() + } + /** * This will open the payment sheet complete flow from the playground with a new or * guest user and complete the confirmation including any browser interactions. @@ -840,6 +903,17 @@ internal class PlaygroundTestDriver( } } + private fun launchCustomerSheet() { + selectors.reload.click() + Espresso.onIdle() + selectors.composeTestRule.waitForIdle() + + selectors.multiStepSelect.waitForEnabled() + selectors.multiStepSelect.click() + + waitForNotPlaygroundActivity() + } + private fun doAuthorization() { selectors.apply { val checkoutMode = @@ -1048,6 +1122,12 @@ internal class PlaygroundTestDriver( }.isSuccess } + private fun isManagePaymentMethodScreen(): Boolean { + return runCatching { + composeTestRule.onNodeWithText("Manage your payment methods").assertIsDisplayed() + }.isSuccess + } + private fun addPaymentMethodNode(): SemanticsNodeInteraction { waitForAddPaymentMethodNode() return composeTestRule.onNodeWithTag(ADD_PAYMENT_METHOD_NODE_TAG) @@ -1058,6 +1138,14 @@ internal class PlaygroundTestDriver( composeTestRule.waitUntilAtLeastOneExists(hasTestTag(ADD_PAYMENT_METHOD_NODE_TAG), 5000L) } + @OptIn(ExperimentalTestApi::class) + private fun waitForManageSavedPaymentMethods() { + composeTestRule.waitUntilAtLeastOneExists( + hasText("Manage your payment methods"), + DEFAULT_UI_TIMEOUT.inWholeMilliseconds + ) + } + private companion object { const val ADD_PAYMENT_METHOD_NODE_TAG = "${SAVED_PAYMENT_METHOD_CARD_TEST_TAG}_+ Add" const val FINANCIAL_CONNECTIONS_ACTIVITY = 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 2b82a6e6a1f..f68f00dffa1 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 @@ -16,6 +16,8 @@ import androidx.test.uiautomator.UiObject import androidx.test.uiautomator.UiSelector import androidx.test.uiautomator.Until import com.google.common.truth.Truth.assertThat +import com.stripe.android.customersheet.ui.CUSTOMER_SHEET_CONFIRM_BUTTON_TEST_TAG +import com.stripe.android.customersheet.ui.CUSTOMER_SHEET_SAVE_BUTTON_TEST_TAG import com.stripe.android.model.PaymentMethod.Type.Blik import com.stripe.android.model.PaymentMethod.Type.CashAppPay import com.stripe.android.paymentsheet.example.playground.RELOAD_TEST_TAG @@ -74,6 +76,16 @@ internal class Selectors( } ) + val customerSheetSaveButton = ComposeButton( + composeTestRule, + hasTestTag(CUSTOMER_SHEET_SAVE_BUTTON_TEST_TAG) + ) + + val customerSheetConfirmButton = ComposeButton( + composeTestRule, + hasTestTag(CUSTOMER_SHEET_CONFIRM_BUTTON_TEST_TAG) + ) + val externalPaymentMethodSucceedButton = ComposeButton( composeTestRule, hasTestTag(FawryActivity.COMPLETED_BUTTON_TEST_TAG) diff --git a/paymentsheet/src/main/java/com/stripe/android/customersheet/ui/CustomerSheetScreen.kt b/paymentsheet/src/main/java/com/stripe/android/customersheet/ui/CustomerSheetScreen.kt index 71ce6bcd692..0898fac8077 100644 --- a/paymentsheet/src/main/java/com/stripe/android/customersheet/ui/CustomerSheetScreen.kt +++ b/paymentsheet/src/main/java/com/stripe/android/customersheet/ui/CustomerSheetScreen.kt @@ -1,5 +1,6 @@ package com.stripe.android.customersheet.ui +import androidx.annotation.RestrictTo import androidx.compose.animation.animateContentSize import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth @@ -293,8 +294,11 @@ private fun EditPaymentMethod( } } -internal const val CUSTOMER_SHEET_CONFIRM_BUTTON_TEST_TAG = "CustomerSheetConfirmButton" -internal const val CUSTOMER_SHEET_SAVE_BUTTON_TEST_TAG = "CustomerSheetSaveButton" +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) +const val CUSTOMER_SHEET_CONFIRM_BUTTON_TEST_TAG = "CustomerSheetConfirmButton" + +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) +const val CUSTOMER_SHEET_SAVE_BUTTON_TEST_TAG = "CustomerSheetSaveButton" private class DefaultCardNumberCompletedEventReporter( private val viewActionHandler: (CustomerSheetViewAction) -> Unit