BIT-1135: Add confirmation dialog to lock-or-logout dialog (#364)

This commit is contained in:
Brian Yencho
2023-12-11 14:57:51 -06:00
committed by GitHub
parent ec78532320
commit a42dbf6663
9 changed files with 194 additions and 19 deletions

View File

@@ -23,6 +23,7 @@ import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
import com.x8bit.bitwarden.ui.platform.base.util.asText
import com.x8bit.bitwarden.ui.platform.components.model.AccountSummary
import com.x8bit.bitwarden.ui.util.assertLockOrLogoutDialogIsDisplayed
import com.x8bit.bitwarden.ui.util.assertLogoutConfirmationDialogIsDisplayed
import com.x8bit.bitwarden.ui.util.assertNoDialogExists
import com.x8bit.bitwarden.ui.util.assertSwitcherIsDisplayed
import com.x8bit.bitwarden.ui.util.assertSwitcherIsNotDisplayed
@@ -31,6 +32,7 @@ import com.x8bit.bitwarden.ui.util.performAccountIconClick
import com.x8bit.bitwarden.ui.util.performAccountLongClick
import com.x8bit.bitwarden.ui.util.performLockAccountClick
import com.x8bit.bitwarden.ui.util.performLogoutAccountClick
import com.x8bit.bitwarden.ui.util.performLogoutAccountConfirmationClick
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
@@ -158,7 +160,7 @@ class LandingScreenTest : BaseComposeTest() {
@Suppress("MaxLineLength")
@Test
fun `logout button click in the lock-or-logout dialog should send LogoutAccountClick action and close the dialog`() {
fun `logout button click in the lock-or-logout dialog should show the logout confirmation dialog and hide the lock-or-logout dialog`() {
// Show the lock-or-logout dialog
val accountSummaries = listOf(ACTIVE_ACCOUNT_SUMMARY)
mutableStateFlow.update {
@@ -169,6 +171,25 @@ class LandingScreenTest : BaseComposeTest() {
composeTestRule.performLogoutAccountClick()
composeTestRule.assertLogoutConfirmationDialogIsDisplayed(
accountSummary = ACTIVE_ACCOUNT_SUMMARY,
)
}
@Suppress("MaxLineLength")
@Test
fun `logout button click in the logout confirmation dialog should send LogoutAccountClick action and close the dialog`() {
// Show the logout confirmation dialog
val accountSummaries = listOf(ACTIVE_ACCOUNT_SUMMARY)
mutableStateFlow.update {
it.copy(accountSummaries = accountSummaries)
}
composeTestRule.performAccountIconClick()
composeTestRule.performAccountLongClick(ACTIVE_ACCOUNT_SUMMARY)
composeTestRule.performLogoutAccountClick()
composeTestRule.performLogoutAccountConfirmationClick()
verify { viewModel.trySendAction(LandingAction.LogoutAccountClick(ACTIVE_ACCOUNT_SUMMARY)) }
composeTestRule.assertNoDialogExists()
}

View File

@@ -19,6 +19,7 @@ import com.x8bit.bitwarden.ui.platform.components.BasicDialogState
import com.x8bit.bitwarden.ui.platform.components.LoadingDialogState
import com.x8bit.bitwarden.ui.platform.components.model.AccountSummary
import com.x8bit.bitwarden.ui.util.assertLockOrLogoutDialogIsDisplayed
import com.x8bit.bitwarden.ui.util.assertLogoutConfirmationDialogIsDisplayed
import com.x8bit.bitwarden.ui.util.assertNoDialogExists
import com.x8bit.bitwarden.ui.util.assertSwitcherIsDisplayed
import com.x8bit.bitwarden.ui.util.assertSwitcherIsNotDisplayed
@@ -27,6 +28,7 @@ import com.x8bit.bitwarden.ui.util.performAccountIconClick
import com.x8bit.bitwarden.ui.util.performAccountLongClick
import com.x8bit.bitwarden.ui.util.performLockAccountClick
import com.x8bit.bitwarden.ui.util.performLogoutAccountClick
import com.x8bit.bitwarden.ui.util.performLogoutAccountConfirmationClick
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
@@ -147,7 +149,7 @@ class LoginScreenTest : BaseComposeTest() {
@Suppress("MaxLineLength")
@Test
fun `logout button click in the lock-or-logout dialog should send LogoutAccountClick action and close the dialog`() {
fun `logout button click in the lock-or-logout dialog should show the logout confirmation dialog and hide the lock-or-logout dialog`() {
// Show the lock-or-logout dialog
val accountSummaries = listOf(ACTIVE_ACCOUNT_SUMMARY)
mutableStateFlow.update {
@@ -158,6 +160,25 @@ class LoginScreenTest : BaseComposeTest() {
composeTestRule.performLogoutAccountClick()
composeTestRule.assertLogoutConfirmationDialogIsDisplayed(
accountSummary = ACTIVE_ACCOUNT_SUMMARY,
)
}
@Suppress("MaxLineLength")
@Test
fun `logout button click in the logout confirmation dialog should send LogoutAccountClick action and close the dialog`() {
// Show the logout confirmation dialog
val accountSummaries = listOf(ACTIVE_ACCOUNT_SUMMARY)
mutableStateFlow.update {
it.copy(accountSummaries = accountSummaries)
}
composeTestRule.performAccountIconClick()
composeTestRule.performAccountLongClick(ACTIVE_ACCOUNT_SUMMARY)
composeTestRule.performLogoutAccountClick()
composeTestRule.performLogoutAccountConfirmationClick()
verify { viewModel.trySendAction(LoginAction.LogoutAccountClick(ACTIVE_ACCOUNT_SUMMARY)) }
composeTestRule.assertNoDialogExists()
}

View File

@@ -16,6 +16,7 @@ import androidx.compose.ui.test.performTextInput
import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
import com.x8bit.bitwarden.ui.platform.components.model.AccountSummary
import com.x8bit.bitwarden.ui.util.assertLockOrLogoutDialogIsDisplayed
import com.x8bit.bitwarden.ui.util.assertLogoutConfirmationDialogIsDisplayed
import com.x8bit.bitwarden.ui.util.assertNoDialogExists
import com.x8bit.bitwarden.ui.util.assertSwitcherIsDisplayed
import com.x8bit.bitwarden.ui.util.assertSwitcherIsNotDisplayed
@@ -25,6 +26,7 @@ import com.x8bit.bitwarden.ui.util.performAccountLongClick
import com.x8bit.bitwarden.ui.util.performAddAccountClick
import com.x8bit.bitwarden.ui.util.performLockAccountClick
import com.x8bit.bitwarden.ui.util.performLogoutAccountClick
import com.x8bit.bitwarden.ui.util.performLogoutAccountConfirmationClick
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
@@ -130,13 +132,28 @@ class VaultUnlockScreenTest : BaseComposeTest() {
@Suppress("MaxLineLength")
@Test
fun `logout button click in the lock-or-logout dialog should send LogoutAccountClick action and close the dialog`() {
fun `logout button click in the lock-or-logout dialog should show the logout confirmation dialog and hide the lock-or-logout dialog`() {
// Show the lock-or-logout dialog
composeTestRule.performAccountIconClick()
composeTestRule.performAccountLongClick(ACTIVE_ACCOUNT_SUMMARY)
composeTestRule.performLogoutAccountClick()
composeTestRule.assertLogoutConfirmationDialogIsDisplayed(
accountSummary = ACTIVE_ACCOUNT_SUMMARY,
)
}
@Suppress("MaxLineLength")
@Test
fun `logout button click in the logout confirmation dialog should send LogoutAccountClick action and close the dialog`() {
// Show the logout confirmation dialog
composeTestRule.performAccountIconClick()
composeTestRule.performAccountLongClick(ACTIVE_ACCOUNT_SUMMARY)
composeTestRule.performLogoutAccountClick()
composeTestRule.performLogoutAccountConfirmationClick()
verify {
viewModel.trySendAction(VaultUnlockAction.LogoutAccountClick(ACTIVE_ACCOUNT_SUMMARY))
}

View File

@@ -78,6 +78,35 @@ fun ComposeContentTestRule.assertLockOrLogoutDialogIsDisplayed(
.assertIsDisplayed()
}
/**
* Asserts the logoung confirmation dialog is currently displayed with information from the given
* [accountSummary].
*/
fun ComposeContentTestRule.assertLogoutConfirmationDialogIsDisplayed(
accountSummary: AccountSummary,
) {
this.waitForIdle()
this
.onNode(isDialog())
.assertIsDisplayed()
this
.onAllNodesWithText("Log out")
.filterToOne(hasAnyAncestor(isDialog()))
.assertIsDisplayed()
this
.onAllNodesWithText("Are you sure you want to log out?", substring = true)
.filterToOne(hasAnyAncestor(isDialog()))
.assertIsDisplayed()
this
.onAllNodesWithText(accountSummary.email, substring = true)
.filterToOne(hasAnyAncestor(isDialog()))
.assertIsDisplayed()
this
.onAllNodesWithText(accountSummary.environmentLabel, substring = true)
.filterToOne(hasAnyAncestor(isDialog()))
.assertIsDisplayed()
}
/**
* Clicks on the given [accountSummary] in the account switcher.
*/
@@ -118,6 +147,16 @@ fun ComposeContentTestRule.performLogoutAccountClick() {
.performClick()
}
/**
* Clicks the "Yes" button in the logout confirmation dialog to confirm the logout.
*/
fun ComposeContentTestRule.performLogoutAccountConfirmationClick() {
this
.onAllNodesWithText("Yes")
.filterToOne(hasAnyAncestor(isDialog()))
.performClick()
}
/**
* Opens the account switcher.
*

View File

@@ -13,6 +13,7 @@ import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
import com.x8bit.bitwarden.ui.platform.base.util.asText
import com.x8bit.bitwarden.ui.platform.components.model.AccountSummary
import com.x8bit.bitwarden.ui.util.assertLockOrLogoutDialogIsDisplayed
import com.x8bit.bitwarden.ui.util.assertLogoutConfirmationDialogIsDisplayed
import com.x8bit.bitwarden.ui.util.assertNoDialogExists
import com.x8bit.bitwarden.ui.util.assertSwitcherIsDisplayed
import com.x8bit.bitwarden.ui.util.assertSwitcherIsNotDisplayed
@@ -22,6 +23,7 @@ import com.x8bit.bitwarden.ui.util.performAccountLongClick
import com.x8bit.bitwarden.ui.util.performAddAccountClick
import com.x8bit.bitwarden.ui.util.performLockAccountClick
import com.x8bit.bitwarden.ui.util.performLogoutAccountClick
import com.x8bit.bitwarden.ui.util.performLogoutAccountConfirmationClick
import com.x8bit.bitwarden.ui.vault.model.VaultItemListingType
import io.mockk.every
import io.mockk.mockk
@@ -145,13 +147,28 @@ class VaultScreenTest : BaseComposeTest() {
@Suppress("MaxLineLength")
@Test
fun `logout button click in the lock-or-logout dialog should send LogoutAccountClick action and close the dialog`() {
fun `logout button click in the lock-or-logout dialog should show the logout confirmation dialog and hide the lock-or-logout dialog`() {
// Show the lock-or-logout dialog
composeTestRule.performAccountIconClick()
composeTestRule.performAccountLongClick(ACTIVE_ACCOUNT_SUMMARY)
composeTestRule.performLogoutAccountClick()
composeTestRule.assertLogoutConfirmationDialogIsDisplayed(
accountSummary = ACTIVE_ACCOUNT_SUMMARY,
)
}
@Suppress("MaxLineLength")
@Test
fun `logout button click in the logout confirmation dialog should send LogoutAccountClick action and close the dialog`() {
// Show the logout confirmation dialog
composeTestRule.performAccountIconClick()
composeTestRule.performAccountLongClick(ACTIVE_ACCOUNT_SUMMARY)
composeTestRule.performLogoutAccountClick()
composeTestRule.performLogoutAccountConfirmationClick()
verify { viewModel.trySendAction(VaultAction.LogoutAccountClick(ACTIVE_ACCOUNT_SUMMARY)) }
composeTestRule.assertNoDialogExists()
}

View File

@@ -151,6 +151,7 @@ class VaultViewModelTest : BaseViewModelTest() {
val accountUserId = "userId"
val accountSummary = mockk<AccountSummary> {
every { userId } returns accountUserId
every { isActive } returns false
}
val viewModel = createViewModel()
@@ -159,16 +160,45 @@ class VaultViewModelTest : BaseViewModelTest() {
verify { vaultRepository.lockVaultIfNecessary(userId = accountUserId) }
}
@Suppress("MaxLineLength")
@Test
fun `on LogoutAccountClick should call logout for the given account`() {
fun `on LogoutAccountClick for an active account should call logout for the given account and set isSwitchingAccounts to true`() {
val accountUserId = "userId"
val accountSummary = mockk<AccountSummary> {
every { userId } returns accountUserId
every { isActive } returns true
}
val viewModel = createViewModel()
viewModel.trySendAction(VaultAction.LogoutAccountClick(accountSummary))
assertEquals(
DEFAULT_STATE.copy(
isSwitchingAccounts = true,
),
viewModel.stateFlow.value,
)
verify { authRepository.logout(userId = accountUserId) }
}
@Suppress("MaxLineLength")
@Test
fun `on LogoutAccountClick for an inactive account should call logout for the given account and set isSwitchingAccounts to false`() {
val accountUserId = "userId"
val accountSummary = mockk<AccountSummary> {
every { userId } returns accountUserId
every { isActive } returns false
}
val viewModel = createViewModel()
viewModel.trySendAction(VaultAction.LogoutAccountClick(accountSummary))
assertEquals(
DEFAULT_STATE.copy(
isSwitchingAccounts = false,
),
viewModel.stateFlow.value,
)
verify { authRepository.logout(userId = accountUserId) }
}