diff --git a/health-sdk/sdk/src/main/java/net/gini/android/health/sdk/paymentcomponent/PaymentComponent.kt b/health-sdk/sdk/src/main/java/net/gini/android/health/sdk/paymentcomponent/PaymentComponent.kt index 6f4bf1f23a..c5e13b9092 100644 --- a/health-sdk/sdk/src/main/java/net/gini/android/health/sdk/paymentcomponent/PaymentComponent.kt +++ b/health-sdk/sdk/src/main/java/net/gini/android/health/sdk/paymentcomponent/PaymentComponent.kt @@ -1,6 +1,7 @@ package net.gini.android.health.sdk.paymentcomponent import android.content.Context +import androidx.annotation.VisibleForTesting import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -22,7 +23,8 @@ class PaymentComponent(private val context: Context, private val giniHealth: Gin MutableStateFlow(SelectedPaymentProviderAppState.NothingSelected) val selectedPaymentProviderAppFlow: StateFlow = _selectedPaymentProviderAppFlow.asStateFlow() - private val paymentComponentPreferences = PaymentComponentPreferences(context) + @VisibleForTesting + internal val paymentComponentPreferences = PaymentComponentPreferences(context) var listener: Listener? = null diff --git a/health-sdk/sdk/src/main/java/net/gini/android/health/sdk/paymentprovider/PaymentProviderApp.kt b/health-sdk/sdk/src/main/java/net/gini/android/health/sdk/paymentprovider/PaymentProviderApp.kt index 64d1c0017e..5c14a2d9db 100644 --- a/health-sdk/sdk/src/main/java/net/gini/android/health/sdk/paymentprovider/PaymentProviderApp.kt +++ b/health-sdk/sdk/src/main/java/net/gini/android/health/sdk/paymentprovider/PaymentProviderApp.kt @@ -5,15 +5,12 @@ import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.content.pm.ResolveInfo -import android.graphics.Bitmap -import android.graphics.BitmapFactory import android.graphics.Color import android.graphics.drawable.BitmapDrawable -import android.graphics.drawable.Drawable import android.net.Uri -import android.util.TypedValue import androidx.annotation.ColorInt import net.gini.android.health.api.models.PaymentProvider +import net.gini.android.health.sdk.util.extensions.generateBitmapDrawableIcon import org.slf4j.LoggerFactory internal const val Scheme = "ginipay" // It has to match the scheme in query tag in manifest @@ -122,22 +119,7 @@ data class PaymentProviderApp( } return PaymentProviderApp( name = paymentProvider.name, - icon = BitmapFactory.decodeByteArray(paymentProvider.icon, 0, paymentProvider.icon.size) - ?.let { bitmap -> - val iconSizePx = TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, - ICON_SIZE, - context.resources.displayMetrics - ).toInt() - val scaledBitamp = Bitmap.createScaledBitmap( - bitmap, - iconSizePx, - iconSizePx, - true - ) - bitmap.recycle() - BitmapDrawable(context.resources, scaledBitamp) - }, + icon = context.generateBitmapDrawableIcon(paymentProvider.icon, paymentProvider.icon.size), colors = PaymentProviderAppColors( backgroundColor = Color.parseColor("#${paymentProvider.colors.backgroundColorRGBHex}"), textColor = Color.parseColor("#${paymentProvider.colors.textColoRGBHex}") diff --git a/health-sdk/sdk/src/main/java/net/gini/android/health/sdk/util/extensions/Context.kt b/health-sdk/sdk/src/main/java/net/gini/android/health/sdk/util/extensions/Context.kt new file mode 100644 index 0000000000..cad7ed55f9 --- /dev/null +++ b/health-sdk/sdk/src/main/java/net/gini/android/health/sdk/util/extensions/Context.kt @@ -0,0 +1,30 @@ +package net.gini.android.health.sdk.util.extensions + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.graphics.drawable.BitmapDrawable +import android.util.TypedValue +import androidx.annotation.VisibleForTesting +import net.gini.android.health.sdk.paymentprovider.PaymentProviderApp + +// In a future refactoring we can split extensions into files according to what component they extend +@VisibleForTesting +internal fun Context.generateBitmapDrawableIcon(icon: ByteArray, iconSize: Int): BitmapDrawable? { + return BitmapFactory.decodeByteArray(icon, 0, iconSize) + ?.let { bitmap -> + val iconSizePx = TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + PaymentProviderApp.ICON_SIZE, + this.resources.displayMetrics + ).toInt() + val scaledBitmap = Bitmap.createScaledBitmap( + bitmap, + iconSizePx, + iconSizePx, + true + ) + bitmap.recycle() + BitmapDrawable(this.resources, scaledBitmap) + } +} \ No newline at end of file diff --git a/health-sdk/sdk/src/test/java/net/gini/android/health/sdk/paymentComponent/PaymentComponentPreferencesTest.kt b/health-sdk/sdk/src/test/java/net/gini/android/health/sdk/paymentComponent/PaymentComponentPreferencesTest.kt new file mode 100644 index 0000000000..caa7c4d904 --- /dev/null +++ b/health-sdk/sdk/src/test/java/net/gini/android/health/sdk/paymentComponent/PaymentComponentPreferencesTest.kt @@ -0,0 +1,58 @@ +package net.gini.android.health.sdk.paymentComponent + +import android.content.Context +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import net.gini.android.health.sdk.paymentcomponent.PaymentComponentPreferences +import net.gini.android.health.sdk.test.ViewModelTestCoroutineRule +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@ExperimentalCoroutinesApi +@RunWith(AndroidJUnit4::class) +class PaymentComponentPreferencesTest { + + @get:Rule + val testCoroutineRule = ViewModelTestCoroutineRule() + private lateinit var context: Context + + @Before + fun setup() { + context = ApplicationProvider.getApplicationContext() + } + + @Test + fun `sets selected payment provider app`() = runTest { + // Given + val paymentComponentPreferences = PaymentComponentPreferences(context) + + assertThat(paymentComponentPreferences.getSelectedPaymentProviderId()).isNull() + + // When + paymentComponentPreferences.saveSelectedPaymentProviderId("123") + + // Then + assertThat(paymentComponentPreferences.getSelectedPaymentProviderId()).isEqualTo("123") + } + + @Test + fun `deletes selected payment provider app`() = runTest { + // Given + val paymentComponentPreferences = PaymentComponentPreferences(context) + + assertThat(paymentComponentPreferences.getSelectedPaymentProviderId()).isNull() + paymentComponentPreferences.saveSelectedPaymentProviderId("123") + assertThat(paymentComponentPreferences.getSelectedPaymentProviderId()).isEqualTo("123") + + // When + paymentComponentPreferences.deleteSelectedPaymentProviderId() + + // Then + assertThat(paymentComponentPreferences.getSelectedPaymentProviderId()).isNull() + } +} \ No newline at end of file diff --git a/health-sdk/sdk/src/test/java/net/gini/android/health/sdk/paymentComponent/PaymentComponentTest.kt b/health-sdk/sdk/src/test/java/net/gini/android/health/sdk/paymentComponent/PaymentComponentTest.kt new file mode 100644 index 0000000000..b019ea6986 --- /dev/null +++ b/health-sdk/sdk/src/test/java/net/gini/android/health/sdk/paymentComponent/PaymentComponentTest.kt @@ -0,0 +1,399 @@ +package net.gini.android.health.sdk.paymentComponent + +import android.content.Context +import android.content.pm.PackageManager +import android.content.res.Resources +import androidx.test.core.app.ApplicationProvider.getApplicationContext +import androidx.test.ext.junit.runners.AndroidJUnit4 +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat +import io.mockk.coEvery +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkStatic +import io.mockk.spyk +import io.mockk.unmockkAll +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.test.runTest +import net.gini.android.core.api.Resource +import net.gini.android.health.api.GiniHealthAPI +import net.gini.android.health.api.HealthApiDocumentManager +import net.gini.android.health.api.models.PaymentProvider +import net.gini.android.health.sdk.GiniHealth +import net.gini.android.health.sdk.paymentcomponent.PaymentComponent +import net.gini.android.health.sdk.paymentcomponent.PaymentProviderAppsState +import net.gini.android.health.sdk.paymentcomponent.SelectedPaymentProviderAppState +import net.gini.android.health.sdk.paymentprovider.PaymentProviderApp +import net.gini.android.health.sdk.paymentprovider.PaymentProviderAppColors +import net.gini.android.health.sdk.paymentprovider.getPaymentProviderApps +import net.gini.android.health.sdk.review.ReviewConfiguration +import net.gini.android.health.sdk.review.ReviewFragment +import net.gini.android.health.sdk.test.ViewModelTestCoroutineRule +import net.gini.android.health.sdk.util.extensions.generateBitmapDrawableIcon +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@ExperimentalCoroutinesApi +@RunWith(AndroidJUnit4::class) +class PaymentComponentTest { + + @get:Rule + val testCoroutineRule = ViewModelTestCoroutineRule() + + private var context: Context? = null + private var giniHealth: GiniHealth? = null + private val giniHealthAPI: GiniHealthAPI = mockk(relaxed = true) { GiniHealthAPI::class.java } + private val documentManager: HealthApiDocumentManager = mockk { HealthApiDocumentManager::class.java } + + private val paymentProvider = PaymentProvider( + id = "payment provider id", + name = "payment provider name", + packageName = "net.gini.android.bank.exampleapp1", + appVersion = "appVersion", + colors = PaymentProvider.Colors( + backgroundColorRGBHex = "112233", + textColoRGBHex = "ffffff" + ), + icon = byteArrayOf(), + playStoreUrl = "" + ) + + private val paymentProvider1 = PaymentProvider( + id = "payment provider id 1", + name = "payment provider name", + packageName = "net.gini.android.bank.exampleapp2", + appVersion = "appVersion", + colors = PaymentProvider.Colors( + backgroundColorRGBHex = "112233", + textColoRGBHex = "ffffff" + ), + icon = byteArrayOf(), + playStoreUrl = "" + ) + + private val paymentProvider2 = PaymentProvider( + id = "payment provider id 2", + name = "payment provider name", + packageName = "net.gini.android.bank.exampleapp3", + appVersion = "appVersion", + colors = PaymentProvider.Colors( + backgroundColorRGBHex = "112233", + textColoRGBHex = "ffffff" + ), + icon = byteArrayOf(), + playStoreUrl = "" + ) + + private val noPlayStoreUrlPaymentProvider = PaymentProvider( + id = "payment provider id 3", + name = "payment provider name", + packageName = "net.gini.android.bank.exampleapp3", + appVersion = "appVersion", + colors = PaymentProvider.Colors( + backgroundColorRGBHex = "112233", + textColoRGBHex = "ffffff" + ), + icon = byteArrayOf(), + ) + + @Before + fun setUp() { + every { giniHealthAPI.documentManager } returns documentManager + giniHealth = GiniHealth(giniHealthAPI) + context = getApplicationContext() + } + + @After + fun tearDown() { + giniHealth = null + context = null + unmockkAll() + } + + private fun createMockedContextAndSetDependencies(paymentProviderList: List, paymentProviderAppsList: List): Context { + val privateContext: Context = mockk() + val resources: Resources = spyk(context!!.resources) + val packageManager: PackageManager = mockk(relaxed = true) + + mockkStatic(Context::generateBitmapDrawableIcon) + mockkStatic(PackageManager::getPaymentProviderApps) + every { privateContext.applicationContext } returns context + every { privateContext.packageManager } returns packageManager + every { privateContext.resources } returns resources + every { privateContext.generateBitmapDrawableIcon(any(), any()) } returns null + every { packageManager.getPaymentProviderApps(paymentProviderList, privateContext) } returns paymentProviderAppsList + + return privateContext + } + + private fun buildPaymentProviderApp(paymentProvider: PaymentProvider, isInstalled: Boolean) = PaymentProviderApp( + name = paymentProvider.name, + icon = null, + colors = PaymentProviderAppColors( + backgroundColor = 0, + textColor = 0 + ), + paymentProvider = paymentProvider, + installedPaymentProviderApp = if (isInstalled) mockk(relaxed = true) else null + ) + + @Test + fun `emits error when it cannot load payment providers`() = runTest { + // Given + coEvery { documentManager.getPaymentProviders() } returns Resource.Error() + + // When + val paymentComponent = PaymentComponent(context!!, giniHealth!!) + paymentComponent.loadPaymentProviderApps() + + // Then + paymentComponent.paymentProviderAppsFlow.test { + assertThat(awaitItem()).isInstanceOf(PaymentProviderAppsState.Error::class.java) + } + } + + @Test + fun `emits error when loading payment providers is cancelled`() = runTest { + // Given + coEvery { documentManager.getPaymentProviders() } returns Resource.Cancelled() + + // When + val paymentComponent = PaymentComponent(context!!, giniHealth!!) + paymentComponent.loadPaymentProviderApps() + + // Then + paymentComponent.paymentProviderAppsFlow.test { + assertThat(awaitItem()).isInstanceOf(PaymentProviderAppsState.Error::class.java) + } + } + + @Test + fun `emits payment provider apps when loaded`() = runTest { + // Given + val paymentProviderList = listOf( + paymentProvider, + paymentProvider1, + paymentProvider2 + ) + + coEvery { documentManager.getPaymentProviders() } returns Resource.Success(paymentProviderList) + + // When + val paymentComponent = PaymentComponent(context!!, giniHealth!!) + paymentComponent.loadPaymentProviderApps() + + // Then + paymentComponent.paymentProviderAppsFlow.test { + val validation = awaitItem() + assertThat(validation).isInstanceOf(PaymentProviderAppsState.Success::class.java) + assertThat((validation as PaymentProviderAppsState.Success).paymentProviderApps.size).isEqualTo(3) + + cancelAndConsumeRemainingEvents() + } + } + + @Test + fun `doesn't emit payment providers which are not installed and which don't have a Play Store url`() = runTest { + // Given + val paymentProviderList = listOf( + paymentProvider, + paymentProvider1, + paymentProvider2, + noPlayStoreUrlPaymentProvider + ) + + coEvery { documentManager.getPaymentProviders() } returns Resource.Success(paymentProviderList) + + val paymentProviderAppList = listOf( + buildPaymentProviderApp(paymentProvider, true), + buildPaymentProviderApp(paymentProvider1, true), + buildPaymentProviderApp(paymentProvider2, true), + buildPaymentProviderApp(noPlayStoreUrlPaymentProvider, false) + ) + val mockedContext = createMockedContextAndSetDependencies(paymentProviderList, paymentProviderAppList) + + //When + val paymentComponent = PaymentComponent(mockedContext, giniHealth!!) + paymentComponent.loadPaymentProviderApps() + + // Then + paymentComponent.paymentProviderAppsFlow.test { + val validation = awaitItem() + assertThat(validation).isInstanceOf(PaymentProviderAppsState.Success::class.java) + assertThat((validation as PaymentProviderAppsState.Success).paymentProviderApps.size).isEqualTo(3) + + cancelAndConsumeRemainingEvents() + } + } + + @Test + fun `emits payment provider if it's not installed but has Play Store url`() = runTest { + // Given + val paymentProviderList = listOf( + paymentProvider + ) + + coEvery { documentManager.getPaymentProviders() } returns Resource.Success(paymentProviderList) + + val paymentProviderAppList = listOf(buildPaymentProviderApp(paymentProvider, false)) + val mockedContext = createMockedContextAndSetDependencies(paymentProviderList, paymentProviderAppList) + + //When + val paymentComponent = PaymentComponent(mockedContext, giniHealth!!) + paymentComponent.loadPaymentProviderApps() + + // Then + paymentComponent.paymentProviderAppsFlow.test { + val validation = awaitItem() + assertThat(validation).isInstanceOf(PaymentProviderAppsState.Success::class.java) + assertThat((validation as PaymentProviderAppsState.Success).paymentProviderApps.size).isEqualTo(1) + + cancelAndConsumeRemainingEvents() + } + } + + @Test + fun `emits payment provider if it has Play Store URL and is installed`() = runTest { + // Given + val paymentProviderList = listOf( + paymentProvider, + paymentProvider1, + paymentProvider2, + noPlayStoreUrlPaymentProvider + ) + + coEvery { documentManager.getPaymentProviders() } returns Resource.Success(paymentProviderList) + + val paymentProviderAppsList = listOf( + buildPaymentProviderApp(paymentProvider, true), + buildPaymentProviderApp(paymentProvider1, true), + buildPaymentProviderApp(paymentProvider2, true), + buildPaymentProviderApp(noPlayStoreUrlPaymentProvider, false) + ) + val mockedContext = createMockedContextAndSetDependencies(paymentProviderList, paymentProviderAppsList) + + //When + val paymentComponent = PaymentComponent(mockedContext, giniHealth!!) + paymentComponent.loadPaymentProviderApps() + + // Then + paymentComponent.paymentProviderAppsFlow.test { + val validation = awaitItem() + assertThat(validation).isInstanceOf(PaymentProviderAppsState.Success::class.java) + assertThat((validation as PaymentProviderAppsState.Success).paymentProviderApps.size).isEqualTo(3) + + cancelAndConsumeRemainingEvents() + } + } + + @Test + fun `returns empty list when no payment providers installed`() = runTest { + // Given + val paymentProviderList = listOf( + noPlayStoreUrlPaymentProvider + ) + + coEvery { documentManager.getPaymentProviders() } returns Resource.Success(paymentProviderList) + + //When + val paymentComponent = PaymentComponent(context!!, giniHealth!!) + paymentComponent.loadPaymentProviderApps() + + // Then + paymentComponent.paymentProviderAppsFlow.test { + val validation = awaitItem() + assertThat(validation).isInstanceOf(PaymentProviderAppsState.Success::class.java) + assertThat((validation as PaymentProviderAppsState.Success).paymentProviderApps).isEmpty() + + cancelAndConsumeRemainingEvents() + } + } + + @Test + fun `rechecks which payment provider apps are installed`() = runTest { + //Given + val paymentProviderList = mutableListOf( + paymentProvider + ) + + coEvery { documentManager.getPaymentProviders() } returns Resource.Success(paymentProviderList) + + //When + val paymentComponent = PaymentComponent(context!!, giniHealth!!) + paymentComponent.loadPaymentProviderApps() + + // Then + paymentComponent.paymentProviderAppsFlow.test { + val validation = awaitItem() + assertThat(validation).isInstanceOf(PaymentProviderAppsState.Success::class.java) + assertThat((validation as PaymentProviderAppsState.Success).paymentProviderApps).isNotEmpty() + + paymentComponent.recheckWhichPaymentProviderAppsAreInstalled() + val validateRecheckEmpty = awaitItem() + assertThat(validateRecheckEmpty).isInstanceOf(PaymentProviderAppsState.Success::class.java) + assertThat((validateRecheckEmpty as PaymentProviderAppsState.Success).paymentProviderApps).isNotEmpty() + + cancelAndConsumeRemainingEvents() + } + } + + @Test + fun `sets selected payment provider`() = runTest { + // Given + val paymentProviderList = listOf( + paymentProvider, + paymentProvider1, + paymentProvider2, + ) + + coEvery { documentManager.getPaymentProviders() } returns Resource.Success(paymentProviderList) + + val paymentComponent = PaymentComponent(context!!, giniHealth!!) + paymentComponent.loadPaymentProviderApps() + + paymentComponent.selectedPaymentProviderAppFlow.test { + val noPaymentProviderSelectedValidation = awaitItem() + + assertThat(noPaymentProviderSelectedValidation).isInstanceOf(SelectedPaymentProviderAppState.NothingSelected::class.java) + assertThat(paymentComponent.paymentProviderAppsFlow.value).isInstanceOf(PaymentProviderAppsState.Success::class.java) + + //When + val paymentProviderToBeSelected = (paymentComponent.paymentProviderAppsFlow.value as PaymentProviderAppsState.Success).paymentProviderApps.last() + paymentComponent.setSelectedPaymentProviderApp(paymentProviderToBeSelected) + + // Then + val paymentProviderSelectedValidation = awaitItem() + assertThat(paymentProviderSelectedValidation).isInstanceOf(SelectedPaymentProviderAppState.AppSelected::class.java) + assertThat((paymentProviderSelectedValidation as SelectedPaymentProviderAppState.AppSelected).paymentProviderApp.paymentProvider.id).isEqualTo(paymentProviderList.last().id) + } + } + + @Test(expected = IllegalStateException::class) + fun `throws exception when trying to create ReviewFragment if no payment provider app is set`() = runTest { + // Given + val reviewConfiguration: ReviewConfiguration = mockk(relaxed = true) + val paymentComponent = PaymentComponent(context!!, giniHealth!!) + + // When trying to instantiate fragment, then exception should be thrown + paymentComponent.getPaymentReviewFragment("", reviewConfiguration) + } + + @Test + fun `instantiates review fragment if payment provider app is set`() = runTest { + // Given + val reviewConfiguration: ReviewConfiguration = mockk(relaxed = true) + val paymentComponent: PaymentComponent = mockk(relaxed = true) + val paymentProviderApp: PaymentProviderApp = mockk(relaxed = true) + + // When + every { paymentComponent.selectedPaymentProviderAppFlow } returns MutableStateFlow(SelectedPaymentProviderAppState.AppSelected(paymentProviderApp)) + + // Then + assertThat(paymentComponent.getPaymentReviewFragment("", reviewConfiguration)).isInstanceOf(ReviewFragment::class.java) + } + +} \ No newline at end of file diff --git a/health-sdk/sdk/src/test/java/net/gini/android/health/sdk/paymentComponent/PaymentComponentViewTest.kt b/health-sdk/sdk/src/test/java/net/gini/android/health/sdk/paymentComponent/PaymentComponentViewTest.kt new file mode 100644 index 0000000000..1d4ba5fb9b --- /dev/null +++ b/health-sdk/sdk/src/test/java/net/gini/android/health/sdk/paymentComponent/PaymentComponentViewTest.kt @@ -0,0 +1,167 @@ +package net.gini.android.health.sdk.paymentComponent + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.text.SpannableString +import android.text.style.ClickableSpan +import android.widget.Button +import android.widget.TextView +import androidx.lifecycle.Lifecycle +import androidx.test.core.app.ActivityScenario +import androidx.test.core.app.ApplicationProvider +import com.google.common.truth.Truth +import io.mockk.mockk +import io.mockk.verify +import kotlinx.coroutines.test.runTest +import net.gini.android.health.sdk.R +import net.gini.android.health.sdk.paymentcomponent.PaymentComponent +import net.gini.android.health.sdk.paymentcomponent.PaymentComponentView +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.Robolectric +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class PaymentComponentViewTest { + private var context: Context? = null + private var scenario: ActivityScenario? = null + private lateinit var paymentComponent: PaymentComponent + private lateinit var paymentComponentListener: PaymentComponent.Listener + + @Before + fun setUp() { + context = ApplicationProvider.getApplicationContext() + context!!.setTheme(R.style.GiniHealthTheme) + paymentComponent = PaymentComponent(context!!, mockk()) + paymentComponentListener = mockk(relaxed = true) + paymentComponent.listener = paymentComponentListener + + // Needed to build the activity in which the custom component will be launched to be tested + Robolectric.buildActivity(Activity::class.java).create().get() + scenario = ActivityScenario.launch( + Intent(context, Activity::class.java) + ) + scenario?.moveToState(Lifecycle.State.CREATED) + } + + @After + fun tearDown() { + context = null + scenario!!.close() + } + + @Test + fun `calls onMoreInformation method of listener when clicking on info button`() = runTest { + // Given + scenario?.onActivity { activity -> + val paymentComponentView = PaymentComponentView(activity, null) + paymentComponentView.paymentComponent = paymentComponent + + // When + (paymentComponentView.findViewById(R.id.ghs_info_circle_icon) as Button).performClick() + // Then + verify { + paymentComponentListener.onMoreInformationClicked() + } + } + } + + @Test + fun `calls onMoreInformation method of listener when clicking on more information label`() = runTest { + // Given + scenario?.onActivity { activity -> + val paymentComponentView = PaymentComponentView(activity, null) + paymentComponentView.paymentComponent = paymentComponent + + val moreInformationLabel = paymentComponentView.findViewById(R.id.ghs_more_information_label) as TextView + + // When + val spannableString = moreInformationLabel.text as SpannableString + val spans = spannableString.getSpans(0, spannableString.length, ClickableSpan::class.java) + + Truth.assertThat(spans.size).isEqualTo(1) + spans[0].onClick(moreInformationLabel) + + // Then + verify { + paymentComponentListener.onMoreInformationClicked() + } + } + } + + @Test + fun `calls onBankPickerClicked method of listener when clicking on select bank button`() = runTest { + // Given + scenario?.onActivity { activity -> + val paymentComponentView = PaymentComponentView(activity, null) + paymentComponentView.paymentComponent = paymentComponent + + // When + (paymentComponentView.findViewById(R.id.ghs_select_bank_button) as Button).performClick() + // Then + verify { + paymentComponentListener.onBankPickerClicked() + } + } + } + + @Test + fun `does not call onPayInvoiceClicked method of listener if no document id is set`() = runTest { + // Given + scenario?.onActivity { activity -> + val paymentComponentView = PaymentComponentView(activity, null) + paymentComponentView.paymentComponent = paymentComponent + + // When + (paymentComponentView.findViewById(R.id.ghs_pay_invoice_button) as Button).performClick() + + // Then + verify(exactly = 0) { paymentComponentListener.onPayInvoiceClicked("") } + } + } + + @Test + fun `calls onPayInvoiceClicked method of listener when clicking on pay invoice button and document id`() = runTest { + // Given + scenario?.onActivity { activity -> + val paymentComponentView = PaymentComponentView(activity, null) + paymentComponentView.paymentComponent = paymentComponent + paymentComponentView.documentId = "123" + + // When + (paymentComponentView.findViewById(R.id.ghs_pay_invoice_button) as Button).performClick() + // Then + verify { + paymentComponentListener.onPayInvoiceClicked("123") + } + } + } + + @Test + fun `disables buttons and deletes document id to reuse`() = runTest { + // Given + scenario?.onActivity { activity -> + val paymentComponentView = PaymentComponentView(activity, null) + paymentComponentView.paymentComponent = paymentComponent + paymentComponentView.documentId = "123" + paymentComponentView.isPayable = true + + Truth.assertThat(paymentComponentView.documentId).isEqualTo("123") + Truth.assertThat(paymentComponentView.isPayable).isEqualTo(true) + Truth.assertThat((paymentComponentView.findViewById(R.id.ghs_pay_invoice_button) as Button).isEnabled).isEqualTo(true) + Truth.assertThat((paymentComponentView.findViewById(R.id.ghs_select_bank_button) as Button).isEnabled).isEqualTo(true) + + // When + paymentComponentView.prepareForReuse() + + // Then + Truth.assertThat(paymentComponentView.documentId).isNull() + Truth.assertThat(paymentComponentView.isPayable).isEqualTo(false) + Truth.assertThat((paymentComponentView.findViewById(R.id.ghs_pay_invoice_button) as Button).isEnabled).isEqualTo(false) + Truth.assertThat((paymentComponentView.findViewById(R.id.ghs_select_bank_button) as Button).isEnabled).isEqualTo(false) + } + } +} \ No newline at end of file