Skip to content

Commit

Permalink
Fix tests and lint
Browse files Browse the repository at this point in the history
  • Loading branch information
toluo-stripe committed Jan 3, 2025
1 parent 8384f51 commit 08e0af6
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 23 deletions.
Original file line number Diff line number Diff line change
@@ -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)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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].
*
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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<ConsumerSession> {
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<NavOptionsBuilder.() -> Unit>()
)

val navOptionsLambdaCaptor = argumentCaptor<NavOptionsBuilder.() -> Unit>()
verify(navController).navigate(eq(LinkScreen.SignUp.route), navOptionsLambdaCaptor.capture())

val capturedLambda = navOptionsLambdaCaptor.firstValue
val navOptionsBuilder = spy(NavOptionsBuilder())
capturedLambda.invoke(navOptionsBuilder)

val popUpToLambdaCaptor = argumentCaptor<PopUpToBuilder.() -> 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<NavOptionsBuilder.() -> 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<String>(), 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<SavedStateRegistryOwner>()
val mockViewModelStoreOwner = mock<ViewModelStoreOwner>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,4 @@ internal class LinkLogoutSheetScreenshotTest {
)
}
}
}
}

0 comments on commit 08e0af6

Please sign in to comment.