BIT-1017: Add "Account already added" dialog to Landing Screen (#344)

This commit is contained in:
Brian Yencho
2023-12-07 09:14:03 -06:00
committed by GitHub
parent 89dfa8dbb7
commit 08900b6838
4 changed files with 226 additions and 1 deletions

View File

@@ -273,6 +273,80 @@ class LandingScreenTest : BaseComposeTest() {
.performClick()
verify { viewModel.trySendAction(LandingAction.DialogDismiss) }
}
@Test
fun `account already added dialog should be shown or hidden according to the state`() {
composeTestRule.onNode(isDialog()).assertDoesNotExist()
mutableStateFlow.update {
it.copy(
dialog = LandingState.DialogState.AccountAlreadyAdded(
accountSummary = mockk(),
),
)
}
composeTestRule.onNode(isDialog()).assertIsDisplayed()
composeTestRule
.onNodeWithText("Account already added")
.assert(hasAnyAncestor(isDialog()))
.assertIsDisplayed()
composeTestRule
.onNodeWithText("Would you like to switch to it now?")
.assert(hasAnyAncestor(isDialog()))
.assertIsDisplayed()
composeTestRule
.onNodeWithText("Yes")
.assert(hasAnyAncestor(isDialog()))
.assertIsDisplayed()
composeTestRule
.onNodeWithText("Cancel")
.assert(hasAnyAncestor(isDialog()))
.assertIsDisplayed()
}
@Test
fun `account already added dialog Cancel click should send DialogDismiss action`() {
mutableStateFlow.update {
it.copy(
dialog = LandingState.DialogState.AccountAlreadyAdded(
accountSummary = mockk(),
),
)
}
composeTestRule
.onNodeWithText("Cancel")
.assert(hasAnyAncestor(isDialog()))
.performClick()
verify { viewModel.trySendAction(LandingAction.DialogDismiss) }
}
@Suppress("MaxLineLength")
@Test
fun `account already added dialog Yes click should send ConfirmSwitchToMatchingAccountClick action`() {
val accountSummary = mockk<AccountSummary>()
mutableStateFlow.update {
it.copy(
dialog = LandingState.DialogState.AccountAlreadyAdded(
accountSummary = accountSummary,
),
)
}
composeTestRule
.onNodeWithText("Yes")
.assert(hasAnyAncestor(isDialog()))
.performClick()
verify {
viewModel.trySendAction(
LandingAction.ConfirmSwitchToMatchingAccountClick(account = accountSummary),
)
}
}
}
private val ACTIVE_ACCOUNT_SUMMARY = AccountSummary(

View File

@@ -3,14 +3,18 @@ package com.x8bit.bitwarden.ui.auth.feature.landing
import androidx.lifecycle.SavedStateHandle
import app.cash.turbine.test
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
import com.x8bit.bitwarden.data.auth.repository.model.UserState
import com.x8bit.bitwarden.data.platform.repository.model.Environment
import com.x8bit.bitwarden.data.platform.repository.util.FakeEnvironmentRepository
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
import com.x8bit.bitwarden.ui.platform.base.util.asText
import com.x8bit.bitwarden.ui.platform.components.model.AccountSummary
import com.x8bit.bitwarden.ui.vault.feature.vault.util.toAccountSummaries
import com.x8bit.bitwarden.ui.vault.feature.vault.util.toAccountSummary
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Assertions.assertEquals
@@ -18,6 +22,7 @@ import org.junit.jupiter.api.Test
class LandingViewModelTest : BaseViewModelTest() {
private val authRepository: AuthRepository = mockk(relaxed = true)
private val fakeEnvironmentRepository = FakeEnvironmentRepository()
@Test
@@ -82,6 +87,32 @@ class LandingViewModelTest : BaseViewModelTest() {
}
}
@Test
fun `SwitchAccountClick should call switchAccount for the given account`() {
val matchingAccountUserId = "matchingAccountUserId"
val accountSummary = mockk<AccountSummary> {
every { userId } returns matchingAccountUserId
}
val viewModel = createViewModel()
viewModel.trySendAction(LandingAction.SwitchAccountClick(accountSummary))
verify { authRepository.switchAccount(userId = matchingAccountUserId) }
}
@Test
fun `ConfirmSwitchToMatchingAccountClick should call switchAccount for the given account`() {
val matchingAccountUserId = "matchingAccountUserId"
val accountSummary = mockk<AccountSummary> {
every { userId } returns matchingAccountUserId
}
val viewModel = createViewModel()
viewModel.trySendAction(LandingAction.ConfirmSwitchToMatchingAccountClick(accountSummary))
verify { authRepository.switchAccount(userId = matchingAccountUserId) }
}
@Test
fun `ContinueButtonClick with valid email should emit NavigateToLogin`() = runTest {
val validEmail = "email@bitwarden.com"
@@ -120,6 +151,51 @@ class LandingViewModelTest : BaseViewModelTest() {
}
}
@Suppress("MaxLineLength")
@Test
fun `ContinueButtonClick with an email input matching an existing account should show the account already added dialog`() {
val rememberedEmail = "active@bitwarden.com"
val activeAccount = UserState.Account(
userId = "activeUserId",
name = "name",
email = rememberedEmail,
avatarColorHex = "avatarColorHex",
isPremium = true,
isVaultUnlocked = true,
)
val userState = UserState(
activeUserId = "activeUserId",
accounts = listOf(activeAccount),
)
val viewModel = createViewModel(
rememberedEmail = rememberedEmail,
userState = userState,
)
val activeAccountSummary = activeAccount.toAccountSummary(isActive = true)
val accountSummaries = userState.toAccountSummaries()
val initialState = DEFAULT_STATE.copy(
emailInput = rememberedEmail,
isContinueButtonEnabled = true,
isRememberMeEnabled = true,
accountSummaries = accountSummaries,
)
assertEquals(
initialState,
viewModel.stateFlow.value,
)
viewModel.trySendAction(LandingAction.ContinueButtonClick)
assertEquals(
initialState.copy(
dialog = LandingState.DialogState.AccountAlreadyAdded(
accountSummary = activeAccountSummary,
),
),
viewModel.stateFlow.value,
)
}
@Test
fun `CreateAccountClick should emit NavigateToCreateAccount`() = runTest {
val viewModel = createViewModel()
@@ -232,7 +308,7 @@ class LandingViewModelTest : BaseViewModelTest() {
initialState = mapOf("state" to initialState),
),
): LandingViewModel = LandingViewModel(
authRepository = mockk(relaxed = true) {
authRepository = authRepository.apply {
every { rememberedEmailAddress } returns rememberedEmail
every { userStateFlow } returns MutableStateFlow(userState)
},