mirror of
https://github.com/bitwarden/android.git
synced 2026-06-01 18:26:31 -05:00
BIT-852: Add account switcher UI (#235)
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package com.x8bit.bitwarden.ui.vault.feature.vault
|
||||
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.assertTextEquals
|
||||
import androidx.compose.ui.test.filterToOne
|
||||
import androidx.compose.ui.test.hasClickAction
|
||||
@@ -9,14 +10,17 @@ import androidx.compose.ui.test.onNodeWithContentDescription
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.compose.ui.test.performScrollToNode
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.AccountSummary
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
@@ -24,6 +28,7 @@ import org.junit.Test
|
||||
class VaultScreenTest : BaseComposeTest() {
|
||||
|
||||
private var onNavigateToVaultAddItemScreenCalled = false
|
||||
private var onDimBottomNavBarRequestCalled = false
|
||||
|
||||
private val mutableEventFlow = MutableSharedFlow<VaultEvent>(
|
||||
extraBufferCapacity = Int.MAX_VALUE,
|
||||
@@ -40,10 +45,49 @@ class VaultScreenTest : BaseComposeTest() {
|
||||
VaultScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateToVaultAddItemScreen = { onNavigateToVaultAddItemScreenCalled = true },
|
||||
onDimBottomNavBarRequest = { onDimBottomNavBarRequestCalled = true },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `account icon click should show the account switcher and trigger the nav bar dim request`() {
|
||||
composeTestRule.onNodeWithText("active@bitwarden.com").assertDoesNotExist()
|
||||
composeTestRule.onNodeWithText("locked@bitwarden.com").assertDoesNotExist()
|
||||
composeTestRule.onNodeWithText("Add account").assertDoesNotExist()
|
||||
assertFalse(onDimBottomNavBarRequestCalled)
|
||||
|
||||
composeTestRule.onNodeWithText("AU").performClick()
|
||||
|
||||
composeTestRule.onNodeWithText("active@bitwarden.com").assertIsDisplayed()
|
||||
composeTestRule.onNodeWithText("locked@bitwarden.com").assertIsDisplayed()
|
||||
composeTestRule.onNodeWithText("Add account").assertIsDisplayed()
|
||||
assertTrue(onDimBottomNavBarRequestCalled)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `account click in the account switcher should send AccountSwitchClick and close switcher`() {
|
||||
// Open the Account Switcher
|
||||
composeTestRule.onNodeWithText("AU").performClick()
|
||||
|
||||
composeTestRule.onNodeWithText("locked@bitwarden.com").performClick()
|
||||
verify { viewModel.trySendAction(VaultAction.AccountSwitchClick(LOCKED_ACCOUNT_SUMMARY)) }
|
||||
composeTestRule.onNodeWithText("locked@bitwarden.com").assertDoesNotExist()
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `Add Account click in the account switcher should send AddAccountClick and close switcher`() {
|
||||
// Open the Account Switcher
|
||||
composeTestRule.onNodeWithText("AU").performClick()
|
||||
|
||||
composeTestRule.onNodeWithText("Add account").performClick()
|
||||
verify { viewModel.trySendAction(VaultAction.AddAccountClick) }
|
||||
composeTestRule.onNodeWithText("Add account").assertDoesNotExist()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `search icon click should send SearchIconClick action`() {
|
||||
mutableStateFlow.update { it.copy(viewState = VaultState.ViewState.NoItems) }
|
||||
@@ -357,9 +401,29 @@ class VaultScreenTest : BaseComposeTest() {
|
||||
}
|
||||
}
|
||||
|
||||
private val ACTIVE_ACCOUNT_SUMMARY = AccountSummary(
|
||||
userId = "activeUserId",
|
||||
name = "Active User",
|
||||
email = "active@bitwarden.com",
|
||||
avatarColorHex = "#aa00aa",
|
||||
status = AccountSummary.Status.ACTIVE,
|
||||
)
|
||||
|
||||
private val LOCKED_ACCOUNT_SUMMARY = AccountSummary(
|
||||
userId = "lockedUserId",
|
||||
name = "Locked User",
|
||||
email = "locked@bitwarden.com",
|
||||
avatarColorHex = "#00aaaa",
|
||||
status = AccountSummary.Status.LOCKED,
|
||||
)
|
||||
|
||||
private val DEFAULT_STATE: VaultState = VaultState(
|
||||
avatarColorString = "FF0000FF",
|
||||
initials = "BW",
|
||||
avatarColorString = "#aa00aa",
|
||||
initials = "AU",
|
||||
accountSummaries = persistentListOf(
|
||||
ACTIVE_ACCOUNT_SUMMARY,
|
||||
LOCKED_ACCOUNT_SUMMARY,
|
||||
),
|
||||
viewState = VaultState.ViewState.Loading,
|
||||
)
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.x8bit.bitwarden.ui.vault.feature.vault
|
||||
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import app.cash.turbine.test
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.AccountSummary
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
@@ -27,6 +28,58 @@ class VaultViewModelTest : BaseViewModelTest() {
|
||||
assertEquals(state, viewModel.stateFlow.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on AccountSwitchClick for the active account should do nothing`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
VaultAction.AccountSwitchClick(
|
||||
accountSummary = mockk {
|
||||
every { status } returns AccountSummary.Status.ACTIVE
|
||||
},
|
||||
)
|
||||
expectNoEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on AccountSwitchClick for a locked account emit NavigateToVaultUnlockScreen`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(
|
||||
VaultAction.AccountSwitchClick(
|
||||
accountSummary = mockk {
|
||||
every { status } returns AccountSummary.Status.LOCKED
|
||||
},
|
||||
),
|
||||
)
|
||||
assertEquals(VaultEvent.NavigateToVaultUnlockScreen, awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on AccountSwitchClick for an unlocked account emit ShowToast`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(
|
||||
VaultAction.AccountSwitchClick(
|
||||
accountSummary = mockk {
|
||||
every { status } returns AccountSummary.Status.UNLOCKED
|
||||
},
|
||||
),
|
||||
)
|
||||
assertEquals(VaultEvent.ShowToast("Not yet implemented."), awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on AddAccountClick should emit NavigateToLoginScreen`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(VaultAction.AddAccountClick)
|
||||
assertEquals(VaultEvent.NavigateToLoginScreen, awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `AddItemClick should emit NavigateToAddItemScreen`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
@@ -126,5 +179,6 @@ class VaultViewModelTest : BaseViewModelTest() {
|
||||
private val DEFAULT_STATE: VaultState = VaultState(
|
||||
avatarColorString = "FF0000FF",
|
||||
initials = "BW",
|
||||
accountSummaries = emptyList(),
|
||||
viewState = VaultState.ViewState.Loading,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
package com.x8bit.bitwarden.ui.vault.feature.vault.util
|
||||
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.AccountSummary
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertNull
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class AccountSummaryExtensionsTest {
|
||||
|
||||
@Test
|
||||
fun `initials should return the starting letters of the first two words in the name`() {
|
||||
assertEquals(
|
||||
"FS",
|
||||
mockk<AccountSummary>() {
|
||||
every { name } returns "First Second Third"
|
||||
}
|
||||
.initials,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `iconRes returns a checkmark for active accounts`() {
|
||||
assertEquals(
|
||||
R.drawable.ic_check_mark,
|
||||
mockk<AccountSummary>() {
|
||||
every { status } returns AccountSummary.Status.ACTIVE
|
||||
}
|
||||
.iconRes,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `iconRes returns a locked lock for locked accounts`() {
|
||||
assertEquals(
|
||||
R.drawable.ic_locked,
|
||||
mockk<AccountSummary>() {
|
||||
every { status } returns AccountSummary.Status.LOCKED
|
||||
}
|
||||
.iconRes,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `iconRes returns an unlocked lock for unlocked accounts`() {
|
||||
assertEquals(
|
||||
R.drawable.ic_unlocked,
|
||||
mockk<AccountSummary>() {
|
||||
every { status } returns AccountSummary.Status.UNLOCKED
|
||||
}
|
||||
.iconRes,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `supportingTextResOrNull returns a null for active accounts`() {
|
||||
assertNull(
|
||||
mockk<AccountSummary>() {
|
||||
every { status } returns AccountSummary.Status.ACTIVE
|
||||
}
|
||||
.supportingTextResOrNull,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `supportingTextResOrNull returns Locked locked accounts`() {
|
||||
assertEquals(
|
||||
R.string.account_locked,
|
||||
mockk<AccountSummary>() {
|
||||
every { status } returns AccountSummary.Status.LOCKED
|
||||
}
|
||||
.supportingTextResOrNull,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `supportingTextResOrNull returns Unlocked for unlocked accounts`() {
|
||||
assertEquals(
|
||||
R.string.account_unlocked,
|
||||
mockk<AccountSummary>() {
|
||||
every { status } returns AccountSummary.Status.UNLOCKED
|
||||
}
|
||||
.supportingTextResOrNull,
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user