mirror of
https://github.com/bitwarden/android.git
synced 2025-01-30 19:53:47 +03:00
Add support for removing passwords and deleting sends from the list screen (#679)
This commit is contained in:
parent
2c749186e1
commit
3c4b823014
4 changed files with 246 additions and 20 deletions
app/src
main/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting
test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting
|
@ -21,6 +21,8 @@ import androidx.compose.ui.res.stringResource
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import com.x8bit.bitwarden.R
|
import com.x8bit.bitwarden.R
|
||||||
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||||
|
import com.x8bit.bitwarden.ui.platform.components.BasicDialogState
|
||||||
|
import com.x8bit.bitwarden.ui.platform.components.BitwardenBasicDialog
|
||||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenErrorContent
|
import com.x8bit.bitwarden.ui.platform.components.BitwardenErrorContent
|
||||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenLoadingContent
|
import com.x8bit.bitwarden.ui.platform.components.BitwardenLoadingContent
|
||||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenLoadingDialog
|
import com.x8bit.bitwarden.ui.platform.components.BitwardenLoadingDialog
|
||||||
|
@ -90,6 +92,9 @@ fun VaultItemListingScreen(
|
||||||
|
|
||||||
VaultItemListingDialogs(
|
VaultItemListingDialogs(
|
||||||
dialogState = state.dialogState,
|
dialogState = state.dialogState,
|
||||||
|
onDismissRequest = remember(viewModel) {
|
||||||
|
{ viewModel.trySendAction(VaultItemListingsAction.DismissDialogClick) }
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
VaultItemListingScaffold(
|
VaultItemListingScaffold(
|
||||||
|
@ -103,8 +108,17 @@ fun VaultItemListingScreen(
|
||||||
@Composable
|
@Composable
|
||||||
private fun VaultItemListingDialogs(
|
private fun VaultItemListingDialogs(
|
||||||
dialogState: VaultItemListingState.DialogState?,
|
dialogState: VaultItemListingState.DialogState?,
|
||||||
|
onDismissRequest: () -> Unit,
|
||||||
) {
|
) {
|
||||||
when (dialogState) {
|
when (dialogState) {
|
||||||
|
is VaultItemListingState.DialogState.Error -> BitwardenBasicDialog(
|
||||||
|
visibilityState = BasicDialogState.Shown(
|
||||||
|
title = dialogState.title,
|
||||||
|
message = dialogState.message,
|
||||||
|
),
|
||||||
|
onDismissRequest = onDismissRequest,
|
||||||
|
)
|
||||||
|
|
||||||
is VaultItemListingState.DialogState.Loading -> BitwardenLoadingDialog(
|
is VaultItemListingState.DialogState.Loading -> BitwardenLoadingDialog(
|
||||||
visibilityState = LoadingDialogState.Shown(dialogState.message),
|
visibilityState = LoadingDialogState.Shown(dialogState.message),
|
||||||
)
|
)
|
||||||
|
|
|
@ -11,6 +11,8 @@ import com.x8bit.bitwarden.data.platform.repository.model.DataState
|
||||||
import com.x8bit.bitwarden.data.platform.repository.util.baseIconUrl
|
import com.x8bit.bitwarden.data.platform.repository.util.baseIconUrl
|
||||||
import com.x8bit.bitwarden.data.platform.repository.util.baseWebSendUrl
|
import com.x8bit.bitwarden.data.platform.repository.util.baseWebSendUrl
|
||||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||||
|
import com.x8bit.bitwarden.data.vault.repository.model.DeleteSendResult
|
||||||
|
import com.x8bit.bitwarden.data.vault.repository.model.RemovePasswordSendResult
|
||||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
||||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
||||||
import com.x8bit.bitwarden.ui.platform.base.util.Text
|
import com.x8bit.bitwarden.ui.platform.base.util.Text
|
||||||
|
@ -28,6 +30,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import java.time.Clock
|
import java.time.Clock
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -73,6 +76,7 @@ class VaultItemListingViewModel @Inject constructor(
|
||||||
|
|
||||||
override fun handleAction(action: VaultItemListingsAction) {
|
override fun handleAction(action: VaultItemListingsAction) {
|
||||||
when (action) {
|
when (action) {
|
||||||
|
is VaultItemListingsAction.DismissDialogClick -> handleDismissDialogClick()
|
||||||
is VaultItemListingsAction.BackClick -> handleBackClick()
|
is VaultItemListingsAction.BackClick -> handleBackClick()
|
||||||
is VaultItemListingsAction.LockClick -> handleLockClick()
|
is VaultItemListingsAction.LockClick -> handleLockClick()
|
||||||
is VaultItemListingsAction.SyncClick -> handleSyncClick()
|
is VaultItemListingsAction.SyncClick -> handleSyncClick()
|
||||||
|
@ -87,10 +91,7 @@ class VaultItemListingViewModel @Inject constructor(
|
||||||
handleRemoveSendPasswordClick(action)
|
handleRemoveSendPasswordClick(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
is VaultItemListingsAction.Internal.VaultDataReceive -> handleVaultDataReceive(action)
|
is VaultItemListingsAction.Internal -> handleInternalAction(action)
|
||||||
is VaultItemListingsAction.Internal.IconLoadingSettingReceive -> {
|
|
||||||
handleIconsSettingReceived(action)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,8 +105,17 @@ class VaultItemListingViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleDeleteSendClick(action: VaultItemListingsAction.DeleteSendClick) {
|
private fun handleDeleteSendClick(action: VaultItemListingsAction.DeleteSendClick) {
|
||||||
// TODO: Implement deletion (BIT-1411)
|
mutableStateFlow.update {
|
||||||
sendEvent(VaultItemListingEvent.ShowToast("Not yet implemented".asText()))
|
it.copy(
|
||||||
|
dialogState = VaultItemListingState.DialogState.Loading(
|
||||||
|
message = R.string.deleting.asText(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
viewModelScope.launch {
|
||||||
|
val result = vaultRepository.deleteSend(action.sendId)
|
||||||
|
sendAction(VaultItemListingsAction.Internal.DeleteSendResultReceive(result))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleShareSendUrlClick(action: VaultItemListingsAction.ShareSendUrlClick) {
|
private fun handleShareSendUrlClick(action: VaultItemListingsAction.ShareSendUrlClick) {
|
||||||
|
@ -115,8 +125,17 @@ class VaultItemListingViewModel @Inject constructor(
|
||||||
private fun handleRemoveSendPasswordClick(
|
private fun handleRemoveSendPasswordClick(
|
||||||
action: VaultItemListingsAction.RemoveSendPasswordClick,
|
action: VaultItemListingsAction.RemoveSendPasswordClick,
|
||||||
) {
|
) {
|
||||||
// TODO: Implement password removal (BIT-1411)
|
mutableStateFlow.update {
|
||||||
sendEvent(VaultItemListingEvent.ShowToast("Not yet implemented".asText()))
|
it.copy(
|
||||||
|
dialogState = VaultItemListingState.DialogState.Loading(
|
||||||
|
message = R.string.removing_send_password.asText(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
viewModelScope.launch {
|
||||||
|
val result = vaultRepository.removePasswordSend(action.sendId)
|
||||||
|
sendAction(VaultItemListingsAction.Internal.RemovePasswordSendResultReceive(result))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleAddVaultItemClick() {
|
private fun handleAddVaultItemClick() {
|
||||||
|
@ -145,6 +164,10 @@ class VaultItemListingViewModel @Inject constructor(
|
||||||
sendEvent(event)
|
sendEvent(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleDismissDialogClick() {
|
||||||
|
mutableStateFlow.update { it.copy(dialogState = null) }
|
||||||
|
}
|
||||||
|
|
||||||
private fun handleBackClick() {
|
private fun handleBackClick() {
|
||||||
sendEvent(
|
sendEvent(
|
||||||
event = VaultItemListingEvent.NavigateBack,
|
event = VaultItemListingEvent.NavigateBack,
|
||||||
|
@ -172,6 +195,74 @@ class VaultItemListingViewModel @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleInternalAction(action: VaultItemListingsAction.Internal) {
|
||||||
|
when (action) {
|
||||||
|
is VaultItemListingsAction.Internal.DeleteSendResultReceive -> {
|
||||||
|
handleDeleteSendResultReceive(action)
|
||||||
|
}
|
||||||
|
|
||||||
|
is VaultItemListingsAction.Internal.RemovePasswordSendResultReceive -> {
|
||||||
|
handleRemovePasswordSendResultReceive(action)
|
||||||
|
}
|
||||||
|
|
||||||
|
is VaultItemListingsAction.Internal.VaultDataReceive -> handleVaultDataReceive(action)
|
||||||
|
is VaultItemListingsAction.Internal.IconLoadingSettingReceive -> {
|
||||||
|
handleIconsSettingReceived(action)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleDeleteSendResultReceive(
|
||||||
|
action: VaultItemListingsAction.Internal.DeleteSendResultReceive,
|
||||||
|
) {
|
||||||
|
when (action.result) {
|
||||||
|
DeleteSendResult.Error -> {
|
||||||
|
mutableStateFlow.update {
|
||||||
|
it.copy(
|
||||||
|
dialogState = VaultItemListingState.DialogState.Error(
|
||||||
|
title = R.string.an_error_has_occurred.asText(),
|
||||||
|
message = R.string.generic_error_message.asText(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DeleteSendResult.Success -> {
|
||||||
|
mutableStateFlow.update { it.copy(dialogState = null) }
|
||||||
|
sendEvent(VaultItemListingEvent.ShowToast(R.string.send_deleted.asText()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleRemovePasswordSendResultReceive(
|
||||||
|
action: VaultItemListingsAction.Internal.RemovePasswordSendResultReceive,
|
||||||
|
) {
|
||||||
|
when (val result = action.result) {
|
||||||
|
is RemovePasswordSendResult.Error -> {
|
||||||
|
mutableStateFlow.update {
|
||||||
|
it.copy(
|
||||||
|
dialogState = VaultItemListingState.DialogState.Error(
|
||||||
|
title = R.string.an_error_has_occurred.asText(),
|
||||||
|
message = result
|
||||||
|
.errorMessage
|
||||||
|
?.asText()
|
||||||
|
?: R.string.generic_error_message.asText(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is RemovePasswordSendResult.Success -> {
|
||||||
|
mutableStateFlow.update { it.copy(dialogState = null) }
|
||||||
|
sendEvent(
|
||||||
|
VaultItemListingEvent.ShowToast(
|
||||||
|
text = R.string.send_password_removed.asText(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun handleVaultDataReceive(
|
private fun handleVaultDataReceive(
|
||||||
action: VaultItemListingsAction.Internal.VaultDataReceive,
|
action: VaultItemListingsAction.Internal.VaultDataReceive,
|
||||||
) {
|
) {
|
||||||
|
@ -304,6 +395,15 @@ data class VaultItemListingState(
|
||||||
*/
|
*/
|
||||||
sealed class DialogState : Parcelable {
|
sealed class DialogState : Parcelable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a dismissible dialog with the given error [message].
|
||||||
|
*/
|
||||||
|
@Parcelize
|
||||||
|
data class Error(
|
||||||
|
val title: Text?,
|
||||||
|
val message: Text,
|
||||||
|
) : DialogState()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a loading dialog with the given [message].
|
* Represents a loading dialog with the given [message].
|
||||||
*/
|
*/
|
||||||
|
@ -548,6 +648,11 @@ sealed class VaultItemListingEvent {
|
||||||
*/
|
*/
|
||||||
sealed class VaultItemListingsAction {
|
sealed class VaultItemListingsAction {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Click to dismiss the dialog.
|
||||||
|
*/
|
||||||
|
data object DismissDialogClick : VaultItemListingsAction()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Click the refresh button.
|
* Click the refresh button.
|
||||||
*/
|
*/
|
||||||
|
@ -610,6 +715,18 @@ sealed class VaultItemListingsAction {
|
||||||
*/
|
*/
|
||||||
sealed class Internal : VaultItemListingsAction() {
|
sealed class Internal : VaultItemListingsAction() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates a result for deleting the send has been received.
|
||||||
|
*/
|
||||||
|
data class DeleteSendResultReceive(val result: DeleteSendResult) : Internal()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates a result for removing the password protection from a send has been received.
|
||||||
|
*/
|
||||||
|
data class RemovePasswordSendResultReceive(
|
||||||
|
val result: RemovePasswordSendResult,
|
||||||
|
) : Internal()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates the icon setting was received.
|
* Indicates the icon setting was received.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -605,6 +605,27 @@ class VaultItemListingScreenTest : BaseComposeTest() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `error dialog should be displayed according to state`() {
|
||||||
|
val errorMessage = "Fail"
|
||||||
|
composeTestRule.onNode(isDialog()).assertDoesNotExist()
|
||||||
|
composeTestRule.onNodeWithText(errorMessage).assertDoesNotExist()
|
||||||
|
|
||||||
|
mutableStateFlow.update {
|
||||||
|
it.copy(
|
||||||
|
dialogState = VaultItemListingState.DialogState.Error(
|
||||||
|
title = null,
|
||||||
|
message = errorMessage.asText(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
composeTestRule
|
||||||
|
.onNodeWithText(errorMessage)
|
||||||
|
.assertIsDisplayed()
|
||||||
|
.assert(hasAnyAncestor(isDialog()))
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `loading dialog should be displayed according to state`() {
|
fun `loading dialog should be displayed according to state`() {
|
||||||
val loadingMessage = "syncing"
|
val loadingMessage = "syncing"
|
||||||
|
|
|
@ -16,6 +16,8 @@ import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCollectionV
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockFolderView
|
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockFolderView
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSendView
|
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSendView
|
||||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||||
|
import com.x8bit.bitwarden.data.vault.repository.model.DeleteSendResult
|
||||||
|
import com.x8bit.bitwarden.data.vault.repository.model.RemovePasswordSendResult
|
||||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
||||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
|
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
|
||||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||||
|
@ -23,6 +25,7 @@ import com.x8bit.bitwarden.ui.platform.base.util.concat
|
||||||
import com.x8bit.bitwarden.ui.vault.feature.itemlisting.util.createMockDisplayItemForCipher
|
import com.x8bit.bitwarden.ui.vault.feature.itemlisting.util.createMockDisplayItemForCipher
|
||||||
import com.x8bit.bitwarden.ui.vault.feature.vault.model.VaultFilterType
|
import com.x8bit.bitwarden.ui.vault.feature.vault.model.VaultFilterType
|
||||||
import com.x8bit.bitwarden.ui.vault.model.VaultItemListingType
|
import com.x8bit.bitwarden.ui.vault.model.VaultItemListingType
|
||||||
|
import io.mockk.coEvery
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.just
|
import io.mockk.just
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
|
@ -40,6 +43,7 @@ import java.time.Clock
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.ZoneOffset
|
import java.time.ZoneOffset
|
||||||
|
|
||||||
|
@Suppress("LargeClass")
|
||||||
class VaultItemListingViewModelTest : BaseViewModelTest() {
|
class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||||
|
|
||||||
private val clock: Clock = Clock.fixed(
|
private val clock: Clock = Clock.fixed(
|
||||||
|
@ -90,6 +94,13 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `DismissDialogClick should clear the dialog state`() {
|
||||||
|
val viewModel = createVaultItemListingViewModel()
|
||||||
|
viewModel.actionChannel.trySend(VaultItemListingsAction.DismissDialogClick)
|
||||||
|
assertEquals(initialState.copy(dialogState = null), viewModel.stateFlow.value)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `SearchIconClick should emit NavigateToVaultSearchScreen`() = runTest {
|
fun `SearchIconClick should emit NavigateToVaultSearchScreen`() = runTest {
|
||||||
val viewModel = createVaultItemListingViewModel()
|
val viewModel = createVaultItemListingViewModel()
|
||||||
|
@ -189,15 +200,44 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `DeleteSendClick should emit ShowToast`() = runTest {
|
fun `DeleteSendClick with deleteSend error should display error dialog`() = runTest {
|
||||||
val sendId = "sendId"
|
val sendId = "sendId1234"
|
||||||
|
coEvery { vaultRepository.deleteSend(sendId) } returns DeleteSendResult.Error
|
||||||
|
|
||||||
val viewModel = createVaultItemListingViewModel()
|
val viewModel = createVaultItemListingViewModel()
|
||||||
viewModel.eventFlow.test {
|
viewModel.stateFlow.test {
|
||||||
viewModel.actionChannel.trySend(
|
assertEquals(initialState, awaitItem())
|
||||||
VaultItemListingsAction.DeleteSendClick(sendId = sendId),
|
viewModel.trySendAction(VaultItemListingsAction.DeleteSendClick(sendId))
|
||||||
|
assertEquals(
|
||||||
|
initialState.copy(
|
||||||
|
dialogState = VaultItemListingState.DialogState.Loading(
|
||||||
|
message = R.string.deleting.asText(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
awaitItem(),
|
||||||
)
|
)
|
||||||
assertEquals(
|
assertEquals(
|
||||||
VaultItemListingEvent.ShowToast("Not yet implemented".asText()),
|
initialState.copy(
|
||||||
|
dialogState = VaultItemListingState.DialogState.Error(
|
||||||
|
title = R.string.an_error_has_occurred.asText(),
|
||||||
|
message = R.string.generic_error_message.asText(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
awaitItem(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `DeleteSendClick with deleteSend success should emit ShowToast`() = runTest {
|
||||||
|
val sendId = "sendId1234"
|
||||||
|
coEvery { vaultRepository.deleteSend(sendId) } returns DeleteSendResult.Success
|
||||||
|
|
||||||
|
val viewModel = createVaultItemListingViewModel()
|
||||||
|
viewModel.eventFlow.test {
|
||||||
|
viewModel.trySendAction(VaultItemListingsAction.DeleteSendClick(sendId))
|
||||||
|
assertEquals(
|
||||||
|
VaultItemListingEvent.ShowToast(R.string.send_deleted.asText()),
|
||||||
awaitItem(),
|
awaitItem(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -216,15 +256,49 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `RemoveSendPasswordClick should emit ShowToast`() = runTest {
|
fun `RemovePasswordClick with removePasswordSend error should display error dialog`() =
|
||||||
val sendId = "sendId"
|
runTest {
|
||||||
|
val sendId = "sendId1234"
|
||||||
|
coEvery {
|
||||||
|
vaultRepository.removePasswordSend(sendId)
|
||||||
|
} returns RemovePasswordSendResult.Error(errorMessage = null)
|
||||||
|
|
||||||
val viewModel = createVaultItemListingViewModel()
|
val viewModel = createVaultItemListingViewModel()
|
||||||
viewModel.eventFlow.test {
|
viewModel.stateFlow.test {
|
||||||
viewModel.actionChannel.trySend(
|
assertEquals(initialState, awaitItem())
|
||||||
VaultItemListingsAction.RemoveSendPasswordClick(sendId = sendId),
|
viewModel.trySendAction(VaultItemListingsAction.RemoveSendPasswordClick(sendId))
|
||||||
|
assertEquals(
|
||||||
|
initialState.copy(
|
||||||
|
dialogState = VaultItemListingState.DialogState.Loading(
|
||||||
|
message = R.string.removing_send_password.asText(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
awaitItem(),
|
||||||
)
|
)
|
||||||
assertEquals(
|
assertEquals(
|
||||||
VaultItemListingEvent.ShowToast("Not yet implemented".asText()),
|
initialState.copy(
|
||||||
|
dialogState = VaultItemListingState.DialogState.Error(
|
||||||
|
title = R.string.an_error_has_occurred.asText(),
|
||||||
|
message = R.string.generic_error_message.asText(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
awaitItem(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `RemovePasswordClick with removePasswordSend success should emit ShowToast`() = runTest {
|
||||||
|
val sendId = "sendId1234"
|
||||||
|
coEvery {
|
||||||
|
vaultRepository.removePasswordSend(sendId)
|
||||||
|
} returns RemovePasswordSendResult.Success(mockk())
|
||||||
|
|
||||||
|
val viewModel = createVaultItemListingViewModel()
|
||||||
|
viewModel.eventFlow.test {
|
||||||
|
viewModel.trySendAction(VaultItemListingsAction.RemoveSendPasswordClick(sendId))
|
||||||
|
assertEquals(
|
||||||
|
VaultItemListingEvent.ShowToast(R.string.send_password_removed.asText()),
|
||||||
awaitItem(),
|
awaitItem(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue