Skip to content

Commit

Permalink
Refactor DefaultEmbeddedSheetLauncherTest and SharedPaymentElementVie…
Browse files Browse the repository at this point in the history
…wModelTest to use DummyActivityResultCaller (#9967)

* Refactor DefaultEmbeddedSheetLauncherTest and SharedPaymentElementViewModelTest to use DummyActivityResultCaller
  • Loading branch information
tjclawson-stripe authored Jan 24, 2025
1 parent 2c1ab02 commit c68950c
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 141 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,27 +1,24 @@
package com.stripe.android.paymentelement.embedded

import androidx.activity.result.ActivityResultCallback
import androidx.activity.result.ActivityResultCaller
import androidx.activity.result.ActivityResultLauncher
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.testing.TestLifecycleOwner
import com.google.common.truth.Truth.assertThat
import com.stripe.android.isInstanceOf
import com.stripe.android.lpmfoundations.paymentmethod.PaymentMethodMetadataFactory
import com.stripe.android.model.PaymentMethodFixtures
import com.stripe.android.paymentelement.confirmation.asCallbackFor
import com.stripe.android.paymentelement.embedded.manage.ManageContract
import com.stripe.android.paymentelement.embedded.manage.ManageResult
import com.stripe.android.paymentsheet.CustomerStateHolder
import com.stripe.android.paymentsheet.PaymentSheetFixtures
import com.stripe.android.paymentsheet.model.PaymentSelection
import com.stripe.android.utils.DummyActivityResultCaller
import com.stripe.android.utils.DummyActivityResultCaller.RegisterCall
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mockito.mock
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.any
import org.mockito.kotlin.capture
import org.mockito.kotlin.whenever
import org.robolectric.RobolectricTestRunner

@RunWith(RobolectricTestRunner::class)
Expand All @@ -32,32 +29,41 @@ internal class DefaultEmbeddedSheetLauncherTest {
val code = "test_code"
val paymentMethodMetadata = PaymentMethodMetadataFactory.create()
val expectedArgs = FormContract.Args(code, paymentMethodMetadata)
launcher.launchForm(code, paymentMethodMetadata)
assertThat(formActivityLauncher.argsTurbine.awaitItem()).isEqualTo(expectedArgs)

sheetLauncher.launchForm(code, paymentMethodMetadata)
val launchCall = dummyActivityResultCallerScenario.awaitLaunchCall()
assertThat(launchCall).isEqualTo(expectedArgs)
}

@Test
fun `formActivityLauncher callback updates selection holder on complete result`() = testScenario {
val selection = PaymentMethodFixtures.CARD_PAYMENT_SELECTION
val result = FormResult.Complete(PaymentMethodFixtures.CARD_PAYMENT_SELECTION)
formContractCallbackCaptor.value.onActivityResult(result)
val callback = formRegisterCall.callback.asCallbackFor<FormResult>()

callback.onActivityResult(result)
assertThat(selectionHolder.selection.value).isEqualTo(selection)
}

@Test
fun `formActivityLauncher callback does not update selection holder on non-complete result`() = testScenario {
val result = FormResult.Cancelled
formContractCallbackCaptor.value.onActivityResult(result)
assertThat(selectionHolder.selection.value).isNull()
val callback = formRegisterCall.callback.asCallbackFor<FormResult>()

callback.onActivityResult(result)
assertThat(selectionHolder.selection.value).isEqualTo(null)
}

@Test
fun `launchManage launches activity with correct parameters`() = testScenario {
val paymentMethodMetadata = PaymentMethodMetadataFactory.create()
val customerState = PaymentSheetFixtures.EMPTY_CUSTOMER_STATE
val expectedArgs = ManageContract.Args(paymentMethodMetadata, customerState, PaymentSelection.GooglePay)
launcher.launchManage(paymentMethodMetadata, customerState, PaymentSelection.GooglePay)
assertThat(manageActivityLauncher.argsTurbine.awaitItem()).isEqualTo(expectedArgs)

sheetLauncher.launchManage(paymentMethodMetadata, customerState, PaymentSelection.GooglePay)
val launchCall = dummyActivityResultCallerScenario.awaitLaunchCall()

assertThat(launchCall).isEqualTo(expectedArgs)
}

@Test
Expand All @@ -68,7 +74,10 @@ internal class DefaultEmbeddedSheetLauncherTest {
customerState,
selection,
)
manageContractCallbackCaptor.value.onActivityResult(result)

val callback = manageRegisterCall.callback.asCallbackFor<ManageResult>()
callback.onActivityResult(result)

assertThat(customerStateHolder.customer.value).isEqualTo(customerState)
assertThat(selectionHolder.selection.value).isEqualTo(selection)
}
Expand All @@ -77,88 +86,83 @@ internal class DefaultEmbeddedSheetLauncherTest {
fun `manageActivityLauncher callback does not update state on non-complete result`() = testScenario {
customerStateHolder.setCustomerState(PaymentSheetFixtures.EMPTY_CUSTOMER_STATE)
val result = ManageResult.Cancelled(customerState = null)
manageContractCallbackCaptor.value.onActivityResult(result)
val callback = manageRegisterCall.callback.asCallbackFor<ManageResult>()

callback.onActivityResult(result)

assertThat(customerStateHolder.customer.value).isEqualTo(PaymentSheetFixtures.EMPTY_CUSTOMER_STATE)
assertThat(selectionHolder.selection.value).isEqualTo(null)
}

@Test
fun `manageActivityLauncher callback updates state on non-complete result`() = testScenario {
val result = ManageResult.Cancelled(customerState = PaymentSheetFixtures.EMPTY_CUSTOMER_STATE)
manageContractCallbackCaptor.value.onActivityResult(result)
val callback = manageRegisterCall.callback.asCallbackFor<ManageResult>()
callback.onActivityResult(result)

assertThat(customerStateHolder.customer.value).isEqualTo(PaymentSheetFixtures.EMPTY_CUSTOMER_STATE)
}

@Test
fun `onDestroy unregisters launchers`() = testScenario {
lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
assertThat(formActivityLauncher.unregisterTurbine.awaitItem()).isEqualTo(Unit)
assertThat(manageActivityLauncher.unregisterTurbine.awaitItem()).isEqualTo(Unit)
val formUnregisteredLauncher = dummyActivityResultCallerScenario.awaitNextUnregisteredLauncher()
val manageUnregisteredLauncher = dummyActivityResultCallerScenario.awaitNextUnregisteredLauncher()

assertThat(formUnregisteredLauncher).isEqualTo(formLauncher)
assertThat(manageUnregisteredLauncher).isEqualTo(manageLauncher)
}

private fun testScenario(
block: suspend Scenario.() -> Unit,
block: suspend Scenario.() -> Unit
) = runTest {
MockitoAnnotations.openMocks(this)
val activityResultCaller = mock<ActivityResultCaller>()
val lifecycleOwner = TestLifecycleOwner()
val formActivityLauncher = FakeEmbeddedActivityLauncher(FormContract)
val manageActivityLauncher = FakeEmbeddedActivityLauncher(ManageContract)
val savedStateHandle = SavedStateHandle()
val selectionHolder = EmbeddedSelectionHolder(savedStateHandle)
val customerStateHolder = CustomerStateHolder(savedStateHandle, selectionHolder.selection)

@Suppress("UNCHECKED_CAST")
val formContractCallbackCaptor: ArgumentCaptor<ActivityResultCallback<FormResult>> = ArgumentCaptor
.forClass(ActivityResultCallback::class.java) as ArgumentCaptor<ActivityResultCallback<FormResult>>

whenever(
activityResultCaller.registerForActivityResult(
any<FormContract>(),
capture(formContractCallbackCaptor)
DummyActivityResultCaller.test {
val sheetLauncher = DefaultEmbeddedSheetLauncher(
activityResultCaller = activityResultCaller,
lifecycleOwner = lifecycleOwner,
selectionHolder = selectionHolder,
customerStateHolder = customerStateHolder
)
).thenReturn(formActivityLauncher)

@Suppress("UNCHECKED_CAST")
val manageContractCallbackCaptor: ArgumentCaptor<ActivityResultCallback<ManageResult>> = ArgumentCaptor
.forClass(ActivityResultCallback::class.java) as ArgumentCaptor<ActivityResultCallback<ManageResult>>

whenever(
activityResultCaller.registerForActivityResult(
any<ManageContract>(),
capture(manageContractCallbackCaptor)
)
).thenReturn(manageActivityLauncher)

val embeddedActivityLauncher = DefaultEmbeddedSheetLauncher(
activityResultCaller = activityResultCaller,
lifecycleOwner = lifecycleOwner,
selectionHolder = selectionHolder,
customerStateHolder = customerStateHolder,
)

Scenario(
formContractCallbackCaptor = formContractCallbackCaptor,
manageContractCallbackCaptor = manageContractCallbackCaptor,
selectionHolder = selectionHolder,
lifecycleOwner = lifecycleOwner,
formActivityLauncher = formActivityLauncher,
manageActivityLauncher = manageActivityLauncher,
launcher = embeddedActivityLauncher,
customerStateHolder = customerStateHolder,
).block()

formActivityLauncher.validate()
manageActivityLauncher.validate()
val formRegisterCall = awaitRegisterCall()
val manageRegisterCall = awaitRegisterCall()

val formLauncher = awaitNextRegisteredLauncher()
val manageLauncher = awaitNextRegisteredLauncher()

assertThat(formRegisterCall).isNotNull()
assertThat(manageRegisterCall).isNotNull()

assertThat(formRegisterCall.contract).isInstanceOf<FormContract>()
assertThat(manageRegisterCall.contract).isInstanceOf<ManageContract>()

Scenario(
selectionHolder = selectionHolder,
lifecycleOwner = lifecycleOwner,
customerStateHolder = customerStateHolder,
dummyActivityResultCallerScenario = this,
formRegisterCall = formRegisterCall,
manageRegisterCall = manageRegisterCall,
formLauncher = formLauncher,
manageLauncher = manageLauncher,
sheetLauncher = sheetLauncher
).block()
}
}

private class Scenario(
val formContractCallbackCaptor: ArgumentCaptor<ActivityResultCallback<FormResult>>,
val manageContractCallbackCaptor: ArgumentCaptor<ActivityResultCallback<ManageResult>>,
val selectionHolder: EmbeddedSelectionHolder,
val lifecycleOwner: TestLifecycleOwner,
val formActivityLauncher: FakeEmbeddedActivityLauncher<FormContract.Args>,
val manageActivityLauncher: FakeEmbeddedActivityLauncher<ManageContract.Args>,
val launcher: EmbeddedSheetLauncher,
val customerStateHolder: CustomerStateHolder,
val dummyActivityResultCallerScenario: DummyActivityResultCaller.Scenario,
val formRegisterCall: RegisterCall<*, *>,
val manageRegisterCall: RegisterCall<*, *>,
val formLauncher: ActivityResultLauncher<*>,
val manageLauncher: ActivityResultLauncher<*>,
val sheetLauncher: EmbeddedSheetLauncher,
)
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ import com.stripe.android.lpmfoundations.paymentmethod.PaymentMethodMetadataFact
import com.stripe.android.model.PaymentIntentFixtures
import com.stripe.android.paymentelement.EmbeddedPaymentElement
import com.stripe.android.paymentelement.ExperimentalEmbeddedPaymentElementApi
import com.stripe.android.paymentelement.FakeActivityResultCaller
import com.stripe.android.paymentelement.confirmation.FakeConfirmationHandler
import com.stripe.android.paymentelement.embedded.manage.ManageContract
import com.stripe.android.paymentsheet.CustomerStateHolder
import com.stripe.android.paymentsheet.PaymentSheet
import com.stripe.android.paymentsheet.PaymentSheet.Appearance.Embedded
Expand All @@ -23,6 +21,7 @@ import com.stripe.android.paymentsheet.model.PaymentSelection
import com.stripe.android.paymentsheet.model.paymentMethodType
import com.stripe.android.paymentsheet.state.PaymentElementLoader
import com.stripe.android.ui.core.cbc.CardBrandChoiceEligibility
import com.stripe.android.utils.DummyActivityResultCaller
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
Expand Down Expand Up @@ -346,13 +345,10 @@ internal class SharedPaymentElementViewModelTest {
}

@Test
fun `initEmbeddedActivityLauncher and clearEmbeddedActivityLauncher successfully init and clear formLauncher`() =
fun `initEmbeddedActivityLauncher and clearEmbeddedActivityLauncher successfully init and clear sheetLauncher`() =
testScenario {
val formLauncher = FakeEmbeddedActivityLauncher(FormContract)
val manageLauncher = FakeEmbeddedActivityLauncher(ManageContract)
val activityResultCaller = FakeActivityResultCaller(formLauncher, manageLauncher)
assertThat(embeddedContentHelper.testSheetLauncher).isNull()
viewModel.initEmbeddedSheetLauncher(activityResultCaller, TestLifecycleOwner())
viewModel.initEmbeddedSheetLauncher(DummyActivityResultCaller.noOp(), TestLifecycleOwner())
assertThat(embeddedContentHelper.testSheetLauncher).isNotNull()
viewModel.clearEmbeddedSheetLauncher()
assertThat(embeddedContentHelper.testSheetLauncher).isNull()
Expand Down

0 comments on commit c68950c

Please sign in to comment.