Skip to content

Commit

Permalink
Refactor bank form result emission and related host surfaces (#9555)
Browse files Browse the repository at this point in the history
* Fix mandate issues with bank form

* Update tests

* Remove need for state in `handlePrimaryButtonClick`

* Update CustomerSheet

* Update tests

* Consider billing details and add tests

* Rename `result` to `linkedAccount`

* Add tests

Make sure we're confirming with the correct payment method type.

* Fix issue with success test
  • Loading branch information
tillh-stripe authored Nov 7, 2024
1 parent b229ed0 commit 758bc81
Show file tree
Hide file tree
Showing 22 changed files with 430 additions and 605 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ internal class TestInstantDebits : BasePlaygroundTest() {

testDriver.confirmLinkBankPayment(
testParameters = params,
afterAuthorization = {
afterAuthorization = { _, _ ->
rules.compose.waitUntil(DEFAULT_UI_TIMEOUT.inWholeMilliseconds) {
rules.compose
.onAllNodesWithTag(TEST_TAG_ACCOUNT_DETAILS)
Expand All @@ -66,7 +66,7 @@ internal class TestInstantDebits : BasePlaygroundTest() {
testParameters = testParameters.copy(
authorizationAction = AuthorizeAction.Cancel,
),
afterAuthorization = {
afterAuthorization = { _, _ ->
ComposeButton(rules.compose, hasTestTag(PAYMENT_SHEET_PRIMARY_BUTTON_TEST_TAG))
.waitFor(isEnabled())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ internal class TestLinkCardBrand : BasePlaygroundTest() {

testDriver.confirmLinkBankPayment(
testParameters = params,
afterAuthorization = {
afterAuthorization = { _, _ ->
rules.compose.waitUntil(DEFAULT_UI_TIMEOUT.inWholeMilliseconds) {
rules.compose
.onAllNodesWithTag(TEST_TAG_ACCOUNT_DETAILS)
Expand All @@ -62,7 +62,7 @@ internal class TestLinkCardBrand : BasePlaygroundTest() {
testParameters = testParameters.copy(
authorizationAction = AuthorizeAction.Cancel,
),
afterAuthorization = {
afterAuthorization = { _, _ ->
ComposeButton(rules.compose, hasTestTag(PAYMENT_SHEET_PRIMARY_BUTTON_TEST_TAG))
.waitFor(isEnabled())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,23 @@ package com.stripe.android.lpm

import androidx.compose.ui.test.hasTestTag
import androidx.compose.ui.test.isEnabled
import androidx.compose.ui.test.onAllNodesWithTag
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.stripe.android.BasePlaygroundTest
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.Currency
import com.stripe.android.paymentsheet.example.playground.settings.CurrencySettingsDefinition
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.DelayedPaymentMethodsSettingsDefinition
import com.stripe.android.paymentsheet.paymentdatacollection.ach.TEST_TAG_ACCOUNT_DETAILS
import com.stripe.android.paymentsheet.ui.PAYMENT_SHEET_PRIMARY_BUTTON_TEST_TAG
import com.stripe.android.test.core.AuthorizeAction
import com.stripe.android.test.core.DEFAULT_UI_TIMEOUT
import com.stripe.android.test.core.TestParameters
import com.stripe.android.test.core.ui.ComposeButton
import com.stripe.android.test.core.ui.PaymentSelection
import org.junit.Test
import org.junit.runner.RunWith

Expand All @@ -27,13 +33,75 @@ internal class TestUSBankAccount : BasePlaygroundTest() {
settings[DelayedPaymentMethodsSettingsDefinition] = true
}

@Test
fun testUSBankAccountSuccess() {
testDriver.confirmUSBankAccount(
testParameters = testParameters.copyPlaygroundSettings {
it[DefaultBillingAddressSettingsDefinition] = DefaultBillingAddress.OnWithRandomEmail
},
afterAuthorization = { _, _ ->
ComposeButton(rules.compose, hasTestTag(PAYMENT_SHEET_PRIMARY_BUTTON_TEST_TAG))
.waitFor(isEnabled())
}
)
}

@Test
fun testUSBankAccountSuccessWithIndecisiveUser() {
// Select another LPM before coming back to the linked bank account
testDriver.confirmUSBankAccount(
testParameters = testParameters.copyPlaygroundSettings {
it[DefaultBillingAddressSettingsDefinition] = DefaultBillingAddress.OnWithRandomEmail
},
afterAuthorization = { _, _ ->
rules.compose.waitUntil(DEFAULT_UI_TIMEOUT.inWholeMilliseconds) {
rules.compose
.onAllNodesWithTag(TEST_TAG_ACCOUNT_DETAILS)
.fetchSemanticsNodes(atLeastOneRootRequired = false)
.isNotEmpty()
}

// Briefly switch to another payment method
val cardSelection = PaymentSelection(rules.compose, "card")
cardSelection.click()

// Come back to the bank tab
val bankSelection = PaymentSelection(rules.compose, "us_bank_account")
bankSelection.click()
},
)
}

@Test
fun testCardAfterConfirmingUSBankAccount() {
// Link a bank account, but pay with a card instead
testDriver.confirmUSBankAccount(
testParameters = testParameters.copyPlaygroundSettings {
it[DefaultBillingAddressSettingsDefinition] = DefaultBillingAddress.OnWithRandomEmail
},
afterAuthorization = { _, populator ->
rules.compose.waitUntil(DEFAULT_UI_TIMEOUT.inWholeMilliseconds) {
rules.compose
.onAllNodesWithTag(TEST_TAG_ACCOUNT_DETAILS)
.fetchSemanticsNodes(atLeastOneRootRequired = false)
.isNotEmpty()
}

// We actually want to confirm with a card
val cardSelection = PaymentSelection(rules.compose, "card")
cardSelection.click()
populator.populateCardDetails()
},
)
}

@Test
fun testUSBankAccountCancelAllowsUserToContinue() {
testDriver.confirmUSBankAccount(
testParameters = testParameters.copy(
authorizationAction = AuthorizeAction.Cancel,
),
afterAuthorization = {
afterAuthorization = { _, _ ->
ComposeButton(rules.compose, hasTestTag(PAYMENT_SHEET_PRIMARY_BUTTON_TEST_TAG))
.waitFor(isEnabled())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -762,12 +762,13 @@ internal class PlaygroundTestDriver(

fun confirmUSBankAccount(
testParameters: TestParameters,
afterAuthorization: (Selectors) -> Unit = {},
afterAuthorization: (Selectors, FieldPopulator) -> Unit = { _, _ -> },
): PlaygroundState? {
return confirmBankAccount(
testParameters = testParameters,
executeFlow = { doUSBankAccountAuthorization() },
executeFlow = { doUSBankAccountAuthorization(testParameters.authorizationAction) },
afterCollectingBankInfo = afterAuthorization,
confirmIntent = true,
)
}

Expand All @@ -777,14 +778,14 @@ internal class PlaygroundTestDriver(
) {
confirmBankAccountInCustomFlow(
testParameters = testParameters,
executeFlow = { doUSBankAccountAuthorization() },
executeFlow = { doUSBankAccountAuthorization(testParameters.authorizationAction) },
afterCollectingBankInfo = afterAuthorization,
)
}

fun confirmLinkBankPayment(
testParameters: TestParameters,
afterAuthorization: (Selectors) -> Unit = {},
afterAuthorization: (Selectors, FieldPopulator) -> Unit = { _, _ -> },
): PlaygroundState? {
return confirmBankAccount(
testParameters = testParameters,
Expand All @@ -808,21 +809,23 @@ internal class PlaygroundTestDriver(
private fun confirmBankAccount(
testParameters: TestParameters,
executeFlow: () -> Unit,
afterCollectingBankInfo: (Selectors) -> Unit = {},
confirmIntent: Boolean = false,
afterCollectingBankInfo: (Selectors, FieldPopulator) -> Unit = { _, _ -> },
confirmIntent: Boolean,
): PlaygroundState? {
setup(testParameters)
launchComplete()

clickPaymentSelection()

FieldPopulator(
val populator = FieldPopulator(
selectors = selectors,
testParameters = testParameters,
populateCustomLpmFields = {},
verifyCustomLpmFields = {},
values = FieldPopulator.Values(),
).populateFields()
)

populator.populateFields()

// Verify device requirements are met prior to attempting confirmation. Do this
// after we have had the chance to capture a screenshot.
Expand All @@ -837,7 +840,7 @@ internal class PlaygroundTestDriver(

executeFlow()

afterCollectingBankInfo(selectors)
afterCollectingBankInfo(selectors, populator)

if (confirmIntent) {
pressBuy()
Expand Down Expand Up @@ -1274,7 +1277,7 @@ internal class PlaygroundTestDriver(
clickButton("Connect account", composeCanDetach = true)

clickButton("Not now")
clickButton("Back to Mobile Example Account")
clickButtonWithTag("done_button")
}

private fun executeEntireInstantDebitsFlow() = with(device) {
Expand All @@ -1290,7 +1293,15 @@ internal class PlaygroundTestDriver(
clickButtonWithTag("done_button")
}

private fun doUSBankAccountAuthorization() {
private fun doUSBankAccountAuthorization(authAction: AuthorizeAction?) {
if (authAction == AuthorizeAction.Cancel) {
cancelAchFlowOnLaunch()
} else {
executeUsBankAccountFlow()
}
}

private fun cancelAchFlowOnLaunch() {
while (currentActivity?.javaClass?.name != FINANCIAL_CONNECTIONS_ACTIVITY) {
TimeUnit.MILLISECONDS.sleep(250)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ internal class ComposeButton(
fun waitFor(semanticsMatcher: SemanticsMatcher): ComposeButton {
composeTestRule.waitUntil(timeoutMillis = DEFAULT_UI_TIMEOUT.inWholeMilliseconds) {
val combinedMatcher = matcher.and(semanticsMatcher)
composeTestRule.onAllNodes(combinedMatcher).fetchSemanticsNodes().isNotEmpty()
composeTestRule
.onAllNodes(combinedMatcher)
.fetchSemanticsNodes(atLeastOneRootRequired = false)
.isNotEmpty()
}
return this
}
Expand Down
3 changes: 1 addition & 2 deletions paymentsheet/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
<ID>LongMethod:USBankAccountForm.kt$@Composable internal fun BillingDetailsForm( instantDebits: Boolean, formArgs: FormArguments, isProcessing: Boolean, isPaymentFlow: Boolean, nameController: TextFieldController, emailController: TextFieldController, phoneController: PhoneNumberController, addressController: AddressController, lastTextFieldIdentifier: IdentifierSpec?, sameAsShippingElement: SameAsShippingElement?, )</ID>
<ID>LongMethod:USBankAccountForm.kt$@Composable internal fun USBankAccountForm( formArgs: FormArguments, usBankAccountFormArgs: USBankAccountFormArguments, modifier: Modifier = Modifier, )</ID>
<ID>LongMethod:USBankAccountForm.kt$@Composable private fun AccountDetailsForm( showCheckbox: Boolean, isProcessing: Boolean, bankName: String?, last4: String?, saveForFutureUseElement: SaveForFutureUseElement, onRemoveAccount: () -> Unit, )</ID>
<ID>LongMethod:USBankAccountFormViewModel.kt$USBankAccountFormViewModel$private fun createNewPaymentSelection( resultIdentifier: ResultIdentifier, last4: String, bankName: String, ): PaymentSelection.New.USBankAccount</ID>
<ID>LongMethod:USBankAccountFormViewModel.kt$USBankAccountFormViewModel$private fun USBankAccountFormScreenState.createNewPaymentSelection( resultIdentifier: ResultIdentifier, last4: String?, bankName: String?, billingDetails: PaymentMethod.BillingDetails, ): PaymentSelection.New.USBankAccount</ID>
<ID>LongMethod:USBankAccountFormViewModelTest.kt$USBankAccountFormViewModelTest$@Test fun `Restores screen state when re-opening screen`()</ID>
<ID>MagicNumber:BaseSheetActivity.kt$BaseSheetActivity$30</ID>
<ID>MagicNumber:NewPaymentMethodTabLayoutUI.kt$.3f</ID>
Expand Down Expand Up @@ -68,7 +68,6 @@
<ID>MaxLineLength:PrimaryButtonTest.kt$PrimaryButtonTest$primaryButton.setAppearanceConfiguration(StripeThemeDefaults.primaryButtonStyle, ColorStateList.valueOf(Color.BLACK))</ID>
<ID>MaxLineLength:SupportedPaymentMethod.kt$SupportedPaymentMethod$/** This describes the image in the LPM selector. These can be found internally [here](https://www.figma.com/file/2b9r3CJbyeVAmKi1VHV2h9/Mobile-Payment-Element?node-id=1128%3A0) */</ID>
<ID>MaxLineLength:USBankAccountFormViewModelTest.kt$USBankAccountFormViewModelTest$fun</ID>
<ID>MaxLineLength:USBankAccountFormViewModelTest.kt$USBankAccountFormViewModelTest$viewModel.handlePrimaryButtonClick(currentScreenState as USBankAccountFormScreenState.VerifyWithMicrodeposits)</ID>
<ID>MaximumLineLength:CardDefinition.kt$internal</ID>
<ID>ThrowsCount:PaymentSheetConfigurationKtx.kt$internal fun PaymentSheet.Configuration.validate()</ID>
<ID>TooManyFunctions:CustomerSheetEventReporter.kt$CustomerSheetEventReporter</ID>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import com.stripe.android.core.strings.ResolvableString
import com.stripe.android.lpmfoundations.luxe.SupportedPaymentMethod
import com.stripe.android.model.CardBrand
import com.stripe.android.model.PaymentMethod
import com.stripe.android.payments.bankaccount.navigation.CollectBankAccountResultInternal
import com.stripe.android.paymentsheet.forms.FormFieldValues
import com.stripe.android.paymentsheet.model.PaymentSelection
import com.stripe.android.paymentsheet.ui.PrimaryButton
Expand Down Expand Up @@ -34,11 +33,8 @@ internal sealed class CustomerSheetViewAction {
val mandateText: ResolvableString?,
val showAbovePrimaryButton: Boolean,
) : CustomerSheetViewAction()
class OnConfirmUSBankAccount(
val usBankAccount: PaymentSelection.New.USBankAccount,
) : CustomerSheetViewAction()
class OnCollectBankAccountResult(
val bankAccountResult: CollectBankAccountResultInternal,
class OnBankAccountSelectionChanged(
val paymentSelection: PaymentSelection.New.USBankAccount?,
) : CustomerSheetViewAction()
class OnFormError(
val error: ResolvableString?,
Expand Down
Loading

0 comments on commit 758bc81

Please sign in to comment.