Skip to content

Commit

Permalink
Add end-to-end tests for Embedded (#10114)
Browse files Browse the repository at this point in the history
  • Loading branch information
samer-stripe authored Feb 14, 2025
1 parent 2fc6c36 commit 222b4f6
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.stripe.android.lpm

import com.stripe.android.BasePlaygroundTest
import com.stripe.android.model.PaymentMethod
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.SupportedPaymentMethodsSettingsDefinition
import com.stripe.android.test.core.TestParameters
import org.junit.Test

internal class TestEmbedded : BasePlaygroundTest() {
@Test
fun testCard() {
testDriver.confirmEmbedded(
testParameters = TestParameters.create(
paymentMethodCode = "card",
authorizationAction = null,
executeInNightlyRun = true,
).copy(
saveForFutureUseCheckboxVisible = true,
),
populateCustomLpmFields = {
populateCardDetails()
},
)
}

@Test
fun testCashAppPay() {
testDriver.confirmEmbedded(
testParameters = TestParameters.create(
paymentMethodCode = "cashapp",
executeInNightlyRun = true,
) { settings ->
settings[CountrySettingsDefinition] = Country.US
settings[CurrencySettingsDefinition] = Currency.USD
settings[SupportedPaymentMethodsSettingsDefinition] = listOf(
PaymentMethod.Type.Card,
PaymentMethod.Type.CashAppPay
).joinToString(",")
},
values = null, // We don't show the form for cash app.
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.isEnabled
import androidx.compose.ui.test.junit4.ComposeTestRule
import androidx.compose.ui.test.onAllNodesWithTag
import androidx.compose.ui.test.onAllNodesWithText
Expand All @@ -34,6 +35,7 @@ import com.karumi.shot.ScreenshotTest
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.PaymentMethodCode
import com.stripe.android.paymentelement.embedded.form.EMBEDDED_FORM_ACTIVITY_PRIMARY_BUTTON
import com.stripe.android.paymentsheet.PaymentSheet
import com.stripe.android.paymentsheet.example.BuildConfig
import com.stripe.android.paymentsheet.example.playground.PaymentSheetPlaygroundActivity
Expand All @@ -52,6 +54,7 @@ import com.stripe.android.paymentsheet.example.playground.settings.RequireCvcRec
import com.stripe.android.paymentsheet.ui.PAYMENT_SHEET_ERROR_TEXT_TEST_TAG
import com.stripe.android.paymentsheet.ui.SAVED_PAYMENT_METHOD_CARD_TEST_TAG
import com.stripe.android.paymentsheet.verticalmode.TEST_TAG_NEW_PAYMENT_METHOD_ROW_BUTTON
import com.stripe.android.paymentsheet.verticalmode.TEST_TAG_PAYMENT_METHOD_EMBEDDED_LAYOUT
import com.stripe.android.paymentsheet.verticalmode.TEST_TAG_PAYMENT_METHOD_VERTICAL_LAYOUT
import com.stripe.android.test.core.ui.BrowserUI
import com.stripe.android.test.core.ui.ComposeButton
Expand Down Expand Up @@ -542,6 +545,59 @@ internal class PlaygroundTestDriver(
return result
}

fun confirmEmbedded(
testParameters: TestParameters,
values: FieldPopulator.Values? = FieldPopulator.Values(),
afterAuthorization: (Selectors) -> Unit = {},
populateCustomLpmFields: FieldPopulator.() -> Unit = {},
): PlaygroundState? {
setup(
testParameters.copyPlaygroundSettings { settings ->
settings.updateConfigurationData { configurationData ->
configurationData.copy(
integrationType = PlaygroundConfigurationData.IntegrationType.Embedded
)
}
}
)
launchEmbedded()

selectLpmInEmbeddedMode(testParameters.paymentMethodCode)

if (values != null) {
FieldPopulator(
selectors,
testParameters,
populateCustomLpmFields,
verifyCustomLpmFields = {},
values = values,
).populateFields()
}

// Verify device requirements are met prior to attempting confirmation. Do this
// after we have had the chance to capture a screenshot.
verifyDeviceSupportsTestAuthorization(
testParameters.authorizationAction,
testParameters.useBrowser
)

val result = playgroundState

if (values != null) {
selectors.embeddedFormBuyButton.click()
} else {
selectors.complete.click()
}

doAuthorization()

afterAuthorization(selectors)

teardown()

return result
}

fun confirmExistingComplete(
customerId: String?,
testParameters: TestParameters,
Expand Down Expand Up @@ -1063,6 +1119,33 @@ internal class PlaygroundTestDriver(
composeTestRule.waitForIdle()
}

private fun selectLpmInEmbeddedMode(paymentMethodCode: PaymentMethodCode) {
composeTestRule.waitUntil(DEFAULT_UI_TIMEOUT.inWholeMilliseconds) {
composeTestRule
.onAllNodes(hasTestTag(TEST_TAG_PAYMENT_METHOD_EMBEDDED_LAYOUT))
.fetchSemanticsNodes()
.isNotEmpty()
}

composeTestRule.onNode(hasTestTag("${TEST_TAG_NEW_PAYMENT_METHOD_ROW_BUTTON}_$paymentMethodCode"))
.performScrollTo()
.performClick()

Espresso.onIdle()
composeTestRule.waitForIdle()
}

private fun waitUntilPrimaryButtonIsCompleted() {
composeTestRule.waitUntil(DEFAULT_UI_TIMEOUT.inWholeMilliseconds) {
composeTestRule.onAllNodesWithTag(EMBEDDED_FORM_ACTIVITY_PRIMARY_BUTTON)
.fetchSemanticsNodes()
.isEmpty()
}

composeTestRule.waitForIdle()
Espresso.onIdle()
}

/**
* Here we wait for an activity different from the playground to be in view. We
* don't specifically look for PaymentSheetActivity or PaymentOptionsActivity because
Expand Down Expand Up @@ -1164,6 +1247,15 @@ internal class PlaygroundTestDriver(
}
}

private fun launchEmbedded() {
selectors.reload.click()
selectors.complete.waitForEnabled()
selectors.complete.click()

// EmbeddedPlaygroundActivity is now on screen.
waitForNotPlaygroundActivity()
}

private fun launchCustomerSheet() {
selectors.reload.click()
Espresso.onIdle()
Expand Down Expand Up @@ -1355,6 +1447,10 @@ internal class PlaygroundTestDriver(
if (isDone) {
playgroundState?.integrationType?.let { integrationType ->
if (integrationType.isPaymentFlow()) {
if (integrationType == PlaygroundConfigurationData.IntegrationType.Embedded) {
waitUntilPrimaryButtonIsCompleted()
}

waitForPlaygroundActivity()
assertThat(resultValue).isEqualTo(SUCCESS_RESULT)
} else if (integrationType.isCustomerFlow()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import com.stripe.android.customersheet.ui.CUSTOMER_SHEET_CONFIRM_BUTTON_TEST_TA
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.paymentelement.embedded.form.EMBEDDED_FORM_ACTIVITY_PRIMARY_BUTTON
import com.stripe.android.paymentsheet.example.playground.RELOAD_TEST_TAG
import com.stripe.android.paymentsheet.example.playground.activity.FawryActivity
import com.stripe.android.paymentsheet.example.samples.ui.shared.CHECKOUT_TEST_TAG
Expand Down Expand Up @@ -52,6 +53,7 @@ internal class Selectors(
val continueButton = BuyButton(composeTestRule)
val complete = ComposeButton(composeTestRule, hasTestTag(CHECKOUT_TEST_TAG))
val reload = ComposeButton(composeTestRule, hasTestTag(RELOAD_TEST_TAG))
val embeddedFormBuyButton = ComposeButton(composeTestRule, hasTestTag(EMBEDDED_FORM_ACTIVITY_PRIMARY_BUTTON))
val multiStepSelect = ComposeButton(
composeTestRule,
hasTestTag(PAYMENT_METHOD_SELECTOR_TEST_TAG)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.stripe.android.paymentelement.embedded.form

import androidx.annotation.RestrictTo
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
Expand All @@ -11,6 +12,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
Expand Down Expand Up @@ -122,6 +124,7 @@ internal fun FormActivityPrimaryButton(
)
) {
PrimaryButton(
modifier = Modifier.testTag(EMBEDDED_FORM_ACTIVITY_PRIMARY_BUTTON),
label = state.primaryButtonLabel.resolve(),
locked = true,
enabled = state.isEnabled,
Expand Down Expand Up @@ -162,3 +165,6 @@ internal fun FormActivityTopBar(
}
}
}

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
const val EMBEDDED_FORM_ACTIVITY_PRIMARY_BUTTON = "EMBEDDED_FORM_ACTIVITY_PRIMARY_BUTTON"
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ internal fun PrimaryButton(
label: String,
locked: Boolean,
enabled: Boolean,
modifier: Modifier = Modifier,
processingState: PrimaryButtonProcessingState = PrimaryButtonProcessingState.Idle(null),
onProcessingCompleted: () -> Unit = {},
onClick: () -> Unit,
Expand Down Expand Up @@ -115,7 +116,7 @@ internal fun PrimaryButton(
LocalContentAlpha provides if (enabled) ContentAlpha.high else ContentAlpha.disabled,
) {
Box(
modifier = Modifier.fillMaxWidth(),
modifier = modifier.fillMaxWidth(),
contentAlignment = Alignment.Center,
) {
TextButton(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.stripe.android.paymentsheet.verticalmode

import androidx.annotation.RestrictTo
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
Expand All @@ -24,7 +25,9 @@ import com.stripe.android.uicore.strings.resolve
import com.stripe.android.uicore.utils.collectAsState
import org.jetbrains.annotations.VisibleForTesting

internal const val TEST_TAG_PAYMENT_METHOD_EMBEDDED_LAYOUT = "TEST_TAG_PAYMENT_METHOD_EMBEDDED_LAYOUT"
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
const val TEST_TAG_PAYMENT_METHOD_EMBEDDED_LAYOUT = "TEST_TAG_PAYMENT_METHOD_EMBEDDED_LAYOUT"

internal const val EMBEDDED_MANDATE_TEXT_TEST_TAG = "EMBEDDED_MANDATE"

@OptIn(ExperimentalEmbeddedPaymentElementApi::class)
Expand Down

0 comments on commit 222b4f6

Please sign in to comment.