BIT-540 Adding Custom Field Edit Actions (#723)

This commit is contained in:
Oleg Semenenko 2024-01-23 08:18:58 -06:00 committed by Álison Fernandes
parent 9cc8763642
commit 17eb3e2e0b
10 changed files with 524 additions and 36 deletions

View file

@ -232,6 +232,7 @@ fun LazyListScope.vaultAddEditCardItems(
VaultAddEditCustomField(
customItem,
onCustomFieldValueChange = commonHandlers.onCustomFieldValueChange,
onCustomFieldAction = commonHandlers.onCustomFieldActionSelect,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),

View file

@ -3,21 +3,27 @@ package com.x8bit.bitwarden.ui.vault.feature.addedit
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.semantics
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.ui.platform.base.util.showNotYetImplementedToast
import com.x8bit.bitwarden.ui.platform.components.BitwardenBasicDialogRow
import com.x8bit.bitwarden.ui.platform.components.BitwardenIconButtonWithResource
import com.x8bit.bitwarden.ui.platform.components.BitwardenMultiSelectButton
import com.x8bit.bitwarden.ui.platform.components.BitwardenPasswordFieldWithActions
import com.x8bit.bitwarden.ui.platform.components.BitwardenRowOfActions
import com.x8bit.bitwarden.ui.platform.components.BitwardenSelectionDialog
import com.x8bit.bitwarden.ui.platform.components.BitwardenTextEntryDialog
import com.x8bit.bitwarden.ui.platform.components.BitwardenTextFieldWithActions
import com.x8bit.bitwarden.ui.platform.components.BitwardenWideSwitch
import com.x8bit.bitwarden.ui.platform.components.model.IconResource
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldAction
import com.x8bit.bitwarden.ui.vault.model.VaultLinkedFieldType
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
@ -28,6 +34,7 @@ import kotlinx.collections.immutable.toImmutableList
*
* @param customField The field that is to be displayed.
* @param onCustomFieldValueChange Invoked when the user changes the value.
* @param onCustomFieldAction Invoked when the user chooses an action.
* @param modifier Modifier for the UI elements.
* @param supportedLinkedTypes The supported linked types for the vault item.
*/
@ -36,15 +43,46 @@ import kotlinx.collections.immutable.toImmutableList
fun VaultAddEditCustomField(
customField: VaultAddEditState.Custom,
onCustomFieldValueChange: (VaultAddEditState.Custom) -> Unit,
onCustomFieldAction: (CustomFieldAction, VaultAddEditState.Custom) -> Unit,
modifier: Modifier = Modifier,
supportedLinkedTypes: ImmutableList<VaultLinkedFieldType> = persistentListOf(),
) {
var shouldShowChooserDialog by remember { mutableStateOf(false) }
var shouldShowEditDialog by remember { mutableStateOf(false) }
if (shouldShowChooserDialog) {
CustomFieldActionDialog(
onCustomFieldAction = { action ->
shouldShowChooserDialog = false
onCustomFieldAction(action, customField)
},
onDismissRequest = { shouldShowChooserDialog = false },
onEditAction = {
shouldShowEditDialog = true
shouldShowChooserDialog = false
},
)
}
if (shouldShowEditDialog) {
BitwardenTextEntryDialog(
title = stringResource(id = R.string.custom_field_name),
textFieldLabel = stringResource(id = R.string.name),
onDismissRequest = { shouldShowEditDialog = false },
onConfirmClick = { name ->
onCustomFieldValueChange(customField.updateName(name))
shouldShowEditDialog = false
},
)
}
when (customField) {
is VaultAddEditState.Custom.BooleanField -> {
CustomFieldBoolean(
label = customField.name,
value = customField.value,
onValueChanged = { onCustomFieldValueChange(customField.copy(value = it)) },
onEditValue = { shouldShowChooserDialog = true },
modifier = modifier,
)
}
@ -56,6 +94,7 @@ fun VaultAddEditCustomField(
onValueChanged = {
onCustomFieldValueChange(customField.copy(value = it))
},
onEditValue = { shouldShowChooserDialog = true },
modifier = modifier,
)
}
@ -68,6 +107,7 @@ fun VaultAddEditCustomField(
onValueChanged = {
onCustomFieldValueChange(customField.copy(vaultLinkedFieldType = it))
},
onEditValue = { shouldShowChooserDialog = true },
modifier = modifier,
)
}
@ -78,6 +118,7 @@ fun VaultAddEditCustomField(
label = customField.name,
value = customField.value,
onValueChanged = { onCustomFieldValueChange(customField.copy(value = it)) },
onEditValue = { shouldShowChooserDialog = true },
modifier = modifier,
)
}
@ -92,10 +133,9 @@ private fun CustomFieldBoolean(
label: String,
value: Boolean,
onValueChanged: (Boolean) -> Unit,
onEditValue: () -> Unit,
modifier: Modifier = Modifier,
) {
val context = LocalContext.current
Row(
modifier = modifier
.semantics(mergeDescendants = true) {}
@ -116,10 +156,7 @@ private fun CustomFieldBoolean(
iconPainter = painterResource(id = R.drawable.ic_settings),
contentDescription = stringResource(id = R.string.edit),
),
onClick = {
// TODO add support for custom field actions (BIT-540)
showNotYetImplementedToast(context = context)
},
onClick = onEditValue,
)
},
)
@ -134,10 +171,9 @@ private fun CustomFieldHiddenField(
label: String,
value: String,
onValueChanged: (String) -> Unit,
onEditValue: () -> Unit,
modifier: Modifier = Modifier,
) {
val context = LocalContext.current
BitwardenPasswordFieldWithActions(
label = label,
value = value,
@ -150,10 +186,7 @@ private fun CustomFieldHiddenField(
iconPainter = painterResource(id = R.drawable.ic_settings),
contentDescription = stringResource(id = R.string.edit),
),
onClick = {
// TODO Add support for custom field actions (BIT-540)
showNotYetImplementedToast(context = context)
},
onClick = onEditValue,
)
},
)
@ -167,10 +200,9 @@ private fun CustomFieldTextField(
label: String,
value: String,
onValueChanged: (String) -> Unit,
onEditValue: () -> Unit,
modifier: Modifier = Modifier,
) {
val context = LocalContext.current
BitwardenTextFieldWithActions(
label = label,
value = value,
@ -183,10 +215,7 @@ private fun CustomFieldTextField(
iconPainter = painterResource(id = R.drawable.ic_settings),
contentDescription = stringResource(id = R.string.edit),
),
onClick = {
// TODO add support for custom field actions (BIT-540)
showNotYetImplementedToast(context = context)
},
onClick = onEditValue,
)
},
)
@ -199,11 +228,11 @@ private fun CustomFieldTextField(
private fun CustomFieldLinkedField(
selectedOption: VaultLinkedFieldType,
onValueChanged: (VaultLinkedFieldType) -> Unit,
onEditValue: () -> Unit,
modifier: Modifier = Modifier,
label: String = "",
label: String = stringResource(id = R.string.options),
supportedLinkedTypes: ImmutableList<VaultLinkedFieldType> = persistentListOf(),
) {
val context = LocalContext.current
val possibleTypesWithStrings = supportedLinkedTypes.associateWith { it.label.invoke() }
Row(
@ -233,12 +262,50 @@ private fun CustomFieldLinkedField(
iconPainter = painterResource(id = R.drawable.ic_settings),
contentDescription = stringResource(id = R.string.edit),
),
onClick = {
// TODO add support for custom field actions (BIT-540)
showNotYetImplementedToast(context = context)
},
onClick = onEditValue,
)
},
)
}
}
/**
* A dialog for editing a custom field item.
*/
@Composable
private fun CustomFieldActionDialog(
onCustomFieldAction: (CustomFieldAction) -> Unit,
onEditAction: () -> Unit,
onDismissRequest: () -> Unit,
) {
BitwardenSelectionDialog(
title = stringResource(id = R.string.options),
onDismissRequest = onDismissRequest,
) {
CustomFieldAction
.entries
.forEach { action ->
BitwardenBasicDialogRow(
text = action.actionText.invoke(),
onClick = {
if (action == CustomFieldAction.EDIT) {
onEditAction()
} else {
onCustomFieldAction(action)
}
},
)
}
}
}
/**
* A helper method that will copy over the new name to a custom field.
*/
private fun VaultAddEditState.Custom.updateName(name: String): VaultAddEditState.Custom =
when (this) {
is VaultAddEditState.Custom.BooleanField -> this.copy(name = name)
is VaultAddEditState.Custom.HiddenField -> this.copy(name = name)
is VaultAddEditState.Custom.LinkedField -> this.copy(name = name)
is VaultAddEditState.Custom.TextField -> this.copy(name = name)
}

