mirror of
https://github.com/bitwarden/android.git
synced 2025-03-15 18:58:59 +03:00
Add UI for saving a send (#526)
This commit is contained in:
parent
978e72899b
commit
1cfd85d9f8
6 changed files with 457 additions and 4 deletions
|
@ -19,11 +19,15 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
|||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.x8bit.bitwarden.R
|
||||
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.BitwardenLoadingContent
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenLoadingDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenScaffold
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenTextButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenTopAppBar
|
||||
import com.x8bit.bitwarden.ui.platform.components.LoadingDialogState
|
||||
import com.x8bit.bitwarden.ui.tools.feature.send.addsend.handlers.AddSendHandlers
|
||||
|
||||
/**
|
||||
|
@ -49,6 +53,13 @@ fun AddSendScreen(
|
|||
}
|
||||
}
|
||||
|
||||
AddSendDialogs(
|
||||
dialogState = state.dialogState,
|
||||
onDismissRequest = remember(viewModel) {
|
||||
{ viewModel.trySendAction(AddSendAction.DismissDialogClick) }
|
||||
},
|
||||
)
|
||||
|
||||
BitwardenScaffold(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
|
@ -96,3 +107,25 @@ fun AddSendScreen(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AddSendDialogs(
|
||||
dialogState: AddSendState.DialogState?,
|
||||
onDismissRequest: () -> Unit,
|
||||
) {
|
||||
when (dialogState) {
|
||||
is AddSendState.DialogState.Error -> BitwardenBasicDialog(
|
||||
visibilityState = BasicDialogState.Shown(
|
||||
title = dialogState.title,
|
||||
message = dialogState.message,
|
||||
),
|
||||
onDismissRequest = onDismissRequest,
|
||||
)
|
||||
|
||||
is AddSendState.DialogState.Loading -> BitwardenLoadingDialog(
|
||||
visibilityState = LoadingDialogState.Shown(dialogState.message),
|
||||
)
|
||||
|
||||
null -> Unit
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,12 +3,18 @@ package com.x8bit.bitwarden.ui.tools.feature.send.addsend
|
|||
import android.os.Parcelable
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.CreateSendResult
|
||||
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.asText
|
||||
import com.x8bit.bitwarden.ui.tools.feature.send.addsend.util.toSendView
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -21,6 +27,7 @@ private const val KEY_STATE = "state"
|
|||
@HiltViewModel
|
||||
class AddSendViewModel @Inject constructor(
|
||||
savedStateHandle: SavedStateHandle,
|
||||
private val vaultRepo: VaultRepository,
|
||||
) : BaseViewModel<AddSendState, AddSendEvent, AddSendAction>(
|
||||
initialState = savedStateHandle[KEY_STATE] ?: AddSendState(
|
||||
viewState = AddSendState.ViewState.Content(
|
||||
|
@ -37,6 +44,7 @@ class AddSendViewModel @Inject constructor(
|
|||
isHideByDefaultChecked = false,
|
||||
),
|
||||
),
|
||||
dialogState = null,
|
||||
),
|
||||
) {
|
||||
|
||||
|
@ -48,6 +56,7 @@ class AddSendViewModel @Inject constructor(
|
|||
|
||||
override fun handleAction(action: AddSendAction): Unit = when (action) {
|
||||
is AddSendAction.CloseClick -> handleCloseClick()
|
||||
AddSendAction.DismissDialogClick -> handleDismissDialogClick()
|
||||
is AddSendAction.SaveClick -> handleSaveClick()
|
||||
is AddSendAction.FileTypeClick -> handleFileTypeClick()
|
||||
is AddSendAction.TextTypeClick -> handleTextTypeClick()
|
||||
|
@ -60,6 +69,33 @@ class AddSendViewModel @Inject constructor(
|
|||
is AddSendAction.HideByDefaultToggle -> handleHideByDefaultToggle(action)
|
||||
is AddSendAction.DeactivateThisSendToggle -> handleDeactivateThisSendToggle(action)
|
||||
is AddSendAction.HideMyEmailToggle -> handleHideMyEmailToggle(action)
|
||||
is AddSendAction.Internal -> handleInternalAction(action)
|
||||
}
|
||||
|
||||
private fun handleInternalAction(action: AddSendAction.Internal): Unit = when (action) {
|
||||
is AddSendAction.Internal.CreateSendResultReceive -> handleCreateSendResultReceive(action)
|
||||
}
|
||||
|
||||
private fun handleCreateSendResultReceive(
|
||||
action: AddSendAction.Internal.CreateSendResultReceive,
|
||||
) {
|
||||
when (action.result) {
|
||||
CreateSendResult.Error -> {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
dialogState = AddSendState.DialogState.Error(
|
||||
title = R.string.an_error_has_occurred.asText(),
|
||||
message = R.string.generic_error_message.asText(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
CreateSendResult.Success -> {
|
||||
mutableStateFlow.update { it.copy(dialogState = null) }
|
||||
sendEvent(AddSendEvent.NavigateBack)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handlePasswordChange(action: AddSendAction.PasswordChange) {
|
||||
|
@ -88,7 +124,38 @@ class AddSendViewModel @Inject constructor(
|
|||
|
||||
private fun handleCloseClick() = sendEvent(AddSendEvent.NavigateBack)
|
||||
|
||||
private fun handleSaveClick() = sendEvent(AddSendEvent.ShowToast("Save Not Implemented"))
|
||||
private fun handleSaveClick() {
|
||||
onContent { content ->
|
||||
if (content.common.name.isBlank()) {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
dialogState = AddSendState.DialogState.Error(
|
||||
title = R.string.an_error_has_occurred.asText(),
|
||||
message = R.string.validation_field_required.asText(
|
||||
R.string.name.asText(),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
return@onContent
|
||||
}
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
dialogState = AddSendState.DialogState.Loading(
|
||||
message = R.string.saving.asText(),
|
||||
),
|
||||
)
|
||||
}
|
||||
viewModelScope.launch {
|
||||
val result = vaultRepo.createSend(content.toSendView())
|
||||
sendAction(AddSendAction.Internal.CreateSendResultReceive(result))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleDismissDialogClick() {
|
||||
mutableStateFlow.update { it.copy(dialogState = null) }
|
||||
}
|
||||
|
||||
private fun handleNameChange(action: AddSendAction.NameChange) {
|
||||
updateCommonContent {
|
||||
|
@ -188,6 +255,7 @@ class AddSendViewModel @Inject constructor(
|
|||
*/
|
||||
@Parcelize
|
||||
data class AddSendState(
|
||||
val dialogState: DialogState?,
|
||||
val viewState: ViewState,
|
||||
) : Parcelable {
|
||||
|
||||
|
@ -251,6 +319,29 @@ data class AddSendState(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the current state of any dialogs on the screen.
|
||||
*/
|
||||
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].
|
||||
*/
|
||||
@Parcelize
|
||||
data class Loading(
|
||||
val message: Text,
|
||||
) : DialogState()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -278,6 +369,11 @@ sealed class AddSendAction {
|
|||
*/
|
||||
data object CloseClick : AddSendAction()
|
||||
|
||||
/**
|
||||
* User clicked to dismiss the current dialog.
|
||||
*/
|
||||
data object DismissDialogClick : AddSendAction()
|
||||
|
||||
/**
|
||||
* User clicked the save button.
|
||||
*/
|
||||
|
@ -337,4 +433,14 @@ sealed class AddSendAction {
|
|||
* User toggled the "deactivate this send" toggle.
|
||||
*/
|
||||
data class DeactivateThisSendToggle(val isChecked: Boolean) : AddSendAction()
|
||||
|
||||
/**
|
||||
* Models actions that the [AddSendViewModel] itself might send.
|
||||
*/
|
||||
sealed class Internal : AddSendAction() {
|
||||
/**
|
||||
* Indicates a result for creating a send has been received.
|
||||
*/
|
||||
data class CreateSendResultReceive(val result: CreateSendResult) : Internal()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
package com.x8bit.bitwarden.ui.tools.feature.send.addsend.util
|
||||
|
||||
import com.bitwarden.core.SendFileView
|
||||
import com.bitwarden.core.SendTextView
|
||||
import com.bitwarden.core.SendType
|
||||
import com.bitwarden.core.SendView
|
||||
import com.x8bit.bitwarden.ui.tools.feature.send.addsend.AddSendState
|
||||
import java.time.Instant
|
||||
|
||||
/**
|
||||
* Transforms [AddSendState] into [SendView].
|
||||
*/
|
||||
// TODO: The 'key' needs to be updated in order to get the save operation to work (BIT-480)
|
||||
fun AddSendState.ViewState.Content.toSendView(): SendView =
|
||||
SendView(
|
||||
id = null,
|
||||
accessId = null,
|
||||
name = common.name,
|
||||
notes = common.noteInput,
|
||||
key = "",
|
||||
password = common.passwordInput.takeUnless { it.isBlank() },
|
||||
type = selectedType.toSendType(),
|
||||
file = toSendFileView(),
|
||||
text = toSendTextView(),
|
||||
maxAccessCount = common.maxAccessCount?.toUInt(),
|
||||
accessCount = 0U,
|
||||
disabled = common.isDeactivateChecked,
|
||||
hideEmail = common.isHideEmailChecked,
|
||||
revisionDate = Instant.now(),
|
||||
deletionDate = Instant.now(),
|
||||
expirationDate = null,
|
||||
)
|
||||
|
||||
private fun AddSendState.ViewState.Content.SendType.toSendType(): SendType =
|
||||
when (this) {
|
||||
AddSendState.ViewState.Content.SendType.File -> SendType.FILE
|
||||
is AddSendState.ViewState.Content.SendType.Text -> SendType.TEXT
|
||||
}
|
||||
|
||||
private fun AddSendState.ViewState.Content.toSendFileView(): SendFileView? =
|
||||
(this.selectedType as? AddSendState.ViewState.Content.SendType.File)?.let {
|
||||
// TODO: Add support for these properties in order to save a file (BIT-480)
|
||||
SendFileView(
|
||||
id = "",
|
||||
fileName = "",
|
||||
size = "",
|
||||
sizeName = "",
|
||||
)
|
||||
}
|
||||
|
||||
private fun AddSendState.ViewState.Content.toSendTextView(): SendTextView? =
|
||||
(this.selectedType as? AddSendState.ViewState.Content.SendType.Text)?.let {
|
||||
SendTextView(
|
||||
text = it.input,
|
||||
hidden = it.isHideByDefaultChecked,
|
||||
)
|
||||
}
|
|
@ -1,11 +1,14 @@
|
|||
package com.x8bit.bitwarden.ui.tools.feature.send.addsend
|
||||
|
||||
import androidx.compose.ui.test.assert
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.assertIsOff
|
||||
import androidx.compose.ui.test.assertIsOn
|
||||
import androidx.compose.ui.test.assertTextEquals
|
||||
import androidx.compose.ui.test.filterToOne
|
||||
import androidx.compose.ui.test.hasAnyAncestor
|
||||
import androidx.compose.ui.test.hasSetTextAction
|
||||
import androidx.compose.ui.test.isDialog
|
||||
import androidx.compose.ui.test.onAllNodesWithText
|
||||
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
|
@ -521,6 +524,60 @@ class AddSendScreenTest : BaseComposeTest() {
|
|||
composeTestRule.onNodeWithText(errorMessage).assertDoesNotExist()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `error dialog should be displayed according to state`() {
|
||||
val errorTitle = "Fail Title"
|
||||
val errorMessage = "Fail Message"
|
||||
composeTestRule.onNode(isDialog()).assertDoesNotExist()
|
||||
composeTestRule.onNodeWithText(errorMessage).assertDoesNotExist()
|
||||
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
dialogState = AddSendState.DialogState.Error(
|
||||
title = errorTitle.asText(),
|
||||
message = errorMessage.asText(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText(errorMessage)
|
||||
.assertIsDisplayed()
|
||||
.assert(hasAnyAncestor(isDialog()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `error dialog Ok click should send DismissDialogClick`() {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
dialogState = AddSendState.DialogState.Error(
|
||||
title = "Fail Title".asText(),
|
||||
message = "Fail Message".asText(),
|
||||
),
|
||||
)
|
||||
}
|
||||
composeTestRule
|
||||
.onNodeWithText("Ok")
|
||||
.performClick()
|
||||
verify { viewModel.trySendAction(AddSendAction.DismissDialogClick) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `loading dialog should be displayed according to state`() {
|
||||
val loadingMessage = "syncing"
|
||||
composeTestRule.onNode(isDialog()).assertDoesNotExist()
|
||||
composeTestRule.onNodeWithText(loadingMessage).assertDoesNotExist()
|
||||
|
||||
mutableStateFlow.update {
|
||||
it.copy(dialogState = AddSendState.DialogState.Loading(loadingMessage.asText()))
|
||||
}
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText(loadingMessage)
|
||||
.assertIsDisplayed()
|
||||
.assert(hasAnyAncestor(isDialog()))
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val DEFAULT_COMMON_STATE = AddSendState.ViewState.Content.Common(
|
||||
name = "",
|
||||
|
@ -543,6 +600,7 @@ class AddSendScreenTest : BaseComposeTest() {
|
|||
|
||||
private val DEFAULT_STATE = AddSendState(
|
||||
viewState = DEFAULT_VIEW_STATE,
|
||||
dialogState = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,14 +2,39 @@ package com.x8bit.bitwarden.ui.tools.feature.send.addsend
|
|||
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import app.cash.turbine.test
|
||||
import com.bitwarden.core.SendView
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.CreateSendResult
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
import com.x8bit.bitwarden.ui.tools.feature.send.addsend.util.toSendView
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.unmockkStatic
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class AddSendViewModelTest : BaseViewModelTest() {
|
||||
|
||||
private val vaultRepository: VaultRepository = mockk()
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
mockkStatic(ADD_SEND_STATE_EXTENSIONS_PATH)
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
fun tearDown() {
|
||||
unmockkStatic(ADD_SEND_STATE_EXTENSIONS_PATH)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `initial state should be correct`() {
|
||||
val viewModel = createViewModel()
|
||||
|
@ -33,12 +58,94 @@ class AddSendViewModelTest : BaseViewModelTest() {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `SaveClick should emit ShowToast`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
fun `SaveClick with createSend success should emit NavigateBack`() = runTest {
|
||||
val viewState = DEFAULT_VIEW_STATE.copy(
|
||||
common = DEFAULT_COMMON_STATE.copy(name = "input"),
|
||||
)
|
||||
val initialState = DEFAULT_STATE.copy(viewState = viewState)
|
||||
val mockSendView = mockk<SendView>()
|
||||
every { viewState.toSendView() } returns mockSendView
|
||||
coEvery { vaultRepository.createSend(mockSendView) } returns CreateSendResult.Success
|
||||
val viewModel = createViewModel(initialState)
|
||||
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(AddSendAction.SaveClick)
|
||||
assertEquals(AddSendEvent.ShowToast("Save Not Implemented"), awaitItem())
|
||||
assertEquals(AddSendEvent.NavigateBack, awaitItem())
|
||||
}
|
||||
assertEquals(initialState, viewModel.stateFlow.value)
|
||||
coVerify(exactly = 1) {
|
||||
vaultRepository.createSend(mockSendView)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `SaveClick with createSend failure should show error dialog`() = runTest {
|
||||
val viewState = DEFAULT_VIEW_STATE.copy(
|
||||
common = DEFAULT_COMMON_STATE.copy(name = "input"),
|
||||
)
|
||||
val initialState = DEFAULT_STATE.copy(viewState = viewState)
|
||||
val mockSendView = mockk<SendView>()
|
||||
every { viewState.toSendView() } returns mockSendView
|
||||
coEvery { vaultRepository.createSend(mockSendView) } returns CreateSendResult.Error
|
||||
val viewModel = createViewModel(initialState)
|
||||
|
||||
viewModel.stateFlow.test {
|
||||
assertEquals(initialState, awaitItem())
|
||||
viewModel.trySendAction(AddSendAction.SaveClick)
|
||||
assertEquals(
|
||||
initialState.copy(
|
||||
dialogState = AddSendState.DialogState.Loading(
|
||||
message = R.string.saving.asText(),
|
||||
),
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
assertEquals(
|
||||
initialState.copy(
|
||||
dialogState = AddSendState.DialogState.Error(
|
||||
title = R.string.an_error_has_occurred.asText(),
|
||||
message = R.string.generic_error_message.asText(),
|
||||
),
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
coVerify(exactly = 1) {
|
||||
vaultRepository.createSend(mockSendView)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `SaveClick with blank name should show error dialog`() {
|
||||
val viewModel = createViewModel(DEFAULT_STATE)
|
||||
|
||||
viewModel.trySendAction(AddSendAction.SaveClick)
|
||||
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(
|
||||
dialogState = AddSendState.DialogState.Error(
|
||||
title = R.string.an_error_has_occurred.asText(),
|
||||
message = R.string.validation_field_required.asText(
|
||||
R.string.name.asText(),
|
||||
),
|
||||
),
|
||||
),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `DismissDialogClick should clear the dialog state`() {
|
||||
val viewModel = createViewModel(
|
||||
DEFAULT_STATE.copy(
|
||||
dialogState = AddSendState.DialogState.Error(
|
||||
title = "Fail Title".asText(),
|
||||
message = "Fail Message".asText(),
|
||||
),
|
||||
),
|
||||
)
|
||||
viewModel.trySendAction(AddSendAction.DismissDialogClick)
|
||||
assertEquals(DEFAULT_STATE, viewModel.stateFlow.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -171,9 +278,13 @@ class AddSendViewModelTest : BaseViewModelTest() {
|
|||
state: AddSendState? = null,
|
||||
): AddSendViewModel = AddSendViewModel(
|
||||
savedStateHandle = SavedStateHandle().apply { set("state", state) },
|
||||
vaultRepo = vaultRepository,
|
||||
)
|
||||
|
||||
companion object {
|
||||
private const val ADD_SEND_STATE_EXTENSIONS_PATH: String =
|
||||
"com.x8bit.bitwarden.ui.tools.feature.send.addsend.util.AddSendStateExtensionsKt"
|
||||
|
||||
private val DEFAULT_COMMON_STATE = AddSendState.ViewState.Content.Common(
|
||||
name = "",
|
||||
maxAccessCount = null,
|
||||
|
@ -195,6 +306,7 @@ class AddSendViewModelTest : BaseViewModelTest() {
|
|||
|
||||
private val DEFAULT_STATE = AddSendState(
|
||||
viewState = DEFAULT_VIEW_STATE,
|
||||
dialogState = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
package com.x8bit.bitwarden.ui.tools.feature.send.addsend.util
|
||||
|
||||
import com.bitwarden.core.SendFileView
|
||||
import com.bitwarden.core.SendType
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSendView
|
||||
import com.x8bit.bitwarden.ui.tools.feature.send.addsend.AddSendState
|
||||
import io.mockk.every
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.unmockkStatic
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.time.Instant
|
||||
|
||||
class AddSendStateExtensionsTest {
|
||||
|
||||
private val fixedInstant: Instant = Instant.parse("2023-10-27T12:00:00Z")
|
||||
|
||||
@AfterEach
|
||||
fun tearDown() {
|
||||
// Some individual tests call mockkStatic so we will make sure this is always undone.
|
||||
unmockkStatic(Instant::class)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toSendView should create an appropriate SendView with file type`() {
|
||||
val sendView = createMockSendView(number = 1, type = SendType.FILE).copy(
|
||||
id = null,
|
||||
accessId = null,
|
||||
key = "",
|
||||
accessCount = 0U,
|
||||
expirationDate = null,
|
||||
text = null,
|
||||
file = SendFileView(
|
||||
id = "",
|
||||
fileName = "",
|
||||
size = "",
|
||||
sizeName = "",
|
||||
),
|
||||
)
|
||||
mockkStatic(Instant::class)
|
||||
every { Instant.now() } returns fixedInstant
|
||||
|
||||
val result = DEFAULT_VIEW_STATE
|
||||
.copy(selectedType = AddSendState.ViewState.Content.SendType.File)
|
||||
.toSendView()
|
||||
|
||||
assertEquals(sendView, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toSendView should create an appropriate SendView with text type`() {
|
||||
val sendView = createMockSendView(number = 1, type = SendType.TEXT).copy(
|
||||
id = null,
|
||||
accessId = null,
|
||||
key = "",
|
||||
accessCount = 0U,
|
||||
expirationDate = null,
|
||||
file = null,
|
||||
)
|
||||
mockkStatic(Instant::class)
|
||||
every { Instant.now() } returns fixedInstant
|
||||
|
||||
val result = DEFAULT_VIEW_STATE.toSendView()
|
||||
|
||||
assertEquals(sendView, result)
|
||||
}
|
||||
}
|
||||
|
||||
private val DEFAULT_COMMON_STATE = AddSendState.ViewState.Content.Common(
|
||||
name = "mockName-1",
|
||||
maxAccessCount = 1,
|
||||
passwordInput = "mockPassword-1",
|
||||
noteInput = "mockNotes-1",
|
||||
isHideEmailChecked = false,
|
||||
isDeactivateChecked = false,
|
||||
)
|
||||
|
||||
private val DEFAULT_SELECTED_TYPE_STATE = AddSendState.ViewState.Content.SendType.Text(
|
||||
input = "mockText-1",
|
||||
isHideByDefaultChecked = false,
|
||||
)
|
||||
|
||||
private val DEFAULT_VIEW_STATE = AddSendState.ViewState.Content(
|
||||
common = DEFAULT_COMMON_STATE,
|
||||
selectedType = DEFAULT_SELECTED_TYPE_STATE,
|
||||
)
|
Loading…
Add table
Reference in a new issue