-
Notifications
You must be signed in to change notification settings - Fork 65
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1878 from Adyen/feature/pay-by-bank-us-test
Pay by Bank US - Component & Delegate Tests
- Loading branch information
Showing
2 changed files
with
353 additions
and
0 deletions.
There are no files selected for viewing
149 changes: 149 additions & 0 deletions
149
paybybank-us/src/test/java/com/adyen/checkout/paybybankus/PayByBankUSComponentTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
/* | ||
* Copyright (c) 2024 Adyen N.V. | ||
* | ||
* This file is open source and available under the MIT license. See the LICENSE file for more info. | ||
* | ||
* Created by ozgur on 5/11/2024. | ||
*/ | ||
|
||
package com.adyen.checkout.paybybankus | ||
|
||
import androidx.lifecycle.LifecycleOwner | ||
import androidx.lifecycle.viewModelScope | ||
import app.cash.turbine.test | ||
import com.adyen.checkout.action.core.internal.DefaultActionHandlingComponent | ||
import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate | ||
import com.adyen.checkout.components.core.internal.ComponentEventHandler | ||
import com.adyen.checkout.components.core.internal.PaymentComponentEvent | ||
import com.adyen.checkout.paybybankus.internal.PayByBankUSComponentViewType | ||
import com.adyen.checkout.paybybankus.internal.PayByBankUSDelegate | ||
import com.adyen.checkout.test.LoggingExtension | ||
import com.adyen.checkout.test.TestDispatcherExtension | ||
import com.adyen.checkout.test.extensions.invokeOnCleared | ||
import com.adyen.checkout.ui.core.internal.ui.TestComponentViewType | ||
import kotlinx.coroutines.flow.MutableStateFlow | ||
import kotlinx.coroutines.test.runTest | ||
import org.junit.jupiter.api.Assertions.assertEquals | ||
import org.junit.jupiter.api.BeforeEach | ||
import org.junit.jupiter.api.Test | ||
import org.junit.jupiter.api.extension.ExtendWith | ||
import org.mockito.Mock | ||
import org.mockito.junit.jupiter.MockitoExtension | ||
import org.mockito.kotlin.any | ||
import org.mockito.kotlin.doReturn | ||
import org.mockito.kotlin.eq | ||
import org.mockito.kotlin.mock | ||
import org.mockito.kotlin.verify | ||
import org.mockito.kotlin.whenever | ||
|
||
@ExtendWith(MockitoExtension::class, TestDispatcherExtension::class, LoggingExtension::class) | ||
internal class PayByBankUSComponentTest( | ||
@Mock private val payByBankUSDelegate: PayByBankUSDelegate, | ||
@Mock private val genericActionDelegate: GenericActionDelegate, | ||
@Mock private val actionHandlingComponent: DefaultActionHandlingComponent, | ||
@Mock private val componentEventHandler: ComponentEventHandler<PayByBankUSComponentState>, | ||
) { | ||
|
||
private lateinit var component: PayByBankUSComponent | ||
|
||
@BeforeEach | ||
fun before() { | ||
whenever(payByBankUSDelegate.viewFlow) doReturn MutableStateFlow(PayByBankUSComponentViewType) | ||
whenever(genericActionDelegate.viewFlow) doReturn MutableStateFlow(null) | ||
|
||
component = PayByBankUSComponent( | ||
payByBankUSDelegate, | ||
genericActionDelegate, | ||
actionHandlingComponent, | ||
componentEventHandler, | ||
) | ||
} | ||
|
||
@Test | ||
fun `when component is created then delegates are initialized`() { | ||
verify(payByBankUSDelegate).initialize(component.viewModelScope) | ||
verify(genericActionDelegate).initialize(component.viewModelScope) | ||
verify(componentEventHandler).initialize(component.viewModelScope) | ||
} | ||
|
||
@Test | ||
fun `when component is cleared then delegates are cleared`() { | ||
component.invokeOnCleared() | ||
|
||
verify(payByBankUSDelegate).onCleared() | ||
verify(genericActionDelegate).onCleared() | ||
verify(componentEventHandler).onCleared() | ||
} | ||
|
||
@Test | ||
fun `when observe is called then observe in delegates is called`() { | ||
val lifecycleOwner = mock<LifecycleOwner>() | ||
val callback: (PaymentComponentEvent<PayByBankUSComponentState>) -> Unit = {} | ||
|
||
component.observe(lifecycleOwner, callback) | ||
|
||
verify(payByBankUSDelegate).observe(lifecycleOwner, component.viewModelScope, callback) | ||
verify(genericActionDelegate).observe(eq(lifecycleOwner), eq(component.viewModelScope), any()) | ||
} | ||
|
||
@Test | ||
fun `when removeObserver is called then removeObserver in delegates is called`() { | ||
component.removeObserver() | ||
|
||
verify(payByBankUSDelegate).removeObserver() | ||
verify(genericActionDelegate).removeObserver() | ||
} | ||
|
||
@Test | ||
fun `when component is initialized then view flow should match pay by bank delegate view flow`() = runTest { | ||
component.viewFlow.test { | ||
assertEquals(PayByBankUSComponentViewType, awaitItem()) | ||
expectNoEvents() | ||
} | ||
} | ||
|
||
@Test | ||
fun `when pay by bank delegate view flow emits a value then component view flow should match that value`() = | ||
runTest { | ||
val payByBankDelegateViewFlow = MutableStateFlow(TestComponentViewType.VIEW_TYPE_1) | ||
whenever(payByBankUSDelegate.viewFlow) doReturn payByBankDelegateViewFlow | ||
component = PayByBankUSComponent( | ||
payByBankUSDelegate, | ||
genericActionDelegate, | ||
actionHandlingComponent, | ||
componentEventHandler, | ||
) | ||
|
||
component.viewFlow.test { | ||
assertEquals(TestComponentViewType.VIEW_TYPE_1, awaitItem()) | ||
|
||
payByBankDelegateViewFlow.emit(TestComponentViewType.VIEW_TYPE_2) | ||
assertEquals(TestComponentViewType.VIEW_TYPE_2, awaitItem()) | ||
|
||
expectNoEvents() | ||
} | ||
} | ||
|
||
@Test | ||
fun `when action delegate view flow emits a value then component view flow should match that value`() = runTest { | ||
val actionDelegateViewFlow = MutableStateFlow(TestComponentViewType.VIEW_TYPE_1) | ||
whenever(genericActionDelegate.viewFlow) doReturn actionDelegateViewFlow | ||
component = PayByBankUSComponent( | ||
payByBankUSDelegate, | ||
genericActionDelegate, | ||
actionHandlingComponent, | ||
componentEventHandler | ||
) | ||
|
||
component.viewFlow.test { | ||
// this value should match the value of the main delegate and not the action delegate | ||
// and in practice the initial value of the action delegate view flow is always null so it should be ignored | ||
assertEquals(PayByBankUSComponentViewType, awaitItem()) | ||
|
||
actionDelegateViewFlow.emit(TestComponentViewType.VIEW_TYPE_2) | ||
assertEquals(TestComponentViewType.VIEW_TYPE_2, awaitItem()) | ||
|
||
expectNoEvents() | ||
} | ||
} | ||
} |
204 changes: 204 additions & 0 deletions
204
...rc/test/java/com/adyen/checkout/paybybankus/internal/ui/DefaultPayByBankUSDelegateTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
/* | ||
* Copyright (c) 2024 Adyen N.V. | ||
* | ||
* This file is open source and available under the MIT license. See the LICENSE file for more info. | ||
* | ||
* Created by ozgur on 5/11/2024. | ||
*/ | ||
|
||
package com.adyen.checkout.paybybankus.internal.ui | ||
|
||
import app.cash.turbine.test | ||
import com.adyen.checkout.components.core.Amount | ||
import com.adyen.checkout.components.core.CheckoutConfiguration | ||
import com.adyen.checkout.components.core.OrderRequest | ||
import com.adyen.checkout.components.core.PaymentMethod | ||
import com.adyen.checkout.components.core.internal.PaymentObserverRepository | ||
import com.adyen.checkout.components.core.internal.analytics.GenericEvents | ||
import com.adyen.checkout.components.core.internal.analytics.TestAnalyticsManager | ||
import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper | ||
import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper | ||
import com.adyen.checkout.core.Environment | ||
import com.adyen.checkout.paybybankus.PayByBankUSComponentState | ||
import com.adyen.checkout.paybybankus.getPayByBankUSConfiguration | ||
import com.adyen.checkout.paybybankus.internal.DefaultPayByBankUSDelegate | ||
import com.adyen.checkout.test.LoggingExtension | ||
import com.adyen.checkout.ui.core.internal.ui.SubmitHandler | ||
import kotlinx.coroutines.CoroutineScope | ||
import kotlinx.coroutines.ExperimentalCoroutinesApi | ||
import kotlinx.coroutines.test.UnconfinedTestDispatcher | ||
import kotlinx.coroutines.test.runTest | ||
import org.junit.jupiter.api.Assertions.assertEquals | ||
import org.junit.jupiter.api.Assertions.assertTrue | ||
import org.junit.jupiter.api.BeforeEach | ||
import org.junit.jupiter.api.Nested | ||
import org.junit.jupiter.api.Test | ||
import org.junit.jupiter.api.extension.ExtendWith | ||
import org.junit.jupiter.params.ParameterizedTest | ||
import org.junit.jupiter.params.provider.Arguments.arguments | ||
import org.junit.jupiter.params.provider.MethodSource | ||
import org.mockito.Mock | ||
import org.mockito.junit.jupiter.MockitoExtension | ||
import org.mockito.kotlin.verify | ||
import java.util.Locale | ||
|
||
@OptIn(ExperimentalCoroutinesApi::class) | ||
@ExtendWith(MockitoExtension::class, LoggingExtension::class) | ||
class DefaultPayByBankUSDelegateTest( | ||
@Mock private val submitHandler: SubmitHandler<PayByBankUSComponentState>, | ||
) { | ||
|
||
private lateinit var analyticsManager: TestAnalyticsManager | ||
private lateinit var delegate: DefaultPayByBankUSDelegate | ||
|
||
@BeforeEach | ||
fun before() { | ||
analyticsManager = TestAnalyticsManager() | ||
delegate = createPayByBankUSDelegate() | ||
} | ||
|
||
@Test | ||
fun `when subscribed then component state flow should propagate a valid state`() = runTest { | ||
delegate.componentStateFlow.test { | ||
with(awaitItem()) { | ||
assertEquals(TEST_PAYMENT_METHOD_TYPE, data.paymentMethod?.type) | ||
assertEquals(TEST_ORDER, data.order) | ||
assertTrue(isInputValid) | ||
assertTrue(isValid) | ||
} | ||
|
||
cancelAndIgnoreRemainingEvents() | ||
} | ||
} | ||
|
||
@ParameterizedTest | ||
@MethodSource("amountSource") | ||
fun `given component state is valid always amount is propagated in component state if set`( | ||
configurationValue: Amount?, | ||
expectedComponentStateValue: Amount?, | ||
) = runTest { | ||
if (configurationValue != null) { | ||
val configuration = createCheckoutConfiguration(configurationValue) | ||
delegate = createPayByBankUSDelegate(configuration = configuration) | ||
} | ||
delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) | ||
delegate.componentStateFlow.test { | ||
assertEquals(expectedComponentStateValue, expectMostRecentItem().data.amount) | ||
} | ||
} | ||
|
||
@Nested | ||
inner class SubmitHandlerTest { | ||
|
||
@Test | ||
fun `when delegate is initialized then submit handler event is initialized`() = runTest { | ||
val coroutineScope = CoroutineScope(UnconfinedTestDispatcher()) | ||
delegate.initialize(coroutineScope) | ||
verify(submitHandler).initialize(coroutineScope, delegate.componentStateFlow) | ||
} | ||
|
||
@Test | ||
fun `when delegate setInteractionBlocked is called then submit handler setInteractionBlocked is called`() = | ||
runTest { | ||
delegate.setInteractionBlocked(true) | ||
verify(submitHandler).setInteractionBlocked(true) | ||
} | ||
|
||
@Test | ||
fun `when delegate onSubmit is called then submit handler onSubmit is called`() = runTest { | ||
delegate.componentStateFlow.test { | ||
delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) | ||
delegate.onSubmit() | ||
verify(submitHandler).onSubmit(expectMostRecentItem()) | ||
} | ||
} | ||
} | ||
|
||
@Nested | ||
inner class AnalyticsTest { | ||
|
||
@Test | ||
fun `when delegate is initialized then analytics manager is initialized`() { | ||
delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) | ||
|
||
analyticsManager.assertIsInitialized() | ||
} | ||
|
||
@Test | ||
fun `when delegate is initialized, then render event is tracked`() { | ||
delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) | ||
|
||
val expectedEvent = GenericEvents.rendered(TEST_PAYMENT_METHOD_TYPE) | ||
analyticsManager.assertLastEventEquals(expectedEvent) | ||
} | ||
|
||
@Test | ||
fun `when onSubmit is called, then submit event is tracked`() { | ||
delegate.onSubmit() | ||
|
||
val expectedEvent = GenericEvents.submit(TEST_PAYMENT_METHOD_TYPE) | ||
analyticsManager.assertLastEventEquals(expectedEvent) | ||
} | ||
|
||
@Test | ||
fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { | ||
analyticsManager.setCheckoutAttemptId(TEST_CHECKOUT_ATTEMPT_ID) | ||
|
||
delegate = createPayByBankUSDelegate() | ||
|
||
delegate.componentStateFlow.test { | ||
assertEquals(TEST_CHECKOUT_ATTEMPT_ID, expectMostRecentItem().data.paymentMethod?.checkoutAttemptId) | ||
} | ||
} | ||
|
||
@Test | ||
fun `when delegate is cleared then analytics manager is cleared`() { | ||
delegate.onCleared() | ||
|
||
analyticsManager.assertIsCleared() | ||
} | ||
} | ||
|
||
private fun createPayByBankUSDelegate( | ||
configuration: CheckoutConfiguration = createCheckoutConfiguration(), | ||
): DefaultPayByBankUSDelegate { | ||
return DefaultPayByBankUSDelegate( | ||
observerRepository = PaymentObserverRepository(), | ||
paymentMethod = PaymentMethod(TEST_PAYMENT_METHOD_TYPE), | ||
order = TEST_ORDER, | ||
componentParams = ButtonComponentParamsMapper(CommonComponentParamsMapper()).mapToParams( | ||
checkoutConfiguration = configuration, | ||
deviceLocale = Locale.US, | ||
dropInOverrideParams = null, | ||
componentSessionParams = null, | ||
componentConfiguration = configuration.getPayByBankUSConfiguration(), | ||
), | ||
analyticsManager = analyticsManager, | ||
submitHandler = submitHandler, | ||
) | ||
} | ||
|
||
private fun createCheckoutConfiguration( | ||
amount: Amount? = null, | ||
) = CheckoutConfiguration( | ||
shopperLocale = Locale.US, | ||
environment = Environment.TEST, | ||
clientKey = TEST_CLIENT_KEY, | ||
amount = amount, | ||
) | ||
|
||
companion object { | ||
private const val TEST_CLIENT_KEY = "test_qwertyuiopasdfghjklzxcvbnmqwerty" | ||
private const val TEST_PAYMENT_METHOD_TYPE = "TEST_PAYMENT_METHOD_TYPE" | ||
private val TEST_ORDER = OrderRequest("PSP", "ORDER_DATA") | ||
private const val TEST_CHECKOUT_ATTEMPT_ID = "TEST_CHECKOUT_ATTEMPT_ID" | ||
|
||
@JvmStatic | ||
fun amountSource() = listOf( | ||
// configurationValue, expectedComponentStateValue | ||
arguments(Amount("EUR", 100), Amount("EUR", 100)), | ||
arguments(Amount("USD", 0), Amount("USD", 0)), | ||
arguments(null, null), | ||
) | ||
} | ||
} |