View file

@ -341,6 +341,7 @@ fun LazyListScope.vaultAddEditIdentityItems(
VaultAddEditCustomField(
customField = customItem,
onCustomFieldValueChange = commonTypeHandlers.onCustomFieldValueChange,
onCustomFieldAction = commonTypeHandlers.onCustomFieldActionSelect,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),

View file

@ -312,6 +312,7 @@ fun LazyListScope.vaultAddEditLoginItems(
VaultAddEditCustomField(
customItem,
onCustomFieldValueChange = commonActionHandler.onCustomFieldValueChange,
onCustomFieldAction = commonActionHandler.onCustomFieldActionSelect,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),

View file

@ -138,6 +138,7 @@ fun LazyListScope.vaultAddEditSecureNotesItems(
VaultAddEditCustomField(
customItem,
onCustomFieldValueChange = commonTypeHandlers.onCustomFieldValueChange,
onCustomFieldAction = commonTypeHandlers.onCustomFieldActionSelect,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),

View file

@ -20,6 +20,7 @@ import com.x8bit.bitwarden.ui.platform.base.util.asText
import com.x8bit.bitwarden.ui.platform.base.util.concat
import com.x8bit.bitwarden.ui.platform.manager.resource.ResourceManager
import com.x8bit.bitwarden.ui.tools.feature.generator.model.GeneratorMode
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldAction
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldType
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.toCustomField
import com.x8bit.bitwarden.ui.vault.feature.addedit.util.toViewState
@ -37,6 +38,7 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.parcelize.IgnoredOnParcel
import kotlinx.parcelize.Parcelize
import java.util.Collections
import javax.inject.Inject
private const val KEY_STATE = "state"
@ -148,6 +150,9 @@ class VaultAddEditViewModel @Inject constructor(
)
is VaultAddEditAction.Common.TooltipClick -> handleTooltipClick()
is VaultAddEditAction.Common.CustomFieldActionSelect -> handleCustomFieldActionSelected(
action,
)
}
}
@ -297,6 +302,67 @@ class VaultAddEditViewModel @Inject constructor(
}
}
private fun handleCustomFieldActionSelected(
action: VaultAddEditAction.Common.CustomFieldActionSelect,
) {
when (action.customFieldAction) {
CustomFieldAction.MOVE_UP -> {
val items =
(mutableStateFlow.value.viewState as VaultAddEditState.ViewState.Content)
.common
.customFieldData
.toMutableList()
val index = items.lastIndexOf(action.customField)
if (index == 0) {
return
}
Collections.swap(items, index, index - 1)
updateCommonContent { commonContent ->
commonContent.copy(
customFieldData = items,
)
}
}
CustomFieldAction.MOVE_DOWN -> {
val items =
(mutableStateFlow.value.viewState as VaultAddEditState.ViewState.Content)
.common
.customFieldData
.toMutableList()
val index = items.indexOf(action.customField)
if (index == items.lastIndex) {
return
}
Collections.swap(items, index, index + 1)
updateCommonContent { commonContent ->
commonContent.copy(
customFieldData = items,
)
}
}
CustomFieldAction.DELETE -> {
updateCommonContent { commonContent ->
commonContent.copy(
customFieldData = commonContent.customFieldData.filter {
it != action.customField
},
)
}
}
// Nothing is done here since we handle this with a CustomFieldValueChange action
CustomFieldAction.EDIT -> Unit
}
}
private fun handleFolderTextInputChange(
action: VaultAddEditAction.Common.FolderChange,
) {
@ -1277,13 +1343,18 @@ data class VaultAddEditState(
*/
abstract val itemId: String
/**
* The name of the custom field.
*/
abstract val name: String
/**
* Represents the data for displaying a custom text field.
*/
@Parcelize
data class TextField(
override val itemId: String,
val name: String,
override val name: String,
val value: String,
) : Custom()
@ -1293,7 +1364,7 @@ data class VaultAddEditState(
@Parcelize
data class HiddenField(
override val itemId: String,
val name: String,
override val name: String,
val value: String,
) : Custom()
@ -1303,7 +1374,7 @@ data class VaultAddEditState(
@Parcelize
data class BooleanField(
override val itemId: String,
val name: String,
override val name: String,
val value: Boolean,
) : Custom()
@ -1313,7 +1384,7 @@ data class VaultAddEditState(
@Parcelize
data class LinkedField(
override val itemId: String,
val name: String,
override val name: String,
val vaultLinkedFieldType: VaultLinkedFieldType?,
) : Custom()
}
@ -1462,6 +1533,14 @@ sealed class VaultAddEditAction {
*/
data class CustomFieldValueChange(val customField: VaultAddEditState.Custom) : Common()
/**
* Fired when the custom field data is changed.
*/
data class CustomFieldActionSelect(
val customFieldAction: CustomFieldAction,
val customField: VaultAddEditState.Custom,
) : Common()
/**
* Represents the action to open tooltip
*/

View file

@ -4,6 +4,7 @@ import com.x8bit.bitwarden.ui.platform.base.util.asText
import com.x8bit.bitwarden.ui.vault.feature.addedit.VaultAddEditAction
import com.x8bit.bitwarden.ui.vault.feature.addedit.VaultAddEditState
import com.x8bit.bitwarden.ui.vault.feature.addedit.VaultAddEditViewModel
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldAction
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldType
/**
@ -33,6 +34,7 @@ data class VaultAddEditCommonHandlers(
val onTooltipClick: () -> Unit,
val onAddNewCustomFieldClick: (CustomFieldType, String) -> Unit,
val onCustomFieldValueChange: (VaultAddEditState.Custom) -> Unit,
val onCustomFieldActionSelect: (CustomFieldAction, VaultAddEditState.Custom) -> Unit,
) {
companion object {
@ -97,6 +99,14 @@ data class VaultAddEditCommonHandlers(
),
)
},
onCustomFieldActionSelect = { customFieldAction, field ->
viewModel.trySendAction(
VaultAddEditAction.Common.CustomFieldActionSelect(
customFieldAction,
field,
),
)
},
)
}
}

View file

@ -0,0 +1,16 @@
package com.x8bit.bitwarden.ui.vault.feature.addedit.model
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.ui.platform.base.util.Text
import com.x8bit.bitwarden.ui.platform.base.util.asText
/**
* Represents the different actions that can be taken in a custom
* item edit menu.
*/
enum class CustomFieldAction(val actionText: Text) {
EDIT(R.string.edit.asText()),
MOVE_UP(R.string.move_up.asText()),
MOVE_DOWN(R.string.move_down.asText()),
DELETE(R.string.delete.asText()),
}

View file

@ -37,6 +37,7 @@ import com.x8bit.bitwarden.ui.util.onAllNodesWithContentDescriptionAfterScroll
import com.x8bit.bitwarden.ui.util.onAllNodesWithTextAfterScroll
import com.x8bit.bitwarden.ui.util.onNodeWithContentDescriptionAfterScroll
import com.x8bit.bitwarden.ui.util.onNodeWithTextAfterScroll
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldAction
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldType
import com.x8bit.bitwarden.ui.vault.model.VaultAddEditType
import com.x8bit.bitwarden.ui.vault.model.VaultCardBrand
@ -1939,7 +1940,7 @@ class VaultAddEditScreenTest : BaseComposeTest() {
verify {
viewModel.trySendAction(
VaultAddEditAction.Common.CustomFieldValueChange(
VaultAddEditState.Custom.TextField("Test ID", "TestText", ""),
VaultAddEditState.Custom.TextField("Test ID 2", "TestText", ""),
),
)
}
@ -1957,7 +1958,7 @@ class VaultAddEditScreenTest : BaseComposeTest() {
verify {
viewModel.trySendAction(
VaultAddEditAction.Common.CustomFieldValueChange(
VaultAddEditState.Custom.HiddenField("Test ID", "TestHidden", ""),
VaultAddEditState.Custom.HiddenField("Test ID 3", "TestHidden", ""),
),
)
}
@ -1975,7 +1976,122 @@ class VaultAddEditScreenTest : BaseComposeTest() {
verify {
viewModel.trySendAction(
VaultAddEditAction.Common.CustomFieldValueChange(
VaultAddEditState.Custom.BooleanField("Test ID", "TestBoolean", true),
VaultAddEditState.Custom.BooleanField("Test ID 1", "TestBoolean", true),
),
)
}
}
@Suppress("MaxLineLength")
@Test
fun `clicking custom field edit icon and Edit option sends a CustomFieldValueChange action`() {
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES_CUSTOM_FIELDS
composeTestRule
.onAllNodesWithContentDescriptionAfterScroll("Edit")
.onFirst()
.performClick()
composeTestRule
.onNodeWithText("Edit")
.performClick()
composeTestRule
.onNodeWithText("Name")
.performTextInput("NewTestBooleanName")
composeTestRule
.onNodeWithText("Ok")
.performClick()
verify {
viewModel.trySendAction(
VaultAddEditAction.Common.CustomFieldValueChange(
VaultAddEditState.Custom.BooleanField("Test ID 1", "NewTestBooleanName", false),
),
)
}
}
@Suppress("MaxLineLength")
@Test
fun `clicking custom field edit icon and Delete option sends a CustomFieldActionSelect delete action`() {
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES_CUSTOM_FIELDS
composeTestRule
.onAllNodesWithContentDescriptionAfterScroll("Edit")
.onFirst()
.performClick()
composeTestRule
.onNodeWithText("Delete")
.performClick()
verify {
viewModel.trySendAction(
VaultAddEditAction.Common.CustomFieldActionSelect(
customFieldAction = CustomFieldAction.DELETE,
customField = VaultAddEditState.Custom.BooleanField(
itemId = "Test ID 1",
name = "TestBoolean",
value = false,
),
),
)
}
}
@Suppress("MaxLineLength")
@Test
fun `clicking custom field edit icon and Move down option sends a CustomFieldActionSelect move down action`() {
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES_CUSTOM_FIELDS
composeTestRule
.onAllNodesWithContentDescriptionAfterScroll("Edit")
.onFirst()
.performClick()
composeTestRule
.onNodeWithText("Move down")
.performClick()
verify {
viewModel.trySendAction(
VaultAddEditAction.Common.CustomFieldActionSelect(
customFieldAction = CustomFieldAction.MOVE_DOWN,
customField = VaultAddEditState.Custom.BooleanField(
itemId = "Test ID 1",
name = "TestBoolean",
value = false,
),
),
)
}
}
@Suppress("MaxLineLength")
@Test
fun `clicking custom field edit icon and Move Up options sends a CustomFieldActionSelect move up action`() {
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES_CUSTOM_FIELDS
composeTestRule
.onAllNodesWithContentDescriptionAfterScroll("Edit")
.onFirst()
.performClick()
composeTestRule
.onNodeWithText("Move Up")
.performClick()
verify {
viewModel.trySendAction(
VaultAddEditAction.Common.CustomFieldActionSelect(
customFieldAction = CustomFieldAction.MOVE_UP,
customField = VaultAddEditState.Custom.BooleanField(
itemId = "Test ID 1",
name = "TestBoolean",
value = false,
),
),
)
}
@ -2111,10 +2227,10 @@ class VaultAddEditScreenTest : BaseComposeTest() {
viewState = VaultAddEditState.ViewState.Content(
common = VaultAddEditState.ViewState.Content.Common(
customFieldData = listOf(
VaultAddEditState.Custom.BooleanField("Test ID", "TestBoolean", false),
VaultAddEditState.Custom.TextField("Test ID", "TestText", "TestTextVal"),
VaultAddEditState.Custom.BooleanField("Test ID 1", "TestBoolean", false),
VaultAddEditState.Custom.TextField("Test ID 2", "TestText", "TestTextVal"),
VaultAddEditState.Custom.HiddenField(
"Test ID",
"Test ID 3",
"TestHidden",
"TestHiddenVal",
),

View file

@ -18,6 +18,7 @@ import com.x8bit.bitwarden.ui.platform.base.util.Text
import com.x8bit.bitwarden.ui.platform.base.util.asText
import com.x8bit.bitwarden.ui.platform.manager.resource.ResourceManager
import com.x8bit.bitwarden.ui.tools.feature.generator.model.GeneratorMode
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldAction
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldType
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.toCustomField
import com.x8bit.bitwarden.ui.vault.feature.addedit.util.toViewState
@ -1444,6 +1445,201 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
)
}
@Test
fun `CustomFieldValueChange should update name field`() = runTest {
val initState = createVaultAddItemState(
typeContentViewState = VaultAddEditState.ViewState.Content.ItemType.SecureNotes,
commonContentViewState = VaultAddEditState.ViewState.Content.Common(
customFieldData = listOf(
VaultAddEditState.Custom.BooleanField(
"TestId 3",
"Boolean Field",
true,
),
),
),
)
assertCustomFieldValueChange(
initState,
CustomFieldType.BOOLEAN,
)
}
@Test
fun `CustomFieldActionSelect with delete action should delete the item`() = runTest {
val customFieldData = VaultAddEditState.Custom.BooleanField(
"TestId 3",
"Boolean Field",
true,
)
val initState = createVaultAddItemState(
typeContentViewState = VaultAddEditState.ViewState.Content.ItemType.SecureNotes,
commonContentViewState = VaultAddEditState.ViewState.Content.Common(
customFieldData = listOf(customFieldData),
),
)
val viewModel = createAddVaultItemViewModel(
savedStateHandle = createSavedStateHandleWithState(
state = initState,
vaultAddEditType = VaultAddEditType.AddItem,
),
)
val currentContentState =
(viewModel.stateFlow.value.viewState as VaultAddEditState.ViewState.Content)
val expectedState = currentContentState
.copy(
common = currentContentState.common.copy(
customFieldData = listOf(),
),
)
viewModel.actionChannel.trySend(
VaultAddEditAction.Common.CustomFieldActionSelect(
CustomFieldAction.DELETE,
customFieldData,
),
)
assertEquals(
expectedState,
viewModel.stateFlow.value.viewState,
)
}
@Test
fun `CustomFieldActionSelect with move up action should move the item up`() = runTest {
val customFieldData =
VaultAddEditState.Custom.BooleanField(
"TestId 3",
"Boolean Field",
true,
)
val initState = createVaultAddItemState(
typeContentViewState = VaultAddEditState.ViewState.Content.ItemType.SecureNotes,
commonContentViewState = VaultAddEditState.ViewState.Content.Common(
customFieldData = listOf(
VaultAddEditState.Custom.BooleanField(
"TestId 1",
"Boolean Field",
true,
), VaultAddEditState.Custom.BooleanField(
"TestId 3",
"Boolean Field",
true,
),
),
),
)
val viewModel = createAddVaultItemViewModel(
savedStateHandle = createSavedStateHandleWithState(
state = initState,
vaultAddEditType = VaultAddEditType.AddItem,
),
)
val currentContentState =
(viewModel.stateFlow.value.viewState as VaultAddEditState.ViewState.Content)
val expectedState = currentContentState
.copy(
common = currentContentState.common.copy(
customFieldData = listOf(
VaultAddEditState.Custom.BooleanField(
"TestId 3",
"Boolean Field",
true,
),
VaultAddEditState.Custom.BooleanField(
"TestId 1",
"Boolean Field",
true,
),
),
),
)
viewModel.actionChannel.trySend(
VaultAddEditAction.Common.CustomFieldActionSelect(
CustomFieldAction.MOVE_UP,
customFieldData,
),
)
assertEquals(
expectedState,
viewModel.stateFlow.value.viewState,
)
}
@Test
fun `CustomFieldActionSelect with move down action should move the item down`() = runTest {
val customFieldData =
VaultAddEditState.Custom.BooleanField(
"TestId 1",
"Boolean Field",
true,
)
val initState = createVaultAddItemState(
typeContentViewState = VaultAddEditState.ViewState.Content.ItemType.SecureNotes,
commonContentViewState = VaultAddEditState.ViewState.Content.Common(
customFieldData = listOf(
VaultAddEditState.Custom.BooleanField(
"TestId 1",
"Boolean Field",
true,
),
VaultAddEditState.Custom.BooleanField(
"TestId 3",
"Boolean Field",
true,
),
),
),
)
val viewModel = createAddVaultItemViewModel(
savedStateHandle = createSavedStateHandleWithState(
state = initState,
vaultAddEditType = VaultAddEditType.AddItem,
),
)
val currentContentState =
(viewModel.stateFlow.value.viewState as VaultAddEditState.ViewState.Content)
val expectedState = currentContentState
.copy(
common = currentContentState.common.copy(
customFieldData = listOf(
VaultAddEditState.Custom.BooleanField(
"TestId 3",
"Boolean Field",
true,
),
VaultAddEditState.Custom.BooleanField(
"TestId 1",
"Boolean Field",
true,
),
),
),
)
viewModel.actionChannel.trySend(
VaultAddEditAction.Common.CustomFieldActionSelect(
CustomFieldAction.MOVE_DOWN,
customFieldData,
),
)
assertEquals(
expectedState,
viewModel.stateFlow.value.viewState,
)
}
@Test
fun `TooltipClick should emit ShowToast with 'Tooltip' message`() = runTest {
viewModel.eventFlow.test {