BIT-471 Show confirm log out dialog (#173)

This commit is contained in:
Andrew Haisting
2023-10-30 10:35:19 -05:00
committed by GitHub
parent ebb8632152
commit 6fb630ba29
4 changed files with 236 additions and 6 deletions

View File

@@ -1,14 +1,22 @@
package com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity
import androidx.compose.ui.test.assert
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.hasAnyAncestor
import androidx.compose.ui.test.isDialog
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
import com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity.AccountSecurityAction.ConfirmLogoutClick
import com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity.AccountSecurityAction.DismissDialog
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.update
import org.junit.Assert.assertTrue
import org.junit.Test
@@ -17,6 +25,7 @@ class AccountSecurityScreenTest : BaseComposeTest() {
@Test
fun `on Log out click should send LogoutClick`() {
val viewModel: AccountSecurityViewModel = mockk {
every { stateFlow } returns MutableStateFlow(DEFAULT_STATE)
every { eventFlow } returns emptyFlow()
every { trySendAction(AccountSecurityAction.LogoutClick) } returns Unit
}
@@ -33,6 +42,7 @@ class AccountSecurityScreenTest : BaseComposeTest() {
@Test
fun `on back click should send BackClick`() {
val viewModel: AccountSecurityViewModel = mockk {
every { stateFlow } returns MutableStateFlow(DEFAULT_STATE)
every { eventFlow } returns emptyFlow()
every { trySendAction(AccountSecurityAction.BackClick) } returns Unit
}
@@ -50,6 +60,7 @@ class AccountSecurityScreenTest : BaseComposeTest() {
fun `on NavigateAccountSecurity should call onNavigateToAccountSecurity`() {
var haveCalledNavigateBack = false
val viewModel = mockk<AccountSecurityViewModel> {
every { stateFlow } returns MutableStateFlow(DEFAULT_STATE)
every { eventFlow } returns flowOf(AccountSecurityEvent.NavigateBack)
}
composeTestRule.setContent {
@@ -60,4 +71,86 @@ class AccountSecurityScreenTest : BaseComposeTest() {
}
assertTrue(haveCalledNavigateBack)
}
@Test
fun `confirm dialog be shown or hidden according to the state`() {
val mutableStateFlow = MutableStateFlow(DEFAULT_STATE)
val viewModel = mockk<AccountSecurityViewModel> {
every { stateFlow } returns mutableStateFlow
every { eventFlow } returns emptyFlow()
every { trySendAction(ConfirmLogoutClick) } returns Unit
}
composeTestRule.setContent {
AccountSecurityScreen(
viewModel = viewModel,
onNavigateBack = { },
)
}
composeTestRule.onNode(isDialog()).assertDoesNotExist()
mutableStateFlow.update { it.copy(shouldShowConfirmLogoutDialog = true) }
composeTestRule
.onNodeWithText("Yes")
.assert(hasAnyAncestor(isDialog()))
.assertIsDisplayed()
composeTestRule
.onNodeWithText("Cancel")
.assert(hasAnyAncestor(isDialog()))
.assertIsDisplayed()
composeTestRule
.onNodeWithText("Are you sure you want to log out?")
.assert(hasAnyAncestor(isDialog()))
.assertIsDisplayed()
}
@Test
fun `on confirm logout click should send ConfirmLogoutClick`() {
val viewModel = mockk<AccountSecurityViewModel> {
every { stateFlow } returns MutableStateFlow(
DEFAULT_STATE.copy(shouldShowConfirmLogoutDialog = true),
)
every { eventFlow } returns emptyFlow()
every { trySendAction(ConfirmLogoutClick) } returns Unit
}
composeTestRule.setContent {
AccountSecurityScreen(
viewModel = viewModel,
onNavigateBack = { },
)
}
composeTestRule
.onNodeWithText("Yes")
.assert(hasAnyAncestor(isDialog()))
.performClick()
verify { viewModel.trySendAction(ConfirmLogoutClick) }
}
@Test
fun `on cancel click should send DismissDialog`() {
val viewModel = mockk<AccountSecurityViewModel> {
every { stateFlow } returns MutableStateFlow(
DEFAULT_STATE.copy(shouldShowConfirmLogoutDialog = true),
)
every { eventFlow } returns emptyFlow()
every { trySendAction(DismissDialog) } returns Unit
}
composeTestRule.setContent {
AccountSecurityScreen(
viewModel = viewModel,
onNavigateBack = { },
)
}
composeTestRule
.onNodeWithText("Cancel")
.assert(hasAnyAncestor(isDialog()))
.performClick()
verify { viewModel.trySendAction(DismissDialog) }
}
companion object {
private val DEFAULT_STATE = AccountSecurityState(
shouldShowConfirmLogoutDialog = false,
)
}
}

View File

@@ -1,5 +1,6 @@
package com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity
import androidx.lifecycle.SavedStateHandle
import app.cash.turbine.test
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
@@ -12,9 +13,19 @@ import org.junit.jupiter.api.Test
class AccountSecurityViewModelTest : BaseViewModelTest() {
@Test
fun `initial state should be correct`() {
val viewModel = AccountSecurityViewModel(
savedStateHandle = SavedStateHandle(),
authRepository = mockk(),
)
assertEquals(DEFAULT_STATE, viewModel.stateFlow.value)
}
@Test
fun `on BackClick should emit NavigateBack`() = runTest {
val viewModel = AccountSecurityViewModel(
savedStateHandle = SavedStateHandle(),
authRepository = mockk(),
)
viewModel.eventFlow.test {
@@ -24,14 +35,63 @@ class AccountSecurityViewModelTest : BaseViewModelTest() {
}
@Test
fun `on LogoutClick should call logout`() = runTest {
fun `on LogoutClick should show confirm log out dialog`() = runTest {
val viewModel = AccountSecurityViewModel(
savedStateHandle = SavedStateHandle(),
authRepository = mockk(),
)
viewModel.trySendAction(AccountSecurityAction.LogoutClick)
viewModel.stateFlow.test {
assertEquals(
DEFAULT_STATE.copy(
shouldShowConfirmLogoutDialog = true,
),
awaitItem(),
)
}
}
@Test
fun `on ConfirmLogoutClick should call logout and hide confirm dialog`() = runTest {
val authRepository: AuthRepository = mockk {
every { logout() } returns Unit
}
val viewModel = AccountSecurityViewModel(
savedStateHandle = SavedStateHandle(),
authRepository = authRepository,
)
viewModel.trySendAction(AccountSecurityAction.LogoutClick)
viewModel.trySendAction(AccountSecurityAction.ConfirmLogoutClick)
viewModel.stateFlow.test {
assertEquals(
DEFAULT_STATE.copy(
shouldShowConfirmLogoutDialog = false,
),
awaitItem(),
)
}
verify { authRepository.logout() }
}
@Test
fun `on DismissDialog should hide dialog`() = runTest {
val viewModel = AccountSecurityViewModel(
savedStateHandle = SavedStateHandle(),
authRepository = mockk(),
)
viewModel.trySendAction(AccountSecurityAction.DismissDialog)
viewModel.stateFlow.test {
assertEquals(
DEFAULT_STATE.copy(
shouldShowConfirmLogoutDialog = false,
),
awaitItem(),
)
}
}
companion object {
private val DEFAULT_STATE = AccountSecurityState(
shouldShowConfirmLogoutDialog = false,
)
}
}