From 92be38d192d8fad25544bc1989efe2c22cc5c01a Mon Sep 17 00:00:00 2001 From: toluo-stripe Date: Thu, 2 Jan 2025 13:37:17 -0500 Subject: [PATCH] Fix tests and lint --- .../android/link/ui/LinkLogoutMenuItem.kt | 12 ++ .../stripe/android/link/ui/LinkLogoutSheet.kt | 11 -- .../stripe/android/link/ui/menus/LinkMenu.kt | 11 -- .../android/link/ui/menus/LinkMenuItem.kt | 12 ++ .../android/link/LinkActivityResultTest.kt | 2 - .../android/link/LinkActivityViewModelTest.kt | 126 ++++++++++++++++++ .../link/ui/LinkLogoutSheetScreenshotTest.kt | 2 +- 7 files changed, 151 insertions(+), 25 deletions(-) create mode 100644 paymentsheet/src/main/java/com/stripe/android/link/ui/LinkLogoutMenuItem.kt create mode 100644 paymentsheet/src/main/java/com/stripe/android/link/ui/menus/LinkMenuItem.kt diff --git a/paymentsheet/src/main/java/com/stripe/android/link/ui/LinkLogoutMenuItem.kt b/paymentsheet/src/main/java/com/stripe/android/link/ui/LinkLogoutMenuItem.kt new file mode 100644 index 00000000000..383ed02a1da --- /dev/null +++ b/paymentsheet/src/main/java/com/stripe/android/link/ui/LinkLogoutMenuItem.kt @@ -0,0 +1,12 @@ +package com.stripe.android.link.ui + +import com.stripe.android.link.ui.menus.LinkMenuItem +import com.stripe.android.paymentsheet.R + +internal sealed class LinkLogoutMenuItem( + override val textResId: Int, + override val isDestructive: Boolean = false +) : LinkMenuItem { + data object Logout : LinkLogoutMenuItem(textResId = R.string.stripe_log_out, isDestructive = true) + data object Cancel : LinkLogoutMenuItem(textResId = com.stripe.android.R.string.stripe_cancel) +} diff --git a/paymentsheet/src/main/java/com/stripe/android/link/ui/LinkLogoutSheet.kt b/paymentsheet/src/main/java/com/stripe/android/link/ui/LinkLogoutSheet.kt index 4c0fb114106..47419ec9b5e 100644 --- a/paymentsheet/src/main/java/com/stripe/android/link/ui/LinkLogoutSheet.kt +++ b/paymentsheet/src/main/java/com/stripe/android/link/ui/LinkLogoutSheet.kt @@ -2,17 +2,6 @@ package com.stripe.android.link.ui import androidx.compose.runtime.Composable import com.stripe.android.link.ui.menus.LinkMenu -import com.stripe.android.link.ui.menus.LinkMenuItem -import com.stripe.android.paymentsheet.R -import com.stripe.android.R as StripeR - -internal sealed class LinkLogoutMenuItem( - override val textResId: Int, - override val isDestructive: Boolean = false -) : LinkMenuItem { - data object Logout : LinkLogoutMenuItem(textResId = R.string.stripe_log_out, isDestructive = true) - data object Cancel : LinkLogoutMenuItem(textResId = StripeR.string.stripe_cancel) -} @Composable internal fun LinkLogoutSheet( diff --git a/paymentsheet/src/main/java/com/stripe/android/link/ui/menus/LinkMenu.kt b/paymentsheet/src/main/java/com/stripe/android/link/ui/menus/LinkMenu.kt index 63a8219b029..8e5649ff111 100644 --- a/paymentsheet/src/main/java/com/stripe/android/link/ui/menus/LinkMenu.kt +++ b/paymentsheet/src/main/java/com/stripe/android/link/ui/menus/LinkMenu.kt @@ -18,17 +18,6 @@ import com.stripe.android.link.theme.HorizontalPadding import com.stripe.android.link.theme.MinimumTouchTargetSize import com.stripe.android.link.theme.linkColors -/** - * An item to be displayed in a [LinkMenu]. - * - * @property textResId The resource ID of the text of the item - * @property isDestructive Whether this item should be rendered with the error text color - */ -internal interface LinkMenuItem { - val textResId: Int - val isDestructive: Boolean -} - /** * Displays a generic bottom sheet with the provided [items]. * diff --git a/paymentsheet/src/main/java/com/stripe/android/link/ui/menus/LinkMenuItem.kt b/paymentsheet/src/main/java/com/stripe/android/link/ui/menus/LinkMenuItem.kt new file mode 100644 index 00000000000..8402af70d3e --- /dev/null +++ b/paymentsheet/src/main/java/com/stripe/android/link/ui/menus/LinkMenuItem.kt @@ -0,0 +1,12 @@ +package com.stripe.android.link.ui.menus + +/** + * An item to be displayed in a [LinkMenu]. + * + * @property textResId The resource ID of the text of the item + * @property isDestructive Whether this item should be rendered with the error text color + */ +internal interface LinkMenuItem { + val textResId: Int + val isDestructive: Boolean +} diff --git a/paymentsheet/src/test/java/com/stripe/android/link/LinkActivityResultTest.kt b/paymentsheet/src/test/java/com/stripe/android/link/LinkActivityResultTest.kt index 8228d35f57e..4d47d32f073 100644 --- a/paymentsheet/src/test/java/com/stripe/android/link/LinkActivityResultTest.kt +++ b/paymentsheet/src/test/java/com/stripe/android/link/LinkActivityResultTest.kt @@ -5,7 +5,6 @@ import android.content.Intent import androidx.core.net.toUri import androidx.core.os.bundleOf import com.google.common.truth.Truth.assertThat -import com.stripe.android.model.PaymentMethod import com.stripe.android.model.PaymentMethodFixtures import org.junit.Test import org.junit.runner.RunWith @@ -98,7 +97,6 @@ class LinkActivityResultTest { @Test fun `complete with activity result from native link`() { - val bundle = bundleOf( LinkActivityContract.EXTRA_RESULT to LinkActivityResult.Completed(PaymentMethodFixtures.CARD_PAYMENT_METHOD) ) diff --git a/paymentsheet/src/test/java/com/stripe/android/link/LinkActivityViewModelTest.kt b/paymentsheet/src/test/java/com/stripe/android/link/LinkActivityViewModelTest.kt index 9dfca46e4bc..50018fefc3a 100644 --- a/paymentsheet/src/test/java/com/stripe/android/link/LinkActivityViewModelTest.kt +++ b/paymentsheet/src/test/java/com/stripe/android/link/LinkActivityViewModelTest.kt @@ -10,11 +10,17 @@ import androidx.lifecycle.ViewModelStore import androidx.lifecycle.ViewModelStoreOwner import androidx.lifecycle.viewmodel.CreationExtras import androidx.lifecycle.viewmodel.MutableCreationExtras +import androidx.navigation.NavGraph import androidx.navigation.NavHostController +import androidx.navigation.NavOptionsBuilder +import androidx.navigation.PopUpToBuilder import androidx.savedstate.SavedStateRegistryOwner import androidx.test.core.app.ApplicationProvider import com.google.common.truth.Truth.assertThat +import com.stripe.android.core.Logger import com.stripe.android.link.account.FakeLinkAccountManager +import com.stripe.android.link.account.LinkAccountManager +import com.stripe.android.model.ConsumerSession import com.stripe.android.testing.FakeLogger import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.test.UnconfinedTestDispatcher @@ -25,6 +31,12 @@ import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.Mockito.never +import org.mockito.Mockito.spy +import org.mockito.Mockito.`when` +import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.times import org.mockito.kotlin.verify @@ -125,6 +137,120 @@ internal class LinkActivityViewModelTest { assertThat(vm.linkAccount).isEqualTo(TestFactory.LINK_ACCOUNT) } + @Test + fun `vm dismisses with cancellation result on successful log out`() = runTest(dispatcher) { + val linkAccountManager = object : FakeLinkAccountManager() { + var callCount = 0 + override suspend fun logOut(): Result { + callCount += 1 + return Result.success(TestFactory.CONSUMER_SESSION) + } + } + linkAccountManager.setLinkAccount(TestFactory.LINK_ACCOUNT) + + var result: LinkActivityResult? = null + fun dismissWithResult(r: LinkActivityResult) { + result = r + } + + val vm = createViewModel(linkAccountManager = linkAccountManager) + vm.dismissWithResult = ::dismissWithResult + + vm.logout() + + assertThat(linkAccountManager.callCount).isEqualTo(1) + assertThat(result).isEqualTo(LinkActivityResult.Canceled(LinkActivityResult.Canceled.Reason.LoggedOut)) + } + + @Test + fun `vm dismisses with error log on failed log out`() = runTest(dispatcher) { + val error = Throwable("oops") + val linkAccountManager = FakeLinkAccountManager() + linkAccountManager.setLinkAccount(TestFactory.LINK_ACCOUNT) + linkAccountManager.logOutResult = Result.failure(error) + + val logger = FakeLogger() + + var result: LinkActivityResult? = null + fun dismissWithResult(r: LinkActivityResult) { + result = r + } + + val vm = createViewModel(linkAccountManager = linkAccountManager, logger = logger) + vm.dismissWithResult = ::dismissWithResult + + vm.logout() + + assertThat(result).isEqualTo(LinkActivityResult.Canceled(LinkActivityResult.Canceled.Reason.LoggedOut)) + assertThat(logger.errorLogs).containsExactly("failed to log out" to error) + } + + @Test + fun `navigate should call NavController navigate with correct route, should clear stack when clearStack is true`() { + val navController: NavHostController = mock() + val clearStack = true + val fakeGraphId = 123 + val mockGraph: NavGraph = mock() + `when`(mockGraph.id).thenReturn(fakeGraphId) + `when`(navController.graph).thenReturn(mockGraph) + + val vm = createViewModel() + vm.navController = navController + + vm.navigate(LinkScreen.SignUp, clearStack) + + verify(navController).navigate( + eq(LinkScreen.SignUp.route), + any Unit>() + ) + + val navOptionsLambdaCaptor = argumentCaptor Unit>() + verify(navController).navigate(eq(LinkScreen.SignUp.route), navOptionsLambdaCaptor.capture()) + + val capturedLambda = navOptionsLambdaCaptor.firstValue + val navOptionsBuilder = spy(NavOptionsBuilder()) + capturedLambda.invoke(navOptionsBuilder) + + val popUpToLambdaCaptor = argumentCaptor Unit>() + verify(navOptionsBuilder).popUpTo(eq(fakeGraphId), popUpToLambdaCaptor.capture()) + + val capturedPopUpToLambda = popUpToLambdaCaptor.firstValue + val popUpToBuilder = PopUpToBuilder() + capturedPopUpToLambda.invoke(popUpToBuilder) + + assertThat(popUpToBuilder.inclusive).isTrue() + } + + @Test + fun `navigate should call NavController navigate with correct route, should not clear when clearStack is false`() { + val navController: NavHostController = mock() + + val vm = createViewModel() + vm.navController = navController + + vm.navigate(LinkScreen.Wallet, clearStack = false) + + val navOptionsLambdaCaptor = argumentCaptor Unit>() + verify(navController).navigate(eq(LinkScreen.Wallet.route), navOptionsLambdaCaptor.capture()) + + val capturedLambda = navOptionsLambdaCaptor.firstValue + val navOptionsBuilder = spy(NavOptionsBuilder()) + capturedLambda.invoke(navOptionsBuilder) + + verify(navOptionsBuilder, never()).popUpTo(any(), any()) + } + + private fun createViewModel( + linkAccountManager: LinkAccountManager = FakeLinkAccountManager(), + logger: Logger = FakeLogger() + ): LinkActivityViewModel { + return LinkActivityViewModel( + linkAccountManager = linkAccountManager, + logger = logger, + activityRetainedComponent = mock() + ) + } + private fun creationExtras(): CreationExtras { val mockOwner = mock() val mockViewModelStoreOwner = mock() diff --git a/paymentsheet/src/test/java/com/stripe/android/link/ui/LinkLogoutSheetScreenshotTest.kt b/paymentsheet/src/test/java/com/stripe/android/link/ui/LinkLogoutSheetScreenshotTest.kt index d573e0d70be..9da09b0dcf1 100644 --- a/paymentsheet/src/test/java/com/stripe/android/link/ui/LinkLogoutSheetScreenshotTest.kt +++ b/paymentsheet/src/test/java/com/stripe/android/link/ui/LinkLogoutSheetScreenshotTest.kt @@ -40,4 +40,4 @@ internal class LinkLogoutSheetScreenshotTest { ) } } -} \ No newline at end of file +}